diff --git a/src/shared/lockfile-util.c b/src/basic/lock-util.c similarity index 58% rename from src/shared/lockfile-util.c rename to src/basic/lock-util.c index df099d7d05..ba0c23445b 100644 --- a/src/shared/lockfile-util.c +++ b/src/basic/lock-util.c @@ -10,7 +10,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "fs-util.h" -#include "lockfile-util.h" +#include "lock-util.h" #include "macro.h" #include "missing_fcntl.h" #include "path-util.h" @@ -21,39 +21,25 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { int r; assert(p); + assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH)); assert(ret); - /* - * 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. - */ + /* We use UNPOSIX locks as they have nice semantics, and are mostly compatible with NFS. */ 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 || ERRNO_IS_NOT_SUPPORTED(errno)) - r = flock(fd, operation); - if (r < 0) - return errno == EAGAIN ? -EBUSY : -errno; - } + r = unposix_lock(fd, operation); + if (r < 0) + return r == -EAGAIN ? -EBUSY : r; /* 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 @@ -73,7 +59,7 @@ int make_lock_file(const char *p, int operation, LockFile *ret) { .operation = operation, }; - return r; + return 0; } int make_lock_file_for(const char *p, int operation, LockFile *ret) { @@ -99,8 +85,6 @@ int make_lock_file_for(const char *p, int operation, LockFile *ret) { } void release_lock_file(LockFile *f) { - int r; - if (!f) return; @@ -111,19 +95,9 @@ void release_lock_file(LockFile *f) { * 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 || ERRNO_IS_NOT_SUPPORTED(errno))) - r = flock(f->fd, LOCK_EX|LOCK_NB); - - if (r >= 0) - f->operation = LOCK_EX|LOCK_NB; - } + (f->operation & ~LOCK_NB) == LOCK_SH && + unposix_lock(f->fd, LOCK_EX|LOCK_NB) >= 0) + f->operation = LOCK_EX|LOCK_NB; if ((f->operation & ~LOCK_NB) == LOCK_EX) unlink_noerrno(f->path); @@ -134,3 +108,47 @@ void release_lock_file(LockFile *f) { f->fd = safe_close(f->fd); f->operation = 0; } + +int unposix_lock(int fd, int operation) { + int cmd, type, r; + + assert(fd >= 0); + + cmd = (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW; + + switch (operation & ~LOCK_NB) { + case LOCK_EX: + type = F_WRLCK; + break; + case LOCK_SH: + type = F_RDLCK; + break; + case LOCK_UN: + type = F_UNLCK; + break; + default: + assert_not_reached(); + } + + r = RET_NERRNO(fcntl(fd, cmd, &(struct flock) { + .l_type = type, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0, + })); + + if (r == -EACCES) /* Treat EACCESS/EAGAIN the same as per man page. */ + r = -EAGAIN; + + return r; +} + +void unposix_unlockpp(int **fd) { + assert(fd); + + if (!*fd || **fd < 0) + return; + + (void) unposix_lock(**fd, LOCK_UN); + *fd = NULL; +} diff --git a/src/shared/lockfile-util.h b/src/basic/lock-util.h similarity index 54% rename from src/shared/lockfile-util.h rename to src/basic/lock-util.h index 20e19c01d4..e8d4c24874 100644 --- a/src/shared/lockfile-util.h +++ b/src/basic/lock-util.h @@ -12,3 +12,11 @@ int make_lock_file_for(const char *p, int operation, LockFile *ret); void release_lock_file(LockFile *f); #define LOCK_FILE_INIT { .fd = -EBADF, .path = NULL } + +/* Open File Description locks with the same interface as flock(). */ +int unposix_lock(int fd, int operation); + +void unposix_unlockpp(int **fd); + +#define CLEANUP_UNPOSIX_UNLOCK(fd) \ + _cleanup_(unposix_unlockpp) _unused_ int *CONCATENATE(_cleanup_unposix_unlock_, UNIQ) = &(fd) diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c index fb86ac2b5b..96024bc485 100644 --- a/src/basic/memfd-util.c +++ b/src/basic/memfd-util.c @@ -133,3 +133,34 @@ int memfd_new_and_map(const char *name, size_t sz, void **p) { return TAKE_FD(fd); } + +int memfd_new_and_seal(const char *name, const void *data, size_t sz) { + _cleanup_close_ int fd = -EBADF; + ssize_t n; + off_t f; + int r; + + assert(data || sz == 0); + + fd = memfd_new(name); + if (fd < 0) + return fd; + + if (sz > 0) { + n = write(fd, data, sz); + if (n < 0) + return -errno; + if ((size_t) n != sz) + return -EIO; + + f = lseek(fd, 0, SEEK_SET); + if (f != 0) + return -errno; + } + + r = memfd_set_sealed(fd); + if (r < 0) + return r; + + return TAKE_FD(fd); +} diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h index 8596c1a369..d6da268da0 100644 --- a/src/basic/memfd-util.h +++ b/src/basic/memfd-util.h @@ -8,6 +8,7 @@ int memfd_new(const char *name); int memfd_new_and_map(const char *name, size_t sz, void **p); +int memfd_new_and_seal(const char *name, const void *data, size_t sz); int memfd_map(int fd, uint64_t offset, size_t size, void **p); diff --git a/src/basic/meson.build b/src/basic/meson.build index 98b4d0ee95..bd8fc16833 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -46,6 +46,7 @@ basic_sources = files( 'ioprio-util.c', 'limits-util.c', 'locale-util.c', + 'lock-util.c', 'log.c', 'login-util.c', 'memfd-util.c', diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 6c5041230a..0a47947d34 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -18,6 +18,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "lock-util.h" #include "macro.h" #include "mkdir.h" #include "parse-util.h" @@ -671,12 +672,7 @@ int reset_uid_gid(void) { } int take_etc_passwd_lock(const char *root) { - struct flock flock = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0, - }; + int r; /* This is roughly the same as lckpwdf(), but not as awful. We don't want to use alarm() and signals, * hence we implement our own trivial version of this. @@ -695,8 +691,9 @@ int take_etc_passwd_lock(const char *root) { if (fd < 0) return log_debug_errno(errno, "Cannot open %s: %m", path); - if (fcntl(fd, F_SETLKW, &flock) < 0) - return log_debug_errno(errno, "Locking %s failed: %m", path); + r = unposix_lock(fd, LOCK_EX); + if (r < 0) + return log_debug_errno(r, "Locking %s failed: %m", path); return TAKE_FD(fd); } diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index 9f4387a21b..42936bf567 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -11,6 +11,7 @@ #include "format-util.h" #include "fs-util.h" #include "io-util.h" +#include "lock-util.h" #include "nscd-flush.h" #include "parse-util.h" #include "random-util.h" @@ -362,27 +363,12 @@ static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) { (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */ } -static int lockfp(int fd, int *fd_lock) { - if (lockf(fd, F_LOCK, 0) < 0) - return -errno; - *fd_lock = fd; - return 0; -} - -static void unlockfp(int *fd_lock) { - if (*fd_lock < 0) - return; - lockf(*fd_lock, F_ULOCK, 0); - *fd_lock = -EBADF; -} - static int dynamic_user_realize( DynamicUser *d, char **suggested_dirs, uid_t *ret_uid, gid_t *ret_gid, bool is_user) { - _cleanup_(unlockfp) int storage_socket0_lock = -EBADF; _cleanup_close_ int uid_lock_fd = -EBADF; _cleanup_close_ int etc_passwd_lock_fd = -EBADF; uid_t num = UID_INVALID; /* a uid if is_user, and a gid otherwise */ @@ -397,10 +383,12 @@ static int dynamic_user_realize( /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist * yet. If it already exists its existing UID/GID will be reused. */ - r = lockfp(d->storage_socket[0], &storage_socket0_lock); + r = unposix_lock(d->storage_socket[0], LOCK_EX); if (r < 0) return r; + CLEANUP_UNPOSIX_UNLOCK(d->storage_socket[0]); + r = dynamic_user_pop(d, &num, &uid_lock_fd); if (r < 0) { int new_uid_lock_fd; @@ -411,7 +399,9 @@ static int dynamic_user_realize( /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the * lock however, so that nobody else blocks on our NSS lookups. */ - unlockfp(&storage_socket0_lock); + r = unposix_lock(d->storage_socket[0], LOCK_UN); + if (r < 0) + return r; /* Let's see if a proper, static user or group by this name exists. Try to take the lock on * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't @@ -461,7 +451,7 @@ static int dynamic_user_realize( } /* So, we found a working UID/lock combination. Let's see if we actually still need it. */ - r = lockfp(d->storage_socket[0], &storage_socket0_lock); + r = unposix_lock(d->storage_socket[0], LOCK_EX); if (r < 0) { unlink_uid_lock(uid_lock_fd, num, d->name); return r; @@ -524,7 +514,6 @@ static int dynamic_user_realize( } int dynamic_user_current(DynamicUser *d, uid_t *ret) { - _cleanup_(unlockfp) int storage_socket0_lock = -EBADF; _cleanup_close_ int lock_fd = -EBADF; uid_t uid; int r; @@ -534,10 +523,12 @@ int dynamic_user_current(DynamicUser *d, uid_t *ret) { /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the * storage socket, and pushes it back in right-away. */ - r = lockfp(d->storage_socket[0], &storage_socket0_lock); + r = unposix_lock(d->storage_socket[0], LOCK_EX); if (r < 0) return r; + CLEANUP_UNPOSIX_UNLOCK(d->storage_socket[0]); + r = dynamic_user_pop(d, &uid, &lock_fd); if (r < 0) return r; @@ -567,7 +558,6 @@ static DynamicUser* dynamic_user_unref(DynamicUser *d) { } static int dynamic_user_close(DynamicUser *d) { - _cleanup_(unlockfp) int storage_socket0_lock = -EBADF; _cleanup_close_ int lock_fd = -EBADF; uid_t uid; int r; @@ -575,10 +565,12 @@ static int dynamic_user_close(DynamicUser *d) { /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the * user is unrealized again, much like it was after it the DynamicUser object was first allocated. */ - r = lockfp(d->storage_socket[0], &storage_socket0_lock); + r = unposix_lock(d->storage_socket[0], LOCK_EX); if (r < 0) return r; + CLEANUP_UNPOSIX_UNLOCK(d->storage_socket[0]); + r = dynamic_user_pop(d, &uid, &lock_fd); if (r == -EAGAIN) /* User wasn't realized yet, nothing to do. */ diff --git a/src/journal/fuzz-journald-native-fd.c b/src/journal/fuzz-journald-native-fd.c index 9adbb8b0dc..edbf834478 100644 --- a/src/journal/fuzz-journald-native-fd.c +++ b/src/journal/fuzz-journald-native-fd.c @@ -23,11 +23,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { dummy_server_init(&s, NULL, 0); - sealed_fd = memfd_new(NULL); + sealed_fd = memfd_new_and_seal(NULL, data, size); assert_se(sealed_fd >= 0); - assert_se(write(sealed_fd, data, size) == (ssize_t) size); - assert_se(memfd_set_sealed(sealed_fd) >= 0); - assert_se(lseek(sealed_fd, 0, SEEK_SET) == 0); ucred = (struct ucred) { .pid = getpid_cached(), .uid = geteuid(), diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 3a0e155f38..143156484b 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -12,7 +12,7 @@ #include "alloc-util.h" #include "ether-addr-util.h" #include "hexdecoct.h" -#include "lockfile-util.h" +#include "lock-util.h" #include "missing_network.h" #include "netif-naming-scheme.h" #include "netlink-util.h" diff --git a/src/shared/data-fd-util.c b/src/shared/data-fd-util.c index 209898fa61..0dbf89293f 100644 --- a/src/shared/data-fd-util.c +++ b/src/shared/data-fd-util.c @@ -30,7 +30,6 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags) { _cleanup_close_ int fd = -EBADF; int isz = 0, r; ssize_t n; - off_t f; assert(data || size == 0); @@ -58,23 +57,13 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags) { return RET_NERRNO(open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY)); if ((flags & ACQUIRE_NO_MEMFD) == 0) { - fd = memfd_new("data-fd"); - if (fd < 0) - goto try_pipe; + fd = memfd_new_and_seal("data-fd", data, size); + if (fd < 0) { + if (ERRNO_IS_NOT_SUPPORTED(fd)) + goto try_pipe; - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - f = lseek(fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - r = memfd_set_sealed(fd); - if (r < 0) - return r; + return fd; + } return TAKE_FD(fd); } diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 6d294efd84..17a3ac679a 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -27,7 +27,7 @@ #include "hashmap.h" #include "hostname-setup.h" #include "id128-util.h" -#include "lockfile-util.h" +#include "lock-util.h" #include "log.h" #include "loop-util.h" #include "macro.h" diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h index a8874228dc..3c6928619c 100644 --- a/src/shared/discover-image.h +++ b/src/shared/discover-image.h @@ -7,7 +7,7 @@ #include "sd-id128.h" #include "hashmap.h" -#include "lockfile-util.h" +#include "lock-util.h" #include "macro.h" #include "path-util.h" #include "string-util.h" diff --git a/src/shared/meson.build b/src/shared/meson.build index 82fd206d69..e58e161713 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -98,7 +98,6 @@ shared_sources = files( 'libmount-util.c', 'local-addresses.c', 'locale-setup.c', - 'lockfile-util.c', 'logs-show.c', 'loop-util.c', 'loopback-setup.c',