mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:44:43 +01:00
std.Io.Threaded: dirCreateFileWindows uses NtCreateFile directly
This commit is contained in:
parent
305fd06756
commit
1badb2a840
8 changed files with 305 additions and 132 deletions
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue