diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index ee38e0266a..9ba9268d77 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -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; diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 5de1eea0d4..ef335b43ae 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -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);