bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenmailbox_list_check_root_delete(struct mailbox_list *list, const char *name,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR);
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen "INBOX can't be deleted.");
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen "Mail storage root can't be deleted.");
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen return t_strdup_printf("%s.%s.%s", my_hostname, my_pid,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenint mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (mailbox_list_check_root_delete(list, name, src) < 0)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* rename the mailbox dir to trash dir, which atomically
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen marks it as being deleted. */
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen for (; rename(src, trash_dest) < 0; count++) {
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen /* either the source was just deleted or
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen the trash dir was deleted. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* can't do this the fast way */
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen "rename(%s, %s) failed: %m", src, trash_dest);
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen /* trash dir already exists. the reasons for this are:
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen a) another process is in the middle of deleting it
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen b) previous process crashed and didn't delete it
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen c) NFS: mailbox was recently deleted, but some connection
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen still has that mailbox open. the directory contains .nfs*
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen files that can't be deleted until the mailbox is closed.
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen Because of c) we'll first try to rename the mailbox under
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen the trash directory and only later try to delete the entire
375ef0ee80a734b45647476b89c0eab76f2e24a1Timo Sirainen trash directory. */
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen } else if (mailbox_list_delete_trash(trash_dest, &error) < 0 &&
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen "unlink_directory(%s) failed: %s", trash_dest, error);
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen if (mailbox_list_delete_trash(trash_dir, &error) < 0 &&
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen "unlink_directory(%s) failed: %s", trash_dir, error);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* it's already renamed to trash dir, which means it's
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen deleted as far as the client is concerned. Report
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenint mailbox_list_delete_mailbox_file(struct mailbox_list *list,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* we can simply unlink() the file */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (!mailbox_list_set_error_from_errno(list)) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenint mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (mailbox_list_check_root_delete(list, name, path) < 0)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (!mailbox_list_set_error_from_errno(list)) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* skip . and .. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (d->d_name[1] == '.' && d->d_name[2] == '\0')
dca6d617a23e3f93af3b8df59acb46478179fe55Timo Sirainen mailbox_dir = list->v.is_internal_name != NULL &&
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen if (mailbox_list_delete_trash(str_c(full_path), &error) < 0) {
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen "unlink_directory(%s) failed: %s",
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* trying to unlink() a directory gives either EPERM or EISDIR
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen (non-POSIX). it doesn't really work anywhere in practise,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen so don't bother stat()ing the file first */
aaaa6e07cf961038cf030d623540a8296c0ffe24Timo Sirainen else if (errno != ENOENT && !UNLINK_EISDIR(errno)) {
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen /* child directories still exist */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_critical(list, "readdir(%s) failed: %m", path);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_critical(list, "closedir(%s) failed: %m",
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen while (ret < 0 && errno == ENOTEMPTY && try_count++ < 10) {
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen /* We didn't see any child directories, so this is
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen either a race condition or .nfs* files were left
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen lying around. In case it's .nfs* files, retry after
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen waiting a bit. Hopefully all processes keeping those
97ffd8e0b0374b9ccbbc788cf796e15d021ad090Timo Sirainen files open will have closed them by then. */
d851acd16c8b42a1afb7ccab8a20b9279998a5daTimo Sirainen /* race condition with another process, which finished
d851acd16c8b42a1afb7ccab8a20b9279998a5daTimo Sirainen deleting it first. */
d851acd16c8b42a1afb7ccab8a20b9279998a5daTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
d851acd16c8b42a1afb7ccab8a20b9279998a5daTimo Sirainen } else if (errno != ENOTEMPTY && errno != EEXIST) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen "Mailbox has children, can't delete it");
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainenstatic bool mailbox_list_path_is_index(struct mailbox_list *list,
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen /* e.g. CONTROL dir could point to the same INDEX dir. */
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen type_root = mailbox_list_get_root_forced(list, type);
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen index_root = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_INDEX);
ae1a57954535642c09c3b8aee184736ddbb06cdfTimo Sirainenvoid mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
eaa2d473ed2ecdb9856cd98a33f4d3063cfaf2a1Timo Sirainen if (list->set.iter_from_index_dir && !list->set.no_noselect &&
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen /* Don't auto-rmdir parent index directories with ITERINDEX.
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen Otherwise it'll get us into inconsistent state with a
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen \NoSelect mailbox in the mail directory but not in index
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen directory. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen root_dir = mailbox_list_get_root_forced(list, type);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (strncmp(path, root_dir, strlen(root_dir)) != 0) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* mbox workaround: name=child/box, root_dir=mail/.imap/,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen path=mail/child/.imap/box. we'll want to try to delete
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen the .imap/ part, but no further. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
d823c19df414cac96c7f50a6f78a13fd03bcb27eTimo Sirainenvoid mailbox_list_delete_mailbox_until_root(struct mailbox_list *list,
d823c19df414cac96c7f50a6f78a13fd03bcb27eTimo Sirainen for (unsigned int i = 0; i < N_ELEMENTS(types); i++) {
d823c19df414cac96c7f50a6f78a13fd03bcb27eTimo Sirainen if (mailbox_list_get_path(list, storage_name, types[i], &path) > 0)
d823c19df414cac96c7f50a6f78a13fd03bcb27eTimo Sirainen mailbox_list_delete_until_root(list, path, types[i]);
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainenstatic int mailbox_list_try_delete(struct mailbox_list *list, const char *name,
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen const char *mailbox_path, *index_path, *path, *error;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mailbox_list_get_path(list, name, type, &path) <= 0 ||
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX,
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen /* CONTROL dir is the same as INDEX dir, which we already
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen deleted. We don't want to continue especially with
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen iter_from_index_dir=yes, because it could be deleting the
3aa1816a6b10a137c8f6242d02e31d479123c8b5Timo Sirainen index directory. */
0784c6f498b36afe6fb079c51e3c3d7c20e26e2cTimo Sirainen /* Note that only ALT currently uses maildir_name in paths.
0784c6f498b36afe6fb079c51e3c3d7c20e26e2cTimo Sirainen INDEX and CONTROL don't. */
0784c6f498b36afe6fb079c51e3c3d7c20e26e2cTimo Sirainen if (type != MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX ||
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* this directory may contain also child mailboxes' data.
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen we don't want to delete that. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen bool rmdir_path = *list->set.maildir_name != '\0';
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainen if (mailbox_list_delete_trash(path, &error) == 0)
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen "unlink_directory(%s) failed: %s", path, error);
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen /* Avoid leaving empty parent directories lying around.
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen These parent directories' existence or removal doesn't
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen affect our return value. */
5693411c1b19c99b9a91a3d9af6c9e681d2d0263Timo Sirainen mailbox_list_delete_until_root(list, path, type);
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainenint mailbox_list_delete_finish(struct mailbox_list *list, const char *name)
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainen ret = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX);
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen ret2 = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX_CACHE);
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainen ret2 = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL);
a1bef9db6db683360e6e7be59a7f9e9718cf6ff5Timo Sirainen ret2 = mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainenint mailbox_list_delete_finish_ret(struct mailbox_list *list,
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen mailbox_list_get_last_mail_error(list) != MAIL_ERROR_NOTFOUND) {
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen /* unexpected error - preserve it */
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen } else if ((ret2 = mailbox_list_delete_finish(list, name)) < 0) {
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen /* unexpected error */
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen } else if (ret2 > 0) {
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen /* successfully deleted */
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen /* nothing deleted by us, but root was successfully deleted */
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen /* nothing deleted by us and the root didn't exist either.
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen make sure the list has the correct error set, since it
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen could have been changed. */
12bd6ddfeb75d81ccc09bf34b374b0637dfe890fTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainenint mailbox_list_delete_trash(const char *path, const char **error_r)
97e511960951550338d69cac98fb5f3ca2badb09Timo Sirainen if (unlink_directory(path, UNLINK_DIRECTORY_FLAG_RMDIR, error_r) < 0) {
4cdfd6cc490503ed4176a6796140470916d06e01Timo Sirainen /* it's a symlink? try just deleting it */
0b25846ba794ce19536a24d4065beaf2a0bd9464Timo Sirainenint mailbox_list_delete_symlink_default(struct mailbox_list *list,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
0b25846ba794ce19536a24d4065beaf2a0bd9464Timo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
0b25846ba794ce19536a24d4065beaf2a0bd9464Timo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
0b25846ba794ce19536a24d4065beaf2a0bd9464Timo Sirainen "Mailbox isn't a symlink");
46eecdfa411d6bd2582b181be1085010f76f9635Timo Sirainen mailbox_list_set_critical(list, "unlink(%s) failed: %m", path);