From 1f56e4ce773f195bbdf2dfc639d967309321441c Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Thu, 26 Apr 2018 22:46:55 +0200 Subject: [PATCH] fs-util: add new CHASE_NOFOLLOW flag to chase_symlinks() 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 | 4 ++-- src/basic/fs-util.h | 1 + src/test/test-fs-util.c | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 1fbd40ade7..09fcc32e0e 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -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; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index b1a366ae49..4b65625861 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -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 */ diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index fc650b513e..d188c24f7b 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -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");