Mercurial > dovecot > core-2.2
view src/lib-storage/list/mailbox-list-index.c @ 13589:75679aca405a
mailbox list indexes: Error handling fixes.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 02 Oct 2011 17:48:23 +0300 |
parents | 940ddec22822 |
children | 6912969d94e8 |
line wrap: on
line source
/* Copyright (c) 2006-2011 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hash.h" #include "mail-index.h" #include "mail-storage-hooks.h" #include "mailbox-list-index.h" struct mailbox_list_index_module mailbox_list_index_module = MODULE_CONTEXT_INIT(&mailbox_list_module_register); void mailbox_list_index_set_index_error(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_set_internal_error(list); mail_index_reset_error(ilist->index); } void mailbox_list_index_reset(struct mailbox_list_index *ilist) { hash_table_clear(ilist->mailbox_names, FALSE); hash_table_clear(ilist->mailbox_hash, FALSE); p_clear(ilist->mailbox_pool); ilist->mailbox_tree = NULL; ilist->highest_name_id = 0; ilist->sync_log_file_seq = 0; ilist->sync_log_file_offset = 0; } struct mailbox_list_index_node * mailbox_list_index_node_find_sibling(struct mailbox_list_index_node *node, const char *name) { while (node != NULL) { if (strcmp(node->name, name) == 0) return node; node = node->next; } return NULL; } static struct mailbox_list_index_node * mailbox_list_index_lookup_real(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_node *node; const char *const *path; unsigned int i; char sep[2]; (void)mailbox_list_index_refresh(list); sep[0] = mailbox_list_get_hierarchy_sep(list); sep[1] = '\0'; path = t_strsplit(name, sep); node = ilist->mailbox_tree; for (i = 0;; i++) { node = mailbox_list_index_node_find_sibling(node, path[i]); if (node == NULL || path[i+1] == NULL) break; node = node->children; } return node; } struct mailbox_list_index_node * mailbox_list_index_lookup(struct mailbox_list *list, const char *name) { struct mailbox_list_index_node *node; T_BEGIN { node = mailbox_list_index_lookup_real(list, name); } T_END; return node; } static int mailbox_list_index_parse_header(struct mailbox_list_index *ilist, struct mail_index_view *view) { const struct mailbox_list_index_header *hdr; const void *data, *p; size_t i, len, size; uint32_t id, prev_id = 0; char *name; mail_index_get_header_ext(view, ilist->ext_id, &data, &size); if (size == 0) return 0; hdr = data; for (i = sizeof(*hdr); i < size; ) { /* get id */ if (i + sizeof(id) > size) return -1; memcpy(&id, CONST_PTR_OFFSET(data, i), sizeof(id)); i += sizeof(id); if (id <= prev_id) { /* allow extra space in the end as long as last id=0 */ return id == 0 ? 0 : -1; } /* get name */ p = memchr(CONST_PTR_OFFSET(data, i), '\0', size-i); if (p == NULL) return -1; len = (const char *)p - (const char *)(CONST_PTR_OFFSET(data, i)); name = p_strndup(ilist->mailbox_pool, CONST_PTR_OFFSET(data, i), len); i += len + 1; /* add id => name to hash table */ hash_table_insert(ilist->mailbox_names, POINTER_CAST(id), name); ilist->highest_name_id = id; } i_assert(i == size); return 0; } static int mailbox_list_index_parse_records(struct mailbox_list_index *ilist, struct mail_index_view *view) { struct mailbox_list_index_node *node; const struct mail_index_record *rec; const struct mailbox_list_index_record *irec; const void *data; bool expunged; uint32_t seq, count; count = mail_index_view_get_messages_count(view); for (seq = 1; seq <= count; seq++) { node = p_new(ilist->mailbox_pool, struct mailbox_list_index_node, 1); rec = mail_index_lookup(view, seq); node->uid = rec->uid; node->flags = rec->flags; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); if (data == NULL) return -1; irec = data; node->name_id = irec->name_id; node->name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(irec->name_id)); if (node->name == NULL) return -1; if (irec->parent_uid != 0) { node->parent = hash_table_lookup(ilist->mailbox_hash, POINTER_CAST(irec->parent_uid)); if (node->parent == NULL) return -1; node->next = node->parent->children; node->parent->children = node; } else { node->next = ilist->mailbox_tree; ilist->mailbox_tree = node; } hash_table_insert(ilist->mailbox_hash, POINTER_CAST(node->uid), node); } return 0; } int mailbox_list_index_parse(struct mailbox_list_index *ilist, struct mail_index_view *view, bool force) { const struct mail_index_header *hdr; int ret; hdr = mail_index_get_header(view); if (!force && hdr->log_file_seq == ilist->sync_log_file_seq && hdr->log_file_head_offset == ilist->sync_log_file_offset) { /* nothing changed */ return 0; } mailbox_list_index_reset(ilist); ilist->sync_log_file_seq = hdr->log_file_seq; ilist->sync_log_file_offset = hdr->log_file_head_offset; ret = mailbox_list_index_parse_header(ilist, view); if (ret == 0) ret = mailbox_list_index_parse_records(ilist, view); if (ret < 0) { i_error("Corrupted mailbox list index %s", ilist->path); mail_index_mark_corrupted(ilist->index); return -1; } return 0; } static bool mailbox_list_index_need_refresh(struct mailbox_list_index *ilist, struct mail_index_view *view) { const struct mailbox_list_index_header *hdr; const void *data; size_t size; mail_index_get_header_ext(view, ilist->ext_id, &data, &size); hdr = data; return hdr != NULL && hdr->refresh_flag != 0; } int mailbox_list_index_refresh(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mail_index_view *view; int ret; if (ilist->iter_refcount > 0) { /* someone's already iterating. don't break them. */ return 0; } if (mail_index_refresh(ilist->index) < 0) { mailbox_list_index_set_index_error(list); return -1; } view = mail_index_view_open(ilist->index); if (ilist->mailbox_tree == NULL || mailbox_list_index_need_refresh(ilist, view)) { /* refresh list of mailboxes */ ret = mailbox_list_index_sync(list); } else { ret = mailbox_list_index_parse(ilist, view, FALSE); } mail_index_view_close(&view); return ret; } void mailbox_list_index_refresh_later(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_header new_hdr; struct mail_index_view *view; struct mail_index_transaction *trans; view = mail_index_view_open(ilist->index); if (!mailbox_list_index_need_refresh(ilist, view)) { new_hdr.refresh_flag = 1; trans = mail_index_transaction_begin(view, MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL); mail_index_update_header_ext(trans, ilist->ext_id, offsetof(struct mailbox_list_index_header, refresh_flag), &new_hdr.refresh_flag, sizeof(new_hdr.refresh_flag)); if (mail_index_transaction_commit(&trans) < 0) mail_index_mark_corrupted(ilist->index); } mail_index_view_close(&view); } static void mailbox_list_index_deinit(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); hash_table_destroy(&ilist->mailbox_hash); hash_table_destroy(&ilist->mailbox_names); pool_unref(&ilist->mailbox_pool); mail_index_close(ilist->index); mail_index_free(&ilist->index); ilist->module_ctx.super.deinit(list); } static int mailbox_list_index_index_open(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const struct mail_storage_settings *set = list->mail_set; enum mail_index_open_flags index_flags; unsigned int lock_timeout; index_flags = mail_storage_settings_to_index_flags(set); lock_timeout = set->mail_max_lock_timeout == 0 ? -1U : set->mail_max_lock_timeout; mail_index_set_lock_method(ilist->index, set->parsed_lock_method, lock_timeout); if (mail_index_open_or_create(ilist->index, index_flags) < 0) { if (mail_index_move_to_memory(ilist->index) < 0) { /* try opening once more. it should be created directly into memory now. */ if (mail_index_open_or_create(ilist->index, index_flags) < 0) i_panic("in-memory index creation failed"); } } return 0; } static int mailbox_list_index_create_mailbox_dir(struct mailbox_list *list, const char *name, enum mailbox_dir_create_type type) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_index_refresh_later(list); return ilist->module_ctx.super.create_mailbox_dir(list, name, type); } static int mailbox_list_index_delete_mailbox(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_index_refresh_later(list); return ilist->module_ctx.super.delete_mailbox(list, name); } static int mailbox_list_index_delete_dir(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); mailbox_list_index_refresh_later(list); return ilist->module_ctx.super.delete_dir(list, name); } static int mailbox_list_index_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children) { struct mailbox_list_index *oldilist = INDEX_LIST_CONTEXT(oldlist); mailbox_list_index_refresh_later(oldlist); if (oldlist != newlist) mailbox_list_index_refresh_later(newlist); return oldilist->module_ctx.super. rename_mailbox(oldlist, oldname, newlist, newname, rename_children); } static void mailbox_list_index_created(struct mailbox_list *list) { struct mailbox_list_index *ilist; const char *dir; dir = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX); if (!list->mail_set->mailbox_list_index) { /* reserve the module context anyway, so syncing code knows that the index is disabled */ ilist = NULL; MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist); return; } if (*dir == '\0') { /* in-memory indexes */ dir = NULL; } else if (list->ns->type != NAMESPACE_PRIVATE) { /* don't create index files for shared/public mailboxes. their indexes may be shared between multiple users, each of which may have different ACLs */ dir = NULL; } ilist = p_new(list->pool, struct mailbox_list_index, 1); ilist->module_ctx.super = list->v; list->v.deinit = mailbox_list_index_deinit; list->v.iter_init = mailbox_list_index_iter_init; list->v.iter_deinit = mailbox_list_index_iter_deinit; list->v.iter_next = mailbox_list_index_iter_next; list->v.create_mailbox_dir = mailbox_list_index_create_mailbox_dir; list->v.delete_mailbox = mailbox_list_index_delete_mailbox; list->v.delete_dir = mailbox_list_index_delete_dir; list->v.rename_mailbox = mailbox_list_index_rename_mailbox; MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist); ilist->path = dir == NULL ? "(in-memory mailbox list index)" : p_strdup_printf(list->pool, "%s/"MAILBOX_LIST_INDEX_PREFIX, dir); ilist->index = mail_index_alloc(dir, MAILBOX_LIST_INDEX_PREFIX); ilist->ext_id = mail_index_ext_register(ilist->index, "list", sizeof(struct mailbox_list_index_header), sizeof(struct mailbox_list_index_record), sizeof(uint32_t)); ilist->mailbox_pool = pool_alloconly_create("mailbox list index", 4096); ilist->mailbox_names = hash_table_create(default_pool, ilist->mailbox_pool, 0, NULL, NULL); ilist->mailbox_hash = hash_table_create(default_pool, ilist->mailbox_pool, 0, NULL, NULL); if (mailbox_list_index_index_open(list) < 0) { list->v = ilist->module_ctx.super; mail_index_free(&ilist->index); MODULE_CONTEXT_UNSET(list, mailbox_list_index_module); } mailbox_list_index_status_init_list(list); } static struct mail_storage_hooks mailbox_list_index_hooks = { .mailbox_list_created = mailbox_list_index_created }; void mailbox_list_index_init(void); /* called in mailbox-list-register.c */ void mailbox_list_index_init(void) { mail_storage_hooks_add_internal(&mailbox_list_index_hooks); mailbox_list_index_status_init(); }