]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mdbox: Purging now also moves mails to alt storage (if it's used).
authorTimo Sirainen <tss@iki.fi>
Tue, 2 Feb 2010 21:49:32 +0000 (23:49 +0200)
committerTimo Sirainen <tss@iki.fi>
Tue, 2 Feb 2010 21:49:32 +0000 (23:49 +0200)
mdbox_altmove setting specifies how old files should be moved.

--HG--
branch : HEAD

13 files changed:
doc/example-config/conf.d/mail.conf
src/lib-storage/index/dbox-common/dbox-file.c
src/lib-storage/index/dbox-common/dbox-file.h
src/lib-storage/index/dbox-multi/mdbox-file-purge.c
src/lib-storage/index/dbox-multi/mdbox-file.c
src/lib-storage/index/dbox-multi/mdbox-file.h
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-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-sync.c

index 4f8393c13a481214e5d4da6ab14107fc041b1ed0..e53a6fab7306bb81f07b506e358cc86b82eb865e 100644 (file)
 # Maximum dbox file age until it's rotated. Typically in days. Day begins
 # from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled.
 #mdbox_rotate_interval = 1d
+
+# When purging, move unchanged files to alt storage after this much time.
+#mdbox_altmove = 1w
index 72143210699ed2caf8f81d0ee233cde2823f910d..18f3459209ba0aa89c6c71d47343f293ffe419ab 100644 (file)
@@ -155,7 +155,7 @@ static int dbox_file_read_header(struct dbox_file *file)
        return ret;
 }
 
-static int dbox_file_open_fd(struct dbox_file *file)
+static int dbox_file_open_fd(struct dbox_file *file, bool try_altpath)
 {
        const char *path;
        bool alt = FALSE;
@@ -169,7 +169,7 @@ static int dbox_file_open_fd(struct dbox_file *file)
                        return -1;
                }
 
-               if (file->alt_path == NULL || alt) {
+               if (file->alt_path == NULL || alt || !try_altpath) {
                        /* not found */
                        return 0;
                }
@@ -182,22 +182,23 @@ static int dbox_file_open_fd(struct dbox_file *file)
        return 1;
 }
 
-int dbox_file_open(struct dbox_file *file, bool *deleted_r)
+static int dbox_file_open_full(struct dbox_file *file, bool try_altpath,
+                              bool *notfound_r)
 {
        int ret;
 
-       *deleted_r = FALSE;
+       *notfound_r = FALSE;
        if (file->input != NULL)
                return 1;
 
        if (file->fd == -1) {
                T_BEGIN {
-                       ret = dbox_file_open_fd(file);
+                       ret = dbox_file_open_fd(file, try_altpath);
                } T_END;
                if (ret <= 0) {
                        if (ret < 0)
                                return -1;
-                       *deleted_r = TRUE;
+                       *notfound_r = TRUE;
                        return 1;
                }
        }
@@ -207,6 +208,16 @@ int dbox_file_open(struct dbox_file *file, bool *deleted_r)
        return dbox_file_read_header(file);
 }
 
+int dbox_file_open(struct dbox_file *file, bool *deleted_r)
+{
+       return dbox_file_open_full(file, TRUE, deleted_r);
+}
+
+int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r)
+{
+       return dbox_file_open_full(file, FALSE, notfound_r);
+}
+
 int dbox_file_header_write(struct dbox_file *file, struct ostream *output)
 {
        string_t *hdr;
index 1384591cb0419374d2019ce123826a56da4e01e7..c8f1030c9c8698684dc0e0f0985d81c7447ccff5 100644 (file)
@@ -125,6 +125,8 @@ void dbox_file_unref(struct dbox_file **file);
 /* Open the file. Returns 1 if ok, 0 if file header is corrupted, -1 if error.
    If file is deleted, deleted_r=TRUE and 1 is returned. */
 int dbox_file_open(struct dbox_file *file, bool *deleted_r);
+/* Try to open file only from primary path. */
+int dbox_file_open_primary(struct dbox_file *file, bool *notfound_r);
 /* Close the file handle from the file, but don't free it. */
 void dbox_file_close(struct dbox_file *file);
 
index 8d3bf361035d6e8e6cfa113235bcac8b6c48cc74..08426fb9814653095feca64c4a7868bedde187da 100644 (file)
@@ -95,6 +95,7 @@ int mdbox_file_purge(struct dbox_file *file)
        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)
@@ -112,6 +113,11 @@ int mdbox_file_purge(struct dbox_file *file)
                return -1;
        }
 
