<commit_before>/* Copyright (c) 2002-2012 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;
	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) {
		/* different attachment dirs 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 (!dbox_attachment_parse_extref(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);
		if (fs_link(dest_storage->attachment_fs, src, dest) < 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);
		}
	} 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;
	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);

	src_path = src_file->primary_path;
	ret = nfs_safe_link(src_path, dest_file->cur_path, FALSE);
	if (ret < 0 && errno == ENOENT && src_file->alt_path != NULL) {
		src_path = src_file->alt_path;
		ret = nfs_safe_link(src_path, dest_file->cur_path, FALSE);
	}
	if (ret < 0) {
		if (ECANTLINK(errno))
			ret = 0;
		else if (errno == ENOENT)
			mail_set_expunged(mail);
		else {
			mail_storage_set_critical(
				_ctx->transaction->box->storage,
				"link(%s, %s) failed: %m",
				src_path, dest_file->cur_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) {
		sdbox_file_unlink_aborted_save((struct sdbox_file *)dest_file);
		dbox_file_unref(&src_file);
		dbox_file_unref(&dest_file);
		return ret;
	}

	dbox_save_add_to_index(ctx);
	index_copy_cache_fields(_ctx, mail, ctx->seq);

	sdbox_save_add_file(_ctx, dest_file);
	if (_ctx->dest_mail != NULL)
		mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
	dbox_file_unref(&src_file);
	return 1;
}<commit_msg>sdbox: If copying with link() fails with ENOENT, fallback to trying regular copying. This also fixes missing error reporting for that case.<commit_after>/* Copyright (c) 2002-2012 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;
	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) {
		/* different attachment dirs 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 (!dbox_attachment_parse_extref(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);
		if (fs_link(dest_storage->attachment_fs, src, dest) < 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);
		}
	} 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;
	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);

	src_path = src_file->primary_path;
	ret = nfs_safe_link(src_path, dest_file->cur_path, FALSE);
	if (ret < 0 && errno == ENOENT && src_file->alt_path != NULL) {
		src_path = src_file->alt_path;
		ret = nfs_safe_link(src_path, dest_file->cur_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_file->cur_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) {
		sdbox_file_unlink_aborted_save((struct sdbox_file *)dest_file);
		dbox_file_unref(&src_file);
		dbox_file_unref(&dest_file);
		return ret;
	}

	dbox_save_add_to_index(ctx);
	index_copy_cache_fields(_ctx, mail, ctx->seq);

	sdbox_save_add_file(_ctx, dest_file);
	if (_ctx->dest_mail != NULL)
		mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
	dbox_file_unref(&src_file);
	return 1;
}
{'fraction_non_alphanumeric': 0.12975206611570247, 'fraction_numerical': 0.00885478158205431, 'mean_word_length': 4.739159891598916, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3623195', 'n_tokens_mistral': 3538, 'n_tokens_neox': 3136, 'n_words': 643}