Mercurial > dovecot > core-2.2
view src/lib-index/mail-index.c @ 1915:79790750c349 HEAD
importing new index code. mbox still broken.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 27 Apr 2004 23:25:52 +0300 |
parents | |
children | 68938dccbc45 |
line wrap: on
line source
/* Copyright (C) 2003-2004 Timo Sirainen */ #include "lib.h" #include "buffer.h" #include "file-lock.h" #include "mmap-util.h" #include "write-full.h" #include "mail-index-private.h" #include "mail-transaction-log.h" #include <stdio.h> #include <stddef.h> #include <time.h> struct mail_index *mail_index_alloc(const char *dir, const char *prefix) { struct mail_index *index; index = i_new(struct mail_index, 1); index->dir = i_strdup(dir); index->prefix = i_strdup(prefix); index->fd = -1; index->mode = 0600; index->gid = (gid_t)-1; return index; } void mail_index_free(struct mail_index *index) { i_free(index->error); i_free(index->dir); i_free(index->prefix); i_free(index); } static int mail_index_check_quick_header(struct mail_index *index, struct mail_index_map *map, const struct mail_index_header *hdr) { if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) { /* either a crash or we've already complained about it */ return -1; } if (map->mmap_used_size > map->mmap_size) { map->records_count = (map->mmap_size - hdr->header_size) / sizeof(struct mail_index_record); map->mmap_used_size = map->mmap_size; mail_index_set_error(index, "Corrupted index file %s: " "messages_count too large (%u > %u)", index->filepath, hdr->messages_count, map->records_count); return 0; } return 1; } static int mail_index_check_header(struct mail_index *index, struct mail_index_map *map, const struct mail_index_header *hdr) { unsigned char compat_data[3]; int ret; #ifndef WORDS_BIGENDIAN compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #else compat_data[0] = 0; #endif compat_data[1] = sizeof(uoff_t); compat_data[2] = sizeof(time_t); if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) { /* major version change - handle silently(?) */ return -1; } if (memcmp(hdr->compat_data, compat_data, sizeof(compat_data)) != 0) { /* architecture change - handle silently(?) */ return -1; } if ((ret = mail_index_check_quick_header(index, map, hdr)) <= 0) return ret; /* following some extra checks that only take a bit of CPU */ if (hdr->uid_validity == 0) { mail_index_set_error(index, "Corrupted index file %s: " "uid_validity = 0", index->filepath); return -1; } if (hdr->next_uid == 0) return 0; if (hdr->seen_messages_count > hdr->messages_count || hdr->deleted_messages_count > hdr->messages_count) return 0; if (hdr->first_recent_uid_lowwater > hdr->next_uid || hdr->first_unseen_uid_lowwater > hdr->next_uid || hdr->first_deleted_uid_lowwater > hdr->next_uid) return 0; return 1; } void mail_index_unmap(struct mail_index *index, struct mail_index_map *map) { if (--map->refcount > 0) return; if (map->buffer != NULL) { i_assert(map->mmap_base == NULL); buffer_free(map->buffer); } else { i_assert(map->buffer == NULL); if (munmap(map->mmap_base, map->mmap_size) < 0) mail_index_set_syscall_error(index, "munmap()"); } i_free(map); } int mail_index_map(struct mail_index *index, int force) { const struct mail_index_header *hdr; struct mail_index_map *map; size_t used_size; int ret; if (!index->use_mmap) { // FIXME return -1; } if (index->map != NULL) { map = index->map; /* see if re-mmaping is needed (file has grown) */ hdr = map->mmap_base; used_size = hdr->header_size + hdr->messages_count * sizeof(struct mail_index_record); if (map->mmap_size >= used_size && !force) return 1; if (munmap(map->mmap_base, map->mmap_size) < 0) mail_index_set_syscall_error(index, "munmap()"); map->mmap_base = NULL; } else { map = i_new(struct mail_index_map, 1); map->refcount = 1; } index->hdr = NULL; index->map = NULL; map->mmap_base = mmap_ro_file(index->fd, &map->mmap_size); if (map->mmap_base == MAP_FAILED) { map->mmap_base = NULL; mail_index_set_syscall_error(index, "mmap()"); mail_index_unmap(index, map); return -1; } if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) { mail_index_set_error(index, "Corrupted index file %s: " "File too small (%"PRIuSIZE_T")", index->filepath, map->mmap_size); mail_index_unmap(index, map); return 0; } hdr = map->mmap_base; if (hdr->header_size < sizeof(*hdr)) { /* header smaller than ours, make a copy so our newer headers won't have garbage in them */ memcpy(&map->hdr_copy, hdr, hdr->header_size); hdr = &map->hdr_copy; } map->hdr = map->mmap_base; map->records = PTR_OFFSET(map->mmap_base, hdr->header_size); map->records_count = hdr->messages_count; map->mmap_used_size = hdr->header_size + map->records_count * sizeof(struct mail_index_record); ret = mail_index_check_header(index, map, hdr); if (ret < 0) { mail_index_unmap(index, map); return 0; } if (ret == 0) index->fsck = TRUE; index->hdr = map->mmap_base; index->map = map; return 1; } struct mail_index_map *mail_index_map_to_memory(struct mail_index_map *map) { const struct mail_index_header *hdr; struct mail_index_map *mem_map; size_t size; if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) { map->refcount++; return map; } size = map->records_count * sizeof(struct mail_index_record); mem_map = i_new(struct mail_index_map, 1); mem_map->refcount = 1; mem_map->buffer = buffer_create_dynamic(default_pool, size, (size_t)-1); buffer_append(mem_map->buffer, map->records, size); mem_map->records = buffer_get_modifyable_data(mem_map->buffer, NULL); mem_map->records_count = map->records_count; hdr = map->mmap_base; memcpy(&mem_map->hdr_copy, map->mmap_base, I_MIN(hdr->header_size, sizeof(mem_map->hdr_copy))); mem_map->hdr = &mem_map->hdr_copy; return mem_map; } void mail_index_header_init(struct mail_index_header *hdr) { time_t now = time(NULL); memset(hdr, 0, sizeof(*hdr)); hdr->major_version = MAIL_INDEX_MAJOR_VERSION; hdr->minor_version = MAIL_INDEX_MINOR_VERSION; hdr->header_size = sizeof(*hdr); #ifndef WORDS_BIGENDIAN hdr->compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN; #endif hdr->compat_data[1] = sizeof(uoff_t); hdr->compat_data[2] = sizeof(time_t); hdr->indexid = now; hdr->uid_validity = now; hdr->next_uid = 1; } int mail_index_write_header(struct mail_index *index, const struct mail_index_header *hdr) { if (index->use_mmap) { if (mprotect(index->map->mmap_base, sizeof(*hdr), PROT_READ | PROT_WRITE) < 0) { mail_index_set_syscall_error(index, "mprotect()"); return -1; } memcpy(index->map->mmap_base, hdr, sizeof(*hdr)); if (msync(index->map->mmap_base, sizeof(*hdr), MS_SYNC) < 0) return mail_index_set_syscall_error(index, "msync()"); if (mprotect(index->map->mmap_base, sizeof(*hdr), PROT_READ) < 0) { mail_index_set_syscall_error(index, "mprotect()"); return -1; } } else { if (pwrite_full(index->fd, hdr, sizeof(*hdr), 0) < 0) { mail_index_set_syscall_error(index, "pwrite_full()"); return -1; } index->map->hdr_copy = *hdr; index->hdr = &index->map->hdr_copy; } return 0; } int mail_index_create_tmp_file(struct mail_index *index, const char **path_r) { const char *path; int fd; path = *path_r = t_strconcat(index->filepath, ".tmp", NULL); fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode); if (fd == -1) return mail_index_file_set_syscall_error(index, path, "open()"); if (index->gid != (gid_t)-1 && fchown(index->fd, (uid_t)-1, index->gid) < 0) { mail_index_file_set_syscall_error(index, path, "fchown()"); return -1; } return fd; } int mail_index_create(struct mail_index *index, struct mail_index_header *hdr) { const char *path; uint32_t seq; uoff_t offset; int ret; /* log file lock protects index creation */ if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0) return -1; hdr->log_file_seq = seq; hdr->log_file_offset = offset; ret = mail_index_try_open(index); if (ret != 0) { mail_transaction_log_sync_unlock(index->log); return ret; } /* create it fully in index.tmp first */ index->fd = mail_index_create_tmp_file(index, &path); if (index->fd == -1) ret = -1; else if (write_full(index->fd, hdr, sizeof(*hdr)) < 0) { mail_index_file_set_syscall_error(index, path, "write_full()"); ret = -1; } else { ret = mail_index_map(index, FALSE); } if (ret == 0) { /* it's corrupted even while we just created it, should never happen unless someone pokes the file directly */ mail_index_set_error(index, "Newly created index file is corrupted: %s", path); ret = -1; } if (ret < 0) { if (unlink(path) < 0 && errno != ENOENT) { mail_index_file_set_syscall_error(index, path, "unlink()"); } return -1; } /* make it visible to others */ if (rename(path, index->filepath) < 0) { mail_index_set_error(index, "rename(%s, %s) failed: %m", path, index->filepath); return -1; } mail_transaction_log_sync_unlock(index->log); return 1; } int mail_index_try_open(struct mail_index *index) { unsigned int lock_id; int ret; index->fd = open(index->filepath, O_RDWR); if (index->fd == -1 && errno == EACCES) { index->fd = open(index->filepath, O_RDONLY); index->readonly = TRUE; } if (index->fd == -1) { if (errno != ENOENT) return mail_index_set_syscall_error(index, "open()"); /* have to create it */ return 0; } else { if (mail_index_lock_shared(index, FALSE, &lock_id) < 0) return -1; ret = mail_index_map(index, FALSE); mail_index_unlock(index, lock_id); if (ret == 0) { /* it's corrupted - recreate it */ (void)close(index->fd); index->fd = -1; } return ret; } } static int mail_index_open2(struct mail_index *index, enum mail_index_open_flags flags) { struct mail_index_header hdr; int ret; ret = mail_index_try_open(index); if (ret == 1) hdr = *index->hdr; else if (ret == 0) { /* doesn't exist, or corrupted */ if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) return 0; mail_index_header_init(&hdr); index->hdr = &hdr; } else if (ret < 0) return -1; index->indexid = hdr.indexid; index->log = mail_transaction_log_open_or_create(index); if (index->log == NULL) return -1; return index->fd != -1 ? 1 : mail_index_create(index, &hdr); } int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags) { int i = 0, ret; if (index->opened) return 0; do { index->shared_lock_count = 0; index->excl_lock_count = 0; index->lock_type = F_UNLCK; index->nodiskspace = FALSE; index->index_lock_timeout = FALSE; index->log_locked = FALSE; index->use_mmap = (flags & MAIL_INDEX_OPEN_FLAG_NO_MMAP) == 0; index->readonly = FALSE; index->filepath = i_strconcat(index->dir, "/", index->prefix, NULL); ret = mail_index_open2(index, flags); if (ret <= 0) break; index->opened = TRUE; if (index->fsck) { index->fsck = FALSE; ret = mail_index_fsck(index); if (ret == 0) { /* completely broken, reopen */ if (i++ < 3) continue; /* too many tries */ ret = -1; } } break; } while (1); if (ret <= 0) mail_index_close(index); return ret; } void mail_index_close(struct mail_index *index) { if (index->log != NULL) { mail_transaction_log_close(index->log); index->log = NULL; } mail_index_unmap(index, index->map); index->map = NULL; if (index->fd != -1) { if (close(index->fd) < 0) mail_index_set_syscall_error(index, "close()"); index->fd = -1; } i_free(index->copy_lock_path); i_free(index->filepath); index->filepath = NULL; index->indexid = 0; index->opened = FALSE; } struct mail_cache *mail_index_get_cache(struct mail_index *index) { return index->cache; } int mail_index_set_error(struct mail_index *index, const char *fmt, ...) { va_list va; i_free(index->error); if (fmt == NULL) index->error = NULL; else { va_start(va, fmt); index->error = i_strdup_vprintf(fmt, va); va_end(va); i_error("%s", index->error); } return -1; } int mail_index_mark_corrupted(struct mail_index *index) { struct mail_index_header hdr; if (index->readonly || index->hdr == NULL || (index->hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) return 0; hdr = *index->hdr; hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED; if (mail_index_write_header(index, &hdr) < 0) return -1; if (fsync(index->fd) < 0) return mail_index_set_syscall_error(index, "fsync()"); return 0; } int mail_index_set_syscall_error(struct mail_index *index, const char *function) { i_assert(function != NULL); if (ENOSPACE(errno)) { index->nodiskspace = TRUE; return -1; } return mail_index_set_error(index, "%s failed with index file %s: %m", function, index->filepath); } int mail_index_file_set_syscall_error(struct mail_index *index, const char *filepath, const char *function) { i_assert(filepath != NULL); i_assert(function != NULL); if (ENOSPACE(errno)) { index->nodiskspace = TRUE; return -1; } return mail_index_set_error(index, "%s failed with file %s: %m", function, filepath); } enum mail_index_error mail_index_get_last_error(struct mail_index *index) { if (index->nodiskspace) return MAIL_INDEX_ERROR_DISKSPACE; if (index->error != NULL) return MAIL_INDEX_ERROR_INTERNAL; return MAIL_INDEX_ERROR_NONE; } const char *mail_index_get_error_message(struct mail_index *index) { return index->error; } void mail_index_reset_error(struct mail_index *index) { if (index->error != NULL) { i_free(index->error); index->error = NULL; } index->nodiskspace = FALSE; index->index_lock_timeout = FALSE; } int mail_index_is_in_memory(struct mail_index *index) { return FALSE; // FIXME }