summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/posix_stdlib.cpp
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/posix/generic/posix_stdlib.cpp
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/posix/generic/posix_stdlib.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/posix_stdlib.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/posix_stdlib.cpp b/lib/mlibc/options/posix/generic/posix_stdlib.cpp
new file mode 100644
index 0000000..4010998
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_stdlib.cpp
@@ -0,0 +1,513 @@
+
+#include <abi-bits/fcntl.h>
+#include <bits/ensure.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <frg/small_vector.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/rtdl-config.hpp>
+
+namespace {
+ constexpr bool debugPathResolution = false;
+}
+
+// Borrowed from musl
+static uint32_t init[] = {
+0x00000000,0x5851f42d,0xc0b18ccf,0xcbb5f646,
+0xc7033129,0x30705b04,0x20fd5db4,0x9a8b7f78,
+0x502959d8,0xab894868,0x6c0356a7,0x88cdb7ff,
+0xb477d43f,0x70a3a52b,0xa8e4baf1,0xfd8341fc,
+0x8ae16fd9,0x742d2f7a,0x0d1f0796,0x76035e09,
+0x40f7702c,0x6fa72ca5,0xaaa84157,0x58a0df74,
+0xc74a0364,0xae533cc4,0x04185faf,0x6de3b115,
+0x0cab8628,0xf043bfa4,0x398150e9,0x37521657};
+
+static int n = 31;
+static int i = 3;
+static int j = 0;
+static uint32_t *x = init + 1;
+
+
+static uint32_t lcg31(uint32_t x) {
+ return (1103515245 * x + 12345) & 0x7fffffff;
+}
+
+static uint64_t lcg64(uint64_t x) {
+ return 6364136223846793005ull * x + 1;
+}
+
+static void *savestate(void) {
+ x[-1] = (n << 16) | (i << 8) | j;
+ return x - 1;
+}
+
+static void loadstate(uint32_t *state) {
+ x = state + 1;
+ n = x[-1] >> 16;
+ i = (x[-1] >> 8) & 0xff;
+ j = x[-1] & 0xff;
+}
+
+long random(void) {
+ long k;
+
+ if(n == 0) {
+ k = x[0] = lcg31(x[0]);
+ return k;
+ }
+ x[i] += x[j];
+ k = x[i] >> 1;
+ if(++i == n)
+ i = 0;
+ if(++j == n)
+ j = 0;
+
+ return k;
+}
+
+double drand48(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void srand48(long int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// Borrowed from musl
+void srandom(unsigned int seed) {
+ int k;
+ uint64_t s = seed;
+
+ if(n == 0) {
+ x[0] = s;
+ return;
+ }
+ i = n == 31 || n == 7 ? 3 : 1;
+ j = 0;
+ for(k = 0; k < n; k++) {
+ s = lcg64(s);
+ x[k] = s >> 32;
+ }
+ // Make sure x contains at least one odd number
+ x[0] |= 1;
+}
+
+char *initstate(unsigned int seed, char *state, size_t size) {
+ void *old;
+
+ if(size < 8)
+ return 0;
+ old = savestate();
+ if(size < 32)
+ n = 0;
+ else if(size < 64)
+ n = 7;
+ else if(size < 128)
+ n = 15;
+ else if(size < 256)
+ n = 31;
+ else
+ n = 63;
+ x = (uint32_t *)state + 1;
+ srandom(seed);
+ savestate();
+ return (char *)old;
+}
+
+char *setstate(char *state) {
+ void *old;
+
+ old = savestate();
+ loadstate((uint32_t *)state);
+ return (char *)old;
+}
+
+// ----------------------------------------------------------------------------
+// Path handling.
+// ----------------------------------------------------------------------------
+
+
+int mkostemps(char *pattern, int suffixlen, int flags) {
+ auto n = strlen(pattern);
+ if(n < (6 + static_cast<size_t>(suffixlen))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ flags &= ~O_WRONLY;
+
+ for(size_t i = 0; i < 6; i++) {
+ if(pattern[n - (6 + suffixlen) + i] == 'X')
+ continue;
+ errno = EINVAL;
+ return -1;
+ }
+
+ // TODO: Do an exponential search.
+ for(size_t i = 0; i < 999999; i++) {
+ char sfx = pattern[n - suffixlen];
+ __ensure(sprintf(pattern + (n - (6 + suffixlen)), "%06zu", i) == 6);
+ pattern[n - suffixlen] = sfx;
+
+ int fd;
+ if(int e = mlibc::sys_open(pattern, O_RDWR | O_CREAT | O_EXCL | flags, S_IRUSR | S_IWUSR, &fd); !e) {
+ return fd;
+ }else if(e != EEXIST) {
+ errno = e;
+ return -1;
+ }
+ }
+
+ errno = EEXIST;
+ return -1;
+}
+
+int mkostemp(char *pattern, int flags) {
+ return mkostemps(pattern, flags, 0);
+}
+
+int mkstemp(char *path) {
+ return mkostemp(path, 0);
+}
+
+int mkstemps(char *pattern, int suffixlen) {
+ return mkostemps(pattern, suffixlen, 0);
+}
+
+char *mkdtemp(char *pattern) {
+ mlibc::infoLogger() << "mlibc mkdtemp(" << pattern << ") called" << frg::endlog;
+ auto n = strlen(pattern);
+ __ensure(n >= 6);
+ if(n < 6) {
+ errno = EINVAL;
+ return NULL;
+ }
+ for(size_t i = 0; i < 6; i++) {
+ if(pattern[n - 6 + i] == 'X')
+ continue;
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // TODO: Do an exponential search.
+ for(size_t i = 0; i < 999999; i++) {
+ __ensure(sprintf(pattern + (n - 6), "%06zu", i) == 6);
+ if(int e = mlibc::sys_mkdir(pattern, S_IRWXU); !e) {
+ return pattern;
+ }else if(e != EEXIST) {
+ errno = e;
+ return NULL;
+ }
+ }
+
+ errno = EEXIST;
+ return NULL;
+}
+
+char *realpath(const char *path, char *out) {
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): Called on '" << path << "'" << frg::endlog;
+ frg::string_view path_view{path};
+
+ // In case of the root, the string only contains the null-terminator.
+ frg::small_vector<char, PATH_MAX, MemoryAllocator> resolv{getAllocator()};
+ size_t ps;
+
+ // If the path is relative, we have to preprend the working directory.
+ if(path[0] == '/') {
+ resolv.push_back(0);
+ ps = 1;
+ }else{
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getcwd, nullptr);
+
+ // Try to getcwd() until the buffer is large enough.
+ resolv.resize(128);
+ while(true) {
+ int e = mlibc::sys_getcwd(resolv.data(), resolv.size());
+ if(e == ERANGE) {
+ resolv.resize(2 * resolv.size());
+ }else if(!e) {
+ break;
+ }else{
+ errno = e;
+ return nullptr;
+ }
+ }
+ frg::string_view cwd_view{resolv.data()};
+ if(cwd_view == "/") {
+ // Restore our invariant that we only store the null-terminator for the root.
+ resolv.resize(1);
+ resolv[0] = 0;
+ }else{
+ resolv.resize(cwd_view.size() + 1);
+ }
+ ps = 0;
+ }
+
+ // Contains unresolved links as a relative path compared to resolv.
+ frg::small_vector<char, PATH_MAX, MemoryAllocator> lnk{getAllocator()};
+ size_t ls = 0;
+
+ auto process_segment = [&] (frg::string_view s_view) -> int {
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): resolv is '" << resolv.data() << "'"
+ << ", segment is " << s_view.data()
+ << ", size: " << s_view.size() << frg::endlog;
+
+ if(!s_view.size() || s_view == ".") {
+ // Keep resolv invariant.
+ return 0;
+ }else if(s_view == "..") {
+ // Remove a single segment from resolv.
+ if(resolv.size() > 1) {
+ auto slash = strrchr(resolv.data(), '/');
+ __ensure(slash); // We never remove the leading sla.
+ resolv.resize((slash - resolv.data()) + 1);
+ *slash = 0; // Replace the slash by a null-terminator.
+ }
+ return 0;
+ }
+
+ // Append the segment to resolv.
+ auto rsz = resolv.size();
+ resolv[rsz - 1] = '/'; // Replace null-terminator by a slash.
+ resolv.resize(rsz + s_view.size() + 1);
+ memcpy(resolv.data() + rsz, s_view.data(), s_view.size());
+ resolv[rsz + s_view.size()] = 0;
+
+ // stat() the path to (1) see if it exists and (2) see if it is a link.
+ if(!mlibc::sys_stat) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): stat()ing '"
+ << resolv.data() << "'" << frg::endlog;
+ struct stat st;
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::path,
+ -1, resolv.data(), AT_SYMLINK_NOFOLLOW, &st); e)
+ return e;
+
+ if(S_ISLNK(st.st_mode)) {
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Encountered symlink '"
+ << resolv.data() << "'" << frg::endlog;
+ }
+
+ if(!mlibc::sys_readlink) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+
+ ssize_t sz = 0;
+ char path[512];
+
+ if (int e = mlibc::sys_readlink(resolv.data(), path, 512, &sz); e)
+ return e;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink resolves to '"
+ << frg::string_view{path, static_cast<size_t>(sz)} << "'" << frg::endlog;
+ }
+
+ if (path[0] == '/') {
+ // Absolute path, replace resolv
+ resolv.resize(sz);
+ strncpy(resolv.data(), path, sz - 1);
+ resolv.data()[sz - 1] = 0;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink is absolute, resolv: '"
+ << resolv.data() << "'" << frg::endlog;
+ }
+ } else {
+ // Relative path, revert changes to resolv, prepend to lnk
+ resolv.resize(rsz);
+ resolv[rsz - 1] = 0;
+
+ auto lsz = lnk.size();
+ lnk.resize((lsz - ls) + sz + 1);
+ memmove(lnk.data() + sz, lnk.data() + ls, lsz - ls);
+ memcpy(lnk.data(), path, sz);
+ lnk[(lsz - ls) + sz] = 0;
+
+ ls = 0;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink is relative, resolv: '"
+ << resolv.data() << "' lnk: '"
+ << frg::string_view{lnk.data(), lnk.size()} << "'" << frg::endlog;
+ }
+ }
+ }
+
+ return 0;
+ };
+
+ // Each iteration of this outer loop consumes segment of the input path.
+ // This design avoids copying the input path into lnk;
+ // the latter could often involve additional allocations.
+ while(ps < path_view.size()) {
+ frg::string_view ps_view;
+ if(auto slash = strchr(path + ps, '/'); slash) {
+ ps_view = frg::string_view{path + ps, static_cast<size_t>(slash - (path + ps))};
+ }else{
+ ps_view = frg::string_view{path + ps, strlen(path) - ps};
+ }
+ ps += ps_view.size() + 1;
+
+ // Handle one segment from the input path.
+ if(int e = process_segment(ps_view); e) {
+ errno = e;
+ return nullptr;
+ }
+
+ // This inner loop consumes segments of lnk.
+ while(ls < lnk.size()) {
+ frg::string_view ls_view;
+ if(auto slash = strchr(lnk.data() + ls, '/'); slash) {
+ ls_view = frg::string_view{lnk.data() + ls, static_cast<size_t>(slash - (lnk.data() + ls))};
+ }else{
+ ls_view = frg::string_view{lnk.data() + ls, strlen(lnk.data()) - ls};
+ }
+ ls += ls_view.size() + 1;
+
+ // Handle one segment from the link
+ if(int e = process_segment(ls_view); e) {
+ errno = e;
+ return nullptr;
+ }
+ }
+
+ // All of lnk was consumed, reset it
+ lnk.resize(0);
+ ls = 0;
+ }
+
+ if(resolv.size() == 1) {
+ resolv.resize(0);
+ resolv.push_back('/');
+ resolv.push_back(0);
+ }
+
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): Returns '" << resolv.data() << "'" << frg::endlog;
+
+ if(resolv.size() > PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return nullptr;
+ }
+
+ if(!out)
+ out = reinterpret_cast<char *>(getAllocator().allocate(resolv.size()));
+ strcpy(out, resolv.data());
+ return out;
+}
+
+// ----------------------------------------------------------------------------
+// Pseudoterminals
+// ----------------------------------------------------------------------------
+
+int ptsname_r(int fd, char *buffer, size_t length) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, ENOSYS);
+
+ if(int e = sysdep(fd, buffer, length); e)
+ return e;
+
+ return 0;
+}
+
+char *ptsname(int fd) {
+ static char buffer[128];
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, NULL);
+
+ if(int e = sysdep(fd, buffer, 128); e) {
+ errno = e;
+ return NULL;
+ }
+
+ return buffer;
+}
+
+int posix_openpt(int flags) {
+ int fd;
+ if(int e = mlibc::sys_open("/dev/ptmx", flags, 0, &fd); e) {
+ errno = e;
+ return -1;
+ }
+
+ return fd;
+}
+
+int unlockpt(int fd) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlockpt, -1);
+
+ if(int e = sysdep(fd); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int grantpt(int) {
+ return 0;
+}
+
+double strtod_l(const char *__restrict__ nptr, char ** __restrict__ endptr, locale_t) {
+ mlibc::infoLogger() << "mlibc: strtod_l ignores locale!" << frg::endlog;
+ return strtod(nptr, endptr);
+}
+
+long double strtold_l(const char *__restrict__, char ** __restrict__, locale_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+float strtof_l(const char *__restrict__ nptr, char **__restrict__ endptr, locale_t) {
+ mlibc::infoLogger() << "mlibc: strtof_l ignores locales" << frg::endlog;
+ return strtof(nptr, endptr);
+}
+
+int strcoll_l(const char *, const char *, locale_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int getloadavg(double *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+char *secure_getenv(const char *name) {
+ if (mlibc::rtdlConfig().secureRequired)
+ return NULL;
+ else
+ return getenv(name);
+}
+
+void *reallocarray(void *ptr, size_t m, size_t n) {
+ if(n && m > -1 / n) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ return realloc(ptr, m * n);
+}
+
+char *canonicalize_file_name(const char *name) {
+ return realpath(name, NULL);
+}