bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "lib.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "nfs-workarounds.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "fs-api.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "dbox-save.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "dbox-attachment.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "sdbox-storage.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "sdbox-file.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "mail-copy.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainensdbox_file_copy_attachments(struct sdbox_file *src_file,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct sdbox_file *dest_file)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct dbox_storage *src_storage = src_file->file.storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct dbox_storage *dest_storage = dest_file->file.storage;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct fs_file *src_fsfile, *dest_fsfile;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ARRAY_TYPE(mail_attachment_extref) extrefs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const struct mail_attachment_extref *extref;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char *extrefs_line, *src, *dest, *dest_relpath;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_t pool;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (src_storage->attachment_dir == NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* no attachments in source storage */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (dest_storage->attachment_dir == NULL ||
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen strcmp(src_storage->attachment_dir,
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen dest_storage->attachment_dir) != 0 ||
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen strcmp(src_storage->storage.set->mail_attachment_fs,
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen dest_storage->storage.set->mail_attachment_fs) != 0 ||
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen strcmp(src_storage->storage.set->mail_attachment_hash,
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen dest_storage->storage.set->mail_attachment_hash) != 0) {
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen /* different attachment dirs/settings between storages.
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen have to copy the slow way. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if ((ret = sdbox_file_get_attachments(&src_file->file,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen &extrefs_line)) <= 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret < 0 ? -1 : 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool = pool_alloconly_create("sdbox attachments copy", 1024);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen p_array_init(&extrefs, pool, 16);
52de839a8249bff5eace53dc1401b28baa0c124bTimo Sirainen if (!index_attachment_parse_extrefs(extrefs_line, pool, &extrefs)) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&dest_file->mbox->box,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "Can't copy %s with corrupted extref metadata: %s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen src_file->file.cur_path, extrefs_line);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_unref(&pool);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dest_file->attachment_pool =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_alloconly_create("sdbox attachment copy paths", 512);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen p_array_init(&dest_file->attachment_paths, dest_file->attachment_pool,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen array_count(&extrefs));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen array_foreach(&extrefs, extref) T_BEGIN {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen src = t_strdup_printf("%s/%s", dest_storage->attachment_dir,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen sdbox_file_attachment_relpath(src_file, extref->path));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dest_relpath = p_strconcat(dest_file->attachment_pool,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->path, "-",
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_generate(), NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dest = t_strdup_printf("%s/%s", dest_storage->attachment_dir,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dest_relpath);
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen /* we verified above that attachment_fs is compatible for
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen src and dest, so it doesn't matter which storage's
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen attachment_fs we use. in any case we need to use the same
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen one or fs_copy() will crash with assert. */
98449946caeaf8a3b413a0d93128315b158cbffbTimo Sirainen src_fsfile = fs_file_init(dest_storage->attachment_fs, src,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen FS_OPEN_MODE_READONLY);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen dest_fsfile = fs_file_init(dest_storage->attachment_fs, dest,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen FS_OPEN_MODE_READONLY);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (fs_copy(src_fsfile, dest_fsfile) < 0) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&dest_file->mbox->box, "%s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_last_error(dest_storage->attachment_fs));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen array_append(&dest_file->attachment_paths,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen &dest_relpath, 1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_file_deinit(&src_fsfile);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fs_file_deinit(&dest_fsfile);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } T_END;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_unref(&pool);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainenstatic int
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainensdbox_copy_hardlink(struct mail_save_context *_ctx, struct mail *mail)
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen{
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek struct dbox_save_context *ctx = DBOX_SAVECTX(_ctx);
df3963075d67f76ade66b6a74764f4eae31d9c87Josef 'Jeff' Sipek struct sdbox_mailbox *dest_mbox = SDBOX_MAILBOX(_ctx->transaction->box);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct sdbox_mailbox *src_mbox;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct dbox_file *src_file, *dest_file;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen const char *src_path, *dest_path;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen int ret;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (strcmp(mail->box->storage->name, SDBOX_STORAGE_NAME) == 0)
df3963075d67f76ade66b6a74764f4eae31d9c87Josef 'Jeff' Sipek src_mbox = SDBOX_MAILBOX(mail->box);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen else {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen /* Source storage isn't sdbox, can't hard link */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return 0;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen src_file = sdbox_file_init(src_mbox, mail->uid);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen dest_file = sdbox_file_init(dest_mbox, 0);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen ctx->ctx.data.flags &= ~DBOX_INDEX_FLAG_ALT;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen src_path = src_file->primary_path;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen dest_path = dest_file->primary_path;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen ret = nfs_safe_link(src_path, dest_path, FALSE);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ret < 0 && errno == ENOENT && src_file->alt_path != NULL) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen src_path = src_file->alt_path;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen if (dest_file->alt_path != NULL) {
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen dest_path = dest_file->cur_path = dest_file->alt_path;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen ctx->ctx.data.flags |= DBOX_INDEX_FLAG_ALT;
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen }
2a77044395c864cc791cecd34b03002094f4973bTimo Sirainen ret = nfs_safe_link(src_path, dest_path, FALSE);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ret < 0) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ECANTLINK(errno))
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen ret = 0;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen else if (errno == ENOENT) {
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen /* try if the fallback copying code can still
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen read the file (the mail could still have the
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen stream open) */
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen ret = 0;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen } else {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mail_set_critical(mail, "link(%s, %s) failed: %m",
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi src_path, dest_path);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen dbox_file_unref(&src_file);
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen dbox_file_unref(&dest_file);
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen return ret;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = sdbox_file_copy_attachments((struct sdbox_file *)src_file,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen (struct sdbox_file *)dest_file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret <= 0) {
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen (void)sdbox_file_unlink_aborted_save((struct sdbox_file *)dest_file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dbox_file_unref(&src_file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dbox_file_unref(&dest_file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
9fd6f70ed6d4733ae5eabd49822bda280393e403Timo Sirainen ((struct sdbox_file *)dest_file)->written_to_disk = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen dbox_save_add_to_index(ctx);
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen index_copy_cache_fields(_ctx, mail, ctx->seq);
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen sdbox_save_add_file(_ctx, dest_file);
a825281071af96cc148e49c64ac36d8c5cf26f71Timo Sirainen mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen dbox_file_unref(&src_file);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return 1;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen}
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainenint sdbox_copy(struct mail_save_context *_ctx, struct mail *mail)
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen{
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct mailbox_transaction_context *_t = _ctx->transaction;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)_t->box;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen int ret;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen ctx->finished = TRUE;
90e39174d397567c101dbf694761371af3682928Timo Sirainen if (mail_storage_copy_can_use_hardlink(mail->box, &mbox->box) &&
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen _ctx->data.guid == NULL) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen T_BEGIN {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen ret = sdbox_copy_hardlink(_ctx, mail);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen } T_END;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ret != 0) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen index_save_context_free(_ctx);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return ret > 0 ? 0 : -1;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen /* non-fatal hardlinking failure, try the slow way */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return mail_storage_copy(_ctx, mail);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen}