diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/dbox-common/dbox-storage.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/dbox-common/dbox-storage.c')
-rw-r--r-- | src/lib-storage/index/dbox-common/dbox-storage.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/src/lib-storage/index/dbox-common/dbox-storage.c b/src/lib-storage/index/dbox-common/dbox-storage.c new file mode 100644 index 0000000..5cac0d6 --- /dev/null +++ b/src/lib-storage/index/dbox-common/dbox-storage.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "path-util.h" +#include "ioloop.h" +#include "fs-api.h" +#include "mkdir-parents.h" +#include "unlink-old-files.h" +#include "mailbox-uidvalidity.h" +#include "mailbox-list-private.h" +#include "index-storage.h" +#include "dbox-storage.h" + +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> +#include <utime.h> + +void dbox_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED, + struct mailbox_list_settings *set) +{ + if (set->layout == NULL) + set->layout = MAILBOX_LIST_NAME_FS; + if (set->subscription_fname == NULL) + set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME; + if (*set->maildir_name == '\0') + set->maildir_name = DBOX_MAILDIR_NAME; + if (*set->mailbox_dir_name == '\0') + set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME; +} + +static bool +dbox_alt_path_has_changed(const char *root_dir, const char *alt_path, + const char *alt_path2, const char *alt_symlink_path) +{ + const char *linkpath, *error; + + if (t_readlink(alt_symlink_path, &linkpath, &error) < 0) { + if (errno == ENOENT) + return alt_path != NULL; + i_error("t_readlink(%s) failed: %s", alt_symlink_path, error); + return FALSE; + } + + if (alt_path == NULL) { + i_warning("dbox %s: Original ALT=%s, " + "but currently no ALT path set", root_dir, linkpath); + return TRUE; + } else if (strcmp(linkpath, alt_path) != 0) { + if (strcmp(linkpath, alt_path2) == 0) { + /* FIXME: for backwards compatibility. old versions + created the symlink to mailboxes/ directory, which + was fine with sdbox, but didn't even exist with + mdbox. we'll silently replace the symlink. */ + return TRUE; + } + i_warning("dbox %s: Original ALT=%s, " + "but currently ALT=%s", root_dir, linkpath, alt_path); + return TRUE; + } + return FALSE; +} + +static void dbox_verify_alt_path(struct mailbox_list *list) +{ + const char *root_dir, *alt_symlink_path, *alt_path, *alt_path2; + + root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR); + alt_symlink_path = + t_strconcat(root_dir, "/"DBOX_ALT_SYMLINK_NAME, NULL); + (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR, + &alt_path); + (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, + &alt_path2); + if (!dbox_alt_path_has_changed(root_dir, alt_path, alt_path2, + alt_symlink_path)) + return; + + /* unlink/create the current alt path symlink */ + i_unlink_if_exists(alt_symlink_path); + if (alt_path != NULL) { + int ret = symlink(alt_path, alt_symlink_path); + if (ret < 0 && errno == ENOENT) { + /* root_dir doesn't exist yet - create it */ + if (mailbox_list_mkdir_root(list, root_dir, + MAILBOX_LIST_PATH_TYPE_DIR) < 0) + return; + ret = symlink(alt_path, alt_symlink_path); + } + if (ret < 0 && errno != EEXIST) { + i_error("symlink(%s, %s) failed: %m", + alt_path, alt_symlink_path); + } + } +} + +int dbox_storage_create(struct mail_storage *_storage, + struct mail_namespace *ns, + const char **error_r) +{ + struct dbox_storage *storage = DBOX_STORAGE(_storage); + const struct mail_storage_settings *set = _storage->set; + const char *error; + + if (*set->mail_attachment_fs != '\0' && + *set->mail_attachment_dir != '\0') { + const char *name, *args, *dir; + + args = strpbrk(set->mail_attachment_fs, ": "); + if (args == NULL) { + name = set->mail_attachment_fs; + args = ""; + } else { + name = t_strdup_until(set->mail_attachment_fs, args++); + } + if (strcmp(name, "sis-queue") == 0 && + (_storage->class_flags & MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG) != 0) { + /* FIXME: the deduplication part doesn't work, because + sdbox renames the files.. */ + *error_r = "mail_attachment_fs: " + "sis-queue not currently supported by sdbox"; + return -1; + } + dir = mail_user_home_expand(_storage->user, + set->mail_attachment_dir); + storage->attachment_dir = p_strdup(_storage->pool, dir); + + if (mailbox_list_init_fs(ns->list, _storage->event, name, args, + storage->attachment_dir, + &storage->attachment_fs, &error) < 0) { + *error_r = t_strdup_printf("mail_attachment_fs: %s", + error); + return -1; + } + } + + if (!ns->list->set.alt_dir_nocheck) + dbox_verify_alt_path(ns->list); + return 0; +} + +void dbox_storage_destroy(struct mail_storage *_storage) +{ + struct dbox_storage *storage = DBOX_STORAGE(_storage); + + fs_deinit(&storage->attachment_fs); + index_storage_destroy(_storage); +} + +uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list) +{ + const char *path; + + path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL); + path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL); + return mailbox_uidvalidity_next(list, path); +} + +void dbox_notify_changes(struct mailbox *box) +{ + const char *dir, *path; + + if (box->notify_callback == NULL) + mailbox_watch_remove_all(box); + else { + if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, + &dir) <= 0) + return; + path = t_strdup_printf("%s/"MAIL_INDEX_PREFIX".log", dir); + mailbox_watch_add(box, path); + } +} + +static bool +dbox_cleanup_temp_files(struct mailbox_list *list, const char *path, + time_t last_scan_time, time_t last_change_time) +{ + unsigned int interval = list->mail_set->mail_temp_scan_interval; + + /* check once in a while if there are temp files to clean up */ + if (interval == 0) { + /* disabled */ + return FALSE; + } else if (last_scan_time >= ioloop_time - (time_t)interval) { + /* not the time to scan it yet */ + return FALSE; + } else { + bool stated = FALSE; + if (last_change_time == (time_t)-1) { + /* Don't know the ctime yet - look it up. */ + struct stat st; + + if (stat(path, &st) < 0) { + if (errno == ENOENT) + i_error("stat(%s) failed: %m", path); + return FALSE; + } + last_change_time = st.st_ctime; + stated = TRUE; + } + if (last_scan_time > last_change_time + DBOX_TMP_DELETE_SECS) { + /* there haven't been any changes to this directory + since we last checked it. If we did an extra stat(), + we need to update the last_scan_time to avoid + stat()ing the next time. */ + return stated; + } + const char *prefix = + mailbox_list_get_global_temp_prefix(list); + (void)unlink_old_files(path, prefix, + ioloop_time - DBOX_TMP_DELETE_SECS); + return TRUE; + } + return FALSE; +} + +int dbox_mailbox_check_existence(struct mailbox *box, time_t *path_ctime_r) +{ + const char *index_path, *box_path = mailbox_get_path(box); + struct stat st; + int ret = -1; + + *path_ctime_r = (time_t)-1; + + if (box->list->set.iter_from_index_dir) { + /* Just because the index directory exists, it doesn't mean + that the mailbox is selectable. Check that by seeing if + dovecot.index.log exists. If it doesn't, fallback to + checking for the dbox-Mails in the mail root directory. + So this also means that if a mailbox is \NoSelect, listing + it will always do a stat() for dbox-Mails in the mail root + directory. That's not ideal, but this makes the behavior + safer and \NoSelect mailboxes are somewhat rare. */ + if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX, + &index_path) < 0) + return -1; + i_assert(index_path != NULL); + index_path = t_strconcat(index_path, "/", box->index_prefix, + ".log", NULL); + ret = stat(index_path, &st); + } + if (ret < 0) { + ret = stat(box_path, &st); + if (ret == 0) + *path_ctime_r = st.st_ctime; + } + + if (ret == 0) { + return 0; + } else if (errno == ENOENT || errno == ENAMETOOLONG) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); + return -1; + } else if (errno == EACCES) { + mailbox_set_critical(box, "%s", + mail_error_eacces_msg("stat", box_path)); + return -1; + } else { + mailbox_set_critical(box, "stat(%s) failed: %m", box_path); + return -1; + } +} + +int dbox_mailbox_open(struct mailbox *box, time_t path_ctime) +{ + const char *box_path = mailbox_get_path(box); + + if (index_storage_mailbox_open(box, FALSE) < 0) + return -1; + mail_index_set_fsync_mode(box->index, + box->storage->set->parsed_fsync_mode, + MAIL_INDEX_FSYNC_MASK_APPENDS | + MAIL_INDEX_FSYNC_MASK_EXPUNGES); + + const struct mail_index_header *hdr = mail_index_get_header(box->view); + if (dbox_cleanup_temp_files(box->list, box_path, + hdr->last_temp_file_scan, path_ctime)) { + /* temp files were scanned. update the last scan timestamp. */ + index_mailbox_update_last_temp_file_scan(box); + } + return 0; +} + +static int dir_is_empty(struct mail_storage *storage, const char *path) +{ + DIR *dir; + struct dirent *d; + int ret = 1; + + dir = opendir(path); + if (dir == NULL) { + if (errno == ENOENT) { + /* race condition with DELETE/RENAME? */ + return 1; + } + mail_storage_set_critical(storage, "opendir(%s) failed: %m", + path); + return -1; + } + while ((d = readdir(dir)) != NULL) { + if (*d->d_name == '.') + continue; + + ret = 0; + break; + } + if (closedir(dir) < 0) { + mail_storage_set_critical(storage, "closedir(%s) failed: %m", + path); + ret = -1; + } + return ret; +} + +int dbox_mailbox_create(struct mailbox *box, + const struct mailbox_update *update, bool directory) +{ + struct dbox_storage *storage = DBOX_STORAGE(box->storage); + const char *alt_path; + struct stat st; + int ret; + + if ((ret = index_storage_mailbox_create(box, directory)) <= 0) + return ret; + if (mailbox_open(box) < 0) + return -1; + if (mail_index_get_header(box->view)->uid_validity != 0 && + !box->storage->rebuilding_list_index) { + mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + return -1; + } + + /* if alt path already exists and contains files, rebuild storage so + that we don't start overwriting files. */ + ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, &alt_path); + if (ret > 0 && stat(alt_path, &st) == 0) { + ret = dir_is_empty(box->storage, alt_path); + if (ret < 0) + return -1; + if (ret == 0) { + mailbox_set_critical(box, + "Existing files in alt path, " + "rebuilding storage to avoid losing messages"); + storage->v.set_mailbox_corrupted(box); + return -1; + } + /* dir is empty, ignore it */ + } + return dbox_mailbox_create_indexes(box, update); +} + +int dbox_mailbox_create_indexes(struct mailbox *box, + const struct mailbox_update *update) +{ + struct dbox_storage *storage = DBOX_STORAGE(box->storage); + struct mail_index_sync_ctx *sync_ctx; + struct mail_index_view *view; + struct mail_index_transaction *trans; + int ret; + + /* use syncing as a lock */ + ret = mail_index_sync_begin(box->index, &sync_ctx, &view, &trans, 0); + if (ret <= 0) { + i_assert(ret != 0); + mailbox_set_index_error(box); + return -1; + } + + if (mail_index_get_header(view)->uid_validity == 0) { + if (storage->v.mailbox_create_indexes(box, update, trans) < 0) { + mail_index_sync_rollback(&sync_ctx); + return -1; + } + } + + return mail_index_sync_commit(&sync_ctx); +} + +int dbox_verify_alt_storage(struct mailbox_list *list) +{ + const char *alt_path; + struct stat st; + + if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_ALT_DIR, + &alt_path)) + return 0; + + /* make sure alt storage is mounted. if it's not, abort the rebuild. */ + if (stat(alt_path, &st) == 0) + return 0; + if (errno != ENOENT) { + i_error("stat(%s) failed: %m", alt_path); + return -1; + } + + /* try to create the alt directory. if it fails, it means alt + storage isn't mounted. */ + if (mailbox_list_mkdir_root(list, alt_path, + MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) + return -1; + return 0; +} + +bool dbox_header_have_flag(struct mailbox *box, uint32_t ext_id, + unsigned int flags_offset, uint8_t flag) +{ + const void *data; + size_t data_size; + uint8_t flags = 0; + + mail_index_get_header_ext(box->view, ext_id, &data, &data_size); + if (flags_offset < data_size) + flags = *((const uint8_t *)data + flags_offset); + return (flags & flag) != 0; +} |