mail-index.c revision 7797aa2479e99aeb71057b7a2584b2cb72e4d3f8
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainenstatic int mail_index_try_open_only(struct mail_index *index);
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainenstruct mail_index *mail_index_alloc(const char *dir, const char *prefix)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen index->extension_pool = pool_alloconly_create("extension", 256);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer_create_dynamic(index->extension_pool, 64, (size_t)-1);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenvoid mail_index_set_permissions(struct mail_index *index,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenuint32_t mail_index_ext_register(struct mail_index *index, const char *name,
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen unsigned int i;
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen extensions = buffer_get_data(index->extensions, &ext_count);
dfaefeabae939803ceb8c503101e86b5496541d1Timo Sirainen /* see if it's there already */
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen for (i = 0; i < ext_count; i++) {
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen i_assert(extensions[i].record_size == record_size);
d1e843e77f4760e303c53d9fce10123fc8d230a1Timo Sirainen ext.name = p_strdup(index->extension_pool, name);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen buffer_append(index->extensions, &ext, sizeof(ext));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic void mail_index_map_init_extbufs(struct mail_index_map *map,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext_size = initial_count * sizeof(struct mail_index_ext);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext_id_map_size = initial_count * sizeof(uint32_t);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen map->extensions = buffer_create_dynamic(map->extension_pool,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen map->ext_id_map = buffer_create_dynamic(map->extension_pool,
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenuint32_t mail_index_map_register_ext(struct mail_index *index,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen last_ext = buffer_get_data(map->extensions, &size);
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen ext = buffer_append_space_unsafe(map->extensions, sizeof(*ext));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ext->name = p_strdup(map->extension_pool, name);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext->record_offset = last_ext->record_offset +
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ext->record_offset = sizeof(struct mail_index_record);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ext_id = mail_index_ext_register(index, name, hdr_size, record_size);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer_write(map->ext_id_map, ext_id * sizeof(uint32_t),
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainenstatic int mail_index_read_extensions(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i, old_count;
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen if (offset == map->hdr->header_size && map->extension_pool == NULL) {
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen /* nothing to do, skip allocatations and all */
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen old_count = index->extensions->used / sizeof(struct mail_index_ext);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen mail_index_map_init_extbufs(map, old_count + 5);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < old_count; i++)
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen buffer_append(map->ext_id_map, &ext_id, sizeof(ext_id));
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen while (offset < map->hdr->header_size && name[offset] != '\0')
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen "Header extension name doesn't end with NUL",
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen while (offset < map->hdr->header_size && (offset % 4) != 0)
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen if (offset + sizeof(*ext_hdr) > map->hdr->header_size ||
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen offset + sizeof(*ext_hdr) + ext_hdr->hdr_size >
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen "Header extension goes outside header",
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen mail_index_map_register_ext(index, map, name + name_offset,
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen offset += sizeof(*ext_hdr) + ext_hdr->hdr_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int mail_index_check_header(struct mail_index *index,
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen const struct mail_index_header *hdr = map->hdr;
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen unsigned char compat_data[sizeof(hdr->compat_data)];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* major version change - handle silently(?) */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen if (memcmp(hdr->compat_data, compat_data, sizeof(compat_data)) != 0) {
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen /* architecture change - handle silently(?) */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen if ((map->hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen /* we've already complained about it */
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen /* following some extra checks that only take a bit of CPU */
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (hdr->uid_validity == 0 && hdr->next_uid != 1) {
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen "uid_validity = 0, next_uid = %u",
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen if (hdr->keywords_mask_size != sizeof(keywords_mask_t)) {
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen "keywords_mask_size mismatch: %d != %d",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hdr->record_size < sizeof(struct mail_index_record)) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen sizeof(struct mail_index_record));
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen if (hdr->recent_messages_count > hdr->messages_count ||
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen hdr->seen_messages_count > hdr->messages_count ||
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen hdr->deleted_messages_count > hdr->messages_count)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (hdr->first_recent_uid_lowwater > hdr->next_uid ||
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen hdr->first_unseen_uid_lowwater > hdr->next_uid ||
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen hdr->first_deleted_uid_lowwater > hdr->next_uid)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return mail_index_read_extensions(index, map);
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainenstatic void mail_index_map_clear(struct mail_index *index,
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen if (munmap(map->mmap_base, map->mmap_size) < 0)
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen mail_index_set_syscall_error(index, "munmap()");
eb1572d7c44ebc7b0b039d085c3dbab2ef7043ddTimo Sirainenvoid mail_index_unmap(struct mail_index *index, struct mail_index_map *map)
6ae329de09afb7214c906d762320847e05469d53Timo Sirainenstatic void mail_index_unmap_forced(struct mail_index *index,
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainenstatic int mail_index_mmap(struct mail_index *index, struct mail_index_map *map)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen /* we had temporarily used a buffer, eg. for updating index */
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen map->mmap_base = index->lock_type != F_WRLCK ?
33b469d1ca66dd2cc496d2d990b8b98e72952a29Timo Sirainen mail_index_set_syscall_error(index, "mmap()");
58ba0fe5a6904d3a65cfe268411f4cbb881234eeTimo Sirainen if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
6b0d8106ae51ffc6ce45636b34d2e21cbefca7fdTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
d81131d3bbb4f0befb62a661d1785cf8c84a17e2Timo Sirainen records_count = (map->mmap_size - hdr->header_size) /
9456a4a3e74929f9d3d5b00b93be6d8eb69bc52aTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
9456a4a3e74929f9d3d5b00b93be6d8eb69bc52aTimo Sirainen "messages_count too large (%u > %u)",
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen if (map->hdr->base_header_size < sizeof(*map->hdr)) {
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* header smaller than ours, make a copy so our newer headers
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen won't have garbage in them */
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen buffer_append_zero(map->hdr_copy_buf, sizeof(*map->hdr) -
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mhdr = buffer_get_modifyable_data(map->hdr_copy_buf, NULL);
c0225f7f6b43d34dc58c17d3304f0fd60ab89894Timo Sirainen map->records = PTR_OFFSET(map->mmap_base, map->hdr->header_size);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen map->records_count = map->hdr->messages_count;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic int mail_index_read_map(struct mail_index *index,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen for (pos = 0; ret > 0 && pos < sizeof(hdr); ) {
ceac44e7560fcbf6fc2f932c7b624a5055dc3bc9Timo Sirainen if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
ceac44e7560fcbf6fc2f932c7b624a5055dc3bc9Timo Sirainen if (hdr.base_header_size < MAIL_INDEX_HEADER_MIN_SIZE ||
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen "Corrupted header sizes (base %u, full %u)",
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen buffer_append_zero(map->hdr_copy_buf, sizeof(hdr) +
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* @UNSAFE */
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen hdrp = buffer_get_modifyable_data(map->hdr_copy_buf,
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen /* @UNSAFE */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen records_size = hdr.messages_count * hdr.record_size;
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen map->buffer = buffer_create_dynamic(default_pool,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* @UNSAFE */
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen data = buffer_append_space_unsafe(map->buffer, records_size);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen ret = pread_full(index->fd, data, records_size,
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen mail_index_set_syscall_error(index, "pread_full()");
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen "Corrupted index file %s: File too small",
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenstatic int mail_index_read_map_with_retry(struct mail_index *index,
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen for (i = 0; i < MAIL_INDEX_ESTALE_RETRY_COUNT; i++) {
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen ret = mail_index_read_map(index, map, &retry);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen /* ESTALE - reopen index file */
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen mail_index_set_syscall_error(index, "close()");
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen /* the file was lost */
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen mail_index_set_syscall_error(index, "open()");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* Too many ESTALE retries */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mail_index_set_syscall_error(index, "read_map()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int mail_index_map_try_existing(struct mail_index_map *map)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* always check corrupted-flag to avoid errors later */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen used_size = hdr->header_size + hdr->messages_count * hdr->record_size;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (map->mmap_size >= used_size && map->hdr == hdr) {
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainenint mail_index_map(struct mail_index *index, int force)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen ret = mail_index_map_try_existing(index->map);
0cce885512b836ce021260a58e7b4f099b36d0f1Timo Sirainen if (index->map != NULL && index->map->refcount > 1) {
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen /* this map is already used by some views and they may have
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen pointers into it. leave them and create a new mapping. */
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen } else if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen /* we have modified this mapping and it's waiting to
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen be written to disk once we drop exclusive lock.
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen mapping couldn't have changed, so do nothing. */
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen /* FIXME: we need to re-read header */
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainen if (munmap(map->mmap_base, map->mmap_size) < 0)
ba8ff75a149d6936f769a2d1dfceaab9da87863bTimo Sirainen mail_index_set_syscall_error(index, "munmap()");
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen if ((ret = mail_index_mmap(index, map)) <= 0) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (mail_index_read_map_with_retry(index, map) < 0) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen map->log_file_offset = map->hdr->log_file_offset;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen map->base_header_size = map->hdr->base_header_size;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainenmail_index_map_to_memory(struct mail_index_map *map, uint32_t new_record_size)
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen unsigned int i, count;
d9a129b491613014ce5f31fe1ab20903e2899ea4Timo Sirainen mem_map->buffer = buffer_create_dynamic(default_pool, size, (size_t)-1);
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen buffer_append(mem_map->buffer, map->records, size);
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen copy_size = I_MIN(map->hdr->record_size, new_record_size);
b83deefd2cf1e293373673eefb4d5cf60907978cTimo Sirainen dest = buffer_append_space_unsafe(mem_map->buffer,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mem_map->records = buffer_get_modifyable_data(mem_map->buffer, NULL);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen mem_map->hdr_copy_buf = buffer_create_dynamic(default_pool,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen buffer_append(mem_map->hdr_copy_buf, map->hdr, map->hdr->header_size);
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen hdr = buffer_get_modifyable_data(mem_map->hdr_copy_buf, NULL);
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen /* copy extensions */
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen count = map->ext_id_map->used / sizeof(uint32_t);
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen buffer_append_buf(mem_map->extensions, map->extensions,
94f84d1c3f786d1b92dd2a1507f83a2dad887c56Timo Sirainen buffer_append_buf(mem_map->ext_id_map, map->ext_id_map,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* fix the name pointers to use our own pool */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen extensions = buffer_get_modifyable_data(mem_map->extensions,
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen for (i = 0; i < count; i++) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen extensions[i].name = p_strdup(mem_map->extension_pool,
unsigned int lock_id;
int ret;
*lock_id_r = 0;
if (ret <= 0)
return ret;
if (ret == 0) {
*lock_id_r = 0;
return ret;
const char *path;
int fd;
return fd;
const char *path;
int ret;
if (ret != 0) {
/* create it fully in index.tmp first */
if (ret == 0) {
if (ret < 0) {
return ret;
#ifndef WORDS_BIGENDIAN
unsigned int lock_id = 0;
int ret;
if (ret > 0)
else if (ret == 0) {
} else if (ret < 0)
if (lock_id != 0) {
lock_id = 0;
if (lock_id == 0) {
int i = 0, ret;
if (ret <= 0)
if (ret == 0) {
if (ret <= 0)
return ret;
ret = 0;
if (ret > 0)
else if (ret == 0) {
if (ret == 0) {
if (lock_id != 0)
if (ret == 0) {
return ret;
const char *function)
const char *filepath,
const char *function)
return MAIL_INDEX_ERROR_DISKSPACE;
return MAIL_INDEX_ERROR_INTERNAL;
return MAIL_INDEX_ERROR_NONE;