I'm planning to use the code in ostree (via libglnx), here's a few minor patches for systemd's lockfile code.
From 9e249575b2b99110a29f32f53aab1c1048b72eb9 Mon Sep 17 00:00:00 2001 From: Colin Walters <[email protected]> Date: Mon, 4 May 2015 16:12:46 -0400 Subject: [PATCH 1/3] lockfile-util.[ch]: Split out from util.[ch]
Continuing the general trend of splitting up util.[ch]. I specifically want to reuse this code in https://github.com/GNOME/libglnx and having it split up will make future copy-pasting easier. --- Makefile.am | 2 + src/shared/lockfile-util.c | 154 +++++++++++++++++++++++++++++++++++++++++++++ src/shared/lockfile-util.h | 39 ++++++++++++ src/shared/machine-image.h | 1 + src/shared/machine-pool.c | 1 + src/shared/util.c | 122 ----------------------------------- src/shared/util.h | 14 ----- 7 files changed, 197 insertions(+), 136 deletions(-) create mode 100644 src/shared/lockfile-util.c create mode 100644 src/shared/lockfile-util.h diff --git a/Makefile.am b/Makefile.am index 164bdfb..1ec1e77 100644 --- a/Makefile.am +++ b/Makefile.am @@ -775,6 +775,8 @@ libsystemd_shared_la_SOURCES = \ src/shared/formats-util.h \ src/shared/fstab-util.c \ src/shared/fstab-util.h \ + src/shared/lockfile-util.c \ + src/shared/lockfile-util.h \ src/shared/path-util.c \ src/shared/path-util.h \ src/shared/time-util.c \ diff --git a/src/shared/lockfile-util.c b/src/shared/lockfile-util.c new file mode 100644 index 0000000..05e16d1 --- /dev/null +++ b/src/shared/lockfile-util.c @@ -0,0 +1,154 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include <sys/file.h> + +#include "util.h" +#include "lockfile-util.h" +#include "fileio.h" + +int make_lock_file(const char *p, int operation, LockFile *ret) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *t = NULL; + int r; + + /* + * We use UNPOSIX locks if they are available. They have nice + * semantics, and are mostly compatible with NFS. However, + * they are only available on new kernels. When we detect we + * are running on an older kernel, then we fall back to good + * old BSD locks. They also have nice semantics, but are + * slightly problematic on NFS, where they are upgraded to + * POSIX locks, even though locally they are orthogonal to + * POSIX locks. + */ + + t = strdup(p); + if (!t) + return -ENOMEM; + + for (;;) { + struct flock fl = { + .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, + .l_whence = SEEK_SET, + }; + struct stat st; + + fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) + return -errno; + + r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); + if (r < 0) { + + /* If the kernel is too old, use good old BSD locks */ + if (errno == EINVAL) + r = flock(fd, operation); + + if (r < 0) + return errno == EAGAIN ? -EBUSY : -errno; + } + + /* If we acquired the lock, let's check if the file + * still exists in the file system. If not, then the + * previous exclusive owner removed it and then closed + * it. In such a case our acquired lock is worthless, + * hence try again. */ + + r = fstat(fd, &st); + if (r < 0) + return -errno; + if (st.st_nlink > 0) + break; + + fd = safe_close(fd); + } + + ret->path = t; + ret->fd = fd; + ret->operation = operation; + + fd = -1; + t = NULL; + + return r; +} + +int make_lock_file_for(const char *p, int operation, LockFile *ret) { + const char *fn; + char *t; + + assert(p); + assert(ret); + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + t = newa(char, strlen(p) + 2 + 4 + 1); + stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); + + return make_lock_file(t, operation, ret); +} + +void release_lock_file(LockFile *f) { + int r; + + if (!f) + return; + + if (f->path) { + + /* If we are the exclusive owner we can safely delete + * the lock file itself. If we are not the exclusive + * owner, we can try becoming it. */ + + if (f->fd >= 0 && + (f->operation & ~LOCK_NB) == LOCK_SH) { + static const struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + r = fcntl(f->fd, F_OFD_SETLK, &fl); + if (r < 0 && errno == EINVAL) + r = flock(f->fd, LOCK_EX|LOCK_NB); + + if (r >= 0) + f->operation = LOCK_EX|LOCK_NB; + } + + if ((f->operation & ~LOCK_NB) == LOCK_EX) + unlink_noerrno(f->path); + + free(f->path); + f->path = NULL; + } + + f->fd = safe_close(f->fd); + f->operation = 0; +} diff --git a/src/shared/lockfile-util.h b/src/shared/lockfile-util.h new file mode 100644 index 0000000..38d4709 --- /dev/null +++ b/src/shared/lockfile-util.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "macro.h" +#include "missing.h" + +typedef struct LockFile { + char *path; + int fd; + int operation; +} LockFile; + +int make_lock_file(const char *p, int operation, LockFile *ret); +int make_lock_file_for(const char *p, int operation, LockFile *ret); +void release_lock_file(LockFile *f); + +#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) + +#define LOCK_FILE_INIT { .fd = -1, .path = NULL } diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index bf41b2e..f041600 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -22,6 +22,7 @@ ***/ #include "time-util.h" +#include "lockfile-util.h" #include "hashmap.h" typedef enum ImageType { diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 41aa1b7..9920d15 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -26,6 +26,7 @@ #include "util.h" #include "process-util.h" +#include "lockfile-util.h" #include "mkdir.h" #include "btrfs-util.h" #include "path-util.h" diff --git a/src/shared/util.c b/src/shared/util.c index 2c7254e..19190df 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -6065,128 +6065,6 @@ int read_attr_path(const char *p, unsigned *ret) { return read_attr_fd(fd, ret); } -int make_lock_file(const char *p, int operation, LockFile *ret) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - /* - * We use UNPOSIX locks if they are available. They have nice - * semantics, and are mostly compatible with NFS. However, - * they are only available on new kernels. When we detect we - * are running on an older kernel, then we fall back to good - * old BSD locks. They also have nice semantics, but are - * slightly problematic on NFS, where they are upgraded to - * POSIX locks, even though locally they are orthogonal to - * POSIX locks. - */ - - t = strdup(p); - if (!t) - return -ENOMEM; - - for (;;) { - struct flock fl = { - .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, - .l_whence = SEEK_SET, - }; - struct stat st; - - fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) - return -errno; - - r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); - if (r < 0) { - - /* If the kernel is too old, use good old BSD locks */ - if (errno == EINVAL) - r = flock(fd, operation); - - if (r < 0) - return errno == EAGAIN ? -EBUSY : -errno; - } - - /* If we acquired the lock, let's check if the file - * still exists in the file system. If not, then the - * previous exclusive owner removed it and then closed - * it. In such a case our acquired lock is worthless, - * hence try again. */ - - r = fstat(fd, &st); - if (r < 0) - return -errno; - if (st.st_nlink > 0) - break; - - fd = safe_close(fd); - } - - ret->path = t; - ret->fd = fd; - ret->operation = operation; - - fd = -1; - t = NULL; - - return r; -} - -int make_lock_file_for(const char *p, int operation, LockFile *ret) { - const char *fn; - char *t; - - assert(p); - assert(ret); - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - t = newa(char, strlen(p) + 2 + 4 + 1); - stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); - - return make_lock_file(t, operation, ret); -} - -void release_lock_file(LockFile *f) { - int r; - - if (!f) - return; - - if (f->path) { - - /* If we are the exclusive owner we can safely delete - * the lock file itself. If we are not the exclusive - * owner, we can try becoming it. */ - - if (f->fd >= 0 && - (f->operation & ~LOCK_NB) == LOCK_SH) { - static const struct flock fl = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - }; - - r = fcntl(f->fd, F_OFD_SETLK, &fl); - if (r < 0 && errno == EINVAL) - r = flock(f->fd, LOCK_EX|LOCK_NB); - - if (r >= 0) - f->operation = LOCK_EX|LOCK_NB; - } - - if ((f->operation & ~LOCK_NB) == LOCK_EX) - unlink_noerrno(f->path); - - free(f->path); - f->path = NULL; - } - - f->fd = safe_close(f->fd); - f->operation = 0; -} - static size_t nul_length(const uint8_t *p, size_t sz) { size_t n = 0; diff --git a/src/shared/util.h b/src/shared/util.h index 9409ad9..4a67d5c 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -882,20 +882,6 @@ int chattr_path(const char *p, unsigned value, unsigned mask); int read_attr_fd(int fd, unsigned *ret); int read_attr_path(const char *p, unsigned *ret); -typedef struct LockFile { - char *path; - int fd; - int operation; -} LockFile; - -int make_lock_file(const char *p, int operation, LockFile *ret); -int make_lock_file_for(const char *p, int operation, LockFile *ret); -void release_lock_file(LockFile *f); - -#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) - -#define LOCK_FILE_INIT { .fd = -1, .path = NULL } - #define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); -- 1.8.3.1
From 45b44522276cbcf67fff29b607283dc66acbedc7 Mon Sep 17 00:00:00 2001 From: Colin Walters <[email protected]> Date: Mon, 4 May 2015 16:23:29 -0400 Subject: [PATCH 2/3] util: Add unlinkat_noerrno() Will be used in a subsequent patch, but it's worth considering the alternative of a treewide port of all current `unlink_noerrno()` users. There's 24 right now. --- src/shared/util.c | 6 +++++- src/shared/util.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/shared/util.c b/src/shared/util.c index 19190df..0972df6 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -299,10 +299,14 @@ void close_many(const int fds[], unsigned n_fd) { } int unlink_noerrno(const char *path) { + return unlinkat_noerrno(AT_FDCWD, path, 0); +} + +int unlinkat_noerrno(int dfd, const char *path, int flags) { PROTECT_ERRNO; int r; - r = unlink(path); + r = unlinkat(dfd, path, flags); if (r < 0) return -errno; diff --git a/src/shared/util.h b/src/shared/util.h index 4a67d5c..f9bba97 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -728,6 +728,8 @@ static inline bool logind_running(void) { int unlink_noerrno(const char *path); +int unlinkat_noerrno(int dfd, const char *path, int flags); + #define alloca0(n) \ ({ \ char *_new_; \ -- 1.8.3.1
From 839641ca295feb583359ce231d43afbd97725fd3 Mon Sep 17 00:00:00 2001 From: Colin Walters <[email protected]> Date: Mon, 4 May 2015 16:18:49 -0400 Subject: [PATCH 3/3] lockfile: Add fd-relative support This won't actually be used in systemd yet, but for ostree (which will copy this lockfile code via libglnx) I'm trying to consistently use fd-relative APIs, so let's support it here. --- src/shared/lockfile-util.c | 17 +++++++++++------ src/shared/lockfile-util.h | 7 ++++--- src/shared/machine-image.c | 6 +++--- src/shared/machine-pool.c | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/shared/lockfile-util.c b/src/shared/lockfile-util.c index 05e16d1..e76226a 100644 --- a/src/shared/lockfile-util.c +++ b/src/shared/lockfile-util.c @@ -31,7 +31,7 @@ #include "lockfile-util.h" #include "fileio.h" -int make_lock_file(const char *p, int operation, LockFile *ret) { +int make_lock_file(int dfd, const char *p, int operation, LockFile *ret) { _cleanup_close_ int fd = -1; _cleanup_free_ char *t = NULL; int r; @@ -58,7 +58,7 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { }; struct stat st; - fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); if (fd < 0) return -errno; @@ -88,6 +88,10 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { fd = safe_close(fd); } + /* Note that if this is not AT_FDCWD, the caller takes responsibility + * for the fd's lifetime being >= that of the lock. + */ + ret->dfd = dfd; ret->path = t; ret->fd = fd; ret->operation = operation; @@ -98,7 +102,7 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { return r; } -int make_lock_file_for(const char *p, int operation, LockFile *ret) { +int make_lock_file_for(int dfd, const char *p, int operation, LockFile *ret) { const char *fn; char *t; @@ -112,7 +116,7 @@ int make_lock_file_for(const char *p, int operation, LockFile *ret) { t = newa(char, strlen(p) + 2 + 4 + 1); stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); - return make_lock_file(t, operation, ret); + return make_lock_file(dfd, t, operation, ret); } void release_lock_file(LockFile *f) { @@ -142,8 +146,9 @@ void release_lock_file(LockFile *f) { f->operation = LOCK_EX|LOCK_NB; } - if ((f->operation & ~LOCK_NB) == LOCK_EX) - unlink_noerrno(f->path); + if ((f->operation & ~LOCK_NB) == LOCK_EX) { + unlinkat_noerrno(f->dfd, f->path, 0); + } free(f->path); f->path = NULL; diff --git a/src/shared/lockfile-util.h b/src/shared/lockfile-util.h index 38d4709..694b177 100644 --- a/src/shared/lockfile-util.h +++ b/src/shared/lockfile-util.h @@ -25,15 +25,16 @@ #include "missing.h" typedef struct LockFile { + int dfd; char *path; int fd; int operation; } LockFile; -int make_lock_file(const char *p, int operation, LockFile *ret); -int make_lock_file_for(const char *p, int operation, LockFile *ret); +int make_lock_file(int dfd, const char *p, int operation, LockFile *ret); +int make_lock_file_for(int dfd, const char *p, int operation, LockFile *ret); void release_lock_file(LockFile *f); #define _cleanup_release_lock_file_ _cleanup_(release_lock_file) -#define LOCK_FILE_INIT { .fd = -1, .path = NULL } +#define LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL } diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index bc215f0..4fefa0c 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -598,14 +598,14 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile return -ENOMEM; } - r = make_lock_file_for(path, operation, &t); + r = make_lock_file_for(AT_FDCWD, path, operation, &t); if (r < 0) return r; if (p) { mkdir_p("/run/systemd/nspawn/locks", 0600); - r = make_lock_file(p, operation, global); + r = make_lock_file(AT_FDCWD, p, operation, global); if (r < 0) { release_lock_file(&t); return r; @@ -646,7 +646,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret) { mkdir_p("/run/systemd/nspawn/locks", 0600); p = strjoina("/run/systemd/nspawn/locks/name-", name); - return make_lock_file(p, operation, ret); + return make_lock_file(AT_FDCWD, p, operation, ret); } bool image_name_is_valid(const char *s) { diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 9920d15..b0f3f54 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -181,7 +181,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { size = 16*1024*1024; /* Make sure we only set the directory up once at a time */ - r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file); + r = make_lock_file(AT_FDCWD, "/run/systemd/machines.lock", LOCK_EX, &lock_file); if (r < 0) return r; -- 1.8.3.1
_______________________________________________ systemd-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/systemd-devel
