diff --git a/doc/langref.html.in b/doc/langref.html.in
index 0f90a88822a1..6ae2e456e471 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -11279,7 +11279,7 @@ pub fn main() !void {
var preopens = PreopenList.init(gpa);
defer preopens.deinit();
- try preopens.populate();
+ try preopens.populate(null);
for (preopens.asSlice()) |preopen, i| {
std.debug.print("{}: {}\n", .{ i, preopen });
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
index bdd1c53ed5df..d5583dcc80d8 100644
--- a/lib/std/fs/path.zig
+++ b/lib/std/fs/path.zig
@@ -735,7 +735,7 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
test "resolve" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
@@ -756,7 +756,7 @@ test "resolveWindows" {
return error.SkipZigTest;
}
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
if (native_os == .windows) {
const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
@@ -802,7 +802,7 @@ test "resolveWindows" {
test "resolvePosix" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
@@ -1216,7 +1216,7 @@ test "relative" {
return error.SkipZigTest;
}
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index 82005152e517..b8e6c4d89a8f 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -47,7 +47,7 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo
test "accessAbsolute" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
@@ -66,7 +66,7 @@ test "accessAbsolute" {
test "openDirAbsolute" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
@@ -103,7 +103,7 @@ test "openDir cwd parent .." {
test "readLinkAbsolute" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
@@ -535,7 +535,7 @@ test "rename" {
test "renameAbsolute" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp_dir = tmpDir(.{});
defer tmp_dir.cleanup();
@@ -980,7 +980,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
test "walker" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{ .iterate = true });
defer tmp.cleanup();
@@ -1031,7 +1031,7 @@ test "walker" {
test ". and .. in fs.Dir functions" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
@@ -1060,7 +1060,7 @@ test ". and .. in fs.Dir functions" {
test ". and .. in absolute functions" {
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig
index 4754ded630d1..81a43062dcf4 100644
--- a/lib/std/fs/wasi.zig
+++ b/lib/std/fs/wasi.zig
@@ -3,6 +3,8 @@ const builtin = @import("builtin");
const os = std.os;
const mem = std.mem;
const math = std.math;
+const fs = std.fs;
+const assert = std.debug.assert;
const Allocator = mem.Allocator;
const wasi = std.os.wasi;
const fd_t = wasi.fd_t;
@@ -34,16 +36,27 @@ pub const PreopenType = union(PreopenTypeTag) {
// Checks whether `other` refers to a subdirectory of `self` and, if so,
// returns the relative path to `other` from `self`
+ //
+ // Expects `other` to be a canonical path, not containing "." or ".."
pub fn getRelativePath(self: Self, other: PreopenType) ?[]const u8 {
if (std.meta.activeTag(self) != std.meta.activeTag(other)) return null;
switch (self) {
- PreopenTypeTag.Dir => |this_path| {
+ PreopenTypeTag.Dir => |self_path| {
const other_path = other.Dir;
- if (mem.indexOfDiff(u8, this_path, other_path)) |index| {
- if (index < this_path.len) return null;
+ if (mem.indexOfDiff(u8, self_path, other_path)) |index| {
+ if (index < self_path.len) return null;
+ }
+
+ const rel_path = other_path[self_path.len..];
+ if (rel_path.len == 0) {
+ return rel_path;
+ } else if (rel_path[0] == '/') {
+ return rel_path[1..];
+ } else {
+ if (self_path[self_path.len - 1] != '/') return null;
+ return rel_path;
}
- return other_path[this_path.len..];
},
}
}
@@ -130,7 +143,22 @@ pub const PreopenList = struct {
/// the preopen list still contains all valid preopened file descriptors that are valid
/// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally,
/// `deinit` still must be called!
- pub fn populate(self: *Self) Error!void {
+ ///
+ /// Usage of `cwd_root`:
+ /// If provided, `cwd_root` is inserted as prefix for any Preopens that
+ /// begin with "." and all paths are normalized as POSIX-style absolute
+ /// paths. `cwd_root` must be an absolute path.
+ ///
+ /// For example:
+ /// "./foo/bar" -> "{cwd_root}/foo/bar"
+ /// "foo/bar" -> "/foo/bar"
+ /// "/foo/bar" -> "/foo/bar"
+ ///
+ /// If `cwd_root` is not provided, all preopen directories are unmodified.
+ ///
+ pub fn populate(self: *Self, cwd_root: ?[]const u8) Error!void {
+ if (cwd_root) |root| assert(fs.path.isAbsolute(root));
+
// Clear contents if we're being called again
for (self.toOwnedSlice()) |preopen| {
switch (preopen.@"type") {
@@ -140,6 +168,7 @@ pub const PreopenList = struct {
errdefer self.deinit();
var fd: fd_t = 3; // start fd has to be beyond stdio fds
+ var path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
while (true) {
var buf: prestat_t = undefined;
switch (wasi.fd_prestat_get(fd, &buf)) {
@@ -156,14 +185,34 @@ pub const PreopenList = struct {
else => |err| return os.unexpectedErrno(err),
}
const preopen_len = buf.u.dir.pr_name_len;
- const path_buf = try self.buffer.allocator.alloc(u8, preopen_len);
- mem.set(u8, path_buf, 0);
- switch (wasi.fd_prestat_dir_name(fd, path_buf.ptr, preopen_len)) {
+
+ mem.set(u8, path_buf[0..preopen_len], 0);
+ switch (wasi.fd_prestat_dir_name(fd, &path_buf, preopen_len)) {
.SUCCESS => {},
else => |err| return os.unexpectedErrno(err),
}
- const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf });
+ // Unfortunately, WASI runtimes (e.g. wasmer) are not consistent about whether the
+ // NULL sentinel is included in the reported Preopen name_len
+ const raw_path = if (path_buf[preopen_len - 1] == 0) blk: {
+ break :blk path_buf[0 .. preopen_len - 1];
+ } else path_buf[0..preopen_len];
+
+ // If we were provided a CWD root to resolve against, we try to treat Preopen dirs as
+ // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "."
+ const path = if (cwd_root) |cwd| blk: {
+ const resolve_paths: [][]const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path };
+ break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) {
+ error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried
+ else => |e| return e,
+ };
+ } else blk: {
+ // If we were provided no CWD root, we preserve the preopen dir without resolving
+ break :blk try self.buffer.allocator.dupe(u8, raw_path);
+ };
+ errdefer self.buffer.allocator.free(path);
+ const preopen = Preopen.new(fd, .{ .Dir = path });
+
try self.buffer.append(preopen);
fd = try math.add(fd_t, fd, 1);
}
@@ -171,27 +220,22 @@ pub const PreopenList = struct {
/// Find a preopen which includes access to `preopen_type`.
///
- /// If the preopen exists, `relative_path` is updated to point to the relative
- /// portion of `preopen_type` and the matching Preopen is returned. If multiple
- /// preopens match the provided resource, the most recent one is used.
+ /// If multiple preopens match the provided resource, the most specific
+ /// match is returned. More recent preopens take priority, as well.
pub fn findContaining(self: Self, preopen_type: PreopenType) ?PreopenUri {
- // Search in reverse, so that most recently added preopens take precedence
- var k: usize = self.buffer.items.len;
- while (k > 0) {
- k -= 1;
-
- const preopen = self.buffer.items[k];
- if (preopen.@"type".getRelativePath(preopen_type)) |rel_path_orig| {
- var rel_path = rel_path_orig;
- while (rel_path.len > 0 and rel_path[0] == '/') rel_path = rel_path[1..];
-
- return PreopenUri{
- .base = preopen,
- .relative_path = if (rel_path.len == 0) "." else rel_path,
- };
+ var best_match: ?PreopenUri = null;
+
+ for (self.buffer.items) |preopen| {
+ if (preopen.@"type".getRelativePath(preopen_type)) |rel_path| {
+ if (best_match == null or rel_path.len <= best_match.?.relative_path.len) {
+ best_match = PreopenUri{
+ .base = preopen,
+ .relative_path = if (rel_path.len == 0) "." else rel_path,
+ };
+ }
}
}
- return null;
+ return best_match;
}
/// Find preopen by fd. If the preopen exists, return it.
@@ -233,8 +277,20 @@ test "extracting WASI preopens" {
var preopens = PreopenList.init(std.testing.allocator);
defer preopens.deinit();
- try preopens.populate();
+ try preopens.populate(null);
const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." }));
+
+ const po_type1 = PreopenType{ .Dir = "/" };
+ try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/" }).?, ""));
+ try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/test/foobar" }).?, "test/foobar"));
+
+ const po_type2 = PreopenType{ .Dir = "/test/foo" };
+ try std.testing.expect(po_type2.getRelativePath(.{ .Dir = "/test/foobar" }) == null);
+
+ const po_type3 = PreopenType{ .Dir = "/test" };
+ try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test" }).?, ""));
+ try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/" }).?, ""));
+ try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/foo/bar" }).?, "foo/bar"));
}
diff --git a/lib/std/os.zig b/lib/std/os.zig
index df42d14c3d4b..f9ee65a31744 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1431,10 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct {
preopens: ?PreopenList = null,
// Memory buffer for storing the relative portion of the CWD
path_buffer: [MAX_PATH_BYTES]u8 = undefined,
- // Current Working Directory, stored as an fd_t and a relative path
- cwd: ?RelativePathWasi = null,
- // Preopen associated with `cwd`, if any
- cwd_preopen: ?Preopen = null,
+ // The absolute path associated with the current working directory
+ cwd: []const u8 = "/",
}{} else undefined;
/// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`.
@@ -1444,31 +1442,33 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct {
/// This must be called before using any relative or absolute paths with `std.os`
/// functions, if you are on WASI without linking libc.
///
+/// The current working directory is initialized to `cwd_root`, and `cwd_root`
+/// is inserted as a prefix for any Preopens whose dir begins with "."
+/// For example:
+/// "./foo/bar" - canonicalizes to -> "{cwd_root}/foo/bar"
+/// "foo/bar" - canonicalizes to -> "/foo/bar"
+/// "/foo/bar" - canonicalizes to -> "/foo/bar"
+///
+/// `cwd_root` must be an absolute path. For initialization behavior similar to
+/// wasi-libc, use "/" as the `cwd_root`
+///
/// `alloc` must not be a temporary or leak-detecting allocator, since `std.os`
/// retains ownership of allocations internally and may never call free().
-pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void {
+pub fn initPreopensWasi(alloc: Allocator, cwd_root: []const u8) !void {
if (builtin.os.tag == .wasi) {
if (!builtin.link_libc) {
- if (wasi_cwd.preopens == null) {
- var preopen_list = PreopenList.init(alloc);
- try preopen_list.populate();
- wasi_cwd.preopens = preopen_list;
- }
- if (cwd_init) |cwd| {
- const preopen = wasi_cwd.preopens.?.findContaining(.{ .Dir = cwd });
- if (preopen) |po| {
- wasi_cwd.cwd_preopen = po.base;
- wasi_cwd.cwd = RelativePathWasi{
- .dir_fd = po.base.fd,
- .relative_path = po.relative_path,
- };
- } else {
- // No matching preopen found
- return error.FileNotFound;
- }
- }
+ var preopen_list = PreopenList.init(alloc);
+ errdefer preopen_list.deinit();
+ try preopen_list.populate(cwd_root);
+
+ var path_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
+ wasi_cwd.cwd = try path_alloc.allocator().dupe(u8, cwd_root);
+
+ if (wasi_cwd.preopens) |preopens| preopens.deinit();
+ wasi_cwd.preopens = preopen_list;
} else {
- if (cwd_init) |cwd| try chdir(cwd);
+ // wasi-libc defaults to an effective CWD root of "/"
+ if (!mem.eql(u8, cwd_root, "/")) return error.UnsupportedDirectory;
}
}
}
@@ -1477,69 +1477,22 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void {
///
/// For absolute paths, this automatically searches among available Preopens to find
/// a match. For relative paths, it uses the "emulated" CWD.
+/// Automatically looks up the correct Preopen corresponding to the provided path.
pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi {
- // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi
- // will fail if the relative path navigates outside of `dir_fd` using ".."
- return resolvePathAndGetWasiPreopen(path, null, out_buffer);
-}
-
-fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi {
var allocator = std.heap.FixedBufferAllocator.init(out_buffer);
var alloc = allocator.allocator();
- if (fs.path.isAbsolute(path) or wasi_cwd.cwd == null) {
- if (wasi_cwd.preopens == null) @panic("On WASI, `initPreopensWasi` must be called to initialize preopens " ++
- "before using any CWD-relative or absolute paths.\n");
-
- if (mem.startsWith(u8, path, "/preopens/fd/")) {
- // "/preopens/fd/" is a special prefix, which refers to a Preopen directly by fd
- const fd_start = "/preopens/fd/".len;
- const fd_end = mem.indexOfScalarPos(u8, path, fd_start, '/') orelse path.len;
- const fd = std.fmt.parseUnsigned(fd_t, path[fd_start..fd_end], 10) catch unreachable;
- const rel_path = if (path.len > fd_end + 1) path[fd_end + 1 ..] else ".";
-
- if (preopen) |p| p.* = wasi_cwd.preopens.?.findByFd(fd);
- return RelativePathWasi{
- .dir_fd = fd,
- .relative_path = alloc.dupe(u8, rel_path) catch return error.NameTooLong,
- };
- }
-
- // For any other absolute path, we need to lookup a containing Preopen
- const abs_path = fs.path.resolve(alloc, &.{ "/", path }) catch return error.NameTooLong;
- const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path });
+ const abs_path = fs.path.resolve(alloc, &.{ wasi_cwd.cwd, path }) catch return error.NameTooLong;
+ const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path });
- if (preopen_uri) |po| {
- if (preopen) |p| p.* = po.base;
- return RelativePathWasi{
- .dir_fd = po.base.fd,
- .relative_path = po.relative_path,
- };
- } else {
- // No matching preopen found
- return error.AccessDenied;
- }
- } else {
- const cwd = wasi_cwd.cwd.?;
-
- // If the path is empty or "." or "./", return CWD
- if (std.mem.eql(u8, path, ".") or std.mem.eql(u8, path, "./")) {
- return cwd;
- }
-
- // First resolve a combined path, where the "/" corresponds to `cwd.dir_fd`
- // not the true filesystem root
- const paths = &.{ "/", cwd.relative_path, path };
- const resolved_path = fs.path.resolve(alloc, paths) catch return error.NameTooLong;
-
- // Strip off the fake root to get the relative path w.r.t. `cwd.dir_fd`
- const resolved_relative_path = resolved_path[1..];
-
- if (preopen) |p| p.* = wasi_cwd.cwd_preopen;
+ if (preopen_uri) |po| {
return RelativePathWasi{
- .dir_fd = cwd.dir_fd,
- .relative_path = resolved_relative_path,
+ .dir_fd = po.base.fd,
+ .relative_path = po.relative_path,
};
+ } else {
+ // No matching preopen found
+ return error.AccessDenied;
}
}
@@ -1980,11 +1933,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
if (builtin.os.tag == .windows) {
return windows.GetCurrentDirectory(out_buffer);
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
- var buf: [MAX_PATH_BYTES]u8 = undefined;
- const path = realpathWasi(".", &buf) catch |err| switch (err) {
- error.NameTooLong => return error.NameTooLong,
- error.InvalidHandle => return error.CurrentWorkingDirectoryUnlinked,
- };
+ const path = wasi_cwd.cwd;
if (out_buffer.len < path.len) return error.NameTooLong;
std.mem.copy(u8, out_buffer, path);
return out_buffer[0..path.len];
@@ -2949,16 +2898,17 @@ pub const ChangeCurDirError = error{
/// `dir_path` is recommended to be a UTF-8 encoded string.
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
if (builtin.os.tag == .wasi and !builtin.link_libc) {
- var preopen: ?Preopen = null;
- const path = try resolvePathAndGetWasiPreopen(dir_path, &preopen, &wasi_cwd.path_buffer);
+ var buf: [MAX_PATH_BYTES]u8 = undefined;
+ var alloc = std.heap.FixedBufferAllocator.init(&buf);
+ const path = try fs.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path });
- const dirinfo = try fstatat(path.dir_fd, path.relative_path, 0);
+ const dirinfo = try fstatat(AT.FDCWD, path, 0);
if (dirinfo.filetype != .DIRECTORY) {
return error.NotDir;
}
- wasi_cwd.cwd_preopen = preopen;
- wasi_cwd.cwd = path;
+ var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
+ wasi_cwd.cwd = try cwd_alloc.allocator().dupe(u8, path);
return;
} else if (builtin.os.tag == .windows) {
var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
@@ -3010,29 +2960,15 @@ pub const FchdirError = error{
} || UnexpectedError;
pub fn fchdir(dirfd: fd_t) FchdirError!void {
- if (builtin.os.tag == .wasi) {
- // Check that this is a directory
- const dirinfo = fstatat(dirfd, ".", 0) catch unreachable;
- if (dirinfo.filetype != .DIRECTORY) {
- return error.NotDir;
- }
-
- wasi_cwd.cwd = .{
- .dir_fd = dirfd,
- .relative_path = ".",
- };
- wasi_cwd.cwd_preopen = null;
- } else {
- while (true) {
- switch (errno(system.fchdir(dirfd))) {
- .SUCCESS => return,
- .ACCES => return error.AccessDenied,
- .BADF => unreachable,
- .NOTDIR => return error.NotDir,
- .INTR => continue,
- .IO => return error.FileSystem,
- else => |err| return unexpectedErrno(err),
- }
+ while (true) {
+ switch (errno(system.fchdir(dirfd))) {
+ .SUCCESS => return,
+ .ACCES => return error.AccessDenied,
+ .BADF => unreachable,
+ .NOTDIR => return error.NotDir,
+ .INTR => continue,
+ .IO => return error.FileSystem,
+ else => |err| return unexpectedErrno(err),
}
}
}
@@ -5102,47 +5038,17 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
return realpathW(pathname_w.span(), out_buffer);
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
- return realpathWasi(pathname, out_buffer);
+ var alloc = std.heap.FixedBufferAllocator.init(out_buffer);
+
+ // NOTE: This emulation is incomplete. Symbolic links are not
+ // currently expanded during path canonicalization.
+ const paths = &.{ wasi_cwd.cwd, pathname };
+ return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong;
}
const pathname_c = try toPosixPath(pathname);
return realpathZ(&pathname_c, out_buffer);
}
-/// Return an emulated canonicalized absolute pathname on WASI.
-///
-/// NOTE: This emulation is incomplete. Symbolic links are not
-/// currently expanded during path canonicalization.
-fn realpathWasi(pathname: []const u8, out_buffer: []u8) ![]u8 {
- var alloc = std.heap.FixedBufferAllocator.init(out_buffer);
- if (fs.path.isAbsolute(pathname))
- return try fs.path.resolve(alloc.allocator(), &.{pathname}) catch error.NameTooLong;
- if (wasi_cwd.cwd) |cwd| {
- if (wasi_cwd.cwd_preopen) |po| {
- var base_cwd_dir = switch (po.@"type") {
- .Dir => |dir| dir,
- };
- const paths: [][]const u8 = if (fs.path.isAbsolute(base_cwd_dir)) blk: {
- break :blk &.{ base_cwd_dir, cwd.relative_path, pathname };
- } else blk: {
- // No absolute path is associated with this preopen, so
- // instead we use a special "/preopens/fd//" prefix
- var buf: [16]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
- std.fmt.formatInt(po.fd, 10, .lower, .{}, fbs.writer()) catch return error.NameTooLong;
- break :blk &.{ "/preopens/fd/", fbs.getWritten(), cwd.relative_path, pathname };
- };
-
- return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong;
- } else {
- // The CWD is not rooted to an existing Preopen,
- // so we have no way to know its absolute path
- return error.InvalidHandle;
- }
- } else {
- return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong;
- }
-}
-
/// Same as `realpath` except `pathname` is null-terminated.
pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
if (builtin.os.tag == .windows) {
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index 8598c7b3be3e..ae0d14ef7e5c 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -49,7 +49,7 @@ test "chdir smoke test" {
test "open smoke test" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
// TODO verify file attributes using `fstat`
@@ -104,7 +104,7 @@ test "open smoke test" {
test "openat smoke test" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
// TODO verify file attributes using `fstatat`
@@ -141,7 +141,7 @@ test "openat smoke test" {
test "symlink with relative paths" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
const cwd = fs.cwd();
cwd.deleteFile("file.txt") catch {};
@@ -197,7 +197,7 @@ test "link with relative paths" {
if (builtin.link_libc) {
return error.SkipZigTest;
} else {
- try os.initPreopensWasi(std.heap.page_allocator, ".");
+ try os.initPreopensWasi(std.heap.page_allocator, "/");
}
},
.linux, .solaris => {},
@@ -237,7 +237,7 @@ test "link with relative paths" {
test "linkat with different directories" {
switch (native_os) {
- .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."),
+ .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"),
.linux, .solaris => {},
else => return error.SkipZigTest,
}
@@ -898,7 +898,7 @@ test "POSIX file locking with fcntl" {
test "rename smoke test" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
@@ -955,7 +955,7 @@ test "rename smoke test" {
test "access smoke test" {
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
+ if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
var tmp = tmpDir(.{});
defer tmp.cleanup();
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
index 56cc86d769d1..004e2d0fa7af 100644
--- a/lib/std/testing.zig
+++ b/lib/std/testing.zig
@@ -380,7 +380,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
if (builtin.os.tag == .wasi and !builtin.link_libc) {
var preopens = std.fs.wasi.PreopenList.init(allocator);
defer preopens.deinit();
- preopens.populate() catch
+ preopens.populate(null) catch
@panic("unable to make tmp dir for testing: unable to populate preopens");
const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
@panic("unable to make tmp dir for testing: didn't find '.' in the preopens");