summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2022-12-23 12:08:19 +0100
committerDaan De Meyer <daan.j.demeyer@gmail.com>2023-03-06 13:42:43 +0100
commit28aa650eb71d8381c25be805f93a2b3999cae633 (patch)
treed739ec43a5638851547487ce211ec2ce2b6f7c5b /src
parent7bf4057d795c84392fd90520460484d2560bce09 (diff)
downloadsystemd-28aa650eb71d8381c25be805f93a2b3999cae633.tar.gz
chase-symlinks: Add CHASE_PARENT
Let's simplify chasing the parent directory of some path by adding CHASE_PARENT.
Diffstat (limited to 'src')
-rw-r--r--src/basic/chase-symlinks.c39
-rw-r--r--src/basic/chase-symlinks.h27
-rw-r--r--src/test/test-fs-util.c24
3 files changed, 63 insertions, 27 deletions
diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c
index bab5a5b286..cc0e263852 100644
--- a/src/basic/chase-symlinks.c
+++ b/src/basic/chase-symlinks.c
@@ -101,6 +101,14 @@ int chase_symlinks_at(
if (isempty(path))
path = ".";
+ if (flags & CHASE_PARENT) {
+ r = path_extract_directory(path, &buffer);
+ if (r == -EDESTADDRREQ)
+ path = "."; /* If we don't have a parent directory, fall back to the dir_fd directory. */
+ else if (r < 0)
+ return r;
+ }
+
/* This function resolves symlinks of the path relative to the given directory file descriptor. If
* CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and a directory file descriptor is provided, symlinks
* are resolved relative to the given directory file descriptor. Otherwise, they are resolved
@@ -169,7 +177,7 @@ int chase_symlinks_at(
/* Shortcut the ret_fd 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 = openat(dir_fd, path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
+ r = openat(dir_fd, buffer ?: path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (r < 0)
return -errno;
@@ -177,9 +185,11 @@ int chase_symlinks_at(
return 0;
}
- buffer = strdup(path);
- if (!buffer)
- return -ENOMEM;
+ if (!buffer) {
+ buffer = strdup(path);
+ if (!buffer)
+ return -ENOMEM;
+ }
/* If we receive an absolute path together with AT_FDCWD, we need to return an absolute path, because
* a relative path would be interpreted relative to the current working directory. */
@@ -380,6 +390,12 @@ int chase_symlinks_at(
close_and_replace(fd, child);
}
+ if (flags & CHASE_PARENT) {
+ r = fd_verify_directory(fd);
+ if (r < 0)
+ return r;
+ }
+
if (ret_path) {
if (!done) {
done = strdup(append_trail_slash ? "./" : ".");
@@ -532,7 +548,7 @@ int chase_symlinks_and_open(
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
- (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS)) == 0) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
r = open(path, open_flags | (FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
if (r < 0)
@@ -574,7 +590,7 @@ int chase_symlinks_and_opendir(
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
- (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS)) == 0) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
d = opendir(path);
if (!d)
@@ -619,7 +635,7 @@ int chase_symlinks_and_stat(
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
- (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS)) == 0 && !ret_fd) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0 && !ret_fd) {
/* Shortcut this call if none of the special features of this call are requested */
if (fstatat(AT_FDCWD, path, ret_stat, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
@@ -662,7 +678,7 @@ int chase_symlinks_and_access(
return -EINVAL;
if (empty_or_root(root) && !ret_path &&
- (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS)) == 0 && !ret_fd) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT)) == 0 && !ret_fd) {
/* Shortcut this call if none of the special features of this call are requested */
if (faccessat(AT_FDCWD, path, access_mode, FLAGS_SET(chase_flags, CHASE_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) < 0)
@@ -729,20 +745,17 @@ int chase_symlinks_and_unlink(
int unlink_flags,
char **ret_path) {
- _cleanup_free_ char *p = NULL, *rp = NULL, *dir = NULL, *fname = NULL;
+ _cleanup_free_ char *p = NULL, *rp = NULL, *fname = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(path);
- r = path_extract_directory(path, &dir);
- if (r < 0)
- return r;
r = path_extract_filename(path, &fname);
if (r < 0)
return r;
- fd = chase_symlinks_and_open(dir, root, chase_flags, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
+ fd = chase_symlinks_and_open(path, root, chase_flags|CHASE_PARENT, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
if (fd < 0)
return fd;
diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h
index be5e2bb696..6aea8d6781 100644
--- a/src/basic/chase-symlinks.h
+++ b/src/basic/chase-symlinks.h
@@ -7,19 +7,20 @@
#include "stat-util.h"
typedef enum ChaseSymlinksFlags {
- CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
- CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
- CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
- CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
- CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
- CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
- CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
- * right-most component refers to symlink, return O_PATH fd of the symlink. */
- CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered.
- * Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
- CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
- * relative to the given directory fd instead of root. */
- CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
+ CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
+ CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
+ CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
+ CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
+ CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
+ * right-most component refers to symlink, return O_PATH fd of the symlink. */
+ CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered.
+ * Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
+ CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
+ * relative to the given directory fd instead of root. */
+ CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
+ CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 3fc5f88d26..97b6bdabc8 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -426,6 +426,18 @@ TEST(chase_symlinks) {
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+ /* Test CHASE_PARENT */
+
+ assert_se(chase_symlinks("/chase/parent", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
+ p = strjoina(temp, "/chase");
+ assert_se(streq(p, result));
+ result = mfree(result);
+ assert_se(chase_symlinks("/chase", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, &result, NULL) >= 0);
+ assert_se(streq(temp, result));
+ result = mfree(result);
+ assert_se(chase_symlinks("/", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
+ assert_se(chase_symlinks(".", temp, CHASE_PREFIX_ROOT|CHASE_PARENT|CHASE_NONEXISTENT, NULL, NULL) == -EADDRNOTAVAIL);
+
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
@@ -463,7 +475,17 @@ TEST(chase_symlinks_at) {
/* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
* host's root. */
- assert_se(chase_symlinks_at(tfd, "/qed", 0, &result, NULL) == -ENOENT);
+ assert_se(chase_symlinks_at(tfd, "/qed", 0, NULL, NULL) == -ENOENT);
+
+ /* Test CHASE_PARENT */
+
+ assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
+ assert_se(streq(result, "chase"));
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "chase", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
+ assert_se(streq(result, "."));
+ result = mfree(result);
}
TEST(unlink_noerrno) {