diff options
Diffstat (limited to 'src/lib-storage/index/dbox-single/sdbox-copy.c')
-rw-r--r-- | src/lib-storage/index/dbox-single/sdbox-copy.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/lib-storage/index/dbox-single/sdbox-copy.c b/src/lib-storage/index/dbox-single/sdbox-copy.c new file mode 100644 index 0000000..48a3f59 --- /dev/null +++ b/src/lib-storage/index/dbox-single/sdbox-copy.c @@ -0,0 +1,185 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "nfs-workarounds.h" +#include "fs-api.h" +#include "dbox-save.h" +#include "dbox-attachment.h" +#include "sdbox-storage.h" +#include "sdbox-file.h" +#include "mail-copy.h" + +static int +sdbox_file_copy_attachments(struct sdbox_file *src_file, + struct sdbox_file *dest_file) +{ + struct dbox_storage *src_storage = src_file->file.storage; + struct dbox_storage *dest_storage = dest_file->file.storage; + struct fs_file *src_fsfile, *dest_fsfile; + ARRAY_TYPE(mail_attachment_extref) extrefs; + const struct mail_attachment_extref *extref; + const char *extrefs_line, *src, *dest, *dest_relpath; + pool_t pool; + int ret; + + if (src_storage->attachment_dir == NULL) { + /* no attachments in source storage */ + return 1; + } + if (dest_storage->attachment_dir == NULL || + strcmp(src_storage->attachment_dir, + dest_storage->attachment_dir) != 0 || + strcmp(src_storage->storage.set->mail_attachment_fs, + dest_storage->storage.set->mail_attachment_fs) != 0 || + strcmp(src_storage->storage.set->mail_attachment_hash, + dest_storage->storage.set->mail_attachment_hash) != 0) { + /* different attachment dirs/settings between storages. + have to copy the slow way. */ + return 0; + } + + if ((ret = sdbox_file_get_attachments(&src_file->file, + &extrefs_line)) <= 0) + return ret < 0 ? -1 : 1; + + pool = pool_alloconly_create("sdbox attachments copy", 1024); + p_array_init(&extrefs, pool, 16); + if (!index_attachment_parse_extrefs(extrefs_line, pool, &extrefs)) { + mailbox_set_critical(&dest_file->mbox->box, + "Can't copy %s with corrupted extref metadata: %s", + src_file->file.cur_path, extrefs_line); + pool_unref(&pool); + return -1; + } + + dest_file->attachment_pool = + pool_alloconly_create("sdbox attachment copy paths", 512); + p_array_init(&dest_file->attachment_paths, dest_file->attachment_pool, + array_count(&extrefs)); + + ret = 1; + array_foreach(&extrefs, extref) T_BEGIN { + src = t_strdup_printf("%s/%s", dest_storage->attachment_dir, + sdbox_file_attachment_relpath(src_file, extref->path)); + dest_relpath = p_strconcat(dest_file->attachment_pool, + extref->path, "-", + guid_generate(), NULL); + dest = t_strdup_printf("%s/%s", dest_storage->attachment_dir, + dest_relpath); + /* we verified above that attachment_fs is compatible for + src and dest, so it doesn't matter which storage's + attachment_fs we use. in any case we need to use the same + one or fs_copy() will crash with assert. */ + src_fsfile = fs_file_init(dest_storage->attachment_fs, src, + FS_OPEN_MODE_READONLY); + dest_fsfile = fs_file_init(dest_storage->attachment_fs, dest, + FS_OPEN_MODE_READONLY); + if (fs_copy(src_fsfile, dest_fsfile) < 0) { + mailbox_set_critical(&dest_file->mbox->box, "%s", + fs_file_last_error(dest_fsfile)); + ret = -1; + } else { + array_push_back(&dest_file->attachment_paths, + &dest_relpath); + } + fs_file_deinit(&src_fsfile); + fs_file_deinit(&dest_fsfile); + } T_END; + pool_unref(&pool); + return ret; +} + +static int +sdbox_copy_hardlink(struct mail_save_context *_ctx, struct mail *mail) +{ + struct dbox_save_context *ctx = DBOX_SAVECTX(_ctx); + struct sdbox_mailbox *dest_mbox = SDBOX_MAILBOX(_ctx->transaction->box); + struct sdbox_mailbox *src_mbox; + struct dbox_file *src_file, *dest_file; + const char *src_path, *dest_path; + int ret; + + if (strcmp(mail->box->storage->name, SDBOX_STORAGE_NAME) == 0) + src_mbox = SDBOX_MAILBOX(mail->box); + else { + /* Source storage isn't sdbox, can't hard link */ + return 0; + } + + src_file = sdbox_file_init(src_mbox, mail->uid); + dest_file = sdbox_file_init(dest_mbox, 0); + + ctx->ctx.data.flags &= ENUM_NEGATE(DBOX_INDEX_FLAG_ALT); + + src_path = src_file->primary_path; + dest_path = dest_file->primary_path; + ret = nfs_safe_link(src_path, dest_path, FALSE); + if (ret < 0 && errno == ENOENT && src_file->alt_path != NULL) { + src_path = src_file->alt_path; + if (dest_file->alt_path != NULL) { + dest_path = dest_file->cur_path = dest_file->alt_path; + ctx->ctx.data.flags |= DBOX_INDEX_FLAG_ALT; + } + ret = nfs_safe_link(src_path, dest_path, FALSE); + } + if (ret < 0) { + if (ECANTLINK(errno)) + ret = 0; + else if (errno == ENOENT) { + /* try if the fallback copying code can still + read the file (the mail could still have the + stream open) */ + ret = 0; + } else { + mail_set_critical(mail, "link(%s, %s) failed: %m", + src_path, dest_path); + } + dbox_file_unref(&src_file); + dbox_file_unref(&dest_file); + return ret; + } + + ret = sdbox_file_copy_attachments((struct sdbox_file *)src_file, + (struct sdbox_file *)dest_file); + if (ret <= 0) { + (void)sdbox_file_unlink_aborted_save((struct sdbox_file *)dest_file); + dbox_file_unref(&src_file); + dbox_file_unref(&dest_file); + return ret; + } + ((struct sdbox_file *)dest_file)->written_to_disk = TRUE; + + dbox_save_add_to_index(ctx); + index_copy_cache_fields(_ctx, mail, ctx->seq); + + sdbox_save_add_file(_ctx, dest_file); + mail_set_seq_saving(_ctx->dest_mail, ctx->seq); + dbox_file_unref(&src_file); + return 1; +} + +int sdbox_copy(struct mail_save_context *_ctx, struct mail *mail) +{ + struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; + struct mailbox_transaction_context *_t = _ctx->transaction; + struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_t->box; + int ret; + + i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); + + ctx->finished = TRUE; + if (mail_storage_copy_can_use_hardlink(mail->box, &mbox->box) && + _ctx->data.guid == NULL) { + T_BEGIN { + ret = sdbox_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); +} |