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. */ + 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; - if (_root) { - r = path_make_absolute_cwd(_root, &root); - 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);