view src/lib-storage/index/dbox-single/sdbox-copy.c @ 21540:6a69f65921ea

lib-storage: Remove unnecessary mail_save_context.dest_mail==NULL checks It can never be NULL after the previous change: "lib-storage: Always create mail_save_context.dest_mail". The code removal in maildir_transaction_save_commit_pre() seemed potentially dangerous, but I don't think such code path is possible anymore. Also even if it is, it's probably fine since the mail_free() is called even earlier than before (although that itself might have been a problem). This also removes last traces of code that made it possible to save mails to mbox without assigning UID to the mail. The previous commit already caused this, so this is just removing dead code.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 09 Feb 2017 17:14:57 +0200
parents 2e2563132d5f
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2002-2017 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)) {
		mail_storage_set_critical(&dest_storage->storage,
			"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) {
			mail_storage_set_critical(&dest_storage->storage, "%s",
				fs_last_error(dest_storage->attachment_fs));
			ret = -1;
		} else {
			array_append(&dest_file->attachment_paths,
				     &dest_relpath, 1);
		}
		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 = (struct dbox_save_context *)_ctx;
	struct sdbox_mailbox *dest_mbox =
		(struct 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 = (struct 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 &= ~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_storage_set_critical(
				_ctx->transaction->box->storage,
				"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);
}