diff --git a/src/library_fs.js b/src/library_fs.js index 68c53ad670bb5..33e0ecef7b644 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 { @@ -879,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); @@ -886,8 +893,12 @@ 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.parent) { + return []; + } if (!node.node_ops.readdir) { throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); } @@ -944,6 +955,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 +1380,7 @@ FS.staticInit(); throw new FS.ErrnoError(errCode); } FS.currentPath = lookup.path; + FS.currentNode = lookup.node; }, createDefaultDirectories() { FS.mkdir('/tmp'); @@ -1479,6 +1494,7 @@ FS.staticInit(); FS.nameTable = new Array(4096); FS.mount(MEMFS, {}, '/'); + FS.chdir('/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); 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 80e616408bac6..d9490a1d25afe 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) => { @@ -688,8 +692,8 @@ var SyscallsLibrary = { }, __syscall_getdents64__deps: ['$stringToUTF8'], __syscall_getdents64: (fd, dirp, count) => { - var stream = SYSCALLS.getStreamFromFD(fd) - stream.getdents ||= FS.readdir(stream.path); + var stream = SYSCALLS.getStreamFromFD(fd); + stream.getdents ||= FS.readdirNode(stream.node); var struct_size = {{{ C_STRUCTS.dirent.__size__ }}}; var pos = 0; @@ -706,8 +710,10 @@ var SyscallsLibrary = { type = 4; // DT_DIR } else if (name === '..') { - var lookup = FS.lookupPath(stream.path, { parent: true }); - id = lookup.node.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 {