+       if (dstorage->set->mdbox_altmove > 0 &&
+           st.st_mtime + 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,
@@ -124,7 +130,7 @@ int mdbox_file_purge(struct dbox_file *file)
        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_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;
index 5e5ebe85f3cbda868195fd7d54586e6e61a60b3a..067291101aac2ddd6470f9ca1c3dc4da5477f220 100644 (file)
@@ -103,10 +103,12 @@ static void mdbox_file_init_paths(struct mdbox_file *file, const char *fname)
 
 static int mdbox_file_create(struct dbox_file *file)
 {
+       bool create_parents;
        int ret;
 
+       create_parents = dbox_file_is_in_alt(file);
        file->fd = file->storage->v.
-               file_create_fd(file, file->primary_path, FALSE);
+               file_create_fd(file, file->cur_path, create_parents);
 
        /* even though we don't need it locked while writing to it, by the
           time we rename() it it needs to be locked. so we might as well do
@@ -122,8 +124,9 @@ static int mdbox_file_create(struct dbox_file *file)
        return 0;
 }
 
-struct dbox_file *
-mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id)
+static struct dbox_file *
+mdbox_file_init_full(struct mdbox_storage *storage,
+                    uint32_t file_id, bool alt_dir)
 {
        struct mdbox_file *file;
        const char *fname;
@@ -150,6 +153,8 @@ mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id)
                t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id);
        mdbox_file_init_paths(file, fname);
        dbox_file_init(&file->file);
+       if (alt_dir)
+               file->file.cur_path = file->file.alt_path;
 
        if (file_id != 0)
                array_append(&storage->open_files, &file, 1);
@@ -158,18 +163,31 @@ mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id)
        return &file->file;
 }
 
+struct dbox_file *
+mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id)
+{
+       return mdbox_file_init_full(storage, file_id, FALSE);
+}
+
+struct dbox_file *
+mdbox_file_init_new_alt(struct mdbox_storage *storage)
+{
+       return mdbox_file_init_full(storage, 0, TRUE);
+}
+
 int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id)
 {
        const char *old_path;
-       const char *new_fname, *new_path;
+       const char *new_dir, *new_fname, *new_path;
 
        i_assert(file->file_id == 0);
        i_assert(file_id != 0);
 
        old_path = file->file.cur_path;
        new_fname = t_strdup_printf(MDBOX_MAIL_FILE_FORMAT, file_id);
-       new_path = t_strdup_printf("%s/%s", file->storage->storage_dir,
-                                  new_fname);
+       new_dir = !dbox_file_is_in_alt(&file->file) ?
+               file->storage->storage_dir : file->storage->alt_storage_dir;
+       new_path = t_strdup_printf("%s/%s", new_dir, new_fname);
        if (rename(old_path, new_path) < 0) {
                mail_storage_set_critical(&file->storage->storage.storage,
                                          "rename(%s, %s) failed: %m",
index 61b1416c9ac4cd8372382dd938e4d7baeb6699b6..9548b726e720774034c71922c9eec1b2e7e4c32c 100644 (file)
@@ -12,6 +12,8 @@ struct mdbox_file {
 
 struct dbox_file *
 mdbox_file_init(struct mdbox_storage *storage, uint32_t file_id);
+struct dbox_file *
+mdbox_file_init_new_alt(struct mdbox_storage *storage);
 
 /* Assign file ID for a newly created file. */
 int mdbox_file_assign_file_id(struct mdbox_file *file, uint32_t file_id);
index 2253b7b50fdf821e6096de625e4caf8bb1b73528..3df8b7b252f8b97f8d236f322f9c5d4026ba54ea 100644 (file)
@@ -19,7 +19,6 @@ struct dbox_map {
        uint32_t created_uid_validity;
 
        uint32_t map_ext_id, ref_ext_id;
-       ARRAY_TYPE(seq_range) ref0_file_ids;
 
        mode_t create_mode, create_dir_mode;
        gid_t create_gid;
@@ -33,6 +32,7 @@ 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;
@@ -43,7 +43,6 @@ struct dbox_map_append_context {
        ARRAY_DEFINE(appends, struct dbox_map_append);
 
        uint32_t first_new_file_id;
-       uint32_t orig_next_uid;
 
        unsigned int files_nonappendable_count;
 
index dc82bd8d556e03c0985a1c90958a092cd52d20aa..ed3d8b60afd38654db71b8ae9db3b3498b2fe111 100644 (file)
@@ -9,6 +9,9 @@
 #include "mdbox-file.h"
 #include "mdbox-map-private.h"
 
+#include <stdlib.h>
+#include <dirent.h>
+
 #define MAX_BACKWARDS_LOOKUPS 10
 
 #define DBOX_FORCE_PURGE_MIN_BYTES (1024*1024*10)
@@ -75,8 +78,6 @@ void dbox_map_deinit(struct dbox_map **_map)
 
        *_map = NULL;
 
-       if (array_is_created(&map->ref0_file_ids))
-               array_free(&map->ref0_file_ids);
        if (map->view != NULL)
                mail_index_view_close(&map->view);
        mail_index_free(&map->index);
@@ -264,12 +265,8 @@ int dbox_map_get_file_msgs(struct dbox_map *map, uint32_t file_id,
        return 0;
 }
 
-struct dbox_file_size {
-       uoff_t file_size;
-       uoff_t ref0_size;
-};
-
-const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
+int dbox_map_get_zero_ref_files(struct dbox_map *map,
+                               ARRAY_TYPE(seq_range) *file_ids_r)
 {
        const struct mail_index_header *hdr;
        const struct dbox_map_mail_index_record *rec;
@@ -278,16 +275,12 @@ const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
        uint32_t seq;
        bool expunged;
 
-       if (array_is_created(&map->ref0_file_ids))
-               array_clear(&map->ref0_file_ids);
-       else
-               i_array_init(&map->ref0_file_ids, 64);
-
        if (dbox_map_open(map, FALSE) < 0) {
                /* some internal error */
-               return &map->ref0_file_ids;
+               return -1;
        }
-       (void)dbox_map_refresh(map);
+       if (dbox_map_refresh(map) < 0)
+               return -1;
 
        hdr = mail_index_get_header(map->view);
        for (seq = 1; seq <= hdr->messages_count; seq++) {
@@ -303,11 +296,10 @@ const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
                                      &data, &expunged);
                if (data != NULL && !expunged) {
                        rec = data;
-                       seq_range_array_add(&map->ref0_file_ids, 0,
-                                           rec->file_id);
+                       seq_range_array_add(file_ids_r, 0, rec->file_id);
                }
        }
-       return &map->ref0_file_ids;
+       return 0;
 }
 
 struct dbox_map_transaction_context *
@@ -481,12 +473,13 @@ 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)
+dbox_map_append_begin(struct dbox_map *map, enum dbox_map_append_flags flags)
 {
        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);
@@ -533,6 +526,34 @@ 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)
+{
+       bool want_altpath, notfound;
+
+       want_altpath = (ctx->flags & DBOX_MAP_APPEND_FLAG_ALT) != 0;
+       if (want_altpath) {
+               if (dbox_file_open(file, &notfound) <= 0)
+                       return FALSE;
+       } else {
+               if (dbox_file_open_primary(file, &notfound) <= 0)
+                       return FALSE;
+       }
+       if (notfound)
+               return FALSE;
+
+       if (file->lock != NULL) {
+               /* already locked, we're possibly in the middle of purging it
+                  in which case we really don't want to write there. */
+               return FALSE;
+       }
+       if (dbox_file_is_in_alt(file) != want_altpath) {
+               /* different alt location than what we want, can't use it */
+               return FALSE;
+       }
+       return TRUE;
+}
+
 static bool
 dbox_map_file_try_append(struct dbox_map_append_context *ctx,
                         uint32_t file_id, time_t stamp, uoff_t mail_size,
@@ -544,7 +565,7 @@ dbox_map_file_try_append(struct dbox_map_append_context *ctx,
        struct dbox_file *file;
        struct dbox_file_append_context *file_append;
        struct stat st;
-       bool deleted, file_too_old = FALSE;
+       bool file_too_old = FALSE;
        int ret;
 
        *file_append_r = NULL;
@@ -552,13 +573,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_file_open(file, &deleted) <= 0 || deleted) {
-               dbox_file_unref(&file);
-               return TRUE;
-       }
-       if (file->lock != NULL) {
-               /* already locked, we're possibly in the middle of purging it
-                  in which case we really don't want to write there. */
+       if (!dbox_try_open(ctx, file)) {
                dbox_file_unref(&file);
                return TRUE;
        }
@@ -611,50 +626,132 @@ dbox_map_is_appending(struct dbox_map_append_context *ctx, uint32_t file_id)
        return FALSE;
 }
 
-static int
-dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
-                             uoff_t mail_size,
-                             struct dbox_file_append_context **file_append_r,
-                             struct ostream **output_r, bool *existing_r)
+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)
 {
        struct dbox_map *map = ctx->map;
-       ARRAY_TYPE(seq_range) checked_file_ids;
        struct dbox_file_append_context *const *file_appends;
-       const struct mail_index_header *hdr;
-       unsigned int i, count, backwards_lookup_count;
-       uint32_t seq, seq1, uid, file_id;
-       uoff_t offset, append_offset, size;
-       time_t stamp;
-       bool retry_later;
-
-       *existing_r = FALSE;
+       unsigned int i, count;
+       uoff_t append_offset;
 
        if (mail_size >= map->set->mdbox_rotate_size)
-               return 0;
+               return NULL;
 
        /* first try to use files already used in this append */
        file_appends = array_get(&ctx->file_appends, &count);
        for (i = count; i > ctx->files_nonappendable_count; i--) {
                append_offset = file_appends[i-1]->output->offset;
                if (append_offset + mail_size <= map->set->mdbox_rotate_size &&
-                   dbox_file_get_append_stream(file_appends[i-1], output_r) > 0) {
-                       *file_append_r = file_appends[i-1];
-                       *existing_r = TRUE;
-                       return 1;
-               }
+                   dbox_file_get_append_stream(file_appends[i-1], output_r) > 0)
+                       return file_appends[i-1];
+
                /* can't append to this file anymore. we could close it
                   otherwise, except that would also lose our lock too early. */
        }
        ctx->files_nonappendable_count = count;
