summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mail-copy.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-storage/mail-copy.c
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.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/mail-copy.c')
-rw-r--r--src/lib-storage/mail-copy.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/lib-storage/mail-copy.c b/src/lib-storage/mail-copy.c
new file mode 100644
index 0000000..82c5f9a
--- /dev/null
+++ b/src/lib-storage/mail-copy.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "mail-storage-private.h"
+#include "mail-copy.h"
+
+static void
+mail_copy_set_failed(struct mail_save_context *ctx, struct mail *mail,
+ const char *func)
+{
+ const char *errstr;
+ enum mail_error error;
+
+ if (ctx->transaction->box->storage == mail->box->storage)
+ return;
+
+ errstr = mail_storage_get_last_error(mail->box->storage, &error);
+ mail_storage_set_error(ctx->transaction->box->storage, error,
+ t_strdup_printf("%s (%s)", errstr, func));
+}
+
+int mail_save_copy_default_metadata(struct mail_save_context *ctx,
+ struct mail *mail)
+{
+ const char *from_envelope, *guid;
+ time_t received_date;
+
+ if (ctx->data.received_date == (time_t)-1) {
+ if (mail_get_received_date(mail, &received_date) < 0) {
+ mail_copy_set_failed(ctx, mail, "received-date");
+ return -1;
+ }
+ mailbox_save_set_received_date(ctx, received_date, 0);
+ }
+ if (ctx->data.from_envelope == NULL) {
+ if (mail_get_special(mail, MAIL_FETCH_FROM_ENVELOPE,
+ &from_envelope) < 0) {
+ mail_copy_set_failed(ctx, mail, "from-envelope");
+ return -1;
+ }
+ if (*from_envelope != '\0')
+ mailbox_save_set_from_envelope(ctx, from_envelope);
+ }
+ if (ctx->data.guid == NULL) {
+ if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) < 0) {
+ mail_copy_set_failed(ctx, mail, "guid");
+ return -1;
+ }
+ if (*guid != '\0')
+ mailbox_save_set_guid(ctx, guid);
+ }
+ return 0;
+}
+
+static int
+mail_storage_try_copy(struct mail_save_context **_ctx, struct mail *mail)
+{
+ struct mail_save_context *ctx = *_ctx;
+ struct mail_private *pmail = (struct mail_private *)mail;
+ struct istream *input;
+
+ ctx->copying_via_save = TRUE;
+
+ /* we need to open the file in any case. caching metadata is unlikely
+ to help anything. */
+ pmail->v.set_uid_cache_updates(mail, TRUE);
+
+ if (mail_get_stream_because(mail, NULL, NULL, "copying", &input) < 0) {
+ mail_copy_set_failed(ctx, mail, "stream");
+ return -1;
+ }
+ if (mail_save_copy_default_metadata(ctx, mail) < 0)
+ return -1;
+
+ if (mailbox_save_begin(_ctx, input) < 0)
+ return -1;
+
+ ssize_t ret;
+ do {
+ if (mailbox_save_continue(ctx) < 0)
+ break;
+ ret = i_stream_read(input);
+ i_assert(ret != 0);
+ } while (ret != -1);
+
+ if (input->stream_errno != 0) {
+ mailbox_set_critical(ctx->transaction->box,
+ "copy: i_stream_read(%s) failed: %s",
+ i_stream_get_name(input), i_stream_get_error(input));
+ return -1;
+ }
+ return 0;
+}
+
+int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail)
+{
+ i_assert(ctx->copying_or_moving);
+
+ if (mail_storage_try_copy(&ctx, mail) < 0) {
+ if (ctx != NULL)
+ mailbox_save_cancel(&ctx);
+ return -1;
+ }
+ return mailbox_save_finish(&ctx);
+}
+
+bool mail_storage_copy_can_use_hardlink(struct mailbox *src,
+ struct mailbox *dest)
+{
+ const struct mailbox_permissions *src_perm =
+ mailbox_get_permissions(src);
+ const struct mailbox_permissions *dest_perm =
+ mailbox_get_permissions(dest);
+
+ if (src_perm->file_uid != dest_perm->file_uid) {
+ /* if we don't have read permissions, we can't hard link
+ (basically we'll catch 0600 files here) */
+ if ((src_perm->file_create_mode & 0022) == 0)
+ return FALSE;
+ }
+
+ return src_perm->file_create_mode == dest_perm->file_create_mode &&
+ src_perm->file_create_gid == dest_perm->file_create_gid &&
+ !dest->disable_reflink_copy_to;
+}