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).
This commit is contained in:
Daan De Meyer
2023-03-21 16:08:29 +01:00
parent aea3ca3613
commit 63bfd52f48
3 changed files with 53 additions and 10 deletions
+30 -10
View File
@@ -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;
path_simplify(q);
if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
if (!strextend(&q, "/"))
q = path_join(empty_to_root(root), p);
if (!q)
return -ENOMEM;
*ret_path = TAKE_PTR(q);
path_simplify(q);
if (FLAGS_SET(flags, CHASE_TRAIL_SLASH) && ENDSWITH_SET(path, "/", "/."))
if (!strextend(&q, "/"))
return -ENOMEM;
free_and_replace(p, q);
}
*ret_path = TAKE_PTR(p);
}
if (ret_fd)
+1
View File
@@ -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);
+22
View File
@@ -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);