Commit d4215ffaa0

Andrew Kelley <andrew@ziglang.org>
2025-10-17 07:50:30
std.Io.Threaded: implement dirCreateFile for WASI
1 parent 1431275
Changed files (3)
lib/std/fs/Dir.zig
@@ -938,31 +938,6 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File
         const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
         return self.createFileW(path_w.span(), flags);
     }
-    if (native_os == .wasi) {
-        return .{
-            .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{
-                .CREAT = true,
-                .TRUNC = flags.truncate,
-                .EXCL = flags.exclusive,
-            }, .{}, .{
-                .FD_READ = flags.read,
-                .FD_WRITE = true,
-                .FD_DATASYNC = true,
-                .FD_SEEK = true,
-                .FD_TELL = true,
-                .FD_FDSTAT_SET_FLAGS = true,
-                .FD_SYNC = true,
-                .FD_ALLOCATE = true,
-                .FD_ADVISE = true,
-                .FD_FILESTAT_SET_TIMES = true,
-                .FD_FILESTAT_SET_SIZE = true,
-                .FD_FILESTAT_GET = true,
-                // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or
-                // FD_WRITE is also set.
-                .POLL_FD_READWRITE = true,
-            }, .{}),
-        };
-    }
     var threaded: Io.Threaded = .init_single_threaded;
     const io = threaded.io();
     const new_file = try Io.Dir.createFile(self.adaptToNewApi(), io, sub_path, flags);
lib/std/Io/Threaded.zig
@@ -190,7 +190,7 @@ pub fn io(t: *Threaded) Io {
             },
             .dirCreateFile = switch (builtin.os.tag) {
                 .windows => @panic("TODO"),
-                .wasi => @panic("TODO"),
+                .wasi => dirCreateFileWasi,
                 else => dirCreateFilePosix,
             },
             .dirOpenFile = dirOpenFile,
@@ -1378,6 +1378,74 @@ fn dirCreateFilePosix(
     return .{ .handle = fd };
 }
 
+fn dirCreateFileWasi(
+    userdata: ?*anyopaque,
+    dir: Io.Dir,
+    sub_path: []const u8,
+    flags: Io.File.CreateFlags,
+) Io.File.OpenError!Io.File {
+    const t: *Threaded = @ptrCast(@alignCast(userdata));
+    const wasi = std.os.wasi;
+    const lookup_flags: wasi.lookupflags_t = .{};
+    const oflags: wasi.oflags_t = .{
+        .CREAT = true,
+        .TRUNC = flags.truncate,
+        .EXCL = flags.exclusive,
+    };
+    const fdflags: wasi.fdflags_t = .{};
+    const base: wasi.rights_t = .{
+        .FD_READ = flags.read,
+        .FD_WRITE = true,
+        .FD_DATASYNC = true,
+        .FD_SEEK = true,
+        .FD_TELL = true,
+        .FD_FDSTAT_SET_FLAGS = true,
+        .FD_SYNC = true,
+        .FD_ALLOCATE = true,
+        .FD_ADVISE = true,
+        .FD_FILESTAT_SET_TIMES = true,
+        .FD_FILESTAT_SET_SIZE = true,
+        .FD_FILESTAT_GET = true,
+        // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or
+        // FD_WRITE is also set.
+        .POLL_FD_READWRITE = true,
+    };
+    const inheriting: wasi.rights_t = .{};
+    var fd: posix.fd_t = undefined;
+    while (true) {
+        try t.checkCancel();
+        switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
+            .SUCCESS => return .{ .handle = fd },
+            .INTR => continue,
+            .CANCELED => return error.Canceled,
+
+            .FAULT => |err| return errnoBug(err),
+            // Provides INVAL with a linux host on a bad path name, but NOENT on Windows
+            .INVAL => return error.BadPathName,
+            .BADF => |err| return errnoBug(err),
+            .ACCES => return error.AccessDenied,
+            .FBIG => return error.FileTooBig,
+            .OVERFLOW => return error.FileTooBig,
+            .ISDIR => return error.IsDir,
+            .LOOP => return error.SymLinkLoop,
+            .MFILE => return error.ProcessFdQuotaExceeded,
+            .NAMETOOLONG => return error.NameTooLong,
+            .NFILE => return error.SystemFdQuotaExceeded,
+            .NODEV => return error.NoDevice,
+            .NOENT => return error.FileNotFound,
+            .NOMEM => return error.SystemResources,
+            .NOSPC => return error.NoSpaceLeft,
+            .NOTDIR => return error.NotDir,
+            .PERM => return error.PermissionDenied,
+            .EXIST => return error.PathAlreadyExists,
+            .BUSY => return error.DeviceBusy,
+            .NOTCAPABLE => return error.AccessDenied,
+            .ILSEQ => return error.BadPathName,
+            else => |err| return posix.unexpectedErrno(err),
+        }
+    }
+}
+
 fn dirOpenFile(
     userdata: ?*anyopaque,
     dir: Io.Dir,
lib/std/posix.zig
@@ -1610,119 +1610,12 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenE
     if (native_os == .windows) {
         @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
     } else if (native_os == .wasi and !builtin.link_libc) {
-        // `mode` is ignored on WASI, which does not support unix-style file permissions
-        const opts = try openOptionsFromFlagsWasi(flags);
-        const fd = 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);
-
-        if (flags.write) {
-            const info = try std.os.fstat_wasi(fd);
-            if (info.filetype == .DIRECTORY)
-                return error.IsDir;
-        }
-
-        return fd;
+        @compileError("use std.Io instead");
     }
     const file_path_c = try toPosixPath(file_path);
     return openatZ(dir_fd, &file_path_c, flags, mode);
 }
 
