Skip to content

File.realpath on Windows: 8.3 short names are not expanded, and MRI's behaviour there is unwitnessed #636

Open

Description

@tannevaled

On the windows CI lane, File.realdirpath of a path under the runner's temp directory answers

C:/Users/RUNNER~1/AppData/Local/Temp/.../file

while Go's filepath.EvalSymlinks — which TestFileRealdirpath had been using as its oracle — answers

C:/Users/runneradmin/AppData/Local/Temp/.../file

RUNNER~1 is the 8.3 short name of runneradmin. Go's EvalSymlinks expands it on Windows (it calls GetFinalPathNameByHandle); realpath_rec in internal/vm/file.go resolves symlinks and .. but leaves the short name alone.

What is not known

Whether MRI expands it. MRI's rb_check_realpath_internal takes a different path on Windows from the emulated walk, and there is no Windows MRI on this development host to witness against. Both answers are defensible from reading alone:

  • the short and long names denote the same file, so neither is "wrong";
  • but File.realpath's contract is a canonical path, and two canonical paths for one file defeats the purpose.

What was done meanwhile

TestFileRealdirpath no longer compares the two canonicalisers on Windows, because they disagree by construction there and the comparison was testing Go's normalisation rather than ours. On POSIX the full equality still holds; on Windows the test asserts the properties that hold either way — absolute, forward-slashed, naming the file. The assertion was not deleted or skipped: it still exercises realdirpath on the windows lane.

Closing this needs a witness: run ruby -e 'p File.realpath(ENV["TMP"] + "/x")' on a Windows box where the temp path contains an 8.3 component, and make rbgo agree.

Related: #635 (File::Stat members Windows cannot supply), #633 (per-platform errno numbers and texts).

Activity

  1. tannevaled commented on Sep 26, 2026

    @tannevaled
    ContributorAuthor

    Reproduced on a runner. This was carried as "the two canonicalisers disagree
    by construction, and MRI's behaviour is unwitnessed"; three assertions added in
    #685 now witness it directly, on Dir.pwd rather than on File.realdirpath,
    so the defect has a live reproduction rather than only a reading.

    From the windows lane of runs/36250946599 (job 108428733698), with the
    process's working directory set to t.TempDir():

    dir_getwd_test.go:236: Dir.pwd          = "C:/Users/RUNNER~1/AppData/Local/Temp/TestDirPwdIntact1868478633/001"
                           want               "C:/Users/runneradmin/AppData/Local/Temp/TestDirPwdIntact1868478633/001"
    dir_getwd_test.go:239: File.expand_path = "C:/Users/RUNNER~1/.../001/x"   want ".../runneradmin/.../001/x"
    

    So the short name is not confined to realpath_rec: it comes straight out of
    GetCurrentDirectory through os.Getwd, which means Dir.pwd,
    File.expand_path and File.absolute_path all inherit it
    , not just the
    realpath family. The expectation in those two lines is Go's
    filepath.EvalSymlinks — the same oracle that disagreed here originally — so this
    is the same disagreement, one layer lower down.

    A second, separate Windows fact showed up beside it in the same test, and it is
    not the 8.3 issue:

    dir_getwd_test.go:248: Dir.pwd through a symlink = "C:/.../002/link"
                           want the physical path      "C:/.../001"
    

    GetCurrentDirectory does not resolve a symlink or junction in the path it
    reports, so a working directory entered through a link is reported as the link.
    On POSIX getcwd(3) always answers the physical path, and MRI's specs rely on
    that. Whether rb_w32_ugetcwd resolves it is unwitnessed here, the same gap this
    issue names — worth recording on this issue since the fix for one is likely the
    fix for both.

    TestDirPwdIntact skips on Windows naming this issue rather than canonicalising
    the expected path, so as not to bake either behaviour into a test. What this issue
    still needs is unchanged: a Windows MRI witness for Dir.pwd,
    File.expand_path and File.realpath on a short-named and on a symlinked path.

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