static void mail_transaction_log_mark_corrupted(struct mail_transaction_log_file *file) { unsigned int offset = offsetof(struct mail_transaction_log_header, indexid); int flags; if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file) || file->log->index->readonly) return; /* indexid=0 marks the log file as corrupted. we opened the file with O_APPEND, and now we need to drop it for pwrite() to work (at least in Linux) */ flags = fcntl(file->fd, F_GETFL, 0); if (flags < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "fcntl(F_GETFL)"); return; } if (fcntl(file->fd, F_SETFL, flags & ~O_APPEND) < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "fcntl(F_SETFL)"); return; } if (pwrite_full(file->fd, &file->hdr.indexid, sizeof(file->hdr.indexid), offset) < 0) { mail_index_file_set_syscall_error(file->log->index, file->filepath, "pwrite()"); } }
static int mail_index_recreate(struct mail_index *index) { struct mail_index_map *map = index->map; struct ostream *output; unsigned int base_size; const char *path; int ret = 0, fd; i_assert(!MAIL_INDEX_IS_IN_MEMORY(index)); i_assert(map->hdr.indexid == index->indexid); i_assert((map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) == 0); i_assert(index->indexid != 0); fd = mail_index_create_tmp_file(index, index->filepath, &path); if (fd == -1) return -1; output = o_stream_create_fd_file(fd, 0, FALSE); o_stream_cork(output); base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr)); o_stream_nsend(output, &map->hdr, base_size); o_stream_nsend(output, CONST_PTR_OFFSET(map->hdr_base, base_size), map->hdr.header_size - base_size); o_stream_nsend(output, map->rec_map->records, map->rec_map->records_count * map->hdr.record_size); o_stream_nflush(output); if (o_stream_nfinish(output) < 0) { mail_index_file_set_syscall_error(index, path, "write()"); ret = -1; } o_stream_destroy(&output); if (ret == 0 && index->fsync_mode != FSYNC_MODE_NEVER) { if (fdatasync(fd) < 0) { mail_index_file_set_syscall_error(index, path, "fdatasync()"); ret = -1; } } if (close(fd) < 0) { mail_index_file_set_syscall_error(index, path, "close()"); ret = -1; } if ((index->flags & MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS) != 0) (void)mail_index_create_backup(index); if (ret == 0 && rename(path, index->filepath) < 0) { mail_index_set_error(index, "rename(%s, %s) failed: %m", path, index->filepath); ret = -1; } if (ret < 0) i_unlink(path); return ret; }
static int mail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush) { struct mail_transaction_log_file *file; struct stat st; i_assert(log->head != NULL); if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head)) return 0; if (nfs_flush && log->nfs_flush) nfs_flush_file_handle_cache(log->filepath); if (nfs_safe_stat(log->filepath, &st) < 0) { if (errno != ENOENT) { mail_index_file_set_syscall_error(log->index, log->filepath, "stat()"); return -1; } /* see if the whole directory got deleted */ if (nfs_safe_stat(log->index->dir, &st) < 0 && errno == ENOENT) { log->index->index_deleted = TRUE; return -1; } /* the file should always exist at this point. if it doesn't, someone deleted it manually while the index was open. try to handle this nicely by creating a new log file. */ file = log->head; if (mail_transaction_log_create(log, FALSE) < 0) return -1; i_assert(file->refcount > 0); file->refcount--; log->index->need_recreate = TRUE; return 0; } else if (log->head->st_ino == st.st_ino && CMP_DEV_T(log->head->st_dev, st.st_dev)) { /* NFS: log files get rotated to .log.2 files instead of being unlinked, so we don't bother checking if the existing file has already been unlinked here (in which case inodes could match but point to different files) */ return 0; } file = mail_transaction_log_file_alloc(log, log->filepath); if (mail_transaction_log_file_open(file, FALSE) <= 0) { mail_transaction_log_file_free(&file); return -1; } i_assert(!file->locked); if (--log->head->refcount == 0) mail_transaction_logs_clean(log); mail_transaction_log_set_head(log, file); return 0; }
static void log_file_set_syscall_error(struct mail_transaction_log_file *file, const char *function) { mail_index_file_set_syscall_error(file->log->index, file->filepath, function); }
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset) { struct mail_transaction_log_file *file; const char *path = log->head->filepath; struct stat st; int ret; i_assert(log->head->locked); if (MAIL_INDEX_IS_IN_MEMORY(log->index)) { file = mail_transaction_log_file_alloc_in_memory(log); if (reset) { file->hdr.prev_file_seq = 0; file->hdr.prev_file_offset = 0; } } else { /* we're locked, we shouldn't need to worry about ESTALE problems in here. */ if (fstat(log->head->fd, &st) < 0) { mail_index_file_set_syscall_error(log->index, log->head->filepath, "fstat()"); return -1; } file = mail_transaction_log_file_alloc(log, path); file->st_dev = st.st_dev; file->st_ino = st.st_ino; file->last_mtime = st.st_mtime; file->last_size = st.st_size; if ((ret = mail_transaction_log_file_create(file, reset)) < 0) { mail_transaction_log_file_free(&file); return -1; } if (ret == 0) { mail_index_set_error(log->index, "Transaction log %s was recreated while we had it locked - " "locking is broken (lock_method=%s)", path, file_lock_method_to_str(log->index->lock_method)); mail_transaction_log_file_free(&file); return -1; } i_assert(file->locked); } if (--log->head->refcount == 0) mail_transaction_logs_clean(log); else { /* the newly created log file is already locked */ mail_transaction_log_file_unlock(log->head, !log->index->log_sync_locked ? "rotating" : "rotating while syncing"); } mail_transaction_log_set_head(log, file); return 0; }
int mail_transaction_log_unlink(struct mail_transaction_log *log) { if (unlink(log->filepath) < 0 && errno != ENOENT && errno != ESTALE) { mail_index_file_set_syscall_error(log->index, log->filepath, "unlink()"); return -1; } return 0; }
int mail_transaction_log_get_mtime(struct mail_transaction_log *log, time_t *mtime_r) { struct stat st; *mtime_r = 0; if (stat(log->filepath, &st) < 0) { if (errno == ENOENT) return 0; mail_index_file_set_syscall_error(log->index, log->filepath, "stat()"); return -1; } *mtime_r = st.st_mtime; return 0; }
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset) { struct mail_transaction_log_file *file; const char *path = log->head->filepath; struct stat st; i_assert(log->head->locked); if (MAIL_INDEX_IS_IN_MEMORY(log->index)) { file = mail_transaction_log_file_alloc_in_memory(log); if (reset) { file->hdr.prev_file_seq = 0; file->hdr.prev_file_offset = 0; } } else { /* we're locked, we shouldn't need to worry about ESTALE problems in here. */ if (fstat(log->head->fd, &st) < 0) { mail_index_file_set_syscall_error(log->index, log->head->filepath, "fstat()"); return -1; } file = mail_transaction_log_file_alloc(log, path); file->st_dev = st.st_dev; file->st_ino = st.st_ino; file->last_mtime = st.st_mtime; file->last_size = st.st_size; if (mail_transaction_log_file_create(file, reset) < 0) { mail_transaction_log_file_free(&file); return -1; } } if (--log->head->refcount == 0) mail_transaction_logs_clean(log); else mail_transaction_log_file_unlock(log->head); mail_transaction_log_set_head(log, file); return 0; }