| author | |
| committer | |
| log | 7b090df6689909ba82ff46be4d21733f84f32c33 |
| tree | 105f0782e6ec8cd105402c689db814f8720c5858 |
| parent | 8f75823728d247bc11d916b7257d7eebc7be06de |
Two major changes here:
1. We store the CWD as a simple `[]const u8` and lookup Preopens for
every absolute or CWD-referenced file operation, based on the
Preopen with the longest match (i.e. most specific path)
2. Preorders are normalized to POSIX absolute paths at init time.
Behavior depends on the "cwd_root" parameter of `initPreopensWasi`:
	`cwd_root` is used for any Preopens that start with "."
	 For example:
"./foo/bar" - inits to -> "{cwd_root}/foo/bar"
"foo/bar" - inits to -> "/foo/bar"
	 "/foo/bar" - inits to -> "/foo/bar"
`cwd_root` must be an absolute path.
	Using "/" as `cwd_root` gives behavior similar to wasi-libc.7 files changed, 159 insertions(+), 197 deletions(-)
doc/langref.html.in+1-1| ... | @@ -11279,7 +11279,7 @@ pub fn main() !void { | ... | @@ -11279,7 +11279,7 @@ pub fn main() !void { |
| 11279 | var preopens = PreopenList.init(gpa); | 11279 | var preopens = PreopenList.init(gpa); |
| 11280 | defer preopens.deinit(); | 11280 | defer preopens.deinit(); |
| 11281 | 11281 | ||
| 11282 | try preopens.populate(); | 11282 | try preopens.populate(null); |
| 11283 | 11283 | ||
| 11284 | for (preopens.asSlice()) |preopen, i| { | 11284 | for (preopens.asSlice()) |preopen, i| { |
| 11285 | std.debug.print("{}: {}\n", .{ i, preopen }); | 11285 | std.debug.print("{}: {}\n", .{ i, preopen }); |
lib/std/fs/path.zig+4-4| ... | @@ -735,7 +735,7 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -735,7 +735,7 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { |
| 735 | 735 | ||
| 736 | test "resolve" { | 736 | test "resolve" { |
| 737 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 737 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 738 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 738 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 739 | 739 | ||
| 740 | const cwd = try process.getCwdAlloc(testing.allocator); | 740 | const cwd = try process.getCwdAlloc(testing.allocator); |
| 741 | defer testing.allocator.free(cwd); | 741 | defer testing.allocator.free(cwd); |
| ... | @@ -756,7 +756,7 @@ test "resolveWindows" { | ... | @@ -756,7 +756,7 @@ test "resolveWindows" { |
| 756 | return error.SkipZigTest; | 756 | return error.SkipZigTest; |
| 757 | } | 757 | } |
| 758 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 758 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 759 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 759 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 760 | if (native_os == .windows) { | 760 | if (native_os == .windows) { |
| 761 | const cwd = try process.getCwdAlloc(testing.allocator); | 761 | const cwd = try process.getCwdAlloc(testing.allocator); |
| 762 | defer testing.allocator.free(cwd); | 762 | defer testing.allocator.free(cwd); |
| ... | @@ -802,7 +802,7 @@ test "resolveWindows" { | ... | @@ -802,7 +802,7 @@ test "resolveWindows" { |
| 802 | 802 | ||
| 803 | test "resolvePosix" { | 803 | test "resolvePosix" { |
| 804 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 804 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 805 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 805 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 806 | 806 | ||
| 807 | try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); | 807 | try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); |
| 808 | try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); | 808 | try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); |
| ... | @@ -1216,7 +1216,7 @@ test "relative" { | ... | @@ -1216,7 +1216,7 @@ test "relative" { |
| 1216 | return error.SkipZigTest; | 1216 | return error.SkipZigTest; |
| 1217 | } | 1217 | } |
| 1218 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 1218 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 1219 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 1219 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 1220 | 1220 | ||
| 1221 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | 1221 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 1222 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | 1222 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
lib/std/fs/test.zig+7-7| ... | @@ -47,7 +47,7 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo | ... | @@ -47,7 +47,7 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo |
| 47 | 47 | ||
| 48 | test "accessAbsolute" { | 48 | test "accessAbsolute" { |
| 49 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 49 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 50 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 50 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 51 | 51 | ||
| 52 | var tmp = tmpDir(.{}); | 52 | var tmp = tmpDir(.{}); |
| 53 | defer tmp.cleanup(); | 53 | defer tmp.cleanup(); |
| ... | @@ -66,7 +66,7 @@ test "accessAbsolute" { | ... | @@ -66,7 +66,7 @@ test "accessAbsolute" { |
| 66 | 66 | ||
| 67 | test "openDirAbsolute" { | 67 | test "openDirAbsolute" { |
| 68 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 68 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 69 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 69 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 70 | 70 | ||
| 71 | var tmp = tmpDir(.{}); | 71 | var tmp = tmpDir(.{}); |
| 72 | defer tmp.cleanup(); | 72 | defer tmp.cleanup(); |
| ... | @@ -103,7 +103,7 @@ test "openDir cwd parent .." { | ... | @@ -103,7 +103,7 @@ test "openDir cwd parent .." { |
| 103 | 103 | ||
| 104 | test "readLinkAbsolute" { | 104 | test "readLinkAbsolute" { |
| 105 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 105 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 106 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 106 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 107 | 107 | ||
| 108 | var tmp = tmpDir(.{}); | 108 | var tmp = tmpDir(.{}); |
| 109 | defer tmp.cleanup(); | 109 | defer tmp.cleanup(); |
| ... | @@ -535,7 +535,7 @@ test "rename" { | ... | @@ -535,7 +535,7 @@ test "rename" { |
| 535 | 535 | ||
| 536 | test "renameAbsolute" { | 536 | test "renameAbsolute" { |
| 537 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 537 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 538 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 538 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 539 | 539 | ||
| 540 | var tmp_dir = tmpDir(.{}); | 540 | var tmp_dir = tmpDir(.{}); |
| 541 | defer tmp_dir.cleanup(); | 541 | defer tmp_dir.cleanup(); |
| ... | @@ -980,7 +980,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { | ... | @@ -980,7 +980,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 980 | 980 | ||
| 981 | test "walker" { | 981 | test "walker" { |
| 982 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 982 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 983 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 983 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 984 | 984 | ||
| 985 | var tmp = tmpDir(.{ .iterate = true }); | 985 | var tmp = tmpDir(.{ .iterate = true }); |
| 986 | defer tmp.cleanup(); | 986 | defer tmp.cleanup(); |
| ... | @@ -1031,7 +1031,7 @@ test "walker" { | ... | @@ -1031,7 +1031,7 @@ test "walker" { |
| 1031 | 1031 | ||
| 1032 | test ". and .. in fs.Dir functions" { | 1032 | test ". and .. in fs.Dir functions" { |
| 1033 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 1033 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 1034 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 1034 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 1035 | 1035 | ||
| 1036 | var tmp = tmpDir(.{}); | 1036 | var tmp = tmpDir(.{}); |
| 1037 | defer tmp.cleanup(); | 1037 | defer tmp.cleanup(); |
| ... | @@ -1060,7 +1060,7 @@ test ". and .. in fs.Dir functions" { | ... | @@ -1060,7 +1060,7 @@ test ". and .. in fs.Dir functions" { |
| 1060 | 1060 | ||
| 1061 | test ". and .. in absolute functions" { | 1061 | test ". and .. in absolute functions" { |
| 1062 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; | 1062 | if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 1063 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 1063 | if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 1064 | 1064 | ||
| 1065 | var tmp = tmpDir(.{}); | 1065 | var tmp = tmpDir(.{}); |
| 1066 | defer tmp.cleanup(); | 1066 | defer tmp.cleanup(); |
lib/std/fs/wasi.zig+84-28| ... | @@ -3,6 +3,8 @@ const builtin = @import("builtin"); | ... | @@ -3,6 +3,8 @@ const builtin = @import("builtin"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const math = std.math; | 5 | const math = std.math; |
| 6 | const fs = std.fs; | ||
| 7 | const assert = std.debug.assert; | ||
| 6 | const Allocator = mem.Allocator; | 8 | const Allocator = mem.Allocator; |
| 7 | const wasi = std.os.wasi; | 9 | const wasi = std.os.wasi; |
| 8 | const fd_t = wasi.fd_t; | 10 | const fd_t = wasi.fd_t; |
| ... | @@ -34,16 +36,27 @@ pub const PreopenType = union(PreopenTypeTag) { | ... | @@ -34,16 +36,27 @@ pub const PreopenType = union(PreopenTypeTag) { |
| 34 | 36 | ||
| 35 | // Checks whether `other` refers to a subdirectory of `self` and, if so, | 37 | // Checks whether `other` refers to a subdirectory of `self` and, if so, |
| 36 | // returns the relative path to `other` from `self` | 38 | // returns the relative path to `other` from `self` |
| 39 | // | ||
| 40 | // Expects `other` to be a canonical path, not containing "." or ".." | ||
| 37 | pub fn getRelativePath(self: Self, other: PreopenType) ?[]const u8 { | 41 | pub fn getRelativePath(self: Self, other: PreopenType) ?[]const u8 { |
| 38 | if (std.meta.activeTag(self) != std.meta.activeTag(other)) return null; | 42 | if (std.meta.activeTag(self) != std.meta.activeTag(other)) return null; |
| 39 | 43 | ||
| 40 | switch (self) { | 44 | switch (self) { |
| 41 | PreopenTypeTag.Dir => |this_path| { | 45 | PreopenTypeTag.Dir => |self_path| { |
| 42 | const other_path = other.Dir; | 46 | const other_path = other.Dir; |
| 43 | if (mem.indexOfDiff(u8, this_path, other_path)) |index| { | 47 | if (mem.indexOfDiff(u8, self_path, other_path)) |index| { |
| 44 | if (index < this_path.len) return null; | 48 | if (index < self_path.len) return null; |
| 49 | } | ||
| 50 | |||
| 51 | const rel_path = other_path[self_path.len..]; | ||
| 52 | if (rel_path.len == 0) { | ||
| 53 | return rel_path; | ||
| 54 | } else if (rel_path[0] == '/') { | ||
| 55 | return rel_path[1..]; | ||
| 56 | } else { | ||
| 57 | if (self_path[self_path.len - 1] != '/') return null; | ||
| 58 | return rel_path; | ||
| 45 | } | 59 | } |
| 46 | return other_path[this_path.len..]; | ||
| 47 | }, | 60 | }, |
| 48 | } | 61 | } |
| 49 | } | 62 | } |
| ... | @@ -130,7 +143,22 @@ pub const PreopenList = struct { | ... | @@ -130,7 +143,22 @@ pub const PreopenList = struct { |
| 130 | /// the preopen list still contains all valid preopened file descriptors that are valid | 143 | /// the preopen list still contains all valid preopened file descriptors that are valid |
| 131 | /// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally, | 144 | /// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally, |
| 132 | /// `deinit` still must be called! | 145 | /// `deinit` still must be called! |
| 133 | pub fn populate(self: *Self) Error!void { | 146 | /// |
| 147 | /// Usage of `cwd_root`: | ||
| 148 | /// If provided, `cwd_root` is inserted as prefix for any Preopens that | ||
| 149 | /// begin with "." and all paths are normalized as POSIX-style absolute | ||
| 150 | /// paths. `cwd_root` must be an absolute path. | ||
| 151 | /// | ||
| 152 | /// For example: | ||
| 153 | /// "./foo/bar" -> "{cwd_root}/foo/bar" | ||
| 154 | /// "foo/bar" -> "/foo/bar" | ||
| 155 | /// "/foo/bar" -> "/foo/bar" | ||
| 156 | /// | ||
| 157 | /// If `cwd_root` is not provided, all preopen directories are unmodified. | ||
| 158 | /// | ||
| 159 | pub fn populate(self: *Self, cwd_root: ?[]const u8) Error!void { | ||
| 160 | if (cwd_root) |root| assert(fs.path.isAbsolute(root)); | ||
| 161 | |||
| 134 | // Clear contents if we're being called again | 162 | // Clear contents if we're being called again |
| 135 | for (self.toOwnedSlice()) |preopen| { | 163 | for (self.toOwnedSlice()) |preopen| { |
| 136 | switch (preopen.@"type") { | 164 | switch (preopen.@"type") { |
| ... | @@ -140,6 +168,7 @@ pub const PreopenList = struct { | ... | @@ -140,6 +168,7 @@ pub const PreopenList = struct { |
| 140 | errdefer self.deinit(); | 168 | errdefer self.deinit(); |
| 141 | var fd: fd_t = 3; // start fd has to be beyond stdio fds | 169 | var fd: fd_t = 3; // start fd has to be beyond stdio fds |
| 142 | 170 | ||
| 171 | var path_buf: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 143 | while (true) { | 172 | while (true) { |
| 144 | var buf: prestat_t = undefined; | 173 | var buf: prestat_t = undefined; |
| 145 | switch (wasi.fd_prestat_get(fd, &buf)) { | 174 | switch (wasi.fd_prestat_get(fd, &buf)) { |
| ... | @@ -156,14 +185,34 @@ pub const PreopenList = struct { | ... | @@ -156,14 +185,34 @@ pub const PreopenList = struct { |
| 156 | else => |err| return os.unexpectedErrno(err), | 185 | else => |err| return os.unexpectedErrno(err), |
| 157 | } | 186 | } |
| 158 | const preopen_len = buf.u.dir.pr_name_len; | 187 | const preopen_len = buf.u.dir.pr_name_len; |
| 159 | const path_buf = try self.buffer.allocator.alloc(u8, preopen_len); | 188 | |
| 160 | mem.set(u8, path_buf, 0); | 189 | mem.set(u8, path_buf[0..preopen_len], 0); |
| 161 | switch (wasi.fd_prestat_dir_name(fd, path_buf.ptr, preopen_len)) { | 190 | switch (wasi.fd_prestat_dir_name(fd, &path_buf, preopen_len)) { |
| 162 | .SUCCESS => {}, | 191 | .SUCCESS => {}, |
| 163 | else => |err| return os.unexpectedErrno(err), | 192 | else => |err| return os.unexpectedErrno(err), |
| 164 | } | 193 | } |
| 165 | 194 | ||
| 166 | const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf }); | 195 | // Unfortunately, WASI runtimes (e.g. wasmer) are not consistent about whether the |
| 196 | // NULL sentinel is included in the reported Preopen name_len | ||
| 197 | const raw_path = if (path_buf[preopen_len - 1] == 0) blk: { | ||
| 198 | break :blk path_buf[0 .. preopen_len - 1]; | ||
| 199 | } else path_buf[0..preopen_len]; | ||
| 200 | |||
| 201 | // If we were provided a CWD root to resolve against, we try to treat Preopen dirs as | ||
| 202 | // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." | ||
| 203 | const path = if (cwd_root) |cwd| blk: { | ||
| 204 | const resolve_paths: [][]const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; | ||
| 205 | break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) { | ||
| 206 | error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried | ||
| 207 | else => |e| return e, | ||
| 208 | }; | ||
| 209 | } else blk: { | ||
| 210 | // If we were provided no CWD root, we preserve the preopen dir without resolving | ||
| 211 | break :blk try self.buffer.allocator.dupe(u8, raw_path); | ||
| 212 | }; | ||
| 213 | errdefer self.buffer.allocator.free(path); | ||
| 214 | const preopen = Preopen.new(fd, .{ .Dir = path }); | ||
| 215 | |||
| 167 | try self.buffer.append(preopen); | 216 | try self.buffer.append(preopen); |
| 168 | fd = try math.add(fd_t, fd, 1); | 217 | fd = try math.add(fd_t, fd, 1); |
| 169 | } | 218 | } |
| ... | @@ -171,27 +220,22 @@ pub const PreopenList = struct { | ... | @@ -171,27 +220,22 @@ pub const PreopenList = struct { |
| 171 | 220 | ||
| 172 | /// Find a preopen which includes access to `preopen_type`. | 221 | /// Find a preopen which includes access to `preopen_type`. |
| 173 | /// | 222 | /// |
| 174 | /// If the preopen exists, `relative_path` is updated to point to the relative | 223 | /// If multiple preopens match the provided resource, the most specific |
| 175 | /// portion of `preopen_type` and the matching Preopen is returned. If multiple | 224 | /// match is returned. More recent preopens take priority, as well. |
| 176 | /// preopens match the provided resource, the most recent one is used. | ||
| 177 | pub fn findContaining(self: Self, preopen_type: PreopenType) ?PreopenUri { | 225 | pub fn findContaining(self: Self, preopen_type: PreopenType) ?PreopenUri { |
| 178 | // Search in reverse, so that most recently added preopens take precedence | 226 | var best_match: ?PreopenUri = null; |
| 179 | var k: usize = self.buffer.items.len; | 227 | |
| 180 | while (k > 0) { | 228 | for (self.buffer.items) |preopen| { |
| 181 | k -= 1; | 229 | if (preopen.@"type".getRelativePath(preopen_type)) |rel_path| { |
| 182 | 230 | if (best_match == null or rel_path.len <= best_match.?.relative_path.len) { | |
| 183 | const preopen = self.buffer.items[k]; | 231 | best_match = PreopenUri{ |
| 184 | if (preopen.@"type".getRelativePath(preopen_type)) |rel_path_orig| { | 232 | .base = preopen, |
| 185 | var rel_path = rel_path_orig; | 233 | .relative_path = if (rel_path.len == 0) "." else rel_path, |
| 186 | while (rel_path.len > 0 and rel_path[0] == '/') rel_path = rel_path[1..]; | 234 | }; |
| 187 | 235 | } | |
| 188 | return PreopenUri{ | ||
| 189 | .base = preopen, | ||
| 190 | .relative_path = if (rel_path.len == 0) "." else rel_path, | ||
| 191 | }; | ||
| 192 | } | 236 | } |
| 193 | } | 237 | } |
| 194 | return null; | 238 | return best_match; |
| 195 | } | 239 | } |
| 196 | 240 | ||
| 197 | /// Find preopen by fd. If the preopen exists, return it. | 241 | /// Find preopen by fd. If the preopen exists, return it. |
| ... | @@ -233,8 +277,20 @@ test "extracting WASI preopens" { | ... | @@ -233,8 +277,20 @@ test "extracting WASI preopens" { |
| 233 | var preopens = PreopenList.init(std.testing.allocator); | 277 | var preopens = PreopenList.init(std.testing.allocator); |
| 234 | defer preopens.deinit(); | 278 | defer preopens.deinit(); |
| 235 | 279 | ||
| 236 | try preopens.populate(); | 280 | try preopens.populate(null); |
| 237 | 281 | ||
| 238 | const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable; | 282 | const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable; |
| 239 | try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." })); | 283 | try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." })); |
| 284 | |||
| 285 | const po_type1 = PreopenType{ .Dir = "/" }; | ||
| 286 | try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/" }).?, "")); | ||
| 287 | try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/test/foobar" }).?, "test/foobar")); | ||
| 288 | |||
| 289 | const po_type2 = PreopenType{ .Dir = "/test/foo" }; | ||
| 290 | try std.testing.expect(po_type2.getRelativePath(.{ .Dir = "/test/foobar" }) == null); | ||
| 291 | |||
| 292 | const po_type3 = PreopenType{ .Dir = "/test" }; | ||
| 293 | try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test" }).?, "")); | ||
| 294 | try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/" }).?, "")); | ||
| 295 | try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/foo/bar" }).?, "foo/bar")); | ||
| 240 | } | 296 | } |
lib/std/os.zig+55-149| ... | @@ -1431,10 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { | ... | @@ -1431,10 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { |
| 1431 | preopens: ?PreopenList = null, | 1431 | preopens: ?PreopenList = null, |
| 1432 | // Memory buffer for storing the relative portion of the CWD | 1432 | // Memory buffer for storing the relative portion of the CWD |
| 1433 | path_buffer: [MAX_PATH_BYTES]u8 = undefined, | 1433 | path_buffer: [MAX_PATH_BYTES]u8 = undefined, |
| 1434 | // Current Working Directory, stored as an fd_t and a relative path | 1434 | // The absolute path associated with the current working directory |
| 1435 | cwd: ?RelativePathWasi = null, | 1435 | cwd: []const u8 = "/", |
| 1436 | // Preopen associated with `cwd`, if any | ||
| 1437 | cwd_preopen: ?Preopen = null, | ||
| 1438 | }{} else undefined; | 1436 | }{} else undefined; |
| 1439 | 1437 | ||
| 1440 | /// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`. | 1438 | /// 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 { | ... | @@ -1444,31 +1442,33 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { |
| 1444 | /// This must be called before using any relative or absolute paths with `std.os` | 1442 | /// This must be called before using any relative or absolute paths with `std.os` |
| 1445 | /// functions, if you are on WASI without linking libc. | 1443 | /// functions, if you are on WASI without linking libc. |
| 1446 | /// | 1444 | /// |
| 1445 | /// The current working directory is initialized to `cwd_root`, and `cwd_root` | ||
| 1446 | /// is inserted as a prefix for any Preopens whose dir begins with "." | ||
| 1447 | /// For example: | ||
| 1448 | /// "./foo/bar" - canonicalizes to -> "{cwd_root}/foo/bar" | ||
| 1449 | /// "foo/bar" - canonicalizes to -> "/foo/bar" | ||
| 1450 | /// "/foo/bar" - canonicalizes to -> "/foo/bar" | ||
| 1451 | /// | ||
| 1452 | /// `cwd_root` must be an absolute path. For initialization behavior similar to | ||
| 1453 | /// wasi-libc, use "/" as the `cwd_root` | ||
| 1454 | /// | ||
| 1447 | /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` | 1455 | /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` |
| 1448 | /// retains ownership of allocations internally and may never call free(). | 1456 | /// retains ownership of allocations internally and may never call free(). |
| 1449 | pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { | 1457 | pub fn initPreopensWasi(alloc: Allocator, cwd_root: []const u8) !void { |
| 1450 | if (builtin.os.tag == .wasi) { | 1458 | if (builtin.os.tag == .wasi) { |
| 1451 | if (!builtin.link_libc) { | 1459 | if (!builtin.link_libc) { |
| 1452 | if (wasi_cwd.preopens == null) { | 1460 | var preopen_list = PreopenList.init(alloc); |
| 1453 | var preopen_list = PreopenList.init(alloc); | 1461 | errdefer preopen_list.deinit(); |
| 1454 | try preopen_list.populate(); | 1462 | try preopen_list.populate(cwd_root); |
| 1455 | wasi_cwd.preopens = preopen_list; | 1463 | |
| 1456 | } | 1464 | var path_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer); |
| 1457 | if (cwd_init) |cwd| { | 1465 | wasi_cwd.cwd = try path_alloc.allocator().dupe(u8, cwd_root); |
| 1458 | const preopen = wasi_cwd.preopens.?.findContaining(.{ .Dir = cwd }); | 1466 | |
| 1459 | if (preopen) |po| { | 1467 | if (wasi_cwd.preopens) |preopens| preopens.deinit(); |
| 1460 | wasi_cwd.cwd_preopen = po.base; | 1468 | wasi_cwd.preopens = preopen_list; |
| 1461 | wasi_cwd.cwd = RelativePathWasi{ | ||
| 1462 | .dir_fd = po.base.fd, | ||
| 1463 | .relative_path = po.relative_path, | ||
| 1464 | }; | ||
| 1465 | } else { | ||
| 1466 | // No matching preopen found | ||
| 1467 | return error.FileNotFound; | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | } else { | 1469 | } else { |
| 1471 | if (cwd_init) |cwd| try chdir(cwd); | 1470 | // wasi-libc defaults to an effective CWD root of "/" |
| 1471 | if (!mem.eql(u8, cwd_root, "/")) return error.UnsupportedDirectory; | ||
| 1472 | } | 1472 | } |
| 1473 | } | 1473 | } |
| 1474 | } | 1474 | } |
| ... | @@ -1477,69 +1477,22 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { | ... | @@ -1477,69 +1477,22 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { |
| 1477 | /// | 1477 | /// |
| 1478 | /// For absolute paths, this automatically searches among available Preopens to find | 1478 | /// For absolute paths, this automatically searches among available Preopens to find |
| 1479 | /// a match. For relative paths, it uses the "emulated" CWD. | 1479 | /// a match. For relative paths, it uses the "emulated" CWD. |
| 1480 | /// Automatically looks up the correct Preopen corresponding to the provided path. | ||
| 1480 | pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { | 1481 | pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { |
| 1481 | // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi | ||
| 1482 | // will fail if the relative path navigates outside of `dir_fd` using ".." | ||
| 1483 | return resolvePathAndGetWasiPreopen(path, null, out_buffer); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { | ||
| 1487 | var allocator = std.heap.FixedBufferAllocator.init(out_buffer); | 1482 | var allocator = std.heap.FixedBufferAllocator.init(out_buffer); |
| 1488 | var alloc = allocator.allocator(); | 1483 | var alloc = allocator.allocator(); |
| 1489 | 1484 | ||
| 1490 | if (fs.path.isAbsolute(path) or wasi_cwd.cwd == null) { | 1485 | const abs_path = fs.path.resolve(alloc, &.{ wasi_cwd.cwd, path }) catch return error.NameTooLong; |
| 1491 | if (wasi_cwd.preopens == null) @panic("On WASI, `initPreopensWasi` must be called to initialize preopens " ++ | 1486 | const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path }); |
| 1492 | "before using any CWD-relative or absolute paths.\n"); | ||
| 1493 | |||
| 1494 | if (mem.startsWith(u8, path, "/preopens/fd/")) { | ||
| 1495 | // "/preopens/fd/<N>" is a special prefix, which refers to a Preopen directly by fd | ||
| 1496 | const fd_start = "/preopens/fd/".len; | ||
| 1497 | const fd_end = mem.indexOfScalarPos(u8, path, fd_start, '/') orelse path.len; | ||
| 1498 | const fd = std.fmt.parseUnsigned(fd_t, path[fd_start..fd_end], 10) catch unreachable; | ||
| 1499 | const rel_path = if (path.len > fd_end + 1) path[fd_end + 1 ..] else "."; | ||
| 1500 | |||
| 1501 | if (preopen) |p| p.* = wasi_cwd.preopens.?.findByFd(fd); | ||
| 1502 | return RelativePathWasi{ | ||
| 1503 | .dir_fd = fd, | ||
| 1504 | .relative_path = alloc.dupe(u8, rel_path) catch return error.NameTooLong, | ||
| 1505 | }; | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | // For any other absolute path, we need to lookup a containing Preopen | ||
| 1509 | const abs_path = fs.path.resolve(alloc, &.{ "/", path }) catch return error.NameTooLong; | ||
| 1510 | const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path }); | ||
| 1511 | 1487 | ||
| 1512 | if (preopen_uri) |po| { | 1488 | if (preopen_uri) |po| { |
| 1513 | if (preopen) |p| p.* = po.base; | ||
| 1514 | return RelativePathWasi{ | ||
| 1515 | .dir_fd = po.base.fd, | ||
| 1516 | .relative_path = po.relative_path, | ||
| 1517 | }; | ||
| 1518 | } else { | ||
| 1519 | // No matching preopen found | ||
| 1520 | return error.AccessDenied; | ||
| 1521 | } | ||
| 1522 | } else { | ||
| 1523 | const cwd = wasi_cwd.cwd.?; | ||
| 1524 | |||
| 1525 | // If the path is empty or "." or "./", return CWD | ||
| 1526 | if (std.mem.eql(u8, path, ".") or std.mem.eql(u8, path, "./")) { | ||
| 1527 | return cwd; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | // First resolve a combined path, where the "/" corresponds to `cwd.dir_fd` | ||
| 1531 | // not the true filesystem root | ||
| 1532 | const paths = &.{ "/", cwd.relative_path, path }; | ||
| 1533 | const resolved_path = fs.path.resolve(alloc, paths) catch return error.NameTooLong; | ||
| 1534 | |||
| 1535 | // Strip off the fake root to get the relative path w.r.t. `cwd.dir_fd` | ||
| 1536 | const resolved_relative_path = resolved_path[1..]; | ||
| 1537 | |||
| 1538 | if (preopen) |p| p.* = wasi_cwd.cwd_preopen; | ||
| 1539 | return RelativePathWasi{ | 1489 | return RelativePathWasi{ |
| 1540 | .dir_fd = cwd.dir_fd, | 1490 | .dir_fd = po.base.fd, |
| 1541 | .relative_path = resolved_relative_path, | 1491 | .relative_path = po.relative_path, |
| 1542 | }; | 1492 | }; |
| 1493 | } else { | ||
| 1494 | // No matching preopen found | ||
| 1495 | return error.AccessDenied; | ||
| 1543 | } | 1496 | } |
| 1544 | } | 1497 | } |
| 1545 | 1498 | ||
| ... | @@ -1980,11 +1933,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | ... | @@ -1980,11 +1933,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { |
| 1980 | if (builtin.os.tag == .windows) { | 1933 | if (builtin.os.tag == .windows) { |
| 1981 | return windows.GetCurrentDirectory(out_buffer); | 1934 | return windows.GetCurrentDirectory(out_buffer); |
| 1982 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 1935 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 1983 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 1936 | const path = wasi_cwd.cwd; |
| 1984 | const path = realpathWasi(".", &buf) catch |err| switch (err) { | ||
| 1985 | error.NameTooLong => return error.NameTooLong, | ||
| 1986 | error.InvalidHandle => return error.CurrentWorkingDirectoryUnlinked, | ||
| 1987 | }; | ||
| 1988 | if (out_buffer.len < path.len) return error.NameTooLong; | 1937 | if (out_buffer.len < path.len) return error.NameTooLong; |
| 1989 | std.mem.copy(u8, out_buffer, path); | 1938 | std.mem.copy(u8, out_buffer, path); |
| 1990 | return out_buffer[0..path.len]; | 1939 | return out_buffer[0..path.len]; |
| ... | @@ -2949,16 +2898,17 @@ pub const ChangeCurDirError = error{ | ... | @@ -2949,16 +2898,17 @@ pub const ChangeCurDirError = error{ |
| 2949 | /// `dir_path` is recommended to be a UTF-8 encoded string. | 2898 | /// `dir_path` is recommended to be a UTF-8 encoded string. |
| 2950 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | 2899 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 2951 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2900 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2952 | var preopen: ?Preopen = null; | 2901 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 2953 | const path = try resolvePathAndGetWasiPreopen(dir_path, &preopen, &wasi_cwd.path_buffer); | 2902 | var alloc = std.heap.FixedBufferAllocator.init(&buf); |
| 2903 | const path = try fs.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path }); | ||
| 2954 | 2904 | ||
| 2955 | const dirinfo = try fstatat(path.dir_fd, path.relative_path, 0); | 2905 | const dirinfo = try fstatat(AT.FDCWD, path, 0); |
| 2956 | if (dirinfo.filetype != .DIRECTORY) { | 2906 | if (dirinfo.filetype != .DIRECTORY) { |
| 2957 | return error.NotDir; | 2907 | return error.NotDir; |
| 2958 | } | 2908 | } |
| 2959 | 2909 | ||
| 2960 | wasi_cwd.cwd_preopen = preopen; | 2910 | var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer); |
| 2961 | wasi_cwd.cwd = path; | 2911 | wasi_cwd.cwd = try cwd_alloc.allocator().dupe(u8, path); |
| 2962 | return; | 2912 | return; |
| 2963 | } else if (builtin.os.tag == .windows) { | 2913 | } else if (builtin.os.tag == .windows) { |
| 2964 | var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; | 2914 | var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; |
| ... | @@ -3010,29 +2960,15 @@ pub const FchdirError = error{ | ... | @@ -3010,29 +2960,15 @@ pub const FchdirError = error{ |
| 3010 | } || UnexpectedError; | 2960 | } || UnexpectedError; |
| 3011 | 2961 | ||
| 3012 | pub fn fchdir(dirfd: fd_t) FchdirError!void { | 2962 | pub fn fchdir(dirfd: fd_t) FchdirError!void { |
| 3013 | if (builtin.os.tag == .wasi) { | 2963 | while (true) { |
| 3014 | // Check that this is a directory | 2964 | switch (errno(system.fchdir(dirfd))) { |
| 3015 | const dirinfo = fstatat(dirfd, ".", 0) catch unreachable; | 2965 | .SUCCESS => return, |
| 3016 | if (dirinfo.filetype != .DIRECTORY) { | 2966 | .ACCES => return error.AccessDenied, |
| 3017 | return error.NotDir; | 2967 | .BADF => unreachable, |
| 3018 | } | 2968 | .NOTDIR => return error.NotDir, |
| 3019 | 2969 | .INTR => continue, | |
| 3020 | wasi_cwd.cwd = .{ | 2970 | .IO => return error.FileSystem, |
| 3021 | .dir_fd = dirfd, | 2971 | else => |err| return unexpectedErrno(err), |
| 3022 | .relative_path = ".", | ||
| 3023 | }; | ||
| 3024 | wasi_cwd.cwd_preopen = null; | ||
| 3025 | } else { | ||
| 3026 | while (true) { | ||
| 3027 | switch (errno(system.fchdir(dirfd))) { | ||
| 3028 | .SUCCESS => return, | ||
| 3029 | .ACCES => return error.AccessDenied, | ||
| 3030 | .BADF => unreachable, | ||
| 3031 | .NOTDIR => return error.NotDir, | ||
| 3032 | .INTR => continue, | ||
| 3033 | .IO => return error.FileSystem, | ||
| 3034 | else => |err| return unexpectedErrno(err), | ||
| 3035 | } | ||
| 3036 | } | 2972 | } |
| 3037 | } | 2973 | } |
| 3038 | } | 2974 | } |
| ... | @@ -5102,47 +5038,17 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE | ... | @@ -5102,47 +5038,17 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE |
| 5102 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); | 5038 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); |
| 5103 | return realpathW(pathname_w.span(), out_buffer); | 5039 | return realpathW(pathname_w.span(), out_buffer); |
| 5104 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 5040 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 5105 | return realpathWasi(pathname, out_buffer); | 5041 | var alloc = std.heap.FixedBufferAllocator.init(out_buffer); |
| 5042 | |||
| 5043 | // NOTE: This emulation is incomplete. Symbolic links are not | ||
| 5044 | // currently expanded during path canonicalization. | ||
| 5045 | const paths = &.{ wasi_cwd.cwd, pathname }; | ||
| 5046 | return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong; | ||
| 5106 | } | 5047 | } |
| 5107 | const pathname_c = try toPosixPath(pathname); | 5048 | const pathname_c = try toPosixPath(pathname); |
| 5108 | return realpathZ(&pathname_c, out_buffer); | 5049 | return realpathZ(&pathname_c, out_buffer); |
| 5109 | } | 5050 | } |
| 5110 | 5051 | ||
| 5111 | /// Return an emulated canonicalized absolute pathname on WASI. | ||
| 5112 | /// | ||
| 5113 | /// NOTE: This emulation is incomplete. Symbolic links are not | ||
| 5114 | /// currently expanded during path canonicalization. | ||
| 5115 | fn realpathWasi(pathname: []const u8, out_buffer: []u8) ![]u8 { | ||
| 5116 | var alloc = std.heap.FixedBufferAllocator.init(out_buffer); | ||
| 5117 | if (fs.path.isAbsolute(pathname)) | ||
| 5118 | return try fs.path.resolve(alloc.allocator(), &.{pathname}) catch error.NameTooLong; | ||
| 5119 | if (wasi_cwd.cwd) |cwd| { | ||
| 5120 | if (wasi_cwd.cwd_preopen) |po| { | ||
| 5121 | var base_cwd_dir = switch (po.@"type") { | ||
| 5122 | .Dir => |dir| dir, | ||
| 5123 | }; | ||
| 5124 | const paths: [][]const u8 = if (fs.path.isAbsolute(base_cwd_dir)) blk: { | ||
| 5125 | break :blk &.{ base_cwd_dir, cwd.relative_path, pathname }; | ||
| 5126 | } else blk: { | ||
| 5127 | // No absolute path is associated with this preopen, so | ||
| 5128 | // instead we use a special "/preopens/fd/<N>/" prefix | ||
| 5129 | var buf: [16]u8 = undefined; | ||
| 5130 | var fbs = std.io.fixedBufferStream(&buf); | ||
| 5131 | std.fmt.formatInt(po.fd, 10, .lower, .{}, fbs.writer()) catch return error.NameTooLong; | ||
| 5132 | break :blk &.{ "/preopens/fd/", fbs.getWritten(), cwd.relative_path, pathname }; | ||
| 5133 | }; | ||
| 5134 | |||
| 5135 | return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong; | ||
| 5136 | } else { | ||
| 5137 | // The CWD is not rooted to an existing Preopen, | ||
| 5138 | // so we have no way to know its absolute path | ||
| 5139 | return error.InvalidHandle; | ||
| 5140 | } | ||
| 5141 | } else { | ||
| 5142 | return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong; | ||
| 5143 | } | ||
| 5144 | } | ||
| 5145 | |||
| 5146 | /// Same as `realpath` except `pathname` is null-terminated. | 5052 | /// Same as `realpath` except `pathname` is null-terminated. |
| 5147 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { | 5053 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 5148 | if (builtin.os.tag == .windows) { | 5054 | if (builtin.os.tag == .windows) { |
lib/std/os/test.zig+7-7| ... | @@ -49,7 +49,7 @@ test "chdir smoke test" { | ... | @@ -49,7 +49,7 @@ test "chdir smoke test" { |
| 49 | 49 | ||
| 50 | test "open smoke test" { | 50 | test "open smoke test" { |
| 51 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 51 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 52 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 52 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 53 | 53 | ||
| 54 | // TODO verify file attributes using `fstat` | 54 | // TODO verify file attributes using `fstat` |
| 55 | 55 | ||
| ... | @@ -104,7 +104,7 @@ test "open smoke test" { | ... | @@ -104,7 +104,7 @@ test "open smoke test" { |
| 104 | 104 | ||
| 105 | test "openat smoke test" { | 105 | test "openat smoke test" { |
| 106 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 106 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 107 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 107 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 108 | 108 | ||
| 109 | // TODO verify file attributes using `fstatat` | 109 | // TODO verify file attributes using `fstatat` |
| 110 | 110 | ||
| ... | @@ -141,7 +141,7 @@ test "openat smoke test" { | ... | @@ -141,7 +141,7 @@ test "openat smoke test" { |
| 141 | 141 | ||
| 142 | test "symlink with relative paths" { | 142 | test "symlink with relative paths" { |
| 143 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 143 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 144 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 144 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 145 | 145 | ||
| 146 | const cwd = fs.cwd(); | 146 | const cwd = fs.cwd(); |
| 147 | cwd.deleteFile("file.txt") catch {}; | 147 | cwd.deleteFile("file.txt") catch {}; |
| ... | @@ -197,7 +197,7 @@ test "link with relative paths" { | ... | @@ -197,7 +197,7 @@ test "link with relative paths" { |
| 197 | if (builtin.link_libc) { | 197 | if (builtin.link_libc) { |
| 198 | return error.SkipZigTest; | 198 | return error.SkipZigTest; |
| 199 | } else { | 199 | } else { |
| 200 | try os.initPreopensWasi(std.heap.page_allocator, "."); | 200 | try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 201 | } | 201 | } |
| 202 | }, | 202 | }, |
| 203 | .linux, .solaris => {}, | 203 | .linux, .solaris => {}, |
| ... | @@ -237,7 +237,7 @@ test "link with relative paths" { | ... | @@ -237,7 +237,7 @@ test "link with relative paths" { |
| 237 | 237 | ||
| 238 | test "linkat with different directories" { | 238 | test "linkat with different directories" { |
| 239 | switch (native_os) { | 239 | switch (native_os) { |
| 240 | .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."), | 240 | .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"), |
| 241 | .linux, .solaris => {}, | 241 | .linux, .solaris => {}, |
| 242 | else => return error.SkipZigTest, | 242 | else => return error.SkipZigTest, |
| 243 | } | 243 | } |
| ... | @@ -898,7 +898,7 @@ test "POSIX file locking with fcntl" { | ... | @@ -898,7 +898,7 @@ test "POSIX file locking with fcntl" { |
| 898 | 898 | ||
| 899 | test "rename smoke test" { | 899 | test "rename smoke test" { |
| 900 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 900 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 901 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 901 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 902 | 902 | ||
| 903 | var tmp = tmpDir(.{}); | 903 | var tmp = tmpDir(.{}); |
| 904 | defer tmp.cleanup(); | 904 | defer tmp.cleanup(); |
| ... | @@ -955,7 +955,7 @@ test "rename smoke test" { | ... | @@ -955,7 +955,7 @@ test "rename smoke test" { |
| 955 | 955 | ||
| 956 | test "access smoke test" { | 956 | test "access smoke test" { |
| 957 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; | 957 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 958 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); | 958 | if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); |
| 959 | 959 | ||
| 960 | var tmp = tmpDir(.{}); | 960 | var tmp = tmpDir(.{}); |
| 961 | defer tmp.cleanup(); | 961 | defer tmp.cleanup(); |
lib/std/testing.zig+1-1| ... | @@ -380,7 +380,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir { | ... | @@ -380,7 +380,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir { |
| 380 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 380 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 381 | var preopens = std.fs.wasi.PreopenList.init(allocator); | 381 | var preopens = std.fs.wasi.PreopenList.init(allocator); |
| 382 | defer preopens.deinit(); | 382 | defer preopens.deinit(); |
| 383 | preopens.populate() catch | 383 | preopens.populate(null) catch |
| 384 | @panic("unable to make tmp dir for testing: unable to populate preopens"); | 384 | @panic("unable to make tmp dir for testing: unable to populate preopens"); |
| 385 | const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse | 385 | const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse |
| 386 | @panic("unable to make tmp dir for testing: didn't find '.' in the preopens"); | 386 | @panic("unable to make tmp dir for testing: didn't find '.' in the preopens"); |