mail-transaction-log.c revision f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
213b139965e8bde6c8aff02ffd9fd39a74c887a9Timo Sirainen/* this lock should never exist for a long time.. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT 120
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen const char *path);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic int mail_transaction_log_rotate(struct mail_transaction_log *log);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenmail_transaction_log_file_lock(struct mail_transaction_log_file *file,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenstatic int mail_transaction_log_lock_head(struct mail_transaction_log *log);
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen const char *fmt, ...)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Corrupted transaction log file %s: %s",
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstatic int mail_transaction_log_check_file_seq(struct mail_transaction_log *log)
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen unsigned int lock_id;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ret = mail_index_lock_shared(index, TRUE, &lock_id);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen else if (file->hdr.file_seq != index->hdr->log_file_seq) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen /* broken - fix it by creating a new log file */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (void)mail_transaction_log_file_lock(file, F_UNLCK);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenmail_transaction_log_open_or_create(struct mail_index *index)
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainen log->head = mail_transaction_log_file_open_or_create(log, path);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen log->head->hdr.file_seq != index->hdr->log_file_seq) {
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen /* head log file isn't same as head index file -
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen shouldn't happen except in race conditions. lock them and
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen check again - FIXME: missing error handling.
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen FIXME: index->hdr check crashes if we created the log */
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen (void)mail_transaction_log_check_file_seq(log);
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ret = file_unlock_dotlock(file->filepath, &file->dotlock);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen "Dotlock was lost for transaction log file %s",
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen ret = file_lock_dotlock(file->filepath, NULL, FALSE,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "file_lock_dotlock()");
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "Timeout while waiting for release of "
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "dotlock for transaction log file %s",
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_lock(struct mail_transaction_log_file *file,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return mail_transaction_log_file_dotlock(file, lock_type);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ret = file_wait_lock_full(file->fd, lock_type, DEFAULT_LOCK_TIMEOUT,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "file_wait_lock()");
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen "Timeout while waiting for release of "
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen "%s fcntl() lock for transaction log file %s",
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen lock_type == F_WRLCK ? "exclusive" : "shared",
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_close(struct mail_transaction_log_file *file)
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainenmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen if (mail_transaction_log_file_lock(file, F_RDLCK) < 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen (void)mail_transaction_log_file_lock(file, F_UNLCK);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen // FIXME: handle ESTALE
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen "pread_full()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "unexpected end of file while reading header");
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen /* corrupted */
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen "Transaction log file %s: marked corrupted",
7471f28b16b81f9af413c879b3efb16eeafd2bd9Timo Sirainen if (file->hdr.indexid != file->log->index->indexid &&
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen /* either index was just recreated, or transaction has wrong
527ed64bc924b4a13b570a8450f8be3efdf71879Timo Sirainen indexid. we don't know here which one is the case, so we'll
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen just fail. If index->indexid == 0, we're rebuilding it and
688f0fdfb7ca946e38ae34459b0ca30b71c8457cTimo Sirainen we just want to lock the transaction log. */
688f0fdfb7ca946e38ae34459b0ca30b71c8457cTimo Sirainen "Transaction log file %s: invalid indexid",
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen "used_size (%u) < old_size (%u)",
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainenstatic int mail_transaction_log_file_create(struct mail_transaction_log *log,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen unsigned int lock_id;
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen fd = file_dotlock_open(path, NULL, LOG_DOTLOCK_TIMEOUT,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT, NULL, NULL);
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen mail_index_file_set_syscall_error(index, path,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen "file_dotlock_open()");
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen /* log creation is locked now - see if someone already created it */
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen mail_index_file_set_syscall_error(index, path,
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen } else if (st.st_dev == dev && st.st_ino == ino) {
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen /* same file, still broken */
6d3bb841d4ec2c12ac2d4ecf6146ef8d8a7dd731Timo Sirainen mail_index_file_set_syscall_error(index, path, "open()");
c680a6b35b459045e92814778908da5a93922107Timo Sirainen index->log_locked = TRUE; /* kludging around assert.. */
c680a6b35b459045e92814778908da5a93922107Timo Sirainen if (mail_index_lock_exclusive(index, &lock_id) < 0) {
096953143c4032bad154637f687551856f7946cbTimo Sirainen /* update log_file_* fields in header */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (mail_index_write_header(index, &idx_hdr) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* creating new index file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(index, path, "write_full()");
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen mail_index_file_set_syscall_error(index, path, "dup()");
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file_dotlock_replace(path, fd, FALSE) <= 0)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* success */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenmail_transaction_log_file_fd_open(struct mail_transaction_log *log,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_file_set_syscall_error(log->index, path, "stat()");
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file = i_new(struct mail_transaction_log_file, 1);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen ret = mail_transaction_log_file_read_hdr(file, &st);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* corrupted header */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen fd = mail_transaction_log_file_create(log, path,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_file_set_syscall_error(log->index, path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = mail_transaction_log_file_read_hdr(file, &st);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen for (p = &log->tail; *p != NULL; p = &(*p)->next)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen mail_index_file_set_syscall_error(log->index, path,
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen fd = mail_transaction_log_file_create(log, path, 0, 0);
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen return mail_transaction_log_file_fd_open(log, path, fd);
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainenvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen if ((*p)->refcount != 0)
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainenstatic int mail_transaction_log_rotate(struct mail_transaction_log *log)
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen fd = mail_transaction_log_file_create(log, log->head->filepath,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen file = mail_transaction_log_file_fd_open(log, log->head->filepath, fd);
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainenstatic int mail_transaction_log_refresh(struct mail_transaction_log *log)
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen mail_index_file_set_syscall_error(log->index, path, "stat()");
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen /* same file */
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen ret = mail_transaction_log_file_read_hdr(log->head, &st);
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen file = mail_transaction_log_file_open_or_create(log, path);
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainenint mail_transaction_log_file_find(struct mail_transaction_log *log,
6f0bb992c9f55de912295493892651d57e5e9827Timo Sirainen for (file = log->tail; file != NULL; file = file->next) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenmail_transaction_log_file_read(struct mail_transaction_log_file *file,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (file->buffer != NULL && file->buffer_offset > offset) {
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainen /* we have to insert missing data to beginning of buffer */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen buffer_copy(file->buffer, size, file->buffer, 0, (size_t)-1);
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen data = buffer_get_space_unsafe(file->buffer, 0, size);
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen ret = pread_full(file->fd, data, size, offset);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* log file was deleted in NFS server, fail silently */
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen file->buffer = buffer_create_dynamic(default_pool,
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen if (file->buffer_offset + size >= file->hdr.used_size) {
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen /* caller should have checked this.. */
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen size = file->hdr.used_size - file->buffer_offset - size;
cf05507f63b12bd1ee4efffc2f316ebcd9fd7089Timo Sirainen data = buffer_append_space_unsafe(file->buffer, size);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen ret = pread_full(file->fd, data, size, offset);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* log file was deleted in NFS server, fail silently */
7f7533a29d0e65876acc0ab9dd56b3d7840c3ee7Timo Sirainenint mail_transaction_log_file_map(struct mail_transaction_log_file *file,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen /* corrupted */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* with mmap_no_write we could alternatively just write to log with
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen msync() rather than pwrite(). that'd cause slightly more disk I/O,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen so rather use more memory. */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen use_mmap = !index->mmap_disable && !index->mmap_no_write;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen if (file->buffer != NULL && file->buffer_offset <= start_offset) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* see if we already have it */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen file->buffer_offset <= start_offset && end_offset == (uoff_t)-1) {
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen /* we've seen the whole file.. do we have all of it mapped? */
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen if (file->buffer_offset + size == file->hdr.used_size)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen if (mail_transaction_log_file_read_hdr(file, &st) <= 0)
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen "offset (%"PRIuUOFF_T"u) < header size (%"PRIuSIZE_T")",
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen ret = mail_transaction_log_file_read(file, start_offset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Unexpected EOF");
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen /* make sure we don't leave ourself in
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen inconsistent state */
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen file->buffer = buffer_create_const_data(default_pool, file->mmap_base,
8bae533c96e129dca8ee7b494d7de5aeb4a043a2Timo Sirainenstatic int mail_transaction_log_lock_head(struct mail_transaction_log *log)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen /* we want to get the head file locked. this is a bit racy,
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen since by the time we have it locked a new log file may have been
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen creating new log file requires locking the head file, so if we
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen can lock it and don't see another file, we can be sure no-one is
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen creating a new log at the moment */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (mail_transaction_log_file_lock(file, F_WRLCK) < 0)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen /* success */
if (ret < 0)
return ret;
const void *data;
int ret;
return ret;
unsigned char *data;
expunges_before = 0;
exp++;
if (!two)
if (expunges_before != 0) {
if (uids) {
if (two) {
exp2++;
if (count != 0) {
if (uids) {
struct mail_index_transaction *t)
sizeof(struct mail_transaction_flag_update),
sizeof(struct mail_transaction_cache_update),
sizeof(struct mail_transaction_expunge),
const void *data;
if (size == 0)
size = 0;
if (external)
if (size != 0) {
int ret;
ret = 0;
if (ret == 0) {
t->hide_transaction) {
if (ret < 0) {
return ret;