mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
stdlib WASI: Add realpath() support for non-absolute Preopens
This commit is contained in:
parent
58f961f4cb
commit
ade2d0c6a2
@ -2707,8 +2707,6 @@ pub const LibExeObjStep = struct {
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--dir=.");
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--mapdir=/cwd::.");
|
||||
try zig_args.append("--test-cmd");
|
||||
try zig_args.append("--allow-unknown-exports"); // TODO: Remove when stage2 is default compiler
|
||||
try zig_args.append("--test-cmd-bin");
|
||||
} else {
|
||||
|
||||
@ -735,7 +735,7 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
|
||||
|
||||
test "resolve" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
const cwd = try process.getCwdAlloc(testing.allocator);
|
||||
defer testing.allocator.free(cwd);
|
||||
@ -756,7 +756,7 @@ test "resolveWindows" {
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .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);
|
||||
@ -802,7 +802,7 @@ test "resolveWindows" {
|
||||
|
||||
test "resolvePosix" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .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");
|
||||
@ -1216,7 +1216,7 @@ test "relative" {
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .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", "..");
|
||||
|
||||
@ -47,7 +47,7 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo
|
||||
|
||||
test "accessAbsolute" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -66,7 +66,7 @@ test "accessAbsolute" {
|
||||
|
||||
test "openDirAbsolute" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -103,7 +103,7 @@ test "openDir cwd parent .." {
|
||||
|
||||
test "readLinkAbsolute" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -512,7 +512,7 @@ test "rename" {
|
||||
|
||||
test "renameAbsolute" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
@ -947,7 +947,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
|
||||
|
||||
test "walker" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{ .iterate = true });
|
||||
defer tmp.cleanup();
|
||||
@ -998,7 +998,7 @@ test "walker" {
|
||||
|
||||
test ". and .. in fs.Dir functions" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -1027,7 +1027,7 @@ test ". and .. in fs.Dir functions" {
|
||||
|
||||
test ". and .. in absolute functions" {
|
||||
if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
@ -194,6 +194,17 @@ pub const PreopenList = struct {
|
||||
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 {
|
||||
@ -224,6 +235,6 @@ test "extracting WASI preopens" {
|
||||
|
||||
try preopens.populate();
|
||||
|
||||
const preopen = preopens.find(PreopenType{ .Dir = "/cwd" }) orelse unreachable;
|
||||
try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "/cwd" }));
|
||||
const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
|
||||
try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." }));
|
||||
}
|
||||
|
||||
114
lib/std/os.zig
114
lib/std/os.zig
@ -1431,6 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct {
|
||||
}{} 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.
|
||||
@ -1482,8 +1484,22 @@ fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffe
|
||||
if (wasi_cwd.preopens == null) @panic("On WASI, `initPreopensWasi` must be called to initialize preopens " ++
|
||||
"before using any CWD-relative or absolute paths.\n");
|
||||
|
||||
// If the path is absolute, we need to lookup a containing Preopen
|
||||
const abs_path = std.fs.path.resolve(alloc, &.{ "/", path }) catch return error.NameTooLong;
|
||||
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 RelativePath{
|
||||
.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| {
|
||||
@ -1507,7 +1523,7 @@ fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffe
|
||||
// First resolve a combined path, where the "/" corresponds to `cwd.dir_fd`
|
||||
// not the true filesystem root
|
||||
const paths = &.{ "/", cwd.relative_path, path };
|
||||
const resolved_path = std.fs.path.resolve(alloc, paths) catch return error.NameTooLong;
|
||||
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..];
|
||||
@ -1956,28 +1972,14 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.GetCurrentDirectory(out_buffer);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
var allocator = std.heap.FixedBufferAllocator.init(out_buffer);
|
||||
var alloc = allocator.allocator();
|
||||
if (wasi_cwd.cwd) |cwd| {
|
||||
if (wasi_cwd.cwd_preopen) |po| {
|
||||
var base_cwd_dir = switch (po.@"type") {
|
||||
.Dir => |dir| dir,
|
||||
};
|
||||
if (!fs.path.isAbsolute(base_cwd_dir)) {
|
||||
// This preopen is not based on an absolute path, so we have
|
||||
// no way to know the absolute path of the CWD
|
||||
return error.CurrentWorkingDirectoryUnlinked;
|
||||
}
|
||||
const paths = &.{ base_cwd_dir, cwd.relative_path };
|
||||
return std.fs.path.resolve(alloc, paths) catch return error.NameTooLong;
|
||||
} else {
|
||||
// The CWD is not rooted to an existing Preopen,
|
||||
// so we have no way to know its absolute path
|
||||
return error.CurrentWorkingDirectoryUnlinked;
|
||||
}
|
||||
} else {
|
||||
return alloc.dupe(u8, "/") catch return error.NameTooLong;
|
||||
}
|
||||
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: {
|
||||
@ -5089,37 +5091,47 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
|
||||
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
} else if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||
// NOTE: This emulation is incomplete. Symbolic links are not
|
||||
// currently expanded during path canonicalization.
|
||||
var alloc = std.heap.FixedBufferAllocator.init(out_buffer);
|
||||
if (fs.path.isAbsolute(pathname))
|
||||
return try fs.path.resolve(alloc.allocator(), &.{pathname}) catch error.NameTooLong;
|
||||
if (wasi_cwd.cwd) |cwd| {
|
||||
if (wasi_cwd.cwd_preopen) |po| {
|
||||
var base_cwd_dir = switch (po.@"type") {
|
||||
.Dir => |dir| dir,
|
||||
};
|
||||
if (!fs.path.isAbsolute(base_cwd_dir)) {
|
||||
// This preopen is not based on an absolute path, so we have
|
||||
// no way to know the absolute path of the CWD
|
||||
return error.InvalidHandle;
|
||||
}
|
||||
|
||||
const paths = &.{ base_cwd_dir, cwd.relative_path, pathname };
|
||||
return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong;
|
||||
} else {
|
||||
// The CWD is not rooted to an existing Preopen,
|
||||
// so we have no way to know its absolute path
|
||||
return error.InvalidHandle;
|
||||
}
|
||||
} else {
|
||||
return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong;
|
||||
}
|
||||
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) {
|
||||
|
||||
@ -49,7 +49,7 @@ test "chdir smoke test" {
|
||||
|
||||
test "open smoke test" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
// TODO verify file attributes using `fstat`
|
||||
|
||||
@ -104,7 +104,7 @@ test "open smoke test" {
|
||||
|
||||
test "openat smoke test" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
// TODO verify file attributes using `fstatat`
|
||||
|
||||
@ -141,7 +141,7 @@ test "openat smoke test" {
|
||||
|
||||
test "symlink with relative paths" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
const cwd = fs.cwd();
|
||||
cwd.deleteFile("file.txt") catch {};
|
||||
@ -197,7 +197,7 @@ test "link with relative paths" {
|
||||
if (builtin.link_libc) {
|
||||
return error.SkipZigTest;
|
||||
} else {
|
||||
try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
}
|
||||
},
|
||||
.linux, .solaris => {},
|
||||
@ -237,7 +237,7 @@ test "link with relative paths" {
|
||||
|
||||
test "linkat with different directories" {
|
||||
switch (native_os) {
|
||||
.wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd"),
|
||||
.wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."),
|
||||
.linux, .solaris => {},
|
||||
else => return error.SkipZigTest,
|
||||
}
|
||||
@ -890,7 +890,7 @@ test "POSIX file locking with fcntl" {
|
||||
|
||||
test "rename smoke test" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@ -947,7 +947,7 @@ test "rename smoke test" {
|
||||
|
||||
test "access smoke test" {
|
||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/cwd");
|
||||
if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, ".");
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
@ -327,8 +327,8 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
|
||||
defer preopens.deinit();
|
||||
preopens.populate() catch
|
||||
@panic("unable to make tmp dir for testing: unable to populate preopens");
|
||||
const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "/cwd" }) orelse
|
||||
@panic("unable to make tmp dir for testing: didn't find '/cwd' in the preopens");
|
||||
const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
|
||||
@panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
|
||||
|
||||
return std.fs.Dir{ .fd = preopen.fd };
|
||||
} else {
|
||||
|
||||
@ -1188,7 +1188,6 @@ pub const TestContext = struct {
|
||||
.wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
|
||||
try argv.append(wasmtime_bin_name);
|
||||
try argv.append("--dir=.");
|
||||
try argv.append("--mapdir=/cwd::.");
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // wasmtime not available; pass test.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user