summaryrefslogtreecommitdiff
path: root/src/shared/util.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-01-14 23:09:02 +0100
committerLennart Poettering <lennart@poettering.net>2015-01-14 23:18:33 +0100
commit30535c16924a3da7b47ea87190d929d617d95c5a (patch)
treea2cd4f490a5a7d71350b31607a690aff42d1077d /src/shared/util.c
parent805e5dda0a01c99d231824e1a9c4a208418bf342 (diff)
nspawn: add file system locks for controlling access to container images
This adds three kinds of file system locks for container images: a) a file system lock next to the actual image, in a .lck file in the same directory the image is located. This lock has the benefit of usually being located on the same NFS share as the image itself, and thus allows locking container images across NFS shares. b) a file system lock in /run, named after st_dev and st_ino of the root of the image. This lock has the advantage that it is unique even if the same image is bind mounted to two different places at the same time, as the ino/dev stays constant for them. c) a file system lock that is only taken when a new disk image is about to be created, that ensures that checking whether the name is already used across the search path, and actually placing the image is not interrupted by other code taking the name. a + b are read-write locks. When a container is booted in read-only mode a read lock is taken, otherwise a write lock. Lock b is always taken after a, to avoid ABBA problems. Lock c is mostly relevant when renaming or cloning images.
Diffstat (limited to 'src/shared/util.c')
-rw-r--r--src/shared/util.c140
1 files changed, 123 insertions, 17 deletions
diff --git a/src/shared/util.c b/src/shared/util.c
index 857bb1b726..884e782c4f 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -62,6 +62,7 @@
#include <sys/xattr.h>
#include <libgen.h>
#include <sys/statvfs.h>
+#include <sys/file.h>
#include <linux/fs.h>
#undef basename
@@ -4303,23 +4304,6 @@ bool machine_name_is_valid(const char *s) {
return true;
}
-bool image_name_is_valid(const char *s) {
- if (!filename_is_valid(s))
- return false;
-
- if (string_has_cc(s, NULL))
- return false;
-
- if (!utf8_is_valid(s))
- return false;
-
- /* Temporary files for atomically creating new files */
- if (startswith(s, ".#"))
- return false;
-
- return true;
-}
-
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
@@ -7819,3 +7803,125 @@ 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;
+}