"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/lib-storage/mail-duplicate.c" between
dovecot-2.3.16.tar.gz and dovecot-2.3.17.tar.gz

About: Dovecot is an IMAP and POP3 server, written with security primarily in mind.

mail-duplicate.c  (dovecot-2.3.16):mail-duplicate.c  (dovecot-2.3.17)
/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
#include "lib.h" #include "lib.h"
#include "ioloop.h" #include "ioloop.h"
#include "hex-binary.h"
#include "mkdir-parents.h"
#include "istream.h" #include "istream.h"
#include "ostream.h" #include "ostream.h"
#include "time-util.h"
#include "home-expand.h" #include "home-expand.h"
#include "file-create-locked.h"
#include "file-dotlock.h" #include "file-dotlock.h"
#include "md5.h"
#include "hash.h" #include "hash.h"
#include "mail-user.h" #include "mail-user.h"
#include "mail-storage-settings.h" #include "mail-storage-settings.h"
#include "mail-duplicate.h" #include "mail-duplicate.h"
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#define COMPRESS_PERCENTAGE 10 #define COMPRESS_PERCENTAGE 10
#define DUPLICATE_BUFSIZE 4096 #define DUPLICATE_BUFSIZE 4096
#define DUPLICATE_VERSION 2 #define DUPLICATE_VERSION 2
#define DUPLICATE_LOCK_FNAME_PREFIX "duplicate.lock."
#define DUPLICATE_LOCK_TIMEOUT_SECS 65
#define DUPLICATE_LOCK_WARN_SECS 4
#define DUPLICATE_LOCK_MAX_LOCKS 100
enum mail_duplicate_lock_result {
MAIL_DUPLICATE_LOCK_OK,
MAIL_DUPLICATE_LOCK_IO_ERROR,
MAIL_DUPLICATE_LOCK_TIMEOUT,
MAIL_DUPLICATE_LOCK_TOO_MANY,
MAIL_DUPLICATE_LOCK_DEADLOCK,
};
struct mail_duplicate_lock {
int fd;
char *path;
struct file_lock *lock;
struct timeval start_time;
};
struct mail_duplicate { struct mail_duplicate {
const void *id; const void *id;
unsigned int id_size; unsigned int id_size;
const char *user; const char *user;
time_t time; time_t time;
struct mail_duplicate_lock lock;
bool marked:1;
bool changed:1;
}; };
struct mail_duplicate_file_header { struct mail_duplicate_file_header {
uint32_t version; uint32_t version;
}; };
struct mail_duplicate_record_header { struct mail_duplicate_record_header {
uint32_t stamp; uint32_t stamp;
uint32_t id_size; uint32_t id_size;
uint32_t user_size; uint32_t user_size;
}; };
struct mail_duplicate_file { struct mail_duplicate_transaction {
pool_t pool; pool_t pool;
struct mail_duplicate_db *db; struct mail_duplicate_db *db;
ino_t db_ino;
struct event *event;
HASH_TABLE(struct mail_duplicate *, struct mail_duplicate *) hash; HASH_TABLE(struct mail_duplicate *, struct mail_duplicate *) hash;
const char *path; const char *path;
unsigned int id_lock_count;
int new_fd;
struct dotlock *dotlock;
bool changed:1; bool changed:1;
}; };
struct mail_duplicate_db { struct mail_duplicate_db {
struct mail_user *user; struct mail_user *user;
struct event *event;
char *path; char *path;
char *lock_dir;
struct dotlock_settings dotlock_set; struct dotlock_settings dotlock_set;
struct mail_duplicate_file *file;
unsigned int transaction_count;
}; };
static const struct dotlock_settings default_mail_duplicate_dotlock_set = { static const struct dotlock_settings default_mail_duplicate_dotlock_set = {
.timeout = 20, .timeout = 20,
.stale_timeout = 10, .stale_timeout = 10,
}; };
static int static int
mail_duplicate_cmp(const struct mail_duplicate *d1, mail_duplicate_cmp(const struct mail_duplicate *d1,
const struct mail_duplicate *d2) const struct mail_duplicate *d2)
skipping to change at line 90 skipping to change at line 124
if ((g = h & 0xf0000000UL) != 0) { if ((g = h & 0xf0000000UL) != 0) {
h = h ^ (g >> 24); h = h ^ (g >> 24);
h = h ^ g; h = h ^ g;
} }
s++; s++;
} }
return h ^ strcase_hash(d->user); return h ^ strcase_hash(d->user);
} }
static enum mail_duplicate_lock_result
duplicate_lock_failed(struct mail_duplicate_transaction *trans,
struct mail_duplicate *dup, const char *error)
{
struct mail_duplicate_lock *lock = &dup->lock;
enum mail_duplicate_lock_result result;
int diff;
i_assert(lock->fd == -1);
i_assert(lock->lock == NULL);
if (errno == EDEADLK) {
/* deadlock */
result = MAIL_DUPLICATE_LOCK_DEADLOCK;
} else if (errno != EAGAIN) {
/* not a lock timeout */
result = MAIL_DUPLICATE_LOCK_IO_ERROR;
} else {
diff = timeval_diff_msecs(&ioloop_timeval,
&lock->start_time);
error = t_strdup_printf("Lock timeout in %d.%03d secs",
diff/1000, diff%1000);
result = MAIL_DUPLICATE_LOCK_TIMEOUT;
}
e_error(trans->event, "Failed to lock %s: %s", lock->path, error);
i_free_and_null(lock->path);
i_zero(lock);
return result;
}
static bool mail_duplicate_is_locked(struct mail_duplicate *dup)
{
struct mail_duplicate_lock *lock = &dup->lock;
return (lock->lock != NULL);
}
static enum mail_duplicate_lock_result
mail_duplicate_lock(struct mail_duplicate_transaction *trans,
struct mail_duplicate *dup)
{
struct file_create_settings lock_set = {
.lock_timeout_secs = DUPLICATE_LOCK_TIMEOUT_SECS,
.lock_settings = {
.lock_method = FILE_LOCK_METHOD_FCNTL,
.allow_deadlock = TRUE,
},
};
struct mail_duplicate_db *db = trans->db;
struct mail_duplicate_lock *lock = &dup->lock;
const char *error;
unsigned char id_md5[MD5_RESULTLEN];
bool created;
int diff;
if (mail_duplicate_is_locked(dup)) {
e_debug(trans->event, "Duplicate ID already locked");
return MAIL_DUPLICATE_LOCK_OK;
}
if (trans->id_lock_count >= DUPLICATE_LOCK_MAX_LOCKS) {
e_debug(trans->event, "Too many duplicate IDs locked");
return MAIL_DUPLICATE_LOCK_TOO_MANY;
}
i_assert(db->lock_dir != NULL);
lock->start_time = ioloop_timeval;
md5_get_digest(dup->id, dup->id_size, id_md5);
lock->path = i_strdup_printf("%s/"DUPLICATE_LOCK_FNAME_PREFIX"%s",
db->lock_dir,
binary_to_hex(id_md5, sizeof(id_md5)));
e_debug(trans->event, "Lock duplicate ID (path=%s)", lock->path);
lock->fd = file_create_locked(lock->path, &lock_set, &lock->lock,
&created, &error);
if (lock->fd == -1 && errno == ENOENT) {
/* parent directory missing - create it */
if (mkdir_parents(db->lock_dir, 0700) < 0 && errno != EEXIST) {
error = t_strdup_printf(
"mkdir_parents(%s) failed: %m", db->lock_dir);
} else {
lock->fd = file_create_locked(lock->path,
&lock_set, &lock->lock,
&created, &error);
}
}
if (lock->fd == -1)
return duplicate_lock_failed(trans, dup, error);
diff = timeval_diff_msecs(&ioloop_timeval, &lock->start_time);
if (diff >= (DUPLICATE_LOCK_WARN_SECS * 1000)) {
e_warning(trans->event, "Locking %s took %d.%03d secs",
lock->path, diff/1000, diff%1000);
}
i_assert(mail_duplicate_is_locked(dup));
trans->id_lock_count++;
return MAIL_DUPLICATE_LOCK_OK;
}
static void
mail_duplicate_unlock(struct mail_duplicate_transaction *trans,
struct mail_duplicate *dup)
{
int orig_errno = errno;
if (dup->lock.path != NULL) {
struct mail_duplicate_lock *lock = &dup->lock;
e_debug(trans->event, "Unlock duplicate ID (path=%s)",
lock->path);
i_unlink(lock->path);
file_lock_free(&lock->lock);
i_close_fd(&lock->fd);
i_free_and_null(lock->path);
i_zero(lock);
i_assert(trans->id_lock_count > 0);
trans->id_lock_count--;
}
errno = orig_errno;
}
static int static int
mail_duplicate_read_records(struct mail_duplicate_file *file, mail_duplicate_read_records(struct mail_duplicate_transaction *trans,
struct istream *input, struct istream *input,
unsigned int record_size) unsigned int record_size)
{ {
const unsigned char *data; const unsigned char *data;
struct mail_duplicate_record_header hdr; struct mail_duplicate_record_header hdr;
size_t size; size_t size;
unsigned int change_count; unsigned int change_count;
change_count = 0; change_count = 0;
while (i_stream_read_bytes(input, &data, &size, record_size) > 0) { while (i_stream_read_bytes(input, &data, &size, record_size) > 0) {
skipping to change at line 123 skipping to change at line 283
sizeof(hdr.id_size)); sizeof(hdr.id_size));
memcpy(&hdr.user_size, memcpy(&hdr.user_size,
data + sizeof(time_t) + sizeof(uint32_t), data + sizeof(time_t) + sizeof(uint32_t),
sizeof(hdr.user_size)); sizeof(hdr.user_size));
} }
i_stream_skip(input, record_size); i_stream_skip(input, record_size);
if (hdr.id_size == 0 || hdr.user_size == 0 || if (hdr.id_size == 0 || hdr.user_size == 0 ||
hdr.id_size > DUPLICATE_BUFSIZE || hdr.id_size > DUPLICATE_BUFSIZE ||
hdr.user_size > DUPLICATE_BUFSIZE) { hdr.user_size > DUPLICATE_BUFSIZE) {
e_error(file->db->user->event, e_error(trans->event,
"broken mail_duplicate file %s", file->path); "broken mail_duplicate file %s", trans->path);
return -1; return -1;
} }
if (i_stream_read_bytes(input, &data, &size, if (i_stream_read_bytes(input, &data, &size,
hdr.id_size + hdr.user_size) <= 0) { hdr.id_size + hdr.user_size) <= 0) {
e_error(file->db->user->event, e_error(trans->event,
"unexpected end of file in %s", file->path); "unexpected end of file in %s", trans->path);
return -1; return -1;
} }
if ((time_t)hdr.stamp >= ioloop_time) { struct mail_duplicate dup_q, *dup;
/* still valid, save it */
struct mail_duplicate *d; dup_q.id = data;
void *new_id; dup_q.id_size = hdr.id_size;
dup_q.user = t_strndup(data + hdr.id_size, hdr.user_size);
new_id = p_malloc(file->pool, hdr.id_size);
memcpy(new_id, data, hdr.id_size); dup = hash_table_lookup(trans->hash, &dup_q);
if ((time_t)hdr.stamp < ioloop_time) {
d = p_new(file->pool, struct mail_duplicate, 1);
d->id = new_id;
d->id_size = hdr.id_size;
d->user = p_strndup(file->pool,
data + hdr.id_size, hdr.user_size);
d->time = hdr.stamp;
hash_table_update(file->hash, d, d);
} else {
change_count++; change_count++;
if (dup != NULL && !dup->changed)
dup->marked = FALSE;
} else {
if (dup == NULL) {
void *new_id;
new_id = p_malloc(trans->pool, hdr.id_size);
memcpy(new_id, data, hdr.id_size);
dup = p_new(trans->pool,
struct mail_duplicate, 1);
dup->id = new_id;
dup->id_size = hdr.id_size;
dup->user = p_strdup(trans->pool, dup_q.user);
hash_table_update(trans->hash, dup, dup);
}
if (!dup->changed) {
dup->marked = TRUE;
dup->time = hdr.stamp;
}
} }
i_stream_skip(input, hdr.id_size + hdr.user_size); i_stream_skip(input, hdr.id_size + hdr.user_size);
} }
if (hash_table_count(file->hash) * if (hash_table_count(trans->hash) *
COMPRESS_PERCENTAGE / 100 > change_count) COMPRESS_PERCENTAGE / 100 > change_count)
file->changed = TRUE; trans->changed = TRUE;
return 0; return 0;
} }
static int mail_duplicate_read(struct mail_duplicate_file *file) static int
mail_duplicate_read_db_from_fd(struct mail_duplicate_transaction *trans, int fd)
{ {
struct istream *input; struct istream *input;
struct mail_duplicate_file_header hdr; struct mail_duplicate_file_header hdr;
const unsigned char *data; const unsigned char *data;
size_t size; size_t size;
int fd; struct stat st;
unsigned int record_size = 0; unsigned int record_size = 0;
fd = open(file->path, O_RDONLY); if (fstat(fd, &st) < 0) {
if (fd == -1) { e_error(trans->event,
if (errno == ENOENT) "stat(%s) failed: %m", trans->path);
return 0;
e_error(file->db->user->event,
"open(%s) failed: %m", file->path);
return -1; return -1;
} }
trans->db_ino = st.st_ino;
/* <timestamp> <id_size> <user_size> <id> <user> */ /* <timestamp> <id_size> <user_size> <id> <user> */
input = i_stream_create_fd(fd, DUPLICATE_BUFSIZE); input = i_stream_create_fd(fd, DUPLICATE_BUFSIZE);
if (i_stream_read_bytes(input, &data, &size, sizeof(hdr)) > 0) { if (i_stream_read_bytes(input, &data, &size, sizeof(hdr)) > 0) {
memcpy(&hdr, data, sizeof(hdr)); memcpy(&hdr, data, sizeof(hdr));
if (hdr.version == 0 || hdr.version > DUPLICATE_VERSION + 10) { if (hdr.version == 0 || hdr.version > DUPLICATE_VERSION + 10) {
/* FIXME: backwards compatibility with v1.0 */ /* FIXME: backwards compatibility with v1.0 */
record_size = sizeof(time_t) + sizeof(uint32_t)*2; record_size = sizeof(time_t) + sizeof(uint32_t)*2;
} else if (hdr.version == DUPLICATE_VERSION) { } else if (hdr.version == DUPLICATE_VERSION) {
record_size = sizeof(struct mail_duplicate_record_header) ; record_size = sizeof(struct mail_duplicate_record_header) ;
i_stream_skip(input, sizeof(hdr)); i_stream_skip(input, sizeof(hdr));
} }
} }
if (record_size == 0 || if (record_size == 0)
mail_duplicate_read_records(file, input, record_size) < 0) i_unlink_if_exists(trans->path);
i_unlink_if_exists(file->path); else T_BEGIN {
if (mail_duplicate_read_records(trans, input, record_size) < 0)
i_unlink_if_exists(trans->path);
} T_END;
i_stream_unref(&input); i_stream_unref(&input);
if (close(fd) < 0)
e_error(file->db->user->event,
"close(%s) failed: %m", file->path);
return 0; return 0;
} }
static struct mail_duplicate_file * static int mail_duplicate_read_db_file(struct mail_duplicate_transaction *trans)
mail_duplicate_file_new(struct mail_duplicate_db *db)
{ {
struct mail_duplicate_file *file; int fd, ret;
pool_t pool;
i_assert(db->path != NULL); e_debug(trans->event, "Reading %s", trans->path);
pool = pool_alloconly_create("mail_duplicates", 10240); fd = open(trans->path, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT)
return 0;
e_error(trans->event,
"open(%s) failed: %m", trans->path);
return -1;
}
ret = mail_duplicate_read_db_from_fd(trans, fd);
file = p_new(pool, struct mail_duplicate_file, 1); if (close(fd) < 0) {
file->pool = pool; e_error(trans->event,
file->db = db; "close(%s) failed: %m", trans->path);
file->path = p_strdup(pool, db->path); }
file->new_fd = file_dotlock_open(&db->dotlock_set, file->path, 0, return ret;
&file->dotlock); }
if (file->new_fd != -1)
static void mail_duplicate_read(struct mail_duplicate_transaction *trans)
{
struct mail_duplicate_db *db = trans->db;
int new_fd;
struct dotlock *dotlock;
new_fd = file_dotlock_open(&db->dotlock_set, trans->path, 0, &dotlock);
if (new_fd != -1)
; ;
else if (errno != EAGAIN) { else if (errno != EAGAIN) {
e_error(db->user->event, e_error(trans->event,
"file_dotlock_open(%s) failed: %m", file->path); "file_dotlock_open(%s) failed: %m", trans->path);
} else { } else {
e_error(db->user->event, e_error(trans->event,
"Creating lock file for %s timed out in %u secs", "Creating lock file for %s timed out in %u secs",
file->path, db->dotlock_set.timeout); trans->path, db->dotlock_set.timeout);
} }
hash_table_create(&file->hash, pool, 0, mail_duplicate_hash, mail_duplica te_cmp);
(void)mail_duplicate_read(file); (void)mail_duplicate_read_db_file(trans);
return file;
if (dotlock != NULL)
file_dotlock_delete(&dotlock);
} }
static void mail_duplicate_file_free(struct mail_duplicate_file **_file) static void mail_duplicate_update(struct mail_duplicate_transaction *trans)
{ {
struct mail_duplicate_file *file = *_file; struct stat st;
*_file = NULL; if (stat(trans->path, &st) < 0) {
if (file->dotlock != NULL) if (errno == ENOENT) {
file_dotlock_delete(&file->dotlock); e_debug(trans->event, "DB file not created yet");
} else {
e_error(trans->event,
"stat(%s) failed: %m", trans->path);
}
} else if (trans->db_ino == st.st_ino) {
e_debug(trans->event, "DB file not changed");
} else {
e_debug(trans->event, "DB file changed: "
"Updating duplicate records from DB file");
hash_table_destroy(&file->hash); mail_duplicate_read(trans);
pool_unref(&file->pool); }
} }
bool mail_duplicate_check(struct mail_duplicate_db *db, struct mail_duplicate_transaction *
const void *id, size_t id_size, const char *user) mail_duplicate_transaction_begin(struct mail_duplicate_db *db)
{ {
struct mail_duplicate d; struct mail_duplicate_transaction *trans;
pool_t pool;
if (db->file == NULL) { db->transaction_count++;
if (db->path == NULL) {
/* mail_duplicate database disabled */ pool = pool_alloconly_create("mail_duplicates", 10240);
return FALSE;
} trans = p_new(pool, struct mail_duplicate_transaction, 1);
db->file = mail_duplicate_file_new(db); trans->pool = pool;
trans->db = db;
trans->event = event_create(db->event);
event_set_append_log_prefix(trans->event, "transaction: ");
if (db->path == NULL) {
/* Duplicate database disabled; return dummy transaction */
e_debug(trans->event, "Transaction begin (dummy)");
return trans;
}
e_debug(trans->event, "Transaction begin; lock %s", db->path);
trans->path = p_strdup(pool, db->path);
hash_table_create(&trans->hash, pool, 0,
mail_duplicate_hash, mail_duplicate_cmp);
mail_duplicate_read(trans);
return trans;
}
static void
mail_duplicate_transaction_free(struct mail_duplicate_transaction **_trans)
{
struct mail_duplicate_transaction *trans = *_trans;
struct hash_iterate_context *iter;
struct mail_duplicate *d;
if (trans == NULL)
return;
*_trans = NULL;
e_debug(trans->event, "Transaction free");
i_assert(trans->db->transaction_count > 0);
trans->db->transaction_count--;
iter = hash_table_iterate_init(trans->hash);
while (hash_table_iterate(iter, trans->hash, &d, &d))
mail_duplicate_unlock(trans, d);
hash_table_iterate_deinit(&iter);
i_assert(trans->id_lock_count == 0);
hash_table_destroy(&trans->hash);
event_unref(&trans->event);
pool_unref(&trans->pool);
}
static struct mail_duplicate *
mail_duplicate_get(struct mail_duplicate_transaction *trans,
const void *id, size_t id_size, const char *user)
{
struct mail_duplicate dup_q, *dup;
dup_q.id = id;
dup_q.id_size = id_size;
dup_q.user = user;
dup = hash_table_lookup(trans->hash, &dup_q);
if (dup == NULL) {
dup = p_new(trans->pool, struct mail_duplicate, 1);
dup->id = p_memdup(trans->pool, id, id_size);
dup->id_size = id_size;
dup->user = p_strdup(trans->pool, user);
dup->time = (time_t)-1;
hash_table_insert(trans->hash, dup, dup);
} }
d.id = id; return dup;
d.id_size = id_size; }
d.user = user;
enum mail_duplicate_check_result
mail_duplicate_check(struct mail_duplicate_transaction *trans,
const void *id, size_t id_size, const char *user)
{
struct mail_duplicate *dup;
if (trans->path == NULL) {
/* Duplicate database disabled */
e_debug(trans->event, "Check ID (dummy)");
return MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND;
}
dup = mail_duplicate_get(trans, id, id_size, user);
switch (mail_duplicate_lock(trans, dup)) {
case MAIL_DUPLICATE_LOCK_OK:
break;
case MAIL_DUPLICATE_LOCK_IO_ERROR:
e_debug(trans->event,
"Check ID: I/O error occurred while locking");
return MAIL_DUPLICATE_CHECK_RESULT_IO_ERROR;
case MAIL_DUPLICATE_LOCK_TIMEOUT:
e_debug(trans->event,
"Check ID: lock timed out");
return MAIL_DUPLICATE_CHECK_RESULT_LOCK_TIMEOUT;
case MAIL_DUPLICATE_LOCK_TOO_MANY:
e_debug(trans->event,
"Check ID: too many IDs locked");
return MAIL_DUPLICATE_CHECK_RESULT_TOO_MANY_LOCKS;
case MAIL_DUPLICATE_LOCK_DEADLOCK:
e_debug(trans->event,
"Check ID: deadlock detected while locking");
return MAIL_DUPLICATE_CHECK_RESULT_DEADLOCK;
}
mail_duplicate_update(trans);
if (dup->marked) {
e_debug(trans->event, "Check ID: found");
return MAIL_DUPLICATE_CHECK_RESULT_EXISTS;
}
return hash_table_lookup(db->file->hash, &d) != NULL; e_debug(trans->event, "Check ID: not found");
return MAIL_DUPLICATE_CHECK_RESULT_NOT_FOUND;
} }
void mail_duplicate_mark(struct mail_duplicate_db *db, void mail_duplicate_mark(struct mail_duplicate_transaction *trans,
const void *id, size_t id_size, const void *id, size_t id_size,
const char *user, time_t timestamp) const char *user, time_t timestamp)
{ {
struct mail_duplicate *d; struct mail_duplicate *dup;
void *new_id;
if (db->file == NULL) { if (trans->path == NULL) {
if (db->path == NULL) { /* Duplicate database disabled */
/* mail_duplicate database disabled */ e_debug(trans->event, "Mark ID (dummy)");
return; return;
}
db->file = mail_duplicate_file_new(db);
} }
new_id = p_malloc(db->file->pool, id_size); e_debug(trans->event, "Mark ID");
memcpy(new_id, id, id_size);
dup = mail_duplicate_get(trans, id, id_size, user);
d = p_new(db->file->pool, struct mail_duplicate, 1); /* Must already be checked and locked */
d->id = new_id; i_assert(mail_duplicate_is_locked(dup));
d->id_size = id_size;
d->user = p_strdup(db->file->pool, user);
d->time = timestamp;
db->file->changed = TRUE; dup->time = timestamp;
hash_table_update(db->file->hash, d, d); dup->marked = TRUE;
dup->changed = TRUE;
trans->changed = TRUE;
} }
void mail_duplicate_db_flush(struct mail_duplicate_db *db) void mail_duplicate_transaction_commit(
struct mail_duplicate_transaction **_trans)
{ {
struct mail_duplicate_file *file = db->file; struct mail_duplicate_transaction *trans = *_trans;
struct mail_duplicate_file_header hdr; struct mail_duplicate_file_header hdr;
struct mail_duplicate_record_header rec; struct mail_duplicate_record_header rec;
struct ostream *output; struct ostream *output;
struct hash_iterate_context *iter; struct hash_iterate_context *iter;
struct mail_duplicate *d; struct mail_duplicate *d;
int new_fd;
struct dotlock *dotlock;
if (file == NULL) if (trans == NULL)
return; return;
if (!file->changed || file->new_fd == -1) { *_trans = NULL;
/* unlock the mail_duplicate database */
mail_duplicate_file_free(&db->file); if (trans->path == NULL) {
e_debug(trans->event, "Commit (dummy)");
mail_duplicate_transaction_free(&trans);
return;
}
if (!trans->changed) {
e_debug(trans->event, "Commit; no changes");
mail_duplicate_transaction_free(&trans);
return;
}
struct mail_duplicate_db *db = trans->db;
i_assert(trans->path != NULL);
e_debug(trans->event, "Commit; overwrite %s", trans->path);
new_fd = file_dotlock_open(&db->dotlock_set, trans->path, 0, &dotlock);
if (new_fd != -1)
;
else if (errno != EAGAIN) {
e_error(trans->event,
"file_dotlock_open(%s) failed: %m",
trans->path);
mail_duplicate_transaction_free(&trans);
return;
} else {
e_error(trans->event,
"Creating lock file for %s timed out in %u secs",
trans->path, db->dotlock_set.timeout);
mail_duplicate_transaction_free(&trans);
return; return;
} }
i_zero(&hdr); i_zero(&hdr);
hdr.version = DUPLICATE_VERSION; hdr.version = DUPLICATE_VERSION;
output = o_stream_create_fd_file(file->new_fd, 0, FALSE); output = o_stream_create_fd_file(new_fd, 0, FALSE);
o_stream_cork(output); o_stream_cork(output);
o_stream_nsend(output, &hdr, sizeof(hdr)); o_stream_nsend(output, &hdr, sizeof(hdr));
i_zero(&rec); i_zero(&rec);
iter = hash_table_iterate_init(file->hash); iter = hash_table_iterate_init(trans->hash);
while (hash_table_iterate(iter, file->hash, &d, &d)) { while (hash_table_iterate(iter, trans->hash, &d, &d)) {
rec.stamp = d->time; if (d->marked) {
rec.id_size = d->id_size; rec.stamp = d->time;
rec.user_size = strlen(d->user); rec.id_size = d->id_size;
rec.user_size = strlen(d->user);
o_stream_nsend(output, &rec, sizeof(rec));
o_stream_nsend(output, d->id, rec.id_size); o_stream_nsend(output, &rec, sizeof(rec));
o_stream_nsend(output, d->user, rec.user_size); o_stream_nsend(output, d->id, rec.id_size);
o_stream_nsend(output, d->user, rec.user_size);
}
} }
hash_table_iterate_deinit(&iter); hash_table_iterate_deinit(&iter);
if (o_stream_finish(output) < 0) { if (o_stream_finish(output) < 0) {
e_error(db->user->event, "write(%s) failed: %s", file->path, e_error(trans->event, "write(%s) failed: %s",
o_stream_get_error(output)); o_stream_unref(&o trans->path, o_stream_get_error(output));
utput); o_stream_unref(&output);
mail_duplicate_file_free(&db->file); mail_duplicate_transaction_free(&trans);
return; return;
} }
o_stream_unref(&output); o_stream_unref(&output);
if (file_dotlock_replace(&file->dotlock, 0) < 0) if (file_dotlock_replace(&dotlock, 0) < 0) {
e_error(db->user->event, "file_dotlock_replace(%s) failed: %m", e_error(trans->event,
file->path); "file_dotlock_replace(%s) failed: %m", trans->path);
mail_duplicate_file_free(&db->file); }
iter = hash_table_iterate_init(trans->hash);
while (hash_table_iterate(iter, trans->hash, &d, &d))
mail_duplicate_unlock(trans, d);
hash_table_iterate_deinit(&iter);
mail_duplicate_transaction_free(&trans);
}
void mail_duplicate_transaction_rollback(
struct mail_duplicate_transaction **_trans)
{
struct mail_duplicate_transaction *trans = *_trans;
if (trans == NULL)
return;
*_trans = NULL;
if (trans->path == NULL)
e_debug(trans->event, "Rollback (dummy)");
else
e_debug(trans->event, "Rollback");
mail_duplicate_transaction_free(&trans);
} }
struct mail_duplicate_db * struct mail_duplicate_db *
mail_duplicate_db_init(struct mail_user *user, const char *name) mail_duplicate_db_init(struct mail_user *user, const char *name)
{ {
struct mail_duplicate_db *db; struct mail_duplicate_db *db;
const struct mail_storage_settings *mail_set; const struct mail_storage_settings *mail_set;
const char *home = NULL; const char *home = NULL;
const char *lock_dir;
db = i_new(struct mail_duplicate_db, 1);
db->event = event_create(user->event);
event_set_append_log_prefix(db->event, "duplicate db: ");
e_debug(db->event, "Initialize");
if (mail_user_get_home(user, &home) <= 0) { if (mail_user_get_home(user, &home) <= 0) {
e_error(user->event, "User %s doesn't have home dir set, " e_error(db->event, "User %s doesn't have home dir set, "
"disabling duplicate database", user->username); "disabling duplicate database", user->username);
} }
db = i_new(struct mail_duplicate_db, 1);
db->user = user; db->user = user;
db->path = home == NULL ? NULL : db->path = home == NULL ? NULL :
i_strconcat(home, "/.dovecot.", name, NULL); i_strconcat(home, "/.dovecot.", name, NULL);
db->dotlock_set = default_mail_duplicate_dotlock_set; db->dotlock_set = default_mail_duplicate_dotlock_set;
lock_dir = mail_user_get_volatile_dir(user);
if (lock_dir == NULL)
lock_dir = home;
db->lock_dir = i_strconcat(lock_dir, "/.dovecot.", name, ".locks",
NULL);
mail_set = mail_user_set_get_storage_set(user); mail_set = mail_user_set_get_storage_set(user);
db->dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; db->dotlock_set.use_excl_lock = mail_set->dotlock_use_excl;
db->dotlock_set.nfs_flush = mail_set->mail_nfs_storage; db->dotlock_set.nfs_flush = mail_set->mail_nfs_storage;
return db; return db;
} }
void mail_duplicate_db_deinit(struct mail_duplicate_db **_db) void mail_duplicate_db_deinit(struct mail_duplicate_db **_db)
{ {
struct mail_duplicate_db *db = *_db; struct mail_duplicate_db *db = *_db;
*_db = NULL; *_db = NULL;
if (db->file != NULL) {
mail_duplicate_db_flush(db); e_debug(db->event, "Cleanup");
i_assert(db->file == NULL);
} i_assert(db->transaction_count == 0);
event_unref(&db->event);
i_free(db->path); i_free(db->path);
i_free(db->lock_dir);
i_free(db); i_free(db);
} }
 End of changes. 68 change blocks. 
129 lines changed or deleted 508 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)