dovecot-2.2: lib: Added file_create_locked()
dovecot at dovecot.org
dovecot at dovecot.org
Sat May 23 20:43:59 UTC 2015
details: http://hg.dovecot.org/dovecot-2.2/rev/78bbfe4e4e8e
changeset: 18740:78bbfe4e4e8e
user: Timo Sirainen <tss at iki.fi>
date: Sat May 23 16:41:34 2015 -0400
description:
lib: Added file_create_locked()
diffstat:
src/lib/Makefile.am | 2 +
src/lib/file-create-locked.c | 126 +++++++++++++++++++++++++++++++++++++++++++
src/lib/file-create-locked.h | 29 +++++++++
3 files changed, 157 insertions(+), 0 deletions(-)
diffs (182 lines):
diff -r b23a19faf304 -r 78bbfe4e4e8e src/lib/Makefile.am
--- a/src/lib/Makefile.am Fri May 22 19:07:56 2015 -0400
+++ b/src/lib/Makefile.am Sat May 23 16:41:34 2015 -0400
@@ -35,6 +35,7 @@
fdatasync-path.c \
fdpass.c \
file-cache.c \
+ file-create-locked.c \
file-copy.c \
file-dotlock.c \
file-lock.c \
@@ -171,6 +172,7 @@
fdatasync-path.h \
fdpass.h \
file-cache.h \
+ file-create-locked.h \
file-copy.h \
file-dotlock.h \
file-lock.h \
diff -r b23a19faf304 -r 78bbfe4e4e8e src/lib/file-create-locked.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/file-create-locked.c Sat May 23 16:41:34 2015 -0400
@@ -0,0 +1,126 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "safe-mkstemp.h"
+#include "file-lock.h"
+#include "file-create-locked.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define MAX_RETRY_COUNT 1000
+
+static int
+try_lock_existing(int fd, const char *path,
+ const struct file_create_settings *set,
+ struct file_lock **lock_r, const char **error_r)
+{
+ struct stat st1, st2;
+
+ if (fstat(fd, &st1) < 0) {
+ *error_r = t_strdup_printf("fstat(%s) failed: %m", path);
+ return -1;
+ }
+ if (file_wait_lock_error(fd, path, F_WRLCK, FILE_LOCK_METHOD_FCNTL,
+ set->lock_timeout_secs, lock_r, error_r) <= 0)
+ return -1;
+ if (stat(path, &st2) == 0) {
+ return st1.st_ino == st2.st_ino &&
+ CMP_DEV_T(st1.st_dev, st2.st_dev) ? 1 : 0;
+ } else if (errno == ENOENT) {
+ return 0;
+ } else {
+ *error_r = t_strdup_printf("stat(%s) failed: %m", path);
+ return -1;
+ }
+}
+
+static int
+try_create_new(const char *path, const struct file_create_settings *set,
+ int *fd_r, struct file_lock **lock_r, const char **error_r)
+{
+ string_t *temp_path = t_str_new(128);
+ int fd, orig_errno, ret = -1;
+ int mode = set->mode != 0 ? set->mode : 0600;
+ uid_t uid = set->uid != 0 ? set->uid : (uid_t)-1;
+ uid_t gid = set->gid != 0 ? set->gid : (gid_t)-1;
+
+ str_append(temp_path, path);
+ if (uid != (uid_t)-1)
+ fd = safe_mkstemp(temp_path, mode, uid, gid);
+ else
+ fd = safe_mkstemp_group(temp_path, mode, gid, set->gid_origin);
+ if (fd == -1) {
+ *error_r = t_strdup_printf("safe_mkstemp(%s) failed: %m", path);
+ return -1;
+ }
+ if (file_try_lock_error(fd, str_c(temp_path), F_WRLCK,
+ FILE_LOCK_METHOD_FCNTL,
+ lock_r, error_r) <= 0) {
+ } else if (link(str_c(temp_path), path) < 0) {
+ if (errno == EEXIST) {
+ /* just created by somebody else */
+ ret = 0;
+ } else if (errno == ENOENT) {
+ /* our temp file was just deleted by somebody else,
+ retry creating it. */
+ ret = 0;
+ } else {
+ *error_r = t_strdup_printf("link(%s, %s) failed: %m",
+ str_c(temp_path), path);
+ }
+ } else {
+ *fd_r = fd;
+ return 1;
+ }
+ orig_errno = errno;
+ i_close_fd(&fd);
+ if (unlink(str_c(temp_path)) < 0)
+ i_error("unlink(%s) failed: %m", str_c(temp_path));
+ errno = orig_errno;
+ return ret;
+}
+
+int file_create_locked(const char *path, const struct file_create_settings *set,
+ struct file_lock **lock_r, bool *created_r,
+ const char **error_r)
+{
+ unsigned int i;
+ int fd, ret;
+
+ for (i = 0; i < MAX_RETRY_COUNT; i++) {
+ fd = open(path, O_RDWR);
+ if (fd != -1) {
+ ret = try_lock_existing(fd, path, set, lock_r, error_r);
+ if (ret < 0) {
+ i_close_fd(&fd);
+ return -1;
+ }
+ if (ret > 0) {
+ /* successfully locked an existing file */
+ *created_r = FALSE;
+ return fd;
+ }
+ } else if (errno != ENOENT) {
+ *error_r = t_strdup_printf("open(%s) failed: %m", path);
+ return -1;
+ } else {
+ /* try to create the file */
+ ret = try_create_new(path, set, &fd, lock_r, error_r);
+ if (ret < 0)
+ return -1;
+ if (ret > 0) {
+ /* successfully created a new locked file */
+ *created_r = TRUE;
+ return fd;
+ }
+ /* the file was just created - try again opening and
+ locking it */
+ }
+ }
+ *error_r = t_strdup_printf("Creating a locked file %s keeps failing", path);
+ errno = EINVAL;
+ return -1;
+}
diff -r b23a19faf304 -r 78bbfe4e4e8e src/lib/file-create-locked.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/file-create-locked.h Sat May 23 16:41:34 2015 -0400
@@ -0,0 +1,29 @@
+#ifndef FILE_CREATE_LOCKED_H
+#define FILE_CREATE_LOCKED_H
+
+struct file_lock;
+
+struct file_create_settings {
+ /* 0 = try locking without waiting */
+ unsigned int lock_timeout_secs;
+
+ /* 0 = 0600 */
+ int mode;
+ /* 0 = default */
+ uid_t uid;
+ /* 0 = default */
+ gid_t gid;
+ const char *gid_origin;
+};
+
+/* Either open an existing file and lock it, or create the file locked.
+ The creation is done by creating a temp file and link()ing it to path.
+ If link() fails, opening is retried again. Returns fd on success,
+ -1 on error. errno is preserved for the last failed syscall, so most
+ importantly ENOENT could mean that the directory doesn't exist and EAGAIN
+ means locking timed out. */
+int file_create_locked(const char *path, const struct file_create_settings *set,
+ struct file_lock **lock_r, bool *created_r,
+ const char **error_r);
+
+#endif
More information about the dovecot-cvs
mailing list