mail-index-data.c revision 67950e1843bd879bab73d8752ee54f60bbd21987
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (C) 2002 Timo Sirainen */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen ((uoff_t) ((char *) (rec) - (char *) ((data)->mmap_base)))
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen/* Never compress the file if it's smaller than this */
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen/* Compress the file when deleted space reaches n% of total size */
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen/* Initial size for the file */
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen#define INDEX_DATA_INITIAL_SIZE (sizeof(MailIndexDataHeader) + 10240)
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen/* When more space is needed, grow the file n% larger than the previous size */
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainenint index_data_set_corrupted(MailIndexData *data, const char *fmt, ...)
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen index_set_error(data->index, "Corrupted index data file %s: %s",
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainenstatic int index_data_set_syscall_error(MailIndexData *data,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_set_error(data->index, "%s failed with index data file %s: %m",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic void mail_index_data_file_close(MailIndexData *data)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (munmap_anon(data->mmap_base, data->mmap_full_length) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_data_set_syscall_error(data, "munmap_anon()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (munmap(data->mmap_base, data->mmap_full_length) < 0)
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen index_data_set_syscall_error(data, "munmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_data_set_syscall_error(data, "close()");
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainenstatic int data_file_reopen(MailIndexData *data)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return index_data_set_syscall_error(data, "open()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int mmap_update(MailIndexData *data, uoff_t pos, size_t size)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data->header->indexid != data->index->indexid) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* index was just rebuilt. we should have noticed
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen this before at index->set_lock() though. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "Warning: Inconsistency - Index "
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "%s was rebuilt while we had it open",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* data file was deleted, reopen it */
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen /* force mmap refresh */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data->mmap_used_length = data->header->used_file_size;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (data->mmap_used_length <= data->mmap_full_length)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* file size changed, re-mmap() */
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen msync(data->mmap_base, data->mmap_used_length, MS_SYNC) < 0)
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen return index_data_set_syscall_error(data, "msync()");
bc793bfcee945ce8871edfa298fe7235744425b6Timo Sirainen if (munmap(data->mmap_base, data->mmap_full_length) < 0)
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen index_data_set_syscall_error(data, "munmap()");
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen data->mmap_base = mmap_rw_file(data->fd, &data->mmap_full_length);
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen return index_data_set_syscall_error(data, "mmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (data->mmap_full_length < sizeof(MailIndexDataHeader))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return index_data_set_corrupted(data, "File too small");
e4423c16a4f798ecf75ca2b0c3ef834000faed4bTimo Sirainen if (hdr->used_file_size < sizeof(MailIndexDataHeader)) {
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen index_data_set_corrupted(data, "used_file_size too small ("
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (hdr->used_file_size > data->mmap_full_length) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_data_set_corrupted(data, "used_file_size larger than "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen debug_mprotect(data->mmap_base, data->mmap_full_length, data->index);
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen path = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen /* doesn't exist, rebuild the index */
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen return index_file_set_syscall_error(index, path, "open()");
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen if (!mmap_update(data, 0, sizeof(MailIndexDataHeader))) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* verify that this really is the data file for wanted index */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (data->header->indexid != index->indexid) {
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen index_set_error(index, "IndexID mismatch for data file %s",
e4423c16a4f798ecf75ca2b0c3ef834000faed4bTimo Sirainenstatic const char *init_data_file(MailIndex *index, MailIndexDataHeader *hdr,
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen if (write_full(fd, hdr, sizeof(MailIndexDataHeader)) < 0) {
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen index_file_set_syscall_error(index, temppath, "write_full()");
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen if (file_set_size(fd, INDEX_DATA_INITIAL_SIZE) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "file_set_size()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* move temp file into .data file, deleting old one
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if it already exists */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen realpath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index_set_error(index, "rename(%s, %s) failed: %m",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen hdr.used_file_size = sizeof(MailIndexDataHeader);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* we'll do anon-mmaping only if initially requested. if we fail
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen because of out of disk space, we'll just let the main index code
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen know it and fail. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fd = mail_index_create_temp_file(index, &temppath);
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen realpath = init_data_file(index, &hdr, fd, temppath);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen data->mmap_full_length = INDEX_DATA_INITIAL_SIZE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen data->mmap_base = mmap_anon(data->mmap_full_length);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen memcpy(data->mmap_base, &hdr, sizeof(MailIndexDataHeader));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data->mmap_used_length = data->header->used_file_size;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data->filepath = i_strdup("(in-memory index data)");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!mmap_update(data, 0, sizeof(MailIndexDataHeader))) {
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen hdr.used_file_size = sizeof(MailIndexDataHeader);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen memcpy(data->mmap_base, &hdr, sizeof(MailIndexDataHeader));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (file_set_size(data->fd, INDEX_DATA_INITIAL_SIZE) < 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return index_data_set_syscall_error(data, "file_set_size()");
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen return index_data_set_syscall_error(data, "lseek()");
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen if (write_full(data->fd, &hdr, sizeof(MailIndexDataHeader)) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return index_data_set_syscall_error(data, "write_full()");
9c4c535b86e9473ad97c6e9242ed84f3d9d69d0dTimo Sirainenint mail_index_data_mark_file_deleted(MailIndexData *data)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (msync(data->mmap_base, sizeof(MailIndexDataHeader), MS_SYNC) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return index_data_set_syscall_error(data, "msync()");
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenvoid mail_index_data_mark_modified(MailIndexData *data)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenstatic int mail_index_data_grow(MailIndexData *data, size_t size)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen new_fsize = data->header->used_file_size + size;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen new_fsize += new_fsize / 100 * INDEX_DATA_GROW_PERCENTAGE;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen base = mremap_anon(data->mmap_base, data->mmap_full_length,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen index_data_set_syscall_error(data, "mremap_anon()");
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen return index_data_set_syscall_error(data, "lseek()");
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (data->header->used_file_size + size <= (uoff_t)pos) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen /* no need to grow, just update mmap */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen i_assert(data->mmap_full_length >= (uoff_t)pos);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen return index_data_set_corrupted(data, "Header is missing");
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen return index_data_set_syscall_error(data, "file_set_size()");
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenuoff_t mail_index_data_append(MailIndexData *data, const void *buffer,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen i_assert(data->index->lock_type == MAIL_LOCK_EXCLUSIVE);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen data->mmap_full_length - size < data->header->used_file_size) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(offset + size <= data->mmap_full_length);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen memcpy((char *) data->mmap_base + offset, buffer, size);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint mail_index_data_delete(MailIndexData *data, MailIndexRecord *index_rec)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen i_assert(data->index->lock_type == MAIL_LOCK_EXCLUSIVE);
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen rec_hdr = mail_index_data_lookup_header(data, index_rec);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* just mark it deleted. */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen data->header->deleted_space += rec_hdr->data_size;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* see if we've reached the max. deleted space in file */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (data->header->used_file_size >= COMPRESS_MIN_SIZE &&
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen (data->index->header->flags & MAIL_INDEX_FLAG_COMPRESS_DATA) == 0) {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen max_del_space = data->header->used_file_size /
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen if (data->header->deleted_space >= max_del_space)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen data->index->set_flags |= MAIL_INDEX_FLAG_COMPRESS_DATA;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint mail_index_data_sync_file(MailIndexData *data, int *fsync_fd)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (msync(data->mmap_base, data->mmap_used_length, MS_SYNC) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return index_data_set_syscall_error(data, "msync()");
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainenmail_index_data_lookup_header(MailIndexData *data, MailIndexRecord *index_rec)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* data not yet written to record */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (!mmap_update(data, pos, sizeof(MailIndexDataRecordHeader)))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (pos + sizeof(MailIndexDataRecordHeader) > data->mmap_used_length) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "Data position of record %u points outside file "
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen index_rec->uid, pos, sizeof(MailIndexDataRecordHeader),
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen "Data position (%"PRIuUOFF_T") is not memory aligned "
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return (MailIndexDataRecordHeader *) ((char *) data->mmap_base + pos);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenmail_index_data_lookup(MailIndexData *data, MailIndexRecord *index_rec,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* data not yet written to record */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen rec_hdr = mail_index_data_lookup_header(data, index_rec);
return NULL;
return NULL;
return rec;
return NULL;
return NULL;
return NULL;
return NULL;
return rec;
return FALSE;
return TRUE;
return FALSE;
return NULL;