fs-util: readlinkat() supports an empty string

From readlinkat(2):
Since Linux 2.6.39, pathname can be an empty string, in which case the
call operates on the symbolic link referred to by dirfd (which should
have been obtained using open(2) with the O_PATH and O_NOFOLLOW flags).
This commit is contained in:
Yu Watanabe
2024-02-16 00:25:37 +09:00
parent d0aef638ac
commit e4c094c055
2 changed files with 41 additions and 2 deletions
+6 -2
View File
@@ -116,7 +116,11 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
int readlinkat_malloc(int fd, const char *p, char **ret) {
size_t l = PATH_MAX;
assert(p);
assert(fd >= 0 || fd == AT_FDCWD);
if (fd < 0 && isempty(p))
return -EISDIR; /* In this case, the fd points to the current working directory, and is
* definitely not a symlink. Let's return earlier. */
for (;;) {
_cleanup_free_ char *c = NULL;
@@ -126,7 +130,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
if (!c)
return -ENOMEM;
n = readlinkat(fd, p, c, l);
n = readlinkat(fd, strempty(p), c, l);
if (n < 0)
return -errno;
+35
View File
@@ -758,4 +758,39 @@ static int intro(void) {
return EXIT_SUCCESS;
}
TEST(readlinkat_malloc) {
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
_cleanup_free_ char *p = NULL, *q = NULL;
const char *expect = "hgoehogefoobar";
tfd = mkdtemp_open(NULL, O_PATH, &t);
assert_se(tfd >= 0);
assert_se(symlinkat(expect, tfd, "linkname") >= 0);
assert_se(readlinkat_malloc(tfd, "linkname", &p) >= 0);
assert_se(streq(p, expect));
p = mfree(p);
fd = openat(tfd, "linkname", O_PATH | O_NOFOLLOW | O_CLOEXEC);
assert_se(fd >= 0);
assert_se(readlinkat_malloc(fd, NULL, &p) >= 0);
assert_se(streq(p, expect));
p = mfree(p);
assert_se(readlinkat_malloc(fd, "", &p) >= 0);
assert_se(streq(p, expect));
p = mfree(p);
fd = safe_close(fd);
assert_se(q = path_join(t, "linkname"));
assert_se(readlinkat_malloc(AT_FDCWD, q, &p) >= 0);
assert_se(streq(p, expect));
p = mfree(p);
assert_se(readlinkat_malloc(INT_MAX, q, &p) >= 0);
assert_se(streq(p, expect));
p = mfree(p);
q = mfree(q);
}
DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);