dovecot-2.0: lib-index: Added mailbox transaction log.
dovecot at dovecot.org
dovecot at dovecot.org
Thu Aug 6 03:30:53 EEST 2009
details: http://hg.dovecot.org/dovecot-2.0/rev/d21f2f0b1e11
changeset: 9732:d21f2f0b1e11
user: Timo Sirainen <tss at iki.fi>
date: Wed Aug 05 20:28:50 2009 -0400
description:
lib-index: Added mailbox transaction log.
It's a much simplified version of mail transaction log.
diffstat:
3 files changed, 325 insertions(+), 2 deletions(-)
src/lib-index/Makefile.am | 6
src/lib-index/mailbox-log.c | 279 +++++++++++++++++++++++++++++++++++++++++++
src/lib-index/mailbox-log.h | 42 ++++++
diffs (truncated from 352 to 300 lines):
diff -r 111812403bea -r d21f2f0b1e11 src/lib-index/Makefile.am
--- a/src/lib-index/Makefile.am Wed Aug 05 20:24:56 2009 -0400
+++ b/src/lib-index/Makefile.am Wed Aug 05 20:28:50 2009 -0400
@@ -41,7 +41,8 @@ libindex_la_SOURCES = \
mail-transaction-log-file.c \
mail-transaction-log-view.c \
mailbox-list-index.c \
- mailbox-list-index-sync.c
+ mailbox-list-index-sync.c \
+ mailbox-log.c
headers = \
mail-cache.h \
@@ -58,7 +59,8 @@ headers = \
mail-transaction-log-private.h \
mail-transaction-log-view-private.h \
mailbox-list-index.h \
- mailbox-list-index-private.h
+ mailbox-list-index-private.h \
+ mailbox-log.h
test_programs = \
test-mail-index-transaction-finish \
diff -r 111812403bea -r d21f2f0b1e11 src/lib-index/mailbox-log.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-index/mailbox-log.c Wed Aug 05 20:28:50 2009 -0400
@@ -0,0 +1,279 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "eacces-error.h"
+#include "mailbox-log.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define MAILBOX_LOG_ROTATE_SIZE (1024*4)
+
+struct mailbox_log {
+ char *filepath, *filepath2;
+ int fd;
+
+ mode_t mode;
+ gid_t gid;
+ char *gid_origin;
+};
+
+struct mailbox_log_iter {
+ struct mailbox_log *log;
+
+ int fd;
+ const char *filepath;
+
+ struct mailbox_log_record buf[128];
+ unsigned int idx, count;
+ uoff_t offset;
+ bool failed;
+};
+
+static void mailbox_log_close(struct mailbox_log *log);
+
+struct mailbox_log *mailbox_log_alloc(const char *path)
+{
+ struct mailbox_log *log;
+
+ log = i_new(struct mailbox_log, 1);
+ log->filepath = i_strdup(path);
+ log->filepath2 = i_strconcat(path, ".2", NULL);
+ log->mode = 0644;
+ log->gid = (gid_t)-1;
+ log->fd = -1;
+ return log;
+}
+
+void mailbox_log_free(struct mailbox_log **_log)
+{
+ struct mailbox_log *log = *_log;
+
+ *_log = NULL;
+
+ mailbox_log_close(log);
+ i_free(log->gid_origin);
+ i_free(log->filepath);
+ i_free(log->filepath2);
+ i_free(log);
+}
+
+static void mailbox_log_close(struct mailbox_log *log)
+{
+ if (log->fd != -1) {
+ if (close(log->fd) < 0)
+ i_error("close(%s) failed: %m", log->filepath);
+ }
+}
+
+void mailbox_log_set_permissions(struct mailbox_log *log, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ log->mode = mode;
+ log->gid = gid;
+ i_free(log->gid_origin);
+ log->gid_origin = i_strdup(gid_origin);
+}
+
+static int mailbox_log_open(struct mailbox_log *log)
+{
+ mode_t old_mode;
+
+ log->fd = open(log->filepath, O_RDWR | O_APPEND);
+ if (log->fd != -1)
+ return 0;
+
+ /* try to create it */
+ old_mode = umask(0666 ^ log->mode);
+ log->fd = open(log->filepath, O_RDWR | O_APPEND | O_CREAT, 0666);
+ umask(old_mode);
+
+ if (log->fd == -1) {
+ if (errno != EACCES)
+ i_error("creat(%s) failed: %m", log->filepath);
+ else
+ i_error("%s", eacces_error_get("creat", log->filepath));
+ return -1;
+ }
+ if (fchown(log->fd, (uid_t)-1, log->gid) < 0) {
+ if (errno != EPERM)
+ i_error("fchown(%s) failed: %m", log->filepath);
+ else {
+ i_error("%s", eperm_error_get_chgrp("fchown",
+ log->filepath, log->gid,
+ log->gid_origin));
+ }
+ }
+ return 0;
+}
+
+static int mailbox_log_rotate_if_needed(struct mailbox_log *log)
+{
+ struct stat st;
+
+ if (fstat(log->fd, &st) < 0) {
+ i_error("fstat(%s) failed: %m", log->filepath);
+ return -1;
+ }
+
+ if (st.st_size < MAILBOX_LOG_ROTATE_SIZE)
+ return 0;
+
+ if (rename(log->filepath, log->filepath2) < 0) {
+ i_error("rename(%s, %s) failed: %m",
+ log->filepath, log->filepath2);
+ return -1;
+ }
+ return 0;
+}
+
+void mailbox_log_record_set_timestamp(struct mailbox_log_record *rec,
+ time_t stamp)
+{
+ rec->timestamp[0] = (stamp & 0xff000000) >> 24;
+ rec->timestamp[1] = (stamp & 0x00ff0000) >> 16;
+ rec->timestamp[2] = (stamp & 0x0000ff00) >> 8;
+ rec->timestamp[3] = (stamp & 0x000000ff);
+}
+
+time_t mailbox_log_record_get_timestamp(const struct mailbox_log_record *rec)
+{
+ return ((time_t)rec->timestamp[0] << 24) |
+ ((time_t)rec->timestamp[1] << 16) |
+ ((time_t)rec->timestamp[2] << 8) |
+ (time_t)rec->timestamp[3];
+}
+
+int mailbox_log_append(struct mailbox_log *log,
+ const struct mailbox_log_record *rec)
+{
+ struct stat st;
+ ssize_t ret;
+
+ /* we don't have to be too strict about appending to the latest log
+ file. the records' ordering doesn't matter and iteration goes
+ through both logs anyway. */
+ if (log->fd == -1) {
+ if (mailbox_log_open(log) < 0)
+ return -1;
+ }
+
+ /* We don't bother with locking, atomic appends will protect us.
+ If they don't (NFS), the worst that can happen is that a few
+ records get overwritten (because they're all the same size).
+ This whole log isn't supposed to be super-reliable anyway. */
+ ret = write(log->fd, rec, sizeof(*rec));
+ if (ret < 0) {
+ i_error("write(%s) failed: %m", log->filepath);
+ return -1;
+ } else if (ret != sizeof(*rec)) {
+ i_error("write(%s) wrote %d/%u bytes", log->filepath,
+ (int)ret, (unsigned int)sizeof(*rec));
+ if (fstat(log->fd, &st) == 0) {
+ if (ftruncate(log->fd, st.st_size - ret) < 0) {
+ i_error("ftruncate(%s) failed: %m",
+ log->filepath);
+ }
+ }
+ return -1;
+ }
+
+ (void)mailbox_log_rotate_if_needed(log);
+ return 0;
+}
+
+static bool mailbox_log_iter_open_next(struct mailbox_log_iter *iter)
+{
+ if (iter->fd != -1) {
+ if (close(iter->fd) < 0)
+ i_error("close(%s) failed: %m", iter->filepath);
+ iter->fd = -1;
+ }
+ if (iter->filepath == NULL)
+ iter->filepath = iter->log->filepath2;
+ else if (iter->filepath == iter->log->filepath2)
+ iter->filepath = iter->log->filepath;
+ else
+ return FALSE;
+
+ iter->fd = open(iter->filepath, O_RDONLY | O_APPEND);
+ if (iter->fd != -1)
+ return TRUE;
+ else if (errno == ENOENT) {
+ if (iter->filepath == iter->log->filepath2)
+ return mailbox_log_iter_open_next(iter);
+ } else {
+ i_error("open(%s) failed: %m", iter->filepath);
+ iter->failed = TRUE;
+ }
+ return FALSE;
+}
+
+struct mailbox_log_iter *mailbox_log_iter_init(struct mailbox_log *log)
+{
+ struct mailbox_log_iter *iter;
+
+ iter = i_new(struct mailbox_log_iter, 1);
+ iter->log = log;
+ iter->fd = -1;
+ (void)mailbox_log_iter_open_next(iter);
+ return iter;
+}
+
+const struct mailbox_log_record *
+mailbox_log_iter_next(struct mailbox_log_iter *iter)
+{
+ const struct mailbox_log_record *rec;
+ uoff_t offset;
+ ssize_t ret;
+
+ if (iter->idx == iter->count) {
+ if (iter->fd == -1)
+ return NULL;
+
+ ret = pread(iter->fd, iter->buf, sizeof(iter->buf),
+ iter->offset);
+ if (ret < 0) {
+ i_error("pread(%s) failed: %m", iter->filepath);
+ iter->failed = TRUE;
+ return NULL;
+ }
+ if (ret == 0) {
+ if (!mailbox_log_iter_open_next(iter))
+ return NULL;
+ iter->idx = iter->count = 0;
+ return mailbox_log_iter_next(iter);
+ }
+ iter->count = ret / sizeof(iter->buf[0]);
+ iter->offset += iter->count * sizeof(iter->buf[0]);
+ }
+ rec = &iter->buf[iter->idx++];
+ if (rec->type < MAILBOX_LOG_RECORD_DELETE_MAILBOX ||
+ rec->type > MAILBOX_LOG_RECORD_UNSUBSCRIBE) {
+ offset = iter->offset -
+ (iter->count - iter->idx) * sizeof(iter->buf[0]);
+ i_error("Corrupted mailbox log at offset %"PRIuUOFF_T": %s",
+ offset, iter->filepath);
+ if (unlink(iter->filepath) < 0)
+ i_error("unlink(%s) failed: %m", iter->filepath);
+ return NULL;
+ }
+ return rec;
+}
+
+int mailbox_log_iter_deinit(struct mailbox_log_iter **_iter)
+{
+ struct mailbox_log_iter *iter = *_iter;
+ int ret = iter->failed ? -1 : 0;
+
+ *_iter = NULL;
+
+ if (iter->fd != -1) {
More information about the dovecot-cvs
mailing list