[WASMFS] JS File Backend Structure (#15562)
Relevant Issue: #15041 Tentative JS File Backend Structure Introduced user-visible header file. Users can create a new JS Backend and create JS Backed Files under both In-Memory and JS Backend directories.
This commit is contained in:
Родитель
63b3f94493
Коммит
7e6d511d91
|
@ -1,7 +1,9 @@
|
|||
var WasmfsLibrary = {
|
||||
$wasmFS$JSMemoryFiles : [],
|
||||
$wasmFS$JSMemoryFreeList: [],
|
||||
$wasmFS$preloadedFiles: [],
|
||||
$wasmFS$preloadedDirs: [],
|
||||
$FS__deps: ['$wasmFS$preloadedFiles', '$wasmFS$preloadedDirs'],
|
||||
$FS__deps: ['$wasmFS$preloadedFiles', '$wasmFS$preloadedDirs', '$wasmFS$JSMemoryFiles', '$wasmFS$JSMemoryFreeList'],
|
||||
$FS : {
|
||||
// TODO: Clean up the following functions - currently copied from library_fs.js directly.
|
||||
createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) {
|
||||
|
@ -85,6 +87,54 @@ var WasmfsLibrary = {
|
|||
// For file preloading, cwd should be '/' to begin with.
|
||||
return '/';
|
||||
}
|
||||
},
|
||||
_emscripten_write_js_file: function(index, buffer, length, offset) {
|
||||
try {
|
||||
if (!wasmFS$JSMemoryFiles[index]) {
|
||||
// Initialize typed array on first write operation.
|
||||
wasmFS$JSMemoryFiles[index] = new Uint8Array(offset + length);
|
||||
}
|
||||
|
||||
if (offset + length > wasmFS$JSMemoryFiles[index].length) {
|
||||
// Resize the typed array if the length of the write buffer exceeds its capacity.
|
||||
var oldContents = wasmFS$JSMemoryFiles[index];
|
||||
var newContents = new Uint8Array(offset + length);
|
||||
newContents.set(oldContents);
|
||||
wasmFS$JSMemoryFiles[index] = newContents;
|
||||
}
|
||||
|
||||
wasmFS$JSMemoryFiles[index].set(HEAPU8.subarray(buffer, buffer + length), offset);
|
||||
return 0;
|
||||
} catch (err) {
|
||||
return {{{ cDefine('EIO') }}};
|
||||
}
|
||||
},
|
||||
_emscripten_read_js_file: function(index, buffer, length, offset) {
|
||||
try {
|
||||
HEAPU8.set(wasmFS$JSMemoryFiles[index].subarray(offset, offset + length), buffer);
|
||||
return 0;
|
||||
} catch (err) {
|
||||
return {{{ cDefine('EIO') }}};
|
||||
}
|
||||
},
|
||||
_emscripten_get_js_file_size: function(index) {
|
||||
return wasmFS$JSMemoryFiles[index] ? wasmFS$JSMemoryFiles[index].length : 0;
|
||||
},
|
||||
_emscripten_create_js_file: function() {
|
||||
// Find a free entry in the $wasmFS$JSMemoryFreeList or append a new entry to
|
||||
// wasmFS$JSMemoryFiles.
|
||||
if (wasmFS$JSMemoryFreeList.length) {
|
||||
// Pop off the top of the free list.
|
||||
var index = wasmFS$JSMemoryFreeList.pop();
|
||||
return index;
|
||||
}
|
||||
wasmFS$JSMemoryFiles.push(null);
|
||||
return wasmFS$JSMemoryFiles.length - 1;
|
||||
},
|
||||
_emscripten_remove_js_file: function(index) {
|
||||
wasmFS$JSMemoryFiles[index] = null;
|
||||
// Add the index to the free list.
|
||||
wasmFS$JSMemoryFreeList.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Backend* backend_t;
|
||||
|
||||
// Obtains the backend_t of a specified path.
|
||||
backend_t wasmfs_get_backend_by_path(char* path);
|
||||
|
||||
// Obtains the backend_t of a specified fd.
|
||||
backend_t wasmfs_get_backend_by_fd(int fd);
|
||||
|
||||
// Creates a JSFile Backend in the new file system.
|
||||
backend_t wasmfs_create_js_file_backend();
|
||||
|
||||
// Creates a file in a specific backend and returns an fd to an open file.
|
||||
uint32_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend);
|
||||
|
||||
// Creates a new directory in the new file system under a specific backend.
|
||||
long wasmfs_create_directory(char* path, long mode, backend_t backend);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
namespace wasmfs {
|
||||
// A backend (or modular backend) provides a base for the new file system to
|
||||
// extend its storage capabilities. Files and directories will be represented in
|
||||
// the file system structure, but their underlying backing could exist in
|
||||
// extend its storage capabilities. Files and directories will be represented
|
||||
// in the file system structure, but their underlying backing could exist in
|
||||
// persistent storage, another thread, etc.
|
||||
class Backend {
|
||||
|
||||
|
@ -29,7 +29,5 @@ public:
|
|||
// Note: Backends will be defined in cpp files, but functions to instantiate
|
||||
// them will be defined in a header file. This is so that any unused backends
|
||||
// are not linked in if they are not called.
|
||||
// TODO: In the next PR, a user-visible header is introduced. Update this
|
||||
// comment then.
|
||||
backend_t createMemoryFileBackend();
|
||||
} // namespace wasmfs
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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.
|
||||
|
||||
// This file defines the file object of the new file system.
|
||||
// Current Status: Work in Progress.
|
||||
// See https://github.com/emscripten-core/emscripten/issues/15041.
|
||||
|
@ -25,6 +26,7 @@ class Backend;
|
|||
// This represents an opaque pointer to a Backend. A user may use this to
|
||||
// specify a backend in file operations.
|
||||
using backend_t = Backend*;
|
||||
const backend_t NullBackend = nullptr;
|
||||
|
||||
class File : public std::enable_shared_from_this<File> {
|
||||
|
||||
|
@ -62,6 +64,8 @@ public:
|
|||
return (ino_t)this;
|
||||
}
|
||||
|
||||
backend_t getBackend() { return backend; }
|
||||
|
||||
class Handle {
|
||||
|
||||
protected:
|
||||
|
@ -101,7 +105,8 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
File(FileKind kind, mode_t mode) : kind(kind), mode(mode) {}
|
||||
File(FileKind kind, mode_t mode, backend_t backend)
|
||||
: kind(kind), mode(mode), backend(backend) {}
|
||||
// A mutex is needed for multiple accesses to the same file.
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
|
@ -121,6 +126,9 @@ protected:
|
|||
// dependencies where the parent and child have shared_ptrs that reference
|
||||
// each other. This prevents the case in which an uncollectable cycle occurs.
|
||||
std::weak_ptr<File> parent;
|
||||
|
||||
// This specifies which backend a file is associated with.
|
||||
backend_t backend;
|
||||
};
|
||||
|
||||
class DataFile : public File {
|
||||
|
@ -131,7 +139,8 @@ class DataFile : public File {
|
|||
|
||||
public:
|
||||
static constexpr FileKind expectedKind = File::DataFileKind;
|
||||
DataFile(mode_t mode) : File(File::DataFileKind, mode) {}
|
||||
DataFile(mode_t mode, backend_t backend)
|
||||
: File(File::DataFileKind, mode, backend) {}
|
||||
virtual ~DataFile() = default;
|
||||
|
||||
class Handle : public File::Handle {
|
||||
|
@ -161,15 +170,10 @@ protected:
|
|||
// This value was also copied from the existing file system.
|
||||
size_t getSize() override { return 4096; }
|
||||
|
||||
// This specifies which backend a directory is associated with. By default,
|
||||
// files and sub-directories added to this directory's entries will be created
|
||||
// through this same backend unless an alternative is specified.
|
||||
backend_t backend;
|
||||
|
||||
public:
|
||||
static constexpr FileKind expectedKind = File::DirectoryKind;
|
||||
Directory(mode_t mode, backend_t backend)
|
||||
: File(File::DirectoryKind, mode), backend(backend) {}
|
||||
: File(File::DirectoryKind, mode, backend) {}
|
||||
|
||||
struct Entry {
|
||||
std::string name;
|
||||
|
@ -231,8 +235,6 @@ public:
|
|||
return entries;
|
||||
}
|
||||
|
||||
backend_t getBackend() { return getDir()->backend; }
|
||||
|
||||
#ifdef WASMFS_DEBUG
|
||||
void printKeys() {
|
||||
for (auto keyPair : getDir()->entries) {
|
||||
|
@ -253,7 +255,6 @@ public:
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Obtains parent directory of a given pathname.
|
||||
// Will return a nullptr if the parent is not a directory.
|
||||
// Will error if the forbiddenAncestor is encountered while processing.
|
||||
|
@ -265,8 +266,8 @@ getDir(std::vector<std::string>::iterator begin,
|
|||
long& err,
|
||||
std::shared_ptr<File> forbiddenAncestor = nullptr);
|
||||
|
||||
// Return a vector of the '/'-delimited components of a path. The first element
|
||||
// will be "/" iff the path is an absolute path.
|
||||
// Return a vector of the '/'-delimited components of a path. The first
|
||||
// element will be "/" iff the path is an absolute path.
|
||||
std::vector<std::string> splitPath(char* pathname);
|
||||
|
||||
} // namespace wasmfs
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2021 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.
|
||||
|
||||
// This file defines the JS file backend and JS file of the new file system.
|
||||
// Current Status: Work in Progress.
|
||||
// See https://github.com/emscripten-core/emscripten/issues/15041.
|
||||
|
||||
#include "backend.h"
|
||||
#include "wasmfs.h"
|
||||
|
||||
using js_index_t = uint32_t;
|
||||
|
||||
extern "C" {
|
||||
int _emscripten_write_js_file(js_index_t index,
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
off_t offset);
|
||||
int _emscripten_read_js_file(js_index_t index,
|
||||
const uint8_t* buffer,
|
||||
size_t length,
|
||||
off_t offset);
|
||||
int _emscripten_get_js_file_size(js_index_t index);
|
||||
int _emscripten_create_js_file();
|
||||
void _emscripten_remove_js_file(js_index_t index);
|
||||
}
|
||||
|
||||
namespace wasmfs {
|
||||
|
||||
// This class describes a file that lives in JS Memory
|
||||
class JSFile : public DataFile {
|
||||
// This index indicates the location of the JS File in the backing JS array.
|
||||
js_index_t index;
|
||||
|
||||
// JSFiles will write from a Wasm Memory buffer into the backing JS array.
|
||||
__wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override {
|
||||
return _emscripten_write_js_file(index, buf, len, offset);
|
||||
}
|
||||
|
||||
// JSFiles will read from the backing JS array into a Wasm Memory buffer.
|
||||
__wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override {
|
||||
// The caller should have already checked that the offset + len does
|
||||
// not exceed the file's size.
|
||||
assert(offset + len <= getSize());
|
||||
return _emscripten_read_js_file(index, buf, len, offset);
|
||||
}
|
||||
|
||||
// The size of the JSFile is defined as the length of the backing JS array.
|
||||
size_t getSize() override { return _emscripten_get_js_file_size(index); }
|
||||
|
||||
public:
|
||||
JSFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {
|
||||
// Create a new file in the backing JS array and store its index.
|
||||
index = _emscripten_create_js_file();
|
||||
}
|
||||
|
||||
// Remove the typed array file contents in the backing JS array.
|
||||
~JSFile() { _emscripten_remove_js_file(index); }
|
||||
};
|
||||
|
||||
class JSFileBackend : public Backend {
|
||||
|
||||
public:
|
||||
std::shared_ptr<DataFile> createFile(mode_t mode) override {
|
||||
return std::make_shared<JSFile>(mode, this);
|
||||
}
|
||||
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
|
||||
return std::make_shared<Directory>(mode, this);
|
||||
}
|
||||
};
|
||||
|
||||
// This function is exposed to users to instantiate a new JSBackend.
|
||||
extern "C" backend_t wasmfs_create_js_file_backend() {
|
||||
return wasmFS.addBackend(std::make_unique<JSFileBackend>());
|
||||
}
|
||||
|
||||
} // namespace wasmfs
|
|
@ -2,6 +2,7 @@
|
|||
// 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.
|
||||
|
||||
// This file defines the memory file class of the new file system.
|
||||
// This should be the only backend file type defined in a header since it is the
|
||||
// default type. Current Status: Work in Progress. See
|
||||
|
@ -23,7 +24,7 @@ class MemoryFile : public DataFile {
|
|||
size_t getSize() override { return buffer.size(); }
|
||||
|
||||
public:
|
||||
MemoryFile(mode_t mode) : DataFile(mode) {}
|
||||
MemoryFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {}
|
||||
|
||||
class Handle : public DataFile::Handle {
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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.
|
||||
|
||||
// This file defines the memory file backend of the new file system.
|
||||
// Current Status: Work in Progress.
|
||||
// See https://github.com/emscripten-core/emscripten/issues/15041.
|
||||
|
@ -15,7 +16,7 @@ class MemoryFileBackend : public Backend {
|
|||
|
||||
public:
|
||||
std::shared_ptr<DataFile> createFile(mode_t mode) override {
|
||||
return std::make_shared<MemoryFile>(mode);
|
||||
return std::make_shared<MemoryFile>(mode, this);
|
||||
}
|
||||
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
|
||||
return std::make_shared<Directory>(mode, this);
|
||||
|
|
|
@ -28,7 +28,7 @@ class StdinFile : public DataFile {
|
|||
size_t getSize() override { return 0; }
|
||||
|
||||
public:
|
||||
StdinFile(mode_t mode) : DataFile(mode) {}
|
||||
StdinFile(mode_t mode) : DataFile(mode, NullBackend) {}
|
||||
static std::shared_ptr<StdinFile> getSingleton();
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ class StdoutFile : public DataFile {
|
|||
size_t getSize() override { return 0; }
|
||||
|
||||
public:
|
||||
StdoutFile(mode_t mode) : DataFile(mode) {}
|
||||
StdoutFile(mode_t mode) : DataFile(mode, NullBackend) {}
|
||||
static std::shared_ptr<StdoutFile> getSingleton();
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ class StderrFile : public DataFile {
|
|||
size_t getSize() override { return 0; }
|
||||
|
||||
public:
|
||||
StderrFile(mode_t mode) : DataFile(mode) {}
|
||||
StderrFile(mode_t mode) : DataFile(mode, NullBackend) {}
|
||||
static std::shared_ptr<StderrFile> getSingleton();
|
||||
};
|
||||
} // namespace wasmfs
|
||||
|
|
|
@ -288,6 +288,58 @@ __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd) {
|
|||
return __WASI_ERRNO_SUCCESS;
|
||||
}
|
||||
|
||||
backend_t wasmfs_get_backend_by_fd(int fd) {
|
||||
auto fileTable = wasmFS.getLockedFileTable();
|
||||
|
||||
// Check that an open file exists corresponding to the given fd.
|
||||
auto openFile = fileTable[fd];
|
||||
if (!openFile) {
|
||||
return NullBackend;
|
||||
}
|
||||
|
||||
auto lockedOpenFile = openFile.locked();
|
||||
return lockedOpenFile.getFile()->getBackend();
|
||||
}
|
||||
|
||||
// This function is exposed to users to allow them to obtain a backend_t for a
|
||||
// specified path.
|
||||
backend_t wasmfs_get_backend_by_path(char* path) {
|
||||
auto pathParts = splitPath(path);
|
||||
|
||||
if (pathParts.empty()) {
|
||||
return NullBackend;
|
||||
}
|
||||
|
||||
// TODO: Remove this when path parsing has been re-factored.
|
||||
if (pathParts.size() == 1 && pathParts[0] == "/") {
|
||||
return wasmFS.getRootDirectory()->getBackend();
|
||||
}
|
||||
|
||||
auto base = pathParts.back();
|
||||
|
||||
long err;
|
||||
auto parentDir = getDir(pathParts.begin(), pathParts.end() - 1, err);
|
||||
|
||||
// Parent node doesn't exist.
|
||||
if (!parentDir) {
|
||||
return NullBackend;
|
||||
}
|
||||
|
||||
auto lockedParentDir = parentDir->locked();
|
||||
|
||||
// TODO: In a future PR, edit function to just return the requested file
|
||||
// instead of having to first obtain the parent dir.
|
||||
auto curr = lockedParentDir.getEntry(base);
|
||||
|
||||
if (curr) {
|
||||
auto dir = curr->dynCast<Directory>();
|
||||
|
||||
return dir ? dir->getBackend() : NullBackend;
|
||||
}
|
||||
|
||||
return NullBackend;
|
||||
}
|
||||
|
||||
static long doStat(std::shared_ptr<File> file, struct stat* buffer) {
|
||||
auto lockedFile = file->locked();
|
||||
|
||||
|
@ -371,7 +423,10 @@ long __syscall_fstat64(long fd, long buf) {
|
|||
return doStat(openFile.locked().getFile(), buffer);
|
||||
}
|
||||
|
||||
__wasi_fd_t __syscall_open(long pathname, long flags, ...) {
|
||||
static __wasi_fd_t doOpen(char* pathname,
|
||||
long flags,
|
||||
mode_t mode,
|
||||
backend_t backend = NullBackend) {
|
||||
int accessMode = (flags & O_ACCMODE);
|
||||
bool canWrite = false;
|
||||
|
||||
|
@ -384,7 +439,7 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
|
|||
((flags) & ~(O_CREAT | O_EXCL | O_DIRECTORY | O_TRUNC | O_APPEND | O_RDWR |
|
||||
O_WRONLY | O_RDONLY | O_LARGEFILE | O_CLOEXEC)) == 0);
|
||||
|
||||
auto pathParts = splitPath((char*)pathname);
|
||||
auto pathParts = splitPath(pathname);
|
||||
|
||||
if (pathParts.empty()) {
|
||||
return -EINVAL;
|
||||
|
@ -417,16 +472,16 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
|
|||
// If O_DIRECTORY is also specified, still create a regular file:
|
||||
// https://man7.org/linux/man-pages/man2/open.2.html#BUGS
|
||||
if (flags & O_CREAT) {
|
||||
// Since mode is optional, mode is specified using varargs.
|
||||
mode_t mode = 0;
|
||||
va_list vl;
|
||||
va_start(vl, flags);
|
||||
mode = va_arg(vl, int);
|
||||
va_end(vl);
|
||||
// Mask all permissions sent via mode.
|
||||
mode &= S_IALLUGO;
|
||||
// Create an empty in-memory file.
|
||||
auto backend = lockedParentDir.getBackend();
|
||||
|
||||
// By default, the backend that the file is created in is the same as the
|
||||
// parent directory. However, if a backend is passed as a parameter, then
|
||||
// that backend is used.
|
||||
if (!backend) {
|
||||
backend = parentDir->getBackend();
|
||||
}
|
||||
auto created = backend->createFile(mode);
|
||||
|
||||
// TODO: When rename is implemented make sure that one can atomically
|
||||
|
@ -456,8 +511,25 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
|
|||
return wasmFS.getLockedFileTable().add(openFile);
|
||||
}
|
||||
|
||||
long __syscall_mkdir(long path, long mode) {
|
||||
auto pathParts = splitPath((char*)path);
|
||||
// This function is exposed to users and allows users to create a file in a
|
||||
// specific backend. An fd to an open file is returned.
|
||||
__wasi_fd_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend) {
|
||||
return doOpen(pathname, O_CREAT, mode, backend);
|
||||
}
|
||||
|
||||
__wasi_fd_t __syscall_open(long pathname, long flags, ...) {
|
||||
// Since mode is optional, mode is specified using varargs.
|
||||
mode_t mode = 0;
|
||||
va_list vl;
|
||||
va_start(vl, flags);
|
||||
mode = va_arg(vl, int);
|
||||
va_end(vl);
|
||||
|
||||
return doOpen((char*)pathname, flags, mode);
|
||||
}
|
||||
|
||||
static long doMkdir(char* path, long mode, backend_t backend = NullBackend) {
|
||||
auto pathParts = splitPath(path);
|
||||
|
||||
if (pathParts.empty()) {
|
||||
return -EINVAL;
|
||||
|
@ -490,7 +562,13 @@ long __syscall_mkdir(long path, long mode) {
|
|||
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
|
||||
mode &= S_IRWXUGO | S_ISVTX;
|
||||
// Create an empty in-memory directory.
|
||||
auto backend = lockedParentDir.getBackend();
|
||||
|
||||
// By default, the backend that the directory is created in is the same as
|
||||
// the parent directory. However, if a backend is passed as a parameter,
|
||||
// then that backend is used.
|
||||
if (!backend) {
|
||||
backend = parentDir->getBackend();
|
||||
}
|
||||
auto created = backend->createDirectory(mode);
|
||||
|
||||
lockedParentDir.setEntry(base, created);
|
||||
|
@ -498,6 +576,16 @@ long __syscall_mkdir(long path, long mode) {
|
|||
}
|
||||
}
|
||||
|
||||
// This function is exposed to users and allows users to specify a particular
|
||||
// backend that a directory should be created within.
|
||||
long wasmfs_create_directory(char* path, long mode, backend_t backend) {
|
||||
return doMkdir(path, mode, backend);
|
||||
}
|
||||
|
||||
long __syscall_mkdir(long path, long mode) {
|
||||
return doMkdir((char*)path, mode);
|
||||
}
|
||||
|
||||
__wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd,
|
||||
__wasi_filedelta_t offset,
|
||||
__wasi_whence_t whence,
|
||||
|
|
|
@ -56,7 +56,7 @@ void WasmFS::preloadFiles() {
|
|||
#endif
|
||||
|
||||
// Obtain the backend of the root directory.
|
||||
auto rootBackend = getRootDirectory()->locked().getBackend();
|
||||
auto rootBackend = getRootDirectory()->getBackend();
|
||||
|
||||
// Ensure that files are preloaded from the main thread.
|
||||
assert(emscripten_is_main_runtime_thread());
|
||||
|
@ -129,7 +129,7 @@ void WasmFS::preloadFiles() {
|
|||
auto base = pathParts.back();
|
||||
|
||||
// TODO: Generalize so that MemoryFile is not hard-coded.
|
||||
auto created = std::make_shared<MemoryFile>((mode_t)mode);
|
||||
auto created = std::make_shared<MemoryFile>((mode_t)mode, rootBackend);
|
||||
|
||||
long err;
|
||||
auto parentDir = getDir(pathParts.begin(), pathParts.end() - 1, err);
|
||||
|
|
|
@ -11230,6 +11230,10 @@ void foo() {}
|
|||
self.set_setting('WASMFS')
|
||||
self.do_run_in_out_file_test(test_file('wasmfs/wasmfs_readfile.c'))
|
||||
|
||||
def test_wasmfs_jsfile(self):
|
||||
self.set_setting('WASMFS')
|
||||
self.do_run_in_out_file_test('wasmfs/wasmfs_jsfile.c')
|
||||
|
||||
@disabled('Running with initial >2GB heaps is not currently supported on the CI version of Node')
|
||||
def test_hello_world_above_2gb(self):
|
||||
self.run_process([EMCC, test_file('hello_world.c'), '-sGLOBAL_BASE=2147483648', '-sINITIAL_MEMORY=3GB'])
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2021 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 <assert.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/wasmfs.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void write_and_read(const char* msg, int fd) {
|
||||
char buf[200] = {};
|
||||
errno = 0;
|
||||
write(fd, msg, strlen(msg));
|
||||
assert(errno == 0);
|
||||
errno = 0;
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
assert(errno == 0);
|
||||
read(fd, buf, sizeof(buf));
|
||||
assert(errno == 0);
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
backend_t JSBackend = wasmfs_create_js_file_backend();
|
||||
|
||||
// Create a new JS file under root.
|
||||
int fd = wasmfs_create_file("/testfile", 0777, JSBackend);
|
||||
|
||||
// Ensure that the size of the JS file is zero.
|
||||
struct stat file;
|
||||
assert(fstat(fd, &file) != -1);
|
||||
assert(file.st_size == 0);
|
||||
|
||||
// Try writing to and reading from the file.
|
||||
const char* msg = "Test with a new JS file\n";
|
||||
write_and_read(msg, fd);
|
||||
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
|
||||
|
||||
// Verify that the size of the JS File is the same as the written buffer.
|
||||
assert(fstat(fd, &file) != -1);
|
||||
assert(file.st_size == strlen(msg));
|
||||
|
||||
// Try seeking to the beginning of the file and overwriting its contents.
|
||||
assert(lseek(fd, 0, SEEK_SET) != -1);
|
||||
write_and_read(msg, fd);
|
||||
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
|
||||
|
||||
// Try appending to the end of the JS File.
|
||||
write_and_read(msg, fd);
|
||||
|
||||
// Verify that the size of the JS File has increased.
|
||||
assert(fstat(fd, &file) != -1);
|
||||
assert(file.st_size == strlen(msg) * 2);
|
||||
|
||||
close(fd);
|
||||
|
||||
assert(unlink("/testfile") != -1);
|
||||
|
||||
// Check that the file has been cleaned up in the JS array.
|
||||
EM_ASM({out("Expect null: " + wasmFS$JSMemoryFiles[0])});
|
||||
|
||||
// Try creating a new JS directory under root.
|
||||
assert(wasmfs_create_directory("/test-dir", 0777, JSBackend) != -1);
|
||||
|
||||
// Try to create a new JS file under this new directory.
|
||||
int fd2 = open("/test-dir/jsfile", O_RDWR | O_CREAT, 0777);
|
||||
|
||||
// Try writing to and reading from the file.
|
||||
const char* msg2 = "Test with a JS file created under a JS directory\n";
|
||||
|
||||
write_and_read(msg2, fd2);
|
||||
// Under first-fit policy, wasmFS$JSMemoryFiles[0] should be populated again.
|
||||
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
|
||||
|
||||
// Check that the JS file has the correct backend type.
|
||||
assert(wasmfs_get_backend_by_fd(fd2) == JSBackend);
|
||||
|
||||
// Try creating an in-memory file under a JS directory.
|
||||
backend_t memoryBackend = wasmfs_get_backend_by_path("/");
|
||||
|
||||
int fd3 = wasmfs_create_file("/test-dir/inmemoryfile", 0777, memoryBackend);
|
||||
|
||||
// Ensure that the JS array size has not changed.
|
||||
int size = EM_ASM_INT({ return wasmFS$JSMemoryFiles.length; });
|
||||
assert(size == 1);
|
||||
|
||||
const char* msg3 =
|
||||
"Test with an in-memory file created under a JS directory\n";
|
||||
write_and_read(msg3, fd3);
|
||||
|
||||
// Create a new JS file under root.
|
||||
int fd4 = wasmfs_create_file("/testfile2", 0777, JSBackend);
|
||||
|
||||
const char* msg4 = "Test with a JS file created under root\n";
|
||||
|
||||
// Try setting the file offset to an arbitrary value.
|
||||
assert(lseek(fd4, 10, SEEK_SET) != -1);
|
||||
|
||||
char buf[200] = {};
|
||||
errno = 0;
|
||||
write(fd4, msg4, strlen(msg4));
|
||||
assert(errno == 0);
|
||||
errno = 0;
|
||||
lseek(fd4, 0, SEEK_SET);
|
||||
assert(errno == 0);
|
||||
int bytesRead = read(fd4, buf, sizeof(buf));
|
||||
|
||||
for (ssize_t i = 0; i < bytesRead; i++) {
|
||||
if (buf[i] == 0) {
|
||||
printf("\\0");
|
||||
} else {
|
||||
printf("%c", buf[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
EM_ASM({out(wasmFS$JSMemoryFiles[1])});
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
Test with a new JS file
|
||||
|
||||
Uint8Array(24) [
|
||||
84, 101, 115, 116, 32, 119, 105,
|
||||
116, 104, 32, 97, 32, 110, 101,
|
||||
119, 32, 74, 83, 32, 102, 105,
|
||||
108, 101, 10
|
||||
]
|
||||
Test with a new JS file
|
||||
|
||||
Uint8Array(24) [
|
||||
84, 101, 115, 116, 32, 119, 105,
|
||||
116, 104, 32, 97, 32, 110, 101,
|
||||
119, 32, 74, 83, 32, 102, 105,
|
||||
108, 101, 10
|
||||
]
|
||||
Test with a new JS file
|
||||
Test with a new JS file
|
||||
|
||||
Expect null: null
|
||||
Test with a JS file created under a JS directory
|
||||
|
||||
Uint8Array(49) [
|
||||
84, 101, 115, 116, 32, 119, 105, 116, 104,
|
||||
32, 97, 32, 74, 83, 32, 102, 105, 108,
|
||||
101, 32, 99, 114, 101, 97, 116, 101, 100,
|
||||
32, 117, 110, 100, 101, 114, 32, 97, 32,
|
||||
74, 83, 32, 100, 105, 114, 101, 99, 116,
|
||||
111, 114, 121, 10
|
||||
]
|
||||
Test with an in-memory file created under a JS directory
|
||||
|
||||
\0\0\0\0\0\0\0\0\0\0Test with a JS file created under root
|
||||
|
||||
Uint8Array(49) [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
84, 101, 115, 116, 32, 119, 105, 116, 104, 32,
|
||||
97, 32, 74, 83, 32, 102, 105, 108, 101, 32,
|
||||
99, 114, 101, 97, 116, 101, 100, 32, 117, 110,
|
||||
100, 101, 114, 32, 114, 111, 111, 116, 10
|
||||
]
|
|
@ -1361,7 +1361,7 @@ class libwasmfs(MTLibrary):
|
|||
def get_files(self):
|
||||
return files_in_path(
|
||||
path='system/lib/wasmfs',
|
||||
filenames=['syscalls.cpp', 'file_table.cpp', 'file.cpp', 'wasmfs.cpp', 'streams.cpp', 'memory_file.cpp', 'memory_file_backend.cpp'])
|
||||
filenames=['syscalls.cpp', 'file_table.cpp', 'file.cpp', 'wasmfs.cpp', 'streams.cpp', 'memory_file.cpp', 'memory_file_backend.cpp', 'js_file_backend.cpp'])
|
||||
|
||||
def can_build(self):
|
||||
return settings.WASMFS
|
||||
|
|
Загрузка…
Ссылка в новой задаче