mdbox-storage-rebuild.c revision 4c1f99ce36acdaafd2046ff2bb4e984c055fa37f
/* 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 "mail-namespace.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 mdbox_map_atomic_context *atomic;
struct hash_table *guid_hash;
struct mailbox_list *default_list;
struct rebuild_msg_mailbox prev_msg;
};
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_map_atomic_context *atomic)
{
struct mdbox_storage_rebuild_context *ctx;
return ctx;
}
static void
{
}
{
return -1;
return 1;
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;
}
static int
{
do {
/* use link()+unlink() instead of rename() to make sure we
don't overwrite any files. */
return 0;
}
return -1;
}
{
bool deleted;
int ret = 0;
/* m.*.broken files are created by file fixing
m.*.lock files are created if flock() isn't available */
i_warning("mdbox rebuild: "
"Skipping file with missing ID: %s/%s",
}
return 0;
}
} else {
/* duplicate file. either readdir() returned it twice
(unlikely) or it exists in both alt and primary storage.
to make sure we don't lose any mails from either of the
files, give this file a new ID and rename it. */
return -1;
}
if (ret == 0)
return ret < 0 ? -1 : 0;
}
static void
{
struct mdbox_rebuild_msg **msgs;
struct mdbox_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;
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;
const char *name;
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)
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 */
}
}
{
struct mdbox_map_mail_index_header map_hdr;
if (rebuild_handle_zero_refs(ctx) < 0)
return -1;
/* update map header */
return 0;
}
static int
const char *storage_dir, bool alt)
{
struct dirent *d;
int ret = 0;
return 0;
"opendir(%s) failed: %m", storage_dir);
return -1;
}
} T_END;
}
"readdir(%s) failed: %m", storage_dir);
ret = -1;
}
"closedir(%s) failed: %m", storage_dir);
ret = -1;
}
return ret;
}
{
const void *data;
return -1;
/* begin by locking the map, so that other processes can't try to
rebuild at the same time. */
return -1;
/* get old map header */
/* get storage rebuild counter after locking */
/* storage was already rebuilt by someone else */
return 0;
}
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_map_atomic_context *atomic)
{
struct mdbox_storage_rebuild_context *ctx;
int ret;
if (ret == 0) {
}
return ret;
}
{
struct mdbox_map_atomic_context *atomic;
int ret;
if (mdbox_map_atomic_finish(&atomic) < 0)
ret = -1;
return ret;
}