bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainenstatic struct dotlock_settings file_dict_dotlock_settings = {
10399559650f552a23949772be79eb6a80198c5aTimo Sirainenfile_dict_init(struct dict *driver, const char *uri,
bdb0f594a5673a0c5a16b92dc49eb2a8a66bdaceTimo Sirainen /* no parameters */
eca38954bcf972618f6b85932a3690acbd2b673aTimo Sirainen *error_r = t_strdup_printf("Invalid parameter: %s", p+1);
a6a6ad107e509cf8952a28f740eb2023284497b9Timo Sirainen dict->path = set->home_dir == NULL ? i_strdup(path) :
a6a6ad107e509cf8952a28f740eb2023284497b9Timo Sirainen i_strdup(home_expand_tilde(path, set->home_dir));
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)
37fed1bc1545f7eb1755b61d6a5ac4d083a693b3Timo Sirainen /* Change nothing while there are iterators or they can crash
37fed1bc1545f7eb1755b61d6a5ac4d083a693b3Timo Sirainen because the hash table content recreated. */
62ff6002b1e37a42303c2c0107f324860232e204Timo Sirainen /* Disable NFS flushing for now since it can cause unnecessary
62ff6002b1e37a42303c2c0107f324860232e204Timo Sirainen problems and there's no easy way for us to know here if
62ff6002b1e37a42303c2c0107f324860232e204Timo Sirainen mail_nfs_storage=yes. In any case it's pretty much an unsupported
62ff6002b1e37a42303c2c0107f324860232e204Timo Sirainen setting nowadays. */
62ff6002b1e37a42303c2c0107f324860232e204Timo Sirainen /*nfs_flush_file_handle_cache(dict->path);*/
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen /* file changed */
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainenstatic int file_dict_open_latest(struct file_dict *dict, const char **error_r)
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen open_type = dict->lock_method == FILE_LOCK_METHOD_DOTLOCK ?
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = eacces_error_get("open", dict->path);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = t_strdup_printf("open(%s) failed: %m", dict->path);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainenstatic int file_dict_refresh(struct file_dict *dict, const char **error_r)
37fed1bc1545f7eb1755b61d6a5ac4d083a693b3Timo Sirainen if (dict->refreshed || dict->dict.iter_count > 0)
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi input = i_stream_create_fd(dict->fd, (size_t)-1);
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen while ((key = i_stream_read_next_line(input)) != NULL) {
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen /* strdup() before the second read */
1f366614aaafcc9496ff85b25988f19c3254ab7cTimo Sirainen key = str_tabunescape(p_strdup(dict->hash_pool, key));
3350b29fce44f8bc8fa015dad57024a8de301d38Timo Sirainen if ((value = i_stream_read_next_line(input)) == NULL)
1f366614aaafcc9496ff85b25988f19c3254ab7cTimo Sirainen value = str_tabunescape(p_strdup(dict->hash_pool, value));
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainenstatic int file_dict_lookup(struct dict *_dict, pool_t pool, const char *key,
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 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]);
37fed1bc1545f7eb1755b61d6a5ac4d083a693b3Timo 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,
76b4207273534f71365bc7f900c23a5160692802Timo Sirainen if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) != 0) {
76b4207273534f71365bc7f900c23a5160692802Timo Sirainen /* match everything */
76b4207273534f71365bc7f900c23a5160692802Timo Sirainen } else if ((ctx->flags & DICT_ITERATE_FLAG_EXACT_KEY) != 0) {
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainenstatic int file_dict_iterate_deinit(struct dict_iterate_context *_ctx,
055389c58fa3915e12fb4e72ec86782ce77c5c72Timo Sirainen const char **error_r)
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;
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen const struct dict_transaction_memory_change *change;
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 */
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 return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainenstatic int file_dict_mkdir(struct file_dict *dict, const char **error_r)
97943a36e08923d625898f5ca8ffd38325a3986dTimo Sirainen if (stat_first_parent(path, &root, &st) < 0) {
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = t_strdup_printf("stat(%s) failed: %m", root);
97943a36e08923d625898f5ca8ffd38325a3986dTimo Sirainen /* preserve parent's permissions when it has setgid bit */
c85f661daf164687fc5af22d74902f94c26597d0Timo Sirainen if (mkdir_parents(path, mode) < 0 && errno != EEXIST) {
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = eacces_error_get("mkdir_parents", path);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = t_strdup_printf("mkdir_parents(%s) failed: %m", path);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainenfile_dict_lock(struct file_dict *dict, struct file_lock **lock_r,
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen const char **error_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);
97943a36e08923d625898f5ca8ffd38325a3986dTimo Sirainen dict->fd = open(dict->path, O_CREAT | O_RDWR, 0600);
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen *error_r = eacces_error_get("creat", dict->path);
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 /* check again if we need to reopen the file because it was
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen just replaced */
b5052fbfdbc2678cc8f12899afe55c998f43b740Timo Sirainen } while ((ret = file_dict_open_latest(dict, error_r)) > 0);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainenfile_dict_write_changes(struct dict_transaction_memory_context *ctx,
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen bool *atomic_inc_not_found_r, const char **error_r)
e3fabe8d0faa9aab7cae2d0eee9653f581a3061dTimo Sirainen struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen temp_path = t_strdup_printf("%s.tmp", dict->path);
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen "dict-file: creat(%s) failed: %m", temp_path);
f739c92a9237db03327dc82e3792e39c160a1e4dTimo Sirainen fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
97943a36e08923d625898f5ca8ffd38325a3986dTimo Sirainen fd = file_dotlock_open(&file_dict_dotlock_settings,
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen "dict-file: 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)) {
1f366614aaafcc9496ff85b25988f19c3254ab7cTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s", temp_path,
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen *error_r = t_strdup_printf("file_dotlock_replace() failed: %m");
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen *error_r = t_strdup_printf("rename(%s, %s) failed: %m",
3954326e793bdef1e94e0ad781ed6cc7e48beebbTimo Sirainenfile_dict_transaction_commit(struct dict_transaction_context *_ctx,
8bf42ce5ef783b96a2ded67524173e95e9b45adaTimo Sirainen (struct dict_transaction_memory_context *)_ctx;
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen if (file_dict_write_changes(ctx, &atomic_inc_not_found, &result.error) < 0)
ade5567577dadb0b275c840208d3ad21a9f00a36Timo Sirainen .transaction_init = file_dict_transaction_init,
ade5567577dadb0b275c840208d3ad21a9f00a36Timo Sirainen .transaction_commit = file_dict_transaction_commit,
ade5567577dadb0b275c840208d3ad21a9f00a36Timo Sirainen .transaction_rollback = dict_transaction_memory_rollback,