diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index ed7966d014..5aba86c2bb 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -220,8 +220,14 @@ pub fn io(t: *Threaded) Io { .fileClose = fileClose, .fileWriteStreaming = fileWriteStreaming, .fileWritePositional = fileWritePositional, - .fileReadStreaming = fileReadStreaming, - .fileReadPositional = fileReadPositional, + .fileReadStreaming = switch (builtin.os.tag) { + .windows => fileReadStreamingWindows, + else => fileReadStreamingPosix, + }, + .fileReadPositional = switch (builtin.os.tag) { + .windows => fileReadPositionalWindows, + else => fileReadPositionalPosix, + }, .fileSeekBy = fileSeekBy, .fileSeekTo = fileSeekTo, .openSelfExe = openSelfExe, @@ -258,15 +264,21 @@ pub fn io(t: *Threaded) Io { .netConnectUnix = netConnectUnix, .netClose = netClose, .netRead = switch (builtin.os.tag) { - .windows => @panic("TODO"), + .windows => netReadWindows, else => netReadPosix, }, .netWrite = switch (builtin.os.tag) { - .windows => @panic("TODO"), + .windows => netWriteWindows, else => netWritePosix, }, - .netSend = netSend, - .netReceive = netReceive, + .netSend = switch (builtin.os.tag) { + .windows => netSendWindows, + else => netSendPosix, + }, + .netReceive = switch (builtin.os.tag) { + .windows => netReceiveWindows, + else => netReceivePosix, + }, .netInterfaceNameResolve = netInterfaceNameResolve, .netInterfaceName = netInterfaceName, .netLookup = netLookup, @@ -284,6 +296,10 @@ const have_futex = switch (builtin.cpu.arch) { .wasm32, .wasm64 => builtin.cpu.has(.wasm, .atomics), else => true, }; +const have_preadv = switch (native_os) { + .windows, .haiku, .serenity => false, // 💩💩💩 + else => true, +}; const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; @@ -1899,12 +1915,19 @@ fn dirOpenFileWindows( flags: Io.File.OpenFlags, ) Io.File.OpenError!Io.File { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); - - const w = windows; - const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path); + const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path); const sub_path_w = sub_path_w_array.span(); + return dirOpenFileWindowsInner(t, dir, sub_path_w, flags); +} +fn dirOpenFileWindowsInner( + t: *Threaded, + dir: Io.Dir, + sub_path_w: [:0]const u16, + flags: Io.File.OpenFlags, +) Io.File.OpenError!Io.File { + try t.checkCancel(); + const w = windows; const handle = try w.OpenFile(sub_path_w, .{ .dir = dir.handle, .access_mask = w.SYNCHRONIZE | @@ -2247,47 +2270,9 @@ fn fileClose(userdata: ?*anyopaque, file: Io.File) void { posix.close(file.handle); } -fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize { +fn fileReadStreamingPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); - if (is_windows) { - const DWORD = windows.DWORD; - var index: usize = 0; - var truncate: usize = 0; - var total: usize = 0; - while (index < data.len) { - try t.checkCancel(); - { - const untruncated = data[index]; - data[index] = untruncated[truncate..]; - defer data[index] = untruncated; - const buffer = data[index..]; - const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); - var n: DWORD = undefined; - if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) { - switch (windows.GetLastError()) { - .IO_PENDING => |err| return windows.statusBug(err), - .OPERATION_ABORTED => continue, - .BROKEN_PIPE => return 0, - .HANDLE_EOF => return 0, - .NETNAME_DELETED => return error.ConnectionResetByPeer, - .LOCK_VIOLATION => return error.LockViolation, - .ACCESS_DENIED => return error.AccessDenied, - .INVALID_HANDLE => return error.NotOpenForReading, - else => |err| return windows.unexpectedError(err), - } - } - total += n; - truncate += n; - } - while (index < data.len and truncate >= data[index].len) { - truncate -= data[index].len; - index += 1; - } - } - return total; - } - var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; for (data) |buf| { @@ -2348,70 +2333,37 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File } } -fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { +fn fileReadStreamingWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + try t.checkCancel(); + + const DWORD = windows.DWORD; + var index: usize = 0; + while (data[index].len == 0) index += 1; + + const buffer = data[index]; + const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); + var n: DWORD = undefined; + if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) { + switch (windows.GetLastError()) { + .IO_PENDING => |err| return windows.errorBug(err), + .OPERATION_ABORTED => return error.Canceled, + .BROKEN_PIPE => return 0, + .HANDLE_EOF => return 0, + .NETNAME_DELETED => return error.ConnectionResetByPeer, + .LOCK_VIOLATION => return error.LockViolation, + .ACCESS_DENIED => return error.AccessDenied, + .INVALID_HANDLE => return error.NotOpenForReading, + else => |err| return windows.unexpectedError(err), + } + } + return n; +} + +fn fileReadPositionalPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); - if (is_windows) { - const DWORD = windows.DWORD; - const OVERLAPPED = windows.OVERLAPPED; - var index: usize = 0; - var truncate: usize = 0; - var total: usize = 0; - while (true) { - try t.checkCancel(); - { - const untruncated = data[index]; - data[index] = untruncated[truncate..]; - defer data[index] = untruncated; - const buffer = data[index..]; - const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); - var n: DWORD = undefined; - var overlapped_data: OVERLAPPED = undefined; - const overlapped: ?*OVERLAPPED = if (offset) |off| blk: { - overlapped_data = .{ - .Internal = 0, - .InternalHigh = 0, - .DUMMYUNIONNAME = .{ - .DUMMYSTRUCTNAME = .{ - .Offset = @as(u32, @truncate(off)), - .OffsetHigh = @as(u32, @truncate(off >> 32)), - }, - }, - .hEvent = null, - }; - break :blk &overlapped_data; - } else null; - if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, overlapped) == 0) { - switch (windows.GetLastError()) { - .IO_PENDING => |err| return windows.statusBug(err), - .OPERATION_ABORTED => continue, - .BROKEN_PIPE => return 0, - .HANDLE_EOF => return 0, - .NETNAME_DELETED => return error.ConnectionResetByPeer, - .LOCK_VIOLATION => return error.LockViolation, - .ACCESS_DENIED => return error.AccessDenied, - .INVALID_HANDLE => return error.NotOpenForReading, - else => |err| return windows.unexpectedError(err), - } - } - total += n; - truncate += n; - } - while (index < data.len and truncate >= data[index].len) { - truncate -= data[index].len; - index += 1; - } - } - return total; - } - - const have_pread_but_not_preadv = switch (native_os) { - .windows, .haiku, .serenity => true, - else => false, - }; - if (have_pread_but_not_preadv) { - @compileError("TODO"); - } + if (!have_preadv) @compileError("TODO"); var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; @@ -2480,6 +2432,48 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset } } +fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + try t.checkCancel(); + + const DWORD = windows.DWORD; + const OVERLAPPED = windows.OVERLAPPED; + + var index: usize = 0; + while (data[index].len == 0) index += 1; + + const buffer = data[index]; + const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); + var n: DWORD = undefined; + var overlapped: OVERLAPPED = .{ + .Internal = 0, + .InternalHigh = 0, + .DUMMYUNIONNAME = .{ + .DUMMYSTRUCTNAME = .{ + .Offset = @as(u32, @truncate(offset)), + .OffsetHigh = @as(u32, @truncate(offset >> 32)), + }, + }, + .hEvent = null, + }; + + if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) == 0) { + switch (windows.GetLastError()) { + .IO_PENDING => |err| return windows.errorBug(err), + .OPERATION_ABORTED => return error.Canceled, + .BROKEN_PIPE => return 0, + .HANDLE_EOF => return 0, + .NETNAME_DELETED => return error.ConnectionResetByPeer, + .LOCK_VIOLATION => return error.LockViolation, + .ACCESS_DENIED => return error.AccessDenied, + .INVALID_HANDLE => return error.NotOpenForReading, + else => |err| return windows.unexpectedError(err), + } + } + + return n; +} + fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); try t.checkCancel(); @@ -2563,11 +2557,9 @@ fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelf // the file, we can let the openFileW call follow the symlink for us. const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; - const prefixed_path_w_array = try windows.wToPrefixedFileW(null, image_path_name); - const prefixed_path_w = prefixed_path_w_array.span(); const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle; - return dirOpenFileWindows(t, .{ .handle = cwd_handle }, prefixed_path_w, flags); + return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags); } @panic("TODO"); } @@ -3493,7 +3485,16 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net. } } -fn netSend( +fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { + if (!have_networking) return .{ error.NetworkDown, 0 }; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = handle; + _ = data; + @panic("TODO"); +} + +fn netSendPosix( userdata: ?*anyopaque, handle: net.Socket.Handle, messages: []net.OutgoingMessage, @@ -3504,9 +3505,9 @@ fn netSend( const posix_flags: u32 = @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) | - @as(u32, if (flags.dont_route) posix.MSG.DONTROUTE else 0) | - @as(u32, if (flags.eor) posix.MSG.EOR else 0) | - @as(u32, if (flags.oob) posix.MSG.OOB else 0) | + @as(u32, if (@hasDecl(posix.MSG, "DONTROUTE") and flags.dont_route) posix.MSG.DONTROUTE else 0) | + @as(u32, if (@hasDecl(posix.MSG, "EOR") and flags.eor) posix.MSG.EOR else 0) | + @as(u32, if (@hasDecl(posix.MSG, "OOB") and flags.oob) posix.MSG.OOB else 0) | @as(u32, if (@hasDecl(posix.MSG, "FASTOPEN") and flags.fastopen) posix.MSG.FASTOPEN else 0) | posix.MSG.NOSIGNAL; @@ -3522,6 +3523,21 @@ fn netSend( return .{ null, i }; } +fn netSendWindows( + userdata: ?*anyopaque, + handle: net.Socket.Handle, + messages: []net.OutgoingMessage, + flags: net.SendFlags, +) struct { ?net.Socket.SendError, usize } { + if (!have_networking) return .{ error.NetworkDown, 0 }; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = handle; + _ = messages; + _ = flags; + @panic("TODO"); +} + fn netSendOne( t: *Threaded, handle: net.Socket.Handle, @@ -3676,7 +3692,7 @@ fn netSendMany( } } -fn netReceive( +fn netReceivePosix( userdata: ?*anyopaque, handle: net.Socket.Handle, message_buffer: []net.IncomingMessage, @@ -3805,6 +3821,25 @@ fn netReceive( } } +fn netReceiveWindows( + userdata: ?*anyopaque, + handle: net.Socket.Handle, + message_buffer: []net.IncomingMessage, + data_buffer: []u8, + flags: net.ReceiveFlags, + timeout: Io.Timeout, +) struct { ?net.Socket.ReceiveTimeoutError, usize } { + if (!have_networking) return .{ error.NetworkDown, 0 }; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = handle; + _ = message_buffer; + _ = data_buffer; + _ = flags; + _ = timeout; + @panic("TODO"); +} + fn netWritePosix( userdata: ?*anyopaque, fd: net.Socket.Handle, @@ -3887,6 +3922,22 @@ fn netWritePosix( } } +fn netWriteWindows( + userdata: ?*anyopaque, + handle: net.Socket.Handle, + header: []const u8, + data: []const []const u8, + splat: usize, +) net.Stream.Writer.Error!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = handle; + _ = header; + _ = data; + _ = splat; + @panic("TODO"); +} + fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void { // OS checks ptr addr before length so zero length vectors must be omitted. if (bytes.len == 0) return; @@ -3899,7 +3950,7 @@ fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; switch (native_os) { - .windows => closeSocketWindows(handle) catch recoverableOsBugDetected(), + .windows => closeSocketWindows(handle), else => posix.close(handle), } } @@ -4559,7 +4610,7 @@ fn lookupDns( message_i += 1; } } - _ = netSend(t, socket.handle, message_buffer[0..message_i], .{}); + _ = netSendPosix(t, socket.handle, message_buffer[0..message_i], .{}); } const timeout: Io.Timeout = .{ .deadline = .{ @@ -4607,7 +4658,7 @@ fn lookupDns( .data_ptr = query.ptr, .data_len = query.len, }; - _ = netSend(t, socket.handle, (&retry_message)[0..1], .{}); + _ = netSendPosix(t, socket.handle, (&retry_message)[0..1], .{}); continue; }, else => continue, @@ -5157,9 +5208,9 @@ fn closeSocketWindows(s: ws2_32.SOCKET) void { if (builtin.mode == .Debug) switch (rc) { 0 => {}, ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) { - else => unreachable, + else => recoverableOsBugDetected(), }, - else => unreachable, + else => recoverableOsBugDetected(), }; } diff --git a/lib/std/Io/net/test.zig b/lib/std/Io/net/test.zig index feef19e0e4..65f857ea46 100644 --- a/lib/std/Io/net/test.zig +++ b/lib/std/Io/net/test.zig @@ -186,15 +186,6 @@ test "listen on a port, send bytes, receive bytes" { const io = testing.io; - if (builtin.os.tag == .windows) { - _ = try std.os.windows.WSAStartup(2, 2); - } - defer { - if (builtin.os.tag == .windows) { - std.os.windows.WSACleanup() catch unreachable; - } - } - // Try only the IPv4 variant as some CI builders have no IPv6 localhost // configured. const localhost: net.IpAddress = .{ .ip4 = .loopback(0) }; @@ -282,15 +273,6 @@ test "listen on a unix socket, send bytes, receive bytes" { const io = testing.io; - if (builtin.os.tag == .windows) { - _ = try std.os.windows.WSAStartup(2, 2); - } - defer { - if (builtin.os.tag == .windows) { - std.os.windows.WSACleanup() catch unreachable; - } - } - const socket_path = try generateFileName("socket.unix"); defer testing.allocator.free(socket_path); diff --git a/lib/std/crypto/Certificate/Bundle.zig b/lib/std/crypto/Certificate/Bundle.zig index e2090e01ac..cc52ce71d3 100644 --- a/lib/std/crypto/Certificate/Bundle.zig +++ b/lib/std/crypto/Certificate/Bundle.zig @@ -144,10 +144,12 @@ fn rescanWithPath(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp, cert_f const RescanWindowsError = Allocator.Error || ParseCertError || std.posix.UnexpectedError || error{FileNotFound}; -fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void { +fn rescanWindows(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp) RescanWindowsError!void { cb.bytes.clearRetainingCapacity(); cb.map.clearRetainingCapacity(); + _ = io; + const w = std.os.windows; const GetLastError = w.GetLastError; const root = [4:0]u16{ 'R', 'O', 'O', 'T' }; @@ -157,7 +159,7 @@ fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void { }; defer _ = w.crypt32.CertCloseStore(store, 0); - const now_sec = std.time.timestamp(); + const now_sec = now.toSeconds(); var ctx = w.crypt32.CertEnumCertificatesInStore(store, null); while (ctx) |context| : (ctx = w.crypt32.CertEnumCertificatesInStore(store, ctx)) { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index ad02698354..26e1b7cfaa 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2735,6 +2735,13 @@ pub fn statusBug(status: NTSTATUS) UnexpectedError { } } +pub fn errorBug(err: Win32Error) UnexpectedError { + switch (builtin.mode) { + .Debug => std.debug.panic("programmer bug caused syscall status: {t}", .{err}), + else => return error.Unexpected, + } +} + pub const Win32Error = @import("windows/win32error.zig").Win32Error; pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS; pub const LANG = @import("windows/lang.zig");