dbox-uidlist.c revision 6e1e9e341ffe21a69a23229c2b896d03066a071e
/* Copyright (C) 2005 Timo Sirainen */
#include "lib.h"
#include "hex-dec.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "seq-range-array.h"
#include "str.h"
#include "istream.h"
#include "ostream.h"
#include "ostream-crlf.h"
#include "write-full.h"
#include "dbox-file.h"
#include "dbox-storage.h"
#include "dbox-uidlist.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>
#define DBOX_SYNC_SECS 1
#define DBOX_APPEND_MAX_OPEN_FDS 64
#define DBOX_UIDLIST_VERSION 1
#define DBOX_UIDLIST_FILENAME "index"
struct dbox_save_file {
ARRAY_DEFINE(seqs, unsigned int);
};
struct dbox_uidlist {
struct dbox_mailbox *mbox;
char *path;
int fd;
int lock_fd;
unsigned int lock_count;
unsigned int version;
unsigned int appending:1;
unsigned int need_full_rewrite:1;
};
struct dbox_uidlist_append_ctx {
struct dbox_uidlist *uidlist;
unsigned int mail_count;
unsigned int open_fds;
unsigned int locked:1;
};
struct dbox_uidlist_sync_ctx {
struct dbox_uidlist *uidlist;
unsigned int modified:1;
};
{
struct dbox_uidlist *uidlist;
return uidlist;
}
{
}
{
unsigned int count;
return FALSE;
/* we can just continue the existing range */
} else {
}
return TRUE;
}
static int dbox_uidlist_entry_cmp(const void *key, const void *p)
{
struct dbox_uidlist_entry *const *entry = p;
}
const struct dbox_uidlist_entry *entry)
{
unsigned int count;
}
const struct dbox_uidlist_entry *src_entry)
{
/* append new file sequence */
} else {
sizeof(*entries),
}
/* new entry */
struct dbox_uidlist_entry, 1);
*dest_entry = *src_entry;
} else {
/* merge to existing entry. UIDs must be growing since only
new mails are appended */
for (i = 0; i < count; i++) {
"%s: UIDs not ordered (file_seq=%u)",
return FALSE;
}
}
}
return TRUE;
}
{
struct dbox_uidlist_entry *entry;
int ret;
/* <uid list> <file seq> [<last write timestamp> <file size>] */
t_push();
/* get uid list */
else {
/* broken */
break;
}
}
/* broken */
digit);
break;
}
/* last_uid isn't up to date */
}
if (*line == ' ')
break;
}
digit = 0;
}
}
if (*line != ' ') {
"Expecting space after UID list";
error = "UID list missing";
}
t_pop();
return FALSE;
}
/* get file seq */
/* get create timestamp */
line++;
if (*line != ' ') {
"%s: Corrupted entry: Expecting space after timestamp",
t_pop();
return FALSE;
}
/* get file size */
t_pop();
return ret;
}
{
const char *line;
int ret;
return 1;
}
return -1;
}
return 0;
}
/* unchanged */
return 1;
}
}
}
return 0;
return -1;
}
return -1;
}
/* read header: <version> <uidvalidity> <next-uid>.
Note that <next-uid> may be updated by UID lines, so it can't be
used directly. */
&uid_validity, &last_uid,
&last_file_seq) != 4 ||
"Corrupted header in file %s (version = %u)",
ret = 0;
} else {
uidlist->last_file_seq : 0;
ret = 1;
ret = 0;
break;
}
}
"%s: last_uid was lowered (%u -> %u)",
ret = 0;
}
"%s: last_file_seq was lowered (%u -> %u)",
ret = 0;
}
}
if (ret == 0) {
/* broken file */
}
return ret;
}
{
if (uidlist->lock_count == 0)
else {
uidlist->lock_count++;
return 0;
}
return -1;
}
uidlist->lock_count++;
return 0;
}
{
if (--uidlist->lock_count > 0) {
return;
}
if (uidlist->need_full_rewrite) {
(void)dbox_uidlist_full_rewrite(uidlist);
return;
}
}
static struct dbox_uidlist_entry *
unsigned int *idx_r)
{
unsigned int count;
return NULL;
return *entry;
}
struct dbox_uidlist_entry *
{
unsigned int idx;
}
{
if (days == 0)
return 0;
/* get beginning of today */
i_panic("mktime(today) failed");
}
struct dbox_uidlist_append_ctx *
{
struct dbox_uidlist_append_ctx *ctx;
return ctx;
}
{
struct dbox_uidlist_entry *const *entries;
const char *lock_path;
int ret = 0;
return 0;
}
t_push();
/* header: <version> <uidvalidity> <next-uid> <last-file-seq>. */
for (i = 0; i < count; i++) {
str_truncate(str, 0);
i_assert(i == 0 ||
/* <uid list> <file seq> [<last write timestamp> <file size>] */
i_assert(range_count != 0);
else {
}
}
(unsigned long)entries[i]->create_time,
break;
}
t_pop();
if (output->stream_errno != 0) {
ret = -1;
}
if (ret < 0)
return -1;
/* grow mtime by one if needed to make sure the last write is noticed */
return -1;
}
}
"fstat(%s) failed: %m", lock_path);
return -1;
}
"utime(%s) failed: %m", lock_path);
return -1;
}
}
/* now, finish the uidlist update by renaming the lock file to
uidlist */
uidlist->lock_count--;
return -1;
return 0;
}
{
const unsigned int *seqs;
str_truncate(str, 0);
/* build uidlist string */
start = 0;
continue;
}
}
/* add creation time and file size */
}
{
struct dbox_save_file *const *files;
unsigned int i, count;
t_push();
for (i = 0; i < count; i++) {
i_panic("dbox_uidlist_next() internal update failed");
}
t_pop();
}
{
struct dbox_save_file *const *files;
unsigned int i, count;
int ret = 1;
return -1;
}
/* we can't update this file without temporarily moving mtime
backwards */
return 0;
}
return -1;
}
/* simply append the change-lines to the index file. if someone's
reading the file at the same time, it doesn't matter. the entries
are complete only after the LF has been written. */
t_push();
for (i = 0; i < count; i++) {
i_panic("dbox_uidlist_next() internal update failed");
}
t_pop();
if (output->stream_errno != 0) {
ret = -1;
}
if (ret < 0)
return -1;
/* grow mtime by one to make sure the last write is noticed */
return -1;
}
return -1;
}
return 1;
}
static int
{
struct dbox_save_file *const *files;
struct dbox_file_header hdr;
unsigned int i, count;
int ret = 0;
for (i = 0; i < count; i++) {
"fsync(%s) failed: %m",
ret = -1;
continue;
}
}
sizeof(hdr.append_offset_hex),
offsetof(struct dbox_file_header,
append_offset_hex)) < 0) {
"pwrite_full(%s) failed: %m",
ret = -1;
}
}
return ret;
}
{
int ret = 0;
if (ctx->mail_count == 0) {
/* nothing actually appended */
return 0;
}
if (dbox_uidlist_write_append_offsets(ctx) < 0)
ret = -1;
else {
if (ret < 0)
return -1;
if (ret == 0)
}
}
}
return ret;
}
{
struct dbox_save_file *const *files;
unsigned int i, count;
/* unlock files */
for (i = 0; i < count; i++) {
}
}
struct dbox_save_file *save_file)
{
return 0;
/* open the file and make sure it's the same as expected,
since we have it locked */
return -1;
}
return -1;
}
return -1;
}
return 0;
}
{
struct dbox_save_file *const *files;
unsigned int i, count;
for (i = 0; i < count; i++) {
return TRUE;
}
return FALSE;
}
static int
{
int fd;
if (fd == -1) {
/* the file was unlinked just now, update its size
so that we don't get back here. */
return 0;
}
"open(%s) failed: %m", path);
return -1;
}
"fstat(%s) failed: %m", path);
return -1;
}
65536, FALSE);
return -1;
}
} else {
return -1;
}
}
file->append_offset)) {
return 0;
}
}
return 1;
}
{
return 0;
"stat(%s) failed: %m", path);
return -1;
}
/* doesn't exist, make sure that index's last file seq is lower */
return -1;
}
static int
struct dbox_uidlist_entry **entry_r,
{
struct dbox_uidlist_entry *const *entries;
unsigned int i, count;
int ret;
for (i = 0;; i++) {
file_seq = 0;
*existing_r = FALSE;
for (; i < count; i++) {
mail_size) &&
*existing_r = TRUE;
break;
}
}
if (file_seq == 0) {
/* create new file */
}
/* try locking the file. */
str_truncate(path, 0);
if (ret > 0) {
/* success. but since we don't have uidlist locked
here, it's possible that the file was just deleted
by someone else. in that case we really don't want
to create the file back and cause problems. */
file_seq);
if (i < count) {
/* dbox file was re-read, find the entry
again */
&count);
for (i = 0; i < count; i++) {
break;
}
}
if (ret == 0) {
break;
}
/* error / it was used, continue with another
file sequence */
if (ret < 0)
return -1;
} else if (ret < 0) {
"file_dotlock_create(%s) failed: %m",
return -1;
}
/* lock already exists, try next file */
}
*file_seq_r = file_seq;
return 0;
}
{
struct dbox_uidlist_entry *entry;
unsigned int i, count;
bool existing;
int ret;
/* check first from already opened files */
for (i = 0; i < count; i++) {
mail_size)) {
return -1;
return 0;
}
}
/* check from other existing files. use uidlist's file_size field.
it's not completely trustworthy though. */
do {
return -1;
if (ret < 0) {
return -1;
}
/* we'll always use CRLF linefeeds for mails (but not the header,
so don't do this before dbox_file_write_header()) */
return 0;
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
break;
}
}
ctx->mail_count++;
}
struct dbox_file *
{
struct dbox_save_file *const *files;
unsigned int i, count;
for (i = 0; i < count; i++) {
}
i_unreached();
return NULL;
}
{
/* Note that unless uidlist is locked, it's not guaranteed that this
actually returns a new unused file sequence. */
return ++uidlist->file_seq_highwater;
}
{
int ret;
/* from now on we'll need to keep uidlist locked until it's
committed or rollbacked */
return -1;
/* update uidlist to make sure we have the latest state */
return -1;
if (ret == 0) {
/* file is deleted */
}
}
return 0;
}
struct dbox_uidlist_sync_ctx **ctx_r,
{
int ret;
*mtime_r = -1;
if (dbox_uidlist_lock(uidlist) < 0)
return -1;
return -1;
}
if (ret == 0) {
/* file is deleted */
} else {
}
return 0;
}
{
int ret = 0;
/* this call may or may not release the dotlock.. */
}
return ret;
}
{
}
{
}
{
}
const struct dbox_uidlist_entry *entry)
{
struct dbox_uidlist_entry *new_entry;
unsigned int count;
struct dbox_uidlist_entry, 1);
else {
unsigned int idx;
}
}
{
struct dbox_uidlist_entry *entry;
unsigned int idx;
}
{
}
}
{
}
{
return -1;
}
*mtime_r = 0;
} else {
}
return 0;
}