From d4215ffaa04b976400bd597cca0cca8182068bf6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Oct 2025 22:50:30 -0700 Subject: [PATCH] std.Io.Threaded: implement dirCreateFile for WASI --- lib/std/Io/Threaded.zig | 70 +++++++++++++++++++++++++- lib/std/fs/Dir.zig | 25 --------- lib/std/posix.zig | 109 +--------------------------------------- 3 files changed, 70 insertions(+), 134 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 743cb845a9cec0f55c8ae189fa41ac96e2f59ccc..59f7d4270ca58dbe347e83df32ec4de29da83e1c 100644 --- a/lib/std/Io/Threaded.zig +++ b/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, diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index c7699a83cfd86b4f84d8b8ba27b9da7cec82f0c6..6da4bab684a2a1d3634cfbc2942670eb1ae2464a 100644 --- a/lib/std/fs/Dir.zig +++ b/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); diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 67a3e787f7a76c3fb8dde27be35cc144c5032da0..9d58a4f643096570d04a8e3b357ee6bf52bcf30b 100644 --- a/lib/std/posix.zig +++ b/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/). -- 2.54.0