+       return NULL;
+}
+
+static int
+dbox_map_find_first_alt(struct dbox_map_append_context *ctx,
+                       uint32_t *min_file_id_r, uint32_t *seq_r)
+{
+       struct mdbox_storage *dstorage = ctx->map->storage;
+       struct mail_storage *storage = &dstorage->storage.storage;
+       DIR *dir;
+       struct dirent *d;
+       const struct mail_index_header *hdr;
+       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
+          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) {
+               mail_storage_set_critical(storage,
+                       "opendir(%s) failed: %m", dstorage->storage_dir);
+               return -1;
+       }
+
+       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;
+
+               file_id = strtoul(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX),
+                                 NULL, 10);
+               if (min_file_id > file_id)
+                       min_file_id = 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;
+       }
+       if (ret < 0)
+               return -1;
+
+       /* 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)
+                       return -1;
+
+               if (file_id < min_file_id)
+                       break;
+       }
+
+       *min_file_id_r = min_file_id;
+       *seq_r = seq;
+       return 0;
+}
+
+static int
+dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
+                             uoff_t mail_size,
+                             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;
+       unsigned int backwards_lookup_count;
+       uint32_t seq, seq1, uid, file_id, min_file_id;
+       uoff_t offset, size;
+       time_t stamp;
+       bool retry_later;
+
+       if (mail_size >= map->set->mdbox_rotate_size)
+               return 0;
 
        /* try to find an existing appendable file */
        stamp = day_begin_stamp(map->set->mdbox_rotate_interval);
        hdr = mail_index_get_header(map->view);
 
