From ee3467c6bd63d38c43ec15e485bd07706483a09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 10:52:30 +0100 Subject: [PATCH 01/22] test-mount-util: move test_path_is_mount_point here path-util.c and mount-util.c are intertwined, but path_is_mount_point() is defined in mount-util.c. No functional difference. --- src/test/test-mount-util.c | 140 +++++++++++++++++++++++++++++++++++++ src/test/test-path-util.c | 139 ------------------------------------ 2 files changed, 140 insertions(+), 139 deletions(-) diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index 2aad7f387c..b3434bd12c 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -26,8 +26,10 @@ #include "fileio.h" #include "hashmap.h" #include "log.h" +#include "log.h" #include "mount-util.h" #include "path-util.h" +#include "rm-rf.h" #include "string-util.h" static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) { @@ -100,6 +102,143 @@ static void test_mnt_id(void) { hashmap_free_free(h); } +static void test_path_is_mount_point(void) { + int fd; + char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX"; + _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL; + _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL; + _cleanup_free_ char *dir2 = NULL, *dir2file = NULL; + + assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("/", NULL, 0) > 0); + + assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("/proc", NULL, 0) > 0); + + assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0); + + assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("/sys", NULL, 0) > 0); + + /* we'll create a hierarchy of different kinds of dir/file/link + * layouts: + * + * /file1, /file2 + * /link1 -> file1, /link2 -> file2 + * /dir1/ + * /dir1/file + * /dirlink1 -> dir1 + * /dirlink1file -> dirlink1/file + * /dir2/ + * /dir2/file + */ + + /* file mountpoints */ + assert_se(mkdtemp(tmp_dir) != NULL); + file1 = path_join(NULL, tmp_dir, "file1"); + assert_se(file1); + file2 = path_join(NULL, tmp_dir, "file2"); + assert_se(file2); + fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); + assert_se(fd > 0); + close(fd); + fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); + assert_se(fd > 0); + close(fd); + link1 = path_join(NULL, tmp_dir, "link1"); + assert_se(link1); + assert_se(symlink("file1", link1) == 0); + link2 = path_join(NULL, tmp_dir, "link2"); + assert_se(link1); + assert_se(symlink("file2", link2) == 0); + + assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(file1, NULL, 0) == 0); + assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(link1, NULL, 0) == 0); + + /* directory mountpoints */ + dir1 = path_join(NULL, tmp_dir, "dir1"); + assert_se(dir1); + assert_se(mkdir(dir1, 0755) == 0); + dirlink1 = path_join(NULL, tmp_dir, "dirlink1"); + assert_se(dirlink1); + assert_se(symlink("dir1", dirlink1) == 0); + dirlink1file = path_join(NULL, tmp_dir, "dirlink1file"); + assert_se(dirlink1file); + assert_se(symlink("dirlink1/file", dirlink1file) == 0); + dir2 = path_join(NULL, tmp_dir, "dir2"); + assert_se(dir2); + assert_se(mkdir(dir2, 0755) == 0); + + assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(dir1, NULL, 0) == 0); + assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0); + + /* file in subdirectory mountpoints */ + dir1file = path_join(NULL, dir1, "file"); + assert_se(dir1file); + fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); + assert_se(fd > 0); + close(fd); + + assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(dir1file, NULL, 0) == 0); + assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0); + + /* these tests will only work as root */ + if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) { + int rt, rf, rlt, rlf, rl1t, rl1f; + + /* files */ + /* capture results in vars, to avoid dangling mounts on failure */ + rf = path_is_mount_point(file2, NULL, 0); + rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW); + rlf = path_is_mount_point(link2, NULL, 0); + rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW); + + assert_se(umount(file2) == 0); + + assert_se(rf == 1); + assert_se(rt == 1); + assert_se(rlf == 0); + assert_se(rlt == 1); + + /* dirs */ + dir2file = path_join(NULL, dir2, "file"); + assert_se(dir2file); + fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); + assert_se(fd > 0); + close(fd); + + assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0); + + rf = path_is_mount_point(dir1, NULL, 0); + rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW); + rlf = path_is_mount_point(dirlink1, NULL, 0); + rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW); + /* its parent is a mount point, but not /file itself */ + rl1f = path_is_mount_point(dirlink1file, NULL, 0); + rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW); + + assert_se(umount(dir1) == 0); + + assert_se(rf == 1); + assert_se(rt == 1); + assert_se(rlf == 0); + assert_se(rlt == 1); + assert_se(rl1f == 0); + assert_se(rl1t == 0); + + } else + printf("Skipping bind mount file test: %m\n"); + + assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -113,6 +252,7 @@ int main(int argc, char *argv[]) { test_mount_propagation_flags(" ", -EINVAL, 0); test_mnt_id(); + test_path_is_mount_point(); return 0; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 68fe941f15..fed829f1f7 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -19,7 +19,6 @@ ***/ #include -#include #include #include "alloc-util.h" @@ -376,143 +375,6 @@ static void test_prefix_root(void) { test_prefix_root_one("/foo///", "//bar", "/foo/bar"); } -static void test_path_is_mount_point(void) { - int fd; - char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX"; - _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL; - _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL; - _cleanup_free_ char *dir2 = NULL, *dir2file = NULL; - - assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/", NULL, 0) > 0); - - assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/proc", NULL, 0) > 0); - - assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0); - - assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/sys", NULL, 0) > 0); - - /* we'll create a hierarchy of different kinds of dir/file/link - * layouts: - * - * /file1, /file2 - * /link1 -> file1, /link2 -> file2 - * /dir1/ - * /dir1/file - * /dirlink1 -> dir1 - * /dirlink1file -> dirlink1/file - * /dir2/ - * /dir2/file - */ - - /* file mountpoints */ - assert_se(mkdtemp(tmp_dir) != NULL); - file1 = path_join(NULL, tmp_dir, "file1"); - assert_se(file1); - file2 = path_join(NULL, tmp_dir, "file2"); - assert_se(file2); - fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); - assert_se(fd > 0); - close(fd); - fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); - assert_se(fd > 0); - close(fd); - link1 = path_join(NULL, tmp_dir, "link1"); - assert_se(link1); - assert_se(symlink("file1", link1) == 0); - link2 = path_join(NULL, tmp_dir, "link2"); - assert_se(link1); - assert_se(symlink("file2", link2) == 0); - - assert_se(path_is_mount_point(file1, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(file1, NULL, 0) == 0); - assert_se(path_is_mount_point(link1, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(link1, NULL, 0) == 0); - - /* directory mountpoints */ - dir1 = path_join(NULL, tmp_dir, "dir1"); - assert_se(dir1); - assert_se(mkdir(dir1, 0755) == 0); - dirlink1 = path_join(NULL, tmp_dir, "dirlink1"); - assert_se(dirlink1); - assert_se(symlink("dir1", dirlink1) == 0); - dirlink1file = path_join(NULL, tmp_dir, "dirlink1file"); - assert_se(dirlink1file); - assert_se(symlink("dirlink1/file", dirlink1file) == 0); - dir2 = path_join(NULL, tmp_dir, "dir2"); - assert_se(dir2); - assert_se(mkdir(dir2, 0755) == 0); - - assert_se(path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dir1, NULL, 0) == 0); - assert_se(path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dirlink1, NULL, 0) == 0); - - /* file in subdirectory mountpoints */ - dir1file = path_join(NULL, dir1, "file"); - assert_se(dir1file); - fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); - assert_se(fd > 0); - close(fd); - - assert_se(path_is_mount_point(dir1file, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dir1file, NULL, 0) == 0); - assert_se(path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dirlink1file, NULL, 0) == 0); - - /* these tests will only work as root */ - if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) { - int rt, rf, rlt, rlf, rl1t, rl1f; - - /* files */ - /* capture results in vars, to avoid dangling mounts on failure */ - rf = path_is_mount_point(file2, NULL, 0); - rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW); - rlf = path_is_mount_point(link2, NULL, 0); - rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW); - - assert_se(umount(file2) == 0); - - assert_se(rf == 1); - assert_se(rt == 1); - assert_se(rlf == 0); - assert_se(rlt == 1); - - /* dirs */ - dir2file = path_join(NULL, dir2, "file"); - assert_se(dir2file); - fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664); - assert_se(fd > 0); - close(fd); - - assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0); - - rf = path_is_mount_point(dir1, NULL, 0); - rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW); - rlf = path_is_mount_point(dirlink1, NULL, 0); - rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW); - /* its parent is a mount point, but not /file itself */ - rl1f = path_is_mount_point(dirlink1file, NULL, 0); - rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW); - - assert_se(umount(dir1) == 0); - - assert_se(rf == 1); - assert_se(rt == 1); - assert_se(rlf == 0); - assert_se(rlt == 1); - assert_se(rl1f == 0); - assert_se(rl1t == 0); - - } else - printf("Skipping bind mount file test: %m\n"); - - assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); -} - static void test_file_in_same_dir(void) { char *t; @@ -621,7 +483,6 @@ int main(int argc, char **argv) { test_strv_resolve(); test_path_startswith(); test_prefix_root(); - test_path_is_mount_point(); test_file_in_same_dir(); test_filename_is_valid(); test_hidden_or_backup_file(); From 553e15f21bd7b1ecb709edfb5686d5768fe942f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 09:37:15 +0100 Subject: [PATCH 02/22] Add a little helper to make /sys/dev/block/major:minor paths --- src/basic/btrfs-util.c | 12 ++++----- src/basic/device-nodes.h | 8 ++++++ src/basic/util.c | 53 ++++++++++++-------------------------- src/shared/dissect-image.c | 7 ++--- 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index b7e237fc0d..ac96e63531 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -42,6 +42,7 @@ #include "btrfs-util.h" #include "chattr-util.h" #include "copy.h" +#include "device-nodes.h" #include "fd-util.h" #include "fileio.h" #include "io-util.h" @@ -910,7 +911,8 @@ int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, u int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) { struct btrfs_ioctl_vol_args args = {}; - _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL; + char p[SYS_BLOCK_PATH_MAX("/loop/backing_file")]; + _cleanup_free_ char *backing = NULL; _cleanup_close_ int loop_fd = -1, backing_fd = -1; struct stat st; dev_t dev = 0; @@ -930,8 +932,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) { if (r == 0) return -ENODEV; - if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0) - return -ENOMEM; + xsprintf_sys_block_path(p, "/loop/backing_file", dev); r = read_one_line_file(p, &backing); if (r == -ENOENT) return -ENODEV; @@ -955,9 +956,8 @@ int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) { if (grow_only && new_size < (uint64_t) st.st_size) return -EINVAL; - if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0) - return -ENOMEM; - loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY); + xsprintf_sys_block_path(p, NULL, dev); + loop_fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY); if (loop_fd < 0) return -errno; diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h index 1e09bdc830..9f3c6d60c0 100644 --- a/src/basic/device-nodes.h +++ b/src/basic/device-nodes.h @@ -23,5 +23,13 @@ #include #include +#include "macro.h" +#include "stdio-util.h" + int encode_devnode_name(const char *str, char *str_enc, size_t len); int whitelisted_char_for_devnode(char c, const char *additional); + +#define SYS_BLOCK_PATH_MAX(suffix) \ + (strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix)) +#define xsprintf_sys_block_path(buf, suffix, devno) \ + xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix)) diff --git a/src/basic/util.c b/src/basic/util.c index 0c278ab20e..f61d9013e6 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -39,6 +39,7 @@ #include "build.h" #include "cgroup-util.h" #include "def.h" +#include "device-nodes.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" @@ -118,63 +119,42 @@ int socket_from_display(const char *display, char **path) { } int block_get_whole_disk(dev_t d, dev_t *ret) { - char *p, *s; + char p[SYS_BLOCK_PATH_MAX("/partition")]; + _cleanup_free_ char *s = NULL; int r; unsigned n, m; assert(ret); /* If it has a queue this is good enough for us */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { + xsprintf_sys_block_path(p, "/queue", d); + if (access(p, F_OK) >= 0) { *ret = d; return 0; } /* If it is a partition find the originating device */ - if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r < 0) + xsprintf_sys_block_path(p, "/partition", d); + if (access(p, F_OK) < 0) return -ENOENT; /* Get parent dev_t */ - if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) - return -ENOMEM; - + xsprintf_sys_block_path(p, "/../dev", d); r = read_one_line_file(p, &s); - free(p); - if (r < 0) return r; r = sscanf(s, "%u:%u", &m, &n); - free(s); - if (r != 2) return -EINVAL; /* Only return this if it is really good enough for us. */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) - return -ENOMEM; + xsprintf_sys_block_path(p, "/queue", makedev(m, n)); + if (access(p, F_OK) < 0) + return -ENOENT; - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = makedev(m, n); - return 0; - } - - return -ENOENT; + *ret = makedev(m, n); + return 0; } bool kexec_loaded(void) { @@ -749,7 +729,8 @@ int get_block_device(const char *path, dev_t *dev) { int get_block_device_harder(const char *path, dev_t *dev) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *p = NULL, *t = NULL; + _cleanup_free_ char *t = NULL; + char p[SYS_BLOCK_PATH_MAX("/slaves")]; struct dirent *de, *found = NULL; const char *q; unsigned maj, min; @@ -767,9 +748,7 @@ int get_block_device_harder(const char *path, dev_t *dev) { if (r <= 0) return r; - if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) - return -ENOMEM; - + xsprintf_sys_block_path(p, "/slaves", dt); d = opendir(p); if (!d) { if (errno == ENOENT) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 219ba8cb0b..1a7ccbe2b2 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -33,6 +33,7 @@ #include "blkid-util.h" #include "copy.h" #include "def.h" +#include "device-nodes.h" #include "dissect-image.h" #include "fd-util.h" #include "fileio.h" @@ -652,7 +653,7 @@ DissectedImage* dissected_image_unref(DissectedImage *m) { } static int is_loop_device(const char *path) { - char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")]; + char s[SYS_BLOCK_PATH_MAX("/../loop/")]; struct stat st; assert(path); @@ -663,13 +664,13 @@ static int is_loop_device(const char *path) { if (!S_ISBLK(st.st_mode)) return -ENOTBLK; - xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev)); + xsprintf_sys_block_path(s, "/loop/", st.st_dev); if (access(s, F_OK) < 0) { if (errno != ENOENT) return -errno; /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */ - xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev)); + xsprintf_sys_block_path(s, "/../loop/", st.st_dev); if (access(s, F_OK) < 0) return errno == ENOENT ? false : -errno; } From c67f84b0254640168977ff52f3a961db407f472e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 11:30:40 +0100 Subject: [PATCH 03/22] Add a helper for /dev/block/major:minor paths --- src/basic/device-nodes.h | 5 +++++ src/shared/bootspec.c | 8 +++----- src/udev/udev-node.c | 19 +++++++++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h index 9f3c6d60c0..6136526f84 100644 --- a/src/basic/device-nodes.h +++ b/src/basic/device-nodes.h @@ -33,3 +33,8 @@ int whitelisted_char_for_devnode(char c, const char *additional); (strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix)) #define xsprintf_sys_block_path(buf, suffix, devno) \ xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix)) + +#define DEV_NUM_PATH_MAX \ + (strlen("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t)) +#define xsprintf_dev_num_path(buf, type, devno) \ + xsprintf(buf, "/dev/%s/%u:%u", type, major(devno), minor(devno)) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 9f80db068d..aa722c304a 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -25,6 +25,7 @@ #include "bootspec.h" #include "conf-files.h" #include "def.h" +#include "device-nodes.h" #include "efivars.h" #include "fd-util.h" #include "fileio.h" @@ -420,7 +421,7 @@ static int verify_esp( sd_id128_t *ret_uuid) { _cleanup_blkid_free_probe_ blkid_probe b = NULL; - _cleanup_free_ char *t = NULL; + char t[DEV_NUM_PATH_MAX]; uint64_t pstart = 0, psize = 0; struct stat st, st2; const char *v, *t2; @@ -478,10 +479,7 @@ static int verify_esp( if (detect_container() > 0 || geteuid() != 0) goto finish; - r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); - if (r < 0) - return log_oom(); - + xsprintf_dev_num_path(t, "block", st.st_dev); errno = 0; b = blkid_new_probe_from_filename(t); if (!b) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index dcea890d7f..ef893247ad 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -25,6 +25,7 @@ #include #include +#include "device-nodes.h" #include "dirent-util.h" #include "format-util.h" #include "fs-util.h" @@ -337,7 +338,7 @@ out: void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid, struct udev_list *seclabel_list) { - char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)]; + char filename[DEV_NUM_PATH_MAX]; struct udev_list_entry *list_entry; log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT, @@ -347,10 +348,9 @@ void udev_node_add(struct udev_device *dev, bool apply, return; /* always add /dev/{block,char}/$major:$minor */ - xsprintf(filename, "/dev/%s/%u:%u", - streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", - major(udev_device_get_devnum(dev)), - minor(udev_device_get_devnum(dev))); + xsprintf_dev_num_path(filename, + streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", + udev_device_get_devnum(dev)); node_symlink(dev, udev_device_get_devnode(dev), filename); /* create/update symlinks, add symlinks to name index */ @@ -360,16 +360,15 @@ void udev_node_add(struct udev_device *dev, bool apply, void udev_node_remove(struct udev_device *dev) { struct udev_list_entry *list_entry; - char filename[sizeof("/dev/block/:") + 2*DECIMAL_STR_MAX(unsigned)]; + char filename[DEV_NUM_PATH_MAX]; /* remove/update symlinks, remove symlinks from name index */ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) link_update(dev, udev_list_entry_get_name(list_entry), false); /* remove /dev/{block,char}/$major:$minor */ - xsprintf(filename, "/dev/%s/%u:%u", - streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", - major(udev_device_get_devnum(dev)), - minor(udev_device_get_devnum(dev))); + xsprintf_dev_num_path(filename, + streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", + udev_device_get_devnum(dev)); unlink(filename); } From ca4d708dc4092268520aee419cbd696f1b0f4558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 1 Nov 2017 16:59:30 +0100 Subject: [PATCH 04/22] test-string-util: add another test for stripping slashes I wrote this for my own "strip_trailing_chars" function, which was in the meanwhile obsoleted by "delete_trailing_chars". Let's just keep the test. --- src/test/test-string-util.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index ed777e1801..7a14b8efd3 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -320,6 +320,19 @@ static void test_delete_trailing_chars(void) { assert_se(s == input3); } +static void test_delete_trailing_slashes(void) { + char s1[] = "foobar//", + s2[] = "foobar/", + s3[] = "foobar", + s4[] = ""; + + assert_se(streq(delete_trailing_chars(s1, "_"), "foobar//")); + assert_se(streq(delete_trailing_chars(s1, "/"), "foobar")); + assert_se(streq(delete_trailing_chars(s2, "/"), "foobar")); + assert_se(streq(delete_trailing_chars(s3, "/"), "foobar")); + assert_se(streq(delete_trailing_chars(s4, "/"), "")); +} + static void test_skip_leading_chars(void) { char input1[] = " \n \r k \n \r ", input2[] = "kkkkthiskkkiskkkaktestkkk", @@ -399,6 +412,7 @@ int main(int argc, char *argv[]) { test_endswith_no_case(); test_delete_chars(); test_delete_trailing_chars(); + test_delete_trailing_slashes(); test_skip_leading_chars(); test_in_charset(); test_split_pair(); From b12d25a8d631af00b200e7aa9dbba6ba4a4a59ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 11:08:30 +0100 Subject: [PATCH 05/22] util-lib: use trailing slash in chase_symlinks, fd_is_mount_point, path_is_mount_point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel will reply with -ENOTDIR when we try to access a non-directory under a name which ends with a slash. But our functions would strip the trailing slash under various circumstances. Keep the trailing slash, so that path_is_mount_point("/path/to/file/") return -ENOTDIR when /path/to/file/ is a file. Tests are added for this change in behaviour. Also, when called with a trailing slash, path_is_mount_point() would get "" from basename(), and call name_to_handle_at(3, "", ...), and always return -ENOENT. Now it'll return -ENOTDIR if the mount point is a file, and true if it is a directory and a mount point. v2: - use strip_trailing_chars() v3: - instead of stripping trailing chars(), do the opposite — preserve them. --- src/basic/fs-util.c | 13 ++++++++-- src/basic/mount-util.c | 3 ++- src/basic/path-util.c | 28 ++++++++++++++++++++++ src/basic/path-util.h | 1 + src/test/test-fs-util.c | 49 +++++++++++++++++++++++++++----------- src/test/test-mount-util.c | 24 ++++++++++++++++++- src/test/test-path-util.c | 15 ++++++++++++ 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index b5fd95ae8e..e74e75a41c 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -661,10 +661,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, todo += m; - /* Just a single slash? Then we reached the end. */ - if (isempty(first) || path_equal(first, "/")) + /* Empty? Then we reached the end. */ + if (isempty(first)) break; + /* Just a single slash? Then we reached the end. */ + if (path_equal(first, "/")) { + /* Preserve the trailing slash */ + if (!strextend(&done, "/", NULL)) + return -ENOMEM; + + break; + } + /* Just a dot? Then let's eat this up. */ if (path_equal(first, "/.")) continue; diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index e32502308a..51c84491be 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -277,6 +277,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) { int r; assert(t); + assert((flags & ~AT_SYMLINK_FOLLOW) == 0); if (path_equal(t, "/")) return 1; @@ -301,7 +302,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) { if (fd < 0) return -errno; - return fd_is_mount_point(fd, basename(t), flags); + return fd_is_mount_point(fd, last_path_component(t), flags); } int path_get_mnt_id(const char *path, int *ret) { diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 883856abc2..059e4fcf6a 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -703,6 +703,34 @@ char* dirname_malloc(const char *path) { return dir2; } +const char *last_path_component(const char *path) { + /* Finds the last component of the path, preserving the + * optional trailing slash that signifies a directory. + * a/b/c → c + * a/b/c/ → c/ + * / → / + * // → / + * /foo/a → a + * /foo/a/ → a/ + * This is different than basename, which returns "" when + * a trailing slash is present. + */ + + unsigned l, k; + + l = k = strlen(path); + while (k > 0 && path[k-1] == '/') + k--; + + if (k == 0) /* the root directory */ + return path + l - 1; + + while (k > 0 && path[k-1] != '/') + k--; + + return path + k; +} + bool filename_is_valid(const char *p) { const char *e; diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 9bd783eaf4..f79cdf928e 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -130,6 +130,7 @@ char *prefix_root(const char *root, const char *path); int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); char* dirname_malloc(const char *path); +const char *last_path_component(const char *path); bool filename_is_valid(const char *p) _pure_; bool path_is_normalized(const char *p) _pure_; diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 3c958917bb..83ddc398b8 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -35,7 +35,7 @@ static void test_chase_symlinks(void) { _cleanup_free_ char *result = NULL; char temp[] = "/tmp/test-chase.XXXXXX"; - const char *top, *p, *q; + const char *top, *p, *pslash, *q, *qslash; int r; assert_se(mkdtemp(temp)); @@ -66,93 +66,114 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/usr")); - result = mfree(result); + + pslash = strjoina(p, "/"); + r = chase_symlinks(pslash, NULL, 0, &result); + assert_se(r > 0); + assert_se(path_equal(result, "/usr/")); + result = mfree(result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r == -ENOENT); + r = chase_symlinks(pslash, temp, 0, &result); + assert_se(r == -ENOENT); + q = strjoina(temp, "/usr"); r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, q)); + result = mfree(result); + + qslash = strjoina(q, "/"); + + r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result); + assert_se(r == 0); + assert_se(path_equal(result, qslash)); + result = mfree(result); assert_se(mkdir(q, 0700) >= 0); - result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, q)); + result = mfree(result); + + r = chase_symlinks(pslash, temp, 0, &result); + assert_se(r > 0); + assert_se(path_equal(result, qslash)); + result = mfree(result); p = strjoina(temp, "/slash"); assert_se(symlink("/", p) >= 0); - result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/")); - result = mfree(result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, temp)); + result = mfree(result); /* Paths that would "escape" outside of the "root" */ p = strjoina(temp, "/6dots"); assert_se(symlink("../../..", p) >= 0); - result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, temp)); + result = mfree(result); p = strjoina(temp, "/6dotsusr"); assert_se(symlink("../../../usr", p) >= 0); - result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, q)); + result = mfree(result); p = strjoina(temp, "/top/8dotsusr"); assert_se(symlink("../../../../usr", p) >= 0); - result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, q)); + result = mfree(result); /* Paths that contain repeated slashes */ p = strjoina(temp, "/slashslash"); assert_se(symlink("///usr///", p) >= 0); - result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/usr")); - result = mfree(result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, q)); + result = mfree(result); /* Paths using . */ - result = mfree(result); r = chase_symlinks("/etc/./.././", NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/")); - result = mfree(result); + r = chase_symlinks("/etc/./.././", "/etc", 0, &result); assert_se(r > 0 && path_equal(result, "/etc")); - result = mfree(result); + r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result); assert_se(r == -ENOTDIR); + result = mfree(result); /* Path that loops back to self */ - result = mfree(result); p = strjoina(temp, "/recursive-symlink"); assert_se(symlink("recursive-symlink", p) >= 0); r = chase_symlinks(p, NULL, 0, &result); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index b3434bd12c..09a624842c 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -111,15 +111,23 @@ static void test_path_is_mount_point(void) { assert_se(path_is_mount_point("/", NULL, AT_SYMLINK_FOLLOW) > 0); assert_se(path_is_mount_point("/", NULL, 0) > 0); + assert_se(path_is_mount_point("//", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("//", NULL, 0) > 0); assert_se(path_is_mount_point("/proc", NULL, AT_SYMLINK_FOLLOW) > 0); assert_se(path_is_mount_point("/proc", NULL, 0) > 0); + assert_se(path_is_mount_point("/proc/", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("/proc/", NULL, 0) > 0); assert_se(path_is_mount_point("/proc/1", NULL, AT_SYMLINK_FOLLOW) == 0); assert_se(path_is_mount_point("/proc/1", NULL, 0) == 0); + assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0); + assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0); assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0); assert_se(path_is_mount_point("/sys", NULL, 0) > 0); + assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0); + assert_se(path_is_mount_point("/sys/", NULL, 0) > 0); /* we'll create a hierarchy of different kinds of dir/file/link * layouts: @@ -191,12 +199,21 @@ static void test_path_is_mount_point(void) { /* these tests will only work as root */ if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) { - int rt, rf, rlt, rlf, rl1t, rl1f; + int rf, rt, rdf, rdt, rlf, rlt, rl1f, rl1t; + const char *file2d; /* files */ /* capture results in vars, to avoid dangling mounts on failure */ + log_info("%s: %s", __func__, file2); rf = path_is_mount_point(file2, NULL, 0); rt = path_is_mount_point(file2, NULL, AT_SYMLINK_FOLLOW); + + file2d = strjoina(file2, "/"); + log_info("%s: %s", __func__, file2d); + rdf = path_is_mount_point(file2d, NULL, 0); + rdt = path_is_mount_point(file2d, NULL, AT_SYMLINK_FOLLOW); + + log_info("%s: %s", __func__, link2); rlf = path_is_mount_point(link2, NULL, 0); rlt = path_is_mount_point(link2, NULL, AT_SYMLINK_FOLLOW); @@ -204,6 +221,8 @@ static void test_path_is_mount_point(void) { assert_se(rf == 1); assert_se(rt == 1); + assert_se(rdf == -ENOTDIR); + assert_se(rdt == -ENOTDIR); assert_se(rlf == 0); assert_se(rlt == 1); @@ -216,10 +235,13 @@ static void test_path_is_mount_point(void) { assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0); + log_info("%s: %s", __func__, dir1); rf = path_is_mount_point(dir1, NULL, 0); rt = path_is_mount_point(dir1, NULL, AT_SYMLINK_FOLLOW); + log_info("%s: %s", __func__, dirlink1); rlf = path_is_mount_point(dirlink1, NULL, 0); rlt = path_is_mount_point(dirlink1, NULL, AT_SYMLINK_FOLLOW); + log_info("%s: %s", __func__, dirlink1file); /* its parent is a mount point, but not /file itself */ rl1f = path_is_mount_point(dirlink1file, NULL, 0); rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW); diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index fed829f1f7..21d52f5d6e 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -399,6 +399,20 @@ static void test_file_in_same_dir(void) { free(t); } +static void test_last_path_component(void) { + assert_se(streq(last_path_component("a/b/c"), "c")); + assert_se(streq(last_path_component("a/b/c/"), "c/")); + assert_se(streq(last_path_component("/"), "/")); + assert_se(streq(last_path_component("//"), "/")); + assert_se(streq(last_path_component("///"), "/")); + assert_se(streq(last_path_component("."), ".")); + assert_se(streq(last_path_component("./."), ".")); + assert_se(streq(last_path_component("././"), "./")); + assert_se(streq(last_path_component("././/"), ".//")); + assert_se(streq(last_path_component("/foo/a"), "a")); + assert_se(streq(last_path_component("/foo/a/"), "a/")); +} + static void test_filename_is_valid(void) { char foo[FILENAME_MAX+2]; int i; @@ -484,6 +498,7 @@ int main(int argc, char **argv) { test_path_startswith(); test_prefix_root(); test_file_in_same_dir(); + test_last_path_component(); test_filename_is_valid(); test_hidden_or_backup_file(); test_skip_dev_prefix(); From a66fee2e970bd3e6ac35db2dd6e377c83093b10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 13:02:10 +0100 Subject: [PATCH 06/22] util-lib: rename fd_check_fstype to fd_is_fs_type Let's use "is" and "fs_type" for consistency with "is_fs_type". "check" is also more ambiguous than "is". --- src/basic/fs-util.c | 2 +- src/basic/stat-util.c | 4 ++-- src/basic/stat-util.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index e74e75a41c..475400177a 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -735,7 +735,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_NO_AUTOFS) && - fd_check_fstype(child, AUTOFS_SUPER_MAGIC) > 0) + fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) return -EREMOTE; if (S_ISLNK(st.st_mode)) { diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index ea554f7b73..483c3363af 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -193,7 +193,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { return F_TYPE_EQUAL(s->f_type, magic_value); } -int fd_check_fstype(int fd, statfs_f_type_t magic_value) { +int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { struct statfs s; if (fstatfs(fd, &s) < 0) @@ -209,7 +209,7 @@ int path_check_fstype(const char *path, statfs_f_type_t magic_value) { if (fd < 0) return -errno; - return fd_check_fstype(fd, magic_value); + return fd_is_fs_type(fd, magic_value); } bool is_temporary_fs(const struct statfs *s) { diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 615315f306..931f9eb7c4 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -57,7 +57,7 @@ int files_same(const char *filea, const char *fileb, int flags); typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_check_fstype(int fd, statfs_f_type_t magic_value); +int fd_is_fs_type(int fd, statfs_f_type_t magic_value); int path_check_fstype(const char *path, statfs_f_type_t magic_value); bool is_temporary_fs(const struct statfs *s) _pure_; From 40fd52f28d464d357376d8c641f5a80fdbda59a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 31 Oct 2017 16:13:05 +0100 Subject: [PATCH 07/22] util-lib: rename path_check_fstype to path_is_fs_type --- src/basic/stat-util.c | 2 +- src/basic/stat-util.h | 2 +- src/nspawn/nspawn-mount.c | 2 +- src/test/test-stat-util.c | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 483c3363af..c6b8507e9d 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -202,7 +202,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } -int path_check_fstype(const char *path, statfs_f_type_t magic_value) { +int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { _cleanup_close_ int fd = -1; fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 931f9eb7c4..8b8d128121 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -58,7 +58,7 @@ typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; int fd_is_fs_type(int fd, statfs_f_type_t magic_value); -int path_check_fstype(const char *path, statfs_f_type_t magic_value); +int path_is_fs_type(const char *path, statfs_f_type_t magic_value); bool is_temporary_fs(const struct statfs *s) _pure_; int fd_is_temporary_fs(int fd); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index e6a92411ad..920e114718 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -405,7 +405,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { unsigned long extra_flags = 0; top = prefix_roota(dest, "/sys"); - r = path_check_fstype(top, SYSFS_MAGIC); + r = path_is_fs_type(top, SYSFS_MAGIC); if (r < 0) return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top); /* /sys might already be mounted as sysfs by the outer child in the diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index b58f31dd53..c606425d2d 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -72,16 +72,16 @@ static void test_path_is_os_tree(void) { assert_se(path_is_os_tree("/idontexist") == -ENOENT); } -static void test_path_check_fstype(void) { +static void test_path_is_fs_type(void) { /* run might not be a mount point in build chroots */ if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) { - assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0); - assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_is_fs_type("/run", TMPFS_MAGIC) > 0); + assert_se(path_is_fs_type("/run", BTRFS_SUPER_MAGIC) == 0); } - assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0); - assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); - assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); - assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT); + assert_se(path_is_fs_type("/proc", PROC_SUPER_MAGIC) > 0); + assert_se(path_is_fs_type("/proc", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_is_fs_type("/proc", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_is_fs_type("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT); } static void test_path_is_temporary_fs(void) { @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { test_files_same(); test_is_symlink(); test_path_is_os_tree(); - test_path_check_fstype(); + test_path_is_fs_type(); test_path_is_temporary_fs(); return 0; From 294bd454701bcb41012466c8a54d8df12357e611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 2 Nov 2017 09:16:47 +0100 Subject: [PATCH 08/22] util-lib: add cleanup function for crypt_free --- src/basic/crypt-util.h | 27 ++++++++++++++++++++++++++ src/basic/meson.build | 1 + src/cryptsetup/cryptsetup.c | 6 ++---- src/shared/dissect-image.c | 36 +++++++++++++---------------------- src/veritysetup/veritysetup.c | 7 ++----- 5 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 src/basic/crypt-util.h diff --git a/src/basic/crypt-util.h b/src/basic/crypt-util.h new file mode 100644 index 0000000000..b95eb9a4e7 --- /dev/null +++ b/src/basic/crypt-util.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + 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 . +***/ + +#if HAVE_LIBCRYPTSETUP +#include + +#include "macro.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free); +#endif diff --git a/src/basic/meson.build b/src/basic/meson.build index bf11757b74..68064ab693 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -61,6 +61,7 @@ basic_sources_plain = files(''' copy.h cpu-set-util.c cpu-set-util.h + crypt-util.h def.h device-nodes.c device-nodes.h diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index b19d03e9f0..38468023cb 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "ask-password-api.h" +#include "crypt-util.h" #include "device-util.h" #include "escape.h" #include "fileio.h" @@ -604,7 +605,7 @@ static int help(void) { } int main(int argc, char *argv[]) { - struct crypt_device *cd = NULL; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r = -EINVAL; if (argc <= 1) { @@ -766,9 +767,6 @@ int main(int argc, char *argv[]) { r = 0; finish: - if (cd) - crypt_free(cd); - free(arg_cipher); free(arg_hash); free(arg_header); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 1a7ccbe2b2..2714b3921c 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -24,6 +24,7 @@ #define CRYPT_LUKS NULL #endif #endif + #include #include #include @@ -32,6 +33,7 @@ #include "ask-password-api.h" #include "blkid-util.h" #include "copy.h" +#include "crypt-util.h" #include "def.h" #include "device-nodes.h" #include "dissect-image.h" @@ -850,7 +852,7 @@ static int decrypt_partition( DecryptedImage *d) { _cleanup_free_ char *node = NULL, *name = NULL; - struct crypt_device *cd; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; assert(m); @@ -877,37 +879,28 @@ static int decrypt_partition( return log_debug_errno(r, "Failed to initialize dm-crypt: %m"); r = crypt_load(cd, CRYPT_LUKS, NULL); - if (r < 0) { - log_debug_errno(r, "Failed to load LUKS metadata: %m"); - goto fail; - } + if (r < 0) + return log_debug_errno(r, "Failed to load LUKS metadata: %m"); r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) | ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0)); - if (r < 0) + if (r < 0) { log_debug_errno(r, "Failed to activate LUKS device: %m"); - if (r == -EPERM) { - r = -EKEYREJECTED; - goto fail; + return r == -EPERM ? -EKEYREJECTED : r; } - if (r < 0) - goto fail; d->decrypted[d->n_decrypted].name = name; name = NULL; d->decrypted[d->n_decrypted].device = cd; + cd = NULL; d->n_decrypted++; m->decrypted_node = node; node = NULL; return 0; - -fail: - crypt_free(cd); - return r; } static int verity_partition( @@ -919,7 +912,7 @@ static int verity_partition( DecryptedImage *d) { _cleanup_free_ char *node = NULL, *name = NULL; - struct crypt_device *cd; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; assert(m); @@ -949,30 +942,27 @@ static int verity_partition( r = crypt_load(cd, CRYPT_VERITY, NULL); if (r < 0) - goto fail; + return r; r = crypt_set_data_device(cd, m->node); if (r < 0) - goto fail; + return r; r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); if (r < 0) - goto fail; + return r; d->decrypted[d->n_decrypted].name = name; name = NULL; d->decrypted[d->n_decrypted].device = cd; + cd = NULL; d->n_decrypted++; m->decrypted_node = node; node = NULL; return 0; - -fail: - crypt_free(cd); - return r; } #endif diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 18554aa231..2376f1dc2c 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -18,10 +18,10 @@ along with systemd; If not, see . ***/ -#include #include #include +#include "crypt-util.h" #include "log.h" #include "hexdecoct.h" #include "string-util.h" @@ -46,7 +46,7 @@ static void log_glue(int level, const char *msg, void *usrptr) { } int main(int argc, char *argv[]) { - struct crypt_device *cd = NULL; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; if (argc <= 1) { @@ -144,9 +144,6 @@ int main(int argc, char *argv[]) { r = 0; finish: - if (cd) - crypt_free(cd); - free(arg_root_hash); free(arg_data_what); free(arg_hash_what); From 691c2e2e8833c838cdd2f67fcbdcadab01552960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 1 Nov 2017 15:56:25 +0100 Subject: [PATCH 09/22] util-lib: export cryptsetup logging glue function --- src/basic/crypt-util.c | 27 +++++++++++++++++++++++++++ src/basic/crypt-util.h | 2 ++ src/basic/meson.build | 1 + src/cryptsetup/cryptsetup.c | 8 ++------ src/veritysetup/veritysetup.c | 8 ++------ 5 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/basic/crypt-util.c diff --git a/src/basic/crypt-util.c b/src/basic/crypt-util.c new file mode 100644 index 0000000000..193cf65dfc --- /dev/null +++ b/src/basic/crypt-util.c @@ -0,0 +1,27 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + 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 . +***/ + +#if HAVE_LIBCRYPTSETUP +#include "crypt-util.h" +#include "log.h" + +void cryptsetup_log_glue(int level, const char *msg, void *usrptr) { + log_debug("%s", msg); +} +#endif diff --git a/src/basic/crypt-util.h b/src/basic/crypt-util.h index b95eb9a4e7..e8e753275a 100644 --- a/src/basic/crypt-util.h +++ b/src/basic/crypt-util.h @@ -24,4 +24,6 @@ #include "macro.h" DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free); + +void cryptsetup_log_glue(int level, const char *msg, void *usrptr); #endif diff --git a/src/basic/meson.build b/src/basic/meson.build index 68064ab693..a37e279e57 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -61,6 +61,7 @@ basic_sources_plain = files(''' copy.h cpu-set-util.c cpu-set-util.h + crypt-util.c crypt-util.h def.h device-nodes.c diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 38468023cb..84bcdb467d 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -255,10 +255,6 @@ static int parse_options(const char *options) { return 0; } -static void log_glue(int level, const char *msg, void *usrptr) { - log_debug("%s", msg); -} - static int disk_major_minor(const char *path, char **ret) { struct stat st; @@ -667,7 +663,7 @@ int main(int argc, char *argv[]) { goto finish; } - crypt_set_log_callback(cd, log_glue, NULL); + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); status = crypt_status(cd, argv[2]); if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) { @@ -751,7 +747,7 @@ int main(int argc, char *argv[]) { goto finish; } - crypt_set_log_callback(cd, log_glue, NULL); + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); r = crypt_deactivate(cd, argv[2]); if (r < 0) { diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 2376f1dc2c..d3066ca429 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -41,10 +41,6 @@ static int help(void) { return 0; } -static void log_glue(int level, const char *msg, void *usrptr) { - log_debug("%s", msg); -} - int main(int argc, char *argv[]) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; @@ -89,7 +85,7 @@ int main(int argc, char *argv[]) { goto finish; } - crypt_set_log_callback(cd, log_glue, NULL); + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); status = crypt_status(cd, argv[2]); if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) { @@ -127,7 +123,7 @@ int main(int argc, char *argv[]) { goto finish; } - crypt_set_log_callback(cd, log_glue, NULL); + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); r = crypt_deactivate(cd, argv[2]); if (r < 0) { From 80750adb228c57de36a7ef39cb1a7e3c06ca44d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 23 Oct 2017 13:40:38 +0200 Subject: [PATCH 10/22] Add systemd-growfs tool --- meson.build | 8 +++ src/basic/missing.h | 4 ++ src/partition/growfs.c | 156 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 src/partition/growfs.c diff --git a/meson.build b/meson.build index 376c8b84be..94917ca667 100644 --- a/meson.build +++ b/meson.build @@ -1936,6 +1936,14 @@ executable('systemd-fsck', install : true, install_dir : rootlibexecdir) +executable('systemd-growfs', + 'src/partition/growfs.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) + executable('systemd-sleep', 'src/sleep/sleep.c', include_directories : includes, diff --git a/src/basic/missing.h b/src/basic/missing.h index 52c7ce57a0..76cb0a23ac 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -1271,4 +1271,8 @@ struct fib_rule_uid_range { #define AF_VSOCK 40 #endif +#ifndef EXT4_IOC_RESIZE_FS +# define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) +#endif + #include "missing_syscall.h" diff --git a/src/partition/growfs.c b/src/partition/growfs.c new file mode 100644 index 0000000000..e8dc6da449 --- /dev/null +++ b/src/partition/growfs.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-nodes.h" +#include "escape.h" +#include "fd-util.h" +#include "format-util.h" +#include "log.h" +#include "missing.h" +#include "mount-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "strv.h" + +static int resize_ext4(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { + assert((uint64_t) (int) blocksize == blocksize); + + if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0) + return -errno; + + return 0; +} + +static int resize_btrfs(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { + struct btrfs_ioctl_vol_args args = {}; + int r; + + assert((uint64_t) (int) blocksize == blocksize); + + r = snprintf(args.name, sizeof(args.name), "%"PRIu64, numblocks * blocksize); + /* The buffer is large enough for any number to fit... */ + assert((size_t) r < sizeof(args.name)); + + if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0) + return -errno; + + return 0; +} + +int main(int argc, char *argv[]) { + dev_t devno; + _cleanup_close_ int mountfd = -1, devfd = -1; + int blocksize; + uint64_t size, numblocks; + char devpath[DEV_NUM_PATH_MAX], fb[FORMAT_BYTES_MAX]; + struct statfs sfs; + int r; + + if (argc != 2) { + log_error("This program requires one argument (the mountpoint)."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = path_is_mount_point(argv[1], NULL, 0); + if (r < 0) { + log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", argv[1]); + return EXIT_FAILURE; + } + if (r == 0) { + log_error_errno(r, "\"%s\" is not a mount point: %m", argv[1]); + return EXIT_FAILURE; + } + + r = get_block_device(argv[1], &devno); + if (r < 0) { + log_error_errno(r, "Failed to determine block device of \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + mountfd = open(argv[1], O_RDONLY|O_CLOEXEC); + if (mountfd < 0) { + log_error_errno(errno, "Failed to open \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + xsprintf_dev_num_path(devpath, "block", devno); + devfd = open(devpath, O_RDONLY|O_CLOEXEC); + if (devfd < 0) { + log_error_errno(errno, "Failed to open \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (ioctl(devfd, BLKBSZGET, &blocksize) != 0) { + log_error_errno(errno, "Failed to query block size of \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (ioctl(devfd, BLKGETSIZE64, &size) != 0) { + log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath); + return EXIT_FAILURE; + } + + if (size % blocksize != 0) + log_notice("Partition size %"PRIu64" is not a multiple of the blocksize %d," + " ignoring %"PRIu64" bytes", size, blocksize, size % blocksize); + + numblocks = size / blocksize; + + if (fstatfs(mountfd, &sfs) < 0) { + log_error_errno(errno, "Failed to stat file system \"%s\": %m", argv[1]); + return EXIT_FAILURE; + } + + switch(sfs.f_type) { + case EXT4_SUPER_MAGIC: + r = resize_ext4(mountfd, devfd, numblocks, blocksize); + break; + case BTRFS_SUPER_MAGIC: + r = resize_btrfs(mountfd, devfd, numblocks, blocksize); + break; + default: + log_error("Don't know how to resize fs %llx on \"%s\"", + (long long unsigned) sfs.f_type, argv[1]); + return EXIT_FAILURE; + } + + if (r < 0) { + log_error_errno(r, "Failed to resize \"%s\" to %"PRIu64" blocks: %m", + argv[1], numblocks); + return EXIT_FAILURE; + } + + log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).", + argv[1], format_bytes(fb, sizeof fb, size), numblocks, blocksize); + return EXIT_SUCCESS; +} From 76d3e0834aff0e4ffb26897a25f7c32ae3667098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 21 Nov 2017 18:55:07 +0100 Subject: [PATCH 11/22] growfs: do not try to resize btrfs partitions smaller then 256MB This will not work, but the kernel does not give any useful message. --- src/partition/growfs.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/partition/growfs.c b/src/partition/growfs.c index e8dc6da449..bebbda1bf3 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -38,27 +38,35 @@ #include "path-util.h" #include "strv.h" -static int resize_ext4(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { +static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { assert((uint64_t) (int) blocksize == blocksize); if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0) - return -errno; + return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (ext4): %m", + path, numblocks); return 0; } -static int resize_btrfs(int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { +static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { struct btrfs_ioctl_vol_args args = {}; int r; assert((uint64_t) (int) blocksize == blocksize); + /* https://bugzilla.kernel.org/show_bug.cgi?id=118111 */ + if (numblocks * blocksize < 256*1024*1024) { + log_warning("%s: resizing of btrfs volumes smaller than 256M is not supported", path); + return -EOPNOTSUPP; + } + r = snprintf(args.name, sizeof(args.name), "%"PRIu64, numblocks * blocksize); /* The buffer is large enough for any number to fit... */ assert((size_t) r < sizeof(args.name)); if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0) - return -errno; + return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (btrfs): %m", + path, numblocks); return 0; } @@ -133,10 +141,10 @@ int main(int argc, char *argv[]) { switch(sfs.f_type) { case EXT4_SUPER_MAGIC: - r = resize_ext4(mountfd, devfd, numblocks, blocksize); + r = resize_ext4(argv[1], mountfd, devfd, numblocks, blocksize); break; case BTRFS_SUPER_MAGIC: - r = resize_btrfs(mountfd, devfd, numblocks, blocksize); + r = resize_btrfs(argv[1], mountfd, devfd, numblocks, blocksize); break; default: log_error("Don't know how to resize fs %llx on \"%s\"", @@ -144,11 +152,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - if (r < 0) { - log_error_errno(r, "Failed to resize \"%s\" to %"PRIu64" blocks: %m", - argv[1], numblocks); + if (r < 0) return EXIT_FAILURE; - } log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).", argv[1], format_bytes(fb, sizeof fb, size), numblocks, blocksize); From c116b0359509e00b0e868bd40459d05ff3b78469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 29 Nov 2017 15:06:53 +0100 Subject: [PATCH 12/22] Define CRYPT_LUKS in crypt-util.h Also do not include libcryptsetup.h directly, but only through crypt-util.h. This way we do not have to repeat the define in every file where it is used. --- src/basic/crypt-util.h | 5 +++++ src/cryptsetup/cryptsetup.c | 6 ------ src/shared/dissect-image.c | 7 ------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/basic/crypt-util.h b/src/basic/crypt-util.h index e8e753275a..537f785607 100644 --- a/src/basic/crypt-util.h +++ b/src/basic/crypt-util.h @@ -23,6 +23,11 @@ #include "macro.h" +/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */ +#ifndef CRYPT_LUKS +#define CRYPT_LUKS NULL +#endif + DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free); void cryptsetup_log_glue(int level, const char *msg, void *usrptr); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 84bcdb467d..ab4d24ca3b 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -19,7 +19,6 @@ ***/ #include -#include #include #include #include @@ -40,11 +39,6 @@ #include "strv.h" #include "util.h" -/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */ -#ifndef CRYPT_LUKS -#define CRYPT_LUKS NULL -#endif - /* internal helper */ #define ANY_LUKS "LUKS" diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 2714b3921c..6f294ed0bf 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -18,13 +18,6 @@ along with systemd; If not, see . ***/ -#if HAVE_LIBCRYPTSETUP -#include -#ifndef CRYPT_LUKS -#define CRYPT_LUKS NULL -#endif -#endif - #include #include #include From c34b75a124c6a23ed2dbf6d6b6d56d1385a2d191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 21 Nov 2017 18:56:52 +0100 Subject: [PATCH 13/22] growfs: add support for resizing encrypted partitions --- meson.build | 1 + src/partition/growfs.c | 81 ++++++++++++++++++++++++++++++++++++++ src/shared/dissect-image.c | 2 +- src/shared/dissect-image.h | 1 + 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 94917ca667..031776c41f 100644 --- a/meson.build +++ b/meson.build @@ -1940,6 +1940,7 @@ executable('systemd-growfs', 'src/partition/growfs.c', include_directories : includes, link_with : [libshared], + dependencies : [libcryptsetup], install_rpath : rootlibexecdir, install : true, install_dir : rootlibexecdir) diff --git a/src/partition/growfs.c b/src/partition/growfs.c index bebbda1bf3..e5dd1d54d2 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -27,7 +27,9 @@ #include #include +#include "crypt-util.h" #include "device-nodes.h" +#include "dissect-image.h" #include "escape.h" #include "fd-util.h" #include "format-util.h" @@ -71,6 +73,81 @@ static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numbl return 0; } +static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) { + char devpath[DEV_NUM_PATH_MAX], main_devpath[DEV_NUM_PATH_MAX]; + _cleanup_close_ int main_devfd = -1; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + uint64_t size; + int r; + + xsprintf_dev_num_path(main_devpath, "block", main_devno); + main_devfd = open(main_devpath, O_RDONLY|O_CLOEXEC); + if (main_devfd < 0) + return log_error_errno(errno, "Failed to open \"%s\": %m", main_devpath); + + if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) + return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m", + main_devpath); + + log_debug("%s is %"PRIu64" bytes", main_devpath, size); + + xsprintf_dev_num_path(devpath, "block", devno); + r = crypt_init(&cd, devpath); + if (r < 0) + return log_error_errno(r, "crypt_init(\"%s\") failed: %m", devpath); + + crypt_set_log_callback(cd, cryptsetup_log_glue, NULL); + + r = crypt_load(cd, CRYPT_LUKS, NULL); + if (r < 0) + return log_debug_errno(r, "Failed to load LUKS metadata for %s: %m", devpath); + + r = crypt_resize(cd, main_devpath, 0); + if (r < 0) + return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath); + + if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0) + log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m", + devpath); + else + log_debug("%s is now %"PRIu64" bytes", main_devpath, size); + + return 1; +} + +static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) { + dev_t devno; + char devpath[DEV_NUM_PATH_MAX]; + _cleanup_free_ char *fstype = NULL; + int r; + + crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL); + crypt_set_debug_level(1); + + r = get_block_device_harder(mountpath, &devno); + if (r < 0) + return log_error_errno(r, "Failed to determine underlying block device of \"%s\": %m", + mountpath); + + log_debug("Underlying device %d:%d, main dev %d:%d, %s", + major(devno), minor(devno), + major(main_devno), minor(main_devno), + devno == main_devno ? "same" : "different"); + if (devno == main_devno) + return 0; + + xsprintf_dev_num_path(devpath, "block", devno); + r = probe_filesystem(devpath, &fstype); + if (r < 0) + return log_warning_errno(r, "Failed to probe \"%s\": %m", devpath); + + if (streq_ptr(fstype, "crypto_LUKS")) + return resize_crypt_luks_device(devno, fstype, main_devno); + + log_debug("Don't know how to resize %s of type %s, ignoring", devpath, strnull(fstype)); + return 0; +} + int main(int argc, char *argv[]) { dev_t devno; _cleanup_close_ int mountfd = -1, devfd = -1; @@ -105,6 +182,10 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + r = maybe_resize_slave_device(argv[1], devno); + if (r < 0) + return EXIT_FAILURE; + mountfd = open(argv[1], O_RDONLY|O_CLOEXEC); if (mountfd < 0) { log_error_errno(errno, "Failed to open \"%s\": %m", argv[1]); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 6f294ed0bf..a7679a1423 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -51,7 +51,7 @@ #include "udev-util.h" #include "xattr-util.h" -_unused_ static int probe_filesystem(const char *node, char **ret_fstype) { +int probe_filesystem(const char *node, char **ret_fstype) { #if HAVE_BLKID _cleanup_blkid_free_probe_ blkid_probe b = NULL; const char *fstype; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 30a12cb540..7c7ce46015 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -86,6 +86,7 @@ struct DissectedImage { char **os_release; }; +int probe_filesystem(const char *node, char **ret_fstype); int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret); DissectedImage* dissected_image_unref(DissectedImage *m); From 995fa2e5e10f2eeff1aad1c5cb6f38480bc92af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 24 Nov 2017 21:31:47 +0100 Subject: [PATCH 14/22] shared/dissect-image: fix return value for probe_filesystem() blkid_new_probe_from_filename() sets errno, for example EPERM. --- src/shared/dissect-image.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index a7679a1423..3c16f60b83 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -57,9 +57,10 @@ int probe_filesystem(const char *node, char **ret_fstype) { const char *fstype; int r; + errno = 0; b = blkid_new_probe_from_filename(node); if (!b) - return -ENOMEM; + return -errno ?: -ENOMEM; blkid_probe_enable_superblocks(b, 1); blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); From 385de88a687728ae8149a332896ff78dd58d2809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 24 Nov 2017 21:34:36 +0100 Subject: [PATCH 15/22] growfs: add option parsing and --help/--version/--dry-run v2: - use arg_target --- src/partition/growfs.c | 109 ++++++++++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 18 deletions(-) diff --git a/src/partition/growfs.c b/src/partition/growfs.c index e5dd1d54d2..f9c604ca6f 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -40,9 +41,15 @@ #include "path-util.h" #include "strv.h" +const char *arg_target = NULL; +bool arg_dry_run = false; + static int resize_ext4(const char *path, int mountfd, int devfd, uint64_t numblocks, uint64_t blocksize) { assert((uint64_t) (int) blocksize == blocksize); + if (arg_dry_run) + return 0; + if (ioctl(mountfd, EXT4_IOC_RESIZE_FS, &numblocks) != 0) return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (ext4): %m", path, numblocks); @@ -66,6 +73,9 @@ static int resize_btrfs(const char *path, int mountfd, int devfd, uint64_t numbl /* The buffer is large enough for any number to fit... */ assert((size_t) r < sizeof(args.name)); + if (arg_dry_run) + return 0; + if (ioctl(mountfd, BTRFS_IOC_RESIZE, &args) != 0) return log_error_errno(errno, "Failed to resize \"%s\" to %"PRIu64" blocks (btrfs): %m", path, numblocks); @@ -102,6 +112,9 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_ if (r < 0) return log_debug_errno(r, "Failed to load LUKS metadata for %s: %m", devpath); + if (arg_dry_run) + return 0; + r = crypt_resize(cd, main_devpath, 0); if (r < 0) return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath); @@ -148,6 +161,65 @@ static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) { return 0; } +static void help(void) { + printf("%s [OPTIONS...] /path/to/mountpoint\n\n" + "Grow filesystem or encrypted payload to device size.\n\n" + "Options:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " -n --dry-run Just print what would be done\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + int c; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "dry-run", no_argument, NULL, 'n' }, + {} + }; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hn", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; + + case ARG_VERSION: + version(); + return 0; + + case 'n': + arg_dry_run = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind + 1 != argc) { + log_error("%s excepts exactly one argument (the mount point).", + program_invocation_short_name); + return -EINVAL; + } + + arg_target = argv[optind]; + + return 1; +} + int main(int argc, char *argv[]) { dev_t devno; _cleanup_close_ int mountfd = -1, devfd = -1; @@ -157,38 +229,39 @@ int main(int argc, char *argv[]) { struct statfs sfs; int r; - if (argc != 2) { - log_error("This program requires one argument (the mountpoint)."); - return EXIT_FAILURE; - } - log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); - r = path_is_mount_point(argv[1], NULL, 0); + r = parse_argv(argc, argv); + if (r < 0) + return EXIT_FAILURE; + if (r == 0) + return EXIT_SUCCESS; + + r = path_is_mount_point(arg_target, NULL, 0); if (r < 0) { - log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", argv[1]); + log_error_errno(r, "Failed to check if \"%s\" is a mount point: %m", arg_target); return EXIT_FAILURE; } if (r == 0) { - log_error_errno(r, "\"%s\" is not a mount point: %m", argv[1]); + log_error_errno(r, "\"%s\" is not a mount point: %m", arg_target); return EXIT_FAILURE; } - r = get_block_device(argv[1], &devno); + r = get_block_device(arg_target, &devno); if (r < 0) { - log_error_errno(r, "Failed to determine block device of \"%s\": %m", argv[1]); + log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target); return EXIT_FAILURE; } - r = maybe_resize_slave_device(argv[1], devno); + r = maybe_resize_slave_device(arg_target, devno); if (r < 0) return EXIT_FAILURE; - mountfd = open(argv[1], O_RDONLY|O_CLOEXEC); + mountfd = open(arg_target, O_RDONLY|O_CLOEXEC); if (mountfd < 0) { - log_error_errno(errno, "Failed to open \"%s\": %m", argv[1]); + log_error_errno(errno, "Failed to open \"%s\": %m", arg_target); return EXIT_FAILURE; } @@ -216,20 +289,20 @@ int main(int argc, char *argv[]) { numblocks = size / blocksize; if (fstatfs(mountfd, &sfs) < 0) { - log_error_errno(errno, "Failed to stat file system \"%s\": %m", argv[1]); + log_error_errno(errno, "Failed to stat file system \"%s\": %m", arg_target); return EXIT_FAILURE; } switch(sfs.f_type) { case EXT4_SUPER_MAGIC: - r = resize_ext4(argv[1], mountfd, devfd, numblocks, blocksize); + r = resize_ext4(arg_target, mountfd, devfd, numblocks, blocksize); break; case BTRFS_SUPER_MAGIC: - r = resize_btrfs(argv[1], mountfd, devfd, numblocks, blocksize); + r = resize_btrfs(arg_target, mountfd, devfd, numblocks, blocksize); break; default: log_error("Don't know how to resize fs %llx on \"%s\"", - (long long unsigned) sfs.f_type, argv[1]); + (long long unsigned) sfs.f_type, arg_target); return EXIT_FAILURE; } @@ -237,6 +310,6 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).", - argv[1], format_bytes(fb, sizeof fb, size), numblocks, blocksize); + arg_target, format_bytes(fb, sizeof fb, size), numblocks, blocksize); return EXIT_SUCCESS; } From b7f28ac51f8f679fbaf01f10979314727af03895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 26 Nov 2017 22:51:29 +0100 Subject: [PATCH 16/22] Add mkfs wrapper which first checks if the partition is empty --- meson.build | 8 ++++ src/partition/makefs.c | 106 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/partition/makefs.c diff --git a/meson.build b/meson.build index 031776c41f..abb152a033 100644 --- a/meson.build +++ b/meson.build @@ -1945,6 +1945,14 @@ executable('systemd-growfs', install : true, install_dir : rootlibexecdir) +executable('systemd-makefs', + 'src/partition/makefs.c', + include_directories : includes, + link_with : [libshared], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) + executable('systemd-sleep', 'src/sleep/sleep.c', include_directories : includes, diff --git a/src/partition/makefs.c b/src/partition/makefs.c new file mode 100644 index 0000000000..c24c6ebcde --- /dev/null +++ b/src/partition/makefs.c @@ -0,0 +1,106 @@ +/*** + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include "alloc-util.h" +#include "dissect-image.h" +#include "signal-util.h" +#include "string-util.h" + +static int makefs(const char *type, const char *device) { + const char *mkfs; + pid_t pid; + + if (streq(type, "swap")) + mkfs = "/sbin/mkswap"; + else + mkfs = strjoina("/sbin/mkfs.", type); + if (access(mkfs, X_OK) != 0) + return log_error_errno(errno, "%s is not executable: %m", mkfs); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "fork(): %m"); + + if (pid == 0) { + const char *cmdline[3] = { mkfs, device, NULL }; + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + execv(cmdline[0], (char**) cmdline); + _exit(EXIT_FAILURE); + } + + return wait_for_terminate_and_warn(mkfs, pid, true); +} + +int main(int argc, char *argv[]) { + const char *device, *type; + _cleanup_free_ char *detected = NULL; + struct stat st; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 3) { + log_error("This program expects two arguments."); + return EXIT_FAILURE; + } + + type = argv[1]; + device = argv[2]; + + if (stat(device, &st) < 0) { + r = log_error_errno(errno, "Failed to stat \"%s\": %m", device); + goto finish; + } + + if (!S_ISBLK(st.st_mode)) + log_info("%s is not a block device.", device); + + r = probe_filesystem(device, &detected); + if (r < 0) { + log_warning_errno(r, "Failed to probe \"%s\": %m", device); + goto finish; + } + + if (detected) { + log_info("%s is not empty (type %s), exiting", device, detected); + goto finish; + } + + r = makefs(type, device); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} From 4191418baf1a8f5b51c216aacdfbccdaec4f7a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 29 Nov 2017 15:49:25 +0100 Subject: [PATCH 17/22] fstab-generator: convert separate booleans to a flag field It's pretty unwieldy with just three flags, and I want to add more. --- src/fstab-generator/fstab-generator.c | 60 +++++++++++++-------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 6d6895a216..913992e365 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -47,6 +47,12 @@ #include "virt.h" #include "volatile-util.h" +typedef enum MountpointFlags { + NOAUTO = 1 << 0, + NOFAIL = 1 << 1, + AUTOMOUNT = 1 << 2, +} MountpointFlags; + static const char *arg_dest = "/tmp"; static const char *arg_dest_late = "/tmp"; static bool arg_fstab_enabled = true; @@ -91,8 +97,7 @@ static int write_what(FILE *f, const char *what) { static int add_swap( const char *what, struct mntent *me, - bool noauto, - bool nofail) { + MountpointFlags flags) { _cleanup_free_ char *name = NULL, *unit = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -150,9 +155,9 @@ static int add_swap( if (r < 0) return r; - if (!noauto) { + if (!(flags & NOAUTO)) { r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, - nofail ? "wants" : "requires", name); + (flags & NOFAIL) ? "wants" : "requires", name); if (r < 0) return r; } @@ -297,9 +302,7 @@ static int add_mount( const char *fstype, const char *opts, int passno, - bool noauto, - bool nofail, - bool automount, + MountpointFlags flags, const char *post, const char *source) { @@ -330,14 +333,14 @@ static int add_mount( return 0; if (path_equal(where, "/")) { - if (noauto) + if (flags & NOAUTO) log_warning("Ignoring \"noauto\" for root device"); - if (nofail) + if (flags & NOFAIL) log_warning("Ignoring \"nofail\" for root device"); - if (automount) + if (flags & AUTOMOUNT) log_warning("Ignoring automount option for root device"); - noauto = nofail = automount = false; + SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false); } r = unit_name_from_path(where, ".mount", &name); @@ -363,7 +366,7 @@ static int add_mount( "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", source); - if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !automount && + if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & AUTOMOUNT) && fstab_test_yes_no_option(opts, "bg\0" "fg\0")) { /* The default retry timeout that mount.nfs uses for 'bg' mounts * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts. @@ -374,13 +377,13 @@ static int add_mount( * By placing these options first, they can be over-ridden by * settings in /etc/fstab. */ opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts, ",fg"); - nofail = true; + SET_FLAG(flags, NOFAIL, true); } - if (!nofail && !automount) + if (!(flags & NOFAIL) && !(flags & AUTOMOUNT)) fprintf(f, "Before=%s\n", post); - if (!automount && opts) { + if (!(flags & AUTOMOUNT) && opts) { r = write_after(f, opts); if (r < 0) return r; @@ -444,14 +447,14 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); - if (!noauto && !automount) { + if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) { r = generator_add_symlink(dest, post, - nofail ? "wants" : "requires", name); + (flags & NOFAIL) ? "wants" : "requires", name); if (r < 0) return r; } - if (automount) { + if (flags & AUTOMOUNT) { r = unit_name_from_path(where, ".automount", &automount_name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); @@ -504,7 +507,7 @@ static int add_mount( return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); r = generator_add_symlink(dest, post, - nofail ? "wants" : "requires", automount_name); + (flags & NOFAIL) ? "wants" : "requires", automount_name); if (r < 0) return r; } @@ -578,7 +581,8 @@ static int parse_fstab(bool initrd) { yes_no(noauto), yes_no(nofail)); if (streq(me->mnt_type, "swap")) - k = add_swap(what, me, noauto, nofail); + k = add_swap(what, me, + noauto*NOAUTO | nofail*NOFAIL); else { bool automount; const char *post; @@ -600,9 +604,7 @@ static int parse_fstab(bool initrd) { me->mnt_type, me->mnt_opts, me->mnt_passno, - noauto, - nofail, - automount, + noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, post, fstab_path); } @@ -663,9 +665,7 @@ static int add_sysroot_mount(void) { arg_root_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ - false, /* noauto off */ - false, /* nofail off */ - false, /* automount off */ + 0, /* noauto off, nofail off, automount off */ SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline"); } @@ -718,9 +718,7 @@ static int add_sysroot_usr_mount(void) { arg_usr_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ - false, /* noauto off */ - false, /* nofail off */ - false, /* automount off */ + 0, SPECIAL_INITRD_FS_TARGET, "/proc/cmdline"); } @@ -759,9 +757,7 @@ static int add_volatile_var(void) { "tmpfs", "mode=0755", 0, - false, - false, - false, + 0, SPECIAL_LOCAL_FS_TARGET, "/proc/cmdline"); } From da495a0385b4df0ed627153950edbc7f4c9faf57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 21 Nov 2017 23:18:05 +0100 Subject: [PATCH 18/22] Add x-systemd.makefs option for fstab I opted to completely generate a unit for both mount points and swaps. For swaps, it would be possible to use fixed template unit like systemd-mkswap@.service, because there's no information passed except the device name. For mount points, that's not possible because both the device name and file system type need to be passed. Nevertheless, I expect that options will need to passed to both mkfs and mkswap, in which case it'll be necessary to create units of both types anyway. --- meson.build | 1 + src/fstab-generator/fstab-generator.c | 32 ++++-- src/shared/generator.c | 156 +++++++++++++++++++++++++- src/shared/generator.h | 17 ++- 4 files changed, 190 insertions(+), 16 deletions(-) diff --git a/meson.build b/meson.build index abb152a033..b9015c8f1a 100644 --- a/meson.build +++ b/meson.build @@ -182,6 +182,7 @@ conf.set_quoted('CATALOG_DATABASE', join_paths(catalog conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH', join_paths(rootlibexecdir, 'systemd-cgroups-agent')) conf.set_quoted('SYSTEMD_BINARY_PATH', join_paths(rootlibexecdir, 'systemd')) conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlibexecdir, 'systemd-fsck')) +conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs')) conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown')) conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-sleep')) conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl')) diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 913992e365..5bdeae04c3 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -51,6 +51,7 @@ typedef enum MountpointFlags { NOAUTO = 1 << 0, NOFAIL = 1 << 1, AUTOMOUNT = 1 << 2, + MAKEFS = 1 << 3, } MountpointFlags; static const char *arg_dest = "/tmp"; @@ -155,6 +156,12 @@ static int add_swap( if (r < 0) return r; + if (flags & MAKEFS) { + r = generator_hook_up_mkswap(arg_dest, what); + if (r < 0) + return r; + } + if (!(flags & NOAUTO)) { r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, (flags & NOFAIL) ? "wants" : "requires", name); @@ -307,10 +314,11 @@ static int add_mount( const char *source) { _cleanup_free_ char - *name = NULL, *unit = NULL, + *name = NULL, *automount_name = NULL, *automount_unit = NULL, *filtered = NULL, *where_escaped = NULL; + const char *unit; _cleanup_fclose_ FILE *f = NULL; int r; @@ -347,9 +355,7 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - unit = strjoin(dest, "/", name); - if (!unit) - return log_oom(); + unit = strjoina(dest, "/", name); f = fopen(unit, "wxe"); if (!f) @@ -447,6 +453,12 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); + if (flags & MAKEFS) { + r = generator_hook_up_mkfs(dest, what, where, fstype); + if (r < 0) + return r; + } + if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) { r = generator_add_symlink(dest, post, (flags & NOFAIL) ? "wants" : "requires", name); @@ -532,7 +544,7 @@ static int parse_fstab(bool initrd) { while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL; - bool noauto, nofail; + bool makefs, noauto, nofail; int k; if (initrd && !mount_in_initrd(me)) @@ -574,15 +586,17 @@ static int parse_fstab(bool initrd) { } } + makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0"); noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); - log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s", + log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s", what, where, me->mnt_type, + yes_no(makefs), yes_no(noauto), yes_no(nofail)); if (streq(me->mnt_type, "swap")) k = add_swap(what, me, - noauto*NOAUTO | nofail*NOFAIL); + makefs*MAKEFS | noauto*NOAUTO | nofail*NOFAIL); else { bool automount; const char *post; @@ -604,7 +618,7 @@ static int parse_fstab(bool initrd) { me->mnt_type, me->mnt_opts, me->mnt_passno, - noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, + makefs*MAKEFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, post, fstab_path); } @@ -665,7 +679,7 @@ static int add_sysroot_mount(void) { arg_root_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ - 0, /* noauto off, nofail off, automount off */ + 0, /* makefs off, noauto off, nofail off, automount off */ SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline"); } diff --git a/src/shared/generator.c b/src/shared/generator.c index 462457e2fe..7c764328d6 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -83,8 +83,8 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { fprintf(f, "# Automatically generated by %1$s\n\n" "[Unit]\n" - "Documentation=man:systemd-fsck-root.service(8)\n" "Description=File System Check on %2$s\n" + "Documentation=man:systemd-fsck-root.service(8)\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" "After=initrd-root-device.target local-fs-pre.target %3$s\n" @@ -248,7 +248,8 @@ int generator_write_device_deps( r = unit_name_from_path(node, ".device", &unit); if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + node); /* See mount_add_default_dependencies for explanation why we create such * dependencies. */ @@ -266,7 +267,8 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) { r = unit_name_from_path(what, ".device", &unit); if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + what); return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", "# Automatically generated by %s\n\n" @@ -277,3 +279,151 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) { unit, unit); } + +int generator_hook_up_mkswap( + const char *dir, + const char *what) { + + _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + /* Nothing to work on. */ + if (!is_device_path(node)) { + log_error("Cannot format something that is not a device node: %s", node); + return -EINVAL; + } + + r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + node); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + escaped = cescape(node); + if (!escaped) + return log_oom(); + + r = unit_name_from_path(what, ".swap", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + what); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Make Swap on %%f\n" + "Documentation=man:systemd-mkswap@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.device\n" + "After=%%i.device\n" + "Before=%s\n" + "Before=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + where_unit, + escaped); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); + + return generator_add_symlink(dir, where_unit, "requires", unit); +} + +int generator_hook_up_mkfs( + const char *dir, + const char *what, + const char *where, + const char *type) { + + _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + /* Nothing to work on. */ + if (!is_device_path(node)) { + log_error("Cannot format something that is not a device node: %s", node); + return -EINVAL; + } + + if (!type || streq(type, "auto")) { + log_error("Cannot format partition %s, filesystem type is not specified", node); + return -EINVAL; + } + + r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + node); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + escaped = cescape(node); + if (!escaped) + return log_oom(); + + r = unit_name_from_path(where, ".mount", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + where); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Make File System on %%f\n" + "Documentation=man:systemd-mkfs@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.device\n" + "After=%%i.device\n" + /* fsck might or might not be used, so let's be safe and order + * ourselves before both systemd-fsck@.service and the mount unit. */ + "Before=systemd-fsck@%%i.service\n" + "Before=%s\n" + "Before=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + where_unit, + type, + escaped); + // XXX: what about local-fs-pre.target? + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); + + return generator_add_symlink(dir, where_unit, "requires", unit); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index 39dd520f9f..f4109b95a3 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -39,11 +39,20 @@ int generator_write_timeouts( char **filtered); int generator_write_device_deps( - const char *dir, - const char *what, - const char *where, - const char *opts); + const char *dir, + const char *what, + const char *where, + const char *opts); int generator_write_initrd_root_device_deps( const char *dir, const char *what); + +int generator_hook_up_mkswap( + const char *dir, + const char *what); +int generator_hook_up_mkfs( + const char *dir, + const char *what, + const char *where, + const char *type); From 7f2806d50937a7fbe6d673ce8fc62634129c3a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 29 Nov 2017 20:02:11 +0100 Subject: [PATCH 19/22] Add x-systemd.growfs option for fstab --- meson.build | 1 + src/fstab-generator/fstab-generator.c | 22 ++++++++--- src/shared/generator.c | 55 +++++++++++++++++++++++++++ src/shared/generator.h | 4 ++ 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index b9015c8f1a..6eff25f45c 100644 --- a/meson.build +++ b/meson.build @@ -183,6 +183,7 @@ conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH', join_paths(rootlib conf.set_quoted('SYSTEMD_BINARY_PATH', join_paths(rootlibexecdir, 'systemd')) conf.set_quoted('SYSTEMD_FSCK_PATH', join_paths(rootlibexecdir, 'systemd-fsck')) conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs')) +conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs')) conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown')) conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-sleep')) conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl')) diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 5bdeae04c3..68e7c23cbe 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -52,6 +52,7 @@ typedef enum MountpointFlags { NOFAIL = 1 << 1, AUTOMOUNT = 1 << 2, MAKEFS = 1 << 3, + GROWFS = 1 << 4, } MountpointFlags; static const char *arg_dest = "/tmp"; @@ -162,6 +163,10 @@ static int add_swap( return r; } + if (flags & GROWFS) + /* TODO: swap devices must be wiped and recreated */ + log_warning("%s: growing swap devices is currently unsupported.", what); + if (!(flags & NOAUTO)) { r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, (flags & NOFAIL) ? "wants" : "requires", name); @@ -459,6 +464,12 @@ static int add_mount( return r; } + if (flags & GROWFS) { + r = generator_hook_up_growfs(dest, where, post); + if (r < 0) + return r; + } + if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) { r = generator_add_symlink(dest, post, (flags & NOFAIL) ? "wants" : "requires", name); @@ -544,7 +555,7 @@ static int parse_fstab(bool initrd) { while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL; - bool makefs, noauto, nofail; + bool makefs, growfs, noauto, nofail; int k; if (initrd && !mount_in_initrd(me)) @@ -587,6 +598,7 @@ static int parse_fstab(bool initrd) { } makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0"); + growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0"); noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s", @@ -596,7 +608,7 @@ static int parse_fstab(bool initrd) { if (streq(me->mnt_type, "swap")) k = add_swap(what, me, - makefs*MAKEFS | noauto*NOAUTO | nofail*NOFAIL); + makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL); else { bool automount; const char *post; @@ -618,12 +630,12 @@ static int parse_fstab(bool initrd) { me->mnt_type, me->mnt_opts, me->mnt_passno, - makefs*MAKEFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, + makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, post, fstab_path); } - if (k < 0) + if (r >= 0 && k < 0) r = k; } @@ -679,7 +691,7 @@ static int add_sysroot_mount(void) { arg_root_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ - 0, /* makefs off, noauto off, nofail off, automount off */ + 0, /* makefs off, growfs off, noauto off, nofail off, automount off */ SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline"); } diff --git a/src/shared/generator.c b/src/shared/generator.c index 7c764328d6..3495a7ef7d 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -427,3 +427,58 @@ int generator_hook_up_mkfs( return generator_add_symlink(dir, where_unit, "requires", unit); } + +int generator_hook_up_growfs( + const char *dir, + const char *where, + const char *target) { + + _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + escaped = cescape(where); + if (!escaped) + return log_oom(); + + r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + where); + + r = unit_name_from_path(where, ".mount", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + where); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Grow File System on %%f\n" + "Documentation=man:systemd-growfs@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.mount\n" + "After=%%i.mount\n" + "Before=shutdown.target\n" + "Before=%s\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_GROWFS_PATH " %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + target, + escaped); + + return generator_add_symlink(dir, where_unit, "wants", unit); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index f4109b95a3..32d1ad021c 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -56,3 +56,7 @@ int generator_hook_up_mkfs( const char *what, const char *where, const char *type); +int generator_hook_up_growfs( + const char *dir, + const char *where, + const char *target); From 7cc84b2cd385b8a838347c2ff29c978689dadf84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 30 Nov 2017 12:09:36 +0100 Subject: [PATCH 20/22] dissect-image: return error if results are ambiguous We let the caller make the decision. Existing callers are OK with treating an ambiguous result the same as no content, but makefs and growfs should refuse such partitions. --- src/partition/growfs.c | 2 ++ src/partition/makefs.c | 6 +++++- src/shared/dissect-image.c | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/partition/growfs.c b/src/partition/growfs.c index f9c604ca6f..8b44c1ded7 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -151,6 +151,8 @@ static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) { xsprintf_dev_num_path(devpath, "block", devno); r = probe_filesystem(devpath, &fstype); + if (r == -EUCLEAN) + return log_warning_errno(r, "Cannot reliably determine probe \"%s\", refusing to proceed.", devpath); if (r < 0) return log_warning_errno(r, "Failed to probe \"%s\": %m", devpath); diff --git a/src/partition/makefs.c b/src/partition/makefs.c index c24c6ebcde..e5e125255b 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -90,7 +90,11 @@ int main(int argc, char *argv[]) { r = probe_filesystem(device, &detected); if (r < 0) { - log_warning_errno(r, "Failed to probe \"%s\": %m", device); + log_warning_errno(r, + r == -EUCLEAN ? + "Cannot reliably determine probe \"%s\", refusing to proceed." : + "Failed to probe \"%s\": %m", + device); goto finish; } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 3c16f60b83..7835d99020 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -52,6 +52,10 @@ #include "xattr-util.h" int probe_filesystem(const char *node, char **ret_fstype) { + /* Try to find device content type and return it in *ret_fstype. If nothing is found, + * 0/NULL will be returned. -EUCLEAN will be returned for ambigous results, and an + * different error otherwise. */ + #if HAVE_BLKID _cleanup_blkid_free_probe_ blkid_probe b = NULL; const char *fstype; @@ -67,10 +71,14 @@ int probe_filesystem(const char *node, char **ret_fstype) { errno = 0; r = blkid_do_safeprobe(b); - if (IN_SET(r, -2, 1)) { - log_debug("Failed to identify any partition type on partition %s", node); + if (r == 1) { + log_debug("No type detected on partition %s", node); goto not_found; } + if (r == -2) { + log_debug("Results ambiguous for partition %s", node); + return -EUCLEAN; + } if (r != 0) return -errno ?: -EIO; @@ -608,7 +616,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI if (!p->fstype && p->node) { r = probe_filesystem(p->node, &p->fstype); - if (r < 0) + if (r < 0 && r != -EUCLEAN) return r; } @@ -1018,7 +1026,7 @@ int dissected_image_decrypt( if (!p->decrypted_fstype && p->decrypted_node) { r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype); - if (r < 0) + if (r < 0 && r != -EUCLEAN) return r; } } From 58e0ac334920adf2d3934913987e3f77df4d0710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 30 Nov 2017 12:55:00 +0100 Subject: [PATCH 21/22] man: add docs for systemd-growfs and systemd-makefs --- man/rules/meson.build | 7 ++ man/systemd-makefs@.service.xml | 120 ++++++++++++++++++++++++++++++++ man/systemd.mount.xml | 38 ++++++++++ 3 files changed, 165 insertions(+) create mode 100644 man/systemd-makefs@.service.xml diff --git a/man/rules/meson.build b/man/rules/meson.build index f74dbf9c77..7385658d6a 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -569,6 +569,13 @@ manpages = [ ['systemd-machine-id-commit.service', '8', [], ''], ['systemd-machine-id-setup', '1', [], ''], ['systemd-machined.service', '8', ['systemd-machined'], 'ENABLE_MACHINED'], + ['systemd-makefs@.service', + '8', + ['systemd-growfs', + 'systemd-growfs@.service', + 'systemd-makefs', + 'systemd-makeswap@.service'], + ''], ['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'], ['systemd-mount', '1', ['systemd-umount'], ''], ['systemd-networkd-wait-online.service', diff --git a/man/systemd-makefs@.service.xml b/man/systemd-makefs@.service.xml new file mode 100644 index 0000000000..073c52fafe --- /dev/null +++ b/man/systemd-makefs@.service.xml @@ -0,0 +1,120 @@ + + + + + + + + systemd-makefs@.service + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-makefs@.service + 8 + + + + systemd-makefs@.service + systemd-makeswap@.service + systemd-growfs@.service + systemd-makefs + systemd-growfs + Creating and growing file systems on demand + + + + systemd-makefs@device.service + systemd-makeswap@device.service + systemd-growfs@mountpoint.service + /usr/lib/systemd/systemd-makefs + /usr/lib/systemd/systemd-growfs + + + + Description + + systemd-makefs@.service, + systemd-makeswap@.service, and + systemd-growfs@.service are used to implement the + and options + in fstab5, + see systemd.mount5. + They are instantiated for each device for which the file system or swap structure + needs to be initalized, and for each mount point where the file system needs to + be grown. + + These services are started at boot, either right before or right after the + mount point or swap device are used. + + systemd-makefs knows very little about specific file + systems and swap devices, and after checking that the block device does not already + contain a file system or other content, it will execute binaries specific to + each filesystem type (/sbin/mkfs.*). + + systemd-growfs knows very little about specific file + systems and swap devices, and will instruct the kernel to grow the mounted + filesystem to full size of the underlying block device. Nevertheless, it needs + to know the + ioctl2 + number specific to each file system, so only certain types are supported. + Currently: + ext45, + btrfs (see + btrfs-man55), + + and dm-crypt partitions (see + cryptsetup8). + + + If the creation of a file system or swap device fails, the mount point or + swap is failed too. If the growing of a file system fails, a warning is emitted. + + + + + See Also + + systemd1, + systemd.mount8, + systemd-fstab-generator8, + mkfs.btrfs8, + mkfs.cramfs8, + mkfs.ext48, + mkfs.fat8, + mkfs.hfsplus8, + mkfs.minix8, + mkfs.ntfs8, + mkfs.xfs8 + + + + diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index bfe52cda0f..663e7fa3ac 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -318,6 +318,44 @@ + + + + The file system or swap structure will be intialized + on the device. If the device is not "empty", i.e. it contains any signature, + the operation will be skipped. It is hence expected that this option + remains set even after the device has been initalized. + + Note that this option can only be used in + /etc/fstab, and will be ignored when part of the + Options= setting in a unit file. + + See + systemd-makefs@.service8. + + + wipefs8 + may be used to remove any signatures from a block device to force + to reinitialize the device. + + + + + + + The file system will be grown to occupy the full block + device. If the file system is already at maximum size, no action will + be performed. It is hence expected that this option remains set even after + the file system has been grown. Only certain file system types are supported, + see + systemd-makefs@.service8 + for details. + + Note that this option can only be used in + /etc/fstab, and will be ignored when part of the + Options= setting in a unit file. + + From 69f9ccf140009197cd673538707b940cdf70bd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 30 Nov 2017 20:54:31 +0100 Subject: [PATCH 22/22] util-lib: handle empty string in last_path_component Now the function returns an empty string when given an empty string. Not sure if this is the best option (maybe this should be an error?), but at least the behaviour is well defined. --- src/basic/path-util.c | 3 +++ src/test/test-path-util.c | 1 + 2 files changed, 4 insertions(+) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 059e4fcf6a..3bde1d1e01 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -719,6 +719,9 @@ const char *last_path_component(const char *path) { unsigned l, k; l = k = strlen(path); + if (l == 0) /* special case — an empty string */ + return path; + while (k > 0 && path[k-1] == '/') k--; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 21d52f5d6e..0db835608a 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -411,6 +411,7 @@ static void test_last_path_component(void) { assert_se(streq(last_path_component("././/"), ".//")); assert_se(streq(last_path_component("/foo/a"), "a")); assert_se(streq(last_path_component("/foo/a/"), "a/")); + assert_se(streq(last_path_component(""), "")); } static void test_filename_is_valid(void) {