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);
doveadm-dump-thread.c \
doveadm-kick.c \
doveadm-mail.c \
+ doveadm-mail-altmove.c \
doveadm-mail-fetch.c \
doveadm-penalty.c \
doveadm-pw.c \
--- /dev/null
+/* 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);
+ }
+}
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)
#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,
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
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 \
+++ /dev/null
-/* 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;
-}
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
#include "index-mail.h"
#include "dbox-mail.h"
#include "mdbox-storage.h"
+#include "mdbox-sync.h"
#include "mdbox-map.h"
#include "mdbox-file.h"
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,
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,
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;
}
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;
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;
}
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)
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;
}
}
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);
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, ¬found) <= 0)
return FALSE;
}
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)
*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;
}
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;
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)
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
/* 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;
}
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;
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. */
}
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 */
/* 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;
}
}
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;
}
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);
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,
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. */
--- /dev/null
+/* 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;
+}
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;
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;
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 = {
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);
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);
}
dbox_storage_get_list_settings,
NULL,
mdbox_mailbox_alloc,
- mdbox_sync_purge
+ mdbox_purge
}
};
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;
};
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
#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,
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;
-}
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);