mailbox-list-index.c revision 85da8c055280cd45553b6b335e9fb226d6e2801e
951N/A/* Copyright (c) 2006-2011 Dovecot authors, see the included COPYING file */
951N/A
951N/A#include "lib.h"
951N/A#include "array.h"
951N/A#include "crc32.h"
951N/A#include "ioloop.h"
951N/A#include "str.h"
951N/A#include "file-cache.h"
951N/A#include "file-dotlock.h"
951N/A#include "mmap-util.h"
951N/A#include "write-full.h"
951N/A#include "nfs-workarounds.h"
951N/A#include "mail-index-private.h"
951N/A#include "mailbox-list-index-private.h"
951N/A
951N/A#include <stdlib.h>
951N/A#include <unistd.h>
951N/A#include <fcntl.h>
951N/A#include <sys/stat.h>
951N/A
951N/Astruct mailbox_list_iter_path {
951N/A const struct mailbox_list_dir_record *dir;
951N/A unsigned int pos;
951N/A unsigned int name_path_len;
951N/A};
951N/A
951N/Astruct mailbox_list_index_view {
951N/A struct mailbox_list_index *index;
951N/A struct mail_index_view *mail_view;
951N/A};
951N/A
951N/Astruct mailbox_list_iter_ctx {
951N/A struct mailbox_list_index_view *view;
951N/A unsigned int recurse_level;
951N/A uint32_t max_uid;
951N/A
951N/A struct mailbox_list_iter_path cur;
951N/A ARRAY_DEFINE(path, struct mailbox_list_iter_path);
951N/A string_t *name_path;
951N/A
951N/A unsigned int failed:1;
951N/A};
951N/A
951N/Astatic const struct dotlock_settings default_dotlock_set = {
951N/A .timeout = 60,
951N/A .stale_timeout = 30
951N/A};
951N/A
951N/Aint mailbox_list_index_set_syscall_error(struct mailbox_list_index *index,
951N/A const char *function)
951N/A{
951N/A i_error("%s failed with file %s: %m", function, index->filepath);
958N/A return -1;
951N/A}
951N/A
968N/Astatic void mailbox_list_index_unmap(struct mailbox_list_index *index)
951N/A{
951N/A if (index->file_cache != NULL)
951N/A file_cache_invalidate(index->file_cache, 0, (uoff_t)-1);
951N/A
951N/A if (index->mmap_base != NULL) {
951N/A if (munmap(index->mmap_base, index->mmap_size) < 0)
951N/A mailbox_list_index_set_syscall_error(index, "munmap()");
951N/A index->mmap_base = NULL;
951N/A }
951N/A index->const_mmap_base = NULL;
951N/A index->mmap_size = 0;
951N/A
951N/A index->hdr = NULL;
951N/A}
951N/A
951N/Avoid mailbox_list_index_file_close(struct mailbox_list_index *index)
951N/A{
951N/A mailbox_list_index_unmap(index);
951N/A
951N/A if (index->file_cache != NULL)
951N/A file_cache_free(&index->file_cache);
951N/A if (index->fd != -1) {
951N/A if (close(index->fd) < 0)
951N/A mailbox_list_index_set_syscall_error(index, "close()");
951N/A index->fd = -1;
951N/A }
951N/A}
951N/A
951N/Aint mailbox_list_index_set_corrupted(struct mailbox_list_index *index,
951N/A const char *str)
951N/A{
951N/A if (!index->mail_index->readonly)
951N/A (void)unlink(index->filepath);
951N/A mailbox_list_index_file_close(index);
951N/A
951N/A i_error("Corrupted mailbox list index file %s: %s",
951N/A index->filepath, str);
951N/A return -1;
951N/A}
951N/A
951N/Astatic int
951N/Amailbox_list_index_check_header(struct mailbox_list_index *index,
951N/A const struct mailbox_list_index_header *hdr)
951N/A{
951N/A if (hdr->major_version != MAILBOX_LIST_INDEX_MAJOR_VERSION)
951N/A return -1;
951N/A
951N/A if (hdr->header_size < sizeof(*hdr)) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "header_size is too small");
951N/A }
951N/A if (hdr->header_size > index->mmap_size) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "header_size is too large");
951N/A }
951N/A
951N/A if (hdr->uid_validity == 0) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "uid_validity is 0");
951N/A }
951N/A if (hdr->next_uid == 0)
951N/A return mailbox_list_index_set_corrupted(index, "next_uid is 0");
951N/A
951N/A if (index->mail_index->map == NULL) {
951N/A /* index already marked as corrupted */
951N/A return -1;
951N/A }
951N/A return 0;
951N/A}
951N/A
951N/Aint mailbox_list_index_map(struct mailbox_list_index *index)
951N/A{
951N/A const struct mailbox_list_index_header *hdr;
951N/A struct stat st;
951N/A ssize_t ret;
951N/A
951N/A mailbox_list_index_unmap(index);
951N/A
951N/A if (!index->mmap_disable) {
951N/A if (fstat(index->fd, &st) < 0) {
951N/A mailbox_list_index_set_syscall_error(index, "fstat()");
951N/A return -1;
951N/A }
951N/A }
951N/A
951N/A if (!index->mmap_disable &&
951N/A st.st_size >= MAILBOX_LIST_INDEX_MMAP_MIN_SIZE) {
951N/A index->mmap_size = st.st_size;
951N/A index->mmap_base = mmap(NULL, index->mmap_size,
951N/A PROT_READ | PROT_WRITE,
951N/A MAP_SHARED, index->fd, 0);
951N/A if (index->mmap_base == MAP_FAILED) {
951N/A index->mmap_base = NULL;
951N/A mailbox_list_index_set_syscall_error(index, "mmap()");
951N/A return -1;
951N/A }
951N/A
951N/A index->const_mmap_base = index->mmap_base;
951N/A } else {
951N/A if (index->file_cache == NULL)
951N/A index->file_cache = file_cache_new(index->fd);
951N/A
951N/A ret = file_cache_read(index->file_cache, 0, SSIZE_T_MAX);
951N/A if (ret < 0) {
951N/A mailbox_list_index_set_syscall_error(index,
951N/A "file_cache_read()");
951N/A return -1;
951N/A }
951N/A index->const_mmap_base = file_cache_get_map(index->file_cache,
951N/A &index->mmap_size);
951N/A }
951N/A
951N/A if (index->mmap_size < sizeof(*hdr)) {
951N/A mailbox_list_index_set_corrupted(index, "File too small");
951N/A index->const_mmap_base = NULL;
1274N/A return 0;
1274N/A }
1274N/A
1274N/A hdr = index->const_mmap_base;
951N/A if (mailbox_list_index_check_header(index, hdr) < 0) {
1274N/A index->const_mmap_base = NULL;
1274N/A return 0;
1274N/A }
1274N/A
1274N/A index->hdr = hdr;
1274N/A return 1;
1274N/A}
1274N/A
1274N/Astatic int mailbox_list_index_map_area(struct mailbox_list_index *index,
951N/A uoff_t offset, size_t size)
951N/A{
951N/A if (offset < index->mmap_size && size <= index->mmap_size - offset)
1274N/A return 1;
1274N/A
1274N/A if (mailbox_list_index_map(index) <= 0)
1274N/A return -1;
951N/A
951N/A if (offset < index->mmap_size && size <= index->mmap_size - offset)
951N/A return 1;
951N/A /* outside the file */
951N/A return 0;
951N/A}
951N/A
951N/Astatic void
951N/Amailbox_list_index_init_header(struct mailbox_list_index *index,
951N/A struct mailbox_list_index_header *hdr,
951N/A uint32_t uid_validity)
951N/A{
951N/A memset(hdr, 0, sizeof(*hdr));
951N/A hdr->major_version = MAILBOX_LIST_INDEX_MAJOR_VERSION;
951N/A hdr->minor_version = MAILBOX_LIST_INDEX_MINOR_VERSION;
951N/A
951N/A hdr->file_seq = index->hdr == NULL ? 1 : index->hdr->file_seq + 1;
1028N/A hdr->header_size = sizeof(*hdr);
1028N/A hdr->used_space = hdr->header_size;
1028N/A
1028N/A hdr->uid_validity = uid_validity;
1028N/A hdr->next_uid = 1;
1028N/A}
1028N/A
1028N/Astatic int mailbox_list_index_is_recreated(struct mailbox_list_index *index)
1028N/A{
1028N/A struct stat st1, st2;
1028N/A
1028N/A if (index->fd == -1)
1028N/A return 1;
1028N/A
951N/A if ((index->mail_index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0)
951N/A nfs_flush_file_handle_cache(index->filepath);
951N/A
951N/A if (nfs_safe_stat(index->filepath, &st1) < 0) {
951N/A if (errno == ENOENT || errno == ESTALE)
951N/A return 1;
951N/A
951N/A mailbox_list_index_set_syscall_error(index, "stat()");
951N/A return -1;
951N/A }
951N/A if (fstat(index->fd, &st2) < 0) {
951N/A if (ESTALE_FSTAT(errno))
951N/A return 1;
951N/A mailbox_list_index_set_syscall_error(index, "fstat()");
951N/A return -1;
951N/A }
951N/A
1028N/A return st1.st_ino != st2.st_ino ||
951N/A !CMP_DEV_T(st1.st_dev, st2.st_dev);
968N/A}
951N/A
951N/Aint mailbox_list_index_file_create(struct mailbox_list_index *index,
1028N/A uint32_t uid_validity)
1028N/A{
1028N/A struct mailbox_list_index_header hdr;
1028N/A struct dotlock *dotlock;
1028N/A int fd, ret;
1028N/A
1028N/A fd = file_dotlock_open(&index->dotlock_set, index->filepath,
1028N/A 0, &dotlock);
1028N/A if (fd == -1) {
1028N/A mailbox_list_index_set_syscall_error(index,
1028N/A "file_dotlock_open()");
1274N/A return -1;
1028N/A }
1028N/A
1028N/A if (index->fd != -1) {
1028N/A /* if the file has been recreated by someone else,
1028N/A retry opening it */
1028N/A ret = mailbox_list_index_is_recreated(index);
1028N/A if (ret != 0) {
1028N/A (void)file_dotlock_delete(&dotlock);
1028N/A return ret < 0 ? -1 : 0;
1028N/A }
1028N/A }
1028N/A
1028N/A mailbox_list_index_init_header(index, &hdr, uid_validity);
1028N/A if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
1028N/A mailbox_list_index_set_syscall_error(index, "write_full()");
1028N/A (void)file_dotlock_delete(&dotlock);
1028N/A return -1;
1028N/A }
1028N/A
1028N/A if (index->mail_index->fsync_mode == FSYNC_MODE_ALWAYS &&
1028N/A fdatasync(fd) < 0) {
1028N/A mailbox_list_index_set_syscall_error(index, "fdatasync()");
1028N/A (void)file_dotlock_delete(&dotlock);
1028N/A return -1;
1028N/A }
1028N/A
1028N/A if (file_dotlock_replace(&dotlock,
1028N/A DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
951N/A mailbox_list_index_set_syscall_error(index,
951N/A "file_dotlock_replace()");
951N/A (void)close(fd);
951N/A return -1;
951N/A }
951N/A
951N/A if (index->fd != -1)
951N/A mailbox_list_index_file_close(index);
951N/A index->fd = fd;
951N/A
951N/A ret = mailbox_list_index_map(index);
951N/A if (ret == 0) {
951N/A i_error("Self-created mailbox list index file %s was corrupted",
951N/A index->filepath);
951N/A return -1;
951N/A }
951N/A return ret;
951N/A}
951N/A
951N/Astatic int
951N/Amailbox_list_index_file_try_open_or_create(struct mailbox_list_index *index)
951N/A{
951N/A int ret;
951N/A
1028N/A i_assert(index->fd == -1);
1028N/A
1028N/A index->fd = open(index->filepath, O_RDWR);
1028N/A if (index->fd == -1) {
1028N/A if (errno != ENOENT) {
1028N/A mailbox_list_index_set_syscall_error(index, "open()");
1028N/A return -1;
1028N/A }
1028N/A } else {
1028N/A ret = mailbox_list_index_map(index);
1028N/A if (ret != 0) {
1028N/A if (ret < 0)
1028N/A mailbox_list_index_file_close(index);
1028N/A return ret;
1028N/A }
1028N/A }
1028N/A
1274N/A ret = mailbox_list_index_file_create(index, ioloop_time);
1028N/A if (ret <= 0)
1028N/A mailbox_list_index_file_close(index);
1028N/A return ret;
1028N/A}
1028N/A
1274N/Aint mailbox_list_index_open_or_create(struct mailbox_list_index *index)
1274N/A{
1028N/A int ret;
1274N/A
1028N/A while ((ret = mailbox_list_index_file_try_open_or_create(index)) == 0) {
1274N/A /* file was recreated by someone else, try reopening */
1028N/A }
1028N/A return ret < 0 ? -1 : 0;
1028N/A}
951N/A
951N/Astruct mailbox_list_index *
951N/Amailbox_list_index_alloc(const char *path, char separator,
951N/A struct mail_index *mail_index)
951N/A{
951N/A struct mailbox_list_index *index;
951N/A
958N/A index = i_new(struct mailbox_list_index, 1);
951N/A index->filepath = i_strdup(path);
951N/A index->separator = separator;
951N/A index->mail_index = mail_index;
951N/A index->fd = -1;
951N/A index->mmap_disable =
951N/A (mail_index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0;
951N/A index->dotlock_set = default_dotlock_set;
951N/A index->dotlock_set.use_excl_lock =
951N/A (mail_index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
951N/A index->dotlock_set.nfs_flush =
1274N/A (mail_index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
951N/A return index;
951N/A}
951N/A
951N/Avoid mailbox_list_index_free(struct mailbox_list_index **_index)
951N/A{
951N/A struct mailbox_list_index *index = *_index;
951N/A
951N/A *_index = NULL;
951N/A
951N/A mailbox_list_index_file_close(index);
951N/A i_free(index->filepath);
951N/A i_free(index);
951N/A}
951N/A
951N/Astruct mailbox_list_index_lookup_key {
951N/A uint32_t name_hash;
951N/A
951N/A struct mailbox_list_index *index;
951N/A const char *name;
951N/A
951N/A bool *failed;
951N/A};
951N/A
951N/Astatic int
951N/Amailbox_list_get_name(struct mailbox_list_index *index, pool_t pool,
951N/A const struct mailbox_list_record *rec,
951N/A const char **name_r)
951N/A{
951N/A size_t max_len;
951N/A const char *name;
951N/A
951N/A if (rec->name_offset >= index->mmap_size) {
951N/A mailbox_list_index_set_corrupted(index, t_strdup_printf(
951N/A "record name_offset (%u) points outside file "
951N/A "(%"PRIuSIZE_T")", rec->name_offset, index->mmap_size));
951N/A return -1;
951N/A }
951N/A max_len = index->mmap_size - rec->name_offset;
951N/A name = CONST_PTR_OFFSET(index->const_mmap_base, rec->name_offset);
951N/A /* get name length. don't bother checking if it's not NUL-terminated,
951N/A because practically it always is even if the file is corrupted.
951N/A just make sure we don't crash if it happens. */
951N/A *name_r = p_strndup(pool, name, max_len);
951N/A if (*name_r == '\0') {
951N/A mailbox_list_index_set_corrupted(index, "Empty mailbox name");
951N/A return -1;
951N/A }
951N/A return 0;
951N/A}
951N/A
951N/Aint mailbox_list_index_get_dir(struct mailbox_list_index_view *view,
951N/A uint32_t *offset,
951N/A const struct mailbox_list_dir_record **dir_r)
951N/A{
951N/A struct mailbox_list_index *index = view->index;
951N/A const struct mailbox_list_dir_record *dir;
951N/A uint32_t next_offset, cur_offset = *offset;
951N/A int ret;
951N/A
951N/A i_assert(index->mmap_size > 0);
951N/A
951N/A do {
951N/A ret = mailbox_list_index_map_area(index, cur_offset,
951N/A sizeof(*dir));
951N/A if (ret <= 0) {
951N/A if (ret < 0)
951N/A return -1;
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "dir_offset points outside file");
951N/A }
951N/A if ((cur_offset % 4) != 0) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "dir_offset not 32bit aligned");
951N/A }
951N/A
951N/A dir = CONST_PTR_OFFSET(index->const_mmap_base, cur_offset);
951N/A next_offset = mail_index_offset_to_uint32(dir->next_offset);
951N/A if (next_offset != 0 && next_offset <= cur_offset) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "next_offset points backwards");
951N/A }
951N/A
951N/A if (dir->count >
951N/A index->mmap_size / sizeof(struct mailbox_list_record)) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "dir count too large");
951N/A }
951N/A if (dir->dir_size < sizeof(*dir) +
951N/A dir->count * sizeof(struct mailbox_list_record)) {
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "dir_size is smaller than record count");
951N/A }
951N/A cur_offset = next_offset;
951N/A } while (cur_offset != 0);
951N/A
951N/A cur_offset = (const char *)dir - (const char *)index->const_mmap_base;
951N/A ret = mailbox_list_index_map_area(index, cur_offset, dir->dir_size);
951N/A if (ret <= 0) {
951N/A if (ret < 0)
951N/A return -1;
951N/A return mailbox_list_index_set_corrupted(index,
951N/A "dir points outside file");
951N/A }
951N/A
951N/A *offset = cur_offset;
951N/A *dir_r = dir;
951N/A return 0;
951N/A}
951N/A
951N/Astatic int mailbox_list_record_cmp(const void *_key, const void *_rec)
951N/A{
951N/A const struct mailbox_list_index_lookup_key *key = _key;
951N/A const struct mailbox_list_record *rec = _rec;
951N/A int ret;
951N/A
951N/A if (key->name_hash < rec->name_hash)
951N/A return -1;
951N/A if (key->name_hash > rec->name_hash)
951N/A return 1;
951N/A
1028N/A T_BEGIN {
1028N/A const char *name;
1028N/A
1028N/A if (mailbox_list_get_name(key->index, unsafe_data_stack_pool,
1028N/A rec, &name) < 0) {
1028N/A *key->failed = TRUE;
1028N/A ret = 0;
1028N/A } else {
1028N/A ret = strcmp(key->name, name);
1028N/A }
1028N/A } T_END;
1028N/A return ret;
1028N/A}
1028N/A
1028N/Aint mailbox_list_index_dir_lookup_rec(struct mailbox_list_index *index,
1028N/A const struct mailbox_list_dir_record *dir,
1028N/A const char *name,
1028N/A const struct mailbox_list_record **rec_r)
1028N/A{
1028N/A const struct mailbox_list_record *rec;
1028N/A struct mailbox_list_index_lookup_key key;
1028N/A bool failed = FALSE;
1028N/A
1274N/A /* binary search the current hierarchy level name. the values are
1274N/A sorted primarily by their hash value and secondarily by the actual
1274N/A name */
1274N/A memset(&key, 0, sizeof(key));
1274N/A key.index = index;
1274N/A key.name = name;
1274N/A key.name_hash = crc32_str(name);
1274N/A key.failed = &failed;
1274N/A
1274N/A rec = bsearch(&key, MAILBOX_LIST_RECORDS(dir), dir->count, sizeof(*rec),
1274N/A mailbox_list_record_cmp);
1274N/A if (failed)
1274N/A return -1;
1028N/A if (rec == NULL)
951N/A return 0;
951N/A
951N/A *rec_r = rec;
968N/A return 1;
968N/A}
968N/A
968N/Astatic int
968N/Amailbox_list_index_lookup_rec(struct mailbox_list_index_view *view,
968N/A uint32_t dir_offset, const char *name,
968N/A const struct mailbox_list_record **rec_r)
968N/A{
968N/A struct mailbox_list_index *index = view->index;
968N/A const struct mailbox_list_dir_record *dir;
951N/A const char *p, *hier_name;
951N/A int ret;
951N/A
951N/A if (dir_offset == sizeof(*index->hdr) &&
951N/A index->mmap_size <= sizeof(*index->hdr)) {
951N/A /* root doesn't exist in the file yet */
951N/A return 0;
951N/A }
951N/A
951N/A if (mailbox_list_index_get_dir(view, &dir_offset, &dir) < 0)
951N/A return -1;
951N/A
951N/A p = strchr(name, index->separator);
951N/A hier_name = p == NULL ? name : t_strdup_until(name, p);
951N/A
951N/A ret = mailbox_list_index_dir_lookup_rec(index, dir, hier_name, rec_r);
951N/A if (ret <= 0)
951N/A return ret;
968N/A
951N/A if (p == NULL) {
951N/A /* found it */
951N/A return 1;
951N/A }
951N/A
951N/A /* recurse to children */
951N/A dir_offset = mail_index_offset_to_uint32((*rec_r)->dir_offset);
951N/A if (dir_offset == 0)
951N/A return 0;
951N/A
951N/A return mailbox_list_index_lookup_rec(view, dir_offset, p + 1, rec_r);
951N/A}
951N/A
951N/Aint mailbox_list_index_refresh(struct mailbox_list_index *index)
951N/A{
951N/A int ret;
951N/A
951N/A if ((ret = mailbox_list_index_is_recreated(index)) <= 0) {
951N/A if (ret < 0)
951N/A return -1;
951N/A
951N/A if (mailbox_list_index_map(index) < 0)
951N/A ret = -1;
951N/A return ret;
951N/A }
968N/A
951N/A mailbox_list_index_file_close(index);
951N/A return mailbox_list_index_open_or_create(index);
951N/A}
951N/A
951N/Aint mailbox_list_index_view_init(struct mailbox_list_index *index,
951N/A struct mail_index_view *mail_view,
1274N/A struct mailbox_list_index_view **view_r)
1274N/A{
951N/A struct mailbox_list_index_view *view;
951N/A const struct mail_index_header *mail_hdr;
951N/A
951N/A mail_hdr = mail_view != NULL ? mail_index_get_header(mail_view) : NULL;
951N/A if (mail_hdr != NULL && mail_hdr->uid_validity != 0 &&
951N/A index->hdr != NULL &&
951N/A mail_hdr->uid_validity != index->hdr->uid_validity) {
951N/A mail_index_set_error(index->mail_index,
1274N/A "uid_validity mismatch in file %s: %u != %u",
1274N/A index->filepath, index->hdr->uid_validity,
951N/A mail_hdr->uid_validity);
951N/A return -1;
951N/A }
1274N/A
1028N/A view = *view_r = i_new(struct mailbox_list_index_view, 1);
951N/A view->index = index;
951N/A view->mail_view = mail_view;
1028N/A return 0;
951N/A}
951N/A
951N/Avoid mailbox_list_index_view_deinit(struct mailbox_list_index_view **_view)
951N/A{
951N/A struct mailbox_list_index_view *view = *_view;
951N/A
951N/A *_view = NULL;
951N/A i_free(view);
951N/A}
951N/A
1274N/Aint mailbox_list_index_lookup(struct mailbox_list_index_view *view,
1274N/A const char *name, uint32_t *uid_r)
1274N/A{
951N/A const struct mailbox_list_record *rec;
951N/A uint32_t offset = sizeof(*view->index->hdr);
951N/A int ret;
951N/A
951N/A ret = mailbox_list_index_lookup_rec(view, offset, name, &rec);
1028N/A if (ret == 0) {
951N/A /* not found, see if it's found after a refresh */
951N/A if ((ret = mailbox_list_index_refresh(view->index)) <= 0)
1028N/A return ret;
951N/A
951N/A ret = mailbox_list_index_lookup_rec(view, offset, name, &rec);
951N/A }
951N/A
951N/A *uid_r = ret <= 0 ? 0 : rec->uid;
951N/A return ret;
951N/A}
951N/A
951N/Astruct mailbox_list_iter_ctx *
951N/Amailbox_list_index_iterate_init(struct mailbox_list_index_view *view,
951N/A const char *path, int recurse_level)
951N/A{
951N/A struct mailbox_list_iter_ctx *ctx;
951N/A const struct mail_index_header *mail_hdr;
951N/A const struct mailbox_list_record *rec;
951N/A uint32_t offset = sizeof(*view->index->hdr);
951N/A int ret;
951N/A
951N/A ctx = i_new(struct mailbox_list_iter_ctx, 1);
951N/A ctx->view = view;
951N/A ctx->recurse_level = recurse_level < 0 ? (unsigned int)-1 :
951N/A (unsigned int)recurse_level;
951N/A ctx->name_path = str_new(default_pool, 512);
951N/A
951N/A if (view->mail_view != NULL) {
951N/A mail_hdr = mail_index_get_header(view->mail_view);
951N/A ctx->max_uid = mail_hdr->next_uid - 1;
951N/A } else {
951N/A ctx->max_uid = (uint32_t)-1;
951N/A }
951N/A
951N/A if (mailbox_list_index_refresh(view->index) < 0)
951N/A ctx->failed = TRUE;
951N/A if (!ctx->failed && *path != '\0') {
951N/A ret = mailbox_list_index_lookup_rec(view, offset, path, &rec);
951N/A if (ret < 0)
951N/A ctx->failed = TRUE;
951N/A else {
951N/A offset = ret == 0 ? 0 :
951N/A mail_index_offset_to_uint32(rec->dir_offset);
951N/A }
951N/A }
951N/A
951N/A if (view->index->mmap_size <= sizeof(*view->index->hdr)) {
951N/A /* root doesn't exist */
951N/A } else if (!ctx->failed && offset != 0) {
1029N/A if (mailbox_list_index_get_dir(view, &offset,
951N/A &ctx->cur.dir) < 0)
951N/A ctx->failed = TRUE;
1097N/A }
968N/A i_array_init(&ctx->path, I_MIN(ctx->recurse_level, 16));
1029N/A return ctx;
1029N/A}
951N/A
951N/Aint mailbox_list_index_iterate_next(struct mailbox_list_iter_ctx *ctx,
951N/A struct mailbox_list_index_info *info_r)
951N/A{
1029N/A const struct mailbox_list_iter_path *cur;
1029N/A const struct mailbox_list_record *recs;
1029N/A uint32_t dir_offset;
1097N/A unsigned int count;
968N/A
1029N/A if (ctx->failed)
1029N/A return -1;
951N/A
951N/A if (ctx->cur.dir == NULL) {
951N/A /* no mailboxes */
951N/A i_assert(array_count(&ctx->path) == 0);
951N/A return 0;
951N/A }
951N/A
951N/A for (;;) {
951N/A if (ctx->cur.pos == ctx->cur.dir->count) {
951N/A count = array_count(&ctx->path);
951N/A if (count == 0) {
951N/A /* we're done */
951N/A return 0;
951N/A }
951N/A
951N/A /* go back to parent path */
951N/A cur = array_idx(&ctx->path, count-1);
951N/A ctx->cur = *cur;
951N/A array_delete(&ctx->path, count-1, 1);
951N/A
951N/A ctx->cur.pos++;
951N/A } else {
951N/A recs = MAILBOX_LIST_RECORDS(ctx->cur.dir);
951N/A recs += ctx->cur.pos;
951N/A
951N/A if (!recs->deleted && recs->uid <= ctx->max_uid)
951N/A break;
951N/A
951N/A ctx->cur.pos++;
951N/A }
951N/A }
951N/A
951N/A T_BEGIN {
951N/A const char *name;
951N/A
951N/A if (mailbox_list_get_name(ctx->view->index,
951N/A unsafe_data_stack_pool,
951N/A recs, &name) < 0)
951N/A ctx->failed = TRUE;
951N/A else {
1274N/A str_truncate(ctx->name_path, ctx->cur.name_path_len);
951N/A if (ctx->cur.name_path_len > 0) {
951N/A str_append_c(ctx->name_path,
951N/A ctx->view->index->separator);
951N/A }
951N/A str_append(ctx->name_path, name);
951N/A }
951N/A } T_END;
951N/A if (ctx->failed)
951N/A return -1;
951N/A
951N/A info_r->name = str_c(ctx->name_path);
951N/A info_r->uid = recs->uid;
951N/A
951N/A dir_offset = mail_index_offset_to_uint32(recs->dir_offset);
951N/A if (dir_offset != 0 && array_count(&ctx->path) < ctx->recurse_level) {
951N/A /* recurse into children */
951N/A array_append(&ctx->path, &ctx->cur, 1);
951N/A
951N/A ctx->cur.name_path_len = str_len(ctx->name_path);
951N/A ctx->cur.pos = 0;
951N/A if (mailbox_list_index_get_dir(ctx->view, &dir_offset,
951N/A &ctx->cur.dir) < 0) {
951N/A ctx->failed = TRUE;
951N/A return -1;
951N/A }
951N/A recs = NULL; /* don't use anymore */
951N/A } else {
951N/A ctx->cur.pos++;
951N/A }
951N/A info_r->has_children = dir_offset != 0;
951N/A return 1;
951N/A}
951N/A
951N/Avoid mailbox_list_index_iterate_deinit(struct mailbox_list_iter_ctx **_ctx)
951N/A{
951N/A struct mailbox_list_iter_ctx *ctx = *_ctx;
951N/A
951N/A *_ctx = NULL;
951N/A array_free(&ctx->path);
951N/A str_free(&ctx->name_path);
951N/A i_free(ctx);
951N/A}
951N/A