mail-modifylog.c revision c2bb1764c359ce85a7f7f789ead11dd613ff9769
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "file-lock.h"
#include "file-set-size.h"
#include "mmap-util.h"
#include "write-full.h"
#include "mail-index.h"
#include "mail-index-util.h"
#include "mail-modifylog.h"
#include <stddef.h>
#include <stdlib.h>
#include <fcntl.h>
/* Maximum size for modify log (isn't exact) */
/* How large chunks to use to grow log file */
/* FIXME: not ANSI-C */
typedef struct _ModifyLogFile ModifyLogFile;
struct _ModifyLogFile {
int fd;
char *filepath;
void *mmap_base;
unsigned int synced_id;
unsigned int anon_mmap:1;
unsigned int modified:1;
unsigned int second_log:1;
};
struct _MailModifyLog {
};
static const ModifyLogExpunge no_expunges = { 0, 0, 0 };
const char *function)
{
"%s failed with modify log file %s: %m",
return FALSE;
}
{
t_push();
t_pop();
/* make sure we don't get back here */
return FALSE;
}
{
int ret;
/* revert back to shared lock */
if (ret < 0) {
return -1;
}
if (ret == 0) {
/* shouldn't happen */
"file_try_lock(F_WRLCK -> F_RDLCK) "
return -1;
}
return 1;
}
{
int ret;
return 0;
/* try grabbing exclusive lock */
if (ret <= 0) {
if (ret < 0)
}
if (keep_lock)
return 0;
else
}
/* returns 1 = yes, 0 = no, -1 = error */
{
int ret;
if (ret == 0) {
/* check the other file too */
return -1;
}
}
return ret;
}
{
unsigned int extra;
return TRUE;
}
/* make sure we're synced before munmap() */
}
file->mmap_used_length = 0;
}
return FALSE;
}
sizeof(ModifyLogRecord);
if (extra != 0) {
/* partial write or corrupted -
truncate the file to valid length */
}
"used_file_size larger than real file size "
return FALSE;
}
sizeof(ModifyLogRecord) != 0) {
return FALSE;
}
return TRUE;
}
{
return FALSE;
return TRUE;
}
{
return log;
}
{
}
file->mmap_full_length = 0;
file->mmap_used_length = 0;
}
{
}
}
{
}
{
return FALSE;
}
return FALSE;
}
return TRUE;
}
{
unsigned int sync_id = SYNC_ID_FULL;
MS_SYNC) < 0)
} else {
return FALSE;
}
}
return TRUE;
}
/* Returns 1 = ok, 0 = can't lock file, -1 = error */
{
if (index->nodiskspace)
return -1;
if (fd == -1) {
return -1;
}
/* 1) there's race condition between open() and file_try_lock(), so
if we can't get a lock it means the other process did
2) this function is also called by try_switch_log() which uses
this check to make sure it's not locked by others. */
if (ret < 0)
/* drop back to read lock */
ret = -1;
}
if (ret > 0) {
return 1;
}
}
return ret;
}
/* Returns 1 = ok, 0 = full, -1 = error */
{
int fd;
if (fd == -1) {
return -1;
}
return -1;
}
if (ret < 0)
ret = -1;
} else {
ret = 1;
}
ret = -1;
/* we have to rebuild it, make sure it's deleted. */
}
/* full */
ret = 0;
}
if (ret > 0)
else
return ret;
}
{
for (i = 0; i < 2; i++) {
return TRUE;
}
return TRUE;
}
/* both logs were opened ok, which shouldn't happen.
safest thing to do is to mark both closed,
delete them and recreate */
"Index %s has both modify logs open",
}
if (ret1 == 1) {
return TRUE;
}
if (ret1 == -1)
break;
/* someone else probably just created the file */
}
if (ret1 == 0) {
/* we tried twice */
}
return FALSE;
}
{
}
{
int ret;
if (index->nodiskspace)
else {
if (ret == 0) {
"Couldn't lock created modify log file %s",
}
/* fatal failure */
return FALSE;
}
}
return TRUE;
}
{
if (!modifylog_files_open_or_create(log) ||
/* fatal failure */
return FALSE;
}
return TRUE;
}
{
}
{
*fsync_fd = -1;
return TRUE;
return TRUE;
}
{
}
/* if head file is closed, change it */
{
return FALSE;
return TRUE;
/* switch file */
if (mail_modifylog_open_and_verify(file) <= 0) {
"Can't switch to open log file");
return FALSE;
}
}
return FALSE;
/* we're non-synced */
return TRUE;
}
{
if (!modifylog_update_head(log))
return FALSE;
return FALSE;
}
return TRUE;
}
{
void *base;
if (base == MAP_FAILED) {
return FALSE;
}
return TRUE;
}
}
return FALSE;
return TRUE;
}
int external_change)
{
if (!external_change) {
case 0:
/* we're the only one having this log open,
no need for modify log. */
return TRUE;
case -1:
return FALSE;
}
}
if (!mail_modifylog_grow(file))
return FALSE;
}
}
return TRUE;
}
unsigned int uid, int external_change)
{
if (!modifylog_update_head(log))
return FALSE;
/* expunges must not be added when log isn't synced */
return TRUE;
/* note the different weirder logic than with
flag changing, because of reordered seq numbers. */
return TRUE;
}
}
return FALSE;
return TRUE;
}
unsigned int uid, int external_change)
{
if (!modifylog_update_head(log))
return FALSE;
return TRUE;
return TRUE;
}
}
return FALSE;
return TRUE;
}
const ModifyLogRecord **arr,
unsigned int *count)
{
}
const ModifyLogRecord **arr1,
unsigned int *count1,
const ModifyLogRecord **arr2,
unsigned int *count2)
{
if (!mmap_update_both(log))
return FALSE;
return TRUE;
}
{
return FALSE;
return FALSE;
}
return TRUE;
}
/* switches to active modify log, updating our sync mark to end of it */
{
if (mail_modifylog_open_and_verify(file) <= 0) {
"Can't switch to open log file");
return FALSE;
}
}
}
{
return TRUE;
/* no need to switch, we're the only user and we just
truncated it */
return TRUE;
}
/* locked or error, keep using the old log */
return TRUE;
}
return FALSE;
}
{
if (!mmap_update_both(log))
return FALSE;
/* tail file is full, switch to next one */
return mail_modifylog_switch_file(log);
}
}
/* if the other file isn't locked, switch to it */
return mail_modifylog_try_switch_file(log);
}
return TRUE;
}
{
}
{
}
{
rec++;
return rec;
return NULL; /* end of head */
/* end of tail, jump to beginning of head */
sizeof(ModifyLogHeader));
}
{
unsigned int count = 0;
/* only head */
sizeof(ModifyLogRecord);
} else {
/* tail */
sizeof(ModifyLogRecord);
/* + head */
sizeof(ModifyLogHeader)) /
sizeof(ModifyLogRecord);
}
}
return count;
}
const ModifyLogExpunge *
unsigned int first_seq,
unsigned int last_seq,
unsigned int *expunges_before)
{
unsigned int before, max_records;
*expunges_before = 0;
if (!mmap_update_both(log))
return NULL;
/* find the first expunged message that affects our range */
break;
}
/* none found */
return &no_expunges;
}
/* allocate memory for the returned array. the file size - synced
position should be quite near the amount of memory we need, unless
there's lots of FLAGS_CHANGED records which is why there's the
second check to make sure it's not unneededly large. */
before = 0;
continue;
/* before our range */
/* within our range, at least partially */
if (max_records-- == 0) {
/* log contains more data than it should
have - must be corrupted. */
"Contains more data than expected");
return NULL;
}
/* partial initial match, update
before-counter */
} else {
}
arr++;
}
/* update the seq. numbers so they can be compared */
}
}
}
/* sort the UID array, not including the terminating 0 */
sizeof(ModifyLogExpunge), compare_expunge);
return expunges;
}
const ModifyLogExpunge *
unsigned int first_uid,
unsigned int last_uid,
unsigned int *expunges_before)
{
/* pretty much copy&pasted from sequence code above ..
kind of annoying */
unsigned int before, max_records;
*expunges_before = 0;
if (!mmap_update_both(log))
return NULL;
/* find the first expunged message that affects our range */
break;
}
/* none found */
return &no_expunges;
}
/* allocate memory for the returned array. the file size - synced
position should be quite near the amount of memory we need, unless
there's lots of FLAGS_CHANGED records which is why there's the
second check to make sure it's not unneededly large. */
before = 0;
continue;
/* before our range */
/* within our range, at least partially */
if (max_records-- == 0) {
/* log contains more data than it should
have - must be corrupted. */
"Contains more data than expected");
return NULL;
}
arr++;
}
}
/* sort the UID array, not including the terminating 0 */
sizeof(ModifyLogExpunge), compare_expunge);
return expunges;
}
{
unsigned int expunges;
/* find the first expunged message that affects our range */
expunges = 0;
rec++;
}
return expunges;
}
{
unsigned int expunges;
if (!mmap_update_both(log))
return 0;
return expunges;
}