mail-transaction-log.c revision a1459641213ad3b0516e1c900229ad543957554a
/* Copyright (C) 2003-2004 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "file-dotlock.h"
#include "read-full.h"
#include "write-full.h"
#include "mmap-util.h"
#include "mail-index-private.h"
#include "mail-index-view-private.h"
#include "mail-transaction-log-private.h"
#include "mail-transaction-util.h"
#include "mail-index-transaction-private.h"
#include <stddef.h>
#define LOG_PREFETCH 1024
/* this lock should never exist for a long time.. */
#define LOG_DOTLOCK_TIMEOUT 30
#define LOG_DOTLOCK_STALE_TIMEOUT 0
#define LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT 300
#define MAIL_TRANSACTION_LOG_PREFIX ".log"
#define LOG_NEW_DOTLOCK_SUFFIX ".newlock"
static struct mail_transaction_log_file *
const char *path);
void
const char *fmt, ...)
{
}
t_push();
"Corrupted transaction log file %s: %s",
t_pop();
/* this may have happened because of broken index.
make sure it's ok. */
}
}
static int
{
int ret;
ret = 1;
else {
}
if (ret > 0) {
return 0;
}
if (ret < 0) {
"file_dotlock_create()");
return -1;
}
"Timeout while waiting for release of "
"dotlock for transaction log file %s",
return -1;
}
static int
{
int ret;
return 0;
if (ret < 0) {
return -1;
}
if (ret == 0) {
"Dotlock was lost for transaction log file %s",
return -1;
}
return 0;
}
static int
{
int ret;
return 0;
return mail_transaction_log_file_dotlock(file);
if (ret > 0) {
return 0;
}
if (ret < 0) {
"mail_index_wait_lock_fd()");
return -1;
}
"Timeout while waiting for release of "
"fcntl() lock for transaction log file %s",
return -1;
}
{
int ret;
return;
return;
}
F_UNLCK, 0);
if (ret <= 0) {
"mail_index_wait_lock_fd()");
}
}
{
struct mail_transaction_log_file *file;
unsigned int lock_id;
int ret;
if (mail_transaction_log_lock_head(log) < 0)
return -1;
if (ret == 0) {
if (ret <= 0)
ret = -1;
/* broken - fix it by creating a new log file */
}
}
else
return ret;
}
struct mail_transaction_log *
{
struct mail_transaction_log *log;
const char *path;
return NULL;
}
/* head log file isn't same as head index file -
shouldn't happen except in race conditions.
lock them and check again */
if (mail_transaction_log_check_file_seq(log) < 0) {
return NULL;
}
}
return log;
}
{
}
}
static void
{
"munmap()");
}
}
}
}
static int
{
struct mail_transaction_log_file *f;
int ret;
if (ret < 0) {
// FIXME: handle ESTALE
"pread_full()");
return -1;
}
if (ret == 0) {
"unexpected end of file while reading header");
return 0;
}
/* incompatible version - fix silently */
return 0;
}
"Header size too small");
return 0;
}
/* @UNSAFE: smaller than we expected - zero out the fields we
shouldn't have filled */
}
/* corrupted */
"Transaction log file %s: marked corrupted",
return 0;
}
/* creating index file, silently rebuild
transaction log as well */
return 0;
}
/* index file was probably just rebuilt and we don't know
about it yet */
"invalid indexid (%u != %u)",
return 0;
}
/* make sure we already don't have a file with the same sequence
opened. it shouldn't happen unless the old log file was
corrupted. */
"invalid new transaction log sequence "
"(%u >= %u)",
return 0;
}
}
return 1;
}
static int
{
struct mail_transaction_log_header hdr;
unsigned int lock_id;
/* log creation is locked now - see if someone already created it */
if (fd2 != -1) {
"fstat()");
/* same file, still broken */
} else {
(void)file_dotlock_delete(dotlock);
return fd2;
}
fd2 = -1;
if (ret < 0)
return -1;
return -1;
}
return -1;
return -1;
}
}
/* make sure the sequence grows */
}
"write_full()");
return -1;
}
return -1;
/* success */
return fd;
}
static int
const char *path,
{
/* With dotlocking we might already have path.lock created, so this
filename has to be different. */
if (fd == -1) {
"file_dotlock_open()");
return -1;
}
(void)file_dotlock_delete(&dotlock);
return -1;
}
if (fd2 < 0) {
(void)file_dotlock_delete(&dotlock);
return -1;
}
return fd2;
}
static struct mail_transaction_log_file *
{
struct mail_transaction_log_file **p;
struct mail_transaction_log_file *file;
int ret;
return NULL;
}
if (ret == 0) {
/* corrupted header */
if (fd == -1)
ret = -1;
"fstat()");
fd = -1;
ret = -1;
}
if (fd != -1) {
}
}
if (ret <= 0) {
return NULL;
}
/* we can get a valid log offset from index file. initialize
sync_offset from it so we don't have to read the whole log
file from beginning. */
} else {
}
/* append to end of list. */
*p = file;
return file;
}
static struct mail_transaction_log_file *
const char *path)
{
int fd;
if (fd == -1) {
"open()");
return NULL;
}
if (fd == -1)
return NULL;
}
}
{
struct mail_transaction_log_file **p, *next;
if ((*p)->refcount != 0)
p = &(*p)->next;
else {
*p = next;
}
}
}
{
struct mail_transaction_log_file *file;
int fd;
"fstat()");
return -1;
}
if (fd == -1)
return -1;
return -1;
if (lock) {
if (mail_transaction_log_file_lock(file) < 0) {
return -1;
}
}
else
return 0;
}
{
struct mail_transaction_log_file *file;
const char *path;
return -1;
}
/* same file */
return 0;
}
return -1;
}
return 0;
}
struct mail_transaction_log_file **file_r)
{
struct mail_transaction_log_file *file;
if (mail_transaction_log_refresh(log) < 0)
return -1;
}
return 1;
}
}
return 0;
}
static int
{
const struct mail_transaction_header *hdr;
const void *data;
if (hdr_size == 0) {
/* unfinished */
}
return 0;
}
return -1;
}
/* record goes outside the file we've seen. or if
we're accessing the log file via unlocked mmaped
memory, it may be just that the memory was updated
after we checked the file size. */
return -1;
}
break;
}
}
return 0;
}
static int
{
void *data;
int ret;
/* we have to insert missing data to beginning of buffer */
if (ret == 0) {
"Unexpected end of file");
return 0;
}
if (ret < 0) {
/* log file was deleted in NFS server,
fail silently */
return 0;
}
"pread()");
return -1;
}
}
}
/* read all records */
do {
if (ret > 0)
read_offset += ret;
if (mail_transaction_log_file_sync(file) < 0)
return -1;
if (ret == 0) {
/* EOF */
}
return 1;
}
/* log file was deleted in NFS server, fail silently */
return 0;
}
"pread()");
return -1;
}
{
/* corrupted */
return 0;
}
return -1;
}
/* with mmap_no_write we could alternatively just write to log with
msync() rather than pwrite(). that'd cause slightly more disk I/O,
so rather use more memory. */
/* see if we already have it */
return 1;
}
"fstat()");
return -1;
}
return -1;
}
/* it's all mmaped already */
if (mail_transaction_log_file_sync(file) < 0)
return -1;
return 1;
}
}
"munmap()");
}
}
if (!use_mmap) {
if (ret <= 0) {
/* make sure we don't leave ourself in
inconsistent state */
}
return ret;
}
} else {
"mmap()");
return -1;
}
MADV_SEQUENTIAL) < 0) {
}
}
file->buffer_offset = 0;
if (mail_transaction_log_file_sync(file) < 0)
return -1;
}
return -1;
}
return 1;
}
{
struct mail_transaction_log_file *file;
int ret = 0;
/* we want to get the head file locked. this is a bit racy,
since by the time we have it locked a new log file may have been
created.
creating new log file requires locking the head file, so if we
can lock it and don't see another file, we can be sure no-one is
creating a new log at the moment */
for (;;) {
if (mail_transaction_log_file_lock(file) < 0)
return -1;
}
/* success */
break;
}
if (ret < 0)
break;
/* try again */
}
return ret;
}
{
if (mail_transaction_log_lock_head(log) < 0)
return -1;
/* update sync_offset */
(uoff_t)-1) < 0) {
return -1;
}
return 0;
}
{
}
{
}