9a9b91c940
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
570 lines
13 KiB
C++
570 lines
13 KiB
C++
|
|
#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/rtld-config.hpp>
|
|
|
|
namespace {
|
|
constexpr bool debugPathResolution = false;
|
|
} // namespace
|
|
|
|
// 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;
|
|
}
|
|
|
|
// erand, drand and srand are borrowed from musl
|
|
namespace {
|
|
|
|
unsigned short seed_48[7] = { 0, 0, 0, 0xe66d, 0xdeec, 0x5, 0xb };
|
|
|
|
uint64_t eand48_step(unsigned short *xi, unsigned short *lc) {
|
|
uint64_t x = xi[0] | (xi[1] + 0U) << 16 | (xi[2] + 0ULL) << 32;
|
|
uint64_t a = lc[0] | (lc[1] + 0U) << 16 | (lc[2] + 0ULL) << 32;
|
|
x = a*x + lc[3];
|
|
xi[0] = x;
|
|
xi[1] = x>>16;
|
|
xi[2] = x>>32;
|
|
return x & 0xffffffffffffull;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
double erand48(unsigned short s[3]) {
|
|
union {
|
|
uint64_t u;
|
|
double f;
|
|
} x = { 0x3ff0000000000000ULL | eand48_step(s, seed_48+3)<<4 };
|
|
return x.f - 1.0;
|
|
}
|
|
|
|
double drand48(void) {
|
|
return erand48(seed_48);
|
|
}
|
|
|
|
unsigned short *seed48(unsigned short *s) {
|
|
static unsigned short p[3];
|
|
memcpy(p, seed_48, sizeof p);
|
|
memcpy(seed_48, s, sizeof p);
|
|
return p;
|
|
}
|
|
|
|
void srand48(long int seed) {
|
|
unsigned short arr[3] = { 0x330e, (unsigned short) seed, (unsigned short) (seed>>16) };
|
|
seed48(arr);
|
|
}
|
|
|
|
long jrand48(unsigned short [3]) {
|
|
__ensure(!"Not implemented");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
long int mrand48(void) {
|
|
__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 nullptr;
|
|
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, 0, flags);
|
|
}
|
|
|
|
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 nullptr;
|
|
}
|
|
for(size_t i = 0; i < 6; i++) {
|
|
if(pattern[n - 6 + i] == 'X')
|
|
continue;
|
|
errno = EINVAL;
|
|
return nullptr;
|
|
}
|
|
|
|
// 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 nullptr;
|
|
}
|
|
}
|
|
|
|
errno = EEXIST;
|
|
return nullptr;
|
|
}
|
|
|
|
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{
|
|
// Try to getcwd() until the buffer is large enough.
|
|
resolv.resize(128);
|
|
int saved_errno = errno;
|
|
while(true) {
|
|
// getcwd could smash errno on failure + resize (ERANGE) + success,
|
|
// so we have to save and restore errno in that scenario.
|
|
char *ret = getcwd(resolv.data(), resolv.size());
|
|
if(ret != nullptr) {
|
|
break;
|
|
}
|
|
|
|
if(errno == ERANGE) {
|
|
errno = saved_errno;
|
|
resolv.resize(2 * resolv.size());
|
|
}else{
|
|
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
|
|
|
|
// Ignore any trailing '/' so all results will not have one to keep consistency.
|
|
while(sz > 1 && path[sz - 1] == '/')
|
|
sz -= 1;
|
|
|
|
resolv.resize(sz + 1);
|
|
strncpy(resolv.data(), path, sz);
|
|
resolv.data()[sz] = 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 nullptr;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int posix_openpt(int flags) {
|
|
int fd, e;
|
|
|
|
if(mlibc::sys_openpt) {
|
|
e = mlibc::sys_openpt(flags, &fd);
|
|
} else {
|
|
e = mlibc::sys_open("/dev/ptmx", flags, 0, &fd);
|
|
}
|
|
|
|
if (e) {
|
|
errno = e;
|
|
return -1;
|
|
} else {
|
|
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 getsubopt(char **__restrict__, char *const *__restrict__, char **__restrict__) {
|
|
__ensure(!"Not implemented");
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
char *secure_getenv(const char *name) {
|
|
if (mlibc::rtldConfig().secureRequired)
|
|
return nullptr;
|
|
else
|
|
return getenv(name);
|
|
}
|
|
|
|
void *reallocarray(void *ptr, size_t m, size_t n) {
|
|
if(n && m > -1 / n) {
|
|
errno = ENOMEM;
|
|
return nullptr;
|
|
}
|
|
|
|
return realloc(ptr, m * n);
|
|
}
|
|
|
|
char *canonicalize_file_name(const char *name) {
|
|
return realpath(name, nullptr);
|
|
}
|