-       ctx->orig_next_uid = hdr->next_uid;
        backwards_lookup_count = 0;
        t_array_init(&checked_file_ids, 16);
-       for (seq = hdr->messages_count; seq > 0; seq--) {
+
+       if ((ctx->flags & DBOX_MAP_APPEND_FLAG_ALT) == 0)
+               seq = hdr->messages_count;
+       else {
+               /* we want to save to alt storage. */
+               if (dbox_map_find_first_alt(ctx, &min_file_id, &seq) < 0)
+                       return -1;
+               seq_range_array_add_range(&checked_file_ids,
+                                         min_file_id, (uint32_t)-1);
+       }
+
+       for (; seq > 0; seq--) {
                if (dbox_map_lookup_seq(map, seq, &file_id, &offset, &size) < 0)
                        return -1;
 
@@ -712,15 +809,24 @@ int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
        if (ctx->failed)
                return -1;
 
-       ret = dbox_map_find_appendable_file(ctx, mail_size, &file_append,
-                                           output_r, &existing);
+       file_append = dbox_map_find_existing_append(ctx, mail_size, output_r);
+       if (file_append != NULL) {
+               ret = 1;
+               existing = TRUE;
+       } else {
+               ret = dbox_map_find_appendable_file(ctx, mail_size,
+                                                   &file_append, output_r);
+               existing = FALSE;
+       }
        if (ret > 0)
                file = file_append->file;
        else if (ret < 0)
                return -1;
        else {
                /* create a new file */
-               file = mdbox_file_init(ctx->map->storage, 0);
+               file = (ctx->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);
 
                ret = dbox_file_get_append_stream(file_append, output_r);
index 788efbd7cfa07f0e2bd9ba7f1eaf27ee5e1b3809..65e2933d5fe3cbd5cc09958900e8171beebe3fe3 100644 (file)
@@ -7,6 +7,10 @@ struct dbox_map_append_context;
 struct dbox_file_append_context;
 struct mdbox_storage;
 
+enum dbox_map_append_flags {
+       DBOX_MAP_APPEND_FLAG_ALT        = 0x01
+};
+
 struct dbox_map_mail_index_header {
        uint32_t highest_file_id;
 };
@@ -56,10 +60,11 @@ int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
 int dbox_map_remove_file_id(struct dbox_map *map, uint32_t file_id);
 
 /* Return all files containing messages with zero refcount. */
-const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map);
+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);
+dbox_map_append_begin(struct dbox_map *map, enum dbox_map_append_flags flags);
 /* 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. */
index dff466816c2aa60e1b5047c2b167f665f646b1bb..9390353cc547c4f865a35dbedd9cbb062a6ceadd 100644 (file)
@@ -85,7 +85,7 @@ mdbox_save_alloc(struct mailbox_transaction_context *t)
        ctx->ctx.ctx.transaction = t;
        ctx->ctx.trans = it->trans;
        ctx->mbox = mbox;
-       ctx->append_ctx = dbox_map_append_begin(mbox->storage->map);
+       ctx->append_ctx = dbox_map_append_begin(mbox->storage->map, 0);
        i_array_init(&ctx->mails, 32);
        t->save_ctx = &ctx->ctx.ctx;
        return t->save_ctx;
index 704bb47e9deb256646fe54137e99975701e038b9..7bee02e054e5860ad752b7fbd0bb6eadb6c80537 100644 (file)
@@ -17,6 +17,7 @@ static bool mdbox_settings_verify(void *_set, pool_t pool ATTR_UNUSED,
 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),
        DEF(SET_UINT, mdbox_max_open_files),
 
        SETTING_DEFINE_LIST_END
@@ -25,6 +26,7 @@ static const struct setting_define mdbox_setting_defines[] = {
 static const struct mdbox_settings mdbox_default_settings = {
        .mdbox_rotate_size = 2*1024*1024,
        .mdbox_rotate_interval = 0,
+       .mdbox_altmove = 3600*24*7,
        .mdbox_max_open_files = 64
 };
 
index 02fd0d0810be9c29057f0f5b4b50528d3599b755..b1fdcc0ec79924f9d119218ebe3707a88e1be067 100644 (file)
@@ -4,6 +4,7 @@
 struct mdbox_settings {
        uoff_t mdbox_rotate_size;
        unsigned int mdbox_rotate_interval;
+       unsigned int mdbox_altmove;
        unsigned int mdbox_max_open_files;
 };
 
index e1030abea6df1a25bc0d9773e131d0ab3a4ec9bf..ed6c39bdada9fd9a4d1e926260768b772c936886 100644 (file)
@@ -11,6 +11,9 @@
 #include "mdbox-file.h"
 #include "mdbox-sync.h"
 
+#include <stdlib.h>
+#include <dirent.h>
+
 #define DBOX_REBUILD_COUNT 3
 
 static int
@@ -311,10 +314,75 @@ 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) {
+               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;
+
+               str_truncate(path, dir_len);
+               str_append(path, d->d_name);
+
+               file_id = strtoul(d->d_name + strlen(MDBOX_MAIL_FILE_PREFIX),
+                                 NULL, 10);
+
+               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;
-       const ARRAY_TYPE(seq_range) *ref0_file_ids;
+       ARRAY_TYPE(seq_range) ref0_file_ids;
        struct dbox_file *file;
        struct seq_range_iter iter;
        unsigned int i = 0;
@@ -322,8 +390,15 @@ int mdbox_sync_purge(struct mail_storage *_storage)
        bool deleted;
        int ret = 0;
 
-       ref0_file_ids = dbox_map_get_zero_ref_files(storage->map);
-       seq_range_array_iter_init(&iter, ref0_file_ids); i = 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) {
@@ -334,5 +409,6 @@ int mdbox_sync_purge(struct mail_storage *_storage)
                }
                dbox_file_unref(&file);
        } T_END;
+       array_free(&ref0_file_ids);
        return ret;
 }