Skip to content

Commit 24f453d

Browse files
committed
client: Resolve symlink from dirfd for empty pathname
man readlinkat(2)[1] points at a special case for readlinkat() syscall as follows: . . . 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). . . . man open(2)[2] further explains the need for such a special case when a symlink is opened with O_PATH and O_NOFOLLOW: . . . If pathname is a symbolic link and the O_NOFOLLOW flag is also specified, then the call returns a file descriptor referring to the symbolic link. This file descriptor can be used as the dirfd argument in calls to fchownat(2), fstatat(2), linkat(2), and readlinkat(2) with an empty pathname to have the calls operate on the symbolic link. . . . Accordingly have a check to resolve symlinks out of dirfd when empty pathnames are encountered within readlinkat(). In addition to that match the standard file system behavior to return ENOENT instead of EINVAL when the inode pointed to by dirfd is not a symbolic link with empty pathnames. Fixes: https://tracker.ceph.com/issues/67833 [1] https://www.man7.org/linux/man-pages/man2/readlinkat.2.html [2] https://www.man7.org/linux/man-pages/man2/open.2.html Signed-off-by: Anoop C S <[email protected]>
1 parent 8443821 commit 24f453d

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

src/client/Client.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7957,6 +7957,12 @@ int Client::readlinkat(int dirfd, const char *relpath, char *buf, loff_t size, c
79577957
return r;
79587958
}
79597959

7960+
if (!strcmp(relpath, "")) {
7961+
if (!dirinode.get()->is_symlink())
7962+
return -CEPHFS_ENOENT;
7963+
return _readlink(dirinode.get(), buf, size);
7964+
}
7965+
79607966
InodeRef in;
79617967
filepath path(relpath);
79627968
r = path_walk(path, &in, perms, false, 0, dirinode);

src/test/libcephfs/test.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3019,6 +3019,18 @@ TEST(LibCephFS, Readlinkat) {
30193019
ASSERT_EQ(0, memcmp(target, rel_file_path, target_len));
30203020

30213021
ASSERT_EQ(0, ceph_close(cmount, fd));
3022+
#if defined(__linux__) && defined(O_PATH)
3023+
// test readlinkat with empty pathname relative to O_PATH|O_NOFOLLOW fd
3024+
fd = ceph_open(cmount, link_path, O_PATH | O_NOFOLLOW, 0);
3025+
ASSERT_LE(0, fd);
3026+
size_t link_target_len = strlen(rel_file_path);
3027+
char link_target[link_target_len+1];
3028+
ASSERT_EQ(link_target_len, ceph_readlinkat(cmount, fd, "", link_target, link_target_len));
3029+
link_target[link_target_len] = '\0';
3030+
ASSERT_EQ(0, memcmp(link_target, rel_file_path, link_target_len));
3031+
ASSERT_EQ(0, ceph_close(cmount, fd));
3032+
#endif /* __linux */
3033+
30223034
ASSERT_EQ(0, ceph_unlink(cmount, link_path));
30233035
ASSERT_EQ(0, ceph_unlink(cmount, file_path));
30243036
ASSERT_EQ(0, ceph_rmdir(cmount, dir_path));

0 commit comments

Comments
 (0)