-/// Open and possibly create a file in WASI.
-pub fn openatWasi(
-    dir_fd: fd_t,
-    file_path: []const u8,
-    lookup_flags: wasi.lookupflags_t,
-    oflags: wasi.oflags_t,
-    fdflags: wasi.fdflags_t,
-    base: wasi.rights_t,
-    inheriting: wasi.rights_t,
-) OpenError!fd_t {
-    while (true) {
-        var fd: fd_t = undefined;
-        switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
-            .SUCCESS => return fd,
-            .INTR => continue,
-
-            .FAULT => unreachable,
-            // Provides INVAL with a linux host on a bad path name, but NOENT on Windows
-            .INVAL => return error.BadPathName,
-            .BADF => unreachable,
-            .ACCES => return error.AccessDenied,
-            .FBIG => return error.FileTooBig,
-            .OVERFLOW => return error.FileTooBig,
-            .ISDIR => return error.IsDir,
-            .LOOP => return error.SymLinkLoop,
-            .MFILE => return error.ProcessFdQuotaExceeded,
-            .NAMETOOLONG => return error.NameTooLong,
-            .NFILE => return error.SystemFdQuotaExceeded,
-            .NODEV => return error.NoDevice,
-            .NOENT => return error.FileNotFound,
-            .NOMEM => return error.SystemResources,
-            .NOSPC => return error.NoSpaceLeft,
-            .NOTDIR => return error.NotDir,
-            .PERM => return error.PermissionDenied,
-            .EXIST => return error.PathAlreadyExists,
-            .BUSY => return error.DeviceBusy,
-            .NOTCAPABLE => return error.AccessDenied,
-            .ILSEQ => return error.BadPathName,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-/// A struct to contain all lookup/rights flags accepted by `wasi.path_open`
-const WasiOpenOptions = struct {
-    oflags: wasi.oflags_t,
-    lookup_flags: wasi.lookupflags_t,
-    fs_rights_base: wasi.rights_t,
-    fs_rights_inheriting: wasi.rights_t,
-    fs_flags: wasi.fdflags_t,
-};
-
-/// Compute rights + flags corresponding to the provided POSIX access mode.
-fn openOptionsFromFlagsWasi(oflag: O) OpenError!WasiOpenOptions {
-    const w = std.os.wasi;
-
-    // Next, calculate the read/write rights to request, depending on the
-    // provided POSIX access mode
-    var rights: w.rights_t = .{};
-    if (oflag.read) {
-        rights.FD_READ = true;
-        rights.FD_READDIR = true;
-    }
-    if (oflag.write) {
-        rights.FD_DATASYNC = true;
-        rights.FD_WRITE = true;
-        rights.FD_ALLOCATE = true;
-        rights.FD_FILESTAT_SET_SIZE = true;
-    }
-
-    // https://github.com/ziglang/zig/issues/18882
-    const flag_bits: u32 = @bitCast(oflag);
-    const oflags_int: u16 = @as(u12, @truncate(flag_bits >> 12));
-    const fs_flags_int: u16 = @as(u12, @truncate(flag_bits));
-
-    return .{
-        // https://github.com/ziglang/zig/issues/18882
-        .oflags = @bitCast(oflags_int),
-        .lookup_flags = .{
-            .SYMLINK_FOLLOW = !oflag.NOFOLLOW,
-        },
-        .fs_rights_base = rights,
-        .fs_rights_inheriting = rights,
-        // https://github.com/ziglang/zig/issues/18882
-        .fs_flags = @bitCast(fs_flags_int),
-    };
-}
-
 /// Open and possibly create a file. Keeps trying if it gets interrupted.
 /// `file_path` is relative to the open directory handle `dir_fd`.
 /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).