diff --git a/src/lib/libsigs.js b/src/lib/libsigs.js index 2f6029b0fdf00..6eea3627450a8 100644 --- a/src/lib/libsigs.js +++ b/src/lib/libsigs.js @@ -402,14 +402,19 @@ sigs = { _wasmfs_jsimpl_write__sig: 'ippppj', _wasmfs_node_close__sig: 'ii', _wasmfs_node_fstat_size__sig: 'iip', + _wasmfs_node_ftruncate__sig: 'iij', _wasmfs_node_get_mode__sig: 'ipp', _wasmfs_node_insert_directory__sig: 'ipi', _wasmfs_node_insert_file__sig: 'ipi', _wasmfs_node_open__sig: 'ipp', _wasmfs_node_read__sig: 'iipiip', _wasmfs_node_readdir__sig: 'ipp', + _wasmfs_node_readlink__sig: 'ippi', + _wasmfs_node_rename__sig: 'ipp', _wasmfs_node_rmdir__sig: 'ip', _wasmfs_node_stat_size__sig: 'ipp', + _wasmfs_node_symlink__sig: 'ipp', + _wasmfs_node_truncate__sig: 'ipj', _wasmfs_node_unlink__sig: 'ip', _wasmfs_node_write__sig: 'iipiip', _wasmfs_opfs_close_access__sig: 'vpip', diff --git a/src/lib/libwasmfs_node.js b/src/lib/libwasmfs_node.js index f0739f46f1029..683ab7f0919b6 100644 --- a/src/lib/libwasmfs_node.js +++ b/src/lib/libwasmfs_node.js @@ -156,11 +156,39 @@ addToLibrary({ }); }, + _wasmfs_node_truncate__i53abi: true, + _wasmfs_node_truncate__deps : ['$wasmfsTry'], + _wasmfs_node_truncate : (path_p, len) => { + return wasmfsTry(() => fs.truncateSync(UTF8ToString(path_p), len)); + }, + + _wasmfs_node_ftruncate__i53abi: true, + _wasmfs_node_ftruncate__deps : ['$wasmfsTry'], + _wasmfs_node_ftruncate : (fd, len) => { + return wasmfsTry(() => fs.ftruncateSync(fd, len)); + }, + _wasmfs_node_open__deps: ['$wasmfsTry'], _wasmfs_node_open: (path_p, mode_p) => { - return wasmfsTry(() => - fs.openSync(UTF8ToString(path_p), UTF8ToString(mode_p)) - ); + return wasmfsTry(() => fs.openSync(UTF8ToString(path_p), UTF8ToString(mode_p))); + }, + + _wasmfs_node_rename__deps: ['$wasmfsTry'], + _wasmfs_node_rename: (from_path_p, to_path_p) => { + return wasmfsTry(() => fs.renameSync(UTF8ToString(from_path_p), UTF8ToString(to_path_p))); + }, + + _wasmfs_node_symlink__deps: ['$wasmfsTry'], + _wasmfs_node_symlink: (target_path_p, linkpath_path_p) => { + return wasmfsTry(() => fs.symlinkSync(UTF8ToString(target_path_p), UTF8ToString(linkpath_path_p))); + }, + + _wasmfs_node_readlink__deps: ['$wasmfsTry'], + _wasmfs_node_readlink: (path_p, target_p, bufsize) => { + return wasmfsTry(() => { + var target = fs.readlinkSync(UTF8ToString(path_p)); + return stringToUTF8(target, target_p, bufsize); + }); }, _wasmfs_node_close__deps: [], @@ -192,4 +220,5 @@ addToLibrary({ // implicitly return 0 }); }, + }); diff --git a/system/lib/wasmfs/backends/node_backend.cpp b/system/lib/wasmfs/backends/node_backend.cpp index 50c3e7fb9e2fb..a3a7ad19b32f3 100644 --- a/system/lib/wasmfs/backends/node_backend.cpp +++ b/system/lib/wasmfs/backends/node_backend.cpp @@ -45,8 +45,13 @@ class NodeState { result = _wasmfs_node_open(path.c_str(), "r"); break; case O_WRONLY: - result = _wasmfs_node_open(path.c_str(), "w"); - break; + // TODO(sbc): Specific handling of O_WRONLY. + // There is no simple way to map O_WRONLY to an fopen-style + // mode string since the only two modes that are write only + // are `w` and `a`. The problem with the former is that it + // truncates to file. The problem with the latter is that it + // opens for appending. For now simply opening in O_RDWR + // mode is enough to pass all our tests. case O_RDWR: result = _wasmfs_node_open(path.c_str(), "r+"); break; @@ -118,7 +123,10 @@ class NodeFile : public DataFile { } int setSize(off_t size) override { - WASMFS_UNREACHABLE("TODO: implement NodeFile::setSize"); + if (state.isOpen()) { + return _wasmfs_node_ftruncate(state.getFD(), size); + } + return _wasmfs_node_truncate(state.path.c_str(), size); } int open(oflags_t flags) override { return state.open(flags); } @@ -147,6 +155,22 @@ class NodeFile : public DataFile { } }; +class NodeSymlink : public Symlink { +public: + std::string path; + + NodeSymlink(backend_t backend, std::string path) + : Symlink(backend), path(path) {} + + virtual std::string getTarget() const { + char buf[PATH_MAX]; + if (_wasmfs_node_readlink(path.c_str(), buf, PATH_MAX) < 0) { + WASMFS_UNREACHABLE("getTarget cannot fail"); + } + return std::string(buf); + } +}; + class NodeDirectory : public Directory { public: NodeState state; @@ -172,8 +196,7 @@ class NodeDirectory : public Directory { } else if (S_ISDIR(mode)) { return std::make_shared(mode, getBackend(), childPath); } else if (S_ISLNK(mode)) { - // return std::make_shared(mode, getBackend(), childPath); - return nullptr; + return std::make_shared(getBackend(), childPath); } else { // Unrecognized file kind not made visible to WasmFS. return nullptr; @@ -212,11 +235,26 @@ class NodeDirectory : public Directory { std::shared_ptr insertSymlink(const std::string& name, const std::string& target) override { - WASMFS_UNREACHABLE("TODO: implement NodeDirectory::insertSymlink"); + auto childPath = getChildPath(name); + if (_wasmfs_node_symlink(target.c_str(), childPath.c_str())) { + return nullptr; + } + return std::make_shared(getBackend(), childPath); } int insertMove(const std::string& name, std::shared_ptr file) override { - WASMFS_UNREACHABLE("TODO: implement NodeDirectory::insertMove"); + std::string fromPath; + + if (file->is()) { + auto nodeFile = std::static_pointer_cast(file); + fromPath = nodeFile->state.path; + } else { + auto nodeDir = std::static_pointer_cast(file); + fromPath = nodeDir->state.path; + } + + auto childPath = getChildPath(name); + return _wasmfs_node_rename(fromPath.c_str(), childPath.c_str()); } ssize_t getNumEntries() override { diff --git a/system/lib/wasmfs/backends/node_backend.h b/system/lib/wasmfs/backends/node_backend.h index 2479938d61a4d..7e6e496b7c73f 100644 --- a/system/lib/wasmfs/backends/node_backend.h +++ b/system/lib/wasmfs/backends/node_backend.h @@ -27,6 +27,15 @@ int _wasmfs_node_insert_directory(const char* path, mode_t mode); int _wasmfs_node_unlink(const char* path); int _wasmfs_node_rmdir(const char* path); +int _wasmfs_node_truncate(const char* path, off_t len); +int _wasmfs_node_ftruncate(int fd, off_t len); + +int _wasmfs_node_rename(const char* oldpath, const char* newpath); + +int _wasmfs_node_symlink(const char *target, const char *linkpath); + +int _wasmfs_node_readlink(const char *path, const char *buf, int bufsize); + // Open the file and return the underlying file descriptor. [[nodiscard]] int _wasmfs_node_open(const char* path, const char* mode); @@ -42,4 +51,5 @@ int _wasmfs_node_read( // the number of bytes written to `nread`. Return 0 on success or an error code. int _wasmfs_node_write( int fd, const void* buf, uint32_t len, uint32_t pos, uint32_t* nwritten); -} + +} // extern "C" diff --git a/test/common.py b/test/common.py index 9425d4b80b9e9..073dc7bb7666f 100644 --- a/test/common.py +++ b/test/common.py @@ -448,6 +448,9 @@ def metafunc(self, fs, *args, **kwargs): self.setup_noderawfs_test() elif fs == 'wasmfs': self.setup_wasmfs_test() + elif fs == 'wasmfs_rawfs': + self.setup_wasmfs_test() + self.setup_noderawfs_test() else: self.emcc_args += ['-DMEMFS'] assert fs is None @@ -458,7 +461,8 @@ def metafunc(self, fs, *args, **kwargs): parameterize(metafunc, {'': (None,), 'nodefs': ('nodefs',), 'rawfs': ('rawfs',), - 'wasmfs': ('wasmfs',)}) + 'wasmfs': ('wasmfs',), + 'wasmfs_rawfs': ('wasmfs_rawfs',)}) return metafunc diff --git a/test/fs/test_stat_unnamed_file_descriptor.c b/test/fs/test_stat_unnamed_file_descriptor.c index a1d92f5cbaba7..58da03bf83810 100644 --- a/test/fs/test_stat_unnamed_file_descriptor.c +++ b/test/fs/test_stat_unnamed_file_descriptor.c @@ -8,13 +8,18 @@ int main() { int fd = open("file.txt", O_RDWR | O_CREAT, 0666); unlink("file.txt"); int res; + struct stat buf; res = fstat(fd, &buf); assert(res == 0); assert(buf.st_atime > 1000000000); + printf("done stat: %d\n", res); + res = fchmod(fd, 0777); assert(res == 0); + res = ftruncate(fd, 10); assert(res == 0); + printf("success\n"); } diff --git a/test/test_core.py b/test/test_core.py index e3c1f5ae950b6..1f91b9dd237c1 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -5922,7 +5922,7 @@ def test_unistd_access(self): # We also report all files as executable since there is no x bit # recorded there. # See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod?view=msvc-170#remarks - if WINDOWS and '-DNODERAWFS' in self.emcc_args: + if WINDOWS and '-DNODERAWFS' in self.emcc_args and not self.get_setting('WASMFS'): out_suffix = '.win' else: out_suffix = '' diff --git a/test/unistd/truncate.c b/test/unistd/truncate.c index 9da761872d18f..4d23f731f4e5a 100644 --- a/test/unistd/truncate.c +++ b/test/unistd/truncate.c @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include #include #include #include @@ -13,14 +14,21 @@ #include void setup() { + int rtn; + FILE* f = fopen("towrite", "w"); - fwrite("abcdef", 6, 1, f); + assert(f); + rtn = fwrite("abcdef", 6, 1, f); + assert(rtn = 6); fclose(f); + f = fopen("toread", "w"); - fwrite("abcdef", 6, 1, f); + assert(f); + rtn = fwrite("abcdef", 6, 1, f); + assert(rtn = 6); fclose(f); - chmod("toread", 0444); + assert(chmod("toread", 0444) == 0); } int main() { @@ -28,11 +36,13 @@ int main() { struct stat s; int f = open("towrite", O_WRONLY); + assert(f); int f2 = open("toread", O_RDONLY); - printf("f2: %d\n", f2); + assert(f2); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 6); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -41,6 +51,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 10); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -49,6 +60,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 4); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -57,6 +69,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 4); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -65,6 +78,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 2); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -74,6 +88,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 0); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -82,6 +97,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f2, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 6); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -94,6 +110,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f2, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 6); memset(&s, 0, sizeof s); errno = 0; printf("\n"); @@ -103,6 +120,7 @@ int main() { printf("errno: %s\n", strerror(errno)); fstat(f2, &s); printf("st_size: %lld\n", s.st_size); + assert(s.st_size == 6); memset(&s, 0, sizeof s); errno = 0; diff --git a/tools/emscripten.py b/tools/emscripten.py index 152b6a98cfabd..4d4aff7e886cb 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -1070,6 +1070,7 @@ def create_pointer_conversion_wrappers(metadata): '_wasmfs_get_cwd': 'p_', '_wasmfs_identify': '_p', '_wasmfs_read_file': 'pp', + '_wasmfs_node_record_dirent': '_pp_', '__dl_seterr': '_pp', '_emscripten_run_on_main_thread_js': '__p_p_', '_emscripten_proxy_execute_task_queue': '_p',