/* Copyright (c) 2011-2018 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 {
bool failed;
};
struct fts_expunge_log_read_ctx {
bool failed;
bool corrupted;
bool unlink;
};
{
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 *
{
return ctx;
}
static struct fts_expunge_log_mailbox *
const guid_128_t mailbox_guid)
{
return mailbox;
}
static struct fts_expunge_log_mailbox *
const guid_128_t mailbox_guid)
{
else {
}
return mailbox;
}
const guid_128_t mailbox_guid,
{
mailbox->uids_count++;
}
const guid_128_t mailbox_guid,
{
/* 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 */
}
struct fts_expunge_log_mailbox *mailbox)
{
/* FIXME: Optimise with a merge */
}
static void
{
/* 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
{
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 *
{
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;
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;
}
struct fts_expunge_log_append_ctx **flattened_r)
{
int ret;
}
*flattened_r = append;
return ret;
}
{
return FALSE;
}
const struct fts_expunge_log_read_record *record)
{
return 0; /* may only remove things that exist */
return 1;
}
struct fts_expunge_log *subtract)
{
unsigned int failures = 0;
failures++;
}
if (failures > 0)
i_warning("fts: Expunge log subtract ignored %u nonexistent mailbox GUIDs",
failures);
return fts_expunge_log_read_end(&read_ctx);
}
/* It could be argued that somehow adding a log (file) to the append context
and then calling the _write() helper would be easier. But then there's the
_commit() vs. _abort() cleanup that would need to be addressed. Just creating
a copy is simpler. */
const char *path)
{
int ret;
return ret;
}