Skip to content

Commit acf75ee

Browse files
authored
Refactor lookupPath to avoid recursion. NFC (#23017)
This removes the recursion from `lookupPath` and in my opinion makes the control flow much more explicit and comprehensible.
1 parent 85725fa commit acf75ee

File tree

2 files changed

+28
-43
lines changed

2 files changed

+28
-43
lines changed

ChangeLog.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ See docs/process.md for more on how version tagging works.
2424
3.1.73 - 11/28/24
2525
-----------------
2626
- libunwind was updated to LLVM 19.1.4. (#22394)
27-
- mimalloc was updated to 2.1.7. (#21548)
2827

2928
3.1.72 - 11/19/24
3029
-----------------

src/library_fs.js

Lines changed: 28 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -174,60 +174,46 @@ FS.staticInit();
174174
path = PATH_FS.resolve(path);
175175

176176
if (!path) return { path: '', node: null };
177+
opts.follow_mount ??= true
177178

178-
var defaults = {
179-
follow_mount: true,
180-
recurse_count: 0
181-
};
182-
opts = Object.assign(defaults, opts)
183-
184-
if (opts.recurse_count > 8) { // max recursive lookup of 8
185-
throw new FS.ErrnoError({{{ cDefs.ELOOP }}});
186-
}
179+
// limit max consecutive symlinks to 40 (SYMLOOP_MAX).
180+
linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) {
181+
// split the absolute path
182+
var parts = path.split('/').filter((p) => !!p);
187183

188-
// split the absolute path
189-
var parts = path.split('/').filter((p) => !!p);
184+
// start at the root
185+
var current = FS.root;
186+
var current_path = '/';
190187

191-
// start at the root
192-
var current = FS.root;
193-
var current_path = '/';
194-
195-
for (var i = 0; i < parts.length; i++) {
196-
var islast = (i === parts.length-1);
197-
if (islast && opts.parent) {
198-
// stop resolving
199-
break;
200-
}
188+
for (var i = 0; i < parts.length; i++) {
189+
var islast = (i === parts.length-1);
190+
if (islast && opts.parent) {
191+
// stop resolving
192+
break;
193+
}
201194

202-
current = FS.lookupNode(current, parts[i]);
203-
current_path = PATH.join2(current_path, parts[i]);
195+
current = FS.lookupNode(current, parts[i]);
196+
current_path = PATH.join2(current_path, parts[i]);
204197

205-
// jump to the mount's root node if this is a mountpoint
206-
if (FS.isMountpoint(current)) {
207-
if (!islast || (islast && opts.follow_mount)) {
198+
// jump to the mount's root node if this is a mountpoint
199+
if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) {
208200
current = current.mounted.root;
209201
}
210-
}
211-
212-
// by default, lookupPath will not follow a symlink if it is the final path component.
213-
// setting opts.follow = true will override this behavior.
214-
if (!islast || opts.follow) {
215-
var count = 0;
216-
while (FS.isLink(current.mode)) {
217-
var link = FS.readlink(current_path);
218-
current_path = PATH_FS.resolve(PATH.dirname(current_path), link);
219202

220-
var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1 });
221-
current = lookup.node;
222-
223-
if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
224-
throw new FS.ErrnoError({{{ cDefs.ELOOP }}});
203+
// by default, lookupPath will not follow a symlink if it is the final path component.
204+
// setting opts.follow = true will override this behavior.
205+
if (FS.isLink(current.mode) && (!islast || opts.follow)) {
206+
if (!current.node_ops.readlink) {
207+
throw new FS.ErrnoError({{{ cDefs.ENOSYS }}});
225208
}
209+
var link = current.node_ops.readlink(current);
210+
path = PATH_FS.resolve(PATH.dirname(current_path), link, ...parts.slice(i + 1));
211+
continue linkloop;
226212
}
227213
}
214+
return { path: current_path, node: current };
228215
}
229-
230-
return { path: current_path, node: current };
216+
throw new FS.ErrnoError({{{ cDefs.ELOOP }}});
231217
},
232218
getPath(node) {
233219
var path;

0 commit comments

Comments
 (0)