mailbox-list-index-sync.c revision 2d39dc1a453546892109b35c0d9770369011a13d
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "crc32.h"
#include "file-cache.h"
#include "file-set-size.h"
#include "mmap-util.h"
#include "ostream.h"
#include "mail-index-private.h"
#include "mailbox-list-index-private.h"
#include <stddef.h>
#define ROOT_INIT_COUNT 128
#define DIR_ALLOC_MORE_COUNT 4
#define MAILBOX_LIST_INDEX_GROW_PERCENTAGE 10
#define MAILBOX_LIST_INDEX_MIN_SIZE 512
struct mailbox_list_sync_record {
const char *name;
/* dir is used if it's non-NULL, otherwise dir_offset is used */
struct mailbox_list_sync_dir *dir;
};
struct mailbox_list_sync_dir {
/* The records are sorted by their name_hash */
/* Offset to the original location in the index, or 0 for new dirs */
unsigned int seen_records_count;
unsigned int new_records_count;
};
struct mailbox_list_index_sync_ctx {
struct mailbox_list_index *index;
struct mailbox_list_index_view *view;
enum mailbox_list_sync_flags flags;
const char *sync_path;
struct mail_index_sync_ctx *mail_sync_ctx;
struct mail_index_view *mail_view;
struct mail_index_transaction *trans;
struct mailbox_list_index_header hdr;
unsigned int failed:1;
unsigned int partial:1;
unsigned int seen_sync_root:1;
};
struct mailbox_list_sync_lookup_key {
const char *name;
};
static struct mailbox_list_sync_dir *
unsigned int initial_count)
{
struct mailbox_list_sync_dir *sync_dir;
return sync_dir;
}
static int
struct mailbox_list_sync_dir **sync_dir_r)
{
const struct mailbox_list_dir_record *dir;
const struct mailbox_list_record *recs;
struct mailbox_list_sync_dir *sync_dir;
struct mailbox_list_sync_record *sync_rec;
const char *name;
unsigned int i;
return -1;
continue;
"Record with UID=0");
}
recs[i].name_offset);
}
*sync_dir_r = sync_dir;
return 0;
}
{
return -1;
return 1;
}
static struct mailbox_list_sync_record *
{
struct mailbox_list_sync_lookup_key key;
struct mailbox_list_sync_record *recs;
unsigned int count;
bool match;
/* binary search the current hierarchy level name. the values are
sorted primarily by their hash value and secondarily by the actual
name */
idx_r);
if (!match)
return NULL;
}
static struct mailbox_list_sync_record *
struct mailbox_list_sync_dir *dir,
{
struct mailbox_list_sync_record *rec;
dir->new_records_count++;
return rec;
}
static int
struct mailbox_list_sync_record *rec)
{
"Record with UID=0");
}
return -1;
"Desync: Record expunged from mail index");
}
return 0;
}
static int
const char *name,
struct mailbox_list_sync_dir **dir_r,
{
const char *p, *hier_name;
struct mailbox_list_sync_dir *dir;
unsigned int idx, child_flags;
return -1;
t_push();
for (;;) {
if (*hier_name == '\0') {
if (p == NULL) {
/* name ended with a separator */
break;
}
/* two separators adjacently, skip this */
name = p + 1;
continue;
}
/* add CHILDREN flag to the parent and remove
NOCHILDREN flag. if we happened to create the
parent ourself, also add NONEXISTENT flag. */
}
/* new record */
/* this record was copied from existing index.
the uid is known, but the sequence isn't. */
break;
}
}
/* remember that we've seen this record */
}
if (p == NULL) {
/* leaf */
break;
}
if (rec->dir_offset != 0) {
break;
}
} else {
1 + DIR_ALLOC_MORE_COUNT);
}
}
name = p + 1;
}
t_pop();
}
{
/* root doesn't exist in the file yet */
} else {
return -1;
}
/* keep sync_root=root until we've built the sync_root path. */
return -1;
}
}
{
struct mail_index_sync_rec sync_rec;
0) < 0)
return -1;
/* we should have only external transactions in here, for which we
don't need to do anything but write them to the index */
;
return 0;
}
{
const struct mail_index_header *hdr;
if (hdr->uid_validity != 0) {
"Desync: uid_validity changed");
}
}
if (hdr->uid_validity == 0) {
TRUE);
}
return 0;
}
const char *path,
enum mailbox_list_sync_flags flags,
struct mailbox_list_index_sync_ctx **ctx_r)
{
struct mailbox_list_index_sync_ctx *ctx;
/* add separator to end of path if it isn't there */
1024*32);
/* mail index syncing acts as the only locking for us */
if (sync_mail_sync_init(ctx) < 0 ||
mailbox_list_index_refresh(index) < 0 ||
sync_mail_sync_init2(ctx) < 0 ||
mailbox_list_index_get_root(ctx) < 0) {
return -1;
}
return 0;
}
struct mail_index_view *
{
}
struct mail_index_transaction *
{
}
{
struct mailbox_list_sync_dir *dir;
}
static int
{
return -1;
}
return mailbox_list_index_map(index);
}
static int
{
/* all allocations must be 32bit aligned */
/* write the data into temporary buffer first */
}
} else {
return -1;
}
}
*base_offset_r = pos;
return 0;
}
static int
struct mailbox_list_sync_dir *sync_dir,
{
const struct mailbox_list_dir_record *dir;
const struct mailbox_list_record *recs;
struct mailbox_list_dir_record *new_dir;
struct mailbox_list_record *new_recs;
struct mailbox_list_sync_record *sync_recs;
unsigned int space_needed, deleted_space;
void *base;
/* count how much space we need and how much we wasted for deleted
records */
nondeleted_count = 0; space_needed = 0;
/* new record */
}
}
}
/* @UNSAFE */
&base, &base_offset) < 0)
return -1;
/* NOTE: any pointers to the index file may have been invalidated
as a result of growing the the memory area */
deleted_space = 0;
} else {
/* the offset should have been verified already to be valid */
/* approximate deleted_space. some of the mailbox names will be
reused, but don't bother calculating them. */
}
(const char *)base;
/* expunge from mail index */
return -1;
if (seq != 0)
// FIXME: expunge also NONEXISTENT parents
continue;
}
/* new record */
size);
} else {
/* existing record. need to find its name_offset */
break;
}
}
dest++;
}
if (index->mmap_disable) {
}
if (offset_pos == 0) {
/* we're writing the root directory */
} else {
/* add a link to this newly created directory. */
if (!index->mmap_disable) {
} else {
}
}
if (index->mmap_disable) {
/* file_cache_write() calls may have moved mmaping */
}
return 0;
}
static int
struct mailbox_list_sync_dir *sync_dir)
{
const struct mailbox_list_dir_record *dir;
struct mailbox_list_record *recs;
const struct mailbox_list_sync_record *sync_recs;
unsigned int i, j, count;
return -1;
else {
/* @UNSAFE: copy the records into a temporary buffer that
we modify and then write back to disk */
t_push();
}
/* records marked with deleted have been removed from sync_recs, so
we need to skip those */
for (i = j = 0; i < count; i++) {
j++;
}
/* expunge from mail index */
seq != 0)
}
}
sizeof(struct mailbox_list_dir_record);
t_pop();
}
return 0;
}
static int
struct mailbox_list_sync_dir *sync_dir,
{
const struct mailbox_list_dir_record *dir;
const struct mailbox_list_record *recs;
const struct mailbox_list_sync_record *sync_recs;
unsigned int i, j, count;
}
/* point to latest dir entry's next_offset */
}
if (sync_dir->new_records_count > 0) {
/* need to recreate the dir record */
partial) < 0)
return -1;
/* NOTE: index may have been remaped here */
} else if (sync_dir->seen_records_count !=
/* just mark the records deleted */
return -1;
}
/* we're doing a full sync only for the root */
}
/* update child mailboxes */
if (count == 0)
return 0;
for (i = j = 0; i < count; i++) {
continue;
/* these may change after each sync_write_dir() call */
/* child_offset_pos needs to point to record's dir_offset */
break;
}
partial) < 0)
return -1;
}
return 0;
}
static int
{
struct mailbox_list_index_header *hdr;
bool partial;
int ret = 0;
0, FALSE);
}
} else {
/* until we've seen the sync root, we're doing only partial
syncing */
}
ret = -1;
/* update header */
if (ret == 0)
"msync()");
ret = -1;
}
} else {
if (ret == 0) {
}
}
return ret;
}
{
/* write all the changes to the index */
}
if (ret == 0) {
/* FIXME: we should use some extension header instead of
reusing sync_size */
}
if (ret < 0)
else {
ret = -1;
}
}
return ret;
}
{
(void)mailbox_list_index_sync_commit(ctx);
}
{
return TRUE;
return FALSE;
}
struct mailbox_list_sync_dir *dir)
{
struct mailbox_list_sync_record *recs;
unsigned int i, count;
/* mark the directories as new */
for (i = 0; i < count; i++) {
if (recs[i].dir_offset == 0)
continue;
return -1;
}
recs[i].dir_offset = 0;
return -1;
}
return 0;
}
{
/* first read everything to memory */
return -1;
/* truncate the index file */
return -1;
/* reset header */
/* and write everything back */
return mailbox_list_index_sync_write(ctx);
}