diff options
| author | Cody Tapscott <topolarity@tapscott.me> | 2022-03-02 23:21:55 -0700 |
|---|---|---|
| committer | Cody Tapscott <topolarity@tapscott.me> | 2022-03-03 14:31:49 -0700 |
| commit | ade2d0c6a28009ac505b4863079ede3ba64fcdd1 (patch) | |
| tree | 9a6a203309c5a601fe6067bf7e8e442822066a45 /lib/std | |
| parent | 58f961f4cb9875bbce3070969438ecf08f392c9f (diff) | |
| download | zig-ade2d0c6a28009ac505b4863079ede3ba64fcdd1.tar.gz zig-ade2d0c6a28009ac505b4863079ede3ba64fcdd1.zip | |
stdlib WASI: Add realpath() support for non-absolute Preopens
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/build.zig | 2 | ||||
| -rw-r--r-- | lib/std/fs/path.zig | 8 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 14 | ||||
| -rw-r--r-- | lib/std/fs/wasi.zig | 15 | ||||
| -rw-r--r-- | lib/std/os.zig | 112 | ||||
| -rw-r--r-- | lib/std/os/test.zig | 14 | ||||
| -rw-r--r-- | lib/std/testing.zig | 4 |
7 files changed, 95 insertions, 74 deletions
diff --git a/lib/std/build.zig b/lib/std/build.zig index 2c81f9dcd4..07e9055b1d 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -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 { diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 07ca298927..bdd1c53ed5 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -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", ".."); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 9791165eae..69fbe5449f 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -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(); diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 061a745855..4754ded630 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -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 = "." })); } diff --git a/lib/std/os.zig b/lib/std/os.zig index 5b904f6905..5c443934be 100644 --- a/lib/std/os.zig +++ b/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,35 +5091,45 @@ 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; - } + return realpathWasi(pathname, out_buffer); + } + const pathname_c = try toPosixPath(pathname); + return realpathZ(&pathname_c, out_buffer); +} - 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; - } +/// 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 { - return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong; + // The CWD is not rooted to an existing Preopen, + // so we have no way to know its absolute path + return error.InvalidHandle; } + } else { + return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong; } - const pathname_c = try toPosixPath(pathname); - return realpathZ(&pathname_c, out_buffer); } /// Same as `realpath` except `pathname` is null-terminated. diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 479794d53a..348f5ffaa8 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -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(); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 8e5a75feb7..8d08792801 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -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 { |
