mail-modifylog.c revision 7f0113f4efa1e4e020d64f714d1d765963005fd5
c636315472e4f87313af7be30b7fbcad4b8ca8a4Stephen Gallagher/* Copyright (C) 2002 Timo Sirainen */
c636315472e4f87313af7be30b7fbcad4b8ca8a4Stephen Gallagher
fd5a4eacd56700ffb08a73121aeacdc806cb0132Sumit Bose#include "lib.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "mmap-util.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "write-full.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "mail-index.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "mail-index-util.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "mail-modifylog.h"
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <stdlib.h>
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher#include <fcntl.h>
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Maximum size for modify log (isn't exact) */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define MAX_MODIFYLOG_SIZE 10240
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define MODIFYLOG_FILE_POSITION(log, ptr) \
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ((int) ((char *) (ptr) - (char *) (log)->mmap_base))
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct _MailModifyLog {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MailIndex *index;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int fd;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher char *filepath;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher void *mmap_base;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher size_t mmap_length;
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagher
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagher ModifyLogHeader *header;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher size_t synced_position;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int synced_id, mmaped_id;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
d921c1eba437662437847279f251a0a5d8f70127Maxim unsigned int modified:1;
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek unsigned int dirty_mmap:1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int second_log:1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher};
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int file_lock(int fd, int wait_lock, int lock_type)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct flock fl;
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fl.l_type = lock_type;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fl.l_whence = SEEK_SET;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher fl.l_start = 0;
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher fl.l_len = 0;
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (fcntl(fd, wait_lock ? F_SETLKW : F_SETLK, &fl) == -1) {
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher if (errno == EACCES || errno == EAGAIN)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Returns 1 = ok, 0 = failed to get the lock, -1 = error */
be1ef1c62ad13612be5e1f879476c24452a5d6d0Stephen Gallagherstatic int mail_modifylog_try_lock(MailModifyLog *log, int lock_type)
be1ef1c62ad13612be5e1f879476c24452a5d6d0Stephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher ret = file_lock(log->fd, FALSE, lock_type);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher if (ret == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "fcntl() failed with file %s: %m",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mail_modifylog_wait_lock(MailModifyLog *log)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (file_lock(log->fd, TRUE, F_RDLCK) < 1) {
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny index_set_error(log->index, "fcntl() failed with file %s: %m",
f1828234a850dd28465425248a83a993f262918fPavel Březina log->filepath);
f1828234a850dd28465425248a83a993f262918fPavel Březina return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* returns 1 = yes, 0 = no, -1 = error */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mail_modifylog_have_other_users(MailModifyLog *log)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina int ret;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina /* try grabbing exclusive lock */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek ret = mail_modifylog_try_lock(log, F_WRLCK);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ret == -1)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* revert back to shared lock */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher switch (mail_modifylog_try_lock(log, F_RDLCK)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case 0:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* shouldn't happen */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "fcntl(F_WRLCK -> F_RDLCK) "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "failed with file %s", log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* fall through */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case -1:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ret == 0 ? 1 : 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozekstatic int mmap_update(MailModifyLog *log)
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher{
fe60346714a73ac3987f786731389320633dd245Pavel Březina unsigned int extra;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!log->dirty_mmap && log->mmaped_id == log->header->sync_id)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->mmap_base != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)munmap(log->mmap_base, log->mmap_length);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher log->mmap_base = mmap_rw_file(log->fd, &log->mmap_length);
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher if (log->mmap_base == MAP_FAILED) {
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher log->mmap_base = NULL;
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek log->header = NULL;
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek index_set_error(log->index,
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher "modify log: mmap() failed with file %s: %m",
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->mmap_length < sizeof(ModifyLogHeader)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* FIXME: we could do better.. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)unlink(log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(0);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher extra = (log->mmap_length - sizeof(ModifyLogHeader)) %
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof(ModifyLogRecord);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (extra != 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* partial write or corrupted -
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher truncate the file to valid length */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->mmap_length -= extra;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)ftruncate(log->fd, (off_t)log->mmap_length);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->dirty_mmap = FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->header = log->mmap_base;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->mmaped_id = log->header->sync_id;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek}
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozekstatic MailModifyLog *mail_modifylog_new(MailIndex *index)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MailModifyLog *log;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek log = i_new(MailModifyLog, 1);
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek log->fd = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->index = index;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->dirty_mmap = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index->modifylog = log;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return log;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
3b1df539835367cb81cd5ff0f9959947d5642e55Stephen Gallagherstatic void mail_modifylog_close(MailModifyLog *log)
3b1df539835367cb81cd5ff0f9959947d5642e55Stephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->dirty_mmap = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->mmap_base != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher munmap(log->mmap_base, log->mmap_length);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->mmap_base = NULL;
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek }
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->fd != -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)close(log->fd);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->fd = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_free(log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
15b266d9f14dad26da8678a79019749d0f69532eStephen Gallagher
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozekstatic int mail_modifylog_init_fd(MailModifyLog *log, int fd,
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek const char *path)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher ModifyLogHeader hdr;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* write header */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher memset(&hdr, 0, sizeof(hdr));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.indexid = log->index->indexid;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "write() failed for modify "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "log %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ftruncate(fd, sizeof(hdr)) == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "ftruncate() failed for modify "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "log %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mail_modifylog_open_and_init_file(MailModifyLog *log,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *path)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int fd, ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fd = open(path, O_RDWR | O_CREAT, 0660);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (fd == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "Error opening modify log "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "file %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
bfbf5cb0f00c60c0f000f56c282377b13b9a89abSumit Bose
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher ret = file_lock(fd, FALSE, F_WRLCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "Error locking modify log "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "file %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 1 && mail_modifylog_init_fd(log, fd, path)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_modifylog_close(log);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->fd = fd;
0ef783e186ef1c9f60e61a4e8e54c44cb366fdfePavel Březina log->filepath = i_strdup(path);
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)close(fd);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherint mail_modifylog_create(MailIndex *index)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MailModifyLog *log;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *path;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log = mail_modifylog_new(index);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher path = t_strconcat(log->index->filepath, ".log", NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!mail_modifylog_open_and_init_file(log, path) ||
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !mail_modifylog_wait_lock(log) ||
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !mmap_update(log)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* fatal failure */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_modifylog_free(log);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_id = log->header->sync_id;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_position = log->mmap_length;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Returns 1 = ok, 0 = full, -1 = error */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mail_modifylog_open_and_verify(MailModifyLog *log, const char *path)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ModifyLogHeader hdr;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int fd, ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fd = open(path, O_RDWR);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (fd == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (errno != ENOENT) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "Can't open modify log "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "file %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
126c9338cf12a3e4404c36bbe4ec14b18f23537cMaxim return -1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = 1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "read() failed when for modify "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "log file %s: %m", path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek if (ret != -1 && hdr.indexid != log->index->indexid) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "IndexID mismatch for modify log "
126c9338cf12a3e4404c36bbe4ec14b18f23537cMaxim "file %s", path);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ret = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we have to rebuild it, make sure it's deleted. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)unlink(path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret != -1 && hdr.sync_id == SYNC_ID_FULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* full */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 1) {
bfbf5cb0f00c60c0f000f56c282377b13b9a89abSumit Bose log->fd = fd;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher log->filepath = i_strdup(path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher } else {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)close(fd);
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mail_modifylog_find_or_create(MailModifyLog *log)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *path1, *path2;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int i, ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek for (i = 0; i < 2; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* first try <index>.log */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher path1 = t_strconcat(log->index->filepath, ".log", NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher path2 = t_strconcat(log->index->filepath, ".log.2", NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
72e60fd4eabcfbcdbfe01e8c38b94052bc6c2067Jakub Hrozek ret = mail_modifylog_open_and_verify(log, path1);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 1)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* then <index>.log.2 */
5a70b84cb66fb8c7a3fce0e3f2e4b61e0b2ea9d4Simo Sorce if (mail_modifylog_open_and_verify(log, path2) == 1)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* try creating/reusing them */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mail_modifylog_open_and_init_file(log, path1))
d844aab866ae237844360cea70e2dccdc90c783dStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher if (mail_modifylog_open_and_init_file(log, path2))
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher return TRUE;
8c3a4809b3420657289b42f028a1c9019b112991Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* maybe the file was just switched, check the logs again */
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina }
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "We could neither use nor create "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "the modify log for index %s", log->index->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
1f1e6cbc59868f06dee3ab4b3df660fcb77ce1c8Jakub Hrozekint mail_modifylog_open_or_create(MailIndex *index)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
4c1bf6607060cea867fccf667063c028dfd51e96Stephen Gallagher MailModifyLog *log;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log = mail_modifylog_new(index);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!mail_modifylog_find_or_create(log) ||
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !mail_modifylog_wait_lock(log) ||
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !mmap_update(log)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* fatal failure */
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher mail_modifylog_free(log);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_id = log->header->sync_id;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_position = log->mmap_length;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid mail_modifylog_free(MailModifyLog *log)
4af1d1869d659fec84c518c26844132fa1df8f64Jakub Hrozek{
eb54e05c9658a7274e3238813c54dd0c6577d3ecPavel Březina log->index->modifylog = NULL;
e9eeb4302e0e426c6cc1a4e65b95a6f7066e80b9Pavel Březina
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek mail_modifylog_close(log);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_free(log);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherint mail_modifylog_sync_file(MailModifyLog *log)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
3a62a99faf8e12965100d0b26fc9e07752bd3e2dStephen Gallagher if (!log->modified)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->mmap_base != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (msync(log->mmap_base, log->mmap_length, MS_SYNC) == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index, "msync() failed for %s: %m",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek if (fsync(log->fd) == -1) {
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek index_set_error(log->index, "fsync() failed for %s: %m",
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek log->filepath);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return FALSE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher log->modified = FALSE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return TRUE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher}
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagherstatic int mail_modifylog_append(MailModifyLog *log, ModifyLogRecord *rec,
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher int external_change)
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher{
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher i_assert(log->index->lock_type == MAIL_LOCK_EXCLUSIVE);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_assert(rec->seq != 0);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_assert(rec->uid != 0);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (!external_change) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher switch (mail_modifylog_have_other_users(log)) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher case 0:
c2352a73f52f600d95966ebe0b0819649ba923faStephen Gallagher /* we're the only one having this log open,
4c1bf6607060cea867fccf667063c028dfd51e96Stephen Gallagher no need for modify log. */
1f1e6cbc59868f06dee3ab4b3df660fcb77ce1c8Jakub Hrozek return TRUE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher case -1:
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return FALSE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (lseek(log->fd, 0, SEEK_END) == -1) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher index_set_error(log->index, "lseek() failed with file %s: %m",
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher log->filepath);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return FALSE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher index_set_error(log->index, "Error appending to file %s: %m",
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher log->filepath);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return FALSE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher
dbea04f585a30d001b574317c068cd03a4fa332bJakub Hrozek log->header->sync_id++;
5a70b84cb66fb8c7a3fce0e3f2e4b61e0b2ea9d4Simo Sorce log->modified = TRUE;
5a70b84cb66fb8c7a3fce0e3f2e4b61e0b2ea9d4Simo Sorce log->dirty_mmap = TRUE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (!external_change) {
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher log->synced_id = log->header->sync_id;
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher log->synced_position += sizeof(ModifyLogRecord);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return TRUE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekint mail_modifylog_add_expunge(MailModifyLog *log, unsigned int seq,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int uid, int external_change)
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher{
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher ModifyLogRecord rec;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher /* expunges must not be added when log isn't synced */
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher i_assert(external_change || log->synced_id == log->header->sync_id);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
a72e9289fe001c85a17acd667ca31d692fd99605Stephen Gallagher rec.type = RECORD_TYPE_EXPUNGE;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher rec.seq = seq;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher rec.uid = uid;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher return mail_modifylog_append(log, &rec, external_change);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher}
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherint mail_modifylog_add_flags(MailModifyLog *log, unsigned int seq,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int uid, int external_change)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ModifyLogRecord rec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec.type = RECORD_TYPE_FLAGS_CHANGED;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher rec.seq = seq;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec.uid = uid;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return mail_modifylog_append(log, &rec, external_change);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher}
e369fc08906383e6d5c39832f31bb6600a33f887Simo Sorce
fe2091327ff44f80d6681c261494e4432404e9baStephen GallagherModifyLogRecord *mail_modifylog_get_nonsynced(MailModifyLog *log,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int *count)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ModifyLogRecord *rec, *end_rec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
8c3a4809b3420657289b42f028a1c9019b112991Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *count = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!mmap_update(log))
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return NULL;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher i_assert(log->synced_position <= log->mmap_length);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(log->synced_position >= sizeof(ModifyLogHeader));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec = (ModifyLogRecord *) ((char *) log->mmap_base +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_position);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher end_rec = (ModifyLogRecord *) ((char *) log->mmap_base +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->mmap_length);
d844aab866ae237844360cea70e2dccdc90c783dStephen Gallagher *count = (unsigned int) (end_rec - rec);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return rec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagherstatic int mail_modifylog_switch_file(MailModifyLog *log)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MailIndex *index = log->index;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina mail_modifylog_free(log);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina return mail_modifylog_open_or_create(index);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina}
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březinastatic void mail_modifylog_try_switch_file(MailModifyLog *log)
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina{
41ef946f3f74a46b9e26118116e4811e259b30efPavel Březina const char *path;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina path = t_strconcat(log->index->filepath,
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina log->second_log ? ".log" : ".log.2", NULL);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina if (mail_modifylog_open_and_init_file(log, path)) {
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina /* FIXME: we want to update the _old_ file's header.
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek and this changes the new one. and it's already closed the
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek old one and mmap() is invalid, and we crash here.. */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek log->header->sync_id = SYNC_ID_FULL;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek }
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek}
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekint mail_modifylog_mark_synced(MailModifyLog *log)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek{
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (!mmap_update(log))
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (log->header->sync_id == SYNC_ID_FULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* log file is full, switch to next one */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return mail_modifylog_switch_file(log);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (log->synced_id == log->header->sync_id) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* we are already synced */
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
4b0309363dbfb9a1409e082b3a84f17b53a751c1Stephen Gallagher log->synced_id = log->header->sync_id;
4b0309363dbfb9a1409e082b3a84f17b53a751c1Stephen Gallagher log->synced_position = log->mmap_length;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek log->modified = TRUE;
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek
52e0894fd65bff4715c88330eb62b28e1635228fStephen Gallagher if (log->mmap_length > MAX_MODIFYLOG_SIZE) {
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek /* if the other file isn't locked, switch to it */
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek mail_modifylog_try_switch_file(log);
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek return TRUE;
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek }
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
8a07521b413a3b5879f824e1872c5770c92ee5c0Stephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int compare_uint(const void *p1, const void *p2)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const unsigned int *u1 = p1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const unsigned int *u2 = p2;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return *u1 < *u2 ? -1 : *u1 > *u2 ? 1 : 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherconst unsigned int *
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghermail_modifylog_seq_get_expunges(MailModifyLog *log,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int first_seq,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int last_seq,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int *expunges_before)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ModifyLogRecord *rec, *end_rec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int last_pos_seq, before, max_records, *arr, *expunges;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *expunges_before = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!mmap_update(log))
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* find the first expunged message that affects our range */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec = (ModifyLogRecord *) ((char *) log->mmap_base +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_position);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher end_rec = (ModifyLogRecord *) ((char *) log->mmap_base +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->mmap_length);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher while (rec < end_rec) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (rec->type == RECORD_TYPE_EXPUNGE && rec->seq <= last_seq)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec++;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (rec >= end_rec) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* none found */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher expunges = t_malloc(sizeof(unsigned int));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *expunges = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return expunges;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* allocate memory for the returned array. the file size - synced
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher position should be quite near the amount of memory we need, unless
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher there's lots of FLAGS_CHANGED records which is why there's the
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher second check to make sure it's not unneededly large. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) /
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof(ModifyLogRecord);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (max_records > last_seq - first_seq + 1)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher max_records = last_seq - first_seq + 1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* last_pos_seq is updated all the time to contain the last_seq
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher comparable to current record's seq. number */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher last_pos_seq = last_seq;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher before = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (; rec < end_rec; rec++) {
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny if (rec->type != RECORD_TYPE_EXPUNGE)
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny continue;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny if (rec->seq + before < first_seq) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* before our range */
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny before++;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny last_pos_seq--;
f1828234a850dd28465425248a83a993f262918fPavel Březina } else if (rec->seq <= last_pos_seq) {
f1828234a850dd28465425248a83a993f262918fPavel Březina /* within our range */
f1828234a850dd28465425248a83a993f262918fPavel Březina last_pos_seq--;
f1828234a850dd28465425248a83a993f262918fPavel Březina
f1828234a850dd28465425248a83a993f262918fPavel Březina if (max_records-- == 0) {
f1828234a850dd28465425248a83a993f262918fPavel Březina /* log contains more data than it should
f1828234a850dd28465425248a83a993f262918fPavel Březina have - must be corrupted. */
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina index_set_error(log->index,
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina "Modify log %s is corrupted",
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina log->filepath);
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek return NULL;
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina }
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek *arr++ = rec->uid;
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina }
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *arr = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* sort the UID array, not including the terminating 0 */
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int),
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose compare_uint);
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose *expunges_before = before;
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose return expunges;
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherconst unsigned int *
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghermail_modifylog_uid_get_expunges(MailModifyLog *log,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int first_uid,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int last_uid)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* pretty much copy&pasted from sequence code above ..
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher kind of annoying */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ModifyLogRecord *rec, *end_rec;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int before, max_records, *arr, *expunges;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (!mmap_update(log))
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* find the first expunged message that affects our range */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec = (ModifyLogRecord *) ((char *) log->mmap_base +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->synced_position);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher end_rec = (ModifyLogRecord *) ((char *) log->mmap_base +
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher log->mmap_length);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher while (rec < end_rec) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (rec->type == RECORD_TYPE_EXPUNGE && rec->uid <= last_uid)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec++;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (rec >= end_rec) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* none found */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher expunges = t_malloc(sizeof(unsigned int));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *expunges = 0;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return expunges;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* allocate memory for the returned array. the file size - synced
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher position should be quite near the amount of memory we need, unless
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher there's lots of FLAGS_CHANGED records which is why there's the
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher second check to make sure it's not unneededly large. */
5843ad321944a028f6dee7e1fd4f9381c4953d07Sumit Bose max_records = (log->mmap_length - MODIFYLOG_FILE_POSITION(log, rec)) /
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof(ModifyLogRecord);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (max_records > last_uid - first_uid + 1)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher max_records = last_uid - first_uid + 1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher expunges = arr = t_malloc((max_records+1) * sizeof(unsigned int));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher before = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher while (rec < end_rec) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (rec->type == RECORD_TYPE_EXPUNGE &&
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec->uid >= first_uid && rec->uid <= last_uid) {
c8b8901b05da9e31dba320f305ec20301e928cfbSumit Bose /* within our range */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (max_records-- == 0) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* log contains more data than it should
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher have - must be corrupted. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_set_error(log->index,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Modify log %s is corrupted",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher log->filepath);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *arr++ = rec->uid;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rec++;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher *arr = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* sort the UID array, not including the terminating 0 */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher qsort(expunges, (unsigned int) (arr - expunges), sizeof(unsigned int),
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher compare_uint);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return expunges;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher