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