mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
WASI: remove absolute path emulation from std lib
Instead of checking for absolute paths and current working directories
in various file system operations, there is one simple solution: allow
overriding `std.fs.cwd` on WASI.
os.realpath is back to causing a compile error when used on WASI. This
caused a compile error in the Sema handling of `@src()`. The compiler
should never call realpath, so the commit that made this change is
reverted (95ab942184). If this breaks
debug info, a different strategy is needed to solve it other than using
realpath.
I also removed the preopens code and replaced it with something much
simpler. There is no longer any global state in the standard library.
Additionally-
* os.openat no longer does an unnecessary fstat on WASI when O.WRONLY
is not provided.
* os.chdir is back to causing a compile error on WASI.
This commit is contained in:
parent
0a2fdfbdb9
commit
d5312d53a0
6 changed files with 145 additions and 547 deletions
|
|
@ -1130,13 +1130,6 @@ pub const Dir = struct {
|
||||||
w.RIGHT.FD_FILESTAT_SET_TIMES |
|
w.RIGHT.FD_FILESTAT_SET_TIMES |
|
||||||
w.RIGHT.FD_FILESTAT_SET_SIZE;
|
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);
|
const fd = try os.openatWasi(self.fd, sub_path, 0x0, 0x0, fdflags, base, 0x0);
|
||||||
return File{ .handle = fd };
|
return File{ .handle = fd };
|
||||||
}
|
}
|
||||||
|
|
@ -1301,13 +1294,6 @@ pub const Dir = struct {
|
||||||
if (flags.exclusive) {
|
if (flags.exclusive) {
|
||||||
oflags |= w.O.EXCL;
|
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);
|
const fd = try os.openatWasi(self.fd, sub_path, 0x0, oflags, 0x0, base, 0x0);
|
||||||
return File{ .handle = fd };
|
return File{ .handle = fd };
|
||||||
}
|
}
|
||||||
|
|
@ -1711,16 +1697,15 @@ pub const Dir = struct {
|
||||||
// TODO do we really need all the rights here?
|
// TODO do we really need all the rights here?
|
||||||
const inheriting: w.rights_t = w.RIGHT.ALL ^ w.RIGHT.SOCK_SHUTDOWN;
|
const inheriting: w.rights_t = w.RIGHT.ALL ^ w.RIGHT.SOCK_SHUTDOWN;
|
||||||
|
|
||||||
const result = blk: {
|
const result = os.openatWasi(
|
||||||
if (self.fd == os.wasi.AT.FDCWD or path.isAbsolute(sub_path)) {
|
self.fd,
|
||||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
sub_path,
|
||||||
var resolved_path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
symlink_flags,
|
||||||
const resolved_path = try os.resolvePathWasi(sub_path, &resolved_path_buf);
|
w.O.DIRECTORY,
|
||||||
break :blk os.openatWasi(resolved_path.dir_fd, resolved_path.relative_path, symlink_flags, w.O.DIRECTORY, 0x0, base, inheriting);
|
0x0,
|
||||||
} else {
|
base,
|
||||||
break :blk os.openatWasi(self.fd, sub_path, symlink_flags, w.O.DIRECTORY, 0x0, base, inheriting);
|
inheriting,
|
||||||
}
|
);
|
||||||
};
|
|
||||||
const fd = result catch |err| switch (err) {
|
const fd = result catch |err| switch (err) {
|
||||||
error.FileTooBig => unreachable, // can't happen for directories
|
error.FileTooBig => unreachable, // can't happen for directories
|
||||||
error.IsDir => unreachable, // we're providing O.DIRECTORY
|
error.IsDir => unreachable, // we're providing O.DIRECTORY
|
||||||
|
|
@ -2667,6 +2652,8 @@ pub const Dir = struct {
|
||||||
pub fn cwd() Dir {
|
pub fn cwd() Dir {
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
|
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
|
||||||
|
} else if (builtin.os.tag == .wasi and @hasDecl(root, "wasi_cwd")) {
|
||||||
|
return root.wasi_cwd();
|
||||||
} else {
|
} else {
|
||||||
return Dir{ .fd = os.AT.FDCWD };
|
return Dir{ .fd = os.AT.FDCWD };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,284 +10,47 @@ const wasi = std.os.wasi;
|
||||||
const fd_t = wasi.fd_t;
|
const fd_t = wasi.fd_t;
|
||||||
const prestat_t = wasi.prestat_t;
|
const prestat_t = wasi.prestat_t;
|
||||||
|
|
||||||
/// Type-tag of WASI preopen.
|
pub const Preopens = struct {
|
||||||
///
|
// Indexed by file descriptor number.
|
||||||
/// WASI currently offers only `Dir` as a valid preopen resource.
|
names: []const []const u8,
|
||||||
pub const PreopenTypeTag = enum {
|
|
||||||
Dir,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Type of WASI preopen.
|
pub fn find(p: Preopens, name: []const u8) ?os.fd_t {
|
||||||
///
|
for (p.names) |elem_name, i| {
|
||||||
/// WASI currently offers only `Dir` as a valid preopen resource.
|
if (mem.eql(u8, elem_name, name)) {
|
||||||
pub const PreopenType = union(PreopenTypeTag) {
|
return @intCast(os.fd_t, i);
|
||||||
/// Preopened directory type.
|
|
||||||
Dir: []const u8,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn eql(self: Self, other: PreopenType) bool {
|
|
||||||
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`
|
|
||||||
//
|
|
||||||
// Expects `other` to be a canonical path, not containing "." or ".."
|
|
||||||
pub fn getRelativePath(self: Self, other: PreopenType) ?[]const u8 {
|
|
||||||
if (std.meta.activeTag(self) != std.meta.activeTag(other)) return null;
|
|
||||||
|
|
||||||
switch (self) {
|
|
||||||
PreopenTypeTag.Dir => |self_path| {
|
|
||||||
const other_path = other.Dir;
|
|
||||||
if (mem.indexOfDiff(u8, self_path, other_path)) |index| {
|
|
||||||
if (index < self_path.len) return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rel_path = other_path[self_path.len..];
|
|
||||||
if (rel_path.len == 0) {
|
|
||||||
return rel_path;
|
|
||||||
} else if (rel_path[0] == '/') {
|
|
||||||
return rel_path[1..];
|
|
||||||
} else {
|
|
||||||
if (self_path[self_path.len - 1] != '/') return null;
|
|
||||||
return rel_path;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void {
|
|
||||||
if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
|
|
||||||
_ = options;
|
|
||||||
try out_stream.print("PreopenType{{ ", .{});
|
|
||||||
switch (self) {
|
|
||||||
PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{std.zig.fmtId(path)}),
|
|
||||||
}
|
|
||||||
return out_stream.print(" }}", .{});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// WASI preopen struct. This struct consists of a WASI file descriptor
|
|
||||||
/// and type of WASI preopen. It can be obtained directly from the WASI
|
|
||||||
/// runtime using `PreopenList.populate()` method.
|
|
||||||
pub const Preopen = struct {
|
|
||||||
/// WASI file descriptor.
|
|
||||||
fd: fd_t,
|
|
||||||
|
|
||||||
/// Type of the preopen.
|
|
||||||
type: PreopenType,
|
|
||||||
|
|
||||||
/// Construct new `Preopen` instance.
|
|
||||||
pub fn new(fd: fd_t, preopen_type: PreopenType) Preopen {
|
|
||||||
return Preopen{
|
|
||||||
.fd = fd,
|
|
||||||
.type = preopen_type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
/// collecting the returned preopens.
|
|
||||||
///
|
|
||||||
/// This struct is intended to be used in any WASI program which intends
|
|
||||||
/// to use the capabilities as passed on by the user of the runtime.
|
|
||||||
pub const PreopenList = struct {
|
|
||||||
const InnerList = std.ArrayList(Preopen);
|
|
||||||
|
|
||||||
/// Internal dynamically-sized buffer for storing the gathered preopens.
|
|
||||||
buffer: InnerList,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Error = error{ OutOfMemory, Overflow } || os.UnexpectedError;
|
|
||||||
|
|
||||||
/// Deinitialize with `deinit`.
|
|
||||||
pub fn init(allocator: Allocator) Self {
|
|
||||||
return Self{ .buffer = InnerList.init(allocator) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Release all allocated memory.
|
|
||||||
pub fn deinit(pm: Self) void {
|
|
||||||
for (pm.buffer.items) |preopen| {
|
|
||||||
switch (preopen.type) {
|
|
||||||
PreopenType.Dir => |path| pm.buffer.allocator.free(path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pm.buffer.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Populate the list with the preopens by issuing `std.os.wasi.fd_prestat_get`
|
|
||||||
/// and `std.os.wasi.fd_prestat_dir_name` syscalls to the runtime.
|
|
||||||
///
|
|
||||||
/// If called more than once, it will clear its contents every time before
|
|
||||||
/// issuing the syscalls.
|
|
||||||
///
|
|
||||||
/// In the unlinkely event of overflowing the number of available file descriptors,
|
|
||||||
/// returns `error.Overflow`. In this case, even though an error condition was reached
|
|
||||||
/// the preopen list still contains all valid preopened file descriptors that are valid
|
|
||||||
/// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally,
|
|
||||||
/// `deinit` still must be called!
|
|
||||||
///
|
|
||||||
/// Usage of `cwd_root`:
|
|
||||||
/// If provided, `cwd_root` is inserted as prefix for any Preopens that
|
|
||||||
/// begin with "." and all paths are normalized as POSIX-style absolute
|
|
||||||
/// paths. `cwd_root` must be an absolute path.
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
/// "./foo/bar" -> "{cwd_root}/foo/bar"
|
|
||||||
/// "foo/bar" -> "/foo/bar"
|
|
||||||
/// "/foo/bar" -> "/foo/bar"
|
|
||||||
///
|
|
||||||
/// If `cwd_root` is not provided, all preopen directories are unmodified.
|
|
||||||
///
|
|
||||||
pub fn populate(self: *Self, cwd_root: ?[]const u8) Error!void {
|
|
||||||
if (cwd_root) |root| assert(fs.path.isAbsolute(root));
|
|
||||||
|
|
||||||
// Clear contents if we're being called again
|
|
||||||
for (try self.toOwnedSlice()) |preopen| {
|
|
||||||
switch (preopen.type) {
|
|
||||||
PreopenType.Dir => |path| self.buffer.allocator.free(path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errdefer self.deinit();
|
|
||||||
var fd: fd_t = 3; // start fd has to be beyond stdio fds
|
|
||||||
|
|
||||||
var path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
|
|
||||||
while (true) {
|
|
||||||
var buf: prestat_t = undefined;
|
|
||||||
switch (wasi.fd_prestat_get(fd, &buf)) {
|
|
||||||
.SUCCESS => {},
|
|
||||||
.OPNOTSUPP => {
|
|
||||||
// not a preopen, so keep going
|
|
||||||
fd = try math.add(fd_t, fd, 1);
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
.BADF => {
|
|
||||||
// OK, no more fds available
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => |err| return os.unexpectedErrno(err),
|
|
||||||
}
|
|
||||||
const preopen_len = buf.u.dir.pr_name_len;
|
|
||||||
|
|
||||||
mem.set(u8, path_buf[0..preopen_len], 0);
|
|
||||||
switch (wasi.fd_prestat_dir_name(fd, &path_buf, preopen_len)) {
|
|
||||||
.SUCCESS => {},
|
|
||||||
else => |err| return os.unexpectedErrno(err),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unfortunately, WASI runtimes (e.g. wasmer) are not consistent about whether the
|
|
||||||
// NULL sentinel is included in the reported Preopen name_len
|
|
||||||
const raw_path = if (path_buf[preopen_len - 1] == 0) blk: {
|
|
||||||
break :blk path_buf[0 .. preopen_len - 1];
|
|
||||||
} else path_buf[0..preopen_len];
|
|
||||||
|
|
||||||
// If we were provided a CWD root to resolve against, we try to treat Preopen dirs as
|
|
||||||
// POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "."
|
|
||||||
const path = if (cwd_root) |cwd| blk: {
|
|
||||||
const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path };
|
|
||||||
break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths);
|
|
||||||
} else blk: {
|
|
||||||
// If we were provided no CWD root, we preserve the preopen dir without resolving
|
|
||||||
break :blk try self.buffer.allocator.dupe(u8, raw_path);
|
|
||||||
};
|
|
||||||
errdefer self.buffer.allocator.free(path);
|
|
||||||
const preopen = Preopen.new(fd, .{ .Dir = path });
|
|
||||||
|
|
||||||
try self.buffer.append(preopen);
|
|
||||||
fd = try math.add(fd_t, fd, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find a preopen which includes access to `preopen_type`.
|
|
||||||
///
|
|
||||||
/// If multiple preopens match the provided resource, the most specific
|
|
||||||
/// match is returned. More recent preopens take priority, as well.
|
|
||||||
pub fn findContaining(self: Self, preopen_type: PreopenType) ?PreopenUri {
|
|
||||||
var best_match: ?PreopenUri = null;
|
|
||||||
|
|
||||||
for (self.buffer.items) |preopen| {
|
|
||||||
if (preopen.type.getRelativePath(preopen_type)) |rel_path| {
|
|
||||||
if (best_match == null or rel_path.len <= best_match.?.relative_path.len) {
|
|
||||||
best_match = PreopenUri{
|
|
||||||
.base = preopen,
|
|
||||||
.relative_path = if (rel_path.len == 0) "." else rel_path,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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;
|
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 {
|
|
||||||
for (self.buffer.items) |*preopen| {
|
|
||||||
if (preopen.type.eql(preopen_type)) {
|
|
||||||
return preopen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the inner buffer as read-only slice.
|
|
||||||
pub fn asSlice(self: Self) []const Preopen {
|
|
||||||
return self.buffer.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The caller owns the returned memory. ArrayList becomes empty.
|
|
||||||
pub fn toOwnedSlice(self: *Self) ![]Preopen {
|
|
||||||
return try self.buffer.toOwnedSlice();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
test "extracting WASI preopens" {
|
pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens {
|
||||||
if (builtin.os.tag != .wasi or builtin.link_libc) return error.SkipZigTest;
|
var names: std.ArrayListUnmanaged([]const u8) = .{};
|
||||||
|
defer names.deinit(gpa);
|
||||||
|
|
||||||
var preopens = PreopenList.init(std.testing.allocator);
|
try names.ensureUnusedCapacity(gpa, 3);
|
||||||
defer preopens.deinit();
|
|
||||||
|
|
||||||
try preopens.populate(null);
|
names.appendAssumeCapacity("stdin"); // 0
|
||||||
|
names.appendAssumeCapacity("stdout"); // 1
|
||||||
const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
|
names.appendAssumeCapacity("stderr"); // 2
|
||||||
try std.testing.expect(preopen.type.eql(PreopenType{ .Dir = "." }));
|
while (true) {
|
||||||
|
const fd = @intCast(wasi.fd_t, names.items.len);
|
||||||
const po_type1 = PreopenType{ .Dir = "/" };
|
var prestat: prestat_t = undefined;
|
||||||
try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/" }).?, ""));
|
switch (wasi.fd_prestat_get(fd, &prestat)) {
|
||||||
try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/test/foobar" }).?, "test/foobar"));
|
.SUCCESS => {},
|
||||||
|
.OPNOTSUPP, .BADF => return .{ .names = names.toOwnedSlice(gpa) },
|
||||||
const po_type2 = PreopenType{ .Dir = "/test/foo" };
|
else => @panic("fd_prestat_get: unexpected error"),
|
||||||
try std.testing.expect(po_type2.getRelativePath(.{ .Dir = "/test/foobar" }) == null);
|
}
|
||||||
|
try names.ensureUnusedCapacity(gpa, 1);
|
||||||
const po_type3 = PreopenType{ .Dir = "/test" };
|
// This length does not include a null byte. Let's keep it this way to
|
||||||
try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test" }).?, ""));
|
// gently encourage WASI implementations to behave properly.
|
||||||
try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/" }).?, ""));
|
const name_len = prestat.u.dir.pr_name_len;
|
||||||
try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/foo/bar" }).?, "foo/bar"));
|
const name = try gpa.alloc(u8, name_len);
|
||||||
|
errdefer gpa.free(name);
|
||||||
|
switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) {
|
||||||
|
.SUCCESS => {},
|
||||||
|
else => @panic("fd_prestat_dir_name: unexpected error"),
|
||||||
|
}
|
||||||
|
names.appendAssumeCapacity(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
212
lib/std/os.zig
212
lib/std/os.zig
|
|
@ -1521,76 +1521,6 @@ 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,
|
|
||||||
// The absolute path associated with the current working directory
|
|
||||||
cwd: []const u8 = "/",
|
|
||||||
}{} 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.
|
|
||||||
///
|
|
||||||
/// The current working directory is initialized to `cwd_root`, and `cwd_root`
|
|
||||||
/// is inserted as a prefix for any Preopens whose dir begins with "."
|
|
||||||
/// For example:
|
|
||||||
/// "./foo/bar" - canonicalizes to -> "{cwd_root}/foo/bar"
|
|
||||||
/// "foo/bar" - canonicalizes to -> "/foo/bar"
|
|
||||||
/// "/foo/bar" - canonicalizes to -> "/foo/bar"
|
|
||||||
///
|
|
||||||
/// `cwd_root` must be an absolute path. For initialization behavior similar to
|
|
||||||
/// wasi-libc, use "/" as the `cwd_root`
|
|
||||||
///
|
|
||||||
/// `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_root: []const u8) !void {
|
|
||||||
if (builtin.os.tag == .wasi) {
|
|
||||||
if (!builtin.link_libc) {
|
|
||||||
var preopen_list = PreopenList.init(alloc);
|
|
||||||
errdefer preopen_list.deinit();
|
|
||||||
try preopen_list.populate(cwd_root);
|
|
||||||
|
|
||||||
var path_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
|
|
||||||
wasi_cwd.cwd = try path_alloc.allocator().dupe(u8, cwd_root);
|
|
||||||
|
|
||||||
if (wasi_cwd.preopens) |preopens| preopens.deinit();
|
|
||||||
wasi_cwd.preopens = preopen_list;
|
|
||||||
} else {
|
|
||||||
// wasi-libc defaults to an effective CWD root of "/"
|
|
||||||
if (!mem.eql(u8, cwd_root, "/")) return error.UnsupportedDirectory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
/// Automatically looks up the correct Preopen corresponding to the provided path.
|
|
||||||
pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi {
|
|
||||||
var allocator = std.heap.FixedBufferAllocator.init(out_buffer);
|
|
||||||
var alloc = allocator.allocator();
|
|
||||||
|
|
||||||
const abs_path = fs.path.resolve(alloc, &.{ wasi_cwd.cwd, path }) catch return error.NameTooLong;
|
|
||||||
const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path });
|
|
||||||
|
|
||||||
if (preopen_uri) |po| {
|
|
||||||
return RelativePathWasi{
|
|
||||||
.dir_fd = po.base.fd,
|
|
||||||
.relative_path = po.relative_path,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// No matching preopen found
|
|
||||||
return error.AccessDenied;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||||
/// See also `openatZ`.
|
/// See also `openatZ`.
|
||||||
|
|
@ -1600,22 +1530,23 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope
|
||||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
// `mode` is ignored on WASI, which does not support unix-style file permissions
|
// `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: {
|
const opts = try openOptionsFromFlagsWasi(dir_fd, flags);
|
||||||
// Resolve absolute or CWD-relative paths to a path within a Preopen
|
const fd = try openatWasi(
|
||||||
var path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
dir_fd,
|
||||||
const path = try resolvePathWasi(file_path, &path_buf);
|
file_path,
|
||||||
|
opts.lookup_flags,
|
||||||
const opts = try openOptionsFromFlagsWasi(path.dir_fd, flags);
|
opts.oflags,
|
||||||
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);
|
opts.fs_flags,
|
||||||
} else blk: {
|
opts.fs_rights_base,
|
||||||
const opts = try openOptionsFromFlagsWasi(dir_fd, flags);
|
opts.fs_rights_inheriting,
|
||||||
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);
|
errdefer close(fd);
|
||||||
|
|
||||||
const info = try fstat(fd);
|
if (flags & O.WRONLY != 0) {
|
||||||
if (flags & O.WRONLY != 0 and info.filetype == .DIRECTORY)
|
const info = try fstat(fd);
|
||||||
return error.IsDir;
|
if (info.filetype == .DIRECTORY)
|
||||||
|
return error.IsDir;
|
||||||
|
}
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
@ -1673,7 +1604,15 @@ fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open and possibly create a file in WASI.
|
/// 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 {
|
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) {
|
while (true) {
|
||||||
var fd: fd_t = undefined;
|
var fd: fd_t = undefined;
|
||||||
switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
|
switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
|
||||||
|
|
@ -2031,7 +1970,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
return windows.GetCurrentDirectory(out_buffer);
|
return windows.GetCurrentDirectory(out_buffer);
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
const path = wasi_cwd.cwd;
|
const path = ".";
|
||||||
if (out_buffer.len < path.len) return error.NameTooLong;
|
if (out_buffer.len < path.len) return error.NameTooLong;
|
||||||
std.mem.copy(u8, out_buffer, path);
|
std.mem.copy(u8, out_buffer, path);
|
||||||
return out_buffer[0..path.len];
|
return out_buffer[0..path.len];
|
||||||
|
|
@ -2125,12 +2064,6 @@ pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
@compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
@compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} 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);
|
return symlinkatWasi(target_path, newdirfd, sym_link_path);
|
||||||
}
|
}
|
||||||
const target_path_c = try toPosixPath(target_path);
|
const target_path_c = try toPosixPath(target_path);
|
||||||
|
|
@ -2284,25 +2217,8 @@ pub fn linkat(
|
||||||
flags: i32,
|
flags: i32,
|
||||||
) LinkatError!void {
|
) LinkatError!void {
|
||||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
var resolve_olddir: bool = (olddir == wasi.AT.FDCWD or fs.path.isAbsolute(oldpath));
|
const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
|
||||||
var resolve_newdir: bool = (newdir == wasi.AT.FDCWD or fs.path.isAbsolute(newpath));
|
const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = 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);
|
return linkatWasi(old, new, flags);
|
||||||
}
|
}
|
||||||
const old = try toPosixPath(oldpath);
|
const old = try toPosixPath(oldpath);
|
||||||
|
|
@ -2423,12 +2339,6 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
|
||||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||||
return unlinkatW(dirfd, file_path_w.span(), flags);
|
return unlinkatW(dirfd, file_path_w.span(), flags);
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} 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);
|
return unlinkatWasi(dirfd, file_path, flags);
|
||||||
} else {
|
} else {
|
||||||
const file_path_c = try toPosixPath(file_path);
|
const file_path_c = try toPosixPath(file_path);
|
||||||
|
|
@ -2597,24 +2507,8 @@ pub fn renameat(
|
||||||
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
|
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);
|
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) {
|
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
var resolve_old: bool = (old_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(old_path));
|
const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
|
||||||
var resolve_new: bool = (new_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(new_path));
|
const new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = 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);
|
return renameatWasi(old, new);
|
||||||
} else {
|
} else {
|
||||||
const old_path_c = try toPosixPath(old_path);
|
const old_path_c = try toPosixPath(old_path);
|
||||||
|
|
@ -2755,12 +2649,6 @@ 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);
|
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
|
||||||
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} 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);
|
return mkdiratWasi(dir_fd, sub_dir_path, mode);
|
||||||
} else {
|
} else {
|
||||||
const sub_dir_path_c = try toPosixPath(sub_dir_path);
|
const sub_dir_path_c = try toPosixPath(sub_dir_path);
|
||||||
|
|
@ -2997,22 +2885,7 @@ pub const ChangeCurDirError = error{
|
||||||
/// `dir_path` is recommended to be a UTF-8 encoded string.
|
/// `dir_path` is recommended to be a UTF-8 encoded string.
|
||||||
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
|
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
|
||||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
var buf: [MAX_PATH_BYTES]u8 = undefined;
|
@compileError("WASI does not support os.chdir");
|
||||||
var alloc = std.heap.FixedBufferAllocator.init(&buf);
|
|
||||||
const path = fs.path.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path }) catch |err| switch (err) {
|
|
||||||
error.OutOfMemory => return error.NameTooLong,
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
|
|
||||||
const dirinfo = try fstatat(AT.FDCWD, path, 0);
|
|
||||||
if (dirinfo.filetype != .DIRECTORY) {
|
|
||||||
return error.NotDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This copy is guaranteed to succeed, since buf and path_buffer are the same size.
|
|
||||||
var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer);
|
|
||||||
wasi_cwd.cwd = cwd_alloc.allocator().dupe(u8, path) catch unreachable;
|
|
||||||
return;
|
|
||||||
} else if (builtin.os.tag == .windows) {
|
} else if (builtin.os.tag == .windows) {
|
||||||
var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
|
var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||||
const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
|
const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
|
||||||
|
|
@ -3143,12 +3016,6 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
|
||||||
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
|
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
|
||||||
pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
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 (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);
|
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||||
}
|
}
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
|
|
@ -4155,12 +4022,6 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink
|
||||||
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
|
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
|
||||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0;
|
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);
|
return fstatatWasi(dirfd, pathname, wasi_flags);
|
||||||
} else if (builtin.os.tag == .windows) {
|
} else if (builtin.os.tag == .windows) {
|
||||||
@compileError("fstatat is not yet implemented on Windows");
|
@compileError("fstatat is not yet implemented on Windows");
|
||||||
|
|
@ -4556,12 +4417,6 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr
|
||||||
var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path };
|
var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path };
|
||||||
|
|
||||||
const file = blk: {
|
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);
|
break :blk fstatat(dirfd, path, flags);
|
||||||
} catch |err| switch (err) {
|
} catch |err| switch (err) {
|
||||||
error.AccessDenied => return error.PermissionDenied,
|
error.AccessDenied => return error.PermissionDenied,
|
||||||
|
|
@ -5147,12 +5002,7 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
|
||||||
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
||||||
return realpathW(pathname_w.span(), out_buffer);
|
return realpathW(pathname_w.span(), out_buffer);
|
||||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
var alloc = std.heap.FixedBufferAllocator.init(out_buffer);
|
@compileError("WASI does not support os.realpath");
|
||||||
|
|
||||||
// NOTE: This emulation is incomplete. Symbolic links are not
|
|
||||||
// currently expanded during path canonicalization.
|
|
||||||
const paths = &.{ wasi_cwd.cwd, pathname };
|
|
||||||
return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong;
|
|
||||||
}
|
}
|
||||||
const pathname_c = try toPosixPath(pathname);
|
const pathname_c = try toPosixPath(pathname);
|
||||||
return realpathZ(&pathname_c, out_buffer);
|
return realpathZ(&pathname_c, out_buffer);
|
||||||
|
|
|
||||||
11
src/Sema.zig
11
src/Sema.zig
|
|
@ -15082,14 +15082,11 @@ fn zirBuiltinSrc(
|
||||||
const file_name_val = blk: {
|
const file_name_val = blk: {
|
||||||
var anon_decl = try block.startAnonDecl();
|
var anon_decl = try block.startAnonDecl();
|
||||||
defer anon_decl.deinit();
|
defer anon_decl.deinit();
|
||||||
const relative_path = try fn_owner_decl.getFileScope().fullPath(sema.arena);
|
// The compiler must not call realpath anywhere.
|
||||||
const absolute_path = std.fs.realpathAlloc(sema.arena, relative_path) catch |err| {
|
const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena());
|
||||||
return sema.fail(block, src, "failed to get absolute path of file '{s}': {s}", .{ relative_path, @errorName(err) });
|
|
||||||
};
|
|
||||||
const aboslute_duped = try anon_decl.arena().dupeZ(u8, absolute_path);
|
|
||||||
const new_decl = try anon_decl.finish(
|
const new_decl = try anon_decl.finish(
|
||||||
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), aboslute_duped.len),
|
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len),
|
||||||
try Value.Tag.bytes.create(anon_decl.arena(), aboslute_duped[0 .. aboslute_duped.len + 1]),
|
try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]),
|
||||||
0, // default alignment
|
0, // default alignment
|
||||||
);
|
);
|
||||||
break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl);
|
break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl);
|
||||||
|
|
|
||||||
|
|
@ -37,34 +37,7 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory {
|
||||||
/// based on a hard-coded Preopen directory ("/zig")
|
/// based on a hard-coded Preopen directory ("/zig")
|
||||||
pub fn findZigExePath(allocator: mem.Allocator) ![]u8 {
|
pub fn findZigExePath(allocator: mem.Allocator) ![]u8 {
|
||||||
if (builtin.os.tag == .wasi) {
|
if (builtin.os.tag == .wasi) {
|
||||||
var args = try std.process.argsWithAllocator(allocator);
|
@compileError("this function is unsupported on WASI");
|
||||||
defer args.deinit();
|
|
||||||
// On WASI, argv[0] is always just the basename of the current executable
|
|
||||||
const argv0 = args.next() orelse return error.FileNotFound;
|
|
||||||
|
|
||||||
// Check these paths:
|
|
||||||
// 1. "/zig/{exe_name}"
|
|
||||||
// 2. "/zig/bin/{exe_name}"
|
|
||||||
const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" };
|
|
||||||
const exe_names_to_check = &[_][]const u8{ fs.path.basename(argv0), "zig.wasm" };
|
|
||||||
|
|
||||||
for (base_paths_to_check) |base_path| {
|
|
||||||
for (exe_names_to_check) |exe_name| {
|
|
||||||
const test_path = fs.path.join(allocator, &.{ base_path, exe_name }) catch continue;
|
|
||||||
defer allocator.free(test_path);
|
|
||||||
|
|
||||||
// Make sure it's a file we're pointing to
|
|
||||||
const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue;
|
|
||||||
if (file.filetype != .REGULAR_FILE) continue;
|
|
||||||
|
|
||||||
// Path seems to be valid, let's try to turn it into an absolute path
|
|
||||||
var real_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
|
|
||||||
if (os.realpath(test_path, &real_path_buf)) |real_path| {
|
|
||||||
return allocator.dupe(u8, real_path); // Success: return absolute path
|
|
||||||
} else |_| continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return error.FileNotFound;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.selfExePathAlloc(allocator);
|
return fs.selfExePathAlloc(allocator);
|
||||||
|
|
@ -107,6 +80,9 @@ pub fn findZigLibDirFromSelfExe(
|
||||||
|
|
||||||
/// Caller owns returned memory.
|
/// Caller owns returned memory.
|
||||||
pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
|
pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
|
||||||
|
if (builtin.os.tag == .wasi) {
|
||||||
|
@compileError("on WASI the global cache dir must be resolved with preopens");
|
||||||
|
}
|
||||||
if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| {
|
if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| {
|
||||||
if (value.len > 0) {
|
if (value.len > 0) {
|
||||||
return value;
|
return value;
|
||||||
|
|
@ -125,17 +101,7 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (builtin.os.tag == .wasi) {
|
return fs.getAppDataDir(allocator, appname);
|
||||||
// On WASI, we have no way to get an App data dir, so we try to use a fixed
|
|
||||||
// Preopen path "/cache" as a last resort
|
|
||||||
const path = "/cache";
|
|
||||||
|
|
||||||
const file = os.fstatat(os.wasi.AT.FDCWD, path, 0) catch return error.CacheDirUnavailable;
|
|
||||||
if (file.filetype != .DIRECTORY) return error.CacheDirUnavailable;
|
|
||||||
return allocator.dupe(u8, path);
|
|
||||||
} else {
|
|
||||||
return fs.getAppDataDir(allocator, appname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to std.fs.path.resolve, with a few important differences:
|
/// Similar to std.fs.path.resolve, with a few important differences:
|
||||||
|
|
|
||||||
85
src/main.zig
85
src/main.zig
|
|
@ -27,6 +27,23 @@ const crash_report = @import("crash_report.zig");
|
||||||
// Crash report needs to override the panic handler and other root decls
|
// Crash report needs to override the panic handler and other root decls
|
||||||
pub usingnamespace crash_report.root_decls;
|
pub usingnamespace crash_report.root_decls;
|
||||||
|
|
||||||
|
var wasi_preopens: fs.wasi.Preopens = undefined;
|
||||||
|
pub inline fn wasi_cwd() fs.Dir {
|
||||||
|
// Expect the first preopen to be current working directory.
|
||||||
|
const cwd_fd: std.os.fd_t = 3;
|
||||||
|
assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
|
||||||
|
return .{ .fd = cwd_fd };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getWasiPreopen(name: []const u8) Compilation.Directory {
|
||||||
|
return .{
|
||||||
|
.path = name,
|
||||||
|
.handle = .{
|
||||||
|
.fd = wasi_preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
||||||
std.log.err(format, args);
|
std.log.err(format, args);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
@ -161,20 +178,14 @@ pub fn main() anyerror!void {
|
||||||
return mainArgs(gpa_tracy.allocator(), arena, args);
|
return mainArgs(gpa_tracy.allocator(), arena, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WASI: `--dir` instructs the WASM runtime to "preopen" a directory, making
|
|
||||||
// it available to the us, the guest program. This is the only way for us to
|
|
||||||
// access files/dirs on the host filesystem
|
|
||||||
if (builtin.os.tag == .wasi) {
|
if (builtin.os.tag == .wasi) {
|
||||||
// This sets our CWD to "/preopens/cwd"
|
wasi_preopens = try fs.wasi.preopensAlloc(arena);
|
||||||
// Dot-prefixed preopens like `--dir=.` are "mounted" at "/preopens/cwd"
|
|
||||||
// Other preopens like `--dir=lib` are "mounted" at "/"
|
|
||||||
try std.os.initPreopensWasi(arena, "/preopens/cwd");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short circuit some of the other logic for bootstrapping.
|
// Short circuit some of the other logic for bootstrapping.
|
||||||
if (build_options.only_c) {
|
if (build_options.only_c) {
|
||||||
assert(mem.eql(u8, args[1], "build-obj"));
|
assert(mem.eql(u8, args[1], "build-exe"));
|
||||||
return buildOutputType(gpa, arena, args, .{ .build = .Obj });
|
return buildOutputType(gpa, arena, args, .{ .build = .Exe });
|
||||||
}
|
}
|
||||||
|
|
||||||
return mainArgs(gpa, arena, args);
|
return mainArgs(gpa, arena, args);
|
||||||
|
|
@ -2300,7 +2311,7 @@ fn buildOutputType(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.fs.path.isAbsolute(lib_name)) {
|
if (fs.path.isAbsolute(lib_name)) {
|
||||||
fatal("cannot use absolute path as a system library: {s}", .{lib_name});
|
fatal("cannot use absolute path as a system library: {s}", .{lib_name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2763,18 +2774,33 @@ fn buildOutputType(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const self_exe_path = try introspect.findZigExePath(arena);
|
const self_exe_path: ?[]const u8 = if (!process.can_spawn)
|
||||||
var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |unresolved_lib_dir| l: {
|
null
|
||||||
const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
|
else
|
||||||
break :l .{
|
introspect.findZigExePath(arena) catch |err| {
|
||||||
.path = lib_dir,
|
fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
|
||||||
.handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
|
|
||||||
fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
} else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
|
|
||||||
fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
|
var zig_lib_directory: Compilation.Directory = d: {
|
||||||
|
if (override_lib_dir) |unresolved_lib_dir| {
|
||||||
|
const lib_dir = try introspect.resolvePath(arena, unresolved_lib_dir);
|
||||||
|
break :d .{
|
||||||
|
.path = lib_dir,
|
||||||
|
.handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
|
||||||
|
fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (builtin.os.tag == .wasi) {
|
||||||
|
break :d getWasiPreopen("/lib");
|
||||||
|
} else if (self_exe_path) |p| {
|
||||||
|
break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
|
||||||
|
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
defer zig_lib_directory.handle.close();
|
defer zig_lib_directory.handle.close();
|
||||||
|
|
||||||
var thread_pool: ThreadPool = undefined;
|
var thread_pool: ThreadPool = undefined;
|
||||||
|
|
@ -2791,7 +2817,16 @@ fn buildOutputType(
|
||||||
}
|
}
|
||||||
|
|
||||||
var global_cache_directory: Compilation.Directory = l: {
|
var global_cache_directory: Compilation.Directory = l: {
|
||||||
const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
|
if (override_global_cache_dir) |p| {
|
||||||
|
break :l .{
|
||||||
|
.handle = try fs.cwd().makeOpenPath(p, .{}),
|
||||||
|
.path = p,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (builtin.os.tag == .wasi) {
|
||||||
|
break :l getWasiPreopen("/cache");
|
||||||
|
}
|
||||||
|
const p = try introspect.resolveGlobalCacheDir(arena);
|
||||||
break :l .{
|
break :l .{
|
||||||
.handle = try fs.cwd().makeOpenPath(p, .{}),
|
.handle = try fs.cwd().makeOpenPath(p, .{}),
|
||||||
.path = p,
|
.path = p,
|
||||||
|
|
@ -3082,7 +3117,7 @@ fn buildOutputType(
|
||||||
gpa,
|
gpa,
|
||||||
arena,
|
arena,
|
||||||
test_exec_args.items,
|
test_exec_args.items,
|
||||||
self_exe_path,
|
self_exe_path.?,
|
||||||
arg_mode,
|
arg_mode,
|
||||||
target_info,
|
target_info,
|
||||||
watch,
|
watch,
|
||||||
|
|
@ -3154,7 +3189,7 @@ fn buildOutputType(
|
||||||
gpa,
|
gpa,
|
||||||
arena,
|
arena,
|
||||||
test_exec_args.items,
|
test_exec_args.items,
|
||||||
self_exe_path,
|
self_exe_path.?,
|
||||||
arg_mode,
|
arg_mode,
|
||||||
target_info,
|
target_info,
|
||||||
watch,
|
watch,
|
||||||
|
|
@ -3179,7 +3214,7 @@ fn buildOutputType(
|
||||||
gpa,
|
gpa,
|
||||||
arena,
|
arena,
|
||||||
test_exec_args.items,
|
test_exec_args.items,
|
||||||
self_exe_path,
|
self_exe_path.?,
|
||||||
arg_mode,
|
arg_mode,
|
||||||
target_info,
|
target_info,
|
||||||
watch,
|
watch,
|
||||||
|
|
@ -3523,7 +3558,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void
|
||||||
defer tree.deinit(comp.gpa);
|
defer tree.deinit(comp.gpa);
|
||||||
|
|
||||||
if (out_dep_path) |dep_file_path| {
|
if (out_dep_path) |dep_file_path| {
|
||||||
const dep_basename = std.fs.path.basename(dep_file_path);
|
const dep_basename = fs.path.basename(dep_file_path);
|
||||||
// Add the files depended on to the cache system.
|
// Add the files depended on to the cache system.
|
||||||
try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
|
try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
|
||||||
// Just to save disk space, we delete the file because it is never needed again.
|
// Just to save disk space, we delete the file because it is never needed again.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue