Mercurial > dovecot > core-2.2
view src/lib-storage/index/dbox-common/dbox-storage.c @ 10254:ea4a80ee0283 HEAD
dbox notify: Look for dbox index files from index dir, not mail root dir.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 05 Nov 2009 15:24:36 -0500 |
parents | 0bb321c347ae |
children | 7f2e9c793af8 |
line wrap: on
line source
/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop.h" #include "randgen.h" #include "hex-binary.h" #include "mkdir-parents.h" #include "unlink-directory.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> 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->dir_guid_fname == NULL) set->dir_guid_fname = DBOX_DIR_GUID_FILE_NAME; if (set->maildir_name == NULL) set->maildir_name = DBOX_MAILDIR_NAME; if (set->mailbox_dir_name == NULL) set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME; } uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list) { const char *path; path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_CONTROL); path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL); return mailbox_uidvalidity_next(path); } void dbox_notify_changes(struct mailbox *box) { struct index_mailbox *ibox = (struct index_mailbox *)box; const char *dir, *path; if (box->notify_callback == NULL) index_mailbox_check_remove_all(ibox); else { dir = mailbox_list_get_path(box->list, box->name, MAILBOX_LIST_PATH_TYPE_INDEX); path = t_strdup_printf("%s/"DBOX_INDEX_PREFIX".log", dir); index_mailbox_check_add(ibox, path); } } static bool dbox_cleanup_if_exists(struct mailbox_list *list, const char *path) { struct stat st; if (stat(path, &st) < 0) return FALSE; /* check once in a while if there are temp files to clean up */ if (st.st_atime > st.st_ctime + DBOX_TMP_DELETE_SECS) { /* there haven't been any changes to this directory since we last checked it. */ } else if (st.st_atime < ioloop_time - DBOX_TMP_SCAN_SECS) { /* time to scan */ const char *prefix = mailbox_list_get_global_temp_prefix(list); (void)unlink_old_files(path, prefix, ioloop_time - DBOX_TMP_DELETE_SECS); } return TRUE; } int dbox_mailbox_open(struct mailbox *box) { struct dbox_storage *storage = (struct dbox_storage *)box->storage; if (box->input != NULL) { mail_storage_set_critical(box->storage, "dbox doesn't support streamed mailboxes"); return -1; } if (dbox_cleanup_if_exists(box->list, box->path)) { return index_storage_mailbox_open(box); } else if (errno == ENOENT) { if (strcmp(box->name, "INBOX") == 0 && (box->list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) { /* INBOX always exists, create it */ if (storage->v.mailbox_create_indexes(box, NULL) < 0) return -1; return box->opened ? 0 : index_storage_mailbox_open(box); } mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name)); return -1; } else if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", mail_error_eacces_msg("stat", box->path)); return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", box->path); return -1; } } static const char * dbox_get_alt_path(struct mailbox_list *list, const char *path) { struct mail_storage *storage = list->ns->storage; const char *root; unsigned int len; if (list->set.alt_dir == NULL || (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0) return NULL; root = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR); len = strlen(root); if (strncmp(path, root, len) != 0 && path[len] == '/') { /* can't determine the alt path - shouldn't happen */ return NULL; } return t_strconcat(list->set.alt_dir, path + len, NULL); } int dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct dbox_storage *storage = (struct dbox_storage *)box->storage; const char *path, *alt_path, *origin; struct stat st; path = mailbox_list_get_path(box->list, box->name, directory ? MAILBOX_LIST_PATH_TYPE_DIR : MAILBOX_LIST_PATH_TYPE_MAILBOX); if (stat(path, &st) == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } if (directory) { mode_t mode; gid_t gid; mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin); if (mkdir_parents_chgrp(path, mode, gid, origin) == 0) return 0; else if (errno == EEXIST) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); } else if (!mail_storage_set_error_from_errno(box->storage)) { mail_storage_set_critical(box->storage, "mkdir(%s) failed: %m", path); } return -1; } /* make sure the alt path doesn't exist yet. it shouldn't (except with race conditions with RENAME/DELETE), but if something crashed and left it lying around we don't want to start overwriting files in it. */ alt_path = dbox_get_alt_path(box->list, path); if (alt_path != NULL && stat(alt_path, &st) == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } return storage->v.mailbox_create_indexes(box, update); } int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED, const char *dir, const char *fname, const char *mailbox_name ATTR_UNUSED, enum mailbox_list_file_type type, enum mailbox_info_flags *flags) { const char *path, *maildir_path; struct stat st, st2; int ret = 1; /* try to avoid stat() with these checks */ if (type != MAILBOX_LIST_FILE_TYPE_DIR && type != MAILBOX_LIST_FILE_TYPE_SYMLINK && type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) { /* it's a file */ *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; } /* need to stat() then */ path = t_strconcat(dir, "/", fname, NULL); if (stat(path, &st) == 0) { if (!S_ISDIR(st.st_mode)) { /* non-directory */ *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; ret = 0; } else if (st.st_nlink == 2) { /* no subdirectories */ *flags |= MAILBOX_NOCHILDREN; } else if (*ctx->list->set.maildir_name != '\0') { /* default configuration: we have one directory containing the mailboxes. if there are 3 links, either this is a selectable mailbox without children or non-selectable mailbox with children */ if (st.st_nlink > 3) *flags |= MAILBOX_CHILDREN; } else { /* non-default configuration: all subdirectories are child mailboxes. */ if (st.st_nlink > 2) *flags |= MAILBOX_CHILDREN; } } else if (errno == ENOENT) { /* doesn't exist - probably a non-existing subscribed mailbox */ *flags |= MAILBOX_NONEXISTENT; } else { /* non-selectable. probably either access denied, or symlink destination not found. don't bother logging errors. */ *flags |= MAILBOX_NOSELECT; } if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) { /* make sure it's a selectable mailbox */ maildir_path = t_strconcat(path, "/", ctx->list->set.maildir_name, NULL); if (stat(maildir_path, &st2) < 0 || !S_ISDIR(st2.st_mode)) *flags |= MAILBOX_NOSELECT; if (st.st_nlink == 3 && *ctx->list->set.maildir_name != '\0') { /* now we know what link count 3 means. */ if ((*flags & MAILBOX_NOSELECT) != 0) *flags |= MAILBOX_CHILDREN; else *flags |= MAILBOX_NOCHILDREN; } } return ret; } static int dbox_list_rename_get_alt_paths(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, enum mailbox_list_path_type path_type, const char **oldpath_r, const char **newpath_r) { const char *path; path = mailbox_list_get_path(oldlist, oldname, path_type); *oldpath_r = dbox_get_alt_path(oldlist, path); if (*oldpath_r == NULL) return 0; path = mailbox_list_get_path(newlist, newname, path_type); *newpath_r = dbox_get_alt_path(newlist, path); if (*newpath_r == NULL) { /* destination dbox storage doesn't have alt-path defined. we can't do the rename easily. */ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Can't rename mailboxes across specified storages."); return -1; } return 1; } int dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { const char *alt_oldpath, *alt_newpath; struct stat st; int ret; ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_DIR, &alt_oldpath, &alt_newpath); if (ret <= 0) return ret; if (stat(alt_newpath, &st) == 0) { /* race condition or a directory left there lying around? safest to just report error. */ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS, "Target mailbox already exists"); return -1; } else if (errno != ENOENT) { mailbox_list_set_critical(oldlist, "stat(%s) failed: %m", alt_newpath); return -1; } return 0; } int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) { enum mailbox_list_path_type path_type; const char *alt_oldpath, *alt_newpath, *path; int ret; path_type = rename_children ? MAILBOX_LIST_PATH_TYPE_DIR : MAILBOX_LIST_PATH_TYPE_MAILBOX; ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname, path_type, &alt_oldpath, &alt_newpath); if (ret <= 0) return ret; if (rename(alt_oldpath, alt_newpath) == 0) { /* ok */ if (!rename_children) { path = mailbox_list_get_path(oldlist, oldname, MAILBOX_LIST_PATH_TYPE_DIR); if (rmdir(path) < 0 && errno != ENOENT && errno != ENOTEMPTY) { mailbox_list_set_critical(oldlist, "rmdir(%s) failed: %m", path); } } } else if (errno != ENOENT) { /* renaming is done already, so just log the error */ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m", alt_oldpath, alt_newpath); } return 0; } static const char *dbox_get_trash_dest(const char *trash_dir) { const char *path; unsigned char randbuf[16]; struct stat st; do { random_fill_weak(randbuf, sizeof(randbuf)); path = t_strconcat(trash_dir, "/", binary_to_hex(randbuf, sizeof(randbuf)), NULL); } while (lstat(path, &st) == 0); return path; } int dbox_list_delete_mailbox1(struct mailbox_list *list, const char *name, const char **trash_dest_r) { struct stat st; const char *path, *trash_dir, *trash_dest; int ret; path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); trash_dir = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR); trash_dir = t_strconcat(trash_dir, "/"DBOX_TRASH_DIR_NAME, NULL); trash_dest = *trash_dest_r = dbox_get_trash_dest(trash_dir); /* first try renaming the actual mailbox to trash directory */ ret = rename(path, trash_dest); if (ret < 0 && errno == ENOENT) { /* either source mailbox doesn't exist or trash directory doesn't exist. try creating the trash and retrying. */ const char *origin; mode_t mode; gid_t gid; mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin); if (mkdir_parents_chgrp(trash_dir, mode, gid, origin) < 0 && errno != EEXIST) { mailbox_list_set_critical(list, "mkdir(%s) failed: %m", trash_dir); return -1; } ret = rename(path, trash_dest); } if (ret == 0) return 1; else if (errno != ENOENT) { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); return -1; } else { /* mailbox not found - what about the directory? */ path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR); if (stat(path, &st) == 0) { /* delete the directory */ } else if (errno == ENOENT) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); return -1; } else if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); return -1; } return 0; } } int dbox_list_delete_mailbox2(struct mailbox_list *list, const char *name, int ret, const char *trash_dest) { const char *path, *alt_path; bool deleted = FALSE; path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (ret > 0) { if (unlink_directory(trash_dest, TRUE) < 0) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", trash_dest); ret = -1; } /* if there's an alt path, delete it too */ alt_path = dbox_get_alt_path(list, path); if (alt_path != NULL) { if (unlink_directory(alt_path, TRUE) < 0) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", alt_path); ret = -1; } } /* try to delete the parent directory also */ deleted = TRUE; path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR); } alt_path = dbox_get_alt_path(list, path); if (alt_path != NULL) (void)rmdir(alt_path); if (rmdir(path) == 0) return ret; else if (errno == ENOTEMPTY) { if (deleted) return ret; mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, t_strdup_printf("Directory %s isn't empty, " "can't delete it.", name)); } else if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "rmdir() failed for %s: %m", path); } return -1; }