1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "nfs-workarounds.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-filename.h"
#include "maildir-keywords.h"
#include "maildir-sync.h"
#include "index-mail.h"
#include "mail-copy.h"
#include <unistd.h>
#include <sys/stat.h>
struct hardlink_ctx {
const char *dest_path;
bool success:1;
};
static int do_hardlink(struct maildir_mailbox *mbox, const char *path,
struct hardlink_ctx *ctx)
{
int ret;
if (mbox->storage->storage.set->mail_nfs_storage)
ret = nfs_safe_link(path, ctx->dest_path, FALSE);
else
ret = link(path, ctx->dest_path);
if (ret < 0) {
if (errno == ENOENT)
return 0;
if (ENOQUOTA(errno)) {
mail_storage_set_error(&mbox->storage->storage,
MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA);
return -1;
}
/* we could handle the EEXIST condition by changing the
filename, but it practically never happens so just fallback
to standard copying for the rare cases when it does. */
if (errno == EACCES || ECANTLINK(errno) || errno == EEXIST)
return 1;
mailbox_set_critical(&mbox->box, "link(%s, %s) failed: %m",
path, ctx->dest_path);
return -1;
}
ctx->success = TRUE;
return 1;
}
static int
maildir_copy_hardlink(struct mail_save_context *ctx, struct mail *mail)
{
struct maildir_mailbox *dest_mbox = MAILDIR_MAILBOX(ctx->transaction->box);
struct maildir_mailbox *src_mbox;
struct maildir_filename *mf;
struct hardlink_ctx do_ctx;
const char *path, *guid, *dest_fname;
uoff_t vsize, size;
enum mail_lookup_abort old_abort;
if (strcmp(mail->box->storage->name, MAILDIR_STORAGE_NAME) == 0)
src_mbox = MAILDIR_MAILBOX(mail->box);
else if (strcmp(mail->box->storage->name, "raw") == 0) {
/* lda uses raw format */
src_mbox = NULL;
} else {
/* Can't hard link files from the source storage */
return 0;
}
/* hard link to tmp/ with a newly generated filename and later when we
have uidlist locked, move it to new/cur. */
dest_fname = maildir_filename_generate();
i_zero(&do_ctx);
do_ctx.dest_path =
t_strdup_printf("%s/tmp/%s", mailbox_get_path(&dest_mbox->box),
dest_fname);
if (src_mbox != NULL) {
/* maildir */
if (maildir_file_do(src_mbox, mail->uid,
do_hardlink, &do_ctx) < 0)
return -1;
} else {
/* raw / lda */
if (mail_get_special(mail, MAIL_FETCH_STORAGE_ID,
&path) < 0 || *path == '\0')
return 0;
if (do_hardlink(dest_mbox, path, &do_ctx) < 0)
return -1;
}
if (!do_ctx.success) {
/* couldn't copy with hardlinking, fallback to copying */
return 0;
}
/* hardlinked to tmp/, treat as normal copied mail */
mf = maildir_save_add(ctx, dest_fname, mail);
if (mail_get_special(mail, MAIL_FETCH_GUID, &guid) == 0) {
if (*guid != '\0')
maildir_save_set_dest_basename(ctx, mf, guid);
}
/* finish copying keywords */
maildir_save_finish_keywords(ctx);
/* remember size/vsize if possible */
old_abort = mail->lookup_abort;
mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
if (mail_get_physical_size(mail, &size) < 0)
size = UOFF_T_MAX;
if (mail_get_virtual_size(mail, &vsize) < 0)
vsize = UOFF_T_MAX;
maildir_save_set_sizes(mf, size, vsize);
mail->lookup_abort = old_abort;
return 1;
}
int maildir_copy(struct mail_save_context *ctx, struct mail *mail)
{
struct mailbox_transaction_context *_t = ctx->transaction;
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(_t->box);
int ret;
i_assert((_t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
if (mbox->storage->set->maildir_copy_with_hardlinks &&
mail_storage_copy_can_use_hardlink(mail->box, &mbox->box)) {
T_BEGIN {
ret = maildir_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);
}
|