mail-index.c revision b79ec51bdeef6ef950eb5e890e65cc0491cf5fe9
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "buffer.h"
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen#include "mmap-util.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "read-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen#include "mail-index-private.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "mail-index-sync-private.h"
146f9076cd456ea1e9b3f8536456d9d3c962fadbStephan Bosch#include "mail-transaction-log.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "mail-cache.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen#include <stdio.h>
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen#include <stddef.h>
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen#include <time.h>
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include <sys/stat.h>
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainenstatic int mail_index_try_open_only(struct mail_index *index);
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainenstruct mail_index *mail_index_alloc(const char *dir, const char *prefix)
1a5573ebc32fae2fe576ec544e1781323c1db609Timo Sirainen{
1a5573ebc32fae2fe576ec544e1781323c1db609Timo Sirainen struct mail_index *index;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index = i_new(struct mail_index, 1);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->dir = i_strdup(dir);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->prefix = i_strdup(prefix);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->fd = -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->extension_pool = pool_alloconly_create("extension", 256);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->extensions = buffer_create_dynamic(index->extension_pool, 64);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->sync_handlers = buffer_create_dynamic(default_pool, 64);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen index->sync_lost_handlers = buffer_create_dynamic(default_pool, 64);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen index->expunge_handlers = buffer_create_dynamic(default_pool, 32);
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen index->mode = 0600;
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen index->gid = (gid_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen index->keywords_ext_id =
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen mail_index_ext_register(index, "keywords", 128, 2, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->keywords_pool = pool_alloconly_create("keywords", 512);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen index->keywords_buf = buffer_create_dynamic(default_pool, 64);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen buffer_append_zero(index->keywords_buf, sizeof(const char *));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen index->keywords = index->keywords_buf->data;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return index;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenvoid mail_index_free(struct mail_index *index)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen{
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen mail_index_close(index);
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen pool_unref(index->extension_pool);
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen pool_unref(index->keywords_pool);
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen buffer_free(index->sync_handlers);
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen buffer_free(index->sync_lost_handlers);
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen buffer_free(index->expunge_handlers);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer_free(index->keywords_buf);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_free(index->error);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_free(index->dir);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_free(index->prefix);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_free(index);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenvoid mail_index_set_permissions(struct mail_index *index,
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen mode_t mode, gid_t gid)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen{
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen index->mode = mode & 0666;
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen index->gid = gid;
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen}
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainenuint32_t mail_index_ext_register(struct mail_index *index, const char *name,
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen uint32_t default_hdr_size,
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen uint16_t default_record_size,
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen uint16_t default_record_align)
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen{
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen const struct mail_index_ext *extensions;
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen struct mail_index_ext ext;
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen size_t ext_count;
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen unsigned int i;
d46a1e3f999dda802dc5137e883adcd7a6629cd3Timo Sirainen
d46a1e3f999dda802dc5137e883adcd7a6629cd3Timo Sirainen extensions = buffer_get_data(index->extensions, &ext_count);
d46a1e3f999dda802dc5137e883adcd7a6629cd3Timo Sirainen ext_count /= sizeof(*extensions);
d1e843e77f4760e303c53d9fce10123fc8d230a1Timo Sirainen
d1e843e77f4760e303c53d9fce10123fc8d230a1Timo Sirainen i_assert(index->sync_handlers->used /
d1e843e77f4760e303c53d9fce10123fc8d230a1Timo Sirainen sizeof(struct mail_index_sync_handler) == ext_count);
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen /* see if it's already there */
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen for (i = 0; i < ext_count; i++) {
08e9fd42eb8007e1f9db62c088eef74f906114a5Josef 'Jeff' Sipek if (strcmp(extensions[i].name, name) == 0)
08e9fd42eb8007e1f9db62c088eef74f906114a5Josef 'Jeff' Sipek return i;
08e9fd42eb8007e1f9db62c088eef74f906114a5Josef 'Jeff' Sipek }
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen memset(&ext, 0, sizeof(ext));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext.name = p_strdup(index->extension_pool, name);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext.index_idx = ext_count;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext.hdr_size = default_hdr_size;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext.record_size = default_record_size;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext.record_align = default_record_align;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen buffer_append(index->extensions, &ext, sizeof(ext));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen buffer_append_zero(index->sync_handlers,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen sizeof(struct mail_index_sync_handler));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen return ext_count;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen}
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainenvoid mail_index_register_expunge_handler(struct mail_index *index,
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen uint32_t ext_id,
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen mail_index_expunge_handler_t *cb)
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer_write(index->expunge_handlers, ext_id * sizeof(cb),
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen &cb, sizeof(cb));
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainenvoid mail_index_register_sync_handler(struct mail_index *index, uint32_t ext_id,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_sync_handler_t *cb,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum mail_index_sync_handler_type type)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen struct mail_index_sync_handler h;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen memset(&h, 0, sizeof(h));
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen h.callback = cb;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen h.type = type;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen buffer_write(index->sync_handlers, ext_id * sizeof(h), &h, sizeof(h));
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen}
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainen
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainenvoid mail_index_register_sync_lost_handler(struct mail_index *index,
7900eb30bac4a46b259522c58362884661483d7cJosef 'Jeff' Sipek mail_index_sync_lost_handler_t *cb)
7900eb30bac4a46b259522c58362884661483d7cJosef 'Jeff' Sipek{
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen buffer_append(index->sync_lost_handlers, &cb, sizeof(cb));
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvi
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvistatic void mail_index_map_init_extbufs(struct mail_index_map *map,
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvi unsigned int initial_count)
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen{
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen size_t ext_size, ext_id_map_size, size;
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen ext_size = initial_count * sizeof(struct mail_index_ext);
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen ext_id_map_size = initial_count * sizeof(uint32_t);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (map->extension_pool == NULL) {
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen size = ext_size + ext_id_map_size +
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen (initial_count * 20); /* for names */
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen map->extension_pool =
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen pool_alloconly_create("extensions",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen nearest_power(size));
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen } else {
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen p_clear(map->extension_pool);
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen }
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen map->extensions = buffer_create_dynamic(map->extension_pool, ext_size);
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen map->ext_id_map =
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen buffer_create_dynamic(map->extension_pool, ext_id_map_size);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenuint32_t mail_index_map_lookup_ext(struct mail_index_map *map, const char *name)
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen{
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen const struct mail_index_ext *extensions;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen size_t i, size;
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvi
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (map->extensions == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return (uint32_t)-1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extensions = buffer_get_data(map->extensions, &size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size /= sizeof(*extensions);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen for (i = 0; i < size; i++) {
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen if (strcmp(extensions[i].name, name) == 0)
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen return i;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen }
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen return (uint32_t)-1;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen}
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainenuint32_t
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainenmail_index_map_register_ext(struct mail_index *index,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen struct mail_index_map *map, const char *name,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen uint32_t hdr_offset, uint32_t hdr_size,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen uint32_t record_offset, uint32_t record_size,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen uint32_t record_align, uint32_t reset_id)
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen{
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen struct mail_index_ext *ext;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen uint32_t idx, ext_id, empty_id = (uint32_t)-1;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen if (map->extensions == NULL) {
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen mail_index_map_init_extbufs(map, 5);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen idx = 0;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen idx = map->extensions->used / sizeof(*ext);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen i_assert(mail_index_map_lookup_ext(map, name) == (uint32_t)-1);
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen ext = buffer_append_space_unsafe(map->extensions, sizeof(*ext));
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen memset(ext, 0, sizeof(*ext));
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext->name = p_strdup(map->extension_pool, name);
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->hdr_offset = hdr_offset;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->hdr_size = hdr_size;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->record_offset = record_offset;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->record_size = record_size;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->record_align = record_align;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen ext->reset_id = reset_id;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen ext_id = mail_index_ext_register(index, name, hdr_size,
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen record_size, record_align);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen ext->index_idx = ext_id;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen while (map->ext_id_map->used < ext_id * sizeof(uint32_t))
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen buffer_append(map->ext_id_map, &empty_id, sizeof(empty_id));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_write(map->ext_id_map, ext_id * sizeof(uint32_t),
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen &idx, sizeof(idx));
6cc4cce2078aca138fbce19305e69e77edcee614Timo Sirainen return idx;
bd6a8056771b6150685dea319ab5a94e021d17f1Josef 'Jeff' Sipek}
9f37ef2a9192e7d47e3d7ac959080fd01120f2e9Aki Tuomi
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainenstatic int size_check(size_t *size_left, size_t size)
bd6a8056771b6150685dea319ab5a94e021d17f1Josef 'Jeff' Sipek{
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen if (size > *size_left)
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen return FALSE;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen *size_left -= size;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen return TRUE;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen}
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainenstatic size_t get_align(size_t name_len)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen{
aa47c9bd1d1fc70cd699c49fd1ca92dbc7615953Timo Sirainen size_t size = sizeof(struct mail_index_ext_header) + name_len;
aa47c9bd1d1fc70cd699c49fd1ca92dbc7615953Timo Sirainen return MAIL_INDEX_HEADER_SIZE_ALIGN(size) - size;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen}
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
aa47c9bd1d1fc70cd699c49fd1ca92dbc7615953Timo Sirainenstatic int mail_index_read_extensions(struct mail_index *index,
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen struct mail_index_map *map)
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen{
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen const struct mail_index_ext_header *ext_hdr;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen unsigned int i, old_count;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen const char *name;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen uint32_t ext_id, offset, name_offset;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen size_t size_left;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen /* extension headers always start from 64bit offsets, so if base header
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen doesn't happen to be 64bit aligned we'll skip some bytes */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen offset = MAIL_INDEX_HEADER_SIZE_ALIGN(map->hdr.base_header_size);
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen if (offset >= map->hdr.header_size && map->extension_pool == NULL) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen /* nothing to do, skip allocatations and all */
4b41116563110d00330896a568eff1078c382827Timo Sirainen return 1;
4b41116563110d00330896a568eff1078c382827Timo Sirainen }
4b41116563110d00330896a568eff1078c382827Timo Sirainen
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen old_count = index->extensions->used / sizeof(struct mail_index_ext);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen mail_index_map_init_extbufs(map, old_count + 5);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen ext_id = (uint32_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < old_count; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append(map->ext_id_map, &ext_id, sizeof(ext_id));
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen while (offset < map->hdr.header_size) {
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_left = map->hdr.header_size - offset;
dbe64f3893616a4005c8946be75d2dc8f6823d72Timo Sirainen if (!size_check(&size_left, sizeof(*ext_hdr)) ||
8a13b020f90e080570658b18c042e7e352c8b14fTimo Sirainen !size_check(&size_left, ext_hdr->name_size) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen !size_check(&size_left, get_align(ext_hdr->name_size)) ||
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen !size_check(&size_left, ext_hdr->hdr_size)) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen "Header extension goes outside header",
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->filepath);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen offset += sizeof(*ext_hdr);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen name_offset = offset;
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen offset += ext_hdr->name_size + get_align(ext_hdr->name_size);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_push();
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen name = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen ext_hdr->name_size);
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen if (mail_index_map_lookup_ext(map, name) != (uint32_t)-1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen "Duplicate header extension %s",
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen index->filepath, name);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen t_pop();
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen return -1;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen }
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen mail_index_map_register_ext(index, map, name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen offset, ext_hdr->hdr_size,
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen ext_hdr->record_offset,
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen ext_hdr->record_size,
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen ext_hdr->record_align,
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen ext_hdr->reset_id);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen t_pop();
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenint mail_index_map_read_keywords(struct mail_index *index,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct mail_index_map *map)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const struct mail_index_ext *ext;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen const struct mail_index_keyword_header *kw_hdr;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen const struct mail_index_keyword_header_rec *kw_rec;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen const char *name, **keywords_list;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen unsigned int i, name_len;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen uint32_t ext_id;
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen ext_id = mail_index_map_lookup_ext(map, "keywords");
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen if (ext_id == (uint32_t)-1) {
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen map->keywords = NULL;
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen map->keywords_count = 0;
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen return 0;
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen }
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen ext = map->extensions->data;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen ext += ext_id;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen kw_rec = (const void *)(kw_hdr + 1);
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen name = (const char *)(kw_rec + kw_hdr->keywords_count);
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen if ((size_t)(name - (const char *)kw_hdr) > ext->hdr_size) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen "keywords_count larger than header size",
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen index->filepath);
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen return -1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* make sure the header is valid */
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen name_len = (const char *)kw_hdr + ext->hdr_size - name;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen for (i = 0; i < kw_hdr->keywords_count; i++) {
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen if (kw_rec[i].name_offset > name_len) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
d938e9e4ec4c0f326dffd5ebe42c1ad893ce7e52Timo Sirainen "name_offset points outside allocated header",
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainen index->filepath);
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainen return -1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen if (name[name_len-1] != '\0') {
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen "header doesn't end with NUL",
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen index->filepath);
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen return -1;
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen }
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen if (map->keywords_pool == NULL)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen map->keywords_pool = pool_alloconly_create("keywords", 1024);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen /* Save keywords in memory. Only new keywords should come into the
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mapping, so keep the existing keyword strings in memory to allow
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen mail_index_lookup_keywords() to safely return direct pointers
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen into them. */
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen if (kw_hdr->keywords_count < map->keywords_count) {
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
4b41218f9e09386ad5d8f279355aad1dfb64392dTimo Sirainen "Keywords removed unexpectedly",
4b41218f9e09386ad5d8f279355aad1dfb64392dTimo Sirainen index->filepath);
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen return -1;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen }
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen if (kw_hdr->keywords_count == map->keywords_count) {
eb1572d7c44ebc7b0b039d085c3dbab2ef7043ddTimo Sirainen /* nothing changed */
eb1572d7c44ebc7b0b039d085c3dbab2ef7043ddTimo Sirainen return 0;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen }
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* @UNSAFE */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen keywords_list = p_new(map->keywords_pool,
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen const char *, kw_hdr->keywords_count + 1);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen for (i = 0; i < map->keywords_count; i++)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen keywords_list[i] = map->keywords[i];
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen for (; i < kw_hdr->keywords_count; i++) {
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen keywords_list[i] = p_strdup(map->keywords_pool,
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen name + kw_rec[i].name_offset);
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen }
382f23541ce657be87b079abd6784d376fb4eb43Timo Sirainen map->keywords = keywords_list;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen map->keywords_count = kw_hdr->keywords_count;
97a5e150cacf7da9dd200e317b75460352038d65Timo Sirainen return 0;
3743948b65bd079cd6f2502d35bd21666c1a2f5aAki Tuomi}
134987bc3b685d7ec71c04e9337f7125cff0698fTimo Sirainen
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainenconst char *const *mail_index_get_keywords(struct mail_index *index)
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen{
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen (void)mail_index_map_read_keywords(index, index->map);
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen return index->map->keywords;
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen}
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen
6ae329de09afb7214c906d762320847e05469d53Timo Sirainenstatic int mail_index_check_header(struct mail_index *index,
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen struct mail_index_map *map)
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen{
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen const struct mail_index_header *hdr = &map->hdr;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen unsigned char compat_data[sizeof(hdr->compat_data)];
7e2671b295927b461adc8b6c4ed6a1c4761fb323Timo Sirainen
7e2671b295927b461adc8b6c4ed6a1c4761fb323Timo Sirainen memset(compat_data, 0, sizeof(compat_data));
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen#ifndef WORDS_BIGENDIAN
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainen compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainen#endif
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainen compat_data[1] = sizeof(uoff_t);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen compat_data[2] = sizeof(time_t);
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* major version change - handle silently(?) */
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen return -1;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (memcmp(hdr->compat_data, compat_data, sizeof(compat_data)) != 0) {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen /* architecture change - handle silently(?) */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen if ((map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* we've already complained about it */
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
3857e2945a3b6744d603f0f5a656849ed8436ba3Timo Sirainen
3857e2945a3b6744d603f0f5a656849ed8436ba3Timo Sirainen /* following some extra checks that only take a bit of CPU */
f90cbe597c41d5cc91debd371f8648bd8e6ffbc2Timo Sirainen if (hdr->uid_validity == 0 && hdr->next_uid != 1) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
33b469d1ca66dd2cc496d2d990b8b98e72952a29Timo Sirainen "uid_validity = 0, next_uid = %u",
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen index->filepath, hdr->next_uid);
58ba0fe5a6904d3a65cfe268411f4cbb881234eeTimo Sirainen return -1;
58ba0fe5a6904d3a65cfe268411f4cbb881234eeTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
6b0d8106ae51ffc6ce45636b34d2e21cbefca7fdTimo Sirainen if (hdr->record_size < sizeof(struct mail_index_record)) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen "record_size too small: %u < %"PRIuSIZE_T,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen index->filepath, hdr->record_size,
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen sizeof(struct mail_index_record));
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen }
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen if (hdr->next_uid == 0)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return 0;
b3484b5b1f47e4cf112f0e371478a2d7794b31bbTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (hdr->recent_messages_count > hdr->messages_count ||
d81131d3bbb4f0befb62a661d1785cf8c84a17e2Timo Sirainen hdr->seen_messages_count > hdr->messages_count ||
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen hdr->deleted_messages_count > hdr->messages_count)
9456a4a3e74929f9d3d5b00b93be6d8eb69bc52aTimo Sirainen return 0;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (hdr->first_recent_uid_lowwater > hdr->next_uid ||
4e3bcf7fdaeef92dd07a2acb1ded58422a907e87Timo Sirainen hdr->first_unseen_uid_lowwater > hdr->next_uid ||
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen hdr->first_deleted_uid_lowwater > hdr->next_uid)
19e161dd9e2c3a2ffc96ee8852bec0720cb30d1cTimo Sirainen return 0;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
540555c5b435203e1c26c8e7b924b2643ae07ae3Timo Sirainen return mail_index_read_extensions(index, map);
540555c5b435203e1c26c8e7b924b2643ae07ae3Timo Sirainen}
540555c5b435203e1c26c8e7b924b2643ae07ae3Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic void mail_index_map_clear(struct mail_index *index,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen struct mail_index_map *map)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen if (map->buffer != NULL) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(map->mmap_base == NULL);
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen buffer_free(map->buffer);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen map->buffer = NULL;
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen } else if (map->mmap_base != NULL) {
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen i_assert(map->buffer == NULL);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (munmap(map->mmap_base, map->mmap_size) < 0)
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen mail_index_set_syscall_error(index, "munmap()");
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen map->mmap_base = NULL;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (map->refcount > 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen memset(&map->hdr, 0, sizeof(map->hdr));
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen map->mmap_size = 0;
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen map->mmap_used_size = 0;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen map->records = NULL;
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen map->records_count = 0;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen}
c0225f7f6b43d34dc58c17d3304f0fd60ab89894Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenvoid mail_index_unmap(struct mail_index *index, struct mail_index_map *map)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (--map->refcount > 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_assert(map->refcount == 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mail_index_map_clear(index, map);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (map->extension_pool != NULL)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen pool_unref(map->extension_pool);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (map->keywords_pool != NULL)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen pool_unref(map->keywords_pool);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen buffer_free(map->hdr_copy_buf);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_free(map);
6df0ab0c1ab91f06b6418cb30eff44405a1b8f02Timo Sirainen}
6df0ab0c1ab91f06b6418cb30eff44405a1b8f02Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic void mail_index_unmap_forced(struct mail_index *index,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen struct mail_index_map *map)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen{
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_index_map_clear(index, map);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_index_unmap(index, map);
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen}
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic void mail_index_map_copy_hdr(struct mail_index_map *map,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen const struct mail_index_header *hdr)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen{
21aaa6affb9f134112b75b5db737309fc35ef1cfMartti Rannanjärvi if (hdr->base_header_size < sizeof(map->hdr)) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* header smaller than ours, make a copy so our newer headers
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen won't have garbage in them */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen memset(&map->hdr, 0, sizeof(map->hdr));
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen memcpy(&map->hdr, hdr, hdr->base_header_size);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen } else {
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen map->hdr = *hdr;
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen }
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen}
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainenstatic int mail_index_mmap(struct mail_index *index, struct mail_index_map *map)
0206dc57f2c04da69599dea5816235cfeb2b897aMartti Rannanjärvi{
0206dc57f2c04da69599dea5816235cfeb2b897aMartti Rannanjärvi const struct mail_index_header *hdr;
6564208826b0f46a00f010d1b5711d85944c3c88Timo Sirainen unsigned int records_count;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_assert(!map->write_to_disk);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (map->buffer != NULL) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* we had temporarily used a buffer, eg. for updating index */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen buffer_free(map->buffer);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen map->buffer = NULL;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen map->mmap_base = index->readonly ?
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen mmap_ro_file(index->fd, &map->mmap_size) :
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen mmap_rw_file(index->fd, &map->mmap_size);
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen if (map->mmap_base == MAP_FAILED) {
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen map->mmap_base = NULL;
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen mail_index_set_syscall_error(index, "mmap()");
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen return -1;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen hdr = map->mmap_base;
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen if (map->mmap_size >
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen offsetof(struct mail_index_header, major_version) &&
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen /* major version change - handle silently */
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen return 0;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen }
2c8ca7e88ec881c473fb90e5f647c1f563877164Timo Sirainen
2c8ca7e88ec881c473fb90e5f647c1f563877164Timo Sirainen if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
6c51e3c3dc8dc5dc6fef2280b7c2c9ebef3de8f0Aki Tuomi mail_index_set_error(index, "Corrupted index file %s: "
6c51e3c3dc8dc5dc6fef2280b7c2c9ebef3de8f0Aki Tuomi "File too small (%"PRIuSIZE_T")",
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen index->filepath, map->mmap_size);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e4c90f0b88e40a8f92b8f5e1f1a3ea701e5c965cTimo Sirainen map->mmap_used_size = hdr->header_size +
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen hdr->messages_count * hdr->record_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (map->mmap_used_size > map->mmap_size) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen records_count = (map->mmap_size - hdr->header_size) /
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen hdr->record_size;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen "messages_count too large (%u > %u)",
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen index->filepath, hdr->messages_count,
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen records_count);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen return 0;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen }
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen mail_index_map_copy_hdr(map, hdr);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen map->hdr_base = map->mmap_base;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen map->records = PTR_OFFSET(map->mmap_base, map->hdr.header_size);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen map->records_count = map->hdr.messages_count;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen return 1;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen}
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainenstatic int mail_index_read_header(struct mail_index *index,
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen struct mail_index_header *hdr, size_t *pos_r)
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen{
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen size_t pos;
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen int ret;
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(hdr, 0, sizeof(*hdr));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen ret = 1;
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen for (pos = 0; ret > 0 && pos < sizeof(*hdr); ) {
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen ret = pread(index->fd, PTR_OFFSET(hdr, pos),
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen sizeof(*hdr) - pos, pos);
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen if (ret > 0)
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen pos += ret;
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen }
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen *pos_r = pos;
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen return ret;
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen}
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainenstatic int mail_index_read_map(struct mail_index *index,
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen struct mail_index_map *map, int *retry_r)
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen{
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen struct mail_index_header hdr;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen void *data = NULL;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen ssize_t ret;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen size_t pos, records_size;
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen i_assert(map->mmap_base == NULL);
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen *retry_r = FALSE;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen ret = mail_index_read_header(index, &hdr, &pos);
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) &&
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen hdr.major_version != MAIL_INDEX_MAJOR_VERSION) {
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen /* major version change - handle silently */
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen return 0;
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen }
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen (ret > 0 || pos >= hdr.base_header_size)) {
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen if (hdr.base_header_size < MAIL_INDEX_HEADER_MIN_SIZE ||
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen hdr.header_size < hdr.base_header_size) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
900bb5e316d030cdebff7ee128ce65881dfb27f7Timo Sirainen "Corrupted header sizes (base %u, full %u)",
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen index->filepath, hdr.base_header_size,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen hdr.header_size);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_reset(map->hdr_copy_buf);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append(map->hdr_copy_buf, &hdr, hdr.base_header_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* @UNSAFE */
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen data = buffer_append_space_unsafe(map->hdr_copy_buf,
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen hdr.header_size -
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen hdr.base_header_size);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen ret = pread_full(index->fd, data,
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen hdr.header_size - hdr.base_header_size,
d9076f5939edf5d20a261494b1a861dcbb0d32e2Timo Sirainen hdr.base_header_size);
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (ret > 0) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen records_size = hdr.messages_count * hdr.record_size;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (map->buffer == NULL) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen map->buffer = buffer_create_dynamic(default_pool,
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen records_size);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen /* @UNSAFE */
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen buffer_set_used_size(map->buffer, 0);
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen data = buffer_append_space_unsafe(map->buffer, records_size);
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen ret = pread_full(index->fd, data, records_size,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen hdr.header_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (ret < 0) {
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (errno == ESTALE) {
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen *retry_r = TRUE;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return 0;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen }
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen mail_index_set_syscall_error(index, "pread_full()");
0cce885512b836ce021260a58e7b4f099b36d0f1Timo Sirainen return -1;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen }
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (ret == 0) {
61cae213dc69070c1cef266578057710e34e0d12Timo Sirainen mail_index_set_error(index,
0cce885512b836ce021260a58e7b4f099b36d0f1Timo Sirainen "Corrupted index file %s: File too small",
9617ac7078a17bd346fed69526861c3e7fd9d809Timo Sirainen index->filepath);
4d527c363482be2b65dd0573d878ecda86cbb0bbTimo Sirainen return 0;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen map->records = data;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen map->records_count = hdr.messages_count;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen mail_index_map_copy_hdr(map, &hdr);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen map->hdr_base = map->hdr_copy_buf->data;
d0d37a769d7e54646de025a43d2cb888478cb218Timo Sirainen
d0d37a769d7e54646de025a43d2cb888478cb218Timo Sirainen index->sync_log_file_seq = hdr.log_file_seq;
d0d37a769d7e54646de025a43d2cb888478cb218Timo Sirainen index->sync_log_file_offset = hdr.log_file_int_offset;
d0d37a769d7e54646de025a43d2cb888478cb218Timo Sirainen return 1;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen}
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic int mail_index_sync_from_transactions(struct mail_index *index,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_index_map **map,
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen int sync_to_index)
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen{
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen const struct mail_index_header *map_hdr = &(*map)->hdr;
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen struct mail_index_view *view;
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainen struct mail_transaction_log_view *log_view;
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainen struct mail_index_sync_map_ctx sync_map_ctx;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct mail_index_header hdr;
ba8ff75a149d6936f769a2d1dfceaab9da87863bTimo Sirainen const struct mail_transaction_header *thdr;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen const void *tdata;
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen uint32_t prev_seq, max_seq;
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen uoff_t prev_offset, max_offset;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen size_t pos;
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen int ret, skipped;
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (sync_to_index) {
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen /* read the real log position where we are supposed to be
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen synced */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen ret = mail_index_read_header(index, &hdr, &pos);
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen if (ret < 0 && errno != ESTALE) {
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen mail_index_set_syscall_error(index, "pread()");
05150df8fbda6b8fdfafcfb0aa9003f1da4ecacaTimo Sirainen return -1;
897368f75a68f97e172ba76540759a10c557f1c3Timo Sirainen }
897368f75a68f97e172ba76540759a10c557f1c3Timo Sirainen if (pos < MAIL_INDEX_HEADER_MIN_SIZE)
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen return 0;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (map_hdr->log_file_seq == hdr.log_file_seq &&
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen map_hdr->log_file_int_offset == hdr.log_file_int_offset) {
64b61cd24d630223478ccbe1934b9f60f0881f59Timo Sirainen /* nothing to do */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return 1;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (map_hdr->log_file_seq > hdr.log_file_seq ||
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen (map_hdr->log_file_seq == hdr.log_file_seq &&
de58be41126e5d68008d2ea706d62ccdc1f29337Timo Sirainen map_hdr->log_file_int_offset > hdr.log_file_int_offset)) {
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen /* we went too far, have to re-read the file */
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen return 0;
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen }
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen if (map_hdr->log_file_ext_offset !=
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen map_hdr->log_file_int_offset ||
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen hdr.log_file_ext_offset != hdr.log_file_int_offset) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* too much trouble to get this right. */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen max_seq = hdr.log_file_seq;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen max_offset = hdr.log_file_int_offset;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen } else {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* sync everything there is */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen max_seq = (uint32_t)-1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen max_offset = (uoff_t)-1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen log_view = mail_transaction_log_view_open(index->log);
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen if (mail_transaction_log_view_set(log_view,
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen map_hdr->log_file_seq,
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen map_hdr->log_file_int_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen max_seq, max_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen MAIL_TRANSACTION_TYPE_MASK) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_transaction_log_view_close(log_view);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen index->map = *map;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1f1ee8db68d9ae1604350801cd8dc33ebe29fe8aTimo Sirainen view = mail_index_view_open(index);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen mail_index_sync_map_init(&sync_map_ctx, view,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen MAIL_INDEX_SYNC_HANDLER_VIEW);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
268b72128fc3400912e9a6b83faf4950a367c2ffTimo Sirainen while ((ret = mail_transaction_log_view_next(log_view, &thdr, &tdata,
268b72128fc3400912e9a6b83faf4950a367c2ffTimo Sirainen &skipped)) > 0) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (mail_index_sync_record(&sync_map_ctx, thdr, tdata) < 0) {
6c51e3c3dc8dc5dc6fef2280b7c2c9ebef3de8f0Aki Tuomi ret = -1;
6c51e3c3dc8dc5dc6fef2280b7c2c9ebef3de8f0Aki Tuomi break;
6c51e3c3dc8dc5dc6fef2280b7c2c9ebef3de8f0Aki Tuomi }
d9a129b491613014ce5f31fe1ab20903e2899ea4Timo Sirainen }
d9a129b491613014ce5f31fe1ab20903e2899ea4Timo Sirainen
d9a129b491613014ce5f31fe1ab20903e2899ea4Timo Sirainen mail_transaction_log_view_get_prev_pos(log_view, &prev_seq,
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen &prev_offset);
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen index->map->hdr.log_file_seq = prev_seq;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen index->map->hdr.log_file_int_offset =
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen index->map->hdr.log_file_ext_offset = prev_offset;
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen
b83deefd2cf1e293373673eefb4d5cf60907978cTimo Sirainen mail_index_sync_map_deinit(&sync_map_ctx);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mail_index_view_close(view);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mail_transaction_log_view_close(log_view);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen *map = index->map;
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen index->map = NULL;
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (sync_to_index) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen /* make sure log file offsets get copied. most of the other
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen fields should stay the same. */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen i_assert(hdr.messages_count == (*map)->hdr.messages_count);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen (*map)->hdr = hdr;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen return ret < 0 ? -1 : 1;
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen}
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainenstatic int mail_index_read_map_with_retry(struct mail_index *index,
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen struct mail_index_map **map,
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen int sync_to_index)
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen{
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen mail_index_sync_lost_handler_t *const *handlers;
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen size_t size;
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen unsigned int i;
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen int ret, retry;
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen if (index->log_locked) {
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen /* we're most likely syncing the index and we really don't
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen want to read more than what was synced last time. */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen sync_to_index = TRUE;
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen }
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen if ((*map)->hdr.indexid != 0) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* sync this as a view from transaction log. */
b3b4f3875850099c9292ad74d08bb385c3988f8fTimo Sirainen index->sync_update = TRUE;
7a2d48763b43c4fd8bc17c444271f0b802113a5fTimo Sirainen ret = mail_index_sync_from_transactions(index, map,
7a2d48763b43c4fd8bc17c444271f0b802113a5fTimo Sirainen sync_to_index);
897368f75a68f97e172ba76540759a10c557f1c3Timo Sirainen index->sync_update = FALSE;
7a2d48763b43c4fd8bc17c444271f0b802113a5fTimo Sirainen if (ret != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
/* transaction log lost/broken, fallback to re-reading it */
}
/* notify all "sync lost" handlers */
handlers = buffer_get_data(index->sync_lost_handlers, &size);
size /= sizeof(*handlers);
for (i = 0; i < size; i++)
(*handlers[i])(index);
for (i = 0; i < MAIL_INDEX_ESTALE_RETRY_COUNT; i++) {
ret = mail_index_read_map(index, *map, &retry);
if (ret != 0 || !retry)
return ret;
/* ESTALE - reopen index file */
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
ret = mail_index_try_open_only(index);
if (ret <= 0) {
if (ret == 0) {
/* the file was lost */
errno = ENOENT;
mail_index_set_syscall_error(index, "open()");
}
return -1;
}
}
/* Too many ESTALE retries */
mail_index_set_syscall_error(index, "read_map()");
return -1;
}
static int mail_index_map_try_existing(struct mail_index_map *map)
{
const struct mail_index_header *hdr;
size_t used_size;
if (MAIL_INDEX_MAP_IS_IN_MEMORY(map))
return 0;
hdr = map->mmap_base;
/* always check corrupted-flag to avoid errors later */
if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0)
return -1;
used_size = hdr->header_size + hdr->messages_count * hdr->record_size;
if (map->mmap_size >= used_size && map->hdr_base == hdr) {
map->records_count = hdr->messages_count;
mail_index_map_copy_hdr(map, hdr);
return 1;
}
return 0;
}
int mail_index_map(struct mail_index *index, int force)
{
struct mail_index_map *map;
int ret;
i_assert(index->map == NULL || index->map->refcount > 0);
i_assert(index->lock_type != F_UNLCK);
if (!force && index->map != NULL) {
i_assert(index->hdr != NULL);
ret = mail_index_map_try_existing(index->map);
if (ret != 0)
return ret;
if (index->lock_type == F_WRLCK) {
/* we're syncing, don't break the mapping */
return 1;
}
}
if (index->map != NULL && index->map->refcount > 1) {
/* this map is already used by some views and they may have
pointers into it. leave them and create a new mapping. */
if (!index->mmap_disable) {
map = NULL;
} else {
/* create a copy of the mapping instead so we don't
have to re-read it */
map = mail_index_map_clone(index->map,
index->map->hdr.record_size);
}
index->map->refcount--;
index->map = NULL;
} else {
map = index->map;
}
if (map == NULL) {
map = i_new(struct mail_index_map, 1);
map->refcount = 1;
map->hdr_copy_buf =
buffer_create_dynamic(default_pool, sizeof(map->hdr));
} else if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
i_assert(!map->write_to_disk);
} else if (map->mmap_base != NULL) {
i_assert(map->buffer == NULL);
if (munmap(map->mmap_base, map->mmap_size) < 0)
mail_index_set_syscall_error(index, "munmap()");
map->mmap_base = NULL;
}
index->hdr = NULL;
index->map = NULL;
if (!index->mmap_disable)
ret = mail_index_mmap(index, map);
else
ret = mail_index_read_map_with_retry(index, &map, force);
i_assert(index->map == NULL);
if (ret <= 0) {
mail_index_unmap_forced(index, map);
return ret;
}
ret = mail_index_check_header(index, map);
if (ret < 0) {
mail_index_unmap_forced(index, map);
return 0;
}
if (ret == 0)
index->fsck = TRUE;
index->hdr = &map->hdr;
index->map = map;
i_assert(map->hdr.messages_count == map->records_count);
return 1;
}
struct mail_index_map *
mail_index_map_clone(struct mail_index_map *map, uint32_t new_record_size)
{
struct mail_index_map *mem_map;
struct mail_index_header *hdr;
struct mail_index_ext *extensions;
void *src, *dest;
size_t size, copy_size;
unsigned int i, count;
size = map->records_count * new_record_size;
mem_map = i_new(struct mail_index_map, 1);
mem_map->refcount = 1;
mem_map->buffer = buffer_create_dynamic(default_pool, size);
if (map->hdr.record_size == new_record_size)
buffer_append(mem_map->buffer, map->records, size);
else {
copy_size = I_MIN(map->hdr.record_size, new_record_size);
src = map->records;
for (i = 0; i < map->records_count; i++) {
dest = buffer_append_space_unsafe(mem_map->buffer,
new_record_size);
memcpy(dest, src, copy_size);
src = PTR_OFFSET(src, map->hdr.record_size);
}
}
mem_map->records = buffer_get_modifyable_data(mem_map->buffer, NULL);
mem_map->records_count = map->records_count;
mem_map->hdr_copy_buf =
buffer_create_dynamic(default_pool, map->hdr.header_size);
if (map->hdr.base_header_size < sizeof(*hdr))
buffer_append_zero(mem_map->hdr_copy_buf, sizeof(*hdr));
buffer_write(mem_map->hdr_copy_buf, 0,
&map->hdr, map->hdr.base_header_size);
buffer_append(mem_map->hdr_copy_buf,
CONST_PTR_OFFSET(map->hdr_base,
map->hdr.base_header_size),
map->hdr.header_size - map->hdr.base_header_size);
hdr = buffer_get_modifyable_data(mem_map->hdr_copy_buf, NULL);
if (hdr->base_header_size < sizeof(*hdr))
hdr->base_header_size = sizeof(*hdr);
hdr->record_size = new_record_size;
mem_map->hdr = *hdr;
mem_map->hdr_base = hdr;
/* copy extensions */
if (map->ext_id_map != NULL) {
count = map->ext_id_map->used / sizeof(uint32_t);
mail_index_map_init_extbufs(mem_map, count + 2);
buffer_append_buf(mem_map->extensions, map->extensions,
0, (size_t)-1);
buffer_append_buf(mem_map->ext_id_map, map->ext_id_map,
0, (size_t)-1);
/* fix the name pointers to use our own pool */
extensions = buffer_get_modifyable_data(mem_map->extensions,
NULL);
for (i = 0; i < count; i++) {
extensions[i].name = p_strdup(mem_map->extension_pool,
extensions[i].name);
}
}
return mem_map;
}
int mail_index_map_get_ext_idx(struct mail_index_map *map,
uint32_t ext_id, uint32_t *idx_r)
{
const uint32_t *id_map;
if (map->ext_id_map == NULL ||
map->ext_id_map->used / sizeof(*id_map) <= ext_id)
return 0;
id_map = map->ext_id_map->data;
*idx_r = id_map[ext_id];
return *idx_r != (uint32_t)-1;
}
static int mail_index_try_open_only(struct mail_index *index)
{
int i;
for (i = 0; i < 3; i++) {
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 || errno != ESTALE)
break;
/* May happen with some OSes with NFS. Try again, although
there's still a race condition with another computer
creating the index file again. However, we can't try forever
as ESTALE happens also if index directory has been deleted
from server.. */
}
if (index->fd == -1) {
if (errno != ENOENT)
return mail_index_set_syscall_error(index, "open()");
/* have to create it */
return 0;
}
return 1;
}
static int
mail_index_try_open(struct mail_index *index, unsigned int *lock_id_r)
{
unsigned int lock_id;
int ret;
if (lock_id_r != NULL)
*lock_id_r = 0;
ret = mail_index_try_open_only(index);
if (ret <= 0)
return ret;
if (mail_index_lock_shared(index, FALSE, &lock_id) < 0) {
(void)close(index->fd);
index->fd = -1;
return -1;
}
ret = mail_index_map(index, FALSE);
if (ret == 0) {
/* it's corrupted - recreate it */
mail_index_unlock(index, lock_id);
if (lock_id_r != NULL)
*lock_id_r = 0;
(void)close(index->fd);
index->fd = -1;
} else {
if (lock_id_r != NULL)
*lock_id_r = lock_id;
else
mail_index_unlock(index, lock_id);
}
return ret;
}
int mail_index_write_base_header(struct mail_index *index,
const struct mail_index_header *hdr)
{
size_t hdr_size;
hdr_size = I_MIN(sizeof(*hdr), hdr->base_header_size);
if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
memcpy(index->map->mmap_base, hdr, hdr_size);
if (msync(index->map->mmap_base, hdr_size, MS_SYNC) < 0)
return mail_index_set_syscall_error(index, "msync()");
} else {
if (pwrite_full(index->fd, hdr, hdr_size, 0) < 0) {
mail_index_set_syscall_error(index, "pwrite_full()");
return -1;
}
index->map->hdr = *hdr;
buffer_write(index->map->hdr_copy_buf, 0, hdr, hdr_size);
}
return 0;
}
int mail_index_create_tmp_file(struct mail_index *index, const char **path_r)
{
mode_t old_mask;
const char *path;
int fd;
path = *path_r = t_strconcat(index->filepath, ".tmp", NULL);
old_mask = umask(0);
fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode);
umask(old_mask);
if (fd == -1)
return mail_index_file_set_syscall_error(index, path, "open()");
if (index->gid != (gid_t)-1 && fchown(fd, (uid_t)-1, index->gid) < 0) {
mail_index_file_set_syscall_error(index, path, "fchown()");
return -1;
}
return fd;
}
static int mail_index_create(struct mail_index *index,
struct mail_index_header *hdr)
{
const char *path;
uint32_t seq;
uoff_t offset;
int ret;
i_assert(index->lock_type == F_UNLCK);
/* log file lock protects index creation */
if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0)
return -1;
ret = mail_index_try_open(index, NULL);
if (ret != 0) {
mail_transaction_log_sync_unlock(index->log);
return ret < 0 ? -1 : 0;
}
/* 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 {
index->lock_type = F_WRLCK;
ret = mail_index_map(index, FALSE);
index->lock_type = F_UNLCK;
}
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()");
}
} else {
/* make it visible to others */
if (rename(path, index->filepath) < 0) {
mail_index_set_error(index, "rename(%s, %s) failed: %m",
path, index->filepath);
ret = -1;
}
}
mail_transaction_log_sync_unlock(index->log);
return ret;
}
static void mail_index_header_init(struct mail_index_header *hdr)
{
time_t now = time(NULL);
i_assert((sizeof(*hdr) % sizeof(uint64_t)) == 0);
memset(hdr, 0, sizeof(*hdr));
hdr->major_version = MAIL_INDEX_MAJOR_VERSION;
hdr->minor_version = MAIL_INDEX_MINOR_VERSION;
hdr->base_header_size = sizeof(*hdr);
hdr->header_size = sizeof(*hdr);
hdr->record_size = sizeof(struct mail_index_record);
#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->next_uid = 1;
}
/* returns -1 = error, 0 = won't create, 1 = ok */
static int mail_index_open_files(struct mail_index *index,
enum mail_index_open_flags flags)
{
struct mail_index_header hdr;
unsigned int lock_id = 0;
int ret, created = FALSE;
ret = mail_index_try_open(index, &lock_id);
if (ret > 0)
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;
if (index->fd == -1) {
if (lock_id != 0) {
mail_index_unlock(index, lock_id);
lock_id = 0;
}
if (mail_index_create(index, &hdr) < 0)
return -1;
created = TRUE;
}
if (lock_id == 0) {
if (mail_index_lock_shared(index, FALSE, &lock_id) < 0)
return -1;
}
index->cache = created ? mail_cache_create(index) :
mail_cache_open_or_create(index);
mail_index_unlock(index, lock_id);
return 1;
}
int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags,
enum mail_index_lock_method lock_method)
{
int i = 0, ret;
if (index->opened) {
if (index->hdr != NULL &&
(index->hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
/* corrupted, reopen files */
mail_index_close(index);
} else {
return 0;
}
}
index->filepath = i_strconcat(index->dir, "/", index->prefix, NULL);
do {
index->shared_lock_count = 0;
index->excl_lock_count = 0;
index->lock_type = F_UNLCK;
index->lock_id = 2;
index->readonly = FALSE;
index->nodiskspace = FALSE;
index->index_lock_timeout = FALSE;
index->log_locked = FALSE;
index->mmap_disable =
(flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0;
index->mmap_no_write =
(flags & MAIL_INDEX_OPEN_FLAG_MMAP_NO_WRITE) != 0;
index->lock_method = lock_method;
/* don't even bother to handle dotlocking without mmap being
disabled. that combination simply doesn't make any sense */
if (lock_method == MAIL_INDEX_LOCK_DOTLOCK &&
!index->mmap_disable) {
i_fatal("lock_method=dotlock and mmap_disable=no "
"combination isn't supported. "
"You don't _really_ want it anyway.");
}
ret = mail_index_open_files(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;
}
if (index->map != NULL) {
mail_index_unmap(index, index->map);
index->map = NULL;
}
if (index->cache != NULL) {
mail_cache_free(index->cache);
index->cache = 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);
index->copy_lock_path = NULL;
i_free(index->filepath);
index->filepath = NULL;
index->indexid = 0;
index->opened = FALSE;
}
int mail_index_reopen(struct mail_index *index, int fd)
{
struct mail_index_map *old_map;
unsigned int old_shared_locks, old_lock_id, lock_id = 0;
int ret, old_fd, old_lock_type;
old_map = index->map;
old_fd = index->fd;
old_map->refcount++;
/* new file, new locks. the old fd can keep it's locks, they don't
matter anymore as no-one's going to modify the file. */
old_lock_type = index->lock_type;
old_lock_id = index->lock_id;
old_shared_locks = index->shared_lock_count;
if (index->lock_type == F_RDLCK)
index->lock_type = F_UNLCK;
index->lock_id += 2;
index->shared_lock_count = 0;
if (fd != -1) {
index->fd = fd;
ret = 0;
} else {
i_assert(index->excl_lock_count == 0);
ret = mail_index_try_open_only(index);
if (ret > 0)
ret = mail_index_lock_shared(index, FALSE, &lock_id);
else if (ret == 0) {
/* index file is lost */
ret = -1;
}
}
if (ret == 0) {
/* read the new mapping. note that with mmap_disable we want
to keep the old mapping in index->map so we can update it
by reading transaction log. */
if (mail_index_map(index, TRUE) <= 0)
ret = -1;
}
if (lock_id != 0)
mail_index_unlock(index, lock_id);
if (ret == 0) {
mail_index_unmap(index, old_map);
if (close(old_fd) < 0)
mail_index_set_syscall_error(index, "close()");
} else {
if (index->map != NULL)
mail_index_unmap(index, index->map);
if (index->fd != -1) {
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
}
index->map = old_map;
index->hdr = &index->map->hdr;
index->fd = old_fd;
index->lock_type = old_lock_type;
index->lock_id = old_lock_id;
index->shared_lock_count = old_shared_locks;
}
return ret;
}
int mail_index_reopen_if_needed(struct mail_index *index)
{
struct stat st1, st2;
if (fstat(index->fd, &st1) < 0)
return mail_index_set_syscall_error(index, "fstat()");
if (stat(index->filepath, &st2) < 0) {
mail_index_set_syscall_error(index, "stat()");
if (errno != ENOENT)
return -1;
/* lost it? recreate */
(void)mail_index_mark_corrupted(index);
return -1;
}
if (st1.st_ino != st2.st_ino ||
!CMP_DEV_T(st1.st_dev, st2.st_dev)) {
if (mail_index_reopen(index, -1) < 0)
return -1;
return 1;
} else {
return 0;
}
}
int mail_index_refresh(struct mail_index *index)
{
unsigned int lock_id;
int ret;
if (index->excl_lock_count > 0) {
/* we have index exclusively locked, nothing could
have changed. */
return 0;
}
if (!index->mmap_disable) {
/* reopening is all we need */
return mail_index_reopen_if_needed(index);
}
/* mail_index_map() simply reads latest changes from transaction log,
which makes us fully refreshed. */
if (mail_index_lock_shared(index, TRUE, &lock_id) < 0)
return -1;
ret = mail_index_map(index, FALSE);
mail_index_unlock(index, lock_id);
return ret <= 0 ? -1 : 0;
}
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;
}
void mail_index_set_inconsistent(struct mail_index *index)
{
index->indexid = 0;
}
void mail_index_mark_corrupted(struct mail_index *index)
{
struct mail_index_header hdr;
mail_index_set_inconsistent(index);
if (index->readonly)
return;
hdr = *index->hdr;
hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED;
if (mail_index_write_base_header(index, &hdr) == 0) {
if (fsync(index->fd) < 0)
mail_index_set_syscall_error(index, "fsync()");
}
}
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;
}
uint32_t mail_index_uint32_to_offset(uint32_t offset)
{
unsigned char buf[4];
i_assert(offset < 0x40000000);
i_assert((offset & 3) == 0);
offset >>= 2;
buf[0] = 0x80 | ((offset & 0x0fe00000) >> 21);
buf[1] = 0x80 | ((offset & 0x001fc000) >> 14);
buf[2] = 0x80 | ((offset & 0x00003f80) >> 7);
buf[3] = 0x80 | (offset & 0x0000007f);
return *((uint32_t *) buf);
}
uint32_t mail_index_offset_to_uint32(uint32_t offset)
{
const unsigned char *buf = (const unsigned char *) &offset;
if ((offset & 0x80808080) != 0x80808080)
return 0;
return (((uint32_t)buf[3] & 0x7f) << 2) |
(((uint32_t)buf[2] & 0x7f) << 9) |
(((uint32_t)buf[1] & 0x7f) << 16) |
(((uint32_t)buf[0] & 0x7f) << 23);
}