authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-11-24 08:10:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 14:43:29-07:00
logfff7f15fb88f6683f9d73565d8d59593ecf7461a
tree2e9f2b717f57e4669e6a53d2be797a924b378660
parentb75197ef88f3d9fa6c7022a7f1a48f69218d4492

std.os: Fix std.os.chdir for WASI

Test coverage was lacking for chdir() on WASI, allowing this to regress. This change makes os.chdir() compile again, and improves the test logic to use our standard CWD support for WASI.

3 files changed, 38 insertions(+), 22 deletions(-)

lib/std/os.zig+6-2
...@@ -2999,15 +2999,19 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {...@@ -2999,15 +2999,19 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
2999 if (builtin.os.tag == .wasi and !builtin.link_libc) {2999 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3000 var buf: [MAX_PATH_BYTES]u8 = undefined;3000 var buf: [MAX_PATH_BYTES]u8 = undefined;
3001 var alloc = std.heap.FixedBufferAllocator.init(&buf);3001 var alloc = std.heap.FixedBufferAllocator.init(&buf);
3002 const path = try fs.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path });3002 const path = fs.path.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path }) catch |err| switch (err) {
3003 error.OutOfMemory => return error.NameTooLong,
3004 else => |e| return e,
3005 };
30033006
3004 const dirinfo = try fstatat(AT.FDCWD, path, 0);3007 const dirinfo = try fstatat(AT.FDCWD, path, 0);
3005 if (dirinfo.filetype != .DIRECTORY) {3008 if (dirinfo.filetype != .DIRECTORY) {
3006 return error.NotDir;3009 return error.NotDir;
3007 }3010 }
30083011
3012 // This copy is guaranteed to succeed, since buf and path_buffer are the same size.
3009 var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);3013 var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
3010 wasi_cwd.cwd = try cwd_alloc.allocator().dupe(u8, path);3014 wasi_cwd.cwd = cwd_alloc.allocator().dupe(u8, path) catch unreachable;
3011 return;3015 return;
3012 } else if (builtin.os.tag == .windows) {3016 } else if (builtin.os.tag == .windows) {
3013 var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;3017 var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
lib/std/os/test.zig+30-3
...@@ -22,7 +22,8 @@ const Dir = std.fs.Dir;...@@ -22,7 +22,8 @@ const Dir = std.fs.Dir;
22const ArenaAllocator = std.heap.ArenaAllocator;22const ArenaAllocator = std.heap.ArenaAllocator;
2323
24test "chdir smoke test" {24test "chdir smoke test" {
25 if (native_os == .wasi) return error.SkipZigTest; // WASI doesn't allow navigating outside of a preopen25 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
26 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/preopens/cwd");
2627
27 // Get current working directory path28 // Get current working directory path
28 var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;29 var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -35,16 +36,42 @@ test "chdir smoke test" {...@@ -35,16 +36,42 @@ test "chdir smoke test" {
35 const new_cwd = try os.getcwd(new_cwd_buf[0..]);36 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
36 try expect(mem.eql(u8, old_cwd, new_cwd));37 try expect(mem.eql(u8, old_cwd, new_cwd));
37 }38 }
38 {39
39 // Next, change current working directory to one level above40 // Next, change current working directory to one level above
41 if (native_os != .wasi) { // WASI does not support navigating outside of Preopens
40 const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute42 const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
41 try os.chdir(parent);43 try os.chdir(parent);
44
42 // Restore cwd because process may have other tests that do not tolerate chdir.45 // Restore cwd because process may have other tests that do not tolerate chdir.
43 defer os.chdir(old_cwd) catch unreachable;46 defer os.chdir(old_cwd) catch unreachable;
47
44 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;48 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
45 const new_cwd = try os.getcwd(new_cwd_buf[0..]);49 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
46 try expect(mem.eql(u8, parent, new_cwd));50 try expect(mem.eql(u8, parent, new_cwd));
47 }51 }
52
53 // Next, change current working directory to a temp directory one level below
54 {
55 // Create a tmp directory
56 var tmp_dir_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
57 var tmp_dir_path = path: {
58 var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf);
59 break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" });
60 };
61 var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{});
62
63 // Change current working directory to tmp directory
64 try os.chdir("zig-test-tmp");
65
66 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
67 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
68 try expect(mem.eql(u8, tmp_dir_path, new_cwd));
69
70 // Restore cwd because process may have other tests that do not tolerate chdir.
71 tmp_dir.close();
72 os.chdir(old_cwd) catch unreachable;
73 try fs.cwd().deleteDir("zig-test-tmp");
74 }
48}75}
4976
50test "open smoke test" {77test "open smoke test" {
lib/std/testing.zig+2-17
...@@ -379,28 +379,13 @@ pub const TmpIterableDir = struct {...@@ -379,28 +379,13 @@ pub const TmpIterableDir = struct {
379 }379 }
380};380};
381381
382fn getCwdOrWasiPreopen() std.fs.Dir {
383 if (builtin.os.tag == .wasi and !builtin.link_libc) {
384 var preopens = std.fs.wasi.PreopenList.init(allocator);
385 defer preopens.deinit();
386 preopens.populate(null) catch
387 @panic("unable to make tmp dir for testing: unable to populate preopens");
388 const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
389 @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
390
391 return std.fs.Dir{ .fd = preopen.fd };
392 } else {
393 return std.fs.cwd();
394 }
395}
396
397pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {382pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
398 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;383 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
399 std.crypto.random.bytes(&random_bytes);384 std.crypto.random.bytes(&random_bytes);
400 var sub_path: [TmpDir.sub_path_len]u8 = undefined;385 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
401 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);386 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
402387
403 var cwd = getCwdOrWasiPreopen();388 var cwd = std.fs.cwd();
404 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch389 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
405 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");390 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
406 defer cache_dir.close();391 defer cache_dir.close();
...@@ -422,7 +407,7 @@ pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir {...@@ -422,7 +407,7 @@ pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir {
422 var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined;407 var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined;
423 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);408 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
424409
425 var cwd = getCwdOrWasiPreopen();410 var cwd = std.fs.cwd();
426 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch411 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
427 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");412 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
428 defer cache_dir.close();413 defer cache_dir.close();