]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mdbox: Moving messages to alt storage is done done with doveadm altmove command.
authorTimo Sirainen <tss@iki.fi>
Tue, 20 Apr 2010 13:25:24 +0000 (16:25 +0300)
committerTimo Sirainen <tss@iki.fi>
Tue, 20 Apr 2010 13:25:24 +0000 (16:25 +0300)
The command can take an arbitrary search query listing what messages should
exist in alt storage. If the message has been copied to multiple mailboxes,
the search query must match all the instances of the message.

Since the search query can now be specified in command line, mdbox_altmove
setting was also removed.

--HG--
branch : HEAD

20 files changed:
src/config/old-set-parser.c
src/doveadm/Makefile.am
src/doveadm/doveadm-mail-altmove.c [new file with mode: 0644]
src/doveadm/doveadm-mail.c
src/doveadm/doveadm-mail.h
src/lib-storage/index/dbox-multi/Makefile.am
src/lib-storage/index/dbox-multi/mdbox-file-purge.c [deleted file]
src/lib-storage/index/dbox-multi/mdbox-file.h
src/lib-storage/index/dbox-multi/mdbox-mail.c
src/lib-storage/index/dbox-multi/mdbox-map-private.h
src/lib-storage/index/dbox-multi/mdbox-map.c
src/lib-storage/index/dbox-multi/mdbox-map.h
src/lib-storage/index/dbox-multi/mdbox-purge.c [new file with mode: 0644]
src/lib-storage/index/dbox-multi/mdbox-save.c
src/lib-storage/index/dbox-multi/mdbox-settings.c
src/lib-storage/index/dbox-multi/mdbox-settings.h
src/lib-storage/index/dbox-multi/mdbox-storage.c
src/lib-storage/index/dbox-multi/mdbox-storage.h
src/lib-storage/index/dbox-multi/mdbox-sync.c
src/lib-storage/index/dbox-multi/mdbox-sync.h

index ace45c85eaa355dc7dc23bbe29ff10588993ee60..56cfdb523b18e7e859132b3adc795f01b9eb67f3 100644 (file)
@@ -134,14 +134,10 @@ old_settings_handle_root(struct config_parser_context *ctx,
                set_rename(ctx, key, "mdbox_rotate_size", value);
                return TRUE;
        }
-       if (strcmp(key, "dbox_rotate_days") == 0) {
-               set_rename(ctx, key, "mdbox_rotate_interval",
-                          t_strconcat(value, "d", NULL));
-               return TRUE;
-       }
 
        if (strcmp(key, "login_dir") == 0 ||
            strcmp(key, "dbox_rotate_min_size") == 0 ||
+           strcmp(key, "dbox_rotate_days") == 0 ||
            strcmp(key, "mail_log_max_lines_per_sec") == 0 ||
            strcmp(key, "maildir_copy_preserve_filename") == 0) {
                obsolete(ctx, "%s has been removed", key);
index 50719ce7f9c35bc52351184b2dbd2b768efc6fa7..9321ed2ae7e6d6440374ed6d95c38de4a1a1b629 100644 (file)
@@ -52,6 +52,7 @@ doveadm_SOURCES = \
        doveadm-dump-thread.c \
        doveadm-kick.c \
        doveadm-mail.c \
+       doveadm-mail-altmove.c \
        doveadm-mail-fetch.c \
        doveadm-penalty.c \
        doveadm-pw.c \
diff --git a/src/doveadm/doveadm-mail-altmove.c b/src/doveadm/doveadm-mail-altmove.c
new file mode 100644 (file)
index 0000000..98df57f
--- /dev/null
@@ -0,0 +1,111 @@
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-index.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "mail-search-build.h"
+#include "mail-search-parser.h"
+#include "doveadm-mail.h"
+
+static struct mail_search_args *build_search_args(const char *const args[])
+{
+       struct mail_search_parser *parser;
+       struct mail_search_args *sargs;
+       const char *error;
+
+       parser = mail_search_parser_init_cmdline(args);
+       if (mail_search_build(mail_search_register_human, parser, "UTF-8",
+                             &sargs, &error) < 0)
+               i_fatal("%s", error);
+       mail_search_parser_deinit(&parser);
+       return sargs;
+}
+
+static int
+cmd_altmove_box(struct mailbox *box, struct mail_search_args *search_args)
+{
+       struct mail_storage *storage;
+       struct mailbox_transaction_context *t;
+       struct mail_search_context *search_ctx;
+       struct mail *mail;
+       const char *box_name;
+       int ret = 0;
+
+       box_name = mailbox_get_vname(box);
+       storage = mailbox_get_storage(box);
+       if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
+               i_error("Syncing mailbox %s failed: %s", box_name,
+                       mail_storage_get_last_error(storage, NULL));
+               return -1;
+       }
+
+       t = mailbox_transaction_begin(box, 0);
+       search_ctx = mailbox_search_init(t, search_args, NULL);
+       mail = mail_alloc(t, 0, NULL);
+       while (mailbox_search_next(search_ctx, mail)) {
+               if (doveadm_debug)
+                       i_debug("altmove: box=%s uid=%u", box_name, mail->uid);
+               mail_update_flags(mail, MODIFY_ADD,
+                                 MAIL_INDEX_MAIL_FLAG_BACKEND);
+       }
+       mail_free(&mail);
+       if (mailbox_search_deinit(&search_ctx) < 0) {
+               i_error("Searching mailbox %s failed: %s", box_name,
+                       mail_storage_get_last_error(storage, NULL));
+               ret = -1;
+       }
+       if (mailbox_transaction_commit(&t) < 0) {
+               i_error("Commiting mailbox %s failed: %s", box_name,
+                       mail_storage_get_last_error(storage, NULL));
+               ret = -1;
+       }
+       return ret;
+}
+
+static void
+cmd_altmove_ns(struct mail_namespace *ns, struct mail_search_args *search_args)
+{
+       struct mailbox_list_iterate_context *iter;
+       const struct mailbox_info *info;
+       struct mailbox *box;
+
+       iter = mailbox_list_iter_init(ns->list, "*",
+                                     MAILBOX_LIST_ITER_RAW_LIST |
+                                     MAILBOX_LIST_ITER_NO_AUTO_INBOX |
+                                     MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
+       while ((info = mailbox_list_iter_next(iter)) != NULL) {
+               box = mailbox_alloc(ns->list, info->name,
+                                   MAILBOX_FLAG_KEEP_RECENT |
+                                   MAILBOX_FLAG_IGNORE_ACLS);
+               (void)cmd_altmove_box(box, search_args);
+               mailbox_free(&box);
+       }
+       if (mailbox_list_iter_deinit(&iter) < 0) {
+               i_error("Listing namespace '%s' mailboxes failed: %s",
+                       ns->prefix,
+                       mailbox_list_get_last_error(ns->list, NULL));
+       }
+
+       if (mail_storage_purge(ns->storage) < 0) {
+               i_error("Purging namespace '%s' failed: %s", ns->prefix,
+                       mail_storage_get_last_error(ns->storage, NULL));
+       }
+}
+
+void cmd_altmove(struct mail_user *user, const char *const args[])
+{
+       struct mail_search_args *search_args;
+       struct mail_namespace *ns;
+
+       if (args[0] == NULL)
+               doveadm_mail_help_name("altmove");
+       search_args = build_search_args(args);
+
+       for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+               if (ns->type != NAMESPACE_PRIVATE || ns->alias_for != NULL)
+                       continue;
+
+               cmd_altmove_ns(ns, search_args);
+       }
+}
index 368f5e1998422ebd57bbbe48bb89968bdf615ca7..f0214e3f14066af1c4dcf0d26f77d74e5a3db4fb 100644 (file)
@@ -312,7 +312,8 @@ void doveadm_mail_help_name(const char *cmd_name)
 static struct doveadm_mail_cmd mail_commands[] = {
        { cmd_purge, "purge", NULL },
        { cmd_force_resync, "force-resync", "<mailbox>" },
-       { cmd_fetch, "fetch", "<mailbox> <search query>" }
+       { cmd_fetch, "fetch", "<mailbox> <search query>" },
+       { cmd_altmove, "altmove", "<search query>" }
 };
 
 void doveadm_mail_init(void)
