summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibC/dirent.cpp
diff options
context:
space:
mode:
authorMart G <martg_@hotmail.com>2021-05-11 18:35:36 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-12 12:50:23 +0200
commitb00cdf8ed834188883ad3dd9716f5bb176dfd7b5 (patch)
tree3bdb13f8fe583997e83c286df5b062522620eb71 /Userland/Libraries/LibC/dirent.cpp
parentc7b60164ed95877ebc18d5783729b360213b7b9d (diff)
downloadserenity-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.cpp31
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;
}