Skip to content

fs.readlinkSync on snapshot paths: missing in standard mode, realpath-shaped in SEA #299

Description

@robertsLando

fs.readlinkSync on snapshot paths does not match Node's behaviour, differently in each packaging mode.

Probe package: lib -> ./reallib (directory symlink), reallib/log.js, entry requiring ./lib/log.

--- unpackaged node ---
lib        -> ./reallib
lib/log.js -> EINVAL readlink        (correct: not a symlink)

--- standard mode ---
lib        -> ENOENT readlink
lib/log.js -> ENOENT readlink

--- SEA mode ---
lib        -> /snapshot/rlprobe/reallib
lib/log.js -> /snapshot/rlprobe/reallib/log.js

fs.realpathSync is correct in both modes (SEA since 6df8da1 on the #296 branch).

Three separate problems:

Standard mode never patches fs.readlinkSync. prelude/bootstrap.js carries only a // fs.promises.readlink ? note, and the throw comes straight from node:fs. The classic bootstrap patches fs itself and does not go through @roberts_lando/vfs, so readlink fails on every snapshot path, symlink or not. Entirely a pkg-side gap.

The manifest stores the resolved target, not the literal one. lib/walker.ts:459 does this.symLinks[file] = realFile and lib/sea-assets.ts:136 only POSIX-normalizes it, so the manifest holds "/rlprobe/lib": "/rlprobe/reallib" where the on-disk link is ./reallib. Even once the provider is consulted, pkg can only return a resolved absolute path. True readlink semantics would mean storing the raw fs.readlinkSync result alongside the resolved one — a manifest format change, and findCommonJunctionPoint rewrites both paths before storage, so it needs care. Alternatively this is documented as realpath-shaped and left alone.

Non-symlinks give ENOENT instead of EINVAL. SEAProvider.readlinkSync falls through to super.readlinkSync, and MemoryProvider's tree holds only directories, so a real archive file that is not a symlink raises ENOENT. It should raise EINVAL when the path is in manifest.stats but absent from symlinks.

Related, on the VFS side: the fs.readlinkSync patch in module_hooks.js answers via findVFSForRealpath and never calls provider.readlinkSync, so SEAProvider.readlinkSync is unreachable in a packaged binary today. That routing change belongs in @roberts_lando/vfs; the three items above remain regardless.

Found while reviewing #296. None of these are caused by that PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions