From 47d83800e1c27c0a26d3ee432973d49a3f83f299 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Tue, 16 Jun 2020 21:51:08 +0300 Subject: [PATCH] Kernel+LibC: Do not return -ENAMETOOLONG from sys$readlink() That's not how readlink() is supposed to work: it should copy as many bytes as fit into the buffer, and return the number of bytes copied. So do that, but add a twist: make sys$readlink() actually return the whole size, not the number of bytes copied. We fix up this return value in userspace, to make LibC's readlink() behave as expected, but this will also allow other code to allocate a buffer of just the right size. Also, avoid an extra copy of the link target. --- Kernel/Process.cpp | 10 +++++----- Libraries/LibC/unistd.cpp | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 568648401e..9c066baf95 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -1953,11 +1953,11 @@ int Process::sys$readlink(const Syscall::SC_readlink_params* user_params) if (contents.is_error()) return contents.error(); - auto link_target = String::copy(contents.value()); - if (link_target.length() > params.buffer.size) - return -ENAMETOOLONG; - copy_to_user(params.buffer.data, link_target.characters(), link_target.length()); - return link_target.length(); + auto& link_target = contents.value(); + auto size_to_copy = min(link_target.size(), params.buffer.size); + copy_to_user(params.buffer.data, link_target.data(), size_to_copy); + // Note: we return the whole size here, not the copied size. + return link_target.size(); } int Process::sys$chdir(const char* user_path, size_t path_length) diff --git a/Libraries/LibC/unistd.cpp b/Libraries/LibC/unistd.cpp index 75b0458639..cc3930a141 100644 --- a/Libraries/LibC/unistd.cpp +++ b/Libraries/LibC/unistd.cpp @@ -364,7 +364,8 @@ ssize_t readlink(const char* path, char* buffer, size_t size) { Syscall::SC_readlink_params params { { path, strlen(path) }, { buffer, size } }; int rc = syscall(SC_readlink, ¶ms); - __RETURN_WITH_ERRNO(rc, rc, -1); + // Return the number of bytes placed in the buffer, not the full path size. + __RETURN_WITH_ERRNO(rc, min((size_t)rc, size), -1); } off_t lseek(int fd, off_t offset, int whence)