[dovecot-cvs] dovecot/src/lib-index Makefile.am, 1.30, 1.31 mailbox-list-index-private.h, NONE, 1.1 mailbox-list-index-sync.c, NONE, 1.1 mailbox-list-index.c, NONE, 1.1 mailbox-list-index.h, NONE, 1.1
tss at dovecot.org
tss at dovecot.org
Sat Nov 25 22:17:42 UTC 2006
- Previous message: [dovecot-cvs] dovecot/src/imap Makefile.am, 1.36, 1.37 cmd-list.c, 1.58, 1.59 common.h, 1.22, 1.23 imap-sync.c, 1.20, 1.21 imap-thread.c, 1.28, 1.29 main.c, 1.82, 1.83 namespace.c, 1.16, 1.17
- Next message: [dovecot-cvs] dovecot/src/lib-storage/index index-status.c, 1.37, 1.38 index-storage.c, 1.91, 1.92 index-storage.h, 1.106, 1.107 index-sync.c, 1.58, 1.59
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /var/lib/cvs/dovecot/src/lib-index
In directory talvi:/tmp/cvs-serv28938/lib-index
Modified Files:
Makefile.am
Added Files:
mailbox-list-index-private.h mailbox-list-index-sync.c
mailbox-list-index.c mailbox-list-index.h
Log Message:
Mailbox list indexing and related changes. Currently works only with
maildir and mmap_disable=no. This allows doing STATUS to synced mailboxes
without opening their index files at all.
Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-index/Makefile.am,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- Makefile.am 15 Oct 2006 12:25:39 -0000 1.30
+++ Makefile.am 25 Nov 2006 22:17:40 -0000 1.31
@@ -28,7 +28,9 @@
mail-transaction-log.c \
mail-transaction-log-append.c \
mail-transaction-log-view.c \
- mail-transaction-util.c
+ mail-transaction-util.c \
+ mailbox-list-index.c \
+ mailbox-list-index-sync.c
noinst_HEADERS = \
mail-cache.h \
@@ -41,4 +43,6 @@
mail-index-view-private.h \
mail-transaction-log.h \
mail-transaction-log-private.h \
- mail-transaction-util.h
+ mail-transaction-util.h \
+ mailbox-list-index.h \
+ mailbox-list-index-private.h
--- NEW FILE: mailbox-list-index-private.h ---
#ifndef __MAILBOX_LIST_INDEX_PRIVATE_H
#define __MAILBOX_LIST_INDEX_PRIVATE_H
#include "mailbox-list-index.h"
#define MAILBOX_LIST_INDEX_MAJOR_VERSION 1
#define MAILBOX_LIST_INDEX_MINOR_VERSION 0
struct mailbox_list_index_header {
uint8_t major_version;
uint8_t minor_version;
uint8_t unused[2];
uint32_t header_size;
uint32_t uid_validity;
/* locking required to access the fields below: */
uint32_t next_uid;
uint32_t used_space;
uint32_t deleted_space;
};
struct mailbox_list_dir_record {
/* If non-zero, contains a pointer to updated directory list.
Stored using mail_index_uint32_to_offset(). */
uint32_t next_offset;
uint32_t count;
/* The records are sorted by their name_hash */
/* struct mailbox_list_record records[count]; */
};
struct mailbox_list_record {
/* CRC32 hash of the name */
uint32_t name_hash;
uint32_t uid:31;
/* Set when this record has been marked as deleted. It will be removed
permanently the next time a new record is added to this directory
or on the next index compression. */
uint32_t deleted:1;
/* Points to a NUL-terminated record name */
uint32_t name_offset;
/* the dir offset is stored using mail_index_uint32_to_offset()
since it may change while we're reading */
uint32_t dir_offset;
};
struct mailbox_list_index {
char *filepath;
char separator;
struct mail_index *mail_index;
int fd;
void *mmap_base;
size_t mmap_size;
const struct mailbox_list_index_header *hdr;
};
#define MAILBOX_LIST_RECORDS(dir) \
((struct mailbox_list_record *)(dir + 1))
#define MAILBOX_LIST_RECORD_IDX(dir, rec) \
((rec) - MAILBOX_LIST_RECORDS(dir))
int mailbox_list_index_set_syscall_error(struct mailbox_list_index *index,
const char *function);
int mailbox_list_index_dir_lookup_rec(struct mailbox_list_index *index,
const struct mailbox_list_dir_record *dir,
const char *name,
const struct mailbox_list_record **rec_r);
int mailbox_list_index_get_dir(struct mailbox_list_index *index,
uint32_t *offset,
const struct mailbox_list_dir_record **dir_r);
int mailbox_list_index_map(struct mailbox_list_index *index);
#endif
--- NEW FILE: mailbox-list-index-sync.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "crc32.h"
#include "file-set-size.h"
#include "mmap-util.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 {
uint32_t name_hash;
uint32_t seq;
uint32_t uid;
const char *name;
/* dir is used if it's non-NULL, otherwise dir_offset is used */
struct mailbox_list_sync_dir *dir;
uint32_t dir_offset;
uint32_t created:1;
uint32_t seen:1;
};
struct mailbox_list_sync_dir {
/* The records are sorted by their name_hash */
ARRAY_DEFINE(records, struct mailbox_list_sync_record);
/* Offset to the original location in the index, or 0 for new dirs */
uint32_t offset;
unsigned int seen_records_count;
unsigned int new_records_count;
};
struct mailbox_list_index_sync_ctx {
struct mailbox_list_index *index;
pool_t pool;
enum mailbox_list_sync_flags flags;
const char *sync_path;
struct mail_index_sync_ctx *mail_sync_ctx;
struct mail_index_view *view;
struct mail_index_transaction *trans;
struct mailbox_list_index_header hdr;
struct mailbox_list_sync_dir *root, *sync_root;
unsigned int failed:1;
unsigned int partial:1;
unsigned int seen_sync_root:1;
};
struct mailbox_list_sync_lookup_key {
uint32_t name_hash;
const char *name;
bool *match;
};
static struct mailbox_list_sync_dir *
mailbox_list_alloc_sync_dir(struct mailbox_list_index_sync_ctx *ctx,
unsigned int initial_count)
{
struct mailbox_list_sync_dir *sync_dir;
sync_dir = p_new(ctx->pool, struct mailbox_list_sync_dir, 1);
p_array_init(&sync_dir->records, ctx->pool, initial_count);
return sync_dir;
}
static int
mailbox_list_copy_sync_dir(struct mailbox_list_index_sync_ctx *ctx,
uint32_t offset,
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;
size_t max_len;
unsigned int i;
if (mailbox_list_index_get_dir(ctx->index, &offset, &dir) < 0)
return -1;
sync_dir = mailbox_list_alloc_sync_dir(ctx, dir->count +
DIR_ALLOC_MORE_COUNT);
sync_dir->offset = offset;
recs = MAILBOX_LIST_RECORDS(dir);
for (i = 0; i < dir->count; i++) {
sync_rec = array_append_space(&sync_dir->records);
sync_rec->name_hash = recs[i].name_hash;
sync_rec->uid = recs[i].uid;
sync_rec->dir_offset =
mail_index_offset_to_uint32(recs[i].dir_offset);
max_len = ctx->index->mmap_size - recs[i].name_offset;
name = CONST_PTR_OFFSET(ctx->index->mmap_base,
recs[i].name_offset);
sync_rec->name = p_strndup(ctx->pool, name, max_len);
}
*sync_dir_r = sync_dir;
return 0;
}
static int mailbox_list_sync_record_cmp(const void *_key, const void *_rec)
{
const struct mailbox_list_sync_lookup_key *key = _key;
const struct mailbox_list_sync_record *rec = _rec;
int ret;
if (key->name_hash < rec->name_hash)
return -1;
if (key->name_hash > rec->name_hash)
return 1;
ret = strcmp(key->name, rec->name);
if (ret == 0)
*key->match = TRUE;
return ret;
}
static struct mailbox_list_sync_record *
mailbox_list_sync_dir_lookup(struct mailbox_list_sync_dir *dir,
const char *name, unsigned int *idx_r)
{
struct mailbox_list_sync_lookup_key key;
const struct mailbox_list_sync_record *recs;
struct mailbox_list_sync_record *rec;
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 */
match = FALSE;
key.name = name;
key.name_hash = crc32_str(name);
key.match = &match;
recs = array_get(&dir->records, &count);
rec = bsearch_insert_pos(&key, recs, count, sizeof(*rec),
mailbox_list_sync_record_cmp);
*idx_r = rec - recs;
return match ? rec : NULL;
}
static struct mailbox_list_sync_record *
mailbox_list_alloc_add_record(struct mailbox_list_index_sync_ctx *ctx,
struct mailbox_list_sync_dir *dir,
const char *name, unsigned int idx)
{
struct mailbox_list_sync_record *rec;
rec = array_insert_space(&dir->records, idx);
rec->name_hash = crc32_str(name);
rec->name = p_strdup(ctx->pool, name);
rec->uid = ctx->hdr.next_uid++;
rec->created = TRUE;
mail_index_append(ctx->trans, rec->uid, &rec->seq);
dir->new_records_count++;
return rec;
}
static int
mailbox_list_index_sync_get_seq(struct mailbox_list_index_sync_ctx *ctx,
struct mailbox_list_sync_record *rec)
{
if (rec->uid == 0) {
return mailbox_list_index_set_corrupted(ctx->index,
"Record with UID=0");
}
if (mail_index_lookup_uid_range(ctx->view, rec->uid, rec->uid,
&rec->seq, &rec->seq) < 0)
return -1;
if (rec->seq == 0) {
return mailbox_list_index_set_corrupted(ctx->index,
"Desync: Record expunged from mail index");
}
return 0;
}
static int
mailbox_list_index_sync_int(struct mailbox_list_index_sync_ctx *ctx,
const char *name,
struct mailbox_list_sync_dir **dir_r,
uint32_t *seq_r)
{
const char *p, *hier_name;
struct mailbox_list_sync_dir *dir;
struct mailbox_list_sync_record *rec = NULL;
unsigned int idx;
if (ctx->failed)
return -1;
dir = ctx->sync_root;
t_push();
for (;;) {
p = strchr(name, ctx->index->separator);
hier_name = p == NULL ? name : t_strdup_until(name, p);
if (*hier_name == '\0') {
if (p == NULL) {
/* name ended with a separator */
break;
}
/* two separators adjacently, skip this */
name = p + 1;
continue;
}
if (rec != NULL) {
mail_index_update_flags(ctx->trans, rec->seq,
MODIFY_REPLACE,
MAILBOX_LIST_INDEX_FLAG_NONEXISTENT |
MAILBOX_LIST_INDEX_FLAG_CHILDREN);
}
rec = mailbox_list_sync_dir_lookup(dir, hier_name, &idx);
if (rec == NULL) {
/* new record */
rec = mailbox_list_alloc_add_record(ctx, dir,
hier_name, idx);
} else if (rec->seq == 0) {
/* this record was copied from existing index.
the uid is known, but the sequence isn't. */
if (mailbox_list_index_sync_get_seq(ctx, rec) < 0) {
ctx->failed = TRUE;
break;
}
}
*seq_r = rec->seq;
/* remember that we've seen this record */
if (!rec->seen) {
rec->seen = TRUE;
dir->seen_records_count++;
}
if (p == NULL) {
/* leaf */
break;
}
if (rec->dir == NULL) {
if (rec->dir_offset != 0) {
if (mailbox_list_copy_sync_dir(ctx,
rec->dir_offset,
&rec->dir) < 0) {
ctx->failed = TRUE;
break;
}
} else {
rec->dir = mailbox_list_alloc_sync_dir(ctx,
1 + DIR_ALLOC_MORE_COUNT);
}
}
name = p + 1;
dir = rec->dir;
}
t_pop();
i_assert(dir != NULL);
*dir_r = dir;
return ctx->failed ? -1 : 0;
}
static int mailbox_list_index_get_root(struct mailbox_list_index_sync_ctx *ctx)
{
uint32_t seq;
i_assert(ctx->index->mmap_size > 0);
if (ctx->index->mmap_size == sizeof(*ctx->index->hdr)) {
/* root doesn't exist in the file yet */
ctx->root = mailbox_list_alloc_sync_dir(ctx,
ROOT_INIT_COUNT);
} else {
if (mailbox_list_copy_sync_dir(ctx, sizeof(*ctx->index->hdr),
&ctx->root) < 0)
return -1;
}
/* keep sync_root=root until we've built the sync_root path. */
ctx->sync_root = ctx->root;
if (*ctx->sync_path != '\0') {
if (mailbox_list_index_sync_more(ctx, ctx->sync_path, &seq) < 0)
return -1;
}
return mailbox_list_index_sync_int(ctx, ctx->sync_path,
&ctx->sync_root, &seq);
}
static int sync_init_mail_sync(struct mailbox_list_index_sync_ctx *ctx)
{
struct mail_index_sync_rec sync_rec;
const struct mail_index_header *hdr;
if (mail_index_sync_begin(ctx->index->mail_index, &ctx->mail_sync_ctx,
&ctx->view, (uint32_t)-1, 0,
FALSE, FALSE) < 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 */
while (mail_index_sync_next(ctx->mail_sync_ctx, &sync_rec) > 0)
;
hdr = mail_index_get_header(ctx->view);
if (hdr->uid_validity != 0) {
if (hdr->uid_validity != ctx->hdr.uid_validity) {
return mailbox_list_index_set_corrupted(ctx->index,
"Desync: uid_validity changed");
}
}
ctx->trans = mail_index_transaction_begin(ctx->view, FALSE, TRUE);
if (hdr->uid_validity == 0) {
mail_index_update_header(ctx->trans,
offsetof(struct mail_index_header, uid_validity),
&ctx->hdr.uid_validity, sizeof(ctx->hdr.uid_validity),
TRUE);
}
return mailbox_list_index_get_root(ctx);
}
int mailbox_list_index_sync_init(struct mailbox_list_index *index,
const char *path,
enum mailbox_list_sync_flags flags,
struct mailbox_list_index_sync_ctx **ctx_r)
{
struct mailbox_list_index_sync_ctx *ctx;
pool_t pool;
size_t len;
/* add separator to end of path if it isn't there */
len = strlen(path);
if (len > 0 && path[len-1] != index->separator)
path = t_strdup_printf("%s%c", path, index->separator);
pool = pool_alloconly_create("mailbox list index sync", 1024*32);
ctx = p_new(pool, struct mailbox_list_index_sync_ctx, 1);
ctx->pool = pool;
ctx->index = index;
ctx->sync_path = p_strdup(pool, path);
ctx->flags = flags;
ctx->hdr = *index->hdr;
/* mail index syncing acts as the only locking for us */
if (sync_init_mail_sync(ctx) < 0) {
mailbox_list_index_sync_commit(&ctx);
return -1;
}
*ctx_r = ctx;
return 0;
}
struct mail_index_view *
mailbox_list_index_sync_get_view(struct mailbox_list_index_sync_ctx *ctx)
{
return ctx->view;
}
struct mail_index_transaction *
mailbox_list_index_sync_get_transaction(struct mailbox_list_index_sync_ctx *ctx)
{
return ctx->trans;
}
int mailbox_list_index_sync_more(struct mailbox_list_index_sync_ctx *ctx,
const char *name, uint32_t *seq_r)
{
struct mailbox_list_sync_dir *dir;
return mailbox_list_index_sync_int(ctx, name, &dir, seq_r);
}
static int
mailbox_list_index_sync_grow(struct mailbox_list_index_sync_ctx *ctx,
uint32_t size)
{
struct mailbox_list_index *index = ctx->index;
uoff_t new_fsize, grow_size;
new_fsize = ctx->hdr.used_space + size;
grow_size = new_fsize / 100 * MAILBOX_LIST_INDEX_GROW_PERCENTAGE;
if (grow_size < MAILBOX_LIST_INDEX_MIN_SIZE)
grow_size = MAILBOX_LIST_INDEX_MIN_SIZE;
new_fsize += grow_size;
new_fsize &= ~(512-1);
i_assert(new_fsize >= ctx->hdr.used_space + size);
if (file_set_size(index->fd, (off_t)new_fsize) < 0) {
mailbox_list_index_set_syscall_error(index, "file_set_size()");
return -1;
}
return mailbox_list_index_map(index);
}
static int
mailbox_list_index_sync_alloc_space(struct mailbox_list_index_sync_ctx *ctx,
uint32_t size, void **base_r,
uint32_t *base_offset_r)
{
size_t pos = ctx->hdr.used_space;
/* all allocations must be 32bit aligned */
pos = (pos + 3) & ~3;
if (pos + size > ctx->index->mmap_size) {
if (mailbox_list_index_sync_grow(ctx, size + 3) < 0)
return -1;
i_assert(pos + size < ctx->index->mmap_size);
}
*base_offset_r = pos;
*base_r = PTR_OFFSET(ctx->index->mmap_base, *base_offset_r);
ctx->hdr.used_space = pos + size;
return 0;
}
static int
mailbox_list_index_sync_recreate_dir(struct mailbox_list_index_sync_ctx *ctx,
struct mailbox_list_sync_dir *sync_dir,
uint32_t offset_pos, bool partial)
{
struct mailbox_list_dir_record *dir, *new_dir;
struct mailbox_list_record *recs, *new_recs;
struct mailbox_list_sync_record *sync_recs;
unsigned int src, dest, orig, count, nondeleted_count;
unsigned int name_space_needed, deleted_space;
uint32_t base_offset, name_pos, size;
void *base;
i_assert((offset_pos % sizeof(uint32_t)) == 0);
i_assert(offset_pos < ctx->index->mmap_size);
/* count how much space we need and how much we wasted for deleted
records */
nondeleted_count = 0; name_space_needed = 0; deleted_space = 0;
sync_recs = array_get_modifiable(&sync_dir->records, &count);
for (src = 0; src < count; src++) {
if (sync_recs[src].seen || partial) {
nondeleted_count++;
if (sync_recs[src].created) {
/* new record */
name_space_needed +=
strlen(sync_recs[src].name) + 1;
}
} else {
deleted_space += sizeof(*new_recs) +
strlen(sync_recs[src].name) + 1;
}
}
/* @UNSAFE */
name_space_needed += sizeof(*dir) +
nondeleted_count * sizeof(*new_recs);
if (mailbox_list_index_sync_alloc_space(ctx, name_space_needed,
&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 */
if (sync_dir->offset == 0) {
dir = NULL;
recs = NULL;
} else {
/* the offset should have been verified already to be valid */
i_assert(sync_dir->offset == offset_pos);
i_assert(sync_dir->offset < ctx->index->mmap_size);
dir = PTR_OFFSET(ctx->index->mmap_base, sync_dir->offset);
recs = MAILBOX_LIST_RECORDS(dir);
}
new_dir = base;
new_dir->count = nondeleted_count;
new_recs = MAILBOX_LIST_RECORDS(new_dir);
name_pos = (const char *)(new_recs + nondeleted_count) -
(const char *)base;
for (src = dest = 0; src < count; src++) {
if (!sync_recs[src].seen && !partial) {
/* expunge from mail index */
uint32_t seq;
if (mail_index_lookup_uid_range(ctx->view,
sync_recs[src].uid,
sync_recs[src].uid,
&seq, &seq) < 0)
return -1;
if (seq != 0)
mail_index_expunge(ctx->trans, seq);
// FIXME: expunge also NONEXISTENT parents
continue;
}
new_recs[dest].name_hash = sync_recs[src].name_hash;
new_recs[dest].dir_offset =
mail_index_uint32_to_offset(sync_recs[src].dir_offset);
if (sync_recs[src].created) {
/* new record */
new_recs[dest].uid = sync_recs[src].uid;
new_recs[dest].name_offset = base_offset + name_pos;
size = strlen(sync_recs[src].name) + 1;
memcpy(PTR_OFFSET(base, name_pos), sync_recs[src].name,
size);
name_pos += size;
} else {
/* existing record. need to find its name_offset */
for (orig = 0; orig < dir->count; orig++) {
if (recs[orig].uid == sync_recs[src].uid)
break;
}
i_assert(orig < dir->count);
new_recs[dest].uid = sync_recs[src].uid;
new_recs[dest].name_offset = recs[orig].name_offset;
}
dest++;
}
i_assert(dest == nondeleted_count);
i_assert(name_pos == name_space_needed);
if (offset_pos == 0) {
/* we're writing the root directory */
i_assert(base_offset == sizeof(*ctx->index->hdr));
} else {
/* add a link to this newly created directory. */
uint32_t *pos;
pos = PTR_OFFSET(ctx->index->mmap_base, offset_pos);
i_assert(mail_index_offset_to_uint32(*pos) == 0);
*pos = mail_index_uint32_to_offset(base_offset);
}
sync_dir->offset = base_offset;
return 0;
}
static int
mailbox_list_index_sync_update_dir(struct mailbox_list_index_sync_ctx *ctx,
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, count;
i_assert(sync_dir->offset != 0);
if (mailbox_list_index_get_dir(ctx->index, &sync_dir->offset, &dir) < 0)
return -1;
sync_recs = array_get(&sync_dir->records, &count);
i_assert(dir->count == count);
i_assert(sync_dir->seen_records_count < count);
recs = MAILBOX_LIST_RECORDS(dir);
for (i = 0; i < dir->count; i++) {
if (!sync_recs[i].seen)
recs[i].deleted = TRUE;
}
return 0;
}
static int
mailbox_list_index_sync_write_dir(struct mailbox_list_index_sync_ctx *ctx,
struct mailbox_list_sync_dir *sync_dir,
uint32_t offset_pos, bool partial)
{
const struct mailbox_list_dir_record *dir;
const struct mailbox_list_record *recs;
const struct mailbox_list_sync_record *sync_recs;
uint32_t child_offset_pos;
unsigned int i, j, count;
if (!ctx->seen_sync_root && ctx->sync_root == sync_dir) {
i_assert(partial);
ctx->seen_sync_root = TRUE;
partial = (ctx->flags & MAILBOX_LIST_SYNC_FLAG_PARTIAL) != 0;
}
if (sync_dir->offset != 0) {
/* point to latest dir entry's next_offset */
offset_pos = sync_dir->offset +
offsetof(struct mailbox_list_dir_record, next_offset);
}
if (sync_dir->new_records_count > 0) {
/* need to recreate the dir record */
if (mailbox_list_index_sync_recreate_dir(ctx, sync_dir,
offset_pos,
partial) < 0)
return -1;
/* NOTE: index may have been remaped here */
} else if (sync_dir->seen_records_count !=
array_count(&sync_dir->records) && !partial) {
/* just mark the records deleted */
if (mailbox_list_index_sync_update_dir(ctx, sync_dir) < 0)
return -1;
}
if (!partial && (ctx->flags & MAILBOX_LIST_SYNC_FLAG_RECURSIVE) == 0) {
/* we're doing a full sync only for the root */
partial = TRUE;
}
/* update child mailboxes */
sync_recs = array_get(&sync_dir->records, &count);
if (count == 0)
return 0;
i_assert(sync_dir->offset != 0 &&
sync_dir->offset < ctx->index->mmap_size);
for (i = j = 0; i < count; i++) {
if (sync_recs[i].dir == NULL)
continue;
/* these may change after each sync_write_dir() call */
dir = CONST_PTR_OFFSET(ctx->index->mmap_base, sync_dir->offset);
recs = MAILBOX_LIST_RECORDS(dir);
/* child_offset_pos needs to point to record's dir_offset */
for (; j < dir->count; j++) {
if (recs[j].uid == sync_recs[i].uid)
break;
}
i_assert(j < dir->count);
child_offset_pos = (const char *)&recs[j].dir_offset -
(const char *)ctx->index->mmap_base;
if (mailbox_list_index_sync_write_dir(ctx, sync_recs[i].dir,
child_offset_pos,
partial) < 0)
return -1;
}
return 0;
}
static int
mailbox_list_index_sync_write(struct mailbox_list_index_sync_ctx *ctx)
{
struct mailbox_list_index_header *hdr;
bool partial;
if (ctx->sync_root == ctx->root) {
ctx->seen_sync_root = TRUE;
partial = (ctx->flags & MAILBOX_LIST_SYNC_FLAG_PARTIAL) != 0;
} else {
/* until we've seen the sync root, we're doing only partial
syncing */
partial = TRUE;
}
if (mailbox_list_index_sync_write_dir(ctx, ctx->root, 0, partial) < 0)
return -1;
/* update header */
hdr = ctx->index->mmap_base;
hdr->next_uid = ctx->hdr.next_uid;
hdr->used_space = ctx->hdr.used_space;
hdr->deleted_space = ctx->hdr.deleted_space;
if (msync(ctx->index->mmap_base, hdr->used_space, MS_SYNC) < 0) {
mailbox_list_index_set_syscall_error(ctx->index, "msync()");
return -1;
}
return 0;
}
int mailbox_list_index_sync_commit(struct mailbox_list_index_sync_ctx **_ctx)
{
struct mailbox_list_index_sync_ctx *ctx = *_ctx;
int ret = ctx->failed ? -1 : 0;
*_ctx = NULL;
if (!ctx->failed) {
/* write all the changes to the index */
ret = mailbox_list_index_sync_write(ctx);
}
if (ctx->mail_sync_ctx != NULL) {
if (ret < 0)
mail_index_transaction_rollback(&ctx->trans);
else {
uint32_t seq;
uoff_t offset;
if (mail_index_transaction_commit(&ctx->trans,
&seq, &offset) < 0)
ret = -1;
}
if (ret < 0)
mail_index_sync_rollback(&ctx->mail_sync_ctx);
else {
if (mail_index_sync_commit(&ctx->mail_sync_ctx) < 0)
ret = -1;
}
}
pool_unref(ctx->pool);
return ret;
}
void mailbox_list_index_sync_rollback(struct mailbox_list_index_sync_ctx **ctx)
{
(*ctx)->failed = TRUE;
(void)mailbox_list_index_sync_commit(ctx);
}
--- NEW FILE: mailbox-list-index.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "array.h"
#include "crc32.h"
#include "ioloop.h"
#include "str.h"
#include "file-dotlock.h"
#include "mmap-util.h"
#include "write-full.h"
#include "mail-index-private.h"
#include "mailbox-list-index-private.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
struct mailbox_list_iter_path {
const struct mailbox_list_dir_record *dir;
unsigned int pos;
unsigned int name_path_len;
};
struct mailbox_list_iter_ctx {
struct mailbox_list_index *index;
unsigned int recurse_level;
struct mailbox_list_iter_path cur;
ARRAY_DEFINE(path, struct mailbox_list_iter_path);
string_t *name_path;
unsigned int failed:1;
};
const struct dotlock_settings dotlock_set = {
MEMBER(temp_prefix) NULL,
MEMBER(lock_suffix) NULL,
MEMBER(timeout) 60,
MEMBER(stale_timeout) 30,
MEMBER(callback) NULL,
MEMBER(context) NULL,
MEMBER(use_excl_lock) FALSE
};
int mailbox_list_index_set_syscall_error(struct mailbox_list_index *index,
const char *function)
{
i_error("%s failed with file %s: %m", index->filepath, function);
return -1;
}
static void mailbox_list_index_unmap(struct mailbox_list_index *index)
{
if (index->mmap_base != NULL) {
if (munmap(index->mmap_base, index->mmap_size) < 0)
mailbox_list_index_set_syscall_error(index, "munmap()");
index->mmap_base = NULL;
index->mmap_size = 0;
}
index->hdr = NULL;
}
static void mailbox_list_index_file_close(struct mailbox_list_index *index)
{
mailbox_list_index_unmap(index);
if (index->fd != -1) {
if (close(index->fd) < 0)
mailbox_list_index_set_syscall_error(index, "close()");
}
}
int mailbox_list_index_set_corrupted(struct mailbox_list_index *index,
const char *str)
{
(void)unlink(index->filepath);
// FIXME: reopen or something
i_error("Corrupted mailbox list index file %s: %s",
index->filepath, str);
return -1;
}
static int
mailbox_list_index_check_header(struct mailbox_list_index *index,
const struct mailbox_list_index_header *hdr)
{
if (hdr->major_version != MAILBOX_LIST_INDEX_MAJOR_VERSION)
return -1;
if (hdr->header_size < sizeof(*hdr)) {
return mailbox_list_index_set_corrupted(index,
"header_size is too small");
}
if (hdr->header_size > index->mmap_size) {
return mailbox_list_index_set_corrupted(index,
"header_size is too large");
}
if (hdr->uid_validity == 0) {
return mailbox_list_index_set_corrupted(index,
"uid_validity is 0");
}
if (hdr->next_uid == 0)
return mailbox_list_index_set_corrupted(index, "next_uid is 0");
if (hdr->uid_validity != index->mail_index->hdr->uid_validity &&
index->mail_index->hdr->uid_validity != 0) {
mail_index_set_error(index->mail_index,
"uid_validity changed in file %s", index->filepath);
mail_index_mark_corrupted(index->mail_index);
}
return 0;
}
int mailbox_list_index_map(struct mailbox_list_index *index)
{
const struct mailbox_list_index_header *hdr;
mailbox_list_index_unmap(index);
// FIXME: handle non-mmaps
index->mmap_base = mmap_rw_file(index->fd, &index->mmap_size);
if (index->mmap_base == MAP_FAILED) {
index->mmap_base = NULL;
return mailbox_list_index_set_syscall_error(index, "mmap()");
}
if (index->mmap_size < sizeof(*hdr)) {
mailbox_list_index_set_corrupted(index, "File too small");
return 0;
}
hdr = index->mmap_base;
if (mailbox_list_index_check_header(index, hdr) < 0)
return 0;
index->hdr = hdr;
return 1;
}
static void
mailbox_list_index_init_header(struct mailbox_list_index_header *hdr)
{
memset(hdr, 0, sizeof(*hdr));
hdr->major_version = MAILBOX_LIST_INDEX_MAJOR_VERSION;
hdr->minor_version = MAILBOX_LIST_INDEX_MINOR_VERSION;
hdr->header_size = sizeof(*hdr);
hdr->used_space = hdr->header_size;
hdr->uid_validity = ioloop_time;
hdr->next_uid = 1;
}
static int mailbox_list_index_is_recreated(struct mailbox_list_index *index)
{
struct stat st1, st2;
if (stat(index->filepath, &st1) < 0) {
mailbox_list_index_set_syscall_error(index, "stat()");
return -1;
}
if (fstat(index->fd, &st2) < 0) {
mailbox_list_index_set_syscall_error(index, "fstat()");
return -1;
}
return st1.st_ino != st2.st_ino ||
!CMP_DEV_T(st1.st_dev, st2.st_dev);
}
static int
mailbox_list_index_file_create(struct mailbox_list_index *index)
{
struct mailbox_list_index_header hdr;
struct dotlock *dotlock;
int fd, ret;
fd = file_dotlock_open(&dotlock_set, index->filepath, 0, &dotlock);
if (fd == -1) {
mailbox_list_index_set_syscall_error(index,
"file_dotlock_open()");
return -1;
}
if (index->fd != -1) {
/* if the file has been recreated by someone else,
retry opening it */
ret = mailbox_list_index_is_recreated(index);
if (ret != 0) {
(void)file_dotlock_delete(&dotlock);
return ret < 0 ? -1 : 0;
}
}
mailbox_list_index_init_header(&hdr);
if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
mailbox_list_index_set_syscall_error(index, "write_full()");
(void)file_dotlock_delete(&dotlock);
return -1;
}
if (file_dotlock_replace(&dotlock,
DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
mailbox_list_index_set_syscall_error(index,
"file_dotlock_replace()");
(void)close(fd);
return -1;
}
if (index->fd != -1)
mailbox_list_index_file_close(index);
index->fd = fd;
ret = mailbox_list_index_map(index);
if (ret == 0) {
i_error("Self-created mailbox list index file %s was corrupted",
index->filepath);
return -1;
}
return ret;
}
static int
mailbox_list_index_file_try_open_or_create(struct mailbox_list_index *index)
{
int ret;
i_assert(index->fd == -1);
index->fd = open(index->filepath, O_RDWR);
if (index->fd == -1) {
if (errno != ENOENT) {
mailbox_list_index_set_syscall_error(index, "open()");
return -1;
}
} else {
ret = mailbox_list_index_map(index);
if (ret != 0) {
if (ret < 0)
mailbox_list_index_file_close(index);
return ret;
}
}
ret = mailbox_list_index_file_create(index);
if (ret <= 0)
mailbox_list_index_file_close(index);
return ret;
}
int mailbox_list_index_open_or_create(struct mailbox_list_index *index)
{
int ret;
while ((ret = mailbox_list_index_file_try_open_or_create(index)) == 0) {
/* file was recreated by someone else, try reopening */
}
return ret < 0 ? -1 : 0;
}
struct mailbox_list_index *
mailbox_list_index_alloc(const char *path, char separator,
struct mail_index *mail_index)
{
struct mailbox_list_index *index;
index = i_new(struct mailbox_list_index, 1);
index->filepath = i_strdup(path);
index->separator = separator;
index->mail_index = mail_index;
index->fd = -1;
return index;
}
void mailbox_list_index_free(struct mailbox_list_index **_index)
{
struct mailbox_list_index *index = *_index;
*_index = NULL;
i_free(index->filepath);
i_free(index);
}
struct mailbox_list_index_lookup_key {
uint32_t name_hash;
struct mailbox_list_index *index;
const char *name;
bool *failed;
};
static int
mailbox_list_get_name(struct mailbox_list_index *index, pool_t pool,
const struct mailbox_list_record *rec,
const char **name_r)
{
size_t max_len;
const char *name;
if (rec->name_offset >= index->mmap_size) {
mailbox_list_index_set_corrupted(index,
"record name_offset points outside file");
return -1;
}
max_len = index->mmap_size - rec->name_offset;
name = CONST_PTR_OFFSET(index->mmap_base, rec->name_offset);
/* get name length. don't bother checking if it's not NUL-terminated,
because practically it always is even if the file is corrupted.
just make sure we don't crash if it happens. */
*name_r = p_strndup(pool, name, max_len);
return 0;
}
static int mailbox_list_record_cmp(const void *_key, const void *_rec)
{
const struct mailbox_list_index_lookup_key *key = _key;
const struct mailbox_list_record *rec = _rec;
const char *name;
int ret;
if (key->name_hash < rec->name_hash)
return -1;
if (key->name_hash > rec->name_hash)
return 1;
t_push();
if (mailbox_list_get_name(key->index, unsafe_data_stack_pool,
rec, &name) < 0) {
*key->failed = TRUE;
ret = -1;
} else {
ret = strcmp(key->name, name);
}
t_pop();
return ret;
}
int mailbox_list_index_get_dir(struct mailbox_list_index *index,
uint32_t *offset,
const struct mailbox_list_dir_record **dir_r)
{
const struct mailbox_list_dir_record *dir;
uint32_t next_offset, cur_offset = *offset;
i_assert(index->mmap_size > 0);
do {
if (cur_offset >= index->mmap_size - sizeof(*dir)) {
return mailbox_list_index_set_corrupted(index,
"dir_offset points outside file");
}
if ((cur_offset % 4) != 0) {
return mailbox_list_index_set_corrupted(index,
"dir_offset not 32bit aligned");
}
dir = CONST_PTR_OFFSET(index->mmap_base, cur_offset);
next_offset = mail_index_offset_to_uint32(dir->next_offset);
if (next_offset != 0 && next_offset <= cur_offset) {
return mailbox_list_index_set_corrupted(index,
"next_offset points backwards");
}
cur_offset = next_offset;
} while (cur_offset != 0);
cur_offset = (const char *)dir - (const char *)index->mmap_base;
if (dir->count > INT_MAX/sizeof(struct mailbox_list_record) ||
dir->count * sizeof(struct mailbox_list_record) >
index->mmap_size - cur_offset) {
mailbox_list_index_set_corrupted(index, "dir count too large");
return -1;
}
*offset = cur_offset;
*dir_r = dir;
return 0;
}
int mailbox_list_index_dir_lookup_rec(struct mailbox_list_index *index,
const struct mailbox_list_dir_record *dir,
const char *name,
const struct mailbox_list_record **rec_r)
{
const struct mailbox_list_record *rec;
struct mailbox_list_index_lookup_key key;
bool failed = FALSE;
/* binary search the current hierarchy level name. the values are
sorted primarily by their hash value and secondarily by the actual
name */
memset(&key, 0, sizeof(key));
key.index = index;
key.name = name;
key.name_hash = crc32_str(name);
key.failed = &failed;
rec = bsearch(&key, MAILBOX_LIST_RECORDS(dir), dir->count, sizeof(*rec),
mailbox_list_record_cmp);
if (failed)
return -1;
if (rec == NULL)
return 0;
*rec_r = rec;
return 1;
}
static int
mailbox_list_index_lookup_rec(struct mailbox_list_index *index,
uint32_t dir_offset, const char *name,
const struct mailbox_list_record **rec_r)
{
const struct mailbox_list_dir_record *dir;
const char *p, *hier_name;
int ret;
if (dir_offset == index->mmap_size &&
dir_offset == sizeof(*index->hdr)) {
/* root doesn't exist in the file yet */
return 0;
}
if (mailbox_list_index_get_dir(index, &dir_offset, &dir) < 0)
return -1;
p = strchr(name, index->separator);
hier_name = p == NULL ? name : t_strdup_until(name, p);
ret = mailbox_list_index_dir_lookup_rec(index, dir, hier_name, rec_r);
if (ret <= 0)
return ret;
if (p == NULL) {
/* found it */
return 1;
}
/* recurse to children */
dir_offset = mail_index_offset_to_uint32((*rec_r)->dir_offset);
if (dir_offset == 0)
return 0;
return mailbox_list_index_lookup_rec(index, dir_offset, p + 1, rec_r);
}
static int mailbox_list_index_refresh(struct mailbox_list_index *index)
{
int ret;
if ((ret = mailbox_list_index_is_recreated(index)) <= 0)
return ret;
mailbox_list_index_file_close(index);
return mailbox_list_index_open_or_create(index);
}
int mailbox_list_index_lookup(struct mailbox_list_index *index,
const char *name, uint32_t *uid_r)
{
const struct mailbox_list_record *rec;
uint32_t offset = sizeof(*index->hdr);
int ret;
ret = mailbox_list_index_lookup_rec(index, offset, name, &rec);
if (ret == 0) {
/* not found, see if it's found after a refresh */
if ((ret = mailbox_list_index_refresh(index)) <= 0)
return ret;
ret = mailbox_list_index_lookup_rec(index, offset, name, &rec);
}
*uid_r = ret <= 0 ? 0 : rec->uid;
return ret;
}
struct mailbox_list_iter_ctx *
mailbox_list_index_iterate_init(struct mailbox_list_index *index,
const char *path, int recurse_level)
{
struct mailbox_list_iter_ctx *ctx;
const struct mailbox_list_record *rec;
uint32_t offset = sizeof(*index->hdr);
int ret;
ctx = i_new(struct mailbox_list_iter_ctx, 1);
ctx->index = index;
ctx->recurse_level = recurse_level < 0 ? (unsigned int)-1 :
(unsigned int)recurse_level;
ctx->name_path = str_new(default_pool, 512);
if (*path != '\0') {
ret = mailbox_list_index_lookup_rec(index, offset, path, &rec);
if (ret < 0)
ctx->failed = TRUE;
else {
offset = ret == 0 ? 0 :
mail_index_offset_to_uint32(rec->dir_offset);
}
}
if (!ctx->failed && offset != 0) {
if (mailbox_list_index_get_dir(index, &offset,
&ctx->cur.dir) < 0)
ctx->failed = TRUE;
}
i_array_init(&ctx->path, I_MIN(ctx->recurse_level, 16));
return ctx;
}
int mailbox_list_index_iterate_next(struct mailbox_list_iter_ctx *ctx,
struct mailbox_list_index_info *info_r)
{
const struct mailbox_list_iter_path *cur;
const struct mailbox_list_record *recs;
const char *name;
uint32_t dir_offset;
unsigned int count;
if (ctx->failed)
return -1;
if (ctx->cur.dir == NULL) {
/* no mailboxes */
i_assert(array_count(&ctx->path) == 0);
return 0;
}
while (ctx->cur.pos == ctx->cur.dir->count) {
count = array_count(&ctx->path);
if (count == 0) {
/* we're done */
return 0;
}
/* go back to parent path */
cur = array_idx(&ctx->path, count-1);
ctx->cur = *cur;
array_delete(&ctx->path, count-1, 1);
ctx->cur.pos++;
}
recs = MAILBOX_LIST_RECORDS(ctx->cur.dir);
recs += ctx->cur.pos;
if (recs->deleted) {
ctx->cur.pos++;
return mailbox_list_index_iterate_next(ctx, info_r);
}
t_push();
if (mailbox_list_get_name(ctx->index, unsafe_data_stack_pool,
recs, &name) < 0) {
ctx->failed = TRUE;
t_pop();
return -1;
}
str_truncate(ctx->name_path, ctx->cur.name_path_len);
if (ctx->cur.name_path_len > 0)
str_append_c(ctx->name_path, ctx->index->separator);
str_append(ctx->name_path, name);
t_pop();
dir_offset = mail_index_offset_to_uint32(recs->dir_offset);
if (dir_offset != 0 && array_count(&ctx->path) < ctx->recurse_level) {
/* recurse into children */
array_append(&ctx->path, &ctx->cur, 1);
ctx->cur.name_path_len = str_len(ctx->name_path);
ctx->cur.pos = 0;
if (mailbox_list_index_get_dir(ctx->index, &dir_offset,
&ctx->cur.dir) < 0) {
ctx->failed = TRUE;
return -1;
}
} else {
ctx->cur.pos++;
}
info_r->name = str_c(ctx->name_path);
info_r->uid = recs->uid;
info_r->has_children = dir_offset != 0;
return 1;
}
void mailbox_list_index_iterate_deinit(struct mailbox_list_iter_ctx **_ctx)
{
struct mailbox_list_iter_ctx *ctx = *_ctx;
*_ctx = NULL;
array_free(&ctx->path);
str_free(&ctx->name_path);
i_free(ctx);
}
--- NEW FILE: mailbox-list-index.h ---
#ifndef __MAILBOX_LIST_INDEX_H
#define __MAILBOX_LIST_INDEX_H
struct mailbox_list_index_sync_ctx;
/* Mailbox list index contains UID <-> mailbox name mapping. It also takes in
a mail_index index which contains UID -> metadata information for the
mailboxes. The mmap, in-memory and lock settings are taken from the
mail_index. */
enum mailbox_list_index_flags {
/* Mailbox has children. They may not be indexed however, so
mailbox_list_index_info.has_children=FALSE is possible. */
MAILBOX_LIST_INDEX_FLAG_CHILDREN = 0x01,
/* Mailbox has no children. mailbox_list_index_info.has_children
should be FALSE. */
MAILBOX_LIST_INDEX_FLAG_NOCHILDREN = 0x02,
/* The mailbox isn't selectable (eg. a directory) */
MAILBOX_LIST_INDEX_FLAG_NOSELECT = 0x04,
/* The mailbox doesn't exist at all. This is only a placeholder for
a child mailbox. When the children are deleted, this mailbox will
be automatically deleted as well. */
MAILBOX_LIST_INDEX_FLAG_NONEXISTENT = 0x08
};
enum mailbox_list_sync_flags {
/* All the child mailboxes are also being synced */
MAILBOX_LIST_SYNC_FLAG_RECURSIVE = 0x01,
/* New mailboxes may be added, but none are removed */
MAILBOX_LIST_SYNC_FLAG_PARTIAL = 0x02
};
struct mailbox_list_index_info {
const char *name;
uint32_t uid;
bool has_children;
};
struct mailbox_list_index *
mailbox_list_index_alloc(const char *path, char separator,
struct mail_index *mail_index);
void mailbox_list_index_free(struct mailbox_list_index **index);
/* Open or create mailbox list index. */
int mailbox_list_index_open_or_create(struct mailbox_list_index *index);
/* Synchronize the index with the backend. */
int mailbox_list_index_sync_init(struct mailbox_list_index *index,
const char *path,
enum mailbox_list_sync_flags flags,
struct mailbox_list_index_sync_ctx **ctx_r);
struct mail_index_view *
mailbox_list_index_sync_get_view(struct mailbox_list_index_sync_ctx *ctx);
struct mail_index_transaction *
mailbox_list_index_sync_get_transaction(struct mailbox_list_index_sync_ctx*ctx);
int mailbox_list_index_sync_more(struct mailbox_list_index_sync_ctx *ctx,
const char *name, uint32_t *seq_r);
int mailbox_list_index_sync_commit(struct mailbox_list_index_sync_ctx **ctx);
void mailbox_list_index_sync_rollback(struct mailbox_list_index_sync_ctx **ctx);
/* Get mailbox UID for a given name. Returns 1 if found, 0 if not,
-1 if error */
int mailbox_list_index_lookup(struct mailbox_list_index *index,
const char *name, uint32_t *uid_r);
/* Iterate through all the mailboxes. If recurse_level is -1, all the child
mailboxes are returned, otherwise it's the number of levels to return
(0 = only the mailboxes directly under the path). Returned mailbox names
are allocated from name_pool. */
struct mailbox_list_iter_ctx *
mailbox_list_index_iterate_init(struct mailbox_list_index *index,
const char *path, int recurse_level);
/* Returns 1 if mailbox was returned, 0 at the end of iteration, -1 if error */
int mailbox_list_index_iterate_next(struct mailbox_list_iter_ctx *ctx,
struct mailbox_list_index_info *info_r);
void mailbox_list_index_iterate_deinit(struct mailbox_list_iter_ctx **ctx);
int mailbox_list_index_set_corrupted(struct mailbox_list_index *index,
const char *str);
#endif
- Previous message: [dovecot-cvs] dovecot/src/imap Makefile.am, 1.36, 1.37 cmd-list.c, 1.58, 1.59 common.h, 1.22, 1.23 imap-sync.c, 1.20, 1.21 imap-thread.c, 1.28, 1.29 main.c, 1.82, 1.83 namespace.c, 1.16, 1.17
- Next message: [dovecot-cvs] dovecot/src/lib-storage/index index-status.c, 1.37, 1.38 index-storage.c, 1.91, 1.92 index-storage.h, 1.106, 1.107 index-sync.c, 1.58, 1.59
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the dovecot-cvs
mailing list