]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
chase-symlinks: Add CHASE_EXTRACT_FILENAME flag
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 14 Mar 2023 17:37:55 +0000 (18:37 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 21 Mar 2023 15:08:29 +0000 (16:08 +0100)
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).

src/basic/chase-symlinks.c
src/basic/chase-symlinks.h
src/test/test-fs-util.c

index 5c2b56ea20a381bd4c86c9f910a0d62b91523824..16d36693b1044437f4d71de7ac1f105fb8e8ecde 100644 (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;
+                        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)
index 448471fa6de9a3c9ee559b7893c79d360b76a805..0064b88cb3ac1db62de1b2a754d7c1f42759565f 100644 (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);
index 3d73e8e5b1562b05350b6f6da54970e78e42d646..69c43485f1610f77e39289b8a09f136a80f04779 100644 (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);