Skip to content

Ask the file system for the real path of a name that holds a backslash - #683

Merged
ronleizrowice-ant merged 4 commits into
mainfrom
fix/real-path-of-a-name-with-a-backslash
Oct 12, 2026
Merged

ronleizrowice-ant merged 4 commits into
mainfrom
fix/real-path-of-a-name-with-a-backslash

Conversation

@ronleizrowice-ant

Copy link
Copy Markdown
Collaborator

Off Windows a backslash is a character of a file name like any other, and runtimes differ in what fs.realpathSync does with one. realPathOf (src/utils/real-path.ts) is the one place the library asks for a real path.

  • A name without a backslash, and every name on Windows, goes to realpathSync as it is: the same calls, one for one.
  • Otherwise the real path is asked of the file system component by component, split at / alone, with lstat and readlink. Errors keep the kernel's code; ELOOP past 40 links.
  • . and .. in the given name are folded in text first, as realpathSync folds them. In a link's target they are read as the kernel reads them.

It replaces fs.realpathSync at all eleven call sites. An ESLint rule keeps the raw call out of src/.

New export: realPathOf, for a caller that judges paths itself and wants the library's answer.

Testing

  • Every kind of rule (denyRead, denyWrite, allowRead, allowWrite, credentials.files deny and mask, a ** pattern) on names that hold a backslash, in the real sandbox on Linux and macOS: 69 rows, on bubblewrap 0.11.2 and 0.4.1.
  • A property test against the kernel's own answer, under Bun and Node.
  • File system calls for an ordinary configuration: equal to main's, call for call. 34 mutations, each failing a test.

The macOS rows have their first run in this pull request's CI.

Merge order

#575 rewrites two of the call sites. Whichever lands second takes the two lines; the ESLint rule names them.

Off Windows a backslash is a character of a file name like any other, and
runtimes differ in what fs.realpathSync does with one. realPathOf is the one
place the library asks for a real path: a name without a backslash, and
every name on Windows, goes to realpathSync as it is; any other is resolved
component by component, split at '/' alone. Dot segments in the given name
are folded in text first, as realpathSync folds them.

It stands at all eleven call sites, an ESLint rule keeps the raw call out of
src/, and the package exports it.
The case asked its own helper, which goes to fs.realpathSync and falls back
to the spelling. Where the temp directory is reached through a link, as on
macOS, the walk gives the real path. The expectation is built from the real
temp directory.
…ernel's

The property compares realPathOf with the kernel, and in a Node child with
Node's fs.realpathSync. Runtimes differ in how they read a link's target that
goes up from behind a name, and for a name with no backslash realPathOf gives
the runtime's answer. So on a tree with such a link the child is asked about
names that hold a backslash alone. The child has a time limit, and its error
output is part of the expectation.
One conflict, with #575, at two call sites in the walk: main's side, asking
realPathOf.

@imichaelmoore imichaelmoore left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/stamp

@ronleizrowice-ant
ronleizrowice-ant merged commit 495c105 into main Oct 12, 2026
16 checks passed
@ronleizrowice-ant
ronleizrowice-ant deleted the fix/real-path-of-a-name-with-a-backslash branch October 12, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants