#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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 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 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(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(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(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(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); }