/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "str.h"
#include "hex-binary.h"
#include "hostpid.h"
#include "randgen.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
const char *path)
{
const char *root_dir;
return 0;
"INBOX can't be deleted.");
return -1;
}
"Mail storage root can't be deleted.");
return -1;
}
static const char *unique_fname(void)
{
}
const char *name,
const char *trash_dir)
{
unsigned int count;
&src) <= 0)
i_unreached();
return -1;
/* rename the mailbox dir to trash dir, which atomically
marks it as being deleted. */
/* either the source was just deleted or
the trash dir was deleted. */
continue;
}
return -1;
}
/* can't do this the fast way */
return 0;
}
if (!EDESTDIREXISTS(errno)) {
return -1;
return -1;
}
/* trash dir already exists. the reasons for this are:
a) another process is in the middle of deleting it
b) previous process crashed and didn't delete it
c) NFS: mailbox was recently deleted, but some connection
still has that mailbox open. the directory contains .nfs*
files that can't be deleted until the mailbox is closed.
Because of c) we'll first try to rename the mailbox under
the trash directory and only later try to delete the entire
trash directory. */
if (trash_dir == trash_dest) {
unique_fname(), NULL);
return -1;
}
}
/* it's already renamed to trash dir, which means it's
deleted as far as the client is concerned. Report
success. */
}
return 1;
}
{
/* we can simply unlink() the file */
return 0;
return -1;
} else {
if (!mailbox_list_set_error_from_errno(list)) {
"unlink(%s) failed: %m", path);
}
return -1;
}
}
bool rmdir_path)
{
struct dirent *d;
const char *error;
int ret = 0;
return -1;
} else {
if (!mailbox_list_set_error_from_errno(list)) {
"opendir(%s) failed: %m", path);
}
}
return -1;
}
if (d->d_name[0] == '.') {
/* skip . and .. */
continue;
continue;
}
if (mailbox_dir) {
"unlink_directory(%s) failed: %s",
} else {
}
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 */
ret = -1;
} else {
/* child directories still exist */
rmdir_path = FALSE;
}
}
if (errno != 0) {
ret = -1;
}
path);
ret = -1;
}
if (ret < 0)
return -1;
if (rmdir_path) {
unsigned int try_count = 0;
/* We didn't see any child directories, so this is
either a race condition or .nfs* files were left
lying around. In case it's .nfs* files, retry after
waiting a bit. Hopefully all processes keeping those
files open will have closed them by then. */
usleep(100000);
}
/* race condition with another process, which finished
deleting it first. */
path);
return -1;
}
}
if (!unlinked_something) {
"Mailbox has children, can't delete it");
return -1;
}
return 0;
}
enum mailbox_list_path_type type)
{
if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
return TRUE;
/* e.g. CONTROL dir could point to the same INDEX dir. */
}
enum mailbox_list_path_type type)
{
const char *root_dir, *p;
/* Don't auto-rmdir parent index directories with ITERINDEX.
Otherwise it'll get us into inconsistent state with a
\NoSelect mailbox in the mail directory but not in index
directory. */
return;
}
the .imap/ part, but no further. */
len--;
if (len == 0)
return;
len--;
len--;
if (len == 0)
return;
}
return;
path);
return;
}
if (p == NULL)
break;
}
}
const char *storage_name)
{
};
const char *path;
for (unsigned int i = 0; i < N_ELEMENTS(types); i++) {
}
}
enum mailbox_list_path_type type)
{
int ret;
&mailbox_path) <= 0 ||
return 0;
if (type == MAILBOX_LIST_PATH_TYPE_CONTROL &&
&index_path) > 0 &&
/* CONTROL dir is the same as INDEX dir, which we already
deleted. We don't want to continue especially with
iter_from_index_dir=yes, because it could be deleting the
index directory. */
return 0;
}
/* Note that only ALT currently uses maildir_name in paths.
INDEX and CONTROL don't. */
if (type != MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX ||
/* this directory may contain also child mailboxes' data.
we don't want to delete that. */
rmdir_path) == 0)
ret = 1;
else {
if (error != MAIL_ERROR_NOTFOUND &&
return -1;
ret = 0;
}
} else {
ret = 1;
ret = 0;
else {
return -1;
}
}
/* Avoid leaving empty parent directories lying around.
These parent directories' existence or removal doesn't
affect our return value. */
return ret;
}
{
return ret;
}
const char *name, bool root_delete_success)
{
int ret2;
if (!root_delete_success &&
/* unexpected error - preserve it */
return -1;
/* unexpected error */
return -1;
} else if (ret2 > 0) {
/* successfully deleted */
return 0;
} else if (root_delete_success) {
/* nothing deleted by us, but root was successfully deleted */
return 0;
} else {
/* nothing deleted by us and the root didn't exist either.
make sure the list has the correct error set, since it
could have been changed. */
return -1;
}
}
{
/* it's a symlink? try just deleting it */
return 0;
return -1;
}
return -1;
}
return 0;
}
const char *name)
{
const char *path;
int ret;
&path);
if (ret < 0)
return -1;
return 0;
} else if (UNLINK_EISDIR(errno)) {
"Mailbox isn't a symlink");
} else {
}
return -1;
}