mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 08:33:06 +00:00
stdlib: Add emulated CWD to std.os for WASI targets
This adds a special CWD file descriptor, AT.FDCWD (-2), to refer to the current working directory. The `*at(...)` functions look for this and resolve relative paths against the stored CWD. Absolute paths are dynamically matched against the stored Preopens. "os.initPreopensWasi()" must be called before std.os functions will resolve relative or absolute paths correctly. This is asserted at runtime. Support has been added for: `open`, `rename`, `mkdir`, `rmdir`, `chdir`, `fchdir`, `link`, `symlink`, `unlink`, `readlink`, `fstatat`, `access`, and `faccessat`. This also includes limited support for `getcwd()` and `realpath()`. These return an error if the CWD does not correspond to a Preopen with an absolute path. They also do not currently expand symlinks.
This commit is contained in:
parent
3605dd307f
commit
58f961f4cb
@ -2707,6 +2707,8 @@ pub const LibExeObjStep = struct {
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--dir=.");
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--mapdir=/cwd::.");
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--allow-unknown-exports"); // TODO: Remove when stage2 is default compiler
|
||||
try zig_args.append("--test-cmd-bin");
|
||||
} else {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -923,6 +923,7 @@ pub const Dir = struct {
|
||||
pub const OpenError = error{
|
||||
FileNotFound,
|
||||
NotDir,
|
||||
InvalidHandle,
|
||||
AccessDenied,
|
||||
SymLinkLoop,
|
||||
ProcessFdQuotaExceeded,
|
||||
@ -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 };
|
||||
}
|
||||
@ -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 };
|
||||
}
|
||||
@ -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);
|
||||
@ -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
|
||||
@ -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,
|
||||
};
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -1870,6 +1906,7 @@ pub const Dir = struct {
|
||||
}
|
||||
|
||||
pub const DeleteTreeError = error{
|
||||
InvalidHandle,
|
||||
AccessDenied,
|
||||
FileTooBig,
|
||||
SymLinkLoop,
|
||||
@ -1935,6 +1972,7 @@ pub const Dir = struct {
|
||||
continue :start_over;
|
||||
},
|
||||
|
||||
error.InvalidHandle,
|
||||
error.AccessDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
@ -2002,6 +2040,7 @@ pub const Dir = struct {
|
||||
continue :scan_dir;
|
||||
},
|
||||
|
||||
error.InvalidHandle,
|
||||
error.AccessDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
@ -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 };
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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) {
|
||||
|
@ -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;
|
||||
@ -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, "/cwd");
|
||||
|
||||
const cwd = try process.getCwdAlloc(testing.allocator);
|
||||
defer testing.allocator.free(cwd);
|
||||
@ -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, "/cwd");
|
||||
if (native_os == .windows) {
|
||||
const cwd = try process.getCwdAlloc(testing.allocator);
|
||||
defer testing.allocator.free(cwd);
|
||||
@ -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, "/cwd");
|
||||
|
||||
try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
|
||||
try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
|
||||
@ -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, "/cwd");
|
||||
|
||||
try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
|
||||
try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
|
||||
|
@ -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;
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{ .iterate = true });
|
||||
defer tmp.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
@ -25,13 +25,29 @@ pub const PreopenType = union(PreopenTypeTag) {
|
||||
const Self = @This();
|
||||
|
||||
pub fn eql(self: Self, other: PreopenType) bool {
|
||||
if (!mem.eql(u8, @tagName(self), @tagName(other))) return false;
|
||||
if (std.meta.activeTag(self) != std.meta.activeTag(other)) return false;
|
||||
|
||||
switch (self) {
|
||||
PreopenTypeTag.Dir => |this_path| return mem.eql(u8, this_path, other.Dir),
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether `other` refers to a subdirectory of `self` and, if so,
|
||||
// returns the relative path to `other` from `self`
|
||||
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| {
|
||||
const other_path = other.Dir;
|
||||
if (mem.indexOfDiff(u8, this_path, other_path)) |index| {
|
||||
if (index < this_path.len) return null;
|
||||
}
|
||||
return other_path[this_path.len..];
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
@ -62,6 +78,15 @@ pub const Preopen = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// WASI resource identifier struct. This is effectively a path within
|
||||
/// a WASI Preopen.
|
||||
pub const PreopenUri = struct {
|
||||
/// WASI Preopen containing the resource.
|
||||
base: Preopen,
|
||||
/// Path to resource within `base`.
|
||||
relative_path: []const u8,
|
||||
};
|
||||
|
||||
/// Dynamically-sized array list of WASI preopens. This struct is a
|
||||
/// convenience wrapper for issuing `std.os.wasi.fd_prestat_get` and
|
||||
/// `std.os.wasi.fd_prestat_dir_name` syscalls to the WASI runtime, and
|
||||
@ -137,12 +162,38 @@ pub const PreopenList = struct {
|
||||
.SUCCESS => {},
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf });
|
||||
try self.buffer.append(preopen);
|
||||
fd = try math.add(fd_t, fd, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Find preopen by type. If the preopen exists, return it.
|
||||
/// Otherwise, return `null`.
|
||||
pub fn find(self: Self, preopen_type: PreopenType) ?*const Preopen {
|
||||
@ -173,8 +224,6 @@ test "extracting WASI preopens" {
|
||||
|
||||
try preopens.populate();
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 1), preopens.asSlice().len);
|
||||
const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
|
||||
try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." }));
|
||||
try std.testing.expectEqual(@as(i32, 3), preopen.fd);
|
||||
const preopen = preopens.find(PreopenType{ .Dir = "/cwd" }) orelse unreachable;
|
||||
try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "/cwd" }));
|
||||
}
|
||||
|
533
lib/std/os.zig
533
lib/std/os.zig
@ -21,9 +21,13 @@ const assert = std.debug.assert;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const elf = std.elf;
|
||||
const fs = std.fs;
|
||||
const dl = @import("dynamic_library.zig");
|
||||
const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
|
||||
const is_windows = builtin.os.tag == .windows;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Preopen = std.fs.wasi.Preopen;
|
||||
const PreopenList = std.fs.wasi.PreopenList;
|
||||
|
||||
pub const darwin = std.c;
|
||||
pub const dragonfly = std.c;
|
||||
@ -93,7 +97,12 @@ pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
|
||||
pub const MMAP2_UNIT = system.MMAP2_UNIT;
|
||||
pub const MSG = system.MSG;
|
||||
pub const NAME_MAX = system.NAME_MAX;
|
||||
pub const O = system.O;
|
||||
pub const O = switch (builtin.os.tag) {
|
||||
// We want to expose the POSIX-like OFLAGS, so we use std.c.wasi.O instead
|
||||
// of std.os.wasi.O, which is for non-POSIX-like `wasi.path_open`, etc.
|
||||
.wasi => std.c.O,
|
||||
else => system.O,
|
||||
};
|
||||
pub const PATH_MAX = system.PATH_MAX;
|
||||
pub const POLL = system.POLL;
|
||||
pub const POSIX_FADV = system.POSIX_FADV;
|
||||
@ -210,6 +219,13 @@ pub const LOG = struct {
|
||||
pub const DEBUG = 7;
|
||||
};
|
||||
|
||||
pub const RelativePath = struct {
|
||||
/// Handle to directory
|
||||
dir_fd: fd_t,
|
||||
/// Path to resource within `dir_fd`.
|
||||
relative_path: []const u8,
|
||||
};
|
||||
|
||||
pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
|
||||
|
||||
/// See also `getenv`. Populated by startup code before main().
|
||||
@ -1239,6 +1255,9 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
/// In WASI, this error may occur when the provided file handle is invalid.
|
||||
InvalidHandle,
|
||||
|
||||
/// In WASI, this error may occur when the file descriptor does
|
||||
/// not hold the required rights to open a new resource relative to it.
|
||||
AccessDenied,
|
||||
@ -1300,6 +1319,8 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return openW(file_path_w.span(), flags, perm);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return openat(wasi.AT.FDCWD, file_path, flags, perm);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openZ(&file_path_c, flags, perm);
|
||||
@ -1311,6 +1332,8 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return openW(file_path_w.span(), flags, perm);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return open(mem.sliceTo(file_path, 0), flags, perm);
|
||||
}
|
||||
|
||||
const open_sym = if (builtin.os.tag == .linux and builtin.link_libc)
|
||||
@ -1347,7 +1370,7 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
|
||||
}
|
||||
}
|
||||
|
||||
fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
|
||||
fn openOptionsFromFlagsWindows(flags: u32) windows.OpenFileOptions {
|
||||
const w = windows;
|
||||
|
||||
var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
|
||||
@ -1387,7 +1410,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
|
||||
/// or makes use of perm argument.
|
||||
pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
|
||||
_ = perm;
|
||||
var options = openOptionsFromFlags(flags);
|
||||
var options = openOptionsFromFlagsWindows(flags);
|
||||
options.dir = std.fs.cwd().fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
@ -1396,21 +1419,187 @@ pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t
|
||||
};
|
||||
}
|
||||
|
||||
var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct {
|
||||
// List of available Preopens
|
||||
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: ?RelativePath = null,
|
||||
// Preopen associated with `cwd`, if any
|
||||
cwd_preopen: ?Preopen = null,
|
||||
}{} else undefined;
|
||||
|
||||
/// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`.
|
||||
///
|
||||
/// This must be called before using any relative or absolute paths with `std.os`
|
||||
/// functions, if you are on WASI without linking libc.
|
||||
///
|
||||
/// `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 {
|
||||
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 = RelativePath{
|
||||
.dir_fd = po.base.fd,
|
||||
.relative_path = po.relative_path,
|
||||
};
|
||||
} else {
|
||||
// No matching preopen found
|
||||
return error.FileNotFound;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cwd_init) |cwd| try chdir(cwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a relative or absolute path to an handle (`fd_t`) and a relative subpath.
|
||||
///
|
||||
/// For absolute paths, this automatically searches among available Preopens to find
|
||||
/// a match. For relative paths, it uses the "emulated" CWD.
|
||||
pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePath {
|
||||
// Note: Due to WASI's "sandboxed" file handles, operations with this RelativePath
|
||||
// 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) !RelativePath {
|
||||
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 the path is absolute, we need to lookup a containing Preopen
|
||||
const abs_path = std.fs.path.resolve(alloc, &.{ "/", 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 RelativePath{
|
||||
.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 = std.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;
|
||||
return RelativePath{
|
||||
.dir_fd = cwd.dir_fd,
|
||||
.relative_path = resolved_relative_path,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||
/// See also `openatZ`.
|
||||
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("use openatWasi instead");
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
// `mode` is ignored on WASI, which does not support unix-style file permissions
|
||||
const fd = if (dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) blk: {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try resolvePathWasi(file_path, &path_buf);
|
||||
|
||||
const opts = try openOptionsFromFlagsWasi(path.dir_fd, flags);
|
||||
break :blk try openatWasi(path.dir_fd, path.relative_path, opts.lookup_flags, opts.oflags, opts.fs_flags, opts.fs_rights_base, opts.fs_rights_inheriting);
|
||||
} else blk: {
|
||||
const opts = try openOptionsFromFlagsWasi(dir_fd, flags);
|
||||
break :blk try openatWasi(dir_fd, file_path, opts.lookup_flags, opts.oflags, opts.fs_flags, opts.fs_rights_base, opts.fs_rights_inheriting);
|
||||
};
|
||||
errdefer close(fd);
|
||||
|
||||
const info = try fstat(fd);
|
||||
if (flags & O.WRONLY != 0 and info.filetype == .DIRECTORY)
|
||||
return error.IsDir;
|
||||
|
||||
return fd;
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openatZ(dir_fd, &file_path_c, flags, mode);
|
||||
}
|
||||
|
||||
const WasiOpenOptions = struct {
|
||||
oflags: wasi.oflags_t,
|
||||
lookup_flags: wasi.lookupflags_t,
|
||||
fs_rights_base: wasi.rights_t,
|
||||
fs_rights_inheriting: wasi.rights_t,
|
||||
fs_flags: wasi.fdflags_t,
|
||||
};
|
||||
|
||||
/// Compute rights + flags corresponding to the provided POSIX access mode.
|
||||
fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions {
|
||||
const w = std.os.wasi;
|
||||
|
||||
// First, discover the rights that we can derive from `fd`
|
||||
var fsb_cur: wasi.fdstat_t = undefined;
|
||||
_ = switch (w.fd_fdstat_get(fd, &fsb_cur)) {
|
||||
.SUCCESS => .{},
|
||||
.BADF => return error.InvalidHandle,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
};
|
||||
|
||||
// Next, calculate the read/write rights to request, depending on the
|
||||
// provided POSIX access mode
|
||||
var rights: w.rights_t = 0;
|
||||
if (oflag & O.RDONLY != 0) {
|
||||
rights |= w.RIGHT.FD_READ | w.RIGHT.FD_READDIR;
|
||||
}
|
||||
if (oflag & O.WRONLY != 0) {
|
||||
rights |= w.RIGHT.FD_DATASYNC | w.RIGHT.FD_WRITE |
|
||||
w.RIGHT.FD_ALLOCATE | w.RIGHT.FD_FILESTAT_SET_SIZE;
|
||||
}
|
||||
|
||||
// Request all other rights unconditionally
|
||||
rights |= ~(w.RIGHT.FD_DATASYNC | w.RIGHT.FD_READ |
|
||||
w.RIGHT.FD_WRITE | w.RIGHT.FD_ALLOCATE |
|
||||
w.RIGHT.FD_READDIR | w.RIGHT.FD_FILESTAT_SET_SIZE);
|
||||
|
||||
// But only take rights that we can actually inherit
|
||||
rights &= fsb_cur.fs_rights_inheriting;
|
||||
|
||||
return WasiOpenOptions{
|
||||
.oflags = @truncate(w.oflags_t, (oflag >> 12)) & 0xfff,
|
||||
.lookup_flags = if (oflag & O.NOFOLLOW == 0) w.LOOKUP_SYMLINK_FOLLOW else 0,
|
||||
.fs_rights_base = rights,
|
||||
.fs_rights_inheriting = fsb_cur.fs_rights_inheriting,
|
||||
.fs_flags = @truncate(w.fdflags_t, oflag & 0xfff),
|
||||
};
|
||||
}
|
||||
|
||||
/// Open and possibly create a file in WASI.
|
||||
pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, lookup_flags: lookupflags_t, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t {
|
||||
while (true) {
|
||||
@ -1450,6 +1639,8 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
|
||||
}
|
||||
|
||||
const openat_sym = if (builtin.os.tag == .linux and builtin.link_libc)
|
||||
@ -1496,7 +1687,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
|
||||
/// or makes use of perm argument.
|
||||
pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
_ = mode;
|
||||
var options = openOptionsFromFlags(flags);
|
||||
var options = openOptionsFromFlagsWindows(flags);
|
||||
options.dir = dir_fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
@ -1764,9 +1955,29 @@ pub const GetCwdError = error{
|
||||
pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.GetCurrentDirectory(out_buffer);
|
||||
}
|
||||
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 if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
var allocator = std.heap.FixedBufferAllocator.init(out_buffer);
|
||||
var alloc = allocator.allocator();
|
||||
if (wasi_cwd.cwd) |cwd| {
|
||||
if (wasi_cwd.cwd_preopen) |po| {
|
||||
var base_cwd_dir = switch (po.@"type") {
|
||||
.Dir => |dir| dir,
|
||||
};
|
||||
if (!fs.path.isAbsolute(base_cwd_dir)) {
|
||||
// This preopen is not based on an absolute path, so we have
|
||||
// no way to know the absolute path of the CWD
|
||||
return error.CurrentWorkingDirectoryUnlinked;
|
||||
}
|
||||
const paths = &.{ base_cwd_dir, cwd.relative_path };
|
||||
return std.fs.path.resolve(alloc, paths) catch return error.NameTooLong;
|
||||
} else {
|
||||
// The CWD is not rooted to an existing Preopen,
|
||||
// so we have no way to know its absolute path
|
||||
return error.CurrentWorkingDirectoryUnlinked;
|
||||
}
|
||||
} else {
|
||||
return alloc.dupe(u8, "/") catch return error.NameTooLong;
|
||||
}
|
||||
}
|
||||
|
||||
const err = if (builtin.link_libc) blk: {
|
||||
@ -1809,11 +2020,10 @@ pub const SymLinkError = error{
|
||||
/// If `sym_link_path` exists, it will not be overwritten.
|
||||
/// See also `symlinkZ.
|
||||
pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("symlink is not supported in WASI; use symlinkat instead");
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
@compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path);
|
||||
}
|
||||
const target_path_c = try toPosixPath(target_path);
|
||||
const sym_link_path_c = try toPosixPath(sym_link_path);
|
||||
@ -1825,6 +2035,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!
|
||||
pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
@compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return symlink(mem.sliceTo(target_path, 0), mem.sliceTo(sym_link_path, 0));
|
||||
}
|
||||
switch (errno(system.symlink(target_path, sym_link_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -1853,11 +2065,16 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
|
||||
/// If `sym_link_path` exists, it will not be overwritten.
|
||||
/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
|
||||
pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return symlinkatWasi(target_path, newdirfd, sym_link_path);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
@compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
if (newdirfd == wasi.AT.FDCWD or fs.path.isAbsolute(target_path)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try resolvePathWasi(sym_link_path, &path_buf);
|
||||
return symlinkatWasi(target_path, path.dir_fd, path.relative_path);
|
||||
}
|
||||
return symlinkatWasi(target_path, newdirfd, sym_link_path);
|
||||
}
|
||||
const target_path_c = try toPosixPath(target_path);
|
||||
const sym_link_path_c = try toPosixPath(sym_link_path);
|
||||
@ -1893,6 +2110,8 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c
|
||||
pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
@compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0));
|
||||
}
|
||||
switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -1930,6 +2149,9 @@ pub const LinkError = UnexpectedError || error{
|
||||
};
|
||||
|
||||
pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags);
|
||||
}
|
||||
switch (errno(system.link(oldpath, newpath, flags))) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
@ -1952,6 +2174,12 @@ pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkErr
|
||||
}
|
||||
|
||||
pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) {
|
||||
error.NotDir => unreachable, // link() does not support directories
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
const old = try toPosixPath(oldpath);
|
||||
const new = try toPosixPath(newpath);
|
||||
return try linkZ(&old, &new, flags);
|
||||
@ -1966,6 +2194,9 @@ pub fn linkatZ(
|
||||
newpath: [*:0]const u8,
|
||||
flags: i32,
|
||||
) LinkatError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
|
||||
}
|
||||
switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
@ -1995,11 +2226,62 @@ pub fn linkat(
|
||||
newpath: []const u8,
|
||||
flags: i32,
|
||||
) LinkatError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
var resolve_olddir: bool = (olddir == wasi.AT.FDCWD or fs.path.isAbsolute(oldpath));
|
||||
var resolve_newdir: bool = (newdir == wasi.AT.FDCWD or fs.path.isAbsolute(newpath));
|
||||
|
||||
var old: RelativePath = .{ .dir_fd = olddir, .relative_path = oldpath };
|
||||
var new: RelativePath = .{ .dir_fd = newdir, .relative_path = newpath };
|
||||
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
if (resolve_olddir or resolve_newdir) {
|
||||
var buf_old: [MAX_PATH_BYTES]u8 = undefined;
|
||||
var buf_new: [MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
if (resolve_olddir)
|
||||
old = try resolvePathWasi(oldpath, &buf_old);
|
||||
|
||||
if (resolve_newdir)
|
||||
new = try resolvePathWasi(newpath, &buf_new);
|
||||
|
||||
return linkatWasi(old, new, flags);
|
||||
}
|
||||
return linkatWasi(old, new, flags);
|
||||
}
|
||||
const old = try toPosixPath(oldpath);
|
||||
const new = try toPosixPath(newpath);
|
||||
return try linkatZ(olddir, &old, newdir, &new, flags);
|
||||
}
|
||||
|
||||
/// WASI-only. The same as `linkat` but targeting WASI.
|
||||
/// See also `linkat`.
|
||||
pub fn linkatWasi(old: RelativePath, new: RelativePath, flags: i32) LinkatError!void {
|
||||
var old_flags: wasi.lookupflags_t = 0;
|
||||
// TODO: Why is this not defined in wasi-libc?
|
||||
if (flags & linux.AT.SYMLINK_FOLLOW != 0) old_flags |= wasi.LOOKUP_SYMLINK_FOLLOW;
|
||||
|
||||
switch (wasi.path_link(old.dir_fd, old_flags, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.DQUOT => return error.DiskQuota,
|
||||
.EXIST => return error.PathAlreadyExists,
|
||||
.FAULT => unreachable,
|
||||
.IO => return error.FileSystem,
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.MLINK => return error.LinkQuotaExceeded,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => return error.NoSpaceLeft,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.PERM => return error.AccessDenied,
|
||||
.ROFS => return error.ReadOnlyFileSystem,
|
||||
.XDEV => return error.NotSameFileSystem,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const UnlinkError = error{
|
||||
FileNotFound,
|
||||
|
||||
@ -2027,7 +2309,10 @@ pub const UnlinkError = error{
|
||||
/// See also `unlinkZ`.
|
||||
pub fn unlink(file_path: []const u8) UnlinkError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("unlink is not supported in WASI; use unlinkat instead");
|
||||
return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) {
|
||||
error.DirNotEmpty => unreachable, // only occurs when targeting directories
|
||||
else => |e| return e,
|
||||
};
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return unlinkW(file_path_w.span());
|
||||
@ -2042,6 +2327,8 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return unlinkW(file_path_w.span());
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return unlink(mem.sliceTo(file_path, 0));
|
||||
}
|
||||
switch (errno(system.unlink(file_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -2079,6 +2366,12 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return unlinkatW(dirfd, file_path_w.span(), flags);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try resolvePathWasi(file_path, &path_buf);
|
||||
return unlinkatWasi(path.dir_fd, path.relative_path, flags);
|
||||
}
|
||||
return unlinkatWasi(dirfd, file_path, flags);
|
||||
} else {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
@ -2123,6 +2416,8 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
|
||||
return unlinkatW(dirfd, file_path_w.span(), flags);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
|
||||
}
|
||||
switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
|
||||
.SUCCESS => return,
|
||||
@ -2181,7 +2476,7 @@ pub const RenameError = error{
|
||||
/// Change the name or location of a file.
|
||||
pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("rename is not supported in WASI; use renameat instead");
|
||||
return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const old_path_w = try windows.sliceToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
|
||||
@ -2199,6 +2494,8 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi
|
||||
const old_path_w = try windows.cStrToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.cStrToPrefixedFileW(new_path);
|
||||
return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
|
||||
}
|
||||
switch (errno(system.rename(old_path, new_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -2243,7 +2540,25 @@ pub fn renameat(
|
||||
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
|
||||
return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path);
|
||||
var resolve_old: bool = (old_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(old_path));
|
||||
var resolve_new: bool = (new_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(new_path));
|
||||
|
||||
var old: RelativePath = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
|
||||
var new: RelativePath = .{ .dir_fd = new_dir_fd, .relative_path = new_path };
|
||||
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
if (resolve_old or resolve_new) {
|
||||
var buf_old: [MAX_PATH_BYTES]u8 = undefined;
|
||||
var buf_new: [MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
if (resolve_old)
|
||||
old = try resolvePathWasi(old_path, &buf_old);
|
||||
if (resolve_new)
|
||||
new = try resolvePathWasi(new_path, &buf_new);
|
||||
|
||||
return renameatWasi(old, new);
|
||||
}
|
||||
return renameatWasi(old, new);
|
||||
} else {
|
||||
const old_path_c = try toPosixPath(old_path);
|
||||
const new_path_c = try toPosixPath(new_path);
|
||||
@ -2253,8 +2568,8 @@ pub fn renameat(
|
||||
|
||||
/// WASI-only. Same as `renameat` expect targeting WASI.
|
||||
/// See also `renameat`.
|
||||
pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
|
||||
switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
|
||||
pub fn renameatWasi(old: RelativePath, new: RelativePath) RenameError!void {
|
||||
switch (wasi.path_rename(old.dir_fd, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => return error.AccessDenied,
|
||||
@ -2290,6 +2605,8 @@ pub fn renameatZ(
|
||||
const old_path_w = try windows.cStrToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.cStrToPrefixedFileW(new_path);
|
||||
return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
|
||||
}
|
||||
|
||||
switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
|
||||
@ -2380,6 +2697,12 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v
|
||||
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
if (dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(sub_dir_path)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try resolvePathWasi(sub_dir_path, &path_buf);
|
||||
return mkdiratWasi(path.dir_fd, path.relative_path, mode);
|
||||
}
|
||||
return mkdiratWasi(dir_fd, sub_dir_path, mode);
|
||||
} else {
|
||||
const sub_dir_path_c = try toPosixPath(sub_dir_path);
|
||||
@ -2414,6 +2737,8 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
|
||||
}
|
||||
switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
|
||||
.SUCCESS => return,
|
||||
@ -2472,10 +2797,10 @@ pub const MakeDirError = error{
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Create a directory.
|
||||
/// `mode` is ignored on Windows.
|
||||
/// `mode` is ignored on Windows and WASI.
|
||||
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("mkdir is not supported in WASI; use mkdirat instead");
|
||||
return mkdirat(wasi.AT.FDCWD, dir_path, mode);
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
@ -2490,6 +2815,8 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return mkdir(mem.sliceTo(dir_path, 0), mode);
|
||||
}
|
||||
switch (errno(system.mkdir(dir_path, mode))) {
|
||||
.SUCCESS => return,
|
||||
@ -2545,7 +2872,11 @@ pub const DeleteDirError = error{
|
||||
/// Deletes an empty directory.
|
||||
pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("rmdir is not supported in WASI; use unlinkat instead");
|
||||
return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) {
|
||||
error.FileSystem => unreachable, // only occurs when targeting files
|
||||
error.IsDir => unreachable, // only occurs when targeting files
|
||||
else => |e| return e,
|
||||
};
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return rmdirW(dir_path_w.span());
|
||||
@ -2560,6 +2891,8 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
return rmdirW(dir_path_w.span());
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return rmdir(mem.sliceTo(dir_path, 0));
|
||||
}
|
||||
switch (errno(system.rmdir(dir_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -2606,7 +2939,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) {
|
||||
@compileError("chdir is not supported in WASI");
|
||||
var preopen: ?Preopen = null;
|
||||
const path = try resolvePathAndGetWasiPreopen(dir_path, &preopen, &wasi_cwd.path_buffer);
|
||||
|
||||
const dirinfo = try fstatat(path.dir_fd, path.relative_path, 0);
|
||||
if (dirinfo.filetype != .DIRECTORY) {
|
||||
return error.NotDir;
|
||||
}
|
||||
|
||||
wasi_cwd.cwd_preopen = preopen;
|
||||
wasi_cwd.cwd = path;
|
||||
return;
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
|
||||
@ -2625,6 +2968,8 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
|
||||
const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
|
||||
if (len > utf16_dir_path.len) return error.NameTooLong;
|
||||
return chdirW(utf16_dir_path[0..len]);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return chdir(mem.sliceTo(dir_path, 0));
|
||||
}
|
||||
switch (errno(system.chdir(dir_path))) {
|
||||
.SUCCESS => return,
|
||||
@ -2655,15 +3000,29 @@ pub const FchdirError = error{
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn fchdir(dirfd: fd_t) FchdirError!void {
|
||||
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),
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2690,7 +3049,7 @@ pub const ReadLinkError = error{
|
||||
/// The return value is a slice of `out_buffer` from index 0.
|
||||
pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("readlink is not supported in WASI; use readlinkat instead");
|
||||
return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
@ -2711,6 +3070,8 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return readlink(mem.sliceTo(file_path, 0), out_buffer);
|
||||
}
|
||||
const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
@ -2733,6 +3094,12 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
|
||||
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
|
||||
pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
var path = try resolvePathWasi(file_path, &path_buf);
|
||||
return readlinkatWasi(path.dir_fd, path.relative_path, out_buffer);
|
||||
}
|
||||
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
@ -2775,6 +3142,8 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
|
||||
}
|
||||
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
@ -3727,7 +4096,14 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink
|
||||
/// See also `fstatatZ` and `fstatatWasi`.
|
||||
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return fstatatWasi(dirfd, pathname, flags);
|
||||
const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0;
|
||||
if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(pathname)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = try resolvePathWasi(pathname, &path_buf);
|
||||
return fstatatWasi(path.dir_fd, path.relative_path, wasi_flags);
|
||||
}
|
||||
return fstatatWasi(dirfd, pathname, wasi_flags);
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
@compileError("fstatat is not yet implemented on Windows");
|
||||
} else {
|
||||
@ -3758,6 +4134,10 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S
|
||||
/// Same as `fstatat` but `pathname` is null-terminated.
|
||||
/// See also `fstatat`.
|
||||
pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return fstatatWasi(dirfd, mem.sliceTo(pathname), flags);
|
||||
}
|
||||
|
||||
const fstatat_sym = if (builtin.os.tag == .linux and builtin.link_libc)
|
||||
system.fstatat64
|
||||
else
|
||||
@ -4056,6 +4436,8 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
|
||||
const path_w = try windows.sliceToPrefixedFileW(path);
|
||||
_ = try windows.GetFileAttributesW(path_w.span().ptr);
|
||||
return;
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return faccessat(wasi.AT.FDCWD, path, mode, 0);
|
||||
}
|
||||
const path_c = try toPosixPath(path);
|
||||
return accessZ(&path_c, mode);
|
||||
@ -4067,6 +4449,8 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
|
||||
const path_w = try windows.cStrToPrefixedFileW(path);
|
||||
_ = try windows.GetFileAttributesW(path_w.span().ptr);
|
||||
return;
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return access(mem.sliceTo(path, 0), mode);
|
||||
}
|
||||
switch (errno(system.access(path, mode))) {
|
||||
.SUCCESS => return,
|
||||
@ -4108,6 +4492,45 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr
|
||||
if (builtin.os.tag == .windows) {
|
||||
const path_w = try windows.sliceToPrefixedFileW(path);
|
||||
return faccessatW(dirfd, path_w.span().ptr, mode, flags);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
var resolved = RelativePath{ .dir_fd = dirfd, .relative_path = path };
|
||||
|
||||
const file = blk: {
|
||||
if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(path)) {
|
||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
resolved = resolvePathWasi(path, &path_buf) catch |err| break :blk @as(FStatAtError!Stat, err);
|
||||
break :blk fstatat(resolved.dir_fd, resolved.relative_path, flags);
|
||||
}
|
||||
break :blk fstatat(dirfd, path, flags);
|
||||
} catch |err| switch (err) {
|
||||
error.AccessDenied => return error.PermissionDenied,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
if (mode != F_OK) {
|
||||
var directory: wasi.fdstat_t = undefined;
|
||||
if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
|
||||
return error.PermissionDenied;
|
||||
}
|
||||
|
||||
var rights: wasi.rights_t = 0;
|
||||
if (mode & R_OK != 0) {
|
||||
rights |= if (file.filetype == .DIRECTORY)
|
||||
wasi.RIGHT.FD_READDIR
|
||||
else
|
||||
wasi.RIGHT.FD_READ;
|
||||
}
|
||||
if (mode & W_OK != 0) {
|
||||
rights |= wasi.RIGHT.FD_WRITE;
|
||||
}
|
||||
// No validation for X_OK
|
||||
|
||||
if ((rights & directory.fs_rights_inheriting) != rights) {
|
||||
return error.PermissionDenied;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const path_c = try toPosixPath(path);
|
||||
return faccessatZ(dirfd, &path_c, mode, flags);
|
||||
@ -4118,6 +4541,8 @@ pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) Acces
|
||||
if (builtin.os.tag == .windows) {
|
||||
const path_w = try windows.cStrToPrefixedFileW(path);
|
||||
return faccessatW(dirfd, path_w.span().ptr, mode, flags);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
|
||||
}
|
||||
switch (errno(system.faccessat(dirfd, path, mode, flags))) {
|
||||
.SUCCESS => return,
|
||||
@ -4645,6 +5070,9 @@ pub const RealPathError = error{
|
||||
SharingViolation,
|
||||
PipeBusy,
|
||||
|
||||
/// On WASI, the current CWD may not be associated with an absolute path.
|
||||
InvalidHandle,
|
||||
|
||||
/// On Windows, file paths must be valid Unicode.
|
||||
InvalidUtf8,
|
||||
|
||||
@ -4660,9 +5088,33 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
@compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
// NOTE: This emulation is incomplete. Symbolic links are not
|
||||
// currently expanded during path canonicalization.
|
||||
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,
|
||||
};
|
||||
if (!fs.path.isAbsolute(base_cwd_dir)) {
|
||||
// This preopen is not based on an absolute path, so we have
|
||||
// no way to know the absolute path of the CWD
|
||||
return error.InvalidHandle;
|
||||
}
|
||||
|
||||
const paths = &.{ base_cwd_dir, 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;
|
||||
}
|
||||
}
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return realpathZ(&pathname_c, out_buffer);
|
||||
@ -4673,6 +5125,8 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.cStrToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
return realpath(mem.sliceTo(pathname, 0), out_buffer);
|
||||
}
|
||||
if (!builtin.link_libc) {
|
||||
const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC;
|
||||
@ -4680,6 +5134,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
|
||||
error.FileLocksNotSupported => unreachable,
|
||||
error.WouldBlock => unreachable,
|
||||
error.FileBusy => unreachable, // not asking for write permissions
|
||||
error.InvalidHandle => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer close(fd);
|
||||
|
@ -22,7 +22,7 @@ const Dir = std.fs.Dir;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
test "chdir smoke test" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
if (native_os == .wasi) return error.SkipZigTest; // WASI doesn't allow navigating outside of a preopen
|
||||
|
||||
// Get current working directory path
|
||||
var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
@ -48,7 +48,8 @@ test "chdir smoke test" {
|
||||
}
|
||||
|
||||
test "open smoke test" {
|
||||
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, "/cwd");
|
||||
|
||||
// TODO verify file attributes using `fstat`
|
||||
|
||||
@ -102,7 +103,8 @@ test "open smoke test" {
|
||||
}
|
||||
|
||||
test "openat smoke test" {
|
||||
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, "/cwd");
|
||||
|
||||
// TODO verify file attributes using `fstatat`
|
||||
|
||||
@ -138,7 +140,8 @@ test "openat smoke test" {
|
||||
}
|
||||
|
||||
test "symlink with relative paths" {
|
||||
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, "/cwd");
|
||||
|
||||
const cwd = fs.cwd();
|
||||
cwd.deleteFile("file.txt") catch {};
|
||||
@ -190,6 +193,13 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
|
||||
|
||||
test "link with relative paths" {
|
||||
switch (native_os) {
|
||||
.wasi => {
|
||||
if (builtin.link_libc) {
|
||||
return error.SkipZigTest;
|
||||
} else {
|
||||
try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
}
|
||||
},
|
||||
.linux, .solaris => {},
|
||||
else => return error.SkipZigTest,
|
||||
}
|
||||
@ -212,14 +222,14 @@ test "link with relative paths" {
|
||||
const nstat = try os.fstat(nfd.handle);
|
||||
|
||||
try testing.expectEqual(estat.ino, nstat.ino);
|
||||
try testing.expectEqual(@as(usize, 2), nstat.nlink);
|
||||
try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
|
||||
}
|
||||
|
||||
try os.unlink("new.txt");
|
||||
|
||||
{
|
||||
const estat = try os.fstat(efd.handle);
|
||||
try testing.expectEqual(@as(usize, 1), estat.nlink);
|
||||
try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
|
||||
}
|
||||
|
||||
try cwd.deleteFile("example.txt");
|
||||
@ -227,6 +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, "/cwd"),
|
||||
.linux, .solaris => {},
|
||||
else => return error.SkipZigTest,
|
||||
}
|
||||
@ -250,14 +261,14 @@ test "linkat with different directories" {
|
||||
const nstat = try os.fstat(nfd.handle);
|
||||
|
||||
try testing.expectEqual(estat.ino, nstat.ino);
|
||||
try testing.expectEqual(@as(usize, 2), nstat.nlink);
|
||||
try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
|
||||
}
|
||||
|
||||
try os.unlinkat(tmp.dir.fd, "new.txt", 0);
|
||||
|
||||
{
|
||||
const estat = try os.fstat(efd.handle);
|
||||
try testing.expectEqual(@as(usize, 1), estat.nlink);
|
||||
try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
|
||||
}
|
||||
|
||||
try cwd.deleteFile("example.txt");
|
||||
@ -388,8 +399,6 @@ test "getrandom" {
|
||||
}
|
||||
|
||||
test "getcwd" {
|
||||
if (native_os == .wasi) return error.SkipZigTest;
|
||||
|
||||
// at least call it so it gets compiled
|
||||
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
||||
_ = os.getcwd(&buf) catch undefined;
|
||||
@ -878,3 +887,107 @@ test "POSIX file locking with fcntl" {
|
||||
try expect(result.status == 0 * 256);
|
||||
}
|
||||
}
|
||||
|
||||
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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// Get base abs path
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = undefined;
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `open`.
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Rename the file
|
||||
var new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
|
||||
try os.rename(file_path, new_file_path);
|
||||
|
||||
// Try opening renamed file
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
|
||||
fd = try os.open(file_path, os.O.RDWR, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening original file - should fail with error.FileNotFound
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
try expectError(error.FileNotFound, os.open(file_path, os.O.RDWR, mode));
|
||||
|
||||
// Create some directory
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try os.mkdir(file_path, mode);
|
||||
|
||||
// Rename the directory
|
||||
new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
|
||||
try os.rename(file_path, new_file_path);
|
||||
|
||||
// Try opening renamed directory
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
|
||||
fd = try os.open(file_path, os.O.RDONLY | os.O.DIRECTORY, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening original directory - should fail with error.FileNotFound
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try expectError(error.FileNotFound, os.open(file_path, os.O.RDONLY | os.O.DIRECTORY, mode));
|
||||
}
|
||||
|
||||
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, "/cwd");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// Get base abs path
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = undefined;
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `open`.
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try to access() the file
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
if (builtin.os.tag == .windows) {
|
||||
try os.access(file_path, os.F_OK);
|
||||
} else {
|
||||
try os.access(file_path, os.F_OK | os.W_OK | os.R_OK);
|
||||
}
|
||||
|
||||
// Try to access() a non-existent file - should fail with error.FileNotFound
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
|
||||
try expectError(error.FileNotFound, os.access(file_path, os.F_OK));
|
||||
|
||||
// Create some directory
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try os.mkdir(file_path, mode);
|
||||
|
||||
// Try to access() the directory
|
||||
file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try os.access(file_path, os.F_OK);
|
||||
}
|
||||
|
@ -15,6 +15,11 @@ comptime {
|
||||
// assert(@alignOf(u64) == 8);
|
||||
}
|
||||
|
||||
pub const F_OK = 0;
|
||||
pub const X_OK = 1;
|
||||
pub const W_OK = 2;
|
||||
pub const R_OK = 4;
|
||||
|
||||
pub const iovec_t = std.os.iovec;
|
||||
pub const ciovec_t = std.os.iovec_const;
|
||||
|
||||
|
@ -327,8 +327,8 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
|
||||
defer preopens.deinit();
|
||||
preopens.populate() 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");
|
||||
const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "/cwd" }) orelse
|
||||
@panic("unable to make tmp dir for testing: didn't find '/cwd' in the preopens");
|
||||
|
||||
return std.fs.Dir{ .fd = preopen.fd };
|
||||
} else {
|
||||
|
@ -369,6 +369,7 @@ fn detectAbiAndDynamicLinker(
|
||||
|
||||
error.IsDir,
|
||||
error.NotDir,
|
||||
error.InvalidHandle,
|
||||
error.AccessDenied,
|
||||
error.NoDevice,
|
||||
error.FileNotFound,
|
||||
@ -670,6 +671,7 @@ pub fn abiAndDynamicLinkerFromFile(
|
||||
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.InvalidHandle,
|
||||
error.AccessDenied,
|
||||
error.NoDevice,
|
||||
=> continue,
|
||||
|
@ -1188,6 +1188,7 @@ pub const TestContext = struct {
|
||||
.wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
|
||||
try argv.append(wasmtime_bin_name);
|
||||
try argv.append("--dir=.");
|
||||
try argv.append("--mapdir=/cwd::.");
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // wasmtime not available; pass test.
|
||||
|
Loading…
Reference in New Issue
Block a user