dict-file.c revision c73415e93ecf1c699ef054d2b179b10976fa23f3
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2008-2012 Dovecot authors, see the included COPYING file */
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int len;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic struct dotlock_settings file_dict_dotlock_settings = {
10399559650f552a23949772be79eb6a80198c5aTimo Sirainenfile_dict_init(struct dict *driver, const char *uri,
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char *base_dir ATTR_UNUSED, struct dict **dict_r,
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen const char **error_r)
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen const char *p;
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen /* no parameters */
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen *error_r = t_strdup_printf("Invalid parameter: %s", p+1);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen dict->hash_pool = pool_alloconly_create("file dict", 1024);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&dict->hash, dict->hash_pool, 0, str_hash, strcmp);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic void file_dict_deinit(struct dict *_dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic bool file_dict_need_refresh(struct file_dict *dict)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* file changed */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenstatic int file_dict_open_latest(struct file_dict *dict)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen open_type = dict->lock_method == FILE_LOCK_METHOD_DOTLOCK ?
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenstatic int file_dict_refresh(struct file_dict *dict)
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen input = i_stream_create_fd(dict->fd, (size_t)-1, FALSE);
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen while ((key = i_stream_read_next_line(input)) != NULL) {
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen /* strdup() before the second read */
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen if ((value = i_stream_read_next_line(input)) == NULL)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic int file_dict_lookup(struct dict *_dict, pool_t pool,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen *value_r = p_strdup(pool, hash_table_lookup(dict->hash, key));
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenfile_dict_iterate_init(struct dict *_dict, const char *const *paths,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)_dict;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int i, path_count;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen pool = pool_alloconly_create("file dict iterate", 256);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx = p_new(pool, struct file_dict_iterate_context, 1);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (path_count = 0; paths[path_count] != NULL; path_count++) ;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->paths = p_new(pool, struct file_dict_iterate_path, path_count + 1);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (i = 0; i < path_count; i++) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen ctx->paths[i].path = p_strdup(pool, paths[i]);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen ctx->iter = hash_table_iterate_init(dict->hash);
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenstatic const struct file_dict_iterate_path *
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainenfile_dict_iterate_find_path(struct file_dict_iterate_context *ctx,
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen const char *key)
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen unsigned int i;
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen for (i = 0; ctx->paths[i].path != NULL; i++) {
9c7f6dbf65ca01026e5f9c8c8b67c7e629c0b5e7Timo Sirainen if (strncmp(ctx->paths[i].path, key, ctx->paths[i].len) == 0)
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainenstatic bool file_dict_iterate(struct dict_iterate_context *_ctx,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 &&
8d25b6ad05b99e75613cb045a121efd51e6afbb6Timo Sirainenstatic int file_dict_iterate_deinit(struct dict_iterate_context *_ctx)
dba8af1faaf9fd3957254bb6f2234b285f77096fTimo Sirainen pool = pool_alloconly_create("file dict transaction", 2048);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen ctx = p_new(pool, struct dict_transaction_memory_context, 1);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen dict_transaction_memory_init(ctx, _dict, pool);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainenstatic void file_dict_apply_changes(struct dict_transaction_memory_context *ctx,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen const char *tmp;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen const struct dict_transaction_memory_change *change;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen unsigned int new_len;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen if (hash_table_lookup_full(dict->hash, change->key,
3c63c219ae7854b4f1d44a671a65572aa242cbcfTimo Sirainen if (old_value == NULL || new_len > strlen(old_value))
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* fall through */
2aed6c2062317d1750f59c5c88e77d9f10967462Timo Sirainen value = p_strconcat(dict->hash_pool, old_value,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenfd_copy_stat_permissions(const struct stat *src_st,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen ((src_st->st_mode & 0070) >> 3 != (src_st->st_mode & 0007))) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen /* group has different permissions from world.
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen preserve the group. */
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (fchown(dest_fd, (uid_t)-1, src_st->st_gid) < 0) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if ((src_st->st_mode & 07777) != (dest_st.st_mode & 07777)) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen if (fchmod(dest_fd, src_st->st_mode & 07777) < 0) {
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenstatic int fd_copy_permissions(int src_fd, const char *src_path,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainenfd_copy_parent_dir_permissions(const char *src_path, int dest_fd,
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen const char *src_dir, *p;
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainenfile_dict_lock(struct file_dict *dict, struct file_lock **lock_r)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen /* quota file doesn't exist yet, we need to create it */
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600);
c719f74d3fd41d9b9fea0edaea1e00ab90da72dcTimo Sirainen (void)fd_copy_parent_dir_permissions(dict->path, dict->fd,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen if (file_wait_lock(dict->fd, dict->path, F_WRLCK,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file_wait_lock(%s) failed: %m", dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen /* check again if we need to reopen the file because it was
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen just replaced */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen } while ((ret = file_dict_open_latest(dict)) > 0);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainenstatic int file_dict_write_changes(struct dict_transaction_memory_context *ctx,
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path = t_strdup_printf("%s.tmp", dict->path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file dict commit: creat(%s) failed: %m",
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen i_error("file dict commit: file_dotlock_open(%s) failed: %m",
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path = file_dotlock_get_lock_path(dotlock);
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* refresh once more now that we're locked */
b141c303bf09b6ae43e1eb4aac1e1a6b796b9d35Timo Sirainen /* preserve the permissions */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen (void)fd_copy_permissions(dict->fd, dict->path, fd, temp_path);
e5ff2112aea089f3de2badf9b1635677791d1384Timo Sirainen /* get initial permissions from parent directory */
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen (void)fd_copy_parent_dir_permissions(dict->path, fd, temp_path);
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen file_dict_apply_changes(ctx, atomic_inc_not_found_r);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen while (hash_table_iterate(iter, dict->hash, &key, &value)) {
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenfile_dict_transaction_commit(struct dict_transaction_context *_ctx,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen (struct dict_transaction_memory_context *)_ctx;