diff options
author | Lennart Poettering <lennart@poettering.net> | 2017-12-01 17:58:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-01 17:58:58 +0100 |
commit | 1a2d4d70845f483bcb3f704fa092f5fd76580d31 (patch) | |
tree | 35991bc9a914c788950c09b1abb072f0b8d350fc /src | |
parent | NEWS: update the text a bit (#7524) (diff) | |
parent | util-lib: handle empty string in last_path_component (diff) | |
download | systemd-1a2d4d70845f483bcb3f704fa092f5fd76580d31.tar.xz systemd-1a2d4d70845f483bcb3f704fa092f5fd76580d31.zip |
Merge pull request #7237 from keszybz/growfs
Create and grow filesystems
Diffstat (limited to 'src')
30 files changed, 1152 insertions, 338 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/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 <http://www.gnu.org/licenses/>. +***/ + +#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 new file mode 100644 index 0000000000..537f785607 --- /dev/null +++ b/src/basic/crypt-util.h @@ -0,0 +1,34 @@ +/* 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 <http://www.gnu.org/licenses/>. +***/ + +#if HAVE_LIBCRYPTSETUP +#include <libcryptsetup.h> + +#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); +#endif diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h index 1e09bdc830..6136526f84 100644 --- a/src/basic/device-nodes.h +++ b/src/basic/device-nodes.h @@ -23,5 +23,18 @@ #include <stddef.h> #include <sys/types.h> +#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)) + +#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/basic/fs-util.c b/src/basic/fs-util.c index b5fd95ae8e..475400177a 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -661,9 +661,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, todo += m; + /* Empty? Then we reached the end. */ + if (isempty(first)) + break; + /* Just a single slash? Then we reached the end. */ - if (isempty(first) || path_equal(first, "/")) + 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, "/.")) @@ -726,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/meson.build b/src/basic/meson.build index bf11757b74..a37e279e57 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -61,6 +61,8 @@ 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 device-nodes.h 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/basic/mount-util.c b/src/basic/mount-util.c index e394e1adf3..e60750c531 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -278,6 +278,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; @@ -302,7 +303,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..3bde1d1e01 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -703,6 +703,37 @@ 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); + if (l == 0) /* special case — an empty string */ + return 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/basic/stat-util.c b/src/basic/stat-util.c index ea554f7b73..c6b8507e9d 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) @@ -202,14 +202,14 @@ int fd_check_fstype(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); 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..8b8d128121 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -57,8 +57,8 @@ 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 path_check_fstype(const char *path, statfs_f_type_t magic_value); +int fd_is_fs_type(int fd, 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/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; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = makedev(m, n); - return 0; - } + xsprintf_sys_block_path(p, "/queue", makedev(m, n)); + if (access(p, F_OK) < 0) + return -ENOENT; - 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/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index b19d03e9f0..ab4d24ca3b 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -19,7 +19,6 @@ ***/ #include <errno.h> -#include <libcryptsetup.h> #include <mntent.h> #include <string.h> #include <sys/mman.h> @@ -28,6 +27,7 @@ #include "alloc-util.h" #include "ask-password-api.h" +#include "crypt-util.h" #include "device-util.h" #include "escape.h" #include "fileio.h" @@ -39,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" @@ -254,10 +249,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; @@ -604,7 +595,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) { @@ -666,7 +657,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)) { @@ -750,7 +741,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) { @@ -766,9 +757,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/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 6d6895a216..68e7c23cbe 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -47,6 +47,14 @@ #include "virt.h" #include "volatile-util.h" +typedef enum MountpointFlags { + NOAUTO = 1 << 0, + NOFAIL = 1 << 1, + AUTOMOUNT = 1 << 2, + MAKEFS = 1 << 3, + GROWFS = 1 << 4, +} MountpointFlags; + static const char *arg_dest = "/tmp"; static const char *arg_dest_late = "/tmp"; static bool arg_fstab_enabled = true; @@ -91,8 +99,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 +157,19 @@ static int add_swap( if (r < 0) return r; - if (!noauto) { + if (flags & MAKEFS) { + r = generator_hook_up_mkswap(arg_dest, what); + if (r < 0) + 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, - nofail ? "wants" : "requires", name); + (flags & NOFAIL) ? "wants" : "requires", name); if (r < 0) return r; } @@ -297,17 +314,16 @@ 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) { _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; @@ -330,23 +346,21 @@ 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); 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) @@ -363,7 +377,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 +388,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 +458,26 @@ 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 & MAKEFS) { + r = generator_hook_up_mkfs(dest, what, where, fstype); + if (r < 0) + 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, - 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 +530,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; } @@ -529,7 +555,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, growfs, noauto, nofail; int k; if (initrd && !mount_in_initrd(me)) @@ -571,14 +597,18 @@ 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 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, nofail); + k = add_swap(what, me, + makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL); else { bool automount; const char *post; @@ -600,14 +630,12 @@ static int parse_fstab(bool initrd) { me->mnt_type, me->mnt_opts, me->mnt_passno, - noauto, - nofail, - automount, + makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT, post, fstab_path); } - if (k < 0) + if (r >= 0 && k < 0) r = k; } @@ -663,9 +691,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, /* makefs off, growfs off, noauto off, nofail off, automount off */ SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline"); } @@ -718,9 +744,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 +783,7 @@ static int add_volatile_var(void) { "tmpfs", "mode=0755", 0, - false, - false, - false, + 0, SPECIAL_LOCAL_FS_TARGET, "/proc/cmdline"); } 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/partition/growfs.c b/src/partition/growfs.c new file mode 100644 index 0000000000..8b44c1ded7 --- /dev/null +++ b/src/partition/growfs.c @@ -0,0 +1,317 @@ +/* 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 <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <linux/magic.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> + +#include "crypt-util.h" +#include "device-nodes.h" +#include "dissect-image.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" + +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); + + return 0; +} + +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 (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); + + 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); + + 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); + + 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 == -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); + + 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; +} + +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; + int blocksize; + uint64_t size, numblocks; + char devpath[DEV_NUM_PATH_MAX], fb[FORMAT_BYTES_MAX]; + struct statfs sfs; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + 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", arg_target); + return EXIT_FAILURE; + } + if (r == 0) { + log_error_errno(r, "\"%s\" is not a mount point: %m", arg_target); + return EXIT_FAILURE; + } + + r = get_block_device(arg_target, &devno); + if (r < 0) { + log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target); + return EXIT_FAILURE; + } + + r = maybe_resize_slave_device(arg_target, devno); + if (r < 0) + return EXIT_FAILURE; + + mountfd = open(arg_target, O_RDONLY|O_CLOEXEC); + if (mountfd < 0) { + log_error_errno(errno, "Failed to open \"%s\": %m", arg_target); + 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", arg_target); + return EXIT_FAILURE; + } + + switch(sfs.f_type) { + case EXT4_SUPER_MAGIC: + r = resize_ext4(arg_target, mountfd, devfd, numblocks, blocksize); + break; + case BTRFS_SUPER_MAGIC: + 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, arg_target); + return EXIT_FAILURE; + } + + if (r < 0) + return EXIT_FAILURE; + + log_info("Successfully resized \"%s\" to %s bytes (%"PRIu64" blocks of %d bytes).", + arg_target, format_bytes(fb, sizeof fb, size), numblocks, blocksize); + return EXIT_SUCCESS; +} diff --git a/src/partition/makefs.c b/src/partition/makefs.c new file mode 100644 index 0000000000..e5e125255b --- /dev/null +++ b/src/partition/makefs.c @@ -0,0 +1,110 @@ +/*** + 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 <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#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, + r == -EUCLEAN ? + "Cannot reliably determine probe \"%s\", refusing to proceed." : + "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; +} 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/shared/dissect-image.c b/src/shared/dissect-image.c index 219ba8cb0b..7835d99020 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -18,12 +18,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#if HAVE_LIBCRYPTSETUP -#include <libcryptsetup.h> -#ifndef CRYPT_LUKS -#define CRYPT_LUKS NULL -#endif -#endif #include <sys/mount.h> #include <sys/prctl.h> #include <sys/wait.h> @@ -32,7 +26,9 @@ #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" #include "fd-util.h" #include "fileio.h" @@ -55,25 +51,34 @@ #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) { + /* 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; 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); 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; @@ -611,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; } @@ -652,7 +657,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 +668,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; } @@ -849,7 +854,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); @@ -876,37 +881,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( @@ -918,7 +914,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); @@ -948,30 +944,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 @@ -1033,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; } } 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); diff --git a/src/shared/generator.c b/src/shared/generator.c index 462457e2fe..3495a7ef7d 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,206 @@ 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); +} + +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 39dd520f9f..32d1ad021c 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -39,11 +39,24 @@ 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); +int generator_hook_up_growfs( + const char *dir, + const char *where, + const char *target); 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); - assert_se(mkdir(q, 0700) >= 0); + 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); + 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 2aad7f387c..09a624842c 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,165 @@ 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("//", 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: + * + * <tmp>/file1, <tmp>/file2 + * <tmp>/link1 -> file1, <tmp>/link2 -> file2 + * <tmp>/dir1/ + * <tmp>/dir1/file + * <tmp>/dirlink1 -> dir1 + * <tmp>/dirlink1file -> dirlink1/file + * <tmp>/dir2/ + * <tmp>/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 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); + + assert_se(umount(file2) == 0); + + assert_se(rf == 1); + assert_se(rt == 1); + assert_se(rdf == -ENOTDIR); + assert_se(rdt == -ENOTDIR); + 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); + + 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); + + 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 +274,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..0db835608a 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -19,7 +19,6 @@ ***/ #include <stdio.h> -#include <sys/mount.h> #include <unistd.h> #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: - * - * <tmp>/file1, <tmp>/file2 - * <tmp>/link1 -> file1, <tmp>/link2 -> file2 - * <tmp>/dir1/ - * <tmp>/dir1/file - * <tmp>/dirlink1 -> dir1 - * <tmp>/dirlink1file -> dirlink1/file - * <tmp>/dir2/ - * <tmp>/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; @@ -537,6 +399,21 @@ 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/")); + assert_se(streq(last_path_component(""), "")); +} + static void test_filename_is_valid(void) { char foo[FILENAME_MAX+2]; int i; @@ -621,8 +498,8 @@ 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_last_path_component(); test_filename_is_valid(); test_hidden_or_backup_file(); test_skip_dev_prefix(); 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; 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(); 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 <sys/stat.h> #include <unistd.h> +#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); } diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 18554aa231..d3066ca429 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -18,10 +18,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <libcryptsetup.h> #include <stdio.h> #include <sys/stat.h> +#include "crypt-util.h" #include "log.h" #include "hexdecoct.h" #include "string-util.h" @@ -41,12 +41,8 @@ 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[]) { - struct crypt_device *cd = NULL; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; if (argc <= 1) { @@ -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) { @@ -144,9 +140,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); |