mdbox-storage-rebuild.c revision f34c99a1b19f82c4e98cc4b7e2f7c91edba0544b
/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "istream.h"
#include "hash.h"
#include "str.h"
#include "dbox-sync-rebuild.h"
#include "mdbox-storage.h"
#include "mdbox-file.h"
#include "mdbox-map-private.h"
#include "mdbox-sync.h"
#include "mdbox-storage-rebuild.h"
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
struct mdbox_rebuild_msg {
unsigned int seen_zero_ref_in_map:1;
};
struct rebuild_msg_mailbox {
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *view;
struct mail_index_transaction *trans;
};
struct mdbox_storage_rebuild_context {
struct mdbox_storage *storage;
struct hash_table *guid_hash;
struct mailbox_list *default_list;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *sync_view;
struct mail_index_transaction *trans;
struct rebuild_msg_mailbox prev_msg;
unsigned int msgs_unsorted:1;
};
static unsigned int guid_hash(const void *p)
{
const uint8_t *s = p;
unsigned int i, g, h = 0;
for (i = 0; i < MAIL_GUID_128_SIZE; i++) {
h = (h << 4) + s[i];
if ((g = h & 0xf0000000UL)) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return h;
}
{
}
static struct mdbox_storage_rebuild_context *
{
struct mdbox_storage_rebuild_context *ctx;
return ctx;
}
static void
{
}
{
return -1;
return 1;
return -1;
return 1;
return 0;
}
struct mdbox_rebuild_msg *const *m2)
{
return -1;
return 1;
return 0;
}
{
const char *guid;
struct mdbox_rebuild_msg *rec;
int ret;
prev_offset = 0;
if (ret > 0) {
break;
}
if (ret == 0) {
/* file is corrupted. fix it and retry. */
break;
first = prev_offset == 0;
if (prev_offset == 0) {
/* use existing file header if it was ok */
}
ret = -1;
break;
}
if (!first) {
/* seek to the offset where we last left off */
prev_offset, NULL);
if (ret <= 0)
break;
}
continue;
}
"Message is missing GUID");
ret = 0;
break;
}
/* duplicate. save this as a refcount=0 to map,
so it will eventually be deleted. */
} else {
}
}
if (ret < 0)
return -1;
return 0;
else
return 1;
}
const char *path)
{
const char *fname;
unsigned int len;
bool deleted;
int ret = 0;
i_warning("dbox rebuild: File name is missing ID: %s",
path);
}
return 0;
}
/* small optimization: typically files are returned sorted. in that
case we don't need to sort them ourself. */
if (ret == 0)
return ret < 0 ? -1 : 0;
}
static void
{
struct mdbox_rebuild_msg **msgs;
struct dbox_map_mail_index_record rec;
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
}
}
{
const struct mail_index_header *hdr;
struct dbox_mail_lookup_rec rec;
unsigned int count;
if (ctx->msgs_unsorted)
return -1;
/* look up the rebuild msg record for this message */
/* map record points to non-existing or
a duplicate message. */
} else {
}
}
/* afterwards we're interested in looking up map_uids.
re-sort the messages to make it easier. */
return 0;
}
static struct mdbox_rebuild_msg *
{
struct mdbox_rebuild_msg **pos;
}
static void
struct dbox_sync_rebuild_context *rebuild_ctx,
struct mdbox_mailbox *mbox,
struct mail_index_view *view,
struct mail_index_transaction *trans)
{
const struct mdbox_mail_index_record *dbox_rec;
struct mdbox_mail_index_record new_dbox_rec;
const struct mail_index_header *hdr;
struct mdbox_rebuild_msg *rec;
const void *data;
bool expunged;
/* see if we can find this message based on
1) GUID, 2) map_uid */
if (map_uid == 0) {
/* not a multi-dbox message, ignore. */
continue;
}
/* multi-dbox message that wasn't found with GUID.
either it's lost or GUID has been corrupted. we can
still try to look it up using map_uid. */
}
/* map_uid is wrong, update it */
&new_dbox_rec, NULL);
} else {
/* everything was ok */
}
/* keep this message */
&new_dbox_rec, NULL);
} T_END;
}
}
static void
struct mdbox_index_header *hdr_r)
{
const void *data;
}
struct mdbox_mailbox *mbox)
{
else {
}
/* make sure we have valid mailbox guid */
sizeof(hdr.mailbox_guid));
} else {
}
}
/* update map's uid-validity */
/* and write changes */
}
static int
{
struct mdbox_mailbox *mbox;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *view;
struct mail_index_transaction *trans;
struct dbox_sync_rebuild_context *rebuild_ctx;
enum mail_error error;
int ret;
if (dbox_mailbox_open(box) < 0) {
mailbox_free(&box);
if (error == MAIL_ERROR_TEMP)
return -1;
/* non-temporary error, ignore */
return 0;
}
if (ret <= 0) {
mailbox_free(&box);
return -1;
}
if (mail_index_sync_commit(&sync_ctx) < 0) {
ret = -1;
}
mailbox_free(&box);
return ret < 0 ? -1 : 0;
}
static int
struct mail_namespace *ns)
{
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
int ret = 0;
MAILBOX_NOSELECT)) == 0) {
T_BEGIN {
} T_END;
if (ret < 0) {
ret = -1;
break;
}
}
}
if (mailbox_list_iter_deinit(&iter) < 0)
ret = -1;
return ret;
}
{
struct mail_namespace *ns;
return -1;
}
}
return 0;
}
{
return -1;
return 0;
}
struct mdbox_rebuild_msg *msg)
{
const struct mail_index_header *hdr;
struct mdbox_mail_index_record dbox_rec;
struct mdbox_mailbox *mbox;
enum mail_error error;
int ret;
/* first see if message contains the mailbox it was originally
saved to */
if (ret > 0)
}
if (ret < 0)
return -1;
/* we shouldn't get here, so apparently we couldn't fix
something. just ignore the mail.. */
return 0;
}
mailbox = "INBOX";
/* we have the destination mailbox. now open it and add the message
there. */
if (dbox_mailbox_open(box) == 0)
break;
/* mailbox doesn't exist currently? see if creating
it helps. */
mailbox_free(&box);
continue;
}
mailbox_free(&box);
if (error == MAIL_ERROR_TEMP)
return -1;
/* see if we can save to INBOX instead. */
mailbox = "INBOX";
} else {
/* this shouldn't happen */
return -1;
}
}
/* switch the mailbox cache if necessary */
return -1;
}
if (ret <= 0) {
mailbox_free(&box);
return -1;
}
}
/* add the new message */
return 0;
}
{
struct mdbox_rebuild_msg **msgs;
unsigned int i, count;
/* if we have messages at this point which have refcount=0, they're
either already expunged or they were somehow lost for some reason.
we'll need to figure out what to do about them. */
for (i = 0; i < count; i++) {
continue;
if (msgs[i]->seen_zero_ref_in_map) {
/* we've seen the map record, trust it. */
continue;
}
/* either map record was lost for this message or the message
was lost from its mailbox. safest way to handle this is to
restore the message. */
return -1;
}
return -1;
}
return 0;
}
{
const struct mail_index_header *hdr;
const void *data;
struct mdbox_rebuild_msg **msgs;
bool expunged;
unsigned int i, count;
/* update refcounts for existing map records */
/* we've already expunged this map record */
continue;
}
}
i++;
}
/* update refcounts for newly created map records */
}
}
{
if (rebuild_handle_zero_refs(ctx) < 0)
return -1;
return 0;
}
static int
const char *storage_dir, bool alt)
{
struct dirent *d;
unsigned int dir_len;
int ret = 0;
return 0;
"opendir(%s) failed: %m", storage_dir);
return -1;
}
strlen(MDBOX_MAIL_FILE_PREFIX)) == 0) {
T_BEGIN {
} T_END;
if (ret < 0) {
ret = -1;
break;
}
}
}
"readdir(%s) failed: %m", storage_dir);
ret = -1;
}
"closedir(%s) failed: %m", storage_dir);
ret = -1;
}
return ret;
}
{
const struct mail_index_header *hdr;
int ret = 0;
return -1;
/* begin by locking the map, so that other processes can't try to
rebuild at the same time. */
if (ret <= 0) {
return -1;
}
}
FALSE) < 0)
return -1;
return -1;
}
if (rebuild_apply_map(ctx) < 0 ||
rebuild_mailboxes(ctx) < 0 ||
rebuild_finish(ctx) < 0 ||
return -1;
return 0;
}
{
struct mdbox_storage_rebuild_context *ctx;
int ret;
/* no multi-dbox files */
return 0;
}
return -1;
}
if (ret == 0)
return ret;
}