mail-transaction-log.c revision 7394389230750c45b105cdefb5850c81cae8cdc0
/* Copyright (C) 2003-2004 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "file-lock.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>
/* 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 LOG_NEW_DOTLOCK_SUFFIX ".newlock"
struct mail_transaction_add_ctx {
struct mail_transaction_log *log;
struct mail_index_view *view;
};
static struct mail_transaction_log_file *
const char *path);
int lock_type);
static int
int lock_type);
void
const char *fmt, ...)
{
}
t_push();
"Corrupted transaction log file %s: %s",
t_pop();
}
sizeof(struct mail_transaction_log_header)) || \
{
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 - FIXME: missing error handling. */
}
return log;
}
{
}
static int
int lock_type)
{
int ret;
return 0;
if (ret < 0) {
return -1;
}
if (ret == 0) {
"Dotlock was lost for transaction log file %s",
return -1;
}
return 0;
}
ret = 1;
else {
}
if (ret > 0) {
return 0;
}
if (ret < 0) {
"file_lock_dotlock()");
return -1;
}
"Timeout while waiting for release of "
"dotlock for transaction log file %s",
return -1;
}
static int
int lock_type)
{
int ret;
return 0;
} else {
}
if (ret > 0) {
return 0;
}
if (ret < 0) {
"file_wait_lock()");
return -1;
}
"Timeout while waiting for release of "
"%s fcntl() lock for transaction log file %s",
return -1;
}
static void
{
"munmap()");
}
}
}
}
static int
{
int ret;
else {
return -1;
/* we have to fstat() again since it may have changed after
locking. */
"fstat()");
return -1;
}
}
if (ret < 0) {
// FIXME: handle ESTALE
"pread_full()");
return -1;
}
if (ret == 0) {
"unexpected end of file while reading header");
return 0;
}
/* 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 */
"Transaction log file %s: invalid indexid (%u != %u)",
return 0;
}
return 0;
}
"used_size (%u) < old_size (%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 {
fd);
return fd2;
}
fd2 = -1;
if (ret < 0)
return -1;
return -1;
}
return -1;
}
/* make sure the sequence grows */
}
"write_full()");
return -1;
}
if (fd2 < 0) {
return -1;
}
return -1;
/* success */
return fd2;
}
static int
{
/* With dotlocking we might already have path.lock created, so this
filename has to be different. */
if (fd == -1) {
"file_dotlock_open()");
return -1;
}
if (fd2 < 0) {
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;
}
/* log replaced with file having same sequence as
previous one. shouldn't happen unless previous
log file was corrupted.. */
break;
}
}
*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;
}
}
}
int lock_type)
{
struct mail_transaction_log_file *file;
int fd;
"fstat()");
return -1;
}
if (fd == -1)
return -1;
return -1;
return -1;
}
}
else
return 0;
}
{
unsigned int lock_id;
int ret;
return -1;
return ret;
}
{
struct mail_transaction_log_file *file;
const char *path;
int ret;
/* lost? */
return mail_transaction_log_recreate(log);
}
return -1;
}
/* same file */
/* corrupted, recreate */
return mail_transaction_log_recreate(log);
}
return ret <= 0 ? -1 : 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
{
void *data;
int ret;
/* we have to insert missing data to beginning of buffer */
/* log file was deleted in NFS server, fail silently */
ret = 0;
}
if (ret <= 0)
return ret;
}
size = 0;
} else {
/* caller should have checked this.. */
return 1;
}
}
if (size == 0)
return 1;
/* log file was deleted in NFS server, fail silently */
ret = 0;
}
return ret;
}
{
/* corrupted */
return 0;
}
/* 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;
}
/* we've seen the whole file.. do we have all of it mapped? */
return 1;
}
}
"munmap()");
}
}
return -1;
return -1;
}
return -1;
}
if (!use_mmap) {
if (ret <= 0) {
if (ret < 0) {
} else {
"Unexpected EOF");
}
/* make sure we don't leave ourself in
inconsistent state */
}
}
return ret;
}
"mmap()");
return -1;
}
file->buffer_offset = 0;
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 (;;) {
return -1;
}
/* success */
break;
}
return -1;
}
if (ret < 0)
break;
/* try again */
}
return ret;
}
static void
const struct mail_transaction_header *hdr,
const void *data)
{
return;
return;
/* we'll just check that none of the appends are already in
transaction log. this could happen if we crashed before we had
a chance to update index file */
/* appends are sorted */
}
break;
}
}
}
if (deleted) {
/* compress deleted appends away */
dest++;
}
}
}
struct mail_index_transaction *t)
{
struct mail_transaction_log_view *sync_view;
const struct mail_transaction_header *hdr;
const void *data;
int ret;
t->view->log_file_offset,
case MAIL_TRANSACTION_APPEND:
break;
case MAIL_TRANSACTION_CACHE_RESET: {
break;
}
}
}
/* make sure we're not writing cache_offsets to old cache file */
if (t->new_cache_file_seq == 0 && max_cache_file_seq != 0 &&
max_cache_file_seq != t->last_cache_file_seq &&
t->cache_updates != NULL) {
buffer_free(t->cache_updates);
t->cache_updates = NULL;
}
return ret;
}
{
struct mail_transaction_header hdr;
if (size == 0)
return 0;
else {
hdr_size = 0;
}
if (type == MAIL_TRANSACTION_EXPUNGE)
if (external)
return -1;
if (hdr_size > 0) {
return -1;
}
return -1;
return 0;
}
{
struct mail_transaction_cache_reset u;
memset(&u, 0, sizeof(u));
u.new_file_seq = t->new_cache_file_seq;
buffer_append(buf, &u, sizeof(u));
return buf;
}
static const buffer_t *
log_get_hdr_update_buffer(struct mail_index_transaction *t)
{
struct mail_transaction_header_update u;
int state = 0;
memset(&u, 0, sizeof(u));
if (state == 0) {
state++;
}
} else {
if (state > 0) {
u.size);
state = 0;
}
}
}
return buf;
}
int mail_transaction_log_append(struct mail_index_transaction *t,
{
struct mail_index *index;
struct mail_transaction_log *log;
struct mail_transaction_log_file *file;
struct mail_index_header idx_hdr;
unsigned int i, lock_id;
int ret;
/* nothing to append */
*log_file_seq_r = 0;
*log_file_offset_r = 0;
return 0;
}
} else {
if (mail_transaction_log_lock_head(log) < 0)
return -1;
}
F_UNLCK);
}
return -1;
}
/* we might want to rotate, but check first that head file
sequence matches the one in index header, ie. we have
everything synced in index. */
/* that didn't work. well, try to continue
anyway */
}
}
}
if (t->cache_updates != NULL &&
/* cache_offsets point to old file, don't allow */
buffer_free(t->cache_updates);
t->cache_updates = NULL;
}
if (mail_transaction_log_scan_pending(log, t) < 0) {
F_UNLCK);
}
return -1;
}
}
ret = 0;
struct mail_transaction_append_header hdr;
}
}
if (t->new_cache_file_seq != 0) {
}
}
&extra_rec_hdr, sizeof(extra_rec_hdr));
continue;
/* FIXME: do data_id mapping conversion */
extra_rec_hdr.idx = i;
}
}
if (t->hdr_changed && ret == 0) {
}
if (ret == 0) {
/* rewrite used_size */
}
t->hide_transaction) {
}
if (ret < 0) {
"pwrite()");
/* we don't know how much of it got written,
it may be corrupted now.. */
"fsync()");
ret = -1;
}
return ret;
}
{
if (mail_transaction_log_lock_head(log) < 0)
return -1;
return 0;
}
{
}
{
}