Commit e73170f972

Andrew Kelley <andrew@ziglang.org>
2022-12-05 23:52:44
std: fix WASI regressions
This branch largely reverts 58f961f4cb9875bbce3070969438ecf08f392c9f. I would like to revisit the proposal to modify the standard library in this way and think more carefully about it before adding isAbsolute() checks everywhere.
1 parent cb01249
Changed files (5)
lib/std/fs/test.zig
@@ -48,8 +48,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, "/");
+    if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
@@ -67,8 +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, "/");
+    if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
@@ -104,8 +102,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, "/");
+    if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
@@ -187,8 +184,6 @@ test "Dir.Iterator" {
 }
 
 test "Dir.Iterator many entries" {
-    if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
-
     var tmp_dir = tmpIterableDir(.{});
     defer tmp_dir.cleanup();
 
@@ -638,8 +633,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, "/");
+    if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
     var tmp_dir = tmpDir(.{});
     defer tmp_dir.cleanup();
@@ -1149,7 +1143,6 @@ 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, "/");
 
     var tmp = tmpIterableDir(.{});
     defer tmp.cleanup();
@@ -1203,7 +1196,6 @@ test "walker" {
 
 test "walker without fully iterating" {
     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 = tmpIterableDir(.{});
     defer tmp.cleanup();
@@ -1227,7 +1219,6 @@ test "walker without fully iterating" {
 
 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, "/");
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
@@ -1255,8 +1246,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, "/");
+    if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
lib/std/os/test.zig
@@ -22,8 +22,7 @@ const Dir = std.fs.Dir;
 const ArenaAllocator = std.heap.ArenaAllocator;
 
 test "chdir 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, "/preopens/cwd");
+    if (native_os == .wasi) return error.SkipZigTest;
 
     // Get current working directory path
     var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
@@ -75,8 +74,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, "/");
+    if (native_os == .wasi) return error.SkipZigTest;
 
     // TODO verify file attributes using `fstat`
 
@@ -131,7 +129,6 @@ 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, "/");
 
     // TODO verify file attributes using `fstatat`
 
@@ -168,7 +165,6 @@ 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, "/");
 
     const cwd = fs.cwd();
     cwd.deleteFile("file.txt") catch {};
@@ -219,15 +215,10 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
 }
 
 test "link with relative paths" {
+    if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
+
     switch (native_os) {
-        .wasi => {
-            if (builtin.link_libc) {
-                return error.SkipZigTest;
-            } else {
-                try os.initPreopensWasi(std.heap.page_allocator, "/");
-            }
-        },
-        .linux, .solaris => {},
+        .wasi, .linux, .solaris => {},
         else => return error.SkipZigTest,
     }
     var cwd = fs.cwd();
@@ -263,9 +254,10 @@ test "link with relative paths" {
 }
 
 test "linkat with different directories" {
+    if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
+
     switch (native_os) {
-        .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"),
-        .linux, .solaris => {},
+        .wasi, .linux, .solaris => {},
         else => return error.SkipZigTest,
     }
     var cwd = fs.cwd();
@@ -950,8 +942,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, "/");
+    if (native_os == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
@@ -1007,8 +998,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, "/");
+    if (native_os == .wasi) return error.SkipZigTest;
 
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
lib/std/os/wasi.zig
@@ -1,6 +1,7 @@
 // wasi_snapshot_preview1 spec available (in witx format) here:
 // * typenames -- https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx
 // * module -- https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/wasi_snapshot_preview1.witx
+const builtin = @import("builtin");
 const std = @import("std");
 const assert = std.debug.assert;
 
@@ -157,7 +158,12 @@ pub const IOV_MAX = 1024;
 
 pub const AT = struct {
     pub const REMOVEDIR: u32 = 0x4;
-    pub const FDCWD: fd_t = -2;
+    /// When linking libc, we follow their convention and use -2 for current working directory.
+    /// However, without libc, Zig does a different convention: it assumes the
+    /// current working directory is the first preopen. This behavior can be
+    /// overridden with a public function called `wasi_cwd` in the root source
+    /// file.
+    pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
 };
 
 // As defined in the wasi_snapshot_preview1 spec file:
lib/std/fs.zig
@@ -1488,19 +1488,7 @@ 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) {
-            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;
-            }
+            @compileError("realpath is not available on WASI");
         }
         if (builtin.os.tag == .windows) {
             const pathname_w = try os.windows.sliceToPrefixedFileW(pathname);
@@ -2652,8 +2640,13 @@ 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 @hasDecl(root, "wasi_cwd")) {
-        return root.wasi_cwd();
+    } else if (builtin.os.tag == .wasi) {
+        if (@hasDecl(root, "wasi_cwd")) {
+            return root.wasi_cwd();
+        } else {
+            // Expect the first preopen to be current working directory.
+            return .{ .fd = 3 };
+        }
     } else {
         return Dir{ .fd = os.AT.FDCWD };
     }
lib/std/os.zig
@@ -2032,7 +2032,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
     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));
+        return symlinkatZ(target_path, fs.cwd().fd, sym_link_path);
     }
     switch (errno(system.symlink(target_path, sym_link_path))) {
         .SUCCESS => return,
@@ -2078,6 +2078,7 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c
         .SUCCESS => {},
         .FAULT => unreachable,
         .INVAL => unreachable,
+        .BADF => unreachable,
         .ACCES => return error.AccessDenied,
         .PERM => return error.AccessDenied,
         .DQUOT => return error.DiskQuota,