Skip to content

Commit

Permalink
lib: Changed unlink_directory() API to return error string.
Browse files Browse the repository at this point in the history
There are many different reasons why it could fail, which a simple errno
can't very clearly indicate.
  • Loading branch information
sirainen authored and GitLab committed Aug 23, 2016
1 parent 5965eaa commit 97e5119
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 38 deletions.
27 changes: 14 additions & 13 deletions src/lib-storage/list/mailbox-list-delete.c
Expand Up @@ -48,7 +48,7 @@ int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
const char *name,
const char *trash_dir)
{
const char *src, *trash_dest;
const char *src, *trash_dest, *error;
unsigned int count;

if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
Expand Down Expand Up @@ -98,18 +98,18 @@ int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
if (trash_dir == trash_dest) {
trash_dest = t_strconcat(trash_dir, "/",
unique_fname(), NULL);
} else if (mailbox_list_delete_trash(trash_dest) < 0 &&
} else if (mailbox_list_delete_trash(trash_dest, &error) < 0 &&
(errno != ENOTEMPTY || count >= 5)) {
mailbox_list_set_critical(list,
"unlink_directory(%s) failed: %m", trash_dest);
"unlink_directory(%s) failed: %s", trash_dest, error);
return -1;
}
}

if (mailbox_list_delete_trash(trash_dir) < 0 &&
if (mailbox_list_delete_trash(trash_dir, &error) < 0 &&
errno != ENOTEMPTY && errno != EBUSY) {
mailbox_list_set_critical(list,
"unlink_directory(%s) failed: %m", trash_dir);
"unlink_directory(%s) failed: %s", trash_dir, error);

/* it's already renamed to trash dir, which means it's
deleted as far as the client is concerned. Report
Expand Down Expand Up @@ -145,6 +145,7 @@ int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
struct dirent *d;
string_t *full_path;
unsigned int dir_len;
const char *error;
bool mailbox_dir, unlinked_something = FALSE;

if (mailbox_list_check_root_delete(list, name, path) < 0)
Expand Down Expand Up @@ -185,10 +186,10 @@ int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
str_append(full_path, d->d_name);

if (mailbox_dir) {
if (mailbox_list_delete_trash(str_c(full_path)) < 0) {
if (mailbox_list_delete_trash(str_c(full_path), &error) < 0) {
mailbox_list_set_critical(list,
"unlink_directory(%s) failed: %m",
str_c(full_path));
"unlink_directory(%s) failed: %s",
str_c(full_path), error);
} else {
unlinked_something = TRUE;
}
Expand Down Expand Up @@ -275,7 +276,7 @@ void mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
static void mailbox_list_try_delete(struct mailbox_list *list, const char *name,
enum mailbox_list_path_type type)
{
const char *mailbox_path, *path;
const char *mailbox_path, *path, *error;

if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&mailbox_path) <= 0 ||
Expand All @@ -292,10 +293,10 @@ static void mailbox_list_try_delete(struct mailbox_list *list, const char *name,
rmdir_path) < 0)
return;
} else {
if (mailbox_list_delete_trash(path) < 0 &&
if (mailbox_list_delete_trash(path, &error) < 0 &&
errno != ENOENT && errno != ENOTEMPTY) {
mailbox_list_set_critical(list,
"unlink_directory(%s) failed: %m", path);
"unlink_directory(%s) failed: %s", path, error);
}
}

Expand All @@ -310,9 +311,9 @@ void mailbox_list_delete_finish(struct mailbox_list *list, const char *name)
mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
}

int mailbox_list_delete_trash(const char *path)
int mailbox_list_delete_trash(const char *path, const char **error_r)
{
if (unlink_directory(path, UNLINK_DIRECTORY_FLAG_RMDIR) < 0) {
if (unlink_directory(path, UNLINK_DIRECTORY_FLAG_RMDIR, error_r) < 0) {
if (errno == ELOOP) {
/* it's a symlink? try just deleting it */
if (unlink(path) == 0)
Expand Down
2 changes: 1 addition & 1 deletion src/lib-storage/list/mailbox-list-delete.h
Expand Up @@ -15,7 +15,7 @@ void mailbox_list_delete_finish(struct mailbox_list *list, const char *name);

void mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
enum mailbox_list_path_type type);
int mailbox_list_delete_trash(const char *path);
int mailbox_list_delete_trash(const char *path, const char **error_r);

