This cleans up the code, makes it faster and also fixes some bugs.
Super-fast maildir migration code was also dropped, at least for now.
--HG--
branch : HEAD
AC_ARG_WITH(storages,
[ --with-storages Build with specified mail storage formats
- (maildir mbox dbox cydir)], [
+ (maildir mbox dbox mdbox cydir)], [
if test "$withval" = "yes" || test "$withval" = "no"; then
AC_MSG_ERROR([--with-storages needs storage list as parameter])
fi
mail_storages="shared `echo "$withval"|sed 's/,/ /g'`" ],
- mail_storages="shared maildir mbox dbox cydir")
+ mail_storages="shared maildir mbox dbox mdbox cydir")
AC_SUBST(mail_storages)
AC_ARG_WITH(sql-drivers,
maildir_libs='$(top_builddir)/src/lib-storage/index/maildir/libstorage_maildir.la'
mbox_libs='$(top_builddir)/src/lib-storage/index/mbox/libstorage_mbox.la'
-dbox_libs='$(top_builddir)/src/lib-storage/index/dbox/libstorage_dbox.la'
+dbox_common_libs='$(top_builddir)/src/lib-storage/index/dbox-common/libstorage_dbox_common.la'
+dbox_libs='$(top_builddir)/src/lib-storage/index/dbox-single/libstorage_dbox_single.la'
+mdbox_libs='$(top_builddir)/src/lib-storage/index/dbox-multi/libstorage_dbox_multi.la'
cydir_libs='$(top_builddir)/src/lib-storage/index/cydir/libstorage_cydir.la'
raw_libs='$(top_builddir)/src/lib-storage/index/raw/libstorage_raw.la'
shared_libs='$(top_builddir)/src/lib-storage/index/shared/libstorage_shared.la'
mail_storages=`(for i in $mail_storages; do echo $i; done)|sort|uniq|xargs echo`
for storage in $mail_storages; do
LINKED_STORAGE_LIBS="$LINKED_STORAGE_LIBS `eval echo \\$${storage}_libs`"
+ if test $storage = dbox || test $storage = mdbox; then
+ LINKED_STORAGE_LIBS="$LINKED_STORAGE_LIBS $dbox_common_libs"
+ dbox_common_libs=""
+ fi
done
AC_SUBST(LINKED_STORAGE_LIBS)
AC_DEFINE_UNQUOTED(MAIL_STORAGES, "$mail_storages", List of compiled in mail storages)
src/lib-storage/index/Makefile
src/lib-storage/index/maildir/Makefile
src/lib-storage/index/mbox/Makefile
-src/lib-storage/index/dbox/Makefile
+src/lib-storage/index/dbox-common/Makefile
+src/lib-storage/index/dbox-multi/Makefile
+src/lib-storage/index/dbox-single/Makefile
src/lib-storage/index/cydir/Makefile
src/lib-storage/index/raw/Makefile
src/lib-storage/index/shared/Makefile
-SUBDIRS = maildir mbox dbox cydir raw shared
+SUBDIRS = maildir mbox dbox-common dbox-multi dbox-single cydir raw shared
noinst_LTLIBRARIES = libstorage_index.la
-noinst_LTLIBRARIES = libstorage_dbox.la
+noinst_LTLIBRARIES = libstorage_dbox_common.la
AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-storage \
-I$(top_srcdir)/src/lib-storage/index
-libstorage_dbox_la_SOURCES = \
+libstorage_dbox_common_la_SOURCES = \
dbox-file.c \
dbox-file-fix.c \
- dbox-file-maildir.c \
dbox-mail.c \
- dbox-map.c \
dbox-save.c \
- dbox-settings.c \
- dbox-sync.c \
- dbox-sync-file.c \
- dbox-sync-rebuild.c \
dbox-storage.c \
- dbox-storage-rebuild.c
+ dbox-sync-rebuild.c
headers = \
dbox-file.h \
- dbox-file-maildir.h \
- dbox-map.h \
- dbox-map-private.h \
- dbox-settings.h \
+ dbox-mail.h \
+ dbox-save.h \
dbox-storage.h \
- dbox-storage-rebuild.h \
- dbox-sync.h
+ dbox-sync-rebuild.h
if INSTALL_HEADERS
pkginc_libdir=$(pkgincludedir)
#include <stdio.h>
+#define DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX ".broken"
+
static int
dbox_file_find_next_magic(struct dbox_file *file, uoff_t *offset_r, bool *pre_r)
{
if (bytes < 0) {
mail_storage_set_critical(&file->storage->storage,
"o_stream_send_istream(%s, %s) failed: %m",
- file->current_path, path);
+ file->cur_path, path);
return -1;
}
if ((uoff_t)bytes != count) {
if (ret < 0) {
errno = output->stream_errno;
mail_storage_set_critical(&file->storage->storage,
- "read(%s) failed: %m", file->current_path);
+ "read(%s) failed: %m", file->cur_path);
return -1;
}
int dbox_file_fix(struct dbox_file *file, uoff_t start_offset)
{
struct ostream *output;
- const char *temp_path, *broken_path;
- char *temp_fname;
+ const char *dir, *fname, *temp_path, *broken_path;
bool deleted;
int fd, ret;
- i_assert(file->input != NULL);
+ i_assert(dbox_file_is_open(file));
- temp_fname = dbox_generate_tmp_filename();
- temp_path = t_strdup_printf("%s/%s", file->storage->storage_dir,
- temp_fname);
- i_free(temp_fname);
+ fname = strrchr(file->cur_path, '/');
+ i_assert(fname != NULL);
+ dir = t_strdup_until(file->cur_path, fname);
+ fname++;
- fd = dbox_create_fd(file->storage, temp_path);
+ temp_path = t_strdup_printf("%s/%s", dir, dbox_generate_tmp_filename());
+ fd = file->storage->v.file_create_fd(file, temp_path, FALSE);
if (fd == -1)
return -1;
}
/* keep a copy of the original file in case someone wants to look
at it */
- broken_path = t_strconcat(file->current_path,
+ broken_path = t_strconcat(file->cur_path,
DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX, NULL);
- if (link(file->current_path, broken_path) < 0) {
+ if (link(file->cur_path, broken_path) < 0) {
mail_storage_set_critical(&file->storage->storage,
"link(%s, %s) failed: %m",
- file->current_path, broken_path);
+ file->cur_path, broken_path);
} else {
i_warning("dbox: Copy of the broken file saved to %s",
broken_path);
}
- if (rename(temp_path, file->current_path) < 0) {
+ if (rename(temp_path, file->cur_path) < 0) {
mail_storage_set_critical(&file->storage->storage,
"rename(%s, %s) failed: %m",
- temp_path, file->current_path);
+ temp_path, file->cur_path);
return -1;
}
if (dbox_file_open(file, &deleted) <= 0) {
mail_storage_set_critical(&file->storage->storage,
"dbox_file_fix(%s): reopening file failed",
- file->current_path);
+ file->cur_path);
return -1;
}
return 0;
#include "str.h"
#include "dbox-storage.h"
#include "dbox-file.h"
-#include "dbox-file-maildir.h"
#include <stdio.h>
#include <stdlib.h>
#define DBOX_READ_BLOCK_SIZE 4096
-char *dbox_generate_tmp_filename(void)
+const char *dbox_generate_tmp_filename(void)
{
static unsigned int create_count = 0;
- return i_strdup_printf("temp.%lu.P%sQ%uM%u.%s",
+ return t_strdup_printf("temp.%lu.P%sQ%uM%u.%s",
(unsigned long)ioloop_timeval.tv_sec, my_pid,
create_count++,
(unsigned int)ioloop_timeval.tv_usec,
{
mail_storage_set_critical(&file->storage->storage,
"%s failed for file %s: %m",
- function, file->current_path);
+ function, file->cur_path);
}
void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
{
va_list args;
- if (file->single_mbox == NULL)
- file->storage->sync_rebuild = TRUE;
+ file->storage->files_corrupted = TRUE;
va_start(args, reason);
mail_storage_set_critical(&file->storage->storage,
"Corrupted dbox file %s (around offset=%"PRIuUOFF_T"): %s",
- file->current_path,
- file->input == NULL ? 0 : file->input->v_offset,
+ file->cur_path, file->input == NULL ? 0 : file->input->v_offset,
t_strdup_vprintf(reason, args));
va_end(args);
}
-static struct dbox_file *
-dbox_find_and_move_open_file(struct dbox_storage *storage, uint32_t file_id)
+void dbox_file_init(struct dbox_file *file)
{
- struct dbox_file *const *files, *file;
- unsigned int i, count;
-
- files = array_get(&storage->open_files, &count);
- for (i = 0; i < count; i++) {
- if (files[i]->file_id == file_id) {
- /* move to last in the array */
- file = files[i];
- array_delete(&storage->open_files, i, 1);
- array_append(&storage->open_files, &file, 1);
- return file;
- }
- }
- return NULL;
+ file->refcount = 1;
+ file->fd = -1;
+ file->cur_offset = (uoff_t)-1;
+ file->cur_path = file->primary_path;
}
-static void dbox_file_free(struct dbox_file *file)
+void dbox_file_free(struct dbox_file *file)
{
i_assert(file->refcount == 0);
if (file->metadata_pool != NULL)
pool_unref(&file->metadata_pool);
dbox_file_close(file);
- i_free(file->current_path);
- i_free(file->fname);
+ i_free(file->primary_path);
+ i_free(file->alt_path);
i_free(file);
}
-void dbox_files_free(struct dbox_storage *storage)
-{
- struct dbox_file *const *files;
- unsigned int i, count;
-
- files = array_get(&storage->open_files, &count);
- for (i = 0; i < count; i++)
- dbox_file_free(files[i]);
- array_clear(&storage->open_files);
-}
-
-void dbox_files_sync_input(struct dbox_storage *storage)
-{
- struct dbox_file *const *files;
- unsigned int i, count;
-
- files = array_get(&storage->open_files, &count);
- for (i = 0; i < count; i++) {
- if (files[i]->input != NULL)
- i_stream_sync(files[i]->input);
- }
-}
-
-static void
-dbox_close_open_files(struct dbox_storage *storage, unsigned int close_count)
-{
- struct dbox_file *const *files;
- unsigned int i, count;
-
- files = array_get(&storage->open_files, &count);
- for (i = 0; i < count;) {
- if (files[i]->refcount == 0) {
- dbox_file_free(files[i]);
- array_delete(&storage->open_files, i, 1);
-
- if (--close_count == 0)
- break;
-
- files = array_get(&storage->open_files, &count);
- } else {
- i++;
- }
- }
-}
-
-static char *
-dbox_file_uid_get_fname(struct dbox_mailbox *mbox, uint32_t uid,
- bool *maildir_file_r)
-{
- const char *fname;
-
- if (uid <= mbox->highest_maildir_uid &&
- dbox_maildir_uid_get_fname(mbox, uid, &fname)) {
- *maildir_file_r = TRUE;
- return i_strdup(fname);
- } else {
- *maildir_file_r = FALSE;
- return i_strdup_printf(DBOX_MAIL_FILE_UID_FORMAT, uid);
- }
-}
-
-const char *dbox_file_get_primary_path(struct dbox_file *file)
-{
- const char *dir;
-
- dir = file->single_mbox != NULL ? file->single_mbox->ibox.box.path :
- file->storage->storage_dir;
- return t_strdup_printf("%s/%s", dir, file->fname);
-}
-
-const char *dbox_file_get_alt_path(struct dbox_file *file)
-{
- const char *dir;
-
- dir = file->single_mbox != NULL ? file->single_mbox->alt_path :
- file->storage->alt_storage_dir;
- return t_strdup_printf("%s/%s", dir, file->fname);
-}
-
-struct dbox_file *
-dbox_file_init_single(struct dbox_mailbox *mbox, uint32_t uid)
-{
- struct dbox_file *file;
- bool maildir;
-
- file = i_new(struct dbox_file, 1);
- file->refcount = 1;
- file->storage = mbox->storage;
- file->single_mbox = mbox;
- file->fd = -1;
- file->cur_offset = (uoff_t)-1;
- if (uid != 0) {
- file->uid = uid;
- file->fname = dbox_file_uid_get_fname(mbox, uid, &maildir);
- file->maildir_file = maildir;
- } else {
- file->fname = dbox_generate_tmp_filename();
- }
- file->current_path = i_strdup_printf("%s/%s", mbox->ibox.box.path,
- file->fname);
- return file;
-}
-
-struct dbox_file *
-dbox_file_init_multi(struct dbox_storage *storage, uint32_t file_id)
-{
- struct dbox_file *file;
- unsigned int count;
-
- file = file_id == 0 ? NULL :
- dbox_find_and_move_open_file(storage, file_id);
- if (file != NULL) {
- file->refcount++;
- return file;
- }
-
- count = array_count(&storage->open_files);
- if (count > storage->set->dbox_max_open_files) {
- dbox_close_open_files(storage, count -
- storage->set->dbox_max_open_files);
- }
-
- file = i_new(struct dbox_file, 1);
- file->refcount = 1;
- file->storage = storage;
- file->file_id = file_id;
- file->fd = -1;
- file->cur_offset = (uoff_t)-1;
- file->fname = file_id == 0 ? dbox_generate_tmp_filename() :
- i_strdup_printf(DBOX_MAIL_FILE_MULTI_FORMAT, file_id);
- file->current_path =
- i_strdup_printf("%s/%s", storage->storage_dir, file->fname);
-
- if (file_id != 0)
- array_append(&storage->open_files, &file, 1);
- return file;
-}
-
-int dbox_file_assign_id(struct dbox_file *file, uint32_t id)
-{
- const char *old_path;
- char *new_fname, *new_path;
- bool maildir;
-
- i_assert(!file->maildir_file);
- i_assert(file->uid == 0 && file->file_id == 0);
- i_assert(id != 0);
-
- old_path = file->current_path;
- if (file->single_mbox != NULL) {
- new_fname = dbox_file_uid_get_fname(file->single_mbox,
- id, &maildir);
- new_path = i_strdup_printf("%s/%s",
- file->single_mbox->ibox.box.path,
- new_fname);
- } else {
- new_fname = i_strdup_printf(DBOX_MAIL_FILE_MULTI_FORMAT, id);
- new_path = i_strdup_printf("%s/%s", file->storage->storage_dir,
- new_fname);
- }
- if (rename(old_path, new_path) < 0) {
- mail_storage_set_critical(&file->storage->storage,
- "rename(%s, %s) failed: %m",
- old_path, new_path);
- i_free(new_fname);
- i_free(new_path);
- return -1;
- }
- i_free(file->fname);
- i_free(file->current_path);
- file->fname = new_fname;
- file->current_path = new_path;
-
- if (file->single_mbox != NULL)
- file->uid = id;
- else {
- file->file_id = id;
- array_append(&file->storage->open_files, &file, 1);
- }
- return 0;
-}
-
void dbox_file_unref(struct dbox_file **_file)
{
struct dbox_file *file = *_file;
- struct dbox_file *const *files, *oldest_file;
- unsigned int i, count;
*_file = NULL;
i_assert(file->refcount > 0);
- if (--file->refcount > 0)
- return;
-
- /* don't cache metadata seeks while file isn't being referenced */
- file->metadata_read_offset = (uoff_t)-1;
-
- if (file->file_id != 0) {
- files = array_get(&file->storage->open_files, &count);
- if (!file->deleted &&
- count <= file->storage->set->dbox_max_open_files) {
- /* we can leave this file open for now */
- return;
- }
-
- /* close the oldest file with refcount=0 */
- for (i = 0; i < count; i++) {
- if (files[i]->refcount == 0)
- break;
- }
- oldest_file = files[i];
- array_delete(&file->storage->open_files, i, 1);
- if (oldest_file != file) {
- dbox_file_free(oldest_file);
- return;
- }
- /* have to close ourself */
- }
-
- dbox_file_free(file);
+ if (--file->refcount == 0)
+ file->storage->v.file_unrefed(file);
}
static int dbox_file_parse_header(struct dbox_file *file, const char *line)
bool alt = FALSE;
/* try the primary path first */
- path = dbox_file_get_primary_path(file);
+ path = file->primary_path;
while ((file->fd = open(path, O_RDWR)) == -1) {
if (errno != ENOENT) {
mail_storage_set_critical(&file->storage->storage,
return -1;
}
- if (file->storage->alt_storage_dir == NULL || alt) {
+ if (file->alt_path == NULL || alt) {
/* not found */
return 0;
}
/* try the alternative path */
- path = dbox_file_get_alt_path(file);
+ path = file->alt_path;
alt = TRUE;
}
- i_free(file->current_path);
- file->current_path = i_strdup(path);
- file->alt_path = alt;
+ file->cur_path = path;
return 1;
}
file->input = i_stream_create_fd(file->fd, 0, FALSE);
i_stream_set_init_buffer_size(file->input, DBOX_READ_BLOCK_SIZE);
- return file->maildir_file ? 1 :
- dbox_file_read_header(file);
-}
-
-int dbox_create_fd(struct dbox_storage *storage, const char *path)
-{
- mode_t old_mask;
- int fd;
-
- old_mask = umask(0666 & ~storage->dir_create_mode);
- fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
- umask(old_mask);
- if (fd == -1) {
- mail_storage_set_critical(&storage->storage,
- "open(%s, O_CREAT) failed: %m", path);
- } else if (storage->create_gid == (gid_t)-1) {
- /* no group change */
- } else if (fchown(fd, (uid_t)-1, storage->create_gid) < 0) {
- if (errno == EPERM) {
- mail_storage_set_critical(&storage->storage, "%s",
- eperm_error_get_chgrp("fchown", path,
- storage->create_gid,
- storage->create_gid_origin));
- } else {
- mail_storage_set_critical(&storage->storage,
- "fchown(%s, -1, %ld) failed: %m",
- path, (long)storage->create_gid);
- }
- /* continue anyway */
- }
- return fd;
+ return dbox_file_read_header(file);
}
int dbox_file_header_write(struct dbox_file *file, struct ostream *output)
(unsigned int)sizeof(struct dbox_message_header),
DBOX_HEADER_CREATE_STAMP, (unsigned int)ioloop_time);
+ file->file_version = DBOX_VERSION;
file->file_header_size = str_len(hdr);
file->msg_header_size = sizeof(struct dbox_message_header);
return o_stream_send(output, str_data(hdr), str_len(hdr));
}
-static int dbox_file_create(struct dbox_file *file)
-{
- i_assert(file->fd == -1);
-
- file->fd = dbox_create_fd(file->storage, file->current_path);
- if (file->fd == -1)
- return -1;
- file->output = o_stream_create_fd_file(file->fd, 0, FALSE);
- if (dbox_file_header_write(file, file->output) < 0) {
- dbox_file_set_syscall_error(file, "write()");
- return -1;
- }
- return 0;
-}
-
-int dbox_file_open_or_create(struct dbox_file *file, bool *deleted_r)
-{
- int ret;
-
- *deleted_r = FALSE;
-
- if (file->file_id == 0 && file->uid == 0) {
- T_BEGIN {
- ret = dbox_file_create(file) < 0 ? -1 : 1;
- } T_END;
- return ret;
- } else if (file->input != NULL)
- return 1;
- else
- return dbox_file_open(file, deleted_r);
-}
-
void dbox_file_close(struct dbox_file *file)
{
dbox_file_unlock(file);
if (file->input != NULL)
i_stream_unref(&file->input);
- if (file->output != NULL)
- o_stream_unref(&file->output);
if (file->fd != -1) {
if (close(file->fd) < 0)
dbox_file_set_syscall_error(file, "close()");
i_assert(file->fd != -1);
- ret = file_try_lock(file->fd, file->current_path, F_WRLCK,
+ ret = file_try_lock(file->fd, file->cur_path, F_WRLCK,
FILE_LOCK_METHOD_FCNTL, &file->lock);
if (ret < 0) {
mail_storage_set_critical(&file->storage->storage,
- "file_try_lock(%s) failed: %m", file->current_path);
+ "file_try_lock(%s) failed: %m", file->cur_path);
}
return ret;
}
void dbox_file_unlock(struct dbox_file *file)
{
- struct stat st;
+ i_assert(!file->appending);
- if (file->lock != NULL) {
- if (file->output != NULL) {
- i_assert(o_stream_get_buffer_used_size(file->output) == 0);
- if (fstat(file->fd, &st) == 0 &&
- (uoff_t)st.st_size != file->output->offset)
- i_fatal("dbox file modified while locked");
- o_stream_unref(&file->output);
- }
+ if (file->lock != NULL)
file_unlock(&file->lock);
- }
if (file->input != NULL)
i_stream_sync(file->input);
}
int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r)
{
struct dbox_message_header hdr;
- struct stat st;
const unsigned char *data;
size_t size;
int ret;
- if (file->maildir_file) {
- if (fstat(file->fd, &st) < 0) {
- dbox_file_set_syscall_error(file, "fstat()");
- return -1;
- }
- *physical_size_r = st.st_size;
- return 1;
- }
-
ret = i_stream_read_data(file->input, &data, &size,
file->msg_header_size - 1);
if (ret <= 0) {
int dbox_file_get_mail_stream(struct dbox_file *file, uoff_t offset,
uoff_t *physical_size_r,
- struct istream **stream_r, bool *expunged_r)
+ struct istream **stream_r)
{
uoff_t size;
int ret;
- *expunged_r = FALSE;
-
- if (file->input == NULL) {
- if ((ret = dbox_file_open(file, expunged_r)) <= 0 ||
- *expunged_r)
- return ret;
- }
+ i_assert(file->input != NULL);
if (offset == 0)
offset = file->file_header_size;
int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r)
{
uoff_t offset, size;
- bool expunged;
int ret;
if (file->cur_offset == (uoff_t)-1) {
}
*last_r = FALSE;
- ret = dbox_file_get_mail_stream(file, offset, &size, NULL, &expunged);
+ ret = dbox_file_get_mail_stream(file, offset, &size, NULL);
if (*offset_r == 0)
*offset_r = file->file_header_size;
return ret;
}
-static int
-dbox_file_seek_append_pos(struct dbox_file *file, uoff_t *append_offset_r)
+struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file)
{
- struct stat st;
+ struct dbox_file_append_context *ctx;
- if (file->file_version != DBOX_VERSION ||
- file->msg_header_size != sizeof(struct dbox_message_header)) {
- /* created by an incompatible version, can't append */
- return 0;
- }
+ i_assert(!file->appending);
- if (fstat(file->fd, &st) < 0) {
- dbox_file_set_syscall_error(file, "fstat()");
- return -1;
- }
- *append_offset_r = st.st_size;
+ file->appending = TRUE;
- file->output = o_stream_create_fd_file(file->fd, 0, FALSE);
- o_stream_seek(file->output, st.st_size);
- return 1;
+ ctx = i_new(struct dbox_file_append_context, 1);
+ ctx->file = file;
+ if (file->fd != -1) {
+ ctx->output = o_stream_create_fd_file(file->fd, 0, FALSE);
+ o_stream_cork(ctx->output);
+ }
+ return ctx;
}
-int dbox_file_get_append_stream(struct dbox_file *file, uoff_t *append_offset_r,
- struct ostream **stream_r)
+int dbox_file_append_commit(struct dbox_file_append_context **_ctx)
{
+ struct dbox_file_append_context *ctx = *_ctx;
int ret;
- if (file->fd == -1) {
- /* creating a new file */
- i_assert(file->output == NULL);
- i_assert(file->file_id == 0 && file->uid == 0);
-
- if (dbox_file_create(file) < 0)
- return -1;
+ i_assert(ctx->file->appending);
- if (file->single_mbox == NULL) {
- /* creating a new multi-file. even though we don't
- need it locked while writing to it, by the time
- we rename() it it needs to be locked. so we might
- as well do it here. */
- if ((ret = dbox_file_try_lock(file)) <= 0) {
- if (ret < 0)
- return -1;
- mail_storage_set_critical(
- &file->storage->storage,
- "dbox: Couldn't lock created file: %s",
- file->current_path);
- return -1;
- }
- }
- i_assert(file->output != NULL);
- } else if (file->output == NULL) {
- i_assert(file->lock != NULL || file->single_mbox != NULL);
+ *_ctx = NULL;
- ret = dbox_file_seek_append_pos(file, append_offset_r);
- if (ret <= 0)
- return ret;
- }
-
- o_stream_ref(file->output);
- *stream_r = file->output;
- return 1;
+ ret = dbox_file_append_flush(ctx);
+ o_stream_unref(&ctx->output);
+ ctx->file->appending = FALSE;
+ i_free(ctx);
+ return 0;
}
-uoff_t dbox_file_get_next_append_offset(struct dbox_file *file)
+void dbox_file_append_rollback(struct dbox_file_append_context **_ctx)
{
- i_assert(file->output != NULL);
+ struct dbox_file_append_context *ctx = *_ctx;
+ struct dbox_file *file = ctx->file;
+ bool close_file = FALSE;
- return file->output->offset;
+ i_assert(ctx->file->appending);
+
+ *_ctx = NULL;
+ if (ctx->first_append_offset == 0) {
+ /* nothing changed */
+ } else if (ctx->first_append_offset == file->file_header_size) {
+ /* rollbacking everything */
+ if (unlink(file->cur_path) < 0)
+ dbox_file_set_syscall_error(file, "unlink()");
+ close_file = TRUE;
+ } else {
+ /* truncating only some mails */
+ o_stream_close(ctx->output);
+ if (ftruncate(file->fd, ctx->first_append_offset) < 0)
+ dbox_file_set_syscall_error(file, "ftruncate()");
+ }
+ if (ctx->output != NULL)
+ o_stream_unref(&ctx->output);
+ i_free(ctx);
+
+ if (close_file)
+ dbox_file_close(file);
+ file->appending = FALSE;
}
-void dbox_file_cancel_append(struct dbox_file *file, uoff_t append_offset)
+int dbox_file_append_flush(struct dbox_file_append_context *ctx)
{
- (void)o_stream_flush(file->output);
+ if (ctx->last_flush_offset == ctx->output->offset)
+ return 0;
- if (file->output->offset != append_offset) {
- if (ftruncate(file->fd, append_offset) < 0)
- dbox_file_set_syscall_error(file, "ftruncate()");
- o_stream_seek(file->output, append_offset);
+ if (o_stream_flush(ctx->output) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "write()");
+ return -1;
}
+
+ if (!ctx->file->storage->storage.set->fsync_disable) {
+ if (fdatasync(ctx->file->fd) < 0) {
+ dbox_file_set_syscall_error(ctx->file, "fdatasync()");
+ return -1;
+ }
+ }
+ ctx->last_flush_offset = ctx->output->offset;
+ return 0;
}
-int dbox_file_flush_append(struct dbox_file *file)
+int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
+ struct ostream **output_r)
{
- i_assert(file->output != NULL);
+ struct dbox_file *file = ctx->file;
+ struct stat st;
- if (o_stream_flush(file->output) < 0) {
- dbox_file_set_syscall_error(file, "write()");
+ if (ctx->output == NULL) {
+ /* file creation had failed */
return -1;
}
- if (!file->storage->storage.set->fsync_disable) {
- if (fdatasync(file->fd) < 0) {
- dbox_file_set_syscall_error(file, "fdatasync()");
+ if (file->file_version == 0) {
+ /* newly created file, write the file header */
+ if (dbox_file_header_write(file, ctx->output) < 0) {
+ dbox_file_set_syscall_error(file, "write()");
return -1;
}
+ *output_r = ctx->output;
+ return 1;
}
- return 0;
+
+ /* file has existing mails */
+ if (file->file_version != DBOX_VERSION ||
+ file->msg_header_size != sizeof(struct dbox_message_header)) {
+ /* created by an incompatible version, can't append */
+ return 0;
+ }
+
+ if (ctx->output->offset == 0) {
+ /* first append to existing file. seek to eof first. */
+ if (fstat(file->fd, &st) < 0) {
+ dbox_file_set_syscall_error(file, "fstat()");
+ return -1;
+ }
+ o_stream_seek(ctx->output, st.st_size);
+ }
+ *output_r = ctx->output;
+ return 1;
}
int dbox_file_metadata_skip_header(struct dbox_file *file)
i_assert(file->cur_offset != (uoff_t)-1);
- if (file->metadata_read_offset == file->cur_offset ||
- file->maildir_file)
+ if (file->metadata_read_offset == file->cur_offset)
return 1;
metadata_offset = file->cur_offset + file->msg_header_size +
const char *const *metadata;
unsigned int i, count;
- if (file->maildir_file)
- return dbox_file_maildir_metadata_get(file, key);
-
metadata = array_get(&file->metadata, &count);
for (i = 0; i < count; i++) {
if (*metadata[i] == (char)key)
int dbox_file_move(struct dbox_file *file, bool alt_path)
{
struct ostream *output;
- const char *dest_dir, *temp_path, *dest_path;
+ const char *dest_dir, *temp_path, *dest_path, *p;
struct stat st;
bool deleted;
int out_fd, ret = 0;
i_assert(file->input != NULL);
i_assert(file->lock != NULL);
- if (file->alt_path == alt_path)
+ if (dbox_file_is_in_alt(file) == alt_path)
return 0;
- if (stat(file->current_path, &st) < 0 && errno == ENOENT) {
+ if (stat(file->cur_path, &st) < 0 && errno == ENOENT) {
/* already expunged/moved by another session */
dbox_file_unlock(file);
return 0;
}
- dest_dir = !alt_path ? dbox_file_get_primary_path(file) :
- dbox_file_get_alt_path(file);
+ dest_path = !alt_path ? file->primary_path : file->alt_path;
+ p = strrchr(dest_path, '/');
+ i_assert(p != NULL);
+ dest_dir = t_strdup_until(dest_path, p);
temp_path = t_strdup_printf("%s/%s", dest_dir,
dbox_generate_tmp_filename());
/* first copy the file. make sure to catch every possible error
since we really don't want to break the file. */
- out_fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- if (out_fd == -1 && errno == ENOENT) {
- if (mkdir_parents(dest_dir, 0700) < 0 && errno != EEXIST) {
- mail_storage_set_critical(&file->storage->storage,
- "mkdir_parents(%s) failed: %m", dest_dir);
- return -1;
- }
- out_fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
- }
- if (out_fd == -1) {
- mail_storage_set_critical(&file->storage->storage,
- "open(%s, O_CREAT) failed: %m", temp_path);
+ out_fd = file->storage->v.file_create_fd(file, temp_path, TRUE);
+ if (out_fd == -1)
return -1;
- }
+
output = o_stream_create_fd_file(out_fd, 0, FALSE);
i_stream_seek(file->input, 0);
while ((ret = o_stream_send_istream(output, file->input)) > 0) ;
mail_storage_set_critical(&file->storage->storage,
"o_stream_send_istream(%s, %s) "
"failed with unknown error",
- temp_path, file->current_path);
+ temp_path, file->cur_path);
}
o_stream_unref(&output);
/* the temp file was successfully written. rename it now to the
destination file. the destination shouldn't exist, but if it does
its contents should be the same (except for maybe older metadata) */
- dest_path = t_strdup_printf("%s/%s", dest_dir, file->fname);
if (rename(temp_path, dest_path) < 0) {
mail_storage_set_critical(&file->storage->storage,
"rename(%s, %s) failed: %m", temp_path, dest_path);
return -1;
}
}
- if (unlink(file->current_path) < 0) {
+ if (unlink(file->cur_path) < 0) {
dbox_file_set_syscall_error(file, "unlink()");
if (errno == EACCES) {
/* configuration problem? revert the write */
sizeof(dbox_msg_hdr->message_size_hex));
dbox_msg_hdr->save_lf = '\n';
}
+
+int dbox_file_unlink(struct dbox_file *file)
+{
+ const char *path;
+ bool alt = FALSE;
+
+ path = file->primary_path;
+ while (unlink(path) < 0) {
+ if (errno != ENOENT) {
+ mail_storage_set_critical(&file->storage->storage,
+ "unlink(%s) failed: %m", path);
+ return -1;
+ }
+ if (file->alt_path == NULL || alt) {
+ /* not found */
+ i_warning("dbox: File unexpectedly lost: %s",
+ file->primary_path);
+ return 0;
+ }
+
+ /* try the alternative path */
+ path = file->alt_path;
+ alt = TRUE;
+ }
+ return 1;
+}
#define DBOX_MAGIC_PRE "\001\002"
#define DBOX_MAGIC_POST "\n\001\003\n"
+struct dbox_file;
+
enum dbox_header_key {
- /* Offset for appending next message. In %08x format so it can be
- updated without moving data in header. If messages have been
- expunged and file must not be appended anymore, the value is filled
- with 'X'. */
- DBOX_HEADER_OLDV1_APPEND_OFFSET = 'A',
/* Must be sizeof(struct dbox_message_header) when appending (hex) */
DBOX_HEADER_MSG_HEADER_SIZE = 'M',
/* Creation UNIX timestamp (hex) */
- DBOX_HEADER_CREATE_STAMP = 'C'
+ DBOX_HEADER_CREATE_STAMP = 'C',
+
+ /* metadata used by old Dovecot versions */
+ DBOX_HEADER_OLDV1_APPEND_OFFSET = 'A'
};
enum dbox_metadata_key {
struct dbox_file {
struct dbox_storage *storage;
- /* set only for single-msg-per-file */
- struct dbox_mailbox *single_mbox;
-
int refcount;
- /* uid is for single-msg-per-file, file_id for multi-msgs-per-file */
- uint32_t uid, file_id;
time_t create_time;
unsigned int file_version;
unsigned int file_header_size;
unsigned int msg_header_size;
- uoff_t cur_offset;
- uoff_t cur_physical_size;
- /* first appended message's offset (while appending) */
- uoff_t first_append_offset;
-
- char *fname;
- char *current_path;
-
+ const char *cur_path;
+ char *primary_path, *alt_path;
int fd;
struct istream *input;
- struct ostream *output;
struct file_lock *lock;
+ uoff_t cur_offset;
+ uoff_t cur_physical_size;
+
/* Metadata for the currently seeked metadata block. */
pool_t metadata_pool;
ARRAY_DEFINE(metadata, const char *);
uoff_t metadata_read_offset;
- unsigned int alt_path:1;
- unsigned int maildir_file:1;
+ unsigned int appending:1;
unsigned int deleted:1;
unsigned int corrupted:1;
};
-#define dbox_file_is_open(file) ((file)->input != NULL)
+struct dbox_file_append_context {
+ struct dbox_file *file;
-struct dbox_file *
-dbox_file_init_single(struct dbox_mailbox *mbox, uint32_t uid);
-struct dbox_file *
-dbox_file_init_multi(struct dbox_storage *storage, uint32_t file_id);
-void dbox_file_unref(struct dbox_file **file);
+ uoff_t first_append_offset, last_flush_offset;
+ struct ostream *output;
+};
-/* Free all currently opened files. */
-void dbox_files_free(struct dbox_storage *storage);
-/* Flush all cached input data from opened files. */
-void dbox_files_sync_input(struct dbox_storage *storage);
+#define dbox_file_is_open(file) ((file)->fd != -1)
+#define dbox_file_is_in_alt(file) ((file)->cur_path == (file)->alt_path)
-/* Assign a newly created file a new id. For single files assign UID,
- for multi files assign map UID. */
-int dbox_file_assign_id(struct dbox_file *file, uint32_t id);
+void dbox_file_init(struct dbox_file *file);
+void dbox_file_unref(struct dbox_file **file);
/* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error.
If file is deleted, deleted_r=TRUE and 1 is returned. */
int dbox_file_open(struct dbox_file *file, bool *deleted_r);
-/* Open the file if uid or file_id is not 0, otherwise create it. */
-int dbox_file_open_or_create(struct dbox_file *file, bool *deleted_r);
/* Close the file handle from the file, but don't free it. */
void dbox_file_close(struct dbox_file *file);
-1 if I/O error. */
int dbox_file_get_mail_stream(struct dbox_file *file, uoff_t offset,
uoff_t *physical_size_r,
- struct istream **stream_r, bool *expunged_r);
+ struct istream **input_r);
/* Start seeking at the beginning of the file. */
void dbox_file_seek_rewind(struct dbox_file *file);
/* Seek to next message after current one. If there are no more messages,
corrupted, -1 if I/O error. */
int dbox_file_seek_next(struct dbox_file *file, uoff_t *offset_r, bool *last_r);
-/* Returns TRUE if mail_size bytes can be appended to the file. */
-bool dbox_file_can_append(struct dbox_file *file, uoff_t mail_size);
+/* Start appending to dbox file */
+struct dbox_file_append_context *dbox_file_append_init(struct dbox_file *file);
+/* Finish writing appended mails. */
+int dbox_file_append_commit(struct dbox_file_append_context **ctx);
+/* Truncate appended mails. */
+void dbox_file_append_rollback(struct dbox_file_append_context **ctx);
/* Get output stream for appending a new message. Returns 1 if ok, 0 if file
can't be appended to (old file version or corruption) or -1 if error. */
-int dbox_file_get_append_stream(struct dbox_file *file, uoff_t *append_offset_r,
- struct ostream **stream_r);
-/* Returns the next offset for append a message. dbox_file_get_append_stream()
- must have been called for this file already at least once. */
-uoff_t dbox_file_get_next_append_offset(struct dbox_file *file);
-/* Truncate file to append_offset */
-void dbox_file_cancel_append(struct dbox_file *file, uoff_t append_offset);
-/* Flush writes to dbox file. */
-int dbox_file_flush_append(struct dbox_file *file);
+int dbox_file_get_append_stream(struct dbox_file_append_context *ctx,
+ struct ostream **output_r);
+/* Flush output buffer. */
+int dbox_file_append_flush(struct dbox_file_append_context *ctx);
/* Read current message's metadata. Returns 1 if ok, 0 if metadata is
corrupted, -1 if I/O error. */
before start_offset is assumed to be valid and is simply copied. The file
is reopened afterwards. Returns 0 if ok, -1 if I/O error. */
int dbox_file_fix(struct dbox_file *file, uoff_t start_offset);
+/* Delete the given dbox file. Returns 1 if deleted, 0 if file wasn't found
+ or -1 if error. */
+int dbox_file_unlink(struct dbox_file *file);
/* Fill dbox_message_header with given size. */
void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr,
uoff_t message_size);
-const char *dbox_file_get_primary_path(struct dbox_file *file);
-const char *dbox_file_get_alt_path(struct dbox_file *file);
void dbox_file_set_syscall_error(struct dbox_file *file, const char *function);
void dbox_file_set_corrupted(struct dbox_file *file, const char *reason, ...)
ATTR_FORMAT(2, 3);
/* private: */
-char *dbox_generate_tmp_filename(void);
-int dbox_create_fd(struct dbox_storage *storage, const char *path);
+const char *dbox_generate_tmp_filename(void);
+void dbox_file_free(struct dbox_file *file);
int dbox_file_header_write(struct dbox_file *file, struct ostream *output);
int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r);
int dbox_file_metadata_skip_header(struct dbox_file *file);
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "str.h"
+#include "index-storage.h"
+#include "index-mail.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+#include "dbox-mail.h"
+
+#include <stdlib.h>
+
+struct mail *
+dbox_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers)
+{
+ struct dbox_mail *mail;
+ pool_t pool;
+
+ pool = pool_alloconly_create("mail", 1024);
+ mail = p_new(pool, struct dbox_mail, 1);
+ mail->imail.mail.pool = pool;
+
+ index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
+ return &mail->imail.mail.mail;
+}
+
+void dbox_mail_close(struct mail *_mail)
+{
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+
+ if (mail->open_file != NULL)
+ dbox_file_unref(&mail->open_file);
+ index_mail_close(_mail);
+}
+
+int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r)
+{
+ struct dbox_storage *storage =
+ (struct dbox_storage *)mail->imail.mail.mail.box->storage;
+ uoff_t offset, size;
+
+ if (storage->v.mail_open(mail, &offset, file_r) < 0)
+ return -1;
+
+ if (dbox_file_get_mail_stream(*file_r, offset, &size, NULL) <= 0)
+ return -1;
+ if (dbox_file_metadata_read(*file_r) <= 0)
+ return -1;
+ return 0;
+}
+
+int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
+{
+ struct index_mail *mail = (struct index_mail *)_mail;
+ struct index_mail_data *data = &mail->data;
+ struct istream *input;
+
+ if (index_mail_get_physical_size(_mail, size_r) == 0)
+ return 0;
+
+ if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
+ return -1;
+
+ i_assert(data->physical_size != (uoff_t)-1);
+ *size_r = data->physical_size;
+ return 0;
+}
+
+int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
+{
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+ struct index_mail_data *data = &mail->imail.data;
+ struct dbox_file *file;
+ const char *value;
+
+ if (index_mail_get_cached_virtual_size(&mail->imail, size_r))
+ return 0;
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ value = dbox_file_metadata_get(file, DBOX_METADATA_VIRTUAL_SIZE);
+ if (value == NULL)
+ return index_mail_get_virtual_size(_mail, size_r);
+
+ data->virtual_size = strtoul(value, NULL, 16);
+ *size_r = data->virtual_size;
+ return 0;
+}
+
+int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
+{
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+ struct index_mail_data *data = &mail->imail.data;
+ struct dbox_file *file;
+ const char *value;
+
+ if (index_mail_get_received_date(_mail, date_r) == 0)
+ return 0;
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ value = dbox_file_metadata_get(file, DBOX_METADATA_RECEIVED_TIME);
+ data->received_date = value == NULL ? 0 : strtoul(value, NULL, 16);
+ *date_r = data->received_date;
+ return 0;
+}
+
+int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
+{
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+ struct index_mail_data *data = &mail->imail.data;
+ struct dbox_file *file;
+ struct stat st;
+ const char *value;
+
+ if (index_mail_get_save_date(_mail, date_r) == 0)
+ return 0;
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ value = dbox_file_metadata_get(file, DBOX_METADATA_SAVE_TIME);
+ data->save_date = value == NULL ? 0 : strtoul(value, NULL, 16);
+
+ if (data->save_date == 0) {
+ /* missing / corrupted save time - use the file's ctime */
+ i_assert(dbox_file_is_open(file));
+ mail->imail.mail.stats_fstat_lookup_count++;
+ if (fstat(file->fd, &st) < 0) {
+ mail_storage_set_critical(_mail->box->storage,
+ "fstat(%s) failed: %m", file->cur_path);
+ return -1;
+ }
+ data->save_date = st.st_ctime;
+ }
+ *date_r = data->save_date;
+ return 0;
+}
+
+static int
+dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key,
+ enum index_cache_field cache_field,
+ const char **value_r)
+{
+ struct index_mail *imail = &mail->imail;
+ const struct mail_cache_field *cache_fields = imail->ibox->cache_fields;
+ struct dbox_file *file;
+ const char *value;
+ string_t *str;
+
+ str = str_new(imail->data_pool, 64);
+ if (mail_cache_lookup_field(imail->trans->cache_view, str,
+ imail->mail.mail.seq,
+ cache_fields[cache_field].idx) > 0) {
+ *value_r = str_c(str);
+ return 0;
+ }
+
+ if (dbox_mail_metadata_read(mail, &file) < 0)
+ return -1;
+
+ value = dbox_file_metadata_get(file, key);
+ if (value == NULL)
+ value = "";
+ index_mail_cache_add_idx(imail, cache_fields[cache_field].idx,
+ value, strlen(value)+1);
+ *value_r = value;
+ return 0;
+}
+
+int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
+ const char **value_r)
+{
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+
+ /* keep the UIDL in cache file, otherwise POP3 would open all
+ mail files and read the metadata. same for GUIDs if they're
+ used. */
+ switch (field) {
+ case MAIL_FETCH_UIDL_BACKEND:
+ return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
+ MAIL_CACHE_POP3_UIDL, value_r);
+ case MAIL_FETCH_GUID:
+ return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID,
+ MAIL_CACHE_GUID, value_r);
+ default:
+ break;
+ }
+
+ return index_mail_get_special(_mail, field, value_r);
+}
+
+int dbox_mail_get_stream(struct mail *_mail, struct message_size *hdr_size,
+ struct message_size *body_size,
+ struct istream **stream_r)
+{
+ struct dbox_storage *storage =
+ (struct dbox_storage *)_mail->box->storage;
+ struct dbox_mail *mail = (struct dbox_mail *)_mail;
+ struct index_mail_data *data = &mail->imail.data;
+ struct istream *input;
+ uoff_t offset, size;
+ int ret;
+
+ if (data->stream == NULL) {
+ if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0)
+ return -1;
+
+ ret = dbox_file_get_mail_stream(mail->open_file, offset,
+ &size, &input);
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+ dbox_file_set_corrupted(mail->open_file,
+ "uid=%u points to broken data at offset="
+ "%"PRIuUOFF_T, _mail->uid, offset);
+ return -1;
+ }
+ data->physical_size = size;
+ data->stream = input;
+ }
+
+ return index_mail_init_stream(&mail->imail, hdr_size, body_size,
+ stream_r);
+}
--- /dev/null
+#ifndef DBOX_MAIL_H
+#define DBOX_MAIL_H
+
+struct dbox_mail {
+ struct index_mail imail;
+
+ struct dbox_file *open_file;
+ uoff_t offset;
+};
+
+struct mail *
+dbox_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers);
+void dbox_mail_close(struct mail *mail);
+
+int dbox_mail_get_physical_size(struct mail *mail, uoff_t *size_r);
+int dbox_mail_get_virtual_size(struct mail *mail, uoff_t *size_r);
+int dbox_mail_get_received_date(struct mail *mail, time_t *date_r);
+int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r);
+int dbox_mail_get_special(struct mail *mail, enum mail_fetch_field field,
+ const char **value_r);
+int dbox_mail_get_stream(struct mail *_mail, struct message_size *hdr_size,
+ struct message_size *body_size,
+ struct istream **stream_r);
+
+int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "istream-crlf.h"
+#include "ostream.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "index-mail.h"
+#include "dbox-file.h"
+#include "dbox-save.h"
+
+void dbox_save_add_to_index(struct dbox_save_context *ctx)
+{
+ enum mail_flags save_flags;
+
+ save_flags = ctx->ctx.flags & ~MAIL_RECENT;
+ mail_index_append(ctx->trans, ctx->ctx.uid, &ctx->seq);
+ mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
+ save_flags);
+ if (ctx->ctx.keywords != NULL) {
+ mail_index_update_keywords(ctx->trans, ctx->seq,
+ MODIFY_REPLACE, ctx->ctx.keywords);
+ }
+ if (ctx->ctx.min_modseq != 0) {
+ mail_index_update_modseq(ctx->trans, ctx->seq,
+ ctx->ctx.min_modseq);
+ }
+}
+
+void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input)
+{
+ struct mail_save_context *_ctx = &ctx->ctx;
+ struct dbox_message_header dbox_msg_hdr;
+ struct istream *crlf_input;
+
+ dbox_save_add_to_index(ctx);
+
+ if (_ctx->dest_mail == NULL) {
+ if (ctx->mail == NULL)
+ ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
+ _ctx->dest_mail = ctx->mail;
+ }
+ mail_set_seq(_ctx->dest_mail, ctx->seq);
+
+ crlf_input = i_stream_create_lf(input);
+ ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input);
+ i_stream_unref(&crlf_input);
+
+ /* write a dummy header. it'll get rewritten when we're finished */
+ memset(&dbox_msg_hdr, 0, sizeof(dbox_msg_hdr));
+ o_stream_cork(ctx->cur_output);
+ if (o_stream_send(ctx->cur_output, &dbox_msg_hdr,
+ sizeof(dbox_msg_hdr)) < 0) {
+ mail_storage_set_critical(_ctx->transaction->box->storage,
+ "o_stream_send(%s) failed: %m",
+ ctx->cur_file->cur_path);
+ ctx->failed = TRUE;
+ }
+
+ if (_ctx->received_date == (time_t)-1)
+ _ctx->received_date = ioloop_time;
+}
+
+int dbox_save_continue(struct mail_save_context *_ctx)
+{
+ struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
+ struct mail_storage *storage = _ctx->transaction->box->storage;
+
+ if (ctx->failed)
+ return -1;
+
+ do {
+ if (o_stream_send_istream(ctx->cur_output, ctx->input) < 0) {
+ if (!mail_storage_set_error_from_errno(storage)) {
+ mail_storage_set_critical(storage,
+ "o_stream_send_istream(%s) failed: %m",
+ ctx->cur_file->cur_path);
+ }
+ ctx->failed = TRUE;
+ return -1;
+ }
+ index_mail_cache_parse_continue(_ctx->dest_mail);
+
+ /* both tee input readers may consume data from our primary
+ input stream. we'll have to make sure we don't return with
+ one of the streams still having data in them. */
+ } while (i_stream_read(ctx->input) > 0);
+ return 0;
+}
+
+void dbox_save_write_metadata(struct mail_save_context *ctx,
+ struct ostream *output,
+ const char *orig_mailbox_name,
+ uint8_t guid_128[MAIL_GUID_128_SIZE])
+{
+ struct dbox_metadata_header metadata_hdr;
+ const char *guid;
+ string_t *str;
+ uoff_t vsize;
+
+ memset(&metadata_hdr, 0, sizeof(metadata_hdr));
+ memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(metadata_hdr.magic_post));
+ o_stream_send(output, &metadata_hdr, sizeof(metadata_hdr));
+
+ str = t_str_new(256);
+ str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
+ (unsigned long)ctx->received_date);
+ str_printfa(str, "%c%lx\n", DBOX_METADATA_SAVE_TIME,
+ (unsigned long)ioloop_time);
+ if (mail_get_virtual_size(ctx->dest_mail, &vsize) < 0)
+ i_unreached();
+ str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
+ (unsigned long long)vsize);
+
+ guid = ctx->guid;
+ if (guid != NULL)
+ mail_generate_guid_128_hash(guid, guid_128);
+ else {
+ mail_generate_guid_128(guid_128);
+ guid = binary_to_hex(guid_128, MAIL_GUID_128_SIZE);
+ }
+ str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
+
+ if (orig_mailbox_name != NULL &&
+ strchr(orig_mailbox_name, '\r') == NULL &&
+ strchr(orig_mailbox_name, '\n') == NULL) {
+ /* save the original mailbox name so if mailbox indexes get
+ corrupted we can place at least some (hopefully most) of
+ the messages to correct mailboxes. */
+ str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
+ orig_mailbox_name);
+ }
+
+ str_append_c(str, '\n');
+ o_stream_send(output, str_data(str), str_len(str));
+}
--- /dev/null
+#ifndef DBOX_SAVE_H
+#define DBOX_SAVE_H
+
+struct dbox_save_context {
+ struct mail_save_context ctx;
+ struct mail_index_transaction *trans;
+
+ /* updated for each appended mail: */
+ uint32_t seq;
+ struct istream *input;
+ struct mail *mail;
+
+ struct dbox_file *cur_file;
+ struct ostream *cur_output;
+
+ unsigned int failed:1;
+ unsigned int finished:1;
+};
+
+void dbox_save_begin(struct dbox_save_context *ctx, struct istream *input);
+int dbox_save_continue(struct mail_save_context *_ctx);
+
+void dbox_save_write_metadata(struct mail_save_context *ctx,
+ struct ostream *output,
+ const char *orig_mailbox_name,
+ uint8_t guid_128_r[MAIL_GUID_128_SIZE]);
+
+void dbox_save_add_to_index(struct dbox_save_context *ctx);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#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"
+#include "index-storage.h"
+#include "dbox-storage.h"
+
+#include <stdio.h>
+
+void dbox_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
+ struct mailbox_list_settings *set)
+{
+ if (set->layout == NULL)
+ set->layout = MAILBOX_LIST_NAME_FS;
+ if (set->subscription_fname == NULL)
+ set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
+ if (set->dir_guid_fname == NULL)
+ set->dir_guid_fname = DBOX_DIR_GUID_FILE_NAME;
+ if (set->maildir_name == NULL)
+ set->maildir_name = DBOX_MAILDIR_NAME;
+ if (set->mailbox_dir_name == NULL)
+ set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME;
+}
+
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list)
+{
+ const char *path;
+
+ path = mailbox_list_get_path(list, NULL,
+ MAILBOX_LIST_PATH_TYPE_CONTROL);
+ path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL);
+ return mailbox_uidvalidity_next(path);
+}
+
+void dbox_notify_changes(struct mailbox *box)
+{
+ struct index_mailbox *ibox = (struct index_mailbox *)box;
+ const char *path;
+
+ if (box->notify_callback == NULL)
+ index_mailbox_check_remove_all(ibox);
+ else {
+ path = t_strdup_printf("%s/"DBOX_INDEX_PREFIX".log",
+ ibox->box.path);
+ index_mailbox_check_add(ibox, path);
+ }
+}
+
+static bool
+dbox_cleanup_if_exists(struct mailbox_list *list, const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0)
+ return FALSE;
+
+ /* check once in a while if there are temp files to clean up */
+ if (st.st_atime > st.st_ctime + DBOX_TMP_DELETE_SECS) {
+ /* there haven't been any changes to this directory since we
+ last checked it. */
+ } else if (st.st_atime < ioloop_time - DBOX_TMP_SCAN_SECS) {
+ /* time to scan */
+ const char *prefix =
+ mailbox_list_get_global_temp_prefix(list);
+
+ (void)unlink_old_files(path, prefix,
+ ioloop_time - DBOX_TMP_DELETE_SECS);
+ }
+ return TRUE;
+}
+
+int dbox_mailbox_open(struct mailbox *box)
+{
+ struct dbox_storage *storage = (struct dbox_storage *)box->storage;
+
+ if (box->input != NULL) {
+ mail_storage_set_critical(box->storage,
+ "dbox doesn't support streamed mailboxes");
+ return -1;
+ }
+
+ if (dbox_cleanup_if_exists(box->list, box->path)) {
+ return index_storage_mailbox_open(box);
+ } else if (errno == ENOENT) {
+ if (strcmp(box->name, "INBOX") == 0 &&
+ (box->list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
+ /* INBOX always exists, create it */
+ if (storage->v.mailbox_create_indexes(box, NULL) < 0)
+ return -1;
+ return box->opened ? 0 :
+ index_storage_mailbox_open(box);
+ }
+
+ mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
+ return -1;
+ } else if (errno == EACCES) {
+ mail_storage_set_critical(box->storage, "%s",
+ mail_error_eacces_msg("stat", box->path));
+ return -1;
+ } else {
+ mail_storage_set_critical(box->storage,
+ "stat(%s) failed: %m", box->path);
+ return -1;
+ }
+}
+
+static const char *
+dbox_get_alt_path(struct mailbox_list *list, const char *path)
+{
+ struct mail_storage *storage = list->ns->storage;
+ const char *root;
+ unsigned int len;
+
+ if (list->set.alt_dir == NULL ||
+ (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0)
+ return NULL;
+
+ root = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
+ len = strlen(root);
+ if (strncmp(path, root, len) != 0 && path[len] == '/') {
+ /* can't determine the alt path - shouldn't happen */
+ return NULL;
+ }
+ return t_strconcat(list->set.alt_dir, path + len, NULL);
+}
+
+int dbox_mailbox_create(struct mailbox *box,
+ const struct mailbox_update *update, bool directory)
+{
+ struct dbox_storage *storage = (struct dbox_storage *)box->storage;
+ const char *path, *alt_path, *origin;
+ struct stat st;
+
+ path = mailbox_list_get_path(box->list, box->name,
+ directory ? MAILBOX_LIST_PATH_TYPE_DIR :
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ if (stat(path, &st) == 0) {
+ mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+ "Mailbox already exists");
+ return -1;
+ }
+
+ if (directory) {
+ mode_t mode;
+ gid_t gid;
+
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode,
+ &gid, &origin);
+ if (mkdir_parents_chgrp(path, mode, gid, origin) == 0)
+ return 0;
+ else if (errno == EEXIST) {
+ mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+ "Mailbox already exists");
+ } else if (!mail_storage_set_error_from_errno(box->storage)) {
+ mail_storage_set_critical(box->storage,
+ "mkdir(%s) failed: %m", path);
+ }
+ return -1;
+ }
+
+ /* make sure the alt path doesn't exist yet. it shouldn't (except with
+ race conditions with RENAME/DELETE), but if something crashed and
+ left it lying around we don't want to start overwriting files in
+ it. */
+ alt_path = dbox_get_alt_path(box->list, path);
+ if (alt_path != NULL && stat(alt_path, &st) == 0) {
+ mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+ "Mailbox already exists");
+ return -1;
+ }
+
+ return storage->v.mailbox_create_indexes(box, update);
+}
+
+int dbox_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 *path, *maildir_path;
+ struct stat st, st2;
+ 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 */
+ path = t_strconcat(dir, "/", fname, NULL);
+ if (stat(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') {
+ /* 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 {
+ /* non-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;
+ }
+ if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) {
+ /* make sure it's a selectable mailbox */
+ maildir_path = t_strconcat(path, "/",
+ ctx->list->set.maildir_name, NULL);
+ if (stat(maildir_path, &st2) < 0 || !S_ISDIR(st2.st_mode))
+ *flags |= MAILBOX_NOSELECT;
+ if (st.st_nlink == 3 && *ctx->list->set.maildir_name != '\0') {
+ /* now we know what link count 3 means. */
+ if ((*flags & MAILBOX_NOSELECT) != 0)
+ *flags |= MAILBOX_CHILDREN;
+ else
+ *flags |= MAILBOX_NOCHILDREN;
+ }
+ }
+ return ret;
+}
+
+static int
+dbox_list_rename_get_alt_paths(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname,
+ enum mailbox_list_path_type path_type,
+ const char **oldpath_r, const char **newpath_r)
+{
+ const char *path;
+
+ path = mailbox_list_get_path(oldlist, oldname, path_type);
+ *oldpath_r = dbox_get_alt_path(oldlist, path);
+ if (*oldpath_r == NULL)
+ return 0;
+
+ path = mailbox_list_get_path(newlist, newname, path_type);
+ *newpath_r = dbox_get_alt_path(newlist, path);
+ if (*newpath_r == NULL) {
+ /* destination dbox storage doesn't have alt-path defined.
+ we can't do the rename easily. */
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailboxes across specified storages.");
+ return -1;
+ }
+ return 1;
+}
+
+int dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname)
+{
+ const char *alt_oldpath, *alt_newpath;
+ struct stat st;
+ int ret;
+
+ ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_DIR,
+ &alt_oldpath, &alt_newpath);
+ if (ret <= 0)
+ return ret;
+
+ if (stat(alt_newpath, &st) == 0) {
+ /* race condition or a directory left there lying around?
+ safest to just report error. */
+ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
+ "Target mailbox already exists");
+ return -1;
+ } else if (errno != ENOENT) {
+ mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
+ alt_newpath);
+ return -1;
+ }
+ return 0;
+}
+
+int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
+{
+ enum mailbox_list_path_type path_type;
+ const char *alt_oldpath, *alt_newpath, *path;
+ int ret;
+
+ path_type = rename_children ? MAILBOX_LIST_PATH_TYPE_DIR :
+ MAILBOX_LIST_PATH_TYPE_MAILBOX;
+ ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
+ path_type, &alt_oldpath,
+ &alt_newpath);
+ if (ret <= 0)
+ return ret;
+
+ if (rename(alt_oldpath, alt_newpath) == 0) {
+ /* ok */
+ if (!rename_children) {
+ path = mailbox_list_get_path(oldlist, oldname,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ if (rmdir(path) < 0 &&
+ errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(oldlist,
+ "rmdir(%s) failed: %m", path);
+ }
+ }
+ } else if (errno != ENOENT) {
+ /* renaming is done already, so just log the error */
+ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
+ alt_oldpath, alt_newpath);
+ }
+ 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;
+}
+
--- /dev/null
+#ifndef DBOX_STORAGE_H
+#define DBOX_STORAGE_H
+
+#include "mail-storage-private.h"
+
+struct dbox_file;
+struct dbox_mail;
+
+#define DBOX_SUBSCRIPTION_FILE_NAME "subscriptions"
+#define DBOX_UIDVALIDITY_FILE_NAME "dovecot-uidvalidity"
+#define DBOX_INDEX_PREFIX "dovecot.index"
+#define DBOX_DIR_GUID_FILE_NAME "dbox-GUID"
+
+#define DBOX_MAILBOX_DIR_NAME "mailboxes"
+#define DBOX_TRASH_DIR_NAME "trash"
+#define DBOX_MAILDIR_NAME "dbox-Mails"
+
+/* How often to scan for stale temp files (based on dir's atime) */
+#define DBOX_TMP_SCAN_SECS (8*60*60)
+/* Delete temp files having ctime older than this. */
+#define DBOX_TMP_DELETE_SECS (36*60*60)
+
+struct dbox_storage_vfuncs {
+ /* dbox file has zero references now. it should be either freed or
+ left open in case it's accessed again soon */
+ void (*file_unrefed)(struct dbox_file *file);
+ /* create a new file using the same permissions as file.
+ if parents=TRUE, create the directory if necessary */
+ int (*file_create_fd)(struct dbox_file *file, const char *path,
+ bool parents);
+ /* open the mail and return its file/offset */
+ int (*mail_open)(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r);
+ /* create/update mailbox indexes */
+ int (*mailbox_create_indexes)(struct mailbox *box,
+ const struct mailbox_update *update);
+};
+
+struct dbox_storage {
+ struct mail_storage storage;
+ struct dbox_storage_vfuncs v;
+
+ unsigned int files_corrupted:1;
+};
+
+void dbox_storage_get_list_settings(const struct mail_namespace *ns,
+ struct mailbox_list_settings *set);
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list);
+void dbox_notify_changes(struct mailbox *box);
+int dbox_mailbox_open(struct mailbox *box);
+int dbox_mailbox_create(struct mailbox *box,
+ const struct mailbox_update *update, bool directory);
+int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+ const char *dir, const char *fname,
+ const char *mailbox_name,
+ enum mailbox_list_file_type type,
+ enum mailbox_info_flags *flags);
+int dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname);
+int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ 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
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "index-storage.h"
+#include "dbox-storage.h"
+#include "dbox-sync-rebuild.h"
+
+static void
+dbox_sync_index_copy_cache(struct dbox_sync_rebuild_context *ctx,
+ struct mail_index_view *view,
+ uint32_t old_seq, uint32_t new_seq)
+{
+ struct mail_index_map *map;
+ const void *data;
+ uint32_t reset_id;
+ bool expunged;
+
+ if (ctx->cache_ext_id == (uint32_t)-1)
+ return;
+
+ mail_index_lookup_ext_full(view, old_seq, ctx->cache_ext_id,
+ &map, &data, &expunged);
+ if (expunged)
+ return;
+
+ if (!mail_index_ext_get_reset_id(view, map, ctx->cache_ext_id,
+ &reset_id) || reset_id == 0)
+ return;
+
+ if (!ctx->cache_used) {
+ /* set reset id */
+ ctx->cache_used = TRUE;
+ ctx->cache_reset_id = reset_id;
+ mail_index_ext_reset(ctx->trans, ctx->cache_ext_id,
+ ctx->cache_reset_id, TRUE);
+ }
+ if (ctx->cache_reset_id == reset_id) {
+ mail_index_update_ext(ctx->trans, new_seq,
+ ctx->cache_ext_id, data, NULL);
+ }
+}
+
+static void
+dbox_sync_index_copy_from_old(struct dbox_sync_rebuild_context *ctx,
+ struct mail_index_view *view,
+ uint32_t old_seq, uint32_t new_seq)
+{
+ struct mail_index *index = mail_index_view_get_index(view);
+ const struct mail_index_record *rec;
+ ARRAY_TYPE(keyword_indexes) old_keywords;
+ struct mail_keywords *kw;
+
+ /* copy flags */
+ rec = mail_index_lookup(view, old_seq);
+ mail_index_update_flags(ctx->trans, new_seq,
+ MODIFY_REPLACE, rec->flags);
+
+ /* copy keywords */
+ t_array_init(&old_keywords, 32);
+ mail_index_lookup_keywords(view, old_seq, &old_keywords);
+ kw = mail_index_keywords_create_from_indexes(index, &old_keywords);
+ mail_index_update_keywords(ctx->trans, new_seq, MODIFY_REPLACE, kw);
+ mail_index_keywords_unref(&kw);
+
+ dbox_sync_index_copy_cache(ctx, view, old_seq, new_seq);
+}
+
+void dbox_sync_rebuild_index_metadata(struct dbox_sync_rebuild_context *ctx,
+ uint32_t new_seq, uint32_t uid)
+{
+ uint32_t old_seq;
+
+ if (mail_index_lookup_seq(ctx->view, uid, &old_seq)) {
+ /* the message exists in the old index.
+ copy the metadata from it. */
+ dbox_sync_index_copy_from_old(ctx, ctx->view, old_seq, new_seq);
+ } else if (ctx->backup_view != NULL &&
+ mail_index_lookup_seq(ctx->backup_view, uid, &old_seq)) {
+ /* copy the metadata from backup index. */
+ dbox_sync_index_copy_from_old(ctx, ctx->backup_view,
+ old_seq, new_seq);
+ }
+}
+
+struct dbox_sync_rebuild_context *
+dbox_sync_index_rebuild_init(struct index_mailbox *ibox,
+ struct mail_index_view *view,
+ struct mail_index_transaction *trans)
+{
+ struct mailbox *box = &ibox->box;
+ struct dbox_sync_rebuild_context *ctx;
+ const char *index_dir;
+ enum mail_index_open_flags open_flags = MAIL_INDEX_OPEN_FLAG_READONLY;
+
+ ctx = i_new(struct dbox_sync_rebuild_context, 1);
+ ctx->ibox = ibox;
+ ctx->view = view;
+ ctx->trans = trans;
+ mail_index_reset(ctx->trans);
+ index_mailbox_reset_uidvalidity(ibox);
+ mail_index_ext_lookup(ibox->index, "cache", &ctx->cache_ext_id);
+
+ /* if backup index file exists, try to use it */
+ index_dir = mailbox_list_get_path(box->list, box->name,
+ MAILBOX_LIST_PATH_TYPE_INDEX);
+ ctx->backup_index =
+ mail_index_alloc(index_dir, DBOX_INDEX_PREFIX".backup");
+
+#ifndef MMAP_CONFLICTS_WRITE
+ if (box->storage->set->mmap_disable)
+#endif
+ open_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
+ if (mail_index_open(ctx->backup_index, open_flags,
+ box->storage->set->parsed_lock_method) <= 0)
+ mail_index_free(&ctx->backup_index);
+ else
+ ctx->backup_view = mail_index_view_open(ctx->backup_index);
+ return ctx;
+}
+
+void dbox_sync_index_rebuild_deinit(struct dbox_sync_rebuild_context **_ctx)
+{
+ struct dbox_sync_rebuild_context *ctx = *_ctx;
+
+ *_ctx = NULL;
+ if (ctx->backup_index != NULL) {
+ mail_index_view_close(&ctx->backup_view);
+ mail_index_free(&ctx->backup_index);
+ }
+ i_free(ctx);
+}
--- /dev/null
+#ifndef DBOX_SYNC_REBUILD_H
+#define DBOX_SYNC_REBUILD_H
+
+struct dbox_sync_rebuild_context {
+ struct index_mailbox *ibox;
+
+ struct mail_index_view *view;
+ struct mail_index_transaction *trans;
+ uint32_t cache_ext_id;
+ uint32_t cache_reset_id;
+
+ struct mail_index *backup_index;
+ struct mail_index_view *backup_view;
+
+ unsigned int cache_used:1;
+};
+
+struct dbox_sync_rebuild_context *
+dbox_sync_index_rebuild_init(struct index_mailbox *mbox,
+ struct mail_index_view *view,
+ struct mail_index_transaction *trans);
+void dbox_sync_index_rebuild_deinit(struct dbox_sync_rebuild_context **ctx);
+
+void dbox_sync_rebuild_index_metadata(struct dbox_sync_rebuild_context *ctx,
+ uint32_t new_seq, uint32_t uid);
+
+#endif
--- /dev/null
+noinst_LTLIBRARIES = libstorage_dbox_multi.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index \
+ -I$(top_srcdir)/src/lib-storage/index/dbox-common
+
+libstorage_dbox_multi_la_SOURCES = \
+ mdbox-file.c \
+ mdbox-file-purge.c \
+ mdbox-mail.c \
+ mdbox-map.c \
+ mdbox-save.c \
+ mdbox-settings.c \
+ mdbox-sync.c \
+ mdbox-storage.c \
+ mdbox-storage-rebuild.c
+
+headers = \
+ mdbox-file.h \
+ mdbox-map.h \
+ mdbox-map-private.h \
+ mdbox-settings.h \
+ mdbox-storage.h \
+ mdbox-storage-rebuild.h \
+ mdbox-sync.h
+
+if INSTALL_HEADERS
+ pkginc_libdir=$(pkgincludedir)
+ pkginc_lib_HEADERS = $(headers)
+else
+ noinst_HEADERS = $(headers)
+endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "mdbox-storage.h"
+#include "mdbox-file.h"
+#include "mdbox-map.h"
+#include "mdbox-sync.h"
+
+#include <stdlib.h>
+
+struct dbox_mail_move {
+ struct dbox_file *file;
+ uint32_t offset;
+};
+ARRAY_DEFINE_TYPE(dbox_mail_move, struct dbox_mail_move);
+
+static int mdbox_map_file_msg_offset_cmp(const void *p1, const void *p2)
+{
+ const struct dbox_map_file_msg *m1 = p1, *m2 = p2;
+
+ if (m1->offset < m2->offset)
+ return -1;
+ else if (m1->offset > m2->offset)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+mdbox_file_copy_metadata(struct dbox_file *file, struct ostream *output)
+{
+ struct dbox_metadata_header meta_hdr;
+ const char *line;
+ const unsigned char *data;
+ size_t size;
+ int ret;
+
+ ret = i_stream_read_data(file->input, &data, &size,
+ sizeof(meta_hdr));
+ if (ret <= 0) {
+ i_assert(ret == -1);
+ if (file->input->stream_errno == 0) {
+ dbox_file_set_corrupted(file, "missing metadata");
+ return 0;
+ }
+ mail_storage_set_critical(&file->storage->storage,
+ "read(%s) failed: %m", file->cur_path);
+ return -1;
+ }
+
+ memcpy(&meta_hdr, data, sizeof(meta_hdr));
+ if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
+ sizeof(meta_hdr.magic_post)) != 0) {
+ dbox_file_set_corrupted(file, "invalid metadata magic");
+ return 0;
+ }
+ i_stream_skip(file->input, sizeof(meta_hdr));
+ if (output != NULL)
+ o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
+ while ((line = i_stream_read_next_line(file->input)) != NULL) {
+ if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+ /* end of metadata */
+ break;
+ }
+ if (output != NULL) {
+ o_stream_send_str(output, line);
+ o_stream_send(output, "\n", 1);
+ }
+ }
+ if (line == NULL) {
+ dbox_file_set_corrupted(file, "missing end-of-metadata line");
+ return 0;
+ }
+ if (output != NULL)
+ o_stream_send(output, "\n", 1);
+ return 1;
+}
+
+int mdbox_file_purge(struct dbox_file *file)
+{
+ struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage;
+ struct dbox_file_append_context *out_file_append;
+ struct stat st;
+ struct istream *input;
+ struct ostream *output = NULL;
+ struct dbox_map_append_context *append_ctx;
+ ARRAY_TYPE(dbox_map_file_msg) msgs_arr;
+ const struct dbox_map_file_msg *msgs;
+ ARRAY_TYPE(seq_range) expunged_map_uids;
+ ARRAY_TYPE(uint32_t) copied_map_uids;
+ unsigned int i, count;
+ uoff_t offset, physical_size, msg_size;
+ int ret;
+
+ if ((ret = dbox_file_try_lock(file)) <= 0)
+ return ret;
+
+ /* make sure the file still exists. another process may have already
+ deleted it. */
+ if (stat(file->cur_path, &st) < 0) {
+ dbox_file_unlock(file);
+ if (errno == ENOENT)
+ return 0;
+
+ mail_storage_set_critical(&file->storage->storage,
+ "stat(%s) failed: %m", file->cur_path);
+ return -1;
+ }
+
+ i_array_init(&msgs_arr, 128);
+ if (dbox_map_get_file_msgs(dstorage->map,
+ ((struct mdbox_file *)file)->file_id,
+ &msgs_arr) < 0) {
+ array_free(&msgs_arr);
+ dbox_file_unlock(file);
+ return -1;
+ }
+ /* sort messages by their offset */
+ array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp);
+
+ msgs = array_get(&msgs_arr, &count);
+ append_ctx = dbox_map_append_begin(dstorage->map);
+ i_array_init(&copied_map_uids, I_MIN(count, 1));
+ i_array_init(&expunged_map_uids, I_MIN(count, 1));
+ offset = file->file_header_size;
+ for (i = 0; i < count; i++) {
+ if ((ret = dbox_file_get_mail_stream(file, offset,
+ &physical_size,
+ NULL)) <= 0)
+ break;
+ msg_size = file->msg_header_size + physical_size;
+
+ if (msgs[i].offset != offset) {
+ /* map doesn't match file's actual contents */
+ dbox_file_set_corrupted(file,
+ "purging found mismatched offsets "
+ "(%"PRIuUOFF_T" vs %u, %u/%u)",
+ offset, msgs[i].offset, i, count);
+ ret = 0;
+ break;
+ }
+
+ if (msgs[i].refcount == 0) {
+ seq_range_array_add(&expunged_map_uids, 0,
+ msgs[i].map_uid);
+ output = NULL;
+ } else {
+ /* non-expunged message. write it to output file. */
+ if (dbox_map_append_next(append_ctx, physical_size,
+ &out_file_append, &output) < 0) {
+ ret = -1;
+ break;
+ }
+ i_assert(file != out_file_append->file);
+
+ i_stream_seek(file->input, offset);
+ input = i_stream_create_limit(file->input, msg_size);
+ ret = o_stream_send_istream(output, input);
+ if (input->stream_errno != 0) {
+ errno = input->stream_errno;
+ mail_storage_set_critical(&file->storage->storage,
+ "read(%s) failed: %m",
+ file->cur_path);
+ i_stream_unref(&input);
+ break;
+ }
+ i_stream_unref(&input);
+ if (output->stream_errno != 0) {
+ errno = output->stream_errno;
+ mail_storage_set_critical(&file->storage->storage,
+ "write(%s) failed: %m",
+ out_file_append->file->cur_path);
+ break;
+ }
+ i_assert(ret == (off_t)msg_size);
+ }
+
+ /* copy/skip metadata */
+ i_stream_seek(file->input, offset + msg_size);
+ if ((ret = mdbox_file_copy_metadata(file, output)) <= 0)
+ break;
+
+ if (output != NULL) {
+ dbox_map_append_finish(append_ctx);
+ array_append(&copied_map_uids, &msgs[i].map_uid, 1);
+ }
+ offset = file->input->v_offset;
+ }
+ if (offset != (uoff_t)st.st_size && ret > 0) {
+ /* file has more messages than what map tells us */
+ dbox_file_set_corrupted(file,
+ "more messages available than in map "
+ "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
+ ret = 0;
+ }
+ array_free(&msgs_arr); msgs = NULL;
+
+ if (ret <= 0) {
+ dbox_map_append_free(&append_ctx);
+ dbox_file_unlock(file);
+ ret = -1;
+ } else if (array_count(&copied_map_uids) == 0) {
+ /* everything expunged in this file, unlink it */
+ ret = dbox_file_unlink(file);
+ dbox_map_append_free(&append_ctx);
+ } else {
+ /* assign new file_id + offset to moved messages */
+ if (dbox_map_append_move(append_ctx, &copied_map_uids,
+ &expunged_map_uids) < 0 ||
+ dbox_map_append_commit(append_ctx) < 0) {
+ dbox_file_unlock(file);
+ ret = -1;
+ } else {
+ ret = 1;
+ (void)dbox_file_unlink(file);
+ }
+ dbox_map_append_free(&append_ctx);
+ }
+ array_free(&copied_map_uids);
+ array_free(&expunged_map_uids);
+ return ret;
+}
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "hex-dec.h"
+#include "hex-binary.h"
+#include "hostpid.h"
+#include "istream.h"
+#include "ostream.h"
+#include "file-lock.h"
+#include "mkdir-parents.h"
+#include "fdatasync-path.h"
+#include "eacces-error.h"
+#include "str.h"
+#include "mdbox-storage.h"
+#include "mdbox-map-private.h"
+#include "mdbox-file.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+static struct mdbox_file *
+mdbox_find_and_move_open_file(struct mdbox_storage *storage, uint32_t file_id)
+{
+ struct mdbox_file *const *files, *file;
+ unsigned int i, count;
+
+ files = array_get(&storage->open_files, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->file_id == file_id) {
+ /* move to last in the array */
+ file = files[i];
+ array_delete(&storage->open_files, i, 1);
+ array_append(&storage->open_files, &file, 1);
+ return file;
+ }
+ }
+ return NULL;
+}
+
+void mdbox_files_free(struct mdbox_storage *storage)
+{
+ struct mdbox_file *const *files;
+ unsigned int i, count;
+
+ files = array_get(&storage->open_files, &count);
+ for (i = 0; i < count; i++)
+ dbox_file_free(&files[i]->file);
+ array_clear(&storage->open_files);
+}
+
+void mdbox_files_sync_input(struct mdbox_storage *storage)
+{
+ struct mdbox_file *const *files;
+ unsigned int i, count;
+
+ files = array_get(&storage->open_files, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->file.input != NULL)
+ i_stream_sync(files[i]->file.input);
+ }
+}
+
+static void
+mdbox_close_open_files(struct mdbox_storage *storage, unsigned int close_count)
+{
+ struct mdbox_file *const *files;
+ unsigned int i, count;
+
+ files = array_get(&storage->open_files, &count);
+ for (i = 0; i < count;) {
+ if (files[i]->file.refcount == 0) {
+ dbox_file_free(&files[i]->file);
+ array_delete(&storage->open_files, i, 1);
+
+ if (--close_count == 0)
+ break;
+
+ files = array_get(&storage->open_files, &count);
+ } else {
+ i++;
+ }
+ }
+}
+
+static void mdbox_file_init_paths(struct mdbox_file *file, const char *fname)
+{
+ i_free(file->file.primary_path);
+ i_free(file->file.alt_path);
+ file->file.primary_path =
+ i_strdup_printf("%s/%s", file->storage->storage_dir, fname);
+ if (file->storage->alt_storage_dir != NULL) {
+ file->file.alt_path =
+ i_strdup_printf("%s/%s", file->storage->alt_storage_dir,
+ fname);
+ }
+ file->file.cur_path = file->file.primary_path;
+}
+
+static int mdbox_file_create(struct dbox_file *file)
+{
+ int ret;
+
+ file->fd = file->storage->v.
+ file_create_fd(file, file->primary_path, FALSE);
+
+ /* even though we don't need it locked while writing to it, by the
+ time we rename() it it needs to be locked. so we might as well do
+ it here. */
+ if ((ret = dbox_file_try_lock(file)) <= 0) {
+ if (ret < 0)
+ return -1;
+ mail_storage_set_critical(&file->storage->storage,
+ "dbox: Couldn't lock created file: %s",
+ file->cur_path);
+ return -1;
+ }
+ return 0;
+}
+
+struct dbox_file *
+mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id)
+{
+ struct mdbox_file *file;
+ const char *fname;
+ unsigned int count;
+
+ file = file_id == 0 ? NULL :
+ mdbox_find_and_move_open_file(storage, file_id);
+ if (file != NULL) {
+ file->file.refcount++;
+ return &file->file;
+ }
+
+ count = array_count(&storage->open_files);
+ if (count > storage->set->mdbox_max_open_files) {
+ mdbox_close_open_files(storage, count -
+ storage->set->mdbox_max_open_files);
+ }
+
+ file = i_new(struct mdbox_file, 1);
+ file->storage = storage;
+ file->file.storage = &storage->storage;
+ file->file_id = file_id;
+ fname = file_id == 0 ? dbox_generate_tmp_filename() :
+ t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id);
+ mdbox_file_init_paths(file, fname);
+ dbox_file_init(&file->file);
+
+ if (file_id != 0)
+ array_append(&storage->open_files, &file, 1);
+ else
+ (void)mdbox_file_create(&file->file);
+ return &file->file;
+}
+
+int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id)
+{
+ const char *old_path;
+ const char *new_fname, *new_path;
+
+ i_assert(file->file_id == 0);
+ i_assert(file_id != 0);
+
+ old_path = file->file.cur_path;
+ new_fname = t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id);
+ new_path = t_strdup_printf("%s/%s", file->storage->storage_dir,
+ new_fname);
+ if (rename(old_path, new_path) < 0) {
+ mail_storage_set_critical(&file->storage->storage.storage,
+ "rename(%s, %s) failed: %m",
+ old_path, new_path);
+ return -1;
+ }
+ mdbox_file_init_paths(file, new_fname);
+ file->file_id = file_id;
+ array_append(&file->storage->open_files, &file, 1);
+ return 0;
+}
+
+void mdbox_file_unrefed(struct dbox_file *file)
+{
+ struct mdbox_file *mfile = (struct mdbox_file *)file;
+ struct mdbox_file *const *files, *oldest_file;
+ unsigned int i, count;
+
+ /* don't cache metadata seeks while file isn't being referenced */
+ file->metadata_read_offset = (uoff_t)-1;
+
+ if (mfile->file_id != 0) {
+ files = array_get(&mfile->storage->open_files, &count);
+ if (!file->deleted &&
+ count <= mfile->storage->set->mdbox_max_open_files) {
+ /* we can leave this file open for now */
+ return;
+ }
+
+ /* close the oldest file with refcount=0 */
+ for (i = 0; i < count; i++) {
+ if (files[i]->file.refcount == 0)
+ break;
+ }
+ oldest_file = files[i];
+ array_delete(&mfile->storage->open_files, i, 1);
+ if (oldest_file != mfile) {
+ dbox_file_free(&oldest_file->file);
+ return;
+ }
+ /* have to close ourself */
+ }
+ dbox_file_free(file);
+}
+
+int mdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
+{
+ struct mdbox_file *mfile = (struct mdbox_file *)file;
+ struct dbox_map *map = mfile->storage->map;
+ mode_t old_mask;
+ const char *p, *dir;
+ int fd;
+
+ old_mask = umask(0666 & ~map->create_mode);
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ umask(old_mask);
+ if (fd == -1 && errno == ENOENT && parents &&
+ (p = strrchr(path, '/')) != NULL) {
+ dir = t_strdup_until(path, p);
+ if (mkdir_parents_chgrp(dir, map->create_dir_mode,
+ map->create_gid,
+ map->create_gid_origin) < 0) {
+ mail_storage_set_critical(&file->storage->storage,
+ "mkdir_parents(%s) failed: %m", dir);
+ return -1;
+ }
+ /* try again */
+ old_mask = umask(0666 & ~map->create_mode);
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ umask(old_mask);
+ }
+ if (fd == -1) {
+ mail_storage_set_critical(&file->storage->storage,
+ "open(%s, O_CREAT) failed: %m", path);
+ } else if (map->create_gid == (gid_t)-1) {
+ /* no group change */
+ } else if (fchown(fd, (uid_t)-1, map->create_gid) < 0) {
+ if (errno == EPERM) {
+ mail_storage_set_critical(&file->storage->storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ map->create_gid,
+ map->create_gid_origin));
+ } else {
+ mail_storage_set_critical(&file->storage->storage,
+ "fchown(%s, -1, %ld) failed: %m",
+ path, (long)map->create_gid);
+ }
+ /* continue anyway */
+ }
+ return fd;
+}
--- /dev/null
+#ifndef MDBOX_FILE_H
+#define MDBOX_FILE_H
+
+#include "dbox-file.h"
+
+struct mdbox_file {
+ struct dbox_file file;
+ struct mdbox_storage *storage;
+
+ uint32_t file_id;
+};
+
+struct dbox_file *
+mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id);
+
+/* Assign file ID for a newly created file. */
+int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id);
+
+void mdbox_file_unrefed(struct dbox_file *file);
+int mdbox_file_create_fd(struct dbox_file *file, const char *path,
+ bool parents);
+
+void mdbox_files_free(struct mdbox_storage *storage);
+void mdbox_files_sync_input(struct mdbox_storage *storage);
+int mdbox_file_purge(struct dbox_file *file);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "str.h"
+#include "index-mail.h"
+#include "dbox-mail.h"
+#include "mdbox-storage.h"
+#include "mdbox-map.h"
+#include "mdbox-file.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view,
+ uint32_t seq, uint32_t *map_uid_r)
+{
+ const struct mdbox_mail_index_record *dbox_rec;
+ struct mdbox_index_header hdr;
+ const void *data;
+ uint32_t uid, cur_map_uid_validity;
+ bool expunged;
+
+ mail_index_lookup_ext(view, seq, mbox->ext_id, &data, &expunged);
+ dbox_rec = data;
+ if (dbox_rec == NULL || dbox_rec->map_uid == 0) {
+ mail_index_lookup_uid(view, seq, &uid);
+ mail_storage_set_critical(&mbox->storage->storage.storage,
+ "dbox %s: map uid lost for uid %u",
+ mbox->ibox.box.path, uid);
+ mbox->storage->storage.files_corrupted = TRUE;
+ return -1;
+ }
+
+ if (mbox->map_uid_validity == 0) {
+ if (mdbox_read_header(mbox, &hdr) < 0) {
+ mbox->storage->storage.files_corrupted = TRUE;
+ return -1;
+ }
+ mbox->map_uid_validity = hdr.map_uid_validity;
+ }
+ if (dbox_map_open(mbox->storage->map, TRUE) < 0)
+ return -1;
+
+ cur_map_uid_validity = dbox_map_get_uid_validity(mbox->storage->map);
+ if (cur_map_uid_validity != mbox->map_uid_validity) {
+ mail_storage_set_critical(&mbox->storage->storage.storage,
+ "dbox %s: map uidvalidity mismatch (%u vs %u)",
+ mbox->ibox.box.path, mbox->map_uid_validity,
+ cur_map_uid_validity);
+ mbox->storage->storage.files_corrupted = TRUE;
+ return -1;
+ }
+ *map_uid_r = dbox_rec->map_uid;
+ return 0;
+}
+
+static void dbox_mail_set_expunged(struct dbox_mail *mail, uint32_t map_uid)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->imail.ibox;
+ struct mail *_mail = &mail->imail.mail.mail;
+
+ (void)mail_index_refresh(mbox->ibox.index);
+ if (mail_index_is_expunged(mbox->ibox.view, _mail->seq)) {
+ mail_set_expunged(_mail);
+ return;
+ }
+
+ dbox_map_set_corrupted(mbox->storage->map,
+ "Unexpectedly lost uid=%u map_uid=%u",
+ _mail->uid, map_uid);
+ mbox->storage->storage.files_corrupted = TRUE;
+}
+
+static int dbox_mail_open_init(struct dbox_mail *mail, uint32_t map_uid)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->imail.ibox;
+ uint32_t file_id;
+ int ret;
+
+ if ((ret = dbox_map_lookup(mbox->storage->map, map_uid,
+ &file_id, &mail->offset)) <= 0) {
+ if (ret < 0)
+ return -1;
+
+ /* map_uid doesn't exist anymore. either it
+ got just expunged or the map index is
+ corrupted. */
+ dbox_mail_set_expunged(mail, map_uid);
+ return -1;
+ } else {
+ mail->open_file = mdbox_file_init(mbox->storage, file_id);
+ }
+ return 0;
+}
+
+int mdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->imail.ibox;
+ struct mail *_mail = &mail->imail.mail.mail;
+ uint32_t prev_file_id = 0, map_uid = 0;
+ bool deleted;
+
+ if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
+ mail_set_aborted(_mail);
+ return -1;
+ }
+
+ do {
+ if (mail->open_file != NULL) {
+ /* already open */
+ } else if (_mail->uid != 0) {
+ if (mdbox_mail_lookup(mbox, mbox->ibox.view, _mail->seq,
+ &map_uid) < 0)
+ return -1;
+ if (dbox_mail_open_init(mail, map_uid) < 0)
+ return -1;
+ } else {
+ /* mail is being saved in this transaction */
+ mail->open_file =
+ mdbox_save_file_get_file(_mail->transaction,
+ _mail->seq,
+ &mail->offset);
+ mail->open_file->refcount++;
+ break;
+ }
+
+ if (!dbox_file_is_open(mail->open_file))
+ mail->imail.mail.stats_open_lookup_count++;
+ if (dbox_file_open(mail->open_file, &deleted) <= 0)
+ return -1;
+ if (deleted) {
+ /* either it's expunged now or moved to another file. */
+ struct mdbox_file *mfile =
+ (struct mdbox_file *)mail->open_file;
+
+ if (mfile->file_id == prev_file_id) {
+ dbox_mail_set_expunged(mail, map_uid);
+ return -1;
+ }
+ prev_file_id = mfile->file_id;
+ if (dbox_map_refresh(mbox->storage->map) < 0)
+ return -1;
+ dbox_file_unref(&mail->open_file);
+ }
+ } while (mail->open_file == NULL);
+
+ *file_r = mail->open_file;
+ *offset_r = mail->offset;
+ return 0;
+}
+
+static int mdbox_mail_get_save_date(struct mail *mail, time_t *date_r)
+{
+ struct mdbox_mailbox *mbox =
+ (struct mdbox_mailbox *)mail->transaction->box;
+ const struct mdbox_mail_index_record *dbox_rec;
+ const void *data;
+ bool expunged;
+
+ mail_index_lookup_ext(mbox->ibox.view, mail->seq,
+ mbox->ext_id, &data, &expunged);
+ dbox_rec = data;
+ if (dbox_rec == NULL || dbox_rec->map_uid == 0) {
+ /* lost for some reason, use fallback */
+ return dbox_mail_get_save_date(mail, date_r);
+ }
+
+ *date_r = dbox_rec->save_date;
+ return TRUE;
+}
+
+struct mail_vfuncs mdbox_mail_vfuncs = {
+ dbox_mail_close,
+ index_mail_free,
+ index_mail_set_seq,
+ index_mail_set_uid,
+ index_mail_set_uid_cache_updates,
+
+ index_mail_get_flags,
+ index_mail_get_keywords,
+ index_mail_get_keyword_indexes,
+ index_mail_get_modseq,
+ index_mail_get_parts,
+ index_mail_get_date,
+ dbox_mail_get_received_date,
+ mdbox_mail_get_save_date,
+ dbox_mail_get_virtual_size,
+ dbox_mail_get_physical_size,
+ index_mail_get_first_header,
+ index_mail_get_headers,
+ index_mail_get_header_stream,
+ dbox_mail_get_stream,
+ dbox_mail_get_special,
+ index_mail_update_flags,
+ index_mail_update_keywords,
+ index_mail_update_modseq,
+ index_mail_update_uid,
+ NULL,
+ index_mail_expunge,
+ index_mail_set_cache_corrupted,
+ index_mail_get_index_mail
+};
-#ifndef DBOX_MAP_PRIVATE_H
-#define DBOX_MAP_PRIVATE_H
+#ifndef MDBOX_MAP_PRIVATE_H
+#define MDBOX_MAP_PRIVATE_H
-#include "dbox-map.h"
+#include "mdbox-map.h"
struct dbox_mail_lookup_rec {
uint32_t map_uid;
uint16_t refcount;
- struct dbox_mail_index_map_record rec;
+ struct dbox_map_mail_index_record rec;
};
struct dbox_map {
- struct dbox_storage *storage;
+ struct mdbox_storage *storage;
+ const struct mdbox_settings *set;
+ char *path;
+
struct mail_index *index;
struct mail_index_view *view;
uint32_t created_uid_validity;
uint32_t map_ext_id, ref_ext_id;
ARRAY_TYPE(seq_range) ref0_file_ids;
+
+ mode_t create_mode, create_dir_mode;
+ gid_t create_gid;
+ const char *create_gid_origin;
};
struct dbox_map_append {
- struct dbox_file *file;
+ struct dbox_file_append_context *file_append;
uoff_t offset, size;
};
struct dbox_map_append_context {
- struct dbox_mailbox *mbox;
struct dbox_map *map;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *sync_view;
struct mail_index_transaction *sync_trans, *trans;
+ ARRAY_DEFINE(file_appends, struct dbox_file_append_context *);
ARRAY_DEFINE(files, struct dbox_file *);
ARRAY_DEFINE(appends, struct dbox_map_append);
#include "hash.h"
#include "ostream.h"
#include "mkdir-parents.h"
-#include "dbox-storage.h"
-#include "dbox-file.h"
-#include "dbox-map-private.h"
+#include "mdbox-storage.h"
+#include "mdbox-file.h"
+#include "mdbox-map-private.h"
#define MAX_BACKWARDS_LOOKUPS 10
#define DBOX_FORCE_PURGE_MIN_BYTES (1024*1024*10)
#define DBOX_FORCE_PURGE_MIN_RATIO 0.5
+#define MAP_STORAGE(map) (&(map)->storage->storage.storage)
+
struct dbox_map_transaction_context {
struct dbox_map *map;
struct mail_index_transaction *trans;
{
va_list args;
+ map->storage->storage.files_corrupted = TRUE;
+
va_start(args, format);
- mail_storage_set_critical(&map->storage->storage,
+ mail_storage_set_critical(MAP_STORAGE(map),
"dbox map %s corrupted: %s",
map->index->filepath,
t_strdup_vprintf(format, args));
va_end(args);
}
-struct dbox_map *dbox_map_init(struct dbox_storage *storage)
+struct dbox_map *
+dbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list,
+ const char *path)
{
struct dbox_map *map;
+ gid_t tmp_gid;
+ const char *tmp_origin;
map = i_new(struct dbox_map, 1);
map->storage = storage;
- map->index = mail_index_alloc(storage->storage_dir,
- DBOX_GLOBAL_INDEX_PREFIX);
+ map->set = storage->set;
+ map->path = i_strdup(path);
+ map->index = mail_index_alloc(path, MDBOX_GLOBAL_INDEX_PREFIX);
map->map_ext_id = mail_index_ext_register(map->index, "map",
- sizeof(struct dbox_mail_index_map_header),
- sizeof(struct dbox_mail_index_map_record),
+ sizeof(struct dbox_map_mail_index_header),
+ sizeof(struct dbox_map_mail_index_record),
sizeof(uint32_t));
map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0,
sizeof(uint16_t), sizeof(uint16_t));
map->created_uid_validity = ioloop_time;
+
+ mailbox_list_get_permissions(root_list, NULL, &map->create_mode,
+ &map->create_gid, &map->create_gid_origin);
+ mailbox_list_get_dir_permissions(root_list, NULL, &map->create_dir_mode,
+ &tmp_gid, &tmp_origin);
+ mail_index_set_permissions(map->index, map->create_mode,
+ map->create_gid, map->create_gid_origin);
return map;
}
if (map->view != NULL)
mail_index_view_close(&map->view);
mail_index_free(&map->index);
+ i_free(map->path);
i_free(map);
}
-static int dbox_map_mkdir_storage(struct dbox_storage *storage)
+static int dbox_map_mkdir_storage(struct dbox_map *map)
{
- if (mkdir_parents_chgrp(storage->storage_dir, storage->dir_create_mode,
- storage->create_gid,
- storage->create_gid_origin) < 0 &&
+ if (mkdir_parents_chgrp(map->path, map->create_dir_mode,
+ map->create_gid, map->create_gid_origin) < 0 &&
errno != EEXIST) {
- mail_storage_set_critical(&storage->storage,
- "mkdir(%s) failed: %m", storage->storage_dir);
+ mail_storage_set_critical(MAP_STORAGE(map),
+ "mkdir(%s) failed: %m", map->path);
return -1;
}
return 0;
int dbox_map_open(struct dbox_map *map, bool create_missing)
{
- struct mail_storage *storage = &map->storage->storage;
enum mail_index_open_flags open_flags;
int ret;
}
open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY |
- mail_storage_settings_to_index_flags(storage->set);
+ mail_storage_settings_to_index_flags(MAP_STORAGE(map)->set);
if (create_missing) {
open_flags |= MAIL_INDEX_OPEN_FLAG_CREATE;
- if (dbox_map_mkdir_storage(map->storage) < 0)
+ if (dbox_map_mkdir_storage(map) < 0)
return -1;
}
ret = mail_index_open(map->index, open_flags,
- storage->set->parsed_lock_method);
+ MAP_STORAGE(map)->set->parsed_lock_method);
if (ret < 0) {
- mail_storage_set_internal_error(storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
return -1;
}
/* some open files may have read partially written mails. now that
map syncing makes the new mails visible, we need to make sure the
partial data is flushed out of memory */
- dbox_files_sync_input(map->storage);
+ mdbox_files_sync_input(map->storage);
if (mail_index_refresh(map->view->index) < 0) {
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
return -1;
}
ctx = mail_index_view_sync_begin(map->view,
MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
if (mail_index_view_sync_commit(&ctx, &delayed_expunges) < 0) {
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
return -1;
}
uint32_t *file_id_r, uoff_t *offset_r,
uoff_t *size_r)
{
- const struct dbox_mail_index_map_record *rec;
+ const struct dbox_map_mail_index_record *rec;
const void *data;
uint32_t uid;
bool expunged;
struct dbox_file_size *size;
struct seq_range_iter iter;
const struct mail_index_header *hdr;
- const struct dbox_mail_index_map_record *rec;
+ const struct dbox_map_mail_index_record *rec;
const uint16_t *ref16_p;
const void *data;
uint32_t seq, file_id;
while (seq_range_array_iter_nth(&iter, i++, &file_id)) {
size = hash_table_lookup(hash, POINTER_CAST(file_id));
if (size->ref0_size*100 / size->file_size >=
- map->storage->set->dbox_purge_min_percentage)
+ map->set->mdbox_purge_min_percentage)
seq_range_array_add(&new_ref0_file_ids, 0, file_id);
}
seq_range_array_intersect(&map->ref0_file_ids, &new_ref0_file_ids);
bool dbox_map_want_purge(struct dbox_map *map)
{
const struct mail_index_header *hdr;
- const struct dbox_mail_index_map_record *rec;
+ const struct dbox_map_mail_index_record *rec;
const uint16_t *ref16_p;
const void *data;
uoff_t ref0_size, total_size;
bool expunged;
uint32_t seq;
- if (map->storage->set->dbox_purge_min_percentage >= 100) {
+ if (map->set->mdbox_purge_min_percentage >= 100) {
/* we never purge anything */
return FALSE;
}
const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
{
const struct mail_index_header *hdr;
- const struct dbox_mail_index_map_record *rec;
+ const struct dbox_map_mail_index_record *rec;
const uint16_t *ref16_p;
const void *data;
uint32_t seq;
else
i_array_init(&map->ref0_file_ids, 64);
- if (map->storage->set->dbox_purge_min_percentage >= 100) {
+ if (map->set->mdbox_purge_min_percentage >= 100) {
/* we're never purging anything */
return &map->ref0_file_ids;
}
rec->file_id);
}
}
- if (map->storage->set->dbox_purge_min_percentage > 0 &&
+ if (map->set->mdbox_purge_min_percentage > 0 &&
array_count(&map->ref0_file_ids) > 0)
dbox_map_filter_zero_refs(map);
return &map->ref0_file_ids;
/* something had crashed. need a full resync. */
i_warning("dbox %s: Inconsistency in map index "
"(%u,%"PRIuUOFF_T" != %u,%"PRIuUOFF_T")",
- map->storage->storage_dir,
- seq1, offset1, seq2, offset2);
- map->storage->sync_rebuild = TRUE;
+ map->path, seq1, offset1, seq2, offset2);
+ map->storage->storage.files_corrupted = TRUE;
} else {
while (mail_index_sync_next(sync_ctx, &sync_rec)) ;
}
&view, &sync_trans, 0);
if (ret <= 0) {
i_assert(ret != 0);
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
mail_index_transaction_rollback(&ctx->trans);
return -1;
dbox_map_sync_handle(map, ctx->sync_ctx);
if (mail_index_transaction_commit(&ctx->trans) < 0) {
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
return -1;
}
*_ctx = NULL;
if (ctx->success) {
if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
}
} else if (ctx->sync_ctx != NULL) {
ctx->changed = TRUE;
cur_diff += mail_index_atomic_inc_ext(ctx->trans, seq,
map->ref_ext_id, diff);
+ i_assert(cur_diff >= 0);
if (cur_diff >= 32768) {
/* we're getting close to the 64k limit. fail early
to make it less likely that two processes increase
the refcount enough times to cross the limit */
- mail_storage_set_error(&map->storage->storage,
+ mail_storage_set_error(MAP_STORAGE(map),
MAIL_ERROR_NOTPOSSIBLE,
"Message has been copied too many times");
return -1;
{
struct dbox_map_transaction_context *map_trans;
const struct mail_index_header *hdr;
- const struct dbox_mail_index_map_record *rec;
+ const struct dbox_map_mail_index_record *rec;
const void *data;
bool expunged;
uint32_t seq;
}
struct dbox_map_append_context *
-dbox_map_append_begin_storage(struct dbox_storage *storage)
+dbox_map_append_begin(struct dbox_map *map)
{
struct dbox_map_append_context *ctx;
ctx = i_new(struct dbox_map_append_context, 1);
- ctx->map = storage->map;
+ ctx->map = map;
ctx->first_new_file_id = (uint32_t)-1;
+ i_array_init(&ctx->file_appends, 64);
i_array_init(&ctx->files, 64);
i_array_init(&ctx->appends, 128);
return ctx;
}
-struct dbox_map_append_context *
-dbox_map_append_begin(struct dbox_mailbox *mbox)
-{
- struct dbox_map_append_context *ctx;
-
- ctx = dbox_map_append_begin_storage(mbox->storage);
- ctx->mbox = mbox;
- return ctx;
-}
-
static time_t day_begin_stamp(unsigned int days)
{
struct tm tm;
static bool
dbox_map_file_try_append(struct dbox_map_append_context *ctx,
uint32_t file_id, time_t stamp, uoff_t mail_size,
- struct dbox_file **file_r, struct ostream **output_r,
- bool *retry_later_r)
+ struct dbox_file_append_context **file_append_r,
+ struct ostream **output_r, bool *retry_later_r)
{
struct dbox_map *map = ctx->map;
- struct dbox_storage *storage = map->storage;
+ struct mdbox_storage *storage = map->storage;
struct dbox_file *file;
+ struct dbox_file_append_context *file_append;
struct stat st;
- uoff_t append_offset;
bool deleted, file_too_old = FALSE;
int ret;
- *file_r = NULL;
+ *file_append_r = NULL;
+ *output_r = NULL;
*retry_later_r = FALSE;
- file = dbox_file_init_multi(storage, file_id);
- if (dbox_file_open_or_create(file, &deleted) <= 0 || deleted) {
+ file = mdbox_file_init(storage, file_id);
+ if (dbox_file_open(file, &deleted) <= 0 || deleted) {
dbox_file_unref(&file);
return TRUE;
}
else if ((ret = dbox_file_try_lock(file)) <= 0) {
/* locking failed */
*retry_later_r = ret == 0;
- } else if (stat(file->current_path, &st) < 0) {
+ } else if (stat(file->cur_path, &st) < 0) {
if (errno != ENOENT)
- i_error("stat(%s) failed: %m", file->current_path);
+ i_error("stat(%s) failed: %m", file->cur_path);
/* the file was unlinked between opening and locking it. */
- } else if (dbox_file_get_append_stream(file, &append_offset,
- output_r) <= 0) {
- /* couldn't append to this file */
- } else if (append_offset + mail_size > storage->set->dbox_rotate_size) {
- /* file was too large after all */
- dbox_file_cancel_append(file, append_offset);
} else {
- /* success */
- *file_r = file;
- return TRUE;
+ file_append = dbox_file_append_init(file);
+ if (dbox_file_get_append_stream(file_append, output_r) <= 0) {
+ /* couldn't append to this file */
+ } else if ((*output_r)->offset + mail_size > map->set->mdbox_rotate_size) {
+ /* file was too large after all */
+ } else {
+ /* success */
+ *file_append_r = file_append;
+ return TRUE;
+ }
+ dbox_file_append_rollback(&file_append);
}
/* failure */
static bool
dbox_map_is_appending(struct dbox_map_append_context *ctx, uint32_t file_id)
{
- struct dbox_file *const *files;
+ struct dbox_file_append_context *const *file_appends;
unsigned int i, count;
/* there shouldn't be many files open, don't bother with anything
faster. */
- files = array_get(&ctx->files, &count);
+ file_appends = array_get(&ctx->file_appends, &count);
for (i = 0; i < count; i++) {
- if (files[i]->file_id == file_id)
+ struct mdbox_file *mfile =
+ (struct mdbox_file *)file_appends[i]->file;
+
+ if (mfile->file_id == file_id)
return TRUE;
}
return FALSE;
static int
dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
- uoff_t mail_size, struct dbox_file **file_r,
+ uoff_t mail_size,
+ struct dbox_file_append_context **file_append_r,
struct ostream **output_r, bool *existing_r)
{
struct dbox_map *map = ctx->map;
- const struct dbox_settings *set = map->storage->set;
ARRAY_TYPE(seq_range) checked_file_ids;
- struct dbox_file *const *files;
+ struct dbox_file_append_context *const *file_appends;
const struct mail_index_header *hdr;
unsigned int i, count, backwards_lookup_count;
uint32_t seq, seq1, uid, file_id;
*existing_r = FALSE;
- if (mail_size >= set->dbox_rotate_size)
+ if (mail_size >= map->set->mdbox_rotate_size)
return 0;
/* first try to use files already used in this append */
- files = array_get(&ctx->files, &count);
+ file_appends = array_get(&ctx->file_appends, &count);
for (i = count; i > ctx->files_nonappendable_count; i--) {
- if (files[i-1]->output == NULL) {
- /* we already decided we can't append to this */
- continue;
- }
-
- append_offset = dbox_file_get_next_append_offset(files[i-1]);
- if (append_offset + mail_size <= set->dbox_rotate_size &&
- dbox_file_get_append_stream(files[i-1], &append_offset,
- output_r) > 0) {
- *file_r = files[i-1];
+ append_offset = file_appends[i-1]->output->offset;
+ if (append_offset + mail_size <= map->set->mdbox_rotate_size &&
+ dbox_file_get_append_stream(file_appends[i-1], output_r) > 0) {
+ *file_append_r = file_appends[i-1];
*existing_r = TRUE;
return 1;
}
- /* can't append to this file anymore */
-#if 0 /* FIXME: we can't close files, otherwise we lose the lock too early */
- if (files[i-1]->fd != -1) {
- /* avoid wasting fds by closing the file, but not if
- we're also reading from it. */
- if (dbox_file_flush_append(files[i-1]) < 0)
- return -1;
- dbox_file_unlock(files[i-1]);
- if (files[i-1]->refcount == 1)
- dbox_file_close(files[i-1]);
- }
-#endif
+ /* can't append to this file anymore. we could close it
+ otherwise, except that would also lose our lock too early. */
}
ctx->files_nonappendable_count = count;
/* try to find an existing appendable file */
- stamp = day_begin_stamp(set->dbox_rotate_days);
+ stamp = day_begin_stamp(map->set->mdbox_rotate_days);
hdr = mail_index_get_header(map->view);
ctx->orig_next_uid = hdr->next_uid;
/* first lookup: this should be enough usually, but we can't
be sure until after locking. also if messages were recently
moved, this message might not be the last one in the file. */
- if (offset + size + mail_size >= set->dbox_rotate_size)
+ if (offset + size + mail_size >= map->set->mdbox_rotate_size)
continue;
if (dbox_map_is_appending(ctx, file_id)) {
mail_index_lookup_uid(map->view, seq, &uid);
if (!dbox_map_file_try_append(ctx, file_id, stamp, mail_size,
- file_r, output_r, &retry_later)) {
+ file_append_r, output_r,
+ &retry_later)) {
/* file is too old. the rest of the files are too. */
break;
}
/* NOTE: we've now refreshed map view. there are no guarantees
about sequences anymore. */
- if (*file_r != NULL)
+ if (*file_append_r != NULL)
return 1;
/* FIXME: use retry_later somehow */
if (uid == 1 ||
}
int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
- struct dbox_file **file_r, struct ostream **output_r)
+ struct dbox_file_append_context **file_append_ctx_r,
+ struct ostream **output_r)
{
- struct dbox_file *file = NULL;
+ struct dbox_file *file;
struct dbox_map_append *append;
- uoff_t append_offset;
+ struct dbox_file_append_context *file_append;
bool existing;
int ret;
if (ctx->failed)
return -1;
- ret = dbox_map_find_appendable_file(ctx, mail_size, &file,
+ ret = dbox_map_find_appendable_file(ctx, mail_size, &file_append,
output_r, &existing);
- if (ret < 0)
+ if (ret > 0)
+ file = file_append->file;
+ else if (ret < 0)
return -1;
-
- if (ret == 0) {
+ else {
/* create a new file */
- file = ctx->map->storage->set->dbox_rotate_size == 0 ?
- dbox_file_init_single(ctx->mbox, 0) :
- dbox_file_init_multi(ctx->map->storage, 0);
- ret = dbox_file_get_append_stream(file, &append_offset,
- output_r);
+ file = mdbox_file_init(ctx->map->storage, 0);
+ file_append = dbox_file_append_init(file);
+
+ ret = dbox_file_get_append_stream(file_append, output_r);
if (ret <= 0) {
i_assert(ret < 0);
- (void)unlink(file->current_path);
+ dbox_file_append_rollback(&file_append);
dbox_file_unref(&file);
return -1;
}
}
- if (file->single_mbox == NULL) {
- append = array_append_space(&ctx->appends);
- append->file = file;
- append->offset = (*output_r)->offset;
- append->size = (uint32_t)-1;
- }
+ append = array_append_space(&ctx->appends);
+ append->file_append = file_append;
+ append->offset = (*output_r)->offset;
+ append->size = (uint32_t)-1;
if (!existing) {
- i_assert(file->first_append_offset == 0);
- i_assert(file->output != NULL);
- file->first_append_offset = file->output->offset;
+ i_assert(file_append->first_append_offset == 0);
+ file_append->first_append_offset = file_append->output->offset;
+ array_append(&ctx->file_appends, &file_append, 1);
array_append(&ctx->files, &file, 1);
}
- *file_r = file;
+ *file_append_ctx_r = file_append;
return 0;
}
-void dbox_map_append_finish_multi_mail(struct dbox_map_append_context *ctx)
+void dbox_map_append_finish(struct dbox_map_append_context *ctx)
{
struct dbox_map_append *appends;
unsigned int count;
+ uoff_t cur_offset;
appends = array_get_modifiable(&ctx->appends, &count);
i_assert(count > 0 && appends[count-1].size == (uint32_t)-1);
- appends[count-1].size = appends[count-1].file->output->offset -
- appends[count-1].offset;
+ cur_offset = appends[count-1].file_append->output->offset;
+ i_assert(cur_offset >= appends[count-1].offset);
+ appends[count-1].size = cur_offset - appends[count-1].offset;
}
static int
dbox_map_get_next_file_id(struct dbox_map *map, struct mail_index_view *view,
uint32_t *file_id_r)
{
- const struct dbox_mail_index_map_header *hdr;
+ const struct dbox_map_mail_index_header *hdr;
const void *data;
size_t data_size;
static int dbox_map_assign_file_ids(struct dbox_map_append_context *ctx,
bool separate_transaction)
{
- struct dbox_file *const *files;
+ struct dbox_file_append_context *const *file_appends;
unsigned int i, count;
uint32_t first_file_id, file_id;
int ret;
&ctx->sync_view, &ctx->sync_trans, 0);
if (ret <= 0) {
i_assert(ret != 0);
- mail_storage_set_internal_error(&ctx->map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(ctx->map));
mail_index_reset_error(ctx->map->index);
return -1;
}
return -1;
}
- /* assign file_ids for newly created multi-files */
+ /* assign file_ids for newly created files */
first_file_id = file_id;
- files = array_get(&ctx->files, &count);
+ file_appends = array_get(&ctx->file_appends, &count);
for (i = 0; i < count; i++) {
- if (files[i]->single_mbox != NULL)
- continue;
+ struct mdbox_file *mfile =
+ (struct mdbox_file *)file_appends[i]->file;
- if (files[i]->output != NULL) {
- if (dbox_file_flush_append(files[i]) < 0) {
- ret = -1;
- break;
- }
+ if (dbox_file_append_flush(file_appends[i]) < 0) {
+ ret = -1;
+ break;
}
- if (files[i]->file_id == 0) {
- if (dbox_file_assign_id(files[i], file_id++) < 0) {
+ if (mfile->file_id == 0) {
+ if (mdbox_file_assign_file_id(mfile, file_id++) < 0) {
ret = -1;
break;
}
{
const struct dbox_map_append *appends;
const struct mail_index_header *hdr;
- struct dbox_mail_index_map_record rec;
+ struct dbox_map_mail_index_record rec;
unsigned int i, count;
ARRAY_TYPE(seq_range) uids;
const struct seq_range *range;
ref16 = 1;
appends = array_get(&ctx->appends, &count);
for (i = 0; i < count; i++) {
+ struct mdbox_file *mfile =
+ (struct mdbox_file *)appends[i].file_append->file;
+
i_assert(appends[i].offset <= (uint32_t)-1);
i_assert(appends[i].size <= (uint32_t)-1);
- rec.file_id = appends[i].file->file_id;
+ rec.file_id = mfile->file_id;
rec.offset = appends[i].offset;
rec.size = appends[i].size;
}
if (mail_index_transaction_commit(&ctx->trans) < 0) {
- mail_storage_set_internal_error(&ctx->map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(ctx->map));
mail_index_reset_error(ctx->map->index);
return -1;
}
const ARRAY_TYPE(seq_range) *expunge_map_uids)
{
const struct dbox_map_append *appends;
- struct dbox_mail_index_map_record rec;
+ struct dbox_map_mail_index_record rec;
struct seq_range_iter iter;
const uint32_t *uids;
unsigned int i, j, map_uids_count, appends_count;
uids = array_get(map_uids, &map_uids_count);
for (i = j = 0; i < map_uids_count; i++) {
+ struct mdbox_file *mfile =
+ (struct mdbox_file *)appends[j].file_append->file;
+
i_assert(j < appends_count);
- rec.file_id = appends[j].file->file_id;
+ rec.file_id = mfile->file_id;
rec.offset = appends[j].offset;
rec.size = appends[j].size;
j++;
return 0;
}
-int dbox_map_append_assign_uids(struct dbox_map_append_context *ctx,
- const ARRAY_TYPE(seq_range) *uids)
-{
- struct dbox_file *const *files;
- struct seq_range_iter iter;
- unsigned int i, count, n = 0;
- uint32_t uid;
- bool ret;
-
- seq_range_array_iter_init(&iter, uids);
- files = array_get(&ctx->files, &count);
- for (i = 0; i < count; i++) {
- if (files[i]->single_mbox == NULL)
- continue;
-
- ret = seq_range_array_iter_nth(&iter, n++, &uid);
- i_assert(ret);
- if (dbox_file_assign_id(files[i], uid) < 0)
- return -1;
- }
- i_assert(!seq_range_array_iter_nth(&iter, n, &uid));
- return 0;
-}
-
int dbox_map_append_commit(struct dbox_map_append_context *ctx)
{
struct dbox_map *map = ctx->map;
+ struct dbox_file_append_context **file_appends;
+ unsigned int i, count;
i_assert(ctx->trans == NULL);
+ file_appends = array_get_modifiable(&ctx->file_appends, &count);
+ for (i = 0; i < count; i++) {
+ if (dbox_file_append_commit(&file_appends[i]) < 0)
+ return -1;
+ }
+
if (ctx->sync_ctx != NULL) {
if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
- mail_storage_set_internal_error(&map->storage->storage);
+ mail_storage_set_internal_error(MAP_STORAGE(map));
mail_index_reset_error(map->index);
return -1;
}
}
+
ctx->committed = TRUE;
return 0;
}
-static void dbox_map_append_file_rollback(struct dbox_file *file)
-{
- struct mail_storage *storage = &file->storage->storage;
-
- if (file->output != NULL) {
- /* flush before truncating */
- (void)o_stream_flush(file->output);
- }
-
- if (file->file_id != 0 &&
- file->first_append_offset > file->file_header_size) {
- if (ftruncate(file->fd, file->first_append_offset) < 0) {
- mail_storage_set_critical(storage,
- "ftruncate(%s, %"PRIuUOFF_T") failed: %m",
- file->current_path, file->first_append_offset);
- }
- } else {
- if (unlink(file->current_path) < 0) {
- mail_storage_set_critical(storage,
- "unlink(%s) failed: %m", file->current_path);
- }
- }
-}
-
void dbox_map_append_free(struct dbox_map_append_context **_ctx)
{
struct dbox_map_append_context *ctx = *_ctx;
+ struct dbox_file_append_context **file_appends;
struct dbox_file **files;
unsigned int i, count;
if (ctx->sync_ctx != NULL)
mail_index_sync_rollback(&ctx->sync_ctx);
- files = array_get_modifiable(&ctx->files, &count);
+ file_appends = array_get_modifiable(&ctx->file_appends, &count);
for (i = 0; i < count; i++) {
- if (!ctx->committed)
- dbox_map_append_file_rollback(files[i]);
+ if (file_appends[i] != NULL)
+ dbox_file_append_rollback(&file_appends[i]);
+ }
- files[i]->first_append_offset = 0;
+ files = array_get_modifiable(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
dbox_file_unlock(files[i]);
dbox_file_unref(&files[i]);
}
+
array_free(&ctx->appends);
+ array_free(&ctx->file_appends);
array_free(&ctx->files);
i_free(ctx);
}
-#ifndef DBOX_MAP_H
-#define DBOX_MAP_H
+#ifndef MDBOX_MAP_H
+#define MDBOX_MAP_H
#include "seq-range-array.h"
-struct dbox_storage;
-struct dbox_mailbox;
-struct dbox_file;
struct dbox_map_append_context;
-struct dbox_mail_lookup_rec;
+struct dbox_file_append_context;
+struct mdbox_storage;
-struct dbox_mail_index_map_header {
+struct dbox_map_mail_index_header {
uint32_t highest_file_id;
};
-struct dbox_mail_index_map_record {
+struct dbox_map_mail_index_record {
uint32_t file_id;
uint32_t offset;
uint32_t size; /* including pre/post metadata */
};
ARRAY_DEFINE_TYPE(dbox_map_file_msg, struct dbox_map_file_msg);
-struct dbox_map *dbox_map_init(struct dbox_storage *storage);
+struct dbox_map *
+dbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list,
+ const char *path);
void dbox_map_deinit(struct dbox_map **map);
/* Open the map. This is done automatically for most operations.
const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map);
struct dbox_map_append_context *
-dbox_map_append_begin(struct dbox_mailbox *mbox);
-struct dbox_map_append_context *
-dbox_map_append_begin_storage(struct dbox_storage *storage);
+dbox_map_append_begin(struct dbox_map *map);
/* Request file for saving a new message with given size (if available). If an
existing file can be used, the record is locked and updated in index.
Returns 0 if ok, -1 if error. */
int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
- struct dbox_file **file_r, struct ostream **output_r);
+ struct dbox_file_append_context **file_append_ctx_r,
+ struct ostream **output_r);
/* Finished saving the last mail. Saves the message size. */
-void dbox_map_append_finish_multi_mail(struct dbox_map_append_context *ctx);
+void dbox_map_append_finish(struct dbox_map_append_context *ctx);
/* Assign map UIDs to all appended msgs to multi-files. */
int dbox_map_append_assign_map_uids(struct dbox_map_append_context *ctx,
uint32_t *first_map_uid_r,
uint32_t *last_map_uid_r);
-/* Assign UIDs to all created single-files. */
-int dbox_map_append_assign_uids(struct dbox_map_append_context *ctx,
- const ARRAY_TYPE(seq_range) *uids);
/* The appends are existing messages that were simply moved to a new file.
map_uids contains the moved messages' map UIDs. */
int dbox_map_append_move(struct dbox_map_append_context *ctx,
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "fdatasync-path.h"
+#include "hex-binary.h"
+#include "hex-dec.h"
+#include "str.h"
+#include "istream.h"
+#include "istream-crlf.h"
+#include "ostream.h"
+#include "write-full.h"
+#include "index-mail.h"
+#include "mail-copy.h"
+#include "dbox-save.h"
+#include "mdbox-storage.h"
+#include "mdbox-map.h"
+#include "mdbox-file.h"
+#include "mdbox-sync.h"
+
+#include <stdlib.h>
+
+struct dbox_save_mail {
+ struct dbox_file_append_context *file_append;
+ uint32_t seq;
+ uint32_t append_offset;
+};
+
+struct mdbox_save_context {
+ struct dbox_save_context ctx;
+
+ struct mdbox_mailbox *mbox;
+ struct mdbox_sync_context *sync_ctx;
+
+ struct dbox_file_append_context *cur_file_append;
+ struct dbox_map_append_context *append_ctx;
+
+ ARRAY_TYPE(uint32_t) copy_map_uids;
+ struct dbox_map_transaction_context *map_trans;
+
+ ARRAY_DEFINE(mails, struct dbox_save_mail);
+};
+
+struct dbox_file *
+mdbox_save_file_get_file(struct mailbox_transaction_context *t,
+ uint32_t seq, uoff_t *offset_r)
+{
+ struct mdbox_save_context *ctx =
+ (struct mdbox_save_context *)t->save_ctx;
+ const struct dbox_save_mail *mails, *mail;
+ unsigned int count;
+
+ mails = array_get(&ctx->mails, &count);
+ i_assert(count > 0);
+ i_assert(seq >= mails[0].seq);
+
+ mail = &mails[mails[0].seq - seq];
+ i_assert(mail->seq == seq);
+
+ if (dbox_file_append_flush(mail->file_append) < 0)
+ ctx->ctx.failed = TRUE;
+
+ *offset_r = mail->append_offset;
+ return mail->file_append->file;
+}
+
+struct mail_save_context *
+mdbox_save_alloc(struct mailbox_transaction_context *t)
+{
+ struct index_transaction_context *it =
+ (struct index_transaction_context *)t;
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)t->box;
+ struct mdbox_save_context *ctx =
+ (struct mdbox_save_context *)t->save_ctx;
+
+ i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
+
+ if (ctx != NULL) {
+ /* use the existing allocated structure */
+ ctx->ctx.finished = FALSE;
+ return &ctx->ctx.ctx;
+ }
+
+ ctx = i_new(struct mdbox_save_context, 1);
+ ctx->ctx.ctx.transaction = t;
+ ctx->ctx.trans = it->trans;
+ ctx->mbox = mbox;
+ ctx->append_ctx = dbox_map_append_begin(mbox->storage->map);
+ i_array_init(&ctx->mails, 32);
+ t->save_ctx = &ctx->ctx.ctx;
+ return t->save_ctx;
+}
+
+int mdbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+ struct dbox_save_mail *save_mail;
+ uoff_t mail_size, append_offset;
+
+ /* get the size of the mail to be saved, if possible */
+ if (i_stream_get_size(input, TRUE, &mail_size) <= 0)
+ mail_size = 0;
+ if (dbox_map_append_next(ctx->append_ctx, mail_size,
+ &ctx->cur_file_append,
+ &ctx->ctx.cur_output) < 0) {
+ ctx->ctx.failed = TRUE;
+ return -1;
+ }
+ i_assert(ctx->ctx.cur_output->offset <= (uint32_t)-1);
+ append_offset = ctx->ctx.cur_output->offset;
+
+ ctx->ctx.cur_file = ctx->cur_file_append->file;
+ dbox_save_begin(&ctx->ctx, input);
+
+ save_mail = array_append_space(&ctx->mails);
+ save_mail->file_append = ctx->cur_file_append;
+ save_mail->seq = ctx->ctx.seq;
+ save_mail->append_offset = append_offset;
+ return ctx->ctx.failed ? -1 : 0;
+}
+
+static int mdbox_save_mail_write_metadata(struct mdbox_save_context *ctx,
+ struct dbox_save_mail *mail)
+{
+ struct dbox_file *file = mail->file_append->file;
+ struct dbox_message_header dbox_msg_hdr;
+ uoff_t message_size;
+ uint8_t guid_128[MAIL_GUID_128_SIZE];
+
+ i_assert(file->msg_header_size == sizeof(dbox_msg_hdr));
+
+ message_size = ctx->ctx.cur_output->offset -
+ mail->append_offset - mail->file_append->file->msg_header_size;
+
+ dbox_save_write_metadata(&ctx->ctx.ctx, ctx->ctx.cur_output,
+ ctx->mbox->ibox.box.name, guid_128);
+ /* save the 128bit GUID to index so if the map index gets corrupted
+ we can still find the message */
+ mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
+ ctx->mbox->guid_ext_id, guid_128, NULL);
+
+ dbox_msg_header_fill(&dbox_msg_hdr, message_size);
+ if (o_stream_pwrite(ctx->ctx.cur_output, &dbox_msg_hdr,
+ sizeof(dbox_msg_hdr), mail->append_offset) < 0) {
+ dbox_file_set_syscall_error(file, "pwrite()");
+ return -1;
+ }
+ return 0;
+}
+
+static int mdbox_save_finish_write(struct mail_save_context *_ctx)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+ struct dbox_save_mail *mails;
+
+ ctx->ctx.finished = TRUE;
+ if (ctx->ctx.cur_output == NULL)
+ return -1;
+
+ index_mail_cache_parse_deinit(_ctx->dest_mail,
+ _ctx->received_date, !ctx->ctx.failed);
+
+ mails = array_idx_modifiable(&ctx->mails, array_count(&ctx->mails) - 1);
+ if (!ctx->ctx.failed) T_BEGIN {
+ if (mdbox_save_mail_write_metadata(ctx, mails) < 0)
+ ctx->ctx.failed = TRUE;
+ else
+ dbox_map_append_finish(ctx->append_ctx);
+ } T_END;
+
+ i_stream_unref(&ctx->ctx.input);
+
+ if (ctx->ctx.failed) {
+ array_delete(&ctx->mails, array_count(&ctx->mails) - 1, 1);
+ return -1;
+ }
+ return 0;
+}
+
+int mdbox_save_finish(struct mail_save_context *ctx)
+{
+ int ret;
+
+ ret = mdbox_save_finish_write(ctx);
+ index_save_context_free(ctx);
+ return ret;
+}
+
+void mdbox_save_cancel(struct mail_save_context *_ctx)
+{
+ struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
+
+ ctx->failed = TRUE;
+ (void)mdbox_save_finish(_ctx);
+}
+
+int mdbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+ struct mailbox_transaction_context *_t = _ctx->transaction;
+ struct mdbox_mailbox *mbox = ctx->mbox;
+ const struct mail_index_header *hdr;
+ uint32_t first_map_uid, last_map_uid;
+
+ i_assert(ctx->ctx.finished);
+
+ /* lock the mailbox before map to avoid deadlocks */
+ if (mdbox_sync_begin(mbox, MDBOX_SYNC_FLAG_NO_PURGE |
+ MDBOX_SYNC_FLAG_FORCE |
+ MDBOX_SYNC_FLAG_FSYNC, &ctx->sync_ctx) < 0) {
+ mdbox_transaction_save_rollback(_ctx);
+ return -1;
+ }
+
+ /* get map UIDs for messages saved to multi-files. they're written
+ to transaction log immediately within this function, but the map
+ is left locked. */
+ if (dbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid,
+ &last_map_uid) < 0) {
+ mdbox_transaction_save_rollback(_ctx);
+ return -1;
+ }
+
+ /* assign UIDs for new messages */
+ hdr = mail_index_get_header(ctx->sync_ctx->sync_view);
+ mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid,
+ &_t->changes->saved_uids);
+
+ /* add map_uids for all messages saved to multi-files */
+ if (first_map_uid != 0) {
+ struct mdbox_mail_index_record rec;
+ const struct dbox_save_mail *mails;
+ unsigned int i, count;
+ uint32_t next_map_uid = first_map_uid;
+
+ mdbox_update_header(mbox, ctx->ctx.trans, NULL);
+
+ memset(&rec, 0, sizeof(rec));
+ rec.save_date = ioloop_time;
+ mails = array_get(&ctx->mails, &count);
+ for (i = 0; i < count; i++) {
+ rec.map_uid = next_map_uid++;
+ i_assert(i == 0 ||
+ mails[i-1].append_offset != mails[i].append_offset);
+ mail_index_update_ext(ctx->ctx.trans, mails[i].seq,
+ mbox->ext_id, &rec, NULL);
+ }
+ i_assert(next_map_uid == last_map_uid + 1);
+ }
+
+ /* increase map's refcount for copied mails */
+ if (array_is_created(&ctx->copy_map_uids)) {
+ ctx->map_trans =
+ dbox_map_transaction_begin(mbox->storage->map, FALSE);
+ if (dbox_map_update_refcounts(ctx->map_trans,
+ &ctx->copy_map_uids, 1) < 0) {
+ mdbox_transaction_save_rollback(_ctx);
+ return -1;
+ }
+ }
+
+ if (ctx->ctx.mail != NULL)
+ mail_free(&ctx->ctx.mail);
+
+ _t->changes->uid_validity = hdr->uid_validity;
+ return 0;
+}
+
+void mdbox_transaction_save_commit_post(struct mail_save_context *_ctx)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+
+ _ctx->transaction = NULL; /* transaction is already freed */
+
+ /* finish writing the mailbox APPENDs */
+ if (mdbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
+ if (ctx->map_trans != NULL)
+ (void)dbox_map_transaction_commit(ctx->map_trans);
+ /* commit only updates the sync tail offset, everything else
+ was already written at this point. */
+ (void)dbox_map_append_commit(ctx->append_ctx);
+ }
+ dbox_map_append_free(&ctx->append_ctx);
+
+ if (!ctx->mbox->storage->storage.storage.set->fsync_disable) {
+ if (fdatasync_path(ctx->mbox->ibox.box.path) < 0) {
+ i_error("fdatasync_path(%s) failed: %m",
+ ctx->mbox->ibox.box.path);
+ }
+ }
+ mdbox_transaction_save_rollback(_ctx);
+}
+
+void mdbox_transaction_save_rollback(struct mail_save_context *_ctx)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+
+ if (!ctx->ctx.finished)
+ mdbox_save_cancel(&ctx->ctx.ctx);
+ if (ctx->append_ctx != NULL)
+ dbox_map_append_free(&ctx->append_ctx);
+ if (ctx->map_trans != NULL)
+ dbox_map_transaction_free(&ctx->map_trans);
+ if (array_is_created(&ctx->copy_map_uids))
+ array_free(&ctx->copy_map_uids);
+
+ if (ctx->sync_ctx != NULL)
+ (void)mdbox_sync_finish(&ctx->sync_ctx, FALSE);
+
+ if (ctx->ctx.mail != NULL)
+ mail_free(&ctx->ctx.mail);
+ array_free(&ctx->mails);
+ i_free(ctx);
+}
+
+int mdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
+{
+ struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
+ struct mdbox_mailbox *src_mbox;
+ struct mdbox_mail_index_record rec;
+ const void *data;
+ bool expunged;
+
+ ctx->ctx.finished = TRUE;
+
+ if (mail->box->storage != _ctx->transaction->box->storage)
+ return mail_storage_copy(_ctx, mail);
+ src_mbox = (struct mdbox_mailbox *)mail->box;
+
+ memset(&rec, 0, sizeof(rec));
+ rec.save_date = ioloop_time;
+ if (mdbox_mail_lookup(src_mbox, src_mbox->ibox.view, mail->seq,
+ &rec.map_uid) < 0)
+ return -1;
+
+ /* remember the map_uid so we can later increase its refcount */
+ if (!array_is_created(&ctx->copy_map_uids))
+ i_array_init(&ctx->copy_map_uids, 32);
+ array_append(&ctx->copy_map_uids, &rec.map_uid, 1);
+
+ /* add message to mailbox index */
+ dbox_save_add_to_index(&ctx->ctx);
+ mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
+ ctx->mbox->ext_id, &rec, NULL);
+
+ mail_index_lookup_ext(src_mbox->ibox.view, mail->seq,
+ src_mbox->guid_ext_id, &data, &expunged);
+ if (data != NULL) {
+ mail_index_update_ext(ctx->ctx.trans, ctx->ctx.seq,
+ ctx->mbox->guid_ext_id, data, NULL);
+ }
+ return 0;
+}
--- /dev/null
+/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "mdbox-settings.h"
+
+#include <stddef.h>
+
+#undef DEF
+#define DEF(type, name) \
+ { type, #name, offsetof(struct mdbox_settings, name), NULL }
+
+static bool mdbox_settings_verify(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r);
+
+static struct setting_define mdbox_setting_defines[] = {
+ DEF(SET_UINT, mdbox_rotate_size),
+ DEF(SET_UINT, mdbox_rotate_min_size),
+ DEF(SET_UINT, mdbox_rotate_days),
+ DEF(SET_UINT, mdbox_max_open_files),
+ DEF(SET_UINT, mdbox_purge_min_percentage),
+
+ SETTING_DEFINE_LIST_END
+};
+
+static struct mdbox_settings mdbox_default_settings = {
+ MEMBER(mdbox_rotate_size) 2048*1024,
+ MEMBER(mdbox_rotate_min_size) 16*1024,
+ MEMBER(mdbox_rotate_days) 0,
+ MEMBER(mdbox_max_open_files) 64,
+ MEMBER(mdbox_purge_min_percentage) 0
+};
+
+static struct setting_parser_info mdbox_setting_parser_info = {
+ MEMBER(defines) mdbox_setting_defines,
+ MEMBER(defaults) &mdbox_default_settings,
+
+ MEMBER(parent) &mail_user_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct mdbox_settings),
+ MEMBER(check_func) mdbox_settings_verify
+};
+
+/* <settings checks> */
+static bool mdbox_settings_verify(void *_set, pool_t pool ATTR_UNUSED,
+ const char **error_r)
+{
+ const struct mdbox_settings *set = _set;
+
+ if (set->mdbox_max_open_files < 2) {
+ *error_r = "mdbox_max_open_files must be at least 2";
+ return FALSE;
+ }
+ return TRUE;
+}
+/* </settings checks> */
+
+const struct setting_parser_info *mdbox_get_setting_parser_info(void)
+{
+ return &mdbox_setting_parser_info;
+}
--- /dev/null
+#ifndef MDBOX_SETTINGS_H
+#define MDBOX_SETTINGS_H
+
+struct mdbox_settings {
+ unsigned int mdbox_rotate_size;
+ unsigned int mdbox_rotate_min_size;
+ unsigned int mdbox_rotate_days;
+ unsigned int mdbox_max_open_files;
+ unsigned int mdbox_purge_min_percentage;
+};
+
+const struct setting_parser_info *mdbox_get_setting_parser_info(void);
+
+#endif
#include "istream.h"
#include "hash.h"
#include "str.h"
-#include "dbox-storage.h"
-#include "dbox-file.h"
-#include "dbox-map-private.h"
-#include "dbox-sync.h"
-#include "dbox-storage-rebuild.h"
+#include "dbox-sync-rebuild.h"
+#include "mdbox-storage.h"
+#include "mdbox-file.h"
+#include "mdbox-map-private.h"
+#include "mdbox-sync.h"
+#include "mdbox-storage-rebuild.h"
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
-struct dbox_rebuild_msg {
+struct mdbox_rebuild_msg {
uint8_t guid_128[MAIL_GUID_128_SIZE];
uint32_t file_id;
uint32_t offset;
uint32_t next_uid;
};
-struct dbox_storage_rebuild_context {
- struct dbox_storage *storage;
+struct mdbox_storage_rebuild_context {
+ struct mdbox_storage *storage;
pool_t pool;
struct hash_table *guid_hash;
- ARRAY_DEFINE(msgs, struct dbox_rebuild_msg *);
+ ARRAY_DEFINE(msgs, struct mdbox_rebuild_msg *);
uint32_t prev_file_id;
uint32_t highest_seen_map_uid;
return memcmp(p1, p2, MAIL_GUID_128_SIZE);
}
-static struct dbox_storage_rebuild_context *
-dbox_storage_rebuild_init(struct dbox_storage *storage)
+static struct mdbox_storage_rebuild_context *
+mdbox_storage_rebuild_init(struct mdbox_storage *storage)
{
- struct dbox_storage_rebuild_context *ctx;
+ struct mdbox_storage_rebuild_context *ctx;
- ctx = i_new(struct dbox_storage_rebuild_context, 1);
+ ctx = i_new(struct mdbox_storage_rebuild_context, 1);
ctx->storage = storage;
ctx->pool = pool_alloconly_create("dbox map rebuild", 1024*256);
ctx->guid_hash = hash_table_create(default_pool, ctx->pool, 0,
return ctx;
}
-static void dbox_storage_rebuild_deinit(struct dbox_storage_rebuild_context *ctx)
+static void
+mdbox_storage_rebuild_deinit(struct mdbox_storage_rebuild_context *ctx)
{
if (ctx->sync_ctx != NULL)
mail_index_sync_rollback(&ctx->sync_ctx);
i_free(ctx);
}
-static int dbox_rebuild_msg_offset_cmp(const void *p1, const void *p2)
+static int mdbox_rebuild_msg_offset_cmp(const void *p1, const void *p2)
{
- const struct dbox_rebuild_msg *const *m1 = p1, *const *m2 = p2;
+ const struct mdbox_rebuild_msg *const *m1 = p1, *const *m2 = p2;
if ((*m1)->file_id < (*m2)->file_id)
return -1;
return 0;
}
-static int dbox_rebuild_msg_uid_cmp(struct dbox_rebuild_msg *const *m1,
- struct dbox_rebuild_msg *const *m2)
+static int mdbox_rebuild_msg_uid_cmp(struct mdbox_rebuild_msg *const *m1,
+ struct mdbox_rebuild_msg *const *m2)
{
if ((*m1)->map_uid < (*m2)->map_uid)
return -1;
return 0;
}
-static int rebuild_add_file(struct dbox_storage_rebuild_context *ctx,
+static int rebuild_add_file(struct mdbox_storage_rebuild_context *ctx,
const char *path)
{
struct dbox_file *file;
const char *fname, *guid;
- struct dbox_rebuild_msg *rec;
+ struct mdbox_rebuild_msg *rec;
uint32_t file_id;
uoff_t offset, prev_offset, size;
- bool last, expunged, first, fixed = FALSE;
+ bool last, first, fixed = FALSE;
+ unsigned int len;
int ret = 0;
fname = strrchr(path, '/');
i_assert(fname != NULL);
- fname += strlen(DBOX_MAIL_FILE_MULTI_PREFIX) + 1;
+ fname += strlen(MDBOX_MAIL_FILE_PREFIX) + 1;
file_id = strtoul(fname, NULL, 10);
if (!is_numeric(fname, '\0') || file_id == 0) {
- i_warning("dbox rebuild: File name is missing ID: %s", path);
+ len = strlen(fname);
+ if (len > 7 && strcmp(fname + len - 7, ".broken") != 0) {
+ i_warning("dbox rebuild: File name is missing ID: %s",
+ path);
+ }
return 0;
}
ctx->msgs_unsorted = TRUE;
ctx->prev_file_id = file_id;
- file = dbox_file_init_multi(ctx->storage, file_id);
+ file = mdbox_file_init(ctx->storage, file_id);
prev_offset = 0;
dbox_file_seek_rewind(file);
while ((ret = dbox_file_seek_next(file, &offset, &last)) >= 0) {
if (!first) {
/* seek to the offset where we last left off */
ret = dbox_file_get_mail_stream(file,
- prev_offset, &size, NULL, &expunged);
+ prev_offset, &size, NULL);
if (ret <= 0)
break;
}
break;
}
- rec = p_new(ctx->pool, struct dbox_rebuild_msg, 1);
+ rec = p_new(ctx->pool, struct mdbox_rebuild_msg, 1);
rec->file_id = file_id;
rec->offset = offset;
rec->size = file->input->v_offset - offset;
}
static void
-rebuild_add_missing_map_uids(struct dbox_storage_rebuild_context *ctx,
+rebuild_add_missing_map_uids(struct mdbox_storage_rebuild_context *ctx,
uint32_t next_uid)
{
- struct dbox_rebuild_msg **msgs;
- struct dbox_mail_index_map_record rec;
+ struct mdbox_rebuild_msg **msgs;
+ struct dbox_map_mail_index_record rec;
unsigned int i, count;
uint32_t seq;
}
}
-static int rebuild_apply_map(struct dbox_storage_rebuild_context *ctx)
+static int rebuild_apply_map(struct mdbox_storage_rebuild_context *ctx)
{
struct dbox_map *map = ctx->storage->map;
const struct mail_index_header *hdr;
- struct dbox_rebuild_msg *const *msgs, **pos;
- struct dbox_rebuild_msg search_msg, *search_msgp = &search_msg;
+ struct mdbox_rebuild_msg *const *msgs, **pos;
+ struct mdbox_rebuild_msg search_msg, *search_msgp = &search_msg;
struct dbox_mail_lookup_rec rec;
uint32_t seq;
unsigned int count;
if (ctx->msgs_unsorted)
- array_sort(&ctx->msgs, dbox_rebuild_msg_offset_cmp);
+ array_sort(&ctx->msgs, mdbox_rebuild_msg_offset_cmp);
msgs = array_get_modifiable(&ctx->msgs, &count);
hdr = mail_index_get_header(ctx->sync_view);
search_msg.file_id = rec.rec.file_id;
search_msg.offset = rec.rec.offset;
pos = bsearch(&search_msgp, msgs, count, sizeof(*msgs),
- dbox_rebuild_msg_offset_cmp);
+ mdbox_rebuild_msg_offset_cmp);
if (pos == NULL) {
/* map record points to non-existing message. */
mail_index_expunge(ctx->trans, seq);
/* afterwards we're interested in looking up map_uids.
re-sort the messages to make it easier. */
- array_sort(&ctx->msgs, dbox_rebuild_msg_uid_cmp);
+ array_sort(&ctx->msgs, mdbox_rebuild_msg_uid_cmp);
return 0;
}
-static struct dbox_rebuild_msg *
-rebuild_lookup_map_uid(struct dbox_storage_rebuild_context *ctx,
+static struct mdbox_rebuild_msg *
+rebuild_lookup_map_uid(struct mdbox_storage_rebuild_context *ctx,
uint32_t map_uid)
{
- struct dbox_rebuild_msg search_msg, *search_msgp = &search_msg;
- struct dbox_rebuild_msg **pos;
+ struct mdbox_rebuild_msg search_msg, *search_msgp = &search_msg;
+ struct mdbox_rebuild_msg **pos;
search_msg.map_uid = map_uid;
- pos = array_bsearch(&ctx->msgs, &search_msgp, dbox_rebuild_msg_uid_cmp);
+ pos = array_bsearch(&ctx->msgs, &search_msgp,
+ mdbox_rebuild_msg_uid_cmp);
return pos == NULL ? NULL : *pos;
}
static void
-rebuild_mailbox_multi(struct dbox_storage_rebuild_context *ctx,
+rebuild_mailbox_multi(struct mdbox_storage_rebuild_context *ctx,
struct dbox_sync_rebuild_context *rebuild_ctx,
- struct dbox_mailbox *mbox,
+ struct mdbox_mailbox *mbox,
struct mail_index_view *view,
struct mail_index_transaction *trans)
{
- const struct dbox_mail_index_record *dbox_rec;
- struct dbox_mail_index_record new_dbox_rec;
+ const struct mdbox_mail_index_record *dbox_rec;
+ struct mdbox_mail_index_record new_dbox_rec;
const struct mail_index_header *hdr;
- struct dbox_rebuild_msg *rec;
+ struct mdbox_rebuild_msg *rec;
const void *data;
bool expunged;
uint32_t seq, uid, new_seq, map_uid;
memset(&new_dbox_rec, 0, sizeof(new_dbox_rec));
hdr = mail_index_get_header(view);
for (seq = 1; seq <= hdr->messages_count; seq++) {
- mail_index_lookup_ext(view, seq, mbox->dbox_ext_id,
+ mail_index_lookup_ext(view, seq, mbox->ext_id,
&data, &expunged);
dbox_rec = data;
map_uid = dbox_rec == NULL ? 0 : dbox_rec->map_uid;
/* map_uid is wrong, update it */
i_assert(rec->map_uid != 0);
new_dbox_rec.map_uid = rec->map_uid;
- mail_index_update_ext(trans, seq, mbox->dbox_ext_id,
+ mail_index_update_ext(trans, seq, mbox->ext_id,
&new_dbox_rec, NULL);
} else {
/* everything was ok */
mail_index_lookup_uid(view, seq, &uid);
mail_index_append(trans, uid, &new_seq);
dbox_sync_rebuild_index_metadata(rebuild_ctx,
- NULL, new_seq, uid);
+ new_seq, uid);
new_dbox_rec.map_uid = rec->map_uid;
- mail_index_update_ext(trans, new_seq,
- mbox->dbox_ext_id,
+ mail_index_update_ext(trans, new_seq, mbox->ext_id,
&new_dbox_rec, NULL);
} T_END;
}
}
static int
-rebuild_mailbox(struct dbox_storage_rebuild_context *ctx,
+rebuild_mailbox(struct mdbox_storage_rebuild_context *ctx,
struct mail_namespace *ns, const char *name)
{
struct mailbox *box;
- struct dbox_mailbox *mbox;
+ struct mdbox_mailbox *mbox;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *view;
struct mail_index_transaction *trans;
enum mail_error error;
int ret;
- box = dbox_mailbox_alloc(&ctx->storage->storage, ns->list, name, NULL,
- MAILBOX_FLAG_READONLY |
- MAILBOX_FLAG_KEEP_RECENT |
- MAILBOX_FLAG_IGNORE_ACLS);
+ box = mdbox_mailbox_alloc(&ctx->storage->storage.storage,
+ ns->list, name, NULL,
+ MAILBOX_FLAG_READONLY |
+ MAILBOX_FLAG_KEEP_RECENT |
+ MAILBOX_FLAG_IGNORE_ACLS);
if (dbox_mailbox_open(box) < 0) {
(void)mail_storage_get_last_error(box->storage, &error);
mailbox_close(&box);
/* non-temporary error, ignore */
return 0;
}
- mbox = (struct dbox_mailbox *)box;
+ mbox = (struct mdbox_mailbox *)box;
ret = mail_index_sync_begin(mbox->ibox.index, &sync_ctx, &view, &trans,
MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES);
return -1;
}
- rebuild_ctx = dbox_sync_index_rebuild_init(mbox, view, trans, TRUE);
- ret = dbox_sync_index_rebuild_singles(rebuild_ctx);
- if (ret == 0)
- rebuild_mailbox_multi(ctx, rebuild_ctx, mbox, view, trans);
+ rebuild_ctx = dbox_sync_index_rebuild_init(&mbox->ibox, view, trans);
+ rebuild_mailbox_multi(ctx, rebuild_ctx, mbox, view, trans);
dbox_sync_index_rebuild_deinit(&rebuild_ctx);
if (mail_index_sync_commit(&sync_ctx) < 0) {
return ret < 0 ? -1 : 0;
}
-static int rebuild_namespace_mailboxes(struct dbox_storage_rebuild_context *ctx,
- struct mail_namespace *ns)
+static int
+rebuild_namespace_mailboxes(struct mdbox_storage_rebuild_context *ctx,
+ struct mail_namespace *ns)
{
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
return ret;
}
-static int rebuild_mailboxes(struct dbox_storage_rebuild_context *ctx)
+static int rebuild_mailboxes(struct mdbox_storage_rebuild_context *ctx)
{
- struct mail_user *user = ctx->storage->storage.user;
+ struct mail_user *user = ctx->storage->storage.storage.user;
struct mail_namespace *ns;
for (ns = user->namespaces; ns != NULL; ns = ns->next) {
- if (ns->storage == &ctx->storage->storage &&
+ if (ns->storage == &ctx->storage->storage.storage &&
ns->alias_for == NULL) {
if (rebuild_namespace_mailboxes(ctx, ns) < 0)
return -1;
return 0;
}
-static int rebuild_restore_msg(struct dbox_storage_rebuild_context *ctx,
- struct dbox_rebuild_msg *msg)
+static int rebuild_restore_msg(struct mdbox_storage_rebuild_context *ctx,
+ struct mdbox_rebuild_msg *msg)
{
- struct mail_storage *storage = &ctx->storage->storage;
+ struct mail_storage *storage = &ctx->storage->storage.storage;
struct dbox_file *file;
const struct mail_index_header *hdr;
- struct dbox_mail_index_record dbox_rec;
+ struct mdbox_mail_index_record dbox_rec;
const char *mailbox = NULL;
struct mailbox *box;
- struct dbox_mailbox *mbox;
+ struct mdbox_mailbox *mbox;
enum mail_error error;
- bool expunged, created;
+ bool deleted, created;
uoff_t size;
int ret;
uint32_t seq;
/* first see if message contains the mailbox it was originally
saved to */
- file = dbox_file_init_multi(ctx->storage, msg->file_id);
- ret = dbox_file_get_mail_stream(file, msg->offset, &size, NULL,
- &expunged);
- if (ret > 0 && !expunged && dbox_file_metadata_read(file) > 0) {
+ file = mdbox_file_init(ctx->storage, msg->file_id);
+ ret = dbox_file_open(file, &deleted);
+ if (ret > 0)
+ ret = dbox_file_get_mail_stream(file, msg->offset, &size, NULL);
+ if (ret > 0 && !deleted && dbox_file_metadata_read(file) > 0) {
mailbox = dbox_file_metadata_get(file,
DBOX_METADATA_ORIG_MAILBOX);
}
dbox_file_unref(&file);
- if (ret <= 0 || expunged) {
+ if (ret <= 0 || deleted) {
if (ret < 0)
return -1;
/* we shouldn't get here, so apparently we couldn't fix
strcmp(mailbox, ctx->prev_msg.box->name) == 0 ?
ctx->prev_msg.box : NULL;
while (box == NULL) {
- box = dbox_mailbox_alloc(storage, ctx->default_list,
- mailbox, NULL,
- MAILBOX_FLAG_READONLY |
- MAILBOX_FLAG_KEEP_RECENT |
- MAILBOX_FLAG_IGNORE_ACLS);
+ box = mdbox_mailbox_alloc(storage, ctx->default_list,
+ mailbox, NULL,
+ MAILBOX_FLAG_READONLY |
+ MAILBOX_FLAG_KEEP_RECENT |
+ MAILBOX_FLAG_IGNORE_ACLS);
if (dbox_mailbox_open(box) == 0)
break;
return -1;
}
}
- mbox = (struct dbox_mailbox *)box;
+ mbox = (struct mdbox_mailbox *)box;
/* switch the mailbox cache if necessary */
if (box != ctx->prev_msg.box && ctx->prev_msg.box != NULL) {
dbox_rec.map_uid = msg->map_uid;
dbox_rec.save_date = ioloop_time;
mail_index_append(ctx->prev_msg.trans, ctx->prev_msg.next_uid++, &seq);
- mail_index_update_ext(ctx->prev_msg.trans, seq, mbox->dbox_ext_id,
+ mail_index_update_ext(ctx->prev_msg.trans, seq, mbox->ext_id,
&dbox_rec, NULL);
mail_index_update_ext(ctx->prev_msg.trans, seq, mbox->guid_ext_id,
msg->guid_128, NULL);
return 0;
}
-static int rebuild_handle_zero_refs(struct dbox_storage_rebuild_context *ctx)
+static int rebuild_handle_zero_refs(struct mdbox_storage_rebuild_context *ctx)
{
- struct dbox_rebuild_msg **msgs;
+ struct mdbox_rebuild_msg **msgs;
unsigned int i, count;
/* if we have messages at this point which have refcount=0, they're
return 0;
}
-static void rebuild_update_refcounts(struct dbox_storage_rebuild_context *ctx)
+static void rebuild_update_refcounts(struct mdbox_storage_rebuild_context *ctx)
{
const struct mail_index_header *hdr;
const void *data;
- struct dbox_rebuild_msg **msgs;
+ struct mdbox_rebuild_msg **msgs;
const uint16_t *ref16_p;
bool expunged;
uint32_t seq, map_uid;
}
}
-static int rebuild_finish(struct dbox_storage_rebuild_context *ctx)
+static int rebuild_finish(struct mdbox_storage_rebuild_context *ctx)
{
if (rebuild_handle_zero_refs(ctx) < 0)
return -1;
return 0;
}
-static int dbox_storage_rebuild_scan(struct dbox_storage_rebuild_context *ctx)
+static int mdbox_storage_rebuild_scan(struct mdbox_storage_rebuild_context *ctx)
{
const struct mail_index_header *hdr;
DIR *dir;
&ctx->sync_view, &ctx->trans, 0);
if (ret <= 0) {
i_assert(ret != 0);
- mail_storage_set_internal_error(&ctx->storage->storage);
+ mail_storage_set_internal_error(&ctx->storage->storage.storage);
mail_index_reset_error(ctx->storage->map->index);
return -1;
}
dir = opendir(ctx->storage->storage_dir);
if (dir == NULL) {
- mail_storage_set_critical(&ctx->storage->storage,
+ mail_storage_set_critical(&ctx->storage->storage.storage,
"opendir(%s) failed: %m", ctx->storage->storage_dir);
return -1;
}
dir_len = str_len(path);
for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
- if (strncmp(d->d_name, DBOX_MAIL_FILE_MULTI_PREFIX,
- strlen(DBOX_MAIL_FILE_MULTI_PREFIX)) == 0) {
+ if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX,
+ strlen(MDBOX_MAIL_FILE_PREFIX)) == 0) {
str_truncate(path, dir_len);
str_append(path, d->d_name);
T_BEGIN {
}
}
if (ret == 0 && errno != 0) {
- mail_storage_set_critical(&ctx->storage->storage,
+ mail_storage_set_critical(&ctx->storage->storage.storage,
"readdir(%s) failed: %m", ctx->storage->storage_dir);
ret = -1;
}
if (closedir(dir) < 0) {
- mail_storage_set_critical(&ctx->storage->storage,
+ mail_storage_set_critical(&ctx->storage->storage.storage,
"closedir(%s) failed: %m", ctx->storage->storage_dir);
ret = -1;
}
return 0;
}
-int dbox_storage_rebuild(struct dbox_storage *storage)
+int mdbox_storage_rebuild(struct mdbox_storage *storage)
{
- struct dbox_storage_rebuild_context *ctx;
+ struct mdbox_storage_rebuild_context *ctx;
struct stat st;
int ret;
return 0;
}
- mail_storage_set_critical(&storage->storage,
+ mail_storage_set_critical(&storage->storage.storage,
"stat(%s) failed: %m", storage->storage_dir);
return -1;
}
- storage->have_multi_msgs = TRUE;
i_warning("dbox %s: rebuilding indexes", storage->storage_dir);
- ctx = dbox_storage_rebuild_init(storage);
- ret = dbox_storage_rebuild_scan(ctx);
- dbox_storage_rebuild_deinit(ctx);
+ ctx = mdbox_storage_rebuild_init(storage);
+ ret = mdbox_storage_rebuild_scan(ctx);
+ mdbox_storage_rebuild_deinit(ctx);
if (ret == 0)
- storage->sync_rebuild = FALSE;
+ storage->storage.files_corrupted = FALSE;
return ret;
}
--- /dev/null
+#ifndef MDBOX_STORAGE_REBUILD_H
+#define MDBOX_STORAGE_REBUILD_H
+
+int mdbox_storage_rebuild(struct mdbox_storage *storage);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#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 "dbox-mail.h"
+#include "dbox-save.h"
+#include "mdbox-map.h"
+#include "mdbox-file.h"
+#include "mdbox-sync.h"
+#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)
+
+struct mdbox_mailbox_list {
+ union mailbox_list_module_context module_ctx;
+};
+
+extern struct mail_storage mdbox_storage;
+extern struct mailbox mdbox_mailbox;
+extern struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs;
+
+static MODULE_CONTEXT_DEFINE_INIT(mdbox_mailbox_list_module,
+ &mailbox_list_module_register);
+
+static struct mail_storage *mdbox_storage_alloc(void)
+{
+ struct mdbox_storage *storage;
+ pool_t pool;
+
+ pool = pool_alloconly_create("dbox storage", 512+256);
+ storage = p_new(pool, struct mdbox_storage, 1);
+ storage->storage.v = mdbox_dbox_storage_vfuncs;
+ storage->storage.storage = mdbox_storage;
+ storage->storage.storage.pool = pool;
+ return &storage->storage.storage;
+}
+
+static int
+mdbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
+ const char **error_r)
+{
+ struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+ const char *dir;
+
+ storage->set = mail_storage_get_driver_settings(_storage);
+ i_assert(storage->set->mdbox_max_open_files >= 2);
+
+ if (*ns->list->set.mailbox_dir_name == '\0') {
+ *error_r = "dbox: MAILBOXDIR must not be empty";
+ return -1;
+ }
+
+ _storage->unique_root_dir =
+ p_strdup(_storage->pool, ns->list->set.root_dir);
+
+ dir = mailbox_list_get_path(ns->list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
+ storage->storage_dir = p_strconcat(_storage->pool, dir,
+ "/"MDBOX_GLOBAL_DIR_NAME, NULL);
+ storage->alt_storage_dir = p_strconcat(_storage->pool,
+ ns->list->set.alt_dir,
+ "/"MDBOX_GLOBAL_DIR_NAME, NULL);
+ i_array_init(&storage->open_files,
+ I_MIN(storage->set->mdbox_max_open_files, 128));
+
+ storage->map = dbox_map_init(storage, ns->list, storage->storage_dir);
+ return 0;
+}
+
+static void mdbox_storage_destroy(struct mail_storage *_storage)
+{
+ struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+
+ if (storage->storage.files_corrupted) {
+ if (mdbox_storage_rebuild(storage) < 0)
+ return;
+ }
+
+ mdbox_files_free(storage);
+ dbox_map_deinit(&storage->map);
+ array_free(&storage->open_files);
+ index_storage_destroy(_storage);
+}
+
+struct mailbox *
+mdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+ const char *name, struct istream *input,
+ enum mailbox_flags flags)
+{
+ struct mdbox_mailbox *mbox;
+ pool_t pool;
+
+ /* dbox can't work without index files */
+ flags &= ~MAILBOX_FLAG_NO_INDEX_FILES;
+
+ pool = pool_alloconly_create("mdbox mailbox", 1024+512);
+ mbox = p_new(pool, struct mdbox_mailbox, 1);
+ mbox->ibox.box = mdbox_mailbox;
+ mbox->ibox.box.pool = pool;
+ mbox->ibox.box.storage = storage;
+ mbox->ibox.box.list = list;
+ mbox->ibox.mail_vfuncs = &mdbox_mail_vfuncs;
+
+ mbox->ibox.save_commit_pre = mdbox_transaction_save_commit_pre;
+ mbox->ibox.save_commit_post = mdbox_transaction_save_commit_post;
+ mbox->ibox.save_rollback = mdbox_transaction_save_rollback;
+
+ index_storage_mailbox_alloc(&mbox->ibox, name, input, flags,
+ DBOX_INDEX_PREFIX);
+ mail_index_set_fsync_types(mbox->ibox.index,
+ MAIL_INDEX_SYNC_TYPE_APPEND |
+ MAIL_INDEX_SYNC_TYPE_EXPUNGE);
+
+ mbox->ibox.index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS |
+ MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
+
+ mbox->storage = (struct mdbox_storage *)storage;
+ mbox->ext_id =
+ mail_index_ext_register(mbox->ibox.index, "mdbox", 0,
+ sizeof(struct mdbox_mail_index_record),
+ sizeof(uint32_t));
+ mbox->hdr_ext_id =
+ mail_index_ext_register(mbox->ibox.index, "mdbox-hdr",
+ sizeof(struct mdbox_index_header), 0, 0);
+ mbox->guid_ext_id =
+ mail_index_ext_register(mbox->ibox.index, "guid",
+ 0, MAIL_GUID_128_SIZE, 1);
+ return &mbox->ibox.box;
+}
+
+int mdbox_read_header(struct mdbox_mailbox *mbox,
+ struct mdbox_index_header *hdr)
+{
+ const void *data;
+ size_t data_size;
+
+ mail_index_get_header_ext(mbox->ibox.view, mbox->hdr_ext_id,
+ &data, &data_size);
+ if (data_size < MDBOX_INDEX_HEADER_MIN_SIZE &&
+ (!mbox->creating || data_size != 0)) {
+ mail_storage_set_critical(&mbox->storage->storage.storage,
+ "dbox %s: Invalid dbox header size",
+ mbox->ibox.box.path);
+ return -1;
+ }
+ memset(hdr, 0, sizeof(*hdr));
+ memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr)));
+ return 0;
+}
+
+void mdbox_update_header(struct mdbox_mailbox *mbox,
+ struct mail_index_transaction *trans,
+ const struct mailbox_update *update)
+{
+ struct mdbox_index_header hdr, new_hdr;
+
+ if (mdbox_read_header(mbox, &hdr) < 0)
+ memset(&hdr, 0, sizeof(hdr));
+
+ new_hdr = hdr;
+
+ if (update != NULL && !mail_guid_128_is_empty(update->mailbox_guid)) {
+ memcpy(new_hdr.mailbox_guid, update->mailbox_guid,
+ sizeof(new_hdr.mailbox_guid));
+ } else if (mail_guid_128_is_empty(new_hdr.mailbox_guid)) {
+ mail_generate_guid_128(new_hdr.mailbox_guid);
+ }
+
+ new_hdr.map_uid_validity =
+ dbox_map_get_uid_validity(mbox->storage->map);
+ if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) {
+ mail_index_update_header_ext(trans, mbox->hdr_ext_id, 0,
+ &new_hdr, sizeof(new_hdr));
+ }
+}
+
+static int mdbox_write_index_header(struct mailbox *box,
+ const struct mailbox_update *update)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+ struct mail_index_transaction *trans;
+ const struct mail_index_header *hdr;
+ uint32_t uid_validity, uid_next;
+
+ if (dbox_map_open(mbox->storage->map, TRUE) < 0)
+ return -1;
+
+ hdr = mail_index_get_header(mbox->ibox.view);
+ trans = mail_index_transaction_begin(mbox->ibox.view, 0);
+ mdbox_update_header(mbox, trans, update);
+
+ if (update != NULL && update->uid_validity != 0)
+ uid_validity = update->uid_validity;
+ else if (hdr->uid_validity == 0) {
+ /* set uidvalidity */
+ uid_validity = dbox_get_uidvalidity_next(box->list);
+ }
+
+ if (hdr->uid_validity != uid_validity) {
+ mail_index_update_header(trans,
+ offsetof(struct mail_index_header, uid_validity),
+ &uid_validity, sizeof(uid_validity), TRUE);
+ }
+ if (update != NULL && hdr->next_uid < update->min_next_uid) {
+ uid_next = update->min_next_uid;
+ mail_index_update_header(trans,
+ offsetof(struct mail_index_header, next_uid),
+ &uid_next, sizeof(uid_next), TRUE);
+ }
+ if (update != NULL && update->min_highest_modseq != 0 &&
+ mail_index_modseq_get_highest(mbox->ibox.view) <
+ update->min_highest_modseq) {
+ mail_index_update_highest_modseq(trans,
+ update->min_highest_modseq);
+ }
+
+ if (mail_index_transaction_commit(&trans) < 0) {
+ mail_storage_set_internal_error(box->storage);
+ mail_index_reset_error(mbox->ibox.index);
+ return -1;
+ }
+ return 0;
+}
+
+static int mdbox_mailbox_create_indexes(struct mailbox *box,
+ const struct mailbox_update *update)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+ const char *origin;
+ mode_t mode;
+ gid_t gid;
+ int ret;
+
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
+ /* create indexes immediately with the dbox header */
+ if (index_storage_mailbox_open(box) < 0)
+ return -1;
+ mbox->creating = TRUE;
+ ret = mdbox_write_index_header(box, update);
+ mbox->creating = FALSE;
+ if (ret < 0)
+ return -1;
+ } else if (errno != EEXIST) {
+ if (!mail_storage_set_error_from_errno(box->storage)) {
+ mail_storage_set_critical(box->storage,
+ "mkdir(%s) failed: %m", box->path);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static void mdbox_storage_get_status_guid(struct mailbox *box,
+ struct mailbox_status *status_r)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+ struct mdbox_index_header hdr;
+
+ if (mdbox_read_header(mbox, &hdr) < 0)
+ memset(&hdr, 0, sizeof(hdr));
+
+ if (mail_guid_128_is_empty(hdr.mailbox_guid)) {
+ /* regenerate it */
+ if (mdbox_write_index_header(box, NULL) < 0 ||
+ mdbox_read_header(mbox, &hdr) < 0)
+ return;
+ }
+ memcpy(status_r->mailbox_guid, hdr.mailbox_guid,
+ sizeof(status_r->mailbox_guid));
+}
+
+static void
+mdbox_storage_get_status(struct mailbox *box, enum mailbox_status_items items,
+ struct mailbox_status *status_r)
+{
+ index_storage_get_status(box, items, status_r);
+
+ if ((items & STATUS_GUID) != 0)
+ mdbox_storage_get_status_guid(box, status_r);
+}
+
+static int
+mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
+{
+ if (!box->opened) {
+ if (index_storage_mailbox_open(box) < 0)
+ return -1;
+ }
+ return mdbox_write_index_header(box, update);
+}
+
+static int
+mdbox_mailbox_unref_mails(struct mailbox_list *list, const char *path)
+{
+ 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;
+ 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_close(&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(mbox->ibox.view);
+ for (seq = 1; seq <= hdr->messages_count; seq++) {
+ mail_index_lookup_ext(mbox->ibox.view, seq, mbox->ext_id,
+ &data, &expunged);
+ dbox_rec = data;
+ if (dbox_rec == NULL) {
+ /* no multi-mails */
+ break;
+ }
+ if (dbox_rec->map_uid != 0)
+ array_append(&map_uids, &dbox_rec->map_uid, 1);
+ }
+
+ /* unreference the map_uids */
+ map_trans = dbox_map_transaction_begin(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_close(&box);
+ return ret;
+}
+
+static int
+mdbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+ struct mdbox_mailbox_list *mlist = MDBOX_LIST_CONTEXT(list);
+ const char *trash_dest;
+ int ret;
+
+ /* 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 (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
+ return -1;
+
+ 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;
+ }
+ }
+ return dbox_list_delete_mailbox2(list, name, ret, trash_dest);
+}
+
+static int
+mdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
+{
+ struct mdbox_mailbox_list *oldmlist = MDBOX_LIST_CONTEXT(oldlist);
+
+ if (oldmlist->module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children) < 0)
+ return -1;
+ return dbox_list_rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
+}
+
+static void dbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
+ struct mailbox_list *list)
+{
+ struct mdbox_mailbox_list *mlist;
+
+ mlist = p_new(list->pool, struct mdbox_mailbox_list, 1);
+ 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;
+
+ MODULE_CONTEXT_SET(list, mdbox_mailbox_list_module, mlist);
+}
+
+struct mail_storage mdbox_storage = {
+ MEMBER(name) MDBOX_STORAGE_NAME,
+ MEMBER(class_flags) MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT,
+
+ {
+ mdbox_get_setting_parser_info,
+ mdbox_storage_alloc,
+ mdbox_storage_create,
+ mdbox_storage_destroy,
+ dbox_storage_add_list,
+ dbox_storage_get_list_settings,
+ NULL,
+ mdbox_mailbox_alloc,
+ mdbox_sync_purge
+ }
+};
+
+struct mailbox mdbox_mailbox = {
+ MEMBER(name) NULL,
+ MEMBER(storage) NULL,
+ MEMBER(list) NULL,
+
+ {
+ index_storage_is_readonly,
+ index_storage_allow_new_keywords,
+ index_storage_mailbox_enable,
+ dbox_mailbox_open,
+ index_storage_mailbox_close,
+ dbox_mailbox_create,
+ mdbox_mailbox_update,
+ mdbox_storage_get_status,
+ NULL,
+ NULL,
+ mdbox_storage_sync_init,
+ index_mailbox_sync_next,
+ index_mailbox_sync_deinit,
+ NULL,
+ dbox_notify_changes,
+ index_transaction_begin,
+ index_transaction_commit,
+ index_transaction_rollback,
+ index_transaction_set_max_modseq,
+ index_keywords_create,
+ index_keywords_create_from_indexes,
+ index_keywords_ref,
+ index_keywords_unref,
+ index_keyword_is_valid,
+ index_storage_get_seq_range,
+ index_storage_get_uid_range,
+ index_storage_get_expunges,
+ NULL,
+ NULL,
+ NULL,
+ dbox_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,
+ mdbox_save_alloc,
+ mdbox_save_begin,
+ dbox_save_continue,
+ mdbox_save_finish,
+ mdbox_save_cancel,
+ mdbox_copy,
+ index_storage_is_inconsistent
+ }
+};
+
+struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs = {
+ mdbox_file_unrefed,
+ mdbox_file_create_fd,
+ mdbox_mail_open,
+ mdbox_mailbox_create_indexes
+};
--- /dev/null
+#ifndef MDBOX_STORAGE_H
+#define MDBOX_STORAGE_H
+
+#include "index-storage.h"
+#include "mailbox-list-private.h"
+#include "dbox-storage.h"
+#include "mdbox-settings.h"
+
+#define MDBOX_STORAGE_NAME "mdbox"
+#define MDBOX_GLOBAL_INDEX_PREFIX "dovecot.map.index"
+#define MDBOX_GLOBAL_DIR_NAME "storage"
+#define MDBOX_MAIL_FILE_PREFIX "m."
+#define MDBOX_MAIL_FILE_FORMAT MDBOX_MAIL_FILE_PREFIX"%u"
+
+#define MDBOX_INDEX_HEADER_MIN_SIZE (sizeof(uint32_t))
+struct mdbox_index_header {
+ uint32_t map_uid_validity;
+ uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+};
+
+struct mdbox_storage {
+ struct dbox_storage storage;
+ union mailbox_list_module_context list_module_ctx;
+ const struct mdbox_settings *set;
+
+ /* paths for storage directories */
+ const char *storage_dir, *alt_storage_dir;
+ struct dbox_map *map;
+
+ ARRAY_DEFINE(open_files, struct mdbox_file *);
+};
+
+struct mdbox_mail_index_record {
+ uint32_t map_uid;
+ /* UNIX timestamp of when the message was saved/copied to this
+ mailbox */
+ uint32_t save_date;
+};
+
+struct mdbox_mailbox {
+ struct index_mailbox ibox;
+ struct mdbox_storage *storage;
+
+ uint32_t map_uid_validity;
+ uint32_t ext_id, hdr_ext_id, guid_ext_id;
+
+ unsigned int creating:1;
+};
+
+extern struct mail_vfuncs mdbox_mail_vfuncs;
+
+struct mailbox *
+mdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+ const char *name, struct istream *input,
+ enum mailbox_flags flags);
+
+int mdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r);
+
+/* Get map_uid for wanted message. */
+int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view,
+ uint32_t seq, uint32_t *map_uid_r);
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list);
+int mdbox_read_header(struct mdbox_mailbox *mbox,
+ struct mdbox_index_header *hdr);
+void mdbox_update_header(struct mdbox_mailbox *mbox,
+ struct mail_index_transaction *trans,
+ const struct mailbox_update *update);
+
+struct mail_save_context *
+mdbox_save_alloc(struct mailbox_transaction_context *_t);
+int mdbox_save_begin(struct mail_save_context *ctx, struct istream *input);
+int mdbox_save_finish(struct mail_save_context *ctx);
+void mdbox_save_cancel(struct mail_save_context *ctx);
+
+struct dbox_file *
+mdbox_save_file_get_file(struct mailbox_transaction_context *t,
+ uint32_t seq, uoff_t *offset_r);
+
+int mdbox_transaction_save_commit_pre(struct mail_save_context *ctx);
+void mdbox_transaction_save_commit_post(struct mail_save_context *ctx);
+void mdbox_transaction_save_rollback(struct mail_save_context *ctx);
+
+int mdbox_copy(struct mail_save_context *ctx, struct mail *mail);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "hex-binary.h"
+#include "str.h"
+#include "mdbox-storage.h"
+#include "mdbox-storage-rebuild.h"
+#include "mdbox-map.h"
+#include "mdbox-file.h"
+#include "mdbox-sync.h"
+
+#define DBOX_REBUILD_COUNT 3
+
+static int
+dbox_sync_verify_expunge_guid(struct mdbox_sync_context *ctx, uint32_t seq,
+ const uint8_t guid_128[MAIL_GUID_128_SIZE])
+{
+ const void *data;
+ uint32_t uid;
+
+ mail_index_lookup_uid(ctx->sync_view, seq, &uid);
+ mail_index_lookup_ext(ctx->sync_view, seq,
+ ctx->mbox->guid_ext_id, &data, NULL);
+ if (mail_guid_128_is_empty(guid_128) ||
+ memcmp(data, guid_128, MAIL_GUID_128_SIZE) == 0)
+ return 0;
+
+ mail_storage_set_critical(&ctx->mbox->storage->storage.storage,
+ "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s",
+ ctx->mbox->ibox.box.vname, uid,
+ binary_to_hex(data, MAIL_GUID_128_SIZE),
+ binary_to_hex(guid_128, MAIL_GUID_128_SIZE));
+ ctx->mbox->storage->storage.files_corrupted = TRUE;
+ return -1;
+}
+
+static int mdbox_sync_expunge(struct mdbox_sync_context *ctx, uint32_t seq,
+ const uint8_t guid_128[MAIL_GUID_128_SIZE])
+{
+ uint32_t map_uid;
+
+ if (seq_range_exists(&ctx->expunged_seqs, seq)) {
+ /* already marked as expunged in this sync */
+ return 0;
+ }
+
+ if (dbox_sync_verify_expunge_guid(ctx, seq, guid_128) < 0)
+ return -1;
+
+ if (mdbox_mail_lookup(ctx->mbox, ctx->sync_view, seq, &map_uid) < 0)
+ return -1;
+
+ seq_range_array_add(&ctx->expunged_seqs, 0, seq);
+ array_append(&ctx->expunged_map_uids, &map_uid, 1);
+ return 0;
+}
+
+static int mdbox_sync_add(struct mdbox_sync_context *ctx,
+ const struct mail_index_sync_rec *sync_rec)
+{
+ uint32_t seq, seq1, seq2;
+
+ if (sync_rec->type != MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
+ /* not interested */
+ return 0;
+ }
+
+ if (!mail_index_lookup_seq_range(ctx->sync_view,
+ sync_rec->uid1, sync_rec->uid2,
+ &seq1, &seq2)) {
+ /* already expunged everything. nothing to do. */
+ return 0;
+ }
+
+ for (seq = seq1; seq <= seq2; seq++) {
+ if (mdbox_sync_expunge(ctx, seq, sync_rec->guid_128) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static void dbox_sync_mark_expunges(struct mdbox_sync_context *ctx)
+{
+ struct mailbox *box = &ctx->mbox->ibox.box;
+ struct seq_range_iter iter;
+ unsigned int n;
+ const void *data;
+ uint32_t seq, uid;
+
+ seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0;
+ while (seq_range_array_iter_nth(&iter, n++, &seq)) {
+ mail_index_lookup_uid(ctx->sync_view, seq, &uid);
+ mail_index_lookup_ext(ctx->sync_view, seq,
+ ctx->mbox->guid_ext_id, &data, NULL);
+ mail_index_expunge_guid(ctx->trans, seq, data);
+
+ if (box->v.sync_notify != NULL)
+ box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE);
+ }
+}
+
+static int mdbox_sync_index_finish_expunges(struct mdbox_sync_context *ctx)
+{
+ struct dbox_map_transaction_context *map_trans;
+ int ret;
+
+ /* prevent a user from saving + expunging messages all the time and
+ using lots of disk space. but avoid doing this in situations where
+ a user simply expunges a lot of mail for the first time. that's why
+ we do this calculation before sync, not after: the purging is
+ triggered only after the second expunge. */
+ if ((ctx->flags & MDBOX_SYNC_FLAG_NO_PURGE) == 0 &&
+ dbox_map_want_purge(ctx->mbox->storage->map))
+ ctx->purge = TRUE;
+
+ map_trans = dbox_map_transaction_begin(ctx->mbox->storage->map, FALSE);
+ ret = dbox_map_update_refcounts(map_trans, &ctx->expunged_map_uids, -1);
+ if (ret == 0) {
+ ret = dbox_map_transaction_commit(map_trans);
+ if (ret == 0)
+ dbox_sync_mark_expunges(ctx);
+ }
+
+ dbox_map_transaction_free(&map_trans);
+ return ret;
+}
+
+static int mdbox_sync_index(struct mdbox_sync_context *ctx)
+{
+ struct mailbox *box = &ctx->mbox->ibox.box;
+ const struct mail_index_header *hdr;
+ struct mail_index_sync_rec sync_rec;
+ uint32_t seq1, seq2;
+ int ret = 0;
+
+ hdr = mail_index_get_header(ctx->sync_view);
+ if (hdr->uid_validity == 0) {
+ /* newly created index file */
+ return 0;
+ }
+
+ /* mark the newly seen messages as recent */
+ if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
+ hdr->next_uid, &seq1, &seq2)) {
+ index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view,
+ seq1, seq2);
+ }
+
+ /* read all changes and group changes to same file_id together */
+ i_array_init(&ctx->expunged_seqs, 64);
+ i_array_init(&ctx->expunged_map_uids, 64);
+ while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
+ if ((ret = mdbox_sync_add(ctx, &sync_rec)) < 0)
+ break;
+ }
+ if (ret == 0 && array_count(&ctx->expunged_seqs) > 0)
+ ret = mdbox_sync_index_finish_expunges(ctx);
+
+ if (box->v.sync_notify != NULL)
+ box->v.sync_notify(box, 0, 0);
+
+ array_free(&ctx->expunged_seqs);
+ array_free(&ctx->expunged_map_uids);
+ return ret == 0 ? 1 :
+ (ctx->mbox->storage->storage.files_corrupted ? 0 : -1);
+}
+
+static int mdbox_refresh_header(struct mdbox_mailbox *mbox, bool retry)
+{
+ struct mail_index_view *view;
+ struct mdbox_index_header hdr;
+ int ret;
+
+ view = mail_index_view_open(mbox->ibox.index);
+ ret = mdbox_read_header(mbox, &hdr);
+ mail_index_view_close(&view);
+
+ if (ret == 0) {
+ ret = mbox->storage->storage.files_corrupted ? -1 : 0;
+ } else if (retry) {
+ (void)mail_index_refresh(mbox->ibox.index);
+ return mdbox_refresh_header(mbox, FALSE);
+ }
+ return ret;
+}
+
+int mdbox_sync_begin(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags,
+ struct mdbox_sync_context **ctx_r)
+{
+ struct mail_storage *storage = mbox->ibox.box.storage;
+ struct mdbox_sync_context *ctx;
+ enum mail_index_sync_flags sync_flags = 0;
+ unsigned int i;
+ int ret;
+ bool rebuild, storage_rebuilt = FALSE;
+
+ rebuild = mdbox_refresh_header(mbox, TRUE) < 0 ||
+ (flags & MDBOX_SYNC_FLAG_FORCE_REBUILD) != 0;
+ if (rebuild) {
+ if (mdbox_storage_rebuild(mbox->storage) < 0)
+ return -1;
+ index_mailbox_reset_uidvalidity(&mbox->ibox);
+ storage_rebuilt = TRUE;
+ }
+
+ ctx = i_new(struct mdbox_sync_context, 1);
+ ctx->mbox = mbox;
+ ctx->flags = flags;
+
+ if ((mbox->ibox.box.flags & MAILBOX_FLAG_KEEP_RECENT) == 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
+ if (!rebuild && (flags & MDBOX_SYNC_FLAG_FORCE) == 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES;
+ if ((flags & MDBOX_SYNC_FLAG_FSYNC) != 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_FSYNC;
+ /* don't write unnecessary dirty flag updates */
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES;
+
+ for (i = 0;; i++) {
+ ret = mail_index_sync_begin(mbox->ibox.index,
+ &ctx->index_sync_ctx,
+ &ctx->sync_view, &ctx->trans,
+ sync_flags);
+ if (ret <= 0) {
+ if (ret < 0)
+ mail_storage_set_index_error(&mbox->ibox);
+ i_free(ctx);
+ *ctx_r = NULL;
+ return ret;
+ }
+
+ /* now that we're locked, check again if we want to rebuild */
+ if (mdbox_refresh_header(mbox, FALSE) < 0)
+ ret = 0;
+ else {
+ if ((ret = mdbox_sync_index(ctx)) > 0)
+ break;
+ }
+
+ /* failure. keep the index locked while we're doing a
+ rebuild. */
+ if (ret == 0) {
+ if (!storage_rebuilt) {
+ /* we'll need to rebuild storage too.
+ try again from the beginning. */
+ mbox->storage->storage.files_corrupted = TRUE;
+ mail_index_sync_rollback(&ctx->index_sync_ctx);
+ i_free(ctx);
+ return mdbox_sync_begin(mbox, flags, ctx_r);
+ }
+ mail_storage_set_critical(storage,
+ "dbox %s: Storage keeps breaking",
+ ctx->mbox->ibox.box.path);
+ ret = -1;
+ }
+ mail_index_sync_rollback(&ctx->index_sync_ctx);
+ if (ret < 0) {
+ i_free(ctx);
+ return -1;
+ }
+ }
+
+ *ctx_r = ctx;
+ return 0;
+}
+
+int mdbox_sync_finish(struct mdbox_sync_context **_ctx, bool success)
+{
+ struct mdbox_sync_context *ctx = *_ctx;
+ int ret = success ? 0 : -1;
+
+ *_ctx = NULL;
+
+ if (success) {
+ if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) {
+ mail_storage_set_index_error(&ctx->mbox->ibox);
+ ret = -1;
+ }
+ } else {
+ mail_index_sync_rollback(&ctx->index_sync_ctx);
+ }
+
+ if (ctx->purge)
+ (void)mdbox_sync_purge(&ctx->mbox->storage->storage.storage);
+ i_free(ctx);
+ return ret;
+}
+
+int mdbox_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags)
+{
+ struct mdbox_sync_context *sync_ctx;
+
+ if (mdbox_sync_begin(mbox, flags, &sync_ctx) < 0)
+ return -1;
+
+ if (sync_ctx == NULL)
+ return 0;
+ return mdbox_sync_finish(&sync_ctx, TRUE);
+}
+
+struct mailbox_sync_context *
+mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
+{
+ struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+ enum mdbox_sync_flags mdbox_sync_flags = 0;
+ int ret = 0;
+
+ if (!box->opened) {
+ if (mailbox_open(box) < 0)
+ ret = -1;
+ }
+
+ if (ret == 0 && (index_mailbox_want_full_sync(&mbox->ibox, flags) ||
+ mbox->storage->storage.files_corrupted)) {
+ if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0)
+ mdbox_sync_flags |= MDBOX_SYNC_FLAG_FORCE_REBUILD;
+ ret = mdbox_sync(mbox, mdbox_sync_flags);
+ }
+
+ return index_mailbox_sync_init(box, flags, ret < 0);
+}
+
+int mdbox_sync_purge(struct mail_storage *_storage)
+{
+ struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+ const ARRAY_TYPE(seq_range) *ref0_file_ids;
+ struct dbox_file *file;
+ struct seq_range_iter iter;
+ unsigned int i = 0;
+ uint32_t file_id;
+ bool deleted;
+ int ret = 0;
+
+ ref0_file_ids = dbox_map_get_zero_ref_files(storage->map);
+ seq_range_array_iter_init(&iter, ref0_file_ids); i = 0;
+ while (seq_range_array_iter_nth(&iter, i++, &file_id)) T_BEGIN {
+ file = mdbox_file_init(storage, file_id);
+ if (dbox_file_open(file, &deleted) > 0 && !deleted) {
+ if (mdbox_file_purge(file) < 0)
+ ret = -1;
+ } else {
+ dbox_map_remove_file_id(storage->map, file_id);
+ }
+ dbox_file_unref(&file);
+ } T_END;
+ return ret;
+}
--- /dev/null
+#ifndef MDBOX_SYNC_H
+#define MDBOX_SYNC_H
+
+struct mailbox;
+struct mdbox_mailbox;
+
+enum mdbox_sync_flags {
+ MDBOX_SYNC_FLAG_FORCE = 0x01,
+ MDBOX_SYNC_FLAG_FSYNC = 0x02,
+ MDBOX_SYNC_FLAG_FORCE_REBUILD = 0x04,
+ MDBOX_SYNC_FLAG_NO_PURGE = 0x08
+};
+
+struct mdbox_sync_context {
+ struct mdbox_mailbox *mbox;
+ struct mail_index_sync_ctx *index_sync_ctx;
+ struct mail_index_view *sync_view;
+ struct mail_index_transaction *trans;
+ enum mdbox_sync_flags flags;
+
+ ARRAY_TYPE(seq_range) expunged_seqs;
+ /* list of expunged map_uids. the same map_uid may be listed more than
+ once in case message has been copied multiple times to mailbox. */
+ ARRAY_TYPE(uint32_t) expunged_map_uids;
+
+ unsigned int purge:1;
+};
+
+int mdbox_sync_begin(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags,
+ struct mdbox_sync_context **ctx_r);
+int mdbox_sync_finish(struct mdbox_sync_context **ctx, bool success);
+int mdbox_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags);
+
+int mdbox_sync_purge(struct mail_storage *storage);
+
+struct mailbox_sync_context *
+mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);
+
+#endif
--- /dev/null
+noinst_LTLIBRARIES = libstorage_dbox_single.la
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index \
+ -I$(top_srcdir)/src/lib-storage/index/dbox-common
+
+libstorage_dbox_single_la_SOURCES = \
+ sdbox-file.c \
+ sdbox-mail.c \
+ sdbox-save.c \
+ sdbox-sync.c \
+ sdbox-sync-file.c \
+ sdbox-sync-rebuild.c \
+ sdbox-storage.c
+
+headers = \
+ sdbox-file.h \
+ sdbox-storage.h \
+ sdbox-sync.h
+
+if INSTALL_HEADERS
+ pkginc_libdir=$(pkgincludedir)
+ pkginc_lib_HEADERS = $(headers)
+else
+ noinst_HEADERS = $(headers)
+endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "eacces-error.h"
+#include "mkdir-parents.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+
+#include <stdio.h>
+
+static void sdbox_file_init_paths(struct sdbox_file *file, const char *fname)
+{
+ i_free(file->file.primary_path);
+ i_free(file->file.alt_path);
+ file->file.primary_path =
+ i_strdup_printf("%s/%s", file->mbox->ibox.box.path, fname);
+ if (file->mbox->alt_path != NULL) {
+ file->file.alt_path =
+ i_strdup_printf("%s/%s", file->mbox->alt_path, fname);
+ }
+}
+
+struct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid)
+{
+ struct sdbox_file *file;
+ const char *fname;
+
+ file = i_new(struct sdbox_file, 1);
+ file->file.storage = &mbox->storage->storage;
+ file->mbox = mbox;
+ T_BEGIN {
+ if (uid != 0) {
+ fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
+ sdbox_file_init_paths(file, fname);
+ file->uid = uid;
+ } else {
+ file->file.primary_path =
+ i_strdup_printf("%s/%s",
+ file->mbox->ibox.box.path,
+ dbox_generate_tmp_filename());
+ }
+ } T_END;
+ dbox_file_init(&file->file);
+
+ if (uid == 0) {
+ file->file.fd = file->file.storage->v.
+ file_create_fd(&file->file, file->file.primary_path,
+ FALSE);
+ }
+ return &file->file;
+}
+
+int sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid)
+{
+ const char *old_path, *new_fname, *new_path;
+
+ i_assert(file->uid == 0);
+ i_assert(uid != 0);
+
+ old_path = file->file.cur_path;
+ new_fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
+ new_path = t_strdup_printf("%s/%s", file->mbox->ibox.box.path,
+ new_fname);
+ if (rename(old_path, new_path) < 0) {
+ mail_storage_set_critical(&file->file.storage->storage,
+ "rename(%s, %s) failed: %m",
+ old_path, new_path);
+ return -1;
+ }
+ sdbox_file_init_paths(file, new_fname);
+ file->uid = uid;
+ return 0;
+}
+
+int sdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
+{
+ struct sdbox_file *sfile = (struct sdbox_file *)file;
+ struct mailbox *box = &sfile->mbox->ibox.box;
+ const char *p, *dir;
+ mode_t old_mask;
+ int fd;
+
+ old_mask = umask(0666 & ~box->file_create_mode);
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ umask(old_mask);
+ if (fd == -1 && errno == ENOENT && parents &&
+ (p = strrchr(path, '/')) != NULL) {
+ dir = t_strdup_until(path, p);
+ if (mkdir_parents_chgrp(dir, box->dir_create_mode,
+ box->file_create_gid,
+ box->file_create_gid_origin) < 0) {
+ mail_storage_set_critical(box->storage,
+ "mkdir_parents(%s) failed: %m", dir);
+ return -1;
+ }
+ /* try again */
+ old_mask = umask(0666 & ~box->file_create_mode);
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ umask(old_mask);
+ }
+ if (fd == -1) {
+ mail_storage_set_critical(box->storage,
+ "open(%s, O_CREAT) failed: %m", path);
+ } else if (box->file_create_gid == (gid_t)-1) {
+ /* no group change */
+ } else if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+ if (errno == EPERM) {
+ mail_storage_set_critical(box->storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ box->file_create_gid,
+ box->file_create_gid_origin));
+ } else {
+ mail_storage_set_critical(box->storage,
+ "fchown(%s, -1, %ld) failed: %m",
+ path, (long)box->file_create_gid);
+ }
+ /* continue anyway */
+ }
+ return fd;
+}
--- /dev/null
+#ifndef SDBOX_FILE_H
+#define SDBOX_FILE_H
+
+#include "dbox-file.h"
+
+struct sdbox_file {
+ struct dbox_file file;
+ struct sdbox_mailbox *mbox;
+
+ /* 0 while file is being created */
+ uint32_t uid;
+};
+
+struct dbox_file *sdbox_file_init(struct sdbox_mailbox *mbox, uint32_t uid);
+
+/* Assign UID for a newly created file (by renaming it) */
+int sdbox_file_assign_uid(struct sdbox_file *file, uint32_t uid);
+
+int sdbox_file_create_fd(struct dbox_file *file, const char *path,
+ bool parents);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "str.h"
+#include "index-mail.h"
+#include "dbox-mail.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+static void sdbox_mail_set_expunged(struct dbox_mail *mail)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)mail->imail.ibox;
+ struct mail *_mail = &mail->imail.mail.mail;
+
+ (void)mail_index_refresh(mbox->ibox.index);
+ if (mail_index_is_expunged(mbox->ibox.view, _mail->seq)) {
+ mail_set_expunged(_mail);
+ return;
+ }
+
+ mail_storage_set_critical(_mail->box->storage,
+ "Unexpectedly lost uid=%u", _mail->uid);
+ mbox->sync_rebuild = TRUE;
+}
+
+static bool sdbox_mail_file_set(struct dbox_mail *mail)
+{
+ struct mail *_mail = &mail->imail.mail.mail;
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_mail->box;
+
+ if (mail->open_file != NULL) {
+ /* already set */
+ return FALSE;
+ } else if (_mail->uid != 0) {
+ mail->open_file = sdbox_file_init(mbox, _mail->uid);
+ return FALSE;
+ } else {
+ /* mail is being saved in this transaction */
+ mail->open_file =
+ sdbox_save_file_get_file(_mail->transaction,
+ _mail->seq);
+ mail->open_file->refcount++;
+ return TRUE;
+ }
+}
+
+int sdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r)
+{
+ struct mail *_mail = &mail->imail.mail.mail;
+ bool deleted;
+
+ if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
+ mail_set_aborted(_mail);
+ return -1;
+ }
+
+ if (!sdbox_mail_file_set(mail)) {
+ if (!dbox_file_is_open(mail->open_file))
+ mail->imail.mail.stats_open_lookup_count++;
+ if (dbox_file_open(mail->open_file, &deleted) <= 0)
+ return -1;
+ if (deleted) {
+ sdbox_mail_set_expunged(mail);
+ return -1;
+ }
+ }
+
+ *file_r = mail->open_file;
+ *offset_r = 0;
+ return 0;
+}
+
+struct mail_vfuncs sdbox_mail_vfuncs = {
+ dbox_mail_close,
+ index_mail_free,
+ index_mail_set_seq,
+ index_mail_set_uid,
+ index_mail_set_uid_cache_updates,
+
+ index_mail_get_flags,
+ index_mail_get_keywords,
+ index_mail_get_keyword_indexes,
+ index_mail_get_modseq,
+ index_mail_get_parts,
+ index_mail_get_date,
+ dbox_mail_get_received_date,
+ dbox_mail_get_save_date,
+ dbox_mail_get_virtual_size,
+ dbox_mail_get_physical_size,
+ index_mail_get_first_header,
+ index_mail_get_headers,
+ index_mail_get_header_stream,
+ dbox_mail_get_stream,
+ dbox_mail_get_special,
+ index_mail_update_flags,
+ index_mail_update_keywords,
+ index_mail_update_modseq,
+ index_mail_update_uid,
+ NULL,
+ index_mail_expunge,
+ index_mail_set_cache_corrupted,
+ index_mail_get_index_mail
+};
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "fdatasync-path.h"
+#include "hex-binary.h"
+#include "hex-dec.h"
+#include "str.h"
+#include "istream.h"
+#include "istream-crlf.h"
+#include "ostream.h"
+#include "write-full.h"
+#include "index-mail.h"
+#include "mail-copy.h"
+#include "dbox-save.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+#include "sdbox-sync.h"
+
+#include <stdlib.h>
+
+struct sdbox_save_context {
+ struct dbox_save_context ctx;
+
+ struct sdbox_mailbox *mbox;
+ struct sdbox_sync_context *sync_ctx;
+ struct dbox_file_append_context *append_ctx;
+
+ uint32_t first_saved_seq;
+ ARRAY_DEFINE(files, struct dbox_file *);
+};
+
+struct dbox_file *
+sdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq)
+{
+ struct sdbox_save_context *ctx =
+ (struct sdbox_save_context *)t->save_ctx;
+ struct dbox_file *const *files;
+ unsigned int count;
+
+ i_assert(seq >= ctx->first_saved_seq);
+
+ files = array_get(&ctx->files, &count);
+ i_assert(count > 0);
+
+ return files[ctx->first_saved_seq - seq];
+}
+
+struct mail_save_context *
+sdbox_save_alloc(struct mailbox_transaction_context *t)
+{
+ struct index_transaction_context *it =
+ (struct index_transaction_context *)t;
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)t->box;
+ struct sdbox_save_context *ctx =
+ (struct sdbox_save_context *)t->save_ctx;
+
+ i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
+
+ if (ctx != NULL) {
+ /* use the existing allocated structure */
+ ctx->ctx.finished = FALSE;
+ return &ctx->ctx.ctx;
+ }
+
+ ctx = i_new(struct sdbox_save_context, 1);
+ ctx->ctx.ctx.transaction = t;
+ ctx->ctx.trans = it->trans;
+ ctx->mbox = mbox;
+ i_array_init(&ctx->files, 32);
+ t->save_ctx = &ctx->ctx.ctx;
+ return t->save_ctx;
+}
+
+int sdbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
+{
+ struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
+ struct dbox_file *file;
+ int ret;
+
+ file = sdbox_file_init(ctx->mbox, 0);
+ ctx->append_ctx = dbox_file_append_init(file);
+ ret = dbox_file_get_append_stream(ctx->append_ctx,
+ &ctx->ctx.cur_output);
+ if (ret <= 0) {
+ i_assert(ret != 0);
+ dbox_file_append_rollback(&ctx->append_ctx);
+ dbox_file_unref(&file);
+ ctx->ctx.failed = TRUE;
+ return -1;
+ }
+ ctx->ctx.cur_file = file;
+ dbox_save_begin(&ctx->ctx, input);
+
+ if (ctx->first_saved_seq == 0)
+ ctx->first_saved_seq = ctx->ctx.seq;
+
+ array_append(&ctx->files, &file, 1);
+ return ctx->ctx.failed ? -1 : 0;
+}
+
+static int dbox_save_mail_write_metadata(struct dbox_save_context *ctx,
+ struct dbox_file *file)
+{
+ struct dbox_message_header dbox_msg_hdr;
+ uoff_t message_size;
+ uint8_t guid_128[MAIL_GUID_128_SIZE];
+
+ i_assert(file->msg_header_size == sizeof(dbox_msg_hdr));
+
+ message_size = ctx->cur_output->offset -
+ file->msg_header_size - file->file_header_size;
+
+ dbox_save_write_metadata(&ctx->ctx, ctx->cur_output, NULL, guid_128);
+ dbox_msg_header_fill(&dbox_msg_hdr, message_size);
+ if (o_stream_pwrite(ctx->cur_output, &dbox_msg_hdr,
+ sizeof(dbox_msg_hdr),
+ file->file_header_size) < 0) {
+ dbox_file_set_syscall_error(file, "pwrite()");
+ return -1;
+ }
+ return 0;
+}
+
+static int dbox_save_finish_write(struct mail_save_context *_ctx)
+{
+ struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
+ struct dbox_file *const *files;
+
+ ctx->ctx.finished = TRUE;
+ if (ctx->ctx.cur_output == NULL)
+ return -1;
+
+ index_mail_cache_parse_deinit(_ctx->dest_mail,
+ _ctx->received_date, !ctx->ctx.failed);
+
+ files = array_idx_modifiable(&ctx->files, array_count(&ctx->files) - 1);
+
+ if (!ctx->ctx.failed) T_BEGIN {
+ if (dbox_save_mail_write_metadata(&ctx->ctx, *files) < 0)
+ ctx->ctx.failed = TRUE;
+ } T_END;
+
+ if (ctx->ctx.failed)
+ dbox_file_append_rollback(&ctx->append_ctx);
+ else if (dbox_file_append_commit(&ctx->append_ctx) < 0)
+ ctx->ctx.failed = TRUE;
+
+ i_stream_unref(&ctx->ctx.input);
+ dbox_file_close(*files);
+ ctx->ctx.cur_output = NULL;
+
+ return ctx->ctx.failed ? -1 : 0;
+}
+
+int sdbox_save_finish(struct mail_save_context *ctx)
+{
+ int ret;
+
+ ret = dbox_save_finish_write(ctx);
+ index_save_context_free(ctx);
+ return ret;
+}
+
+void sdbox_save_cancel(struct mail_save_context *_ctx)
+{
+ struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
+
+ ctx->failed = TRUE;
+ (void)sdbox_save_finish(_ctx);
+}
+
+static int dbox_save_assign_uids(struct sdbox_save_context *ctx,
+ const ARRAY_TYPE(seq_range) *uids)
+{
+ struct dbox_file *const *files;
+ struct seq_range_iter iter;
+ unsigned int i, count, n = 0;
+ uint32_t uid;
+ bool ret;
+
+ seq_range_array_iter_init(&iter, uids);
+ files = array_get(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ struct sdbox_file *sfile = (struct sdbox_file *)files[i];
+
+ ret = seq_range_array_iter_nth(&iter, n++, &uid);
+ i_assert(ret);
+ if (sdbox_file_assign_uid(sfile, uid) < 0)
+ return -1;
+ }
+ i_assert(!seq_range_array_iter_nth(&iter, n, &uid));
+ return 0;
+}
+
+static void dbox_save_unref_files(struct sdbox_save_context *ctx)
+{
+ struct mail_storage *storage = &ctx->mbox->storage->storage.storage;
+ struct dbox_file **files;
+ unsigned int i, count;
+
+ files = array_get_modifiable(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ if (ctx->ctx.failed) {
+ if (unlink(files[i]->cur_path) < 0) {
+ mail_storage_set_critical(storage,
+ "unlink(%s) failed: %m",
+ files[i]->cur_path);
+ }
+ }
+ dbox_file_unref(&files[i]);
+ }
+ array_free(&ctx->files);
+}
+
+int sdbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
+{
+ struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
+ struct mailbox_transaction_context *_t = _ctx->transaction;
+ const struct mail_index_header *hdr;
+
+ i_assert(ctx->ctx.finished);
+
+ if (array_count(&ctx->files) == 0)
+ return 0;
+
+ if (sdbox_sync_begin(ctx->mbox, SDBOX_SYNC_FLAG_FORCE |
+ SDBOX_SYNC_FLAG_FSYNC, &ctx->sync_ctx) < 0) {
+ sdbox_transaction_save_rollback(_ctx);
+ return -1;
+ }
+
+ /* assign UIDs for new messages */
+ hdr = mail_index_get_header(ctx->sync_ctx->sync_view);
+ mail_index_append_finish_uids(ctx->ctx.trans, hdr->next_uid,
+ &_t->changes->saved_uids);
+ if (dbox_save_assign_uids(ctx, &_t->changes->saved_uids) < 0) {
+ sdbox_transaction_save_rollback(_ctx);
+ return -1;
+ }
+
+ if (ctx->ctx.mail != NULL)
+ mail_free(&ctx->ctx.mail);
+
+ _t->changes->uid_validity = hdr->uid_validity;
+ return 0;
+}
+
+void sdbox_transaction_save_commit_post(struct mail_save_context *_ctx)
+{
+ struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
+
+ _ctx->transaction = NULL; /* transaction is already freed */
+
+ if (array_count(&ctx->files) == 0) {
+ sdbox_transaction_save_rollback(_ctx);
+ return;
+ }
+
+ if (sdbox_sync_finish(&ctx->sync_ctx, TRUE) < 0)
+ ctx->ctx.failed = TRUE;
+
+ if (!ctx->mbox->storage->storage.storage.set->fsync_disable) {
+ if (fdatasync_path(ctx->mbox->ibox.box.path) < 0) {
+ i_error("fdatasync_path(%s) failed: %m",
+ ctx->mbox->ibox.box.path);
+ }
+ }
+ sdbox_transaction_save_rollback(_ctx);
+}
+
+void sdbox_transaction_save_rollback(struct mail_save_context *_ctx)
+{
+ struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
+
+ if (!ctx->ctx.finished)
+ sdbox_save_cancel(_ctx);
+ dbox_save_unref_files(ctx);
+
+ if (ctx->sync_ctx != NULL)
+ (void)sdbox_sync_finish(&ctx->sync_ctx, FALSE);
+
+ if (ctx->ctx.mail != NULL)
+ mail_free(&ctx->ctx.mail);
+ i_free(ctx);
+}
+
+int sdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
+{
+ struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
+
+ /* FIXME: use hard linking */
+
+ ctx->finished = TRUE;
+ return mail_storage_copy(_ctx, mail);
+}
--- /dev/null
+/* Copyright (c) 2007-2009 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)
+
+struct sdbox_mailbox_list {
+ union mailbox_list_module_context module_ctx;
+};
+
+extern struct mail_storage dbox_storage;
+extern struct mailbox sdbox_mailbox;
+extern struct dbox_storage_vfuncs sdbox_dbox_storage_vfuncs;
+
+static MODULE_CONTEXT_DEFINE_INIT(sdbox_mailbox_list_module,
+ &mailbox_list_module_register);
+
+static struct mail_storage *sdbox_storage_alloc(void)
+{
+ struct sdbox_storage *storage;
+ pool_t pool;
+
+ pool = pool_alloconly_create("dbox storage", 512+256);
+ storage = p_new(pool, struct sdbox_storage, 1);
+ storage->storage.v = sdbox_dbox_storage_vfuncs;
+ storage->storage.storage = dbox_storage;
+ storage->storage.storage.pool = pool;
+ return &storage->storage.storage;
+}
+
+struct mailbox *
+sdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+ const char *name, struct istream *input,
+ enum mailbox_flags flags)
+{
+ struct sdbox_mailbox *mbox;
+ pool_t pool;
+
+ /* dbox can't work without index files */
+ flags &= ~MAILBOX_FLAG_NO_INDEX_FILES;
+
+ pool = pool_alloconly_create("dbox mailbox", 1024+512);
+ mbox = p_new(pool, struct sdbox_mailbox, 1);
+ mbox->ibox.box = sdbox_mailbox;
+ mbox->ibox.box.pool = pool;
+ mbox->ibox.box.storage = storage;
+ mbox->ibox.box.list = list;
+ mbox->ibox.mail_vfuncs = &sdbox_mail_vfuncs;
+
+ mbox->ibox.save_commit_pre = sdbox_transaction_save_commit_pre;
+ mbox->ibox.save_commit_post = sdbox_transaction_save_commit_post;
+ mbox->ibox.save_rollback = sdbox_transaction_save_rollback;
+
+ index_storage_mailbox_alloc(&mbox->ibox, name, input, flags,
+ DBOX_INDEX_PREFIX);
+ mail_index_set_fsync_types(mbox->ibox.index,
+ MAIL_INDEX_SYNC_TYPE_APPEND |
+ MAIL_INDEX_SYNC_TYPE_EXPUNGE);
+
+ mbox->ibox.index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS |
+ MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
+
+ mbox->storage = (struct sdbox_storage *)storage;
+ mbox->alt_path =
+ p_strconcat(pool, list->set.alt_dir, "/",
+ list->set.maildir_name, NULL);
+ mbox->hdr_ext_id =
+ mail_index_ext_register(mbox->ibox.index, "dbox-hdr",
+ sizeof(struct sdbox_index_header), 0, 0);
+ return &mbox->ibox.box;
+}
+
+int sdbox_read_header(struct sdbox_mailbox *mbox,
+ struct sdbox_index_header *hdr)
+{
+ const void *data;
+ size_t data_size;
+
+ mail_index_get_header_ext(mbox->ibox.view, mbox->hdr_ext_id,
+ &data, &data_size);
+ if (data_size < SDBOX_INDEX_HEADER_MIN_SIZE &&
+ (!mbox->creating || data_size != 0)) {
+ mail_storage_set_critical(&mbox->storage->storage.storage,
+ "dbox %s: Invalid dbox header size",
+ mbox->ibox.box.path);
+ return -1;
+ }
+ memset(hdr, 0, sizeof(*hdr));
+ memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr)));
+ return 0;
+}
+
+void sdbox_update_header(struct sdbox_mailbox *mbox,
+ struct mail_index_transaction *trans,
+ const struct mailbox_update *update)
+{
+ struct sdbox_index_header hdr, new_hdr;
+
+ if (sdbox_read_header(mbox, &hdr) < 0)
+ memset(&hdr, 0, sizeof(hdr));
+
+ new_hdr = hdr;
+
+ if (update != NULL && !mail_guid_128_is_empty(update->mailbox_guid)) {
+ memcpy(new_hdr.mailbox_guid, update->mailbox_guid,
+ sizeof(new_hdr.mailbox_guid));
+ } else if (mail_guid_128_is_empty(new_hdr.mailbox_guid)) {
+ mail_generate_guid_128(new_hdr.mailbox_guid);
+ }
+
+ if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) {
+ mail_index_update_header_ext(trans, mbox->hdr_ext_id, 0,
+ &new_hdr, sizeof(new_hdr));
+ }
+}
+
+static int sdbox_write_index_header(struct mailbox *box,
+ const struct mailbox_update *update)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
+ struct mail_index_transaction *trans;
+ const struct mail_index_header *hdr;
+ uint32_t uid_validity, uid_next;
+
+ hdr = mail_index_get_header(mbox->ibox.view);
+ trans = mail_index_transaction_begin(mbox->ibox.view, 0);
+ sdbox_update_header(mbox, trans, update);
+
+ if (update != NULL && update->uid_validity != 0)
+ uid_validity = update->uid_validity;
+ else if (hdr->uid_validity == 0) {
+ /* set uidvalidity */
+ uid_validity = dbox_get_uidvalidity_next(box->list);
+ }
+
+ if (hdr->uid_validity != uid_validity) {
+ mail_index_update_header(trans,
+ offsetof(struct mail_index_header, uid_validity),
+ &uid_validity, sizeof(uid_validity), TRUE);
+ }
+ if (update != NULL && hdr->next_uid < update->min_next_uid) {
+ uid_next = update->min_next_uid;
+ mail_index_update_header(trans,
+ offsetof(struct mail_index_header, next_uid),
+ &uid_next, sizeof(uid_next), TRUE);
+ }
+ if (update != NULL && update->min_highest_modseq != 0 &&
+ mail_index_modseq_get_highest(mbox->ibox.view) <
+ update->min_highest_modseq) {
+ mail_index_update_highest_modseq(trans,
+ update->min_highest_modseq);
+ }
+
+ if (mail_index_transaction_commit(&trans) < 0) {
+ mail_storage_set_internal_error(box->storage);
+ mail_index_reset_error(mbox->ibox.index);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdbox_mailbox_create_indexes(struct mailbox *box,
+ const struct mailbox_update *update)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
+ const char *origin;
+ mode_t mode;
+ gid_t gid;
+ int ret;
+
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
+ /* create indexes immediately with the dbox header */
+ if (index_storage_mailbox_open(box) < 0)
+ return -1;
+ mbox->creating = TRUE;
+ ret = sdbox_write_index_header(box, update);
+ mbox->creating = FALSE;
+ if (ret < 0)
+ return -1;
+ } else if (errno != EEXIST) {
+ if (!mail_storage_set_error_from_errno(box->storage)) {
+ mail_storage_set_critical(box->storage,
+ "mkdir(%s) failed: %m", box->path);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static void sdbox_storage_get_status_guid(struct mailbox *box,
+ struct mailbox_status *status_r)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
+ struct sdbox_index_header hdr;
+
+ if (sdbox_read_header(mbox, &hdr) < 0)
+ memset(&hdr, 0, sizeof(hdr));
+
+ if (mail_guid_128_is_empty(hdr.mailbox_guid)) {
+ /* regenerate it */
+ if (sdbox_write_index_header(box, NULL) < 0 ||
+ sdbox_read_header(mbox, &hdr) < 0)
+ return;
+ }
+ memcpy(status_r->mailbox_guid, hdr.mailbox_guid,
+ sizeof(status_r->mailbox_guid));
+}
+
+static void
+dbox_storage_get_status(struct mailbox *box, enum mailbox_status_items items,
+ struct mailbox_status *status_r)
+{
+ index_storage_get_status(box, items, status_r);
+
+ if ((items & STATUS_GUID) != 0)
+ sdbox_storage_get_status_guid(box, status_r);
+}
+
+static int
+dbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
+{
+ if (!box->opened) {
+ if (index_storage_mailbox_open(box) < 0)
+ return -1;
+ }
+ return sdbox_write_index_header(box, update);
+}
+
+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;
+
+ /* 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 (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)
+{
+ struct sdbox_mailbox_list *oldmlist = SDBOX_LIST_CONTEXT(oldlist);
+
+ if (oldmlist->module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children) < 0)
+ return -1;
+ return dbox_list_rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
+}
+
+static void sdbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
+ struct mailbox_list *list)
+{
+ struct sdbox_mailbox_list *mlist;
+
+ mlist = p_new(list->pool, struct sdbox_mailbox_list, 1);
+ 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;
+
+ MODULE_CONTEXT_SET(list, sdbox_mailbox_list_module, mlist);
+}
+
+struct mail_storage dbox_storage = {
+ MEMBER(name) SDBOX_STORAGE_NAME,
+ MEMBER(class_flags) 0,
+
+ {
+ NULL,
+ sdbox_storage_alloc,
+ NULL,
+ index_storage_destroy,
+ sdbox_storage_add_list,
+ dbox_storage_get_list_settings,
+ NULL,
+ sdbox_mailbox_alloc,
+ NULL
+ }
+};
+
+struct mailbox sdbox_mailbox = {
+ MEMBER(name) NULL,
+ MEMBER(storage) NULL,
+ MEMBER(list) NULL,
+
+ {
+ index_storage_is_readonly,
+ index_storage_allow_new_keywords,
+ index_storage_mailbox_enable,
+ dbox_mailbox_open,
+ index_storage_mailbox_close,
+ dbox_mailbox_create,
+ dbox_mailbox_update,
+ dbox_storage_get_status,
+ NULL,
+ NULL,
+ sdbox_storage_sync_init,
+ index_mailbox_sync_next,
+ index_mailbox_sync_deinit,
+ NULL,
+ dbox_notify_changes,
+ index_transaction_begin,
+ index_transaction_commit,
+ index_transaction_rollback,
+ index_transaction_set_max_modseq,
+ index_keywords_create,
+ index_keywords_create_from_indexes,
+ index_keywords_ref,
+ index_keywords_unref,
+ index_keyword_is_valid,
+ index_storage_get_seq_range,
+ index_storage_get_uid_range,
+ index_storage_get_expunges,
+ NULL,
+ NULL,
+ NULL,
+ dbox_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,
+ sdbox_save_alloc,
+ sdbox_save_begin,
+ dbox_save_continue,
+ sdbox_save_finish,
+ sdbox_save_cancel,
+ sdbox_copy,
+ index_storage_is_inconsistent
+ }
+};
+
+struct dbox_storage_vfuncs sdbox_dbox_storage_vfuncs = {
+ dbox_file_free,
+ sdbox_file_create_fd,
+ sdbox_mail_open,
+ sdbox_mailbox_create_indexes
+};
--- /dev/null
+#ifndef SDBOX_STORAGE_H
+#define SDBOX_STORAGE_H
+
+#include "index-storage.h"
+#include "dbox-storage.h"
+#include "mailbox-list-private.h"
+
+#define SDBOX_STORAGE_NAME "dbox"
+#define SDBOX_MAIL_FILE_PREFIX "u."
+#define SDBOX_MAIL_FILE_FORMAT SDBOX_MAIL_FILE_PREFIX"%u"
+
+/* Flag specifies if the message should be in primary or alternative storage */
+#define SDBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND
+
+#define SDBOX_INDEX_HEADER_MIN_SIZE (sizeof(uint32_t))
+struct sdbox_index_header {
+ uint32_t oldv1_highest_maildir_uid;
+ uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+};
+
+struct sdbox_storage {
+ struct dbox_storage storage;
+ union mailbox_list_module_context list_module_ctx;
+};
+
+struct sdbox_mailbox {
+ struct index_mailbox ibox;
+ struct sdbox_storage *storage;
+
+ uint32_t hdr_ext_id;
+ const char *alt_path;
+
+ unsigned int creating:1;
+ unsigned int sync_rebuild:1;
+};
+
+extern struct mail_vfuncs sdbox_mail_vfuncs;
+
+struct mailbox *
+sdbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
+ const char *name, struct istream *input,
+ enum mailbox_flags flags);
+
+int sdbox_mail_open(struct dbox_mail *mail, uoff_t *offset_r,
+ struct dbox_file **file_r);
+
+uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list);
+int sdbox_read_header(struct sdbox_mailbox *mbox,
+ struct sdbox_index_header *hdr);
+void sdbox_update_header(struct sdbox_mailbox *mbox,
+ struct mail_index_transaction *trans,
+ const struct mailbox_update *update);
+
+struct mail_save_context *
+sdbox_save_alloc(struct mailbox_transaction_context *_t);
+int sdbox_save_begin(struct mail_save_context *ctx, struct istream *input);
+int sdbox_save_finish(struct mail_save_context *ctx);
+void sdbox_save_cancel(struct mail_save_context *ctx);
+
+struct dbox_file *
+sdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq);
+
+int sdbox_transaction_save_commit_pre(struct mail_save_context *ctx);
+void sdbox_transaction_save_commit_post(struct mail_save_context *ctx);
+void sdbox_transaction_save_rollback(struct mail_save_context *ctx);
+
+int sdbox_copy(struct mail_save_context *ctx, struct mail *mail);
+
+#endif
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+#include "sdbox-sync.h"
+
+#include <stdlib.h>
+
+static void
+dbox_sync_file_move_if_needed(struct dbox_file *file,
+ enum sdbox_sync_entry_type type)
+{
+ bool move_to_alt = type == SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT;
+
+ if (move_to_alt != dbox_file_is_in_alt(file)) {
+ /* move the file. if it fails, nothing broke so
+ don't worry about it. */
+ if (dbox_file_try_lock(file) > 0) {
+ (void)dbox_file_move(file, move_to_alt);
+ dbox_file_unlock(file);
+ }
+ }
+}
+
+static void
+dbox_sync_mark_single_file_expunged(struct sdbox_sync_context *ctx,
+ const struct sdbox_sync_file_entry *entry)
+{
+ struct mailbox *box = &ctx->mbox->ibox.box;
+ uint32_t seq;
+
+ mail_index_lookup_seq(ctx->sync_view, entry->uid, &seq);
+ mail_index_expunge(ctx->trans, seq);
+
+ if (box->v.sync_notify != NULL)
+ box->v.sync_notify(box, entry->uid, MAILBOX_SYNC_TYPE_EXPUNGE);
+}
+
+int sdbox_sync_file(struct sdbox_sync_context *ctx,
+ const struct sdbox_sync_file_entry *entry)
+{
+ struct sdbox_mailbox *mbox = ctx->mbox;
+ struct dbox_file *file;
+ int ret = 1;
+
+ file = sdbox_file_init(mbox, entry->uid);
+ switch (entry->type) {
+ case SDBOX_SYNC_ENTRY_TYPE_EXPUNGE:
+ if (dbox_file_unlink(file) >= 0) {
+ dbox_sync_mark_single_file_expunged(ctx, entry);
+ ret = 1;
+ }
+ break;
+ case SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT:
+ case SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT:
+ dbox_sync_file_move_if_needed(file, entry->type);
+ break;
+ }
+ dbox_file_unref(&file);
+ return ret;
+}
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "dbox-sync-rebuild.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+#include "sdbox-sync.h"
+
+#include <stdlib.h>
+#include <dirent.h>
+
+static void sdbox_sync_set_uidvalidity(struct dbox_sync_rebuild_context *ctx)
+{
+ struct mailbox *box = &ctx->ibox->box;
+ uint32_t uid_validity;
+
+ /* if uidvalidity is set in the old index, use it */
+ uid_validity = mail_index_get_header(ctx->view)->uid_validity;
+ if (uid_validity == 0)
+ uid_validity = dbox_get_uidvalidity_next(box->list);
+
+ mail_index_update_header(ctx->trans,
+ offsetof(struct mail_index_header, uid_validity),
+ &uid_validity, sizeof(uid_validity), TRUE);
+}
+
+static int sdbox_sync_add_file_index(struct dbox_sync_rebuild_context *ctx,
+ struct dbox_file *file, uint32_t uid)
+{
+ uint32_t seq;
+ uoff_t size;
+ bool deleted;
+ int ret;
+
+ if ((ret = dbox_file_open(file, &deleted)) > 0) {
+ if (deleted)
+ return 0;
+ ret = dbox_file_get_mail_stream(file, 0, &size, NULL);
+ }
+
+ if (ret <= 0) {
+ if (ret < 0)
+ return -1;
+
+ i_warning("dbox: Ignoring broken file: %s", file->cur_path);
+ return 0;
+ }
+
+ mail_index_append(ctx->trans, uid, &seq);
+ T_BEGIN {
+ dbox_sync_rebuild_index_metadata(ctx, seq, uid);
+ } T_END;
+ return 0;
+}
+
+static int
+sdbox_sync_add_file(struct dbox_sync_rebuild_context *ctx,
+ const char *fname, bool primary)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)ctx->ibox;
+ struct dbox_file *file;
+ unsigned long uid;
+ char *p;
+ int ret;
+
+ if (strncmp(fname, SDBOX_MAIL_FILE_PREFIX,
+ strlen(SDBOX_MAIL_FILE_PREFIX)) != 0)
+ return 0;
+ fname += strlen(SDBOX_MAIL_FILE_PREFIX);
+
+ uid = strtoul(fname, &p, 10);
+ if (*p != '\0' || uid == 0 || uid >= (uint32_t)-1) {
+ i_warning("dbox %s: Ignoring invalid filename %s",
+ ctx->ibox->box.path, fname);
+ return 0;
+ }
+
+ file = sdbox_file_init(mbox, uid);
+ if (!primary)
+ file->cur_path = file->alt_path;
+ ret = sdbox_sync_add_file_index(ctx, file, uid);
+ dbox_file_unref(&file);
+ return ret;
+}
+
+static int sdbox_sync_index_rebuild_dir(struct dbox_sync_rebuild_context *ctx,
+ const char *path, bool primary)
+{
+ struct mail_storage *storage = ctx->ibox->box.storage;
+ DIR *dir;
+ struct dirent *d;
+ int ret = 0;
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ if (errno == ENOENT) {
+ if (!primary) {
+ /* alt directory doesn't exist, ignore */
+ return 0;
+ }
+ mailbox_set_deleted(&ctx->ibox->box);
+ return -1;
+ }
+ mail_storage_set_critical(storage,
+ "opendir(%s) failed: %m", path);
+ return -1;
+ }
+ do {
+ errno = 0;
+ if ((d = readdir(dir)) == NULL)
+ break;
+
+ ret = sdbox_sync_add_file(ctx, d->d_name, primary);
+ } while (ret >= 0);
+ if (errno != 0) {
+ mail_storage_set_critical(storage,
+ "readdir(%s) failed: %m", path);
+ ret = -1;
+ }
+
+ if (closedir(dir) < 0) {
+ mail_storage_set_critical(storage,
+ "closedir(%s) failed: %m", path);
+ ret = -1;
+ }
+ return ret;
+}
+
+static void sdbox_sync_update_header(struct dbox_sync_rebuild_context *ctx)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)ctx->ibox;
+ struct sdbox_index_header hdr;
+
+ if (sdbox_read_header(mbox, &hdr) < 0)
+ memset(&hdr, 0, sizeof(hdr));
+ if (!mail_guid_128_is_empty(hdr.mailbox_guid))
+ mail_generate_guid_128(hdr.mailbox_guid);
+ mail_index_update_header_ext(ctx->trans, mbox->hdr_ext_id, 0,
+ &hdr, sizeof(hdr));
+}
+
+static int
+sdbox_sync_index_rebuild_singles(struct dbox_sync_rebuild_context *ctx)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)ctx->ibox;
+ int ret = 0;
+
+ sdbox_sync_set_uidvalidity(ctx);
+ if (sdbox_sync_index_rebuild_dir(ctx, ctx->ibox->box.path, TRUE) < 0)
+ ret = -1;
+ else if (mbox->alt_path != NULL)
+ ret = sdbox_sync_index_rebuild_dir(ctx, mbox->alt_path, FALSE);
+ sdbox_sync_update_header(ctx);
+ return ret;
+}
+
+int sdbox_sync_index_rebuild(struct sdbox_mailbox *mbox)
+{
+ struct dbox_sync_rebuild_context *ctx;
+ struct mail_index_view *view;
+ struct mail_index_transaction *trans;
+ int ret;
+
+ view = mail_index_view_open(mbox->ibox.index);
+ trans = mail_index_transaction_begin(view,
+ MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
+
+ ctx = dbox_sync_index_rebuild_init(&mbox->ibox, view, trans);
+ ret = sdbox_sync_index_rebuild_singles(ctx);
+ dbox_sync_index_rebuild_deinit(&ctx);
+
+ if (ret < 0)
+ mail_index_transaction_rollback(&trans);
+ else
+ ret = mail_index_transaction_commit(&trans);
+ mail_index_view_close(&view);
+
+ if (ret == 0)
+ mbox->sync_rebuild = FALSE;
+ return ret;
+}
--- /dev/null
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "str.h"
+#include "hash.h"
+#include "sdbox-storage.h"
+#include "sdbox-file.h"
+#include "sdbox-sync.h"
+
+#define SDBOX_REBUILD_COUNT 3
+
+static unsigned int sdbox_sync_file_entry_hash(const void *p)
+{
+ const struct sdbox_sync_file_entry *entry = p;
+
+ return entry->uid;
+}
+
+static int sdbox_sync_file_entry_cmp(const void *p1, const void *p2)
+{
+ const struct sdbox_sync_file_entry *entry1 = p1, *entry2 = p2;
+
+ /* this is only for hashing, don't bother ever returning 1. */
+ if (entry1->uid != entry2->uid)
+ return -1;
+ return 0;
+}
+
+static int sdbox_sync_add_seq(struct sdbox_sync_context *ctx,
+ const struct mail_index_sync_rec *sync_rec,
+ uint32_t seq)
+{
+ struct sdbox_sync_file_entry *entry, lookup_entry;
+
+ i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE ||
+ sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS);
+
+ memset(&lookup_entry, 0, sizeof(lookup_entry));
+ mail_index_lookup_uid(ctx->sync_view, seq, &lookup_entry.uid);
+
+ entry = hash_table_lookup(ctx->syncs, &lookup_entry);
+ if (entry == NULL) {
+ entry = p_new(ctx->pool, struct sdbox_sync_file_entry, 1);
+ *entry = lookup_entry;
+ hash_table_insert(ctx->syncs, entry, entry);
+ }
+
+ if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE)
+ entry->type = SDBOX_SYNC_ENTRY_TYPE_EXPUNGE;
+ else if ((sync_rec->add_flags & SDBOX_INDEX_FLAG_ALT) != 0)
+ entry->type = SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT;
+ else
+ entry->type = SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT;
+ return 1;
+}
+
+static int sdbox_sync_add(struct sdbox_sync_context *ctx,
+ const struct mail_index_sync_rec *sync_rec)
+{
+ uint32_t seq, seq1, seq2;
+ int ret;
+
+ if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
+ /* we're interested */
+ } else if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS) {
+ /* we care only about alt flag changes */
+ if ((sync_rec->add_flags & SDBOX_INDEX_FLAG_ALT) == 0 &&
+ (sync_rec->remove_flags & SDBOX_INDEX_FLAG_ALT) == 0)
+ return 1;
+ } else {
+ /* not interested */
+ return 1;
+ }
+
+ if (!mail_index_lookup_seq_range(ctx->sync_view,
+ sync_rec->uid1, sync_rec->uid2,
+ &seq1, &seq2)) {
+ /* already expunged everything. nothing to do. */
+ return 1;
+ }
+
+ for (seq = seq1; seq <= seq2; seq++) {
+ if ((ret = sdbox_sync_add_seq(ctx, sync_rec, seq)) <= 0)
+ return ret;
+ }
+ return 1;
+}
+
+static int sdbox_sync_index(struct sdbox_sync_context *ctx)
+{
+ struct mailbox *box = &ctx->mbox->ibox.box;
+ const struct mail_index_header *hdr;
+ struct mail_index_sync_rec sync_rec;
+ struct hash_iterate_context *iter;
+ void *key, *value;
+ uint32_t seq1, seq2;
+ int ret = 1;
+
+ hdr = mail_index_get_header(ctx->sync_view);
+ if (hdr->uid_validity == 0) {
+ /* newly created index file */
+ return 0;
+ }
+
+ /* mark the newly seen messages as recent */
+ if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
+ hdr->next_uid, &seq1, &seq2)) {
+ index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view,
+ seq1, seq2);
+ }
+
+ /* read all changes and group changes to same file_id together */
+ ctx->pool = pool_alloconly_create("dbox sync pool", 1024*32);
+ ctx->syncs = hash_table_create(default_pool, ctx->pool, 0,
+ sdbox_sync_file_entry_hash,
+ sdbox_sync_file_entry_cmp);
+
+ while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
+ if ((ret = sdbox_sync_add(ctx, &sync_rec)) <= 0)
+ break;
+ }
+
+ if (ret > 0) {
+ /* now sync each file separately */
+ iter = hash_table_iterate_init(ctx->syncs);
+ while (hash_table_iterate(iter, &key, &value)) {
+ const struct sdbox_sync_file_entry *entry = value;
+
+ if ((ret = sdbox_sync_file(ctx, entry)) <= 0)
+ break;
+ }
+ hash_table_iterate_deinit(&iter);
+ }
+
+ if (box->v.sync_notify != NULL)
+ box->v.sync_notify(box, 0, 0);
+
+ hash_table_destroy(&ctx->syncs);
+ pool_unref(&ctx->pool);
+ return ret;
+}
+
+static int sdbox_refresh_header(struct sdbox_mailbox *mbox, bool retry)
+{
+ struct mail_index_view *view;
+ struct sdbox_index_header hdr;
+ int ret;
+
+ view = mail_index_view_open(mbox->ibox.index);
+ ret = sdbox_read_header(mbox, &hdr);
+ mail_index_view_close(&view);
+
+ if (ret == 0) {
+ ret = mbox->sync_rebuild ? -1 : 0;
+ } else if (retry) {
+ (void)mail_index_refresh(mbox->ibox.index);
+ return sdbox_refresh_header(mbox, FALSE);
+ }
+ return ret;
+}
+
+int sdbox_sync_begin(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags,
+ struct sdbox_sync_context **ctx_r)
+{
+ struct mail_storage *storage = mbox->ibox.box.storage;
+ struct sdbox_sync_context *ctx;
+ enum mail_index_sync_flags sync_flags = 0;
+ unsigned int i;
+ int ret;
+ bool rebuild;
+
+ rebuild = sdbox_refresh_header(mbox, TRUE) < 0 ||
+ (flags & SDBOX_SYNC_FLAG_FORCE_REBUILD) != 0;
+
+ ctx = i_new(struct sdbox_sync_context, 1);
+ ctx->mbox = mbox;
+ ctx->flags = flags;
+
+ if ((mbox->ibox.box.flags & MAILBOX_FLAG_KEEP_RECENT) == 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
+ if (!rebuild && (flags & SDBOX_SYNC_FLAG_FORCE) == 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES;
+ if ((flags & SDBOX_SYNC_FLAG_FSYNC) != 0)
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_FSYNC;
+ /* don't write unnecessary dirty flag updates */
+ sync_flags |= MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES;
+
+ for (i = 0;; i++) {
+ ret = mail_index_sync_begin(mbox->ibox.index,
+ &ctx->index_sync_ctx,
+ &ctx->sync_view, &ctx->trans,
+ sync_flags);
+ if (ret <= 0) {
+ if (ret < 0)
+ mail_storage_set_index_error(&mbox->ibox);
+ i_free(ctx);
+ *ctx_r = NULL;
+ return ret;
+ }
+
+ /* now that we're locked, check again if we want to rebuild */
+ if (sdbox_refresh_header(mbox, FALSE) < 0)
+ ret = 0;
+ else {
+ if ((ret = sdbox_sync_index(ctx)) > 0)
+ break;
+ }
+
+ /* failure. keep the index locked while we're doing a
+ rebuild. */
+ if (ret == 0) {
+ if (i >= SDBOX_REBUILD_COUNT) {
+ mail_storage_set_critical(storage,
+ "dbox %s: Index keeps breaking",
+ ctx->mbox->ibox.box.path);
+ ret = -1;
+ } else {
+ /* do a full resync and try again. */
+ i_warning("dbox %s: Rebuilding index",
+ ctx->mbox->ibox.box.path);
+ ret = sdbox_sync_index_rebuild(mbox);
+ }
+ }
+ mail_index_sync_rollback(&ctx->index_sync_ctx);
+ if (ret < 0) {
+ i_free(ctx);
+ return -1;
+ }
+ }
+
+ *ctx_r = ctx;
+ return 0;
+}
+
+int sdbox_sync_finish(struct sdbox_sync_context **_ctx, bool success)
+{
+ struct sdbox_sync_context *ctx = *_ctx;
+ int ret = success ? 0 : -1;
+
+ *_ctx = NULL;
+
+ if (success) {
+ if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) {
+ mail_storage_set_index_error(&ctx->mbox->ibox);
+ ret = -1;
+ }
+ } else {
+ mail_index_sync_rollback(&ctx->index_sync_ctx);
+ }
+ if (ctx->path != NULL)
+ str_free(&ctx->path);
+
+ i_free(ctx);
+ return ret;
+}
+
+int sdbox_sync(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags)
+{
+ struct sdbox_sync_context *sync_ctx;
+
+ if (sdbox_sync_begin(mbox, flags, &sync_ctx) < 0)
+ return -1;
+
+ if (sync_ctx == NULL)
+ return 0;
+ return sdbox_sync_finish(&sync_ctx, TRUE);
+}
+
+struct mailbox_sync_context *
+sdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
+{
+ struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
+ enum sdbox_sync_flags sdbox_sync_flags = 0;
+ int ret = 0;
+
+ if (!box->opened) {
+ if (mailbox_open(box) < 0)
+ ret = -1;
+ }
+
+ if (ret == 0 && (index_mailbox_want_full_sync(&mbox->ibox, flags) ||
+ mbox->sync_rebuild)) {
+ if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0)
+ sdbox_sync_flags |= SDBOX_SYNC_FLAG_FORCE_REBUILD;
+ ret = sdbox_sync(mbox, sdbox_sync_flags);
+ }
+
+ return index_mailbox_sync_init(box, flags, ret < 0);
+}
--- /dev/null
+#ifndef SDBOX_SYNC_H
+#define SDBOX_SYNC_H
+
+struct mailbox;
+struct sdbox_mailbox;
+
+enum sdbox_sync_flags {
+ SDBOX_SYNC_FLAG_FORCE = 0x01,
+ SDBOX_SYNC_FLAG_FSYNC = 0x02,
+ SDBOX_SYNC_FLAG_FORCE_REBUILD = 0x04
+};
+
+enum sdbox_sync_entry_type {
+ SDBOX_SYNC_ENTRY_TYPE_EXPUNGE,
+ SDBOX_SYNC_ENTRY_TYPE_MOVE_FROM_ALT,
+ SDBOX_SYNC_ENTRY_TYPE_MOVE_TO_ALT
+};
+
+struct sdbox_sync_file_entry {
+ uint32_t uid;
+ enum sdbox_sync_entry_type type;
+};
+
+struct sdbox_sync_context {
+ struct sdbox_mailbox *mbox;
+ struct mail_index_sync_ctx *index_sync_ctx;
+ struct mail_index_view *sync_view;
+ struct mail_index_transaction *trans;
+ enum sdbox_sync_flags flags;
+
+ string_t *path;
+ unsigned int path_dir_prefix_len;
+
+ pool_t pool;
+ struct hash_table *syncs; /* struct sdbox_sync_file_entry */
+};
+
+int sdbox_sync_begin(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags,
+ struct sdbox_sync_context **ctx_r);
+int sdbox_sync_finish(struct sdbox_sync_context **ctx, bool success);
+int sdbox_sync(struct sdbox_mailbox *mbox, enum sdbox_sync_flags flags);
+
+int sdbox_sync_file(struct sdbox_sync_context *ctx,
+ const struct sdbox_sync_file_entry *entry);
+
+int sdbox_sync_index_rebuild(struct sdbox_mailbox *mbox);
+
+struct mailbox_sync_context *
+sdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);
+
+#endif
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "maildir/maildir-storage.h"
-#include "maildir/maildir-uidlist.h"
-#include "maildir/maildir-filename.h"
-#include "dbox-storage.h"
-#include "dbox-file.h"
-#include "dbox-file-maildir.h"
-
-#include <stdlib.h>
-
-const char *dbox_file_maildir_metadata_get(struct dbox_file *file,
- enum dbox_metadata_key key)
-{
- struct stat st;
- uoff_t size;
- const char *p, *value = NULL;
-
- switch (key) {
- case DBOX_METADATA_GUID:
- p = strchr(file->fname, MAILDIR_INFO_SEP);
- value = p == NULL ? file->fname :
- t_strdup_until(file->fname, p);
- break;
- case DBOX_METADATA_RECEIVED_TIME:
- case DBOX_METADATA_SAVE_TIME:
- if (file->fd != -1) {
- if (fstat(file->fd, &st) < 0) {
- dbox_file_set_syscall_error(file, "fstat()");
- return NULL;
- }
- } else {
- if (stat(file->current_path, &st) < 0) {
- if (errno == ENOENT)
- return NULL;
- dbox_file_set_syscall_error(file, "stat()");
- return NULL;
- }
- }
- value = t_strdup_printf("%lx", (unsigned long)
- (key == DBOX_METADATA_RECEIVED_TIME ?
- st.st_mtime : st.st_ctime));
- break;
- case DBOX_METADATA_VIRTUAL_SIZE:
- if (!maildir_filename_get_size(file->fname,
- MAILDIR_EXTRA_VIRTUAL_SIZE,
- &size)) {
- value = maildir_uidlist_lookup_ext(
- file->single_mbox->maildir_uidlist,
- file->uid, MAILDIR_UIDLIST_REC_EXT_VSIZE);
- if (value == NULL)
- break;
- size = strtoull(value, NULL, 10);
- }
- value = t_strdup_printf("%llx", (unsigned long long)size);
- break;
- case DBOX_METADATA_POP3_UIDL:
- value = maildir_uidlist_lookup_ext(
- file->single_mbox->maildir_uidlist,
- file->uid, MAILDIR_UIDLIST_REC_EXT_POP3_UIDL);
- if (value != NULL && *value == '\0') {
- /* special case: use base filename */
- p = strchr(file->fname, MAILDIR_INFO_SEP);
- if (p == NULL)
- value = file->fname;
- else
- value = t_strdup_until(file->fname, p);
- }
- break;
- case DBOX_METADATA_ORIG_MAILBOX:
- case DBOX_METADATA_OLDV1_EXPUNGED:
- case DBOX_METADATA_OLDV1_FLAGS:
- case DBOX_METADATA_OLDV1_KEYWORDS:
- case DBOX_METADATA_OLDV1_SPACE:
- case DBOX_METADATA_EXT_REF:
- break;
- }
- return value;
-}
-
-bool dbox_maildir_uid_get_fname(struct dbox_mailbox *mbox, uint32_t uid,
- const char **fname_r)
-{
- enum maildir_uidlist_rec_flag flags;
-
- if (maildir_uidlist_lookup(mbox->maildir_uidlist, uid, &flags,
- fname_r) <= 0)
- return FALSE;
- return TRUE;
-}
+++ /dev/null
-#ifndef DBOX_FILE_MAILDIR_H
-#define DBOX_FILE_MAILDIR_H
-
-const char *dbox_file_maildir_metadata_get(struct dbox_file *file,
- enum dbox_metadata_key key);
-bool dbox_maildir_uid_get_fname(struct dbox_mailbox *mbox, uint32_t uid,
- const char **fname_r);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "str.h"
-#include "index-mail.h"
-#include "dbox-storage.h"
-#include "dbox-map.h"
-#include "dbox-file.h"
-
-#include <stdlib.h>
-#include <sys/stat.h>
-
-struct dbox_mail {
- struct index_mail imail;
-
- struct dbox_file *open_file;
- uoff_t offset;
- uint32_t map_uid;
-};
-
-struct mail *
-dbox_mail_alloc(struct mailbox_transaction_context *t,
- enum mail_fetch_field wanted_fields,
- struct mailbox_header_lookup_ctx *wanted_headers)
-{
- struct dbox_mail *mail;
- pool_t pool;
-
- pool = pool_alloconly_create("mail", 1024);
- mail = p_new(pool, struct dbox_mail, 1);
- mail->imail.mail.pool = pool;
-
- index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
- return &mail->imail.mail.mail;
-}
-
-static void dbox_mail_close(struct mail *_mail)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
-
- if (mail->open_file != NULL)
- dbox_file_unref(&mail->open_file);
- index_mail_close(_mail);
-}
-
-int dbox_mail_lookup(struct dbox_mailbox *mbox, struct mail_index_view *view,
- uint32_t seq, uint32_t *map_uid_r)
-{
- const struct dbox_mail_index_record *dbox_rec;
- struct dbox_index_header hdr;
- const void *data;
- uint32_t cur_map_uid_validity;
- bool expunged;
-
- mail_index_lookup_ext(view, seq, mbox->dbox_ext_id, &data, &expunged);
- dbox_rec = data;
- if (dbox_rec == NULL || dbox_rec->map_uid == 0) {
- *map_uid_r = 0;
- return 0;
- }
-
- if (mbox->map_uid_validity == 0) {
- if (dbox_read_header(mbox, &hdr) < 0) {
- mbox->storage->sync_rebuild = TRUE;
- return -1;
- }
- mbox->map_uid_validity = hdr.map_uid_validity;
- }
- if (dbox_map_open(mbox->storage->map, TRUE) < 0)
- return -1;
-
- cur_map_uid_validity = dbox_map_get_uid_validity(mbox->storage->map);
- if (cur_map_uid_validity != mbox->map_uid_validity) {
- mail_storage_set_critical(&mbox->storage->storage,
- "dbox %s: map uidvalidity mismatch (%u vs %u)",
- mbox->ibox.box.path, mbox->map_uid_validity,
- cur_map_uid_validity);
- mbox->storage->sync_rebuild = TRUE;
- return -1;
- }
- *map_uid_r = dbox_rec->map_uid;
- return 0;
-}
-
-static void dbox_mail_set_expunged(struct dbox_mail *mail)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)mail->imail.ibox;
- struct mail *_mail = &mail->imail.mail.mail;
-
- (void)mail_index_refresh(mbox->ibox.index);
- if (mail_index_is_expunged(mbox->ibox.view, _mail->seq)) {
- mail_set_expunged(_mail);
- return;
- }
-
- if (mail->map_uid != 0) {
- dbox_map_set_corrupted(mbox->storage->map,
- "Unexpectedly lost uid=%u map_uid=%u",
- _mail->uid, mail->map_uid);
- } else {
- mail_storage_set_critical(&mbox->storage->storage,
- "Unexpectedly lost uid=%u", _mail->uid);
- }
- mbox->storage->sync_rebuild = TRUE;
-}
-
-static int dbox_mail_open_init(struct dbox_mail *mail)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)mail->imail.ibox;
- struct mail *_mail = &mail->imail.mail.mail;
- uint32_t file_id;
- int ret;
-
- if (mail->map_uid == 0)
- mail->open_file = dbox_file_init_single(mbox, _mail->uid);
- else if ((ret = dbox_map_lookup(mbox->storage->map, mail->map_uid,
- &file_id, &mail->offset)) <= 0) {
- if (ret < 0)
- return -1;
-
- /* map_uid doesn't exist anymore. either it
- got just expunged or the map index is
- corrupted. */
- dbox_mail_set_expunged(mail);
- return -1;
- } else {
- mail->open_file = dbox_file_init_multi(mbox->storage, file_id);
- }
- return 0;
-}
-
-static int dbox_mail_open(struct dbox_mail *mail,
- uoff_t *offset_r, struct dbox_file **file_r)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)mail->imail.ibox;
- struct mail *_mail = &mail->imail.mail.mail;
- uint32_t prev_file_id = 0;
- bool deleted;
-
- if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
- return mail_set_aborted(_mail);
-
- do {
- if (mail->open_file != NULL) {
- /* already open */
- } else if (_mail->uid != 0) {
- if (dbox_mail_lookup(mbox, mbox->ibox.view, _mail->seq,
- &mail->map_uid) < 0)
- return -1;
- if (dbox_mail_open_init(mail) < 0)
- return -1;
- } else {
- /* mail is being saved in this transaction */
- mail->open_file =
- dbox_save_file_get_file(_mail->transaction,
- _mail->seq,
- &mail->offset);
- mail->open_file->refcount++;
- break;
- }
-
- if (!dbox_file_is_open(mail->open_file))
- mail->imail.mail.stats_open_lookup_count++;
- if (dbox_file_open(mail->open_file, &deleted) <= 0)
- return -1;
- if (deleted) {
- /* either it's expunged now or moved to another file. */
- if (mail->open_file->file_id == prev_file_id) {
- dbox_mail_set_expunged(mail);
- return -1;
- }
- prev_file_id = mail->open_file->file_id;
- if (dbox_map_refresh(mbox->storage->map) < 0)
- return -1;
- dbox_file_unref(&mail->open_file);
- }
- } while (mail->open_file == NULL);
-
- *file_r = mail->open_file;
- *offset_r = mail->offset;
- return 0;
-}
-
-static int
-dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r)
-{
- uoff_t offset, size;
- bool expunged;
-
- if (dbox_mail_open(mail, &offset, file_r) < 0)
- return -1;
-
- if (dbox_file_get_mail_stream(*file_r, offset, &size,
- NULL, &expunged) <= 0)
- return -1;
- i_assert(!expunged);
- if (dbox_file_metadata_read(*file_r) <= 0)
- return -1;
- return 0;
-}
-
-static int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
- struct index_mail_data *data = &mail->imail.data;
- struct dbox_file *file;
- const char *value;
-
- if (index_mail_get_received_date(_mail, date_r) == 0)
- return 0;
-
- if (dbox_mail_metadata_read(mail, &file) < 0)
- return -1;
-
- value = dbox_file_metadata_get(file, DBOX_METADATA_RECEIVED_TIME);
- data->received_date = value == NULL ? 0 : strtoul(value, NULL, 16);
- *date_r = data->received_date;
- return 0;
-}
-
-static bool multi_dbox_get_save_date(struct mail *mail, time_t *date_r)
-{
- struct dbox_mailbox *mbox =
- (struct dbox_mailbox *)mail->transaction->box;
- const struct dbox_mail_index_record *dbox_rec;
- const void *data;
- bool expunged;
-
- mail_index_lookup_ext(mbox->ibox.view, mail->seq,
- mbox->dbox_ext_id, &data, &expunged);
- dbox_rec = data;
- if (dbox_rec == NULL || dbox_rec->map_uid == 0)
- return FALSE;
-
- *date_r = dbox_rec->save_date;
- return TRUE;
-}
-
-static int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
- struct index_mail_data *data = &mail->imail.data;
- struct dbox_file *file;
- struct stat st;
- const char *value;
-
- if (multi_dbox_get_save_date(_mail, date_r))
- return 0;
-
- if (index_mail_get_save_date(_mail, date_r) == 0)
- return 0;
-
- if (dbox_mail_metadata_read(mail, &file) < 0)
- return -1;
-
- value = dbox_file_metadata_get(file, DBOX_METADATA_SAVE_TIME);
- data->save_date = value == NULL ? 0 : strtoul(value, NULL, 16);
-
- if (data->save_date == 0) {
- /* missing / corrupted save time - use the file's ctime */
- i_assert(file->fd != -1);
- mail->imail.mail.stats_fstat_lookup_count++;
- if (fstat(file->fd, &st) < 0) {
- mail_storage_set_critical(_mail->box->storage,
- "fstat(%s) failed: %m", file->current_path);
- return -1;
- }
- data->save_date = st.st_ctime;
- }
- *date_r = data->save_date;
- return 0;
-}
-
-static int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
- struct index_mail_data *data = &mail->imail.data;
- struct dbox_file *file;
- const char *value;
-
- if (index_mail_get_cached_virtual_size(&mail->imail, size_r))
- return 0;
-
- if (dbox_mail_metadata_read(mail, &file) < 0)
- return -1;
-
- value = dbox_file_metadata_get(file, DBOX_METADATA_VIRTUAL_SIZE);
- if (value == NULL)
- return index_mail_get_virtual_size(_mail, size_r);
-
- data->virtual_size = strtoul(value, NULL, 16);
- *size_r = data->virtual_size;
- return 0;
-}
-
-static int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
-{
- struct index_mail *mail = (struct index_mail *)_mail;
- struct index_mail_data *data = &mail->data;
- struct istream *input;
-
- if (index_mail_get_physical_size(_mail, size_r) == 0)
- return 0;
-
- if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
- return -1;
-
- i_assert(data->physical_size != (uoff_t)-1);
- *size_r = data->physical_size;
- return 0;
-}
-
-static int
-dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key,
- enum index_cache_field cache_field,
- const char **value_r)
-{
- struct index_mail *imail = &mail->imail;
- const struct mail_cache_field *cache_fields = imail->ibox->cache_fields;
- struct dbox_file *file;
- const char *value;
- string_t *str;
-
- str = str_new(imail->data_pool, 64);
- if (mail_cache_lookup_field(imail->trans->cache_view, str,
- imail->mail.mail.seq,
- cache_fields[cache_field].idx) > 0) {
- *value_r = str_c(str);
- return 0;
- }
-
- if (dbox_mail_metadata_read(mail, &file) < 0)
- return -1;
-
- value = dbox_file_metadata_get(file, key);
- if (value == NULL)
- value = "";
- index_mail_cache_add_idx(imail, cache_fields[cache_field].idx,
- value, strlen(value)+1);
- *value_r = value;
- return 0;
-}
-
-static int
-dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
- const char **value_r)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
-
- /* keep the UIDL in cache file, otherwise POP3 would open all
- mail files and read the metadata. same for GUIDs if they're
- used. */
- switch (field) {
- case MAIL_FETCH_UIDL_BACKEND:
- return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
- MAIL_CACHE_POP3_UIDL, value_r);
- case MAIL_FETCH_GUID:
- return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID,
- MAIL_CACHE_GUID, value_r);
- default:
- break;
- }
-
- return index_mail_get_special(_mail, field, value_r);
-}
-
-static int
-dbox_mail_get_stream(struct mail *_mail, struct message_size *hdr_size,
- struct message_size *body_size, struct istream **stream_r)
-{
- struct dbox_mail *mail = (struct dbox_mail *)_mail;
- struct index_mail_data *data = &mail->imail.data;
- struct istream *input;
- uoff_t offset, size;
- bool expunged;
- int ret;
-
- if (data->stream == NULL) {
- if (dbox_mail_open(mail, &offset, &mail->open_file) < 0)
- return -1;
-
- ret = dbox_file_get_mail_stream(mail->open_file, offset,
- &size, &input, &expunged);
- if (ret <= 0) {
- if (ret < 0)
- return -1;
- dbox_file_set_corrupted(mail->open_file,
- "uid=%u points to broken data at offset="
- "%"PRIuUOFF_T, _mail->uid, offset);
- return -1;
- }
- i_assert(!expunged);
- data->physical_size = size;
- data->stream = input;
- }
-
- return index_mail_init_stream(&mail->imail, hdr_size, body_size,
- stream_r);
-}
-
-struct mail_vfuncs dbox_mail_vfuncs = {
- dbox_mail_close,
- index_mail_free,
- index_mail_set_seq,
- index_mail_set_uid,
- index_mail_set_uid_cache_updates,
-
- index_mail_get_flags,
- index_mail_get_keywords,
- index_mail_get_keyword_indexes,
- index_mail_get_modseq,
- index_mail_get_parts,
- index_mail_get_date,
- dbox_mail_get_received_date,
- dbox_mail_get_save_date,
- dbox_mail_get_virtual_size,
- dbox_mail_get_physical_size,
- index_mail_get_first_header,
- index_mail_get_headers,
- index_mail_get_header_stream,
- dbox_mail_get_stream,
- dbox_mail_get_special,
- index_mail_update_flags,
- index_mail_update_keywords,
- index_mail_update_modseq,
- index_mail_update_uid,
- NULL,
- index_mail_expunge,
- index_mail_set_cache_corrupted,
- index_mail_get_index_mail
-};
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "fdatasync-path.h"
-#include "hex-binary.h"
-#include "hex-dec.h"
-#include "str.h"
-#include "istream.h"
-#include "istream-crlf.h"
-#include "ostream.h"
-#include "write-full.h"
-#include "index-mail.h"
-#include "mail-copy.h"
-#include "dbox-storage.h"
-#include "dbox-map.h"
-#include "dbox-file.h"
-#include "dbox-sync.h"
-
-#include <stdlib.h>
-
-struct dbox_save_mail {
- struct dbox_file *file;
- uint32_t seq;
- uint32_t append_offset;
- uoff_t message_size;
-};
-
-struct dbox_save_context {
- struct mail_save_context ctx;
-
- struct dbox_mailbox *mbox;
- struct mail_index_transaction *trans;
-
- struct dbox_map_append_context *append_ctx;
- struct dbox_sync_context *sync_ctx;
-
- ARRAY_TYPE(uint32_t) copy_map_uids;
- struct dbox_map_transaction_context *map_trans;
-
- /* updated for each appended mail: */
- uint32_t seq;
- struct istream *input;
- struct mail *mail;
-
- struct dbox_file *cur_file;
- struct ostream *cur_output;
-
- ARRAY_DEFINE(mails, struct dbox_save_mail);
- unsigned int single_count;
-
- unsigned int failed:1;
- unsigned int finished:1;
-};
-
-struct dbox_file *
-dbox_save_file_get_file(struct mailbox_transaction_context *t,
- uint32_t seq, uoff_t *offset_r)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)t->save_ctx;
- const struct dbox_save_mail *mails, *mail;
- unsigned int count;
-
- mails = array_get(&ctx->mails, &count);
- i_assert(count > 0);
- i_assert(seq >= mails[0].seq);
-
- mail = &mails[mails[0].seq - seq];
- i_assert(mail->seq == seq);
-
- if (o_stream_flush(mail->file->output) < 0) {
- dbox_file_set_syscall_error(mail->file, "write()");
- ctx->failed = TRUE;
- }
-
- *offset_r = mail->append_offset;
- return mail->file;
-}
-
-struct mail_save_context *
-dbox_save_alloc(struct mailbox_transaction_context *t)
-{
- struct index_transaction_context *it =
- (struct index_transaction_context *)t;
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)t->box;
- struct dbox_save_context *ctx = (struct dbox_save_context *)t->save_ctx;
-
- i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
-
- if (ctx != NULL) {
- /* use the existing allocated structure */
- ctx->finished = FALSE;
- return &ctx->ctx;
- }
-
- ctx = i_new(struct dbox_save_context, 1);
- ctx->ctx.transaction = t;
- ctx->mbox = mbox;
- ctx->trans = it->trans;
- ctx->append_ctx = dbox_map_append_begin(mbox);
- i_array_init(&ctx->mails, 32);
- t->save_ctx = &ctx->ctx;
- return t->save_ctx;
-}
-
-static void dbox_save_add_to_index(struct dbox_save_context *ctx)
-{
- enum mail_flags save_flags;
-
- save_flags = ctx->ctx.flags & ~MAIL_RECENT;
- mail_index_append(ctx->trans, ctx->ctx.uid, &ctx->seq);
- mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
- save_flags);
- if (ctx->ctx.keywords != NULL) {
- mail_index_update_keywords(ctx->trans, ctx->seq,
- MODIFY_REPLACE, ctx->ctx.keywords);
- }
- if (ctx->ctx.min_modseq != 0) {
- mail_index_update_modseq(ctx->trans, ctx->seq,
- ctx->ctx.min_modseq);
- }
-}
-
-int dbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
- struct dbox_message_header dbox_msg_hdr;
- struct dbox_save_mail *save_mail;
- struct istream *crlf_input;
- uoff_t mail_size;
-
- /* get the size of the mail to be saved, if possible */
- if (i_stream_get_size(input, TRUE, &mail_size) <= 0)
- mail_size = 0;
- if (dbox_map_append_next(ctx->append_ctx, mail_size,
- &ctx->cur_file, &ctx->cur_output) < 0) {
- ctx->failed = TRUE;
- return -1;
- }
-
- dbox_save_add_to_index(ctx);
-
- if (_ctx->dest_mail == NULL) {
- if (ctx->mail == NULL)
- ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
- _ctx->dest_mail = ctx->mail;
- }
- mail_set_seq(_ctx->dest_mail, ctx->seq);
-
- crlf_input = i_stream_create_lf(input);
- ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input);
- i_stream_unref(&crlf_input);
-
- save_mail = array_append_space(&ctx->mails);
- save_mail->file = ctx->cur_file;
- save_mail->seq = ctx->seq;
- i_assert(ctx->cur_output->offset <= (uint32_t)-1);
- save_mail->append_offset = ctx->cur_output->offset;
-
- /* write a dummy header. it'll get rewritten when we're finished */
- memset(&dbox_msg_hdr, 0, sizeof(dbox_msg_hdr));
- o_stream_cork(ctx->cur_output);
- if (o_stream_send(ctx->cur_output, &dbox_msg_hdr,
- sizeof(dbox_msg_hdr)) < 0) {
- mail_storage_set_critical(_ctx->transaction->box->storage,
- "o_stream_send(%s) failed: %m",
- ctx->cur_file->current_path);
- ctx->failed = TRUE;
- }
-
- if (_ctx->received_date == (time_t)-1)
- _ctx->received_date = ioloop_time;
- return ctx->failed ? -1 : 0;
-}
-
-int dbox_save_continue(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
- struct mail_storage *storage = &ctx->mbox->storage->storage;
-
- if (ctx->failed)
- return -1;
-
- do {
- if (o_stream_send_istream(ctx->cur_output, ctx->input) < 0) {
- if (!mail_storage_set_error_from_errno(storage)) {
- mail_storage_set_critical(storage,
- "o_stream_send_istream(%s) failed: %m",
- ctx->cur_file->current_path);
- }
- ctx->failed = TRUE;
- return -1;
- }
- index_mail_cache_parse_continue(_ctx->dest_mail);
-
- /* both tee input readers may consume data from our primary
- input stream. we'll have to make sure we don't return with
- one of the streams still having data in them. */
- } while (i_stream_read(ctx->input) > 0);
- return 0;
-}
-
-static void dbox_save_write_metadata(struct dbox_save_context *ctx)
-{
- struct dbox_metadata_header metadata_hdr;
- uint8_t guid_128[MAIL_GUID_128_SIZE];
- const char *guid;
- string_t *str;
- uoff_t vsize;
-
- memset(&metadata_hdr, 0, sizeof(metadata_hdr));
- memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
- sizeof(metadata_hdr.magic_post));
- o_stream_send(ctx->cur_output, &metadata_hdr, sizeof(metadata_hdr));
-
- str = t_str_new(256);
- str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
- (unsigned long)ctx->ctx.received_date);
- str_printfa(str, "%c%lx\n", DBOX_METADATA_SAVE_TIME,
- (unsigned long)ioloop_time);
- if (mail_get_virtual_size(ctx->ctx.dest_mail, &vsize) < 0)
- i_unreached();
- str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
- (unsigned long long)vsize);
-
- guid = ctx->ctx.guid;
- if (ctx->ctx.guid != NULL)
- mail_generate_guid_128_hash(guid, guid_128);
- else {
- mail_generate_guid_128(guid_128);
- guid = binary_to_hex(guid_128, sizeof(guid_128));
- }
- if (ctx->cur_file->single_mbox == NULL) {
- /* multi-file: save the 128bit GUID to index so if the map
- index gets corrupted we can still find the message */
- mail_index_update_ext(ctx->trans, ctx->seq,
- ctx->mbox->guid_ext_id,
- guid_128, NULL);
- }
- str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
- if (ctx->cur_file->single_mbox == NULL &&
- strchr(ctx->mbox->ibox.box.name, '\r') == NULL &&
- strchr(ctx->mbox->ibox.box.name, '\n') == NULL) {
- /* multi-file: save the original mailbox name so if mailbox
- indexes get corrupted we can place at least some
- (hopefully most) of the messages to correct mailboxes. */
- str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
- ctx->mbox->ibox.box.name);
- }
-
- str_append_c(str, '\n');
- o_stream_send(ctx->cur_output, str_data(str), str_len(str));
-}
-
-static int dbox_save_mail_write_metadata(struct dbox_save_context *ctx,
- struct dbox_save_mail *mail)
-{
- struct dbox_message_header dbox_msg_hdr;
-
- i_assert(mail->file->msg_header_size == sizeof(dbox_msg_hdr));
-
- dbox_save_write_metadata(ctx);
- dbox_msg_header_fill(&dbox_msg_hdr, mail->message_size);
- if (o_stream_pwrite(ctx->cur_output, &dbox_msg_hdr,
- sizeof(dbox_msg_hdr), mail->append_offset) < 0) {
- dbox_file_set_syscall_error(mail->file, "pwrite()");
- return -1;
- }
- if (mail->file->single_mbox != NULL) {
- /* we're done writing to single-files now */
- if (dbox_file_flush_append(mail->file) < 0)
- return -1;
- }
- return 0;
-}
-
-static int dbox_save_finish_write(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
- struct dbox_save_mail *save_mail;
- unsigned int count;
-
- ctx->finished = TRUE;
- if (ctx->cur_output == NULL)
- return -1;
-
- index_mail_cache_parse_deinit(_ctx->dest_mail,
- _ctx->received_date, !ctx->failed);
-
- count = array_count(&ctx->mails);
- save_mail = array_idx_modifiable(&ctx->mails, count - 1);
-
- if (!ctx->failed) T_BEGIN {
- save_mail->message_size = ctx->cur_output->offset -
- save_mail->append_offset -
- save_mail->file->msg_header_size;
-
- if (dbox_save_mail_write_metadata(ctx, save_mail) < 0)
- ctx->failed = TRUE;
- } T_END;
-
- if (o_stream_flush(ctx->cur_output) < 0) {
- dbox_file_set_syscall_error(save_mail->file, "write()");
- ctx->failed = TRUE;
- }
-
- o_stream_unref(&ctx->cur_output);
- i_stream_unref(&ctx->input);
-
- if (ctx->failed) {
- dbox_file_cancel_append(save_mail->file,
- save_mail->append_offset);
- array_delete(&ctx->mails, count - 1, 1);
- return -1;
- }
-
- if (save_mail->file->single_mbox != NULL) {
- dbox_file_close(save_mail->file);
- ctx->single_count++;
- } else {
- dbox_map_append_finish_multi_mail(ctx->append_ctx);
- }
- return 0;
-}
-
-int dbox_save_finish(struct mail_save_context *ctx)
-{
- int ret;
-
- ret = dbox_save_finish_write(ctx);
- index_save_context_free(ctx);
- return ret;
-}
-
-void dbox_save_cancel(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
-
- ctx->failed = TRUE;
- (void)dbox_save_finish(_ctx);
-}
-
-int dbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
- struct mailbox_transaction_context *_t = _ctx->transaction;
- const struct mail_index_header *hdr;
- uint32_t first_map_uid, last_map_uid;
-
- i_assert(ctx->finished);
-
- /* lock the mailbox before map to avoid deadlocks */
- if (dbox_sync_begin(ctx->mbox, DBOX_SYNC_FLAG_NO_PURGE |
- DBOX_SYNC_FLAG_FORCE |
- DBOX_SYNC_FLAG_FSYNC, &ctx->sync_ctx) < 0) {
- dbox_transaction_save_rollback(_ctx);
- return -1;
- }
-
- /* get map UIDs for messages saved to multi-files. they're written
- to transaction log immediately within this function, but the map
- is left locked. */
- if (dbox_map_append_assign_map_uids(ctx->append_ctx, &first_map_uid,
- &last_map_uid) < 0) {
- dbox_transaction_save_rollback(_ctx);
- return -1;
- }
-
- /* assign UIDs for new messages */
- hdr = mail_index_get_header(ctx->sync_ctx->sync_view);
- mail_index_append_finish_uids(ctx->trans, hdr->next_uid,
- &_t->changes->saved_uids);
-
- /* if we saved any single-files, rename the files to contain UIDs */
- if (ctx->single_count > 0) {
- if (dbox_map_append_assign_uids(ctx->append_ctx,
- &_t->changes->saved_uids) < 0) {
- dbox_transaction_save_rollback(_ctx);
- return -1;
- }
- }
-
- /* add map_uids for all messages saved to multi-files */
- if (first_map_uid != 0) {
- struct dbox_mail_index_record rec;
- const struct dbox_save_mail *mails;
- unsigned int i, count;
- uint32_t next_map_uid = first_map_uid;
-
- dbox_update_header(ctx->mbox, ctx->trans, NULL);
-
- memset(&rec, 0, sizeof(rec));
- rec.save_date = ioloop_time;
- mails = array_get(&ctx->mails, &count);
- for (i = 0; i < count; i++) {
- if (mails[i].file->single_mbox != NULL)
- continue;
-
- rec.map_uid = next_map_uid++;
- mail_index_update_ext(ctx->trans, mails[i].seq,
- ctx->mbox->dbox_ext_id,
- &rec, NULL);
- }
- i_assert(next_map_uid == last_map_uid + 1);
- }
-
- /* increase map's refcount for copied mails */
- if (array_is_created(&ctx->copy_map_uids)) {
- ctx->map_trans =
- dbox_map_transaction_begin(ctx->mbox->storage->map,
- FALSE);
- if (dbox_map_update_refcounts(ctx->map_trans,
- &ctx->copy_map_uids, 1) < 0) {
- dbox_transaction_save_rollback(_ctx);
- return -1;
- }
- }
-
- if (ctx->mail != NULL)
- mail_free(&ctx->mail);
-
- _t->changes->uid_validity = hdr->uid_validity;
- return 0;
-}
-
-void dbox_transaction_save_commit_post(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
-
- _ctx->transaction = NULL; /* transaction is already freed */
-
- /* finish writing the mailbox APPENDs */
- if (dbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
- if (ctx->map_trans != NULL)
- (void)dbox_map_transaction_commit(ctx->map_trans);
- /* commit only updates the sync tail offset, everything else
- was already written at this point. */
- (void)dbox_map_append_commit(ctx->append_ctx);
- }
- dbox_map_append_free(&ctx->append_ctx);
-
- if (!ctx->mbox->storage->storage.set->fsync_disable) {
- if (fdatasync_path(ctx->mbox->ibox.box.path) < 0) {
- i_error("fdatasync_path(%s) failed: %m",
- ctx->mbox->ibox.box.path);
- }
- }
- dbox_transaction_save_rollback(_ctx);
-}
-
-void dbox_transaction_save_rollback(struct mail_save_context *_ctx)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
-
- if (!ctx->finished)
- dbox_save_cancel(&ctx->ctx);
- if (ctx->append_ctx != NULL)
- dbox_map_append_free(&ctx->append_ctx);
- if (ctx->map_trans != NULL)
- dbox_map_transaction_free(&ctx->map_trans);
- if (array_is_created(&ctx->copy_map_uids))
- array_free(&ctx->copy_map_uids);
-
- if (ctx->sync_ctx != NULL)
- (void)dbox_sync_finish(&ctx->sync_ctx, FALSE);
-
- if (ctx->mail != NULL)
- mail_free(&ctx->mail);
- array_free(&ctx->mails);
- i_free(ctx);
-}
-
-int dbox_copy(struct mail_save_context *_ctx, struct mail *mail)
-{
- struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
- struct dbox_mailbox *src_mbox;
- struct dbox_mail_index_record rec;
- const void *data;
- bool expunged;
-
- ctx->finished = TRUE;
-
- if (mail->box->storage != _ctx->transaction->box->storage)
- return mail_storage_copy(_ctx, mail);
- src_mbox = (struct dbox_mailbox *)mail->box;
-
- memset(&rec, 0, sizeof(rec));
- rec.save_date = ioloop_time;
- if (dbox_mail_lookup(src_mbox, src_mbox->ibox.view, mail->seq,
- &rec.map_uid) < 0)
- return -1;
-
- if (rec.map_uid == 0) {
- /* FIXME: we could hard link */
- return mail_storage_copy(_ctx, mail);
- }
-
- /* remember the map_uid so we can later increase its refcount */
- if (!array_is_created(&ctx->copy_map_uids))
- i_array_init(&ctx->copy_map_uids, 32);
- array_append(&ctx->copy_map_uids, &rec.map_uid, 1);
-
- /* add message to mailbox index */
- dbox_save_add_to_index(ctx);
- mail_index_update_ext(ctx->trans, ctx->seq, ctx->mbox->dbox_ext_id,
- &rec, NULL);
-
- mail_index_lookup_ext(src_mbox->ibox.view, mail->seq,
- src_mbox->guid_ext_id, &data, &expunged);
- if (data != NULL) {
- mail_index_update_ext(ctx->trans, ctx->seq,
- ctx->mbox->guid_ext_id, data, NULL);
- }
- return 0;
-}
+++ /dev/null
-/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "settings-parser.h"
-#include "mail-storage-settings.h"
-#include "dbox-settings.h"
-
-#include <stddef.h>
-
-#undef DEF
-#define DEF(type, name) \
- { type, #name, offsetof(struct dbox_settings, name), NULL }
-
-static bool dbox_settings_verify(void *_set, pool_t pool ATTR_UNUSED,
- const char **error_r);
-
-static struct setting_define dbox_setting_defines[] = {
- DEF(SET_UINT, dbox_rotate_size),
- DEF(SET_UINT, dbox_rotate_min_size),
- DEF(SET_UINT, dbox_rotate_days),
- DEF(SET_UINT, dbox_max_open_files),
- DEF(SET_UINT, dbox_purge_min_percentage),
-
- SETTING_DEFINE_LIST_END
-};
-
-static struct dbox_settings dbox_default_settings = {
- MEMBER(dbox_rotate_size) 2048*1024,
- MEMBER(dbox_rotate_min_size) 16*1024,
- MEMBER(dbox_rotate_days) 0,
- MEMBER(dbox_max_open_files) 64,
- MEMBER(dbox_purge_min_percentage) 0
-};
-
-static struct setting_parser_info dbox_setting_parser_info = {
- MEMBER(defines) dbox_setting_defines,
- MEMBER(defaults) &dbox_default_settings,
-
- MEMBER(parent) &mail_user_setting_parser_info,
- MEMBER(dynamic_parsers) NULL,
-
- MEMBER(parent_offset) (size_t)-1,
- MEMBER(type_offset) (size_t)-1,
- MEMBER(struct_size) sizeof(struct dbox_settings),
- MEMBER(check_func) dbox_settings_verify
-};
-
-/* <settings checks> */
-static bool dbox_settings_verify(void *_set, pool_t pool ATTR_UNUSED,
- const char **error_r)
-{
- const struct dbox_settings *set = _set;
-
- if (set->dbox_max_open_files < 2) {
- *error_r = "dbox_max_open_files must be at least 2";
- return FALSE;
- }
- return TRUE;
-}
-/* </settings checks> */
-
-const struct setting_parser_info *dbox_get_setting_parser_info(void)
-{
- return &dbox_setting_parser_info;
-}
+++ /dev/null
-#ifndef DBOX_SETTINGS_H
-#define DBOX_SETTINGS_H
-
-struct dbox_settings {
- unsigned int dbox_rotate_size;
- unsigned int dbox_rotate_min_size;
- unsigned int dbox_rotate_days;
- unsigned int dbox_max_open_files;
- unsigned int dbox_purge_min_percentage;
-};
-
-const struct setting_parser_info *dbox_get_setting_parser_info(void);
-
-#endif
+++ /dev/null
-#ifndef DBOX_STORAGE_REBUILD_H
-#define DBOX_STORAGE_REBUILD_H
-
-int dbox_storage_rebuild(struct dbox_storage *storage);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#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 "maildir/maildir-uidlist.h"
-#include "dbox-map.h"
-#include "dbox-file.h"
-#include "dbox-sync.h"
-#include "dbox-storage-rebuild.h"
-#include "dbox-storage.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-#define DBOX_LIST_CONTEXT(obj) \
- MODULE_CONTEXT(obj, dbox_mailbox_list_module)
-
-struct dbox_mailbox_list {
- union mailbox_list_module_context module_ctx;
- const struct dbox_settings *set;
-};
-
-extern struct mail_storage dbox_storage;
-extern struct mailbox dbox_mailbox;
-
-static MODULE_CONTEXT_DEFINE_INIT(dbox_mailbox_list_module,
- &mailbox_list_module_register);
-
-static struct mail_storage *dbox_storage_alloc(void)
-{
- struct dbox_storage *storage;
- pool_t pool;
-
- pool = pool_alloconly_create("dbox storage", 512+256);
- storage = p_new(pool, struct dbox_storage, 1);
- storage->storage = dbox_storage;
- storage->storage.pool = pool;
- return &storage->storage;
-}
-
-static int
-dbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
- const char **error_r)
-{
- struct dbox_storage *storage = (struct dbox_storage *)_storage;
- const char *dir, *origin;
-
- storage->set = mail_storage_get_driver_settings(_storage);
- i_assert(storage->set->dbox_max_open_files >= 2);
-
- if (*ns->list->set.mailbox_dir_name == '\0') {
- *error_r = "dbox: MAILBOXDIR must not be empty";
- return -1;
- }
-
- _storage->unique_root_dir =
- p_strdup(_storage->pool, ns->list->set.root_dir);
-
- dir = mailbox_list_get_path(ns->list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
- storage->storage_dir = p_strconcat(_storage->pool, dir,
- "/"DBOX_GLOBAL_DIR_NAME, NULL);
- storage->alt_storage_dir = p_strconcat(_storage->pool,
- ns->list->set.alt_dir,
- "/"DBOX_GLOBAL_DIR_NAME, NULL);
- i_array_init(&storage->open_files,
- I_MIN(storage->set->dbox_max_open_files, 128));
-
- storage->map = dbox_map_init(storage);
- mailbox_list_get_dir_permissions(ns->list, NULL,
- &storage->dir_create_mode,
- &storage->create_gid, &origin);
- storage->create_gid_origin = p_strdup(_storage->pool, origin);
- return 0;
-}
-
-static void dbox_storage_destroy(struct mail_storage *_storage)
-{
- struct dbox_storage *storage = (struct dbox_storage *)_storage;
-
- if (storage->sync_rebuild) {
- if (dbox_storage_rebuild(storage) < 0)
- return;
- }
-
- dbox_files_free(storage);
- dbox_map_deinit(&storage->map);
- array_free(&storage->open_files);
- index_storage_destroy(_storage);
-}
-
-static void
-dbox_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
- struct mailbox_list_settings *set)
-{
- if (set->layout == NULL)
- set->layout = MAILBOX_LIST_NAME_FS;
- if (set->subscription_fname == NULL)
- set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
- if (set->dir_guid_fname == NULL)
- set->dir_guid_fname = DBOX_DIR_GUID_FILE_NAME;
- if (set->maildir_name == NULL)
- set->maildir_name = DBOX_MAILDIR_NAME;
- if (set->mailbox_dir_name == NULL)
- set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME;
-}
-
-static const char *
-dbox_get_alt_path(struct mailbox_list *list, const char *path)
-{
-#if 0 // FIXME
- const char *root;
- unsigned int len;
-
- if (storage->alt_dir == NULL)
- return NULL;
-
- root = mailbox_list_get_path(storage->storage.list, NULL,
- MAILBOX_LIST_PATH_TYPE_DIR);
-
- len = strlen(root);
- if (strncmp(path, root, len) != 0 && path[len] == '/') {
- /* can't determine the alt path - shouldn't happen */
- return NULL;
- }
- return t_strconcat(storage->alt_dir, path + len, NULL);
-#endif
- return NULL;
-}
-
-struct mailbox *
-dbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
- const char *name, struct istream *input,
- enum mailbox_flags flags)
-{
- struct dbox_mailbox *mbox;
- pool_t pool;
-
- /* dbox can't work without index files */
- flags &= ~MAILBOX_FLAG_NO_INDEX_FILES;
-
- pool = pool_alloconly_create("dbox mailbox", 1024+512);
- mbox = p_new(pool, struct dbox_mailbox, 1);
- mbox->ibox.box = dbox_mailbox;
- mbox->ibox.box.pool = pool;
- mbox->ibox.box.storage = storage;
- mbox->ibox.box.list = list;
- mbox->ibox.mail_vfuncs = &dbox_mail_vfuncs;
-
- mbox->ibox.save_commit_pre = dbox_transaction_save_commit_pre;
- mbox->ibox.save_commit_post = dbox_transaction_save_commit_post;
- mbox->ibox.save_rollback = dbox_transaction_save_rollback;
-
- index_storage_mailbox_alloc(&mbox->ibox, name, input, flags,
- DBOX_INDEX_PREFIX);
- mail_index_set_fsync_types(mbox->ibox.index,
- MAIL_INDEX_SYNC_TYPE_APPEND |
- MAIL_INDEX_SYNC_TYPE_EXPUNGE);
-
- mbox->ibox.index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS |
- MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
-
- mbox->storage = (struct dbox_storage *)storage;
- mbox->alt_path =
- p_strdup(pool, dbox_get_alt_path(list, mbox->ibox.box.path));
- mbox->dbox_ext_id =
- mail_index_ext_register(mbox->ibox.index, "dbox", 0,
- sizeof(struct dbox_mail_index_record),
- sizeof(uint32_t));
- mbox->dbox_hdr_ext_id =
- mail_index_ext_register(mbox->ibox.index, "dbox-hdr",
- sizeof(struct dbox_index_header), 0, 0);
- mbox->guid_ext_id =
- mail_index_ext_register(mbox->ibox.index, "guid",
- 0, MAIL_GUID_128_SIZE, 1);
-
- mbox->maildir_uidlist = maildir_uidlist_init_readonly(&mbox->ibox);
- return &mbox->ibox.box;
-}
-
-uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list)
-{
- const char *path;
-
- path = mailbox_list_get_path(list, NULL,
- MAILBOX_LIST_PATH_TYPE_CONTROL);
- path = t_strconcat(path, "/"DBOX_UIDVALIDITY_FILE_NAME, NULL);
- return mailbox_uidvalidity_next(path);
-}
-
-int dbox_read_header(struct dbox_mailbox *mbox, struct dbox_index_header *hdr)
-{
- const void *data;
- size_t data_size;
-
- mail_index_get_header_ext(mbox->ibox.view, mbox->dbox_hdr_ext_id,
- &data, &data_size);
- if (data_size < DBOX_INDEX_HEADER_MIN_SIZE &&
- (!mbox->creating || data_size != 0)) {
- mail_storage_set_critical(&mbox->storage->storage,
- "dbox %s: Invalid dbox header size",
- mbox->ibox.box.path);
- return -1;
- }
- memset(hdr, 0, sizeof(*hdr));
- memcpy(hdr, data, I_MIN(data_size, sizeof(*hdr)));
- return 0;
-}
-
-void dbox_update_header(struct dbox_mailbox *mbox,
- struct mail_index_transaction *trans,
- const struct mailbox_update *update)
-{
- struct dbox_index_header hdr, new_hdr;
-
- if (dbox_read_header(mbox, &hdr) < 0)
- memset(&hdr, 0, sizeof(hdr));
-
- new_hdr = hdr;
-
- if (update != NULL && !mail_guid_128_is_empty(update->mailbox_guid)) {
- memcpy(new_hdr.mailbox_guid, update->mailbox_guid,
- sizeof(new_hdr.mailbox_guid));
- } else if (mail_guid_128_is_empty(new_hdr.mailbox_guid)) {
- mail_generate_guid_128(new_hdr.mailbox_guid);
- }
-
- new_hdr.map_uid_validity =
- dbox_map_get_uid_validity(mbox->storage->map);
- if (memcmp(&hdr, &new_hdr, sizeof(hdr)) != 0) {
- mail_index_update_header_ext(trans, mbox->dbox_hdr_ext_id, 0,
- &new_hdr, sizeof(new_hdr));
- }
-}
-
-static int dbox_write_index_header(struct mailbox *box,
- const struct mailbox_update *update)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
- struct mail_index_transaction *trans;
- const struct mail_index_header *hdr;
- uint32_t uid_validity, uid_next;
-
- if (dbox_map_open(mbox->storage->map, TRUE) < 0)
- return -1;
-
- hdr = mail_index_get_header(mbox->ibox.view);
- trans = mail_index_transaction_begin(mbox->ibox.view, 0);
- dbox_update_header(mbox, trans, update);
-
- if (update != NULL && update->uid_validity != 0)
- uid_validity = update->uid_validity;
- else if (hdr->uid_validity == 0) {
- /* set uidvalidity */
- uid_validity = dbox_get_uidvalidity_next(box->list);
- }
-
- if (hdr->uid_validity != uid_validity) {
- mail_index_update_header(trans,
- offsetof(struct mail_index_header, uid_validity),
- &uid_validity, sizeof(uid_validity), TRUE);
- }
- if (update != NULL && hdr->next_uid < update->min_next_uid) {
- uid_next = update->min_next_uid;
- mail_index_update_header(trans,
- offsetof(struct mail_index_header, next_uid),
- &uid_next, sizeof(uid_next), TRUE);
- }
- if (update != NULL && update->min_highest_modseq != 0 &&
- mail_index_modseq_get_highest(mbox->ibox.view) <
- update->min_highest_modseq) {
- mail_index_update_highest_modseq(trans,
- update->min_highest_modseq);
- }
-
- if (mail_index_transaction_commit(&trans) < 0) {
- mail_storage_set_internal_error(box->storage);
- mail_index_reset_error(mbox->ibox.index);
- return -1;
- }
- return 0;
-}
-
-static int dbox_mailbox_create_indexes(struct mailbox *box,
- const struct mailbox_update *update)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
- const char *origin;
- mode_t mode;
- gid_t gid;
- int ret;
-
- mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
- if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
- /* create indexes immediately with the dbox header */
- if (index_storage_mailbox_open(box) < 0)
- return -1;
- mbox->creating = TRUE;
- ret = dbox_write_index_header(box, update);
- mbox->creating = FALSE;
- if (ret < 0)
- return -1;
- } else if (errno != EEXIST) {
- if (!mail_storage_set_error_from_errno(box->storage)) {
- mail_storage_set_critical(box->storage,
- "mkdir(%s) failed: %m", box->path);
- }
- return -1;
- }
- return 0;
-}
-
-static bool
-dbox_cleanup_if_exists(struct mailbox_list *list, const char *path)
-{
- struct stat st;
-
- if (stat(path, &st) < 0)
- return FALSE;
-
- /* check once in a while if there are temp files to clean up */
- if (st.st_atime > st.st_ctime + DBOX_TMP_DELETE_SECS) {
- /* there haven't been any changes to this directory since we
- last checked it. */
- } else if (st.st_atime < ioloop_time - DBOX_TMP_SCAN_SECS) {
- /* time to scan */
- const char *prefix =
- mailbox_list_get_global_temp_prefix(list);
-
- (void)unlink_old_files(path, prefix,
- ioloop_time - DBOX_TMP_DELETE_SECS);
- }
- return TRUE;
-}
-
-int dbox_mailbox_open(struct mailbox *box)
-{
- if (box->input != NULL) {
- mail_storage_set_critical(box->storage,
- "dbox doesn't support streamed mailboxes");
- return -1;
- }
-
- if (dbox_cleanup_if_exists(box->list, box->path)) {
- return index_storage_mailbox_open(box);
- } else if (errno == ENOENT) {
- if (strcmp(box->name, "INBOX") == 0 &&
- (box->list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
- /* INBOX always exists, create it */
- if (dbox_mailbox_create_indexes(box, NULL) < 0)
- return -1;
- return box->opened ? 0 :
- index_storage_mailbox_open(box);
- }
-
- mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
- T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
- return -1;
- } else if (errno == EACCES) {
- mail_storage_set_critical(box->storage, "%s",
- mail_error_eacces_msg("stat", box->path));
- return -1;
- } else {
- mail_storage_set_critical(box->storage,
- "stat(%s) failed: %m", box->path);
- return -1;
- }
-}
-
-static void dbox_mailbox_close(struct mailbox *box)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
-
- maildir_uidlist_deinit(&mbox->maildir_uidlist);
- index_storage_mailbox_close(box);
-}
-
-static void dbox_storage_get_status_guid(struct mailbox *box,
- struct mailbox_status *status_r)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
- struct dbox_index_header hdr;
-
- if (dbox_read_header(mbox, &hdr) < 0)
- memset(&hdr, 0, sizeof(hdr));
-
- if (mail_guid_128_is_empty(hdr.mailbox_guid)) {
- /* regenerate it */
- if (dbox_write_index_header(box, NULL) < 0 ||
- dbox_read_header(mbox, &hdr) < 0)
- return;
- }
- memcpy(status_r->mailbox_guid, hdr.mailbox_guid,
- sizeof(status_r->mailbox_guid));
-}
-
-static void
-dbox_storage_get_status(struct mailbox *box, enum mailbox_status_items items,
- struct mailbox_status *status_r)
-{
- index_storage_get_status(box, items, status_r);
-
- if ((items & STATUS_GUID) != 0)
- dbox_storage_get_status_guid(box, status_r);
-}
-
-static int
-dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
- bool directory)
-{
- const char *path, *alt_path, *origin;
- struct stat st;
-
- path = mailbox_list_get_path(box->list, box->name,
- directory ? MAILBOX_LIST_PATH_TYPE_DIR :
- MAILBOX_LIST_PATH_TYPE_MAILBOX);
- if (stat(path, &st) == 0) {
- mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
- "Mailbox already exists");
- return -1;
- }
-
- if (directory) {
- mode_t mode;
- gid_t gid;
-
- mailbox_list_get_dir_permissions(box->list, NULL, &mode,
- &gid, &origin);
- if (mkdir_parents_chgrp(path, mode, gid, origin) == 0)
- return 0;
- else if (errno == EEXIST) {
- mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
- "Mailbox already exists");
- } else if (!mail_storage_set_error_from_errno(box->storage)) {
- mail_storage_set_critical(box->storage,
- "mkdir(%s) failed: %m", path);
- }
- return -1;
- }
-
- /* make sure the alt path doesn't exist yet. it shouldn't (except with
- race conditions with RENAME/DELETE), but if something crashed and
- left it lying around we don't want to start overwriting files in
- it. */
- alt_path = dbox_get_alt_path(box->list, path);
- if (alt_path != NULL && stat(alt_path, &st) == 0) {
- mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
- "Mailbox already exists");
- return -1;
- }
-
- return dbox_mailbox_create_indexes(box, update);
-}
-
-static int
-dbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
-{
- if (!box->opened) {
- if (index_storage_mailbox_open(box) < 0)
- return -1;
- }
- return dbox_write_index_header(box, update);
-}
-
-static int
-dbox_mailbox_unref_mails(struct mailbox_list *list, const char *path)
-{
- struct dbox_storage *storage = (struct dbox_storage *)list->ns->storage;
- const struct mail_storage_settings *old_set;
- struct mail_storage_settings tmp_set;
- struct mailbox *box;
- struct dbox_mailbox *mbox;
- const struct mail_index_header *hdr;
- const struct dbox_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 = dbox_mailbox_alloc(&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_close(&box);
- return -1;
- }
- mbox = (struct dbox_mailbox *)box;
-
- /* get a list of all map_uids in this mailbox */
- i_array_init(&map_uids, 128);
- hdr = mail_index_get_header(mbox->ibox.view);
- for (seq = 1; seq <= hdr->messages_count; seq++) {
- mail_index_lookup_ext(mbox->ibox.view, seq, mbox->dbox_ext_id,
- &data, &expunged);
- dbox_rec = data;
- if (dbox_rec == NULL) {
- /* no multi-mails */
- break;
- }
- if (dbox_rec->map_uid != 0)
- array_append(&map_uids, &dbox_rec->map_uid, 1);
- }
-
- /* unreference the map_uids */
- map_trans = dbox_map_transaction_begin(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_close(&box);
- return ret;
-}
-
-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;
-}
-
-static int
-dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
- struct dbox_mailbox_list *mlist = DBOX_LIST_CONTEXT(list);
- struct stat st;
- const char *path, *alt_path, *trash_dir, *trash_dest;
- bool deleted = FALSE;
- int ret;
-
- /* 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 (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
- return -1;
-
- 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 = 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) {
- if (dbox_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 (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);
- } 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;
- }
- ret = 0;
- }
-
- 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;
-}
-
-static int
-dbox_list_rename_get_alt_paths(struct mailbox_list *oldlist,
- const char *oldname,
- struct mailbox_list *newlist,
- const char *newname,
- enum mailbox_list_path_type path_type,
- const char **oldpath_r, const char **newpath_r)
-{
- const char *path;
-
- path = mailbox_list_get_path(oldlist, oldname, path_type);
- *oldpath_r = dbox_get_alt_path(oldlist, path);
- if (*oldpath_r == NULL)
- return 0;
-
- path = mailbox_list_get_path(newlist, newname, path_type);
- *newpath_r = dbox_get_alt_path(newlist, path);
- if (*newpath_r == NULL) {
- /* destination dbox storage doesn't have alt-path defined.
- we can't do the rename easily. */
- mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
- "Can't rename mailboxes across specified storages.");
- return -1;
- }
- return 1;
-}
-
-static int
-dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
- const char *oldname,
- struct mailbox_list *newlist,
- const char *newname)
-{
- const char *alt_oldpath, *alt_newpath;
- struct stat st;
- int ret;
-
- ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
- MAILBOX_LIST_PATH_TYPE_DIR,
- &alt_oldpath, &alt_newpath);
- if (ret <= 0)
- return ret;
-
- if (stat(alt_newpath, &st) == 0) {
- /* race condition or a directory left there lying around?
- safest to just report error. */
- mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
- "Target mailbox already exists");
- return -1;
- } else if (errno != ENOENT) {
- mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
- alt_newpath);
- return -1;
- }
- return 0;
-}
-
-static int
-dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
- struct mailbox_list *newlist, const char *newname,
- bool rename_children)
-{
- struct dbox_mailbox_list *oldmlist = DBOX_LIST_CONTEXT(oldlist);
- enum mailbox_list_path_type path_type;
- const char *alt_oldpath, *alt_newpath, *path;
- int ret;
-
- ret = oldmlist->module_ctx.super.
- rename_mailbox(oldlist, oldname, newlist, newname,
- rename_children);
- if (ret < 0)
- return -1;
-
- path_type = rename_children ? MAILBOX_LIST_PATH_TYPE_DIR :
- MAILBOX_LIST_PATH_TYPE_MAILBOX;
- ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
- path_type, &alt_oldpath,
- &alt_newpath);
- if (ret <= 0)
- return ret;
-
- if (rename(alt_oldpath, alt_newpath) == 0) {
- /* ok */
- if (!rename_children) {
- path = mailbox_list_get_path(oldlist, oldname,
- MAILBOX_LIST_PATH_TYPE_DIR);
- if (rmdir(path) < 0 &&
- errno != ENOENT && errno != ENOTEMPTY) {
- mailbox_list_set_critical(oldlist,
- "rmdir(%s) failed: %m", path);
- }
- }
- } else if (errno != ENOENT) {
- /* renaming is done already, so just log the error */
- mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
- alt_oldpath, alt_newpath);
- }
- return 0;
-}
-
-static void dbox_notify_changes(struct mailbox *box)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
- const char *path;
-
- if (box->notify_callback == NULL)
- index_mailbox_check_remove_all(&mbox->ibox);
- else {
- path = t_strdup_printf("%s/"DBOX_INDEX_PREFIX".log",
- mbox->ibox.box.path);
- index_mailbox_check_add(&mbox->ibox, path);
- }
-}
-
-static int dbox_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 *path, *maildir_path;
- struct stat st, st2;
- 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 */
- path = t_strconcat(dir, "/", fname, NULL);
- if (stat(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') {
- /* 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 {
- /* non-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;
- }
- if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) {
- /* make sure it's a selectable mailbox */
- maildir_path = t_strconcat(path, "/"DBOX_MAILDIR_NAME, NULL);
- if (stat(maildir_path, &st2) < 0 || !S_ISDIR(st2.st_mode))
- *flags |= MAILBOX_NOSELECT;
- if (st.st_nlink == 3 && *ctx->list->set.maildir_name != '\0') {
- /* now we know what link count 3 means. */
- if ((*flags & MAILBOX_NOSELECT) != 0)
- *flags |= MAILBOX_CHILDREN;
- else
- *flags |= MAILBOX_NOCHILDREN;
- }
- }
- return ret;
-}
-
-static void dbox_storage_add_list(struct mail_storage *storage,
- struct mailbox_list *list)
-{
- struct dbox_mailbox_list *mlist;
-
- mlist = p_new(list->pool, struct dbox_mailbox_list, 1);
- mlist->module_ctx.super = list->v;
- mlist->set = mail_storage_get_driver_settings(storage);
-
- list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
- list->v.delete_mailbox = dbox_list_delete_mailbox;
- list->v.rename_mailbox = dbox_list_rename_mailbox;
- list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
-
- MODULE_CONTEXT_SET(list, dbox_mailbox_list_module, mlist);
-}
-
-struct mail_storage dbox_storage = {
- MEMBER(name) DBOX_STORAGE_NAME,
- MEMBER(class_flags) MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT, /* FIXME: for multi-dbox only.. */
-
- {
- dbox_get_setting_parser_info,
- dbox_storage_alloc,
- dbox_storage_create,
- dbox_storage_destroy,
- dbox_storage_add_list,
- dbox_storage_get_list_settings,
- NULL,
- dbox_mailbox_alloc,
- dbox_sync_purge
- }
-};
-
-struct mailbox dbox_mailbox = {
- MEMBER(name) NULL,
- MEMBER(storage) NULL,
- MEMBER(list) NULL,
-
- {
- index_storage_is_readonly,
- index_storage_allow_new_keywords,
- index_storage_mailbox_enable,
- dbox_mailbox_open,
- dbox_mailbox_close,
- dbox_mailbox_create,
- dbox_mailbox_update,
- dbox_storage_get_status,
- NULL,
- NULL,
- dbox_storage_sync_init,
- index_mailbox_sync_next,
- index_mailbox_sync_deinit,
- NULL,
- dbox_notify_changes,
- index_transaction_begin,
- index_transaction_commit,
- index_transaction_rollback,
- index_transaction_set_max_modseq,
- index_keywords_create,
- index_keywords_create_from_indexes,
- index_keywords_ref,
- index_keywords_unref,
- index_keyword_is_valid,
- index_storage_get_seq_range,
- index_storage_get_uid_range,
- index_storage_get_expunges,
- NULL,
- NULL,
- NULL,
- dbox_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,
- dbox_save_alloc,
- dbox_save_begin,
- dbox_save_continue,
- dbox_save_finish,
- dbox_save_cancel,
- dbox_copy,
- index_storage_is_inconsistent
- }
-};
+++ /dev/null
-#ifndef DBOX_STORAGE_H
-#define DBOX_STORAGE_H
-
-#include "index-storage.h"
-#include "mailbox-list-private.h"
-#include "dbox-settings.h"
-
-#define DBOX_STORAGE_NAME "dbox"
-#define DBOX_SUBSCRIPTION_FILE_NAME "subscriptions"
-#define DBOX_UIDVALIDITY_FILE_NAME "dovecot-uidvalidity"
-#define DBOX_INDEX_PREFIX "dovecot.index"
-#define DBOX_DIR_GUID_FILE_NAME "dbox-GUID"
-
-#define DBOX_MAILBOX_DIR_NAME "mailboxes"
-#define DBOX_TRASH_DIR_NAME "trash"
-#define DBOX_MAILDIR_NAME "dbox-Mails"
-#define DBOX_GLOBAL_INDEX_PREFIX "dovecot.map.index"
-#define DBOX_GLOBAL_DIR_NAME "storage"
-#define DBOX_MAIL_FILE_MULTI_PREFIX "m."
-#define DBOX_MAIL_FILE_UID_PREFIX "u."
-#define DBOX_MAIL_FILE_MULTI_FORMAT DBOX_MAIL_FILE_MULTI_PREFIX"%u"
-#define DBOX_MAIL_FILE_UID_FORMAT DBOX_MAIL_FILE_UID_PREFIX"%u"
-#define DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX ".broken"
-
-/* How often to scan for stale temp files (based on dir's atime) */
-#define DBOX_TMP_SCAN_SECS (8*60*60)
-/* Delete temp files having ctime older than this. */
-#define DBOX_TMP_DELETE_SECS (36*60*60)
-
-/* Flag specifies if the message should be in primary or alternative storage */
-#define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND
-
-#define DBOX_INDEX_HEADER_MIN_SIZE (sizeof(uint32_t))
-struct dbox_index_header {
- uint32_t map_uid_validity;
- uint32_t highest_maildir_uid;
- uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
-};
-
-struct dbox_storage {
- struct mail_storage storage;
- union mailbox_list_module_context list_module_ctx;
- const struct dbox_settings *set;
-
- /* root path for alt directory */
- const char *alt_dir;
- /* paths for storage directories */
- const char *storage_dir, *alt_storage_dir;
- struct dbox_map *map;
-
- /* mode/gid to use for new dbox storage files */
- mode_t dir_create_mode;
- gid_t create_gid;
- const char *create_gid_origin;
-
- ARRAY_DEFINE(open_files, struct dbox_file *);
-
- unsigned int sync_rebuild:1;
- unsigned int have_multi_msgs:1;
-};
-
-struct dbox_mail_index_record {
- uint32_t map_uid;
- /* UNIX timestamp of when the message was saved/copied to this
- mailbox */
- uint32_t save_date;
-};
-
-struct dbox_mailbox {
- struct index_mailbox ibox;
- struct dbox_storage *storage;
-
- struct maildir_uidlist *maildir_uidlist;
- uint32_t highest_maildir_uid;
- uint32_t map_uid_validity;
-
- uint32_t dbox_ext_id, dbox_hdr_ext_id, guid_ext_id;
-
- const char *alt_path;
-
- unsigned int creating:1;
-};
-
-extern struct mail_vfuncs dbox_mail_vfuncs;
-
-struct mailbox *
-dbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
- const char *name, struct istream *input,
- enum mailbox_flags flags);
-int dbox_mailbox_open(struct mailbox *box);
-
-struct mail *
-dbox_mail_alloc(struct mailbox_transaction_context *t,
- enum mail_fetch_field wanted_fields,
- struct mailbox_header_lookup_ctx *wanted_headers);
-
-/* Get map_uid for wanted message. */
-int dbox_mail_lookup(struct dbox_mailbox *mbox, struct mail_index_view *view,
- uint32_t seq, uint32_t *map_uid_r);
-uint32_t dbox_get_uidvalidity_next(struct mailbox_list *list);
-int dbox_read_header(struct dbox_mailbox *mbox, struct dbox_index_header *hdr);
-void dbox_update_header(struct dbox_mailbox *mbox,
- struct mail_index_transaction *trans,
- const struct mailbox_update *update);
-
-struct mail_save_context *
-dbox_save_alloc(struct mailbox_transaction_context *_t);
-int dbox_save_begin(struct mail_save_context *ctx, struct istream *input);
-int dbox_save_continue(struct mail_save_context *ctx);
-int dbox_save_finish(struct mail_save_context *ctx);
-void dbox_save_cancel(struct mail_save_context *ctx);
-
-struct dbox_file *
-dbox_save_file_get_file(struct mailbox_transaction_context *t,
- uint32_t seq, uoff_t *offset_r);
-
-int dbox_transaction_save_commit_pre(struct mail_save_context *ctx);
-void dbox_transaction_save_commit_post(struct mail_save_context *ctx);
-void dbox_transaction_save_rollback(struct mail_save_context *ctx);
-
-int dbox_copy(struct mail_save_context *ctx, struct mail *mail);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "hex-binary.h"
-#include "dbox-storage.h"
-#include "dbox-file.h"
-#include "dbox-map.h"
-#include "dbox-sync.h"
-
-#include <stdlib.h>
-
-struct dbox_mail_move {
- struct dbox_file *file;
- uint32_t offset;
-};
-ARRAY_DEFINE_TYPE(dbox_mail_move, struct dbox_mail_move);
-
-static int dbox_sync_file_unlink(struct dbox_file *file)
-{
- const char *path, *primary_path;
- bool alt = FALSE;
-
- path = primary_path = dbox_file_get_primary_path(file);
- while (unlink(path) < 0) {
- if (errno != ENOENT) {
- mail_storage_set_critical(&file->storage->storage,
- "unlink(%s) failed: %m", path);
- return -1;
- }
- if (file->storage->alt_storage_dir == NULL || alt) {
- /* not found */
- i_warning("dbox: File unexpectedly lost: %s/%s",
- primary_path, file->fname);
- return 0;
- }
-
- /* try the alternative path */
- path = dbox_file_get_alt_path(file);
- alt = TRUE;
- }
- return 1;
-}
-
-static int dbox_map_file_msg_offset_cmp(const void *p1, const void *p2)
-{
- const struct dbox_map_file_msg *m1 = p1, *m2 = p2;
-
- if (m1->offset < m2->offset)
- return -1;
- else if (m1->offset > m2->offset)
- return 1;
- else
- return 0;
-}
-
-static int
-dbox_sync_file_copy_metadata(struct dbox_file *file, struct ostream *output)
-{
- struct dbox_metadata_header meta_hdr;
- const char *line;
- const unsigned char *data;
- size_t size;
- int ret;
-
- ret = i_stream_read_data(file->input, &data, &size,
- sizeof(meta_hdr));
- if (ret <= 0) {
- i_assert(ret == -1);
- if (file->input->stream_errno == 0) {
- dbox_file_set_corrupted(file, "missing metadata");
- return 0;
- }
- mail_storage_set_critical(&file->storage->storage,
- "read(%s) failed: %m", file->current_path);
- return -1;
- }
-
- memcpy(&meta_hdr, data, sizeof(meta_hdr));
- if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
- sizeof(meta_hdr.magic_post)) != 0) {
- dbox_file_set_corrupted(file, "invalid metadata magic");
- return 0;
- }
- i_stream_skip(file->input, sizeof(meta_hdr));
- if (output != NULL)
- o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
- while ((line = i_stream_read_next_line(file->input)) != NULL) {
- if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
- /* end of metadata */
- break;
- }
- if (output != NULL) {
- o_stream_send_str(output, line);
- o_stream_send(output, "\n", 1);
- }
- }
- if (line == NULL) {
- dbox_file_set_corrupted(file, "missing end-of-metadata line");
- return 0;
- }
- if (output != NULL)
- o_stream_send(output, "\n", 1);
- return 1;
-}
-
-int dbox_sync_file_purge(struct dbox_file *file)
-{
- struct mail_storage *storage = &file->storage->storage;
- struct dbox_file *out_file;
- struct stat st;
- struct istream *input;
- struct ostream *output = NULL;
- struct dbox_map_append_context *append_ctx;
- ARRAY_TYPE(dbox_map_file_msg) msgs_arr;
- const struct dbox_map_file_msg *msgs;
- ARRAY_TYPE(seq_range) expunged_map_uids;
- ARRAY_TYPE(uint32_t) copied_map_uids;
- unsigned int i, count;
- uoff_t offset, physical_size, msg_size;
- bool expunged;
- int ret;
-
- if ((ret = dbox_file_try_lock(file)) <= 0)
- return ret;
-
- /* make sure the file still exists. another process may have already
- deleted it. */
- if (stat(file->current_path, &st) < 0) {
- dbox_file_unlock(file);
- if (errno == ENOENT)
- return 0;
-
- mail_storage_set_critical(storage,
- "stat(%s) failed: %m", file->current_path);
- return -1;
- }
-
- i_array_init(&msgs_arr, 128);
- if (dbox_map_get_file_msgs(file->storage->map, file->file_id,
- &msgs_arr) < 0) {
- array_free(&msgs_arr);
- dbox_file_unlock(file);
- return -1;
- }
- /* sort messages by their offset */
- array_sort(&msgs_arr, dbox_map_file_msg_offset_cmp);
-
- msgs = array_get(&msgs_arr, &count);
- append_ctx = dbox_map_append_begin_storage(file->storage);
- i_array_init(&copied_map_uids, I_MIN(count, 1));
- i_array_init(&expunged_map_uids, I_MIN(count, 1));
- offset = file->file_header_size;
- for (i = 0; i < count; i++) {
- if ((ret = dbox_file_get_mail_stream(file, offset,
- &physical_size,
- NULL, &expunged)) <= 0)
- break;
- msg_size = file->msg_header_size + physical_size;
-
- if (msgs[i].offset != offset) {
- /* map doesn't match file's actual contents */
- dbox_file_set_corrupted(file,
- "purging found mismatched offsets "
- "(%"PRIuUOFF_T" vs %u, %u/%u)",
- offset, msgs[i].offset, i, count);
- ret = 0;
- break;
- }
-
- if (msgs[i].refcount == 0) {
- seq_range_array_add(&expunged_map_uids, 0,
- msgs[i].map_uid);
- output = NULL;
- } else {
- /* non-expunged message. write it to output file. */
- if (dbox_map_append_next(append_ctx, physical_size,
- &out_file, &output) < 0) {
- ret = -1;
- break;
- }
- i_assert(file->file_id != out_file->file_id);
-
- i_stream_seek(file->input, offset);
- input = i_stream_create_limit(file->input, msg_size);
- ret = o_stream_send_istream(output, input);
- if (input->stream_errno != 0) {
- errno = input->stream_errno;
- mail_storage_set_critical(storage,
- "read(%s) failed: %m",
- file->current_path);
- i_stream_unref(&input);
- break;
- }
- i_stream_unref(&input);
- if (output->stream_errno != 0) {
- errno = output->stream_errno;
- mail_storage_set_critical(storage,
- "write(%s) failed: %m",
- out_file->current_path);
- break;
- }
- i_assert(ret == (off_t)msg_size);
- }
-
- /* copy/skip metadata */
- i_stream_seek(file->input, offset + msg_size);
- if ((ret = dbox_sync_file_copy_metadata(file, output)) <= 0)
- break;
-
- if (output != NULL) {
- dbox_map_append_finish_multi_mail(append_ctx);
- array_append(&copied_map_uids, &msgs[i].map_uid, 1);
- }
- offset = file->input->v_offset;
- }
- if (offset != (uoff_t)st.st_size && ret > 0) {
- /* file has more messages than what map tells us */
- dbox_file_set_corrupted(file,
- "more messages available than in map "
- "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
- ret = 0;
- }
- array_free(&msgs_arr); msgs = NULL;
-
- if (ret <= 0) {
- dbox_map_append_free(&append_ctx);
- dbox_file_unlock(file);
- ret = -1;
- } else if (array_count(&copied_map_uids) == 0) {
- /* everything expunged in this file, unlink it */
- ret = dbox_sync_file_unlink(file);
- dbox_map_append_free(&append_ctx);
- } else {
- /* assign new file_id + offset to moved messages */
- if (dbox_map_append_move(append_ctx, &copied_map_uids,
- &expunged_map_uids) < 0 ||
- dbox_map_append_commit(append_ctx) < 0) {
- dbox_file_unlock(file);
- ret = -1;
- } else {
- ret = 1;
- (void)dbox_sync_file_unlink(file);
- }
- dbox_map_append_free(&append_ctx);
- }
- array_free(&copied_map_uids);
- array_free(&expunged_map_uids);
- return ret;
-}
-
-static void
-dbox_sync_file_move_if_needed(struct dbox_file *file,
- const struct dbox_sync_file_entry *entry)
-{
- if (!entry->move_to_alt && !entry->move_from_alt)
- return;
-
- if (entry->move_to_alt != file->alt_path) {
- /* move the file. if it fails, nothing broke so
- don't worry about it. */
- if (dbox_file_try_lock(file) > 0) {
- (void)dbox_file_move(file, !file->alt_path);
- dbox_file_unlock(file);
- }
- }
-}
-
-static int
-dbox_sync_verify_expunge_guid(struct dbox_sync_context *ctx,
- const struct dbox_sync_expunge *expunge)
-{
- const void *data;
- uint32_t uid;
-
- mail_index_lookup_uid(ctx->sync_view, expunge->seq, &uid);
- mail_index_lookup_ext(ctx->sync_view, expunge->seq,
- ctx->mbox->guid_ext_id, &data, NULL);
- if (mail_guid_128_is_empty(expunge->guid_128) ||
- memcmp(data, expunge->guid_128, MAIL_GUID_128_SIZE) == 0)
- return 0;
-
- mail_storage_set_critical(&ctx->mbox->storage->storage,
- "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s",
- ctx->mbox->ibox.box.vname, uid,
- binary_to_hex(data, MAIL_GUID_128_SIZE),
- binary_to_hex(expunge->guid_128, MAIL_GUID_128_SIZE));
- return -1;
-}
-
-static int
-dbox_sync_verify_expunge_guids(struct dbox_sync_context *ctx,
- const struct dbox_sync_file_entry *entry)
-{
- const struct dbox_sync_expunge *expunges;
- unsigned int i, count;
-
- expunges = array_get(&entry->expunges, &count);
- for (i = 0; i < count; i++) {
- if (dbox_sync_verify_expunge_guid(ctx, &expunges[i]) < 0)
- return -1;
- }
- return 0;
-}
-
-static void
-dbox_sync_mark_expunges(struct dbox_sync_context *ctx,
- const struct dbox_sync_file_entry *entry)
-{
- struct mailbox *box = &ctx->mbox->ibox.box;
- const struct dbox_sync_expunge *expunges;
- unsigned int i, count;
- const void *data;
- uint32_t uid;
-
- expunges = array_get(&entry->expunges, &count);
- for (i = 0; i < count; i++) {
- mail_index_lookup_uid(ctx->sync_view, expunges[i].seq, &uid);
- mail_index_lookup_ext(ctx->sync_view, expunges[i].seq,
- ctx->mbox->guid_ext_id, &data, NULL);
- mail_index_expunge_guid(ctx->trans, expunges[i].seq, data);
-
- if (box->v.sync_notify != NULL)
- box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE);
- }
-}
-
-int dbox_sync_file(struct dbox_sync_context *ctx,
- const struct dbox_sync_file_entry *entry)
-{
- struct dbox_mailbox *mbox = ctx->mbox;
- struct dbox_file *file;
- int ret = 1;
-
- file = entry->file_id != 0 ?
- dbox_file_init_multi(mbox->storage, entry->file_id) :
- dbox_file_init_single(mbox, entry->uid);
- if (!array_is_created(&entry->expunges)) {
- /* no expunges - we want to move it */
- dbox_sync_file_move_if_needed(file, entry);
- } else if (dbox_sync_verify_expunge_guids(ctx, entry) < 0) {
- /* guid mismatches, see if index rebuilding helps */
- ret = 0;
- } else if (entry->uid != 0) {
- /* single-message file, we can unlink it */
- if ((ret = dbox_sync_file_unlink(file)) == 0) {
- /* file was lost, delete it */
- dbox_sync_mark_expunges(ctx, entry);
- ret = 1;
- }
- } else {
- if (ctx->map_trans == NULL) {
- ctx->map_trans =
- dbox_map_transaction_begin(mbox->storage->map,
- FALSE);
- }
- if (dbox_map_update_refcounts(ctx->map_trans,
- (void *)&entry->expunges, -1) < 0)
- ret = -1;
- else
- dbox_sync_mark_expunges(ctx, entry);
- }
- dbox_file_unref(&file);
- return ret;
-}
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "dbox-storage.h"
-#include "maildir/maildir-uidlist.h"
-#include "maildir/maildir-keywords.h"
-#include "maildir/maildir-filename.h"
-#include "dbox-map.h"
-#include "dbox-file.h"
-#include "dbox-sync.h"
-
-#include <stdlib.h>
-#include <dirent.h>
-
-struct dbox_sync_rebuild_context {
- struct dbox_mailbox *mbox;
-
- struct mail_index_view *view;
- struct mail_index_transaction *trans;
- uint32_t cache_ext_id;
- uint32_t cache_reset_id;
-
- struct mail_index *backup_index;
- struct mail_index_view *backup_view;
-
- struct maildir_uidlist_sync_ctx *maildir_sync_ctx;
- struct maildir_keywords *mk;
- struct maildir_keywords_sync_ctx *maildir_sync_keywords;
-
- uint32_t highest_uid;
-
- unsigned int cache_used:1;
- unsigned int storage_rebuild:1;
-};
-
-static void dbox_sync_set_uidvalidity(struct dbox_sync_rebuild_context *ctx)
-{
- struct mailbox *box = &ctx->mbox->ibox.box;
- uint32_t uid_validity;
-
- /* if uidvalidity is set in the old index, use it */
- uid_validity = mail_index_get_header(ctx->view)->uid_validity;
- if (uid_validity == 0)
- uid_validity = dbox_get_uidvalidity_next(box->list);
-
- mail_index_update_header(ctx->trans,
- offsetof(struct mail_index_header, uid_validity),
- &uid_validity, sizeof(uid_validity), TRUE);
-}
-
-static void
-dbox_sync_index_copy_cache(struct dbox_sync_rebuild_context *ctx,
- struct mail_index_view *view,
- uint32_t old_seq, uint32_t new_seq)
-{
- struct mail_index_map *map;
- const void *data;
- uint32_t reset_id;
- bool expunged;
-
- if (ctx->cache_ext_id == (uint32_t)-1)
- return;
-
- mail_index_lookup_ext_full(view, old_seq, ctx->cache_ext_id,
- &map, &data, &expunged);
- if (expunged)
- return;
-
- if (!mail_index_ext_get_reset_id(view, map, ctx->cache_ext_id,
- &reset_id) || reset_id == 0)
- return;
-
- if (!ctx->cache_used) {
- /* set reset id */
- ctx->cache_used = TRUE;
- ctx->cache_reset_id = reset_id;
- mail_index_ext_reset(ctx->trans, ctx->cache_ext_id,
- ctx->cache_reset_id, TRUE);
- }
- if (ctx->cache_reset_id == reset_id) {
- mail_index_update_ext(ctx->trans, new_seq,
- ctx->cache_ext_id, data, NULL);
- }
-}
-
-static void
-dbox_sync_index_copy_from_old(struct dbox_sync_rebuild_context *ctx,
- struct mail_index_view *view,
- uint32_t old_seq, uint32_t new_seq)
-{
- struct mail_index *index = mail_index_view_get_index(view);
- const struct mail_index_record *rec;
- ARRAY_TYPE(keyword_indexes) old_keywords;
- struct mail_keywords *kw;
-
- /* copy flags */
- rec = mail_index_lookup(view, old_seq);
- mail_index_update_flags(ctx->trans, new_seq,
- MODIFY_REPLACE, rec->flags);
-
- /* copy keywords */
- t_array_init(&old_keywords, 32);
- mail_index_lookup_keywords(view, old_seq, &old_keywords);
- kw = mail_index_keywords_create_from_indexes(index, &old_keywords);
- mail_index_update_keywords(ctx->trans, new_seq, MODIFY_REPLACE, kw);
- mail_index_keywords_unref(&kw);
-
- dbox_sync_index_copy_cache(ctx, view, old_seq, new_seq);
-}
-
-static void
-dbox_sync_index_copy_from_maildir(struct dbox_sync_rebuild_context *ctx,
- struct dbox_file *file, uint32_t seq)
-{
- ARRAY_TYPE(keyword_indexes) keyword_indexes;
- struct mail_keywords *keywords;
- enum mail_flags flags;
-
- t_array_init(&keyword_indexes, 32);
- maildir_filename_get_flags(ctx->maildir_sync_keywords,
- file->fname, &flags, &keyword_indexes);
- mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE, flags);
-
- keywords = mail_index_keywords_create_from_indexes(ctx->mbox->ibox.index,
- &keyword_indexes);
- mail_index_update_keywords(ctx->trans, seq, MODIFY_REPLACE, keywords);
- mail_index_keywords_unref(&keywords);
-}
-
-void dbox_sync_rebuild_index_metadata(struct dbox_sync_rebuild_context *ctx,
- struct dbox_file *file,
- uint32_t new_seq, uint32_t uid)
-{
- uint32_t old_seq;
-
- if (mail_index_lookup_seq(ctx->view, uid, &old_seq)) {
- /* the message exists in the old index.
- copy the metadata from it. */
- dbox_sync_index_copy_from_old(ctx, ctx->view, old_seq, new_seq);
- } else if (ctx->backup_view != NULL &&
- mail_index_lookup_seq(ctx->backup_view, uid, &old_seq)) {
- /* copy the metadata from backup index. */
- dbox_sync_index_copy_from_old(ctx, ctx->backup_view,
- old_seq, new_seq);
- } else if (file != NULL && file->maildir_file) {
- /* we're probably doing initial sync after migration from
- maildir. preserve the old flags. */
- dbox_sync_index_copy_from_maildir(ctx, file, new_seq);
- }
-}
-
-static int dbox_sync_add_file_index(struct dbox_sync_rebuild_context *ctx,
- struct dbox_file *file)
-{
- uint32_t seq;
- uoff_t size;
- bool expunged;
- int ret;
-
- ret = dbox_file_get_mail_stream(file, 0, &size, NULL, &expunged);
- if (ret <= 0) {
- if (ret < 0)
- return -1;
-
- i_warning("dbox: Ignoring broken file: %s", file->current_path);
- return 0;
- }
- if (expunged) {
- /* the file just got deleted? */
- return 0;
- }
-
- mail_index_append(ctx->trans, file->uid, &seq);
- T_BEGIN {
- dbox_sync_rebuild_index_metadata(ctx, file, seq, file->uid);
- } T_END;
- return 0;
-}
-
-static int
-dbox_sync_add_uid_file(struct dbox_sync_rebuild_context *ctx,
- const char *dir, const char *fname)
-{
- struct dbox_file *file;
- unsigned long uid;
- char *p;
- int ret;
-
- fname += sizeof(DBOX_MAIL_FILE_UID_PREFIX)-1;
- uid = strtoul(fname, &p, 10);
- if (*p != '\0' || uid == 0 || uid >= (uint32_t)-1) {
- i_warning("dbox %s: Ignoring invalid filename %s",
- ctx->mbox->ibox.box.path, fname);
- return 0;
- }
-
- if (ctx->highest_uid < uid)
- ctx->highest_uid = uid;
-
- file = dbox_file_init_single(ctx->mbox, uid);
- file->current_path = i_strdup_printf("%s/%s", dir, fname);
-
- ret = dbox_sync_add_file_index(ctx, file);
- dbox_file_unref(&file);
- return ret;
-}
-
-static int
-dbox_sync_add_maildir_file(struct dbox_sync_rebuild_context *ctx,
- const char *fname)
-{
- int ret;
-
- if (ctx->maildir_sync_ctx == NULL) {
- i_assert(ctx->mk == NULL);
-
- ctx->mk = maildir_keywords_init_readonly(&ctx->mbox->ibox.box);
- ctx->maildir_sync_keywords =
- maildir_keywords_sync_init(ctx->mk,
- ctx->mbox->ibox.index);
-
- ret = maildir_uidlist_sync_init(ctx->mbox->maildir_uidlist,
- MAILDIR_UIDLIST_SYNC_NOLOCK,
- &ctx->maildir_sync_ctx);
- if (ret <= 0) {
- i_assert(ret < 0);
- return -1;
- }
- }
-
- /* sync all maildir files first and let maildir uidlist code assign
- UIDs for unseen files. */
- ret = maildir_uidlist_sync_next(ctx->maildir_sync_ctx, fname, 0);
- if (ret == 0) {
- i_warning("%s: Ignoring duplicate maildir file: %s",
- ctx->mbox->ibox.box.path, fname);
- }
- return ret;
-}
-
-static int
-dbox_sync_add_file(struct dbox_sync_rebuild_context *ctx,
- const char *path, const char *fname, bool primary)
-{
- if (strncmp(fname, DBOX_MAIL_FILE_UID_PREFIX,
- sizeof(DBOX_MAIL_FILE_UID_PREFIX)-1) == 0)
- return dbox_sync_add_uid_file(ctx, path, fname);
-
- if (primary && strstr(fname, ":2,") != NULL)
- return dbox_sync_add_maildir_file(ctx, fname);
- return 0;
-}
-
-static int dbox_sync_index_rebuild_dir(struct dbox_sync_rebuild_context *ctx,
- const char *path, bool primary)
-{
- struct mail_storage *storage = ctx->mbox->ibox.box.storage;
- DIR *dir;
- struct dirent *d;
- int ret = 0;
-
- dir = opendir(path);
- if (dir == NULL) {
- if (errno == ENOENT) {
- if (!primary) {
- /* alt directory doesn't exist, ignore */
- return 0;
- }
- mailbox_set_deleted(&ctx->mbox->ibox.box);
- return -1;
- }
- mail_storage_set_critical(storage,
- "opendir(%s) failed: %m", path);
- return -1;
- }
- do {
- errno = 0;
- if ((d = readdir(dir)) == NULL)
- break;
-
- T_BEGIN {
- ret = dbox_sync_add_file(ctx, path, d->d_name, primary);
- } T_END;
- } while (ret >= 0);
- if (errno != 0) {
- mail_storage_set_critical(storage,
- "readdir(%s) failed: %m", path);
- ret = -1;
- }
-
- if (closedir(dir) < 0) {
- mail_storage_set_critical(storage,
- "closedir(%s) failed: %m", path);
- ret = -1;
- }
- return ret;
-}
-
-static int dbox_sync_maildir_finish(struct dbox_sync_rebuild_context *ctx)
-{
- struct dbox_mailbox *mbox = ctx->mbox;
- struct maildir_uidlist_iter_ctx *iter;
- struct mail_index_view *trans_view;
- struct dbox_file *file;
- const char *fname;
- enum maildir_uidlist_rec_flag flags;
- uint32_t uid, next_uid;
- int ret = 0;
-
- if (ctx->maildir_sync_ctx == NULL)
- return 0;
-
- /* we'll need the uidlist to contain the latest filenames.
- since there's no easy way to figure out if they changed, just
- recreate the uidlist always. */
- maildir_uidlist_sync_recreate(ctx->maildir_sync_ctx);
-
- /* update the maildir uidlist's next_uid if we have seen higher
- dbox UIDs */
- trans_view = mail_index_transaction_open_updated_view(ctx->trans);
- next_uid = mail_index_get_header(trans_view)->next_uid;
- mail_index_view_close(&trans_view);
- maildir_uidlist_set_next_uid(mbox->maildir_uidlist, next_uid, FALSE);
- maildir_uidlist_set_next_uid(mbox->maildir_uidlist,
- ctx->highest_uid + 1, FALSE);
- /* assign UIDs for new maildir mails before iterating */
- maildir_uidlist_sync_finish(ctx->maildir_sync_ctx);
-
- mbox->highest_maildir_uid =
- maildir_uidlist_get_next_uid(mbox->maildir_uidlist);
-
- iter = maildir_uidlist_iter_init(mbox->maildir_uidlist);
- while (maildir_uidlist_iter_next(iter, &uid, &flags, &fname)) {
- file = dbox_file_init_single(mbox, uid);
- file->current_path =
- i_strdup_printf("%s/%s", ctx->mbox->ibox.box.path,
- fname);
-
- ret = dbox_sync_add_file_index(ctx, file);
- dbox_file_unref(&file);
- if (ret < 0)
- break;
- }
- maildir_uidlist_iter_deinit(&iter);
- return ret < 0 ? -1 : 0;
-}
-
-static void dbox_sync_update_header(struct dbox_sync_rebuild_context *ctx)
-{
- struct dbox_index_header hdr;
-
- if (dbox_read_header(ctx->mbox, &hdr) < 0)
- memset(&hdr, 0, sizeof(hdr));
- if (!mail_guid_128_is_empty(hdr.mailbox_guid))
- mail_generate_guid_128(hdr.mailbox_guid);
- if (hdr.highest_maildir_uid < ctx->mbox->highest_maildir_uid)
- hdr.highest_maildir_uid = ctx->mbox->highest_maildir_uid;
- hdr.map_uid_validity = !ctx->storage_rebuild ? 0 :
- dbox_map_get_uid_validity(ctx->mbox->storage->map);
- mail_index_update_header_ext(ctx->trans, ctx->mbox->dbox_hdr_ext_id, 0,
- &hdr, sizeof(hdr));
-}
-
-struct dbox_sync_rebuild_context *
-dbox_sync_index_rebuild_init(struct dbox_mailbox *mbox,
- struct mail_index_view *view,
- struct mail_index_transaction *trans,
- bool storage_rebuild)
-{
- struct mailbox *box = &mbox->ibox.box;
- struct dbox_sync_rebuild_context *ctx;
- const char *index_dir;
- enum mail_index_open_flags open_flags = MAIL_INDEX_OPEN_FLAG_READONLY;
-
- ctx = i_new(struct dbox_sync_rebuild_context, 1);
- ctx->mbox = mbox;
- ctx->view = view;
- ctx->trans = trans;
- ctx->storage_rebuild = storage_rebuild;
- mail_index_reset(ctx->trans);
- index_mailbox_reset_uidvalidity(&mbox->ibox);
- mail_index_ext_lookup(mbox->ibox.index, "cache", &ctx->cache_ext_id);
-
- /* if backup index file exists, try to use it */
- index_dir = mailbox_list_get_path(box->list, box->name,
- MAILBOX_LIST_PATH_TYPE_INDEX);
- ctx->backup_index =
- mail_index_alloc(index_dir, DBOX_INDEX_PREFIX".backup");
-
-#ifndef MMAP_CONFLICTS_WRITE
- if (box->storage->set->mmap_disable)
-#endif
- open_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
- if (mail_index_open(ctx->backup_index, open_flags,
- box->storage->set->parsed_lock_method) <= 0)
- mail_index_free(&ctx->backup_index);
- else
- ctx->backup_view = mail_index_view_open(ctx->backup_index);
- return ctx;
-}
-
-int dbox_sync_index_rebuild_singles(struct dbox_sync_rebuild_context *ctx)
-{
- int ret = 0;
-
- dbox_sync_set_uidvalidity(ctx);
- if (dbox_sync_index_rebuild_dir(ctx, ctx->mbox->ibox.box.path, TRUE) < 0)
- ret = -1;
- else if (ctx->mbox->alt_path != NULL) {
- if (dbox_sync_index_rebuild_dir(ctx, ctx->mbox->alt_path,
- FALSE) < 0)
- ret = -1;
- }
-
- if (ret == 0) {
- if (dbox_sync_maildir_finish(ctx) < 0)
- ret = -1;
- }
-
- if (ctx->maildir_sync_ctx != NULL) {
- if (maildir_uidlist_sync_deinit(&ctx->maildir_sync_ctx,
- ret == 0) < 0)
- ret = -1;
- }
- if (ctx->maildir_sync_keywords != NULL)
- maildir_keywords_sync_deinit(&ctx->maildir_sync_keywords);
- if (ctx->mk != NULL)
- maildir_keywords_deinit(&ctx->mk);
- return ret;
-}
-
-void dbox_sync_index_rebuild_deinit(struct dbox_sync_rebuild_context **_ctx)
-{
- struct dbox_sync_rebuild_context *ctx = *_ctx;
-
- *_ctx = NULL;
- if (ctx->backup_index != NULL) {
- mail_index_view_close(&ctx->backup_view);
- mail_index_free(&ctx->backup_index);
- }
- dbox_sync_update_header(ctx);
- i_free(ctx);
-}
-
-int dbox_sync_index_rebuild(struct dbox_mailbox *mbox)
-{
- struct dbox_sync_rebuild_context *ctx;
- struct mail_index_view *view;
- struct mail_index_transaction *trans;
- int ret;
-
- view = mail_index_view_open(mbox->ibox.index);
- trans = mail_index_transaction_begin(view,
- MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
-
- ctx = dbox_sync_index_rebuild_init(mbox, view, trans, FALSE);
- ret = dbox_sync_index_rebuild_singles(ctx);
- dbox_sync_index_rebuild_deinit(&ctx);
-
- if (ret < 0)
- mail_index_transaction_rollback(&trans);
- else
- ret = mail_index_transaction_commit(&trans);
- mail_index_view_close(&view);
-
- if (ret == 0)
- mbox->storage->sync_rebuild = FALSE;
- return ret;
-}
+++ /dev/null
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "ioloop.h"
-#include "str.h"
-#include "hash.h"
-#include "dbox-storage.h"
-#include "dbox-storage-rebuild.h"
-#include "dbox-map.h"
-#include "dbox-file.h"
-#include "dbox-sync.h"
-
-#define DBOX_REBUILD_COUNT 3
-
-static unsigned int dbox_sync_file_entry_hash(const void *p)
-{
- const struct dbox_sync_file_entry *entry = p;
-
- if (entry->file_id != 0)
- return entry->file_id | 0x80000000;
- else
- return entry->uid;
-}
-
-static int dbox_sync_file_entry_cmp(const void *p1, const void *p2)
-{
- const struct dbox_sync_file_entry *entry1 = p1, *entry2 = p2;
-
- /* this is only for hashing, don't bother ever returning 1. */
- if (entry1->file_id != entry2->file_id)
- return -1;
- if (entry1->uid != entry2->uid)
- return -1;
- return 0;
-}
-
-static int dbox_sync_add_seq(struct dbox_sync_context *ctx,
- const struct mail_index_sync_rec *sync_rec,
- uint32_t seq)
-{
- struct dbox_sync_file_entry *entry, lookup_entry;
- struct dbox_sync_expunge *expunge;
- uint32_t map_uid;
- uoff_t offset;
- int ret;
-
- i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE ||
- sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS);
-
- memset(&lookup_entry, 0, sizeof(lookup_entry));
- if (dbox_mail_lookup(ctx->mbox, ctx->sync_view, seq, &map_uid) < 0)
- return ctx->mbox->storage->sync_rebuild ? 0 : -1;
- if (map_uid == 0)
- mail_index_lookup_uid(ctx->sync_view, seq, &lookup_entry.uid);
- else {
- ret = dbox_map_lookup(ctx->mbox->storage->map, map_uid,
- &lookup_entry.file_id, &offset);
- if (ret <= 0) {
- if (ret < 0)
- return -1;
- /* mailbox is locked while syncing, so if ret=0 the
- message got expunged from storage before it was
- expunged from mailbox. that shouldn't happen. */
- dbox_map_set_corrupted(ctx->mbox->storage->map,
- "unexpectedly lost map_uid=%u", map_uid);
- return 0;
- }
- }
-
- entry = hash_table_lookup(ctx->syncs, &lookup_entry);
- if (entry == NULL) {
- entry = p_new(ctx->pool, struct dbox_sync_file_entry, 1);
- *entry = lookup_entry;
- hash_table_insert(ctx->syncs, entry, entry);
- }
-
- if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
- if (!array_is_created(&entry->expunges)) {
- p_array_init(&entry->expunges, ctx->pool,
- lookup_entry.uid != 0 ? 1 : 3);
- }
-
- expunge = array_append_space(&entry->expunges);
- expunge->map_uid = map_uid;
- expunge->seq = seq;
- memcpy(expunge->guid_128, sync_rec->guid_128,
- sizeof(expunge->guid_128));
- if (entry->file_id != 0)
- ctx->have_storage_expunges = TRUE;
- } else {
- if ((sync_rec->add_flags & DBOX_INDEX_FLAG_ALT) != 0)
- entry->move_to_alt = TRUE;
- else
- entry->move_from_alt = TRUE;
- }
- return 1;
-}
-
-static int dbox_sync_add(struct dbox_sync_context *ctx,
- const struct mail_index_sync_rec *sync_rec)
-{
- uint32_t seq, seq1, seq2;
- int ret;
-
- if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
- /* we're interested */
- } else if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS) {
- /* we care only about alt flag changes */
- if ((sync_rec->add_flags & DBOX_INDEX_FLAG_ALT) == 0 &&
- (sync_rec->remove_flags & DBOX_INDEX_FLAG_ALT) == 0)
- return 1;
- } else {
- /* not interested */
- return 1;
- }
-
- if (!mail_index_lookup_seq_range(ctx->sync_view,
- sync_rec->uid1, sync_rec->uid2,
- &seq1, &seq2)) {
- /* already expunged everything. nothing to do. */
- return 1;
- }
-
- for (seq = seq1; seq <= seq2; seq++) {
- if ((ret = dbox_sync_add_seq(ctx, sync_rec, seq)) <= 0)
- return ret;
- }
- return 1;
-}
-
-static int dbox_sync_index(struct dbox_sync_context *ctx)
-{
- struct mailbox *box = &ctx->mbox->ibox.box;
- const struct mail_index_header *hdr;
- struct mail_index_sync_rec sync_rec;
- struct hash_iterate_context *iter;
- void *key, *value;
- uint32_t seq1, seq2;
- int ret = 1;
-
- hdr = mail_index_get_header(ctx->sync_view);
- if (hdr->uid_validity == 0) {
- /* newly created index file */
- return 0;
- }
-
- /* mark the newly seen messages as recent */
- if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
- hdr->next_uid, &seq1, &seq2)) {
- index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view,
- seq1, seq2);
- }
-
- /* read all changes and group changes to same file_id together */
- ctx->pool = pool_alloconly_create("dbox sync pool", 1024*32);
- ctx->syncs = hash_table_create(default_pool, ctx->pool, 0,
- dbox_sync_file_entry_hash,
- dbox_sync_file_entry_cmp);
-
- while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
- if ((ret = dbox_sync_add(ctx, &sync_rec)) <= 0)
- break;
- }
-
- if (ret > 0) {
- if (ctx->have_storage_expunges) {
- /* prevent a user from saving + expunging messages
- all the time and using lots of disk space.
- but avoid doing this in situations where a user
- simply expunges a lot of mail for the first time.
- that's why we do this calculation before current
- sync: the purging is triggered only after the
- second expunge. */
- if ((ctx->flags & DBOX_SYNC_FLAG_NO_PURGE) == 0 &&
- dbox_map_want_purge(ctx->mbox->storage->map))
- ctx->purge = TRUE;
- }
-
- /* now sync each file separately */
- iter = hash_table_iterate_init(ctx->syncs);
- while (hash_table_iterate(iter, &key, &value)) {
- const struct dbox_sync_file_entry *entry = value;
-
- if ((ret = dbox_sync_file(ctx, entry)) <= 0)
- break;
- }
- hash_table_iterate_deinit(&iter);
- }
-
- if (ret > 0 && ctx->map_trans != NULL) {
- if (dbox_map_transaction_commit(ctx->map_trans) < 0)
- ret = -1;
- dbox_map_transaction_free(&ctx->map_trans);
- }
-
- if (box->v.sync_notify != NULL)
- box->v.sync_notify(box, 0, 0);
-
- hash_table_destroy(&ctx->syncs);
- pool_unref(&ctx->pool);
- return ret;
-}
-
-static int dbox_refresh_header(struct dbox_mailbox *mbox, bool retry)
-{
- struct mail_index_view *view;
- struct dbox_index_header hdr;
- int ret;
-
- view = mail_index_view_open(mbox->ibox.index);
- ret = dbox_read_header(mbox, &hdr);
- mail_index_view_close(&view);
-
- if (ret == 0) {
- mbox->highest_maildir_uid = hdr.highest_maildir_uid;
- ret = mbox->storage->sync_rebuild ? -1 : 0;
- } else if (retry) {
- (void)mail_index_refresh(mbox->ibox.index);
- return dbox_refresh_header(mbox, FALSE);
- }
- return ret;
-}
-
-int dbox_sync_begin(struct dbox_mailbox *mbox, enum dbox_sync_flags flags,
- struct dbox_sync_context **ctx_r)
-{
- struct mail_storage *storage = mbox->ibox.box.storage;
- struct dbox_sync_context *ctx;
- enum mail_index_sync_flags sync_flags = 0;
- unsigned int i;
- int ret;
- bool rebuild, storage_rebuilt = FALSE;
-
- rebuild = dbox_refresh_header(mbox, TRUE) < 0 ||
- (flags & DBOX_SYNC_FLAG_FORCE_REBUILD) != 0;
- if (rebuild) {
- if (dbox_storage_rebuild(mbox->storage) < 0)
- return -1;
- index_mailbox_reset_uidvalidity(&mbox->ibox);
- storage_rebuilt = TRUE;
- }
-
- ctx = i_new(struct dbox_sync_context, 1);
- ctx->mbox = mbox;
- ctx->flags = flags;
-
- if ((mbox->ibox.box.flags & MAILBOX_FLAG_KEEP_RECENT) == 0)
- sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
- if (!rebuild && (flags & DBOX_SYNC_FLAG_FORCE) == 0)
- sync_flags |= MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES;
- if ((flags & DBOX_SYNC_FLAG_FSYNC) != 0)
- sync_flags |= MAIL_INDEX_SYNC_FLAG_FSYNC;
- /* don't write unnecessary dirty flag updates */
- sync_flags |= MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES;
-
- for (i = 0;; i++) {
- ret = mail_index_sync_begin(mbox->ibox.index,
- &ctx->index_sync_ctx,
- &ctx->sync_view, &ctx->trans,
- sync_flags);
- if (ret <= 0) {
- if (ret < 0)
- mail_storage_set_index_error(&mbox->ibox);
- i_free(ctx);
- *ctx_r = NULL;
- return ret;
- }
-
- /* now that we're locked, check again if we want to rebuild */
- if (dbox_refresh_header(mbox, FALSE) < 0)
- ret = 0;
- else {
- if ((ret = dbox_sync_index(ctx)) > 0)
- break;
- }
-
- /* failure. keep the index locked while we're doing a
- rebuild. */
- if (ret == 0) {
- if (!storage_rebuilt) {
- /* we'll need to rebuild storage too.
- try again from the beginning. */
- mbox->storage->sync_rebuild = TRUE;
- mail_index_sync_rollback(&ctx->index_sync_ctx);
- i_free(ctx);
- return dbox_sync_begin(mbox, flags, ctx_r);
- }
- if (mbox->storage->have_multi_msgs) {
- mail_storage_set_critical(storage,
- "dbox %s: Storage keeps breaking",
- ctx->mbox->ibox.box.path);
- ret = -1;
- } else if (i >= DBOX_REBUILD_COUNT) {
- mail_storage_set_critical(storage,
- "dbox %s: Index keeps breaking",
- ctx->mbox->ibox.box.path);
- ret = -1;
- } else {
- /* do a full resync and try again. */
- i_warning("dbox %s: Rebuilding index",
- ctx->mbox->ibox.box.path);
- ret = dbox_sync_index_rebuild(mbox);
- }
- }
- mail_index_sync_rollback(&ctx->index_sync_ctx);
- if (ret < 0) {
- i_free(ctx);
- return -1;
- }
- }
-
- *ctx_r = ctx;
- return 0;
-}
-
-int dbox_sync_finish(struct dbox_sync_context **_ctx, bool success)
-{
- struct dbox_sync_context *ctx = *_ctx;
- int ret = success ? 0 : -1;
-
- *_ctx = NULL;
-
- if (ctx->map_trans != NULL)
- dbox_map_transaction_free(&ctx->map_trans);
-
- if (success) {
- if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) {
- mail_storage_set_index_error(&ctx->mbox->ibox);
- ret = -1;
- }
- } else {
- mail_index_sync_rollback(&ctx->index_sync_ctx);
- }
- if (ctx->path != NULL)
- str_free(&ctx->path);
-
- if (ctx->purge)
- (void)dbox_sync_purge(&ctx->mbox->storage->storage);
- i_free(ctx);
- return ret;
-}
-
-int dbox_sync(struct dbox_mailbox *mbox, enum dbox_sync_flags flags)
-{
- struct dbox_sync_context *sync_ctx;
-
- if (dbox_sync_begin(mbox, flags, &sync_ctx) < 0)
- return -1;
-
- if (sync_ctx == NULL)
- return 0;
- return dbox_sync_finish(&sync_ctx, TRUE);
-}
-
-struct mailbox_sync_context *
-dbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
-{
- struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
- enum dbox_sync_flags dbox_sync_flags = 0;
- int ret = 0;
-
- if (!box->opened) {
- if (mailbox_open(box) < 0)
- ret = -1;
- }
-
- if (ret == 0 && (index_mailbox_want_full_sync(&mbox->ibox, flags) ||
- mbox->storage->sync_rebuild)) {
- if ((flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0)
- dbox_sync_flags |= DBOX_SYNC_FLAG_FORCE_REBUILD;
- ret = dbox_sync(mbox, dbox_sync_flags);
- }
-
- return index_mailbox_sync_init(box, flags, ret < 0);
-}
-
-int dbox_sync_purge(struct mail_storage *_storage)
-{
- struct dbox_storage *storage = (struct dbox_storage *)_storage;
- const ARRAY_TYPE(seq_range) *ref0_file_ids;
- struct dbox_file *file;
- struct seq_range_iter iter;
- unsigned int i = 0;
- uint32_t file_id;
- bool deleted;
- int ret = 0;
-
- ref0_file_ids = dbox_map_get_zero_ref_files(storage->map);
- seq_range_array_iter_init(&iter, ref0_file_ids); i = 0;
- while (seq_range_array_iter_nth(&iter, i++, &file_id)) T_BEGIN {
- file = dbox_file_init_multi(storage, file_id);
- if (dbox_file_open_or_create(file, &deleted) > 0 && !deleted) {
- if (dbox_sync_file_purge(file) < 0)
- ret = -1;
- } else {
- dbox_map_remove_file_id(storage->map, file_id);
- }
- dbox_file_unref(&file);
- } T_END;
- return ret;
-}
+++ /dev/null
-#ifndef DBOX_SYNC_H
-#define DBOX_SYNC_H
-
-struct mailbox;
-struct dbox_mailbox;
-
-enum dbox_sync_flags {
- DBOX_SYNC_FLAG_FORCE = 0x01,
- DBOX_SYNC_FLAG_FSYNC = 0x02,
- DBOX_SYNC_FLAG_FORCE_REBUILD = 0x04,
- DBOX_SYNC_FLAG_NO_PURGE = 0x08
-};
-
-struct dbox_sync_expunge {
- /* keep map_uid first, so we can just cast it to
- dbox_map_update_refcounts() */
- uint32_t map_uid;
- uint32_t seq;
- uint8_t guid_128[MAIL_GUID_128_SIZE];
-};
-
-struct dbox_sync_file_entry {
- uint32_t uid, file_id;
-
- unsigned int move_from_alt:1;
- unsigned int move_to_alt:1;
- ARRAY_DEFINE(expunges, struct dbox_sync_expunge);
-};
-
-struct dbox_sync_context {
- struct dbox_mailbox *mbox;
- struct mail_index_sync_ctx *index_sync_ctx;
- struct mail_index_view *sync_view;
- struct mail_index_transaction *trans;
- struct dbox_map_transaction_context *map_trans;
- enum dbox_sync_flags flags;
-
- string_t *path;
- unsigned int path_dir_prefix_len;
-
- pool_t pool;
- struct hash_table *syncs; /* struct dbox_sync_file_entry */
-
- unsigned int have_storage_expunges:1;
- unsigned int purge:1;
-};
-
-int dbox_sync_begin(struct dbox_mailbox *mbox, enum dbox_sync_flags flags,
- struct dbox_sync_context **ctx_r);
-int dbox_sync_finish(struct dbox_sync_context **ctx, bool success);
-int dbox_sync(struct dbox_mailbox *mbox, enum dbox_sync_flags flags);
-
-int dbox_sync_purge(struct mail_storage *storage);
-int dbox_sync_file(struct dbox_sync_context *ctx,
- const struct dbox_sync_file_entry *entry);
-int dbox_sync_file_purge(struct dbox_file *file);
-
-struct dbox_sync_rebuild_context *
-dbox_sync_index_rebuild_init(struct dbox_mailbox *mbox,
- struct mail_index_view *view,
- struct mail_index_transaction *trans,
- bool storage_rebuild);
-int dbox_sync_index_rebuild_singles(struct dbox_sync_rebuild_context *ctx);
-void dbox_sync_rebuild_index_metadata(struct dbox_sync_rebuild_context *ctx,
- struct dbox_file *file,
- uint32_t new_seq, uint32_t uid);
-void dbox_sync_index_rebuild_deinit(struct dbox_sync_rebuild_context **ctx);
-
-int dbox_sync_index_rebuild(struct dbox_mailbox *mbox);
-
-struct mailbox_sync_context *
-dbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);
-
-#endif