index a3a5983894aaf2a880f1ec9e94711dfb8cb23252..db58b810f09687e96b49cc97e33dc925e0dc6b2d 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef DOVEADM_MAIL_H
 #define DOVEADM_MAIL_H
 
+#include "doveadm.h"
+
 struct mail_user;
 
 typedef void doveadm_mail_command_t(struct mail_user *mail_user,
@@ -29,5 +31,6 @@ struct mailbox *
 doveadm_mailbox_find_and_sync(struct mail_user *user, const char *mailbox);
 
 void cmd_fetch(struct mail_user *user, const char *const args[]);
+void cmd_altmove(struct mail_user *user, const char *const args[]);
 
 #endif
index ccc5d67cb6bd7b2a88e175490f260f09cc8cf181..8926c854cd7af3ff8da1fd2bda5b4e401b3409b1 100644 (file)
@@ -13,9 +13,9 @@ AM_CPPFLAGS = \
 
 libstorage_dbox_multi_la_SOURCES = \
        mdbox-file.c \
-       mdbox-file-purge.c \
        mdbox-mail.c \
        mdbox-map.c \
+       mdbox-purge.c \
        mdbox-save.c \
        mdbox-settings.c \
        mdbox-sync.c \
diff --git a/src/lib-storage/index/dbox-multi/mdbox-file-purge.c b/src/lib-storage/index/dbox-multi/mdbox-file-purge.c
deleted file mode 100644 (file)
index 718fe14..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "hex-binary.h"
-#include "mdbox-storage.h"
-#include "mdbox-file.h"
-#include "mdbox-map.h"
-#include "mdbox-sync.h"
-
-#include <stdlib.h>
-
-struct dbox_mail_move {
-       struct dbox_file *file;
-       uint32_t offset;
-};
-ARRAY_DEFINE_TYPE(dbox_mail_move, struct dbox_mail_move);
-
-static int mdbox_map_file_msg_offset_cmp(const void *p1, const void *p2)
-{
-       const struct dbox_map_file_msg *m1 = p1, *m2 = p2;
-
-       if (m1->offset < m2->offset)
-               return -1;
-       else if (m1->offset > m2->offset)
-               return 1;
-       else
-               return 0;
-}
-
-static int
-mdbox_file_copy_metadata(struct dbox_file *file, struct ostream *output)
-{
-       struct dbox_metadata_header meta_hdr;
-       const char *line;
-       const unsigned char *data;
-       size_t size;
-       int ret;
-
-       ret = i_stream_read_data(file->input, &data, &size,
-                                sizeof(meta_hdr));
-       if (ret <= 0) {
-               i_assert(ret == -1);
-               if (file->input->stream_errno == 0) {
-                       dbox_file_set_corrupted(file, "missing metadata");
-                       return 0;
-               }
-               mail_storage_set_critical(&file->storage->storage,
-                       "read(%s) failed: %m", file->cur_path);
-               return -1;
-       }
-
-       memcpy(&meta_hdr, data, sizeof(meta_hdr));
-       if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
-                  sizeof(meta_hdr.magic_post)) != 0) {
-               dbox_file_set_corrupted(file, "invalid metadata magic");
-               return 0;
-       }
-       i_stream_skip(file->input, sizeof(meta_hdr));
-       if (output != NULL)
-               o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
-       while ((line = i_stream_read_next_line(file->input)) != NULL) {
-               if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
-                       /* end of metadata */
-                       break;
-               }
-               if (output != NULL) {
-                       o_stream_send_str(output, line);
-                       o_stream_send(output, "\n", 1);
-               }
-       }
-       if (line == NULL) {
-               dbox_file_set_corrupted(file, "missing end-of-metadata line");
-               return 0;
-       }
-       if (output != NULL)
-               o_stream_send(output, "\n", 1);
-       return 1;
-}
-
-int mdbox_file_purge(struct dbox_file *file)
-{
-       struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage;
-       struct dbox_file_append_context *out_file_append;
-       struct stat st;
-       struct istream *input;
-       struct ostream *output = NULL;
-       struct dbox_map_append_context *append_ctx;
-       ARRAY_TYPE(dbox_map_file_msg) msgs_arr;
-       const struct dbox_map_file_msg *msgs;
-       ARRAY_TYPE(seq_range) expunged_map_uids;
-       ARRAY_TYPE(uint32_t) copied_map_uids;
-       unsigned int i, count;
-       uoff_t offset, physical_size, msg_size;
-       enum dbox_map_append_flags append_flags = 0;
-       int ret;
-
-       if ((ret = dbox_file_try_lock(file)) <= 0)
-               return ret;
-
-       /* make sure the file still exists. another process may have already
-          deleted it. */
-       if (stat(file->cur_path, &st) < 0) {
-               dbox_file_unlock(file);
-               if (errno == ENOENT)
-                       return 0;
-
-               mail_storage_set_critical(&file->storage->storage,
-                       "stat(%s) failed: %m", file->cur_path);
-               return -1;
-       }
-
-       if (dstorage->set->mdbox_altmove > 0 &&
-           st.st_mtime + (time_t)dstorage->set->mdbox_altmove < ioloop_time &&
-           dstorage->alt_storage_dir != NULL)
-               append_flags |= DBOX_MAP_APPEND_FLAG_ALT;
-
-       i_array_init(&msgs_arr, 128);
-       if (dbox_map_get_file_msgs(dstorage->map,
-                                  ((struct mdbox_file *)file)->file_id,
-                                  &msgs_arr) < 0) {
-               array_free(&msgs_arr);
-               dbox_file_unlock(file);
-               return -1;
-       }
-       /* sort messages by their offset */
-       array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp);
-
-       msgs = array_get(&msgs_arr, &count);
-       append_ctx = dbox_map_append_begin(dstorage->map, append_flags);
-       i_array_init(&copied_map_uids, I_MIN(count, 1));
-       i_array_init(&expunged_map_uids, I_MIN(count, 1));
-       offset = file->file_header_size;
-       for (i = 0; i < count; i++) {
-               if ((ret = dbox_file_get_mail_stream(file, offset, NULL)) <= 0)
-                       break;
-               physical_size = file->cur_physical_size;
-               msg_size = file->msg_header_size + physical_size;
-
-               if (msgs[i].offset != offset) {
-                       /* map doesn't match file's actual contents */
-                       dbox_file_set_corrupted(file,
-                               "purging found mismatched offsets "
-                               "(%"PRIuUOFF_T" vs %u, %u/%u)",
-                               offset, msgs[i].offset, i, count);
-                       ret = 0;
-                       break;
-               }
-
-               if (msgs[i].refcount == 0) {
-                       seq_range_array_add(&expunged_map_uids, 0,
-                                           msgs[i].map_uid);
-                       output = NULL;
-               } else {
-                       /* non-expunged message. write it to output file. */
-                       if (dbox_map_append_next(append_ctx, physical_size,
-                                                &out_file_append, &output) < 0) {
-                               ret = -1;
-                               break;
-                       }
-                       i_assert(file != out_file_append->file);
-
-                       i_stream_seek(file->input, offset);
-                       input = i_stream_create_limit(file->input, msg_size);
-                       ret = o_stream_send_istream(output, input);
-                       if (input->stream_errno != 0) {
-                               errno = input->stream_errno;
-                               mail_storage_set_critical(&file->storage->storage,
-                                       "read(%s) failed: %m",
-                                       file->cur_path);
-                               i_stream_unref(&input);
-                               break;
-                       }
-                       i_stream_unref(&input);
-                       if (output->stream_errno != 0) {
-                               errno = output->stream_errno;
-                               mail_storage_set_critical(&file->storage->storage,
-                                       "write(%s) failed: %m",
-                                       out_file_append->file->cur_path);
-                               break;
-                       }
-                       i_assert(ret == (off_t)msg_size);
-               }
-
-               /* copy/skip metadata */
-               i_stream_seek(file->input, offset + msg_size);
-               if ((ret = mdbox_file_copy_metadata(file, output)) <= 0)
-                       break;
-
-               if (output != NULL) {
-                       dbox_map_append_finish(append_ctx);
-                       array_append(&copied_map_uids, &msgs[i].map_uid, 1);
-               }
-               offset = file->input->v_offset;
-       }
-       if (offset != (uoff_t)st.st_size && ret > 0) {
-               /* file has more messages than what map tells us */
-               dbox_file_set_corrupted(file,
-                       "more messages available than in map "
-                       "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
-               ret = 0;
-       }
-       array_free(&msgs_arr); msgs = NULL;
-
-       if (ret <= 0) {
-               dbox_map_append_free(&append_ctx);
-               dbox_file_unlock(file);
-               ret = -1;
-       } else {
-               /* assign new file_id + offset to moved messages */
-               if (dbox_map_append_move(append_ctx, &copied_map_uids,
-                                        &expunged_map_uids) < 0 ||
-                   dbox_map_append_commit(append_ctx) < 0) {
-                       dbox_file_unlock(file);
-                       ret = -1;
-               } else {
-                       ret = 1;
-                       (void)dbox_file_unlink(file);
-               }
-               dbox_map_append_free(&append_ctx);
-       }
-       array_free(&copied_map_uids);
-       array_free(&expunged_map_uids);
-       return ret;
-}
index 4505364c6797373f9a490b1397d8a8969f2cda08..9095cce745b7d5c02248a8af0fc404eeecf5acb3 100644 (file)
@@ -25,6 +25,5 @@ int mdbox_file_create_fd(struct dbox_file *file, const char *path,
 
 void mdbox_files_free(struct mdbox_storage *storage);
 void mdbox_files_sync_input(struct mdbox_storage *storage);
-int mdbox_file_purge(struct dbox_file *file);
 
 #endif
index cdbfe4e6b622352c3122a62fa397452569e5b59f..c9d8c4e4dae6a02afd9237562f9f1c596f82f5ae 100644 (file)
@@ -7,6 +7,7 @@
 #include "index-mail.h"
 #include "dbox-mail.h"
 #include "mdbox-storage.h"
+#include "mdbox-sync.h"
 #include "mdbox-map.h"
 #include "mdbox-file.h"
 
@@ -172,6 +173,20 @@ static int mdbox_mail_get_save_date(struct mail *mail, time_t *date_r)
        return TRUE;
 }
 
