blob: b380c12ac15ce78a5da34aea237936b269966bd8 [file] [edit]
// Copyright 2022 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
#include <syscall_arch.h>
#include <unistd.h>
#include "backend.h"
#include "file.h"
#include "paths.h"
using namespace wasmfs;
extern "C" {
__wasi_fd_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend);
// Copy the file specified by the pathname into JS.
// Return a pointer to the JS buffer in HEAPU8.
// The buffer will also contain the file length.
// Caller must free the returned pointer.
void* _wasmfs_read_file(char* path) {
struct stat file;
int err = 0;
err = stat(path, &file);
if (err < 0) {
emscripten_console_error("Fatal error in FS.readFile");
abort();
}
// The function will return a pointer to a buffer with the file length in the
// first 8 bytes. The remaining bytes will contain the buffer contents. This
// allows the caller to use HEAPU8.subarray(buf + 8, buf + 8 + length).
off_t size = file.st_size;
uint8_t* result = (uint8_t*)malloc((size + sizeof(size)));
*(uint32_t*)result = size;
int fd = open(path, O_RDONLY);
if (fd < 0) {
emscripten_console_error("Fatal error in FS.readFile");
abort();
}
int numRead = pread(fd, result + sizeof(size), size, 0);
// TODO: Generalize this so that it is thread-proof.
// Must guarantee that the file size has not changed by the time it is read.
assert(numRead == size);
err = close(fd);
if (err < 0) {
emscripten_console_error("Fatal error in FS.readFile");
abort();
}
return result;
}
// Writes to a file, possibly creating it, and returns the number of bytes
// written successfully.
int _wasmfs_write_file(char* pathname, char* data, size_t data_size) {
auto parsedParent = path::parseParent(pathname);
if (parsedParent.getError()) {
return 0;
}
auto& [parent, childNameView] = parsedParent.getParentChild();
std::string childName(childNameView);
std::shared_ptr<File> child;
{
auto lockedParent = parent->locked();
child = lockedParent.getChild(childName);
if (!child) {
// Lookup failed; try creating the file.
child = lockedParent.insertDataFile(childName, 0777);
if (!child) {
// File creation failed; nothing else to do.
return 0;
}
}
}
auto dataFile = child->dynCast<DataFile>();
if (!dataFile) {
// There is something here but it isn't a data file.
return 0;
}
auto result = dataFile->locked().write((uint8_t*)data, data_size, 0);
if (result != __WASI_ERRNO_SUCCESS) {
return 0;
}
return data_size;
}
int _wasmfs_mkdir(char* path, int mode) {
return __syscall_mkdirat(AT_FDCWD, (intptr_t)path, mode);
}
int _wasmfs_chdir(char* path) { return __syscall_chdir((intptr_t)path); }
void _wasmfs_symlink(char* old_path, char* new_path) {
__syscall_symlink((intptr_t)old_path, (intptr_t)new_path);
}
int _wasmfs_chmod(char* path, mode_t mode) {
return __syscall_chmod((intptr_t)path, mode);
}
// Helper method that identifies what a path is:
// ENOENT - if nothing exists there
// EISDIR - if it is a directory
// EEXIST - if it is a normal file
int _wasmfs_identify(char* path) {
struct stat file;
int err = 0;
err = stat(path, &file);
if (err < 0) {
return ENOENT;
}
if (S_ISDIR(file.st_mode)) {
return EISDIR;
}
return EEXIST;
}
}