dovecot-2.1: dict file: Optionally use fcntl/flock locking, inst...
dovecot at dovecot.org
dovecot at dovecot.org
Tue Apr 10 17:57:22 EEST 2012
details: http://hg.dovecot.org/dovecot-2.1/rev/a1c0e4046d81
changeset: 14408:a1c0e4046d81
user: Timo Sirainen <tss at iki.fi>
date: Tue Apr 10 17:57:09 2012 +0300
description:
dict file: Optionally use fcntl/flock locking, instead of dotlocks.
diffstat:
src/lib-dict/dict-file.c | 142 +++++++++++++++++++++++++++++++++++++++-------
1 files changed, 120 insertions(+), 22 deletions(-)
diffs (235 lines):
diff -r 20e1aa322b1e -r a1c0e4046d81 src/lib-dict/dict-file.c
--- a/src/lib-dict/dict-file.c Tue Apr 10 10:09:39 2012 +0300
+++ b/src/lib-dict/dict-file.c Tue Apr 10 17:57:09 2012 +0300
@@ -3,12 +3,14 @@
#include "lib.h"
#include "array.h"
#include "hash.h"
+#include "file-lock.h"
#include "file-dotlock.h"
#include "nfs-workarounds.h"
#include "istream.h"
#include "ostream.h"
#include "dict-private.h"
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
@@ -17,10 +19,13 @@
struct file_dict {
struct dict dict;
pool_t hash_pool;
+ enum file_lock_method lock_method;
char *path;
struct hash_table *hash;
int fd;
+
+ bool refreshed;
};
struct file_dict_iterate_path {
@@ -75,8 +80,17 @@
const char *base_dir ATTR_UNUSED)
{
struct file_dict *dict;
-
+
dict = i_new(struct file_dict, 1);
+ if (strncmp(uri, "lock=fcntl ", 11) == 0) {
+ dict->lock_method = FILE_LOCK_METHOD_FCNTL;
+ uri += 11;
+ } else if (strncmp(uri, "lock=flock ", 11) == 0) {
+ dict->lock_method = FILE_LOCK_METHOD_FLOCK;
+ uri += 11;
+ } else {
+ dict->lock_method = FILE_LOCK_METHOD_DOTLOCK;
+ }
dict->dict = *driver;
dict->path = i_strdup(uri);
dict->hash_pool = pool_alloconly_create("file dict", 1024);
@@ -126,10 +140,9 @@
return FALSE;
}
-static int file_dict_refresh(struct file_dict *dict)
+static int file_dict_open_latest(struct file_dict *dict)
{
- struct istream *input;
- char *key, *value;
+ int open_type;
if (!file_dict_need_refresh(dict))
return 0;
@@ -138,13 +151,29 @@
if (close(dict->fd) < 0)
i_error("close(%s) failed: %m", dict->path);
}
- dict->fd = open(dict->path, O_RDONLY);
+
+ open_type = dict->lock_method == FILE_LOCK_METHOD_DOTLOCK ?
+ O_RDONLY : O_RDWR;
+ dict->fd = open(dict->path, open_type);
if (dict->fd == -1) {
if (errno == ENOENT)
return 0;
i_error("open(%s) failed: %m", dict->path);
return -1;
}
+ dict->refreshed = FALSE;
+ return 1;
+}
+
+static int file_dict_refresh(struct file_dict *dict)
+{
+ struct istream *input;
+ char *key, *value;
+
+ if (file_dict_open_latest(dict) < 0)
+ return -1;
+ if (dict->refreshed)
+ return 0;
hash_table_clear(dict->hash, TRUE);
p_clear(dict->hash_pool);
@@ -157,6 +186,7 @@
hash_table_insert(dict->hash, key, value);
}
i_stream_destroy(&input);
+ dict->refreshed = TRUE;
return 0;
}
@@ -381,35 +411,92 @@
return fd_copy_stat_permissions(&src_st, dest_fd, dest_path);
}
+static int
+file_dict_lock(struct file_dict *dict, struct file_lock **lock_r)
+{
+ int fd, ret;
+
+ if (file_dict_open_latest(dict) < 0)
+ return -1;
+
+ if (dict->fd == -1) {
+ /* quota file doesn't exist yet, we need to create it */
+ fd = open(dict->path, O_CREAT | O_RDWR, 0600);
+ if (fd == -1) {
+ i_error("creat(%s) failed: %m", dict->path);
+ return -1;
+ }
+ (void)fd_copy_parent_dir_permissions(dict->path, fd, dict->path);
+ (void)close(fd);
+ }
+
+ do {
+ if (file_wait_lock(dict->fd, dict->path, F_WRLCK,
+ dict->lock_method,
+ file_dict_dotlock_settings.timeout,
+ lock_r) <= 0) {
+ i_error("file_wait_lock(%s) failed: %m", dict->path);
+ return -1;
+ }
+ /* check again if we need to reopen the file because it was
+ just replaced */
+ } while ((ret = file_dict_open_latest(dict)) > 0);
+
+ return ret < 0 ? -1 : 0;
+}
+
static int file_dict_write_changes(struct file_dict_transaction_context *ctx)
{
struct file_dict *dict = (struct file_dict *)ctx->ctx.dict;
- struct dotlock *dotlock;
+ struct dotlock *dotlock = NULL;
+ struct file_lock *lock = NULL;
+ const char *temp_path = NULL;
struct hash_iterate_context *iter;
struct ostream *output;
void *key, *value;
- int fd;
+ int fd = -1;
- fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
- &dotlock);
- if (fd == -1) {
- i_error("file dict commit: file_dotlock_open(%s) failed: %m",
- dict->path);
- return -1;
+ switch (dict->lock_method) {
+ case FILE_LOCK_METHOD_FCNTL:
+ case FILE_LOCK_METHOD_FLOCK:
+ if (file_dict_lock(dict, &lock) < 0)
+ return -1;
+ temp_path = t_strdup_printf("%s.tmp", dict->path);
+ fd = creat(temp_path, 0600);
+ if (fd == -1) {
+ i_error("file dict commit: creat(%s) failed: %m",
+ temp_path);
+ return -1;
+ }
+ break;
+ case FILE_LOCK_METHOD_DOTLOCK:
+ fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0,
+ &dotlock);
+ if (fd == -1) {
+ i_error("file dict commit: file_dotlock_open(%s) failed: %m",
+ dict->path);
+ return -1;
+ }
+ temp_path = file_dotlock_get_lock_path(dotlock);
+ break;
}
+
/* refresh once more now that we're locked */
if (file_dict_refresh(dict) < 0) {
- file_dotlock_delete(&dotlock);
+ if (dotlock != NULL)
+ file_dotlock_delete(&dotlock);
+ else {
+ (void)close(fd);
+ file_unlock(&lock);
+ }
return -1;
}
if (dict->fd != -1) {
/* preserve the permissions */
- (void)fd_copy_permissions(dict->fd, dict->path, fd,
- file_dotlock_get_lock_path(dotlock));
+ (void)fd_copy_permissions(dict->fd, dict->path, fd, temp_path);
} else {
/* get initial permissions from parent directory */
- (void)fd_copy_parent_dir_permissions(dict->path, fd,
- file_dotlock_get_lock_path(dotlock));
+ (void)fd_copy_parent_dir_permissions(dict->path, fd, temp_path);
}
file_dict_apply_changes(ctx);
@@ -425,10 +512,21 @@
hash_table_iterate_deinit(&iter);
o_stream_destroy(&output);
- if (file_dotlock_replace(&dotlock,
- DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
- (void)close(fd);
- return -1;
+ if (dotlock != NULL) {
+ if (file_dotlock_replace(&dotlock,
+ DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
+ (void)close(fd);
+ return -1;
+ }
+ } else {
+ if (rename(temp_path, dict->path) < 0) {
+ i_error("rename(%s, %s) failed: %m",
+ temp_path, dict->path);
+ file_unlock(&lock);
+ (void)close(fd);
+ return -1;
+ }
+ file_lock_free(&lock);
}
if (dict->fd != -1)
More information about the dovecot-cvs
mailing list