mail-index.c revision b62397096a70a9103dbcffd75b7be50501dbce04
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen unsigned int extra;
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen if (index->mmap_full_length < sizeof(MailIndexHeader)) {
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen extra = (index->mmap_full_length - sizeof(MailIndexHeader)) %
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen /* partial write or corrupted -
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen truncate the file to valid length */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen (void)ftruncate(index->fd, (off_t)index->mmap_full_length);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen /* keep the header set even if we fail, so we can update the flags */
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (hdr->used_file_size > index->mmap_full_length) {
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen index_set_corrupted(index, "used_file_size larger than real "
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen if ((hdr->used_file_size - sizeof(MailIndexHeader)) %
2d2ebe91d56e9a158de000c9d0026f65600fbcfaTimo Sirainen sizeof(MailIndexRecord) != 0) {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen index_set_corrupted(index, "Invalid used_file_size in header "
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen if (hdr->messages_count < hdr->seen_messages_count) {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen index_set_corrupted(index, "Invalid seen messages count "
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (hdr->messages_count < hdr->deleted_messages_count) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index_set_corrupted(index, "Invalid deleted messages count "
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen index->mmap_used_length = hdr->used_file_size;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen index->header = (MailIndexHeader *) index->mmap_base;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen /* make sure file size hasn't changed */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (index->header->sync_id == index->sync_id) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->mmap_used_length = index->header->used_file_size;
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (index->mmap_used_length > index->mmap_full_length) {
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen "updating sync_id");
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return index_set_syscall_error(index, "msync()");
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (munmap(index->mmap_base, index->mmap_full_length) < 0)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen return index_set_syscall_error(index, "munmap()");
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen index->mmap_base = mmap_rw_file(index->fd, &index->mmap_full_length);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen if (munmap_anon(index->mmap_base, index->mmap_full_length) < 0)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen index_set_syscall_error(index, "munmap_anon()");
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (munmap(index->mmap_base, index->mmap_full_length) < 0)
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainenstatic int mail_index_sync_file(MailIndex *index)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned int i;
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen for (i = 0; i < sizeof(fsync_fds)/sizeof(fsync_fds[0]); i++)
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen if (!mail_index_data_sync_file(index->data, &fsync_fds[0]))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (msync(index->mmap_base, index->mmap_used_length, MS_SYNC) < 0)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen return index_set_syscall_error(index, "msync()");
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen if (!mail_tree_sync_file(index->tree, &fsync_fds[1]))
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (!mail_modifylog_sync_file(index->modifylog, &fsync_fds[2]))
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen for (i = 0; i < sizeof(fsync_fds)/sizeof(fsync_fds[0]); i++) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (fsync_fds[i] != -1 && fdatasync(fsync_fds[i]) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index_set_error(index, "fdatasync(%u) failed: %m", i);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return index_set_syscall_error(index, "fdatasync()");
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainenstatic void mail_index_update_timestamp(MailIndex *index)
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen /* keep index's modify stamp same as the sync file's stamp */
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenint mail_index_fmdatasync(MailIndex *index, size_t size)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen if (msync(index->mmap_base, size, MS_SYNC) < 0)
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen return index_set_syscall_error(index, "msync()");
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen return index_set_syscall_error(index, "fdatasync()");
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainenstatic void mail_index_update_header_changes(MailIndex *index)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->header->cache_fields = index->set_cache_fields;
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainenstatic int mail_index_write_header_changes(MailIndex *index)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* use our own locking here so we don't mess up with any other
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen index states, like inconsistency. */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (file_wait_lock(index->fd, F_WRLCK, DEFAULT_LOCK_TIMEOUT) <= 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return index_set_syscall_error(index, "file_wait_lock()");
4e8d6d03c2ff85448df79b181a2ea850fb5d4199Timo Sirainen mprotect(index->mmap_base, index->mmap_used_length,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen failed = msync(index->mmap_base, sizeof(MailIndexHeader), MS_SYNC) < 0;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mprotect(index->mmap_base, index->mmap_used_length, PROT_NONE);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (file_wait_lock(index->fd, F_UNLCK, 0) <= 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return index_set_syscall_error(index, "file_wait_lock()");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstatic int mail_index_lock_remove(MailIndex *index)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (file_wait_lock(index->fd, F_UNLCK, 0) <= 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return index_set_syscall_error(index, "file_wait_lock()");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* releasing shared lock. we may need to update some
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen flags in header. */
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen if ((old_flags | index->set_flags) != old_flags ||
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen (old_cache | index->set_cache_fields) != old_cache)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return mail_index_write_header_changes(index);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen debug_mprotect(index->mmap_base, index->mmap_full_length, index);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic int mail_index_lock_change(MailIndex *index, MailLockType lock_type,
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen /* shared -> exclusive isn't allowed without try_lock */
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen i_assert(try_lock || lock_type != MAIL_LOCK_EXCLUSIVE ||
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen /* index is in inconsistent state and nothing else than
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen free() is allowed for it. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index_set_syscall_error(index, "file_try_lock()");
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen "file_wait_lock()");
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen debug_mprotect(index->mmap_base, index->mmap_full_length, index);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen (void)index->set_lock(index, MAIL_LOCK_UNLOCK);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen if (index->indexid != index->header->indexid) {
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen /* index was rebuilt, there's no way we can maintain
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen consistency */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen index_set_error(index, "Warning: Inconsistency - Index "
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "%s was rebuilt while we had it open",
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen /* someone just partially updated the index, need to fsck it */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen /* we need exclusive lock so fsck()'s set_lock() won't
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen get us back here */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (file_wait_lock(index->fd, MAIL_LOCK_EXCLUSIVE,
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "file_wait_lock()");
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen /* check again, in case it was already fscked while we had
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen it unlocked for a while */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen /* drop exclusive lock */
71da447014454c84828d9dface77219875554d7dTimo Sirainen /* while holding exclusive lock, keep the FSCK flag on.
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen when the lock is released, the FSCK flag will also be
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (!mail_index_fmdatasync(index, sizeof(MailIndexHeader))) {
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen (void)index->set_lock(index, MAIL_LOCK_UNLOCK);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic int mail_index_lock_full(MailIndex *index, MailLockType lock_type,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* anonymous mmaps are private and don't need any locking */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen debug_mprotect(index->mmap_base, index->mmap_full_length,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen mail_modifylog_notify_lock_drop(index->modifylog);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* dropping exclusive lock (either unlock or to shared) */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen keep_fsck = (index->set_flags & MAIL_INDEX_FLAG_FSCK) != 0;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* remove the FSCK flag only after successful fsync() */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (mail_index_sync_file(index) && !keep_fsck) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen index->header->flags &= ~MAIL_INDEX_FLAG_FSCK;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (msync(index->mmap_base, sizeof(MailIndexHeader),
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* we only failed to remove the fsck flag,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen so this isn't fatal. */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return mail_index_lock_change(index, lock_type, try_lock);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint mail_index_set_lock(MailIndex *index, MailLockType lock_type)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return mail_index_lock_full(index, lock_type, FALSE);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint mail_index_try_lock(MailIndex *index, MailLockType lock_type)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen return mail_index_lock_full(index, lock_type, TRUE);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint mail_index_verify_hole_range(MailIndex *index)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen "first_hole_index points outside file");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* check that first_hole_records is in valid range */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (max_records - hdr->first_hole_index < hdr->first_hole_records) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen "first_hole_records points outside file");
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo SirainenMailIndexHeader *mail_index_get_header(MailIndex *index)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
d78be924ad150840e018eda6a8a7d5e46a39bda2Timo SirainenMailIndexRecord *mail_index_lookup(MailIndex *index, unsigned int seq)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned int idx;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* out of range */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (hdr->first_hole_records == 0 || hdr->first_hole_index > idx) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* easy, it's just at the expected index */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen format = "Invalid first_hole_index in header: %"PRIuUOFF_T;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen MAIL_INDEX_RECORD_COUNT(index) - hdr->messages_count) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* only one hole in file, skip it and we're at
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen correct position */
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen format = "Invalid hole locations in header: %"PRIuUOFF_T;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* find from binary tree */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen idx = mail_tree_lookup_sequence(index->tree, seq);
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen index_set_corrupted(index, "Sequence %u not found from "
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen "binary tree (%u msgs says header)",
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen format = "Invalid offset returned by binary tree: %"PRIuUOFF_T;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen rec = (MailIndexRecord *) ((char *) index->mmap_base +
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo SirainenMailIndexRecord *mail_index_next(MailIndex *index, MailIndexRecord *rec)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen i_assert(rec >= (MailIndexRecord *) index->mmap_base);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* go to the next non-deleted record */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo SirainenMailIndexRecord *mail_index_lookup_uid_range(MailIndex *index,
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen unsigned int *seq_r)
904ca86f5d3f27505f50f5342214aabb78629cc8Timo Sirainen unsigned int idx;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen idx = mail_tree_lookup_uid_range(index->tree, seq_r,
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen index_set_error(index, "Corrupted binary tree for index %s: "
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen "lookup returned index outside range "
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen "(%u >= %"PRIuSIZE_T")", index->filepath, idx,
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen index->set_flags |= MAIL_INDEX_FLAG_REBUILD_TREE;
return NULL;
return rec;
return NULL;
return NULL;
*size = 0;
return NULL;
*size = 0;
return NULL;
*size = 0;
return NULL;
switch (field) {
case DATA_HDR_INTERNAL_DATE:
case DATA_HDR_VIRTUAL_SIZE:
case DATA_HDR_HEADER_SIZE:
case DATA_HDR_BODY_SIZE:
*size = 0;
return NULL;
return *date;
sizeof(MailIndexHeader)) +
rec++;
sizeof(MailIndexRecord);
return FALSE;
return FALSE;
return TRUE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;
int external_change)
unsigned int grow_count;
void *base;
return FALSE;
return TRUE;
return NULL;
return rec;
return TRUE;