From f078c7138f0dfbab95d8963e6c067515c6a54460 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 18 Dec 2025 18:32:25 -0800 Subject: [PATCH] std: Update/fix some usages/implementations of std.Io APIs --- lib/std/Io/Terminal.zig | 4 +- lib/std/Io/Threaded.zig | 186 +++++++++++++---------------- lib/std/debug/SelfInfo/Windows.zig | 3 +- lib/std/fs/test.zig | 153 +++++++++++++----------- lib/std/os/windows.zig | 6 +- 5 files changed, 172 insertions(+), 180 deletions(-) diff --git a/lib/std/Io/Terminal.zig b/lib/std/Io/Terminal.zig index a39f2175d238f7ad8ee446853ce9a0010227d55a..d1b257b9975dbd9ba49f2cbd2ff7e1bb8f651688 100644 --- a/lib/std/Io/Terminal.zig +++ b/lib/std/Io/Terminal.zig @@ -56,7 +56,7 @@ pub const Mode = union(enum) { error.NotTerminalDevice, error.Unexpected => {}, } - if (is_windows and file.isTty(io)) { + if (is_windows and try file.isTty(io)) { const windows = std.os.windows; var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) { @@ -74,7 +74,7 @@ pub const Mode = union(enum) { pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; -pub fn setColor(t: Terminal, color: Color) Io.Writer.Error!void { +pub fn setColor(t: Terminal, color: Color) SetColorError!void { switch (t.mode) { .no_color => return, .escape_codes => { diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index da15a70903b0250f8f056c7e48b72409f20bdeaa..e0fa45d73c050f8b9eba628dd91b60781d65fa33 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -1744,7 +1744,7 @@ fn dirMakeOpenPathWindows( // stat the file and return an error if it's not a directory // this is important because otherwise a dangling symlink // could cause an infinite loop - const fstat = dirStatFileWindows(t, dir, component.path, .{ + const fstat = try dirStatFileWindows(t, dir, component.path, .{ .follow_symlinks = options.follow_symlinks, }); if (fstat.kind != .directory) return error.NotDir; @@ -2146,7 +2146,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { return .{ .inode = info.InternalInformation.IndexNumber, .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)), - .mode = 0, + .permissions = .default_file, .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: { var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined; const tag_rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE.ATTRIBUTE_TAG_INFO), .AttributeTag); @@ -2168,6 +2168,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime), .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime), .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime), + .nlink = {}, }; } @@ -2584,26 +2585,33 @@ fn dirCreateFileWindows( .OPEN_IF, }); errdefer w.CloseHandle(handle); + var io_status_block: w.IO_STATUS_BLOCK = undefined; - const range_off: w.LARGE_INTEGER = 0; - const range_len: w.LARGE_INTEGER = 1; const exclusive = switch (flags.lock) { .none => return .{ .handle = handle }, .shared => false, .exclusive => true, }; - try w.LockFile( + const status = w.ntdll.NtLockFile( handle, null, null, null, &io_status_block, - &range_off, - &range_len, + &windows_lock_range_off, + &windows_lock_range_len, null, @intFromBool(flags.lock_nonblocking), @intFromBool(exclusive), ); + switch (status) { + .SUCCESS => {}, + .INSUFFICIENT_RESOURCES => return error.SystemResources, + .LOCK_NOT_GRANTED => return error.WouldBlock, + .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer + else => return windows.unexpectedStatus(status), + } + return .{ .handle = handle }; } @@ -2992,25 +3000,30 @@ pub fn dirOpenFileWtf16( }; errdefer w.CloseHandle(handle); - const range_off: w.LARGE_INTEGER = 0; - const range_len: w.LARGE_INTEGER = 1; const exclusive = switch (flags.lock) { .none => return .{ .handle = handle }, .shared => false, .exclusive => true, }; - try w.LockFile( + const status = w.ntdll.NtLockFile( handle, null, null, null, &io_status_block, - &range_off, - &range_len, + &windows_lock_range_off, + &windows_lock_range_len, null, @intFromBool(flags.lock_nonblocking), @intFromBool(exclusive), ); + switch (status) { + .SUCCESS => {}, + .INSUFFICIENT_RESOURCES => return error.SystemResources, + .LOCK_NOT_GRANTED => return error.WouldBlock, + .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer + else => return windows.unexpectedStatus(status), + } return .{ .handle = handle }; } @@ -3750,7 +3763,7 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D null, &io_status_block, unreserved_buffer.ptr, - unreserved_buffer.len, + std.math.cast(w.ULONG, unreserved_buffer.len) orelse std.math.maxInt(w.ULONG), .BothDirectory, w.FALSE, null, @@ -3849,15 +3862,14 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out var path_name_w = try w.sliceToPrefixedFileW(dir.handle, sub_path); - const access_mask = w.GENERIC_READ | w.SYNCHRONIZE; - const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE; - const creation = w.FILE_OPEN; const h_file = blk: { const res = w.OpenFile(path_name_w.span(), .{ .dir = dir.handle, - .access_mask = access_mask, - .share_access = share_access, - .creation = creation, + .access_mask = .{ + .GENERIC = .{ .READ = true }, + .STANDARD = .{ .SYNCHRONIZE = true }, + }, + .creation = .OPEN, .filter = .any, }) catch |err| switch (err) { error.WouldBlock => unreachable, @@ -4220,7 +4232,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov try current_thread.checkCancel(); - const sub_path_w = try w.sliceToPrefixedFileW(dir.handle, sub_path); + const sub_path_w_buf = try w.sliceToPrefixedFileW(dir.handle, sub_path); + const sub_path_w = sub_path_w_buf.span(); const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2)); var nt_name: w.UNICODE_STRING = .{ @@ -4239,31 +4252,32 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov return error.FileBusy; } - const create_options_flags: w.ULONG = if (remove_dir) - w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT - else - w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT; - - var attr: w.OBJECT_ATTRIBUTES = .{ - .Length = @sizeOf(w.OBJECT_ATTRIBUTES), - .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, - .Attributes = w.OBJ_CASE_INSENSITIVE, - .ObjectName = &nt_name, - .SecurityDescriptor = null, - .SecurityQualityOfService = null, - }; var io_status_block: w.IO_STATUS_BLOCK = undefined; var tmp_handle: w.HANDLE = undefined; var rc = w.ntdll.NtCreateFile( &tmp_handle, - w.SYNCHRONIZE | w.DELETE, - &attr, + .{ .STANDARD = .{ + .RIGHTS = .{ .DELETE = true }, + .SYNCHRONIZE = true, + } }, + &.{ + .Length = @sizeOf(w.OBJECT_ATTRIBUTES), + .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, + .Attributes = .{}, + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }, &io_status_block, null, - 0, - w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE, - w.FILE_OPEN, - create_options_flags, + .{}, + .VALID_FLAGS, + .OPEN, + .{ + .DIRECTORY_FILE = remove_dir, + .NON_DIRECTORY_FILE = !remove_dir, + .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead? + }, null, 0, ); @@ -4463,16 +4477,24 @@ fn dirRenameWindows( const t: *Threaded = @ptrCast(@alignCast(userdata)); const current_thread = Thread.getCurrent(t); - const old_path_w = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path); - const new_path_w = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path); + const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path); + const old_path_w = old_path_w_buf.span(); + const new_path_w_buf = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path); + const new_path_w = new_path_w_buf.span(); const replace_if_exists = true; try current_thread.checkCancel(); const src_fd = w.OpenFile(old_path_w, .{ .dir = old_dir.handle, - .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | w.DELETE, - .creation = w.FILE_OPEN, + .access_mask = .{ + .GENERIC = .{ .WRITE = true }, + .STANDARD = .{ + .RIGHTS = .{ .DELETE = true }, + .SYNCHRONIZE = true, + }, + }, + .creation = .OPEN, .filter = .any, // This function is supposed to rename both files and directories. .follow_symlinks = false, }) catch |err| switch (err) { @@ -4729,9 +4751,12 @@ fn dirSymLinkWindows( }; const symlink_handle = w.OpenFile(sym_link_path_w.span(), .{ - .access_mask = w.SYNCHRONIZE | w.GENERIC_READ | w.GENERIC_WRITE, + .access_mask = .{ + .GENERIC = .{ .READ = true, .WRITE = true }, + .STANDARD = .{ .SYNCHRONIZE = true }, + }, .dir = dir, - .creation = w.FILE_CREATE, + .creation = .CREATE, .filter = if (flags.is_directory) .dir_only else .non_directory_only, }) catch |err| switch (err) { error.IsDir => return error.PathAlreadyExists, @@ -4913,55 +4938,14 @@ fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buf try current_thread.checkCancel(); - var sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path); + var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path); - const result_handle = w.OpenFile(sub_path_w.span(), .{ - .access_mask = w.FILE_READ_ATTRIBUTES | w.SYNCHRONIZE, - .dir = dir, - .creation = w.FILE_OPEN, - .follow_symlinks = false, - .filter = .any, - }) catch |err| switch (err) { - error.IsDir, error.NotDir => return error.Unexpected, // filter = .any - error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN - error.WouldBlock => return error.Unexpected, - error.NoDevice => return error.FileNotFound, - error.PipeBusy => return error.AccessDenied, - else => |e| return e, - }; - defer w.CloseHandle(result_handle); + const result_w = try w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data); - var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(w.REPARSE_DATA_BUFFER)) = undefined; - _ = w.DeviceIoControl(result_handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) { - error.AccessDenied => return error.Unexpected, - error.UnrecognizedVolume => return error.Unexpected, - else => |e| return e, - }; - - const reparse_struct: *const w.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0])); - const wide_result = switch (reparse_struct.ReparseTag) { - w.IO_REPARSE_TAG_SYMLINK => r: { - const buf: *const w.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0])); - const offset = buf.SubstituteNameOffset >> 1; - const len = buf.SubstituteNameLength >> 1; - const path_buf: [*]const u16 = &buf.PathBuffer; - const is_relative = buf.Flags & w.SYMLINK_FLAG_RELATIVE != 0; - break :r try w.parseReadLinkPath(path_buf[offset..][0..len], is_relative, buffer); - }, - w.IO_REPARSE_TAG_MOUNT_POINT => r: { - const buf: *const w.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0])); - const offset = buf.SubstituteNameOffset >> 1; - const len = buf.SubstituteNameLength >> 1; - const path_buf: [*]const u16 = &buf.PathBuffer; - break :r try w.parseReadLinkPath(path_buf[offset..][0..len], false, buffer); - }, - else => return error.UnsupportedReparsePointType, - }; - - const len = std.unicode.calcWtf8Len(wide_result); + const len = std.unicode.calcWtf8Len(result_w); if (len > buffer.len) return error.NameTooLong; - return std.unicode.wtf16LeToWtf8(buffer, wide_result); + return std.unicode.wtf16LeToWtf8(buffer, result_w); } fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize { @@ -6061,10 +6045,9 @@ fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockErro fn fileUnlock(userdata: ?*anyopaque, file: File) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - const current_thread = Thread.getCurrent(t); + _ = t; if (is_windows) { - try current_thread.checkCancel(); var io_status_block: windows.IO_STATUS_BLOCK = undefined; const status = windows.ntdll.NtUnlockFile( file.handle, @@ -6131,7 +6114,7 @@ fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError! &io_status_block, &windows_lock_range_off, &windows_lock_range_len, - null, + 0, ); if (is_debug) switch (status) { .SUCCESS => {}, @@ -6841,17 +6824,16 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex // If ImagePathName is a symlink, then it will contain the path of the // symlink, not the path that the symlink points to. We want the path // that the symlink points to, though, so we need to get the realpath. - var path_name_w = try w.wToPrefixedFileW(null, image_path_name); + var path_name_w_buf = try w.wToPrefixedFileW(null, image_path_name); - const access_mask = w.GENERIC_READ | w.SYNCHRONIZE; - const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE; - const creation = w.FILE_OPEN; const h_file = blk: { - const res = w.OpenFile(path_name_w.span(), .{ + const res = w.OpenFile(path_name_w_buf.span(), .{ .dir = null, - .access_mask = access_mask, - .share_access = share_access, - .creation = creation, + .access_mask = .{ + .GENERIC = .{ .READ = true }, + .STANDARD = .{ .SYNCHRONIZE = true }, + }, + .creation = .OPEN, .filter = .any, }) catch |err| switch (err) { error.WouldBlock => unreachable, @@ -6861,7 +6843,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex }; defer w.CloseHandle(h_file); - const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer); + const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data); const len = std.unicode.calcWtf8Len(wide_slice); if (len > out_buffer.len) diff --git a/lib/std/debug/SelfInfo/Windows.zig b/lib/std/debug/SelfInfo/Windows.zig index 9874efd4974c8f127e3ef520fca864a9333faf17..c296b287026758f57f6ee8c53a0e22648e43062f 100644 --- a/lib/std/debug/SelfInfo/Windows.zig +++ b/lib/std/debug/SelfInfo/Windows.zig @@ -222,7 +222,7 @@ const Module = struct { pdb.file_reader.file.close(io); pdb.deinit(); } - if (di.mapped_file) |*mf| mf.deinit(); + if (di.mapped_file) |*mf| mf.deinit(io); var arena = di.arena.promote(gpa); arena.deinit(); @@ -331,7 +331,6 @@ const Module = struct { error.SystemResources, error.WouldBlock, error.AccessDenied, - error.ProcessNotFound, error.PermissionDenied, error.NoSpaceLeft, error.DeviceBusy, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 40ba20c9a84a2bb74b1bdd2a7e7b6ee009560f32..48eb733455913186c9206a8bef61039001fda9cb 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -27,7 +27,7 @@ const PathType = enum { pub fn isSupported(self: PathType, target_os: std.Target.Os) bool { return switch (self) { .relative => true, - .absolute => std.os.isGetFdPathSupportedOnTarget(target_os), + .absolute => target_os.tag == .windows, // TODO: implement getPathForHandle for other targets .unc => target_os.tag == .windows, }; } @@ -49,7 +49,7 @@ const PathType = enum { // The final path may not actually exist which would cause realpath to fail. // So instead, we get the path of the dir and join it with the relative path. var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; - const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf); + const dir_path = try getPathForHandle(dir.handle, &fd_path_buf); return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }); } }.transform, @@ -58,7 +58,7 @@ const PathType = enum { // Any drive absolute path (C:\foo) can be converted into a UNC path by // using '127.0.0.1' as the server name and '$' as the share name. var fd_path_buf: [Dir.max_path_bytes]u8 = undefined; - const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf); + const dir_path = try getPathForHandle(dir.handle, &fd_path_buf); const windows_path_type = windows.getWin32PathType(u8, dir_path); switch (windows_path_type) { .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }), @@ -77,6 +77,19 @@ const PathType = enum { } }; +fn getPathForHandle(handle: File.Handle, out_buffer: *[Dir.max_path_bytes]u8) ![]u8 { + switch (native_os) { + .windows => { + var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; + const wide_slice = try windows.GetFinalPathNameByHandle(handle, .{}, wide_buf[0..]); + + const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice); + return out_buffer[0..end_index]; + }, + else => @compileError("TODO or unsupported"), + } +} + const TestContext = struct { io: Io, path_type: PathType, @@ -488,16 +501,16 @@ test "Dir.Iterator" { // Create iterator. var iter = tmp_dir.dir.iterate(); - while (try iter.next()) |entry| { + while (try iter.next(io)) |entry| { // We cannot just store `entry` as on Windows, we're re-using the name buffer // which means we'll actually share the `name` pointer between entries! const name = try allocator.dupe(u8, entry.name); - try entries.append(Dir.Entry{ .name = name, .kind = entry.kind }); + try entries.append(Dir.Entry{ .name = name, .kind = entry.kind, .inode = 0 }); } try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..' - try expect(contains(&entries, .{ .name = "some_file", .kind = .file })); - try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory })); + try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 })); + try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 })); } test "Dir.Iterator many entries" { @@ -523,17 +536,17 @@ test "Dir.Iterator many entries" { // Create iterator. var iter = tmp_dir.dir.iterate(); - while (try iter.next()) |entry| { + while (try iter.next(io)) |entry| { // We cannot just store `entry` as on Windows, we're re-using the name buffer // which means we'll actually share the `name` pointer between entries! const name = try allocator.dupe(u8, entry.name); - try entries.append(.{ .name = name, .kind = entry.kind }); + try entries.append(.{ .name = name, .kind = entry.kind, .inode = 0 }); } i = 0; while (i < num) : (i += 1) { const name = try std.fmt.bufPrint(&buf, "{}", .{i}); - try expect(contains(&entries, .{ .name = name, .kind = .file })); + try expect(contains(&entries, .{ .name = name, .kind = .file, .inode = 0 })); } } @@ -559,16 +572,16 @@ test "Dir.Iterator twice" { // Create iterator. var iter = tmp_dir.dir.iterate(); - while (try iter.next()) |entry| { + while (try iter.next(io)) |entry| { // We cannot just store `entry` as on Windows, we're re-using the name buffer // which means we'll actually share the `name` pointer between entries! const name = try allocator.dupe(u8, entry.name); - try entries.append(Dir.Entry{ .name = name, .kind = entry.kind }); + try entries.append(Dir.Entry{ .name = name, .kind = entry.kind, .inode = 0 }); } try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..' - try expect(contains(&entries, .{ .name = "some_file", .kind = .file })); - try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory })); + try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 })); + try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 })); } } @@ -595,18 +608,18 @@ test "Dir.Iterator reset" { while (i < 2) : (i += 1) { var entries = std.array_list.Managed(Dir.Entry).init(allocator); - while (try iter.next()) |entry| { + while (try iter.next(io)) |entry| { // We cannot just store `entry` as on Windows, we're re-using the name buffer // which means we'll actually share the `name` pointer between entries! const name = try allocator.dupe(u8, entry.name); - try entries.append(.{ .name = name, .kind = entry.kind }); + try entries.append(.{ .name = name, .kind = entry.kind, .inode = 0 }); } try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..' - try expect(contains(&entries, .{ .name = "some_file", .kind = .file })); - try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory })); + try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 })); + try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 })); - iter.reset(); + iter.reader.reset(); } } @@ -617,7 +630,7 @@ test "Dir.Iterator but dir is deleted during iteration" { defer tmp.cleanup(); // Create directory and setup an iterator for it - var subdir = try tmp.dir.makeOpenPath(io, "subdir", .{ .iterate = true }); + var subdir = try tmp.dir.makeOpenPath(io, "subdir", .{ .open_options = .{ .iterate = true } }); defer subdir.close(io); var iterator = subdir.iterate(); @@ -632,8 +645,8 @@ test "Dir.Iterator but dir is deleted during iteration" { tmp.dir.deleteTree(io, "subdir") catch return error.SkipZigTest; // Now, when we try to iterate, the next call should return null immediately. - const entry = try iterator.next(); - try std.expect(entry == null); + const entry = try iterator.next(io); + try testing.expect(entry == null); // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux` if (native_os == .linux) { @@ -652,8 +665,8 @@ fn contains(entries: *const std.array_list.Managed(Dir.Entry), el: Dir.Entry) bo return false; } -test "Dir.realpath smoke test" { - if (!comptime std.os.isGetFdPathSupportedOnTarget(builtin.os)) return error.SkipZigTest; +test "Dir.realPath smoke test" { + if (native_os == .wasi) return error.SkipZigTest; try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void { @@ -686,10 +699,10 @@ test "Dir.realpath smoke test" { // First, test non-alloc version { - const file_path = try ctx.dir.realpath(test_file_path, &buf); + const file_path = try ctx.dir.realPath(io, test_file_path, &buf); try expectEqualStrings(expected_file_path, file_path); - const dir_path = try ctx.dir.realpath(test_dir_path, &buf); + const dir_path = try ctx.dir.realPath(io, test_dir_path, &buf); try expectEqualStrings(expected_dir_path, dir_path); } @@ -769,7 +782,7 @@ test "Dir.statFile" { try expectError(error.FileNotFound, ctx.dir.statFile(io, test_dir_name, .{})); - try ctx.dir.makeDir(io, test_dir_name); + try ctx.dir.makeDir(io, test_dir_name, .default_dir); const stat = try ctx.dir.statFile(io, test_dir_name, .{}); try expectEqual(.directory, stat.kind); @@ -1148,7 +1161,7 @@ test "openExecutable" { const io = testing.io; - const self_exe_file = try std.fs.openExecutable(.{}); + const self_exe_file = try std.process.openExecutable(io, .{}); self_exe_file.close(io); } @@ -1157,7 +1170,8 @@ test "executablePath" { const io = testing.io; var buf: [Dir.max_path_bytes]u8 = undefined; - const buf_self_exe_path = try std.process.executablePath(io, &buf); + const len = try std.process.executablePath(io, &buf); + const buf_self_exe_path = buf[0..len]; const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator); defer testing.allocator.free(alloc_self_exe_path); try expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path); @@ -1246,7 +1260,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" { .data = "blah", }); - try ctx.dir.deleteTreeMinStackSize(dir_path); + try ctx.dir.deleteTreeMinStackSize(io, dir_path); try expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{})); } }.impl); @@ -1395,7 +1409,7 @@ fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !vo defer walker.deinit(); var count: usize = 0; - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { try expectEqualStrings(maxed_filename, entry.basename); count += 1; } @@ -1452,7 +1466,7 @@ test "writev, readv" { try writer.interface.flush(); try expectEqual(@as(u64, line1.len + line2.len), try src_file.length(io)); - var reader = writer.moveToReader(io); + var reader = writer.moveToReader(); try reader.seekTo(0); try reader.interface.readVecAll(&read_vecs); try expectEqualStrings(&buf1, "line2\n"); @@ -1483,7 +1497,7 @@ test "pwritev, preadv" { try writer.interface.flush(); try expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.length(io)); - var reader = writer.moveToReader(io); + var reader = writer.moveToReader(); try reader.seekTo(16); try reader.interface.readVecAll(&read_vecs); try expectEqualStrings(&buf1, "line2\n"); @@ -1549,7 +1563,7 @@ test "sendfile" { try expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10))); try file_writer.interface.writeVecAll(&trailers); try file_writer.interface.flush(); - var fr = file_writer.moveToReader(io); + var fr = file_writer.moveToReader(); try fr.seekTo(0); const amt = try fr.interface.readSliceShort(&written_buf); try expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]); @@ -1586,7 +1600,7 @@ test "sendfile with buffered data" { try expectEqual(4, try file_writer.interface.sendFileAll(&file_reader, .limited(4))); var written_buf: [8]u8 = undefined; - var fr = file_writer.moveToReader(io); + var fr = file_writer.moveToReader(); try fr.seekTo(0); const amt = try fr.interface.readSliceShort(&written_buf); @@ -1609,7 +1623,7 @@ test "copyFile" { try ctx.dir.copyFile(src_file, ctx.dir, dest_file, io, .{}); defer ctx.dir.deleteFile(io, dest_file) catch {}; - try ctx.dir.copyFile(src_file, ctx.dir, dest_file2, io, .{ .permissions = File.default_mode }); + try ctx.dir.copyFile(src_file, ctx.dir, dest_file2, io, .{ .permissions = .default_file }); defer ctx.dir.deleteFile(io, dest_file2) catch {}; try expectFileContents(io, ctx.dir, dest_file, data); @@ -1714,12 +1728,12 @@ test "open file with exclusive lock twice, make sure second lock waits" { errdefer file.close(io); const S = struct { - fn checkFn(dir: *Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { + fn checkFn(inner_ctx: *TestContext, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { started.set(); - const file1 = try dir.createFile(io, path, .{ .lock = .exclusive }); + const file1 = try inner_ctx.dir.createFile(inner_ctx.io, path, .{ .lock = .exclusive }); locked.set(); - file1.close(io); + file1.close(inner_ctx.io); } }; @@ -1727,7 +1741,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { var locked: std.Thread.ResetEvent = .unset; const t = try std.Thread.spawn(.{}, S.checkFn, .{ - &ctx.dir, + ctx, filename, &started, &locked, @@ -1848,7 +1862,7 @@ test "walker" { defer walker.deinit(); var num_walked: usize = 0; - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { expect(expected_basenames.has(entry.basename)) catch |err| { std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)}); return err; @@ -1910,10 +1924,10 @@ test "selective walker, skip entries that start with ." { defer walker.deinit(); var num_walked: usize = 0; - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { if (entry.basename[0] == '.') continue; if (entry.kind == .directory) { - try walker.enter(entry); + try walker.enter(io, entry); } expect(expected_basenames.has(entry.basename)) catch |err| { @@ -1953,7 +1967,7 @@ test "walker without fully iterating" { try tmp.dir.makePath(io, "b"); var num_walked: usize = 0; - while (try walker.next()) |_| { + while (try walker.next(io)) |_| { num_walked += 1; break; } @@ -2093,48 +2107,42 @@ test "invalid UTF-8/WTF-8 paths" { // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte const invalid_path = try ctx.transformPath("\xFF"); - try expectError(expected_err, ctx.dir.openFile(invalid_path, .{})); + try expectError(expected_err, ctx.dir.openFile(io, invalid_path, .{})); - try expectError(expected_err, ctx.dir.createFile(invalid_path, .{})); + try expectError(expected_err, ctx.dir.createFile(io, invalid_path, .{})); - try expectError(expected_err, ctx.dir.makeDir(invalid_path, .default_dir)); + try expectError(expected_err, ctx.dir.makeDir(io, invalid_path, .default_dir)); - try expectError(expected_err, ctx.dir.makePath(invalid_path)); - try expectError(expected_err, ctx.dir.makeOpenPath(invalid_path, .{})); + try expectError(expected_err, ctx.dir.makePath(io, invalid_path)); + try expectError(expected_err, ctx.dir.makeOpenPath(io, invalid_path, .{})); - try expectError(expected_err, ctx.dir.openDir(invalid_path, .{})); + try expectError(expected_err, ctx.dir.openDir(io, invalid_path, .{})); - try expectError(expected_err, ctx.dir.deleteFile(invalid_path)); + try expectError(expected_err, ctx.dir.deleteFile(io, invalid_path)); try expectError(expected_err, ctx.dir.deleteDir(io, invalid_path)); try expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io)); try expectError(expected_err, ctx.dir.symLink(io, invalid_path, invalid_path, .{})); - if (native_os == .wasi) { - try expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{})); - } try expectError(expected_err, ctx.dir.readLink(io, invalid_path, &[_]u8{})); - if (native_os == .wasi) { - try expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{})); - } - try expectError(expected_err, ctx.dir.readFile(invalid_path, &[_]u8{})); - try expectError(expected_err, ctx.dir.readFileAlloc(invalid_path, testing.allocator, .limited(0))); + try expectError(expected_err, ctx.dir.readFile(io, invalid_path, &[_]u8{})); + try expectError(expected_err, ctx.dir.readFileAlloc(io, invalid_path, testing.allocator, .limited(0))); try expectError(expected_err, ctx.dir.deleteTree(io, invalid_path)); - try expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path)); + try expectError(expected_err, ctx.dir.deleteTreeMinStackSize(io, invalid_path)); try expectError(expected_err, ctx.dir.writeFile(io, .{ .sub_path = invalid_path, .data = "" })); - try expectError(expected_err, ctx.dir.access(invalid_path, .{})); + try expectError(expected_err, ctx.dir.access(io, invalid_path, .{})); var dir = ctx.dir; try expectError(expected_err, dir.updateFile(io, invalid_path, dir, invalid_path, .{})); try expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, io, .{})); - try expectError(expected_err, ctx.dir.statFile(invalid_path)); + try expectError(expected_err, ctx.dir.statFile(io, invalid_path, .{})); if (native_os != .wasi) { try expectError(expected_err, ctx.dir.realPath(io, invalid_path, &[_]u8{})); @@ -2146,17 +2154,17 @@ test "invalid UTF-8/WTF-8 paths" { if (native_os != .wasi and ctx.path_type != .relative) { try expectError(expected_err, Dir.copyFileAbsolute(invalid_path, invalid_path, io, .{})); try expectError(expected_err, Dir.makeDirAbsolute(io, invalid_path, .default_dir)); - try expectError(expected_err, Dir.deleteDirAbsolute(invalid_path)); + try expectError(expected_err, Dir.deleteDirAbsolute(io, invalid_path)); try expectError(expected_err, Dir.renameAbsolute(invalid_path, invalid_path, io)); try expectError(expected_err, Dir.openDirAbsolute(io, invalid_path, .{})); try expectError(expected_err, Dir.openFileAbsolute(io, invalid_path, .{})); - try expectError(expected_err, Dir.accessAbsolute(invalid_path, .{})); - try expectError(expected_err, Dir.createFileAbsolute(invalid_path, .{})); - try expectError(expected_err, Dir.deleteFileAbsolute(invalid_path)); + try expectError(expected_err, Dir.accessAbsolute(io, invalid_path, .{})); + try expectError(expected_err, Dir.createFileAbsolute(io, invalid_path, .{})); + try expectError(expected_err, Dir.deleteFileAbsolute(io, invalid_path)); var readlink_buf: [Dir.max_path_bytes]u8 = undefined; - try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf)); - try expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{})); - try expectError(expected_err, Dir.realPathAlloc(io, invalid_path, testing.allocator)); + try expectError(expected_err, Dir.readLinkAbsolute(io, invalid_path, &readlink_buf)); + try expectError(expected_err, Dir.symLinkAbsolute(io, invalid_path, invalid_path, .{})); + try expectError(expected_err, Dir.realPathAbsoluteAlloc(io, invalid_path, testing.allocator)); } } }.impl); @@ -2327,7 +2335,8 @@ test "readlink on Windows" { fn testReadLinkWindows(io: Io, target_path: []const u8, symlink_path: []const u8) !void { var buffer: [Dir.max_path_bytes]u8 = undefined; - const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer); + const len = try Dir.readLinkAbsolute(io, symlink_path, &buffer); + const given = buffer[0..len]; try expect(mem.eql(u8, target_path, given)); } @@ -2495,7 +2504,7 @@ test "access smoke test" { { // Create some directory - try tmp.dir.makeDir(io, "some_dir", .default_file); + try tmp.dir.makeDir(io, "some_dir", .default_dir); } { @@ -2563,6 +2572,8 @@ test "open smoke test" { } test "hard link with different directories" { + if (native_os == .wasi or native_os == .windows) return error.SkipZigTest; + const io = testing.io; var tmp = tmpDir(.{}); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 7a2b2aaf50a9ac10740a9e86523322f399f0ccb5..02357d6d2af24845c1ece2394d97822ad15a5b91 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3582,7 +3582,7 @@ test QueryObjectName { //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths. var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - const handle = tmp.dir.fd; + const handle = tmp.dir.handle; var out_buffer: [PATH_MAX_WIDE]u16 = undefined; const result_path = try QueryObjectName(handle, &out_buffer); @@ -3845,7 +3845,7 @@ test GetFinalPathNameByHandle { //any file will do var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); - const handle = tmp.dir.fd; + const handle = tmp.dir.handle; var buffer: [PATH_MAX_WIDE]u16 = undefined; //check with sufficient size @@ -4638,7 +4638,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE } // We can also skip GetFinalPathNameByHandle if the handle matches // the handle returned by Io.Dir.cwd() - if (dir.? == Io.Dir.cwd().fd) { + if (dir.? == Io.Dir.cwd().handle) { break :path_to_get path; } // At this point, we know we have a relative path that had too many -- 2.54.0