maildir-copy.c revision 721b683078dab7a46e2a42cfcb81c7d066b57dea
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2002-2004 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "array.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-storage.h"
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen#include "maildir-uidlist.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "maildir-keywords.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "index-mail.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-copy.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include <stdlib.h>
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include <unistd.h>
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainenstruct hardlink_ctx {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *dest_path;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen bool success;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen};
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int do_hardlink(struct maildir_mailbox *mbox, const char *path,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen void *context)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct hardlink_ctx *ctx = context;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (link(path, ctx->dest_path) < 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (errno == ENOENT)
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen return 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen if (ENOSPACE(errno)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_error(STORAGE(mbox->storage),
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen "Not enough disk space");
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (errno == EACCES || errno == EXDEV)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return 1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "link(%s, %s) failed: %m",
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen path, ctx->dest_path);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return -1;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen }
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen ctx->success = TRUE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenmaildir_copy_hardlink(struct maildir_transaction_context *t, struct mail *mail,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen enum mail_flags flags, struct mail_keywords *keywords,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct mail *dest_mail)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen struct maildir_mailbox *dest_mbox =
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen (struct maildir_mailbox *)t->ictx.ibox;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen struct maildir_mailbox *src_mbox =
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen (struct maildir_mailbox *)mail->box;
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen struct maildir_save_context *ctx;
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen struct hardlink_ctx do_ctx;
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen const char *dest_fname;
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen uint32_t seq;
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen if (t->save_ctx == NULL)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen t->save_ctx = maildir_save_transaction_init(t);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen ctx = t->save_ctx;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen /* don't allow caller to specify recent flag */
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen flags &= ~MAIL_RECENT;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (dest_mbox->ibox.keep_recent)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen flags |= MAIL_RECENT;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&do_ctx, 0, sizeof(do_ctx));
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen /* the generated filename is _always_ unique, so we don't bother
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen trying to check if it already exists */
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen dest_fname = maildir_generate_tmp_filename(&ioloop_timeval);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen if (keywords == NULL || keywords->count == 0) {
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen /* no keywords, hardlink directly to destination */
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen if (flags == MAIL_RECENT) {
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen do_ctx.dest_path =
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen t_strconcat(dest_mbox->path, "/new/",
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen dest_fname, NULL);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen } else {
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen const char *fname;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen fname = maildir_filename_set_flags(NULL, dest_fname,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen flags, NULL);
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen do_ctx.dest_path =
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen t_strconcat(dest_mbox->path, "/cur/",
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen fname, NULL);
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen /* keywords, hardlink to tmp/ with basename and later when we
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen have uidlist locked, move it to new/cur. */
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen do_ctx.dest_path =
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen t_strconcat(dest_mbox->path, "/tmp/", dest_fname, NULL);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (maildir_file_do(src_mbox, mail->uid, do_hardlink, &do_ctx) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (!do_ctx.success) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* couldn't copy with hardlinking, fallback to copying */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (keywords == NULL || keywords->count == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* hardlinked to destination, set hardlinked-flag */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen seq = maildir_save_add(t, dest_fname,
c29216637957d4b3126c6929ac5ba98138256ce1Timo Sirainen flags | MAILDIR_SAVE_FLAG_HARDLINK, NULL,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen dest_mail != NULL);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen } else {
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen /* hardlinked to tmp/, treat as normal copied mail */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq = maildir_save_add(t, dest_fname, flags, keywords,
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen dest_mail != NULL);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen }
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (dest_mail != NULL) {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen i_assert(seq != 0);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (mail_set_seq(dest_mail, seq) < 0)
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen return -1;
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen }
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen return 1;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen}
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint maildir_copy(struct mailbox_transaction_context *_t, struct mail *mail,
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen enum mail_flags flags, struct mail_keywords *keywords,
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen struct mail *dest_mail)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen{
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen struct maildir_transaction_context *t =
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen (struct maildir_transaction_context *)_t;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen int ret;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen if (mbox->storage->copy_with_hardlinks &&
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen mail->box->storage == mbox->ibox.box.storage) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen t_push();
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen ret = maildir_copy_hardlink(t, mail, flags,
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen keywords, dest_mail);
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen t_pop();
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (ret > 0)
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen return 0;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (ret < 0)
4b41116563110d00330896a568eff1078c382827Timo Sirainen return -1;
4b41116563110d00330896a568eff1078c382827Timo Sirainen
4b41116563110d00330896a568eff1078c382827Timo Sirainen /* non-fatal hardlinking failure, try the slow way */
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen }
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen return mail_storage_copy(_t, mail, flags, keywords, dest_mail);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen