From bd5969fc876a10b18613302db7087ef3c40f18e1 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Thu, 7 Mar 2024 17:28:00 -0500 Subject: lib: Add mlibc Signed-off-by: Ian Moffett --- lib/mlibc/options/posix/generic/dirent-stubs.cpp | 180 +++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 lib/mlibc/options/posix/generic/dirent-stubs.cpp (limited to 'lib/mlibc/options/posix/generic/dirent-stubs.cpp') diff --git a/lib/mlibc/options/posix/generic/dirent-stubs.cpp b/lib/mlibc/options/posix/generic/dirent-stubs.cpp new file mode 100644 index 0000000..1352585 --- /dev/null +++ b/lib/mlibc/options/posix/generic/dirent-stubs.cpp @@ -0,0 +1,180 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Code taken from musl +int alphasort(const struct dirent **a, const struct dirent **b) { + return strcoll((*a)->d_name, (*b)->d_name); +} + +int closedir(DIR *dir) { + // TODO: Deallocate the dir structure. + close(dir->__handle); + return 0; +} +int dirfd(DIR *dir) { + return dir->__handle; +} +DIR *fdopendir(int fd) { + struct stat st; + + if(fstat(fd, &st) < 0) { + return nullptr; + } + // Musl implements this, but O_PATH is only declared on the linux abi + /*if(fcntl(fd, F_GETFL) & O_PATH) { + errno = EBADF; + return nullptr; + }*/ + if(!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return nullptr; + } + auto dir = frg::construct<__mlibc_dir_struct>(getAllocator()); + __ensure(dir); + dir->__ent_next = 0; + dir->__ent_limit = 0; + int flags = fcntl(fd, F_GETFD); + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + dir->__handle = fd; + return dir; +} +DIR *opendir(const char *path) { + auto dir = frg::construct<__mlibc_dir_struct>(getAllocator()); + __ensure(dir); + dir->__ent_next = 0; + dir->__ent_limit = 0; + + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_open_dir, nullptr); + if(int e = mlibc::sys_open_dir(path, &dir->__handle); e) { + errno = e; + frg::destruct(getAllocator(), dir); + return nullptr; + }else{ + return dir; + } +} +struct dirent *readdir(DIR *dir) { + __ensure(dir->__ent_next <= dir->__ent_limit); + if(dir->__ent_next == dir->__ent_limit) { + MLIBC_CHECK_OR_ENOSYS(mlibc::sys_read_entries, nullptr); + if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e) + __ensure(!"mlibc::sys_read_entries() failed"); + dir->__ent_next = 0; + if(!dir->__ent_limit) + return nullptr; + } + + auto entp = reinterpret_cast(dir->__ent_buffer + dir->__ent_next); + // We only copy as many bytes as we need to avoid buffer-overflows. + memcpy(&dir->__current, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1); + dir->__ent_next += entp->d_reclen; + return &dir->__current; +} +int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result) { + if(!mlibc::sys_read_entries) { + MLIBC_MISSING_SYSDEP(); + return ENOSYS; + } + + __ensure(dir->__ent_next <= dir->__ent_limit); + if(dir->__ent_next == dir->__ent_limit) { + if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e) + __ensure(!"mlibc::sys_read_entries() failed"); + dir->__ent_next = 0; + if(!dir->__ent_limit) { + *result = NULL; + return 0; + } + } + + auto entp = reinterpret_cast(dir->__ent_buffer + dir->__ent_next); + // We only copy as many bytes as we need to avoid buffer-overflows. + memcpy(entry, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1); + dir->__ent_next += entp->d_reclen; + *result = entry; + return 0; +} + +void rewinddir(DIR *dir) { + lseek(dir->__handle, 0, SEEK_SET); + dir->__ent_next = 0; +} + +int scandir(const char *path, struct dirent ***res, int (*select)(const struct dirent *), + int (*compare)(const struct dirent **, const struct dirent **)) { + DIR *dir = opendir(path); + if (!dir) + return -1; // errno will be set by opendir() + + // we should save the errno + int old_errno = errno; + errno = 0; + + struct dirent *dir_ent; + struct dirent **array = nullptr, **tmp = nullptr; + int length = 0; + int count = 0; + while((dir_ent = readdir(dir)) && !errno) { + if(select && !select(dir_ent)) + continue; + + if(count >= length) { + length = 2*length + 1; + tmp = static_cast(realloc(array, + length * sizeof(struct dirent*))); + // we need to check the call actually goes through + // before we overwrite array so that we can + // deallocate the already written entries should realloc() + // have failed + if(!tmp) + break; + array = tmp; + } + array[count] = static_cast(malloc(dir_ent->d_reclen)); + if(!array[count]) + break; + + memcpy(array[count], dir_ent, dir_ent->d_reclen); + count++; + } + + if(errno) { + if(array) + while(count-- > 0) + free(array[count]); + free(array); + return -1; + } + + // from here we can set the old errno back + errno = old_errno; + + if(compare) + qsort(array, count, sizeof(struct dirent*), + (int (*)(const void *, const void *)) compare); + *res = array; + return count; +} +void seekdir(DIR *, long) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} +long telldir(DIR *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +int versionsort(const struct dirent **a, const struct dirent **b) { + return strverscmp((*a)->d_name, (*b)->d_name); +} -- cgit v1.2.3