fts-expunge-log.c revision 02c335c23bf5fa225a467c19f2c063fb0dc7b8c3
/* Copyright (c) 2011-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "crc32.h"
#include "hash.h"
#include "istream.h"
#include "write-full.h"
#include "seq-range-array.h"
#include "mail-storage.h"
#include "fts-expunge-log.h"
#include <unistd.h>
#include <fcntl.h>
struct fts_expunge_log_record {
/* CRC32 of this entire record (except this checksum) */
/* Size of this entire record */
/* Mailbox GUID */
/* { uid1, uid2 } pairs */
/* uint32_t expunge_uid_ranges[]; */
/* Total number of messages expunged so far in this log */
/* uint32_t expunge_count; */
};
struct fts_expunge_log {
char *path;
int fd;
};
struct fts_expunge_log_mailbox {
unsigned uids_count;
};
struct fts_expunge_log_append_ctx {
struct fts_expunge_log *log;
struct fts_expunge_log_mailbox *prev_mailbox;
bool failed;
};
struct fts_expunge_log_read_ctx {
struct fts_expunge_log *log;
struct fts_expunge_log_read_record read_rec;
bool failed;
bool corrupted;
bool unlink;
};
{
struct fts_expunge_log *log;
return log;
}
{
}
{
int fd;
/* FIXME: use proper permissions */
if (fd == -1) {
return 0;
return -1;
}
i_close_fd(&fd);
return -1;
}
return 1;
}
static int
{
/* same file */
return 0;
}
/* file changed */
/* recreate the file */
} else {
return -1;
}
}
static int
{
return -1;
}
*expunge_count_r = 0;
return 0;
}
/* we'll assume that write()s atomically grow the file size, as
O_APPEND almost guarantees. even if not, having a race condition
isn't the end of the world. the expunge count is simply read wrong
and fts optimize is performed earlier or later than intended. */
if (ret < 0) {
return -1;
}
if (ret != sizeof(*expunge_count_r)) {
(int)ret, (int)sizeof(*expunge_count_r));
return -1;
}
return 0;
}
struct fts_expunge_log_append_ctx *
{
struct fts_expunge_log_append_ctx *ctx;
return ctx;
}
static struct fts_expunge_log_mailbox *
const guid_128_t mailbox_guid)
{
struct fts_expunge_log_mailbox *mailbox;
return mailbox;
}
static struct fts_expunge_log_mailbox *
const guid_128_t mailbox_guid)
{
struct fts_expunge_log_mailbox *mailbox;
else {
}
return mailbox;
}
const guid_128_t mailbox_guid,
{
struct fts_expunge_log_mailbox *mailbox;
mailbox->uids_count++;
}
const guid_128_t mailbox_guid,
{
struct fts_expunge_log_mailbox *mailbox;
/* To be honest, an unbacked log doesn't need to maintain the uids_count,
but we don't know here if we're supporting an unbacked log or not, so we
have to maintain the value, just in case.
At the moment, the only caller of this function is for unbacked logs. */
}
const struct fts_expunge_log_read_record *record)
{
/* FIXME: Optimise with a merge */
}
static void
{
struct hash_iterate_context *iter;
struct fts_expunge_log_mailbox *mailbox;
struct fts_expunge_log_record *rec;
/* uint32_t expunge_uid_ranges[]; */
sizeof(struct seq_range));
/* uint32_t expunge_count; */
/* update the header now that we know the record contents */
rec->record_size -
}
}
static int
{
uint32_t expunge_count, *e;
int ret;
/* Unbacked expunge logs cannot be written, by definition */
/* try to append to the latest file */
return -1;
return -1;
/* the file was opened with O_APPEND, so this write() should be
appended atomically without any need for locking. */
for (;;) {
}
break;
/* the log was unlinked, so we'll need to write again to
the new file. the expunge_count needs to be reset to zero
from here. */
sizeof(uint32_t));
i_assert(*e > expunge_count);
*e -= expunge_count;
expunge_count = 0;
}
buffer_free(&buf);
if (ret == 0) {
/* finish by closing the log. this forces NFS to flush the
changes to disk without our having to explicitly play with
fsync() */
/* FIXME: we should ftruncate() in case there
were partial writes.. */
ret = -1;
}
}
return ret;
}
bool commit)
{
return ret;
}
unsigned int *expunges_r)
{
int ret;
*expunges_r = 0;
return ret;
}
}
{
}
{
}
struct fts_expunge_log_read_ctx *
{
struct fts_expunge_log_read_ctx *ctx;
return ctx;
}
static bool
unsigned int *uids_size_r)
{
return FALSE;
}
static void
unsigned int wanted_size)
{
} else {
i_error("Corrupted fts expunge log %s: "
}
}
const struct fts_expunge_log_read_record *
{
const unsigned char *data;
const struct fts_expunge_log_record *rec;
unsigned int uids_size;
return NULL;
/* initial read to try to get the record */
/* expected EOF - mark the file as read by unlinking it */
/* try reading again, in case something new was written */
}
/* expected EOF */
return NULL;
}
return NULL;
}
i_error("Corrupted fts expunge log %s: "
"Invalid record size: %u",
return NULL;
}
/* read the entire record */
return NULL;
}
}
/* verify that the record checksum is valid */
i_error("Corrupted fts expunge log %s: "
"Record checksum mismatch: %u != %u",
return NULL;
}
/* create the UIDs array by pointing it directly into input
stream's buffer */
sizeof(struct seq_range));
}
{
}
return ret;
}
int fts_expunge_log_flatten(const char *path,
struct fts_expunge_log_append_ctx **flattened_r)
{
struct fts_expunge_log *read;
struct fts_expunge_log_read_ctx *read_ctx;
const struct fts_expunge_log_read_record *record;
struct fts_expunge_log_append_ctx *append;
int ret;
}
*flattened_r = append;
return ret;
}
{
const struct fts_expunge_log_mailbox *mailbox;
return FALSE;
}