diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index b9a0474de99359e7f6f04f50e5a222dc466a06c5..212ee2fba18bcff3c33bcdaf8ae66fb7dff8fd9c 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -7987,6 +7987,9 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u .LOCK_VIOLATION => return syscall.fail(error.LockViolation), .ACCESS_DENIED => return syscall.fail(error.AccessDenied), .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading), + // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing + // a handle to a directory. + .INVALID_FUNCTION => return syscall.fail(error.IsDir), else => |err| { syscall.finish(); return windows.unexpectedError(err); @@ -8144,6 +8147,9 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] .LOCK_VIOLATION => return syscall.fail(error.LockViolation), .ACCESS_DENIED => return syscall.fail(error.AccessDenied), .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading), + // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing + // a handle to a directory. + .INVALID_FUNCTION => return syscall.fail(error.IsDir), else => |err| { syscall.finish(); return windows.unexpectedError(err); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 63c51e6ed5b7429af7be18379a41fa30cd9c81d3..07e9a7760573603c562d8f770283cee1bb832ff8 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -182,20 +182,21 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: } // For use in test setup. If the symlink creation fails on Windows with -// AccessDenied, then make the test failure silent (it is not a Zig failure). +// AccessDenied/PermissionDenied/FileSystem, then make the test failure silent (it is not a Zig failure). fn setupSymlink(io: Io, dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void { return dir.symLink(io, target, link, flags) catch |err| switch (err) { - // Symlink requires admin privileges on windows, so this test can legitimately fail. - error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err, + // On Windows, symlinks require admin privileges and the underlying filesystem must support symlinks + error.AccessDenied, error.PermissionDenied, error.FileSystem => if (native_os == .windows) return error.SkipZigTest else return err, else => return err, }; } // For use in test setup. If the symlink creation fails on Windows with -// AccessDenied, then make the test failure silent (it is not a Zig failure). +// AccessDeniedPermissionDenied/FileSystem, then make the test failure silent (it is not a Zig failure). fn setupSymlinkAbsolute(io: Io, target: []const u8, link: []const u8, flags: SymLinkFlags) !void { return Dir.symLinkAbsolute(io, target, link, flags) catch |err| switch (err) { - error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err, + // On Windows, symlinks require admin privileges and the underlying filesystem must support symlinks + error.AccessDenied, error.PermissionDenied, error.FileSystem => if (native_os == .windows) return error.SkipZigTest else return err, else => return err, }; } @@ -847,7 +848,16 @@ test "file operations on directories" { { const handle = try ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = true, .mode = .read_only }); - handle.close(io); + defer handle.close(io); + + // Reading from the handle should fail + const expected_err = switch (native_os) { + .wasi => error.NotOpenForReading, + else => error.IsDir, + }; + var buf: [1]u8 = undefined; + try expectError(expected_err, handle.readStreaming(io, &.{&buf})); + try expectError(expected_err, handle.readPositional(io, &.{&buf}, 0)); } try expectError(error.IsDir, ctx.dir.openFile(io, test_dir_name, .{ .allow_directory = false, .mode = .read_only })); @@ -2364,13 +2374,7 @@ test "readlinkat" { try tmp.dir.writeFile(io, .{ .sub_path = "file.txt", .data = "nonsense" }); // create a symbolic link - tmp.dir.symLink(io, "file.txt", "link", .{}) catch |err| switch (err) { - error.AccessDenied => { - // Symlink requires admin privileges on windows, so this test can legitimately fail. - if (native_os == .windows) return error.SkipZigTest; - }, - else => |e| return e, - }; + try setupSymlink(io, tmp.dir, "file.txt", "link", .{}); // read the link var buffer: [Dir.max_path_bytes]u8 = undefined; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index a96334add99833f02b85874ac6d2aa65f8903417..a1512e6e4f67adefe593ccbf0ca72300015b2712 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -267,7 +267,11 @@ pub const FILE = struct { pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 { const start: [*]const u8 = @ptrCast(fri); - return start[0 .. @offsetOf(RENAME_INFORMATION, "FileName") + fri.FileNameLength]; + // The ABI size of the documented struct is 24 bytes, and attempting to use any size + // less than that will trigger INFO_LENGTH_MISMATCH, so enforce a minimum in cases where, + // for example, FileNameLength is 1 so only 22 bytes are technically needed. + const size = @max(24, @offsetOf(RENAME_INFORMATION, "FileName") + fri.FileNameLength); + return start[0..size]; } };