Mercurial > dovecot > core-2.2
changeset 9732:d21f2f0b1e11 HEAD
lib-index: Added mailbox transaction log.
It's a much simplified version of mail transaction log.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 05 Aug 2009 20:28:50 -0400 |
parents | 111812403bea |
children | fd534ee2c08e |
files | src/lib-index/Makefile.am src/lib-index/mailbox-log.c src/lib-index/mailbox-log.h |
diffstat | 3 files changed, 325 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- 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 @@ 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 @@ 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 \
--- /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) { + if (close(iter->fd) < 0) + i_error("close(%s) failed: %m", iter->filepath); + } + i_free(iter); + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/mailbox-log.h Wed Aug 05 20:28:50 2009 -0400 @@ -0,0 +1,42 @@ +#ifndef MAILBOX_LOG_H +#define MAILBOX_LOG_H + +#include "mail-types.h" + +enum mailbox_log_record_type { + MAILBOX_LOG_RECORD_DELETE_MAILBOX = 1, + MAILBOX_LOG_RECORD_DELETE_DIR, + MAILBOX_LOG_RECORD_RENAME, + MAILBOX_LOG_RECORD_SUBSCRIBE, + MAILBOX_LOG_RECORD_UNSUBSCRIBE +}; + +struct mailbox_log_record { + uint8_t type; + uint8_t padding[3]; + uint8_t mailbox_guid[MAIL_GUID_128_SIZE]; + uint8_t timestamp[4]; +}; + +struct mailbox_log *mailbox_log_alloc(const char *path); +void mailbox_log_free(struct mailbox_log **log); + +void mailbox_log_set_permissions(struct mailbox_log *log, mode_t mode, + gid_t gid, const char *gid_origin); + +void mailbox_log_record_set_timestamp(struct mailbox_log_record *rec, + time_t stamp); +time_t mailbox_log_record_get_timestamp(const struct mailbox_log_record *rec); + +/* Append a new record to mailbox log. Returns 0 if ok, -1 if error. */ +int mailbox_log_append(struct mailbox_log *log, + const struct mailbox_log_record *rec); + +/* Iterate through all records in mailbox log. */ +struct mailbox_log_iter *mailbox_log_iter_init(struct mailbox_log *log); +const struct mailbox_log_record * +mailbox_log_iter_next(struct mailbox_log_iter *iter); +/* Returns 0 if ok, -1 if I/O error. */ +int mailbox_log_iter_deinit(struct mailbox_log_iter **iter); + +#endif