authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-22 18:08:13-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-22 18:08:13-08:00
log1badb2a840c47d49b49fa685db7a1553c0a40ee7
tree2c76cac07cb70044f199d3174deda145f3b4cc9d
parent305fd06756d0a90e330c957d85738f195b94bc10

std.Io.Threaded: dirCreateFileWindows uses NtCreateFile directly


8 files changed, 305 insertions(+), 132 deletions(-)

lib/std/Io/Dir.zig+3-3
......@@ -12,6 +12,8 @@ const Allocator = std.mem.Allocator;
1212
1313handle: Handle,
1414
15pub const Handle = std.posix.fd_t;
16
1517pub const path = std.fs.path;
1618
1719/// The maximum length of a file path that the operating system will accept.
......@@ -396,8 +398,6 @@ pub fn walk(dir: Dir, allocator: Allocator) Allocator.Error!Walker {
396398 return .{ .inner = try walkSelectively(dir, allocator) };
397399}
398400
399pub const Handle = std.posix.fd_t;
400
401401pub const PathNameError = error{
402402 /// Returned when an insufficient buffer is provided that cannot fit the
403403 /// path name.
......@@ -1698,7 +1698,7 @@ pub fn copyFile(
16981698 options: CopyFileOptions,
16991699) CopyFileError!void {
17001700 const file = try source_dir.openFile(io, source_path, .{});
1701 var file_reader: File.Reader = .init(.{ .handle = file.handle }, io, &.{});
1701 var file_reader: File.Reader = .init(file, io, &.{});
17021702 defer file_reader.file.close(io);
17031703
17041704 const permissions = options.permissions orelse blk: {
lib/std/Io/File.zig+26-4
......@@ -11,13 +11,14 @@ const Dir = std.Io.Dir;
1111
1212handle: Handle,
1313
14pub const Handle = std.posix.fd_t;
15
1416pub const Reader = @import("File/Reader.zig");
1517pub const Writer = @import("File/Writer.zig");
1618pub const Atomic = @import("File/Atomic.zig");
1719/// Memory intended to remain consistent with file contents.
1820pub const MemoryMap = @import("File/MemoryMap.zig");
1921
20pub const Handle = std.posix.fd_t;
2122pub const INode = std.posix.ino_t;
2223pub const NLink = std.posix.nlink_t;
2324pub const Uid = std.posix.uid_t;
......@@ -73,15 +74,36 @@ pub const Stat = struct {
7374};
7475
7576pub fn stdout() File {
76 return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdOutput else std.posix.STDOUT_FILENO };
77 return switch (native_os) {
78 .windows => .{
79 .handle = std.os.windows.peb().ProcessParameters.hStdOutput,
80 },
81 else => .{
82 .handle = std.posix.STDOUT_FILENO,
83 },
84 };
7785}
7886
7987pub fn stderr() File {
80 return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdError else std.posix.STDERR_FILENO };
88 return switch (native_os) {
89 .windows => .{
90 .handle = std.os.windows.peb().ProcessParameters.hStdError,
91 },
92 else => .{
93 .handle = std.posix.STDERR_FILENO,
94 },
95 };
8196}
8297
8398pub fn stdin() File {
84 return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdInput else std.posix.STDIN_FILENO };
99 return switch (native_os) {
100 .windows => .{
101 .handle = std.os.windows.peb().ProcessParameters.hStdInput,
102 },
103 else => .{
104 .handle = std.posix.STDIN_FILENO,
105 },
106 };
85107}
86108
87109pub const StatError = error{
lib/std/Io/File/Writer.zig+1-1
......@@ -101,7 +101,7 @@ pub fn moveToReader(w: *Writer) File.Reader {
101101 defer w.* = undefined;
102102 return .{
103103 .io = w.io,
104 .file = .{ .handle = w.file.handle },
104 .file = w.file,
105105 .mode = w.mode,
106106 .pos = w.pos,
107107 .interface = File.Reader.initInterface(w.interface.buffer),
lib/std/Io/Threaded.zig+153-76
......@@ -571,7 +571,7 @@ const Future = struct {
571571 num_completed: *std.atomic.Value(u32),
572572 thread: ?*Thread,
573573 ) void {
574 var need_signal: bool = thread != null and thread.?.cancelAwaitable(.fromFuture(future));
574 var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;
575575 var timeout_ns: u64 = 1 << 10;
576576 while (true) {
577577 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
......@@ -628,7 +628,7 @@ const Thread = struct {
628628
629629 const Handle = Handle: {
630630 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
631 if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE;
631 if (is_windows) break :Handle windows.HANDLE;
632632 break :Handle void;
633633 };
634634
......@@ -1364,7 +1364,7 @@ fn worker(t: *Threaded) void {
13641364 .id = std.Thread.getCurrentId(),
13651365 .handle = handle: {
13661366 if (std.Thread.use_pthreads) break :handle std.c.pthread_self();
1367 if (builtin.target.os.tag == .windows) break :handle undefined; // populated below
1367 if (is_windows) break :handle undefined; // populated below
13681368 },
13691369 .status = .init(.{
13701370 .cancelation = .none,
......@@ -1376,7 +1376,7 @@ fn worker(t: *Threaded) void {
13761376 };
13771377 Thread.current = &thread;
13781378
1379 if (builtin.target.os.tag == .windows) {
1379 if (is_windows) {
13801380 assert(windows.ntdll.NtOpenThread(
13811381 &thread.handle,
13821382 .{
......@@ -1397,7 +1397,7 @@ fn worker(t: *Threaded) void {
13971397 &windows.teb().ClientId,
13981398 ) == .SUCCESS);
13991399 }
1400 defer if (builtin.target.os.tag == .windows) {
1400 defer if (is_windows) {
14011401 windows.CloseHandle(thread.handle);
14021402 };
14031403
......@@ -3430,53 +3430,133 @@ fn dirCreateFileWindows(
34303430 sub_path: []const u8,
34313431 flags: File.CreateFlags,
34323432) File.OpenError!File {
3433 const w = windows;
34343433 const t: *Threaded = @ptrCast(@alignCast(userdata));
34353434 _ = t;
34363435
3437 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
3436 if (std.mem.eql(u8, sub_path, ".")) return error.IsDir;
3437 if (std.mem.eql(u8, sub_path, "..")) return error.IsDir;
3438
3439 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
34383440 const sub_path_w = sub_path_w_array.span();
3441 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
34393442
3440 const handle = handle: {
3441 const syscall: Syscall = try .start();
3442 while (true) {
3443 if (w.OpenFile(sub_path_w, .{
3444 .dir = dir.handle,
3445 .access_mask = .{
3446 .STANDARD = .{ .SYNCHRONIZE = true },
3447 .GENERIC = .{
3448 .WRITE = true,
3449 .READ = flags.read,
3450 },
3451 },
3452 .creation = if (flags.exclusive)
3453 .CREATE
3454 else if (flags.truncate)
3455 .OVERWRITE_IF
3456 else
3457 .OPEN_IF,
3458 })) |handle| {
3459 syscall.finish();
3460 break :handle handle;
3461 } else |err| switch (err) {
3462 error.OperationCanceled => {
3463 try syscall.checkCancel();
3464 continue;
3465 },
3466 else => |e| return syscall.fail(e),
3467 }
3468 }
3443 var nt_name: windows.UNICODE_STRING = .{
3444 .Length = path_len_bytes,
3445 .MaximumLength = path_len_bytes,
3446 .Buffer = @constCast(sub_path_w.ptr),
34693447 };
3470 errdefer w.CloseHandle(handle);
3448 const attr: windows.OBJECT_ATTRIBUTES = .{
3449 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
3450 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
3451 .Attributes = .{
3452 .INHERIT = false,
3453 },
3454 .ObjectName = &nt_name,
3455 .SecurityDescriptor = null,
3456 .SecurityQualityOfService = null,
3457 };
3458 const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
3459 .CREATE
3460 else if (flags.truncate)
3461 .OVERWRITE_IF
3462 else
3463 .OPEN_IF;
3464
3465 const access_mask: windows.ACCESS_MASK = .{
3466 .STANDARD = .{ .SYNCHRONIZE = true },
3467 .GENERIC = .{
3468 .WRITE = true,
3469 .READ = flags.read,
3470 },
3471 };
3472
3473 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
3474
3475 // There are multiple kernel bugs being worked around with retries.
3476 const max_attempts = 13;
3477 var attempt: u5 = 0;
3478
3479 var handle: windows.HANDLE = undefined;
3480 var syscall: Syscall = try .start();
3481 while (true) switch (windows.ntdll.NtCreateFile(
3482 &handle,
3483 access_mask,
3484 &attr,
3485 &io_status_block,
3486 null,
3487 .{ .NORMAL = true },
3488 .VALID_FLAGS, // share access
3489 create_disposition,
3490 .{
3491 .NON_DIRECTORY_FILE = true,
3492 .IO = .SYNCHRONOUS_NONALERT,
3493 },
3494 null,
3495 0,
3496 )) {
3497 .SUCCESS => {
3498 syscall.finish();
3499 break;
3500 },
3501 .CANCELLED => {
3502 try syscall.checkCancel();
3503 continue;
3504 },
3505 .SHARING_VIOLATION => {
3506 // This occurs if the file attempting to be opened is a running
3507 // executable. However, there's a kernel bug: the error may be
3508 // incorrectly returned for an indeterminate amount of time
3509 // after an executable file is closed. Here we work around the
3510 // kernel bug with retry attempts.
3511 syscall.finish();
3512 if (max_attempts - attempt == 0) return error.SharingViolation;
3513 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
3514 attempt += 1;
3515 syscall = try .start();
3516 continue;
3517 },
3518 .DELETE_PENDING => {
3519 // This error means that there *was* a file in this location on
3520 // the file system, but it was deleted. However, the OS is not
3521 // finished with the deletion operation, and so this CreateFile
3522 // call has failed. Here, we simulate the kernel bug being
3523 // fixed by sleeping and retrying until the error goes away.
3524 syscall.finish();
3525 if (max_attempts - attempt == 0) return error.SharingViolation;
3526 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
3527 attempt += 1;
3528 syscall = try .start();
3529 continue;
3530 },
3531 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
3532 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
3533 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
3534 .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
3535 .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
3536 .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
3537 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
3538 .PIPE_BUSY => return syscall.fail(error.PipeBusy),
3539 .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
3540 .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
3541 .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
3542 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
3543 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
3544 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
3545 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
3546 .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
3547 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
3548 else => |err| return syscall.unexpectedNtstatus(err),
3549 };
3550 errdefer windows.CloseHandle(handle);
34713551
3472 var io_status_block: w.IO_STATUS_BLOCK = undefined;
34733552 const exclusive = switch (flags.lock) {
34743553 .none => return .{ .handle = handle },
34753554 .shared => false,
34763555 .exclusive => true,
34773556 };
3478 const syscall: Syscall = try .start();
3479 while (true) switch (w.ntdll.NtLockFile(
3557
3558 syscall = try .start();
3559 while (true) switch (windows.ntdll.NtLockFile(
34803560 handle,
34813561 null,
34823562 null,
......@@ -3968,7 +4048,10 @@ pub fn dirOpenFileWtf16(
39684048 var attr: w.OBJECT_ATTRIBUTES = .{
39694049 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
39704050 .RootDirectory = dir_handle,
3971 .Attributes = .{},
4051 .Attributes = .{
4052 // TODO should we set INHERIT=false?
4053 //.INHERIT = false,
4054 },
39724055 .ObjectName = &nt_name,
39734056 .SecurityDescriptor = null,
39744057 .SecurityQualityOfService = null,
......@@ -7923,15 +8006,14 @@ fn fileClose(userdata: ?*anyopaque, files: []const File) void {
79238006 for (files) |file| posix.close(file.handle);
79248007}
79258008
7926const fileReadStreaming = switch (native_os) {
7927 .windows => fileReadStreamingWindows,
7928 else => fileReadStreamingPosix,
7929};
7930
7931fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
8009fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
79328010 const t: *Threaded = @ptrCast(@alignCast(userdata));
79338011 _ = t;
8012 if (is_windows) return fileReadStreamingWindows(file, data);
8013 return fileReadStreamingPosix(file, data);
8014}
79348015
8016fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usize {
79358017 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
79368018 var i: usize = 0;
79378019 for (data) |buf| {
......@@ -8013,10 +8095,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)
80138095 }
80148096}
80158097
8016fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
8017 const t: *Threaded = @ptrCast(@alignCast(userdata));
8018 _ = t;
8019
8098fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!usize {
80208099 const DWORD = windows.DWORD;
80218100 var index: usize = 0;
80228101 while (index < data.len and data[index].len == 0) index += 1;
......@@ -8059,10 +8138,7 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u
80598138 }
80608139}
80618140
8062fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
8063 const t: *Threaded = @ptrCast(@alignCast(userdata));
8064 _ = t;
8065
8141fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
80668142 if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
80678143
80688144 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
......@@ -8144,15 +8220,14 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8
81448220 }
81458221}
81468222
8147const fileReadPositional = switch (native_os) {
8148 .windows => fileReadPositionalWindows,
8149 else => fileReadPositionalPosix,
8150};
8151
8152fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
8223fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
81538224 const t: *Threaded = @ptrCast(@alignCast(userdata));
81548225 _ = t;
8226 if (is_windows) return fileReadPositionalWindows(file, data, offset);
8227 return fileReadPositionalPosix(file, data, offset);
8228}
81558229
8230fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
81568231 var index: usize = 0;
81578232 while (index < data.len and data[index].len == 0) index += 1;
81588233 if (index == data.len) return 0;
......@@ -8244,7 +8319,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
82448319 }
82458320 }
82468321
8247 if (native_os == .windows) {
8322 if (is_windows) {
82488323 const syscall: Syscall = try .start();
82498324 while (true) {
82508325 if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) {
......@@ -8329,7 +8404,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
83298404 _ = t;
83308405 const fd = file.handle;
83318406
8332 if (native_os == .windows) {
8407 if (is_windows) {
83338408 // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
83348409 // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
83358410 // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
......@@ -11628,7 +11703,7 @@ fn netWriteWindows(
1162811703 splat: usize,
1162911704) net.Stream.Writer.Error!usize {
1163011705 const t: *Threaded = @ptrCast(@alignCast(userdata));
11631 comptime assert(native_os == .windows);
11706 comptime assert(is_windows);
1163211707
1163311708 var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;
1163411709 var len: u32 = 0;
......@@ -11896,7 +11971,7 @@ fn netInterfaceNameResolve(
1189611971 }
1189711972 }
1189811973
11899 if (native_os == .windows) {
11974 if (is_windows) {
1190011975 try Thread.checkCancel();
1190111976 @panic("TODO implement netInterfaceNameResolve for Windows");
1190211977 }
......@@ -11930,7 +12005,7 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
1193012005 @panic("TODO implement netInterfaceName for linux");
1193112006 }
1193212007
11933 if (native_os == .windows) {
12008 if (is_windows) {
1193412009 @panic("TODO implement netInterfaceName for windows");
1193512010 }
1193612011
......@@ -15247,11 +15322,13 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
1524715322
1524815323 const int = try t.environ.zig_progress_handle;
1524915324
15250 return .{ .handle = switch (@typeInfo(Io.File.Handle)) {
15251 .int => int,
15252 .pointer => @ptrFromInt(int),
15253 else => return error.UnsupportedOperation,
15254 } };
15325 return .{
15326 .handle = switch (@typeInfo(Io.File.Handle)) {
15327 .int => int,
15328 .pointer => @ptrFromInt(int),
15329 else => return error.UnsupportedOperation,
15330 },
15331 };
1525515332}
1525615333
1525715334pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
......@@ -15562,13 +15639,13 @@ test {
1556215639 _ = @import("Threaded/test.zig");
1556315640}
1556415641
15565const use_parking_futex = switch (builtin.target.os.tag) {
15642const use_parking_futex = switch (native_os) {
1556615643 .windows => true, // RtlWaitOnAddress is a userland implementation anyway
1556715644 .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
1556815645 .illumos => true, // Illumos has no futex mechanism
1556915646 else => false,
1557015647};
15571const use_parking_sleep = switch (builtin.target.os.tag) {
15648const use_parking_sleep = switch (native_os) {
1557215649 // On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in
1557315650 // kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the
1557415651 // latter for futex, so we may as well use it for sleeping too, to maximise code reuse. I'm
......@@ -15926,7 +16003,7 @@ const parking_sleep = struct {
1592616003/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
1592716004fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void {
1592816005 comptime assert(use_parking_futex or use_parking_sleep);
15929 switch (builtin.target.os.tag) {
16006 switch (native_os) {
1593016007 .windows => {
1593116008 var timeout_buf: windows.LARGE_INTEGER = undefined;
1593216009 const raw_timeout: ?*windows.LARGE_INTEGER = if (opt_deadline) |deadline| timeout: {
......@@ -15980,7 +16057,7 @@ fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) err
1598016057 }
1598116058}
1598216059
15983const UnparkTid = switch (builtin.target.os.tag) {
16060const UnparkTid = switch (native_os) {
1598416061 // `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread handles?
1598516062 .windows => usize,
1598616063 else => std.Thread.Id,
......@@ -15988,7 +16065,7 @@ const UnparkTid = switch (builtin.target.os.tag) {
1598816065/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
1598916066fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
1599016067 comptime assert(use_parking_futex or use_parking_sleep);
15991 switch (builtin.target.os.tag) {
16068 switch (native_os) {
1599216069 .windows => {
1599316070 // TODO: this condition is currently disabled because mingw-w64 does not contain this
1599416071 // symbol. Once it's added, enable this check to use the new bulk API where possible.
lib/std/Progress.zig+3-1
......@@ -977,7 +977,9 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
977977 0..,
978978 ) |main_parent, *main_storage, main_index| {
979979 if (main_parent == .unused) continue;
980 const file: Io.File = .{ .handle = main_storage.getIpcFd() orelse continue };
980 const file: Io.File = .{
981 .handle = main_storage.getIpcFd() orelse continue,
982 };
981983 const opt_saved_metadata = findOld(file.handle, old_ipc_metadata_fds, old_ipc_metadata);
982984 var bytes_read: usize = 0;
983985 while (true) {
lib/std/os/windows.zig+93-41
......@@ -392,6 +392,8 @@ pub const FILE = struct {
392392 Characteristics: ULONG,
393393 };
394394
395 pub const USE_FILE_POINTER_POSITION = -2;
396
395397 // ref: um/WinBase.h
396398
397399 pub const ATTRIBUTE_TAG_INFO = extern struct {
......@@ -466,9 +468,11 @@ pub const FILE = struct {
466468 pub const CREATE_DISPOSITION = enum(ULONG) {
467469 /// If the file already exists, replace it with the given file. If it does not, create the given file.
468470 SUPERSEDE = 0x00000000,
469 /// If the file already exists, open it instead of creating a new file. If it does not, fail the request and do not create a new file.
471 /// If the file already exists, open it instead of creating a new file.
472 /// If it does not, fail the request and do not create a new file.
470473 OPEN = 0x00000001,
471 /// If the file already exists, fail the request and do not create or open the given file. If it does not, create the given file.
474 /// If the file already exists, fail the request and do not create or
475 /// open the given file. If it does not, create the given file.
472476 CREATE = 0x00000002,
473477 /// If the file already exists, open it. If it does not, create the given file.
474478 OPEN_IF = 0x00000003,
......@@ -482,75 +486,122 @@ pub const FILE = struct {
482486
483487 /// Define the create/open option flags
484488 pub const MODE = packed struct(ULONG) {
485 /// The file being created or opened is a directory file. With this flag, the CreateDisposition parameter must be set to `.CREATE`, `.FILE_OPEN`, or `.OPEN_IF`.
486 /// With this flag, other compatible CreateOptions flags include only the following: `SYNCHRONOUS_IO`, `WRITE_THROUGH`, `OPEN_FOR_BACKUP_INTENT`, and `OPEN_BY_FILE_ID`.
489 /// The file being created or opened is a directory file. With this
490 /// flag, the CreateDisposition parameter must be set to `.CREATE`,
491 /// `.FILE_OPEN`, or `.OPEN_IF`. With this flag, other compatible
492 /// CreateOptions flags include only the following: `SYNCHRONOUS_IO`,
493 /// `WRITE_THROUGH`, `OPEN_FOR_BACKUP_INTENT`, and `OPEN_BY_FILE_ID`.
487494 DIRECTORY_FILE: bool = false,
488 /// Applications that write data to the file must actually transfer the data into the file before any requested write operation is considered complete.
489 /// This flag is automatically set if the CreateOptions flag `NO_INTERMEDIATE_BUFFERING` is set.
495 /// Applications that write data to the file must actually transfer the
496 /// data into the file before any requested write operation is
497 /// considered complete. This flag is automatically set if the
498 /// CreateOptions flag `NO_INTERMEDIATE_BUFFERING` is set.
490499 WRITE_THROUGH: bool = false,
491500 /// All accesses to the file are sequential.
492501 SEQUENTIAL_ONLY: bool = false,
493 /// The file cannot be cached or buffered in a driver's internal buffers. This flag is incompatible with the DesiredAccess `FILE_APPEND_DATA` flag.
502 /// The file cannot be cached or buffered in a driver's internal
503 /// buffers. This flag is incompatible with the DesiredAccess
504 /// `FILE_APPEND_DATA` flag.
494505 NO_INTERMEDIATE_BUFFERING: bool = false,
495506 IO: enum(u2) {
496507 /// All operations on the file are performed asynchronously.
497508 ASYNCHRONOUS = 0b00,
498 /// All operations on the file are performed synchronously. Any wait on behalf of the caller is subject to premature termination from alerts.
499 /// This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess `SYNCHRONIZE` flag also must be set.
509 /// All operations on the file are performed synchronously. Any
510 /// wait on behalf of the caller is subject to premature
511 /// termination from alerts. This flag also causes the I/O system
512 /// to maintain the file position context. If this flag is set, the
513 /// DesiredAccess `SYNCHRONIZE` flag also must be set.
500514 SYNCHRONOUS_ALERT = 0b01,
501 /// All operations on the file are performed synchronously. Waits in the system to synchronize I/O queuing and completion are not subject to alerts.
502 /// This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess `SYNCHRONIZE` flag also must be set.
515 /// All operations on the file are performed synchronously. Waits
516 /// in the system to synchronize I/O queuing and completion are not
517 /// subject to alerts. This flag also causes the I/O system to
518 /// maintain the file position context. If this flag is set, the
519 /// DesiredAccess `SYNCHRONIZE` flag also must be set.
503520 SYNCHRONOUS_NONALERT = 0b10,
504521 _,
505522
506523 pub const VALID_FLAGS: @This() = @enumFromInt(0b11);
507524 } = .ASYNCHRONOUS,
508 /// The file being opened must not be a directory file or this call fails. The file object being opened can represent a data file, a logical, virtual, or physical
509 /// device, or a volume.
525 /// The file being opened must not be a directory file or this call
526 /// fails. The file object being opened can represent a data file, a
527 /// logical, virtual, or physical device, or a volume.
510528 NON_DIRECTORY_FILE: bool = false,
511 /// Create a tree connection for this file in order to open it over the network. This flag is not used by device and intermediate drivers.
529 /// Create a tree connection for this file in order to open it over the
530 /// network. This flag is not used by device and intermediate drivers.
512531 CREATE_TREE_CONNECTION: bool = false,
513 /// Complete this operation immediately with an alternate success code of `STATUS_OPLOCK_BREAK_IN_PROGRESS` if the target file is oplocked, rather than blocking
514 /// the caller's thread. If the file is oplocked, another caller already has access to the file. This flag is not used by device and intermediate drivers.
532 /// Complete this operation immediately with an alternate success code
533 /// of `STATUS_OPLOCK_BREAK_IN_PROGRESS` if the target file is
534 /// oplocked, rather than blocking the caller's thread. If the file is
535 /// oplocked, another caller already has access to the file. This flag
536 /// is not used by device and intermediate drivers.
515537 COMPLETE_IF_OPLOCKED: bool = false,
516 /// If the extended attributes on an existing file being opened indicate that the caller must understand EAs to properly interpret the file, fail this request
517 /// because the caller does not understand how to deal with EAs. This flag is irrelevant for device and intermediate drivers.
538 /// If the extended attributes on an existing file being opened
539 /// indicate that the caller must understand EAs to properly interpret
540 /// the file, fail this request because the caller does not understand
541 /// how to deal with EAs. This flag is irrelevant for device and
542 /// intermediate drivers.
518543 NO_EA_KNOWLEDGE: bool = false,
519544 OPEN_REMOTE_INSTANCE: bool = false,
520 /// Accesses to the file can be random, so no sequential read-ahead operations should be performed on the file by FSDs or the system.
545 /// Accesses to the file can be random, so no sequential read-ahead
546 /// operations should be performed on the file by FSDs or the system.
521547 RANDOM_ACCESS: bool = false,
522 /// Delete the file when the last handle to it is passed to `NtClose`. If this flag is set, the `DELETE` flag must be set in the DesiredAccess parameter.
548 /// Delete the file when the last handle to it is passed to `NtClose`.
549 /// If this flag is set, the `DELETE` flag must be set in the
550 /// DesiredAccess parameter.
523551 DELETE_ON_CLOSE: bool = false,
524 /// The file name that is specified by the `ObjectAttributes` parameter includes the 8-byte file reference number for the file. This number is assigned by and
525 /// specific to the particular file system. If the file is a reparse point, the file name will also include the name of a device. Note that the FAT file system
526 /// does not support this flag. This flag is not used by device and intermediate drivers.
552 /// The file name that is specified by the `ObjectAttributes` parameter
553 /// includes the 8-byte file reference number for the file. This number
554 /// is assigned by and specific to the particular file system. If the
555 /// file is a reparse point, the file name will also include the name
556 /// of a device. Note that the FAT file system does not support this
557 /// flag. This flag is not used by device and intermediate drivers.
527558 OPEN_BY_FILE_ID: bool = false,
528 /// The file is being opened for backup intent. Therefore, the system should check for certain access rights and grant the caller the appropriate access to the
529 /// file before checking the DesiredAccess parameter against the file's security descriptor. This flag not used by device and intermediate drivers.
559 /// The file is being opened for backup intent. Therefore, the system
560 /// should check for certain access rights and grant the caller the
561 /// appropriate access to the file before checking the DesiredAccess
562 /// parameter against the file's security descriptor. This flag not
563 /// used by device and intermediate drivers.
530564 OPEN_FOR_BACKUP_INTENT: bool = false,
531 /// Suppress inheritance of `FILE_ATTRIBUTE.COMPRESSED` from the parent directory. This allows creation of a non-compressed file in a directory that is marked
532 /// compressed.
565 /// Suppress inheritance of `FILE_ATTRIBUTE.COMPRESSED` from the parent
566 /// directory. This allows creation of a non-compressed file in a
567 /// directory that is marked compressed.
533568 NO_COMPRESSION: bool = false,
534 /// The file is being opened and an opportunistic lock on the file is being requested as a single atomic operation. The file system checks for oplocks before it
535 /// performs the create operation and will fail the create with a return code of STATUS_CANNOT_BREAK_OPLOCK if the result would be to break an existing oplock.
536 /// For more information, see the Remarks section.
569 /// The file is being opened and an opportunistic lock on the file is
570 /// being requested as a single atomic operation. The file system
571 /// checks for oplocks before it performs the create operation and will
572 /// fail the create with a return code of STATUS_CANNOT_BREAK_OPLOCK if
573 /// the result would be to break an existing oplock. For more
574 /// information, see the Remarks section.
537575 ///
538 /// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This flag is not supported.
576 /// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows
577 /// XP: This flag is not supported.
539578 ///
540 /// This flag is supported on the following file systems: NTFS, FAT, and exFAT.
579 /// This flag is supported on the following file systems: NTFS, FAT,
580 /// and exFAT.
541581 OPEN_REQUIRING_OPLOCK: bool = false,
542582 Reserved17: u3 = 0,
543 /// This flag allows an application to request a filter opportunistic lock to prevent other applications from getting share violations. If there are already open
544 /// handles, the create request will fail with STATUS_OPLOCK_NOT_GRANTED. For more information, see the Remarks section.
583 /// This flag allows an application to request a filter opportunistic
584 /// lock to prevent other applications from getting share violations.
585 /// If there are already open handles, the create request will fail
586 /// with STATUS_OPLOCK_NOT_GRANTED. For more information, see the
587 /// Remarks section.
545588 RESERVE_OPFILTER: bool = false,
546 /// Open a file with a reparse point and bypass normal reparse point processing for the file. For more information, see the Remarks section.
589 /// Open a file with a reparse point and bypass normal reparse point
590 /// processing for the file. For more information, see the Remarks
591 /// section.
547592 OPEN_REPARSE_POINT: bool = false,
548 /// Instructs any filters that perform offline storage or virtualization to not recall the contents of the file as a result of this open.
593 /// Instructs any filters that perform offline storage or
594 /// virtualization to not recall the contents of the file as a result
595 /// of this open.
549596 OPEN_NO_RECALL: bool = false,
550 /// This flag instructs the file system to capture the user associated with the calling thread. Any subsequent calls to `FltQueryVolumeInformation` or
551 /// `ZwQueryVolumeInformationFile` using the returned handle will assume the captured user, rather than the calling user at the time, for purposes of computing
552 /// the free space available to the caller. This applies to the following FsInformationClass values: `FileFsSizeInformation`, `FileFsFullSizeInformation`, and
553 /// `FileFsFullSizeInformationEx`.
597 /// This flag instructs the file system to capture the user associated
598 /// with the calling thread. Any subsequent calls to
599 /// `FltQueryVolumeInformation` or `ZwQueryVolumeInformationFile` using
600 /// the returned handle will assume the captured user, rather than the
601 /// calling user at the time, for purposes of computing the free space
602 /// available to the caller. This applies to the following
603 /// FsInformationClass values: `FileFsSizeInformation`,
604 /// `FileFsFullSizeInformation`, and `FileFsFullSizeInformationEx`.
554605 OPEN_FOR_FREE_SPACE_QUERY: bool = false,
555606 Reserved24: u8 = 0,
556607
......@@ -597,7 +648,8 @@ pub const FILE = struct {
597648 // ref: km/ntifs.h
598649
599650 pub const INFORMATION = extern struct {
600 /// The set of flags that specify the mode in which the file can be accessed. These flags are a subset of `MODE`.
651 /// The set of flags that specify the mode in which the file can be
652 /// accessed. These flags are a subset of `MODE`.
601653 Mode: MODE,
602654 };
603655 };
lib/std/os/windows/ntdll.zig+15-3
......@@ -567,9 +567,8 @@ pub extern "ntdll" fn NtWaitForAlertByThreadId(
567567 Address: ?*const anyopaque,
568568 Timeout: ?*const LARGE_INTEGER,
569569) callconv(.winapi) NTSTATUS;
570pub extern "ntdll" fn NtAlertThreadByThreadId(
571 ThreadId: DWORD,
572) callconv(.winapi) NTSTATUS;
570pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS;
571pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
573572pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
574573 ThreadIds: [*]const ULONG_PTR,
575574 ThreadCount: ULONG,
......@@ -589,3 +588,16 @@ pub extern "ntdll" fn NtCancelSynchronousIoFile(
589588 RequestToCancel: ?*IO_STATUS_BLOCK,
590589 IoStatusBlock: *IO_STATUS_BLOCK,
591590) callconv(.winapi) NTSTATUS;
591
592pub extern "ntdll" fn NtDelayExecution(
593 Alertable: BOOLEAN,
594 DelayInterval: *const LARGE_INTEGER,
595) callconv(.winapi) NTSTATUS;
596
597pub extern "ntdll" fn NtCancelIoFileEx(
598 FileHandle: HANDLE,
599 /// Documentation has this as IO_STATUS_BLOCK but it's actually the APC
600 /// context parameter.
601 IoRequestToCancel: ?*anyopaque,
602 IoStatusBlock: *IO_STATUS_BLOCK,
603) callconv(.winapi) NTSTATUS;
src/link/Lld.zig+11-3
......@@ -278,7 +278,7 @@ pub fn flush(
278278 };
279279 result catch |err| switch (err) {
280280 error.OutOfMemory, error.LinkFailure => |e| return e,
281 else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
281 else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {t}", .{e}),
282282 };
283283}
284284
......@@ -1630,7 +1630,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
16301630 }) catch |err| break :term err;
16311631
16321632 var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
1633 stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
1633 stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
1634 error.StreamTooLong => unreachable, // unlimited
1635 error.OutOfMemory => |e| return e,
1636 error.ReadFailed => return stderr_reader.err.?,
1637 };
16341638 break :term child.wait(io);
16351639 }) catch |first_err| term: {
16361640 const err = switch (first_err) {
......@@ -1682,7 +1686,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
16821686 break :term rsp_child.wait(io) catch |err| break :err err;
16831687 } else {
16841688 var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
1685 stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
1689 stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
1690 error.StreamTooLong => unreachable, // unlimited
1691 error.OutOfMemory => |e| return e,
1692 error.ReadFailed => return stderr_reader.err.?,
1693 };
16861694 break :term rsp_child.wait(io) catch |err| break :err err;
16871695 }
16881696 },