From c9d5c9c0e19eea79ca0f09fe58e5c0b76b8001e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Nov 2016 19:41:20 +0100 Subject: core: make unit_free() accept NULL pointers We generally try to make our destructors robust regarding NULL pointers, much in the same way as glibc's free(). Do this also for unit_free(). Follow-up for #4748. --- src/core/device.c | 2 +- src/core/mount.c | 2 +- src/core/swap.c | 2 +- src/core/unit.c | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/device.c b/src/core/device.c index 8e2e3c7bed..074e93ffe2 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -359,7 +359,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa fail: log_unit_warning_errno(u, r, "Failed to set up device unit: %m"); - if (delete && u) + if (delete) unit_free(u); return r; diff --git a/src/core/mount.c b/src/core/mount.c index 1c2be28d55..0c4d061c27 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1509,7 +1509,7 @@ static int mount_setup_unit( fail: log_warning_errno(r, "Failed to set up mount unit: %m"); - if (delete && u) + if (delete) unit_free(u); return r; diff --git a/src/core/swap.c b/src/core/swap.c index bf404db8c3..e9468e105c 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -420,7 +420,7 @@ static int swap_setup_unit( fail: log_unit_warning_errno(u, r, "Failed to load swap unit: %m"); - if (delete && u) + if (delete) unit_free(u); return r; diff --git a/src/core/unit.c b/src/core/unit.c index cba6342eca..e485c01fc1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -516,7 +516,8 @@ void unit_free(Unit *u) { Iterator i; char *t; - assert(u); + if (!u) + return; if (u->transient_file) fclose(u->transient_file); -- cgit v1.2.3-54-g00ecf From e187369587b1c6a5f65a12e7ec0bf7844905d014 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Nov 2016 21:35:21 +0100 Subject: tree-wide: stop using canonicalize_file_name(), use chase_symlinks() instead Let's use chase_symlinks() everywhere, and stop using GNU canonicalize_file_name() everywhere. For most cases this should not change behaviour, however increase exposure of our function to get better tested. Most importantly in a few cases (most notably nspawn) it can take the correct root directory into account when chasing symlinks. --- src/basic/fs-util.c | 26 ++++++------- src/basic/fs-util.h | 2 +- src/basic/mount-util.c | 14 ++++--- src/basic/mount-util.h | 2 +- src/basic/path-util.c | 45 +++++++++++----------- src/basic/path-util.h | 4 +- src/core/automount.c | 2 +- src/core/machine-id-setup.c | 2 +- src/core/mount-setup.c | 2 +- src/core/namespace.c | 4 +- src/delta/delta.c | 13 ++++--- src/gpt-auto-generator/gpt-auto-generator.c | 2 +- src/journal/journalctl.c | 18 ++++----- src/libsystemd/sd-device/sd-device.c | 2 +- src/login/logind-user.c | 2 +- src/nspawn/nspawn-mount.c | 40 +++++++++++++------ src/nspawn/nspawn.c | 6 +-- src/shared/condition.c | 2 +- src/shared/machine-pool.c | 2 +- src/test/test-copy.c | 2 +- src/test/test-path-util.c | 60 ++++++++++++++--------------- 21 files changed, 135 insertions(+), 117 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d2c322a0de..dede7cd224 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -224,25 +224,25 @@ int readlink_and_make_absolute(const char *p, char **r) { return 0; } -int readlink_and_canonicalize(const char *p, char **r) { +int readlink_and_canonicalize(const char *p, const char *root, char **ret) { char *t, *s; - int j; + int r; assert(p); - assert(r); + assert(ret); - j = readlink_and_make_absolute(p, &t); - if (j < 0) - return j; + r = readlink_and_make_absolute(p, &t); + if (r < 0) + return r; - s = canonicalize_file_name(t); - if (s) { + r = chase_symlinks(t, root, &s); + if (r < 0) + /* If we can't follow up, then let's return the original string, slightly cleaned up. */ + *ret = path_kill_slashes(t); + else { + *ret = s; free(t); - *r = s; - } else - *r = t; - - path_kill_slashes(*r); + } return 0; } diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 31df47cf1e..01c3301d71 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -39,7 +39,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret); int readlink_malloc(const char *p, char **r); int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); -int readlink_and_canonicalize(const char *p, char **r); +int readlink_and_canonicalize(const char *p, const char *root, char **r); int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 5d37fb48be..4a72d4700a 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -29,6 +29,7 @@ #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "hashmap.h" #include "mount-util.h" #include "parse-util.h" @@ -205,9 +206,10 @@ fallback_fstat: } /* flags can be AT_SYMLINK_FOLLOW or 0 */ -int path_is_mount_point(const char *t, int flags) { - _cleanup_close_ int fd = -1; +int path_is_mount_point(const char *t, const char *root, int flags) { _cleanup_free_ char *canonical = NULL, *parent = NULL; + _cleanup_close_ int fd = -1; + int r; assert(t); @@ -219,9 +221,9 @@ int path_is_mount_point(const char *t, int flags) { * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we * look at needs to be /usr, not /. */ if (flags & AT_SYMLINK_FOLLOW) { - canonical = canonicalize_file_name(t); - if (!canonical) - return -errno; + r = chase_symlinks(t, root, &canonical); + if (r < 0) + return r; t = canonical; } @@ -473,7 +475,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { return r; /* Deal with mount points that are obstructed by a later mount */ - r = path_is_mount_point(x, 0); + r = path_is_mount_point(x, NULL, 0); if (r == -ENOENT || r == 0) continue; if (r < 0) diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h index 4f305df19f..b840956d63 100644 --- a/src/basic/mount-util.h +++ b/src/basic/mount-util.h @@ -30,7 +30,7 @@ #include "missing.h" int fd_is_mount_point(int fd, const char *filename, int flags); -int path_is_mount_point(const char *path, int flags); +int path_is_mount_point(const char *path, const char *root, int flags); int repeat_unmount(const char *path, int flags); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 5cdac50c68..475a73d292 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -220,10 +220,11 @@ int path_strv_make_absolute_cwd(char **l) { return 0; } -char **path_strv_resolve(char **l, const char *prefix) { +char **path_strv_resolve(char **l, const char *root) { char **s; unsigned k = 0; bool enomem = false; + int r; if (strv_isempty(l)) return l; @@ -233,17 +234,17 @@ char **path_strv_resolve(char **l, const char *prefix) { * changes on failure. */ STRV_FOREACH(s, l) { - char *t, *u; _cleanup_free_ char *orig = NULL; + char *t, *u; if (!path_is_absolute(*s)) { free(*s); continue; } - if (prefix) { + if (root) { orig = *s; - t = strappend(prefix, orig); + t = prefix_root(root, orig); if (!t) { enomem = true; continue; @@ -251,28 +252,26 @@ char **path_strv_resolve(char **l, const char *prefix) { } else t = *s; - errno = 0; - u = canonicalize_file_name(t); - if (!u) { - if (errno == ENOENT) { - if (prefix) { - u = orig; - orig = NULL; - free(t); - } else - u = t; - } else { + r = chase_symlinks(t, root, &u); + if (r == -ENOENT) { + if (root) { + u = orig; + orig = NULL; free(t); - if (errno == ENOMEM || errno == 0) - enomem = true; + } else + u = t; + } else if (r < 0) { + free(t); - continue; - } - } else if (prefix) { + if (r == -ENOMEM) + enomem = true; + + continue; + } else if (root) { char *x; free(t); - x = path_startswith(u, prefix); + x = path_startswith(u, root); if (x) { /* restore the slash if it was lost */ if (!startswith(x, "/")) @@ -304,12 +303,12 @@ char **path_strv_resolve(char **l, const char *prefix) { return l; } -char **path_strv_resolve_uniq(char **l, const char *prefix) { +char **path_strv_resolve_uniq(char **l, const char *root) { if (strv_isempty(l)) return l; - if (!path_strv_resolve(l, prefix)) + if (!path_strv_resolve(l, root)) return NULL; return strv_uniq(l); diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 66545f52d9..d2bc0d3b8e 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -66,8 +66,8 @@ static inline bool path_equal_ptr(const char *a, const char *b) { }) int path_strv_make_absolute_cwd(char **l); -char** path_strv_resolve(char **l, const char *prefix); -char** path_strv_resolve_uniq(char **l, const char *prefix); +char** path_strv_resolve(char **l, const char *root); +char** path_strv_resolve_uniq(char **l, const char *root); int find_binary(const char *name, char **filename); diff --git a/src/core/automount.c b/src/core/automount.c index 5fa6eb7b18..8ff1ca90f7 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -783,7 +783,7 @@ static int automount_start(Unit *u) { assert(a); assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); - if (path_is_mount_point(a->where, 0) > 0) { + if (path_is_mount_point(a->where, NULL, 0) > 0) { log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where); return -EEXIST; } diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 76dfcfa6d7..c83bb561c7 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -199,7 +199,7 @@ int machine_id_commit(const char *root) { etc_machine_id = prefix_roota(root, "/etc/machine-id"); - r = path_is_mount_point(etc_machine_id, 0); + r = path_is_mount_point(etc_machine_id, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id); if (r == 0) { diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index ca63a93e8b..6338067d7e 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -159,7 +159,7 @@ static int mount_one(const MountPoint *p, bool relabel) { if (relabel) (void) label_fix(p->where, true, true); - r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW); if (r < 0 && r != -ENOENT) { log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); return (p->mode & MNT_FATAL) ? r : 0; diff --git a/src/core/namespace.c b/src/core/namespace.c index e9ad26bfc3..9954b9e97d 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -596,7 +596,7 @@ static int apply_mount( case READONLY: case READWRITE: - r = path_is_mount_point(bind_mount_path(m), 0); + r = path_is_mount_point(bind_mount_path(m), NULL, 0); if (r < 0) return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m)); if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */ @@ -860,7 +860,7 @@ int setup_namespace( if (root_directory) { /* Turn directory into bind mount, if it isn't one yet */ - r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW); if (r < 0) goto finish; if (r == 0) { diff --git a/src/delta/delta.c b/src/delta/delta.c index 04de75475d..70ff793304 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -87,14 +87,15 @@ static enum { static int equivalent(const char *a, const char *b) { _cleanup_free_ char *x = NULL, *y = NULL; + int r; - x = canonicalize_file_name(a); - if (!x) - return -errno; + r = chase_symlinks(a, NULL, &x); + if (r < 0) + return r; - y = canonicalize_file_name(b); - if (!y) - return -errno; + r = chase_symlinks(b, NULL, &y); + if (r < 0) + return r; return path_equal(x, y); } diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 52cde493e5..0f95f0d813 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -252,7 +252,7 @@ static bool path_is_busy(const char *where) { int r; /* already a mountpoint; generators run during reload */ - r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW); if (r > 0) return false; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 22cab67824..d0d4524c9f 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -938,21 +938,21 @@ static int add_matches(sd_journal *j, char **args) { have_term = false; } else if (path_is_absolute(*i)) { - _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL; - const char *path; + _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL; struct stat st; - p = canonicalize_file_name(*i); - path = p ?: *i; + r = chase_symlinks(*i, NULL, &p); + if (r < 0) + return log_error_errno(r, "Couldn't canonicalize path: %m"); - if (lstat(path, &st) < 0) + if (lstat(p, &st) < 0) return log_error_errno(errno, "Couldn't stat file: %m"); if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) { - if (executable_is_script(path, &interpreter) > 0) { + if (executable_is_script(p, &interpreter) > 0) { _cleanup_free_ char *comm; - comm = strndup(basename(path), 15); + comm = strndup(basename(p), 15); if (!comm) return log_oom(); @@ -968,7 +968,7 @@ static int add_matches(sd_journal *j, char **args) { return log_oom(); } } else { - t = strappend("_EXE=", path); + t = strappend("_EXE=", p); if (!t) return log_oom(); } @@ -979,7 +979,7 @@ static int add_matches(sd_journal *j, char **args) { r = sd_journal_add_match(j, t2, 0); } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { - r = add_matches_for_device(j, path); + r = add_matches_for_device(j, p); if (r < 0) return r; } else { diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 411453e08d..1081979bf9 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -164,7 +164,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { } if (verify) { - r = readlink_and_canonicalize(_syspath, &syspath); + r = readlink_and_canonicalize(_syspath, NULL, &syspath); if (r == -ENOENT) /* the device does not exist (any more?) */ return -ENODEV; diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 0d1417ea16..888a97c2fc 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -338,7 +338,7 @@ static int user_mkdir_runtime_path(User *u) { if (r < 0) return log_error_errno(r, "Failed to create /run/user: %m"); - if (path_is_mount_point(u->runtime_path, 0) <= 0) { + if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) { _cleanup_free_ char *t = NULL; (void) mkdir_label(u->runtime_path, 0700); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 91cb0861d3..d72b0a0b9b 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -418,7 +418,7 @@ int mount_all(const char *dest, if (!where) return log_oom(); - r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(where, dest, AT_SYMLINK_FOLLOW); if (r < 0 && r != -ENOENT) return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); @@ -726,14 +726,19 @@ static int get_controllers(Set *subsystems) { return 0; } -static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, - CGroupUnified unified_requested, bool read_only) { +static int mount_legacy_cgroup_hierarchy( + const char *dest, + const char *controller, + const char *hierarchy, + CGroupUnified unified_requested, + bool read_only) { + const char *to, *fstype, *opts; int r; to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy); - r = path_is_mount_point(to, 0); + r = path_is_mount_point(to, dest, 0); if (r < 0 && r != -ENOENT) return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to); if (r > 0) @@ -773,8 +778,13 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle /* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */ static int mount_legacy_cgns_supported( - CGroupUnified unified_requested, bool userns, uid_t uid_shift, - uid_t uid_range, const char *selinux_apifs_context) { + const char *dest, + CGroupUnified unified_requested, + bool userns, + uid_t uid_shift, + uid_t uid_range, + const char *selinux_apifs_context) { + _cleanup_set_free_free_ Set *controllers = NULL; const char *cgroup_root = "/sys/fs/cgroup", *c; int r; @@ -782,7 +792,7 @@ static int mount_legacy_cgns_supported( (void) mkdir_p(cgroup_root, 0755); /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ - r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW); if (r < 0) return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); if (r == 0) { @@ -871,8 +881,12 @@ skip_controllers: /* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */ static int mount_legacy_cgns_unsupported( const char *dest, - CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, + CGroupUnified unified_requested, + bool userns, + uid_t uid_shift, + uid_t uid_range, const char *selinux_apifs_context) { + _cleanup_set_free_free_ Set *controllers = NULL; const char *cgroup_root; int r; @@ -882,7 +896,7 @@ static int mount_legacy_cgns_unsupported( (void) mkdir_p(cgroup_root, 0755); /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ - r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW); if (r < 0) return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); if (r == 0) { @@ -975,7 +989,7 @@ static int mount_unified_cgroups(const char *dest) { (void) mkdir_p(p, 0755); - r = path_is_mount_point(p, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW); if (r < 0) return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p); if (r > 0) { @@ -995,14 +1009,16 @@ static int mount_unified_cgroups(const char *dest) { int mount_cgroups( const char *dest, CGroupUnified unified_requested, - bool userns, uid_t uid_shift, uid_t uid_range, + bool userns, + uid_t uid_shift, + uid_t uid_range, const char *selinux_apifs_context, bool use_cgns) { if (unified_requested >= CGROUP_UNIFIED_ALL) return mount_unified_cgroups(dest); else if (use_cgns) - return mount_legacy_cgns_supported(unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); + return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 2770770cd0..77b6be95e2 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1666,7 +1666,7 @@ static int setup_journal(const char *directory) { p = strjoina("/var/log/journal/", id); q = prefix_roota(directory, p); - if (path_is_mount_point(p, 0) > 0) { + if (path_is_mount_point(p, NULL, 0) > 0) { if (try) return 0; @@ -1674,7 +1674,7 @@ static int setup_journal(const char *directory) { return -EEXIST; } - if (path_is_mount_point(q, 0) > 0) { + if (path_is_mount_point(q, NULL, 0) > 0) { if (try) return 0; @@ -4132,7 +4132,7 @@ int main(int argc, char *argv[]) { * the specified is not a mount point we * create the new snapshot in the parent * directory, just next to it. */ - r = path_is_mount_point(arg_directory, 0); + r = path_is_mount_point(arg_directory, NULL, 0); if (r < 0) { log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory); goto finish; diff --git a/src/shared/condition.c b/src/shared/condition.c index 525e65aedf..0b77d2c22d 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -399,7 +399,7 @@ static int condition_test_path_is_mount_point(Condition *c) { assert(c->parameter); assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); - return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0; + return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0; } static int condition_test_path_is_read_write(Condition *c) { diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 23890c63a0..c581bdeb79 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -225,7 +225,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { return 1; } - if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) { + if (path_is_mount_point("/var/lib/machines", NULL, AT_SYMLINK_FOLLOW) > 0) { log_debug("/var/lib/machines is already a mount point, not creating loopback file for it."); return 0; } diff --git a/src/test/test-copy.c b/src/test/test-copy.c index 91d2a0bcd4..e65516f080 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -144,7 +144,7 @@ static void test_copy_tree(void) { assert_se((f = strjoin(original_dir, *p))); assert_se((l = strjoin(copy_dir, *link))); - assert_se(readlink_and_canonicalize(l, &target) == 0); + assert_se(readlink_and_canonicalize(l, NULL, &target) == 0); assert_se(path_equal(f, target)); } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index a6a09a0031..22df20a1eb 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -337,17 +337,17 @@ static void test_path_is_mount_point(void) { _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL; _cleanup_free_ char *dir2 = NULL, *dir2file = NULL; - assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/", 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", AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/proc", 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", AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point("/proc/1", 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", AT_SYMLINK_FOLLOW) > 0); - assert_se(path_is_mount_point("/sys", 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: @@ -381,10 +381,10 @@ static void test_path_is_mount_point(void) { assert_se(link1); assert_se(symlink("file2", link2) == 0); - assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(file1, 0) == 0); - assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(link1, 0) == 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"); @@ -400,10 +400,10 @@ static void test_path_is_mount_point(void) { assert_se(dir2); assert_se(mkdir(dir2, 0755) == 0); - assert_se(path_is_mount_point(dir1, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dir1, 0) == 0); - assert_se(path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dirlink1, 0) == 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"); @@ -412,10 +412,10 @@ static void test_path_is_mount_point(void) { assert_se(fd > 0); close(fd); - assert_se(path_is_mount_point(dir1file, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dir1file, 0) == 0); - assert_se(path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW) == 0); - assert_se(path_is_mount_point(dirlink1file, 0) == 0); + 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) { @@ -423,10 +423,10 @@ static void test_path_is_mount_point(void) { /* files */ /* capture results in vars, to avoid dangling mounts on failure */ - rf = path_is_mount_point(file2, 0); - rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW); - rlf = path_is_mount_point(link2, 0); - rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW); + 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); @@ -444,13 +444,13 @@ static void test_path_is_mount_point(void) { assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0); - rf = path_is_mount_point(dir1, 0); - rt = path_is_mount_point(dir1, AT_SYMLINK_FOLLOW); - rlf = path_is_mount_point(dirlink1, 0); - rlt = path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW); + 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, 0); - rl1t = path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW); + rl1f = path_is_mount_point(dirlink1file, NULL, 0); + rl1t = path_is_mount_point(dirlink1file, NULL, AT_SYMLINK_FOLLOW); assert_se(umount(dir1) == 0); -- cgit v1.2.3-54-g00ecf From 3f342ec4b028914fa558855609cd932364558c05 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 24 Nov 2016 21:03:36 +0100 Subject: nspawn: properly handle image/directory paths that are symlinks This resolves any paths specified on --directory=, --template=, and --image= before using them. This makes sure nspawn can be used correctly on symlinked images and directory trees. Fixes: #2001 --- src/nspawn/nspawn.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 77b6be95e2..7749a460eb 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2656,6 +2656,25 @@ static int determine_names(void) { return 0; } +static int chase_symlinks_and_update(char **p) { + char *chased; + int r; + + assert(p); + + if (!*p) + return 0; + + r = chase_symlinks(*p, NULL, &chased); + if (r < 0) + return log_error_errno(r, "Failed to resolve path %s: %m", *p); + + free(*p); + *p = chased; + + return 0; +} + static int determine_uid_shift(const char *directory) { int r; @@ -4126,6 +4145,10 @@ int main(int argc, char *argv[]) { if (arg_ephemeral) { _cleanup_free_ char *np = NULL; + r = chase_symlinks_and_update(&arg_directory); + if (r < 0) + goto finish; + /* If the specified path is a mount point we * generate the new snapshot immediately * inside it under a random name. However if @@ -4181,6 +4204,10 @@ int main(int argc, char *argv[]) { } if (arg_template) { + r = chase_symlinks_and_update(&arg_template); + if (r < 0) + goto finish; + r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | @@ -4199,6 +4226,10 @@ int main(int argc, char *argv[]) { log_info("Populated %s from template %s.", arg_directory, arg_template); } } + + r = chase_symlinks_and_update(&arg_directory); + if (r < 0) + goto finish; } if (arg_start_mode == START_BOOT) { @@ -4222,6 +4253,10 @@ int main(int argc, char *argv[]) { assert(arg_image); assert(!arg_template); + r = chase_symlinks_and_update(&arg_image); + if (r < 0) + goto finish; + if (arg_ephemeral) { _cleanup_free_ char *np = NULL; -- cgit v1.2.3-54-g00ecf From 8cd328d82e800fc6e2fe810500eea025ffbd93c1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2016 01:00:31 +0100 Subject: nspawn: accept --ephemeral --template= as alternative for --ephemeral --directory= As suggested in PR #3667. This PR simply ensures that --template= can be used as alternative to --directory= when --ephemeral is used, following the logic that for ephemeral options the source directory is actually a template. This does not deprecate usage of --directory= with --ephemeral, as I am not convinced the old logic wouldn't make sense. Fixes: #3667 --- src/nspawn/nspawn.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 7749a460eb..624c4f6a40 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1133,6 +1133,16 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_ephemeral && arg_template && !arg_directory) { + /* User asked for ephemeral execution but specified --template= instead of --directory=. Semantically + * such an invocation makes some sense, see https://github.com/systemd/systemd/issues/3667. Let's + * accept this here, and silently make "--ephemeral --template=" equivalent to "--ephemeral + * --directory=". */ + + arg_directory = arg_template; + arg_template = NULL; + } + if (arg_template && !(arg_directory || arg_machine)) { log_error("--template= needs --directory= or --machine=."); return -EINVAL; -- cgit v1.2.3-54-g00ecf From 5639193139792b48665b9c5d7d7ca165af124be6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2016 18:58:31 +0100 Subject: nspawn: use realloc_multiply() where it makes sense --- src/nspawn/nspawn-mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index d72b0a0b9b..9d76f4a381 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -47,7 +47,7 @@ CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) { assert(t >= 0); assert(t < _CUSTOM_MOUNT_TYPE_MAX); - c = realloc(*l, (*n + 1) * sizeof(CustomMount)); + c = realloc_multiply(*l, (*n + 1), sizeof(CustomMount)); if (!c) return NULL; -- cgit v1.2.3-54-g00ecf From 4da92e58570a8a864eb031ae372fc4a611e7faf1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2016 18:59:06 +0100 Subject: nspawn: coding style: don't mix variable declarations and function calls --- src/nspawn/nspawn-mount.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 9d76f4a381..cbc5b93c2a 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -464,12 +464,14 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl const char *p = options; unsigned long flags = *mount_flags; char *opts = NULL; + int r; assert(options); for (;;) { _cleanup_free_ char *word = NULL; - int r = extract_first_word(&p, &word, ",", 0); + + r = extract_first_word(&p, &word, ",", 0); if (r < 0) return log_error_errno(r, "Failed to extract mount option: %m"); if (r == 0) -- cgit v1.2.3-54-g00ecf From fc4b68e557307b04d42bcef3181fef00b7558177 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2016 18:59:39 +0100 Subject: fs-util: add chase_symlinks_prefix() and extend comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chase_symlinks() currently expects a fully qualified, absolute path, relative to the host's root as first argument. Which is useful in many ways, and similar to the paths unlink(), rename(), open(), … expect. Sometimes it's however useful to first prefix the specified path with the specified root directory. Add a new call chase_symlinks_prefix() for this, that is a simple wrapper. --- src/basic/fs-util.c | 25 ++++++++++++++++++++++--- src/basic/fs-util.h | 3 ++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index dede7cd224..30e1b2a674 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -610,15 +610,24 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following * symlinks relative to a root directory, instead of the root of the host. * - * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly - * this means the path parameter passed in is not prefixed by it. + * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following + * relative symlinks to ensure they cannot be used to "escape" the root directory. (For cases where this is + * attempted -EINVAL is returned.). The path parameter passed shall *not* be prefixed by it. * * Algorithmically this operates on two path buffers: "done" are the components of the path we already * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * at a minimum. */ + * at a minimum. + * + * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got + * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this + * function what to do when encountering a symlink with an absolute path as directory: prefix it by the + * specified path. + * + * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the + * passed root. */ r = path_make_absolute_cwd(path, &buffer); if (r < 0) @@ -780,3 +789,13 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { return 0; } + +int chase_symlinks_prefix(const char *path, const char *root, char **ret) { + const char *t; + + /* Same as chase_symlinks(), but prefixes 'path' by 'root' first. */ + + t = prefix_roota(root, path); + + return chase_symlinks(t, root, ret); +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 01c3301d71..2d039e3b98 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -78,4 +78,5 @@ union inotify_event_buffer { int inotify_add_watch_fd(int fd, int what, uint32_t mask); -int chase_symlinks(const char *path, const char *_root, char **ret); +int chase_symlinks(const char *path_with_prefix, const char *root, char **ret); +int chase_symlinks_prefix(const char *path_without_prefix, const char *root, char **ret); -- cgit v1.2.3-54-g00ecf From 68cf43c3154cd18bfdf46df07f854d8272062ab9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2016 19:01:36 +0100 Subject: nspawn: use chase_symlinks() on all paths specified via --tmpfs=, --bind= and so on Fixes: #2860 --- src/nspawn/nspawn-mount.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index cbc5b93c2a..914e43da98 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -495,10 +495,10 @@ static int parse_mount_bind_options(const char *options, unsigned long *mount_fl } static int mount_bind(const char *dest, CustomMount *m) { - struct stat source_st, dest_st; - const char *where; + + _cleanup_free_ char *mount_opts = NULL, *where = NULL; unsigned long mount_flags = MS_BIND | MS_REC; - _cleanup_free_ char *mount_opts = NULL; + struct stat source_st, dest_st; int r; assert(m); @@ -512,7 +512,9 @@ static int mount_bind(const char *dest, CustomMount *m) { if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); - where = prefix_roota(dest, m->destination); + r = chase_symlinks_prefix(m->destination, dest, &where); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s: %m", m->destination); if (stat(where, &dest_st) >= 0) { if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { @@ -563,14 +565,16 @@ static int mount_tmpfs( bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context) { - const char *where, *options; - _cleanup_free_ char *buf = NULL; + const char *options; + _cleanup_free_ char *buf = NULL, *where = NULL; int r; assert(dest); assert(m); - where = prefix_roota(dest, m->destination); + r = chase_symlinks_prefix(m->destination, dest, &where); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s: %m", m->destination); r = mkdir_p_label(where, 0755); if (r < 0 && r != -EEXIST) @@ -600,14 +604,17 @@ static char *joined_and_escaped_lower_dirs(char * const *lower) { } static int mount_overlay(const char *dest, CustomMount *m) { - _cleanup_free_ char *lower = NULL; - const char *where, *options; + + _cleanup_free_ char *lower = NULL, *where = NULL; + const char *options; int r; assert(dest); assert(m); - where = prefix_roota(dest, m->destination); + r = chase_symlinks_prefix(m->destination, dest, &where); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s: %m", m->destination); r = mkdir_label(where, 0755); if (r < 0 && r != -EEXIST) -- cgit v1.2.3-54-g00ecf From df878e682d009bc3c0842499fedc44703a7e7ede Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 26 Nov 2016 22:18:58 -0500 Subject: test-fs-util: add a test case with repeated ".." parts that would escape the root --- src/test/test-fs-util.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 53a3cdc663..7c6deb5416 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -60,6 +60,8 @@ static void test_chase_symlinks(void) { p = strjoina(temp, "/start"); assert_se(symlink("top/dot/dotdota", p) >= 0); + /* Paths that use symlinks underneath the "root" */ + r = chase_symlinks(p, NULL, &result); assert_se(r >= 0); assert_se(path_equal(result, "/usr")); @@ -88,6 +90,31 @@ static void test_chase_symlinks(void) { assert_se(r >= 0); assert_se(path_equal(result, temp)); + /* 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, &result); + assert_se(r == -EINVAL); + + p = strjoina(temp, "/6dotsusr"); + assert_se(symlink("../../../usr", p) >= 0); + + result = mfree(result); + r = chase_symlinks(p, temp, &result); + assert_se(r == -EINVAL); + + p = strjoina(temp, "/top/8dotsusr"); + assert_se(symlink("../../../../usr", p) >= 0); + + result = mfree(result); + r = chase_symlinks(p, temp, &result); + assert_se(r == -EINVAL); + + /* Paths that contain repeated slashes */ + p = strjoina(temp, "/slashslash"); assert_se(symlink("///usr///", p) >= 0); @@ -101,6 +128,8 @@ static void test_chase_symlinks(void) { assert_se(r >= 0); assert_se(path_equal(result, q)); + /* Paths using . */ + result = mfree(result); r = chase_symlinks("/etc/./.././", NULL, &result); assert_se(r >= 0); @@ -114,6 +143,8 @@ static void test_chase_symlinks(void) { r = chase_symlinks("/etc/machine-id/foo", NULL, &result); assert_se(r == -ENOTDIR); + /* Path that loops back to self */ + result = mfree(result); p = strjoina(temp, "/recursive-symlink"); assert_se(symlink("recursive-symlink", p) >= 0); -- cgit v1.2.3-54-g00ecf From a4eaf3cf822dae1d076dfc98afc3ab0d53871dac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 15:54:42 +0100 Subject: fs-util: change chase_symlinks() behaviour in regards to escaping the root dir Previously, we'd generate an EINVAL error if it is attempted to escape a root directory with relative ".." symlinks. With this commit this is changed so that ".." from the root directory is a NOP, following the kernel's own behaviour where /.. is equivalent to /. As suggested by @keszybz. --- src/basic/fs-util.c | 12 +++++++----- src/test/test-fs-util.c | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 30e1b2a674..c20faf67b0 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -611,8 +611,8 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { * symlinks relative to a root directory, instead of the root of the host. * * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. (For cases where this is - * attempted -EINVAL is returned.). The path parameter passed shall *not* be prefixed by it. + * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed + * shall *not* be prefixed by it. * * Algorithmically this operates on two path buffers: "done" are the components of the path we already * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to @@ -674,18 +674,20 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { _cleanup_free_ char *parent = NULL; int fd_parent = -1; + /* If we already are at the top, then going up will not change anything. This is in-line with + * how the kernel handles this. */ if (isempty(done) || path_equal(done, "/")) - return -EINVAL; + continue; parent = dirname_malloc(done); if (!parent) return -ENOMEM; - /* Don't allow this to leave the root dir */ + /* Don't allow this to leave the root dir. */ if (root && path_startswith(done, root) && !path_startswith(parent, root)) - return -EINVAL; + continue; free_and_replace(done, parent); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 7c6deb5416..792ad46847 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -97,21 +97,21 @@ static void test_chase_symlinks(void) { result = mfree(result); r = chase_symlinks(p, temp, &result); - assert_se(r == -EINVAL); + assert_se(r == 0 && path_equal(result, temp)); p = strjoina(temp, "/6dotsusr"); assert_se(symlink("../../../usr", p) >= 0); result = mfree(result); r = chase_symlinks(p, temp, &result); - assert_se(r == -EINVAL); + assert_se(r == 0 && path_equal(result, q)); p = strjoina(temp, "/top/8dotsusr"); assert_se(symlink("../../../../usr", p) >= 0); result = mfree(result); r = chase_symlinks(p, temp, &result); - assert_se(r == -EINVAL); + assert_se(r == 0 && path_equal(result, q)); /* Paths that contain repeated slashes */ @@ -137,7 +137,7 @@ static void test_chase_symlinks(void) { result = mfree(result); r = chase_symlinks("/etc/./.././", "/etc", &result); - assert_se(r == -EINVAL); + assert_se(r == 0 && path_equal(result, "/etc")); result = mfree(result); r = chase_symlinks("/etc/machine-id/foo", NULL, &result); -- cgit v1.2.3-54-g00ecf From c4f4fce79e157800212f3cba1b21870097030e81 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 16:49:30 +0100 Subject: fs-util: add flags parameter to chase_symlinks() Let's remove chase_symlinks_prefix() and instead introduce a flags parameter to chase_symlinks(), with a flag CHASE_PREFIX_ROOT that exposes the behaviour of chase_symlinks_prefix(). --- src/basic/fs-util.c | 34 ++++++++++++++-------------------- src/basic/fs-util.h | 7 +++++-- src/basic/mount-util.c | 2 +- src/basic/path-util.c | 2 +- src/core/namespace.c | 2 +- src/delta/delta.c | 6 +++--- src/journal/journalctl.c | 2 +- src/nspawn/nspawn-mount.c | 6 +++--- src/nspawn/nspawn.c | 2 +- src/systemctl/systemctl.c | 2 +- src/test/test-fs-util.c | 28 ++++++++++++++-------------- 11 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index c20faf67b0..0a3e983631 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -235,7 +235,7 @@ int readlink_and_canonicalize(const char *p, const char *root, char **ret) { if (r < 0) return r; - r = chase_symlinks(t, root, &s); + r = chase_symlinks(t, root, 0, &s); if (r < 0) /* If we can't follow up, then let's return the original string, slightly cleaned up. */ *ret = path_kill_slashes(t); @@ -598,7 +598,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } -int chase_symlinks(const char *path, const char *_root, char **ret) { +int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; _cleanup_close_ int fd = -1; unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ @@ -611,8 +611,9 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { * symlinks relative to a root directory, instead of the root of the host. * * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed - * shall *not* be prefixed by it. + * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is + * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first + * prefixed accordingly. * * Algorithmically this operates on two path buffers: "done" are the components of the path we already * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to @@ -629,16 +630,19 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the * passed root. */ - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - if (_root) { - r = path_make_absolute_cwd(_root, &root); + if (original_root) { + r = path_make_absolute_cwd(original_root, &root); if (r < 0) return r; + + if (flags & CHASE_PREFIX_ROOT) + path = prefix_roota(root, path); } + r = path_make_absolute_cwd(path, &buffer); + if (r < 0) + return r; + fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); if (fd < 0) return -errno; @@ -791,13 +795,3 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { return 0; } - -int chase_symlinks_prefix(const char *path, const char *root, char **ret) { - const char *t; - - /* Same as chase_symlinks(), but prefixes 'path' by 'root' first. */ - - t = prefix_roota(root, path); - - return chase_symlinks(t, root, ret); -} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 2d039e3b98..ee3d6bf7af 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -78,5 +78,8 @@ union inotify_event_buffer { int inotify_add_watch_fd(int fd, int what, uint32_t mask); -int chase_symlinks(const char *path_with_prefix, const char *root, char **ret); -int chase_symlinks_prefix(const char *path_without_prefix, const char *root, char **ret); +enum { + CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ +}; + +int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 4a72d4700a..352c3505fb 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -221,7 +221,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) { * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we * look at needs to be /usr, not /. */ if (flags & AT_SYMLINK_FOLLOW) { - r = chase_symlinks(t, root, &canonical); + r = chase_symlinks(t, root, 0, &canonical); if (r < 0) return r; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 475a73d292..9a51e0d8bc 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -252,7 +252,7 @@ char **path_strv_resolve(char **l, const char *root) { } else t = *s; - r = chase_symlinks(t, root, &u); + r = chase_symlinks(t, root, 0, &u); if (r == -ENOENT) { if (root) { u = orig; diff --git a/src/core/namespace.c b/src/core/namespace.c index 9954b9e97d..aca47a4d2f 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -665,7 +665,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned _cleanup_free_ char *chased = NULL; int k; - k = chase_symlinks(bind_mount_path(f), root_directory, &chased); + k = chase_symlinks(bind_mount_path(f), root_directory, 0, &chased); if (k < 0) { /* Get only real errors */ if (r >= 0 && (k != -ENOENT || !f->ignore)) diff --git a/src/delta/delta.c b/src/delta/delta.c index 70ff793304..107b105fde 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -89,11 +89,11 @@ static int equivalent(const char *a, const char *b) { _cleanup_free_ char *x = NULL, *y = NULL; int r; - r = chase_symlinks(a, NULL, &x); + r = chase_symlinks(a, NULL, 0, &x); if (r < 0) return r; - r = chase_symlinks(b, NULL, &y); + r = chase_symlinks(b, NULL, 0, &y); if (r < 0) return r; @@ -361,7 +361,7 @@ static int should_skip_prefix(const char* p) { int r; _cleanup_free_ char *target = NULL; - r = chase_symlinks(p, NULL, &target); + r = chase_symlinks(p, NULL, 0, &target); if (r < 0) return r; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d0d4524c9f..10d3ff3b45 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -941,7 +941,7 @@ static int add_matches(sd_journal *j, char **args) { _cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL; struct stat st; - r = chase_symlinks(*i, NULL, &p); + r = chase_symlinks(*i, NULL, 0, &p); if (r < 0) return log_error_errno(r, "Couldn't canonicalize path: %m"); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 914e43da98..de9e40d55f 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -512,7 +512,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); - r = chase_symlinks_prefix(m->destination, dest, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); @@ -572,7 +572,7 @@ static int mount_tmpfs( assert(dest); assert(m); - r = chase_symlinks_prefix(m->destination, dest, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); @@ -612,7 +612,7 @@ static int mount_overlay(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks_prefix(m->destination, dest, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 624c4f6a40..1a625e142e 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2675,7 +2675,7 @@ static int chase_symlinks_and_update(char **p) { if (!*p) return 0; - r = chase_symlinks(*p, NULL, &chased); + r = chase_symlinks(*p, NULL, 0, &chased); if (r < 0) return log_error_errno(r, "Failed to resolve path %s: %m", *p); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index c3cb8ef108..67516cf5fb 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2487,7 +2487,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un if (!path) return log_oom(); - r = chase_symlinks(path, arg_root, &lpath); + r = chase_symlinks(path, arg_root, 0, &lpath); if (r == -ENOENT) continue; if (r == -ENOMEM) diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 792ad46847..fac3a1d089 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -62,18 +62,18 @@ static void test_chase_symlinks(void) { /* Paths that use symlinks underneath the "root" */ - r = chase_symlinks(p, NULL, &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, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r == -ENOENT); q = strjoina(temp, "/usr"); assert_se(mkdir(q, 0700) >= 0); - r = chase_symlinks(p, temp, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r >= 0); assert_se(path_equal(result, q)); @@ -81,12 +81,12 @@ static void test_chase_symlinks(void) { assert_se(symlink("/", p) >= 0); result = mfree(result); - r = chase_symlinks(p, NULL, &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, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r >= 0); assert_se(path_equal(result, temp)); @@ -96,21 +96,21 @@ static void test_chase_symlinks(void) { assert_se(symlink("../../..", p) >= 0); result = mfree(result); - r = chase_symlinks(p, temp, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r == 0 && path_equal(result, temp)); p = strjoina(temp, "/6dotsusr"); assert_se(symlink("../../../usr", p) >= 0); result = mfree(result); - r = chase_symlinks(p, temp, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r == 0 && path_equal(result, q)); p = strjoina(temp, "/top/8dotsusr"); assert_se(symlink("../../../../usr", p) >= 0); result = mfree(result); - r = chase_symlinks(p, temp, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r == 0 && path_equal(result, q)); /* Paths that contain repeated slashes */ @@ -119,28 +119,28 @@ static void test_chase_symlinks(void) { assert_se(symlink("///usr///", p) >= 0); result = mfree(result); - r = chase_symlinks(p, NULL, &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, &result); + r = chase_symlinks(p, temp, 0, &result); assert_se(r >= 0); assert_se(path_equal(result, q)); /* Paths using . */ result = mfree(result); - r = chase_symlinks("/etc/./.././", NULL, &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", &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, &result); + r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result); assert_se(r == -ENOTDIR); /* Path that loops back to self */ @@ -148,7 +148,7 @@ static void test_chase_symlinks(void) { result = mfree(result); p = strjoina(temp, "/recursive-symlink"); assert_se(symlink("recursive-symlink", p) >= 0); - r = chase_symlinks(p, NULL, &result); + r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ELOOP); assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); -- cgit v1.2.3-54-g00ecf From a9fb08670f392e044c345b623e70287f6c6a02c1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 18:02:45 +0100 Subject: fs-util: add new CHASE_NON_EXISTING flag to chase_symlinks() This new flag controls whether to consider a problem if the referenced path doesn't actually exist. If specified it's OK if the final file doesn't exist. Note that this permits one or more final components of the path not to exist, but these must not contain "../" for safety reasons (or, to be extra safe, neither "./" and a couple of others, i.e. what path_is_safe() permits). This new flag is useful when resolving paths before issuing an mkdir() or open(O_CREAT) on a path, as it permits that the file or directory is created later. The return code of chase_symlinks() is changed to return 1 if the file exists, and 0 if it doesn't. The latter is only returned in case CHASE_NON_EXISTING is set. --- src/basic/fs-util.c | 22 +++++++++++++++++-- src/basic/fs-util.h | 1 + src/test/test-fs-util.c | 56 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 0a3e983631..0ca4656fdd 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -602,6 +602,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; _cleanup_close_ int fd = -1; unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ + bool exists = true; char *todo; int r; @@ -707,8 +708,25 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* Otherwise let's see what this is. */ child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) + if (child < 0) { + + if (errno == ENOENT && + (flags & CHASE_NON_EXISTING) && + (isempty(todo) || path_is_safe(todo))) { + + /* If CHASE_NON_EXISTING is set, and the path does not exist, then that's OK, return + * what we got so far. But don't allow this if the remaining path contains "../ or "./" + * or something else weird. */ + + if (!strextend(&done, first, todo, NULL)) + return -ENOMEM; + + exists = false; + break; + } + return -errno; + } if (fstat(child, &st) < 0) return -errno; @@ -793,5 +811,5 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, *ret = done; done = NULL; - return 0; + return exists; } diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index ee3d6bf7af..3931534a42 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -80,6 +80,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ + CHASE_NON_EXISTING = 2, /* If set, it's OK if the path doesn't actually exist. */ }; int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index fac3a1d089..2570bc5859 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -63,7 +63,7 @@ static void test_chase_symlinks(void) { /* Paths that use symlinks underneath the "root" */ r = chase_symlinks(p, NULL, 0, &result); - assert_se(r >= 0); + assert_se(r > 0); assert_se(path_equal(result, "/usr")); result = mfree(result); @@ -71,10 +71,15 @@ static void test_chase_symlinks(void) { assert_se(r == -ENOENT); q = strjoina(temp, "/usr"); + + r = chase_symlinks(p, temp, CHASE_NON_EXISTING, &result); + assert_se(r == 0); + assert_se(path_equal(result, q)); + assert_se(mkdir(q, 0700) >= 0); r = chase_symlinks(p, temp, 0, &result); - assert_se(r >= 0); + assert_se(r > 0); assert_se(path_equal(result, q)); p = strjoina(temp, "/slash"); @@ -82,12 +87,12 @@ static void test_chase_symlinks(void) { result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); - assert_se(r >= 0); + 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(r > 0); assert_se(path_equal(result, temp)); /* Paths that would "escape" outside of the "root" */ @@ -97,21 +102,21 @@ static void test_chase_symlinks(void) { result = mfree(result); r = chase_symlinks(p, temp, 0, &result); - assert_se(r == 0 && path_equal(result, temp)); + assert_se(r > 0 && path_equal(result, temp)); 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)); + assert_se(r > 0 && path_equal(result, q)); 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)); + assert_se(r > 0 && path_equal(result, q)); /* Paths that contain repeated slashes */ @@ -120,24 +125,24 @@ static void test_chase_symlinks(void) { result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); - assert_se(r >= 0); + 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(r > 0); assert_se(path_equal(result, q)); /* Paths using . */ result = mfree(result); r = chase_symlinks("/etc/./.././", NULL, 0, &result); - assert_se(r >= 0); + 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")); + assert_se(r > 0 && path_equal(result, "/etc")); result = mfree(result); r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result); @@ -151,6 +156,35 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ELOOP); + /* Path which doesn't exist */ + + p = strjoina(temp, "/idontexist"); + r = chase_symlinks(p, NULL, 0, &result); + assert_se(r == -ENOENT); + + r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + assert_se(r == 0); + assert_se(path_equal(result, p)); + result = mfree(result); + + p = strjoina(temp, "/idontexist/meneither"); + r = chase_symlinks(p, NULL, 0, &result); + assert_se(r == -ENOENT); + + r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + assert_se(r == 0); + assert_se(path_equal(result, p)); + result = mfree(result); + + /* Path which doesn't exist, but contains weird stuff */ + + p = strjoina(temp, "/idontexist/.."); + r = chase_symlinks(p, NULL, 0, &result); + assert_se(r == -ENOENT); + + r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + assert_se(r == -ENOENT); + assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } -- cgit v1.2.3-54-g00ecf From 8ce48cf0f899c50a7dfba8efe45a729f61df060a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 18:13:11 +0100 Subject: nspawn: use the new CHASE_NON_EXISTING flag when resolving mount points This restores the ability to implicitly create files/directories to mount specified mount points on. --- src/nspawn/nspawn-mount.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index de9e40d55f..a9f5ad8af3 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -414,11 +414,11 @@ int mount_all(const char *dest, if (!ro && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO)) continue; - where = prefix_root(dest, mount_table[k].where); - if (!where) - return log_oom(); + r = chase_symlinks(mount_table[k].where, dest, CHASE_NON_EXISTING|CHASE_PREFIX_ROOT, &where); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s: %m", mount_table[k].where); - r = path_is_mount_point(where, dest, AT_SYMLINK_FOLLOW); + r = path_is_mount_point(where, NULL, 0); if (r < 0 && r != -ENOENT) return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); @@ -512,11 +512,14 @@ static int mount_bind(const char *dest, CustomMount *m) { if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); + if (r > 0) { /* Path exists already? */ + + if (stat(where, &dest_st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", where); - if (stat(where, &dest_st) >= 0) { if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { log_error("Cannot bind mount directory %s on file %s.", m->source, where); return -EINVAL; @@ -527,7 +530,7 @@ static int mount_bind(const char *dest, CustomMount *m) { return -EINVAL; } - } else if (errno == ENOENT) { + } else { /* Path doesn't exist yet? */ r = mkdir_parents_label(where, 0755); if (r < 0) return log_error_errno(r, "Failed to make parents of %s: %m", where); @@ -543,8 +546,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (r < 0) return log_error_errno(r, "Failed to create mount point %s: %m", where); - } else - return log_error_errno(errno, "Failed to stat %s: %m", where); + } r = mount_verbose(LOG_ERR, m->source, where, NULL, mount_flags, mount_opts); if (r < 0) @@ -572,13 +574,14 @@ static int mount_tmpfs( assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); - - r = mkdir_p_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); + if (r == 0) { /* Doesn't exist yet? */ + r = mkdir_p_label(where, 0755); + if (r < 0) + return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); + } r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false, selinux_apifs_context, &buf); if (r < 0) @@ -612,13 +615,14 @@ static int mount_overlay(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s: %m", m->destination); - - r = mkdir_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); + if (r == 0) { /* Doesn't exist yet? */ + r = mkdir_label(where, 0755); + if (r < 0) + return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); + } (void) mkdir_p_label(m->source, 0755); -- cgit v1.2.3-54-g00ecf From 8d4aa2bb32c0fb9ab136167a1594bb4e4a09c8d5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 18:16:43 +0100 Subject: nspawn: make use of CHASE_NON_EXISTING when locking image If --template= is used on an image, then the image might not exist initially. We can use CHASE_NON_EXISTING to properly lock the image already before it exists. Let's do so. --- src/nspawn/nspawn.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 1a625e142e..9a308688e1 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2666,7 +2666,7 @@ static int determine_names(void) { return 0; } -static int chase_symlinks_and_update(char **p) { +static int chase_symlinks_and_update(char **p, unsigned flags) { char *chased; int r; @@ -2675,7 +2675,7 @@ static int chase_symlinks_and_update(char **p) { if (!*p) return 0; - r = chase_symlinks(*p, NULL, 0, &chased); + r = chase_symlinks(*p, NULL, flags, &chased); if (r < 0) return log_error_errno(r, "Failed to resolve path %s: %m", *p); @@ -4155,7 +4155,7 @@ int main(int argc, char *argv[]) { if (arg_ephemeral) { _cleanup_free_ char *np = NULL; - r = chase_symlinks_and_update(&arg_directory); + r = chase_symlinks_and_update(&arg_directory, 0); if (r < 0) goto finish; @@ -4203,6 +4203,10 @@ int main(int argc, char *argv[]) { remove_directory = true; } else { + r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NON_EXISTING : 0); + if (r < 0) + goto finish; + r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); if (r == -EBUSY) { log_error_errno(r, "Directory tree %s is currently busy.", arg_directory); @@ -4214,7 +4218,7 @@ int main(int argc, char *argv[]) { } if (arg_template) { - r = chase_symlinks_and_update(&arg_template); + r = chase_symlinks_and_update(&arg_template, 0); if (r < 0) goto finish; @@ -4236,10 +4240,6 @@ int main(int argc, char *argv[]) { log_info("Populated %s from template %s.", arg_directory, arg_template); } } - - r = chase_symlinks_and_update(&arg_directory); - if (r < 0) - goto finish; } if (arg_start_mode == START_BOOT) { @@ -4263,7 +4263,7 @@ int main(int argc, char *argv[]) { assert(arg_image); assert(!arg_template); - r = chase_symlinks_and_update(&arg_image); + r = chase_symlinks_and_update(&arg_image, 0); if (r < 0) goto finish; -- cgit v1.2.3-54-g00ecf From 48cbe5f80bd3cb1bb93b00168d8f446493be0280 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 23:47:24 +0100 Subject: nspawn: use -ENOMEM instead of log_oom() in one case The function is of the "library" kind and doesn't log ENOMEM in all other cases, hence fix the one outlier. --- src/nspawn/nspawn-mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index a9f5ad8af3..f8a79eca2f 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -132,7 +132,7 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); if (!m) - return log_oom(); + return -ENOMEM; m->source = source; m->destination = destination; -- cgit v1.2.3-54-g00ecf From ad85779a50bb26c80a4c9b597b486d86cef07c79 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 23:47:58 +0100 Subject: nspawn: split out overlayfs argument parsing into a function of its own Add overlay_mount_parse() similar in style to tmpfs_mount_parse() and bind_mount_parse(). --- src/nspawn/nspawn-mount.c | 55 +++++++++++++++++++++++++++++++++++++++ src/nspawn/nspawn-mount.h | 2 ++ src/nspawn/nspawn.c | 66 +++++------------------------------------------ 3 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index f8a79eca2f..6782c74b80 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -180,6 +180,61 @@ int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) { return 0; } +int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) { + _cleanup_free_ char *upper = NULL, *destination = NULL; + _cleanup_strv_free_ char **lower = NULL; + CustomMount *m; + unsigned k = 0; + char **i; + int r; + + r = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + + STRV_FOREACH(i, lower) { + if (!path_is_absolute(*i)) + return -EINVAL; + + k++; + } + + if (k < 2) + return -EADDRNOTAVAIL; + if (k == 2) { + /* If two parameters are specified, + * the first one is the lower, the + * second one the upper directory. And + * we'll also define the destination + * mount point the same as the upper. */ + upper = lower[1]; + lower[1] = NULL; + + destination = strdup(upper); + if (!destination) + return -ENOMEM; + + } else { + upper = lower[k - 2]; + destination = lower[k - 1]; + lower[k - 2] = NULL; + } + + m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY); + if (!m) + return -ENOMEM; + + m->destination = destination; + m->source = upper; + m->lower = lower; + m->read_only = read_only; + + upper = destination = NULL; + lower = NULL; + + return 0; +} + static int tmpfs_patch_options( const char *options, bool userns, diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 74aee7ee7f..83acd01e00 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -61,8 +61,10 @@ typedef struct CustomMount { CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); void custom_mount_free_all(CustomMount *l, unsigned n); + int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s); +int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); int custom_mount_compare(const void *a, const void *b); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 9a308688e1..e739df7f72 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -789,69 +789,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_OVERLAY: - case ARG_OVERLAY_RO: { - _cleanup_free_ char *upper = NULL, *destination = NULL; - _cleanup_strv_free_ char **lower = NULL; - CustomMount *m; - unsigned n = 0; - char **i; - - r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - else if (r < 0) { - log_error("Invalid overlay specification: %s", optarg); - return r; - } - - STRV_FOREACH(i, lower) { - if (!path_is_absolute(*i)) { - log_error("Overlay path %s is not absolute.", *i); - return -EINVAL; - } - - n++; - } - - if (n < 2) { - log_error("--overlay= needs at least two colon-separated directories specified."); - return -EINVAL; - } - - if (n == 2) { - /* If two parameters are specified, - * the first one is the lower, the - * second one the upper directory. And - * we'll also define the destination - * mount point the same as the upper. */ - upper = lower[1]; - lower[1] = NULL; - - destination = strdup(upper); - if (!destination) - return log_oom(); - - } else { - upper = lower[n - 2]; - destination = lower[n - 1]; - lower[n - 2] = NULL; - } - - m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY); - if (!m) - return log_oom(); - - m->destination = destination; - m->source = upper; - m->lower = lower; - m->read_only = c == ARG_OVERLAY_RO; - - upper = destination = NULL; - lower = NULL; + case ARG_OVERLAY_RO: + r = overlay_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_OVERLAY_RO); + if (r == -EADDRNOTAVAIL) + return log_error_errno(r, "--overlay(-ro)= needs at least two colon-separated directories specified."); + if (r < 0) + return log_error_errno(r, "Failed to parse --overlay(-ro)= argument %s: %m", optarg); arg_settings_mask |= SETTING_CUSTOM_MOUNTS; break; - } case 'E': { char **n; -- cgit v1.2.3-54-g00ecf From 7b4318b6a51e996fcd17855bbef21f8ad3c08da9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Nov 2016 23:55:04 +0100 Subject: nspawn: add ability to configure overlay mounts to .nspawn files Fixes: #4634 --- man/systemd.nspawn.xml | 11 +++++++++++ src/nspawn/nspawn-gperf.gperf | 2 ++ src/nspawn/nspawn-settings.c | 26 ++++++++++++++++++++++++++ src/nspawn/nspawn-settings.h | 1 + 4 files changed, 40 insertions(+) diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index b1344d6c10..7143188356 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -334,6 +334,17 @@ is privileged (see above). + + Overlay= + OverlayReadOnly= + + Adds an overlay mount point. Takes a colon-separated list of paths. This option may be used + multiple times to configure multiple overlay mounts. This option is equivalent to the command line switches + and , see + systemd-nspawn1 for details + about the specific options supported. This setting is privileged (see above). + + PrivateUsersChown= diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 3231a48d5a..c0fa4bfa1f 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -33,6 +33,8 @@ Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, Files.Bind, config_parse_bind, 0, 0 Files.BindReadOnly, config_parse_bind, 1, 0 Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Files.Overlay, config_parse_overlay, 0, 0 +Files.OverlayReadOnly, config_parse_overlay, 1, 0 Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 09c8f070ba..22b74d88e4 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -293,6 +293,32 @@ int config_parse_tmpfs( return 0; } +int config_parse_overlay( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue); + + return 0; +} + int config_parse_veth_extra( const char *unit, const char *filename, diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 231e6d7266..4bd0c642df 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -111,6 +111,7 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_overlay(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_network_zone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -- cgit v1.2.3-54-g00ecf From e28c7cd0665364bb910fe2cead882623c23c28ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Nov 2016 15:07:43 +0100 Subject: tree-wide: set SA_RESTART for signal handlers we install We already set it in most cases, but make sure to set it in all others too, and document that that's a good idea. --- CODING_STYLE | 5 +++++ src/activate/activate.c | 2 +- src/nspawn/nspawn.c | 2 +- src/udev/udevadm-monitor.c | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CODING_STYLE b/CODING_STYLE index e89b3c67e5..ed61ea9d28 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -429,3 +429,8 @@ and Linux/GNU-specific APIs, we generally prefer the POSIX APIs. If there aren't, we are happy to use GNU or Linux APIs, and expect non-GNU implementations of libc to catch up with glibc. + +- Whenever installing a signal handler, make sure to set SA_RESTART for it, so + that interrupted system calls are automatically restarted, and we minimize + hassles with handling EINTR (in particular as EINTR handling is pretty broken + on Linux). diff --git a/src/activate/activate.c b/src/activate/activate.c index a0cfc22000..6ebd820410 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -339,7 +339,7 @@ static void sigchld_hdl(int sig) { static int install_chld_handler(void) { static const struct sigaction act = { - .sa_flags = SA_NOCLDSTOP, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, .sa_handler = sigchld_hdl, }; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e739df7f72..ea50be25ea 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3632,7 +3632,7 @@ static int run(int master, static const struct sigaction sa = { .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, }; _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index f631834341..11abebb351 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -143,7 +143,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) { /* set signal handlers */ act.sa_handler = sig_handler; - act.sa_flags = SA_RESTART; + act.sa_flags = SA_RESTART|SA_RESTART; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigemptyset(&mask); -- cgit v1.2.3-54-g00ecf From 86c0dd4a71c29d300632d39cf56ddb0fd0facb24 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Nov 2016 16:02:47 +0100 Subject: nspawn: permit prefixing of source paths in --bind= and --overlay= with "+" If a source path is prefixed with "+" it is taken relative to the container's root directory instead of the host. This permits easily establishing bind and overlay mounts based on data from the container rather than the host. This also reworks custom_mounts_prepare(), and turns it into two functions: one custom_mount_check_all() that remains in nspawn.c but purely verifies the validity of the custom mounts configured. And one called custom_mount_prepare_all() that actually does the preparation step, sorts the custom mounts, resolves relative paths, and allocates temporary directories as necessary. --- man/systemd-nspawn.xml | 29 ++++---- src/nspawn/nspawn-mount.c | 166 +++++++++++++++++++++++++++++++------------ src/nspawn/nspawn-mount.h | 4 +- src/nspawn/nspawn-register.c | 5 ++ src/nspawn/nspawn.c | 26 ++----- 5 files changed, 148 insertions(+), 82 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index dbbf9890c8..84fa9cadef 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -740,21 +740,17 @@ - Bind mount a file or directory from the host - into the container. Takes one of: a path argument — in which - case the specified path will be mounted from the host to the - same path in the container —, or a colon-separated pair of - paths — in which case the first specified path is the source - in the host, and the second path is the destination in the - container —, or a colon-separated triple of source path, - destination path and mount options. Mount options are - comma-separated and currently, only "rbind" and "norbind" - are allowed. Defaults to "rbind". Backslash escapes are interpreted, so - \: may be used to embed colons in either path. - This option may be specified multiple times for - creating multiple independent bind mount points. The - option creates read-only bind - mounts. + Bind mount a file or directory from the host into the container. Takes one of: a path + argument — in which case the specified path will be mounted from the host to the same path in the container —, + or a colon-separated pair of paths — in which case the first specified path is the source in the host, and the + second path is the destination in the container —, or a colon-separated triple of source path, destination path + and mount options. The source path may optionally be prefixed with a + character. If so, the + source path is taken relative to the images root directory. This permits setting up bind mounts within the + container image. Mount options are comma-separated and currently, only "rbind" and "norbind" are allowed, + controlling whether to create a recursive or a regular bind mount. Defaults to "rbind". Backslash escapes are + interpreted, so \: may be used to embed colons in either path. This option may be specified + multiple times for creating multiple independent bind mount points. The option + creates read-only bind mounts. @@ -808,6 +804,9 @@ point for the overlay file system in the container. At least two paths have to be specified. + The source paths may optionally be prefixed with + character. If so they are taken + relative to the image's root directory. + For details about overlay file systems, see overlayfs.txt. Note that the semantics of overlay file systems are substantially diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 6782c74b80..291a88a9ac 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -81,7 +81,7 @@ void custom_mount_free_all(CustomMount *l, unsigned n) { free(l); } -int custom_mount_compare(const void *a, const void *b) { +static int custom_mount_compare(const void *a, const void *b) { const CustomMount *x = a, *y = b; int r; @@ -97,6 +97,91 @@ int custom_mount_compare(const void *a, const void *b) { return 0; } +static bool source_path_is_valid(const char *p) { + assert(p); + + if (*p == '+') + p++; + + return path_is_absolute(p); +} + +static char *resolve_source_path(const char *dest, const char *source) { + + if (!source) + return NULL; + + if (source[0] == '+') + return prefix_root(dest, source + 1); + + return strdup(source); +} + +int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n) { + unsigned i; + int r; + + /* Prepare all custom mounts. This will make source we know all temporary directories. This is called in the + * parent process, so that we know the temporary directories to remove on exit before we fork off the + * children. */ + + assert(l || n == 0); + + /* Order the custom mounts, and make sure we have a working directory */ + qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare); + + for (i = 0; i < n; i++) { + CustomMount *m = l + i; + + if (m->source) { + char *s; + + s = resolve_source_path(dest, m->source); + if (!s) + return log_oom(); + + free(m->source); + m->source = s; + } + + if (m->type == CUSTOM_MOUNT_OVERLAY) { + char **j; + + STRV_FOREACH(j, m->lower) { + char *s; + + s = resolve_source_path(dest, *j); + if (!s) + return log_oom(); + + free(*j); + *j = s; + } + + if (m->work_dir) { + char *s; + + s = resolve_source_path(dest, m->work_dir); + if (!s) + return log_oom(); + + free(m->work_dir); + m->work_dir = s; + } else { + assert(m->source); + + r = tempfn_random(m->source, NULL, &m->work_dir); + if (r < 0) + return log_error_errno(r, "Failed to acquire working directory: %m"); + } + + (void) mkdir_label(m->work_dir, 0700); + } + } + + return 0; +} + int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) { _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; const char *p = s; @@ -111,22 +196,19 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only return r; if (r == 0) return -EINVAL; - if (r == 1) { - destination = strdup(source); + destination = strdup(source[0] == '+' ? source+1 : source); if (!destination) return -ENOMEM; } - if (r == 2 && !isempty(p)) { opts = strdup(p); if (!opts) return -ENOMEM; } - if (!path_is_absolute(source)) + if (!source_path_is_valid(source)) return -EINVAL; - if (!path_is_absolute(destination)) return -EINVAL; @@ -184,40 +266,43 @@ int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_o _cleanup_free_ char *upper = NULL, *destination = NULL; _cleanup_strv_free_ char **lower = NULL; CustomMount *m; - unsigned k = 0; - char **i; - int r; - - r = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - - STRV_FOREACH(i, lower) { - if (!path_is_absolute(*i)) - return -EINVAL; - - k++; - } + int k; + k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (k < 0) + return k; if (k < 2) return -EADDRNOTAVAIL; if (k == 2) { - /* If two parameters are specified, - * the first one is the lower, the - * second one the upper directory. And - * we'll also define the destination - * mount point the same as the upper. */ + /* If two parameters are specified, the first one is the lower, the second one the upper directory. And + * we'll also define the destination mount point the same as the upper. */ + + if (!source_path_is_valid(lower[0]) || + !source_path_is_valid(lower[1])) + return -EINVAL; + upper = lower[1]; lower[1] = NULL; - destination = strdup(upper); + destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */ if (!destination) return -ENOMEM; - } else { - upper = lower[k - 2]; + int i; + + /* If more than two parameters are specified, the last one is the destination, the second to last one + * the "upper", and all before that the "lower" directories. */ + + for (i = 0; i < k - 1; i++) + if (!source_path_is_valid(lower[i])) + return -EINVAL; + destination = lower[k - 1]; + upper = lower[k - 2]; lower[k - 2] = NULL; + + if (!path_is_absolute(destination)) + return -EINVAL; } m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY); @@ -556,6 +641,7 @@ static int mount_bind(const char *dest, CustomMount *m) { struct stat source_st, dest_st; int r; + assert(dest); assert(m); if (m->options) { @@ -646,7 +732,7 @@ static int mount_tmpfs( return mount_verbose(LOG_ERR, "tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options); } -static char *joined_and_escaped_lower_dirs(char * const *lower) { +static char *joined_and_escaped_lower_dirs(char **lower) { _cleanup_strv_free_ char **sv = NULL; sv = strv_copy(lower); @@ -663,7 +749,7 @@ static char *joined_and_escaped_lower_dirs(char * const *lower) { static int mount_overlay(const char *dest, CustomMount *m) { - _cleanup_free_ char *lower = NULL, *where = NULL; + _cleanup_free_ char *lower = NULL, *where = NULL, *escaped_source = NULL; const char *options; int r; @@ -685,23 +771,15 @@ static int mount_overlay(const char *dest, CustomMount *m) { if (!lower) return log_oom(); - if (m->read_only) { - _cleanup_free_ char *escaped_source = NULL; - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); + escaped_source = shell_escape(m->source, ",:"); + if (!escaped_source) + return log_oom(); + if (m->read_only) options = strjoina("lowerdir=", escaped_source, ":", lower); - } else { - _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL; + else { + _cleanup_free_ char *escaped_work_dir = NULL; - assert(m->work_dir); - (void) mkdir_label(m->work_dir, 0700); - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); escaped_work_dir = shell_escape(m->work_dir, ",:"); if (!escaped_work_dir) return log_oom(); diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 83acd01e00..dc8be438ac 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -59,15 +59,13 @@ typedef struct CustomMount { } CustomMount; CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); - void custom_mount_free_all(CustomMount *l, unsigned n); +int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n); int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s); int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); -int custom_mount_compare(const void *a, const void *b); - int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); int mount_sysfs(const char *dest, MountSettingsMask mount_settings); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 06c56d9ec8..e3ab39faea 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -135,6 +135,11 @@ int register_machine( continue; r = is_device_node(cm->source); + if (r == -ENOENT) { + /* The bind source might only appear as the image is put together, hence don't complain */ + log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source); + continue; + } if (r < 0) return log_error_errno(r, "Failed to stat %s: %m", cm->source); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ea50be25ea..84c213785c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -280,14 +280,9 @@ static void help(void) { , program_invocation_short_name); } -static int custom_mounts_prepare(void) { +static int custom_mount_check_all(void) { unsigned i; - int r; - - /* Ensure the mounts are applied prefix first. */ - qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare); - /* Allocate working directories for the overlay file systems that need it */ for (i = 0; i < arg_n_custom_mounts; i++) { CustomMount *m = &arg_custom_mounts[i]; @@ -301,19 +296,6 @@ static int custom_mounts_prepare(void) { return -EINVAL; } } - - if (m->type != CUSTOM_MOUNT_OVERLAY) - continue; - - if (m->work_dir) - continue; - - if (m->read_only) - continue; - - r = tempfn_random(m->source, NULL, &m->work_dir); - if (r < 0) - return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source); } return 0; @@ -1147,6 +1129,10 @@ static int parse_argv(int argc, char *argv[]) { else arg_use_cgns = r; + r = custom_mount_check_all(); + if (r < 0) + return r; + return 1; } @@ -4284,7 +4270,7 @@ int main(int argc, char *argv[]) { remove_image = false; } - r = custom_mounts_prepare(); + r = custom_mount_prepare_all(arg_directory, arg_custom_mounts, arg_n_custom_mounts); if (r < 0) goto finish; -- cgit v1.2.3-54-g00ecf From c7a4890ce42b6c074ba622c4b82bfd3f738df736 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 30 Nov 2016 18:57:42 +0100 Subject: nspawn: optionally, automatically allocated --bind=/--overlay source from /var/tmp This extends the --bind= and --overlay= syntax so that an empty string as source/upper directory is taken as request to automatically allocate a temporary directory below /var/tmp, whose lifetime is bound to the nspawn runtime. In combination with the "+" path extension this permits a switch "--overlay=+/var::/var" in order to use the container's shipped /var, combine it with a writable temporary directory and mount it to the runtime /var of the container. --- man/systemd-nspawn.xml | 27 +++++++++++++++++---------- src/nspawn/nspawn-mount.c | 45 +++++++++++++++++++++++++++++++++++++++------ src/nspawn/nspawn-mount.h | 1 + 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 84fa9cadef..cd0a90d82f 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -741,16 +741,18 @@ Bind mount a file or directory from the host into the container. Takes one of: a path - argument — in which case the specified path will be mounted from the host to the same path in the container —, - or a colon-separated pair of paths — in which case the first specified path is the source in the host, and the - second path is the destination in the container —, or a colon-separated triple of source path, destination path + argument — in which case the specified path will be mounted from the host to the same path in the container, or + a colon-separated pair of paths — in which case the first specified path is the source in the host, and the + second path is the destination in the container, or a colon-separated triple of source path, destination path and mount options. The source path may optionally be prefixed with a + character. If so, the - source path is taken relative to the images root directory. This permits setting up bind mounts within the - container image. Mount options are comma-separated and currently, only "rbind" and "norbind" are allowed, - controlling whether to create a recursive or a regular bind mount. Defaults to "rbind". Backslash escapes are - interpreted, so \: may be used to embed colons in either path. This option may be specified - multiple times for creating multiple independent bind mount points. The option - creates read-only bind mounts. + source path is taken relative to the image's root directory. This permits setting up bind mounts within the + container image. The source path may be specified as empty string, in which case a temporary directory below + the host's /var/tmp directory is used. It is automatically removed when the container is + shut down. Mount options are comma-separated and currently, only and + are allowed, controlling whether to create a recursive or a regular bind + mount. Defaults to "rbind". Backslash escapes are interpreted, so \: may be used to embed + colons in either path. This option may be specified multiple times for creating multiple independent bind + mount points. The option creates read-only bind mounts. @@ -805,7 +807,12 @@ two paths have to be specified. The source paths may optionally be prefixed with + character. If so they are taken - relative to the image's root directory. + relative to the image's root directory. The uppermost source path may also be specified as empty string, in + which case a temporary directory below the host's /var/tmp is used. The directory is + removed automatically when the container is shut down. This behaviour is useful in order to make read-only + container directories writable while the container is running. For example, use the + --overlay=+/var::/var option in order to automatically overlay a writable temporary + directory on a read-only /var directory. For details about overlay file systems, see overlayfs.txt. Note diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 291a88a9ac..3a069cf0e0 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -75,6 +75,11 @@ void custom_mount_free_all(CustomMount *l, unsigned n) { free(m->work_dir); } + if (m->rm_rf_tmpdir) { + (void) rm_rf(m->rm_rf_tmpdir, REMOVE_ROOT|REMOVE_PHYSICAL); + free(m->rm_rf_tmpdir); + } + strv_free(m->lower); } @@ -142,6 +147,24 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, unsigned n) { free(m->source); m->source = s; + } else { + /* No source specified? In that case, use a throw-away temporary directory in /var/tmp */ + + m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX"); + if (!m->rm_rf_tmpdir) + return log_oom(); + + if (!mkdtemp(m->rm_rf_tmpdir)) { + m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir); + return log_error_errno(errno, "Failed to acquire temporary directory: %m"); + } + + m->source = strjoin(m->rm_rf_tmpdir, "/src"); + if (!m->source) + return log_oom(); + + if (mkdir(m->source, 0755) < 0) + return log_error_errno(errno, "Failed to create %s: %m", m->source); } if (m->type == CUSTOM_MOUNT_OVERLAY) { @@ -207,8 +230,11 @@ int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only return -ENOMEM; } - if (!source_path_is_valid(source)) + if (isempty(source)) + source = NULL; + else if (!source_path_is_valid(source)) return -EINVAL; + if (!path_is_absolute(destination)) return -EINVAL; @@ -288,19 +314,26 @@ int overlay_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_o if (!destination) return -ENOMEM; } else { - int i; + char **i; /* If more than two parameters are specified, the last one is the destination, the second to last one * the "upper", and all before that the "lower" directories. */ - for (i = 0; i < k - 1; i++) - if (!source_path_is_valid(lower[i])) - return -EINVAL; - destination = lower[k - 1]; upper = lower[k - 2]; lower[k - 2] = NULL; + STRV_FOREACH(i, lower) + if (!source_path_is_valid(*i)) + return -EINVAL; + + /* If the upper directory is unspecified, then let's create it automatically as a throw-away directory + * in /var/tmp */ + if (isempty(upper)) + upper = NULL; + else if (!source_path_is_valid(upper)) + return -EINVAL; + if (!path_is_absolute(destination)) return -EINVAL; } diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index dc8be438ac..467082a737 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -56,6 +56,7 @@ typedef struct CustomMount { char *options; char *work_dir; char **lower; + char *rm_rf_tmpdir; } CustomMount; CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); -- cgit v1.2.3-54-g00ecf From ec57bd426a1130e93946b39c20823c7420902695 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Dec 2016 12:40:23 +0100 Subject: nspawn: improve log messages When complaining about the inability to resolve a path, show the full path, not just the relative one. As suggested by @keszybz. --- src/nspawn/nspawn-mount.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 3a069cf0e0..9024ea1385 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -589,7 +589,7 @@ int mount_all(const char *dest, r = chase_symlinks(mount_table[k].where, dest, CHASE_NON_EXISTING|CHASE_PREFIX_ROOT, &where); if (r < 0) - return log_error_errno(r, "Failed to resolve %s: %m", mount_table[k].where); + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where); r = path_is_mount_point(where, NULL, 0); if (r < 0 && r != -ENOENT) @@ -688,7 +688,7 @@ static int mount_bind(const char *dest, CustomMount *m) { r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) - return log_error_errno(r, "Failed to resolve %s: %m", m->destination); + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r > 0) { /* Path exists already? */ if (stat(where, &dest_st) < 0) @@ -750,7 +750,7 @@ static int mount_tmpfs( r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) - return log_error_errno(r, "Failed to resolve %s: %m", m->destination); + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ r = mkdir_p_label(where, 0755); if (r < 0) @@ -791,7 +791,7 @@ static int mount_overlay(const char *dest, CustomMount *m) { r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); if (r < 0) - return log_error_errno(r, "Failed to resolve %s: %m", m->destination); + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ r = mkdir_label(where, 0755); if (r < 0) -- cgit v1.2.3-54-g00ecf From cb638b5e96465e695eaf34859ac762236b4bcb1e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 Dec 2016 12:49:23 +0100 Subject: util-lib: rename CHASE_NON_EXISTING → CHASE_NONEXISTENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As suggested by @keszybz --- src/basic/fs-util.c | 4 ++-- src/basic/fs-util.h | 2 +- src/nspawn/nspawn-mount.c | 8 ++++---- src/nspawn/nspawn.c | 2 +- src/test/test-fs-util.c | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 0ca4656fdd..b30cec4f92 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -711,10 +711,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (child < 0) { if (errno == ENOENT && - (flags & CHASE_NON_EXISTING) && + (flags & CHASE_NONEXISTENT) && (isempty(todo) || path_is_safe(todo))) { - /* If CHASE_NON_EXISTING is set, and the path does not exist, then that's OK, return + /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return * what we got so far. But don't allow this if the remaining path contains "../ or "./" * or something else weird. */ diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 3931534a42..0d925c6b84 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -80,7 +80,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NON_EXISTING = 2, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */ }; int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 9024ea1385..c9d5ac419e 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -587,7 +587,7 @@ int mount_all(const char *dest, if (!ro && (bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO)) continue; - r = chase_symlinks(mount_table[k].where, dest, CHASE_NON_EXISTING|CHASE_PREFIX_ROOT, &where); + r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where); @@ -686,7 +686,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r > 0) { /* Path exists already? */ @@ -748,7 +748,7 @@ static int mount_tmpfs( assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ @@ -789,7 +789,7 @@ static int mount_overlay(const char *dest, CustomMount *m) { assert(dest); assert(m); - r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NON_EXISTING, &where); + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); if (r == 0) { /* Doesn't exist yet? */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 84c213785c..ddd6a64ec6 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -4135,7 +4135,7 @@ int main(int argc, char *argv[]) { remove_directory = true; } else { - r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NON_EXISTING : 0); + r = chase_symlinks_and_update(&arg_directory, arg_template ? CHASE_NONEXISTENT : 0); if (r < 0) goto finish; diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 2570bc5859..b502cd0ad1 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -72,7 +72,7 @@ static void test_chase_symlinks(void) { q = strjoina(temp, "/usr"); - r = chase_symlinks(p, temp, CHASE_NON_EXISTING, &result); + r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, q)); @@ -162,7 +162,7 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); - r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, p)); result = mfree(result); @@ -171,7 +171,7 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); - r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, p)); result = mfree(result); @@ -182,7 +182,7 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); - r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result); + r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == -ENOENT); assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); -- cgit v1.2.3-54-g00ecf