Path: blob/main/system/lib/wasmfs/js_api.cpp
6174 views
// Copyright 2022 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45#include <dirent.h>6#include <syscall_arch.h>7#include <unistd.h>8#include <emscripten/wasmfs.h>910#include "backend.h"11#include "file.h"12#include "paths.h"1314// Some APIs return data using a thread-local allocation that is never freed.15// This is simpler and more efficient as it avoids the JS caller needing to free16// the allocation (which would have both the overhead of free, and also of a17// call back into wasm), but on the other hand it does mean more memory may be18// used. This seems a reasonable tradeoff as heavy workloads should ideally19// avoid the JS API anyhow.2021using namespace wasmfs;2223extern "C" {2425// Copy the file specified by the pathname into JS.26// Return zero on success, errno on failure.27// Output point and length are written to `out_buf` and `out_size` params.28int _wasmfs_read_file(const char* path, uint8_t** out_buf, off_t* out_size) {29static_assert(sizeof(off_t) == 8, "File offset type must be 64-bit");3031struct stat file;32int err = 0;33err = stat(path, &file);34if (err < 0) {35return errno;36}3738off_t size = file.st_size;3940static thread_local uint8_t* buffer = nullptr;41buffer = (uint8_t*)realloc(buffer, size);4243int fd = open(path, O_RDONLY);44if (fd < 0) {45return errno;46}47ssize_t numRead = read(fd, buffer, size);48if (numRead < 0) {49return errno;50}51// TODO: Generalize this so that it is thread-proof.52// Must guarantee that the file size has not changed by the time it is read.53assert(numRead == size);54err = close(fd);55if (err < 0) {56return errno;57}5859*out_size = size;60*out_buf = buffer;61return 0;62}6364// Writes to a file, possibly creating it, and returns the number of bytes65// written successfully. If the file already exists, appends to it.66int _wasmfs_write_file(const char* pathname, char* data, size_t data_size) {67auto parsedParent = path::parseParent(pathname);68if (parsedParent.getError()) {69return 0;70}71auto& [parent, childNameView] = parsedParent.getParentChild();72std::string childName(childNameView);7374std::shared_ptr<File> child;75{76auto lockedParent = parent->locked();77child = lockedParent.getChild(childName);78if (!child) {79// Lookup failed; try creating the file.80child = lockedParent.insertDataFile(childName, 0777);81if (!child) {82// File creation failed; nothing else to do.83return 0;84}85}86}8788auto dataFile = child->dynCast<DataFile>();89if (!dataFile) {90// There is something here but it isn't a data file.91return 0;92}9394auto lockedFile = dataFile->locked();95int err = lockedFile.open(O_WRONLY);96if (err < 0) {97emscripten_err("Fatal error in FS.writeFile");98abort();99}100101auto offset = lockedFile.getSize();102auto result = lockedFile.write((uint8_t*)data, data_size, offset);103if (result != __WASI_ERRNO_SUCCESS) {104return 0;105}106107err = lockedFile.close();108if (err < 0) {109emscripten_err("Fatal error in FS.writeFile");110abort();111}112113return data_size;114}115116int _wasmfs_mkdir(const char* path, int mode) {117return __syscall_mkdirat(AT_FDCWD, (intptr_t)path, mode);118}119120int _wasmfs_rmdir(const char* path) {121return __syscall_unlinkat(AT_FDCWD, (intptr_t)path, AT_REMOVEDIR);122}123124int _wasmfs_open(const char* path, int flags, mode_t mode) {125return __syscall_openat(AT_FDCWD, (intptr_t)path, flags, mode);126}127128int _wasmfs_mknod(const char* path, mode_t mode, dev_t dev) {129return __syscall_mknodat(AT_FDCWD, (intptr_t)path, mode, dev);130}131132int _wasmfs_unlink(const char* path) {133return __syscall_unlinkat(AT_FDCWD, (intptr_t)path, 0);134}135136int _wasmfs_chdir(const char* path) { return __syscall_chdir((intptr_t)path); }137138int _wasmfs_symlink(const char* old_path, const char* new_path) {139return __syscall_symlinkat((intptr_t)old_path, AT_FDCWD, (intptr_t)new_path);140}141142int _wasmfs_readlink(const char* path, char** out_ptr) {143static thread_local char* readBuf = (char*)malloc(PATH_MAX);144int bytes =145__syscall_readlinkat(AT_FDCWD, (intptr_t)path, (intptr_t)readBuf, PATH_MAX);146if (bytes < 0) {147return bytes;148}149readBuf[bytes] = '\0';150*out_ptr = readBuf;151return 0;152}153154int _wasmfs_write(int fd, void* buf, size_t count) {155__wasi_ciovec_t iovs[1];156iovs[0].buf = (uint8_t*)buf;157iovs[0].buf_len = count;158159__wasi_size_t numBytes;160__wasi_errno_t err = __wasi_fd_write(fd, iovs, 1, &numBytes);161if (err) {162return -err;163}164return numBytes;165}166167int _wasmfs_pwrite(int fd, void* buf, size_t count, off_t offset) {168__wasi_ciovec_t iovs[1];169iovs[0].buf = (uint8_t*)buf;170iovs[0].buf_len = count;171172__wasi_size_t numBytes;173__wasi_errno_t err = __wasi_fd_pwrite(fd, iovs, 1, offset, &numBytes);174if (err) {175return -err;176}177return numBytes;178}179180int _wasmfs_chmod(const char* path, mode_t mode) {181return __syscall_chmod((intptr_t)path, mode);182}183184int _wasmfs_fchmod(int fd, mode_t mode) { return __syscall_fchmod(fd, mode); }185186int _wasmfs_lchmod(const char* path, mode_t mode) {187return __syscall_fchmodat2(188AT_FDCWD, (intptr_t)path, mode, AT_SYMLINK_NOFOLLOW);189}190191int _wasmfs_llseek(int fd, off_t offset, int whence) {192__wasi_filesize_t newOffset;193int err = __wasi_fd_seek(fd, offset, whence, &newOffset);194if (err > 0) {195return -err;196}197return newOffset;198}199200int _wasmfs_rename(const char* oldpath, const char* newpath) {201return __syscall_renameat(202AT_FDCWD, (intptr_t)oldpath, AT_FDCWD, (intptr_t)newpath);203}204205int _wasmfs_read(int fd, void* buf, size_t count) {206__wasi_iovec_t iovs[1];207iovs[0].buf = (uint8_t*)buf;208iovs[0].buf_len = count;209210__wasi_size_t numBytes;211__wasi_errno_t err = __wasi_fd_read(fd, iovs, 1, &numBytes);212if (err) {213return -err;214}215return numBytes;216}217218int _wasmfs_pread(int fd, void* buf, size_t count, off_t offset) {219__wasi_iovec_t iovs[1];220iovs[0].buf = (uint8_t*)buf;221iovs[0].buf_len = count;222223__wasi_size_t numBytes;224__wasi_errno_t err = __wasi_fd_pread(fd, iovs, 1, offset, &numBytes);225if (err) {226return -err;227}228return numBytes;229}230231int _wasmfs_truncate(const char* path, off_t length) {232return __syscall_truncate64((intptr_t)path, length);233}234235int _wasmfs_ftruncate(int fd, off_t length) {236return __syscall_ftruncate64(fd, length);237}238239int _wasmfs_close(int fd) { return __wasi_fd_close(fd); }240241void* _wasmfs_mmap(size_t length, int prot, int flags, int fd, off_t offset) {242return (void*)__syscall_mmap2(0, length, prot, flags, fd, offset);243}244245int _wasmfs_msync(void* addr, size_t length, int flags) {246return __syscall_msync((intptr_t)addr, length, flags);247}248249int _wasmfs_munmap(void* addr, size_t length) {250return __syscall_munmap((intptr_t)addr, length);251}252253int _wasmfs_utime(const char* path, double atime_ms, double mtime_ms) {254struct timespec times[2];255times[0].tv_sec = (long)atime_ms / 1000;256times[0].tv_nsec = ((long)atime_ms % 1000) * 1000000;257times[1].tv_sec = (long)mtime_ms / 1000;258times[1].tv_nsec = ((long)mtime_ms % 1000) * 1000000;259260return __syscall_utimensat(AT_FDCWD, (intptr_t)path, (intptr_t)times, 0);261}262263int _wasmfs_stat(const char* path, struct stat* statBuf) {264return __syscall_stat64((intptr_t)path, (intptr_t)statBuf);265}266267int _wasmfs_lstat(const char* path, struct stat* statBuf) {268return __syscall_lstat64((intptr_t)path, (intptr_t)statBuf);269}270271// The legacy JS API requires a mountpoint to already exist, so WasmFS will272// attempt to remove the target directory if it exists before replacing it with273// a mounted directory.274int _wasmfs_mount(const char* path, ::backend_t created_backend) {275int err = __syscall_rmdir((intptr_t)path);276277// The legacy JS API mount requires the directory to already exist, but we278// will also allow it to be missing.279if (err && err != -ENOENT) {280return err;281}282283return wasmfs_create_directory(path, 0777, created_backend);284}285286// Helper method that identifies what a path is:287// ENOENT - if nothing exists there288// EISDIR - if it is a directory289// EEXIST - if it is a normal file290int _wasmfs_identify(const char* path) {291struct stat file;292int err = 0;293err = stat(path, &file);294if (err < 0) {295return ENOENT;296}297if (S_ISDIR(file.st_mode)) {298return EISDIR;299}300return EEXIST;301}302303struct wasmfs_readdir_state {304int i;305int nentries;306struct dirent** entries;307};308309struct wasmfs_readdir_state* _wasmfs_readdir_start(const char* path) {310struct dirent** entries;311int nentries = scandir(path, &entries, NULL, alphasort);312if (nentries == -1) {313return NULL;314}315struct wasmfs_readdir_state* state =316(struct wasmfs_readdir_state*)malloc(sizeof(*state));317if (state == NULL) {318return NULL;319}320state->i = 0;321state->nentries = nentries;322state->entries = entries;323return state;324}325326const char* _wasmfs_readdir_get(struct wasmfs_readdir_state* state) {327if (state->i < state->nentries) {328return state->entries[state->i++]->d_name;329}330return NULL;331}332333void _wasmfs_readdir_finish(struct wasmfs_readdir_state* state) {334for (int i = 0; i < state->nentries; i++) {335free(state->entries[i]);336}337free(state->entries);338free(state);339}340341char* _wasmfs_get_cwd(void) {342// TODO: PATH_MAX is 4K atm, so it might be good to reduce this somehow.343static thread_local char* path = (char*)malloc(PATH_MAX);344return getcwd(path, PATH_MAX);345}346347} // extern "C"348349350