summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-01-09 12:29:12 +0100
committerDaan De Meyer <daan.j.demeyer@gmail.com>2023-03-06 13:42:43 +0100
commite864dfa6717fd63d290b867f44bd1c6d661a9047 (patch)
tree362ec7bd854fe660385c81c2d06f6d7ead00132b
parentchase-symlinks: Add CHASE_PARENT (diff)
downloadsystemd-e864dfa6717fd63d290b867f44bd1c6d661a9047.tar.xz
systemd-e864dfa6717fd63d290b867f44bd1c6d661a9047.zip
chase-symlinks: Add CHASE_MKDIR_0755
-rw-r--r--src/basic/chase-symlinks.c38
-rw-r--r--src/basic/chase-symlinks.h1
-rw-r--r--src/test/test-fs-util.c14
3 files changed, 38 insertions, 15 deletions
diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c
index cc0e263852..38fb582c88 100644
--- a/src/basic/chase-symlinks.c
+++ b/src/basic/chase-symlinks.c
@@ -98,6 +98,9 @@ int chase_symlinks_at(
if ((flags & CHASE_STEP) && ret_fd)
return -EINVAL;
+ if (FLAGS_SET(flags, CHASE_MKDIR_0755|CHASE_NONEXISTENT))
+ return -EINVAL;
+
if (isempty(path))
path = ".";
@@ -172,7 +175,7 @@ int chase_symlinks_at(
if (!(flags &
(CHASE_AT_RESOLVE_IN_ROOT|CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP|
- CHASE_PROHIBIT_SYMLINKS)) &&
+ CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755)) &&
!ret_path && ret_fd) {
/* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root
@@ -295,15 +298,15 @@ int chase_symlinks_at(
}
/* Otherwise let's see what this is. */
- child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
- if (child < 0) {
- if (errno == ENOENT &&
- (flags & CHASE_NONEXISTENT) &&
- (isempty(todo) || path_is_safe(todo))) {
- /* 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 something else weird. */
+ child = r = RET_NERRNO(openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH));
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ if (!isempty(todo) && !path_is_safe(todo))
+ return r;
+ if (flags & CHASE_NONEXISTENT) {
if (!path_extend(&done, first, todo))
return -ENOMEM;
@@ -311,7 +314,12 @@ int chase_symlinks_at(
break;
}
- return -errno;
+ if (!(flags & CHASE_MKDIR_0755))
+ return r;
+
+ child = open_mkdir_at(fd, first, O_CLOEXEC|O_PATH|O_EXCL, 0755);
+ if (child < 0)
+ return child;
}
if (fstat(child, &st) < 0)
@@ -390,7 +398,7 @@ int chase_symlinks_at(
close_and_replace(fd, child);
}
- if (flags & CHASE_PARENT) {
+ if (flags & (CHASE_PARENT|CHASE_MKDIR_0755)) {
r = fd_verify_directory(fd);
if (r < 0)
return r;
@@ -548,7 +556,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|CHASE_PARENT)) == 0) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 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)
@@ -590,7 +598,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|CHASE_PARENT)) == 0) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
/* Shortcut this call if none of the special features of this call are requested */
d = opendir(path);
if (!d)
@@ -635,7 +643,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|CHASE_PARENT)) == 0 && !ret_fd) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 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)
@@ -678,7 +686,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|CHASE_PARENT)) == 0 && !ret_fd) {
+ (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 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)
diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h
index 6aea8d6781..7308227c71 100644
--- a/src/basic/chase-symlinks.h
+++ b/src/basic/chase-symlinks.h
@@ -21,6 +21,7 @@ typedef enum ChaseSymlinksFlags {
* 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. */
+ CHASE_MKDIR_0755 = 1 << 11, /* Create any missing directories in 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 97b6bdabc8..ef7f41885a 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -486,6 +486,20 @@ TEST(chase_symlinks_at) {
assert_se(chase_symlinks_at(tfd, "chase", CHASE_NONEXISTENT|CHASE_PARENT, &result, NULL) >= 0);
assert_se(streq(result, "."));
result = mfree(result);
+
+ /* Test CHASE_MKDIR_0755 */
+
+ assert_se(chase_symlinks_at(tfd, "m/k/d/i/r", CHASE_MKDIR_0755, &result, NULL) >= 0);
+ assert_se(faccessat(tfd, "m/k/d/i/r", F_OK, 0) >= 0);
+ assert_se(streq(result, "m/k/d/i/r"));
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "m/../q", CHASE_MKDIR_0755, &result, NULL) >= 0);
+ assert_se(faccessat(tfd, "q", F_OK, 0) >= 0);
+ assert_se(streq(result, "q"));
+ result = mfree(result);
+
+ assert_se(chase_symlinks_at(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT);
}
TEST(unlink_noerrno) {