diff options
author | Mart G <martg_@hotmail.com> | 2021-05-11 18:35:36 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-12 12:50:23 +0200 |
commit | b00cdf8ed834188883ad3dd9716f5bb176dfd7b5 (patch) | |
tree | 3bdb13f8fe583997e83c286df5b062522620eb71 /Userland/Libraries/LibC/dirent.cpp | |
parent | c7b60164ed95877ebc18d5783729b360213b7b9d (diff) | |
download | serenity-b00cdf8ed834188883ad3dd9716f5bb176dfd7b5.zip |
Kernel+LibC: Make get_dir_entries syscall retriable
The get_dir_entries syscall failed if the serialized form of all the
directory entries together was too large to fit in its temporary buffer.
Now the kernel uses a fixed size buffer, that is flushed to an output
buffer when it is full. If this flushing operation fails because there
is not enough space available, the syscall will return -EINVAL. That
error code is then used in userspace as a signal to allocate a larger
buffer and retry the syscall.
Diffstat (limited to 'Userland/Libraries/LibC/dirent.cpp')
-rw-r--r-- | Userland/Libraries/LibC/dirent.cpp | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/Userland/Libraries/LibC/dirent.cpp b/Userland/Libraries/LibC/dirent.cpp index 67688bc584..81e6329c16 100644 --- a/Userland/Libraries/LibC/dirent.cpp +++ b/Userland/Libraries/LibC/dirent.cpp @@ -98,15 +98,30 @@ static int allocate_dirp_buffer(DIR* dirp) } size_t size_to_allocate = max(st.st_size, static_cast<off_t>(4096)); dirp->buffer = (char*)malloc(size_to_allocate); - ssize_t nread = syscall(SC_get_dir_entries, dirp->fd, dirp->buffer, size_to_allocate); - if (nread < 0) { - // uh-oh, the syscall returned an error - free(dirp->buffer); - dirp->buffer = nullptr; - return -nread; + if (!dirp->buffer) + return ENOMEM; + for (;;) { + ssize_t nread = syscall(SC_get_dir_entries, dirp->fd, dirp->buffer, size_to_allocate); + if (nread < 0) { + if (nread == -EINVAL) { + size_to_allocate *= 2; + char* new_buffer = (char*)realloc(dirp->buffer, size_to_allocate); + if (new_buffer) { + dirp->buffer = new_buffer; + continue; + } else { + nread = -ENOMEM; + } + } + // uh-oh, the syscall returned an error + free(dirp->buffer); + dirp->buffer = nullptr; + return -nread; + } + dirp->buffer_size = nread; + dirp->nextptr = dirp->buffer; + break; } - dirp->buffer_size = nread; - dirp->nextptr = dirp->buffer; return 0; } |