Skip to content

stdlib: Add emulated CWD to std.os for WASI targets #11021

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 3 commits into from
Mar 5, 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
10 changes: 5 additions & 5 deletions lib/std/c/wasi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,21 @@ pub const Stat = extern struct {
/// https://github.com/WebAssembly/wasi-libc/blob/main/expected/wasm32-wasi/predefined-macros.txt
pub const O = struct {
pub const ACCMODE = (EXEC | RDWR | SEARCH);
pub const APPEND = FDFLAG.APPEND;
pub const APPEND = @as(u32, FDFLAG.APPEND);
pub const CLOEXEC = (0);
pub const CREAT = ((1 << 0) << 12); // = __WASI_OFLAGS_CREAT << 12
pub const DIRECTORY = ((1 << 1) << 12); // = __WASI_OFLAGS_DIRECTORY << 12
pub const DSYNC = FDFLAG.DSYNC;
pub const DSYNC = @as(u32, FDFLAG.DSYNC);
pub const EXCL = ((1 << 2) << 12); // = __WASI_OFLAGS_EXCL << 12
pub const EXEC = (0x02000000);
pub const NOCTTY = (0);
pub const NOFOLLOW = (0x01000000);
pub const NONBLOCK = (1 << FDFLAG.NONBLOCK);
pub const NONBLOCK = @as(u32, FDFLAG.NONBLOCK);
pub const RDONLY = (0x04000000);
pub const RDWR = (RDONLY | WRONLY);
pub const RSYNC = (1 << FDFLAG.RSYNC);
pub const RSYNC = @as(u32, FDFLAG.RSYNC);
pub const SEARCH = (0x08000000);
pub const SYNC = (1 << FDFLAG.SYNC);
pub const SYNC = @as(u32, FDFLAG.SYNC);
pub const TRUNC = ((1 << 3) << 12); // = __WASI_OFLAGS_TRUNC << 12
pub const TTY_INIT = (0);
pub const WRONLY = (0x10000000);
Expand Down
1 change: 1 addition & 0 deletions lib/std/child_process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ pub const ChildProcess = struct {
error.DeviceBusy => unreachable,
error.FileLocksNotSupported => unreachable,
error.BadPathName => unreachable, // Windows-only
error.InvalidHandle => unreachable, // WASI-only
error.WouldBlock => unreachable,
else => |e| return e,
}
Expand Down
72 changes: 44 additions & 28 deletions lib/std/fs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ pub const Dir = struct {
pub const OpenError = error{
FileNotFound,
NotDir,
InvalidHandle,
AccessDenied,
SymLinkLoop,
ProcessFdQuotaExceeded,
Expand Down Expand Up @@ -981,6 +982,13 @@ pub const Dir = struct {
w.RIGHT.FD_FILESTAT_SET_TIMES |
w.RIGHT.FD_FILESTAT_SET_SIZE;
}
if (self.fd == os.wasi.AT.FDCWD or path.isAbsolute(sub_path)) {
// Resolve absolute or CWD-relative paths to a path within a Preopen
var resolved_path_buf: [MAX_PATH_BYTES]u8 = undefined;
const resolved_path = try os.resolvePathWasi(sub_path, &resolved_path_buf);
const fd = try os.openatWasi(resolved_path.dir_fd, resolved_path.relative_path, 0x0, 0x0, fdflags, base, 0x0);
return File{ .handle = fd };
}
const fd = try os.openatWasi(self.fd, sub_path, 0x0, 0x0, fdflags, base, 0x0);
return File{ .handle = fd };
}
Expand Down Expand Up @@ -1145,6 +1153,13 @@ pub const Dir = struct {
if (flags.exclusive) {
oflags |= w.O.EXCL;
}
if (self.fd == os.wasi.AT.FDCWD or path.isAbsolute(sub_path)) {
// Resolve absolute or CWD-relative paths to a path within a Preopen
var resolved_path_buf: [MAX_PATH_BYTES]u8 = undefined;
const resolved_path = try os.resolvePathWasi(sub_path, &resolved_path_buf);
const fd = try os.openatWasi(resolved_path.dir_fd, resolved_path.relative_path, 0x0, oflags, 0x0, base, 0x0);
return File{ .handle = fd };
}
const fd = try os.openatWasi(self.fd, sub_path, 0x0, oflags, 0x0, base, 0x0);
return File{ .handle = fd };
}
Expand Down Expand Up @@ -1330,7 +1345,19 @@ pub const Dir = struct {
/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
if (builtin.os.tag == .wasi) {
@compileError("realpath is unsupported in WASI");
if (self.fd == os.wasi.AT.FDCWD or path.isAbsolute(pathname)) {
var buffer: [MAX_PATH_BYTES]u8 = undefined;
const out_path = try os.realpath(pathname, &buffer);
if (out_path.len > out_buffer.len) {
return error.NameTooLong;
}
mem.copy(u8, out_buffer, out_path);
return out_buffer[0..out_path.len];
} else {
// Unfortunately, we have no ability to look up the path for an fd_t
// on WASI, so we have to give up here.
return error.InvalidHandle;
}
}
if (builtin.os.tag == .windows) {
const pathname_w = try os.windows.sliceToPrefixedFileW(pathname);
Expand Down Expand Up @@ -1507,7 +1534,16 @@ pub const Dir = struct {
// TODO do we really need all the rights here?
const inheriting: w.rights_t = w.RIGHT.ALL ^ w.RIGHT.SOCK_SHUTDOWN;

const result = os.openatWasi(self.fd, sub_path, symlink_flags, w.O.DIRECTORY, 0x0, base, inheriting);
const result = blk: {
if (self.fd == os.wasi.AT.FDCWD or path.isAbsolute(sub_path)) {
// Resolve absolute or CWD-relative paths to a path within a Preopen
var resolved_path_buf: [MAX_PATH_BYTES]u8 = undefined;
const resolved_path = try os.resolvePathWasi(sub_path, &resolved_path_buf);
break :blk os.openatWasi(resolved_path.dir_fd, resolved_path.relative_path, symlink_flags, w.O.DIRECTORY, 0x0, base, inheriting);
} else {
break :blk os.openatWasi(self.fd, sub_path, symlink_flags, w.O.DIRECTORY, 0x0, base, inheriting);
}
};
const fd = result catch |err| switch (err) {
error.FileTooBig => unreachable, // can't happen for directories
error.IsDir => unreachable, // we're providing O.DIRECTORY
Expand Down Expand Up @@ -1622,7 +1658,7 @@ pub const Dir = struct {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteFileW(sub_path_w.span());
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
else => |e| return e,
};
Expand Down Expand Up @@ -1761,7 +1797,7 @@ pub const Dir = struct {
sym_link_path: []const u8,
_: SymLinkFlags,
) !void {
return os.symlinkatWasi(target_path, self.fd, sym_link_path);
return os.symlinkat(target_path, self.fd, sym_link_path);
}

/// Same as `symLink`, except the pathname parameters are null-terminated.
Expand Down Expand Up @@ -1807,7 +1843,7 @@ pub const Dir = struct {

/// WASI-only. Same as `readLink` except targeting WASI.
pub fn readLinkWasi(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
return os.readlinkatWasi(self.fd, sub_path, buffer);
return os.readlinkat(self.fd, sub_path, buffer);
}

/// Same as `readLink`, except the `pathname` parameter is null-terminated.
Expand Down Expand Up @@ -1870,6 +1906,7 @@ pub const Dir = struct {
}

pub const DeleteTreeError = error{
InvalidHandle,
AccessDenied,
FileTooBig,
SymLinkLoop,
Expand Down Expand Up @@ -1935,6 +1972,7 @@ pub const Dir = struct {
continue :start_over;
},

error.InvalidHandle,
error.AccessDenied,
error.SymLinkLoop,
error.ProcessFdQuotaExceeded,
Expand Down Expand Up @@ -2002,6 +2040,7 @@ pub const Dir = struct {
continue :scan_dir;
},

error.InvalidHandle,
error.AccessDenied,
error.SymLinkLoop,
error.ProcessFdQuotaExceeded,
Expand Down Expand Up @@ -2272,8 +2311,6 @@ pub const Dir = struct {
pub fn cwd() Dir {
if (builtin.os.tag == .windows) {
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
@compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead");
} else {
return Dir{ .fd = os.AT.FDCWD };
}
Expand All @@ -2285,26 +2322,17 @@ pub fn cwd() Dir {
///
/// Asserts that the path parameter has no null bytes.
pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI.");
}
assert(path.isAbsolute(absolute_path));
return cwd().openDir(absolute_path, flags);
}

/// Same as `openDirAbsolute` but the path parameter is null-terminated.
pub fn openDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI.");
}
assert(path.isAbsoluteZ(absolute_path_c));
return cwd().openDirZ(absolute_path_c, flags);
}
/// Same as `openDirAbsolute` but the path parameter is null-terminated.
pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptions) File.OpenError!Dir {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute directory; use openDir instead for WASI.");
}
assert(path.isAbsoluteWindowsW(absolute_path_c));
return cwd().openDirW(absolute_path_c, flags);
}
Expand Down Expand Up @@ -2339,25 +2367,16 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi
/// open it and handle the error for file not found.
/// See `accessAbsoluteZ` for a function that accepts a null-terminated path.
pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI.");
}
assert(path.isAbsolute(absolute_path));
try cwd().access(absolute_path, flags);
}
/// Same as `accessAbsolute` but the path parameter is null-terminated.
pub fn accessAbsoluteZ(absolute_path: [*:0]const u8, flags: File.OpenFlags) Dir.AccessError!void {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI.");
}
assert(path.isAbsoluteZ(absolute_path));
try cwd().accessZ(absolute_path, flags);
}
/// Same as `accessAbsolute` but the path parameter is WTF-16 encoded.
pub fn accessAbsoluteW(absolute_path: [*:0]const 16, flags: File.OpenFlags) Dir.AccessError!void {
if (builtin.os.tag == .wasi) {
@compileError("WASI doesn't have the concept of an absolute path; use access instead for WASI.");
}
assert(path.isAbsoluteWindowsW(absolute_path));
try cwd().accessW(absolute_path, flags);
}
Expand Down Expand Up @@ -2458,9 +2477,6 @@ pub const SymLinkFlags = struct {
/// If `sym_link_path` exists, it will not be overwritten.
/// See also `symLinkAbsoluteZ` and `symLinkAbsoluteW`.
pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags: SymLinkFlags) !void {
if (builtin.os.tag == .wasi) {
@compileError("symLinkAbsolute is not supported in WASI; use Dir.symLinkWasi instead");
}
assert(path.isAbsolute(target_path));
assert(path.isAbsolute(sym_link_path));
if (builtin.os.tag == .windows) {
Expand Down
13 changes: 9 additions & 4 deletions lib/std/fs/path.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const fmt = std.fmt;
const Allocator = mem.Allocator;
const math = std.math;
const windows = std.os.windows;
const os = std.os;
const fs = std.fs;
const process = std.process;
const native_os = builtin.target.os.tag;
Expand Down Expand Up @@ -733,7 +734,8 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
}

test "resolve" {
if (native_os == .wasi) 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, ".");

const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
Expand All @@ -753,7 +755,8 @@ test "resolveWindows" {
// TODO https://github.com/ziglang/zig/issues/3288
return error.SkipZigTest;
}
if (native_os == .wasi) 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 == .windows) {
const cwd = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd);
Expand Down Expand Up @@ -798,7 +801,8 @@ test "resolveWindows" {
}

test "resolvePosix" {
if (native_os == .wasi) 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, ".");

try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
Expand Down Expand Up @@ -1211,7 +1215,8 @@ test "relative" {
// TODO https://github.com/ziglang/zig/issues/3288
return error.SkipZigTest;
}
if (native_os == .wasi) 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, ".");

try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
Expand Down
22 changes: 15 additions & 7 deletions lib/std/fs/test.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("../std.zig");
const builtin = @import("builtin");
const testing = std.testing;
const os = std.os;
const fs = std.fs;
const mem = std.mem;
const wasi = std.os.wasi;
Expand Down Expand Up @@ -45,7 +46,8 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo
}

test "accessAbsolute" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{});
defer tmp.cleanup();
Expand All @@ -63,7 +65,8 @@ test "accessAbsolute" {
}

test "openDirAbsolute" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{});
defer tmp.cleanup();
Expand Down Expand Up @@ -99,7 +102,8 @@ test "openDir cwd parent .." {
}

test "readLinkAbsolute" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{});
defer tmp.cleanup();
Expand Down Expand Up @@ -507,7 +511,8 @@ test "rename" {
}

test "renameAbsolute" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp_dir = tmpDir(.{});
defer tmp_dir.cleanup();
Expand Down Expand Up @@ -941,7 +946,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
}

test "walker" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{ .iterate = true });
defer tmp.cleanup();
Expand Down Expand Up @@ -991,7 +997,8 @@ test "walker" {
}

test ". and .. in fs.Dir functions" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{});
defer tmp.cleanup();
Expand Down Expand Up @@ -1019,7 +1026,8 @@ test ". and .. in fs.Dir functions" {
}

test ". and .. in absolute functions" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
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, ".");

var tmp = tmpDir(.{});
defer tmp.cleanup();
Expand Down
Loading