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:
+30
-10
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user