Mercurial > dovecot > core-2.2
changeset 10676:1fd7833c16ca HEAD
lib-storage: Mailbox deletion API changed.
Mailbox deletion should now be free of race conditions. The actual file
deletion code is now responsibility of mailbox_list backend.
line wrap: on
line diff
--- a/src/dsync/dsync-worker-local.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/dsync/dsync-worker-local.c Tue Feb 09 04:11:53 2010 +0200 @@ -1105,6 +1105,7 @@ (struct local_dsync_worker *)_worker; struct local_dsync_mailbox *lbox; const mailbox_guid_t *mailbox = &dsync_box->mailbox_guid; + struct mailbox *box; lbox = hash_table_lookup(worker->mailbox_hash, mailbox); if (lbox == NULL) { @@ -1116,12 +1117,15 @@ mailbox_list_set_changelog_timestamp(lbox->ns->list, dsync_box->last_change); - if (mailbox_list_delete_mailbox(lbox->ns->list, - lbox->storage_name) < 0) { + box = mailbox_alloc(lbox->ns->list, lbox->storage_name, NULL, 0); + if (mailbox_delete(box) < 0) { + struct mail_storage *storage = mailbox_get_storage(box); + i_error("Can't delete mailbox %s: %s", lbox->storage_name, - mailbox_list_get_last_error(lbox->ns->list, NULL)); + mail_storage_get_last_error(storage, NULL)); dsync_worker_set_failure(_worker); } + mailbox_free(&box); mailbox_list_set_changelog_timestamp(lbox->ns->list, (time_t)-1); }
--- a/src/imap/cmd-delete.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/imap/cmd-delete.c Tue Feb 09 04:11:53 2010 +0200 @@ -7,7 +7,7 @@ { struct client *client = cmd->client; struct mail_namespace *ns; - struct mailbox *mailbox; + struct mailbox *box; const char *name; /* <mailbox> */ @@ -25,19 +25,18 @@ if (ns == NULL) return TRUE; - mailbox = mailbox_alloc(ns->list, name, NULL, 0); + box = mailbox_alloc(ns->list, name, NULL, 0); if (client->mailbox != NULL && - mailbox_backends_equal(mailbox, client->mailbox)) { + mailbox_backends_equal(box, client->mailbox)) { /* deleting selected mailbox. close it first */ client_search_updates_free(client); mailbox_free(&client->mailbox); } - mailbox_free(&mailbox); - if (mailbox_list_delete_mailbox(ns->list, name) < 0) - client_send_list_error(cmd, ns->list); - else { + if (mailbox_delete(box) < 0) + client_send_storage_error(cmd, mailbox_get_storage(box)); + else client_send_tagline(cmd, "OK Delete completed."); - } + mailbox_free(&box); return TRUE; }
--- a/src/lib-storage/index/cydir/cydir-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/cydir/cydir-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -1,31 +1,16 @@ /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "array.h" -#include "str.h" -#include "mkdir-parents.h" +#include "mail-copy.h" #include "index-mail.h" -#include "mail-copy.h" #include "cydir-sync.h" #include "cydir-storage.h" -#include <unistd.h> -#include <dirent.h> #include <sys/stat.h> -#define CYDIR_LIST_CONTEXT(obj) \ - MODULE_CONTEXT(obj, cydir_mailbox_list_module) - -struct cydir_mailbox_list { - union mailbox_list_module_context module_ctx; -}; - extern struct mail_storage cydir_storage; extern struct mailbox cydir_mailbox; -static MODULE_CONTEXT_DEFINE_INIT(cydir_mailbox_list_module, - &mailbox_list_module_register); - static struct mail_storage *cydir_storage_alloc(void) { struct cydir_storage *storage; @@ -128,97 +113,6 @@ index_storage_mailbox_update(box, update); } -static int -cydir_delete_nonrecursive(struct mailbox_list *list, const char *path, - const char *name) -{ - DIR *dir; - struct dirent *d; - string_t *full_path; - unsigned int dir_len; - bool unlinked_something = FALSE; - - dir = opendir(path); - if (dir == NULL) { - if (!mailbox_list_set_error_from_errno(list)) { - mailbox_list_set_critical(list, - "opendir(%s) failed: %m", path); - } - return -1; - } - - full_path = t_str_new(256); - str_append(full_path, path); - str_append_c(full_path, '/'); - dir_len = str_len(full_path); - - errno = 0; - while ((d = readdir(dir)) != NULL) { - if (d->d_name[0] == '.') { - /* skip . and .. */ - if (d->d_name[1] == '\0') - continue; - if (d->d_name[1] == '.' && d->d_name[2] == '\0') - continue; - } - - str_truncate(full_path, dir_len); - str_append(full_path, d->d_name); - - /* trying to unlink() a directory gives either EPERM or EISDIR - (non-POSIX). it doesn't really work anywhere in practise, - so don't bother stat()ing the file first */ - if (unlink(str_c(full_path)) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { - mailbox_list_set_critical(list, "unlink(%s) failed: %m", - str_c(full_path)); - } - } - - if (closedir(dir) < 0) { - mailbox_list_set_critical(list, "closedir(%s) failed: %m", - path); - } - - if (rmdir(path) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != ENOTEMPTY) { - mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); - return -1; - } - - if (!unlinked_something) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - t_strdup_printf("Directory %s isn't empty, " - "can't delete it.", name)); - return -1; - } - return 0; -} - -static int -cydir_list_delete_mailbox(struct mailbox_list *list, const char *name) -{ - struct cydir_mailbox_list *mlist = CYDIR_LIST_CONTEXT(list); - struct stat st; - const char *src; - - /* delete the index and control directories */ - if (mlist->module_ctx.super.delete_mailbox(list, name) < 0) - return -1; - - /* check if the mailbox actually exists */ - src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(src, &st) != 0 && errno == ENOENT) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - return -1; - } - - return cydir_delete_nonrecursive(list, src, name); -} - static void cydir_notify_changes(struct mailbox *box) { struct cydir_mailbox *mbox = (struct cydir_mailbox *)box; @@ -286,15 +180,7 @@ static void cydir_storage_add_list(struct mail_storage *storage ATTR_UNUSED, struct mailbox_list *list) { - struct cydir_mailbox_list *mlist; - - mlist = p_new(list->pool, struct cydir_mailbox_list, 1); - mlist->module_ctx.super = list->v; - list->v.iter_is_mailbox = cydir_list_iter_is_mailbox; - list->v.delete_mailbox = cydir_list_delete_mailbox; - - MODULE_CONTEXT_SET(list, cydir_mailbox_list_module, mlist); } struct mail_storage cydir_storage = { @@ -323,6 +209,7 @@ index_storage_mailbox_close, cydir_mailbox_create, index_storage_mailbox_update, + index_storage_mailbox_delete, index_storage_get_status, NULL, NULL,
--- a/src/lib-storage/index/dbox-common/dbox-mail.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-mail.h Tue Feb 09 04:11:53 2010 +0200 @@ -1,6 +1,8 @@ #ifndef DBOX_MAIL_H #define DBOX_MAIL_H +#include "index-mail.h" + struct dbox_mail { struct index_mail imail;
--- a/src/lib-storage/index/dbox-common/dbox-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -2,10 +2,7 @@ #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" @@ -326,123 +323,3 @@ } 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; -} -
--- a/src/lib-storage/index/dbox-common/dbox-storage.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-storage.h Tue Feb 09 04:11:53 2010 +0200 @@ -62,9 +62,4 @@ struct mailbox_list *newlist, const char *newname, bool rename_children); -int dbox_list_delete_mailbox1(struct mailbox_list *list, const char *name, - const char **trash_dest_r); -int dbox_list_delete_mailbox2(struct mailbox_list *list, const char *name, - int ret, const char *trash_dest); - #endif
--- a/src/lib-storage/index/dbox-multi/mdbox-map.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-map.c Tue Feb 09 04:11:53 2010 +0200 @@ -371,6 +371,11 @@ return 0; } +void dbox_map_transaction_set_failed(struct dbox_map_transaction_context *ctx) +{ + ctx->success = FALSE; +} + void dbox_map_transaction_free(struct dbox_map_transaction_context **_ctx) { struct dbox_map_transaction_context *ctx = *_ctx;
--- a/src/lib-storage/index/dbox-multi/mdbox-map.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-map.h Tue Feb 09 04:11:53 2010 +0200 @@ -53,6 +53,7 @@ /* Write transaction to map and leave it locked. Call _free() to update tail offset and unlock. */ int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx); +void dbox_map_transaction_set_failed(struct dbox_map_transaction_context *ctx); void dbox_map_transaction_free(struct dbox_map_transaction_context **ctx); int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -2,17 +2,10 @@ #include "lib.h" #include "array.h" -#include "ioloop.h" -#include "str.h" -#include "hex-binary.h" -#include "randgen.h" #include "mkdir-parents.h" -#include "unlink-directory.h" -#include "unlink-old-files.h" -#include "index-mail.h" -#include "mail-copy.h" #include "mail-index-modseq.h" -#include "mailbox-uidvalidity.h" +#include "mail-index-alloc-cache.h" +#include "mailbox-log.h" #include "dbox-mail.h" #include "dbox-save.h" #include "mdbox-map.h" @@ -21,12 +14,6 @@ #include "mdbox-storage-rebuild.h" #include "mdbox-storage.h" -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <dirent.h> -#include <sys/stat.h> - #define MDBOX_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mdbox_mailbox_list_module) @@ -234,8 +221,7 @@ } if (mail_index_transaction_commit(&trans) < 0) { - mail_storage_set_internal_error(box->storage); - mail_index_reset_error(box->index); + mail_storage_set_index_error(box); return -1; } return 0; @@ -292,44 +278,22 @@ return mdbox_write_index_header(box, update); } -static int -mdbox_mailbox_unref_mails(struct mailbox_list *list, const char *path) +static int mdbox_mailbox_unref_mails(struct mdbox_mailbox *mbox) { - struct mdbox_storage *storage = - (struct mdbox_storage *)list->ns->storage; - const struct mail_storage_settings *old_set; - struct mail_storage_settings tmp_set; - struct mailbox *box; - struct mdbox_mailbox *mbox; + struct dbox_map_transaction_context *map_trans; const struct mail_index_header *hdr; const struct mdbox_mail_index_record *dbox_rec; - struct dbox_map_transaction_context *map_trans; ARRAY_TYPE(uint32_t) map_uids; const void *data; bool expunged; uint32_t seq; int ret; - old_set = list->mail_set; - tmp_set = *list->mail_set; - tmp_set.mail_full_filesystem_access = TRUE; - list->mail_set = &tmp_set; - box = mdbox_mailbox_alloc(&storage->storage.storage, list, path, NULL, - MAILBOX_FLAG_IGNORE_ACLS | - MAILBOX_FLAG_KEEP_RECENT); - ret = mailbox_open(box); - list->mail_set = old_set; - if (ret < 0) { - mailbox_free(&box); - return -1; - } - mbox = (struct mdbox_mailbox *)box; - /* get a list of all map_uids in this mailbox */ i_array_init(&map_uids, 128); - hdr = mail_index_get_header(box->view); + hdr = mail_index_get_header(mbox->box.view); for (seq = 1; seq <= hdr->messages_count; seq++) { - mail_index_lookup_ext(box->view, seq, mbox->ext_id, + mail_index_lookup_ext(mbox->box.view, seq, mbox->ext_id, &data, &expunged); dbox_rec = data; if (dbox_rec == NULL) { @@ -341,37 +305,24 @@ } /* unreference the map_uids */ - map_trans = dbox_map_transaction_begin(storage->map, FALSE); + map_trans = dbox_map_transaction_begin(mbox->storage->map, FALSE); ret = dbox_map_update_refcounts(map_trans, &map_uids, -1); if (ret == 0) ret = dbox_map_transaction_commit(map_trans); dbox_map_transaction_free(&map_trans); array_free(&map_uids); - mailbox_free(&box); return ret; } -static int -mdbox_list_delete_mailbox(struct mailbox_list *list, const char *name) +static int mdbox_mailbox_delete(struct mailbox *box) { - struct mdbox_mailbox_list *mlist = MDBOX_LIST_CONTEXT(list); - const char *trash_dest; - int ret; - - /* delete the index and control directories */ - if (mlist->module_ctx.super.delete_mailbox(list, name) < 0) - return -1; + struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box; - if ((ret = dbox_list_delete_mailbox1(list, name, &trash_dest)) < 0) - return -1; - if (ret > 0) { - if (mdbox_mailbox_unref_mails(list, trash_dest) < 0) { - /* we've already renamed it. there's no going back. */ - mailbox_list_set_internal_error(list); - ret = -1; - } + if (box->opened) { + if (mdbox_mailbox_unref_mails(mbox) < 0) + return -1; } - return dbox_list_delete_mailbox2(list, name, ret, trash_dest); + return index_storage_mailbox_delete(box); } static int @@ -398,7 +349,6 @@ mlist->module_ctx.super = list->v; list->v.iter_is_mailbox = dbox_list_iter_is_mailbox; - list->v.delete_mailbox = mdbox_list_delete_mailbox; list->v.rename_mailbox = mdbox_list_rename_mailbox; list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre; @@ -431,6 +381,7 @@ index_storage_mailbox_close, dbox_mailbox_create, mdbox_mailbox_update, + mdbox_mailbox_delete, mdbox_storage_get_status, NULL, NULL,
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/dbox-single/sdbox-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -1,26 +1,13 @@ /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "array.h" -#include "ioloop.h" -#include "hex-binary.h" -#include "randgen.h" -#include "mkdir-parents.h" -#include "unlink-directory.h" -#include "unlink-old-files.h" -#include "index-mail.h" #include "mail-index-modseq.h" -#include "mailbox-uidvalidity.h" #include "dbox-mail.h" #include "dbox-save.h" #include "sdbox-file.h" #include "sdbox-sync.h" #include "sdbox-storage.h" -#include <stdio.h> -#include <unistd.h> -#include <sys/stat.h> - #define SDBOX_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, sdbox_mailbox_list_module) @@ -232,22 +219,6 @@ } static int -sdbox_list_delete_mailbox(struct mailbox_list *list, const char *name) -{ - struct sdbox_mailbox_list *mlist = SDBOX_LIST_CONTEXT(list); - const char *trash_dest; - int ret; - - /* delete the index and control directories */ - if (mlist->module_ctx.super.delete_mailbox(list, name) < 0) - return -1; - - if ((ret = dbox_list_delete_mailbox1(list, name, &trash_dest)) < 0) - return -1; - return dbox_list_delete_mailbox2(list, name, ret, trash_dest); -} - -static int sdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) @@ -271,7 +242,6 @@ mlist->module_ctx.super = list->v; list->v.iter_is_mailbox = dbox_list_iter_is_mailbox; - list->v.delete_mailbox = sdbox_list_delete_mailbox; list->v.rename_mailbox = sdbox_list_rename_mailbox; list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre; @@ -304,6 +274,7 @@ index_storage_mailbox_close, dbox_mailbox_create, dbox_mailbox_update, + index_storage_mailbox_delete, dbox_storage_get_status, NULL, NULL,
--- a/src/lib-storage/index/index-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/index-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -10,6 +10,7 @@ #include "mail-index-alloc-cache.h" #include "mail-index-private.h" #include "mail-index-modseq.h" +#include "mailbox-log.h" #include "mailbox-list-private.h" #include "index-storage.h" #include "index-mail.h" @@ -245,9 +246,11 @@ if (hook_mailbox_opened != NULL) hook_mailbox_opened(box); - if (mail_index_is_deleted(box->index)) { - mailbox_set_deleted(box); - return -1; + if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) { + if (mail_index_is_deleted(box->index)) { + mailbox_set_deleted(box); + return -1; + } } return 0; } @@ -305,10 +308,8 @@ { if ((feature & MAILBOX_FEATURE_CONDSTORE) != 0) { box->enabled_features |= MAILBOX_FEATURE_CONDSTORE; - if (!box->opened) { - if (mailbox_open(box) < 0) - return -1; - } + if (mailbox_open(box) < 0) + return -1; T_BEGIN { mail_index_modseq_enable(box->index); } T_END; @@ -393,10 +394,8 @@ struct mail_index_transaction *trans; int ret; - if (!box->opened) { - if (mailbox_open(box) < 0) - return -1; - } + if (mailbox_open(box) < 0) + return -1; if (update->cache_fields != NULL) index_storage_mailbox_update_cache_fields(box, update); @@ -435,6 +434,56 @@ return ret; } +int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted) +{ + uint8_t dir_sha128[MAIL_GUID_128_SIZE]; + enum mail_error error; + + if (mailbox_list_delete_dir(box->list, box->name) == 0) + return 0; + + (void)mailbox_list_get_last_error(box->list, &error); + if (error != MAIL_ERROR_NOTFOUND || !mailbox_deleted) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + /* failed directory deletion, but mailbox deletion succeeded. + this was probably maildir++, which internally deleted the + directory as well. add changelog record about that too. */ + mailbox_name_get_sha128(box->name, dir_sha128); + mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_DIR, + dir_sha128); + return 0; +} + +int index_storage_mailbox_delete(struct mailbox *box) +{ + struct mailbox_status status; + + if (!box->opened) { + /* \noselect mailbox, try deleting only the directory */ + return index_storage_mailbox_delete_dir(box, FALSE); + } + + mailbox_get_status(box, STATUS_GUID, &status); + + /* Make sure the indexes are closed before trying to delete the + directory that contains them. It can still fail with some NFS + implementations if indexes are opened by another session, but + that can't really be helped. */ + mailbox_close(box); + mail_index_alloc_cache_destroy_unrefed(); + + if (box->list->v.delete_mailbox(box->list, box->name) < 0) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + + mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_MAILBOX, + status.mailbox_guid); + return index_storage_mailbox_delete_dir(box, TRUE); +} + bool index_storage_is_readonly(struct mailbox *box) { return (box->flags & MAILBOX_FLAG_READONLY) != 0 ||
--- a/src/lib-storage/index/index-storage.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/index-storage.h Tue Feb 09 04:11:53 2010 +0200 @@ -76,6 +76,8 @@ void index_storage_mailbox_close(struct mailbox *box); int index_storage_mailbox_update(struct mailbox *box, const struct mailbox_update *update); +int index_storage_mailbox_delete(struct mailbox *box); +int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted); bool index_storage_is_readonly(struct mailbox *box); bool index_storage_allow_new_keywords(struct mailbox *box);
--- a/src/lib-storage/index/index-transaction.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/index-transaction.c Tue Feb 09 04:11:53 2010 +0200 @@ -121,8 +121,10 @@ _t->changes = changes_r; ret = mail_index_transaction_commit_full(&itrans, &result); - if (ret < 0 && mail_index_is_deleted(_t->box->index)) - mailbox_set_deleted(_t->box); + _t = NULL; + + if (ret < 0 && mail_index_is_deleted(box->index)) + mailbox_set_deleted(box); changes_r->ignored_uid_changes = result.ignored_uid_changes; changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
--- a/src/lib-storage/index/maildir/maildir-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/maildir/maildir-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -2,31 +2,24 @@ #include "lib.h" #include "ioloop.h" -#include "array.h" -#include "hostpid.h" -#include "str.h" #include "mkdir-parents.h" #include "eacces-error.h" #include "unlink-directory.h" #include "unlink-old-files.h" -#include "mailbox-log.h" #include "mailbox-uidvalidity.h" +#include "list/mailbox-list-maildir.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" #include "maildir-sync.h" #include "index-mail.h" -#include <stdio.h> -#include <stdlib.h> -#include <dirent.h> -#include <unistd.h> #include <sys/stat.h> #define MAILDIR_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, maildir_mailbox_list_module) -struct maildir_mailbox_list { +struct maildir_mailbox_list_context { union mailbox_list_module_context module_ctx; const struct maildir_settings *set; }; @@ -54,7 +47,7 @@ static bool maildir_storage_is_valid_existing_name(struct mailbox_list *list, const char *name) { - struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(list); + struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list); const char *p; if (!mlist->module_ctx.super.is_valid_existing_name(list, name)) @@ -70,7 +63,7 @@ static bool maildir_storage_is_valid_create_name(struct mailbox_list *list, const char *name) { - struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(list); + struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list); bool ret = TRUE; if (!mlist->module_ctx.super.is_valid_create_name(list, name)) @@ -526,247 +519,13 @@ } } -static const char * -maildir_get_unlink_dest(struct mailbox_list *list, const char *name) -{ - const char *root_dir; - char sep; - - if (list->mail_set->mail_full_filesystem_access && - (*name == '/' || *name == '~')) - return NULL; - - if (strcmp(mailbox_list_get_driver_name(list), - MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0) { - /* Not maildir++ driver. Don't use this trick. */ - return NULL; - } - - root_dir = mailbox_list_get_path(list, NULL, - MAILBOX_LIST_PATH_TYPE_DIR); - sep = mailbox_list_get_hierarchy_sep(list); - return t_strdup_printf("%s/%c%c"MAILDIR_UNLINK_DIRNAME, root_dir, - sep, sep); -} - -static int -maildir_delete_nonrecursive(struct mailbox_list *list, const char *path, - const char *name) -{ - DIR *dir; - struct dirent *d; - string_t *full_path; - unsigned int dir_len; - bool unlinked_something = FALSE; - - dir = opendir(path); - if (dir == NULL) { - if (errno == ENOENT) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - } else { - mailbox_list_set_critical(list, - "opendir(%s) failed: %m", path); - } - return -1; - } - - full_path = t_str_new(256); - str_append(full_path, path); - str_append_c(full_path, '/'); - dir_len = str_len(full_path); - - errno = 0; - while ((d = readdir(dir)) != NULL) { - if (d->d_name[0] == '.') { - /* skip . and .. */ - if (d->d_name[1] == '\0') - continue; - if (d->d_name[1] == '.' && d->d_name[2] == '\0') - continue; - } - - str_truncate(full_path, dir_len); - str_append(full_path, d->d_name); - - if (maildir_is_internal_name(d->d_name)) { - if (unlink_directory(str_c(full_path), TRUE) < 0) { - mailbox_list_set_critical(list, - "unlink_directory(%s) failed: %m", - str_c(full_path)); - } else { - unlinked_something = TRUE; - } - continue; - } - - /* trying to unlink() a directory gives either EPERM or EISDIR - (non-POSIX). it doesn't really work anywhere in practise, - so don't bother stat()ing the file first */ - if (unlink(str_c(full_path)) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { - mailbox_list_set_critical(list, - "unlink_directory(%s) failed: %m", - str_c(full_path)); - } - } - - if (closedir(dir) < 0) { - mailbox_list_set_critical(list, "closedir(%s) failed: %m", - path); - } - - if (rmdir(path) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != ENOTEMPTY) { - mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); - return -1; - } - - if (!unlinked_something) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - t_strdup_printf("Directory %s isn't empty, " - "can't delete it.", name)); - return -1; - } - 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_free(&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_sha128[MAIL_GUID_128_SIZE]; - struct stat st; - const char *src, *dest, *base; - int ret; - - mailbox_get_guid(list, name, mailbox_guid); - - /* delete the index and control directories */ - if (mlist->super.delete_mailbox(list, name) < 0) - return -1; - - /* check if the mailbox actually exists */ - src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (lstat(src, &st) != 0 && errno == ENOENT) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - return -1; - } - - if (!S_ISDIR(st.st_mode)) { - /* a symlink most likely */ - if (unlink(src) < 0 && errno != ENOENT) { - mailbox_list_set_critical(list, - "unlink(%s) failed: %m", src); - return -1; - } - return 0; - } - - if (strcmp(name, "INBOX") == 0) { - /* we shouldn't get this far if this is the actual INBOX. - more likely we're just deleting a namespace/INBOX. - be anyway sure that we don't accidentally delete the entire - maildir (INBOX explicitly configured to maildir root). */ - base = mailbox_list_get_path(list, NULL, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (strcmp(base, src) == 0) { - mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, - "INBOX can't be deleted."); - return -1; - } - } - - dest = maildir_get_unlink_dest(list, name); - if (dest == NULL) { - /* delete the directory directly without any renaming */ - ret = maildir_delete_nonrecursive(list, src, name); - } else { - ret = maildir_delete_with_trash(list, src, dest, name); - } - - if (ret == 0) { - mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_MAILBOX, - mailbox_guid); - mailbox_name_get_sha128(name, dir_sha128); - mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR, - dir_sha128); - } - return 0; -} - static int maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) { - struct maildir_mailbox_list *oldmlist = MAILDIR_LIST_CONTEXT(oldlist); + struct maildir_mailbox_list_context *oldmlist = + MAILDIR_LIST_CONTEXT(oldlist); const char *path1, *path2; if (strcmp(oldname, "INBOX") == 0) { @@ -819,6 +578,13 @@ } } +static bool +maildir_is_mailbox_dir(struct mailbox_list *list ATTR_UNUSED, + const char *dir ATTR_UNUSED, const char *name) +{ + return maildir_is_internal_name(name); +} + static int maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED, @@ -917,11 +683,12 @@ enum mailbox_list_file_type type, enum mailbox_info_flags *flags) { - struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(ctx->list); + struct maildir_mailbox_list_context *mlist = + MAILDIR_LIST_CONTEXT(ctx->list); int ret; if (fname[1] == mailbox_list_get_hierarchy_sep(ctx->list) && - strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0) { + strcmp(fname+2, MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME) == 0) { const char *path; struct stat st; @@ -1002,12 +769,13 @@ static void maildir_storage_add_list(struct mail_storage *storage, struct mailbox_list *list) { - struct maildir_mailbox_list *mlist; + struct maildir_mailbox_list_context *mlist; - mlist = p_new(list->pool, struct maildir_mailbox_list, 1); + mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1); mlist->module_ctx.super = list->v; mlist->set = mail_storage_get_driver_settings(storage); + list->v.is_mailbox_dir = maildir_is_mailbox_dir; if (strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0) { list->v.iter_is_mailbox = maildirplusplus_iter_is_mailbox; } else { @@ -1017,7 +785,6 @@ maildir_storage_is_valid_create_name; list->v.iter_is_mailbox = maildir_list_iter_is_mailbox; } - list->v.delete_mailbox = maildir_list_delete_mailbox; list->v.rename_mailbox = maildir_list_rename_mailbox; MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist); } @@ -1048,6 +815,7 @@ maildir_mailbox_close, maildir_mailbox_create, maildir_mailbox_update, + index_storage_mailbox_delete, maildir_storage_get_status, maildir_list_index_has_changed, maildir_list_index_update_sync,
--- a/src/lib-storage/index/maildir/maildir-storage.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/maildir/maildir-storage.h Tue Feb 09 04:11:53 2010 +0200 @@ -6,7 +6,6 @@ #define MAILDIR_STORAGE_NAME "maildir" #define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions" #define MAILDIR_INDEX_PREFIX "dovecot.index" -#define MAILDIR_UNLINK_DIRNAME "DOVECOT-TRASHED" #define MAILDIR_UIDVALIDITY_FNAME "dovecot-uidvalidity" /* "base,S=123:2," means:
--- a/src/lib-storage/index/mbox/mbox-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/mbox/mbox-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -2,11 +2,8 @@ #include "lib.h" #include "ioloop.h" -#include "array.h" #include "istream.h" #include "restrict-access.h" -#include "mkdir-parents.h" -#include "unlink-directory.h" #include "mbox-storage.h" #include "mbox-lock.h" #include "mbox-file.h" @@ -15,10 +12,6 @@ #include "mail-copy.h" #include "index-mail.h" -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> #include <sys/stat.h> /* How often to touch the dotlock file when using KEEP_LOCKED flag */ @@ -478,9 +471,8 @@ if ((ret = stat(box->path, &st)) == 0 && !S_ISDIR(st.st_mode)) return mbox_mailbox_open_existing(mbox); else if (ret == 0) { - mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, - t_strdup_printf("Mailbox isn't selectable: %s", - box->name)); + mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, + "Mailbox isn't selectable"); return -1; } else if (ENOTFOUND(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, @@ -703,79 +695,6 @@ } } -static int mbox_list_delete_mailbox(struct mailbox_list *list, - const char *name) -{ - struct mbox_mailbox_list *mlist = MBOX_LIST_CONTEXT(list); - struct stat st; - const char *path, *index_dir; - - path = mailbox_list_get_path(list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (lstat(path, &st) < 0) { - if (ENOTFOUND(errno)) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - } else if (!mailbox_list_set_error_from_errno(list)) { - mailbox_list_set_critical(list, - "lstat() failed for %s: %m", path); - } - return -1; - } - - if (S_ISDIR(st.st_mode)) { - /* deleting a directory. allow it only if it doesn't contain - anything. Delete the ".imap" directory first in case there - have been indexes. */ - index_dir = mailbox_list_get_path(list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - index_dir = *index_dir == '\0' ? "" : - t_strconcat(index_dir, "/"MBOX_INDEX_DIR_NAME, NULL); - - if (*index_dir != '\0' && rmdir(index_dir) < 0 && - !ENOTFOUND(errno) && errno != ENOTEMPTY) { - if (!mailbox_list_set_error_from_errno(list)) { - mailbox_list_set_critical(list, - "rmdir() failed for %s: %m", index_dir); - } - return -1; - } - - if (rmdir(path) == 0) - return 0; - - if (ENOTFOUND(errno)) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - } else if (errno == ENOTEMPTY) { - 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; - } - - /* delete index / control files first */ - if (mlist->module_ctx.super.delete_mailbox(list, name) < 0) - return -1; - - if (unlink(path) < 0) { - if (ENOTFOUND(errno)) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - } else if (!mailbox_list_set_error_from_errno(list)) { - mailbox_list_set_critical(list, - "unlink() failed for %s: %m", path); - } - return -1; - } - - return 0; -} - static void mbox_storage_add_list(struct mail_storage *storage, struct mailbox_list *list) { @@ -791,7 +710,6 @@ list->v.get_path = mbox_list_get_path; } list->v.iter_is_mailbox = mbox_list_iter_is_mailbox; - list->v.delete_mailbox = mbox_list_delete_mailbox; list->v.is_valid_existing_name = mbox_is_valid_existing_name; list->v.is_valid_create_name = mbox_is_valid_create_name; @@ -877,6 +795,7 @@ mbox_mailbox_close, mbox_mailbox_create, mbox_mailbox_update, + index_storage_mailbox_delete, mbox_storage_get_status, NULL, NULL,
--- a/src/lib-storage/index/raw/raw-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/raw/raw-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -1,7 +1,6 @@ /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" -#include "array.h" #include "ioloop.h" #include "istream.h" #include "index-mail.h" @@ -109,14 +108,6 @@ return -1; } -static int raw_list_delete_mailbox(struct mailbox_list *list, - const char *name ATTR_UNUSED) -{ - mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, - "Raw mailbox deletion isn't supported"); - return -1; -} - static void raw_notify_changes(struct mailbox *box ATTR_UNUSED) { } @@ -167,7 +158,6 @@ struct mailbox_list *list) { list->v.iter_is_mailbox = raw_list_iter_is_mailbox; - list->v.delete_mailbox = raw_list_delete_mailbox; } struct mail_storage raw_storage = { @@ -196,6 +186,7 @@ index_storage_mailbox_close, raw_mailbox_create, raw_mailbox_update, + index_storage_mailbox_delete, index_storage_get_status, NULL, NULL,
--- a/src/lib-storage/index/shared/shared-list.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/index/shared/shared-list.c Tue Feb 09 04:11:53 2010 +0200 @@ -255,7 +255,7 @@ if (shared_storage_get_namespace(&ns, &name) < 0) return -1; - ret = mailbox_list_delete_mailbox(ns->list, name); + ret = ns->list->v.delete_mailbox(ns->list, name); if (ret < 0) shared_list_copy_error(list, ns); return ret; @@ -353,6 +353,7 @@ shared_list_iter_next, shared_list_iter_deinit, NULL, + NULL, shared_list_set_subscribed, shared_list_create_mailbox_dir, shared_list_delete_mailbox,
--- a/src/lib-storage/list/Makefile.am Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/list/Makefile.am Tue Feb 09 04:11:53 2010 +0200 @@ -11,6 +11,7 @@ libstorage_list_la_SOURCES = \ index-mailbox-list.c \ index-mailbox-list-sync.c \ + mailbox-list-delete.c \ mailbox-list-fs.c \ mailbox-list-fs-iter.c \ mailbox-list-maildir.c \ @@ -20,6 +21,7 @@ headers = \ index-mailbox-list.h \ + mailbox-list-delete.h \ mailbox-list-fs.h \ mailbox-list-maildir.h \ mailbox-list-subscriptions.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-delete.c Tue Feb 09 04:11:53 2010 +0200 @@ -0,0 +1,284 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "unlink-directory.h" +#include "mailbox-list-private.h" +#include "mailbox-list-delete.h" + +#include <stdio.h> +#include <dirent.h> +#include <unistd.h> + +static int +mailbox_list_check_root_delete(struct mailbox_list *list, const char *name, + const char *path) +{ + const char *root_dir; + + root_dir = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + if (strcmp(root_dir, path) != 0) + return 0; + + if (strcmp(name, "INBOX") == 0 && + (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) { + mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, + "INBOX can't be deleted."); + return -1; + } + mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, + "Mail storage root can't be deleted."); + return -1; +} + +int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list, + const char *name, + const char *trash_dir) +{ + const char *src; + unsigned int count; + + src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (mailbox_list_check_root_delete(list, name, src) < 0) + return -1; + + /* rename the mailbox dir to trash dir, which atomically + marks it as being deleted. */ + count = 0; + while (rename(src, trash_dir) < 0) { + if (ENOTFOUND(errno)) { + mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + return -1; + } + if (errno == EXDEV) { + /* can't do this the fast way */ + return 0; + } + if (!EDESTDIREXISTS(errno)) { + if (mailbox_list_set_error_from_errno(list)) + return -1; + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", src, trash_dir); + return -1; + } + + /* already existed, delete it and try again */ + if (unlink_directory(trash_dir, TRUE) < 0 && + (errno != ENOTEMPTY || count >= 5)) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", trash_dir); + return -1; + } + count++; + } + + if (unlink_directory(trash_dir, TRUE) < 0 && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", trash_dir); + + /* it's already renamed to trash dir, which means it's + deleted as far as the client is concerned. Report + success. */ + } + return 1; +} + +int mailbox_list_delete_mailbox_file(struct mailbox_list *list, + const char *name) +{ + const char *path; + + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + + /* we can simply unlink() the file */ + if (unlink(path) == 0) + return 0; + else if (ENOTFOUND(errno)) { + 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, + "unlink(%s) failed: %m", path); + } + return -1; + } +} + +int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, + const char *name, const char *path, + bool rmdir_path) +{ + DIR *dir; + struct dirent *d; + string_t *full_path; + unsigned int dir_len; + bool mailbox_dir, unlinked_something = FALSE; + + if (mailbox_list_check_root_delete(list, name, path) < 0) + return -1; + + dir = opendir(path); + if (dir == NULL) { + if (errno == ENOENT) { + mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + } else { + if (!mailbox_list_set_error_from_errno(list)) { + mailbox_list_set_critical(list, + "opendir(%s) failed: %m", path); + } + } + return -1; + } + + full_path = t_str_new(256); + str_append(full_path, path); + str_append_c(full_path, '/'); + dir_len = str_len(full_path); + + for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) { + if (d->d_name[0] == '.') { + /* skip . and .. */ + if (d->d_name[1] == '\0') + continue; + if (d->d_name[1] == '.' && d->d_name[2] == '\0') + continue; + } + + str_truncate(full_path, dir_len); + mailbox_dir = list->v.is_mailbox_dir != NULL && + list->v.is_mailbox_dir(list, str_c(full_path), + d->d_name); + str_append(full_path, d->d_name); + + if (mailbox_dir) { + if (unlink_directory(str_c(full_path), TRUE) < 0) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", + str_c(full_path)); + } else { + unlinked_something = TRUE; + } + continue; + } + + /* trying to unlink() a directory gives either EPERM or EISDIR + (non-POSIX). it doesn't really work anywhere in practise, + so don't bother stat()ing the file first */ + if (unlink(str_c(full_path)) == 0) + unlinked_something = TRUE; + else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", + str_c(full_path)); + } + } + if (errno != 0) + mailbox_list_set_critical(list, "readdir(%s) failed: %m", path); + if (closedir(dir) < 0) { + mailbox_list_set_critical(list, "closedir(%s) failed: %m", + path); + } + + if (rmdir_path) { + if (rmdir(path) == 0) + unlinked_something = TRUE; + else if (errno != ENOENT && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, "rmdir(%s) failed: %m", + path); + return -1; + } + } + + if (!unlinked_something) { + mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, + "Mailbox has children, can't delete it"); + return -1; + } + return 0; +} + +static void +mailbox_list_delete_until_root(struct mailbox_list *list, const char *path, + enum mailbox_list_path_type type) +{ + const char *root_dir, *p; + unsigned int len; + + root_dir = mailbox_list_get_path(list, NULL, type); + if (strncmp(path, root_dir, strlen(root_dir)) != 0) { + /* mbox workaround: name=child/box, root_dir=mail/.imap/, + path=mail/child/.imap/box. we'll want to try to delete + the .imap/ part, but no further. */ + len = strlen(path); + while (len > 0 && path[len-1] != '/') + len--; + if (len == 0) + return; + len--; + while (len > 0 && path[len-1] != '/') + len--; + if (len == 0) + return; + + root_dir = t_strndup(path, len-1); + } + while (strcmp(path, root_dir) != 0) { + if (rmdir(path) < 0 && errno != ENOENT) { + if (errno == ENOTEMPTY) + return; + + mailbox_list_set_critical(list, "rmdir(%s) failed: %m", + path); + return; + } + p = strrchr(path, '/'); + if (p == NULL) + break; + + path = t_strdup_until(path, p); + } +} + +static void mailbox_list_try_delete(struct mailbox_list *list, const char *name, + enum mailbox_list_path_type type) +{ + const char *mailbox_path, *path; + + mailbox_path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + path = mailbox_list_get_path(list, name, type); + if (path == NULL || *path == '\0' || strcmp(path, mailbox_path) == 0) + return; + + if (*list->set.maildir_name == '\0' && + (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { + /* this directory may contain also child mailboxes' data. + we don't want to delete that. */ + bool rmdir_path = *list->set.maildir_name != '\0'; + if (mailbox_list_delete_mailbox_nonrecursive(list, name, path, + rmdir_path) < 0) + return; + } else { + if (unlink_directory(path, TRUE) < 0 && + errno != ENOENT && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", path); + } + } + + /* avoid leaving empty directories lying around */ + mailbox_list_delete_until_root(list, path, type); +} + +void mailbox_list_delete_finish(struct mailbox_list *list, const char *name) +{ + mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX); + mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL); + mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-delete.h Tue Feb 09 04:11:53 2010 +0200 @@ -0,0 +1,14 @@ +#ifndef MAILBOX_LIST_DELETE_H +#define MAILBOX_LIST_DELETE_H + +int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list, + const char *name, + const char *trash_dir); +int mailbox_list_delete_mailbox_file(struct mailbox_list *list, + const char *name); +int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, + const char *name, const char *path, + bool rmdir_path); +void mailbox_list_delete_finish(struct mailbox_list *list, const char *name); + +#endif
--- a/src/lib-storage/list/mailbox-list-fs.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-fs.c Tue Feb 09 04:11:53 2010 +0200 @@ -6,6 +6,7 @@ #include "mailbox-log.h" #include "subscription-file.h" #include "mail-storage.h" +#include "mailbox-list-delete.h" #include "mailbox-list-fs.h" #include <stdio.h> @@ -361,10 +362,54 @@ return -1; } +static const char *mailbox_list_fs_get_trash_dir(struct mailbox_list *list) +{ + const char *root_dir; + + root_dir = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + return t_strdup_printf("%s/"MAILBOX_LIST_FS_TRASH_DIR_NAME, root_dir); +} + static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name) { - /* let the backend handle the rest */ - return mailbox_list_delete_index_control(list, name); + const char *path, *trash_dir; + int ret = 0; + + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + if (mailbox_list_delete_mailbox_file(list, name) < 0) + return -1; + ret = 1; + } + + if (*list->set.maildir_name != '\0' && + *list->set.mailbox_dir_name != '\0' && ret == 0) { + trash_dir = mailbox_list_fs_get_trash_dir(list); + ret = mailbox_list_delete_maildir_via_trash(list, name, + trash_dir); + if (ret < 0) + return -1; + + /* try to delete the parent directory */ + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_DIR); + if (rmdir(path) < 0 && errno != ENOENT && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, "rmdir(%s) failed: %m", + path); + } + } + + if (ret == 0) { + bool rmdir_path = *list->set.maildir_name != '\0'; + + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (mailbox_list_delete_mailbox_nonrecursive(list, name, path, + rmdir_path) < 0) + return -1; + } + mailbox_list_delete_finish(list, name); + return 0; } static int fs_list_delete_dir(struct mailbox_list *list, const char *name) @@ -385,7 +430,7 @@ T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); } else if (errno == ENOTEMPTY) { mailbox_list_set_error(list, MAIL_ERROR_EXISTS, - "Mailbox exists"); + "Mailbox has children, delete them first"); } else { mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); } @@ -546,6 +591,7 @@ fs_list_iter_next, fs_list_iter_deinit, NULL, + NULL, fs_list_set_subscribed, fs_list_create_mailbox_dir, fs_list_delete_mailbox,
--- a/src/lib-storage/list/mailbox-list-fs.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-fs.h Tue Feb 09 04:11:53 2010 +0200 @@ -7,6 +7,10 @@ problems when they reach the limit. */ #define FS_MAX_CREATE_MAILBOX_NAME_LENGTH (MAILBOX_LIST_NAME_MAX_LENGTH/2) +/* When doing deletion via renaming it first to trash directory, use this as + the trash directory name */ +#define MAILBOX_LIST_FS_TRASH_DIR_NAME "..DOVECOT-TrasH" + struct fs_mailbox_list { struct mailbox_list list;
--- a/src/lib-storage/list/mailbox-list-maildir.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir.c Tue Feb 09 04:11:53 2010 +0200 @@ -6,6 +6,7 @@ #include "eacces-error.h" #include "mkdir-parents.h" #include "subscription-file.h" +#include "mailbox-list-delete.h" #include "mailbox-list-maildir.h" #include <stdio.h> @@ -484,11 +485,46 @@ maildir_list_create_maildirfolder_file(list, path); } +static const char *mailbox_list_maildir_get_trash_dir(struct mailbox_list *list) +{ + const char *root_dir; + + root_dir = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + return t_strdup_printf("%s/%c%c"MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME, + root_dir, list->hierarchy_sep, + list->hierarchy_sep); +} + static int maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) { - /* let the backend handle the rest */ - return mailbox_list_delete_index_control(list, name); + const char *path, *trash_dir; + int ret = 0; + + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + if (mailbox_list_delete_mailbox_file(list, name) < 0) + return -1; + ret = 1; + } + + trash_dir = mailbox_list_maildir_get_trash_dir(list); + ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir); + if (ret < 0) + return -1; + + if (ret == 0) { + /* we could actually use just unlink_directory() + but error handling is easier this way :) */ + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (mailbox_list_delete_mailbox_nonrecursive(list, name, + path, TRUE) < 0) + return -1; + } + + mailbox_list_delete_finish(list, name); + return 0; } static int maildir_list_delete_dir(struct mailbox_list *list, const char *name) @@ -584,6 +620,7 @@ maildir_list_iter_next, maildir_list_iter_deinit, NULL, + NULL, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, maildir_list_delete_mailbox, @@ -615,6 +652,7 @@ maildir_list_iter_next, maildir_list_iter_deinit, NULL, + NULL, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, maildir_list_delete_mailbox,
--- a/src/lib-storage/list/mailbox-list-maildir.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir.h Tue Feb 09 04:11:53 2010 +0200 @@ -7,6 +7,10 @@ problems when they reach the limit. */ #define MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH (MAILBOX_LIST_NAME_MAX_LENGTH/2) +/* When doing deletion via renaming it first to trash directory, use this as + the trash directory name */ +#define MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME "DOVECOT-TRASHED" + struct maildir_mailbox_list { struct mailbox_list list;
--- a/src/lib-storage/mail-copy.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mail-copy.h Tue Feb 09 04:11:53 2010 +0200 @@ -1,6 +1,9 @@ #ifndef MAIL_COPY_H #define MAIL_COPY_H +struct mail; +struct mail_save_context; + int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail); #endif
--- a/src/lib-storage/mail-storage-private.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mail-storage-private.h Tue Feb 09 04:11:53 2010 +0200 @@ -96,6 +96,7 @@ int (*create)(struct mailbox *box, const struct mailbox_update *update, bool directory); int (*update)(struct mailbox *box, const struct mailbox_update *update); + int (*delete)(struct mailbox *box); void (*get_status)(struct mailbox *box, enum mailbox_status_items items, struct mailbox_status *status_r); @@ -436,6 +437,8 @@ void mail_storage_set_internal_error(struct mail_storage *storage); void mail_storage_set_index_error(struct mailbox *box); bool mail_storage_set_error_from_errno(struct mail_storage *storage); +void mail_storage_copy_list_error(struct mail_storage *storage, + struct mailbox_list *list); int mail_set_aborted(struct mail *mail); void mail_set_expunged(struct mail *mail);
--- a/src/lib-storage/mail-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mail-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -381,6 +381,16 @@ } } +void mail_storage_copy_list_error(struct mail_storage *storage, + struct mailbox_list *list) +{ + const char *str; + enum mail_error error; + + str = mailbox_list_get_last_error(list, &error); + mail_storage_set_error(storage, error, str); +} + void mail_storage_set_index_error(struct mailbox *box) { if (mail_index_is_deleted(box->index)) @@ -489,6 +499,9 @@ { int ret; + if (box->opened) + return 0; + if (!mailbox_list_is_valid_existing_name(box->list, box->name)) { mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, "Invalid mailbox name"); @@ -555,11 +568,7 @@ if (box->list->v.create_mailbox_dir(box->list, box->name, directory) < 0) { - const char *str; - enum mail_error error; - - str = mailbox_list_get_last_error(box->list, &error); - mail_storage_set_error(box->storage, error, str); + mail_storage_copy_list_error(box->storage, box->list); return -1; } mailbox_refresh_permissions(box); @@ -572,6 +581,54 @@ return box->v.update(box, update); } +static int mailbox_mark_index_deleted(struct mailbox *box) +{ + struct mail_index_transaction *trans; + + trans = mail_index_transaction_begin(box->view, 0); + mail_index_set_deleted(trans); + if (mail_index_transaction_commit(&trans) < 0) { + mail_storage_set_index_error(box); + return -1; + } + + /* sync the mailbox. this finishes the index deletion and it can + succeed only for a single session. we do it here, so the rest of + the deletion code doesn't have to worry about race conditions. */ + return mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ); +} + +int mailbox_delete(struct mailbox *box) +{ + enum mail_error error; + int ret; + + if (*box->name == '\0') { + mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, + "Storage root can't be deleted"); + return -1; + } + if (strcmp(box->name, "INBOX") == 0 && + (box->list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, + "INBOX can't be deleted."); + return -1; + } + + if (mailbox_open(box) < 0) { + (void)mail_storage_get_last_error(box->storage, &error); + if (error != MAIL_ERROR_NOTFOUND) + return -1; + /* \noselect mailbox */ + } else { + if (mailbox_mark_index_deleted(box) < 0) + return -1; + } + ret = box->v.delete(box); + mailbox_close(box); + return ret; +} + struct mail_storage *mailbox_get_storage(const struct mailbox *box) { return box->storage;
--- a/src/lib-storage/mail-storage.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mail-storage.h Tue Feb 09 04:11:53 2010 +0200 @@ -41,7 +41,9 @@ This causes ACL plugin to use POST right rather than INSERT. */ MAILBOX_FLAG_POST_SESSION = 0x80, /* Force opening mailbox and ignoring any ACLs */ - MAILBOX_FLAG_IGNORE_ACLS = 0x100 + MAILBOX_FLAG_IGNORE_ACLS = 0x100, + /* Open mailbox even if it's already marked as deleted */ + MAILBOX_FLAG_OPEN_DELETED = 0x200 }; enum mailbox_feature { @@ -345,6 +347,8 @@ bool directory); /* Update existing mailbox's metadata. */ int mailbox_update(struct mailbox *box, const struct mailbox_update *update); +/* Delete mailbox (and its parent directory, if it has no siblings) */ +int mailbox_delete(struct mailbox *box); /* Enable the given feature for the mailbox. */ int mailbox_enable(struct mailbox *box, enum mailbox_feature features);
--- a/src/lib-storage/mailbox-list-private.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mailbox-list-private.h Tue Feb 09 04:11:53 2010 +0200 @@ -54,6 +54,10 @@ const char *mailbox_name, enum mailbox_list_file_type type, enum mailbox_info_flags *flags_r); + /* Returns TRUE if dir/name points to mailbox's internal directory. + If it does, mailbox deletion assumes it can safely delete it. */ + bool (*is_mailbox_dir)(struct mailbox_list *list, const char *dir, + const char *name); int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set);
--- a/src/lib-storage/mailbox-list.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mailbox-list.c Tue Feb 09 04:11:53 2010 +0200 @@ -15,7 +15,6 @@ #include "unlink-directory.h" #include "imap-match.h" #include "imap-utf7.h" -#include "mail-index-alloc-cache.h" #include "mailbox-log.h" #include "mailbox-tree.h" #include "mail-storage-private.h" @@ -732,29 +731,6 @@ return 0; } -int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name) -{ - if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') { - mailbox_list_set_error(list, MAIL_ERROR_PARAMS, - "Invalid mailbox name"); - return -1; - } - if (strcmp(name, "INBOX") == 0 && - (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) { - mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, - "INBOX can't be deleted."); - return -1; - } - - /* Make sure the indexes are closed before trying to delete the - directory that contains them. It can still fail with some NFS - implementations if indexes are opened by another session, but - that can't really be helped. */ - mail_index_alloc_cache_destroy_unrefed(); - - return list->v.delete_mailbox(list, name); -} - int mailbox_list_delete_dir(struct mailbox_list *list, const char *name) { if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') { @@ -840,51 +816,6 @@ list->changelog_timestamp = stamp; } -static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir) -{ - if (unlink_directory(dir, TRUE) == 0 || errno == ENOENT) - return 0; - - if (errno == ENOTEMPTY) { - /* We're most likely using NFS and we can't delete - .nfs* files. */ - mailbox_list_set_error(list, MAIL_ERROR_INUSE, - "Mailbox is still open in another session, " - "can't delete it."); - } else { - mailbox_list_set_critical(list, - "unlink_directory(%s) failed: %m", dir); - } - return -1; -} - -int mailbox_list_delete_index_control(struct mailbox_list *list, - const char *name) -{ - const char *path, *index_dir, *dir; - - path = mailbox_list_get_path(list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - - /* delete the index directory first, so that if we crash we don't - leave indexes for deleted mailboxes lying around */ - index_dir = mailbox_list_get_path(list, name, - MAILBOX_LIST_PATH_TYPE_INDEX); - if (*index_dir != '\0' && strcmp(index_dir, path) != 0) { - if (mailbox_list_try_delete(list, index_dir) < 0) - return -1; - } - - /* control directory next */ - dir = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL); - if (*dir != '\0' && strcmp(dir, path) != 0 && - strcmp(dir, index_dir) != 0) { - if (mailbox_list_try_delete(list, dir) < 0) - return -1; - } - return 0; -} - static void node_fix_parents(struct mailbox_node *node) { /* If we happened to create any of the parents, we need to mark them
--- a/src/lib-storage/mailbox-list.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/mailbox-list.h Tue Feb 09 04:11:53 2010 +0200 @@ -248,8 +248,6 @@ int mailbox_list_set_subscribed(struct mailbox_list *list, const char *name, bool set); -/* Delete the given mailbox. If it has children, they aren't deleted. */ -int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name); /* Delete a non-selectable mailbox. Fail if the mailbox is selectable. */ int mailbox_list_delete_dir(struct mailbox_list *list, const char *name); /* Rename mailbox. Renaming across different mailbox lists is possible only
--- a/src/lib-storage/test-mailbox.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/lib-storage/test-mailbox.c Tue Feb 09 04:11:53 2010 +0200 @@ -52,6 +52,13 @@ return -1; } +static int test_mailbox_delete(struct mailbox *box) +{ + mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, + "Test mailbox delete isn't supported"); + return -1; +} + static void test_mailbox_get_status(struct mailbox *box ATTR_UNUSED, enum mailbox_status_items items ATTR_UNUSED, struct mailbox_status *status_r) @@ -308,6 +315,7 @@ test_mailbox_close, test_mailbox_create, test_mailbox_update, + test_mailbox_delete, test_mailbox_get_status, NULL, NULL,
--- a/src/plugins/acl/acl-mailbox-list.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/acl/acl-mailbox-list.c Tue Feb 09 04:11:53 2010 +0200 @@ -488,31 +488,6 @@ } static int -acl_mailbox_list_delete(struct mailbox_list *list, const char *name) -{ - struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); - bool can_see; - int ret; - - ret = acl_mailbox_list_have_right(list, name, FALSE, - ACL_STORAGE_RIGHT_DELETE, &can_see); - if (ret <= 0) { - if (ret < 0) - return -1; - if (can_see) { - mailbox_list_set_error(list, MAIL_ERROR_PERM, - MAIL_ERRSTR_NO_PERMISSION); - } else { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - } - return -1; - } - - return alist->module_ctx.super.delete_mailbox(list, name); -} - -static int acl_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) @@ -626,7 +601,6 @@ list->v.iter_deinit = acl_mailbox_list_iter_deinit; list->v.get_mailbox_name_status = acl_get_mailbox_name_status; list->v.create_mailbox_dir = acl_mailbox_list_create_dir; - list->v.delete_mailbox = acl_mailbox_list_delete; list->v.rename_mailbox = acl_mailbox_list_rename; acl_storage_rights_ctx_init(&alist->rights, backend);
--- a/src/plugins/acl/acl-mailbox.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/acl/acl-mailbox.c Tue Feb 09 04:11:53 2010 +0200 @@ -19,6 +19,7 @@ struct acl_mailbox { union mailbox_module_context module_ctx; struct acl_object *aclobj; + bool skip_acl_checks; }; struct acl_transaction_context { @@ -41,6 +42,9 @@ struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list); int ret; + if (abox->skip_acl_checks) + return 1; + ret = acl_object_have_right(abox->aclobj, alist->rights.acl_storage_right_idx[right_idx]); if (ret > 0) @@ -151,6 +155,41 @@ return abox->module_ctx.super.update(box, update); } +static void acl_mailbox_fail_not_found(struct mailbox *box) +{ + int ret; + + ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); + if (ret > 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_PERM, + MAIL_ERRSTR_NO_PERMISSION); + } else if (ret == 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name)); + } +} + +static int +acl_mailbox_delete(struct mailbox *box) +{ + struct acl_mailbox *abox = ACL_CONTEXT(box); + int ret; + + ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_DELETE); + if (ret <= 0) { + if (ret == 0) + acl_mailbox_fail_not_found(box); + return -1; + } + + /* deletion might internally open the mailbox. let it succeed even if + we don't have READ permission. */ + abox->skip_acl_checks = TRUE; + ret = abox->module_ctx.super.delete(box); + abox->skip_acl_checks = FALSE; + return ret; +} + static int acl_get_write_rights(struct mailbox *box, bool *flags_r, bool *flag_seen_r, bool *flag_del_r) @@ -391,7 +430,8 @@ /* mailbox can be opened either for reading or appending new messages */ if ((box->flags & MAILBOX_FLAG_IGNORE_ACLS) != 0 || - (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0) + (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0 || + abox->skip_acl_checks) return 0; if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) { @@ -402,24 +442,14 @@ } ret = acl_object_have_right(abox->aclobj, idx_arr[open_right]); - if (ret > 0) - return 0; - if (ret < 0) + if (ret <= 0) { + if (ret == 0) { + /* no access. */ + acl_mailbox_fail_not_found(box); + } return -1; - - /* no access. */ - ret = acl_object_have_right(abox->aclobj, - idx_arr[ACL_STORAGE_RIGHT_LOOKUP]); - if (ret < 0) - return -1; - if (ret > 0) { - mail_storage_set_error(box->storage, MAIL_ERROR_PERM, - MAIL_ERRSTR_NO_PERMISSION); - } else { - mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name)); } - return -1; + return 0; } static int acl_mailbox_open(struct mailbox *box) @@ -454,6 +484,7 @@ box->v.close = acl_mailbox_close; box->v.create = acl_mailbox_create; box->v.update = acl_mailbox_update; + box->v.delete = acl_mailbox_delete; box->v.mail_alloc = acl_mail_alloc; box->v.save_begin = acl_save_begin; box->v.keywords_create = acl_keywords_create;
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c Tue Feb 09 04:11:53 2010 +0200 @@ -46,7 +46,6 @@ union mailbox_list_module_context module_ctx; unsigned int internal_namespace:1; - unsigned int deleting:1; }; struct lazy_expunge_transaction { @@ -231,25 +230,6 @@ return _mail; } -static void lazy_expunge_mailbox_allocated(struct mailbox *box) -{ - struct lazy_expunge_mailbox_list *llist = - LAZY_EXPUNGE_LIST_CONTEXT(box->list); - union mailbox_module_context *mbox; - - if (llist != NULL && !llist->internal_namespace) { - mbox = p_new(box->pool, union mailbox_module_context, 1); - mbox->super = box->v; - - box->v.transaction_begin = lazy_expunge_transaction_begin; - box->v.transaction_commit = lazy_expunge_transaction_commit; - box->v.transaction_rollback = lazy_expunge_transaction_rollback; - box->v.mail_alloc = lazy_expunge_mail_alloc; - MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module, - mbox); - } -} - static int mailbox_move(struct mailbox_list *src_list, const char *src_name, struct mailbox_list *dest_list, const char **_dest_name) @@ -291,10 +271,9 @@ } static int -mailbox_move_all_mails(struct mailbox_list *list, - const char *src_name, const char *dest_name) +mailbox_move_all_mails(struct mailbox *src_box, const char *dest_name) { - struct mailbox *src_box, *dest_box; + struct mailbox *dest_box; struct mail_search_args *search_args; struct mailbox_transaction_context *src_trans, *dest_trans; struct mail_search_context *search_ctx; @@ -304,7 +283,7 @@ enum mail_error error; int ret; - dest_box = mailbox_alloc(list, dest_name, NULL, 0); + dest_box = mailbox_alloc(src_box->list, dest_name, NULL, 0); if (mailbox_open(dest_box) < 0) { errstr = mail_storage_get_last_error(dest_box->storage, &error); i_error("lazy_expunge: Couldn't open DELETE dest mailbox " @@ -313,19 +292,6 @@ return -1; } - src_box = mailbox_alloc(list, src_name, NULL, MAILBOX_FLAG_KEEP_LOCKED); - if (mailbox_open(src_box) < 0) { - errstr = mail_storage_get_last_error(src_box->storage, &error); - mailbox_free(&src_box); - mailbox_free(&dest_box); - - if (error == MAIL_ERROR_NOTFOUND) - return 0; - i_error("lazy_expunge: Couldn't open DELETE source mailbox " - "%s: %s", src_name, errstr); - return -1; - } - src_trans = mailbox_transaction_begin(src_box, 0); dest_trans = mailbox_transaction_begin(dest_box, MAILBOX_TRANSACTION_FLAG_EXTERNAL); @@ -357,50 +323,49 @@ else mailbox_transaction_rollback(&dest_trans); - mailbox_free(&src_box); - mailbox_free(&dest_box); + if (ret == 0) + ret = mailbox_delete(src_box); - if (ret == 0) - ret = mailbox_list_delete_mailbox(list, src_name); + mailbox_free(&dest_box); return ret; } -static int -lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name) +static int mailbox_mark_index_undeleted(struct mailbox *box) { + struct mail_index_transaction *trans; + + trans = mail_index_transaction_begin(box->view, + MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); + mail_index_set_undeleted(trans); + if (mail_index_transaction_commit(&trans) < 0) { + mail_storage_set_index_error(box); + return -1; + } + return 0; +} + +static int lazy_expunge_mailbox_delete(struct mailbox *box) +{ + struct mailbox_list *list = box->list; struct lazy_expunge_mailbox_list *llist = LAZY_EXPUNGE_LIST_CONTEXT(list); struct mail_namespace *expunge_ns, *dest_ns; - enum mailbox_name_status status; - const char *destname; + struct mailbox *expunge_box; + const char *destname, *str; + enum mail_error error; struct tm *tm; char timestamp[256]; int ret; - if (llist->internal_namespace || llist->deleting) - return llist->module_ctx.super.delete_mailbox(list, name); - - /* first do the normal sanity checks */ - if (strcmp(name, "INBOX") == 0) { - mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, - "INBOX can't be deleted."); - return -1; - } - - if (mailbox_list_get_mailbox_name_status(list, name, &status) < 0) - return -1; - if (status == MAILBOX_NAME_INVALID) { - mailbox_list_set_error(list, MAIL_ERROR_PARAMS, - "Invalid mailbox name"); - return -1; - } + if (llist->internal_namespace) + return llist->module_ctx.super.delete_mailbox(list, box->name); expunge_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_EXPUNGE); dest_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_DELETE); if (expunge_ns == dest_ns) { /* if there are no expunged messages in this mailbox, we can simply rename the mailbox to the destination name */ - destname = name; + destname = box->name; } else { /* destination mailbox name needs to contain a timestamp */ tm = localtime(&ioloop_time); @@ -409,31 +374,69 @@ i_strocpy(timestamp, dec2str(ioloop_time), sizeof(timestamp)); } - destname = t_strconcat(name, "-", timestamp, NULL); + destname = t_strconcat(box->name, "-", timestamp, NULL); } /* first move the actual mailbox */ - if ((ret = mailbox_move(list, name, dest_ns->list, &destname)) < 0) + if ((ret = mailbox_move(list, box->name, dest_ns->list, &destname)) < 0) return -1; if (ret == 0) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name)); return -1; } - if (expunge_ns == dest_ns && strcmp(destname, name) != 0) { - llist->deleting = TRUE; - (void)mailbox_move_all_mails(dest_ns->list, destname, name); - llist->deleting = FALSE; + /* other sessions now see the mailbox completely deleted. + since it's not really deleted in the lazy-expunge namespace, + we might want to change it again. so mark the index undeleted. */ + expunge_box = mailbox_alloc(dest_ns->list, destname, NULL, + MAILBOX_FLAG_OPEN_DELETED); + if (mailbox_open(expunge_box) < 0) { + str = mail_storage_get_last_error(expunge_box->storage, &error); + i_error("lazy_expunge: Couldn't open DELETEd mailbox " + "%s: %s", destname, str); + mailbox_free(&expunge_box); + return -1; } + if (mailbox_mark_index_undeleted(expunge_box) < 0) { + mailbox_free(&expunge_box); + return -1; + } + + if (expunge_ns == dest_ns && strcmp(destname, box->name) != 0) + ret = mailbox_move_all_mails(expunge_box, box->name); + else + ret = 0; + mailbox_free(&expunge_box); /* next move the expunged messages mailbox, if it exists */ dest_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_DELETE_EXPUNGE); if (expunge_ns != dest_ns) { - (void)mailbox_move(expunge_ns->list, name, - dest_ns->list, &destname); + if (mailbox_move(expunge_ns->list, box->name, + dest_ns->list, &destname) < 0) + ret = -1; } - return 0; + return ret; +} + +static void lazy_expunge_mailbox_allocated(struct mailbox *box) +{ + struct lazy_expunge_mailbox_list *llist = + LAZY_EXPUNGE_LIST_CONTEXT(box->list); + union mailbox_module_context *mbox; + + if (llist != NULL && !llist->internal_namespace) { + mbox = p_new(box->pool, union mailbox_module_context, 1); + mbox->super = box->v; + + box->v.transaction_begin = lazy_expunge_transaction_begin; + box->v.transaction_commit = lazy_expunge_transaction_commit; + box->v.transaction_rollback = lazy_expunge_transaction_rollback; + box->v.mail_alloc = lazy_expunge_mail_alloc; + box->v.delete = lazy_expunge_mailbox_delete; + MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module, + mbox); + } } static void lazy_expunge_mail_namespace_storage_added(struct mail_namespace *ns) @@ -458,7 +461,6 @@ if (luser != NULL && ns->type == NAMESPACE_PRIVATE) { llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1); llist->module_ctx.super = list->v; - list->v.delete_mailbox = lazy_expunge_mailbox_list_delete; MODULE_CONTEXT_SET(list, lazy_expunge_mailbox_list_module, llist);
--- a/src/plugins/listescape/listescape-plugin.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/listescape/listescape-plugin.c Tue Feb 09 04:11:53 2010 +0200 @@ -216,20 +216,6 @@ } static int -listescape_delete_mailbox(struct mailbox_list *list, const char *name) -{ - struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list); - int ret; - - /* at least quota plugin opens the mailbox when deleting it */ - name = list_escape(list->ns, name, FALSE); - mlist->name_escaped = TRUE; - ret = mlist->module_ctx.super.delete_mailbox(list, name); - mlist->name_escaped = FALSE; - return ret; -} - -static int listescape_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) @@ -318,7 +304,6 @@ list->v.iter_init = listescape_mailbox_list_iter_init; list->v.iter_next = listescape_mailbox_list_iter_next; list->v.iter_deinit = listescape_mailbox_list_iter_deinit; - list->v.delete_mailbox = listescape_delete_mailbox; list->v.rename_mailbox = listescape_rename_mailbox; list->v.set_subscribed = listescape_set_subscribed; list->v.get_mailbox_name_status = listescape_get_mailbox_name_status;
--- a/src/plugins/mail-log/mail-log-plugin.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/mail-log/mail-log-plugin.c Tue Feb 09 04:11:53 2010 +0200 @@ -379,14 +379,13 @@ } static void -mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, - struct mailbox_list *list ATTR_UNUSED, - const char *name) +mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, struct mailbox *box) { if ((mail_log_set.events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0) return; - i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN)); + i_info("Mailbox deleted: %s", + str_sanitize(box->name, MAILBOX_NAME_LOG_LEN)); } static void
--- a/src/plugins/notify/notify-noop.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/notify/notify-noop.c Tue Feb 09 04:11:53 2010 +0200 @@ -19,11 +19,9 @@ void notify_noop_mail_transaction_commit(void *txn ATTR_UNUSED, struct mail_transaction_commit_changes *changes ATTR_UNUSED) {} void notify_noop_mail_transaction_rollback(void *txn ATTR_UNUSED) {} -void *notify_noop_mailbox_delete_begin(struct mailbox_list *list ATTR_UNUSED, - const char *name ATTR_UNUSED) { return NULL; } +void *notify_noop_mailbox_delete_begin(struct mailbox *box ATTR_UNUSED) { return NULL; } void notify_noop_mailbox_delete_commit(void *txn ATTR_UNUSED, - struct mailbox_list *list ATTR_UNUSED, - const char *name ATTR_UNUSED) {} + struct mailbox *box ATTR_UNUSED) {} void notify_noop_mailbox_delete_rollback(void *txn ATTR_UNUSED) {} void notify_noop_mailbox_rename(struct mailbox_list *oldlist ATTR_UNUSED, const char *oldname ATTR_UNUSED,
--- a/src/plugins/notify/notify-plugin-private.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/notify/notify-plugin-private.h Tue Feb 09 04:11:53 2010 +0200 @@ -14,10 +14,8 @@ void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t, struct mail_transaction_commit_changes *changes); void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t); -void notify_contexts_mailbox_delete_begin(struct mailbox_list *list, - const char *name); -void notify_contexts_mailbox_delete_commit(struct mailbox_list *list, - const char *name); +void notify_contexts_mailbox_delete_begin(struct mailbox *box); +void notify_contexts_mailbox_delete_commit(struct mailbox *box); void notify_contexts_mailbox_delete_rollback(void); void notify_contexts_mailbox_rename(struct mailbox_list *oldlist, const char *oldname,
--- a/src/plugins/notify/notify-plugin.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/notify/notify-plugin.c Tue Feb 09 04:11:53 2010 +0200 @@ -132,25 +132,20 @@ } } -void notify_contexts_mailbox_delete_begin(struct mailbox_list *list, - const char *name) +void notify_contexts_mailbox_delete_begin(struct mailbox *box) +{ + struct notify_context *ctx; + + for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) + ctx->mailbox_delete_txn = ctx->v.mailbox_delete_begin(box); +} + +void notify_contexts_mailbox_delete_commit(struct mailbox *box) { struct notify_context *ctx; for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { - ctx->mailbox_delete_txn = - ctx->v.mailbox_delete_begin(list, name); - } -} - -void notify_contexts_mailbox_delete_commit(struct mailbox_list *list, - const char *name) -{ - struct notify_context *ctx; - - for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) { - ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn, - list, name); + ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn, box); ctx->mailbox_delete_txn = NULL; } }
--- a/src/plugins/notify/notify-plugin.h Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/notify/notify-plugin.h Tue Feb 09 04:11:53 2010 +0200 @@ -7,6 +7,7 @@ struct mail_storage; struct mailbox_transaction_context; struct mailbox_list; +struct mailbox; struct notify_context; struct module; @@ -22,10 +23,8 @@ void (*mail_transaction_commit)(void *txn, struct mail_transaction_commit_changes *changes); void (*mail_transaction_rollback)(void *txn); - void *(*mailbox_delete_begin)(struct mailbox_list *list, - const char *name); - void (*mailbox_delete_commit)(void *txn, struct mailbox_list *list, - const char *name); + void *(*mailbox_delete_begin)(struct mailbox *box); + void (*mailbox_delete_commit)(void *txn, struct mailbox *box); void (*mailbox_delete_rollback)(void *txn); void (*mailbox_rename)(struct mailbox_list *oldlist, const char *oldname, @@ -44,10 +43,8 @@ void notify_noop_mail_transaction_commit(void *txn, struct mail_transaction_commit_changes *changes); void notify_noop_mail_transaction_rollback(void *txn); -void *notify_noop_mailbox_delete_begin(struct mailbox_list *list, - const char *name); -void notify_noop_mailbox_delete_commit(void *txn, struct mailbox_list *list, - const char *name); +void *notify_noop_mailbox_delete_begin(struct mailbox *box); +void notify_noop_mailbox_delete_commit(void *txn, struct mailbox *box); void notify_noop_mailbox_delete_rollback(void *txn); void notify_noop_mailbox_rename(struct mailbox_list *oldlist, const char *oldname,
--- a/src/plugins/notify/notify-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/notify/notify-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -199,6 +199,20 @@ lbox->super.transaction_rollback(t); } +static int +notify_mailbox_delete(struct mailbox *box) +{ + union mailbox_module_context *lbox = NOTIFY_CONTEXT(box); + + notify_contexts_mailbox_delete_begin(box); + if (lbox->super.delete(box) < 0) { + notify_contexts_mailbox_delete_rollback(); + return -1; + } + notify_contexts_mailbox_delete_commit(box); + return 0; +} + static void notify_mailbox_allocated(struct mailbox *box) { union mailbox_module_context *lbox; @@ -213,24 +227,11 @@ box->v.transaction_begin = notify_transaction_begin; box->v.transaction_commit = notify_transaction_commit; box->v.transaction_rollback = notify_transaction_rollback; + box->v.delete = notify_mailbox_delete; MODULE_CONTEXT_SET_SELF(box, notify_storage_module, lbox); } static int -notify_mailbox_list_delete(struct mailbox_list *list, const char *name) -{ - union mailbox_list_module_context *llist = NOTIFY_LIST_CONTEXT(list); - - notify_contexts_mailbox_delete_begin(list, name); - if (llist->super.delete_mailbox(list, name) < 0) { - notify_contexts_mailbox_delete_rollback(); - return -1; - } - notify_contexts_mailbox_delete_commit(list, name); - return 0; -} - -static int notify_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) @@ -254,7 +255,6 @@ llist = p_new(list->pool, union mailbox_list_module_context, 1); llist->super = list->v; - list->v.delete_mailbox = notify_mailbox_list_delete; list->v.rename_mailbox = notify_mailbox_list_rename; MODULE_CONTEXT_SET_SELF(list, notify_mailbox_list_module, llist);
--- a/src/plugins/quota/quota-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/quota/quota-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -331,6 +331,48 @@ return ret; } +static int +quota_mailbox_delete_shrink_quota(struct mailbox *box) +{ + struct mail_search_context *ctx; + struct mailbox_transaction_context *t; + struct quota_transaction_context *qt; + struct mail *mail; + struct mail_search_args *search_args; + + t = mailbox_transaction_begin(box, 0); + qt = quota_transaction_begin(box); + + search_args = mail_search_build_init(); + mail_search_build_add_all(search_args); + ctx = mailbox_search_init(t, search_args, NULL); + mail_search_args_unref(&search_args); + + mail = mail_alloc(t, 0, NULL); + while (mailbox_search_next(ctx, mail)) + quota_free(qt, mail); + mail_free(&mail); + + if (mailbox_search_deinit(&ctx) < 0) { + /* maybe we missed some mails. */ + quota_recalculate(qt); + } + (void)quota_transaction_commit(&qt); + mailbox_transaction_rollback(&t); + return 0; +} + +static int quota_mailbox_delete(struct mailbox *box) +{ + struct quota_mailbox *qbox = QUOTA_CONTEXT(box); + + if (box->opened) { + if (quota_mailbox_delete_shrink_quota(box) < 0) + return -1; + } + return qbox->module_ctx.super.delete(box); +} + static void quota_mailbox_close(struct mailbox *box) { struct quota_mailbox *qbox = QUOTA_CONTEXT(box); @@ -364,44 +406,11 @@ box->v.copy = quota_copy; box->v.sync_notify = quota_mailbox_sync_notify; box->v.sync_deinit = quota_mailbox_sync_deinit; + box->v.delete = quota_mailbox_delete; box->v.close = quota_mailbox_close; MODULE_CONTEXT_SET(box, quota_storage_module, qbox); } -static int -quota_mailbox_delete_shrink_quota(struct mailbox *box) -{ - struct mail_search_context *ctx; - struct mailbox_transaction_context *t; - struct quota_transaction_context *qt; - struct mail *mail; - struct mail_search_args *search_args; - int ret; - - if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) - return -1; - - t = mailbox_transaction_begin(box, 0); - qt = QUOTA_CONTEXT(t); - - search_args = mail_search_build_init(); - mail_search_build_add_all(search_args); - ctx = mailbox_search_init(t, search_args, NULL); - mail_search_args_unref(&search_args); - - mail = mail_alloc(t, 0, NULL); - while (mailbox_search_next(ctx, mail)) - quota_free(qt, mail); - mail_free(&mail); - - ret = mailbox_search_deinit(&ctx); - if (ret < 0) - mailbox_transaction_rollback(&t); - else - ret = mailbox_transaction_commit(&t); - return ret; -} - static void quota_mailbox_list_deinit(struct mailbox_list *list) { struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list); @@ -410,44 +419,6 @@ qlist->module_ctx.super.deinit(list); } -static int -quota_mailbox_list_delete(struct mailbox_list *list, const char *name) -{ - struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list); - struct mailbox *box; - enum mail_error error; - const char *str; - int ret; - - /* This is a bit annoying to handle. We'll have to open the mailbox - and free the quota for all the messages existing in it. Open the - mailbox locked so that other processes can't mess up the quota - calculations by adding/removing mails while we're doing this. */ - box = mailbox_alloc(list, name, NULL, MAILBOX_FLAG_KEEP_RECENT | - MAILBOX_FLAG_KEEP_LOCKED); - if (mailbox_open(box) < 0) { - str = mail_storage_get_last_error(mailbox_get_storage(box), - &error); - if (error != MAIL_ERROR_NOTPOSSIBLE) { - ret = -1; - } else { - /* mailbox isn't selectable */ - ret = 0; - } - } else { - if ((ret = quota_mailbox_delete_shrink_quota(box)) < 0) { - str = mail_storage_get_last_error(box->storage, &error); - mailbox_list_set_error(list, error, str); - } - } - if (box != NULL) - mailbox_free(&box); - - /* FIXME: here's an unfortunate race condition */ - return ret < 0 ? -1 : - qlist->module_ctx.super.delete_mailbox(list, name); -} - struct quota *quota_get_mail_user_quota(struct mail_user *user) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); @@ -522,7 +493,6 @@ qlist = p_new(list->pool, struct quota_mailbox_list, 1); qlist->module_ctx.super = list->v; list->v.deinit = quota_mailbox_list_deinit; - list->v.delete_mailbox = quota_mailbox_list_delete; MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist); /* register to owner's quota roots */
--- a/src/plugins/virtual/virtual-storage.c Tue Feb 09 04:09:28 2010 +0200 +++ b/src/plugins/virtual/virtual-storage.c Tue Feb 09 04:11:53 2010 +0200 @@ -305,98 +305,6 @@ return -1; } -static int -virtual_delete_nonrecursive(struct mailbox_list *list, const char *path, - const char *name) -{ - DIR *dir; - struct dirent *d; - string_t *full_path; - unsigned int dir_len; - bool unlinked_something = FALSE; - - dir = opendir(path); - if (dir == NULL) { - if (!mailbox_list_set_error_from_errno(list)) { - mailbox_list_set_critical(list, - "opendir(%s) failed: %m", path); - } - return -1; - } - - full_path = t_str_new(256); - str_append(full_path, path); - str_append_c(full_path, '/'); - dir_len = str_len(full_path); - - errno = 0; - while ((d = readdir(dir)) != NULL) { - if (d->d_name[0] == '.') { - /* skip . and .. */ - if (d->d_name[1] == '\0') - continue; - if (d->d_name[1] == '.' && d->d_name[2] == '\0') - continue; - } - - str_truncate(full_path, dir_len); - str_append(full_path, d->d_name); - - /* trying to unlink() a directory gives either EPERM or EISDIR - (non-POSIX). it doesn't really work anywhere in practise, - so don't bother stat()ing the file first */ - if (unlink(str_c(full_path)) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { - mailbox_list_set_critical(list, - "unlink(%s) failed: %m", - str_c(full_path)); - } - } - - if (closedir(dir) < 0) { - mailbox_list_set_critical(list, "closedir(%s) failed: %m", - path); - } - - if (rmdir(path) == 0) - unlinked_something = TRUE; - else if (errno != ENOENT && errno != ENOTEMPTY) { - mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); - return -1; - } - - if (!unlinked_something) { - mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, - t_strdup_printf("Directory %s isn't empty, " - "can't delete it.", name)); - return -1; - } - return 0; -} - -static int -virtual_list_delete_mailbox(struct mailbox_list *list, const char *name) -{ - struct virtual_mailbox_list *mlist = VIRTUAL_LIST_CONTEXT(list); - struct stat st; - const char *src; - - /* delete the index and control directories */ - if (mlist->module_ctx.super.delete_mailbox(list, name) < 0) - return -1; - - /* check if the mailbox actually exists */ - src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(src, &st) != 0 && errno == ENOENT) { - mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, - T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); - return -1; - } - - return virtual_delete_nonrecursive(list, src, name); -} - static void virtual_notify_changes(struct mailbox *box ATTR_UNUSED) { /* FIXME: maybe some day */ @@ -548,9 +456,7 @@ mlist->module_ctx.super = list->v; list->ns->flags |= NAMESPACE_FLAG_NOQUOTA; - list->v.iter_is_mailbox = virtual_list_iter_is_mailbox; - list->v.delete_mailbox = virtual_list_delete_mailbox; MODULE_CONTEXT_SET(list, virtual_mailbox_list_module, mlist); } @@ -581,6 +487,7 @@ virtual_mailbox_close, virtual_mailbox_create, virtual_mailbox_update, + index_storage_mailbox_delete, index_storage_get_status, NULL, NULL,