diff --git a/lib/std/Io.zig b/lib/std/Io.zig index a20868afe2..2c0fd77dea 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -883,6 +883,10 @@ pub const Timestamp = struct { return .{ .nanoseconds = t.nanoseconds, .clock = clock }; } + pub fn fromNanoseconds(x: i96) Timestamp { + return .{ .nanoseconds = x }; + } + pub fn toSeconds(t: Timestamp) i64 { return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s)); } diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 6a5eab2bdf..743cb845a9 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -167,7 +167,7 @@ pub fn io(t: *Threaded) Io { .dirMake = switch (builtin.os.tag) { .windows => @panic("TODO"), - .wasi => @panic("TODO"), + .wasi => dirMakeWasi, else => dirMakePosix, }, .dirStat = dirStat, @@ -906,6 +906,37 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: } } +fn dirMakeWasi(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { + if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode); + const t: *Threaded = @ptrCast(@alignCast(userdata)); + while (true) { + try t.checkCancel(); + switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) { + .SUCCESS => return, + .INTR => continue, + .CANCELED => return error.Canceled, + + .ACCES => return error.AccessDenied, + .BADF => |err| return errnoBug(err), + .PERM => return error.PermissionDenied, + .DQUOT => return error.DiskQuota, + .EXIST => return error.PathAlreadyExists, + .FAULT => |err| return errnoBug(err), + .LOOP => return error.SymLinkLoop, + .MLINK => return error.LinkQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + } +} + fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); try t.checkCancel(); @@ -1005,13 +1036,13 @@ fn dirStatPathWasi( const t: *Threaded = @ptrCast(@alignCast(userdata)); const wasi = std.os.wasi; const flags: wasi.lookupflags_t = .{ - .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), + .SYMLINK_FOLLOW = options.follow_symlinks, }; var stat: wasi.filestat_t = undefined; while (true) { try t.checkCancel(); switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { - .SUCCESS => return statFromWasi(stat), + .SUCCESS => return statFromWasi(&stat), .INTR => continue, .CANCELED => return error.Canceled, @@ -1166,19 +1197,19 @@ fn dirAccessWasi( userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, - options: Io.File.OpenFlags, -) Io.File.AccessError!void { + options: Io.Dir.AccessOptions, +) Io.Dir.AccessError!void { if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options); const t: *Threaded = @ptrCast(@alignCast(userdata)); const wasi = std.os.wasi; const flags: wasi.lookupflags_t = .{ - .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), + .SYMLINK_FOLLOW = options.follow_symlinks, }; - const stat = while (true) { - var stat: wasi.filestat_t = undefined; + var stat: wasi.filestat_t = undefined; + while (true) { try t.checkCancel(); switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { - .SUCCESS => break statFromWasi(stat), + .SUCCESS => break, .INTR => continue, .CANCELED => return error.Canceled, @@ -1194,9 +1225,9 @@ fn dirAccessWasi( .ILSEQ => return error.BadPathName, else => |err| return posix.unexpectedErrno(err), } - }; + } - if (!options.mode.read and !options.mode.write and !options.mode.execute) + if (!options.read and !options.write and !options.execute) return; var directory: wasi.fdstat_t = undefined; @@ -1204,14 +1235,14 @@ fn dirAccessWasi( return error.AccessDenied; var rights: wasi.rights_t = .{}; - if (options.mode.read) { + if (options.read) { if (stat.filetype == .DIRECTORY) { rights.FD_READDIR = true; } else { rights.FD_READ = true; } } - if (options.mode.write) + if (options.write) rights.FD_WRITE = true; // No validation for execution. @@ -3262,9 +3293,9 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat { .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, else => .unknown, }, - .atime = st.atim, - .mtime = st.mtim, - .ctime = st.ctim, + .atime = .fromNanoseconds(st.atim), + .mtime = .fromNanoseconds(st.mtim), + .ctime = .fromNanoseconds(st.ctim), }; } diff --git a/lib/std/os.zig b/lib/std/os.zig index 7fe64290b1..7bee7ed104 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -201,7 +201,13 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix. } } -pub fn fstat_wasi(fd: posix.fd_t) posix.FStatError!wasi.filestat_t { +pub const FstatError = error{ + SystemResources, + AccessDenied, + Unexpected, +}; + +pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t { var stat: wasi.filestat_t = undefined; switch (wasi.fd_filestat_get(fd, &stat)) { .SUCCESS => return stat, diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 999fe5e50a..67a3e787f7 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -2809,37 +2809,13 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirErro const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); } else if (native_os == .wasi and !builtin.link_libc) { - return mkdiratWasi(dir_fd, sub_dir_path, mode); + @compileError("use std.Io instead"); } else { const sub_dir_path_c = try toPosixPath(sub_dir_path); return mkdiratZ(dir_fd, &sub_dir_path_c, mode); } } -pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirError!void { - _ = mode; - switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) { - .SUCCESS => return, - .ACCES => return error.AccessDenied, - .BADF => unreachable, - .PERM => return error.PermissionDenied, - .DQUOT => return error.DiskQuota, - .EXIST => return error.PathAlreadyExists, - .FAULT => unreachable, - .LOOP => return error.SymLinkLoop, - .MLINK => return error.LinkQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .ROFS => return error.ReadOnlyFileSystem, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return unexpectedErrno(err), - } -} - /// Same as `mkdirat` except the parameters are null-terminated. pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void { if (native_os == .windows) { diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 230440a2f1..e85f1d7471 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -109,64 +109,6 @@ test "open smoke test" { } } -test "openat smoke test" { - if (native_os == .windows) return error.SkipZigTest; - - // TODO verify file attributes using `fstatat` - - var tmp = tmpDir(.{}); - defer tmp.cleanup(); - - var fd: posix.fd_t = undefined; - const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; - - // Create some file using `openat`. - fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ - .ACCMODE = .RDWR, - .CREAT = true, - .EXCL = true, - }), mode); - posix.close(fd); - - // Try this again with the same flags. This op should fail with error.PathAlreadyExists. - try expectError(error.PathAlreadyExists, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ - .ACCMODE = .RDWR, - .CREAT = true, - .EXCL = true, - }), mode)); - - // Try opening without `EXCL` flag. - fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ - .ACCMODE = .RDWR, - .CREAT = true, - }), mode); - posix.close(fd); - - // Try opening as a directory which should fail. - try expectError(error.NotDir, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ - .ACCMODE = .RDWR, - .DIRECTORY = true, - }), mode)); - - // Create some directory - try posix.mkdirat(tmp.dir.fd, "some_dir", mode); - - // Open dir using `open` - fd = try posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{ - .ACCMODE = .RDONLY, - .DIRECTORY = true, - }), mode); - posix.close(fd); - - // Try opening as file which should fail (skip on wasi+libc due to - // https://github.com/bytecodealliance/wasmtime/issues/9054) - if (native_os != .wasi or !builtin.link_libc) { - try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{ - .ACCMODE = .RDWR, - }), mode)); - } -} - test "readlink on Windows" { if (native_os != .windows) return error.SkipZigTest;