]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
chase: optimize the special case where no root dir specified
authorLennart Poettering <lennart@poettering.net>
Sun, 28 Dec 2025 09:50:29 +0000 (10:50 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 17 Jan 2026 11:37:39 +0000 (12:37 +0100)
Now that we can recognize the root dir in chaseat() sanely, let's use it
top optimize the very common special case where we have no root dir to
consider, and directly call open_tree().

src/basic/chase.c
src/test/test-chase.c

index 969f88afd298444f10cef8b6ec2cf48a1373b085..323e46237543f058d0a32c115990238832b4cca5 100644 (file)
@@ -128,6 +128,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
         unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
         bool exists = true, append_trail_slash = false;
         struct stat st; /* stat obtained from fd */
+        bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */
         const char *todo;
         int r;
 
@@ -220,6 +221,33 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
 
         _cleanup_close_ int _dir_fd = -EBADF;
         if (dir_fd == XAT_FDROOT) {
+
+                /* Shortcut the common case where no root dir is specified, and no special flags are given to
+                 * a regular open() */
+                if (!ret_path &&
+                    (flags & (CHASE_STEP|CHASE_NO_AUTOFS|CHASE_NONEXISTENT|CHASE_SAFE|CHASE_WARN|CHASE_PROHIBIT_SYMLINKS|CHASE_PARENT|CHASE_MKDIR_0755)) == 0) {
+                        _cleanup_free_ char *slash_path = NULL;
+
+                        if (!path_is_absolute(path)) {
+                                slash_path = strjoin("/", path);
+                                if (!slash_path)
+                                        return -ENOMEM;
+                        }
+
+                        /* We use open_tree() rather than regular open() here, because it gives us direct
+                         * control over automount behaviour, and otherwise is equivalent to open() with
+                         * O_PATH */
+                        fd = open_tree(-EBADF, slash_path ?: path, OPEN_TREE_CLOEXEC|(FLAGS_SET(flags, CHASE_TRIGGER_AUTOFS) ? 0 : AT_NO_AUTOMOUNT));
+                        if (fd < 0)
+                                return -errno;
+
+                        if (fstat(fd, &st) < 0)
+                                return -errno;
+
+                        exists = true;
+                        goto success;
+                }
+
                 _dir_fd = open("/", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
                 if (_dir_fd < 0)
                         return -errno;
@@ -259,7 +287,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
         if (r < 0)
                 return r;
 
-        bool need_absolute = r;
+        need_absolute = r;
         if (need_absolute) {
                 done = strdup("/");
                 if (!done)
@@ -522,6 +550,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
                 close_and_replace(fd, child);
         }
 
+success:
         if (exists) {
                 if (FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY)) {
                         r = stat_verify_directory(&st);
index 2dc554ebd18cf4fb8bd4f8a2e514efa7db4d46f3..129ea19b7237d23ba8047c5997b5ef735bf7ce50 100644 (file)
@@ -500,15 +500,34 @@ TEST(chaseat) {
         fd = safe_close(fd);
 
         /* Same but with XAT_FDROOT */
-        ASSERT_OK(chaseat(XAT_FDROOT, p, 0, &result, NULL));
+        _cleanup_close_ int found_fd1 = -EBADF;
+        ASSERT_OK(chaseat(XAT_FDROOT, p, 0, &result, &found_fd1));
         ASSERT_STREQ(result, "/usr");
         result = mfree(result);
 
-        ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
+        _cleanup_close_ int found_fd2 = -EBADF;
+        ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, &result, &found_fd2));
         ASSERT_STREQ(result, "/usr");
         result = mfree(result);
-
-        fd = safe_close(fd);
+        assert(fd_inode_same(found_fd1, found_fd2) > 0);
+
+        /* Do the same XAT_FDROOT tests again, this time without querying the path, so that the open_tree()
+         * shortcut can work */
+        _cleanup_close_ int found_fd3 = -EBADF;
+        ASSERT_OK(chaseat(XAT_FDROOT, p, 0, NULL, &found_fd3));
+        assert(fd_inode_same(found_fd1, found_fd3) > 0);
+        assert(fd_inode_same(found_fd2, found_fd3) > 0);
+
+        _cleanup_close_ int found_fd4 = -EBADF;
+        ASSERT_OK(chaseat(XAT_FDROOT, p, CHASE_AT_RESOLVE_IN_ROOT, NULL, &found_fd4));
+        assert(fd_inode_same(found_fd1, found_fd4) > 0);
+        assert(fd_inode_same(found_fd2, found_fd4) > 0);
+        assert(fd_inode_same(found_fd3, found_fd4) > 0);
+
+        found_fd1 = safe_close(found_fd1);
+        found_fd2 = safe_close(found_fd2);
+        found_fd3 = safe_close(found_fd3);
+        found_fd4 = safe_close(found_fd4);
 
         /* If the file descriptor does not point to the root directory, the result will be relative
          * unless the result is outside of the specified file descriptor. */