diff --git a/src/library_wasmfs_opfs.js b/src/library_wasmfs_opfs.js index a0fe22b56b308..9f65fdabcf7b5 100644 --- a/src/library_wasmfs_opfs.js +++ b/src/library_wasmfs_opfs.js @@ -354,9 +354,13 @@ mergeInto(LibraryManager.library, { _wasmfs_opfs_get_size_access__deps: ['$wasmfsOPFSAccessHandles'], _wasmfs_opfs_get_size_access: async function(ctx, accessID, sizePtr) { let accessHandle = wasmfsOPFSAccessHandles.get(accessID); - // TODO: Error handling - let size = await accessHandle.getSize(); - {{{ makeSetValue('sizePtr', 0, 'size', 'i32') }}}; + let size; + try { + size = await accessHandle.getSize(); + } catch { + size = -{{{ cDefine('EIO') }}}; + } + {{{ makeSetValue('sizePtr', 0, 'size', 'i64') }}}; _emscripten_proxy_finish(ctx); }, @@ -369,14 +373,21 @@ mergeInto(LibraryManager.library, { _wasmfs_opfs_get_size_file__deps: ['$wasmfsOPFSFileHandles'], _wasmfs_opfs_get_size_file: async function(ctx, fileID, sizePtr) { let fileHandle = wasmfsOPFSFileHandles.get(fileID); - // TODO: Error handling - let size = (await fileHandle.getFile()).size; - {{{ makeSetValue('sizePtr', 0, 'size', 'i32') }}}; + let size; + try { + size = (await fileHandle.getFile()).size; + } catch { + size = -{{{ cDefine('EIO') }}}; + } + {{{ makeSetValue('sizePtr', 0, 'size', 'i64') }}}; _emscripten_proxy_finish(ctx); }, _wasmfs_opfs_set_size_access__deps: ['$wasmfsOPFSAccessHandles'], - _wasmfs_opfs_set_size_access: async function(ctx, accessID, size, errPtr) { + _wasmfs_opfs_set_size_access: async function(ctx, accessID, + {{{ defineI64Param('size') }}}, + errPtr) { + {{{ receiveI64ParamAsDouble('size') }}}; let accessHandle = wasmfsOPFSAccessHandles.get(accessID); try { await accessHandle.truncate(size); @@ -388,7 +399,10 @@ mergeInto(LibraryManager.library, { }, _wasmfs_opfs_set_size_file__deps: ['$wasmfsOPFSFileHandles'], - _wasmfs_opfs_set_size_file: async function(ctx, fileID, size, errPtr) { + _wasmfs_opfs_set_size_file: async function(ctx, fileID, + {{{ defineI64Param('size') }}}, + errPtr) { + {{{ receiveI64ParamAsDouble('size') }}}; let fileHandle = wasmfsOPFSFileHandles.get(fileID); try { let writable = await fileHandle.createWritable({keepExistingData: true}); diff --git a/system/lib/wasmfs/backends/node_backend.cpp b/system/lib/wasmfs/backends/node_backend.cpp index 9e9d7c4906e3e..a2a447b114936 100644 --- a/system/lib/wasmfs/backends/node_backend.cpp +++ b/system/lib/wasmfs/backends/node_backend.cpp @@ -137,7 +137,7 @@ class NodeFile : public DataFile { : DataFile(mode, backend), state(path) {} private: - size_t getSize() override { + off_t getSize() override { // TODO: This should really be using a 64-bit file size type. uint32_t size; if (state.isOpen()) { @@ -151,10 +151,10 @@ class NodeFile : public DataFile { return 0; } } - return size_t(size); + return off_t(size); } - int setSize(size_t size) override { + int setSize(off_t size) override { WASMFS_UNREACHABLE("TODO: implement NodeFile::setSize"); } diff --git a/system/lib/wasmfs/backends/opfs_backend.cpp b/system/lib/wasmfs/backends/opfs_backend.cpp index 23136ffc2a422..7e51be79522bc 100644 --- a/system/lib/wasmfs/backends/opfs_backend.cpp +++ b/system/lib/wasmfs/backends/opfs_backend.cpp @@ -88,23 +88,22 @@ int _wasmfs_opfs_write_access(int access_id, // Get the size via an AccessHandle. void _wasmfs_opfs_get_size_access(em_proxying_ctx* ctx, int access_id, - uint32_t* size); + off_t* size); +// TODO: return 64-byte off_t. uint32_t _wasmfs_opfs_get_size_blob(int blob_id); // Get the size of a file handle via a File Blob. -void _wasmfs_opfs_get_size_file(em_proxying_ctx* ctx, - int file_id, - uint32_t* size); +void _wasmfs_opfs_get_size_file(em_proxying_ctx* ctx, int file_id, off_t* size); void _wasmfs_opfs_set_size_access(em_proxying_ctx* ctx, int access_id, - uint32_t size, + off_t size, int* err); void _wasmfs_opfs_set_size_file(em_proxying_ctx* ctx, int file_id, - uint32_t size, + off_t size, int* err); void _wasmfs_opfs_flush_access(em_proxying_ctx* ctx, int access_id, int* err); @@ -222,9 +221,8 @@ class OPFSFile : public DataFile { } private: - size_t getSize() override { - // TODO: 64-bit sizes. - uint32_t size; + off_t getSize() override { + off_t size; switch (state.getKind()) { case OpenState::None: proxy([&](auto ctx) { @@ -242,10 +240,10 @@ class OPFSFile : public DataFile { default: WASMFS_UNREACHABLE("Unexpected open state"); } - return size_t(size); + return size; } - int setSize(size_t size) override { + int setSize(off_t size) override { int err = 0; switch (state.getKind()) { case OpenState::Access: @@ -259,6 +257,8 @@ class OPFSFile : public DataFile { // become invalidated and refreshing it while ensuring other in-flight // operations on the same file do not observe the invalidated blob would // be extremely complicated. + // TODO: Can we assume there are no other in-flight operations on this + // file and do something better here? return -EIO; case OpenState::None: { proxy([&](auto ctx) { diff --git a/system/lib/wasmfs/backends/proxied_file_backend.cpp b/system/lib/wasmfs/backends/proxied_file_backend.cpp index 07e78930d8a28..c540320bf3bd5 100644 --- a/system/lib/wasmfs/backends/proxied_file_backend.cpp +++ b/system/lib/wasmfs/backends/proxied_file_backend.cpp @@ -51,13 +51,13 @@ class ProxiedFile : public DataFile { // Querying the size of the Proxied File returns the size of the underlying // file given by the proxying mechanism. - size_t getSize() override { - size_t result; + off_t getSize() override { + off_t result; proxy([&]() { result = baseFile->locked().getSize(); }); return result; } - int setSize(size_t size) override { + int setSize(off_t size) override { WASMFS_UNREACHABLE("TODO: ProxiedFS setSize"); } diff --git a/system/lib/wasmfs/file.h b/system/lib/wasmfs/file.h index 84fe5635df8a6..5d8e2077bcd02 100644 --- a/system/lib/wasmfs/file.h +++ b/system/lib/wasmfs/file.h @@ -101,8 +101,9 @@ class File : public std::enable_shared_from_this { // A mutex is needed for multiple accesses to the same file. std::recursive_mutex mutex; - // May be called on files that have not been opened. - virtual size_t getSize() = 0; + // The the size in bytes of a file or return a negative error code. May be + // called on files that have not been opened. + virtual off_t getSize() = 0; mode_t mode = 0; // User and group mode bits for access permission. @@ -144,7 +145,7 @@ class DataFile : public File { // Sets the size of the file to a specific size. If new space is allocated, it // should be zero-initialized. May be called on files that have not been // opened. Returns 0 on success or a negative error code. - virtual int setSize(size_t size) = 0; + virtual int setSize(off_t size) = 0; // Sync the file data to the underlying persistent storage, if any. Returns 0 // on success or a negative error code. @@ -254,7 +255,7 @@ class Directory : public File { protected: // 4096 bytes is the size of a block in ext4. // This value was also copied from the JS file system. - size_t getSize() override { return 4096; } + off_t getSize() override { return 4096; } }; class Symlink : public File { @@ -270,7 +271,7 @@ class Symlink : public File { virtual std::string getTarget() const = 0; protected: - size_t getSize() override { return getTarget().size(); } + off_t getSize() override { return getTarget().size(); } }; class File::Handle { @@ -286,7 +287,7 @@ class File::Handle { Handle(std::shared_ptr file) : file(file), lock(file->mutex) {} Handle(std::shared_ptr file, std::defer_lock_t) : file(file), lock(file->mutex, std::defer_lock) {} - size_t getSize() { return file->getSize(); } + off_t getSize() { return file->getSize(); } mode_t getMode() { return file->mode; } void setMode(mode_t mode) { // The type bits can never be changed (whether something is a file or a @@ -325,7 +326,7 @@ class DataFile::Handle : public File::Handle { return getFile()->write(buf, len, offset); } - [[nodiscard]] int setSize(size_t size) { return getFile()->setSize(size); } + [[nodiscard]] int setSize(off_t size) { return getFile()->setSize(size); } // TODO: Design a proper API for flushing files. [[nodiscard]] int flush() { return getFile()->flush(); } diff --git a/system/lib/wasmfs/js_impl_backend.h b/system/lib/wasmfs/js_impl_backend.h index fe66b9a04de4e..919cb3bfef5f9 100644 --- a/system/lib/wasmfs/js_impl_backend.h +++ b/system/lib/wasmfs/js_impl_backend.h @@ -96,11 +96,11 @@ class JSImplFile : public DataFile { int flush() override { return 0; } - size_t getSize() override { + off_t getSize() override { return _wasmfs_jsimpl_get_size(getBackendIndex(), getFileIndex()); } - int setSize(size_t size) override { + int setSize(off_t size) override { WASMFS_UNREACHABLE("TODO: JSImpl setSize"); } diff --git a/system/lib/wasmfs/memory_backend.h b/system/lib/wasmfs/memory_backend.h index 7965ad49da43a..814baeb460f6b 100644 --- a/system/lib/wasmfs/memory_backend.h +++ b/system/lib/wasmfs/memory_backend.h @@ -24,8 +24,8 @@ class MemoryFile : public DataFile { ssize_t write(const uint8_t* buf, size_t len, off_t offset) override; ssize_t read(uint8_t* buf, size_t len, off_t offset) override; int flush() override { return 0; } - size_t getSize() override { return buffer.size(); } - int setSize(size_t size) override { + off_t getSize() override { return buffer.size(); } + int setSize(off_t size) override { buffer.resize(size); return 0; } diff --git a/system/lib/wasmfs/pipe_backend.h b/system/lib/wasmfs/pipe_backend.h index 105d93531e73f..3336f28f9dd60 100644 --- a/system/lib/wasmfs/pipe_backend.h +++ b/system/lib/wasmfs/pipe_backend.h @@ -46,10 +46,10 @@ class PipeFile : public DataFile { int flush() override { return 0; } - size_t getSize() override { return data->size(); } + off_t getSize() override { return data->size(); } // TODO: Should this return an error? - int setSize(size_t size) override { return 0; } + int setSize(off_t size) override { return 0; } public: // PipeFiles do not have or need a backend. Pass NullBackend to the parent for diff --git a/system/lib/wasmfs/proxied_async_js_impl_backend.h b/system/lib/wasmfs/proxied_async_js_impl_backend.h index 025c85abbcca1..9c0d1e4e63cd2 100644 --- a/system/lib/wasmfs/proxied_async_js_impl_backend.h +++ b/system/lib/wasmfs/proxied_async_js_impl_backend.h @@ -66,7 +66,7 @@ void _wasmfs_jsimpl_async_read(em_proxying_ctx* ctx, void _wasmfs_jsimpl_async_get_size(em_proxying_ctx* ctx, js_index_t backend, js_index_t index, - size_t* result); + off_t* result); } namespace wasmfs { @@ -108,8 +108,8 @@ class ProxiedAsyncJSImplFile : public DataFile { int flush() override { return 0; } - size_t getSize() override { - size_t result; + off_t getSize() override { + off_t result; proxy([&](auto ctx) { _wasmfs_jsimpl_async_get_size( ctx.ctx, getBackendIndex(), getFileIndex(), &result); @@ -117,7 +117,7 @@ class ProxiedAsyncJSImplFile : public DataFile { return result; } - int setSize(size_t size) override { + int setSize(off_t size) override { WASMFS_UNREACHABLE("TODO: ProxiedAsyncJSImplFile setSize"); } diff --git a/system/lib/wasmfs/special_files.cpp b/system/lib/wasmfs/special_files.cpp index a00eb72c10fe4..882f3c899f327 100644 --- a/system/lib/wasmfs/special_files.cpp +++ b/system/lib/wasmfs/special_files.cpp @@ -29,8 +29,8 @@ class NullFile : public DataFile { ssize_t read(uint8_t* buf, size_t len, off_t offset) override { return 0; } int flush() override { return 0; } - size_t getSize() override { return 0; } - int setSize(size_t size) override { return -EPERM; } + off_t getSize() override { return 0; } + int setSize(off_t size) override { return -EPERM; } public: NullFile() : DataFile(S_IRUGO | S_IWUGO, NullBackend, S_IFCHR) {} @@ -50,8 +50,8 @@ class StdinFile : public DataFile { }; int flush() override { return 0; } - size_t getSize() override { return 0; } - int setSize(size_t size) override { return -EPERM; } + off_t getSize() override { return 0; } + int setSize(off_t size) override { return -EPERM; } public: StdinFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; } @@ -78,8 +78,8 @@ class WritingStdFile : public DataFile { return 0; } - size_t getSize() override { return 0; } - int setSize(size_t size) override { return -EPERM; } + off_t getSize() override { return 0; } + int setSize(off_t size) override { return -EPERM; } ssize_t writeToJS(const uint8_t* buf, size_t len, @@ -152,8 +152,8 @@ class RandomFile : public DataFile { }; int flush() override { return 0; } - size_t getSize() override { return 0; } - int setSize(size_t size) override { return -EPERM; } + off_t getSize() override { return 0; } + int setSize(off_t size) override { return -EPERM; } public: RandomFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; } diff --git a/system/lib/wasmfs/syscalls.cpp b/system/lib/wasmfs/syscalls.cpp index 4b3d14bcff84f..6da912cb6e61e 100644 --- a/system/lib/wasmfs/syscalls.cpp +++ b/system/lib/wasmfs/syscalls.cpp @@ -115,7 +115,12 @@ static __wasi_errno_t writeAtOffset(OffsetHandling setOffset, if (setOffset == OffsetHandling::OpenFileState) { if (lockedOpenFile.getFlags() & O_APPEND) { - offset = lockedFile.getSize(); + off_t size = lockedFile.getSize(); + if (size < 0) { + // Translate to WASI standard of positive return codes. + return -size; + } + offset = size; lockedOpenFile.setPosition(offset); } else { offset = lockedOpenFile.getPosition(); @@ -339,7 +344,11 @@ int __syscall_newfstatat(int dirfd, intptr_t path, intptr_t buf, int flags) { auto lockedFile = file->locked(); auto buffer = (struct stat*)buf; - buffer->st_size = lockedFile.getSize(); + off_t size = lockedFile.getSize(); + if (size < 0) { + return size; + } + buffer->st_size = size; // ATTN: hard-coded constant values are copied from the existing JS file // system. Specific values were chosen to match existing library_fs.js @@ -595,16 +604,14 @@ doMkdir(path::ParsedParent parsed, int mode, backend_t backend = NullBackend) { backend = parent->getBackend(); } - std::shared_ptr created; if (backend == parent->getBackend()) { - created = lockedParent.insertDirectory(childName, mode); - if (!created) { + if (!lockedParent.insertDirectory(childName, mode)) { // TODO Receive a specific error code, and report it here. For now, report // a generic error. return -EIO; } } else { - created = backend->createDirectory(mode); + auto created = backend->createDirectory(mode); if (!created) { // TODO Receive a specific error code, and report it here. For now, report // a generic error. @@ -654,7 +661,12 @@ __wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd, } else if (whence == SEEK_END) { // Only the open file state is altered in seek. Locking the underlying // data file here once is sufficient. - position = lockedOpenFile.getFile()->locked().getSize() + offset; + off_t size = lockedOpenFile.getFile()->locked().getSize(); + if (size < 0) { + // Translate to WASI standard of positive return codes. + return -size; + } + position = size + offset; } else { return __WASI_ERRNO_INVAL; } @@ -1312,8 +1324,8 @@ int __syscall_poll(intptr_t fds_, int nfds, int timeout) { if (writeBit && (flags == O_RDONLY || flags == O_RDWR)) { // If there is data in the file, then there is also the ability to read. // TODO: Does this need to consider the position as well? That is, if - // the position is at the end, we can't read from the current - // position at least. + // the position is at the end, we can't read from the current position + // at least. If we update this, make sure the size isn't an error! if (openFile->locked().getFile()->locked().getSize() > 0) { mask |= writeBit; } @@ -1361,7 +1373,11 @@ int __syscall_fallocate(int fd, int mode, uint64_t off, uint64_t len) { // before, which for a backend with sparse data storage could make a // difference. For that we'd need a new backend API. auto newNeededSize = off + len; - if (newNeededSize > locked.getSize()) { + off_t size = locked.getSize(); + if (size < 0) { + return size; + } + if (newNeededSize > size) { if (auto err = locked.setSize(newNeededSize)) { return err; }