int mailbox_list_delete_symlink_default(struct mailbox_list *list,
const char *name);
Expand Down
6 changes: 3 additions & 3 deletions src/lib-storage/list/mailbox-list-index-backend.c
Expand Up @@ -460,7 +460,7 @@ static void
index_list_try_delete(struct mailbox_list *_list, const char *name,
enum mailbox_list_path_type type)
{
const char *mailbox_path, *path;
const char *mailbox_path, *path, *error;

if (mailbox_list_get_path(_list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&mailbox_path) <= 0 ||
Expand All @@ -477,10 +477,10 @@ index_list_try_delete(struct mailbox_list *_list, const char *name,
rmdir_path) < 0)
return;
} else {
if (mailbox_list_delete_trash(path) < 0 &&
if (mailbox_list_delete_trash(path, &error) < 0 &&
errno != ENOENT && errno != ENOTEMPTY) {
mailbox_list_set_critical(_list,
"unlink_directory(%s) failed: %m", path);
"unlink_directory(%s) failed: %s", path, error);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib-storage/list/mailbox-list-maildir-iter.c
Expand Up @@ -245,7 +245,7 @@ int maildir_list_get_mailbox_flags(struct mailbox_list *list,
static bool maildir_delete_trash_dir(struct maildir_list_iterate_context *ctx,
const char *fname)
{
const char *path;
const char *path, *error;
struct stat st;

if (fname[1] != ctx->prefix_char || ctx->prefix_char == '\0' ||
Expand All @@ -258,7 +258,7 @@ static bool maildir_delete_trash_dir(struct maildir_list_iterate_context *ctx,
path = t_strdup_printf("%s/%s", ctx->dir, fname);
if (stat(path, &st) == 0 &&
st.st_mtime < ioloop_time - 3600)
(void)mailbox_list_delete_trash(path);
(void)mailbox_list_delete_trash(path, &error);

return TRUE;
}
Expand Down
53 changes: 41 additions & 12 deletions src/lib/unlink-directory.c
Expand Up @@ -33,6 +33,7 @@
#define _GNU_SOURCE /* for O_NOFOLLOW with Linux */

#include "lib.h"
#include "abspath.h"
#include "unlink-directory.h"

#include <fcntl.h>
Expand All @@ -41,7 +42,8 @@
#include <sys/stat.h>

static int
unlink_directory_r(const char *dir, enum unlink_directory_flags flags)
unlink_directory_r(const char *dir, enum unlink_directory_flags flags,
const char **error_r)
{
DIR *dirp;
struct dirent *d;
Expand All @@ -50,30 +52,40 @@ unlink_directory_r(const char *dir, enum unlink_directory_flags flags)

#ifdef O_NOFOLLOW
dir_fd = open(dir, O_RDONLY | O_NOFOLLOW);
if (dir_fd == -1)
if (dir_fd == -1) {
*error_r = t_strdup_printf(
"open(%s, O_RDONLY | O_NOFOLLOW) failed: %m", dir);
return -1;
}
#else
struct stat st2;

if (lstat(dir, &st) < 0)
if (lstat(dir, &st) < 0) {
*error_r = t_strdup_printf("lstat(%s) failed: %m", dir);
return -1;
}

if (!S_ISDIR(st.st_mode)) {
if ((st.st_mode & S_IFMT) != S_IFLNK)
if ((st.st_mode & S_IFMT) != S_IFLNK) {
*error_r = t_strdup_printf("%s is not a directory: %s", dir);
errno = ENOTDIR;
else {
} else {
/* be compatible with O_NOFOLLOW */
errno = ELOOP;
*error_r = t_strdup_printf("%s is a symlink, not a directory: %s", dir);
}
return -1;
}

dir_fd = open(dir, O_RDONLY);
if (dir_fd == -1)
if (dir_fd == -1) {
*error_r = t_strdup_printf("open(%s, O_RDONLY) failed: %m", dir);
return -1;
}

if (fstat(dir_fd, &st2) < 0) {
i_close_fd(&dir_fd);
*error_r = t_strdup_printf("fstat(%s) failed: %m", dir);
return -1;
}

Expand All @@ -82,17 +94,21 @@ unlink_directory_r(const char *dir, enum unlink_directory_flags flags)
/* directory was just replaced with something else. */
i_close_fd(&dir_fd);
errno = ENOTDIR;
*error_r = t_strdup_printf(
"%s race condition: directory was just replaced", dir);
return -1;
}
#endif
if (fchdir(dir_fd) < 0) {
i_close_fd(&dir_fd);
*error_r = t_strdup_printf("fchdir(%s) failed: %m", dir);
return -1;
}

dirp = opendir(".");
if (dirp == NULL) {
i_close_fd(&dir_fd);
*error_r = t_strdup_printf("opendir(.) (in %s) failed: %m", dir);
return -1;
}

Expand All @@ -117,7 +133,7 @@ unlink_directory_r(const char *dir, enum unlink_directory_flags flags)
errno = 0;
} else if (S_ISDIR(st.st_mode) &&
(flags & UNLINK_DIRECTORY_FLAG_FILES_ONLY) == 0) {
if (unlink_directory_r(d->d_name, flags) < 0) {
if (unlink_directory_r(d->d_name, flags, error_r) < 0) {
if (errno != ENOENT)
break;
errno = 0;
Expand Down Expand Up @@ -156,33 +172,45 @@ unlink_directory_r(const char *dir, enum unlink_directory_flags flags)
old_errno = errno;

i_close_fd(&dir_fd);
if (closedir(dirp) < 0)
if (closedir(dirp) < 0) {
*error_r = t_strdup_printf("closedir(%s) failed: %m", dir);
return -1;
}

if (old_errno != 0) {
errno = old_errno;
*error_r = t_strdup_printf("readdir(%s) failed: %m", dir);
return -1;
}

return 0;
}

int unlink_directory(const char *dir, enum unlink_directory_flags flags)
int unlink_directory(const char *dir, enum unlink_directory_flags flags,
const char **error_r)
{
const char *orig_dir;
int fd, ret, old_errno;

if (t_get_current_dir(&orig_dir) < 0)
orig_dir = ".";

fd = open(".", O_RDONLY);
if (fd == -1)
if (fd == -1) {
*error_r = t_strdup_printf(
"Can't preserve current directory %s: "
"open(.) failed: %m", orig_dir);
return -1;
}

ret = unlink_directory_r(dir, flags);
ret = unlink_directory_r(dir, flags, error_r);
if (ret < 0 && errno == ENOENT)
ret = 0;
old_errno = errno;

if (fchdir(fd) < 0) {
i_fatal("unlink_directory(%s): "
"Can't fchdir() back to our original dir: %m", dir);
"Can't fchdir() back to our original dir %s: %m", dir, orig_dir);
}
i_close_fd(&fd);

Expand All @@ -193,6 +221,7 @@ int unlink_directory(const char *dir, enum unlink_directory_flags flags)

if ((flags & UNLINK_DIRECTORY_FLAG_RMDIR) != 0) {
if (rmdir(dir) < 0 && errno != ENOENT) {
*error_r = t_strdup_printf("rmdir(%s) failed: %m", dir);
if (errno == EEXIST) {
/* standardize errno */
errno = ENOTEMPTY;
Expand Down
7 changes: 5 additions & 2 deletions src/lib/unlink-directory.h
Expand Up @@ -11,7 +11,10 @@ enum unlink_directory_flags {
};

/* Unlink directory and/or everything under it.
Returns 0 if successful, -1 if error. */
int unlink_directory(const char *dir, enum unlink_directory_flags flags);
Returns 0 if successful, -1 if error. If the directory doesn't exist,
-1 and errno=ENOENT is returned. The returned error message contains the
exact syscall that failed, e.g. "open(path) failed: Permission denied" */
int unlink_directory(const char *dir, enum unlink_directory_flags flags,
const char **error_r);

#endif
11 changes: 6 additions & 5 deletions src/plugins/fts-lucene/lucene-wrapper.cc
Expand Up @@ -265,7 +265,7 @@ void lucene_index_unselect_mailbox(struct lucene_index *index)
static void lucene_handle_error(struct lucene_index *index, CLuceneError &err,
const char *msg)
{
const char *what = err.what();
const char *error, *what = err.what();

i_error("lucene index %s: %s failed (#%d): %s",
index->path, msg, err.number(), what);
Expand All @@ -275,9 +275,9 @@ static void lucene_handle_error(struct lucene_index *index, CLuceneError &err,
err.number() == CL_ERR_IO)) {
/* delete corrupted index. most IO errors are also about
missing files and other such corruption.. */
if (unlink_directory(index->path, (enum unlink_directory_flags)0) < 0 &&
if (unlink_directory(index->path, (enum unlink_directory_flags)0, &error) < 0 &&
errno != ENOENT)
i_error("unlink_directory(%s) failed: %m", index->path);
i_error("unlink_directory(%s) failed: %s", index->path, error);
rescan_clear_unseen_mailboxes(index, NULL);
}
}
Expand Down Expand Up @@ -412,6 +412,7 @@ int lucene_index_get_doc_count(struct lucene_index *index, uint32_t *count_r)
static int lucene_settings_check(struct lucene_index *index)
{
uint32_t set_checksum;
const char *error;
int ret = 0;

set_checksum = fts_lucene_settings_checksum(&index->set);
Expand All @@ -422,8 +423,8 @@ static int lucene_settings_check(struct lucene_index *index)
i_warning("fts-lucene: Settings have changed, rebuilding index for mailbox");

/* settings changed, rebuild index */
if (unlink_directory(index->path, (enum unlink_directory_flags)0) < 0) {
i_error("unlink_directory(%s) failed: %m", index->path);
if (unlink_directory(index->path, (enum unlink_directory_flags)0, &error) < 0) {
i_error("unlink_directory(%s) failed: %s", index->path, error);
ret = -1;
} else {
rescan_clear_unseen_mailboxes(index, NULL);
Expand Down

0 comments on commit 97e5119

Please sign in to comment.