mkxp-z/binding-sandbox/wasi.cpp
刘皓 13c0657691
Handle current working directory properly in libretro builds
Any relative paths that the game tries to access in libretro builds will
now be relative to whatever is the current working directory in the Ruby
sandbox, which will also now be initialized to the game directory during
initialization. Before, all of the bindings that took paths were
hardcoded to prepend the path with the game directory.
2025-05-06 16:55:28 -04:00

833 lines
29 KiB
C++

/*
** wasi.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <algorithm>
#include <random>
#include <sstream>
#include <mkxp-sandbox-ruby.h>
#include "filesystem.h"
#include "core.h"
#include "wasi.h"
//#define WASI_DEBUG(...) mkxp_retro::log_printf(RETRO_LOG_INFO, __VA_ARGS__)
#define WASI_DEBUG(...)
#define WASM_MEM(address) ((void *)&wasi->ruby->w2c_memory.data[address])
static inline size_t strlen_safe(const char *str, size_t max_length) {
const char *ptr = (const char *)std::memchr(str, 0, max_length);
return ptr == NULL ? max_length : ptr - str;
}
std::string *wasi_file_entry::dir_handle() {
return (std::string *)handle;
}
struct FileSystem::File *wasi_file_entry::file_handle() {
return (struct FileSystem::File *)handle;
}
wasi_t::w2c_wasi__snapshot__preview1(std::shared_ptr<struct w2c_ruby> ruby) : ruby(ruby) {
// Initialize WASI file descriptor table
fdtable.push_back({.type = wasi_fd_type::STDIN});
fdtable.push_back({.type = wasi_fd_type::STDOUT});
fdtable.push_back({.type = wasi_fd_type::STDERR});
fdtable.push_back({.type = wasi_fd_type::FS, .handle = new std::string("/game")});
fdtable.push_back({.type = wasi_fd_type::FS, .handle = new std::string("/dist")});
}
wasi_t::~w2c_wasi__snapshot__preview1() {
// Close all of the open WASI file descriptors
for (size_t i = fdtable.size(); i > 0;) {
deallocate_file_descriptor(--i);
}
}
struct fs_enumerate_data {
wasi_t *wasi;
u32 fd;
usize original_buf;
usize buf;
u32 buf_len;
u64 initial_cookie;
u64 cookie;
usize result;
};
u32 wasi_t::allocate_file_descriptor(enum wasi_fd_type type, void *handle) {
if (vacant_fds.empty()) {
u32 fd = fdtable.size();
fdtable.push_back({.type = type, .handle = handle});
return fd;
} else {
u32 fd = vacant_fds.back();
vacant_fds.pop_back();
return fd;
}
}
void wasi_t::deallocate_file_descriptor(u32 fd) {
if (fdtable[fd].handle != NULL) {
switch (fdtable[fd].type) {
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
delete fdtable[fd].dir_handle();
break;
case wasi_fd_type::FSFILE:
delete fdtable[fd].file_handle();
break;
default:
break;
}
}
if (!fdtable.empty() && fd == fdtable.size() - 1) {
fdtable.pop_back();
} else {
fdtable[fd] = {.type = wasi_fd_type::VACANT, .handle = NULL};
vacant_fds.push_back(fd);
}
}
extern "C" u32 w2c_wasi__snapshot__preview1_args_get(wasi_t *wasi, usize argv, usize argv_buf) {
WASI_DEBUG("args_get()\n");
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_args_sizes_get(wasi_t *wasi, usize argc, usize argv_buf_size) {
WASI_DEBUG("args_sizes_get(0x%08x, 0x%08x)\n", argc, argv_buf_size);
WASM_SET(u32, argc, 0);
WASM_SET(u32, argv_buf_size, 0);
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_clock_res_get(wasi_t *wasi, u32 id, usize result) {
WASI_DEBUG("clock_res_get(%u)\n", id);
WASM_SET(u64, result, 1000L);
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_clock_time_get(wasi_t *wasi, u32 id, u64 precision, usize result) {
WASI_DEBUG("clock_time_get(%u, %lu)\n", id, precision);
WASM_SET(u64, result, mkxp_retro::perf.get_time_usec != nullptr ? mkxp_retro::perf.get_time_usec() * 1000L : 0);
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_environ_get(wasi_t *wasi, usize env, usize env_buf) {
WASI_DEBUG("environ_get(0x%08x, 0x%08x)\n", env, env_buf);
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_environ_sizes_get(wasi_t *wasi, usize env_size, usize env_buf_size) {
WASI_DEBUG("environ_sizes_get()\n");
WASM_SET(u32, env_size, 0);
WASM_SET(u32, env_buf_size, 0);
return WASI_ESUCCESS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_advise(wasi_t *wasi, u32 fd, u64 offset, u64 len, u32 advice) {
WASI_DEBUG("fd_advise(%u, %lu, %lu, 0x%08x)\n", fd, offset, len, advice);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_close(wasi_t *wasi, u32 fd) {
WASI_DEBUG("fd_close(%u)\n", fd);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
wasi->deallocate_file_descriptor(fd);
return WASI_ESUCCESS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_datasync(wasi_t *wasi, u32 fd) {
WASI_DEBUG("fd_datasync(%u)\n", fd);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_fdstat_get(wasi_t *wasi, u32 fd, usize result) {
WASI_DEBUG("fd_fdstat_get(%u)\n", fd);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSFILE:
WASM_SET(u8, result, WASI_IFCHR); // fs_filetype
WASM_SET(u16, result + 2, 0); // fs_flags
WASM_SET(u64, result + 8, WASI_FD_READ | WASI_FD_WRITE | WASI_FD_FILESTAT_GET); // fs_rights_base
WASM_SET(u64, result + 16, 0); // fs_rights_inheriting
return WASI_ESUCCESS;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
WASM_SET(u8, result, WASI_IFDIR); // fs_filetype
WASM_SET(u16, result + 2, 0); // fs_flags
WASM_SET(u64, result + 8, WASI_PATH_OPEN | WASI_FD_READDIR | WASI_PATH_FILESTAT_GET | WASI_FD_FILESTAT_GET); // fs_rights_base
WASM_SET(u64, result + 16, 0); // fs_rights_inheriting
return WASI_ESUCCESS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_fdstat_set_flags(wasi_t *wasi, u32 fd, u32 flags) {
WASI_DEBUG("fd_fdstat_set_flags(%u, %u)\n", fd, flags);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
return WASI_ESUCCESS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_filestat_get(wasi_t *wasi, u32 fd, usize result) {
WASI_DEBUG("fd_filestat_get(%u)\n", fd);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
WASM_SET(u64, result, fd); // dev
WASM_SET(u64, result + 8, 0); // ino
WASM_SET(u8, result + 16, WASI_IFCHR); // filetype
WASM_SET(u32, result + 24, 1); // nlink
WASM_SET(u64, result + 32, 0); // size
WASM_SET(u64, result + 40, 0); // atim
WASM_SET(u64, result + 48, 0); // mtim
WASM_SET(u64, result + 56, 0); // ctim
return WASI_ESUCCESS;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
{
PHYSFS_Stat stat;
if (!PHYSFS_stat(wasi->fdtable[fd].dir_handle()->c_str(), &stat)) {
return WASI_ENOENT;
}
if (stat.filetype != PHYSFS_FILETYPE_DIRECTORY) {
return WASI_EIO;
}
WASM_SET(u64, result, fd); // dev
WASM_SET(u64, result + 8, 0); // ino // TODO: generate a pseudorandom inode number
WASM_SET(u8, result + 16, WASI_IFDIR); // filetype
WASM_SET(u32, result + 24, 1); // nlink
WASM_SET(u64, result + 32, stat.filesize); // size
WASM_SET(u64, result + 40, stat.accesstime * 1000000000L); // atim
WASM_SET(u64, result + 48, stat.modtime * 1000000000L); // mtim
WASM_SET(u64, result + 56, stat.createtime * 1000000000L); // ctim
return WASI_ESUCCESS;
}
case wasi_fd_type::FSFILE:
{
PHYSFS_Stat stat;
if (!PHYSFS_stat(wasi->fdtable[fd].file_handle()->path(), &stat)) {
return WASI_ENOENT;
}
if (stat.filetype != PHYSFS_FILETYPE_REGULAR) {
return WASI_EIO;
}
WASM_SET(u64, result, fd); // dev
WASM_SET(u64, result + 8, 0); // ino // TODO: generate a pseudorandom inode number
WASM_SET(u8, result + 16, WASI_IFREG); // filetype
WASM_SET(u32, result + 24, 1); // nlink
WASM_SET(u64, result + 32, stat.filesize); // size
WASM_SET(u64, result + 40, stat.accesstime * 1000000000L); // atim
WASM_SET(u64, result + 48, stat.modtime * 1000000000L); // mtim
WASM_SET(u64, result + 56, stat.createtime * 1000000000L); // ctim
return WASI_ESUCCESS;
}
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_filestat_set_size(wasi_t *wasi, u32 fd, u64 size) {
WASI_DEBUG("fd_filestat_set_size(%u, %lu)\n", fd, size);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
return WASI_EINVAL;
default:
return WASI_ENOSYS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_pread(wasi_t *wasi, u32 fd, usize iovs, u32 iovs_len, u64 offset, usize result) {
WASI_DEBUG("fd_pread(%u, 0x%08x (%u), %lu)\n", fd, iovs, iovs_len, offset);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_prestat_dir_name(wasi_t *wasi, u32 fd, usize path, u32 path_len) {
WASI_DEBUG("fd_prestat_dir_name(%u, 0x%x (%u))\n", fd, path, path_len);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::FS:
std::strncpy((char *)WASM_MEM(path), wasi->fdtable[fd].dir_handle()->c_str(), path_len);
return WASI_ESUCCESS;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
return WASI_EINVAL;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_prestat_get(wasi_t *wasi, u32 fd, usize result) {
WASI_DEBUG("fd_prestat_get(%u)\n", fd);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::FS:
WASM_SET(u32, result, 0);
WASM_SET(u32, result + 4, wasi->fdtable[fd].dir_handle()->length());
return WASI_ESUCCESS;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
return WASI_EINVAL;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_pwrite(wasi_t *wasi, u32 fd, usize iovs, u32 iovs_len, u64 offset, usize result) {
WASI_DEBUG("fd_pwrite(%u, 0x%08x (%u), %lu)\n", fd, iovs, iovs_len, offset);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_read(wasi_t *wasi, u32 fd, usize iovs, u32 iovs_len, usize result) {
WASI_DEBUG("fd_read(%u, 0x%08x (%u))\n", fd, iovs, iovs_len);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
WASM_SET(u32, result, 0);
return WASI_ESUCCESS;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
return WASI_EINVAL;
case wasi_fd_type::FSFILE:
{
u32 size = 0;
while (iovs_len > 0) {
PHYSFS_sint64 n = PHYSFS_readBytes(wasi->fdtable[fd].file_handle()->get(), WASM_MEM(WASM_GET(u32, iovs)), WASM_GET(u32, iovs + 4));
if (n < 0) return WASI_EIO;
size += n;
iovs += 8;
--iovs_len;
}
WASM_SET(u32, result, size);
return WASI_ESUCCESS;
}
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_readdir(wasi_t *wasi, u32 fd, usize buf, u32 buf_len, u64 cookie, usize result) {
WASI_DEBUG("fd_readdir(%u, 0x%08x (%u), %lu)\n", fd, buf, buf_len, cookie);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSFILE:
return WASI_EINVAL;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
{
struct fs_enumerate_data edata = {
.wasi = wasi,
.fd = fd,
.original_buf = buf,
.buf = buf,
.buf_len = buf_len,
.initial_cookie = cookie,
.cookie = 0,
.result = result,
};
bool success = PHYSFS_enumerate(
wasi->fdtable[fd].dir_handle()->c_str(),
[](void *data, const char *path, const char *filename) {
struct fs_enumerate_data *edata = (struct fs_enumerate_data *)data;
wasi_t *wasi = edata->wasi;
PHYSFS_Stat stat;
if (!PHYSFS_stat(edata->wasi->fdtable[edata->fd].dir_handle()->c_str(), &stat)) {
return PHYSFS_ENUM_OK;
}
if (stat.filetype != PHYSFS_FILETYPE_DIRECTORY && stat.filetype != PHYSFS_FILETYPE_REGULAR) {
return PHYSFS_ENUM_OK;
}
if (edata->cookie++ < edata->initial_cookie) {
return PHYSFS_ENUM_OK;
}
if (edata->buf - edata->original_buf + 8 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
WASM_SET(u64, edata->buf, edata->cookie);
edata->buf += 8;
if (edata->buf - edata->original_buf + 8 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
WASM_SET(u64, edata->buf, 0); // TODO: generate a pseudorandom inode number
edata->buf += 8;
if (edata->buf - edata->original_buf + 4 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
WASM_SET(u32, edata->buf, std::strlen(filename));
edata->buf += 4;
if (edata->buf - edata->original_buf + 4 > edata->buf_len) {
return PHYSFS_ENUM_STOP;
}
WASM_SET(u8, edata->buf, stat.filetype);
edata->buf += 4;
u32 len = std::min(std::strlen(filename), (size_t)(edata->original_buf + edata->buf_len - edata->buf));
std::memcpy(WASM_MEM(edata->buf), filename, std::strlen(filename));
edata->buf += len;
return PHYSFS_ENUM_OK;
},
(void *)&edata
);
if (success) {
WASM_SET(u32, result, edata.buf - edata.original_buf);
return WASI_ESUCCESS;
}
return success ? WASI_ESUCCESS : WASI_ENOENT;
}
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_renumber(wasi_t *wasi, u32 fd, u32 to) {
WASI_DEBUG("fd_renumber(%u, %u)\n", fd, to);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FS:
return WASI_EINVAL;
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
break;
}
if (fd == to) {
return WASI_ESUCCESS;
}
if (to >= wasi->fdtable.size()) return WASI_EBADF;
switch (wasi->fdtable[to].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FS:
return WASI_EINVAL;
case wasi_fd_type::FSDIR:
case wasi_fd_type::FSFILE:
wasi->deallocate_file_descriptor(to);
if (to == wasi->fdtable.size()) {
wasi->fdtable.push_back(wasi->fdtable[fd]);
} else {
wasi->fdtable[to] = wasi->fdtable[fd];
}
if (!wasi->fdtable.empty() && fd == wasi->fdtable.size() - 1) {
wasi->fdtable.pop_back();
} else {
wasi->fdtable[fd] = {.type = wasi_fd_type::VACANT, .handle = NULL};
wasi->vacant_fds.push_back(fd);
}
return WASI_ESUCCESS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_seek(wasi_t *wasi, u32 fd, u64 offset, u32 whence, usize result) {
WASI_DEBUG("fd_seek(%u, %lu, %u)\n", fd, offset, whence);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_sync(wasi_t *wasi, u32 fd) {
WASI_DEBUG("fd_sync(%u)\n", fd);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_tell(wasi_t *wasi, u32 fd, usize result) {
WASI_DEBUG("fd_tell(%u)\n", fd);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
return WASI_EINVAL;
case wasi_fd_type::FSFILE:
WASM_SET(u64, result, PHYSFS_tell(wasi->fdtable[fd].file_handle()->get()));
return WASI_ESUCCESS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_fd_write(wasi_t *wasi, u32 fd, usize iovs, u32 iovs_len, usize result) {
WASI_DEBUG("fd_write(%u, 0x%08x (%u))\n", fd, iovs, iovs_len);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
WASM_SET(u32, result, 0);
return WASI_ESUCCESS;
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
{
u32 size = 0;
std::string buf;
while (iovs_len > 0) {
buf.append((const char *)WASM_MEM(WASM_GET(u32, iovs)), strlen_safe((const char *)WASM_MEM(WASM_GET(u32, iovs)), WASM_GET(u32, iovs + 4)));
size += WASM_GET(u32, iovs + 4);
iovs += 8;
--iovs_len;
}
std::string line;
std::istringstream stream(buf);
while (std::getline(stream, line)) {
mkxp_retro::log_printf(wasi->fdtable[fd].type == wasi_fd_type::STDOUT ? RETRO_LOG_INFO : RETRO_LOG_ERROR, "%s\n", line.c_str());
}
WASM_SET(u32, result, size);
return WASI_ESUCCESS;
}
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
return WASI_EINVAL;
case wasi_fd_type::FSFILE:
return WASI_EROFS;
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_create_directory(wasi_t *wasi, u32 fd, usize path, u32 path_len) {
WASI_DEBUG("path_create_directory(%u, \"%.*s\")\n", fd, path_len, (char *)WASM_MEM(path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_filestat_get(wasi_t *wasi, u32 fd, u32 flags, usize path, u32 path_len, usize result) {
WASI_DEBUG("path_filestat_get(%u, %u, \"%.*s\")\n", fd, flags, path_len, (char *)WASM_MEM(path));
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSFILE:
return WASI_EINVAL;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
{
PHYSFS_Stat stat;
std::string new_path(*wasi->fdtable[fd].dir_handle());
new_path.push_back('/');
new_path.append((const char *)WASM_MEM(path), strlen_safe((const char *)WASM_MEM(path), path_len));
new_path = mkxp_retro::fs->normalize(new_path.c_str(), false, true);
if (std::strncmp(new_path.c_str(), wasi->fdtable[fd].dir_handle()->c_str(), wasi->fdtable[fd].dir_handle()->length()) != 0) {
return WASI_EPERM;
}
if (!PHYSFS_stat(new_path.c_str(), &stat)) {
return WASI_ENOENT;
}
if (stat.filetype != PHYSFS_FILETYPE_DIRECTORY && stat.filetype != PHYSFS_FILETYPE_REGULAR) {
return WASI_EIO;
}
WASM_SET(u64, result, fd); // dev
WASM_SET(u64, result + 8, 0); // ino // TODO: generate a pseudorandom inode number
WASM_SET(u8, result + 16, stat.filetype == PHYSFS_FILETYPE_DIRECTORY ? WASI_IFDIR : WASI_IFREG); // filetype
WASM_SET(u32, result + 24, 1); // nlink
WASM_SET(u64, result + 32, stat.filetype); // size
WASM_SET(u64, result + 40, stat.accesstime * 1000000000L); // atim
WASM_SET(u64, result + 48, stat.modtime * 1000000000L); // mtim
WASM_SET(u64, result + 56, stat.createtime * 1000000000L); // ctim
return WASI_ESUCCESS;
}
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_filestat_set_times(wasi_t *wasi, u32 fd, u32 flags, usize path, u32 path_len, u64 atim, u64 ntim, u32 fst_flags) {
WASI_DEBUG("path_filestat_set_times(%u, %u, \"%.*s\", %lu, %lu, %u)\n", fd, flags, path_len, (char *)WASM_MEM(path), atim, ntim, fst_flags);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_link(wasi_t *wasi, u32 old_fd, u32 old_flags, usize old_path, u32 old_path_len, u32 new_fd, usize new_path, u32 new_path_len) {
WASI_DEBUG("path_link(%u, %u, \"%.*s\", %u, \"%.*s\")\n", old_fd, old_flags, old_path_len, (char *)WASM_MEM(old_path), new_fd, new_path_len, (char *)WASM_MEM(new_path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_open(wasi_t *wasi, u32 fd, u32 dirflags, usize path, u32 path_len, u32 oflags, u64 fs_base_rights, u64 fs_rights_inheriting, u32 fdflags, usize result) {
WASI_DEBUG("path_open(%u, %u, \"%.*s\", %u, %lu, %lu, %u)\n", fd, dirflags, path_len, (char *)WASM_MEM(path), oflags, fs_base_rights, fs_rights_inheriting, fdflags);
if (fd >= wasi->fdtable.size()) {
return WASI_EBADF;
}
switch (wasi->fdtable[fd].type) {
case wasi_fd_type::VACANT:
return WASI_EBADF;
case wasi_fd_type::STDIN:
case wasi_fd_type::STDOUT:
case wasi_fd_type::STDERR:
case wasi_fd_type::FSFILE:
return WASI_EINVAL;
case wasi_fd_type::FS:
case wasi_fd_type::FSDIR:
{
PHYSFS_Stat stat;
std::string new_path(*wasi->fdtable[fd].dir_handle());
new_path.push_back('/');
new_path.append((const char *)WASM_MEM(path), strlen_safe((const char *)WASM_MEM(path), path_len));
new_path = mkxp_retro::fs->normalize(new_path.c_str(), false, true);
if (std::strncmp(new_path.c_str(), wasi->fdtable[fd].dir_handle()->c_str(), wasi->fdtable[fd].dir_handle()->length()) != 0) {
return WASI_EPERM;
}
if (!PHYSFS_stat(new_path.c_str(), &stat)) {
return WASI_ENOENT;
}
if (stat.filetype != PHYSFS_FILETYPE_DIRECTORY && stat.filetype != PHYSFS_FILETYPE_REGULAR) {
return WASI_EIO;
}
if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
std::string *handle = new std::string(new_path);
WASM_SET(u32, result, wasi->allocate_file_descriptor(wasi_fd_type::FSDIR, handle));
} else {
struct FileSystem::File *handle = new FileSystem::File(*mkxp_retro::fs, new_path.c_str(), FileSystem::OpenMode::Read);
WASM_SET(u32, result, wasi->allocate_file_descriptor(wasi_fd_type::FSFILE, handle));
}
return WASI_ESUCCESS;
}
}
return WASI_EBADF;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_readlink(wasi_t *wasi, u32 fd, usize path, u32 path_len, usize buf, u32 buf_len, usize result) {
WASI_DEBUG("path_readlink(%u, \"%.*s\", 0x%08x (%u))\n", fd, path_len, (char *)WASM_MEM(path), buf, buf_len);
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_remove_directory(wasi_t *wasi, u32 fd, usize path, u32 path_len) {
WASI_DEBUG("path_remove_directory(%u, \"%.*s\")\n", fd, path_len, (char *)WASM_MEM(path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_rename(wasi_t *wasi, u32 fd, usize old_path, u32 old_path_len, u32 new_fd, usize new_path, u32 new_path_len) {
WASI_DEBUG("path_rename(%u, \"%.*s\", %u, \"%.*s\")\n", fd, old_path_len, (char *)WASM_MEM(old_path), new_fd, new_path_len, (char *)WASM_MEM(new_path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_symlink(wasi_t *wasi, usize old_path, u32 old_path_len, u32 fd, usize new_path, u32 new_path_len) {
WASI_DEBUG("path_symlink(\"%.*s\", %u, \"%.*s\")\n", old_path_len, (char *)WASM_MEM(old_path), fd, new_path_len, (char *)WASM_MEM(new_path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_path_unlink_file(wasi_t *wasi, u32 fd, usize path, u32 path_len) {
WASI_DEBUG("path_unlink_file(%u, \"%.*s\")\n", fd, path_len, (char *)WASM_MEM(path));
return WASI_ENOSYS;
}
extern "C" u32 w2c_wasi__snapshot__preview1_poll_oneoff(wasi_t *wasi, usize in, usize out, u32 nsubscriptions, usize result) {
WASI_DEBUG("poll_oneoff(0x%08x, 0x%08x, %u)\n", in, out, nsubscriptions);
return WASI_ENOSYS;
}
extern "C" void w2c_wasi__snapshot__preview1_proc_exit(wasi_t *wasi, u32 rval) {
WASI_DEBUG("proc_exit(%u)\n", rval);
std::abort();
}
extern "C" u32 w2c_wasi__snapshot__preview1_random_get(wasi_t *wasi, usize buf, u32 buf_len) {
WASI_DEBUG("random_get(0x%08x (%u))\n", buf, buf_len);
static std::random_device dev;
static std::mt19937 rng(dev());
static u32 rng_buffer;
static u32 rng_buffer_size = 0;
while (buf_len > 0) {
if (rng_buffer_size == 0) {
rng_buffer = rng();
rng_buffer_size = 4;
} else {
u32 n = std::min(rng_buffer_size, buf_len);
std::memcpy(WASM_MEM(buf), (u8 *)&rng_buffer + (4 - n), n);
buf += n;
buf_len -= n;
rng_buffer_size -= n;
}
}
return WASI_ESUCCESS;
}