+static void
+mdbox_mail_update_flags(struct mail *mail, enum modify_type modify_type,
+                       enum mail_flags flags)
+{
+       if ((flags & DBOX_INDEX_FLAG_ALT) != 0) {
+               mdbox_purge_alt_flag_change(mail, modify_type != MODIFY_REMOVE);
+               flags &= ~DBOX_INDEX_FLAG_ALT;
+               if (flags == 0 && modify_type != MODIFY_REPLACE)
+                       return;
+       }
+
+       index_mail_update_flags(mail, modify_type, flags);
+}
+
 struct mail_vfuncs mdbox_mail_vfuncs = {
        dbox_mail_close,
        index_mail_free,
@@ -194,7 +209,7 @@ struct mail_vfuncs mdbox_mail_vfuncs = {
        index_mail_get_header_stream,
        dbox_mail_get_stream,
        dbox_mail_get_special,
-       index_mail_update_flags,
+       mdbox_mail_update_flags,
        index_mail_update_keywords,
        index_mail_update_modseq,
        index_mail_update_uid,
index 3df8b7b252f8b97f8d236f322f9c5d4026ba54ea..7aaf712fce51a6e00483c49440e6b776a5bd4a06 100644 (file)
@@ -32,7 +32,6 @@ struct dbox_map_append {
 
 struct dbox_map_append_context {
        struct dbox_map *map;
-       enum dbox_map_append_flags flags;
 
        struct mail_index_sync_ctx *sync_ctx;
        struct mail_index_view *sync_view;
index 4bb8a8eeaf559bde8ad876c357382ba29a2a7c7a..4772715414aca9f0cd66a759cc7826a1466d1de7 100644 (file)
@@ -169,8 +169,7 @@ int dbox_map_refresh(struct dbox_map *map)
 }
 
 static int dbox_map_lookup_seq(struct dbox_map *map, uint32_t seq,
-                              uint32_t *file_id_r, uoff_t *offset_r,
-                              uoff_t *size_r)
+                              const struct dbox_map_mail_index_record **rec_r)
 {
        const struct dbox_map_mail_index_record *rec;
        const void *data;
@@ -186,10 +185,7 @@ static int dbox_map_lookup_seq(struct dbox_map *map, uint32_t seq,
                dbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid);
                return -1;
        }
-
-       *file_id_r = rec->file_id;
-       *offset_r = rec->offset;
-       *size_r = rec->size;
+       *rec_r = rec;
        return 0;
 }
 
@@ -209,8 +205,8 @@ dbox_map_get_seq(struct dbox_map *map, uint32_t map_uid, uint32_t *seq_r)
 int dbox_map_lookup(struct dbox_map *map, uint32_t map_uid,
                    uint32_t *file_id_r, uoff_t *offset_r)
 {
+       const struct dbox_map_mail_index_record *rec;
        uint32_t seq;
-       uoff_t size;
        int ret;
 
        if (dbox_map_open_or_create(map) < 0)
@@ -219,8 +215,42 @@ int dbox_map_lookup(struct dbox_map *map, uint32_t map_uid,
        if ((ret = dbox_map_get_seq(map, map_uid, &seq)) <= 0)
                return ret;
 
-       if (dbox_map_lookup_seq(map, seq, file_id_r, offset_r, &size) < 0)
+       if (dbox_map_lookup_seq(map, seq, &rec) < 0)
                return -1;
+       *file_id_r = rec->file_id;
+       *offset_r = rec->offset;
+       return 1;
+}
+
+int dbox_map_lookup_full(struct dbox_map *map, uint32_t map_uid,
+                        struct dbox_map_mail_index_record *rec_r,
+                        uint16_t *refcount_r)
+{
+       const struct dbox_map_mail_index_record *rec;
+       const uint16_t *ref16_p;
+       const void *data;
+       uint32_t seq;
+       bool expunged;
+       int ret;
+
+       if (dbox_map_open_or_create(map) < 0)
+               return -1;
+
+       if ((ret = dbox_map_get_seq(map, map_uid, &seq)) <= 0)
+               return ret;
+
+       if (dbox_map_lookup_seq(map, seq, &rec) < 0)
+               return -1;
+       *rec_r = *rec;
+
+       mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
+                             &data, &expunged);
+       if (data == NULL) {
+               dbox_map_set_corrupted(map, "missing ref extension");
+               return -1;
+       }
+       ref16_p = data;
+       *refcount_r = *ref16_p;
        return 1;
 }
 
@@ -507,13 +537,12 @@ int dbox_map_remove_file_id(struct dbox_map *map, uint32_t file_id)
 }
 
 struct dbox_map_append_context *
