mail-cache.c revision 5c841f2879f7870540c231a12eebfb5f6425219c
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher/* Copyright (C) 2003 Timo Sirainen */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#define _XOPEN_SOURCE 500 /* for pwrite() / Linux */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek/* Never compress the file if it's smaller than this */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher/* Compress the file when deleted space reaches n% of total size */
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek/* Compress the file when n% of rows contain continued rows.
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozek 200% means that there's 2 continued rows per record. */
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek/* Initial size for the file */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#define MAIL_CACHE_INITIAL_SIZE (sizeof(struct mail_cache_header) + 10240)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher/* When more space is needed, grow the file n% larger than the previous size */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ((char *) (cache)->mmap_base + ((data_pos) & ~3)))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher uint32_t used_fields; /* enum mail_cache_field */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher uint32_t header_offsets[MAIL_CACHE_HEADERS_COUNT];
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher uint32_t fields; /* enum mail_cache_field */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher uint32_t size; /* full record size, including this header */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher enum mail_cache_field default_cache_fields;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct mail_cache_transaction_ctx *trans_ctx;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher unsigned int first_uid, last_uid, prev_uid;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherunsigned int mail_cache_field_sizes[32] = {
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher 0, 0, 0, 0, 0,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* variable sized */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherenum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT] = {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic const unsigned char *null4[] = { 0, 0, 0, 0 };
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic const char *
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghermail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic int mail_cache_write(struct mail_cache_transaction_ctx *ctx);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghermail_cache_lookup(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic uint32_t uint32_to_offset(uint32_t offset)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buf[0] = 0x80 | ((offset & 0x0fe00000) >> 21);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buf[1] = 0x80 | ((offset & 0x001fc000) >> 14);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buf[2] = 0x80 | ((offset & 0x00003f80) >> 7);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic uint32_t offset_to_uint32(uint32_t offset)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher const unsigned char *buf = (const unsigned char *) &offset;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekstatic int mail_cache_set_syscall_error(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher index_set_error(cache->index, "%s failed with index cache file %s: %m",
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagherstatic int mail_cache_create_memory(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->mmap_length = MAIL_CACHE_INITIAL_SIZE;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->mmap_base = mmap_anon(cache->mmap_length);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher index_set_error(cache->index, "mmap_anon(%"PRIuSIZE_T")",
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->filepath = i_strdup_printf("(in-memory index cache for %s)",
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void mail_cache_file_close(struct mail_cache *cache)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_syscall_error(cache, "munmap_anon()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (munmap(cache->mmap_base, cache->mmap_length) < 0)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher mail_cache_set_syscall_error(cache, "munmap()");
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher mail_cache_set_syscall_error(cache, "close()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic int mail_cache_file_reopen(struct mail_cache *cache)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* cache was set corrupted, we'll have to quit */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek return mail_cache_set_syscall_error(cache, "open()");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic int mmap_verify_header(struct mail_cache *cache)
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* check that the header is still ok */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (cache->mmap_length < sizeof(struct mail_cache_header))
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek return mail_cache_set_corrupted(cache, "File too small");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (cache->header->indexid != cache->index->indexid) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* index id changed */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek mail_cache_set_corrupted(cache, "indexid changed");
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek cache->index->inconsistent = TRUE; /* easiest way to rebuild */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* we've updated used_file_size, do nothing */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek cache->used_file_size = nbo_to_uint32(hdr->used_file_size);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* only check the header if we're locked */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (cache->used_file_size < sizeof(struct mail_cache_header)) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek mail_cache_set_corrupted(cache, "used_file_size too small");
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if ((cache->used_file_size % sizeof(uint32_t)) != 0) {
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek mail_cache_set_corrupted(cache, "used_file_size not aligned");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (cache->used_file_size > cache->mmap_length) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* maybe a crash truncated the file - just fix it */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek hdr->used_file_size = uint32_to_nbo(cache->mmap_length & ~3);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (msync(cache->mmap_base, sizeof(*hdr), MS_SYNC) < 0)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek return mail_cache_set_syscall_error(cache, "msync()");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic int mmap_update_nocheck(struct mail_cache *cache,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek cache->header->indexid != cache->index->indexid) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* indexid changed, most likely it was rebuilt.
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek try reopening. */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* force mmap refresh */
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (size != 0 && offset < cache->mmap_length &&
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* already mapped */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* in the middle of transaction - write the changes */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (msync(cache->mmap_base, cache->mmap_length,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_syscall_error(cache, "msync()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (munmap(cache->mmap_base, cache->mmap_length) < 0)
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher mail_cache_set_syscall_error(cache, "munmap()");
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher /* map the whole file */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->mmap_base = mmap_rw_file(cache->fd, &cache->mmap_length);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher return mail_cache_set_syscall_error(cache, "mmap()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek return mmap_update_nocheck(cache, offset, size) &&
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagherstatic int mail_cache_open_and_verify(struct mail_cache *cache, int silent)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->fd = open(cache->filepath, O_RDWR);
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher mail_cache_set_syscall_error(cache, "open()");
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher mail_cache_set_syscall_error(cache, "fstat()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (st.st_size < sizeof(struct mail_cache_header))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (!mmap_update_nocheck(cache, 0, sizeof(struct mail_cache_header)))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* verify that this really is the cache for wanted index */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void mail_index_clear_cache_offsets(struct mail_index *index)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekstatic int mail_cache_open_or_create_file(struct mail_cache *cache,
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek cache->filepath = i_strconcat(cache->index->filepath,
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher ret = mail_cache_open_and_verify(cache, FALSE);
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* we'll have to clear cache_offsets which requires exclusive lock */
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek if (!mail_index_set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* maybe a rebuild.. */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher MAIL_CACHE_LOCK_STALE_TIMEOUT, NULL, NULL);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_syscall_error(cache, "file_dotlock_open()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* see if someone else just created the cache file */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek /* rebuild then */
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek mail_cache_set_syscall_error(cache, "write_full()");
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek mail_cache_set_syscall_error(cache, "file_set_size()");
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
0172959f117b545c8a6b1893f5f56818d82dd624Jakub Hrozekint mail_cache_open_or_create(struct mail_index *index)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher cache->split_header_pool = pool_alloconly_create("Headers", 512);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* we'll do anon-mmaping only if initially requested. if we fail
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher because of out of disk space, we'll just let the main index code
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek know it and fail. */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (!mail_cache_create_memory(cache, &hdr)) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (!mail_cache_open_or_create_file(cache, &hdr)) {
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher /* unset inconsistency - we already rebuilt the cache file */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghervoid mail_cache_free(struct mail_cache *cache)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghervoid mail_cache_set_defaults(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher enum mail_cache_field default_cache_fields,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->default_cache_fields = default_cache_fields;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher cache->never_cache_fields = never_cache_fields;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic const struct mail_cache_record *
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghermail_cache_compress_record(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct mail_index_record *rec, int header_idx,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher enum mail_cache_field orig_cached_fields, cached_fields, field;
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek const void *data;
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher buffer = buffer_create_dynamic(data_stack_pool, 4096, (size_t)-1);
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher orig_cached_fields = mail_cache_get_fields(cache, rec);
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher cached_fields = orig_cached_fields & ~MAIL_CACHE_HEADERS_MASK;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher buffer_append(buffer, &cache_rec, sizeof(cache_rec));
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher for (i = 0, field = 1; i < 31; i++, field <<= 1) {
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher if (!mail_cache_lookup_field(cache, rec, field, &data, &size)) {
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher buffer_append(buffer, &nb_size, sizeof(nb_size));
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher buffer_append(buffer, null4, 4 - (size & 3));
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* now merge all the headers if we have them all */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if ((orig_cached_fields & mail_cache_header_fields[header_idx]) != 0) {
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek buffer_append(buffer, &nb_size, sizeof(nb_size));
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek for (i = 0; i <= header_idx; i++) {
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek buffer_append(buffer, null4, 4 - (nb_size & 3));
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek buffer_write(buffer, pos, &nb_size, sizeof(nb_size));
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek cache_rec.size = uint32_to_nbo(buffer_get_used_size(buffer));
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozek buffer_write(buffer, 0, &cache_rec, sizeof(cache_rec));
e5c33e0bd03a2deb8e5011deeb3ae93f960910eeJakub Hrozekstatic int mail_cache_copy(struct mail_cache *cache, int fd)
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek unsigned char *mmap_base;
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek const char *str;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* pick some reasonably good file size */
524ceecc11f3d458eb3c1cf1489c3ff6ccb22226Jakub Hrozek new_file_size = (new_file_size + 1023) & ~1023;
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek return mail_cache_set_syscall_error(cache, "file_set_size()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mmap_base = mmap(NULL, new_file_size, PROT_READ | PROT_WRITE,
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek return mail_cache_set_syscall_error(cache, "mmap()");
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek /* skip file's header */
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek /* merge all the header pieces into one. if some message doesn't have
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek all the required pieces, we'll just have to drop them all. */
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek for (i = MAIL_CACHE_HEADERS_COUNT-1; i >= 0; i--) {
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek str = mail_cache_get_header_fields_str(cache, i);
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek hdr->header_offsets[0] = uint32_to_offset(offset);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy(mmap_base + offset, &nb_size, sizeof(nb_size));
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher rec = cache->index->lookup(cache->index, 1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher else if (offset_to_uint32(cache_rec->next_offset) == 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* just one unmodified block, copy it */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy(mmap_base + offset, cache_rec, size);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher rec->cache_offset = uint32_to_offset(offset);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* multiple blocks, sort them into buffer */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache_rec = mail_cache_compress_record(cache, rec,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy(mmap_base + offset, cache_rec, size);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher rec = cache->index->next(cache->index, rec);
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher /* update header */
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher hdr->used_file_size = uint32_to_nbo(offset);
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher hdr->field_usage_start = uint32_to_nbo(ioloop_time);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* write everything to disk */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher if (msync(mmap_base, offset, MS_SYNC) < 0)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek return mail_cache_set_syscall_error(cache, "msync()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher return mail_cache_set_syscall_error(cache, "munmap()");
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher return mail_cache_set_syscall_error(cache, "fdatasync()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_cache_compress(struct mail_cache *cache)
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher if (!cache->index->set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_cache_set_syscall_error(cache, "file_dotlock_open()");
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek /* now we'll begin the actual moving. keep rebuild-flag on
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek while doing it. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek cache->index->header->flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek if (!mail_index_fmdatasync(cache->index, cache->index->header_size))
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek "file_dotlock_replace()");
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek cache->index->header->flags &= ~(MAIL_INDEX_HDR_FLAG_REBUILD |
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozekint mail_cache_truncate(struct mail_cache *cache)
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozek hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_cache_set_syscall_error(cache, "file_dotlock_open()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_cache_set_syscall_error(cache, "write_full()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_cache_set_syscall_error(cache, "file_set_size()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek (void)file_dotlock_delete(cache->filepath, fd);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_cache_mark_file_deleted(struct mail_cache *cache)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (pwrite(cache->fd, &indexid, sizeof(indexid), 0) < 0)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek return mail_cache_set_syscall_error(cache, "pwrite()");
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_cache_lock(struct mail_cache *cache, int nonblock)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_syscall_error(cache, "file_try_lock()");
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher mail_cache_set_syscall_error(cache, "file_wait_lock()");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherint mail_cache_unlock(struct mail_cache *cache)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_syscall_error(cache, "file_wait_lock(F_UNLCK)");
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallaghervoid mail_cache_unlock_later(struct mail_cache *cache)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_cache_is_locked(struct mail_cache *cache)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherint mail_cache_transaction_begin(struct mail_cache *cache, int nonblock,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct mail_cache_transaction_ctx **ctx_r)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher *ctx_r = i_new(struct mail_cache_transaction_ctx, 1);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek buffer_create_dynamic(system_pool, 8192, (size_t)-1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherint mail_cache_transaction_end(struct mail_cache_transaction_ctx *ctx)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher (void)mail_cache_transaction_rollback(ctx);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void mail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memset(&ctx->cache_rec, 0, sizeof(ctx->cache_rec));
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ctx->first_uid = ctx->last_uid = ctx->prev_uid = 0;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buffer_set_used_size(ctx->cache_marks, 0);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher buffer_set_used_size(ctx->index_marks, 0);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic void mark_update(buffer_t **buf, uint32_t offset, uint32_t data)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher *buf = buffer_create_dynamic(system_pool, 1024, (size_t)-1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* data is in big endian, we want to update only the lowest byte */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buffer_append(*buf, &offset, sizeof(offset));
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagherstatic int write_mark_updates(struct mail_index *index, buffer_t *marks,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (pwrite(fd, data+1, sizeof(*data), data[0]) < 0) {
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek index_file_set_syscall_error(index, path, "pwrite()");
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekstatic void write_mark_updates_in_memory(buffer_t *marks, void *mmap_base,
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher i_assert(offset <= mmap_length - sizeof(uint32_t));
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher memcpy((char *) mmap_base + offset, data, sizeof(uint32_t));
65a9065538fd85e6ead925d344e6b421900eb8c2Jakub Hrozekstatic void commit_all_changes_in_memory(struct mail_cache_transaction_ctx *ctx)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagherstatic int commit_all_changes(struct mail_cache_transaction_ctx *ctx)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* write everything to disk */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher if (msync(cache->mmap_base, cache->mmap_length, MS_SYNC) < 0)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher return mail_cache_set_syscall_error(cache, "msync()");
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher return mail_cache_set_syscall_error(cache, "fdatasync()");
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher buffer_get_used_size(ctx->cache_marks) != 0) {
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* now that we're sure it's there, set on all the used-bits */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher if (!write_mark_updates(cache->index, ctx->cache_marks,
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* update continued records count */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher cont = nbo_to_uint32(cache->header->continued_record_count);
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher cont += buffer_get_used_size(ctx->cache_marks) /
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if (cont * 100 / cache->index->header->messages_count >=
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* too many continued rows, compress */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek cache->header->continued_record_count = uint32_to_nbo(cont);
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* write index last */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (!write_mark_updates(cache->index, ctx->index_marks,
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagherint mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher uint32_to_nbo(ctx->cache->used_file_size);
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher if (ctx->next_unused_header_lowwater == MAIL_CACHE_HEADERS_COUNT) {
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* they're all used - compress the cache to get more */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekint mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher unsigned int i;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* no need to actually modify the file - we just didn't update
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher used_file_size */
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher cache->used_file_size = nbo_to_uint32(cache->header->used_file_size);
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek /* make sure we don't cache the headers */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek for (i = 0; i < ctx->next_unused_header_lowwater; i++) {
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher if (offset_to_uint32(cache->header->header_offsets[i]) == 0)
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagherstatic int mail_cache_grow(struct mail_cache *cache, uint32_t size)
486237ee009f1d84fc4c85665dce80ade76f7079Stephen Gallagher grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek base = mremap_anon(cache->mmap_base, cache->mmap_length,
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher mail_cache_set_syscall_error(cache, "mremap_anon()");
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher return mail_cache_set_syscall_error(cache, "fstat()");
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if (cache->used_file_size + size <= (uoff_t)st.st_size) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* no need to grow, just update mmap */
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek i_assert(cache->mmap_length >= (uoff_t)st.st_size);
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if (st.st_size < (off_t)sizeof(struct mail_cache_header))
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek return mail_cache_set_corrupted(cache, "Header is missing");
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek if (file_set_size(cache->fd, (off_t)new_fsize) < 0)
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek return mail_cache_set_syscall_error(cache, "file_set_size()");
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozekstatic uint32_t mail_cache_append_space(struct mail_cache_transaction_ctx *ctx,
481ec0e1eb0058195732cb320845b41f6f4d43ebJakub Hrozek /* NOTE: must be done within transaction or rollback would break it */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher index_set_error(ctx->cache->index, "Cache file too large: %s",
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (offset + size > ctx->cache->mmap_length) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic const char *
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghermail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned char *buf;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher offset = offset_to_uint32(cache->header->header_offsets[idx]);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (offset + sizeof(data_size) > cache->mmap_length) {
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher mail_cache_set_corrupted(cache, "Header %u points outside file",
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek memcpy(&data_size, buf + offset, sizeof(data_size));
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (!mmap_update(cache, offset, data_size))
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (offset + data_size > cache->mmap_length) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher mail_cache_set_corrupted(cache, "Header %u points outside file",
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (buf[offset + data_size - 1] != '\0') {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher "Header %u points to invalid string", idx);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstatic const char *const *
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghersplit_header(struct mail_cache *cache, const char *header)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buf = buffer_create_dynamic(cache->split_header_pool, 32, (size_t)-1);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher str = p_strdup(cache->split_header_pool, *tmp);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherconst char *const *mail_cache_get_header_fields(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher unsigned int idx)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const char *str;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* t_strsplit() is a bit slow, so we cache it */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (cache->header->header_offsets[idx] != cache->split_offsets[idx]) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher str = mail_cache_get_header_fields_str(cache, i);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek cache->split_headers[i] = split_header(cache, str);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherint mail_cache_set_header_fields(struct mail_cache_transaction_ctx *ctx,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher unsigned int idx, const char *const headers[])
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_assert(idx >= ctx->next_unused_header_lowwater);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher i_assert(offset_to_uint32(cache->header->header_offsets[idx]) == 0);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buffer = buffer_create_dynamic(data_stack_pool, 512, (size_t)-1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher buffer_append(buffer, *headers, strlen(*headers));
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher offset = mail_cache_append_space(ctx, size + sizeof(uint32_t));
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy((char *) cache->mmap_base + offset + sizeof(uint32_t),
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy((char *) cache->mmap_base + offset,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* update cached headers */
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher cache->split_offsets[idx] = cache->header->header_offsets[idx];
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher split_header(cache, buffer_get_data(buffer, NULL));
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher /* mark used-bit to be updated later. not really needed for
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher read-safety, but if transaction get rolled back we can't let
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek this point to invalid location. */
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher update_offset = (char *) &cache->header->header_offsets[idx] -
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher mark_update(&ctx->cache_marks, update_offset,
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher /* make sure get_header_fields() still works for this header
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek while the transaction isn't yet committed. */
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher ctx->next_unused_header_lowwater = idx + 1;
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozekcache_get_record(struct mail_cache *cache, uint32_t offset)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (!mmap_update(cache, offset, sizeof(*cache_rec) + 1024))
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek if (offset + sizeof(*cache_rec) > cache->mmap_length) {
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek mail_cache_set_corrupted(cache, "record points outside file");
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek mail_cache_set_corrupted(cache, "invalid record size");
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek mail_cache_set_corrupted(cache, "record points outside file");
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozekcache_get_next_record(struct mail_cache *cache, struct mail_cache_record *rec)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek next = cache_get_record(cache, rec->next_offset);
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek mail_cache_set_corrupted(cache, "next_offset points backwards");
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozekstatic int mail_cache_write(struct mail_cache_transaction_ctx *ctx)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const void *buf;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek buf = buffer_get_data(ctx->cache_data, &buf_size);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek write_offset = mail_cache_append_space(ctx, size);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek rec = INDEX_RECORD_AT(ctx->cache->index, ctx->last_idx);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek cache_rec = cache_get_record(cache, rec->cache_offset);
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek /* first cache record - update offset in index file */
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek i_assert(cache->index->lock_type == MAIL_LOCK_EXCLUSIVE);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* mark cache_offset to be updated later */
056302a92862fda16351d7192600746746f38e5dStephen Gallagher update_offset = (char *) &rec->cache_offset -
056302a92862fda16351d7192600746746f38e5dStephen Gallagher mark_update(&ctx->index_marks, update_offset,
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek /* find the last cache record */
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek while ((next = cache_get_next_record(cache, cache_rec)) != NULL)
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek /* mark next_offset to be updated later */
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek update_offset = (char *) &cache_rec->next_offset -
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher mark_update(&ctx->cache_marks, update_offset,
056302a92862fda16351d7192600746746f38e5dStephen Gallagher memcpy((char *) cache->mmap_base + write_offset,
056302a92862fda16351d7192600746746f38e5dStephen Gallagher memcpy((char *) cache->mmap_base + write_offset +
056302a92862fda16351d7192600746746f38e5dStephen Gallagher /* reset the write context */
056302a92862fda16351d7192600746746f38e5dStephen Gallagher memset(&ctx->cache_rec, 0, sizeof(ctx->cache_rec));
056302a92862fda16351d7192600746746f38e5dStephen Gallaghermail_cache_lookup(struct mail_cache *cache, const struct mail_index_record *rec,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned int idx;
056302a92862fda16351d7192600746746f38e5dStephen Gallagher cache->trans_ctx->first_uid <= rec->uid &&
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek (cache->trans_ctx->prev_uid != rec->uid || fields == 0 ||
056302a92862fda16351d7192600746746f38e5dStephen Gallagher (cache->trans_ctx->prev_fields & fields) != 0)) {
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagher /* we have to auto-commit since we're not capable of looking
056302a92862fda16351d7192600746746f38e5dStephen Gallagher into uncommitted records. it would be possible by checking
056302a92862fda16351d7192600746746f38e5dStephen Gallagher index_marks and cache_marks, but it's just more trouble
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek than worth. */
056302a92862fda16351d7192600746746f38e5dStephen Gallagher idx = INDEX_RECORD_INDEX(cache->index, rec);
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher if (!mail_cache_transaction_commit(cache->trans_ctx))
e59e09b5010f262228bbdeb92a79b733bf5854b3Stephen Gallagher cache_rec = cache_get_record(cache, rec->cache_offset);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekstatic int get_field_num(enum mail_cache_field field)
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic size_t get_insert_offset(struct mail_cache_transaction_ctx *ctx,
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek const unsigned char *buf;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned int mask;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagherint mail_cache_add(struct mail_cache_transaction_ctx *ctx,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek struct mail_index_record *rec, enum mail_cache_field field,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek unsigned char *buf;
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek unsigned int idx;
dd3ba5c5b7d2a9d109963ae9e6c94fff34872221Stephen Gallagher nb_data_size = uint32_to_nbo((uint32_t)data_size);
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek i_assert(mail_cache_field_sizes[field_num] == data_size);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek } else if ((field & MAIL_CACHE_STRING_MASK) != 0) {
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek i_assert(((char *) data)[data_size-1] == '\0');
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek /* NOTE: we use index because the record pointer might not last. */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek idx = INDEX_RECORD_INDEX(ctx->cache->index, rec);
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek if (ctx->last_idx != idx && ctx->last_idx != (unsigned int)-1) {
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek i_assert((ctx->cache_rec.fields & field) == 0);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek /* fields must be ordered. find where to insert it. */
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek buf = buffer_append_space_unsafe(ctx->cache_data, full_size);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek buffer_copy(ctx->cache_data, offset + full_size,
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek /* @UNSAFE */
056302a92862fda16351d7192600746746f38e5dStephen Gallagher if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek memcpy(buf, &nb_data_size, sizeof(nb_data_size));
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher memcpy(buf, data, data_size); buf += data_size;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* remember the transaction uid range */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (rec->uid < ctx->first_uid || ctx->first_uid == 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherint mail_cache_delete(struct mail_cache_transaction_ctx *ctx,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* NOTE: it would be nice to erase the cached data for the record,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher but some other processes might still be using them. So, we just
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher update the deleted_space in header */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher deleted_space = nbo_to_uint32(cache->header->deleted_space);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher deleted_space -= nbo_to_uint32(cache_rec->size);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache_rec = cache_get_next_record(cache, cache_rec);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* see if we've reached the max. deleted space in file */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher max_del_space = cache->used_file_size / 100 * COMPRESS_PERCENTAGE;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->used_file_size >= COMPRESS_MIN_SIZE)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache->index->set_flags |= MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek cache->header->deleted_space = uint32_to_nbo(deleted_space);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghermail_cache_get_fields(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache_rec = mail_cache_lookup(cache, rec, 0);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek cache_rec = cache_get_next_record(cache, cache_rec);
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozekstatic int cache_get_field(struct mail_cache *cache,
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher unsigned char *buf;
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek unsigned int mask;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher rec_size = nbo_to_uint32(cache_rec->size);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher for (i = 0, mask = 1; i < 31; i++, mask <<= 1) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* all records are at least 32bit. we have to check this
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher before getting data_size. */
6463ed1dcdd45416468b3fa178bd856b5a9ed2c3Jakub Hrozek "Record continues outside it's allocated size");
a9228ebcce14888b3123bdf46e610e0900bcd2ccJakub Hrozek memcpy(&data_size, buf + offset, sizeof(data_size));
a9228ebcce14888b3123bdf46e610e0900bcd2ccJakub Hrozek "Record continues outside it's allocated size");
a9228ebcce14888b3123bdf46e610e0900bcd2ccJakub Hrozek "Field size is 0");
a9228ebcce14888b3123bdf46e610e0900bcd2ccJakub Hrozekstatic int cache_lookup_field(struct mail_cache *cache,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache_rec = mail_cache_lookup(cache, rec, field);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher return cache_get_field(cache, cache_rec, field,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher cache_rec = cache_get_next_record(cache, cache_rec);
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozekint mail_cache_lookup_field(struct mail_cache *cache,
a23014d69b56cbdf48ad05229c334648b5309d8fJakub Hrozek if (!cache_lookup_field(cache, rec, field, &data, size_r))
056302a92862fda16351d7192600746746f38e5dStephen Gallagherconst char *mail_cache_lookup_string_field(struct mail_cache *cache,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek const void *data;
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_assert((field & MAIL_CACHE_STRING_MASK) != 0);
056302a92862fda16351d7192600746746f38e5dStephen Gallagher if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
056302a92862fda16351d7192600746746f38e5dStephen Gallagher "String field %x doesn't end with NUL", field);
2ea6196484055397cc4bc011c5960f790431fa9dStephen Gallagherint mail_cache_copy_fixed_field(struct mail_cache *cache,
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_assert((field & MAIL_CACHE_FIXED_MASK) != 0);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_panic("cache: fixed field %x wrong size "
056302a92862fda16351d7192600746746f38e5dStephen Gallaghervoid mail_cache_mark_missing(struct mail_cache *cache,
056302a92862fda16351d7192600746746f38e5dStephen Gallagher // FIXME: count these
056302a92862fda16351d7192600746746f38e5dStephen Gallaghermail_cache_get_index_flags(struct mail_cache *cache,
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozek if (!mail_cache_copy_fixed_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
64a424ec1b268427822c646f7781e26e56c197f6Jakub Hrozekint mail_cache_update_index_flags(struct mail_cache *cache,
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek if (!cache_lookup_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
7a14e8f66c0e932fe2954d792614a3b61d444bd1Jakub Hrozek "Missing index flags for record %u", rec->uid);
5ee3fba0bd812242a1ffe189f5ddf2689e6e6811Jakub Hrozekint mail_cache_update_location_offset(struct mail_cache *cache,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek if (!cache_lookup_field(cache, rec, MAIL_CACHE_LOCATION_OFFSET,
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek "Missing location offset for record %u", rec->uid);
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozekvoid *mail_cache_get_mmaped(struct mail_cache *cache, size_t *size)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozekint mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
b20208b80e99abb79c00d5ec526caa9465859c52Jakub Hrozek cache->index->inconsistent = TRUE; /* easiest way to rebuild */
be5cc3c013ece0c957f2f8c28a217052227dfd07Jakub Hrozek index_set_error(cache->index, "Corrupted index cache file %s: %s",