std.Io.Threaded: dirCreateFileWindows uses NtCreateFile directly

This commit is contained in:
Andrew Kelley 2026-01-22 18:08:13 -08:00
parent 305fd06756
commit 1badb2a840
8 changed files with 305 additions and 132 deletions

View file

@ -12,6 +12,8 @@ const Allocator = std.mem.Allocator;
handle: Handle,
pub const Handle = std.posix.fd_t;
pub const path = std.fs.path;
/// 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 {
return .{ .inner = try walkSelectively(dir, allocator) };
}
pub const Handle = std.posix.fd_t;
pub const PathNameError = error{
/// Returned when an insufficient buffer is provided that cannot fit the
/// path name.
@ -1698,7 +1698,7 @@ pub fn copyFile(
options: CopyFileOptions,
) CopyFileError!void {
const file = try source_dir.openFile(io, source_path, .{});
var file_reader: File.Reader = .init(.{ .handle = file.handle }, io, &.{});
var file_reader: File.Reader = .init(file, io, &.{});
defer file_reader.file.close(io);
const permissions = options.permissions orelse blk: {

View file

@ -11,13 +11,14 @@ const Dir = std.Io.Dir;
handle: Handle,
pub const Handle = std.posix.fd_t;
pub const Reader = @import("File/Reader.zig");
pub const Writer = @import("File/Writer.zig");
pub const Atomic = @import("File/Atomic.zig");
/// Memory intended to remain consistent with file contents.
pub const MemoryMap = @import("File/MemoryMap.zig");
pub const Handle = std.posix.fd_t;
pub const INode = std.posix.ino_t;
pub const NLink = std.posix.nlink_t;
pub const Uid = std.posix.uid_t;
@ -73,15 +74,36 @@ pub const Stat = struct {
};
pub fn stdout() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdOutput else std.posix.STDOUT_FILENO };
return switch (native_os) {
.windows => .{
.handle = std.os.windows.peb().ProcessParameters.hStdOutput,
},
else => .{
.handle = std.posix.STDOUT_FILENO,
},
};
}
pub fn stderr() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdError else std.posix.STDERR_FILENO };
return switch (native_os) {
.windows => .{
.handle = std.os.windows.peb().ProcessParameters.hStdError,
},
else => .{
.handle = std.posix.STDERR_FILENO,
},
};
}
pub fn stdin() File {
return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdInput else std.posix.STDIN_FILENO };
return switch (native_os) {
.windows => .{
.handle = std.os.windows.peb().ProcessParameters.hStdInput,
},
else => .{
.handle = std.posix.STDIN_FILENO,
},
};
}
pub const StatError = error{

View file

@ -101,7 +101,7 @@ pub fn moveToReader(w: *Writer) File.Reader {
defer w.* = undefined;
return .{
.io = w.io,
.file = .{ .handle = w.file.handle },
.file = w.file,
.mode = w.mode,
.pos = w.pos,
.interface = File.Reader.initInterface(w.interface.buffer),

View file

@ -571,7 +571,7 @@ const Future = struct {
num_completed: *std.atomic.Value(u32),
thread: ?*Thread,
) void {
var need_signal: bool = thread != null and thread.?.cancelAwaitable(.fromFuture(future));
var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;
var timeout_ns: u64 = 1 << 10;
while (true) {
need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
@ -628,7 +628,7 @@ const Thread = struct {
const Handle = Handle: {
if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE;
if (is_windows) break :Handle windows.HANDLE;
break :Handle void;
};
@ -1364,7 +1364,7 @@ fn worker(t: *Threaded) void {
.id = std.Thread.getCurrentId(),
.handle = handle: {
if (std.Thread.use_pthreads) break :handle std.c.pthread_self();
if (builtin.target.os.tag == .windows) break :handle undefined; // populated below
if (is_windows) break :handle undefined; // populated below
},
.status = .init(.{
.cancelation = .none,
@ -1376,7 +1376,7 @@ fn worker(t: *Threaded) void {
};
Thread.current = &thread;
if (builtin.target.os.tag == .windows) {
if (is_windows) {
assert(windows.ntdll.NtOpenThread(
&thread.handle,
.{
@ -1397,7 +1397,7 @@ fn worker(t: *Threaded) void {
&windows.teb().ClientId,
) == .SUCCESS);
}
defer if (builtin.target.os.tag == .windows) {
defer if (is_windows) {
windows.CloseHandle(thread.handle);
};
@ -3430,53 +3430,133 @@ fn dirCreateFileWindows(
sub_path: []const u8,
flags: File.CreateFlags,
) File.OpenError!File {
const w = windows;
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
if (std.mem.eql(u8, sub_path, ".")) return error.IsDir;
if (std.mem.eql(u8, sub_path, "..")) return error.IsDir;
const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
const sub_path_w = sub_path_w_array.span();
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
const handle = handle: {
const syscall: Syscall = try .start();
while (true) {
if (w.OpenFile(sub_path_w, .{
.dir = dir.handle,
.access_mask = .{
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{
.WRITE = true,
.READ = flags.read,
},
},
.creation = if (flags.exclusive)
.CREATE
else if (flags.truncate)
.OVERWRITE_IF
else
.OPEN_IF,
})) |handle| {
syscall.finish();
break :handle handle;
} else |err| switch (err) {
error.OperationCanceled => {
try syscall.checkCancel();
continue;
},
else => |e| return syscall.fail(e),
}
}
var nt_name: windows.UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
errdefer w.CloseHandle(handle);
const attr: windows.OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
.Attributes = .{
.INHERIT = false,
},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
.CREATE
else if (flags.truncate)
.OVERWRITE_IF
else
.OPEN_IF;
const access_mask: windows.ACCESS_MASK = .{
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{
.WRITE = true,
.READ = flags.read,
},
};
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
// There are multiple kernel bugs being worked around with retries.
const max_attempts = 13;
var attempt: u5 = 0;
var handle: windows.HANDLE = undefined;
var syscall: Syscall = try .start();
while (true) switch (windows.ntdll.NtCreateFile(
&handle,
access_mask,
&attr,
&io_status_block,
null,
.{ .NORMAL = true },
.VALID_FLAGS, // share access
create_disposition,
.{
.NON_DIRECTORY_FILE = true,
.IO = .SYNCHRONOUS_NONALERT,
},
null,
0,
)) {
.SUCCESS => {
syscall.finish();
break;
},
.CANCELLED => {
try syscall.checkCancel();
continue;
},
.SHARING_VIOLATION => {
// This occurs if the file attempting to be opened is a running
// executable. However, there's a kernel bug: the error may be
// incorrectly returned for an indeterminate amount of time
// after an executable file is closed. Here we work around the
// kernel bug with retry attempts.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
continue;
},
.DELETE_PENDING => {
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. Here, we simulate the kernel bug being
// fixed by sleeping and retrying until the error goes away.
syscall.finish();
if (max_attempts - attempt == 0) return error.SharingViolation;
try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
attempt += 1;
syscall = try .start();
continue;
},
.OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
.OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
.OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
.BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
.BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
.NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
.PIPE_BUSY => return syscall.fail(error.PipeBusy),
.PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
.OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
.FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
.NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
.USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
.VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
.INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
.OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
.INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
else => |err| return syscall.unexpectedNtstatus(err),
};
errdefer windows.CloseHandle(handle);
var io_status_block: w.IO_STATUS_BLOCK = undefined;
const exclusive = switch (flags.lock) {
.none => return .{ .handle = handle },
.shared => false,
.exclusive => true,
};
const syscall: Syscall = try .start();
while (true) switch (w.ntdll.NtLockFile(
syscall = try .start();
while (true) switch (windows.ntdll.NtLockFile(
handle,
null,
null,
@ -3968,7 +4048,10 @@ pub fn dirOpenFileWtf16(
var attr: w.OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
.RootDirectory = dir_handle,
.Attributes = .{},
.Attributes = .{
// TODO should we set INHERIT=false?
//.INHERIT = false,
},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
@ -7923,15 +8006,14 @@ fn fileClose(userdata: ?*anyopaque, files: []const File) void {
for (files) |file| posix.close(file.handle);
}
const fileReadStreaming = switch (native_os) {
.windows => fileReadStreamingWindows,
else => fileReadStreamingPosix,
};
fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
if (is_windows) return fileReadStreamingWindows(file, data);
return fileReadStreamingPosix(file, data);
}
fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usize {
var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
var i: usize = 0;
for (data) |buf| {
@ -8013,10 +8095,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8)
}
}
fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!usize {
const DWORD = windows.DWORD;
var index: usize = 0;
while (index < data.len and data[index].len == 0) index += 1;
@ -8059,10 +8138,7 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u
}
}
fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
if (!have_preadv) @compileError("TODO implement fileReadPositionalPosix for cursed operating systems that don't support preadv (it's only Haiku)");
var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
@ -8144,15 +8220,14 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8
}
}
const fileReadPositional = switch (native_os) {
.windows => fileReadPositionalWindows,
else => fileReadPositionalPosix,
};
fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
if (is_windows) return fileReadPositionalWindows(file, data, offset);
return fileReadPositionalPosix(file, data, offset);
}
fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
var index: usize = 0;
while (index < data.len and data[index].len == 0) index += 1;
if (index == data.len) return 0;
@ -8244,7 +8319,7 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
}
}
if (native_os == .windows) {
if (is_windows) {
const syscall: Syscall = try .start();
while (true) {
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
_ = t;
const fd = file.handle;
if (native_os == .windows) {
if (is_windows) {
// "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
// is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
@ -11628,7 +11703,7 @@ fn netWriteWindows(
splat: usize,
) net.Stream.Writer.Error!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
comptime assert(native_os == .windows);
comptime assert(is_windows);
var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;
var len: u32 = 0;
@ -11896,7 +11971,7 @@ fn netInterfaceNameResolve(
}
}
if (native_os == .windows) {
if (is_windows) {
try Thread.checkCancel();
@panic("TODO implement netInterfaceNameResolve for Windows");
}
@ -11930,7 +12005,7 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
@panic("TODO implement netInterfaceName for linux");
}
if (native_os == .windows) {
if (is_windows) {
@panic("TODO implement netInterfaceName for windows");
}
@ -15247,11 +15322,13 @@ fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
const int = try t.environ.zig_progress_handle;
return .{ .handle = switch (@typeInfo(Io.File.Handle)) {
.int => int,
.pointer => @ptrFromInt(int),
else => return error.UnsupportedOperation,
} };
return .{
.handle = switch (@typeInfo(Io.File.Handle)) {
.int => int,
.pointer => @ptrFromInt(int),
else => return error.UnsupportedOperation,
},
};
}
pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
@ -15562,13 +15639,13 @@ test {
_ = @import("Threaded/test.zig");
}
const use_parking_futex = switch (builtin.target.os.tag) {
const use_parking_futex = switch (native_os) {
.windows => true, // RtlWaitOnAddress is a userland implementation anyway
.netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
.illumos => true, // Illumos has no futex mechanism
else => false,
};
const use_parking_sleep = switch (builtin.target.os.tag) {
const use_parking_sleep = switch (native_os) {
// On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in
// kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the
// 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 {
/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
fn park(opt_deadline: ?std.Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{Timeout}!void {
comptime assert(use_parking_futex or use_parking_sleep);
switch (builtin.target.os.tag) {
switch (native_os) {
.windows => {
var timeout_buf: windows.LARGE_INTEGER = undefined;
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
}
}
const UnparkTid = switch (builtin.target.os.tag) {
const UnparkTid = switch (native_os) {
// `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread handles?
.windows => usize,
else => std.Thread.Id,
@ -15988,7 +16065,7 @@ const UnparkTid = switch (builtin.target.os.tag) {
/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
comptime assert(use_parking_futex or use_parking_sleep);
switch (builtin.target.os.tag) {
switch (native_os) {
.windows => {
// TODO: this condition is currently disabled because mingw-w64 does not contain this
// symbol. Once it's added, enable this check to use the new bulk API where possible.

View file

@ -977,7 +977,9 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
0..,
) |main_parent, *main_storage, main_index| {
if (main_parent == .unused) continue;
const file: Io.File = .{ .handle = main_storage.getIpcFd() orelse continue };
const file: Io.File = .{
.handle = main_storage.getIpcFd() orelse continue,
};
const opt_saved_metadata = findOld(file.handle, old_ipc_metadata_fds, old_ipc_metadata);
var bytes_read: usize = 0;
while (true) {

View file

@ -392,6 +392,8 @@ pub const FILE = struct {
Characteristics: ULONG,
};
pub const USE_FILE_POINTER_POSITION = -2;
// ref: um/WinBase.h
pub const ATTRIBUTE_TAG_INFO = extern struct {
@ -466,9 +468,11 @@ pub const FILE = struct {
pub const CREATE_DISPOSITION = enum(ULONG) {
/// If the file already exists, replace it with the given file. If it does not, create the given file.
SUPERSEDE = 0x00000000,
/// 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.
/// 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.
OPEN = 0x00000001,
/// 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.
/// 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.
CREATE = 0x00000002,
/// If the file already exists, open it. If it does not, create the given file.
OPEN_IF = 0x00000003,
@ -482,75 +486,122 @@ pub const FILE = struct {
/// Define the create/open option flags
pub const MODE = packed struct(ULONG) {
/// 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`.
/// With this flag, other compatible CreateOptions flags include only the following: `SYNCHRONOUS_IO`, `WRITE_THROUGH`, `OPEN_FOR_BACKUP_INTENT`, and `OPEN_BY_FILE_ID`.
/// 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`. With this flag, other compatible
/// CreateOptions flags include only the following: `SYNCHRONOUS_IO`,
/// `WRITE_THROUGH`, `OPEN_FOR_BACKUP_INTENT`, and `OPEN_BY_FILE_ID`.
DIRECTORY_FILE: bool = false,
/// Applications that write data to the file must actually transfer the data into the file before any requested write operation is considered complete.
/// This flag is automatically set if the CreateOptions flag `NO_INTERMEDIATE_BUFFERING` is set.
/// Applications that write data to the file must actually transfer the
/// data into the file before any requested write operation is
/// considered complete. This flag is automatically set if the
/// CreateOptions flag `NO_INTERMEDIATE_BUFFERING` is set.
WRITE_THROUGH: bool = false,
/// All accesses to the file are sequential.
SEQUENTIAL_ONLY: bool = false,
/// The file cannot be cached or buffered in a driver's internal buffers. This flag is incompatible with the DesiredAccess `FILE_APPEND_DATA` flag.
/// The file cannot be cached or buffered in a driver's internal
/// buffers. This flag is incompatible with the DesiredAccess
/// `FILE_APPEND_DATA` flag.
NO_INTERMEDIATE_BUFFERING: bool = false,
IO: enum(u2) {
/// All operations on the file are performed asynchronously.
ASYNCHRONOUS = 0b00,
/// All operations on the file are performed synchronously. Any wait on behalf of the caller is subject to premature termination from alerts.
/// 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.
/// All operations on the file are performed synchronously. Any
/// wait on behalf of the caller is subject to premature
/// termination from alerts. 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.
SYNCHRONOUS_ALERT = 0b01,
/// All operations on the file are performed synchronously. Waits in the system to synchronize I/O queuing and completion are not subject to alerts.
/// 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.
/// All operations on the file are performed synchronously. Waits
/// in the system to synchronize I/O queuing and completion are not
/// subject to alerts. 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.
SYNCHRONOUS_NONALERT = 0b10,
_,
pub const VALID_FLAGS: @This() = @enumFromInt(0b11);
} = .ASYNCHRONOUS,
/// 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
/// device, or a volume.
/// 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 device, or a volume.
NON_DIRECTORY_FILE: bool = false,
/// 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.
/// 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.
CREATE_TREE_CONNECTION: bool = false,
/// Complete this operation immediately with an alternate success code of `STATUS_OPLOCK_BREAK_IN_PROGRESS` if the target file is oplocked, rather than blocking
/// 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.
/// Complete this operation immediately with an alternate success code
/// of `STATUS_OPLOCK_BREAK_IN_PROGRESS` if the target file is
/// oplocked, rather than blocking 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.
COMPLETE_IF_OPLOCKED: bool = false,
/// 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
/// because the caller does not understand how to deal with EAs. This flag is irrelevant for device and intermediate drivers.
/// 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 because the caller does not understand
/// how to deal with EAs. This flag is irrelevant for device and
/// intermediate drivers.
NO_EA_KNOWLEDGE: bool = false,
OPEN_REMOTE_INSTANCE: bool = false,
/// Accesses to the file can be random, so no sequential read-ahead operations should be performed on the file by FSDs or the system.
/// Accesses to the file can be random, so no sequential read-ahead
/// operations should be performed on the file by FSDs or the system.
RANDOM_ACCESS: bool = false,
/// 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.
/// 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.
DELETE_ON_CLOSE: bool = false,
/// 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
/// 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
/// does not support this flag. This flag is not used by device and intermediate drivers.
/// 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 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 does not support this
/// flag. This flag is not used by device and intermediate drivers.
OPEN_BY_FILE_ID: bool = false,
/// 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
/// file before checking the DesiredAccess parameter against the file's security descriptor. This flag not used by device and intermediate drivers.
/// 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 file before checking the DesiredAccess
/// parameter against the file's security descriptor. This flag not
/// used by device and intermediate drivers.
OPEN_FOR_BACKUP_INTENT: bool = false,
/// Suppress inheritance of `FILE_ATTRIBUTE.COMPRESSED` from the parent directory. This allows creation of a non-compressed file in a directory that is marked
/// compressed.
/// Suppress inheritance of `FILE_ATTRIBUTE.COMPRESSED` from the parent
/// directory. This allows creation of a non-compressed file in a
/// directory that is marked compressed.
NO_COMPRESSION: bool = false,
/// 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
/// 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.
/// For more information, see the Remarks section.
/// 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 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. For more
/// information, see the Remarks section.
///
/// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This flag is not supported.
/// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows
/// XP: This flag is not supported.
///
/// This flag is supported on the following file systems: NTFS, FAT, and exFAT.
/// This flag is supported on the following file systems: NTFS, FAT,
/// and exFAT.
OPEN_REQUIRING_OPLOCK: bool = false,
Reserved17: u3 = 0,
/// This flag allows an application to request a filter opportunistic lock to prevent other applications from getting share violations. If there are already open
/// handles, the create request will fail with STATUS_OPLOCK_NOT_GRANTED. For more information, see the Remarks section.
/// This flag allows an application to request a filter opportunistic
/// lock to prevent other applications from getting share violations.
/// If there are already open handles, the create request will fail
/// with STATUS_OPLOCK_NOT_GRANTED. For more information, see the
/// Remarks section.
RESERVE_OPFILTER: bool = false,
/// Open a file with a reparse point and bypass normal reparse point processing for the file. For more information, see the Remarks section.
/// Open a file with a reparse point and bypass normal reparse point
/// processing for the file. For more information, see the Remarks
/// section.
OPEN_REPARSE_POINT: bool = false,
/// Instructs any filters that perform offline storage or virtualization to not recall the contents of the file as a result of this open.
/// Instructs any filters that perform offline storage or
/// virtualization to not recall the contents of the file as a result
/// of this open.
OPEN_NO_RECALL: bool = false,
/// This flag instructs the file system to capture the user associated with the calling thread. Any subsequent calls to `FltQueryVolumeInformation` or
/// `ZwQueryVolumeInformationFile` using the returned handle will assume the captured user, rather than the calling user at the time, for purposes of computing
/// the free space available to the caller. This applies to the following FsInformationClass values: `FileFsSizeInformation`, `FileFsFullSizeInformation`, and
/// `FileFsFullSizeInformationEx`.
/// This flag instructs the file system to capture the user associated
/// with the calling thread. Any subsequent calls to
/// `FltQueryVolumeInformation` or `ZwQueryVolumeInformationFile` using
/// the returned handle will assume the captured user, rather than the
/// calling user at the time, for purposes of computing the free space
/// available to the caller. This applies to the following
/// FsInformationClass values: `FileFsSizeInformation`,
/// `FileFsFullSizeInformation`, and `FileFsFullSizeInformationEx`.
OPEN_FOR_FREE_SPACE_QUERY: bool = false,
Reserved24: u8 = 0,
@ -597,7 +648,8 @@ pub const FILE = struct {
// ref: km/ntifs.h
pub const INFORMATION = extern struct {
/// The set of flags that specify the mode in which the file can be accessed. These flags are a subset of `MODE`.
/// The set of flags that specify the mode in which the file can be
/// accessed. These flags are a subset of `MODE`.
Mode: MODE,
};
};

View file

@ -567,9 +567,8 @@ pub extern "ntdll" fn NtWaitForAlertByThreadId(
Address: ?*const anyopaque,
Timeout: ?*const LARGE_INTEGER,
) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtAlertThreadByThreadId(
ThreadId: DWORD,
) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtAlertThreadByThreadId(ThreadId: DWORD) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
ThreadIds: [*]const ULONG_PTR,
ThreadCount: ULONG,
@ -589,3 +588,16 @@ pub extern "ntdll" fn NtCancelSynchronousIoFile(
RequestToCancel: ?*IO_STATUS_BLOCK,
IoStatusBlock: *IO_STATUS_BLOCK,
) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtDelayExecution(
Alertable: BOOLEAN,
DelayInterval: *const LARGE_INTEGER,
) callconv(.winapi) NTSTATUS;
pub extern "ntdll" fn NtCancelIoFileEx(
FileHandle: HANDLE,
/// Documentation has this as IO_STATUS_BLOCK but it's actually the APC
/// context parameter.
IoRequestToCancel: ?*anyopaque,
IoStatusBlock: *IO_STATUS_BLOCK,
) callconv(.winapi) NTSTATUS;

View file

@ -278,7 +278,7 @@ pub fn flush(
};
result catch |err| switch (err) {
error.OutOfMemory, error.LinkFailure => |e| return e,
else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {t}", .{e}),
};
}
@ -1630,7 +1630,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
}) catch |err| break :term err;
var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
error.StreamTooLong => unreachable, // unlimited
error.OutOfMemory => |e| return e,
error.ReadFailed => return stderr_reader.err.?,
};
break :term child.wait(io);
}) catch |first_err| term: {
const err = switch (first_err) {
@ -1682,7 +1686,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
break :term rsp_child.wait(io) catch |err| break :err err;
} else {
var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited);
stderr = stderr_reader.interface.allocRemaining(gpa, .unlimited) catch |err| switch (err) {
error.StreamTooLong => unreachable, // unlimited
error.OutOfMemory => |e| return e,
error.ReadFailed => return stderr_reader.err.?,
};
break :term rsp_child.wait(io) catch |err| break :err err;
}
},