| |
| |
| |
| |
| |
|
|
| addToLibrary({ |
| $MEMFS__deps: ['$FS', '$mmapAlloc'], |
| $MEMFS: { |
| ops_table: null, |
| mount(mount) { |
| return MEMFS.createNode(null, '/', {{{ cDefs.S_IFDIR | 0o777 }}}, 0); |
| }, |
| createNode(parent, name, mode, dev) { |
| if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { |
| |
| throw new FS.ErrnoError({{{ cDefs.EPERM }}}); |
| } |
| MEMFS.ops_table ||= { |
| dir: { |
| node: { |
| getattr: MEMFS.node_ops.getattr, |
| setattr: MEMFS.node_ops.setattr, |
| lookup: MEMFS.node_ops.lookup, |
| mknod: MEMFS.node_ops.mknod, |
| rename: MEMFS.node_ops.rename, |
| unlink: MEMFS.node_ops.unlink, |
| rmdir: MEMFS.node_ops.rmdir, |
| readdir: MEMFS.node_ops.readdir, |
| symlink: MEMFS.node_ops.symlink |
| }, |
| stream: { |
| llseek: MEMFS.stream_ops.llseek |
| } |
| }, |
| file: { |
| node: { |
| getattr: MEMFS.node_ops.getattr, |
| setattr: MEMFS.node_ops.setattr |
| }, |
| stream: { |
| llseek: MEMFS.stream_ops.llseek, |
| read: MEMFS.stream_ops.read, |
| write: MEMFS.stream_ops.write, |
| mmap: MEMFS.stream_ops.mmap, |
| msync: MEMFS.stream_ops.msync |
| } |
| }, |
| link: { |
| node: { |
| getattr: MEMFS.node_ops.getattr, |
| setattr: MEMFS.node_ops.setattr, |
| readlink: MEMFS.node_ops.readlink |
| }, |
| stream: {} |
| }, |
| chrdev: { |
| node: { |
| getattr: MEMFS.node_ops.getattr, |
| setattr: MEMFS.node_ops.setattr |
| }, |
| stream: FS.chrdev_stream_ops |
| } |
| }; |
| var node = FS.createNode(parent, name, mode, dev); |
| if (FS.isDir(node.mode)) { |
| node.node_ops = MEMFS.ops_table.dir.node; |
| node.stream_ops = MEMFS.ops_table.dir.stream; |
| node.contents = {}; |
| } else if (FS.isFile(node.mode)) { |
| node.node_ops = MEMFS.ops_table.file.node; |
| node.stream_ops = MEMFS.ops_table.file.stream; |
| |
| |
| node.usedBytes = 0; |
| |
| |
| |
| |
| node.contents = MEMFS.emptyFileContents ??= new Uint8Array(0); |
| } else if (FS.isLink(node.mode)) { |
| node.node_ops = MEMFS.ops_table.link.node; |
| node.stream_ops = MEMFS.ops_table.link.stream; |
| } else if (FS.isChrdev(node.mode)) { |
| node.node_ops = MEMFS.ops_table.chrdev.node; |
| node.stream_ops = MEMFS.ops_table.chrdev.stream; |
| } |
| node.atime = node.mtime = node.ctime = Date.now(); |
| |
| if (parent) { |
| parent.contents[name] = node; |
| parent.atime = parent.mtime = parent.ctime = node.atime; |
| } |
| return node; |
| }, |
|
|
| |
| getFileDataAsTypedArray(node) { |
| #if ASSERTIONS |
| assert(FS.isFile(node.mode), 'getFileDataAsTypedArray called on non-file'); |
| #endif |
| return node.contents.subarray(0, node.usedBytes); |
| }, |
|
|
| |
| |
| |
| |
| |
| expandFileStorage(node, newCapacity) { |
| var prevCapacity = node.contents.length; |
| if (prevCapacity >= newCapacity) return; |
| |
| |
| |
| |
| |
| var CAPACITY_DOUBLING_MAX = 1024 * 1024; |
| newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); |
| if (prevCapacity) newCapacity = Math.max(newCapacity, 256); |
| var oldContents = MEMFS.getFileDataAsTypedArray(node); |
| node.contents = new Uint8Array(newCapacity); |
| node.contents.set(oldContents); |
| }, |
|
|
| |
| |
| resizeFileStorage(node, newSize) { |
| if (node.usedBytes == newSize) return; |
| var oldContents = node.contents; |
| node.contents = new Uint8Array(newSize); |
| node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); |
| node.usedBytes = newSize; |
| }, |
|
|
| node_ops: { |
| getattr(node) { |
| var attr = {}; |
| |
| attr.dev = FS.isChrdev(node.mode) ? node.id : 1; |
| attr.ino = node.id; |
| attr.mode = node.mode; |
| attr.nlink = 1; |
| attr.uid = 0; |
| attr.gid = 0; |
| attr.rdev = node.rdev; |
| if (FS.isDir(node.mode)) { |
| attr.size = 4096; |
| } else if (FS.isFile(node.mode)) { |
| attr.size = node.usedBytes; |
| } else if (FS.isLink(node.mode)) { |
| attr.size = node.link.length; |
| } else { |
| attr.size = 0; |
| } |
| attr.atime = new Date(node.atime); |
| attr.mtime = new Date(node.mtime); |
| attr.ctime = new Date(node.ctime); |
| |
| |
| attr.blksize = 4096; |
| attr.blocks = Math.ceil(attr.size / attr.blksize); |
| return attr; |
| }, |
| setattr(node, attr) { |
| for (const key of ["mode", "atime", "mtime", "ctime"]) { |
| if (attr[key] != null) { |
| node[key] = attr[key]; |
| } |
| } |
| if (attr.size !== undefined) { |
| MEMFS.resizeFileStorage(node, attr.size); |
| } |
| }, |
| lookup(parent, name) { |
| #if ASSERTIONS |
| throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); |
| #else |
| |
| |
| if (!MEMFS.doesNotExistError) { |
| MEMFS.doesNotExistError = new FS.ErrnoError({{{ cDefs.ENOENT }}}); |
| |
| MEMFS.doesNotExistError.stack = '<generic error, no stack>'; |
| } |
| throw MEMFS.doesNotExistError; |
| #endif |
| }, |
| mknod(parent, name, mode, dev) { |
| return MEMFS.createNode(parent, name, mode, dev); |
| }, |
| rename(old_node, new_dir, new_name) { |
| var new_node; |
| try { |
| new_node = FS.lookupNode(new_dir, new_name); |
| } catch (e) {} |
| if (new_node) { |
| if (FS.isDir(old_node.mode)) { |
| |
| for (var i in new_node.contents) { |
| throw new FS.ErrnoError({{{ cDefs.ENOTEMPTY }}}); |
| } |
| } |
| FS.hashRemoveNode(new_node); |
| } |
| |
| delete old_node.parent.contents[old_node.name]; |
| new_dir.contents[new_name] = old_node; |
| old_node.name = new_name; |
| new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now(); |
| }, |
| unlink(parent, name) { |
| delete parent.contents[name]; |
| parent.ctime = parent.mtime = Date.now(); |
| }, |
| rmdir(parent, name) { |
| var node = FS.lookupNode(parent, name); |
| for (var i in node.contents) { |
| throw new FS.ErrnoError({{{ cDefs.ENOTEMPTY }}}); |
| } |
| delete parent.contents[name]; |
| parent.ctime = parent.mtime = Date.now(); |
| }, |
| readdir(node) { |
| return ['.', '..', ...Object.keys(node.contents)]; |
| }, |
| symlink(parent, newname, oldpath) { |
| var node = MEMFS.createNode(parent, newname, 0o777 | {{{ cDefs.S_IFLNK }}}, 0); |
| node.link = oldpath; |
| return node; |
| }, |
| readlink(node) { |
| if (!FS.isLink(node.mode)) { |
| throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); |
| } |
| return node.link; |
| }, |
| }, |
| stream_ops: { |
| read(stream, buffer, offset, length, position) { |
| var contents = stream.node.contents; |
| if (position >= stream.node.usedBytes) return 0; |
| var size = Math.min(stream.node.usedBytes - position, length); |
| #if ASSERTIONS |
| assert(size >= 0); |
| #endif |
| buffer.set(contents.subarray(position, position + size), offset); |
| return size; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| write(stream, buffer, offset, length, position, canOwn) { |
| #if ASSERTIONS |
| assert(buffer.subarray, 'FS.write expects a TypedArray'); |
| #endif |
| #if ALLOW_MEMORY_GROWTH |
| |
| |
| |
| |
| if (buffer.buffer === HEAP8.buffer) { |
| canOwn = false; |
| } |
| #endif |
|
|
| if (!length) return 0; |
| var node = stream.node; |
| node.mtime = node.ctime = Date.now(); |
|
|
| if (canOwn) { |
| #if ASSERTIONS |
| assert(position === 0, 'canOwn must imply no weird position inside the file'); |
| #endif |
| node.contents = buffer.subarray(offset, offset + length); |
| node.usedBytes = length; |
| } else if (node.usedBytes === 0 && position === 0) { |
| node.contents = buffer.slice(offset, offset + length); |
| node.usedBytes = length; |
| } else { |
| MEMFS.expandFileStorage(node, position+length); |
| |
| node.contents.set(buffer.subarray(offset, offset + length), position); |
| node.usedBytes = Math.max(node.usedBytes, position + length); |
| } |
| return length; |
| }, |
|
|
| llseek(stream, offset, whence) { |
| var position = offset; |
| if (whence === {{{ cDefs.SEEK_CUR }}}) { |
| position += stream.position; |
| } else if (whence === {{{ cDefs.SEEK_END }}}) { |
| if (FS.isFile(stream.node.mode)) { |
| position += stream.node.usedBytes; |
| } |
| } |
| if (position < 0) { |
| throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); |
| } |
| return position; |
| }, |
| mmap(stream, length, position, prot, flags) { |
| if (!FS.isFile(stream.node.mode)) { |
| throw new FS.ErrnoError({{{ cDefs.ENODEV }}}); |
| } |
| var ptr; |
| var allocated; |
| var contents = stream.node.contents; |
| |
| if (!(flags & {{{ cDefs.MAP_PRIVATE }}}) && contents.buffer === HEAP8.buffer) { |
| |
| |
| allocated = false; |
| ptr = contents.byteOffset; |
| } else { |
| allocated = true; |
| ptr = mmapAlloc(length); |
| if (!ptr) { |
| throw new FS.ErrnoError({{{ cDefs.ENOMEM }}}); |
| } |
| if (contents) { |
| |
| if (position > 0 || position + length < contents.length) { |
| if (contents.subarray) { |
| contents = contents.subarray(position, position + length); |
| } else { |
| contents = Array.prototype.slice.call(contents, position, position + length); |
| } |
| } |
| HEAP8.set(contents, ptr); |
| } |
| } |
| return { ptr, allocated }; |
| }, |
| msync(stream, buffer, offset, length, mmapFlags) { |
| MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); |
| |
| return 0; |
| } |
| } |
| } |
| }); |
|
|
|
|