authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-12-18 18:32:25-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:10-08:00
logf078c7138f0dfbab95d8963e6c067515c6a54460
tree23fc317aad23768a35d8027cc03b5a96861755d0
parent1edae601a1d35caa78eddc4b18f7d613c4af70f9

std: Update/fix some usages/implementations of std.Io APIs


5 files changed, 172 insertions(+), 180 deletions(-)

lib/std/Io/Terminal.zig+2-2
......@@ -56,7 +56,7 @@ pub const Mode = union(enum) {
5656 error.NotTerminalDevice, error.Unexpected => {},
5757 }
5858
59 if (is_windows and file.isTty(io)) {
59 if (is_windows and try file.isTty(io)) {
6060 const windows = std.os.windows;
6161 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
6262 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != 0) {
......@@ -74,7 +74,7 @@ pub const Mode = union(enum) {
7474
7575pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error;
7676
77pub fn setColor(t: Terminal, color: Color) Io.Writer.Error!void {
77pub fn setColor(t: Terminal, color: Color) SetColorError!void {
7878 switch (t.mode) {
7979 .no_color => return,
8080 .escape_codes => {
lib/std/Io/Threaded.zig+84-102
......@@ -1744,7 +1744,7 @@ fn dirMakeOpenPathWindows(
17441744 // stat the file and return an error if it's not a directory
17451745 // this is important because otherwise a dangling symlink
17461746 // could cause an infinite loop
1747 const fstat = dirStatFileWindows(t, dir, component.path, .{
1747 const fstat = try dirStatFileWindows(t, dir, component.path, .{
17481748 .follow_symlinks = options.follow_symlinks,
17491749 });
17501750 if (fstat.kind != .directory) return error.NotDir;
......@@ -2146,7 +2146,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
21462146 return .{
21472147 .inode = info.InternalInformation.IndexNumber,
21482148 .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
2149 .mode = 0,
2149 .permissions = .default_file,
21502150 .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: {
21512151 var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined;
21522152 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 {
21682168 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
21692169 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
21702170 .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
2171 .nlink = {},
21712172 };
21722173}
21732174
......@@ -2584,26 +2585,33 @@ fn dirCreateFileWindows(
25842585 .OPEN_IF,
25852586 });
25862587 errdefer w.CloseHandle(handle);
2588
25872589 var io_status_block: w.IO_STATUS_BLOCK = undefined;
2588 const range_off: w.LARGE_INTEGER = 0;
2589 const range_len: w.LARGE_INTEGER = 1;
25902590 const exclusive = switch (flags.lock) {
25912591 .none => return .{ .handle = handle },
25922592 .shared => false,
25932593 .exclusive => true,
25942594 };
2595 try w.LockFile(
2595 const status = w.ntdll.NtLockFile(
25962596 handle,
25972597 null,
25982598 null,
25992599 null,
26002600 &io_status_block,
2601 &range_off,
2602 &range_len,
2601 &windows_lock_range_off,
2602 &windows_lock_range_len,
26032603 null,
26042604 @intFromBool(flags.lock_nonblocking),
26052605 @intFromBool(exclusive),
26062606 );
2607 switch (status) {
2608 .SUCCESS => {},
2609 .INSUFFICIENT_RESOURCES => return error.SystemResources,
2610 .LOCK_NOT_GRANTED => return error.WouldBlock,
2611 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
2612 else => return windows.unexpectedStatus(status),
2613 }
2614
26072615 return .{ .handle = handle };
26082616}
26092617
......@@ -2992,25 +3000,30 @@ pub fn dirOpenFileWtf16(
29923000 };
29933001 errdefer w.CloseHandle(handle);
29943002
2995 const range_off: w.LARGE_INTEGER = 0;
2996 const range_len: w.LARGE_INTEGER = 1;
29973003 const exclusive = switch (flags.lock) {
29983004 .none => return .{ .handle = handle },
29993005 .shared => false,
30003006 .exclusive => true,
30013007 };
3002 try w.LockFile(
3008 const status = w.ntdll.NtLockFile(
30033009 handle,
30043010 null,
30053011 null,
30063012 null,
30073013 &io_status_block,
3008 &range_off,
3009 &range_len,
3014 &windows_lock_range_off,
3015 &windows_lock_range_len,
30103016 null,
30113017 @intFromBool(flags.lock_nonblocking),
30123018 @intFromBool(exclusive),
30133019 );
3020 switch (status) {
3021 .SUCCESS => {},
3022 .INSUFFICIENT_RESOURCES => return error.SystemResources,
3023 .LOCK_NOT_GRANTED => return error.WouldBlock,
3024 .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
3025 else => return windows.unexpectedStatus(status),
3026 }
30143027 return .{ .handle = handle };
30153028}
30163029
......@@ -3750,7 +3763,7 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D
37503763 null,
37513764 &io_status_block,
37523765 unreserved_buffer.ptr,
3753 unreserved_buffer.len,
3766 std.math.cast(w.ULONG, unreserved_buffer.len) orelse std.math.maxInt(w.ULONG),
37543767 .BothDirectory,
37553768 w.FALSE,
37563769 null,
......@@ -3849,15 +3862,14 @@ fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out
38493862
38503863 var path_name_w = try w.sliceToPrefixedFileW(dir.handle, sub_path);
38513864
3852 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
3853 const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
3854 const creation = w.FILE_OPEN;
38553865 const h_file = blk: {
38563866 const res = w.OpenFile(path_name_w.span(), .{
38573867 .dir = dir.handle,
3858 .access_mask = access_mask,
3859 .share_access = share_access,
3860 .creation = creation,
3868 .access_mask = .{
3869 .GENERIC = .{ .READ = true },
3870 .STANDARD = .{ .SYNCHRONIZE = true },
3871 },
3872 .creation = .OPEN,
38613873 .filter = .any,
38623874 }) catch |err| switch (err) {
38633875 error.WouldBlock => unreachable,
......@@ -4220,7 +4232,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
42204232
42214233 try current_thread.checkCancel();
42224234
4223 const sub_path_w = try w.sliceToPrefixedFileW(dir.handle, sub_path);
4235 const sub_path_w_buf = try w.sliceToPrefixedFileW(dir.handle, sub_path);
4236 const sub_path_w = sub_path_w_buf.span();
42244237
42254238 const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
42264239 var nt_name: w.UNICODE_STRING = .{
......@@ -4239,31 +4252,32 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
42394252 return error.FileBusy;
42404253 }
42414254
4242 const create_options_flags: w.ULONG = if (remove_dir)
4243 w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT
4244 else
4245 w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT;
4246
4247 var attr: w.OBJECT_ATTRIBUTES = .{
4248 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
4249 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
4250 .Attributes = w.OBJ_CASE_INSENSITIVE,
4251 .ObjectName = &nt_name,
4252 .SecurityDescriptor = null,
4253 .SecurityQualityOfService = null,
4254 };
42554255 var io_status_block: w.IO_STATUS_BLOCK = undefined;
42564256 var tmp_handle: w.HANDLE = undefined;
42574257 var rc = w.ntdll.NtCreateFile(
42584258 &tmp_handle,
4259 w.SYNCHRONIZE | w.DELETE,
4260 &attr,
4259 .{ .STANDARD = .{
4260 .RIGHTS = .{ .DELETE = true },
4261 .SYNCHRONIZE = true,
4262 } },
4263 &.{
4264 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
4265 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
4266 .Attributes = .{},
4267 .ObjectName = &nt_name,
4268 .SecurityDescriptor = null,
4269 .SecurityQualityOfService = null,
4270 },
42614271 &io_status_block,
42624272 null,
4263 0,
4264 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
4265 w.FILE_OPEN,
4266 create_options_flags,
4273 .{},
4274 .VALID_FLAGS,
4275 .OPEN,
4276 .{
4277 .DIRECTORY_FILE = remove_dir,
4278 .NON_DIRECTORY_FILE = !remove_dir,
4279 .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead?
4280 },
42674281 null,
42684282 0,
42694283 );
......@@ -4463,16 +4477,24 @@ fn dirRenameWindows(
44634477 const t: *Threaded = @ptrCast(@alignCast(userdata));
44644478 const current_thread = Thread.getCurrent(t);
44654479
4466 const old_path_w = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);
4467 const new_path_w = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path);
4480 const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);
4481 const old_path_w = old_path_w_buf.span();
4482 const new_path_w_buf = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path);
4483 const new_path_w = new_path_w_buf.span();
44684484 const replace_if_exists = true;
44694485
44704486 try current_thread.checkCancel();
44714487
44724488 const src_fd = w.OpenFile(old_path_w, .{
44734489 .dir = old_dir.handle,
4474 .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | w.DELETE,
4475 .creation = w.FILE_OPEN,
4490 .access_mask = .{
4491 .GENERIC = .{ .WRITE = true },
4492 .STANDARD = .{
4493 .RIGHTS = .{ .DELETE = true },
4494 .SYNCHRONIZE = true,
4495 },
4496 },
4497 .creation = .OPEN,
44764498 .filter = .any, // This function is supposed to rename both files and directories.
44774499 .follow_symlinks = false,
44784500 }) catch |err| switch (err) {
......@@ -4729,9 +4751,12 @@ fn dirSymLinkWindows(
47294751 };
47304752
47314753 const symlink_handle = w.OpenFile(sym_link_path_w.span(), .{
4732 .access_mask = w.SYNCHRONIZE | w.GENERIC_READ | w.GENERIC_WRITE,
4754 .access_mask = .{
4755 .GENERIC = .{ .READ = true, .WRITE = true },
4756 .STANDARD = .{ .SYNCHRONIZE = true },
4757 },
47334758 .dir = dir,
4734 .creation = w.FILE_CREATE,
4759 .creation = .CREATE,
47354760 .filter = if (flags.is_directory) .dir_only else .non_directory_only,
47364761 }) catch |err| switch (err) {
47374762 error.IsDir => return error.PathAlreadyExists,
......@@ -4913,55 +4938,14 @@ fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buf
49134938
49144939 try current_thread.checkCancel();
49154940
4916 var sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
4941 var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
49174942
4918 const result_handle = w.OpenFile(sub_path_w.span(), .{
4919 .access_mask = w.FILE_READ_ATTRIBUTES | w.SYNCHRONIZE,
4920 .dir = dir,
4921 .creation = w.FILE_OPEN,
4922 .follow_symlinks = false,
4923 .filter = .any,
4924 }) catch |err| switch (err) {
4925 error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
4926 error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
4927 error.WouldBlock => return error.Unexpected,
4928 error.NoDevice => return error.FileNotFound,
4929 error.PipeBusy => return error.AccessDenied,
4930 else => |e| return e,
4931 };
4932 defer w.CloseHandle(result_handle);
4933
4934 var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(w.REPARSE_DATA_BUFFER)) = undefined;
4935 _ = w.DeviceIoControl(result_handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {
4936 error.AccessDenied => return error.Unexpected,
4937 error.UnrecognizedVolume => return error.Unexpected,
4938 else => |e| return e,
4939 };
4940
4941 const reparse_struct: *const w.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
4942 const wide_result = switch (reparse_struct.ReparseTag) {
4943 w.IO_REPARSE_TAG_SYMLINK => r: {
4944 const buf: *const w.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
4945 const offset = buf.SubstituteNameOffset >> 1;
4946 const len = buf.SubstituteNameLength >> 1;
4947 const path_buf: [*]const u16 = &buf.PathBuffer;
4948 const is_relative = buf.Flags & w.SYMLINK_FLAG_RELATIVE != 0;
4949 break :r try w.parseReadLinkPath(path_buf[offset..][0..len], is_relative, buffer);
4950 },
4951 w.IO_REPARSE_TAG_MOUNT_POINT => r: {
4952 const buf: *const w.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
4953 const offset = buf.SubstituteNameOffset >> 1;
4954 const len = buf.SubstituteNameLength >> 1;
4955 const path_buf: [*]const u16 = &buf.PathBuffer;
4956 break :r try w.parseReadLinkPath(path_buf[offset..][0..len], false, buffer);
4957 },
4958 else => return error.UnsupportedReparsePointType,
4959 };
4943 const result_w = try w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data);
49604944
4961 const len = std.unicode.calcWtf8Len(wide_result);
4945 const len = std.unicode.calcWtf8Len(result_w);
49624946 if (len > buffer.len) return error.NameTooLong;
49634947
4964 return std.unicode.wtf16LeToWtf8(buffer, wide_result);
4948 return std.unicode.wtf16LeToWtf8(buffer, result_w);
49654949}
49664950
49674951fn 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
60616045
60626046fn fileUnlock(userdata: ?*anyopaque, file: File) void {
60636047 const t: *Threaded = @ptrCast(@alignCast(userdata));
6064 const current_thread = Thread.getCurrent(t);
6048 _ = t;
60656049
60666050 if (is_windows) {
6067 try current_thread.checkCancel();
60686051 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
60696052 const status = windows.ntdll.NtUnlockFile(
60706053 file.handle,
......@@ -6131,7 +6114,7 @@ fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!
61316114 &io_status_block,
61326115 &windows_lock_range_off,
61336116 &windows_lock_range_len,
6134 null,
6117 0,
61356118 );
61366119 if (is_debug) switch (status) {
61376120 .SUCCESS => {},
......@@ -6841,17 +6824,16 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
68416824 // If ImagePathName is a symlink, then it will contain the path of the
68426825 // symlink, not the path that the symlink points to. We want the path
68436826 // that the symlink points to, though, so we need to get the realpath.
6844 var path_name_w = try w.wToPrefixedFileW(null, image_path_name);
6827 var path_name_w_buf = try w.wToPrefixedFileW(null, image_path_name);
68456828
6846 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
6847 const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
6848 const creation = w.FILE_OPEN;
68496829 const h_file = blk: {
6850 const res = w.OpenFile(path_name_w.span(), .{
6830 const res = w.OpenFile(path_name_w_buf.span(), .{
68516831 .dir = null,
6852 .access_mask = access_mask,
6853 .share_access = share_access,
6854 .creation = creation,
6832 .access_mask = .{
6833 .GENERIC = .{ .READ = true },
6834 .STANDARD = .{ .SYNCHRONIZE = true },
6835 },
6836 .creation = .OPEN,
68556837 .filter = .any,
68566838 }) catch |err| switch (err) {
68576839 error.WouldBlock => unreachable,
......@@ -6861,7 +6843,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.Ex
68616843 };
68626844 defer w.CloseHandle(h_file);
68636845
6864 const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
6846 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
68656847
68666848 const len = std.unicode.calcWtf8Len(wide_slice);
68676849 if (len > out_buffer.len)
lib/std/debug/SelfInfo/Windows.zig+1-2
......@@ -222,7 +222,7 @@ const Module = struct {
222222 pdb.file_reader.file.close(io);
223223 pdb.deinit();
224224 }
225 if (di.mapped_file) |*mf| mf.deinit();
225 if (di.mapped_file) |*mf| mf.deinit(io);
226226
227227 var arena = di.arena.promote(gpa);
228228 arena.deinit();
......@@ -331,7 +331,6 @@ const Module = struct {
331331 error.SystemResources,
332332 error.WouldBlock,
333333 error.AccessDenied,
334 error.ProcessNotFound,
335334 error.PermissionDenied,
336335 error.NoSpaceLeft,
337336 error.DeviceBusy,
lib/std/fs/test.zig+82-71
......@@ -27,7 +27,7 @@ const PathType = enum {
2727 pub fn isSupported(self: PathType, target_os: std.Target.Os) bool {
2828 return switch (self) {
2929 .relative => true,
30 .absolute => std.os.isGetFdPathSupportedOnTarget(target_os),
30 .absolute => target_os.tag == .windows, // TODO: implement getPathForHandle for other targets
3131 .unc => target_os.tag == .windows,
3232 };
3333 }
......@@ -49,7 +49,7 @@ const PathType = enum {
4949 // The final path may not actually exist which would cause realpath to fail.
5050 // So instead, we get the path of the dir and join it with the relative path.
5151 var fd_path_buf: [Dir.max_path_bytes]u8 = undefined;
52 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);
52 const dir_path = try getPathForHandle(dir.handle, &fd_path_buf);
5353 return Dir.path.joinZ(allocator, &.{ dir_path, relative_path });
5454 }
5555 }.transform,
......@@ -58,7 +58,7 @@ const PathType = enum {
5858 // Any drive absolute path (C:\foo) can be converted into a UNC path by
5959 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.
6060 var fd_path_buf: [Dir.max_path_bytes]u8 = undefined;
61 const dir_path = try std.os.getFdPath(dir.handle, &fd_path_buf);
61 const dir_path = try getPathForHandle(dir.handle, &fd_path_buf);
6262 const windows_path_type = windows.getWin32PathType(u8, dir_path);
6363 switch (windows_path_type) {
6464 .unc_absolute => return Dir.path.joinZ(allocator, &.{ dir_path, relative_path }),
......@@ -77,6 +77,19 @@ const PathType = enum {
7777 }
7878};
7979
80fn getPathForHandle(handle: File.Handle, out_buffer: *[Dir.max_path_bytes]u8) ![]u8 {
81 switch (native_os) {
82 .windows => {
83 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
84 const wide_slice = try windows.GetFinalPathNameByHandle(handle, .{}, wide_buf[0..]);
85
86 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
87 return out_buffer[0..end_index];
88 },
89 else => @compileError("TODO or unsupported"),
90 }
91}
92
8093const TestContext = struct {
8194 io: Io,
8295 path_type: PathType,
......@@ -488,16 +501,16 @@ test "Dir.Iterator" {
488501
489502 // Create iterator.
490503 var iter = tmp_dir.dir.iterate();
491 while (try iter.next()) |entry| {
504 while (try iter.next(io)) |entry| {
492505 // We cannot just store `entry` as on Windows, we're re-using the name buffer
493506 // which means we'll actually share the `name` pointer between entries!
494507 const name = try allocator.dupe(u8, entry.name);
495 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
508 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind, .inode = 0 });
496509 }
497510
498511 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
499 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
500 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
512 try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 }));
513 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 }));
501514}
502515
503516test "Dir.Iterator many entries" {
......@@ -523,17 +536,17 @@ test "Dir.Iterator many entries" {
523536
524537 // Create iterator.
525538 var iter = tmp_dir.dir.iterate();
526 while (try iter.next()) |entry| {
539 while (try iter.next(io)) |entry| {
527540 // We cannot just store `entry` as on Windows, we're re-using the name buffer
528541 // which means we'll actually share the `name` pointer between entries!
529542 const name = try allocator.dupe(u8, entry.name);
530 try entries.append(.{ .name = name, .kind = entry.kind });
543 try entries.append(.{ .name = name, .kind = entry.kind, .inode = 0 });
531544 }
532545
533546 i = 0;
534547 while (i < num) : (i += 1) {
535548 const name = try std.fmt.bufPrint(&buf, "{}", .{i});
536 try expect(contains(&entries, .{ .name = name, .kind = .file }));
549 try expect(contains(&entries, .{ .name = name, .kind = .file, .inode = 0 }));
537550 }
538551}
539552
......@@ -559,16 +572,16 @@ test "Dir.Iterator twice" {
559572
560573 // Create iterator.
561574 var iter = tmp_dir.dir.iterate();
562 while (try iter.next()) |entry| {
575 while (try iter.next(io)) |entry| {
563576 // We cannot just store `entry` as on Windows, we're re-using the name buffer
564577 // which means we'll actually share the `name` pointer between entries!
565578 const name = try allocator.dupe(u8, entry.name);
566 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
579 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind, .inode = 0 });
567580 }
568581
569582 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
570 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
571 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
583 try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 }));
584 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 }));
572585 }
573586}
574587
......@@ -595,18 +608,18 @@ test "Dir.Iterator reset" {
595608 while (i < 2) : (i += 1) {
596609 var entries = std.array_list.Managed(Dir.Entry).init(allocator);
597610
598 while (try iter.next()) |entry| {
611 while (try iter.next(io)) |entry| {
599612 // We cannot just store `entry` as on Windows, we're re-using the name buffer
600613 // which means we'll actually share the `name` pointer between entries!
601614 const name = try allocator.dupe(u8, entry.name);
602 try entries.append(.{ .name = name, .kind = entry.kind });
615 try entries.append(.{ .name = name, .kind = entry.kind, .inode = 0 });
603616 }
604617
605618 try expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
606 try expect(contains(&entries, .{ .name = "some_file", .kind = .file }));
607 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory }));
619 try expect(contains(&entries, .{ .name = "some_file", .kind = .file, .inode = 0 }));
620 try expect(contains(&entries, .{ .name = "some_dir", .kind = .directory, .inode = 0 }));
608621
609 iter.reset();
622 iter.reader.reset();
610623 }
611624}
612625
......@@ -617,7 +630,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
617630 defer tmp.cleanup();
618631
619632 // Create directory and setup an iterator for it
620 var subdir = try tmp.dir.makeOpenPath(io, "subdir", .{ .iterate = true });
633 var subdir = try tmp.dir.makeOpenPath(io, "subdir", .{ .open_options = .{ .iterate = true } });
621634 defer subdir.close(io);
622635
623636 var iterator = subdir.iterate();
......@@ -632,8 +645,8 @@ test "Dir.Iterator but dir is deleted during iteration" {
632645 tmp.dir.deleteTree(io, "subdir") catch return error.SkipZigTest;
633646
634647 // Now, when we try to iterate, the next call should return null immediately.
635 const entry = try iterator.next();
636 try std.expect(entry == null);
648 const entry = try iterator.next(io);
649 try testing.expect(entry == null);
637650
638651 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
639652 if (native_os == .linux) {
......@@ -652,8 +665,8 @@ fn contains(entries: *const std.array_list.Managed(Dir.Entry), el: Dir.Entry) bo
652665 return false;
653666}
654667
655test "Dir.realpath smoke test" {
656 if (!comptime std.os.isGetFdPathSupportedOnTarget(builtin.os)) return error.SkipZigTest;
668test "Dir.realPath smoke test" {
669 if (native_os == .wasi) return error.SkipZigTest;
657670
658671 try testWithAllSupportedPathTypes(struct {
659672 fn impl(ctx: *TestContext) !void {
......@@ -686,10 +699,10 @@ test "Dir.realpath smoke test" {
686699
687700 // First, test non-alloc version
688701 {
689 const file_path = try ctx.dir.realpath(test_file_path, &buf);
702 const file_path = try ctx.dir.realPath(io, test_file_path, &buf);
690703 try expectEqualStrings(expected_file_path, file_path);
691704
692 const dir_path = try ctx.dir.realpath(test_dir_path, &buf);
705 const dir_path = try ctx.dir.realPath(io, test_dir_path, &buf);
693706 try expectEqualStrings(expected_dir_path, dir_path);
694707 }
695708
......@@ -769,7 +782,7 @@ test "Dir.statFile" {
769782
770783 try expectError(error.FileNotFound, ctx.dir.statFile(io, test_dir_name, .{}));
771784
772 try ctx.dir.makeDir(io, test_dir_name);
785 try ctx.dir.makeDir(io, test_dir_name, .default_dir);
773786
774787 const stat = try ctx.dir.statFile(io, test_dir_name, .{});
775788 try expectEqual(.directory, stat.kind);
......@@ -1148,7 +1161,7 @@ test "openExecutable" {
11481161
11491162 const io = testing.io;
11501163
1151 const self_exe_file = try std.fs.openExecutable(.{});
1164 const self_exe_file = try std.process.openExecutable(io, .{});
11521165 self_exe_file.close(io);
11531166}
11541167
......@@ -1157,7 +1170,8 @@ test "executablePath" {
11571170
11581171 const io = testing.io;
11591172 var buf: [Dir.max_path_bytes]u8 = undefined;
1160 const buf_self_exe_path = try std.process.executablePath(io, &buf);
1173 const len = try std.process.executablePath(io, &buf);
1174 const buf_self_exe_path = buf[0..len];
11611175 const alloc_self_exe_path = try std.process.executablePathAlloc(io, testing.allocator);
11621176 defer testing.allocator.free(alloc_self_exe_path);
11631177 try expectEqualSlices(u8, buf_self_exe_path, alloc_self_exe_path);
......@@ -1246,7 +1260,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
12461260 .data = "blah",
12471261 });
12481262
1249 try ctx.dir.deleteTreeMinStackSize(dir_path);
1263 try ctx.dir.deleteTreeMinStackSize(io, dir_path);
12501264 try expectError(error.FileNotFound, ctx.dir.openDir(io, dir_path, .{}));
12511265 }
12521266 }.impl);
......@@ -1395,7 +1409,7 @@ fn testFilenameLimits(io: Io, iterable_dir: Dir, maxed_filename: []const u8) !vo
13951409 defer walker.deinit();
13961410
13971411 var count: usize = 0;
1398 while (try walker.next()) |entry| {
1412 while (try walker.next(io)) |entry| {
13991413 try expectEqualStrings(maxed_filename, entry.basename);
14001414 count += 1;
14011415 }
......@@ -1452,7 +1466,7 @@ test "writev, readv" {
14521466 try writer.interface.flush();
14531467 try expectEqual(@as(u64, line1.len + line2.len), try src_file.length(io));
14541468
1455 var reader = writer.moveToReader(io);
1469 var reader = writer.moveToReader();
14561470 try reader.seekTo(0);
14571471 try reader.interface.readVecAll(&read_vecs);
14581472 try expectEqualStrings(&buf1, "line2\n");
......@@ -1483,7 +1497,7 @@ test "pwritev, preadv" {
14831497 try writer.interface.flush();
14841498 try expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.length(io));
14851499
1486 var reader = writer.moveToReader(io);
1500 var reader = writer.moveToReader();
14871501 try reader.seekTo(16);
14881502 try reader.interface.readVecAll(&read_vecs);
14891503 try expectEqualStrings(&buf1, "line2\n");
......@@ -1549,7 +1563,7 @@ test "sendfile" {
15491563 try expectEqual(10, try file_writer.interface.sendFileAll(&file_reader, .limited(10)));
15501564 try file_writer.interface.writeVecAll(&trailers);
15511565 try file_writer.interface.flush();
1552 var fr = file_writer.moveToReader(io);
1566 var fr = file_writer.moveToReader();
15531567 try fr.seekTo(0);
15541568 const amt = try fr.interface.readSliceShort(&written_buf);
15551569 try expectEqualStrings("header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n", written_buf[0..amt]);
......@@ -1586,7 +1600,7 @@ test "sendfile with buffered data" {
15861600 try expectEqual(4, try file_writer.interface.sendFileAll(&file_reader, .limited(4)));
15871601
15881602 var written_buf: [8]u8 = undefined;
1589 var fr = file_writer.moveToReader(io);
1603 var fr = file_writer.moveToReader();
15901604 try fr.seekTo(0);
15911605 const amt = try fr.interface.readSliceShort(&written_buf);
15921606
......@@ -1609,7 +1623,7 @@ test "copyFile" {
16091623 try ctx.dir.copyFile(src_file, ctx.dir, dest_file, io, .{});
16101624 defer ctx.dir.deleteFile(io, dest_file) catch {};
16111625
1612 try ctx.dir.copyFile(src_file, ctx.dir, dest_file2, io, .{ .permissions = File.default_mode });
1626 try ctx.dir.copyFile(src_file, ctx.dir, dest_file2, io, .{ .permissions = .default_file });
16131627 defer ctx.dir.deleteFile(io, dest_file2) catch {};
16141628
16151629 try expectFileContents(io, ctx.dir, dest_file, data);
......@@ -1714,12 +1728,12 @@ test "open file with exclusive lock twice, make sure second lock waits" {
17141728 errdefer file.close(io);
17151729
17161730 const S = struct {
1717 fn checkFn(dir: *Dir, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void {
1731 fn checkFn(inner_ctx: *TestContext, path: []const u8, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void {
17181732 started.set();
1719 const file1 = try dir.createFile(io, path, .{ .lock = .exclusive });
1733 const file1 = try inner_ctx.dir.createFile(inner_ctx.io, path, .{ .lock = .exclusive });
17201734
17211735 locked.set();
1722 file1.close(io);
1736 file1.close(inner_ctx.io);
17231737 }
17241738 };
17251739
......@@ -1727,7 +1741,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
17271741 var locked: std.Thread.ResetEvent = .unset;
17281742
17291743 const t = try std.Thread.spawn(.{}, S.checkFn, .{
1730 &ctx.dir,
1744 ctx,
17311745 filename,
17321746 &started,
17331747 &locked,
......@@ -1848,7 +1862,7 @@ test "walker" {
18481862 defer walker.deinit();
18491863
18501864 var num_walked: usize = 0;
1851 while (try walker.next()) |entry| {
1865 while (try walker.next(io)) |entry| {
18521866 expect(expected_basenames.has(entry.basename)) catch |err| {
18531867 std.debug.print("found unexpected basename: {f}\n", .{std.ascii.hexEscape(entry.basename, .lower)});
18541868 return err;
......@@ -1910,10 +1924,10 @@ test "selective walker, skip entries that start with ." {
19101924 defer walker.deinit();
19111925
19121926 var num_walked: usize = 0;
1913 while (try walker.next()) |entry| {
1927 while (try walker.next(io)) |entry| {
19141928 if (entry.basename[0] == '.') continue;
19151929 if (entry.kind == .directory) {
1916 try walker.enter(entry);
1930 try walker.enter(io, entry);
19171931 }
19181932
19191933 expect(expected_basenames.has(entry.basename)) catch |err| {
......@@ -1953,7 +1967,7 @@ test "walker without fully iterating" {
19531967 try tmp.dir.makePath(io, "b");
19541968
19551969 var num_walked: usize = 0;
1956 while (try walker.next()) |_| {
1970 while (try walker.next(io)) |_| {
19571971 num_walked += 1;
19581972 break;
19591973 }
......@@ -2093,48 +2107,42 @@ test "invalid UTF-8/WTF-8 paths" {
20932107 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte
20942108 const invalid_path = try ctx.transformPath("\xFF");
20952109
2096 try expectError(expected_err, ctx.dir.openFile(invalid_path, .{}));
2110 try expectError(expected_err, ctx.dir.openFile(io, invalid_path, .{}));
20972111
2098 try expectError(expected_err, ctx.dir.createFile(invalid_path, .{}));
2112 try expectError(expected_err, ctx.dir.createFile(io, invalid_path, .{}));
20992113
2100 try expectError(expected_err, ctx.dir.makeDir(invalid_path, .default_dir));
2114 try expectError(expected_err, ctx.dir.makeDir(io, invalid_path, .default_dir));
21012115
2102 try expectError(expected_err, ctx.dir.makePath(invalid_path));
2103 try expectError(expected_err, ctx.dir.makeOpenPath(invalid_path, .{}));
2116 try expectError(expected_err, ctx.dir.makePath(io, invalid_path));
2117 try expectError(expected_err, ctx.dir.makeOpenPath(io, invalid_path, .{}));
21042118
2105 try expectError(expected_err, ctx.dir.openDir(invalid_path, .{}));
2119 try expectError(expected_err, ctx.dir.openDir(io, invalid_path, .{}));
21062120
2107 try expectError(expected_err, ctx.dir.deleteFile(invalid_path));
2121 try expectError(expected_err, ctx.dir.deleteFile(io, invalid_path));
21082122
21092123 try expectError(expected_err, ctx.dir.deleteDir(io, invalid_path));
21102124
21112125 try expectError(expected_err, ctx.dir.rename(invalid_path, ctx.dir, invalid_path, io));
21122126
21132127 try expectError(expected_err, ctx.dir.symLink(io, invalid_path, invalid_path, .{}));
2114 if (native_os == .wasi) {
2115 try expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
2116 }
21172128
21182129 try expectError(expected_err, ctx.dir.readLink(io, invalid_path, &[_]u8{}));
2119 if (native_os == .wasi) {
2120 try expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
2121 }
21222130
2123 try expectError(expected_err, ctx.dir.readFile(invalid_path, &[_]u8{}));
2124 try expectError(expected_err, ctx.dir.readFileAlloc(invalid_path, testing.allocator, .limited(0)));
2131 try expectError(expected_err, ctx.dir.readFile(io, invalid_path, &[_]u8{}));
2132 try expectError(expected_err, ctx.dir.readFileAlloc(io, invalid_path, testing.allocator, .limited(0)));
21252133
21262134 try expectError(expected_err, ctx.dir.deleteTree(io, invalid_path));
2127 try expectError(expected_err, ctx.dir.deleteTreeMinStackSize(invalid_path));
2135 try expectError(expected_err, ctx.dir.deleteTreeMinStackSize(io, invalid_path));
21282136
21292137 try expectError(expected_err, ctx.dir.writeFile(io, .{ .sub_path = invalid_path, .data = "" }));
21302138
2131 try expectError(expected_err, ctx.dir.access(invalid_path, .{}));
2139 try expectError(expected_err, ctx.dir.access(io, invalid_path, .{}));
21322140
21332141 var dir = ctx.dir;
21342142 try expectError(expected_err, dir.updateFile(io, invalid_path, dir, invalid_path, .{}));
21352143 try expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, io, .{}));
21362144
2137 try expectError(expected_err, ctx.dir.statFile(invalid_path));
2145 try expectError(expected_err, ctx.dir.statFile(io, invalid_path, .{}));
21382146
21392147 if (native_os != .wasi) {
21402148 try expectError(expected_err, ctx.dir.realPath(io, invalid_path, &[_]u8{}));
......@@ -2146,17 +2154,17 @@ test "invalid UTF-8/WTF-8 paths" {
21462154 if (native_os != .wasi and ctx.path_type != .relative) {
21472155 try expectError(expected_err, Dir.copyFileAbsolute(invalid_path, invalid_path, io, .{}));
21482156 try expectError(expected_err, Dir.makeDirAbsolute(io, invalid_path, .default_dir));
2149 try expectError(expected_err, Dir.deleteDirAbsolute(invalid_path));
2157 try expectError(expected_err, Dir.deleteDirAbsolute(io, invalid_path));
21502158 try expectError(expected_err, Dir.renameAbsolute(invalid_path, invalid_path, io));
21512159 try expectError(expected_err, Dir.openDirAbsolute(io, invalid_path, .{}));
21522160 try expectError(expected_err, Dir.openFileAbsolute(io, invalid_path, .{}));
2153 try expectError(expected_err, Dir.accessAbsolute(invalid_path, .{}));
2154 try expectError(expected_err, Dir.createFileAbsolute(invalid_path, .{}));
2155 try expectError(expected_err, Dir.deleteFileAbsolute(invalid_path));
2161 try expectError(expected_err, Dir.accessAbsolute(io, invalid_path, .{}));
2162 try expectError(expected_err, Dir.createFileAbsolute(io, invalid_path, .{}));
2163 try expectError(expected_err, Dir.deleteFileAbsolute(io, invalid_path));
21562164 var readlink_buf: [Dir.max_path_bytes]u8 = undefined;
2157 try expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf));
2158 try expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{}));
2159 try expectError(expected_err, Dir.realPathAlloc(io, invalid_path, testing.allocator));
2165 try expectError(expected_err, Dir.readLinkAbsolute(io, invalid_path, &readlink_buf));
2166 try expectError(expected_err, Dir.symLinkAbsolute(io, invalid_path, invalid_path, .{}));
2167 try expectError(expected_err, Dir.realPathAbsoluteAlloc(io, invalid_path, testing.allocator));
21602168 }
21612169 }
21622170 }.impl);
......@@ -2327,7 +2335,8 @@ test "readlink on Windows" {
23272335
23282336fn testReadLinkWindows(io: Io, target_path: []const u8, symlink_path: []const u8) !void {
23292337 var buffer: [Dir.max_path_bytes]u8 = undefined;
2330 const given = try Dir.readLinkAbsolute(io, symlink_path, &buffer);
2338 const len = try Dir.readLinkAbsolute(io, symlink_path, &buffer);
2339 const given = buffer[0..len];
23312340 try expect(mem.eql(u8, target_path, given));
23322341}
23332342
......@@ -2495,7 +2504,7 @@ test "access smoke test" {
24952504
24962505 {
24972506 // Create some directory
2498 try tmp.dir.makeDir(io, "some_dir", .default_file);
2507 try tmp.dir.makeDir(io, "some_dir", .default_dir);
24992508 }
25002509
25012510 {
......@@ -2563,6 +2572,8 @@ test "open smoke test" {
25632572}
25642573
25652574test "hard link with different directories" {
2575 if (native_os == .wasi or native_os == .windows) return error.SkipZigTest;
2576
25662577 const io = testing.io;
25672578
25682579 var tmp = tmpDir(.{});
lib/std/os/windows.zig+3-3
......@@ -3582,7 +3582,7 @@ test QueryObjectName {
35823582 //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
35833583 var tmp = std.testing.tmpDir(.{});
35843584 defer tmp.cleanup();
3585 const handle = tmp.dir.fd;
3585 const handle = tmp.dir.handle;
35863586 var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
35873587
35883588 const result_path = try QueryObjectName(handle, &out_buffer);
......@@ -3845,7 +3845,7 @@ test GetFinalPathNameByHandle {
38453845 //any file will do
38463846 var tmp = std.testing.tmpDir(.{});
38473847 defer tmp.cleanup();
3848 const handle = tmp.dir.fd;
3848 const handle = tmp.dir.handle;
38493849 var buffer: [PATH_MAX_WIDE]u16 = undefined;
38503850
38513851 //check with sufficient size
......@@ -4638,7 +4638,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE
46384638 }
46394639 // We can also skip GetFinalPathNameByHandle if the handle matches
46404640 // the handle returned by Io.Dir.cwd()
4641 if (dir.? == Io.Dir.cwd().fd) {
4641 if (dir.? == Io.Dir.cwd().handle) {
46424642 break :path_to_get path;
46434643 }
46444644 // At this point, we know we have a relative path that had too many