mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
fs-util: add new CHASE_STEP flag to chase_symlinks()
If the flag is set only a single step of the normalization is executed, and the resulting path is returned. This allows callers to normalize piecemeal, taking into account every single intermediary path of the normalization.
This commit is contained in:
parent
12777909c9
commit
49eb36596b
@ -594,6 +594,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
|
||||
return -EINVAL;
|
||||
|
||||
if ((flags & (CHASE_STEP|CHASE_OPEN)) == (CHASE_STEP|CHASE_OPEN))
|
||||
return -EINVAL;
|
||||
|
||||
if (isempty(path))
|
||||
return -EINVAL;
|
||||
|
||||
@ -615,13 +618,34 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
* 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. */
|
||||
* specified path.
|
||||
*
|
||||
* There are three ways to invoke this function:
|
||||
*
|
||||
* 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned
|
||||
* in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set 0 is returned if the file
|
||||
* doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set >= 0 is returned if the destination was
|
||||
* found, -ENOENT if it doesn't.
|
||||
*
|
||||
* 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file
|
||||
* descriptor is returned as return value. This is useful to open files relative to some root
|
||||
* directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
|
||||
* fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with
|
||||
* CHASE_NONEXISTENT.
|
||||
*
|
||||
* 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
|
||||
* symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if
|
||||
* a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the
|
||||
* path is fully normalized, and == 0 for each normalization step. This may be combined with
|
||||
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
|
||||
*
|
||||
* */
|
||||
|
||||
/* A root directory of "/" or "" is identical to none */
|
||||
if (noop_root(original_root))
|
||||
original_root = NULL;
|
||||
|
||||
if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN)) == CHASE_OPEN) {
|
||||
if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
|
||||
/* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
|
||||
* and doesn't care about any of the other special features we provide either. */
|
||||
r = open(path, O_PATH|O_CLOEXEC);
|
||||
@ -718,6 +742,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
|
||||
free_and_replace(done, parent);
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
goto chased_one;
|
||||
|
||||
fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (fd_parent < 0)
|
||||
return -errno;
|
||||
@ -834,6 +861,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
free(buffer);
|
||||
todo = buffer = joined;
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
goto chased_one;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -872,7 +902,36 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
return 1;
|
||||
|
||||
return exists;
|
||||
|
||||
chased_one:
|
||||
|
||||
if (ret) {
|
||||
char *c;
|
||||
|
||||
if (done) {
|
||||
if (todo) {
|
||||
c = strjoin(done, todo);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
c = TAKE_PTR(done);
|
||||
} else {
|
||||
if (todo)
|
||||
c = strdup(todo);
|
||||
else
|
||||
c = strdup("/");
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_open(
|
||||
|
@ -75,6 +75,7 @@ enum {
|
||||
CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
|
||||
CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
|
||||
CHASE_TRAIL_SLASH = 1U << 5, /* If set, any trailing slash will be preserved */
|
||||
CHASE_STEP = 1U << 6, /* If set, just execute a single step of the normalization */
|
||||
};
|
||||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "util.h"
|
||||
|
||||
static void test_chase_symlinks(void) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
_cleanup_free_ char *result = NULL, *z = NULL, *w = NULL;
|
||||
char temp[] = "/tmp/test-chase.XXXXXX";
|
||||
const char *top, *p, *pslash, *q, *qslash;
|
||||
int r, pfd;
|
||||
@ -271,6 +271,49 @@ static void test_chase_symlinks(void) {
|
||||
assert_se(sd_id128_equal(a, b));
|
||||
}
|
||||
|
||||
/* Test CHASE_ONE */
|
||||
|
||||
p = strjoina(temp, "/start");
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
p = strjoina(temp, "/top/dot/dotdota");
|
||||
assert_se(streq(p, result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
p = strjoina(temp, "/top/./dotdota");
|
||||
assert_se(streq(p, result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
p = strjoina(temp, "/top/../a");
|
||||
assert_se(streq(p, result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
p = strjoina(temp, "/a");
|
||||
assert_se(streq(p, result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
p = strjoina(temp, "/b");
|
||||
assert_se(streq(p, result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks(p, NULL, CHASE_STEP, &result);
|
||||
assert_se(r == 0);
|
||||
assert_se(streq("/usr", result));
|
||||
result = mfree(result);
|
||||
|
||||
r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
|
||||
assert_se(r > 0);
|
||||
assert_se(streq("/usr", result));
|
||||
result = mfree(result);
|
||||
|
||||
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user