diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/index/maildir/maildir-copy.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-storage/index/maildir/maildir-copy.c')
-rw-r--r-- | src/lib-storage/index/maildir/maildir-copy.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/lib-storage/index/maildir/maildir-copy.c b/src/lib-storage/index/maildir/maildir-copy.c new file mode 100644 index 0000000..1645ddb --- /dev/null +++ b/src/lib-storage/index/maildir/maildir-copy.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ioloop.h" +#include "nfs-workarounds.h" +#include "maildir-storage.h" +#include "maildir-uidlist.h" +#include "maildir-filename.h" +#include "maildir-keywords.h" +#include "maildir-sync.h" +#include "index-mail.h" +#include "mail-copy.h" + +#include <unistd.h> +#include <sys/stat.h> + +struct hardlink_ctx { + const char *dest_path; + bool success:1; +}; + +static int do_hardlink(struct maildir_mailbox *mbox, const char *path, + struct hardlink_ctx *ctx) +{ + int ret; + + if (mbox->storage->storage.set->mail_nfs_storage) + ret = nfs_safe_link(path, ctx->dest_path, FALSE); + else + ret = link(path, ctx->dest_path); + if (ret < 0) { + if (errno == ENOENT) + return 0; + + if (ENOQUOTA(errno)) { + mail_storage_set_error(&mbox->storage->storage, + MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA); + return -1; + } + + /* we could handle the EEXIST condition by changing the + filename, but it practically never happens so just fallback + to standard copying for the rare cases when it does. */ + if (errno == EACCES || ECANTLINK(errno) || errno == EEXIST) + return 1; + + mailbox_set_critical(&mbox->box, "link(%s, %s) failed: %m", + path, ctx->dest_path); + return -1; + } + + ctx->success = TRUE; + return 1; +} + +static int +maildir_copy_hardlink(struct mail_save_context *ctx, struct mail *mail) +{ + struct maildir_mailbox *dest_mbox = MAILDIR_MAILBOX(ctx->transaction->box); + struct maildir_mailbox *src_mbox; + 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 = MAILDIR_MAILBOX(mail->box); + else if (strcmp(mail->box->storage->name, "raw") == 0) { + /* lda uses raw format */ + src_mbox = NULL; + } else { + /* Can't hard link files from the source storage */ + return 0; + } + + /* hard link to tmp/ with a newly generated filename and later when we + have uidlist locked, move it to new/cur. */ + dest_fname = maildir_filename_generate(); + i_zero(&do_ctx); + do_ctx.dest_path = + t_strdup_printf("%s/tmp/%s", mailbox_get_path(&dest_mbox->box), + dest_fname); + if (src_mbox != NULL) { + /* maildir */ + if (maildir_file_do(src_mbox, mail->uid, + do_hardlink, &do_ctx) < 0) + return -1; + } else { + /* raw / lda */ + if (mail_get_special(mail, MAIL_FETCH_STORAGE_ID, + &path) < 0 || *path == '\0') + return 0; + if (do_hardlink(dest_mbox, path, &do_ctx) < 0) + return -1; + } + + if (!do_ctx.success) { + /* couldn't copy with hardlinking, fallback to copying */ + return 0; + } + + /* hardlinked to tmp/, treat as normal copied mail */ + mf = maildir_save_add(ctx, dest_fname, mail); + if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) == 0) { + if (*guid != '\0') + maildir_save_set_dest_basename(ctx, mf, guid); + } + + /* finish copying keywords */ + maildir_save_finish_keywords(ctx); + + /* 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_MAX; + if (mail_get_virtual_size(mail, &vsize) < 0) + vsize = UOFF_T_MAX; + maildir_save_set_sizes(mf, size, vsize); + mail->lookup_abort = old_abort; + return 1; +} + +int maildir_copy(struct mail_save_context *ctx, struct mail *mail) +{ + struct mailbox_transaction_context *_t = ctx->transaction; + struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_t->box); + int ret; + + i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); + + if (mbox->storage->set->maildir_copy_with_hardlinks && + mail_storage_copy_can_use_hardlink(mail->box, &mbox->box)) { + T_BEGIN { + ret = maildir_copy_hardlink(ctx, mail); + } T_END; + + if (ret != 0) { + index_save_context_free(ctx); + return ret > 0 ? 0 : -1; + } + + /* non-fatal hardlinking failure, try the slow way */ + } + + return mail_storage_copy(ctx, mail); +} |