authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-12 00:59:10+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-12 00:59:10+01:00
log545982c0298812869712a58b9f16772f9b09abcd
tree21a0d65aad839acf6e891cccea4a1565813bb1ef
parent514f6e589ca441c3ac115b8c764bdc9f6d4aca01
parent4cf7dc22fa6796d231397ee38a54536d4e94f5bc

Merge pull request 'A few Windows fixes' (#30757) from squeek502/zig:windows-misc-fixes into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30757 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

3 files changed, 28 insertions(+), 14 deletions(-)

lib/std/Io/Threaded.zig+6
...@@ -7987,6 +7987,9 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u...@@ -7987,6 +7987,9 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u
7987 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),7987 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
7988 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),7988 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
7989 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),7989 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
7990 // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
7991 // a handle to a directory.
7992 .INVALID_FUNCTION => return syscall.fail(error.IsDir),
7990 else => |err| {7993 else => |err| {
7991 syscall.finish();7994 syscall.finish();
7992 return windows.unexpectedError(err);7995 return windows.unexpectedError(err);
...@@ -8144,6 +8147,9 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []...@@ -8144,6 +8147,9 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []
8144 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),8147 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
8145 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),8148 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
8146 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),8149 .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading),
8150 // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
8151 // a handle to a directory.
8152 .INVALID_FUNCTION => return syscall.fail(error.IsDir),
8147 else => |err| {8153 else => |err| {
8148 syscall.finish();8154 syscall.finish();
8149 return windows.unexpectedError(err);8155 return windows.unexpectedError(err);
lib/std/fs/test.zig+17-13
...@@ -182,20 +182,21 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:...@@ -182,20 +182,21 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:
182}182}
183183
184// For use in test setup. If the symlink creation fails on Windows with184// For use in test setup. If the symlink creation fails on Windows with
185// AccessDenied, then make the test failure silent (it is not a Zig failure).185// AccessDenied/PermissionDenied/FileSystem, then make the test failure silent (it is not a Zig failure).
186fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {186fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
187 return dir.symLink(io, target, link, flags) catch |err| switch (err) {187 return dir.symLink(io, target, link, flags) catch |err| switch (err) {
188 // Symlink requires admin privileges on windows, so this test can legitimately fail.188 // On Windows, symlinks require admin privileges and the underlying filesystem must support symlinks
189 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,189 error.AccessDenied, error.PermissionDenied, error.FileSystem => if (native_os == .windows) return error.SkipZigTest else return err,
190 else => return err,190 else => return err,
191 };191 };
192}192}
193193
194// For use in test setup. If the symlink creation fails on Windows with194// For use in test setup. If the symlink creation fails on Windows with
195// AccessDenied, then make the test failure silent (it is not a Zig failure).195// AccessDeniedPermissionDenied/FileSystem, then make the test failure silent (it is not a Zig failure).
196fn setupSymlinkAbsolute(io: Io, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {196fn setupSymlinkAbsolute(io: Io, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
197 return Dir.symLinkAbsolute(io, target, link, flags) catch |err| switch (err) {197 return Dir.symLinkAbsolute(io, target, link, flags) catch |err| switch (err) {
198 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,198 // On Windows, symlinks require admin privileges and the underlying filesystem must support symlinks
199 error.AccessDenied, error.PermissionDenied, error.FileSystem => if (native_os == .windows) return error.SkipZigTest else return err,
199 else => return err,200 else => return err,
200 };201 };
201}202}
...@@ -847,7 +848,16 @@ test "file operations on directories" {...@@ -847,7 +848,16 @@ test "file operations on directories" {
847848
848 {849 {
849 const handle = try ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = true, .mode = .read_only });850 const handle = try ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = true, .mode = .read_only });
850 handle.close(io);851 defer handle.close(io);
852
853 // Reading from the handle should fail
854 const expected_err = switch (native_os) {
855 .wasi => error.NotOpenForReading,
856 else => error.IsDir,
857 };
858 var buf: [1]u8 = undefined;
859 try expectError(expected_err, handle.readStreaming(io, &.{&buf}));
860 try expectError(expected_err, handle.readPositional(io, &.{&buf}, 0));
851 }861 }
852 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only }));862 try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only }));
853863
...@@ -2364,13 +2374,7 @@ test "readlinkat" {...@@ -2364,13 +2374,7 @@ test "readlinkat" {
2364 try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" });2374 try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" });
23652375
2366 // create a symbolic link2376 // create a symbolic link
2367 tmp.dir.symLink(io, "file.txt", "link", .{}) catch |err| switch (err) {2377 try setupSymlink(io, tmp.dir, "file.txt", "link", .{});
2368 error.AccessDenied => {
2369 // Symlink requires admin privileges on windows, so this test can legitimately fail.
2370 if (native_os == .windows) return error.SkipZigTest;
2371 },
2372 else => |e| return e,
2373 };
23742378
2375 // read the link2379 // read the link
2376 var buffer: [Dir.max_path_bytes]u8 = undefined;2380 var buffer: [Dir.max_path_bytes]u8 = undefined;
lib/std/os/windows.zig+5-1
...@@ -267,7 +267,11 @@ pub const FILE = struct {...@@ -267,7 +267,11 @@ pub const FILE = struct {
267267
268 pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 {268 pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 {
269 const start: [*]const u8 = @ptrCast(fri);269 const start: [*]const u8 = @ptrCast(fri);
270 return start[0 .. @offsetOf(RENAME_INFORMATION, "FileName") + fri.FileNameLength];270 // The ABI size of the documented struct is 24 bytes, and attempting to use any size
271 // less than that will trigger INFO_LENGTH_MISMATCH, so enforce a minimum in cases where,
272 // for example, FileNameLength is 1 so only 22 bytes are technically needed.
273 const size = @max(24, @offsetOf(RENAME_INFORMATION, "FileName") + fri.FileNameLength);
274 return start[0..size];
271 }275 }
272 };276 };
273277