@@ -1431,6 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct {
1431
1431
}{} else undefined ;
1432
1432
1433
1433
/// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`.
1434
+ /// Note that `cwd_init` corresponds to a Preopen directory, not necessarily
1435
+ /// a POSIX path. For example, "." matches a Preopen provided with `--dir=.`
1434
1436
///
1435
1437
/// This must be called before using any relative or absolute paths with `std.os`
1436
1438
/// functions, if you are on WASI without linking libc.
@@ -1482,8 +1484,22 @@ fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffe
1482
1484
if (wasi_cwd .preopens == null ) @panic ("On WASI, `initPreopensWasi` must be called to initialize preopens " ++
1483
1485
"before using any CWD-relative or absolute paths.\n " );
1484
1486
1485
- // If the path is absolute, we need to lookup a containing Preopen
1486
- const abs_path = std .fs .path .resolve (alloc , &.{ "/" , path }) catch return error .NameTooLong ;
1487
+ if (mem .startsWith (u8 , path , "/preopens/fd/" )) {
1488
+ // "/preopens/fd/<N>" is a special prefix, which refers to a Preopen directly by fd
1489
+ const fd_start = "/preopens/fd/" .len ;
1490
+ const fd_end = mem .indexOfScalarPos (u8 , path , fd_start , '/' ) orelse path .len ;
1491
+ const fd = std .fmt .parseUnsigned (fd_t , path [fd_start .. fd_end ], 10 ) catch unreachable ;
1492
+ const rel_path = if (path .len > fd_end + 1 ) path [fd_end + 1 .. ] else "." ;
1493
+
1494
+ if (preopen ) | p | p .* = wasi_cwd .preopens .? .findByFd (fd );
1495
+ return RelativePath {
1496
+ .dir_fd = fd ,
1497
+ .relative_path = alloc .dupe (u8 , rel_path ) catch return error .NameTooLong ,
1498
+ };
1499
+ }
1500
+
1501
+ // For any other absolute path, we need to lookup a containing Preopen
1502
+ const abs_path = fs .path .resolve (alloc , &.{ "/" , path }) catch return error .NameTooLong ;
1487
1503
const preopen_uri = wasi_cwd .preopens .? .findContaining (.{ .Dir = abs_path });
1488
1504
1489
1505
if (preopen_uri ) | po | {
@@ -1507,7 +1523,7 @@ fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffe
1507
1523
// First resolve a combined path, where the "/" corresponds to `cwd.dir_fd`
1508
1524
// not the true filesystem root
1509
1525
const paths = &.{ "/" , cwd .relative_path , path };
1510
- const resolved_path = std . fs .path .resolve (alloc , paths ) catch return error .NameTooLong ;
1526
+ const resolved_path = fs .path .resolve (alloc , paths ) catch return error .NameTooLong ;
1511
1527
1512
1528
// Strip off the fake root to get the relative path w.r.t. `cwd.dir_fd`
1513
1529
const resolved_relative_path = resolved_path [1.. ];
@@ -1956,28 +1972,14 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1956
1972
if (builtin .os .tag == .windows ) {
1957
1973
return windows .GetCurrentDirectory (out_buffer );
1958
1974
} else if (builtin .os .tag == .wasi and ! builtin .link_libc ) {
1959
- var allocator = std .heap .FixedBufferAllocator .init (out_buffer );
1960
- var alloc = allocator .allocator ();
1961
- if (wasi_cwd .cwd ) | cwd | {
1962
- if (wasi_cwd .cwd_preopen ) | po | {
1963
- var base_cwd_dir = switch (po .@"type" ) {
1964
- .Dir = > | dir | dir ,
1965
- };
1966
- if (! fs .path .isAbsolute (base_cwd_dir )) {
1967
- // This preopen is not based on an absolute path, so we have
1968
- // no way to know the absolute path of the CWD
1969
- return error .CurrentWorkingDirectoryUnlinked ;
1970
- }
1971
- const paths = &.{ base_cwd_dir , cwd .relative_path };
1972
- return std .fs .path .resolve (alloc , paths ) catch return error .NameTooLong ;
1973
- } else {
1974
- // The CWD is not rooted to an existing Preopen,
1975
- // so we have no way to know its absolute path
1976
- return error .CurrentWorkingDirectoryUnlinked ;
1977
- }
1978
- } else {
1979
- return alloc .dupe (u8 , "/" ) catch return error .NameTooLong ;
1980
- }
1975
+ var buf : [MAX_PATH_BYTES ]u8 = undefined ;
1976
+ const path = realpathWasi ("." , & buf ) catch | err | switch (err ) {
1977
+ error .NameTooLong = > return error .NameTooLong ,
1978
+ error .InvalidHandle = > return error .CurrentWorkingDirectoryUnlinked ,
1979
+ };
1980
+ if (out_buffer .len < path .len ) return error .NameTooLong ;
1981
+ std .mem .copy (u8 , out_buffer , path );
1982
+ return out_buffer [0.. path .len ];
1981
1983
}
1982
1984
1983
1985
const err = if (builtin .link_libc ) blk : {
@@ -5089,35 +5091,45 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
5089
5091
const pathname_w = try windows .sliceToPrefixedFileW (pathname );
5090
5092
return realpathW (pathname_w .span (), out_buffer );
5091
5093
} else if (builtin .os .tag == .wasi and ! builtin .link_libc ) {
5092
- // NOTE: This emulation is incomplete. Symbolic links are not
5093
- // currently expanded during path canonicalization.
5094
- var alloc = std .heap .FixedBufferAllocator .init (out_buffer );
5095
- if (fs .path .isAbsolute (pathname ))
5096
- return try fs .path .resolve (alloc .allocator (), &.{pathname }) catch error .NameTooLong ;
5097
- if (wasi_cwd .cwd ) | cwd | {
5098
- if (wasi_cwd .cwd_preopen ) | po | {
5099
- var base_cwd_dir = switch (po .@"type" ) {
5100
- .Dir = > | dir | dir ,
5101
- };
5102
- if (! fs .path .isAbsolute (base_cwd_dir )) {
5103
- // This preopen is not based on an absolute path, so we have
5104
- // no way to know the absolute path of the CWD
5105
- return error .InvalidHandle ;
5106
- }
5094
+ return realpathWasi (pathname , out_buffer );
5095
+ }
5096
+ const pathname_c = try toPosixPath (pathname );
5097
+ return realpathZ (& pathname_c , out_buffer );
5098
+ }
5107
5099
5108
- const paths = &.{ base_cwd_dir , cwd .relative_path , pathname };
5109
- return fs .path .resolve (alloc .allocator (), paths ) catch error .NameTooLong ;
5110
- } else {
5111
- // The CWD is not rooted to an existing Preopen,
5112
- // so we have no way to know its absolute path
5113
- return error .InvalidHandle ;
5114
- }
5100
+ /// Return an emulated canonicalized absolute pathname on WASI.
5101
+ ///
5102
+ /// NOTE: This emulation is incomplete. Symbolic links are not
5103
+ /// currently expanded during path canonicalization.
5104
+ fn realpathWasi (pathname : []const u8 , out_buffer : []u8 ) ! []u8 {
5105
+ var alloc = std .heap .FixedBufferAllocator .init (out_buffer );
5106
+ if (fs .path .isAbsolute (pathname ))
5107
+ return try fs .path .resolve (alloc .allocator (), &.{pathname }) catch error .NameTooLong ;
5108
+ if (wasi_cwd .cwd ) | cwd | {
5109
+ if (wasi_cwd .cwd_preopen ) | po | {
5110
+ var base_cwd_dir = switch (po .@"type" ) {
5111
+ .Dir = > | dir | dir ,
5112
+ };
5113
+ const paths : [][]const u8 = if (fs .path .isAbsolute (base_cwd_dir )) blk : {
5114
+ break :blk &.{ base_cwd_dir , cwd .relative_path , pathname };
5115
+ } else blk : {
5116
+ // No absolute path is associated with this preopen, so
5117
+ // instead we use a special "/preopens/fd/<N>/" prefix
5118
+ var buf : [16 ]u8 = undefined ;
5119
+ var fbs = std .io .fixedBufferStream (& buf );
5120
+ std .fmt .formatInt (po .fd , 10 , .lower , .{}, fbs .writer ()) catch return error .NameTooLong ;
5121
+ break :blk &.{ "/preopens/fd/" , fbs .getWritten (), cwd .relative_path , pathname };
5122
+ };
5123
+
5124
+ return fs .path .resolve (alloc .allocator (), paths ) catch error .NameTooLong ;
5115
5125
} else {
5116
- return try fs .path .resolve (alloc .allocator (), &.{ "/" , pathname }) catch error .NameTooLong ;
5126
+ // The CWD is not rooted to an existing Preopen,
5127
+ // so we have no way to know its absolute path
5128
+ return error .InvalidHandle ;
5117
5129
}
5130
+ } else {
5131
+ return try fs .path .resolve (alloc .allocator (), &.{ "/" , pathname }) catch error .NameTooLong ;
5118
5132
}
5119
- const pathname_c = try toPosixPath (pathname );
5120
- return realpathZ (& pathname_c , out_buffer );
5121
5133
}
5122
5134
5123
5135
/// Same as `realpath` except `pathname` is null-terminated.
0 commit comments