Merge pull request #11021 from topolarity/wasi-cwd
stdlib: Add emulated CWD to std.os for WASI targets
This commit is contained in:
@@ -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, ".");
|
||||
|
||||
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, ".");
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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,49 @@ 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 fd. If the preopen exists, return it.
|
||||
/// Otherwise, return `null`.
|
||||
pub fn findByFd(self: Self, fd: fd_t) ?Preopen {
|
||||
for (self.buffer.items) |preopen| {
|
||||
if (preopen.fd == fd) {
|
||||
return preopen;
|
||||
}
|
||||
}
|
||||
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 +235,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);
|
||||
}
|
||||
|
||||
550
lib/std/os.zig
550
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,17 @@ pub const LOG = struct {
|
||||
pub const DEBUG = 7;
|
||||
};
|
||||
|
||||
/// An fd-relative file path
|
||||
///
|
||||
/// This is currently only used for WASI-specific functionality, but the concept
|
||||
/// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions.
|
||||
pub const RelativePathWasi = 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 +1259,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 +1323,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 +1336,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 +1374,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 +1414,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 +1423,204 @@ 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: ?RelativePathWasi = 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`.
|
||||
/// Note that `cwd_init` corresponds to a Preopen directory, not necessarily
|
||||
/// a POSIX path. For example, "." matches a Preopen provided with `--dir=.`
|
||||
///
|
||||
/// 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 = RelativePathWasi{
|
||||
.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) !RelativePathWasi {
|
||||
// Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi
|
||||
// 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) !RelativePathWasi {
|
||||
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 (mem.startsWith(u8, path, "/preopens/fd/")) {
|
||||
// "/preopens/fd/<N>" is a special prefix, which refers to a Preopen directly by fd
|
||||
const fd_start = "/preopens/fd/".len;
|
||||
const fd_end = mem.indexOfScalarPos(u8, path, fd_start, '/') orelse path.len;
|
||||
const fd = std.fmt.parseUnsigned(fd_t, path[fd_start..fd_end], 10) catch unreachable;
|
||||
const rel_path = if (path.len > fd_end + 1) path[fd_end + 1 ..] else ".";
|
||||
|
||||
if (preopen) |p| p.* = wasi_cwd.preopens.?.findByFd(fd);
|
||||
return RelativePathWasi{
|
||||
.dir_fd = fd,
|
||||
.relative_path = alloc.dupe(u8, rel_path) catch return error.NameTooLong,
|
||||
};
|
||||
}
|
||||
|
||||
// For any other absolute path, we need to lookup a containing Preopen
|
||||
const abs_path = 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 RelativePathWasi{
|
||||
.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 = 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 RelativePathWasi{
|
||||
.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);
|
||||
}
|
||||
|
||||
/// A struct to contain all lookup/rights flags accepted by `wasi.path_open`
|
||||
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 +1660,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 +1708,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 +1976,15 @@ 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 buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const path = realpathWasi(".", &buf) catch |err| switch (err) {
|
||||
error.NameTooLong => return error.NameTooLong,
|
||||
error.InvalidHandle => return error.CurrentWorkingDirectoryUnlinked,
|
||||
};
|
||||
if (out_buffer.len < path.len) return error.NameTooLong;
|
||||
std.mem.copy(u8, out_buffer, path);
|
||||
return out_buffer[0..path.len];
|
||||
}
|
||||
|
||||
const err = if (builtin.link_libc) blk: {
|
||||
@@ -1809,11 +2027,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 +2042,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 +2072,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 +2117,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 +2156,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 +2181,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 +2201,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 +2233,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: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
|
||||
var new: RelativePathWasi = .{ .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: RelativePathWasi, new: RelativePathWasi, 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 +2316,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 +2334,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 +2373,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 +2423,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 +2483,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 +2501,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 +2547,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: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
|
||||
var new: RelativePathWasi = .{ .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 +2575,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: RelativePathWasi, new: RelativePathWasi) 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 +2612,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 +2704,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 +2744,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 +2804,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 +2822,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 +2879,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 +2898,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 +2946,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 +2975,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 +3007,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 +3056,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 +3077,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 +3101,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 +3149,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 +4103,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 +4141,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 +4443,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 +4456,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 +4499,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 = RelativePathWasi{ .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 +4548,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 +5077,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,19 +5095,55 @@ 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) {
|
||||
return realpathWasi(pathname, out_buffer);
|
||||
}
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return realpathZ(&pathname_c, out_buffer);
|
||||
}
|
||||
|
||||
/// Return an emulated canonicalized absolute pathname on WASI.
|
||||
///
|
||||
/// NOTE: This emulation is incomplete. Symbolic links are not
|
||||
/// currently expanded during path canonicalization.
|
||||
fn realpathWasi(pathname: []const u8, out_buffer: []u8) ![]u8 {
|
||||
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,
|
||||
};
|
||||
const paths: [][]const u8 = if (fs.path.isAbsolute(base_cwd_dir)) blk: {
|
||||
break :blk &.{ base_cwd_dir, cwd.relative_path, pathname };
|
||||
} else blk: {
|
||||
// No absolute path is associated with this preopen, so
|
||||
// instead we use a special "/preopens/fd/<N>/" prefix
|
||||
var buf: [16]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
std.fmt.formatInt(po.fd, 10, .lower, .{}, fbs.writer()) catch return error.NameTooLong;
|
||||
break :blk &.{ "/preopens/fd/", fbs.getWritten(), 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `realpath` except `pathname` is null-terminated.
|
||||
pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
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 +5151,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, ".");
|
||||
|
||||
// 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, ".");
|
||||
|
||||
// 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, ".");
|
||||
|
||||
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, ".");
|
||||
}
|
||||
},
|
||||
.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, "."),
|
||||
.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, ".");
|
||||
|
||||
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, ".");
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user