Skip to content

std.fs.realpath bugs/inconsistencies on Windows #4658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
9 of 10 tasks
squeek502 opened this issue Mar 6, 2020 · 2 comments
Open
9 of 10 tasks

std.fs.realpath bugs/inconsistencies on Windows #4658

squeek502 opened this issue Mar 6, 2020 · 2 comments
Labels
bug Observed behavior contradicts documented or intended behavior os-windows standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@squeek502
Copy link
Collaborator

squeek502 commented Mar 6, 2020

Some related comments can be found in #4655 and #4605. Currently fs.realpath/os.realpath has different/broken symlink resolution behavior on different platforms (or maybe just Windows). The doc comments say:

zig/lib/std/os.zig

Lines 3027 to 3032 in eaccfff

/// Return the canonicalized absolute pathname.
/// Expands all symbolic links and resolves references to `.`, `..`, and
/// extra `/` characters in `pathname`.
/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
/// See also `realpathC` and `realpathW`.
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {

Janky test code (realpath.zig built via zig build-exe realpath.zig):

const std = @import("std");

pub fn main() anyerror!void {
    var args = try std.process.argsAlloc(std.heap.page_allocator);
    std.debug.assert(args.len > 1);
    std.debug.warn("path={}\n", .{args[1]});
    var real_path = try std.fs.realpathAlloc(std.heap.page_allocator, args[1]);
    std.debug.warn("realpath={}\n", .{real_path});
}

On Windows:

Setup:

mkdir foo
mkdir foo\bar
mkdir foo\bar\dir
echo. > foo\bar\file
mkdir dir
echo. > file
echo. > foo\file
mklink /J link foo\bar
C:\tmp>.\realpath.exe dir
path=dir
error: AccessDenied
C:\Users\Ryan\Programming\Zig\bin\lib\zig\std\os\windows.zig:81:31: 0x13fb88907 in std.os.windows.CreateFileW (realpath.obj)
            .ACCESS_DENIED => return error.AccessDenied,
                              ^
C:\Users\Ryan\Programming\Zig\bin\lib\zig\std\os.zig:3004:20: 0x13fb83e4f in std.os.realpathW (realpath.obj)
    const h_file = try windows.CreateFileW(
                   ^
C:\Users\Ryan\Programming\Zig\bin\lib\zig\std\os.zig:2965:9: 0x13fb83d97 in std.os.realpath (realpath.obj)
        return realpathW(&pathname_w, out_buffer);
        ^
C:\Users\Ryan\Programming\Zig\bin\lib\zig\std\fs.zig:1664:36: 0x13fb78861 in std.fs.realpathAlloc (realpath.obj)
    return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
                                   ^
C:\tmp\realpath.zig:7:21: 0x13fb62f62 in main (realpath.obj)
    var real_path = try std.fs.realpathAlloc(std.heap.page_allocator, args[1]);
                    ^
  • realpath works correctly for files on Windows
C:\tmp>.\realpath.exe file
path=file
realpath=C:\tmp\file
  • realpath resolves files in a symlinked dir on Windows
C:\tmp>.\realpath.exe link\file
path=link\file
realpath=C:\tmp\foo\bar\file

C:\tmp>.\realpath.exe link\.\file
path=link\.\file
realpath=C:\tmp\foo\bar\file
  • realpath resolves files in a symlinked dir when cwd is the symlinked dir on Windows
C:\tmp\link>..\realpath.exe file
path=file
realpath=C:\tmp\foo\bar\file

C:\tmp\link>..\realpath.exe .\file
path=.\file
realpath=C:\tmp\foo\bar\file
  • realpath does not resolve symlinks before resolving .. on Windows (note: the intended resolution order is not currently specified in the realpath doc comments)
C:\tmp\link>..\realpath.exe .\..\file
path=.\..\file
realpath=C:\tmp\file

C:\tmp>.\realpath.exe link\..\file
path=link\..\file
realpath=C:\tmp\file

On Linux:

Setup:

mkdir -p foo/bar/dir
touch foo/bar/file
mkdir dir
touch file
touch foo/file
ln -s foo/bar link
  • realpath works for directories on Linux
~/tmp$ ./realpath dir
path=dir
realpath=/home/ryan/tmp/dir
  • realpath works correctly for files on Linux
~/tmp$ ./realpath file
path=file
realpath=/home/ryan/tmp/file
  • realpath resolves files in a symlinked dir on Linux
~/tmp$ ./realpath link/file
path=link/file
realpath=/home/ryan/tmp/foo/bar/file

~/tmp$ ./realpath link/./file
path=link/./file
realpath=/home/ryan/tmp/foo/bar/file
  • realpath resolves files in a symlinked dir when cwd is the symlinked dir on Linux
~/tmp/link$ ~/tmp/realpath file
path=file
realpath=/home/ryan/tmp/foo/bar/file

~/tmp/link$ ~/tmp/realpath ./file
path=./file
realpath=/home/ryan/tmp/foo/bar/file
  • realpath resolves symlinks before resolving .. on Linux (note: the intended resolution order is not currently specified in the realpath doc comments)
~/tmp/link$ ~/tmp/realpath ./../file
path=./../file
realpath=/home/ryan/tmp/foo/file

~/tmp$ ./realpath link/../file
path=link/../file
realpath=/home/ryan/tmp/foo/file
@andrewrk andrewrk added os-windows standard library This issue involves writing Zig code for the standard library. bug Observed behavior contradicts documented or intended behavior labels Mar 6, 2020
@andrewrk andrewrk added this to the 0.7.0 milestone Mar 6, 2020
@andrewrk
Copy link
Member

andrewrk commented Mar 6, 2020

Thanks, these are some really useful test cases.

@squeek502
Copy link
Collaborator Author

Relevant info from #4655 (comment):

Right now, the status quo is:

  • On Linux, std.fs.realpath resolves symlinks before ... This matches the system behavior as far as I can tell (cat link/../file will output the contents of linked/../file and fail if it doesn't exist), although there seem to be some edge cases (from my testing, if link is a symlink to linked, then cd link/../dir takes you to ./dir if it exists [ignoring the symlink], otherwise it will take you to linked/../dir [resolving the symlink before ..]).

  • On Windows, std.fs.realpath resolves .. before symlinks. This matches the system behavior as far as I can tell (cd link\..\dir will never take you to linked\..\dir; if .\dir does not exist, it will fail with "The system cannot find the path specified.". Same deal with type link\..\file). Please correct me if I'm wrong on this, but I'm not even sure if there exists a function in the Windows API that resolves symlinks before ... daurnimator has mentioned that Zig might need something like RtlDosPathNameToRelativeNtPathName_U_WithStatus but from using the test code at the bottom of this article, that function does not resolve symlinks before .. either. If there is precedence for Linux-like symlink resolution on Windows, it would be helpful to get that information added to std.fs.realpath bugs/inconsistencies on Windows #4658

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior os-windows standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

2 participants