]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fs-util: add new CHASE_NOFOLLOW flag to chase_symlinks()
authorFranck Bui <fbui@suse.com>
Thu, 26 Apr 2018 20:46:55 +0000 (22:46 +0200)
committerFranck Bui <fbui@suse.com>
Mon, 30 Jul 2018 13:54:03 +0000 (15:54 +0200)
This flag mimics what "O_NOFOLLOW|O_PATH" does for open(2) that is
chase_symlinks() will not resolve the final pathname component if it's a
symlink and instead will return a file descriptor referring to the symlink
itself.

Note: if CHASE_SAFE is also passed, no safety checking is performed on the
transition done if the symlink would have been followed.

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

index 1fbd40ade77004c2d98659cedf5c6214d7b5810a..09fcc32e0ef51c4a102f9b1f2971f6da70d7b9f7 100644 (file)
@@ -695,7 +695,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
                 /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
                  * and doesn't care about any of the other special features we provide either. */
-                r = open(path, O_PATH|O_CLOEXEC);
+                r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
                 if (r < 0)
                         return -errno;
 
@@ -850,7 +850,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
                         return -EREMOTE;
 
-                if (S_ISLNK(st.st_mode)) {
+                if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         char *joined;
 
                         _cleanup_free_ char *destination = NULL;
index b1a366ae491b757780e504f8192900e57042a22d..4b65625861af310161f8ef89d49fbcfe32b18ee1 100644 (file)
@@ -73,6 +73,7 @@ enum {
         CHASE_OPEN        = 1 << 4, /* If set, return an O_PATH object to the final component */
         CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
         CHASE_STEP        = 1 << 6, /* If set, just execute a single step of the normalization */
+        CHASE_NOFOLLOW    = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
 };
 
 /* How many iterations to execute before returning -ELOOP */
index fc650b513ed2e04388596724e0af642e8972d77f..d188c24f7bbe97f511672cf90f3b6f4d6256c6d3 100644 (file)
@@ -22,6 +22,7 @@ static void test_chase_symlinks(void) {
         _cleanup_free_ char *result = NULL;
         char temp[] = "/tmp/test-chase.XXXXXX";
         const char *top, *p, *pslash, *q, *qslash;
+        struct stat st;
         int r, pfd;
 
         assert_se(mkdtemp(temp));
@@ -266,6 +267,30 @@ static void test_chase_symlinks(void) {
                 assert_se(sd_id128_equal(a, b));
         }
 
+        /* Test CHASE_NOFOLLOW */
+
+        p = strjoina(temp, "/target");
+        q = strjoina(temp, "/symlink");
+        assert_se(symlink(p, q) >= 0);
+        pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
+        assert_se(pfd > 0);
+        assert_se(path_equal(result, q));
+        assert_se(fstat(pfd, &st) >= 0);
+        assert_se(S_ISLNK(st.st_mode));
+        result = mfree(result);
+
+        /* s1 -> s2 -> nonexistent */
+        q = strjoina(temp, "/s1");
+        assert_se(symlink("s2", q) >= 0);
+        p = strjoina(temp, "/s2");
+        assert_se(symlink("nonexistent", p) >= 0);
+        pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
+        assert_se(pfd > 0);
+        assert_se(path_equal(result, q));
+        assert_se(fstat(pfd, &st) >= 0);
+        assert_se(S_ISLNK(st.st_mode));
+        result = mfree(result);
+
         /* Test CHASE_ONE */
 
         p = strjoina(temp, "/start");