From: Daan De Meyer Date: Tue, 14 Mar 2023 17:37:55 +0000 (+0100) Subject: chase-symlinks: Add CHASE_EXTRACT_FILENAME flag X-Git-Tag: v254-rc1~964^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=63bfd52f48a772c86ff84e3a1ba1fcd19e0008f8;p=thirdparty%2Fsystemd.git chase-symlinks: Add CHASE_EXTRACT_FILENAME flag Useful in combination with CHASE_PARENT to get a directory file descriptor to the parent directory of the resolved path and the filename of the resolved path in the parent directory (which might not be the same as the filename of the input path because of symlinks). --- diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index 5c2b56ea20a..16d36693b10 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -89,6 +89,8 @@ int chase_symlinks_at( assert(path); assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); + assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME)); + assert(!FLAGS_SET(flags, CHASE_TRAIL_SLASH|CHASE_EXTRACT_FILENAME)); assert(dir_fd >= 0 || dir_fd == AT_FDCWD); /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ @@ -98,6 +100,9 @@ int chase_symlinks_at( if ((flags & CHASE_STEP)) assert(!ret_fd); + if ((flags & CHASE_EXTRACT_FILENAME)) + assert(ret_path); + if (isempty(path)) path = "."; @@ -403,6 +408,17 @@ int chase_symlinks_at( } if (ret_path) { + if (FLAGS_SET(flags, CHASE_EXTRACT_FILENAME) && done) { + _cleanup_free_ char *f = NULL; + + r = path_extract_filename(done, &f); + if (r < 0 && r != -EDESTADDRREQ) + return r; + + /* If we get EDESTADDRREQ we clear done and it will get reinitialized by the next block. */ + free_and_replace(done, f); + } + if (!done) { done = strdup(append_trail_slash ? "./" : "."); if (!done) @@ -520,19 +536,23 @@ int chase_symlinks( return r; if (ret_path) { - _cleanup_free_ char *q = NULL; + if (!FLAGS_SET(flags, CHASE_EXTRACT_FILENAME)) { + _cleanup_free_ char *q = NULL; - q = path_join(empty_to_root(root), p); - if (!q) - return -ENOMEM; + q = path_join(empty_to_root(root), p); + if (!q) + return -ENOMEM; - path_simplify(q); + path_simplify(q); - if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/.")) - if (!strextend(&q, "/")) - return -ENOMEM; + if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/.")) + if (!strextend(&q, "/")) + return -ENOMEM; - *ret_path = TAKE_PTR(q); + free_and_replace(p, q); + } + + *ret_path = TAKE_PTR(p); } if (ret_fd) diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 448471fa6de..0064b88cb3a 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -24,6 +24,7 @@ typedef enum ChaseSymlinksFlags { * full path is still stored in ret_path and only the returned * file descriptor will point to the parent directory. */ CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. */ + CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved 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 3d73e8e5b15..69c43485f16 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -517,6 +517,28 @@ TEST(chase_symlinks_at) { assert_se(chase_symlinks_at(tfd, "i/../p", CHASE_MKDIR_0755, NULL, NULL) == -ENOENT); + /* Test CHASE_FILENAME */ + + assert_se(chase_symlinks_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0); + assert_se(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW) >= 0); + assert_se(streq(result, "parent")); + fd = safe_close(fd); + result = mfree(result); + + assert_se(chase_symlinks_at(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd) >= 0); + assert_se(faccessat(fd, result, F_OK, 0) >= 0); + assert_se(streq(result, "chase")); + fd = safe_close(fd); + result = mfree(result); + + assert_se(chase_symlinks_at(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0); + assert_se(streq(result, ".")); + result = mfree(result); + + assert_se(chase_symlinks_at(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL) >= 0); + assert_se(streq(result, ".")); + result = mfree(result); + /* Test chase_symlinks_at_and_open() */ fd = chase_symlinks_at_and_open(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);