Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-storage/list/index-mailbox-list.c @ 6275:913b188f4dd4 HEAD
Removed explicit locking from views and maps. They were already locked all
the time when they were used. Because of this change several functions can
no longer fail, so they were changed to return void, and a lot of pointless
error handling was removed.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 12 Aug 2007 16:43:05 +0300 |
parents | 813c976ed476 |
children | 5f66277bbe40 |
line wrap: on
line source
/* Copyright (C) 2006-2007 Timo Sirainen */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "file-lock.h" #include "imap-match.h" #include "mail-index.h" #include "mail-storage.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-index.h" #include "index-mailbox-list.h" #include <stdlib.h> #include <time.h> #include <sys/stat.h> /* min 2 seconds */ #define MAILBOX_LIST_SYNC_SECS 2 struct index_mailbox_list_module index_mailbox_list_module = MODULE_CONTEXT_INIT(&mailbox_list_module_register); static void (*index_next_hook_mailbox_list_created)(struct mailbox_list *list); static enum mailbox_info_flags index_mailbox_list_index_flags_translate(enum mailbox_list_index_flags flags) { enum mailbox_info_flags info_flags = 0; if ((flags & MAILBOX_LIST_INDEX_FLAG_CHILDREN) != 0) info_flags |= MAILBOX_CHILDREN; if ((flags & MAILBOX_LIST_INDEX_FLAG_NOCHILDREN) != 0) info_flags |= MAILBOX_NOCHILDREN; if ((flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) info_flags |= MAILBOX_NONEXISTENT; if ((flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) info_flags |= MAILBOX_NOSELECT; return info_flags; } static enum mailbox_list_index_flags index_mailbox_list_info_flags_translate(enum mailbox_info_flags info_flags) { enum mailbox_list_index_flags flags = 0; if ((info_flags & MAILBOX_CHILDREN) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_CHILDREN; else if ((info_flags & MAILBOX_NOCHILDREN) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NOCHILDREN; if ((info_flags & MAILBOX_NONEXISTENT) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NONEXISTENT; if ((info_flags & MAILBOX_NOSELECT) != 0) flags |= MAILBOX_LIST_INDEX_FLAG_NOSELECT; return flags; } static int index_mailbox_list_is_synced(struct index_mailbox_list_iterate_context *ctx) { const struct mail_index_header *hdr; struct stat st; const char *path = ctx->ctx.list->set.root_dir; if (ctx->view == NULL) { /* uid_validity changed */ return 0; } /* FIXME: single sync_stamp works only with maildir++ */ if (stat(path, &st) < 0) { mailbox_list_set_critical(ctx->ctx.list, "stat(%s) failed: %m", path); return -1; } /* if mtime is older than 2 secs, we set the first bit on if mtime is 0-2 secs old, we set the first bit off. this way we'll always do a resync later when syncing a recently changed directory. if the directory changes while we're syncing it we'll resync it again later. this would work with 1 second difference if we didn't store the dirtyness flag in the stamp's first bit. */ if (st.st_mtime < ioloop_time - MAILBOX_LIST_SYNC_SECS) st.st_mtime |= 1; else st.st_mtime &= ~1; ctx->sync_stamp = st.st_mtime; hdr = mail_index_get_header(ctx->mail_view); return hdr->sync_stamp == ctx->sync_stamp; } static void pattern_parse(struct mailbox_list *list, const char *pattern, const char **prefix_r, int *recurse_level_r) { char sep = list->hierarchy_sep; const char *prefix_start, *prefix_end; bool seen_wildcards = FALSE; int recurse_level = 0; prefix_start = prefix_end = pattern; for (; *pattern != '\0'; pattern++) { if (*pattern == '%') seen_wildcards = TRUE; else if (*pattern == '*') { recurse_level = -1; break; } if (*pattern == sep) { if (!seen_wildcards) prefix_end = pattern; recurse_level++; } } *prefix_r = prefix_start == prefix_end ? "" : t_strdup_until(prefix_start, prefix_end); *recurse_level_r = recurse_level; } static int index_mailbox_list_sync(struct index_mailbox_list_iterate_context *ctx) { struct mailbox_list *list = ctx->ctx.list; struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_iterate_context *iter; struct mailbox_list_index_sync_ctx *sync_ctx; const struct mailbox_info *info; enum mailbox_list_sync_flags sync_flags; enum mailbox_list_index_flags flags; const char *patterns[2]; uint32_t seq; int ret = 0; /* FIXME: this works nicely with maildir++, but not others */ sync_flags = MAILBOX_LIST_SYNC_FLAG_RECURSIVE; patterns[0] = "*"; patterns[1] = NULL; if (mailbox_list_index_sync_init(ilist->list_index, "", sync_flags, &sync_ctx) < 0) return -1; ctx->trans = mailbox_list_index_sync_get_transaction(sync_ctx); iter = ilist->module_ctx.super. iter_init(list, patterns, MAILBOX_LIST_ITER_RETURN_CHILDREN); while ((info = ilist->module_ctx.super.iter_next(iter)) != NULL) { if (mailbox_list_index_sync_more(sync_ctx, info->name, &seq) < 0) { ret = -1; break; } flags = index_mailbox_list_info_flags_translate(info->flags); mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE, flags); } if (ilist->module_ctx.super.iter_deinit(iter) < 0) ret = -1; if (ret < 0) { mailbox_list_index_sync_rollback(&sync_ctx); return -1; } /* FIXME: single sync_stamp works only with maildir++ */ mail_index_update_header(ctx->trans, offsetof(struct mail_index_header, sync_stamp), &ctx->sync_stamp, sizeof(ctx->sync_stamp), TRUE); return mailbox_list_index_sync_commit(&sync_ctx); } static bool index_mailbox_list_iter_init_try(struct index_mailbox_list_iterate_context *ctx, const char *const *patterns) { struct mailbox_list *list = ctx->ctx.list; enum mailbox_list_iter_flags flags = ctx->ctx.flags; struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); const char *prefix, *cur_prefix, *const *tmp; enum mailbox_list_iter_flags subs_flags; int cur_recurse_level; subs_flags = MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_NO_FLAGS; if ((flags & MAILBOX_LIST_ITER_RAW_LIST) != 0 || (flags & (subs_flags | MAILBOX_LIST_ITER_RETURN_CHILDREN)) == subs_flags) { /* Ignore indexes completely */ return FALSE; } ctx->glob = imap_match_init_multiple(default_pool, patterns, TRUE, list->hierarchy_sep); if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { /* we'll need to know the subscriptions */ ctx->subs_tree = mailbox_tree_init(list->hierarchy_sep); if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->subs_tree, ctx->glob, FALSE) < 0) { /* let the backend handle this failure */ return FALSE; } } /* Refresh index before opening our view */ if (mail_index_refresh(ilist->mail_index) < 0) return FALSE; ctx->mail_view = mail_index_view_open(ilist->mail_index); if (mailbox_list_index_view_init(ilist->list_index, ctx->mail_view, &ctx->view) < 0) ctx->view = NULL; /* FIXME: we could just do multiple lookups for different patterns */ prefix = NULL; for (tmp = patterns; *tmp != NULL; tmp++) { pattern_parse(list, *tmp, &cur_prefix, &cur_recurse_level); if (prefix != NULL && strcmp(prefix, cur_prefix) != 0) prefix = ""; if (cur_recurse_level > ctx->recurse_level || cur_recurse_level == -1) ctx->recurse_level = cur_recurse_level; } if (prefix == NULL) prefix = ""; if (index_mailbox_list_is_synced(ctx) <= 0) { if (index_mailbox_list_sync(ctx) < 0) return FALSE; /* updated, we'll have to reopen views */ mail_index_view_close(&ctx->mail_view); if (ctx->view != NULL) mailbox_list_index_view_deinit(&ctx->view); ctx->mail_view = mail_index_view_open(ilist->mail_index); if (mailbox_list_index_view_init(ilist->list_index, ctx->mail_view, &ctx->view) < 0) return FALSE; } if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { ctx->subs_iter = mailbox_tree_iterate_init(ctx->subs_tree, NULL, MAILBOX_MATCHED); } else { /* list from index */ ctx->info_pool = pool_alloconly_create("mailbox name pool", 256); ctx->iter_ctx = mailbox_list_index_iterate_init(ctx->view, prefix, ctx->recurse_level); ctx->prefix = *prefix == '\0' ? i_strdup(ctx->ns_prefix) : i_strdup_printf("%s%s%c", ctx->ns_prefix, prefix, list->hierarchy_sep); } return TRUE; } static struct mailbox_list_iterate_context * index_mailbox_list_iter_init(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); struct index_mailbox_list_iterate_context *ctx; ctx = i_new(struct index_mailbox_list_iterate_context, 1); ctx->ctx.list = list; ctx->ctx.flags = flags; ctx->ns_prefix = list->ns->prefix; ctx->ns_prefix_len = strlen(ctx->ns_prefix); if (!index_mailbox_list_iter_init_try(ctx, patterns)) { /* no indexing */ ctx->backend_ctx = ilist->module_ctx.super. iter_init(list, patterns, flags); } return &ctx->ctx; } static int list_index_get_info_flags(struct index_mailbox_list_iterate_context *ctx, uint32_t uid, enum mailbox_info_flags *flags_r) { struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(ctx->ctx.list); const struct mail_index_record *rec; uint32_t seq; mail_index_lookup_uid_range(ctx->mail_view, uid, uid, &seq, &seq); if (seq == 0) { i_error("Mailbox list index desynced: " "Record uid=%u expunged from mail index", uid); mail_index_mark_corrupted(ilist->mail_index); return -1; } if (mail_index_lookup(ctx->mail_view, seq, &rec) < 0) return -1; *flags_r = index_mailbox_list_index_flags_translate(rec->flags); return 0; } static int list_index_iter_next(struct index_mailbox_list_iterate_context *ctx, const struct mailbox_info **info_r) { struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(ctx->ctx.list); struct mailbox_list_index_info iinfo; struct mailbox_node *subs_node; int ret; /* find the next matching mailbox */ for (;;) { p_clear(ctx->info_pool); ret = mailbox_list_index_iterate_next(ctx->iter_ctx, &iinfo); if (ret <= 0) { *info_r = NULL; return ret; } ctx->info.name = *ctx->prefix == '\0' ? iinfo.name : p_strconcat(ctx->info_pool, ctx->prefix, iinfo.name, NULL); if (imap_match(ctx->glob, ctx->info.name) != IMAP_MATCH_YES) continue; if (list_index_get_info_flags(ctx, iinfo.uid, &ctx->info.flags) < 0) return -1; if ((ctx->info.flags & MAILBOX_NOCHILDREN) != 0 && iinfo.has_children) { i_error("Mailbox list index desynced: " "Children flags for uid=%u wrong in mail index", iinfo.uid); mail_index_mark_corrupted(ilist->mail_index); return -1; } /* skip nonexistent mailboxes when finding with "*" */ if ((ctx->info.flags & MAILBOX_NONEXISTENT) != 0 && ctx->recurse_level < 0) continue; if (ctx->subs_tree != NULL) { /* get subscription states */ subs_node = mailbox_tree_lookup(ctx->subs_tree, ctx->info.name); if (subs_node != NULL) { ctx->info.flags |= subs_node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); } } *info_r = &ctx->info; return 0; } } static const struct mailbox_info * index_mailbox_list_iter_next(struct mailbox_list_iterate_context *_ctx) { struct index_mailbox_list_iterate_context *ctx = (struct index_mailbox_list_iterate_context *)_ctx; struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(_ctx->list); const struct mailbox_info *info; struct mailbox_node *subs_node; const char *index_name; uint32_t uid; if (ctx->iter_ctx != NULL) { /* listing mailboxes from index */ if (list_index_iter_next(ctx, &info) < 0) { ctx->failed = TRUE; return NULL; } return info; } else if (ctx->backend_ctx != NULL) { /* index isn't being used */ return ilist->module_ctx.super.iter_next(ctx->backend_ctx); } /* listing subscriptions, but we also want flags */ subs_node = mailbox_tree_iterate_next(ctx->subs_iter, &ctx->info.name); if (subs_node == NULL) return NULL; index_name = ctx->info.name; if (ctx->ns_prefix_len > 0 && strncmp(ctx->info.name, ctx->ns_prefix, ctx->ns_prefix_len) == 0) index_name += ctx->ns_prefix_len; if (mailbox_list_index_lookup(ctx->view, index_name, &uid) < 0 || list_index_get_info_flags(ctx, uid, &ctx->info.flags) < 0) { ctx->failed = TRUE; return NULL; } ctx->info.flags |= subs_node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); return &ctx->info; } static int index_mailbox_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct index_mailbox_list_iterate_context *ctx = (struct index_mailbox_list_iterate_context *)_ctx; struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(_ctx->list); int ret = ctx->failed ? -1 : 0; if (ctx->subs_iter != NULL) mailbox_tree_iterate_deinit(&ctx->subs_iter); if (ctx->iter_ctx != NULL) mailbox_list_index_iterate_deinit(&ctx->iter_ctx); if (ctx->info_pool != NULL) pool_unref(ctx->info_pool); if (ctx->mail_view != NULL) mail_index_view_close(&ctx->mail_view); if (ctx->view != NULL) mailbox_list_index_view_deinit(&ctx->view); if (ctx->backend_ctx != NULL) ret = ilist->module_ctx.super.iter_deinit(ctx->backend_ctx); if (ctx->glob != NULL) imap_match_deinit(&ctx->glob); i_free(ctx->prefix); i_free(ctx); return ret; } static void index_mailbox_list_deinit(struct mailbox_list *list) { struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_index_free(&ilist->list_index); mailbox_list_index_view_deinit(&ilist->list_sync_view); mail_index_free(&ilist->mail_index); ilist->module_ctx.super.deinit(list); } static int index_mailbox_list_open_indexes(struct mailbox_list *list, const char *dir) { struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list); const char *path; enum mail_index_open_flags index_flags; enum mail_storage_flags storage_flags; int ret; /* FIXME: a bit ugly way to get the flags, but this will do for now.. */ index_flags = MAIL_INDEX_OPEN_FLAG_CREATE; storage_flags = *list->set.mail_storage_flags; #ifndef MMAP_CONFLICTS_WRITE if ((storage_flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0) #endif index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE; if (mail_index_open(ilist->mail_index, index_flags, *list->set.lock_method) < 0) { if (mail_index_move_to_memory(ilist->mail_index) < 0) { /* try opening once more. it should be created directly into memory now. */ ret = mail_index_open(ilist->mail_index, index_flags, *list->set.lock_method); if (ret <= 0) { /* everything failed. there's a bug in the code, but just work around it by disabling the index completely */ return -1; } } } path = t_strconcat(dir, "/"MAILBOX_LIST_INDEX_NAME, NULL); ilist->list_index = mailbox_list_index_alloc(path, list->hierarchy_sep, ilist->mail_index); if (mailbox_list_index_open_or_create(ilist->list_index) < 0) { /* skip indexing */ mailbox_list_index_free(&ilist->list_index); return -1; } if (mailbox_list_index_view_init(ilist->list_index, NULL, &ilist->list_sync_view) < 0) { mailbox_list_index_free(&ilist->list_index); return -1; } return 0; } static void index_mailbox_list_created(struct mailbox_list *list) { struct index_mailbox_list *ilist = NULL; const char *dir; /* FIXME: for now we only work with maildir++ */ dir = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX); if (*dir == '\0' || getenv("MAILBOX_LIST_INDEX_DISABLE") != NULL || strcmp(list->name, "maildir++") != 0) { /* reserve the module context anyway, so syncing code knows that the index is disabled */ MODULE_CONTEXT_SET(list, index_mailbox_list_module, ilist); return; } ilist = p_new(list->pool, struct index_mailbox_list, 1); ilist->module_ctx.super = list->v; list->v.deinit = index_mailbox_list_deinit; list->v.iter_init = index_mailbox_list_iter_init; list->v.iter_deinit = index_mailbox_list_iter_deinit; list->v.iter_next = index_mailbox_list_iter_next; MODULE_CONTEXT_SET(list, index_mailbox_list_module, ilist); ilist->mail_index = mail_index_alloc(dir, MAIL_INDEX_PREFIX); /* sync_init allocates the extensions. do it here before opening the index files, so that our initial memory pool size guesses are a bit more optimal */ index_mailbox_list_sync_init_list(list); if (index_mailbox_list_open_indexes(list, dir) < 0) { list->v = ilist->module_ctx.super; mail_index_free(&ilist->mail_index); MODULE_CONTEXT_UNSET(list, index_mailbox_list_module); } } void index_mailbox_list_init(void); /* called in mailbox-list-register.c */ void index_mailbox_list_init(void) { index_next_hook_mailbox_list_created = hook_mailbox_list_created; hook_mailbox_list_created = index_mailbox_list_created; index_mailbox_list_sync_init(); }