diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/shared/machine-image.c | 56 | ||||
-rw-r--r-- | src/shared/util.c | 24 | ||||
-rw-r--r-- | src/shared/util.h | 3 |
3 files changed, 73 insertions, 10 deletions
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 25689ca93c..117994d6d8 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -120,6 +120,8 @@ static int image_make( (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS); if (S_ISDIR(st.st_mode)) { + _cleanup_close_ int fd = -1; + unsigned file_attr = 0; if (!ret) return 1; @@ -127,15 +129,14 @@ static int image_make( if (!pretty) pretty = filename; + fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); + if (fd < 0) + return -errno; + /* btrfs subvolumes have inode 256 */ if (st.st_ino == 256) { - _cleanup_close_ int fd = -1; struct statfs sfs; - fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (fd < 0) - return -errno; - if (fstatfs(fd, &sfs) < 0) return -errno; @@ -173,13 +174,17 @@ static int image_make( } } - /* It's just a normal directory. */ + /* If the IMMUTABLE bit is set, we consider the + * directory read-only. Since the ioctl is not + * supported everywhere we ignore failures. */ + (void) read_attr_fd(fd, &file_attr); + /* It's just a normal directory. */ r = image_new(IMAGE_DIRECTORY, pretty, path, filename, - read_only, + read_only || (file_attr & FS_IMMUTABLE_FL), 0, 0, ret); @@ -347,6 +352,11 @@ int image_remove(Image *i) { return btrfs_subvol_remove(i->path); case IMAGE_DIRECTORY: + /* Allow deletion of read-only directories */ + (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + + /* fall through */ + case IMAGE_GPT: return rm_rf_dangerous(i->path, false, true, false); @@ -357,6 +367,7 @@ int image_remove(Image *i) { int image_rename(Image *i, const char *new_name) { _cleanup_free_ char *new_path = NULL, *nn = NULL; + unsigned file_attr = 0; int r; assert(i); @@ -376,8 +387,16 @@ int image_rename(Image *i, const char *new_name) { switch (i->type) { - case IMAGE_SUBVOLUME: case IMAGE_DIRECTORY: + /* Turn of the immutable bit while we rename the image, so that we can rename it */ + (void) read_attr_path(i->path, &file_attr); + + if (file_attr & FS_IMMUTABLE_FL) + (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + + /* fall through */ + + case IMAGE_SUBVOLUME: new_path = file_in_same_dir(i->path, new_name); break; @@ -403,6 +422,10 @@ int image_rename(Image *i, const char *new_name) { if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0) return -errno; + /* Restore the immutable bit, if it was set before */ + if (file_attr & FS_IMMUTABLE_FL) + (void) chattr_path(new_path, true, FS_IMMUTABLE_FL); + free(i->path); i->path = new_path; new_path = NULL; @@ -468,6 +491,22 @@ int image_read_only(Image *i, bool b) { r = btrfs_subvol_set_read_only(i->path, b); if (r < 0) return r; + + break; + + case IMAGE_DIRECTORY: + /* For simple directory trees we cannot use the access + mode of the top-level directory, since it has an + effect on the container itself. However, we can + use the "immutable" flag, to at least make the + top-level directory read-only. It's not as good as + a read-only subvolume, but at least something, and + we can read the value back.*/ + + r = chattr_path(i->path, b, FS_IMMUTABLE_FL); + if (r < 0) + return r; + break; case IMAGE_GPT: { @@ -487,7 +526,6 @@ int image_read_only(Image *i, bool b) { break; } - case IMAGE_DIRECTORY: default: return -ENOTSUP; } diff --git a/src/shared/util.c b/src/shared/util.c index 9fd2d89556..857bb1b726 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -7791,9 +7791,31 @@ int chattr_path(const char *p, bool b, unsigned mask) { if (mask == 0) return 0; - fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd < 0) return -errno; return chattr_fd(fd, b, mask); } + +int read_attr_fd(int fd, unsigned *ret) { + assert(fd >= 0); + + if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0) + return -errno; + + return 0; +} + +int read_attr_path(const char *p, unsigned *ret) { + _cleanup_close_ int fd = -1; + + assert(p); + assert(ret); + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return read_attr_fd(fd, ret); +} diff --git a/src/shared/util.h b/src/shared/util.h index e58a17a825..5d9637efc0 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -1077,4 +1077,7 @@ int same_fd(int a, int b); int chattr_fd(int fd, bool b, unsigned mask); int chattr_path(const char *p, bool b, unsigned mask); +int read_attr_fd(int fd, unsigned *ret); +int read_attr_path(const char *p, unsigned *ret); + #define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) |