mail-index.c revision fe5c1094aec29b5d29356ad5f06c970eb4ce7a78
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainenstruct mail_index_module_register mail_index_module_register = { 0 };
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct mail_index *mail_index_alloc(const char *dir, const char *prefix)
8fa41238067c854435884c459963fde6f8c6436bTimo Sirainen pool_alloconly_create(MEMPOOL_GROWING"index extension", 1024);
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen p_array_init(&index->extensions, index->extension_pool, 5);
5aeb15e5817fbd4b1d8de540aa7673e3819a8030Timo Sirainen mail_index_ext_register(index, MAIL_INDEX_EXT_KEYWORDS,
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen index->keywords_pool = pool_alloconly_create("keywords", 512);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_create(default_pool, index->keywords_pool, 0,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen index->log = mail_transaction_log_alloc(index);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid mail_index_free(struct mail_index **_index)
80fc743146da5130de34174cdaad2576f103723fTimo Sirainenvoid mail_index_set_fsync_types(struct mail_index *index,
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainenvoid mail_index_set_permissions(struct mail_index *index,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen mode_t mode, gid_t gid, const char *gid_origin)
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainenuint32_t mail_index_ext_register(struct mail_index *index, const char *name,
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen if (*name == '\0' || strcmp(name, str_sanitize(name, -1)) != 0)
ffd9a1898a18fadfc5dce399162c25d50548f905Timo Sirainen i_panic("mail_index_ext_register(%s): Invalid name", name);
1e923fcf497665fe071a154c31fb452766b0b2deTimo Sirainen if (default_record_size != 0 && default_record_align == 0) {
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen if (mail_index_ext_lookup(index, name, &ext_id))
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen rext.name = p_strdup(index->extension_pool, name);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen rext.index_idx = array_count(&index->extensions);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainenbool mail_index_ext_lookup(struct mail_index *index, const char *name,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen const struct mail_index_registered_ext *extensions;
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen unsigned int i, count;
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen extensions = array_get(&index->extensions, &count);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen for (i = 0; i < count; i++) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenvoid mail_index_register_expunge_handler(struct mail_index *index,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen i_assert(rext->expunge_handler == NULL || rext->expunge_handler == cb);
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen rext->expunge_handler_call_always = call_always;
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenvoid mail_index_unregister_expunge_handler(struct mail_index *index,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenvoid mail_index_register_sync_handler(struct mail_index *index, uint32_t ext_id,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen i_assert(rext->sync_handler.callback == NULL);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenvoid mail_index_unregister_sync_handler(struct mail_index *index,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen i_assert(rext->sync_handler.callback != NULL);
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainenvoid mail_index_register_sync_lost_handler(struct mail_index *index,
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen array_append(&index->sync_lost_handlers, &cb, 1);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenvoid mail_index_unregister_sync_lost_handler(struct mail_index *index,
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen mail_index_sync_lost_handler_t *const *handlers;
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen unsigned int i, count;
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen handlers = array_get(&index->sync_lost_handlers, &count);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen for (i = 0; i < count; i++) {
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen array_delete(&index->sync_lost_handlers, i, 1);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool mail_index_keyword_lookup(struct mail_index *index,
6a04c5112961c5f4fb2d2f25192b3dc424d62ad0Timo Sirainen /* keywords_hash keeps a name => index mapping of keywords.
6a04c5112961c5f4fb2d2f25192b3dc424d62ad0Timo Sirainen Keywords are never removed from it, so the index values are valid
6a04c5112961c5f4fb2d2f25192b3dc424d62ad0Timo Sirainen for the lifetime of the mail_index. */
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen if (hash_table_lookup_full(index->keywords_hash, keyword,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen *idx_r = POINTER_CAST_TO(value, unsigned int);
6825360d446542046757b06064282301c4c6b27cTimo Sirainenvoid mail_index_keyword_lookup_or_create(struct mail_index *index,
6825360d446542046757b06064282301c4c6b27cTimo Sirainen unsigned int *idx_r)
6825360d446542046757b06064282301c4c6b27cTimo Sirainen if (mail_index_keyword_lookup(index, keyword, idx_r))
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen keyword = keyword_dup = p_strdup(index->keywords_pool, keyword);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenconst ARRAY_TYPE(keywords) *mail_index_get_keywords(struct mail_index *index)
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainenmail_index_keywords_create(struct mail_index *index,
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen const char *const keywords[])
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen /* @UNSAFE */
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen /* look up the keywords from index. they're never removed from there
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen so we can permanently store indexes to them. */
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen mail_index_keyword_lookup_or_create(index, keywords[src],
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen /* ignore if this is a duplicate */
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen for (i = 0; i < src; i++) {
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainenmail_index_keywords_create_from_indexes(struct mail_index *index,
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen const unsigned int *indexes;
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* @UNSAFE */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* copy but skip duplicates */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen for (i = 0; i < src; i++) {
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainenvoid mail_index_keywords_ref(struct mail_keywords *keywords)
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainenvoid mail_index_keywords_unref(struct mail_keywords **_keywords)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenint mail_index_try_open_only(struct mail_index *index)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* Note that our caller must close index->fd by itself. */
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen index->fd = nfs_safe_open(index->filepath, O_RDWR);
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen return mail_index_set_syscall_error(index, "open()");
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen /* have to create it */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen i_assert(index->map == NULL || index->map->rec_map->lock_id == 0);
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD);
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen /* it's corrupted - recreate it */
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen mail_index_set_syscall_error(index, "close()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_index_create_tmp_file(struct mail_index *index, const char **path_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen path = *path_r = t_strconcat(index->filepath, ".tmp", NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return mail_index_file_set_syscall_error(index, path, "open()");
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainenstatic int mail_index_open_files(struct mail_index *index,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0)
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen /* if dovecot.index exists, read it first so that we can get
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen the correct indexid and log sequence */
af1e2b2ab5d1c5ca5afe482ef8c8161c17acc190Timo Sirainen /* Create a new indexid for us. If we're opening index
af1e2b2ab5d1c5ca5afe482ef8c8161c17acc190Timo Sirainen into memory, index->map doesn't exist yet. */
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen ret = mail_transaction_log_create(index->log, FALSE);
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen ret = index->map != NULL ? 1 : mail_index_try_open(index);
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen /* corrupted */
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen ret = mail_transaction_log_create(index->log, TRUE);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* open/create failed, fallback to in-memory indexes */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0)
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen index->cache = created ? mail_cache_create(index) :
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainenint mail_index_open(struct mail_index *index, enum mail_index_open_flags flags,
0c27b881989bc2b391281650ee89a8cc4d89f5e7Timo Sirainen /* corrupted, reopen files */
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen index->filepath = MAIL_INDEX_IS_IN_MEMORY(index) ?
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen i_strconcat(index->dir, "/", index->prefix, NULL);
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen index->readonly = (flags & MAIL_INDEX_OPEN_FLAG_READONLY) != 0;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 &&
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen (flags & MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE) != 0)
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen i_fatal("nfs flush requires fsync_disable=no");
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0 &&
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen (flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0)
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen i_fatal("nfs flush requires mmap_disable=yes");
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen if ((ret = mail_index_open_files(index, flags)) <= 0) {
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* doesn't exist and create flag not used */
41e6eb07b411ea58352ba9d2cc8cf340325d49f3Timo Sirainenint mail_index_open_or_create(struct mail_index *index,
41e6eb07b411ea58352ba9d2cc8cf340325d49f3Timo Sirainen ret = mail_index_open(index, flags, lock_method);
c2feb7d13482d0f60691cd71d06d42a80df99397Timo Sirainenvoid mail_index_close_file(struct mail_index *index)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_syscall_error(index, "close()");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid mail_index_close(struct mail_index *index)
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainenint mail_index_unlink(struct mail_index *index)
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainen /* main index */
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainen if (unlink(index->filepath) < 0 && errno != ENOENT)
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainen path = t_strconcat(index->filepath, MAIL_TRANSACTION_LOG_SUFFIX, NULL);
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainen path = t_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenint mail_index_reopen_if_changed(struct mail_index *index)
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) == 0);
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0)
6143fece58262865ce89b5012b73ef08f2ad6abcTimo Sirainen if (nfs_safe_stat(index->filepath, &st2) < 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return mail_index_set_syscall_error(index, "stat()");
dec85d9856c33f427a06dda01e0e50de0bc8fa7dTimo Sirainen return mail_index_set_syscall_error(index, "fstat()");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* the same file */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* new file, new locks. the old fd can keep its locks, they don't
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen matter anymore as no-one's going to modify the file. */
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainenint mail_index_refresh(struct mail_index *index)
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen ret = mail_index_map(index, MAIL_INDEX_SYNC_HANDLER_HEAD);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct mail_cache *mail_index_get_cache(struct mail_index *index)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_index_set_error(struct mail_index *index, const char *fmt, ...)
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainenbool mail_index_is_in_memory(struct mail_index *index)
0d0451206a91e9f96e522075dce28a89adc2325dTimo Sirainenint mail_index_move_to_memory(struct mail_index *index)
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0)
f4e0148e539c6dd4e12b8305ab0dd5e63c46ba67Timo Sirainen /* set the index as being into memory */
2bd96c58be42146cb84076331604cadb2994fce5Timo Sirainen index->filepath = i_strdup("(in-memory index)");
27db4ce5fe399c981e09dcf9e885a1546afd34f4Timo Sirainen /* index was never even opened. just mark it as being in
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen memory and let the caller re-open the index. */
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainen /* move index map to memory */
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
ae14dfd895881f9d1c6899b0c09f1a8b51447d61Timo Sirainen /* move transaction log to memory */
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen mail_transaction_log_move_to_memory(index->log);
31189eeac1ccaaf1201c60427f8c1087b0f5dfceTimo Sirainen mail_index_set_syscall_error(index, "close()");
87712707722ef7d73acb065546e61afa4455cd9eTimo Sirainenvoid mail_index_mark_corrupted(struct mail_index *index)
8d131435ba4648c8821160ec38d508c97177c715Timo Sirainen index->map->hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED;
bc1d1497d715cc5c820ff518f070f78c39ef6cdcTimo Sirainen if (unlink(index->filepath) < 0 && errno != ENOENT && errno != ESTALE)
bc1d1497d715cc5c820ff518f070f78c39ef6cdcTimo Sirainen mail_index_set_syscall_error(index, "unlink()");
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainenvoid mail_index_fchown(struct mail_index *index, int fd, const char *path)
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen /* no gid changing */
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen } else if (fchown(fd, (uid_t)-1, index->gid) == 0) {
1f80b32fc28f7a723ff07c1694230a090808b506Timo Sirainen /* success */
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen } if ((index->mode & 0060) >> 3 == (index->mode & 0006)) {
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen /* group and world permissions are the same, so group doesn't
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen really matter. ignore silently. */
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen mail_index_file_set_syscall_error(index, path, "fchown()");
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen eperm_error_get_chgrp("fchown", path, index->gid,
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen /* continue, but change permissions so that only the common
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen subset of group and world is used. this makes sure no one
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen gets any extra permissions. */
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen mode = ((index->mode & 0060) >> 3) & (index->mode & 0006);
93688bfedcfb2b9c02750b8d4d409123a386de5cTimo Sirainen mail_index_file_set_syscall_error(index, path, "fchmod()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_index_set_syscall_error(struct mail_index *index,
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return mail_index_set_error(index, "%s failed with index file %s: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_index_file_set_syscall_error(struct mail_index *index,
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((index->flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) == 0)
1dd875d96ab5640f78250079961c10e99ed4aa79Timo Sirainen eacces_error_get(t_strcut(function, '('), filepath));
1dd875d96ab5640f78250079961c10e99ed4aa79Timo Sirainen return mail_index_set_error(index, "%s failed with file %s: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenconst char *mail_index_get_error_message(struct mail_index *index)