diff options
author | Ben Wiederhake <BenWiederhake.GitHub@gmx.de> | 2021-01-16 15:48:56 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-16 22:40:53 +0100 |
commit | ea5825f2c9e2cc641daa5584b6129c1f7ef3fa9e (patch) | |
tree | 5771045035aa4ef5885c3081eaab845656ee76ba /Userland/Libraries/LibC/unistd.cpp | |
parent | 7ed002d1ca52fed6e31ad50f5dff471dcb62be5f (diff) | |
download | serenity-ea5825f2c9e2cc641daa5584b6129c1f7ef3fa9e.zip |
Kernel+LibC: Make sys$getcwd truncate the result silently
This gives us the superpower of knowing the ideal buffer length if it fails.
See also https://github.com/SerenityOS/serenity/discussions/4357
Diffstat (limited to 'Userland/Libraries/LibC/unistd.cpp')
-rw-r--r-- | Userland/Libraries/LibC/unistd.cpp | 63 |
1 files changed, 60 insertions, 3 deletions
diff --git a/Userland/Libraries/LibC/unistd.cpp b/Userland/Libraries/LibC/unistd.cpp index 718ff6961c..404a944406 100644 --- a/Userland/Libraries/LibC/unistd.cpp +++ b/Userland/Libraries/LibC/unistd.cpp @@ -322,22 +322,79 @@ int fchdir(int fd) char* getcwd(char* buffer, size_t size) { + if (buffer && size == 0) { + // POSIX requires that we set errno to EINVAL here, but in our syscall it makes sense to + // allow "probing" the Kernel with a zero-sized buffer, and it does not return -EINVAL. + // So we have to inject EINVAL here. + errno = EINVAL; + return nullptr; + } + bool self_allocated = false; if (!buffer) { - size = size ? size : PATH_MAX; + size = size ? size : 64; buffer = (char*)malloc(size); self_allocated = true; } + int rc = syscall(SC_getcwd, buffer, size); - if (rc < 0 && self_allocated) { + if (rc < 0) { + if (self_allocated) + free(buffer); + errno = -rc; + return nullptr; + } + + size_t actual_size = static_cast<size_t>(rc); + if (actual_size <= size) { + return buffer; + } + + // If we get here, the current directory path was silently truncated. + + if (!self_allocated) { + // In this case, POSIX causes information loss: the caller cannot learn about the ideal + // buffer size. This is the reason we went with silently truncation instead. + errno = ERANGE; + return nullptr; + } + + // Try again. + free(buffer); + size = actual_size; + buffer = (char*)malloc(size); + rc = syscall(SC_getcwd, buffer, size); + if (rc < 0) { + // Can only happen if we lose a race. Let's pretend we lost the race in the first place. free(buffer); + errno = -rc; + return nullptr; } - __RETURN_WITH_ERRNO(rc, buffer, nullptr); + + actual_size = static_cast<size_t>(rc); + if (actual_size < size) { + // If we're here, then cwd has become longer while we were looking at it. (Race with another thread?) + // There's not much we can do, unless we want to loop endlessly + // in this case. Let's leave it up to the caller whether to loop. + free(buffer); + errno = EAGAIN; + return nullptr; + } + + return buffer; } char* getwd(char* buf) { + if (buf == nullptr) { + errno = EINVAL; + return nullptr; + } auto* p = getcwd(buf, PATH_MAX); + if (errno == ERANGE) { + // POSIX quirk + errno = ENAMETOOLONG; + } return p; } |