-dbox_map_append_begin(struct dbox_map *map, enum dbox_map_append_flags flags)
+dbox_map_append_begin(struct dbox_map *map)
 {
        struct dbox_map_append_context *ctx;
 
        ctx = i_new(struct dbox_map_append_context, 1);
        ctx->map = map;
-       ctx->flags = flags;
        ctx->first_new_file_id = (uint32_t)-1;
        i_array_init(&ctx->file_appends, 64);
        i_array_init(&ctx->files, 64);
@@ -560,12 +589,10 @@ static time_t day_begin_stamp(unsigned int interval)
        return stamp - (interval - unit);
 }
 
-static bool dbox_try_open(struct dbox_map_append_context *ctx,
-                         struct dbox_file *file)
+static bool dbox_try_open(struct dbox_file *file, bool want_altpath)
 {
-       bool want_altpath, notfound;
+       bool notfound;
 
-       want_altpath = (ctx->flags & DBOX_MAP_APPEND_FLAG_ALT) != 0;
        if (want_altpath) {
                if (dbox_file_open(file, &notfound) <= 0)
                        return FALSE;
@@ -589,7 +616,7 @@ static bool dbox_try_open(struct dbox_map_append_context *ctx,
 }
 
 static bool
-dbox_map_file_try_append(struct dbox_map_append_context *ctx,
+dbox_map_file_try_append(struct dbox_map_append_context *ctx, bool want_altpath,
                         uint32_t file_id, time_t stamp, uoff_t mail_size,
                         struct dbox_file_append_context **file_append_r,
                         struct ostream **output_r, bool *retry_later_r)
@@ -607,7 +634,7 @@ dbox_map_file_try_append(struct dbox_map_append_context *ctx,
        *retry_later_r = FALSE;
 
        file = mdbox_file_init(storage, file_id);
-       if (!dbox_try_open(ctx, file)) {
+       if (!dbox_try_open(file, want_altpath)) {
                dbox_file_unref(&file);
                return TRUE;
        }
@@ -662,7 +689,8 @@ dbox_map_is_appending(struct dbox_map_append_context *ctx, uint32_t file_id)
 
 static struct dbox_file_append_context *
 dbox_map_find_existing_append(struct dbox_map_append_context *ctx,
-                             uoff_t mail_size, struct ostream **output_r)
+                             uoff_t mail_size, bool want_altpath,
+                             struct ostream **output_r)
 {
        struct dbox_map *map = ctx->map;
        struct dbox_file_append_context *const *file_appends, *append;
@@ -678,6 +706,9 @@ dbox_map_find_existing_append(struct dbox_map_append_context *ctx,
        for (i = count; i > ctx->files_nonappendable_count; i--) {
                append = file_appends[i-1];
 
+               if (dbox_file_is_in_alt(append->file) != want_altpath)
+                       continue;
+
                append_offset = append->output->offset;
                if (append_offset + mail_size <= map->set->mdbox_rotate_size &&
                    dbox_file_get_append_stream(append, output_r) > 0)
@@ -703,8 +734,8 @@ dbox_map_find_first_alt(struct dbox_map_append_context *ctx,
        DIR *dir;
        struct dirent *d;
        const struct mail_index_header *hdr;
+       const struct dbox_map_mail_index_record *rec;
        uint32_t seq, file_id, min_file_id = -1U;
-       uoff_t offset, size;
        int ret = 0;
 
        /* we want to quickly find the latest alt file, but we also want to
@@ -745,11 +776,10 @@ dbox_map_find_first_alt(struct dbox_map_append_context *ctx,
        /* find the newest message in alt storage from map view */
        hdr = mail_index_get_header(ctx->map->view);
        for (seq = hdr->messages_count; seq > 0; seq--) {
-               if (dbox_map_lookup_seq(ctx->map, seq, &file_id,
-                                       &offset, &size) < 0)
+               if (dbox_map_lookup_seq(ctx->map, seq, &rec) < 0)
                        return -1;
 
-               if (file_id < min_file_id)
+               if (rec->file_id < min_file_id)
                        break;
        }
 
@@ -760,16 +790,16 @@ dbox_map_find_first_alt(struct dbox_map_append_context *ctx,
 
 static int
 dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
-                             uoff_t mail_size,
+                             uoff_t mail_size, bool want_altpath,
                              struct dbox_file_append_context **file_append_r,
                              struct ostream **output_r)
 {
        struct dbox_map *map = ctx->map;
        ARRAY_TYPE(seq_range) checked_file_ids;
        const struct mail_index_header *hdr;
+       const struct dbox_map_mail_index_record *rec;
        unsigned int backwards_lookup_count;
-       uint32_t seq, seq1, uid, file_id, min_file_id;
-       uoff_t offset, size;
+       uint32_t seq, seq1, uid, min_file_id;
        time_t stamp;
        bool retry_later;
 
@@ -783,7 +813,7 @@ dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
        backwards_lookup_count = 0;
        t_array_init(&checked_file_ids, 16);
 
-       if ((ctx->flags & DBOX_MAP_APPEND_FLAG_ALT) == 0)
+       if (!want_altpath)
                seq = hdr->messages_count;
        else {
                /* we want to save to alt storage. */
@@ -794,12 +824,12 @@ dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
        }
 
        for (; seq > 0; seq--) {
-               if (dbox_map_lookup_seq(map, seq, &file_id, &offset, &size) < 0)
+               if (dbox_map_lookup_seq(map, seq, &rec) < 0)
                        return -1;
 
-               if (seq_range_exists(&checked_file_ids, file_id))
+               if (seq_range_exists(&checked_file_ids, rec->file_id))
                        continue;
-               seq_range_array_add(&checked_file_ids, 0, file_id);
+               seq_range_array_add(&checked_file_ids, 0, rec->file_id);
 
                if (++backwards_lookup_count > MAX_BACKWARDS_LOOKUPS) {
                        /* we've wasted enough time here */
@@ -809,18 +839,19 @@ dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
                /* first lookup: this should be enough usually, but we can't
                   be sure until after locking. also if messages were recently
                   moved, this message might not be the last one in the file. */
-               if (offset + size + mail_size >= map->set->mdbox_rotate_size)
+               if (rec->offset + rec->size + mail_size >=
+                                       map->set->mdbox_rotate_size)
                        continue;
 
-               if (dbox_map_is_appending(ctx, file_id)) {
+               if (dbox_map_is_appending(ctx, rec->file_id)) {
                        /* already checked this */
                        continue;
                }
 
                mail_index_lookup_uid(map->view, seq, &uid);
-               if (!dbox_map_file_try_append(ctx, file_id, stamp, mail_size,
-                                             file_append_r, output_r,
-                                             &retry_later)) {
+               if (!dbox_map_file_try_append(ctx, want_altpath, rec->file_id,
+                                             stamp, mail_size, file_append_r,
+                                             output_r, &retry_later)) {
                        /* file is too old. the rest of the files are too. */
                        break;
                }
@@ -839,24 +870,27 @@ dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
 }
 
 int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
+                        enum dbox_map_append_flags flags,
                         struct dbox_file_append_context **file_append_ctx_r,
                         struct ostream **output_r)
 {
        struct dbox_file *file;
        struct dbox_map_append *append;
        struct dbox_file_append_context *file_append;
-       bool existing;
+       bool existing, want_altpath;
        int ret;
 
        if (ctx->failed)
                return -1;
 
-       file_append = dbox_map_find_existing_append(ctx, mail_size, output_r);
+       want_altpath = (flags & DBOX_MAP_APPEND_FLAG_ALT) != 0;
+       file_append = dbox_map_find_existing_append(ctx, mail_size,
+                                                   want_altpath, output_r);
        if (file_append != NULL) {
                ret = 1;
                existing = TRUE;
        } else {
-               ret = dbox_map_find_appendable_file(ctx, mail_size,
+               ret = dbox_map_find_appendable_file(ctx, mail_size, flags,
                                                    &file_append, output_r);
                existing = FALSE;
        }
@@ -866,7 +900,7 @@ int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
                return -1;
        else {
                /* create a new file */
-               file = (ctx->flags & DBOX_MAP_APPEND_FLAG_ALT) == 0 ?
+               file = (flags & DBOX_MAP_APPEND_FLAG_ALT) == 0 ?
                        mdbox_file_init(ctx->map->storage, 0) :
                        mdbox_file_init_new_alt(ctx->map->storage);
                file_append = dbox_file_append_init(file);
index c7bbbffbb981c04beab90e6ca7d6f27c63f42c02..513c5f838afa09d6c37c2484f782874dc51eae5a 100644 (file)
@@ -45,6 +45,10 @@ int dbox_map_refresh(struct dbox_map *map);
    is already expunged, -1 if error. */
 int dbox_map_lookup(struct dbox_map *map, uint32_t map_uid,
                    uint32_t *file_id_r, uoff_t *offset_r);
+/* Like dbox_map_lookup(), but look up everything. */
+int dbox_map_lookup_full(struct dbox_map *map, uint32_t map_uid,
+                        struct dbox_map_mail_index_record *rec_r,
+                        uint16_t *refcount_r);
 
 /* Get all messages from file */
 int dbox_map_get_file_msgs(struct dbox_map *map, uint32_t file_id,
@@ -69,11 +73,12 @@ int dbox_map_get_zero_ref_files(struct dbox_map *map,
                                ARRAY_TYPE(seq_range) *file_ids_r);
 
 struct dbox_map_append_context *
-dbox_map_append_begin(struct dbox_map *map, enum dbox_map_append_flags flags);
+dbox_map_append_begin(struct dbox_map *map);
 /* Request file for saving a new message with given size (if available). If an
    existing file can be used, the record is locked and updated in index.
    Returns 0 if ok, -1 if error. */
 int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
+                        enum dbox_map_append_flags flags,
                         struct dbox_file_append_context **file_append_ctx_r,
                         struct ostream **output_r);
 /* Finished saving the last mail. Saves the message size. */
diff --git a/src/lib-storage/index/dbox-multi/mdbox-purge.c b/src/lib-storage/index/dbox-multi/mdbox-purge.c
new file mode 100644 (file)
index 0000000..e0649cd
--- /dev/null
@@ -0,0 +1,538 @@
+/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "hash.h"
+#include "mdbox-storage.h"
+#include "mdbox-storage-rebuild.h"
+#include "mdbox-file.h"
+#include "mdbox-map.h"
+#include "mdbox-sync.h"
+
+#include <stdlib.h>
+#include <dirent.h>
+
+/*
+   Altmoving works like:
+
+   1. Message's DBOX_INDEX_FLAG_ALT flag is changed. This is caught by mdbox
+      code and map UID's alt-refcount is updated. It won't be written to disk.
+   2. mdbox_purge() is called, which checks if map UID's refcount equals
+      to its alt-refcount. If it does, it's moved to alt storage. Moving to
+      primary storage is done if _ALT flag was removed from any message.
+*/
+
+enum mdbox_msg_action {
+       MDBOX_MSG_ACTION_MOVE_TO_ALT = 1,
+       MDBOX_MSG_ACTION_MOVE_FROM_ALT
+};
+
+struct mdbox_purge_context {
+       pool_t pool;
+       struct mdbox_storage *storage;
+
+       uint32_t lowest_primary_file_id;
+       /* list of file_ids that exist in primary storage. this list is looked
+          up while there is no locking, so it may not be accurate anymore by
+          the time it's used. */
+       ARRAY_TYPE(seq_range) primary_file_ids;
+       /* list of file_ids that we need to purge */
+       ARRAY_TYPE(seq_range) purge_file_ids;
+
+       /* map_uid => mdbox_msg_action */
+       struct hash_table *altmoves;
+       bool have_altmoves;
+
+       struct dbox_map_append_context *append_ctx;
+};
+
+static int mdbox_map_file_msg_offset_cmp(const void *p1, const void *p2)
+{
+       const struct dbox_map_file_msg *m1 = p1, *m2 = p2;
+
+       if (m1->offset < m2->offset)
+               return -1;
+       else if (m1->offset > m2->offset)
+               return 1;
+       else
+               return 0;
+}
+
+static int
+mdbox_file_copy_metadata(struct dbox_file *file, struct ostream *output)
+{
+       struct dbox_metadata_header meta_hdr;
+       const char *line;
+       const unsigned char *data;
+       size_t size;
+       int ret;
+
+       ret = i_stream_read_data(file->input, &data, &size,
+                                sizeof(meta_hdr));
+       if (ret <= 0) {
+               i_assert(ret == -1);
+               if (file->input->stream_errno == 0) {
+                       dbox_file_set_corrupted(file, "missing metadata");
+                       return 0;
+               }
+               mail_storage_set_critical(&file->storage->storage,
+                       "read(%s) failed: %m", file->cur_path);
+               return -1;
+       }
+
+       memcpy(&meta_hdr, data, sizeof(meta_hdr));
+       if (memcmp(meta_hdr.magic_post, DBOX_MAGIC_POST,
+                  sizeof(meta_hdr.magic_post)) != 0) {
+               dbox_file_set_corrupted(file, "invalid metadata magic");
+               return 0;
+       }
+       i_stream_skip(file->input, sizeof(meta_hdr));
+       if (output != NULL)
+               o_stream_send(output, &meta_hdr, sizeof(meta_hdr));
+       while ((line = i_stream_read_next_line(file->input)) != NULL) {
+               if (*line == DBOX_METADATA_OLDV1_SPACE || *line == '\0') {
+                       /* end of metadata */
+                       break;
+               }
+               if (output != NULL) {
+                       o_stream_send_str(output, line);
+                       o_stream_send(output, "\n", 1);
+               }
+       }
+       if (line == NULL) {
+               dbox_file_set_corrupted(file, "missing end-of-metadata line");
+               return 0;
+       }
+       if (output != NULL)
+               o_stream_send(output, "\n", 1);
+       return 1;
+}
+
+static bool
+mdbox_purge_want_altpath(struct mdbox_purge_context *ctx, uint32_t map_uid)
+{
+       enum mdbox_msg_action action;
+       void *value;
+
+       if (!ctx->have_altmoves)
+               return FALSE;
+
+       value = hash_table_lookup(ctx->altmoves, POINTER_CAST(map_uid));
+       if (value == NULL)
+               return FALSE;
+
+       action = POINTER_CAST_TO(value, enum mdbox_msg_action);
+       return action == MDBOX_MSG_ACTION_MOVE_TO_ALT;
+}
+
+static int
+mdbox_purge_save_msg(struct mdbox_purge_context *ctx, struct dbox_file *file,
+                    const struct dbox_map_file_msg *msg)
+{
+       struct dbox_file_append_context *out_file_append;
+       struct istream *input;
+       struct ostream *output;
+       enum dbox_map_append_flags append_flags;
+       uoff_t msg_size;
+       off_t ret;
+       int read_errno;
+
+       if (ctx->append_ctx == NULL)
+               ctx->append_ctx = dbox_map_append_begin(ctx->storage->map);
+
+       append_flags = !mdbox_purge_want_altpath(ctx, msg->map_uid) ? 0 :
+               DBOX_MAP_APPEND_FLAG_ALT;
+       msg_size = file->msg_header_size + file->cur_physical_size;
+       if (dbox_map_append_next(ctx->append_ctx, file->cur_physical_size,
+                                append_flags, &out_file_append, &output) < 0)
+               return -1;
+
+       i_assert(file != out_file_append->file);
+
+       input = i_stream_create_limit(file->input, msg_size);
+       ret = o_stream_send_istream(output, input);
+       read_errno = input->stream_errno;
+       i_stream_unref(&input);
+
+       if (read_errno != 0) {
+               errno = read_errno;
+               mail_storage_set_critical(&file->storage->storage,
+                       "read(%s) failed: %m", file->cur_path);
+               return -1;
+       }
+       if (output->stream_errno != 0) {
+               errno = output->stream_errno;
+               mail_storage_set_critical(&file->storage->storage,
+                                         "write(%s) failed: %m",
+                                         out_file_append->file->cur_path);
+               return -1;
+       }
+       i_assert(ret == (off_t)msg_size);
+
+       /* copy metadata */
+       if ((ret = mdbox_file_copy_metadata(file, output)) <= 0)
+               return ret;
+
+       dbox_map_append_finish(ctx->append_ctx);
+       return 1;
+}
+
+static int
+mdbox_file_purge(struct mdbox_purge_context *ctx, struct dbox_file *file)
+{
+       struct mdbox_storage *dstorage = (struct mdbox_storage *)file->storage;
+       struct stat st;
+       ARRAY_TYPE(dbox_map_file_msg) msgs_arr;
+       const struct dbox_map_file_msg *msgs;
+       ARRAY_TYPE(seq_range) expunged_map_uids;
+       ARRAY_TYPE(uint32_t) copied_map_uids;
+       unsigned int i, count;
+       uoff_t offset;
+       int ret;
+
+       if ((ret = dbox_file_try_lock(file)) <= 0)
+               return ret;
+
+       /* make sure the file still exists. another process may have already
+          deleted it. */
+       if (stat(file->cur_path, &st) < 0) {
+               dbox_file_unlock(file);
+               if (errno == ENOENT)
+                       return 0;
+
+               mail_storage_set_critical(&file->storage->storage,
+                       "stat(%s) failed: %m", file->cur_path);
+               return -1;
+       }
+
+       /* get list of map UIDs that exist in this file (again has to be done
+          after locking) */
+       i_array_init(&msgs_arr, 128);
+       if (dbox_map_get_file_msgs(dstorage->map,
+                                  ((struct mdbox_file *)file)->file_id,
+                                  &msgs_arr) < 0) {
+               array_free(&msgs_arr);
+               dbox_file_unlock(file);
+               return -1;
+       }
+       /* sort messages by their offset */
+       array_sort(&msgs_arr, mdbox_map_file_msg_offset_cmp);
+
+       msgs = array_get(&msgs_arr, &count);
+       i_array_init(&copied_map_uids, I_MIN(count, 1));
+       i_array_init(&expunged_map_uids, I_MIN(count, 1));
+       offset = file->file_header_size;
+       for (i = 0; i < count; i++) {
+               if ((ret = dbox_file_get_mail_stream(file, offset, NULL)) <= 0)
+                       break;
+
+               if (msgs[i].offset != offset) {
+                       /* map doesn't match file's actual contents */
+                       dbox_file_set_corrupted(file,
+                               "purging found mismatched offsets "
+                               "(%"PRIuUOFF_T" vs %u, %u/%u)",
+                               offset, msgs[i].offset, i, count);
+                       ret = 0;
+                       break;
+               }
+
+               if (msgs[i].refcount == 0) {
+                       /* skip over expunged message */
+                       i_stream_seek(file->input, offset +
+                                     file->msg_header_size +
+                                     file->cur_physical_size);
+                       /* skip metadata */
+                       if ((ret = mdbox_file_copy_metadata(file, NULL)) <= 0)
+                               break;
+                       seq_range_array_add(&expunged_map_uids, 0,
+                                           msgs[i].map_uid);
+               } else {
+                       /* non-expunged message. write it to output file. */
+                       i_stream_seek(file->input, offset);
+                       if (mdbox_purge_save_msg(ctx, file, &msgs[i]) < 0) {
+                               ret = -1;
+                               break;
+                       }
+                       array_append(&copied_map_uids, &msgs[i].map_uid, 1);
+               }
+               offset = file->input->v_offset;
+       }
+       if (offset != (uoff_t)st.st_size && ret > 0) {
+               /* file has more messages than what map tells us */
+               dbox_file_set_corrupted(file,
+                       "more messages available than in map "
+                       "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", offset, st.st_size);
+               ret = 0;
+       }
+       array_free(&msgs_arr); msgs = NULL;
+
+       if (ret <= 0)
+               ret = -1;
+       else {
+               /* assign new file_id + offset to moved messages */
+               if (dbox_map_append_move(ctx->append_ctx, &copied_map_uids,
+                                        &expunged_map_uids) < 0 ||
+                   dbox_map_append_commit(ctx->append_ctx) < 0)
+                       ret = -1;
+               else {
+                       ret = 1;
+                       (void)dbox_file_unlink(file);
+               }
+       }
+       dbox_map_append_free(&ctx->append_ctx);
+       if (ret < 0)
+               dbox_file_unlock(file);
+       array_free(&copied_map_uids);
+       array_free(&expunged_map_uids);
+       return ret;
+}
+
+void mdbox_purge_alt_flag_change(struct mail *mail, bool move_to_alt)
+{
+       struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)mail->box;
+       ARRAY_TYPE(uint32_t) *dest;
+       uint32_t map_uid;
+
+       /* we'll assume here that alt flag won't be changed multiple times
+          for the same mail. it shouldn't happen with current code, and
+          checking for it would just slow down the code.
+
+          so the way it works currently is just that map_uids are added to
+          an array, which is later sorted and processed further. note that
+          it's still possible that the same map_uid exists in the array
+          multiple times. */
+       if (mdbox_mail_lookup(mbox, mbox->box.view, mail->seq, &map_uid) < 0)
+               return;
+
+       dest = move_to_alt ? &mbox->storage->move_to_alt_map_uids :
+               &mbox->storage->move_from_alt_map_uids;
+
+       if (!array_is_created(dest))
+               i_array_init(dest, 256);
+       array_append(dest, &map_uid, 1);
+}
+
+static struct mdbox_purge_context *
+mdbox_purge_alloc(struct mdbox_storage *storage)
+{
+       struct mdbox_purge_context *ctx;
+       pool_t pool;
+
+       pool = pool_alloconly_create("mdbox purge context", 1024*32);
+       ctx = p_new(pool, struct mdbox_purge_context, 1);
+       ctx->pool = pool;
+       ctx->storage = storage;
+       ctx->lowest_primary_file_id = (uint32_t)-1;
+       i_array_init(&ctx->primary_file_ids, 64);
+       i_array_init(&ctx->purge_file_ids, 64);
+       ctx->altmoves = hash_table_create(default_pool, pool, 0, NULL, NULL);
+       return ctx;
+}
+
+static void mdbox_purge_free(struct mdbox_purge_context **_ctx)
+{
+       struct mdbox_purge_context *ctx = *_ctx;
+
+       *_ctx = NULL;
+
+       hash_table_destroy(&ctx->altmoves);
+       array_free(&ctx->primary_file_ids);
+       array_free(&ctx->purge_file_ids);
+       pool_unref(&ctx->pool);
+}
+
+static int mdbox_purge_get_primary_files(struct mdbox_purge_context *ctx)
+{
+       struct mdbox_storage *dstorage = ctx->storage;
+       struct mail_storage *storage = &dstorage->storage.storage;
+       DIR *dir;
+       struct dirent *d;
+       string_t *path;
+       unsigned int file_id, dir_len;
+       int ret = 0;
+
+       if (!array_is_created(&dstorage->move_to_alt_map_uids) &&
+           !array_is_created(&dstorage->move_from_alt_map_uids)) {
+               /* we don't need to do alt moving, don't bother getting list
+                  of primary files */
+               return 0;
+       }
+
+       dir = opendir(dstorage->storage_dir);
+       if (dir == NULL) {
+               if (errno == ENOENT) {
+                       /* no storage directory at all yet */
+                       return 0;
+               }
+               mail_storage_set_critical(storage,
+                       "opendir(%s) failed: %m", dstorage->storage_dir);
+               return -1;
+       }
+
+       path = t_str_new(256);
+       str_append(path, dstorage->storage_dir);
+       str_append_c(path, '/');
+       dir_len = str_len(path);
+
+       for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
+               if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX,
+                           strlen(MDBOX_MAIL_FILE_PREFIX)) != 0)
+                       continue;
+               if (str_to_uint32(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX),
+                                 &file_id) < 0)
+                       continue;
+
+               str_truncate(path, dir_len);
+               str_append(path, d->d_name);
+               seq_range_array_add(&ctx->primary_file_ids, 0, file_id);
+       }
+       if (array_count(&ctx->primary_file_ids) > 0) {
+               const struct seq_range *range =
+                       array_idx(&ctx->primary_file_ids, 0);
+               ctx->lowest_primary_file_id = range[0].seq1;
+       }
+       if (errno != 0) {
+               mail_storage_set_critical(storage,
+                       "readdir(%s) failed: %m", dstorage->storage_dir);
+               ret = -1;
+       }
+       if (closedir(dir) < 0) {
+               mail_storage_set_critical(storage,
+                       "closedir(%s) failed: %m", dstorage->storage_dir);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int uint32_t_cmp(const uint32_t *u1, const uint32_t *u2)
+{
+       if (*u1 < *u2)
+               return -1;
+       if (*u1 > *u2)
+               return 1;
+       return 0;
+}
+
+static int mdbox_altmove_add_files(struct mdbox_purge_context *ctx)
+{
+       struct mdbox_storage *dstorage = ctx->storage;
+       const uint32_t *map_uids;
+       unsigned int i, count, alt_refcount = 0;
+       struct dbox_map_mail_index_record cur_rec;
+       uint32_t cur_map_uid;
+       uint16_t cur_refcount = 0;
+       uoff_t offset;
+       int ret = 0;
+
+       /* first add move-to-alt actions */
+       if (array_is_created(&dstorage->move_to_alt_map_uids)) {
+               array_sort(&dstorage->move_to_alt_map_uids, uint32_t_cmp);
+               map_uids = array_get(&dstorage->move_to_alt_map_uids, &count);
+       } else {
+               map_uids = NULL;
+               count = 0;
+       }
+       cur_map_uid = 0;
+       for (i = 0; i < count; i++) {
+               if (cur_map_uid != map_uids[i]) {
+                       cur_map_uid = map_uids[i];
+                       if (dbox_map_lookup_full(dstorage->map, cur_map_uid,
+                                                &cur_rec, &cur_refcount) < 0) {
+                               cur_refcount = (uint16_t)-1;
+                               ret = -1;
+                       }
+                       alt_refcount = 1;
+               } else {
+                       alt_refcount++;
+               }
+
+               if (alt_refcount == cur_refcount &&
+                   seq_range_exists(&ctx->primary_file_ids, cur_rec.file_id)) {
+                       /* all instances marked as moved to alt storage */
+                       hash_table_insert(ctx->altmoves,
+                               POINTER_CAST(cur_map_uid),
+                               POINTER_CAST(MDBOX_MSG_ACTION_MOVE_TO_ALT));
+                       seq_range_array_add(&ctx->purge_file_ids, 0,
+                                           cur_rec.file_id);
+               }
+       }
+
+       /* next add move-from-alt actions. they override move-to-alt actions
+          in case there happen to be any conflicts (shouldn't). only a single
+          move-from-alt record is needed to do the move. */
+       if (array_is_created(&dstorage->move_from_alt_map_uids))
+               map_uids = array_get(&dstorage->move_from_alt_map_uids, &count);
+       else {
+               map_uids = NULL;
+               count = 0;
+       }
+       cur_map_uid = 0;
+       for (i = 0; i < count; i++) {
+               if (cur_map_uid == map_uids[i])
+                       continue;
+               cur_map_uid = map_uids[i];
+
+               if (dbox_map_lookup(dstorage->map, cur_map_uid,
+                                   &cur_rec.file_id, &offset) < 0) {
+                       ret = -1;
+                       continue;
+               }
+               if (seq_range_exists(&ctx->primary_file_ids, cur_rec.file_id)) {
+                       /* already in primary storage */
+                       continue;
+               }
+
+               hash_table_insert(ctx->altmoves, POINTER_CAST(cur_map_uid),
+                                 POINTER_CAST(MDBOX_MSG_ACTION_MOVE_FROM_ALT));
+               seq_range_array_add(&ctx->purge_file_ids, 0, cur_rec.file_id);
+       }
+       ctx->have_altmoves = hash_table_count(ctx->altmoves) > 0;
+       return ret;
+}
+
+int mdbox_purge(struct mail_storage *_storage)
+{
+       struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
+       struct mdbox_purge_context *ctx;
+       struct dbox_file *file;
+       struct seq_range_iter iter;
+       unsigned int i = 0;
+       uint32_t file_id;
+       bool deleted;
+       int ret;
+
+       ctx = mdbox_purge_alloc(storage);
+       ret = dbox_map_get_zero_ref_files(storage->map, &ctx->purge_file_ids);
+       if (storage->alt_storage_dir != NULL) {
+               if (mdbox_purge_get_primary_files(ctx) < 0)
+                       ret = -1;
+               else {
+                       /* add files that can be altmoved */
+                       if (mdbox_altmove_add_files(ctx) < 0)
+                               ret = -1;
+               }
+       }
+
+       seq_range_array_iter_init(&iter, &ctx->purge_file_ids); i = 0;
+       while (seq_range_array_iter_nth(&iter, i++, &file_id)) T_BEGIN {
+               file = mdbox_file_init(storage, file_id);
+               if (dbox_file_open(file, &deleted) > 0 && !deleted) {
+                       if (mdbox_file_purge(ctx, file) < 0)
+                               ret = -1;
+               } else {
+                       dbox_map_remove_file_id(storage->map, file_id);
+               }
+               dbox_file_unref(&file);
+       } T_END;
+       mdbox_purge_free(&ctx);
+
+       if (storage->storage.files_corrupted) {
+               /* purging found corrupted files */
+               (void)mdbox_storage_rebuild(storage);
+               ret = -1;
+       }
+       return ret;
+}
index 19eb49abbace48e2f370ba8195f138d0be68bd97..0958d1c2647c95b83c3ad00b1e25f2b07b8d0107 100644 (file)
@@ -112,7 +112,7 @@ mdbox_save_alloc(struct mailbox_transaction_context *t)
        ctx->ctx.ctx.transaction = t;
        ctx->ctx.trans = t->itrans;
        ctx->mbox = mbox;
-       ctx->append_ctx = dbox_map_append_begin(mbox->storage->map, 0);
+       ctx->append_ctx = dbox_map_append_begin(mbox->storage->map);
        i_array_init(&ctx->mails, 32);
        t->save_ctx = &ctx->ctx.ctx;
        return t->save_ctx;
@@ -135,7 +135,7 @@ int mdbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
                st = i_stream_stat(input, FALSE);
                mail_size = st->st_size > 0 ? st->st_size : 0;
        }
-       if (dbox_map_append_next(ctx->append_ctx, mail_size,
+       if (dbox_map_append_next(ctx->append_ctx, mail_size, 0,
                                 &ctx->cur_file_append,
                                 &ctx->ctx.dbox_output) < 0) {
                ctx->ctx.failed = TRUE;
index de5624e6407c4c0a8f76d61449173f8c7867c9c2..2afb6c087372bf9a6156037a73a24eeb35a66bc0 100644 (file)
 static const struct setting_define mdbox_setting_defines[] = {
        DEF(SET_SIZE, mdbox_rotate_size),
        DEF(SET_TIME, mdbox_rotate_interval),
-       DEF(SET_TIME, mdbox_altmove),
 
        SETTING_DEFINE_LIST_END
 };
 
 static const struct mdbox_settings mdbox_default_settings = {
        .mdbox_rotate_size = 2*1024*1024,
-       .mdbox_rotate_interval = 0,
-       .mdbox_altmove = 3600*24*7
+       .mdbox_rotate_interval = 0
 };
 
 static const struct setting_parser_info mdbox_setting_parser_info = {
index a0ef7782d5514c866fe07e994aaaab058d33e56e..968f03a32ac5c199c22517e2fe1527e670c74441 100644 (file)
@@ -4,7 +4,6 @@
 struct mdbox_settings {
        uoff_t mdbox_rotate_size;
        unsigned int mdbox_rotate_interval;
-       unsigned int mdbox_altmove;
 };
 
 const struct setting_parser_info *mdbox_get_setting_parser_info(void);
index 34b6a9f8311564f2da0b4d15ae8138e7751ac07d..6773b00aa1f79682daadaec8b537f0e171defec9 100644 (file)
@@ -78,6 +78,11 @@ static void mdbox_storage_destroy(struct mail_storage *_storage)
        dbox_map_deinit(&storage->map);
        if (storage->to_close_unused_files != NULL)
                timeout_remove(&storage->to_close_unused_files);
+
+       if (array_is_created(&storage->move_from_alt_map_uids))
+               array_free(&storage->move_from_alt_map_uids);
+       if (array_is_created(&storage->move_to_alt_map_uids))
+               array_free(&storage->move_to_alt_map_uids);
        array_free(&storage->open_files);
 }
 
@@ -330,7 +335,7 @@ struct mail_storage mdbox_storage = {
                dbox_storage_get_list_settings,
                NULL,
                mdbox_mailbox_alloc,
-               mdbox_sync_purge
+               mdbox_purge
        }
 };
 
index 61f3f3e0e551b79678c3a99fbcc1670a0b0aaff4..1bb431a173ab6f4da3ffb99f2c1e6d4ab33aa63a 100644 (file)
@@ -32,6 +32,9 @@ struct mdbox_storage {
        ARRAY_DEFINE(open_files, struct mdbox_file *);
        struct timeout *to_close_unused_files;
 
+       ARRAY_TYPE(uint32_t) move_to_alt_map_uids;
+       ARRAY_TYPE(uint32_t) move_from_alt_map_uids;
+
        unsigned int rebuilding_storage:1;
 };
 
@@ -88,4 +91,7 @@ void mdbox_transaction_save_rollback(struct mail_save_context *ctx);
 
 int mdbox_copy(struct mail_save_context *ctx, struct mail *mail);
 
+void mdbox_purge_alt_flag_change(struct mail *mail, bool move_to_alt);
+int mdbox_purge(struct mail_storage *storage);
+
 #endif
index 79270eb9136df7f5db9b1f9ce915092ff7095413..6557262063e0bd7e4f96eb1c3fddc81975a15b90 100644 (file)
@@ -24,7 +24,6 @@
 #include "mdbox-sync.h"
 
 #include <stdlib.h>
-#include <dirent.h>
 
 static int
 dbox_sync_verify_expunge_guid(struct mdbox_sync_context *ctx, uint32_t seq,
@@ -330,112 +329,3 @@ mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
 
        return index_mailbox_sync_init(box, flags, ret < 0);
 }
-
-static int mdbox_sync_altmove_add_files(struct mdbox_storage *dstorage,
-                                       ARRAY_TYPE(seq_range) *file_ids)
-{
-       struct mail_storage *storage = &dstorage->storage.storage;
-       DIR *dir;
-       struct dirent *d;
-       struct stat st;
-       time_t altmove_mtime;
-       string_t *path;
-       unsigned int file_id, dir_len;
-       int ret = 0;
-
-       if (dstorage->set->mdbox_altmove == 0 ||
-           dstorage->alt_storage_dir == NULL)
-               return 0;
-
-       altmove_mtime = ioloop_time - dstorage->set->mdbox_altmove;
-
-       /* we want to quickly find the latest alt file, but we also want to
-          avoid accessing the alt storage as much as possible. so we'll do
-          this by finding the lowest numbered file (n) from primary storage.
-          hopefully one of n-[1..m] is appendable in alt storage. */
-       dir = opendir(dstorage->storage_dir);
-       if (dir == NULL) {
-               if (errno == ENOENT) {
-                       /* no storage directory at all yet */
-                       return 0;
-               }
-               mail_storage_set_critical(storage,
-                       "opendir(%s) failed: %m", dstorage->storage_dir);
-               return -1;
-       }
-
-       path = t_str_new(256);
-       str_append(path, dstorage->storage_dir);
-       str_append_c(path, '/');
-       dir_len = str_len(path);
-
-       for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
-               if (strncmp(d->d_name, MDBOX_MAIL_FILE_PREFIX,
-                           strlen(MDBOX_MAIL_FILE_PREFIX)) != 0)
-                       continue;
-               if (str_to_uint32(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX),
-                                 &file_id) < 0)
-                       continue;
-
-               str_truncate(path, dir_len);
-               str_append(path, d->d_name);
-
-               if (stat(str_c(path), &st) < 0) {
-                       mail_storage_set_critical(storage,
-                               "stat(%s) failed: %m", str_c(path));
-               } else if (st.st_mtime < altmove_mtime) {
-                       seq_range_array_add(file_ids, 0, file_id);
-               }
-       }
-       if (errno != 0) {
-               mail_storage_set_critical(storage,
-                       "readdir(%s) failed: %m", dstorage->storage_dir);
-               ret = -1;
-       }
-       if (closedir(dir) < 0) {
-               mail_storage_set_critical(storage,
-                       "closedir(%s) failed: %m", dstorage->storage_dir);
-               ret = -1;
-       }
-       return ret;
-}
-
-int mdbox_sync_purge(struct mail_storage *_storage)
-{
-       struct mdbox_storage *storage = (struct mdbox_storage *)_storage;
-       ARRAY_TYPE(seq_range) ref0_file_ids;
-       struct dbox_file *file;
-       struct seq_range_iter iter;
-       unsigned int i = 0;
-       uint32_t file_id;
-       bool deleted;
-       int ret = 0;
-
-       i_array_init(&ref0_file_ids, 64);
-       if (dbox_map_get_zero_ref_files(storage->map, &ref0_file_ids) < 0)
-               ret = -1;
-
-       /* add also files that can be altmoved */
-       if (mdbox_sync_altmove_add_files(storage, &ref0_file_ids) < 0)
-               ret = -1;
-
-       seq_range_array_iter_init(&iter, &ref0_file_ids); i = 0;
-       while (seq_range_array_iter_nth(&iter, i++, &file_id)) T_BEGIN {
-               file = mdbox_file_init(storage, file_id);
-               if (dbox_file_open(file, &deleted) > 0 && !deleted) {
-                       if (mdbox_file_purge(file) < 0)
-                               ret = -1;
-               } else {
-                       dbox_map_remove_file_id(storage->map, file_id);
-               }
-               dbox_file_unref(&file);
-       } T_END;
-       array_free(&ref0_file_ids);
-
-       if (storage->storage.files_corrupted) {
-               /* purging found corrupted files */
-               (void)mdbox_storage_rebuild(storage);
-               ret = -1;
-       }
-       return ret;
-}
index 37d0d6e585531ded3537c5ee35edc5914f1ce2a3..630303d41fd2d5caf9c625c1ffd58a4a8f0d3ed7 100644 (file)
@@ -27,8 +27,6 @@ int mdbox_sync_begin(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags,
 int mdbox_sync_finish(struct mdbox_sync_context **ctx, bool success);
 int mdbox_sync(struct mdbox_mailbox *mbox, enum mdbox_sync_flags flags);
 
-int mdbox_sync_purge(struct mail_storage *storage);
-
 struct mailbox_sync_context *
 mdbox_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);