cydir-storage.c revision c2ced58969b446666ab1067c6bfdf495367ed621
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2007-2008 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "array.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "str.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mkdir-parents.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#include "index-mail.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "mail-copy.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "cydir-sync.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#include "cydir-storage.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen#include <unistd.h>
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen#include <dirent.h>
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen#include <sys/stat.h>
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen#define CREATE_MODE 0770 /* umask() should limit it more */
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen#define CYDIR_LIST_CONTEXT(obj) \
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen MODULE_CONTEXT(obj, cydir_mailbox_list_module)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenextern struct mail_storage cydir_storage;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenextern struct mailbox cydir_mailbox;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(cydir_mailbox_list_module,
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen &mailbox_list_module_register);
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenstatic int
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainencydir_list_delete_mailbox(struct mailbox_list *list, const char *name);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int cydir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen const char *dir, const char *fname,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *mailbox_name,
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen enum mailbox_list_file_type type,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen enum mailbox_info_flags *flags);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstatic int
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainencydir_get_list_settings(struct mailbox_list_settings *list_set,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *data, enum mail_storage_flags flags,
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen const char **layout_r, const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen *layout_r = "fs";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen memset(list_set, 0, sizeof(*list_set));
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen list_set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME;
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen list_set->maildir_name = "";
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (data == NULL || *data == '\0' || *data == ':') {
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen /* we won't do any guessing for this format. */
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen if (debug)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen i_info("cydir: mailbox location not given");
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen *error_r = "Root mail directory not given";
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen return -1;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen }
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (debug)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen i_info("cydir: data=%s", data);
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen return mailbox_list_settings_parse(data, list_set, layout_r, NULL,
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen error_r);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen}
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenstatic struct mail_storage *cydir_alloc(void)
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct cydir_storage *storage;
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen pool_t pool;
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool = pool_alloconly_create("cydir storage", 512+256);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen storage = p_new(pool, struct cydir_storage, 1);
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen storage->storage = cydir_storage;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen storage->storage.pool = pool;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen return &storage->storage;
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainenstatic int cydir_create(struct mail_storage *_storage, const char *data,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct cydir_storage *storage = (struct cydir_storage *)_storage;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct mailbox_list_settings list_set;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen struct stat st;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen const char *layout;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (cydir_get_list_settings(&list_set, data, _storage->flags,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen &layout, error_r) < 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return -1;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen list_set.mail_storage_flags = &_storage->flags;
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen list_set.lock_method = &_storage->lock_method;
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if ((_storage->flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (stat(list_set.root_dir, &st) < 0) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (errno == ENOENT) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf(
"Root mail directory doesn't exist: %s",
list_set.root_dir);
} else if (errno == EACCES) {
*error_r = mail_storage_eacces_msg("stat",
list_set.root_dir);
} else {
*error_r = t_strdup_printf(
"stat(%s) failed: %m",
list_set.root_dir);
}
return -1;
}
} else if (mkdir_parents(list_set.root_dir,
CREATE_MODE) == 0 || errno == EEXIST) {
} else if (errno == EACCES) {
*error_r = mail_storage_eacces_msg("mkdir", list_set.root_dir);
return -1;
} else {
*error_r = t_strdup_printf("mkdir(%s) failed: %m",
list_set.root_dir);
return -1;
}
if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
return -1;
storage->list_module_ctx.super = _storage->list->v;
_storage->list->v.iter_is_mailbox = cydir_list_iter_is_mailbox;
_storage->list->v.delete_mailbox = cydir_list_delete_mailbox;
MODULE_CONTEXT_SET_FULL(_storage->list, cydir_mailbox_list_module,
storage, &storage->list_module_ctx);
/* finish list init after we've overridden vfuncs */
mailbox_list_init(_storage->list, _storage->ns, &list_set,
mail_storage_get_list_flags(_storage->flags));
return 0;
}
static int create_cydir(struct mail_storage *storage, const char *path)
{
if (mkdir_parents(path, CREATE_MODE) < 0 && errno != EEXIST) {
if (!mail_storage_set_error_from_errno(storage)) {
mail_storage_set_critical(storage,
"mkdir(%s) failed: %m", path);
}
return -1;
}
return 0;
}
static struct mailbox *
cydir_open(struct cydir_storage *storage, const char *name,
enum mailbox_open_flags flags)
{
struct mail_storage *_storage = &storage->storage;
struct cydir_mailbox *mbox;
struct mail_index *index;
const char *path;
pool_t pool;
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
index = index_storage_alloc(_storage, name, flags, CYDIR_INDEX_PREFIX);
mail_index_set_fsync_types(index, MAIL_INDEX_SYNC_TYPE_APPEND |
MAIL_INDEX_SYNC_TYPE_EXPUNGE);
pool = pool_alloconly_create("cydir mailbox", 1024+512);
mbox = p_new(pool, struct cydir_mailbox, 1);
mbox->ibox.box = cydir_mailbox;
mbox->ibox.box.pool = pool;
mbox->ibox.storage = &storage->storage;
mbox->ibox.mail_vfuncs = &cydir_mail_vfuncs;
mbox->ibox.index = index;
mbox->storage = storage;
mbox->path = p_strdup(pool, path);
index_storage_mailbox_init(&mbox->ibox, name, flags, FALSE);
return &mbox->ibox.box;
}
static struct mailbox *
cydir_mailbox_open(struct mail_storage *_storage, const char *name,
struct istream *input, enum mailbox_open_flags flags)
{
struct cydir_storage *storage = (struct cydir_storage *)_storage;
const char *path;
struct stat st;
if (input != NULL) {
mail_storage_set_critical(_storage,
"cydir doesn't support streamed mailboxes");
return NULL;
}
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (stat(path, &st) == 0)
return cydir_open(storage, name, flags);
else if (errno == ENOENT) {
if (strcmp(name, "INBOX") == 0) {
/* INBOX always exists, create it */
if (create_cydir(_storage, path) < 0)
return NULL;
return cydir_open(storage, "INBOX", flags);
}
mail_storage_set_error(_storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
} else if (errno == EACCES) {
mail_storage_set_critical(_storage, "%s",
mail_storage_eacces_msg("stat", path));
} else {
mail_storage_set_critical(_storage, "stat(%s) failed: %m",
path);
}
return NULL;
}
static int cydir_mailbox_create(struct mail_storage *_storage,
const char *name,
bool directory ATTR_UNUSED)
{
const char *path;
struct stat st;
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (stat(path, &st) == 0) {
mail_storage_set_error(_storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox already exists");
return -1;
}
return create_cydir(_storage, path);
}
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_NOTPOSSIBLE,
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_storage *storage = CYDIR_LIST_CONTEXT(list);
struct stat st;
const char *src;
/* 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. */
index_storage_destroy_unrefed();
/* delete the index and control directories */
if (storage->list_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;
if (box->notify_callback == NULL)
index_mailbox_check_remove_all(&mbox->ibox);
else
index_mailbox_check_add(&mbox->ibox, mbox->path);
}
static int cydir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
ATTR_UNUSED,
const char *dir, const char *fname,
const char *mailbox_name ATTR_UNUSED,
enum mailbox_list_file_type type,
enum mailbox_info_flags *flags)
{
const char *mail_path;
struct stat st;
int ret = 1;
/* try to avoid stat() with these checks */
if (type != MAILBOX_LIST_FILE_TYPE_DIR &&
type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) {
/* it's a file */
*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
return 0;
}
/* need to stat() then */
mail_path = t_strconcat(dir, "/", fname, NULL);
if (stat(mail_path, &st) == 0) {
if (!S_ISDIR(st.st_mode)) {
/* non-directory */
*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
ret = 0;
} else if (st.st_nlink == 2) {
/* no subdirectories */
*flags |= MAILBOX_NOCHILDREN;
} else if (*ctx->list->set.maildir_name != '\0') {
/* non-default configuration: we have one directory
containing the mailboxes. if there are 3 links,
either this is a selectable mailbox without children
or non-selectable mailbox with children */
if (st.st_nlink > 3)
*flags |= MAILBOX_CHILDREN;
} else {
/* default configuration: all subdirectories are
child mailboxes. */
if (st.st_nlink > 2)
*flags |= MAILBOX_CHILDREN;
}
} else if (errno == ENOENT) {
/* doesn't exist - probably a non-existing subscribed mailbox */
*flags |= MAILBOX_NONEXISTENT;
} else {
/* non-selectable. probably either access denied, or symlink
destination not found. don't bother logging errors. */
*flags |= MAILBOX_NOSELECT;
}
return ret;
}
static void cydir_class_init(void)
{
cydir_transaction_class_init();
}
static void cydir_class_deinit(void)
{
cydir_transaction_class_deinit();
}
struct mail_storage cydir_storage = {
MEMBER(name) CYDIR_STORAGE_NAME,
MEMBER(mailbox_is_file) FALSE,
{
cydir_class_init,
cydir_class_deinit,
cydir_alloc,
cydir_create,
index_storage_destroy,
NULL,
cydir_mailbox_open,
cydir_mailbox_create
}
};
struct mailbox cydir_mailbox = {
MEMBER(name) NULL,
MEMBER(storage) NULL,
{
index_storage_is_readonly,
index_storage_allow_new_keywords,
index_storage_mailbox_close,
index_storage_get_status,
NULL,
NULL,
cydir_storage_sync_init,
index_mailbox_sync_next,
index_mailbox_sync_deinit,
NULL,
cydir_notify_changes,
index_transaction_begin,
index_transaction_commit,
index_transaction_rollback,
index_keywords_create,
index_keywords_free,
index_storage_get_uids,
index_mail_alloc,
index_header_lookup_init,
index_header_lookup_deinit,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next_nonblock,
index_storage_search_next_update_seq,
cydir_save_init,
cydir_save_continue,
cydir_save_finish,
cydir_save_cancel,
mail_storage_copy,
index_storage_is_inconsistent
}
};