From 44979a64f8cb4f9cc38f8d2fdd9c55cffc2411b6 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 16 Dec 2024 12:37:04 +0100 Subject: [PATCH 1/3] Avoid some unneccesary path resolution There are a bunch of codepaths where we have a node or a stream and we turn it back into a path, then eventually call `lookupPath` on it to turn it back into a node. This removes a few of these instances. --- src/library_fs.js | 46 ++++++++++++++++++++++++++---------------- src/library_syscall.js | 11 ++++++---- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/library_fs.js b/src/library_fs.js index 68c53ad670bb5..38c31f0029be6 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -52,7 +52,8 @@ FS.staticInit(); streams: [], nextInode: 1, nameTable: null, - currentPath: '/', + currentPath: null, + currentNode: null, initialized: false, // Whether we are currently ignoring permissions. Useful when preparing the // filesystem and creating files inside read-only folders. @@ -173,21 +174,22 @@ FS.staticInit(); // lookupPath(path, opts = {}) { if (!path) return { path: '', node: null }; - opts.follow_mount ??= true + opts.follow_mount ??= true; - if (!PATH.isAbs(path)) { - path = FS.cwd() + '/' + path; + var current; + var current_path; + if (PATH.isAbs(path)) { + current = FS.root; + current_path = "/"; + } else { + current = FS.currentNode; + current_path = FS.currentPath; } + var pathParts = (x) => x.split('/').filter((p) => !!p && (p !== '.')); + var parts = pathParts(path); // limit max consecutive symlinks to 40 (SYMLOOP_MAX). linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { - // split the absolute path - var parts = path.split('/').filter((p) => !!p && (p !== '.')); - - // start at the root - var current = FS.root; - var current_path = '/'; - for (var i = 0; i < parts.length; i++) { var islast = (i === parts.length-1); if (islast && opts.parent) { @@ -226,10 +228,14 @@ FS.staticInit(); throw new FS.ErrnoError({{{ cDefs.ENOSYS }}}); } var link = current.node_ops.readlink(current); - if (!PATH.isAbs(link)) { - link = PATH.dirname(current_path) + '/' + link; + if (PATH.isAbs(link)) { + current = FS.root; + current_path = "/"; + } else { + current = current.parent; + current_path = PATH.dirname(current_path); } - path = link + '/' + parts.slice(i + 1).join('/'); + parts = [].concat(pathParts(link), parts.slice(i + 1)); continue linkloop; } } @@ -405,7 +411,7 @@ FS.staticInit(); if (!FS.isDir(node.mode)) { return {{{ cDefs.ENOTDIR }}}; } - if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + if (FS.isRoot(node) || node === FS.currentNode) { return {{{ cDefs.EBUSY }}}; } } else { @@ -886,8 +892,9 @@ FS.staticInit(); #endif }, readdir(path) { - var lookup = FS.lookupPath(path, { follow: true }); - var node = lookup.node; + FS.readdirNode(FS.lookupPath(path, { follow: true }).node); + }, + readdirNode(node) { if (!node.node_ops.readdir) { throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); } @@ -944,6 +951,9 @@ FS.staticInit(); if (!node) { throw new FS.ErrnoError({{{ cDefs.ENOENT }}}); } + return FS.statNode(node); + }, + statNode(node) { if (!node.node_ops.getattr) { throw new FS.ErrnoError({{{ cDefs.EPERM }}}); } @@ -1366,6 +1376,7 @@ FS.staticInit(); throw new FS.ErrnoError(errCode); } FS.currentPath = lookup.path; + FS.currentNode = lookup.node; }, createDefaultDirectories() { FS.mkdir('/tmp'); @@ -1479,6 +1490,7 @@ FS.staticInit(); FS.nameTable = new Array(4096); FS.mount(MEMFS, {}, '/'); + FS.chdir('/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); diff --git a/src/library_syscall.js b/src/library_syscall.js index 80e616408bac6..0d45063c74ef6 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -526,7 +526,11 @@ var SyscallsLibrary = { #endif // ~PROXY_POSIX_SOCKETS==0 __syscall_fchdir: (fd) => { var stream = SYSCALLS.getStreamFromFD(fd); - FS.chdir(stream.path); + if (!FS.isDir(stream.node.mode)) { + return -{{{ cDefs.ENOTDIR }}}; + } + FS.currentPath = stream.path; + FS.currentNode = stream.node; return 0; }, __syscall__newselect: (nfds, readfds, writefds, exceptfds, timeout) => { @@ -689,7 +693,7 @@ var SyscallsLibrary = { __syscall_getdents64__deps: ['$stringToUTF8'], __syscall_getdents64: (fd, dirp, count) => { var stream = SYSCALLS.getStreamFromFD(fd) - stream.getdents ||= FS.readdir(stream.path); + stream.getdents ||= FS.readdirNode(stream.node); var struct_size = {{{ C_STRUCTS.dirent.__size__ }}}; var pos = 0; @@ -706,8 +710,7 @@ var SyscallsLibrary = { type = 4; // DT_DIR } else if (name === '..') { - var lookup = FS.lookupPath(stream.path, { parent: true }); - id = lookup.node.id; + id = stream.node.parent.id; type = 4; // DT_DIR } else { From de946f233ee8262881c3d61624e1b77aae50c9ce Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 16 Dec 2024 15:17:40 +0100 Subject: [PATCH 2/3] Fix noderawfs --- src/library_noderawfs.js | 1 + src/library_syscall.js | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/library_noderawfs.js b/src/library_noderawfs.js index 81ab16962a89c..5f032c10081f6 100644 --- a/src/library_noderawfs.js +++ b/src/library_noderawfs.js @@ -68,6 +68,7 @@ addToLibrary({ rename(...args) { fs.renameSync(...args); }, rmdir(...args) { fs.rmdirSync(...args); }, readdir(...args) { return ['.', '..'].concat(fs.readdirSync(...args)); }, + readdirNode(node) { return ['.', '..'].concat(fs.readdirSync(node.path)); }, unlink(...args) { fs.unlinkSync(...args); }, readlink(...args) { return fs.readlinkSync(...args); }, stat(path, dontFollow) { diff --git a/src/library_syscall.js b/src/library_syscall.js index 0d45063c74ef6..d9490a1d25afe 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -692,7 +692,7 @@ var SyscallsLibrary = { }, __syscall_getdents64__deps: ['$stringToUTF8'], __syscall_getdents64: (fd, dirp, count) => { - var stream = SYSCALLS.getStreamFromFD(fd) + var stream = SYSCALLS.getStreamFromFD(fd); stream.getdents ||= FS.readdirNode(stream.node); var struct_size = {{{ C_STRUCTS.dirent.__size__ }}}; @@ -710,7 +710,10 @@ var SyscallsLibrary = { type = 4; // DT_DIR } else if (name === '..') { - id = stream.node.parent.id; + // In Node rawfs, node.parent is null so we fall back to calling + // lookupPath. + var parent = stream.node.parent ?? FS.lookupPath(stream.path, { parent: true }).node; + id = parent.id; type = 4; // DT_DIR } else { From 99fb293138b72521a982c84ff789d43637ad5e51 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 16 Dec 2024 17:48:16 +0100 Subject: [PATCH 3/3] Fix test_unlink --- src/library_fs.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/library_fs.js b/src/library_fs.js index 38c31f0029be6..33e0ecef7b644 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -885,6 +885,7 @@ FS.staticInit(); #endif parent.node_ops.rmdir(parent, name); FS.destroyNode(node); + node.parent = null; #if FS_DEBUG if (FS.trackingDelegate['onDeletePath']) { FS.trackingDelegate['onDeletePath'](path); @@ -895,6 +896,9 @@ FS.staticInit(); FS.readdirNode(FS.lookupPath(path, { follow: true }).node); }, readdirNode(node) { + if (!node.parent) { + return []; + } if (!node.node_ops.readdir) { throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); }