Mercurial > dovecot > core-2.2
changeset 12158:77e71551fe55
maildir: Cleaned up filename/guid preserving code on save/copy.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 23 Sep 2010 17:26:20 +0100 |
parents | 4a753371ae73 |
children | 0818f0a49704 |
files | src/lib-storage/index/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-storage.h |
diffstat | 3 files changed, 107 insertions(+), 128 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-storage/index/maildir/maildir-copy.c Thu Sep 23 16:40:42 2010 +0100 +++ b/src/lib-storage/index/maildir/maildir-copy.c Thu Sep 23 17:26:20 2010 +0100 @@ -3,7 +3,6 @@ #include "lib.h" #include "array.h" #include "ioloop.h" -#include "str.h" #include "nfs-workarounds.h" #include "maildir-storage.h" #include "maildir-uidlist.h" @@ -18,85 +17,19 @@ #include <sys/stat.h> struct hardlink_ctx { - string_t *dest_path; - const char *dest_fname; - unsigned int base_end_pos; - - unsigned int size_set:1; - unsigned int vsize_set:1; + const char *dest_path; unsigned int success:1; - unsigned int preserve_filename:1; }; -static int do_save_mail_size(struct maildir_mailbox *mbox, const char *path, - struct hardlink_ctx *ctx) -{ - const char *fname, *str; - struct stat st; - uoff_t size; - - fname = strrchr(path, '/'); - fname = fname != NULL ? fname + 1 : path; - - if (!maildir_filename_get_size(fname, MAILDIR_EXTRA_FILE_SIZE, - &size)) { - if (stat(path, &st) < 0) { - if (errno == ENOENT) - return 0; - mail_storage_set_critical(&mbox->storage->storage, - "stat(%s) failed: %m", path); - return -1; - } - size = st.st_size; - } - - str = t_strdup_printf(",%c=%"PRIuUOFF_T, MAILDIR_EXTRA_FILE_SIZE, size); - str_insert(ctx->dest_path, ctx->base_end_pos, str); - - ctx->dest_fname = strrchr(str_c(ctx->dest_path), '/') + 1; - ctx->size_set = TRUE; - return 1; -} - -static void do_save_mail_vsize(const char *path, struct hardlink_ctx *ctx) -{ - const char *fname, *str; - uoff_t size; - - fname = strrchr(path, '/'); - fname = fname != NULL ? fname + 1 : path; - - if (!maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE, - &size)) - return; - - str = t_strdup_printf(",%c=%"PRIuUOFF_T, - MAILDIR_EXTRA_VIRTUAL_SIZE, size); - str_insert(ctx->dest_path, ctx->base_end_pos, str); - - ctx->dest_fname = strrchr(str_c(ctx->dest_path), '/') + 1; - ctx->vsize_set = TRUE; -} - static int do_hardlink(struct maildir_mailbox *mbox, const char *path, struct hardlink_ctx *ctx) { int ret; - if (!ctx->preserve_filename) { - if (!ctx->size_set) { - if ((ret = do_save_mail_size(mbox, path, ctx)) <= 0) - return ret; - } - /* set virtual size if it's in the original file name */ - if (!ctx->vsize_set) - do_save_mail_vsize(path, ctx); - } - if (mbox->storage->storage.set->mail_nfs_storage) - ret = nfs_safe_link(path, str_c(ctx->dest_path), FALSE); + ret = nfs_safe_link(path, ctx->dest_path, FALSE); else - ret = link(path, str_c(ctx->dest_path)); + ret = link(path, ctx->dest_path); if (ret < 0) { if (errno == ENOENT) return 0; @@ -115,7 +48,7 @@ mail_storage_set_critical(&mbox->storage->storage, "link(%s, %s) failed: %m", - path, str_c(ctx->dest_path)); + path, ctx->dest_path); return -1; } @@ -124,13 +57,16 @@ } static int -maildir_copy_hardlink(struct mail_save_context *ctx, - struct hardlink_ctx *do_ctx, struct mail *mail) +maildir_copy_hardlink(struct mail_save_context *ctx, struct mail *mail) { struct maildir_mailbox *dest_mbox = (struct maildir_mailbox *)ctx->transaction->box; struct maildir_mailbox *src_mbox; - const char *path, *guid; + struct maildir_filename *mf; + struct hardlink_ctx do_ctx; + const char *path, *guid, *dest_fname; + uoff_t vsize, size; + enum mail_lookup_abort old_abort; if (strcmp(mail->box->storage->name, MAILDIR_STORAGE_NAME) == 0) src_mbox = (struct maildir_mailbox *)mail->box; @@ -142,45 +78,47 @@ return 0; } - do_ctx->dest_path = str_new(default_pool, 512); - - if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) < 0) - guid = ""; - if (*guid == '\0') { - /* the generated filename is _always_ unique, so we don't - bother trying to check if it already exists */ - do_ctx->dest_fname = maildir_filename_generate(); - } else { - do_ctx->dest_fname = guid; - do_ctx->preserve_filename = TRUE; - } - - /* hard link to tmp/ with basename and later when we + /* hard link to tmp/ with a newly generated filename and later when we have uidlist locked, move it to new/cur. */ - str_printfa(do_ctx->dest_path, "%s/tmp/%s", - dest_mbox->box.path, do_ctx->dest_fname); - do_ctx->base_end_pos = str_len(do_ctx->dest_path); + dest_fname = maildir_filename_generate(); + memset(&do_ctx, 0, sizeof(do_ctx)); + do_ctx.dest_path = + t_strdup_printf("%s/tmp/%s", dest_mbox->box.path, dest_fname); if (src_mbox != NULL) { /* maildir */ if (maildir_file_do(src_mbox, mail->uid, - do_hardlink, do_ctx) < 0) + do_hardlink, &do_ctx) < 0) return -1; } else { /* raw / lda */ if (mail_get_special(mail, MAIL_FETCH_UIDL_FILE_NAME, &path) < 0 || *path == '\0') return 0; - if (do_hardlink(dest_mbox, path, do_ctx) < 0) + if (do_hardlink(dest_mbox, path, &do_ctx) < 0) return -1; } - if (!do_ctx->success) { + if (!do_ctx.success) { /* couldn't copy with hardlinking, fallback to copying */ return 0; } /* hardlinked to tmp/, treat as normal copied mail */ - maildir_save_add(ctx, do_ctx->dest_fname, do_ctx->preserve_filename); + mf = maildir_save_add(ctx, dest_fname); + if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) == 0) { + if (*guid != '\0') + maildir_save_set_dest_basename(ctx, mf, guid); + } + + /* remember size/vsize if possible */ + old_abort = mail->lookup_abort; + mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; + if (mail_get_physical_size(mail, &size) < 0) + size = (uoff_t)-1; + if (mail_get_virtual_size(mail, &vsize) < 0) + vsize = (uoff_t)-1; + maildir_save_set_sizes(mf, size, vsize); + mail->lookup_abort = old_abort; return 1; } @@ -195,7 +133,6 @@ { struct mailbox_transaction_context *_t = ctx->transaction; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_t->box; - struct hardlink_ctx do_ctx; int ret; i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); @@ -203,10 +140,7 @@ if (mbox->storage->set->maildir_copy_with_hardlinks && maildir_compatible_file_modes(&mbox->box, mail->box)) { T_BEGIN { - memset(&do_ctx, 0, sizeof(do_ctx)); - ret = maildir_copy_hardlink(ctx, &do_ctx, mail); - if (do_ctx.dest_path != NULL) - str_free(&do_ctx.dest_path); + ret = maildir_copy_hardlink(ctx, mail); } T_END; if (ret != 0) {
--- a/src/lib-storage/index/maildir/maildir-save.c Thu Sep 23 16:40:42 2010 +0100 +++ b/src/lib-storage/index/maildir/maildir-save.c Thu Sep 23 17:26:20 2010 +0100 @@ -136,8 +136,8 @@ return &ctx->ctx; } -void maildir_save_add(struct mail_save_context *_ctx, const char *base_fname, - bool preserve_filename) +struct maildir_filename * +maildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct maildir_filename *mf; @@ -159,13 +159,10 @@ keyword_count = _ctx->keywords == NULL ? 0 : _ctx->keywords->count; mf = p_malloc(ctx->pool, sizeof(*mf) + sizeof(unsigned int) * keyword_count); - mf->tmp_name = mf->dest_basename = p_strdup(ctx->pool, base_fname); + mf->tmp_name = mf->dest_basename = p_strdup(ctx->pool, tmp_fname); mf->flags = _ctx->flags; mf->size = (uoff_t)-1; mf->vsize = (uoff_t)-1; - mf->preserve_filename = preserve_filename; - if (preserve_filename) - ctx->have_preserved_filenames = TRUE; ctx->file_last = mf; i_assert(*ctx->files_tail == NULL); @@ -220,6 +217,25 @@ ctx->input = input; ctx->cur_dest_mail = _ctx->dest_mail; } + return mf; +} + +void maildir_save_set_dest_basename(struct mail_save_context *_ctx, + struct maildir_filename *mf, + const char *basename) +{ + struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; + + mf->preserve_filename = TRUE; + mf->dest_basename = p_strdup(ctx->pool, basename); + ctx->have_preserved_filenames = TRUE; +} + +void maildir_save_set_sizes(struct maildir_filename *mf, + uoff_t size, uoff_t vsize) +{ + mf->size = size; + mf->vsize = vsize; } static bool @@ -297,12 +313,12 @@ } static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir, - const char **fname) + const char **fname_r) { struct mailbox *box = &mbox->box; struct stat st; unsigned int prefix_len; - const char *tmp_fname = *fname; + const char *tmp_fname; string_t *path; int fd; @@ -312,8 +328,7 @@ prefix_len = str_len(path); for (;;) { - if (tmp_fname == NULL) - tmp_fname = maildir_filename_generate(); + tmp_fname = maildir_filename_generate(); str_truncate(path, prefix_len); str_append(path, tmp_fname); @@ -341,7 +356,7 @@ tmp_fname = NULL; } - *fname = tmp_fname; + *fname_r = tmp_fname; if (fd == -1) { if (ENOSPACE(errno)) { mail_storage_set_error(box->storage, @@ -381,13 +396,14 @@ int maildir_save_begin(struct mail_save_context *_ctx, struct istream *input) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; + struct maildir_filename *mf; /* new mail, new failure state */ ctx->failed = FALSE; T_BEGIN { /* create a new file in tmp/ directory */ - const char *fname = _ctx->guid; + const char *fname; ctx->fd = maildir_create_tmp(ctx->mbox, ctx->tmpdir, &fname); if (ctx->fd == -1) @@ -397,7 +413,11 @@ ctx->input = i_stream_create_crlf(input); else ctx->input = i_stream_create_lf(input); - maildir_save_add(_ctx, fname, fname == _ctx->guid); + mf = maildir_save_add(_ctx, fname); + if (_ctx->guid != NULL) { + maildir_save_set_dest_basename(_ctx, mf, + _ctx->guid); + } } } T_END; @@ -766,7 +786,8 @@ static void maildir_filename_check_conflicts(struct maildir_save_context *ctx, - struct maildir_filename *mf) + struct maildir_filename *mf, + struct maildir_filename *prev_mf) { uoff_t size; @@ -775,7 +796,9 @@ ctx->locked_uidlist_refresh = TRUE; } - if (maildir_uidlist_get_full_filename(ctx->mbox->uidlist, + if ((prev_mf != NULL && + strcmp(mf->dest_basename, prev_mf->dest_basename) == 0) || + maildir_uidlist_get_full_filename(ctx->mbox->uidlist, mf->dest_basename) != NULL) { /* file already exists. give it another name. but preserve the size/vsize in the filename if possible */ @@ -795,22 +818,38 @@ } static int -maildir_save_move_files_to_newcur(struct maildir_save_context *ctx, - struct maildir_filename **last_mf_r) +maildir_filename_dest_basename_cmp(struct maildir_filename *const *f1, + struct maildir_filename *const *f2) { - struct maildir_filename *mf; + return strcmp((*f1)->dest_basename, (*f2)->dest_basename); +} + +static int +maildir_save_move_files_to_newcur(struct maildir_save_context *ctx) +{ + ARRAY_DEFINE(files, struct maildir_filename *); + struct maildir_filename *mf, *const *mfp, *prev_mf; bool newdir, new_changed, cur_changed; int ret; - *last_mf_r = NULL; + /* put files into an array sorted by the destination filename. + this way we can easily check if there are duplicate destination + filenames within this transaction. */ + t_array_init(&files, ctx->files_count); + for (mf = ctx->files; mf != NULL; mf = mf->next) + array_append(&files, &mf, 1); + array_sort(&files, maildir_filename_dest_basename_cmp); - new_changed = cur_changed = FALSE; - for (mf = ctx->files; mf != NULL; mf = mf->next) { + new_changed = cur_changed = FALSE; prev_mf = FALSE; + array_foreach(&files, mfp) { + mf = *mfp; T_BEGIN { const char *dest; - if (mf->preserve_filename) - maildir_filename_check_conflicts(ctx, mf); + if (mf->preserve_filename) { + maildir_filename_check_conflicts(ctx, mf, + prev_mf); + } newdir = maildir_get_dest_filename(ctx, mf, &dest); if (newdir) @@ -821,7 +860,7 @@ } T_END; if (ret < 0) return -1; - *last_mf_r = mf; + prev_mf = mf; } return maildir_transaction_fsync_dirs(ctx, new_changed, cur_changed); @@ -871,7 +910,6 @@ { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mailbox_transaction_context *_t = _ctx->transaction; - struct maildir_filename *last_mf; enum maildir_uidlist_sync_flags sync_flags; int ret; @@ -917,7 +955,9 @@ return -1; } - ret = maildir_save_move_files_to_newcur(ctx, &last_mf); + T_BEGIN { + ret = maildir_save_move_files_to_newcur(ctx); + } T_END; if (ctx->locked) { if (ret == 0) { /* update dovecot-uidlist file. */
--- a/src/lib-storage/index/maildir/maildir-storage.h Thu Sep 23 16:40:42 2010 +0100 +++ b/src/lib-storage/index/maildir/maildir-storage.h Thu Sep 23 17:26:20 2010 +0100 @@ -117,8 +117,13 @@ int maildir_save_finish(struct mail_save_context *ctx); void maildir_save_cancel(struct mail_save_context *ctx); -void maildir_save_add(struct mail_save_context *_ctx, const char *base_fname, - bool preserve_filename); +struct maildir_filename * +maildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname); +void maildir_save_set_dest_basename(struct mail_save_context *ctx, + struct maildir_filename *mf, + const char *basename); +void maildir_save_set_sizes(struct maildir_filename *mf, + uoff_t size, uoff_t vsize); const char *maildir_save_file_get_path(struct mailbox_transaction_context *t, uint32_t seq);