Skip to content

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

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

Merged
merged 1 commit into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/std/os.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2999,15 +2999,19 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
if (builtin.os.tag == .wasi and !builtin.link_libc) {
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 path = fs.path.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path }) catch |err| switch (err) {
error.OutOfMemory => return error.NameTooLong,
else => |e| return e,
};

const dirinfo = try fstatat(AT.FDCWD, path, 0);
if (dirinfo.filetype != .DIRECTORY) {
return error.NotDir;
}

// This copy is guaranteed to succeed, since buf and path_buffer are the same size.
var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
wasi_cwd.cwd = try cwd_alloc.allocator().dupe(u8, path);
wasi_cwd.cwd = cwd_alloc.allocator().dupe(u8, path) catch unreachable;
return;
} else if (builtin.os.tag == .windows) {
var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
Expand Down
33 changes: 30 additions & 3 deletions lib/std/os/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const Dir = std.fs.Dir;
const ArenaAllocator = std.heap.ArenaAllocator;

test "chdir smoke test" {
if (native_os == .wasi) return error.SkipZigTest; // WASI doesn't allow navigating outside of a preopen
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, "/preopens/cwd");

// Get current working directory path
var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
Expand All @@ -35,16 +36,42 @@ test "chdir smoke test" {
const new_cwd = try os.getcwd(new_cwd_buf[0..]);
try expect(mem.eql(u8, old_cwd, new_cwd));
}
{
// Next, change current working directory to one level above

// Next, change current working directory to one level above
if (native_os != .wasi) { // WASI does not support navigating outside of Preopens
const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
try os.chdir(parent);

// Restore cwd because process may have other tests that do not tolerate chdir.
defer os.chdir(old_cwd) catch unreachable;

var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
const new_cwd = try os.getcwd(new_cwd_buf[0..]);
try expect(mem.eql(u8, parent, new_cwd));
}

// Next, change current working directory to a temp directory one level below
{
// Create a tmp directory
var tmp_dir_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
var tmp_dir_path = path: {
var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf);
break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" });
};
var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{});

// Change current working directory to tmp directory
try os.chdir("zig-test-tmp");

var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
const new_cwd = try os.getcwd(new_cwd_buf[0..]);
try expect(mem.eql(u8, tmp_dir_path, new_cwd));

// Restore cwd because process may have other tests that do not tolerate chdir.
tmp_dir.close();
os.chdir(old_cwd) catch unreachable;
try fs.cwd().deleteDir("zig-test-tmp");
}
}

test "open smoke test" {
Expand Down
19 changes: 2 additions & 17 deletions lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -379,28 +379,13 @@ pub const TmpIterableDir = struct {
}
};

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(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");

return std.fs.Dir{ .fd = preopen.fd };
} else {
return std.fs.cwd();
}
}

pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
std.crypto.random.bytes(&random_bytes);
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);

var cwd = getCwdOrWasiPreopen();
var cwd = std.fs.cwd();
var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
@panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
defer cache_dir.close();
Expand All @@ -422,7 +407,7 @@ pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir {
var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);

var cwd = getCwdOrWasiPreopen();
var cwd = std.fs.cwd();
var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
@panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
defer cache_dir.close();
Expand Down