From: Timo Sirainen Date: Thu, 6 Aug 2009 00:29:24 +0000 (-0400) Subject: lib-storage: Fill mailbox log with mailbox delete/rename/(un)subscribes. X-Git-Tag: 2.0.alpha1~319 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=37847ec8eaec9ad55c9df10ae109efe7b37ac573;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: Fill mailbox log with mailbox delete/rename/(un)subscribes. --HG-- branch : HEAD --- diff --git a/src/lib-storage/index/maildir/maildir-storage.c b/src/lib-storage/index/maildir/maildir-storage.c index d79a9963cd..35d81ef9d4 100644 --- a/src/lib-storage/index/maildir/maildir-storage.c +++ b/src/lib-storage/index/maildir/maildir-storage.c @@ -9,6 +9,7 @@ #include "eacces-error.h" #include "unlink-directory.h" #include "unlink-old-files.h" +#include "mailbox-log.h" #include "mailbox-uidvalidity.h" #include "maildir-storage.h" #include "maildir-uidlist.h" @@ -709,13 +710,80 @@ maildir_delete_nonrecursive(struct mailbox_list *list, const char *path, return 0; } +static int +maildir_delete_with_trash(struct mailbox_list *list, const char *src, + const char *dest, const char *name) +{ + unsigned int count; + + /* rename the .maildir into ..DOVECOT-TRASH which atomically + marks it as being deleted. If we die before deleting the + ..DOVECOT-TRASH directory, it gets deleted the next time + mailbox listing sees it. */ + count = 0; + while (rename(src, dest) < 0) { + if (errno == ENOENT) { + /* it was just deleted under us by + another process */ + mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + return -1; + } + if (!EDESTDIREXISTS(errno)) { + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", src, dest); + return -1; + } + + /* already existed, delete it and try again */ + if (unlink_directory(dest, TRUE) < 0 && + (errno != ENOTEMPTY || count >= 5)) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", dest); + return -1; + } + count++; + } + + if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", dest); + + /* it's already renamed to ..dir, which means it's + deleted as far as the client is concerned. Report + success. */ + } + return 0; +} + +static void mailbox_get_guid(struct mailbox_list *list, const char *name, + uint8_t mailbox_guid[MAIL_GUID_128_SIZE]) +{ + struct mailbox *box; + struct mailbox_status status; + + box = mailbox_alloc(list, name, NULL, MAILBOX_FLAG_KEEP_RECENT); + if (mailbox_open(box) < 0) + memset(mailbox_guid, 0, MAIL_GUID_128_SIZE); + else { + mailbox_get_status(box, STATUS_GUID, &status); + memcpy(mailbox_guid, status.mailbox_guid, MAIL_GUID_128_SIZE); + } + mailbox_close(&box); +} + static int maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) { union mailbox_list_module_context *mlist = MAILDIR_LIST_CONTEXT(list); + uint8_t mailbox_guid[MAIL_GUID_128_SIZE]; + uint8_t dir_guid[MAIL_GUID_128_SIZE]; struct stat st; const char *src, *dest, *base; - int count; + int ret; + + mailbox_get_guid(list, name, mailbox_guid); + (void)mailbox_list_get_guid(list, name, dir_guid); /* Make sure the indexes are closed before trying to delete the directory that contains them. It can still fail with some NFS @@ -762,45 +830,16 @@ maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) dest = maildir_get_unlink_dest(list, name); if (dest == NULL) { /* delete the directory directly without any renaming */ - return maildir_delete_nonrecursive(list, src, name); - } - - /* rename the .maildir into ..DOVECOT-TRASH which atomically - marks it as being deleted. If we die before deleting the - ..DOVECOT-TRASH directory, it gets deleted the next time - mailbox listing sees it. */ - count = 0; - while (rename(src, dest) < 0) { - if (errno == ENOENT) { - /* it was just deleted under us by - another process */ - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - return -1; - } - if (!EDESTDIREXISTS(errno)) { - mailbox_list_set_critical(list, - "rename(%s, %s) failed: %m", src, dest); - return -1; - } - - /* already existed, delete it and try again */ - if (unlink_directory(dest, TRUE) < 0 && - (errno != ENOTEMPTY || count >= 5)) { - mailbox_list_set_critical(list, - "unlink_directory(%s) failed: %m", dest); - return -1; - } - count++; + ret = maildir_delete_nonrecursive(list, src, name); + } else { + ret = maildir_delete_with_trash(list, src, dest, name); } - if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) { - mailbox_list_set_critical(list, - "unlink_directory(%s) failed: %m", dest); - - /* it's already renamed to ..dir, which means it's - deleted as far as the client is concerned. Report - success. */ + if (ret == 0) { + mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_MAILBOX, + mailbox_guid); + mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR, + dir_guid); } return 0; } diff --git a/src/lib-storage/mailbox-list-private.h b/src/lib-storage/mailbox-list-private.h index d7aae18bb2..887064ceb2 100644 --- a/src/lib-storage/mailbox-list-private.h +++ b/src/lib-storage/mailbox-list-private.h @@ -9,6 +9,9 @@ #define MAILBOX_LIST_NAME_IMAPDIR "imapdir" #define MAILBOX_LIST_NAME_FS "fs" +#define MAILBOX_LOG_FILE_NAME "dovecot.mailbox.log" + +enum mailbox_log_record_type; struct dirent; struct imap_match_glob; struct mailbox_tree_context; @@ -95,6 +98,8 @@ struct mailbox_list { /* origin (e.g. path) where the file_create_gid was got from */ const char *file_create_gid_origin; + struct mailbox_log *changelog; + char *error_string; enum mail_error error; bool temporary_error; @@ -144,6 +149,10 @@ enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d); bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name); +void mailbox_list_add_change(struct mailbox_list *list, + enum mailbox_log_record_type type, + const uint8_t mailbox_guid[MAIL_GUID_128_SIZE]); + void mailbox_list_clear_error(struct mailbox_list *list); void mailbox_list_set_error(struct mailbox_list *list, enum mail_error error, const char *string); diff --git a/src/lib-storage/mailbox-list.c b/src/lib-storage/mailbox-list.c index e625255fe6..ac7bbe31e9 100644 --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@ -5,10 +5,17 @@ #include "ioloop.h" #include "mkdir-parents.h" #include "str.h" +#include "sha1.h" #include "home-expand.h" +#include "close-keep-errno.h" +#include "eacces-error.h" +#include "read-full.h" +#include "write-full.h" +#include "safe-mkstemp.h" #include "unlink-directory.h" #include "imap-match.h" #include "imap-utf7.h" +#include "mailbox-log.h" #include "mailbox-tree.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" @@ -94,6 +101,7 @@ int mailbox_list_create(const char *driver, struct mail_namespace *ns, { const struct mailbox_list *const *class_p; struct mailbox_list *list; + const char *path; unsigned int idx; i_assert(ns->list == NULL); @@ -162,6 +170,12 @@ int mailbox_list_create(const char *driver, struct mail_namespace *ns, mail_namespace_finish_list_init(ns, list); + path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX); + if (path != NULL) { + path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL); + list->changelog = mailbox_log_alloc(path); + } + if (hook_mailbox_list_created != NULL) hook_mailbox_list_created(list); return 0; @@ -281,6 +295,8 @@ void mailbox_list_destroy(struct mailbox_list **_list) *_list = NULL; i_free_and_null(list->error_string); + if (list->changelog != NULL) + mailbox_log_free(&list->changelog); list->v.deinit(list); } @@ -661,12 +677,42 @@ int mailbox_list_mailbox(struct mailbox_list *list, const char *name, flags_r); } +void mailbox_list_add_change(struct mailbox_list *list, + enum mailbox_log_record_type type, + const uint8_t mailbox_guid[MAIL_GUID_128_SIZE]) +{ + struct mailbox_log_record rec; + + if (list->changelog == NULL || mail_guid_128_is_empty(mailbox_guid)) + return; + + memset(&rec, 0, sizeof(rec)); + rec.type = type; + memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid)); + mailbox_log_record_set_timestamp(&rec, ioloop_time); + (void)mailbox_log_append(list->changelog, &rec); +} + int mailbox_list_set_subscribed(struct mailbox_list *list, const char *name, bool set) { + uint8_t guid[MAIL_GUID_128_SIZE]; + unsigned char sha[SHA1_RESULTLEN]; + mailbox_list_clear_error(list); - return list->v.set_subscribed(list, name, set); + if (list->v.set_subscribed(list, name, set) < 0) + return -1; + + /* subscriptions are about names, not about mailboxes. it's possible + to have a subscription to non-existing mailbox. renames also don't + change subscriptions. so instead of using actual GUIDs, we'll use + hash of the name. */ + sha1_get_digest(name, strlen(name), sha); + memcpy(guid, sha, I_MIN(sizeof(guid), sizeof(sha))); + mailbox_list_add_change(list, set ? MAILBOX_LOG_RECORD_SUBSCRIBE : + MAILBOX_LOG_RECORD_UNSUBSCRIBE, guid); + return 0; } int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name) @@ -697,6 +743,7 @@ int mailbox_list_rename_mailbox(struct mailbox_list *oldlist, { struct mail_storage *oldstorage; struct mail_storage *newstorage; + uint8_t guid[MAIL_GUID_128_SIZE]; if (mailbox_list_get_storage(&oldlist, &oldname, &oldstorage) < 0) return -1; @@ -729,8 +776,12 @@ int mailbox_list_rename_mailbox(struct mailbox_list *oldlist, return -1; } - return oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname, - rename_children); + (void)mailbox_list_get_guid(oldlist, oldname, guid); + if (oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname, + rename_children) < 0) + return -1; + mailbox_list_add_change(oldlist, MAILBOX_LOG_RECORD_RENAME, guid); + return 0; } static int mailbox_list_read_guid(struct mailbox_list *list, const char *path, @@ -836,6 +887,11 @@ int mailbox_list_get_guid(struct mailbox_list *list, const char *name, return ret; } +struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list) +{ + return list->changelog; +} + static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir) { if (unlink_directory(dir, TRUE) == 0 || errno == ENOENT) diff --git a/src/lib-storage/mailbox-list.h b/src/lib-storage/mailbox-list.h index 6cf2fff22c..c20a0e8e3f 100644 --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@ -186,6 +186,9 @@ int mailbox_list_get_mailbox_name_status(struct mailbox_list *list, /* Get 128bit mailbox directory GUID, creating it if necessary. */ int mailbox_list_get_guid(struct mailbox_list *list, const char *name, uint8_t mailbox_guid[MAIL_GUID_128_SIZE]); +/* Returns mailbox's change log, or NULL if it doesn't have one. */ +struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list); + /* Returns a prefix that temporary files should use without conflicting with the namespace. */