mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
std.Threaded: replace more kernel32 functions with ntdll
This commit is contained in:
+17
-18
@@ -35,7 +35,7 @@ Set-Location -Path 'build-debug'
|
||||
|
||||
# CMake gives a syntax error when file paths with backward slashes are used.
|
||||
# Here, we use forward slashes only to work around this.
|
||||
& cmake .. `
|
||||
cmake .. `
|
||||
-GNinja `
|
||||
-DCMAKE_INSTALL_PREFIX="stage3-debug" `
|
||||
-DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
|
||||
@@ -54,7 +54,7 @@ ninja install
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Main test suite..."
|
||||
& "stage3-debug\bin\zig.exe" build test docs `
|
||||
stage3-debug\bin\zig build test docs `
|
||||
--maxrss $ZSF_MAX_RSS `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
--search-prefix "$PREFIX_PATH" `
|
||||
@@ -66,26 +66,25 @@ Write-Output "Main test suite..."
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
|
||||
& "stage3-debug\bin\zig.exe" test `
|
||||
..\test\behavior.zig `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-femit-bin="test-x86_64-windows-msvc.c" `
|
||||
--test-no-exec `
|
||||
-target x86_64-windows-msvc `
|
||||
-lc
|
||||
CheckLastExitCode
|
||||
|
||||
& "stage3-debug\bin\zig.exe" build-obj `
|
||||
stage3-debug\bin\zig build-obj `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-OReleaseSmall `
|
||||
--name compiler_rt `
|
||||
-femit-bin="compiler_rt-x86_64-windows-msvc.c" `
|
||||
--dep build_options `
|
||||
-target x86_64-windows-msvc `
|
||||
-Mroot="..\lib\compiler_rt.zig" `
|
||||
-Mbuild_options="config.zig"
|
||||
-lc `
|
||||
..\lib\compiler_rt.zig
|
||||
CheckLastExitCode
|
||||
|
||||
stage3-debug\bin\zig test `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-femit-bin="behavior-x86_64-windows-msvc.c" `
|
||||
--test-no-exec `
|
||||
-target x86_64-windows-msvc `
|
||||
-lc `
|
||||
..\test\behavior.zig
|
||||
CheckLastExitCode
|
||||
|
||||
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
@@ -97,8 +96,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Build and run behavior tests with msvc..."
|
||||
& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
|
||||
cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
|
||||
CheckLastExitCode
|
||||
|
||||
& .\test-x86_64-windows-msvc.exe
|
||||
.\behavior-x86_64-windows-msvc
|
||||
CheckLastExitCode
|
||||
|
||||
@@ -35,7 +35,7 @@ Set-Location -Path 'build-release'
|
||||
|
||||
# CMake gives a syntax error when file paths with backward slashes are used.
|
||||
# Here, we use forward slashes only to work around this.
|
||||
& cmake .. `
|
||||
cmake .. `
|
||||
-GNinja `
|
||||
-DCMAKE_INSTALL_PREFIX="stage3-release" `
|
||||
-DCMAKE_PREFIX_PATH="$($PREFIX_PATH -Replace "\\", "/")" `
|
||||
@@ -54,7 +54,7 @@ ninja install
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Main test suite..."
|
||||
& "stage3-release\bin\zig.exe" build test docs `
|
||||
stage3-release\bin\zig.exe build test docs `
|
||||
--maxrss $ZSF_MAX_RSS `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
--search-prefix "$PREFIX_PATH" `
|
||||
@@ -67,7 +67,7 @@ CheckLastExitCode
|
||||
|
||||
# Ensure that stage3 and stage4 are byte-for-byte identical.
|
||||
Write-Output "Build and compare stage4..."
|
||||
& "stage3-release\bin\zig.exe" build `
|
||||
stage3-release\bin\zig.exe build `
|
||||
--prefix stage4-release `
|
||||
-Denable-llvm `
|
||||
-Dno-lib `
|
||||
@@ -85,26 +85,25 @@ Compare-Object (Get-Content stage3-release\bin\zig.exe) (Get-Content stage4-rele
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
|
||||
& "stage3-release\bin\zig.exe" test `
|
||||
..\test\behavior.zig `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-femit-bin="test-x86_64-windows-msvc.c" `
|
||||
--test-no-exec `
|
||||
-target x86_64-windows-msvc `
|
||||
-lc
|
||||
CheckLastExitCode
|
||||
|
||||
& "stage3-release\bin\zig.exe" build-obj `
|
||||
stage3-release\bin\zig.exe build-obj `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-OReleaseSmall `
|
||||
--name compiler_rt `
|
||||
-femit-bin="compiler_rt-x86_64-windows-msvc.c" `
|
||||
--dep build_options `
|
||||
-target x86_64-windows-msvc `
|
||||
-Mroot="..\lib\compiler_rt.zig" `
|
||||
-Mbuild_options="config.zig"
|
||||
-lc `
|
||||
..\lib\compiler_rt.zig
|
||||
CheckLastExitCode
|
||||
|
||||
stage3-release\bin\zig.exe test `
|
||||
--zig-lib-dir "$ZIG_LIB_DIR" `
|
||||
-ofmt=c `
|
||||
-femit-bin="behavior-x86_64-windows-msvc.c" `
|
||||
--test-no-exec `
|
||||
-target x86_64-windows-msvc `
|
||||
-lc `
|
||||
..\test\behavior.zig
|
||||
CheckLastExitCode
|
||||
|
||||
Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
@@ -116,8 +115,8 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files (x86)\Microsoft Visual Studio\
|
||||
CheckLastExitCode
|
||||
|
||||
Write-Output "Build and run behavior tests with msvc..."
|
||||
& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
|
||||
cl /I..\lib /W3 /Z7 behavior-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /link /nologo /debug /subsystem:console kernel32.lib ntdll.lib libcmt.lib ws2_32.lib
|
||||
CheckLastExitCode
|
||||
|
||||
& .\test-x86_64-windows-msvc.exe
|
||||
.\behavior-x86_64-windows-msvc
|
||||
CheckLastExitCode
|
||||
|
||||
@@ -621,7 +621,7 @@ pub fn main(init: process.Init.Minimal) !void {
|
||||
}) catch &caption_buf;
|
||||
var debouncing_node = main_progress_node.start(caption, 0);
|
||||
var in_debounce = false;
|
||||
while (true) switch (try w.wait(gpa, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
|
||||
while (true) switch (try w.wait(gpa, io, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
|
||||
.timeout => {
|
||||
assert(in_debounce);
|
||||
debouncing_node.end();
|
||||
|
||||
+1
-1
@@ -632,7 +632,7 @@ export fn fuzzer_main(limit_kind: abi.LimitKind, amount: u64) void {
|
||||
|
||||
export fn fuzzer_unslide_address(addr: usize) usize {
|
||||
const si = std.debug.getSelfDebugInfo() catch @compileError("unsupported");
|
||||
const slide = si.getModuleSlide(std.debug.getDebugInfoAllocator(), io, addr) catch |err| {
|
||||
const slide = si.getModuleSlide(io, addr) catch |err| {
|
||||
std.debug.panic("failed to find virtual address slide: {t}", .{err});
|
||||
};
|
||||
return addr - slide;
|
||||
|
||||
+145
-133
@@ -180,7 +180,7 @@ const Os = switch (builtin.os.tag) {
|
||||
const gop = try w.dir_table.getOrPut(gpa, path);
|
||||
if (!gop.found_existing) {
|
||||
var mount_id: MountId = undefined;
|
||||
const dir_handle = Os.getDirHandle(gpa, path, &mount_id) catch |err| switch (err) {
|
||||
const dir_handle = getDirHandle(gpa, path, &mount_id) catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
std.debug.assert(w.dir_table.swapRemove(path));
|
||||
continue;
|
||||
@@ -291,12 +291,13 @@ const Os = switch (builtin.os.tag) {
|
||||
w.dir_count = w.dir_table.count();
|
||||
}
|
||||
|
||||
fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
|
||||
fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
|
||||
_ = io;
|
||||
const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms());
|
||||
if (events_len == 0)
|
||||
return .timeout;
|
||||
for (w.os.poll_fds.values()) |poll_fd| {
|
||||
if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try Os.markDirtySteps(w, gpa, poll_fd.fd))
|
||||
if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, gpa, poll_fd.fd))
|
||||
return .dirty;
|
||||
}
|
||||
return .clean;
|
||||
@@ -306,12 +307,8 @@ const Os = switch (builtin.os.tag) {
|
||||
const windows = std.os.windows;
|
||||
|
||||
/// Keyed differently but indexes correspond 1:1 with `dir_table`.
|
||||
handle_table: HandleTable,
|
||||
dir_list: std.AutoArrayHashMapUnmanaged(usize, *Directory),
|
||||
io_cp: ?windows.HANDLE,
|
||||
counter: usize = 0,
|
||||
|
||||
const HandleTable = std.AutoArrayHashMapUnmanaged(FileId, ReactionSet);
|
||||
handle_table: std.ArrayHashMapUnmanaged(*Directory, void, Directory.TableAdapter, false),
|
||||
ready_dirs: std.DoublyLinkedList,
|
||||
|
||||
const FileId = struct {
|
||||
volumeSerialNumber: windows.ULONG,
|
||||
@@ -319,55 +316,61 @@ const Os = switch (builtin.os.tag) {
|
||||
};
|
||||
|
||||
const Directory = struct {
|
||||
handle: windows.HANDLE,
|
||||
reaction_set: ReactionSet,
|
||||
id: FileId,
|
||||
overlapped: windows.OVERLAPPED,
|
||||
file: Io.File,
|
||||
state: enum { idle, listening, ready },
|
||||
iosb: windows.IO_STATUS_BLOCK,
|
||||
// 64 KB is the packet size limit when monitoring over a network.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw#remarks
|
||||
buffer: [64 * 1024]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined,
|
||||
buffer: [64 * 1024]u8 align(@alignOf(windows.FILE.NOTIFY.INFORMATION)),
|
||||
ready_node: std.DoublyLinkedList.Node,
|
||||
|
||||
/// Start listening for events, buffer field will be overwritten eventually.
|
||||
fn startListening(self: *@This()) !void {
|
||||
const r = windows.kernel32.ReadDirectoryChangesW(
|
||||
self.handle,
|
||||
@ptrCast(&self.buffer),
|
||||
self.buffer.len,
|
||||
0,
|
||||
fn startListening(dir: *Directory, w: *Watch) !void {
|
||||
assert(dir.file.flags.nonblocking);
|
||||
assert(dir.state == .idle);
|
||||
switch (windows.ntdll.NtNotifyChangeDirectoryFileEx(
|
||||
dir.file.handle,
|
||||
null,
|
||||
¬ifyApc,
|
||||
w,
|
||||
&dir.iosb,
|
||||
&dir.buffer,
|
||||
dir.buffer.len,
|
||||
.{
|
||||
.creation = true,
|
||||
.dir_name = true,
|
||||
.file_name = true,
|
||||
.last_write = true,
|
||||
.size = true,
|
||||
.FILE_NAME = true,
|
||||
.DIR_NAME = true,
|
||||
.SIZE = true,
|
||||
.LAST_WRITE = true,
|
||||
.CREATION = true,
|
||||
},
|
||||
null,
|
||||
&self.overlapped,
|
||||
null,
|
||||
);
|
||||
if (r == windows.FALSE) {
|
||||
switch (windows.GetLastError()) {
|
||||
.INVALID_FUNCTION => return error.ReadDirectoryChangesUnsupported,
|
||||
else => |err| return windows.unexpectedError(err),
|
||||
}
|
||||
windows.FALSE,
|
||||
.Notify,
|
||||
)) {
|
||||
.SUCCESS, .PENDING => dir.state = .listening,
|
||||
.ILLEGAL_FUNCTION => return error.ReadDirectoryChangesUnsupported,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(gpa: Allocator, path: Cache.Path) !*@This() {
|
||||
fn notifyApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void {
|
||||
const w: *Watch = @ptrCast(@alignCast(apc_context));
|
||||
const dir: *Directory = @fieldParentPtr("iosb", iosb);
|
||||
assert(iosb.u.Status != .PENDING);
|
||||
assert(dir.state == .listening);
|
||||
w.os.ready_dirs.append(&dir.ready_node);
|
||||
dir.state = .ready;
|
||||
}
|
||||
|
||||
fn init(gpa: Allocator, path: Cache.Path) !*Directory {
|
||||
// The following code is a drawn out NtCreateFile call. (mostly adapted from Io.Dir.makeOpenDirAccessMaskW)
|
||||
// It's necessary in order to get the specific flags that are required when calling ReadDirectoryChangesW.
|
||||
var dir_handle: windows.HANDLE = undefined;
|
||||
const root_fd = path.root_dir.handle.handle;
|
||||
const sub_path = path.subPathOrDot();
|
||||
const sub_path_w = try std.Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call
|
||||
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
|
||||
|
||||
var nt_name = windows.UNICODE_STRING{
|
||||
.Length = @intCast(path_len_bytes),
|
||||
.MaximumLength = @intCast(path_len_bytes),
|
||||
.Buffer = @constCast(sub_path_w.span().ptr),
|
||||
};
|
||||
const sub_path_w = try Io.Threaded.sliceToPrefixedFileW(root_fd, sub_path); // TODO eliminate this call
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
|
||||
switch (windows.ntdll.NtCreateFile(
|
||||
&dir_handle,
|
||||
.{
|
||||
@@ -379,7 +382,7 @@ const Os = switch (builtin.os.tag) {
|
||||
},
|
||||
&.{
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd,
|
||||
.ObjectName = &nt_name,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
},
|
||||
&iosb,
|
||||
null,
|
||||
@@ -410,20 +413,49 @@ const Os = switch (builtin.os.tag) {
|
||||
|
||||
const dir_id = try getFileId(dir_handle);
|
||||
|
||||
const dir_ptr = try gpa.create(@This());
|
||||
dir_ptr.* = .{
|
||||
.handle = dir_handle,
|
||||
const dir = try gpa.create(Directory);
|
||||
dir.* = .{
|
||||
.reaction_set = .empty,
|
||||
.id = dir_id,
|
||||
.overlapped = std.mem.zeroes(windows.OVERLAPPED),
|
||||
.file = .{ .handle = dir_handle, .flags = .{ .nonblocking = true } },
|
||||
.state = .idle,
|
||||
.iosb = undefined,
|
||||
.buffer = undefined,
|
||||
.ready_node = undefined,
|
||||
};
|
||||
return dir_ptr;
|
||||
return dir;
|
||||
}
|
||||
|
||||
fn deinit(self: *@This(), gpa: Allocator) void {
|
||||
_ = windows.kernel32.CancelIo(self.handle);
|
||||
windows.CloseHandle(self.handle);
|
||||
gpa.destroy(self);
|
||||
fn deinit(dir: *Directory, gpa: Allocator, w: *Watch) void {
|
||||
state: switch (dir.state) {
|
||||
.idle => {},
|
||||
.listening => {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(dir.file.handle, &dir.iosb, &cancel_iosb);
|
||||
while (switch (dir.state) {
|
||||
.idle => unreachable,
|
||||
.listening => true,
|
||||
.ready => false,
|
||||
}) Io.Threaded.waitForApcOrAlert();
|
||||
continue :state .ready;
|
||||
},
|
||||
.ready => w.os.ready_dirs.remove(&dir.ready_node),
|
||||
}
|
||||
windows.CloseHandle(dir.file.handle);
|
||||
gpa.destroy(dir);
|
||||
}
|
||||
|
||||
/// Useful to make `*Directory` a key in `std.ArrayHashMap`.
|
||||
const TableAdapter = struct {
|
||||
pub fn hash(_: TableAdapter, lhs_dir: *Directory) u32 {
|
||||
return @truncate(Hash.hash(lhs_dir.id.volumeSerialNumber, @ptrCast(&lhs_dir.id.indexNumber)));
|
||||
}
|
||||
pub fn eql(_: TableAdapter, lhs_dir: *Directory, rhs_dir: *Directory, rhs_index: usize) bool {
|
||||
_ = rhs_index;
|
||||
return lhs_dir.id.volumeSerialNumber == rhs_dir.id.volumeSerialNumber and
|
||||
lhs_dir.id.indexNumber == rhs_dir.id.indexNumber;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
fn init(cwd_path: []const u8) !Watch {
|
||||
@@ -433,9 +465,8 @@ const Os = switch (builtin.os.tag) {
|
||||
.dir_count = 0,
|
||||
.os = switch (builtin.os.tag) {
|
||||
.windows => .{
|
||||
.handle_table = .{},
|
||||
.dir_list = .{},
|
||||
.io_cp = null,
|
||||
.handle_table = .empty,
|
||||
.ready_dirs = .{},
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
@@ -479,29 +510,22 @@ const Os = switch (builtin.os.tag) {
|
||||
|
||||
fn markDirtySteps(w: *Watch, gpa: Allocator, dir: *Directory) !bool {
|
||||
var any_dirty = false;
|
||||
const bytes_returned = try windows.GetOverlappedResult(dir.handle, &dir.overlapped, false);
|
||||
const bytes_returned = dir.iosb.Information;
|
||||
if (bytes_returned == 0) {
|
||||
std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
|
||||
markAllFilesDirty(w, gpa);
|
||||
try dir.startListening();
|
||||
try dir.startListening(w);
|
||||
return true;
|
||||
}
|
||||
var file_name_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var notify: *align(1) windows.FILE_NOTIFY_INFORMATION = undefined;
|
||||
var offset: usize = 0;
|
||||
while (true) {
|
||||
notify = @ptrCast(&dir.buffer[offset]);
|
||||
const file_name_field: [*]u16 = @ptrFromInt(@intFromPtr(notify) + @sizeOf(windows.FILE_NOTIFY_INFORMATION));
|
||||
const file_name_len = std.unicode.wtf16LeToWtf8(&file_name_buf, file_name_field[0 .. notify.FileNameLength / 2]);
|
||||
const file_name = file_name_buf[0..file_name_len];
|
||||
if (w.os.handle_table.getIndex(dir.id)) |reaction_set_i| {
|
||||
const reaction_set = w.os.handle_table.values()[reaction_set_i];
|
||||
if (reaction_set.getPtr(".")) |glob_set|
|
||||
any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
|
||||
if (reaction_set.getPtr(file_name)) |step_set| {
|
||||
any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
|
||||
}
|
||||
}
|
||||
const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset]));
|
||||
const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())];
|
||||
if (dir.reaction_set.getPtr(".")) |glob_set|
|
||||
any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
|
||||
if (dir.reaction_set.getPtr(file_name)) |step_set|
|
||||
any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
|
||||
if (notify.NextEntryOffset == 0)
|
||||
break;
|
||||
|
||||
@@ -509,7 +533,7 @@ const Os = switch (builtin.os.tag) {
|
||||
}
|
||||
|
||||
// We call this now since at this point we have finished reading dir.buffer.
|
||||
try dir.startListening();
|
||||
try dir.startListening(w);
|
||||
return any_dirty;
|
||||
}
|
||||
|
||||
@@ -517,41 +541,32 @@ const Os = switch (builtin.os.tag) {
|
||||
// Add missing marks and note persisted ones.
|
||||
for (steps) |step| {
|
||||
for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| {
|
||||
const reaction_set = rs: {
|
||||
const dir = dir: {
|
||||
const gop = try w.dir_table.getOrPut(gpa, path);
|
||||
if (!gop.found_existing) {
|
||||
const dir = try Os.Directory.init(gpa, path);
|
||||
errdefer dir.deinit(gpa);
|
||||
const dir: *Directory = try .init(gpa, path);
|
||||
errdefer dir.deinit(gpa, w);
|
||||
// `dir.id` may already be present in the table in
|
||||
// the case that we have multiple Cache.Path instances
|
||||
// that compare inequal but ultimately point to the same
|
||||
// directory on the file system.
|
||||
// In such case, we must revert adding this directory, but keep
|
||||
// the additions to the step set.
|
||||
const dh_gop = try w.os.handle_table.getOrPut(gpa, dir.id);
|
||||
const dh_gop = try w.os.handle_table.getOrPut(gpa, dir);
|
||||
if (dh_gop.found_existing) {
|
||||
dir.deinit(gpa);
|
||||
dir.deinit(gpa, w);
|
||||
_ = w.dir_table.pop();
|
||||
break :dir w.os.handle_table.keys()[dh_gop.index];
|
||||
} else {
|
||||
assert(dh_gop.index == gop.index);
|
||||
dh_gop.value_ptr.* = .{};
|
||||
try dir.startListening();
|
||||
const key = w.os.counter;
|
||||
w.os.counter +%= 1;
|
||||
try w.os.dir_list.put(gpa, key, dir);
|
||||
w.os.io_cp = try windows.CreateIoCompletionPort(
|
||||
dir.handle,
|
||||
w.os.io_cp,
|
||||
key,
|
||||
0,
|
||||
);
|
||||
try dir.startListening(w);
|
||||
break :dir dir;
|
||||
}
|
||||
break :rs &w.os.handle_table.values()[dh_gop.index];
|
||||
}
|
||||
break :rs &w.os.handle_table.values()[gop.index];
|
||||
break :dir w.os.handle_table.keys()[gop.index];
|
||||
};
|
||||
for (files.items) |basename| {
|
||||
const gop = try reaction_set.getOrPut(gpa, basename);
|
||||
const gop = try dir.reaction_set.getOrPut(gpa, basename);
|
||||
if (!gop.found_existing) gop.value_ptr.* = .{};
|
||||
try gop.value_ptr.put(gpa, step, w.generation);
|
||||
}
|
||||
@@ -562,11 +577,11 @@ const Os = switch (builtin.os.tag) {
|
||||
// Remove marks for files that are no longer inputs.
|
||||
var i: usize = 0;
|
||||
while (i < w.os.handle_table.entries.len) {
|
||||
const dir = w.os.handle_table.keys()[i];
|
||||
{
|
||||
const reaction_set = &w.os.handle_table.values()[i];
|
||||
var step_set_i: usize = 0;
|
||||
while (step_set_i < reaction_set.entries.len) {
|
||||
const step_set = &reaction_set.values()[step_set_i];
|
||||
while (step_set_i < dir.reaction_set.entries.len) {
|
||||
const step_set = &dir.reaction_set.values()[step_set_i];
|
||||
var dirent_i: usize = 0;
|
||||
while (dirent_i < step_set.entries.len) {
|
||||
const generations = step_set.values();
|
||||
@@ -580,53 +595,45 @@ const Os = switch (builtin.os.tag) {
|
||||
step_set_i += 1;
|
||||
continue;
|
||||
}
|
||||
reaction_set.swapRemoveAt(step_set_i);
|
||||
dir.reaction_set.swapRemoveAt(step_set_i);
|
||||
}
|
||||
if (reaction_set.entries.len > 0) {
|
||||
if (dir.reaction_set.entries.len > 0) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
w.os.dir_list.values()[i].deinit(gpa);
|
||||
w.os.dir_list.swapRemoveAt(i);
|
||||
w.dir_table.swapRemoveAt(i);
|
||||
w.os.handle_table.swapRemoveAt(i);
|
||||
dir.deinit(gpa, w);
|
||||
}
|
||||
w.generation +%= 1;
|
||||
}
|
||||
w.dir_count = w.dir_table.count();
|
||||
}
|
||||
|
||||
fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
|
||||
var bytes_transferred: std.os.windows.DWORD = undefined;
|
||||
var key: usize = undefined;
|
||||
var overlapped_ptr: ?*std.os.windows.OVERLAPPED = undefined;
|
||||
return while (true) switch (std.os.windows.GetQueuedCompletionStatus(
|
||||
w.os.io_cp.?,
|
||||
&bytes_transferred,
|
||||
&key,
|
||||
&overlapped_ptr,
|
||||
@bitCast(timeout.to_i32_ms()),
|
||||
)) {
|
||||
.Normal => {
|
||||
if (bytes_transferred == 0)
|
||||
break error.Unexpected;
|
||||
|
||||
// This 'orelse' detects a race condition that happens when we receive a
|
||||
// completion notification for a directory that no longer exists in our list.
|
||||
const dir = w.os.dir_list.get(key) orelse break .clean;
|
||||
|
||||
break if (try Os.markDirtySteps(w, gpa, dir))
|
||||
.dirty
|
||||
else
|
||||
.clean;
|
||||
},
|
||||
.Timeout => break .timeout,
|
||||
// This status is issued because CancelIo was called, skip and try again.
|
||||
.Canceled => continue,
|
||||
else => break error.Unexpected,
|
||||
};
|
||||
fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
|
||||
for (0..2) |attempt| {
|
||||
while (w.os.ready_dirs.popFirst()) |ready_node| {
|
||||
const dir: *Directory = @fieldParentPtr("ready_node", ready_node);
|
||||
assert(dir.state == .ready);
|
||||
dir.state = .idle;
|
||||
switch (dir.iosb.u.Status) {
|
||||
.SUCCESS => return if (try markDirtySteps(w, gpa, dir)) .dirty else .clean,
|
||||
.PENDING => unreachable,
|
||||
.CANCELLED => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
try dir.startListening(w);
|
||||
}
|
||||
try io.checkCancel();
|
||||
if (attempt == 1) return .timeout;
|
||||
const delay_interval: windows.LARGE_INTEGER = switch (timeout) {
|
||||
.none => std.math.minInt(windows.LARGE_INTEGER),
|
||||
.ms => |ms| -@as(windows.LARGE_INTEGER, ms) * (std.time.ns_per_ms / 100),
|
||||
};
|
||||
_ = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval);
|
||||
} else unreachable;
|
||||
}
|
||||
},
|
||||
.dragonfly, .freebsd, .netbsd, .openbsd, .ios, .tvos, .visionos, .watchos => struct {
|
||||
@@ -796,7 +803,8 @@ const Os = switch (builtin.os.tag) {
|
||||
w.dir_count = w.dir_table.count();
|
||||
}
|
||||
|
||||
fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
|
||||
fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
|
||||
_ = io;
|
||||
var timespec_buffer: posix.timespec = undefined;
|
||||
var event_buffer: [100]posix.Kevent = undefined;
|
||||
var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(×pec_buffer));
|
||||
@@ -852,7 +860,8 @@ const Os = switch (builtin.os.tag) {
|
||||
try w.os.fse.setPaths(gpa, steps);
|
||||
w.dir_count = w.os.fse.watch_roots.len;
|
||||
}
|
||||
fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
|
||||
fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
|
||||
_ = io;
|
||||
return w.os.fse.wait(gpa, switch (timeout) {
|
||||
.none => null,
|
||||
.ms => |ms| @as(u64, ms) * std.time.ns_per_ms,
|
||||
@@ -890,10 +899,13 @@ pub const Match = struct {
|
||||
};
|
||||
|
||||
fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {
|
||||
for (w.os.handle_table.values()) |value| {
|
||||
for (switch (builtin.os.tag) {
|
||||
.windows => w.os.handle_table.keys(),
|
||||
else => w.os.handle_table.values(),
|
||||
}) |item| {
|
||||
const reaction_set = switch (builtin.os.tag) {
|
||||
.linux => value.reaction_set,
|
||||
else => value,
|
||||
.linux, .windows => item.reaction_set,
|
||||
else => item,
|
||||
};
|
||||
for (reaction_set.values()) |step_set| {
|
||||
for (step_set.keys()) |step| {
|
||||
@@ -951,6 +963,6 @@ pub const WaitResult = enum {
|
||||
clean,
|
||||
};
|
||||
|
||||
pub fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
|
||||
return Os.wait(w, gpa, timeout);
|
||||
pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
|
||||
return Os.wait(w, gpa, io, timeout);
|
||||
}
|
||||
|
||||
+482
-510
@@ -83,7 +83,7 @@ csprng: Csprng = .{},
|
||||
system_basic_information: SystemBasicInformation = .{},
|
||||
|
||||
const SystemBasicInformation = if (!is_windows) struct {} else struct {
|
||||
buffer: windows.SYSTEM_BASIC_INFORMATION = undefined,
|
||||
buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,
|
||||
initialized: std.atomic.Value(bool) = .{ .raw = false },
|
||||
};
|
||||
|
||||
@@ -1392,7 +1392,7 @@ const AlertableSyscall = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn waitForApcOrAlert() void {
|
||||
pub fn waitForApcOrAlert() void {
|
||||
const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
|
||||
_ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout);
|
||||
}
|
||||
@@ -2556,9 +2556,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
|
||||
else => |e| e,
|
||||
},
|
||||
},
|
||||
.device_io_control => |*o| return .{
|
||||
.device_io_control = try deviceIoControl(t, o),
|
||||
},
|
||||
.device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2970,7 +2968,7 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
|
||||
break :o;
|
||||
}
|
||||
const buffer = o.data[data_index];
|
||||
const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
|
||||
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
|
||||
|
||||
if (o.file.flags.nonblocking) {
|
||||
context.file = o.file.handle;
|
||||
@@ -3259,22 +3257,11 @@ fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, pe
|
||||
_ = t;
|
||||
_ = permissions; // TODO use this value
|
||||
|
||||
const sub_path_w_array = try 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;
|
||||
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
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,
|
||||
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.Attributes = .{ .INHERIT = false },
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w.span())),
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
@@ -3438,21 +3425,14 @@ fn dirCreateDirPathOpenWindows(
|
||||
};
|
||||
|
||||
components: while (true) {
|
||||
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, component.path);
|
||||
const sub_path_w = sub_path_w_array.span();
|
||||
const is_last = it.peekNext() == null;
|
||||
const create_disposition: w.FILE.CREATE_DISPOSITION = if (is_last) .OPEN_IF else .CREATE;
|
||||
|
||||
var result: Dir = .{ .handle = undefined };
|
||||
|
||||
const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
|
||||
var nt_name: w.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
const sub_path_w = try sliceToPrefixedFileW(dir.handle, component.path);
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
};
|
||||
var io_status_block: w.IO_STATUS_BLOCK = undefined;
|
||||
|
||||
const is_last = it.peekNext() == null;
|
||||
var result: Dir = .{ .handle = undefined };
|
||||
var iosb: w.IO_STATUS_BLOCK = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (w.ntdll.NtCreateFile(
|
||||
&result.handle,
|
||||
@@ -3468,15 +3448,12 @@ fn dirCreateDirPathOpenWindows(
|
||||
.SYNCHRONIZE = true,
|
||||
},
|
||||
},
|
||||
&.{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
},
|
||||
&io_status_block,
|
||||
&attr,
|
||||
&iosb,
|
||||
null,
|
||||
.{ .NORMAL = true },
|
||||
.VALID_FLAGS,
|
||||
create_disposition,
|
||||
if (is_last) .OPEN_IF else .CREATE,
|
||||
.{
|
||||
.DIRECTORY_FILE = true,
|
||||
.IO = .SYNCHRONOUS_NONALERT,
|
||||
@@ -3944,15 +3921,15 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
|
||||
};
|
||||
}
|
||||
|
||||
fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION {
|
||||
fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {
|
||||
if (!t.system_basic_information.initialized.load(.acquire)) {
|
||||
mutexLock(&t.mutex);
|
||||
defer mutexUnlock(&t.mutex);
|
||||
|
||||
switch (windows.ntdll.NtQuerySystemInformation(
|
||||
.SystemBasicInformation,
|
||||
.Basic,
|
||||
&t.system_basic_information.buffer,
|
||||
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
|
||||
@sizeOf(windows.SYSTEM.BASIC_INFORMATION),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
@@ -4139,22 +4116,11 @@ fn dirAccessWindows(
|
||||
|
||||
_ = options; // TODO
|
||||
|
||||
const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const sub_path_w = sub_path_w_array.span();
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return;
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return;
|
||||
|
||||
const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse
|
||||
return error.NameTooLong;
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
const attr: windows.OBJECT_ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
if (std.mem.eql(u8, sub_path, ".") or std.mem.eql(u8, sub_path, "..")) return;
|
||||
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
};
|
||||
var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
@@ -4360,18 +4326,10 @@ fn dirCreateFileWindows(
|
||||
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 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;
|
||||
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
const attr: windows.OBJECT_ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
};
|
||||
const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
|
||||
.CREATE
|
||||
@@ -4966,20 +4924,14 @@ fn dirOpenFileWindows(
|
||||
|
||||
pub fn dirOpenFileWtf16(
|
||||
dir_handle: ?windows.HANDLE,
|
||||
sub_path_w: [:0]const u16,
|
||||
sub_path_w: []const u16,
|
||||
flags: File.OpenFlags,
|
||||
) File.OpenError!File {
|
||||
const allow_directory = flags.allow_directory and !flags.isWrite();
|
||||
if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
|
||||
if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
|
||||
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
|
||||
const w = windows;
|
||||
|
||||
var nt_name: w.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
var io_status_block: w.IO_STATUS_BLOCK = undefined;
|
||||
var attempt: u5 = 0;
|
||||
var syscall: Syscall = try .start();
|
||||
@@ -4996,7 +4948,7 @@ pub fn dirOpenFileWtf16(
|
||||
},
|
||||
&.{
|
||||
.RootDirectory = dir_handle,
|
||||
.ObjectName = &nt_name,
|
||||
.ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
|
||||
},
|
||||
&io_status_block,
|
||||
null,
|
||||
@@ -5329,20 +5281,19 @@ fn dirOpenDirHaiku(
|
||||
|
||||
pub fn dirOpenDirWindows(
|
||||
dir: Dir,
|
||||
sub_path_w: [:0]const u16,
|
||||
sub_path_w: []const u16,
|
||||
options: Dir.OpenOptions,
|
||||
) Dir.OpenError!Dir {
|
||||
const w = windows;
|
||||
|
||||
const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
|
||||
var nt_name: w.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
var io_status_block: w.IO_STATUS_BLOCK = undefined;
|
||||
var result: Dir = .{ .handle = undefined };
|
||||
|
||||
const attr: w.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
|
||||
};
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (w.ntdll.NtCreateFile(
|
||||
&result.handle,
|
||||
@@ -5359,10 +5310,7 @@ pub fn dirOpenDirWindows(
|
||||
.SYNCHRONIZE = true,
|
||||
},
|
||||
},
|
||||
&.{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
},
|
||||
&attr,
|
||||
&io_status_block,
|
||||
null,
|
||||
.{ .NORMAL = true },
|
||||
@@ -6194,13 +6142,15 @@ pub fn GetFinalPathNameByHandle(
|
||||
input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
|
||||
@memcpy(input_buf[@sizeOf(windows.MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
|
||||
|
||||
{
|
||||
const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf });
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
}
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = false } },
|
||||
.code = windows.IOCTL.MOUNTMGR.QUERY_POINTS,
|
||||
.in = &input_buf,
|
||||
.out = &output_buf,
|
||||
})).u.Status) {
|
||||
.SUCCESS => {},
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
const mount_points_struct: *const windows.MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
|
||||
|
||||
@@ -6251,11 +6201,15 @@ pub fn GetFinalPathNameByHandle(
|
||||
vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
|
||||
@memcpy(@as([*]windows.WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
|
||||
|
||||
const rc = windows.DeviceIoControl(mgmt_handle, windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf });
|
||||
switch (rc) {
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = true } },
|
||||
.code = windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH,
|
||||
.in = &vol_input_buf,
|
||||
.out = &vol_output_buf,
|
||||
})).u.Status) {
|
||||
.SUCCESS => {},
|
||||
.UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
const volume_paths_struct: *const windows.MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
|
||||
const volume_path = std.mem.sliceTo(@as(
|
||||
@@ -6352,20 +6306,17 @@ pub const QueryObjectNameError = error{
|
||||
};
|
||||
|
||||
pub fn QueryObjectName(handle: windows.HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
|
||||
const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
|
||||
const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT.NAME_INFORMATION)) orelse return error.NameTooLong;
|
||||
|
||||
const info: *windows.OBJECT_NAME_INFORMATION = @ptrCast(out_buffer_aligned);
|
||||
const info: *windows.OBJECT.NAME_INFORMATION = @ptrCast(out_buffer_aligned);
|
||||
// buffer size is specified in bytes
|
||||
const out_buffer_len = std.math.cast(windows.ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(windows.ULONG);
|
||||
// last argument would return the length required for full_buffer, not exposed here
|
||||
return switch (windows.ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) {
|
||||
.SUCCESS => blk: {
|
||||
// info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
|
||||
// if the object was "unnamed", not sure if this can happen for file handles
|
||||
if (info.Name.MaximumLength == 0) break :blk error.Unexpected;
|
||||
// resulting string length is specified in bytes
|
||||
const path_length_unterminated = @divExact(info.Name.Length, 2);
|
||||
break :blk info.Name.Buffer.?[0..path_length_unterminated];
|
||||
return switch (windows.ntdll.NtQueryObject(handle, .Name, info, out_buffer_len, null)) {
|
||||
.SUCCESS => {
|
||||
// info.Name from ObQueryNameString is documented to be empty if the object
|
||||
// was "unnamed", not sure if this can happen for file handles
|
||||
return if (info.Name.isEmpty()) error.Unexpected else info.Name.slice();
|
||||
},
|
||||
.ACCESS_DENIED => error.AccessDenied,
|
||||
.INVALID_HANDLE => error.InvalidHandle,
|
||||
@@ -6620,8 +6571,12 @@ pub const WindowsPathSpace = struct {
|
||||
data: [windows.PATH_MAX_WIDE:0]u16,
|
||||
len: usize,
|
||||
|
||||
pub fn span(self: *const WindowsPathSpace) [:0]const u16 {
|
||||
return self.data[0..self.len :0];
|
||||
pub fn span(wps: *const WindowsPathSpace) [:0]const u16 {
|
||||
return wps.data[0..wps.len :0];
|
||||
}
|
||||
|
||||
pub fn string(wps: *const WindowsPathSpace) windows.UNICODE_STRING {
|
||||
return .init(wps.span());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7076,25 +7031,19 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
|
||||
_ = t;
|
||||
const w = windows;
|
||||
|
||||
const sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const sub_path_w = sub_path_w_buf.span();
|
||||
|
||||
const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
|
||||
var nt_name: w.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
// The Windows API makes this mutable, but it will not mutate here.
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
if (std.mem.eql(u8, sub_path, "..")) {
|
||||
// Can't remove the parent directory with an open handle.
|
||||
return error.FileBusy;
|
||||
}
|
||||
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
if (std.mem.eql(u8, sub_path, ".")) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
sub_path_w.len = 0;
|
||||
}
|
||||
const attr: w.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
};
|
||||
|
||||
var io_status_block: w.IO_STATUS_BLOCK = undefined;
|
||||
var tmp_handle: w.HANDLE = undefined;
|
||||
@@ -7106,10 +7055,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov
|
||||
.RIGHTS = .{ .DELETE = true },
|
||||
.SYNCHRONIZE = true,
|
||||
} },
|
||||
&.{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
},
|
||||
&attr,
|
||||
&io_status_block,
|
||||
null,
|
||||
.{},
|
||||
@@ -7741,7 +7687,7 @@ fn dirSymLinkWindows(
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
|
||||
var is_target_absolute = false;
|
||||
const final_target_path = target_path: {
|
||||
if (windows.hasCommonNtPrefix(u16, target_path_w.span())) {
|
||||
if (w.hasCommonNtPrefix(u16, target_path_w.span())) {
|
||||
// Already an NT path, no need to do anything to it
|
||||
break :target_path target_path_w.span();
|
||||
} else {
|
||||
@@ -7785,13 +7731,16 @@ fn dirSymLinkWindows(
|
||||
@memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
|
||||
const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
|
||||
@memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
|
||||
const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
|
||||
switch (rc) {
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = .{ .handle = symlink_handle, .flags = .{ .nonblocking = false } },
|
||||
.code = .SET_REPARSE_POINT,
|
||||
.in = buffer[0..buf_len],
|
||||
})).u.Status) {
|
||||
.SUCCESS => {},
|
||||
.PRIVILEGE_NOT_HELD => return error.PermissionDenied,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.INVALID_DEVICE_REQUEST => return error.FileSystem,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
else => |status| return w.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7905,17 +7854,10 @@ fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []
|
||||
fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
|
||||
// This gets used once for `sub_path` and then reused again temporarily
|
||||
// before converting back to `buffer`.
|
||||
var sub_path_w_buf = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const sub_path_w = sub_path_w_buf.span();
|
||||
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
const attr: windows.OBJECT_ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
|
||||
.ObjectName = &nt_name,
|
||||
var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
|
||||
.ObjectName = @constCast(&sub_path_w.string()),
|
||||
};
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
var result_handle: windows.HANDLE = undefined;
|
||||
@@ -8038,14 +7980,14 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink
|
||||
const len = buf.SubstituteNameLength >> 1;
|
||||
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
||||
const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
|
||||
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data);
|
||||
break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data);
|
||||
},
|
||||
@as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
|
||||
const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
|
||||
const offset = buf.SubstituteNameOffset >> 1;
|
||||
const len = buf.SubstituteNameLength >> 1;
|
||||
const path_buf = @as([*]const u16, &buf.PathBuffer);
|
||||
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data);
|
||||
break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data);
|
||||
},
|
||||
else => return error.UnsupportedReparsePointType,
|
||||
};
|
||||
@@ -8541,13 +8483,14 @@ fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
|
||||
|
||||
fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
return t.isTty(file);
|
||||
_ = t;
|
||||
return isTty(file);
|
||||
}
|
||||
|
||||
fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
fn isTty(file: File) Io.Cancelable!bool {
|
||||
if (is_windows) {
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
@@ -8626,8 +8569,9 @@ fn isTty(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
|
||||
fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
if (!is_windows) return if (!try t.supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
|
||||
if (!is_windows) return if (!try supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
|
||||
|
||||
// For Windows Terminal, VT Sequences processing is enabled by default.
|
||||
const console: File = .{
|
||||
@@ -8635,7 +8579,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
|
||||
.flags = .{ .nonblocking = false },
|
||||
};
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = console,
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
@@ -8661,7 +8605,7 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
|
||||
var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
|
||||
get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
);
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = console,
|
||||
.code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
|
||||
.in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
|
||||
@@ -8673,13 +8617,14 @@ fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiE
|
||||
|
||||
fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
return t.supportsAnsiEscapeCodes(file);
|
||||
_ = t;
|
||||
return supportsAnsiEscapeCodes(file);
|
||||
}
|
||||
|
||||
fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
|
||||
if (is_windows) {
|
||||
var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
|
||||
switch ((try t.deviceIoControl(&.{
|
||||
switch ((try deviceIoControl(&.{
|
||||
.file = .{
|
||||
.handle = windows.peb().ProcessParameters.ConsoleHandle,
|
||||
.flags = .{ .nonblocking = false },
|
||||
@@ -8701,7 +8646,7 @@ fn supportsAnsiEscapeCodes(t: *Threaded, file: File) Io.Cancelable!bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (try t.isTty(file)) return true;
|
||||
if (try isTty(file)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -8775,7 +8720,7 @@ fn isCygwinPty(file: File) Io.Cancelable!bool {
|
||||
},
|
||||
};
|
||||
|
||||
const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
|
||||
const name_info: *const windows.FILE.NAME_INFORMATION = @ptrCast(&name_info_bytes);
|
||||
const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
|
||||
const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
|
||||
// The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
|
||||
@@ -9002,49 +8947,38 @@ fn fileSetTimestamps(
|
||||
_ = t;
|
||||
|
||||
if (is_windows) {
|
||||
var access_time_buffer: windows.FILETIME = undefined;
|
||||
var modify_time_buffer: windows.FILETIME = undefined;
|
||||
var system_time_buffer: windows.LARGE_INTEGER = undefined;
|
||||
|
||||
if (options.access_timestamp == .now or options.modify_timestamp == .now) {
|
||||
system_time_buffer = windows.ntdll.RtlGetSystemTimePrecise();
|
||||
}
|
||||
|
||||
const access_ptr = switch (options.access_timestamp) {
|
||||
.unchanged => null,
|
||||
.now => @panic("TODO do SystemTimeToFileTime logic here"),
|
||||
.new => |ts| p: {
|
||||
access_time_buffer = windows.nanoSecondsToFileTime(ts);
|
||||
break :p &access_time_buffer;
|
||||
const now_sys = if (options.access_timestamp == .now or options.modify_timestamp == .now)
|
||||
windows.ntdll.RtlGetSystemTimePrecise()
|
||||
else
|
||||
undefined;
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
var info: windows.FILE.BASIC_INFORMATION = .{
|
||||
.CreationTime = 0,
|
||||
.LastAccessTime = switch (options.access_timestamp) {
|
||||
.unchanged => 0,
|
||||
.now => now_sys,
|
||||
.new => |ts| windows.toSysTime(ts),
|
||||
},
|
||||
};
|
||||
|
||||
const modify_ptr = switch (options.modify_timestamp) {
|
||||
.unchanged => null,
|
||||
.now => @panic("TODO do SystemTimeToFileTime logic here"),
|
||||
.new => |ts| p: {
|
||||
modify_time_buffer = windows.nanoSecondsToFileTime(ts);
|
||||
break :p &modify_time_buffer;
|
||||
.LastWriteTime = switch (options.modify_timestamp) {
|
||||
.unchanged => 0,
|
||||
.now => now_sys,
|
||||
.new => |ts| windows.toSysTime(ts),
|
||||
},
|
||||
.ChangeTime = 0,
|
||||
.FileAttributes = .{},
|
||||
};
|
||||
var syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtSetInformationFile(
|
||||
file.handle,
|
||||
&iosb,
|
||||
&info,
|
||||
@sizeOf(windows.FILE.BASIC_INFORMATION),
|
||||
.Basic,
|
||||
)) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
else => |status| return syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
|
||||
// https://github.com/ziglang/zig/issues/1840
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (windows.kernel32.SetFileTime(file.handle, null, access_ptr, modify_ptr)) {
|
||||
0 => switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return windows.unexpectedError(err);
|
||||
},
|
||||
},
|
||||
else => return syscall.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (native_os == .wasi and !builtin.link_libc) {
|
||||
@@ -9636,15 +9570,43 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingErro
|
||||
}
|
||||
|
||||
fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize {
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
var index: usize = 0;
|
||||
while (data.len - index != 0 and data[index].len == 0) index += 1;
|
||||
if (data.len - index == 0) return 0;
|
||||
const buffer = data[index];
|
||||
const short_buffer_len = @min(std.math.maxInt(u32), buffer.len);
|
||||
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
|
||||
if (!file.flags.nonblocking) {
|
||||
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
|
||||
if (file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtReadFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
null, // byte offset
|
||||
null, // key
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
// Once we get here we must not return from the function until the
|
||||
// operation completes, thereby releasing reference to the iosb.
|
||||
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
|
||||
error.Canceled => |e| {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
|
||||
while (!done) waitForApcOrAlert();
|
||||
return e;
|
||||
},
|
||||
};
|
||||
waitForApcOrAlert();
|
||||
alertable_syscall.finish();
|
||||
},
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtReadFile(
|
||||
file.handle,
|
||||
@@ -9665,41 +9627,10 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingEr
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
iosb.u.Status = status;
|
||||
return ntReadFileResult(&iosb);
|
||||
break;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
var done: bool = false;
|
||||
|
||||
switch (windows.ntdll.NtReadFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
null, // byte offset
|
||||
null, // key
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
// Once we get here we must not return from the function until the
|
||||
// operation completes, thereby releasing reference to io_status_block.
|
||||
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
|
||||
error.Canceled => |e| {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
|
||||
while (!done) waitForApcOrAlert();
|
||||
return e;
|
||||
},
|
||||
};
|
||||
waitForApcOrAlert();
|
||||
alertable_syscall.finish();
|
||||
},
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
return ntReadFileResult(&iosb);
|
||||
}
|
||||
|
||||
@@ -9714,8 +9645,9 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
|
||||
.CANCELLED => unreachable,
|
||||
.SUCCESS => return io_status_block.Information,
|
||||
.END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream,
|
||||
.INVALID_HANDLE => return error.NotOpenForReading,
|
||||
.INVALID_DEVICE_REQUEST => return error.IsDir,
|
||||
.LOCK_NOT_GRANTED => return error.LockViolation,
|
||||
.FILE_LOCK_CONFLICT => return error.LockViolation,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
@@ -9731,7 +9663,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
|
||||
.QUOTA_EXCEEDED => return error.SystemResources,
|
||||
.PIPE_BROKEN => return error.BrokenPipe,
|
||||
.INVALID_HANDLE => return error.NotOpenForWriting,
|
||||
.LOCK_NOT_GRANTED => return error.LockViolation,
|
||||
.FILE_LOCK_CONFLICT => return error.LockViolation,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.WORKING_SET_QUOTA => return error.SystemResources,
|
||||
.DISK_FULL => return error.NoSpaceLeft,
|
||||
@@ -9740,8 +9672,6 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
|
||||
}
|
||||
|
||||
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;
|
||||
var i: usize = 0;
|
||||
for (data) |buf| {
|
||||
@@ -9787,9 +9717,44 @@ fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.Rea
|
||||
}
|
||||
}
|
||||
|
||||
if (have_preadv) {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return @bitCast(rc);
|
||||
},
|
||||
.INTR, .TIMEDOUT => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.NXIO => return syscall.fail(error.Unseekable),
|
||||
.SPIPE => return syscall.fail(error.Unseekable),
|
||||
.OVERFLOW => return syscall.fail(error.Unseekable),
|
||||
.NOBUFS => return syscall.fail(error.SystemResources),
|
||||
.NOMEM => return syscall.fail(error.SystemResources),
|
||||
.AGAIN => return syscall.fail(error.WouldBlock),
|
||||
.IO => return syscall.fail(error.InputOutput),
|
||||
.ISDIR => return syscall.fail(error.IsDir),
|
||||
.NOTCONN => |err| return syscall.errnoBug(err), // not a socket
|
||||
.CONNRESET => |err| return syscall.errnoBug(err), // not a socket
|
||||
.INVAL => |err| return syscall.errnoBug(err),
|
||||
.FAULT => |err| return syscall.errnoBug(err),
|
||||
.BADF => {
|
||||
syscall.finish();
|
||||
if (native_os == .wasi) return error.IsDir; // File operation on directory.
|
||||
return error.NotOpenForReading;
|
||||
},
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
|
||||
const rc = posix.pread(file.handle, dest[0].ptr, @intCast(dest[0].len), @bitCast(offset));
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
@@ -9838,116 +9803,119 @@ fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.R
|
||||
}
|
||||
|
||||
fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize {
|
||||
const DWORD = windows.DWORD;
|
||||
const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
|
||||
var overlapped: windows.OVERLAPPED = .{
|
||||
.Internal = 0,
|
||||
.InternalHigh = 0,
|
||||
.DUMMYUNIONNAME = .{
|
||||
.DUMMYSTRUCTNAME = .{
|
||||
.Offset = @truncate(offset),
|
||||
.OffsetHigh = @truncate(offset >> 32),
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
|
||||
const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
|
||||
if (file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtReadFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
&signed_offset,
|
||||
null, // key
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
// Once we get here we must not return from the function until the
|
||||
// operation completes, thereby releasing reference to the iosb.
|
||||
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
|
||||
error.Canceled => |e| {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
|
||||
while (!done) waitForApcOrAlert();
|
||||
return e;
|
||||
},
|
||||
};
|
||||
waitForApcOrAlert();
|
||||
alertable_syscall.finish();
|
||||
},
|
||||
},
|
||||
.hEvent = null,
|
||||
};
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
var n: DWORD = undefined;
|
||||
if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) {
|
||||
syscall.finish();
|
||||
return n;
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
switch (windows.GetLastError()) {
|
||||
.IO_PENDING => |err| {
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtReadFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
&signed_offset,
|
||||
null, // key
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
return windows.errorBug(err);
|
||||
iosb.u.Status = status;
|
||||
break;
|
||||
},
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.BROKEN_PIPE, .HANDLE_EOF => {
|
||||
syscall.finish();
|
||||
return 0;
|
||||
},
|
||||
.NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected,
|
||||
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected,
|
||||
// TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing
|
||||
// a handle to a directory.
|
||||
.INVALID_FUNCTION => return syscall.fail(error.IsDir),
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return windows.unexpectedError(err);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
return ntReadFileResult(&iosb) catch |err| switch (err) {
|
||||
error.EndOfStream => 0,
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
|
||||
fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
const fd = file.handle;
|
||||
|
||||
if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
|
||||
var result: u64 = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (posix.errno(posix.system.llseek(fd, @bitCast(offset), &result, posix.SEEK.CUR))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |e| {
|
||||
syscall.finish();
|
||||
switch (e) {
|
||||
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
|
||||
.INVAL => return error.Unseekable,
|
||||
.OVERFLOW => return error.Unseekable,
|
||||
.SPIPE => return error.Unseekable,
|
||||
.NXIO => return error.Unseekable,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_windows) {
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
var info: windows.FILE.POSITION_INFORMATION = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
if (windows.kernel32.SetFilePointerEx(fd, offset, null, windows.FILE_CURRENT) != 0) {
|
||||
return syscall.finish();
|
||||
}
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.INVALID_FUNCTION => return syscall.fail(error.Unseekable),
|
||||
.NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.INVALID_HANDLE => unreachable,
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return windows.unexpectedError(err);
|
||||
},
|
||||
}
|
||||
}
|
||||
while (true) switch (windows.ntdll.NtQueryInformationFile(
|
||||
file.handle,
|
||||
&iosb,
|
||||
&info,
|
||||
@sizeOf(windows.FILE.POSITION_INFORMATION),
|
||||
.Position,
|
||||
)) {
|
||||
.SUCCESS => break,
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
|
||||
else => |status| return syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
info.CurrentByteOffset = @bitCast((if (offset >= 0) std.math.add(
|
||||
u64,
|
||||
@bitCast(info.CurrentByteOffset),
|
||||
@intCast(offset),
|
||||
) else std.math.sub(
|
||||
u64,
|
||||
@bitCast(info.CurrentByteOffset),
|
||||
@intCast(-offset),
|
||||
)) catch |err| switch (err) {
|
||||
error.Overflow => return syscall.fail(error.Unseekable),
|
||||
});
|
||||
while (true) switch (windows.ntdll.NtSetInformationFile(
|
||||
file.handle,
|
||||
&iosb,
|
||||
&info,
|
||||
@sizeOf(windows.FILE.POSITION_INFORMATION),
|
||||
.Position,
|
||||
)) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
|
||||
else => |status| return syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
}
|
||||
|
||||
if (native_os == .wasi and !builtin.link_libc) {
|
||||
var new_offset: std.os.wasi.filesize_t = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
|
||||
switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
@@ -9974,9 +9942,37 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
|
||||
|
||||
if (posix.SEEK == void) return error.Unseekable;
|
||||
|
||||
if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
|
||||
var result: u64 = undefined;
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |e| {
|
||||
syscall.finish();
|
||||
switch (e) {
|
||||
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
|
||||
.INVAL => return error.Unseekable,
|
||||
.OVERFLOW => return error.Unseekable,
|
||||
.SPIPE => return error.Unseekable,
|
||||
.NXIO => return error.Unseekable,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {
|
||||
switch (posix.errno(lseek_sym(file.handle, offset, posix.SEEK.CUR))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
@@ -10003,41 +9999,31 @@ fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!voi
|
||||
fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
const fd = file.handle;
|
||||
|
||||
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
|
||||
const ipos: windows.LARGE_INTEGER = @bitCast(offset);
|
||||
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
var info: windows.FILE.POSITION_INFORMATION = .{ .CurrentByteOffset = @bitCast(offset) };
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
if (windows.kernel32.SetFilePointerEx(fd, ipos, null, windows.FILE_BEGIN) != 0) {
|
||||
return syscall.finish();
|
||||
}
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.INVALID_FUNCTION => return syscall.fail(error.Unseekable),
|
||||
.NEGATIVE_SEEK => return syscall.fail(error.Unseekable),
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.INVALID_HANDLE => unreachable,
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return windows.unexpectedError(err);
|
||||
},
|
||||
}
|
||||
}
|
||||
while (true) switch (windows.ntdll.NtSetInformationFile(
|
||||
file.handle,
|
||||
&iosb,
|
||||
&info,
|
||||
@sizeOf(windows.FILE.POSITION_INFORMATION),
|
||||
.Position,
|
||||
)) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
|
||||
else => |status| return syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
}
|
||||
|
||||
if (native_os == .wasi and !builtin.link_libc) {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
var new_offset: std.os.wasi.filesize_t = undefined;
|
||||
switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
|
||||
switch (std.os.wasi.fd_seek(file.handle, @bitCast(offset), .SET, &new_offset)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return;
|
||||
@@ -10064,7 +10050,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!voi
|
||||
|
||||
if (posix.SEEK == void) return error.Unseekable;
|
||||
|
||||
return posixSeekTo(fd, offset);
|
||||
return posixSeekTo(file.handle, offset);
|
||||
}
|
||||
|
||||
fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
|
||||
@@ -10131,8 +10117,7 @@ fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.O
|
||||
// If ImagePathName is a symlink, then it will contain the path of the symlink,
|
||||
// not the path that the symlink points to. However, because we are opening
|
||||
// the file, we can let the openFileW call follow the symlink for us.
|
||||
const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
|
||||
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
|
||||
const image_path_name = windows.peb().ProcessParameters.ImagePathName.sliceZ();
|
||||
const prefixed_path_w = try wToPrefixedFileW(null, image_path_name);
|
||||
return dirOpenFileWtf16(null, prefixed_path_w.span(), flags);
|
||||
},
|
||||
@@ -10334,14 +10319,13 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
|
||||
return error.FileNotFound;
|
||||
},
|
||||
.windows => {
|
||||
const w = windows;
|
||||
const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
|
||||
const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
|
||||
|
||||
// If ImagePathName is a symlink, then it will contain the path of the
|
||||
// symlink, not the path that the symlink points to. We want the path
|
||||
// that the symlink points to, though, so we need to get the realpath.
|
||||
var path_name_w_buf = try wToPrefixedFileW(null, image_path_name);
|
||||
var path_name_w_buf = try wToPrefixedFileW(
|
||||
null,
|
||||
windows.peb().ProcessParameters.ImagePathName.sliceZ(),
|
||||
);
|
||||
|
||||
const h_file = handle: {
|
||||
if (OpenFile(path_name_w_buf.span(), .{
|
||||
@@ -10360,7 +10344,7 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
|
||||
else => |e| return e,
|
||||
}
|
||||
};
|
||||
defer w.CloseHandle(h_file);
|
||||
defer windows.CloseHandle(h_file);
|
||||
|
||||
const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
|
||||
|
||||
@@ -10388,15 +10372,15 @@ fn fileWritePositional(
|
||||
|
||||
if (is_windows) {
|
||||
if (header.len != 0) {
|
||||
return writeFilePositionalWindows(file.handle, header, offset);
|
||||
return writeFilePositionalWindows(file, header, offset);
|
||||
}
|
||||
for (data[0 .. data.len - 1]) |buf| {
|
||||
if (buf.len == 0) continue;
|
||||
return writeFilePositionalWindows(file.handle, buf, offset);
|
||||
return writeFilePositionalWindows(file, buf, offset);
|
||||
}
|
||||
const pattern = data[data.len - 1];
|
||||
if (pattern.len == 0 or splat == 0) return 0;
|
||||
return writeFilePositionalWindows(file.handle, pattern, offset);
|
||||
return writeFilePositionalWindows(file, pattern, offset);
|
||||
}
|
||||
|
||||
var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
|
||||
@@ -10505,50 +10489,64 @@ fn fileWritePositional(
|
||||
}
|
||||
}
|
||||
|
||||
fn writeFilePositionalWindows(
|
||||
handle: windows.HANDLE,
|
||||
bytes: []const u8,
|
||||
offset: u64,
|
||||
) File.WritePositionalError!usize {
|
||||
var bytes_written: windows.DWORD = undefined;
|
||||
var overlapped: windows.OVERLAPPED = .{
|
||||
.Internal = 0,
|
||||
.InternalHigh = 0,
|
||||
.DUMMYUNIONNAME = .{
|
||||
.DUMMYSTRUCTNAME = .{
|
||||
.Offset = @truncate(offset),
|
||||
.OffsetHigh = @truncate(offset >> 32),
|
||||
fn writeFilePositionalWindows(file: File, buffer: []const u8, offset: u64) File.WritePositionalError!usize {
|
||||
assert(buffer.len != 0);
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
const short_buffer_len = std.math.lossyCast(u32, buffer.len);
|
||||
const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
|
||||
if (file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtWriteFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
&signed_offset,
|
||||
null, // key
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
// Once we get here we must not return from the function until the
|
||||
// operation completes, thereby releasing reference to the iosb.
|
||||
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
|
||||
error.Canceled => |e| {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
|
||||
while (!done) waitForApcOrAlert();
|
||||
return e;
|
||||
},
|
||||
};
|
||||
waitForApcOrAlert();
|
||||
alertable_syscall.finish();
|
||||
},
|
||||
},
|
||||
.hEvent = null,
|
||||
};
|
||||
const adjusted_len = std.math.lossyCast(u32, bytes.len);
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
if (windows.kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, &overlapped) != 0) {
|
||||
syscall.finish();
|
||||
return bytes_written;
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
switch (windows.GetLastError()) {
|
||||
.OPERATION_ABORTED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.INVALID_USER_BUFFER => return syscall.fail(error.SystemResources),
|
||||
.NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources),
|
||||
.NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources),
|
||||
.NO_DATA => return syscall.fail(error.BrokenPipe),
|
||||
.INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free
|
||||
.LOCK_VIOLATION => return syscall.fail(error.LockViolation),
|
||||
.ACCESS_DENIED => return syscall.fail(error.AccessDenied),
|
||||
.WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
|
||||
.DISK_FULL => return syscall.fail(error.NoSpaceLeft),
|
||||
else => |err| {
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtWriteFile(
|
||||
file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&iosb,
|
||||
buffer.ptr,
|
||||
short_buffer_len,
|
||||
&signed_offset,
|
||||
null, // key
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
return windows.unexpectedError(err);
|
||||
iosb.u.Status = status;
|
||||
return ntWriteFileResult(&iosb);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
return ntWriteFileResult(&iosb);
|
||||
}
|
||||
|
||||
fn fileWriteStreaming(
|
||||
@@ -10673,9 +10671,7 @@ fn fileWriteStreaming(
|
||||
|
||||
fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize {
|
||||
assert(buffer.len != 0);
|
||||
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
|
||||
if (file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtWriteFile(
|
||||
@@ -10706,7 +10702,6 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
|
||||
},
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
return ntWriteFileResult(&iosb);
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtWriteFile(
|
||||
@@ -10721,17 +10716,15 @@ fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!u
|
||||
null, // key
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
|
||||
.CANCELLED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.CANCELLED => try syscall.checkCancel(),
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
iosb.u.Status = status;
|
||||
return ntWriteFileResult(&iosb);
|
||||
break;
|
||||
},
|
||||
};
|
||||
}
|
||||
return ntWriteFileResult(&iosb);
|
||||
}
|
||||
|
||||
fn fileWriteFileStreaming(
|
||||
@@ -11433,13 +11426,22 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
|
||||
return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns };
|
||||
},
|
||||
.awake, .boot => {
|
||||
// QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
|
||||
const qpc = windows.QueryPerformanceCounter();
|
||||
// We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
|
||||
// (a read-only page of info updated and mapped by the kernel to all processes):
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
|
||||
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
|
||||
const qpf = windows.QueryPerformanceFrequency();
|
||||
const qpf: u64 = qpf: {
|
||||
var qpf: windows.LARGE_INTEGER = undefined;
|
||||
assert(windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != windows.FALSE);
|
||||
break :qpf @bitCast(qpf);
|
||||
};
|
||||
|
||||
// QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
|
||||
const qpc: u64 = qpc: {
|
||||
var qpc: windows.LARGE_INTEGER = undefined;
|
||||
assert(windows.ntdll.RtlQueryPerformanceCounter(&qpc) != windows.FALSE);
|
||||
break :qpc @bitCast(qpc);
|
||||
};
|
||||
|
||||
// 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
|
||||
// https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
|
||||
@@ -11458,7 +11460,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
|
||||
// https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L442-L485
|
||||
if (windows.ntdll.NtQueryInformationProcess(
|
||||
handle,
|
||||
windows.PROCESSINFOCLASS.Times,
|
||||
.Times,
|
||||
×,
|
||||
@sizeOf(windows.KERNEL_USER_TIMES),
|
||||
null,
|
||||
@@ -11474,7 +11476,7 @@ fn nowWindows(clock: Io.Clock) Io.Timestamp {
|
||||
// https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L2971-L3019
|
||||
if (windows.ntdll.NtQueryInformationThread(
|
||||
handle,
|
||||
windows.THREADINFOCLASS.Times,
|
||||
.Times,
|
||||
×,
|
||||
@sizeOf(windows.KERNEL_USER_TIMES),
|
||||
null,
|
||||
@@ -14132,21 +14134,16 @@ fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathEr
|
||||
}
|
||||
|
||||
fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {
|
||||
if (native_os == .wasi) return error.OperationUnsupported;
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
_ = t;
|
||||
|
||||
if (native_os == .wasi) return error.OperationUnsupported;
|
||||
|
||||
if (is_windows) {
|
||||
var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buffer);
|
||||
const path_len_bytes = std.math.cast(u16, dir_path.len * 2) orelse return error.NameTooLong;
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(dir_path.ptr),
|
||||
};
|
||||
var dir_path_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buf);
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&nt_name)) {
|
||||
while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(dir_path))) {
|
||||
.SUCCESS => return syscall.finish(),
|
||||
.OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
|
||||
.OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
|
||||
@@ -15465,26 +15462,27 @@ fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
|
||||
fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void {
|
||||
_ = t; // TODO cancelation
|
||||
const handle = child.id.?;
|
||||
if (windows.kernel32.TerminateProcess(handle, exit_code) == 0) {
|
||||
switch (windows.GetLastError()) {
|
||||
.ACCESS_DENIED => {
|
||||
// Usually when TerminateProcess triggers a ACCESS_DENIED error, it
|
||||
// indicates that the process has already exited, but there may be
|
||||
// some rare edge cases where our process handle no longer has the
|
||||
// PROCESS_TERMINATE access right, so let's do another check to make
|
||||
// sure the process is really no longer running:
|
||||
const minimal_timeout: windows.LARGE_INTEGER = -1;
|
||||
switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) {
|
||||
.SUCCESS => return error.AlreadyTerminated,
|
||||
else => return error.AccessDenied,
|
||||
}
|
||||
},
|
||||
else => |err| return windows.unexpectedError(err),
|
||||
}
|
||||
_ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code));
|
||||
switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) {
|
||||
.SUCCESS => {
|
||||
const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
|
||||
_ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
|
||||
childCleanupWindows(child);
|
||||
},
|
||||
.ACCESS_DENIED => {
|
||||
// Usually when TerminateProcess triggers a ACCESS_DENIED error, it
|
||||
// indicates that the process has already exited, but there may be
|
||||
// some rare edge cases where our process handle no longer has the
|
||||
// PROCESS_TERMINATE access right, so let's do another check to make
|
||||
// sure the process is really no longer running:
|
||||
const minimal_timeout: windows.LARGE_INTEGER = -1;
|
||||
return switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) {
|
||||
windows.NTSTATUS.WAIT_0 => error.AlreadyTerminated,
|
||||
else => error.AccessDenied,
|
||||
};
|
||||
},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
|
||||
_ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
|
||||
childCleanupWindows(child);
|
||||
}
|
||||
|
||||
fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
|
||||
@@ -15501,12 +15499,12 @@ fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child
|
||||
else => |status| return alertable_syscall.unexpectedNtstatus(status),
|
||||
};
|
||||
|
||||
var info: windows.PROCESS_BASIC_INFORMATION = undefined;
|
||||
var info: windows.PROCESS.BASIC_INFORMATION = undefined;
|
||||
const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess(
|
||||
handle,
|
||||
.BasicInformation,
|
||||
&info,
|
||||
@sizeOf(windows.PROCESS_BASIC_INFORMATION),
|
||||
@sizeOf(windows.PROCESS.BASIC_INFORMATION),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) },
|
||||
@@ -15521,12 +15519,12 @@ fn childCleanupWindows(child: *process.Child) void {
|
||||
const handle = child.id orelse return;
|
||||
|
||||
if (child.request_resource_usage_statistics) {
|
||||
var vmc: windows.VM_COUNTERS = undefined;
|
||||
var vmc: windows.PROCESS.VM_COUNTERS = undefined;
|
||||
switch (windows.ntdll.NtQueryInformationProcess(
|
||||
handle,
|
||||
.VmCounters,
|
||||
&vmc,
|
||||
@sizeOf(windows.VM_COUNTERS),
|
||||
@sizeOf(windows.PROCESS.VM_COUNTERS),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => child.resource_usage_statistics.rusage = vmc,
|
||||
@@ -15853,7 +15851,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
|
||||
.cbReserved2 = 0,
|
||||
.lpReserved2 = null,
|
||||
};
|
||||
var piProcInfo: windows.PROCESS_INFORMATION = undefined;
|
||||
var piProcInfo: windows.PROCESS.INFORMATION = undefined;
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
@@ -16062,7 +16060,6 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
|
||||
if (t.random_file.handle) |handle| return handle;
|
||||
}
|
||||
|
||||
const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' };
|
||||
var fresh_handle: windows.HANDLE = undefined;
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
var syscall: Syscall = try .start();
|
||||
@@ -16072,13 +16069,9 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
|
||||
.STANDARD = .{ .SYNCHRONIZE = true },
|
||||
.SPECIFIC = .{ .FILE = .{ .READ_DATA = true } },
|
||||
},
|
||||
&.{
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING{
|
||||
.Length = @sizeOf(@TypeOf(device_path)),
|
||||
.MaximumLength = 0,
|
||||
.Buffer = @constCast(&device_path),
|
||||
}),
|
||||
},
|
||||
&.{ .ObjectName = @constCast(&windows.UNICODE_STRING.init(
|
||||
&.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' },
|
||||
)) },
|
||||
&io_status_block,
|
||||
.VALID_FLAGS,
|
||||
.{ .IO = .SYNCHRONOUS_NONALERT },
|
||||
@@ -16111,7 +16104,6 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
|
||||
if (t.null_file.handle) |handle| return handle;
|
||||
}
|
||||
|
||||
const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' };
|
||||
var fresh_handle: windows.HANDLE = undefined;
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
var syscall: Syscall = try .start();
|
||||
@@ -16123,11 +16115,9 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
|
||||
},
|
||||
&.{
|
||||
.Attributes = .{ .INHERIT = true },
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING{
|
||||
.Length = @sizeOf(@TypeOf(device_path)),
|
||||
.MaximumLength = 0,
|
||||
.Buffer = @constCast(&device_path),
|
||||
}),
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING.init(
|
||||
&.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' },
|
||||
)),
|
||||
},
|
||||
&io_status_block,
|
||||
.VALID_FLAGS,
|
||||
@@ -16173,7 +16163,6 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
|
||||
if (t.pipe_file.handle) |handle| return handle;
|
||||
}
|
||||
|
||||
const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' };
|
||||
var fresh_handle: windows.HANDLE = undefined;
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
var syscall: Syscall = try .start();
|
||||
@@ -16181,11 +16170,9 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
|
||||
&fresh_handle,
|
||||
.{ .STANDARD = .{ .SYNCHRONIZE = true } },
|
||||
&.{
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING{
|
||||
.Length = @sizeOf(@TypeOf(device_path)),
|
||||
.MaximumLength = 0,
|
||||
.Buffer = @constCast(&device_path),
|
||||
}),
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING.init(
|
||||
&.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' },
|
||||
)),
|
||||
},
|
||||
&io_status_block,
|
||||
.VALID_FLAGS,
|
||||
@@ -16253,7 +16240,7 @@ fn windowsCreateProcessPathExt(
|
||||
cwd_ptr: ?[*:0]u16,
|
||||
flags: windows.CreateProcessFlags,
|
||||
lpStartupInfo: *windows.STARTUPINFOW,
|
||||
lpProcessInformation: *windows.PROCESS_INFORMATION,
|
||||
lpProcessInformation: *windows.PROCESS.INFORMATION,
|
||||
) !void {
|
||||
const app_name_len = app_buf.items.len;
|
||||
const dir_path_len = dir_buf.items.len;
|
||||
@@ -16341,13 +16328,9 @@ fn windowsCreateProcessPathExt(
|
||||
// On NTFS, `blah.exe*` will always return `blah.exe` first if it exists.
|
||||
// On FAT32, it's possible for something like `blah.exe.obj` to be returned first.
|
||||
while (true) {
|
||||
const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong;
|
||||
var app_name_unicode_string = windows.UNICODE_STRING{
|
||||
.Length = app_name_len_bytes,
|
||||
.MaximumLength = app_name_len_bytes,
|
||||
.Buffer = @constCast(app_name_wildcard.ptr),
|
||||
};
|
||||
const rc = windows.ntdll.NtQueryDirectoryFile(
|
||||
// If we get nothing with the wildcard, then we can just bail out
|
||||
// as we know appending PATHEXT will not yield anything.
|
||||
switch (windows.ntdll.NtQueryDirectoryFile(
|
||||
dir.handle,
|
||||
null,
|
||||
null,
|
||||
@@ -16357,18 +16340,14 @@ fn windowsCreateProcessPathExt(
|
||||
file_information_buf.len,
|
||||
.Directory,
|
||||
windows.FALSE, // single result
|
||||
&app_name_unicode_string,
|
||||
&.init(app_name_wildcard),
|
||||
windows.FALSE, // restart iteration
|
||||
);
|
||||
|
||||
// If we get nothing with the wildcard, then we can just bail out
|
||||
// as we know appending PATHEXT will not yield anything.
|
||||
switch (rc) {
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
.NO_SUCH_FILE => return error.FileNotFound,
|
||||
.NO_MORE_FILES => break,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
|
||||
// According to the docs, this can only happen if there is not enough room in the
|
||||
@@ -16514,7 +16493,7 @@ fn windowsCreateProcess(
|
||||
cwd_ptr: ?[*:0]u16,
|
||||
flags: windows.CreateProcessFlags,
|
||||
lpStartupInfo: *windows.STARTUPINFOW,
|
||||
lpProcessInformation: *windows.PROCESS_INFORMATION,
|
||||
lpProcessInformation: *windows.PROCESS.INFORMATION,
|
||||
) !void {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
@@ -17138,7 +17117,7 @@ pub const CreatePipeOptions = struct {
|
||||
default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100,
|
||||
|
||||
pub const End = struct {
|
||||
attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES = .{},
|
||||
attributes: windows.OBJECT.ATTRIBUTES.Flags = .{},
|
||||
mode: windows.FILE.MODE,
|
||||
};
|
||||
};
|
||||
@@ -18373,7 +18352,7 @@ const CreateFileMapError = error{
|
||||
OutOfMemory,
|
||||
MappingAlreadyExists,
|
||||
Unseekable,
|
||||
FileLockConflict,
|
||||
LockViolation,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
fn createFileMap(
|
||||
@@ -18408,7 +18387,7 @@ fn createFileMap(
|
||||
file.handle,
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
.FILE_LOCK_CONFLICT => return error.FileLockConflict,
|
||||
.FILE_LOCK_CONFLICT => return error.LockViolation,
|
||||
.INVALID_FILE_FOR_SECTION => return error.OperationUnsupported,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.SECTION_TOO_BIG => return error.SectionOversize,
|
||||
@@ -18724,7 +18703,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
|
||||
while (true) {
|
||||
const buf = memory[i..];
|
||||
if (buf.len == 0) break;
|
||||
i += try writeFilePositionalWindows(file.handle, memory[i..], offset + i);
|
||||
i += try writeFilePositionalWindows(file, memory[i..], offset + i);
|
||||
}
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
var i: usize = 0;
|
||||
@@ -18809,8 +18788,7 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
|
||||
}
|
||||
}
|
||||
|
||||
fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
|
||||
_ = t;
|
||||
fn deviceIoControl(o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
|
||||
if (is_windows) {
|
||||
const NtControlFile = switch (o.code.DeviceType) {
|
||||
.FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
|
||||
@@ -19104,16 +19082,10 @@ fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows
|
||||
|
||||
var result: windows.HANDLE = undefined;
|
||||
|
||||
const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
|
||||
var nt_name: windows.UNICODE_STRING = .{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w.ptr),
|
||||
};
|
||||
const attr: windows.OBJECT_ATTRIBUTES = .{
|
||||
const attr: windows.OBJECT.ATTRIBUTES = .{
|
||||
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
|
||||
.Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != windows.FALSE else false },
|
||||
.ObjectName = &nt_name,
|
||||
.ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w)),
|
||||
.SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
|
||||
};
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ fn RtlDosPathNameToNtPathName_U(path: [:0]const u16) !Io.Threaded.WindowsPathSpa
|
||||
defer windows.ntdll.RtlFreeUnicodeString(&out);
|
||||
|
||||
var path_space: Io.Threaded.WindowsPathSpace = undefined;
|
||||
const out_path = out.Buffer.?[0 .. out.Length / 2];
|
||||
const out_path = out.slice();
|
||||
@memcpy(path_space.data[0..out_path.len], out_path);
|
||||
path_space.len = out.Length / 2;
|
||||
path_space.data[path_space.len] = 0;
|
||||
|
||||
+3
-13
@@ -88,20 +88,10 @@ pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void {
|
||||
},
|
||||
.windows => {
|
||||
var buf: [max_name_len]u16 = undefined;
|
||||
const len = try std.unicode.wtf8ToWtf16Le(&buf, name);
|
||||
const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong;
|
||||
|
||||
// Note: NT allocates its own copy, no use-after-free here.
|
||||
const unicode_string = windows.UNICODE_STRING{
|
||||
.Length = byte_len,
|
||||
.MaximumLength = byte_len,
|
||||
.Buffer = &buf,
|
||||
};
|
||||
|
||||
switch (windows.ntdll.NtSetInformationThread(
|
||||
self.getHandle(),
|
||||
.NameInformation,
|
||||
&unicode_string,
|
||||
&windows.UNICODE_STRING.init(buf[0..try std.unicode.wtf8ToWtf16Le(&buf, name)]),
|
||||
@sizeOf(windows.UNICODE_STRING),
|
||||
)) {
|
||||
.SUCCESS => return,
|
||||
@@ -217,8 +207,8 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => {
|
||||
const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf));
|
||||
const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]);
|
||||
const string: *const windows.UNICODE_STRING = @ptrCast(&buf);
|
||||
const len = std.unicode.wtf16LeToWtf8(buffer, string.slice());
|
||||
return if (len > 0) buffer[0..len] else null;
|
||||
},
|
||||
.NOT_IMPLEMENTED => return error.Unsupported,
|
||||
|
||||
+18
-27
@@ -37,12 +37,13 @@ pub const cpu_context = @import("debug/cpu_context.zig");
|
||||
///
|
||||
/// ```
|
||||
/// pub const init: SelfInfo;
|
||||
/// pub fn deinit(si: *SelfInfo, gpa: Allocator) void;
|
||||
/// pub fn deinit(si: *SelfInfo, io: Io) void;
|
||||
///
|
||||
/// /// Returns the symbol and source location of the instruction at `address`.
|
||||
/// pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) SelfInfoError!Symbol;
|
||||
/// pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) SelfInfoError!Symbol;
|
||||
/// /// Returns a name for the "module" (e.g. shared library or executable image) containing `address`.
|
||||
/// pub fn getModuleName(si: *SelfInfo, gpa: Allocator, address: usize) SelfInfoError![]const u8;
|
||||
/// pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) SelfInfoError![]const u8;
|
||||
/// pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) SelfInfoError!usize;
|
||||
///
|
||||
/// /// Whether a reliable stack unwinding strategy, such as DWARF unwinding, is available.
|
||||
/// pub const can_unwind: bool;
|
||||
@@ -51,15 +52,15 @@ pub const cpu_context = @import("debug/cpu_context.zig");
|
||||
/// /// An address representing the instruction pointer in the last frame.
|
||||
/// pc: usize,
|
||||
///
|
||||
/// pub fn init(ctx: *cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext;
|
||||
/// pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void;
|
||||
/// pub fn init(ctx: *cpu_context.Native) Allocator.Error!UnwindContext;
|
||||
/// pub fn deinit(ctx: *UnwindContext) void;
|
||||
/// /// Returns the frame pointer associated with the last unwound stack frame.
|
||||
/// /// If the frame pointer is unknown, 0 may be returned instead.
|
||||
/// pub fn getFp(uc: *UnwindContext) usize;
|
||||
/// };
|
||||
/// /// Only required if `can_unwind == true`. Unwinds a single stack frame, returning the frame's
|
||||
/// /// return address, or 0 if the end of the stack has been reached.
|
||||
/// pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) SelfInfoError!usize;
|
||||
/// pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) SelfInfoError!usize;
|
||||
/// ```
|
||||
pub const SelfInfo = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "SelfInfo"))
|
||||
root.debug.SelfInfo
|
||||
@@ -669,7 +670,6 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
|
||||
t.setColor(.reset) catch {};
|
||||
return;
|
||||
}
|
||||
const di_gpa = getDebugInfoAllocator();
|
||||
const di = getSelfDebugInfo() catch |err| switch (err) {
|
||||
error.UnsupportedTarget => {
|
||||
t.setColor(.dim) catch {};
|
||||
@@ -696,7 +696,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
|
||||
.useless, .unsafe => {},
|
||||
.safe, .ideal => continue, // no need to even warn
|
||||
}
|
||||
const module_name = di.getModuleName(di_gpa, io, unwind_error.address) catch "???";
|
||||
const module_name = di.getModuleName(io, unwind_error.address) catch "???";
|
||||
const caption: []const u8 = switch (unwind_error.err) {
|
||||
error.MissingDebugInfo => "unwind info unavailable",
|
||||
error.InvalidDebugInfo => "unwind info invalid",
|
||||
@@ -741,7 +741,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, t: Io.Termin
|
||||
}
|
||||
// `ret_addr` is the return address, which is *after* the function call.
|
||||
// Subtract 1 to get an address *in* the function call for a better source location.
|
||||
try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset);
|
||||
try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
|
||||
printed_any_frame = true;
|
||||
},
|
||||
};
|
||||
@@ -788,7 +788,6 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
|
||||
// `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace.
|
||||
const n_frames = st.index;
|
||||
if (n_frames == 0) return writer.writeAll("(empty stack trace)\n");
|
||||
const di_gpa = getDebugInfoAllocator();
|
||||
const di = getSelfDebugInfo() catch |err| switch (err) {
|
||||
error.UnsupportedTarget => {
|
||||
t.setColor(.dim) catch {};
|
||||
@@ -802,7 +801,7 @@ pub fn writeStackTrace(st: *const StackTrace, t: Io.Terminal) Writer.Error!void
|
||||
for (st.instruction_addresses[0..captured_frames]) |ret_addr| {
|
||||
// `ret_addr` is the return address, which is *after* the function call.
|
||||
// Subtract 1 to get an address *in* the function call for a better source location.
|
||||
try printSourceAtAddress(di_gpa, io, di, t, ret_addr -| StackIterator.ra_call_offset);
|
||||
try printSourceAtAddress(io, di, t, ret_addr -| StackIterator.ra_call_offset);
|
||||
}
|
||||
if (n_frames > captured_frames) {
|
||||
t.setColor(.bold) catch {};
|
||||
@@ -875,7 +874,7 @@ const StackIterator = union(enum) {
|
||||
switch (si.*) {
|
||||
.ctx_first => {},
|
||||
.fp => {},
|
||||
.di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()),
|
||||
.di => |*unwind_context| unwind_context.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,8 +979,7 @@ const StackIterator = union(enum) {
|
||||
},
|
||||
.di => |*unwind_context| {
|
||||
const di = getSelfDebugInfo() catch unreachable;
|
||||
const di_gpa = getDebugInfoAllocator();
|
||||
const ret_addr = di.unwindFrame(di_gpa, io, unwind_context) catch |err| {
|
||||
const ret_addr = di.unwindFrame(io, unwind_context) catch |err| {
|
||||
const pc = unwind_context.pc;
|
||||
const fp = unwind_context.getFp();
|
||||
it.* = .{ .fp = fp };
|
||||
@@ -1109,14 +1107,8 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
fn printSourceAtAddress(
|
||||
gpa: Allocator,
|
||||
io: Io,
|
||||
debug_info: *SelfInfo,
|
||||
t: Io.Terminal,
|
||||
address: usize,
|
||||
) Writer.Error!void {
|
||||
const symbol: Symbol = debug_info.getSymbol(gpa, io, address) catch |err| switch (err) {
|
||||
fn printSourceAtAddress(io: Io, debug_info: *SelfInfo, t: Io.Terminal, address: usize) Writer.Error!void {
|
||||
const symbol: Symbol = debug_info.getSymbol(io, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo,
|
||||
error.UnsupportedDebugInfo,
|
||||
error.InvalidDebugInfo,
|
||||
@@ -1134,14 +1126,14 @@ fn printSourceAtAddress(
|
||||
break :s .unknown;
|
||||
},
|
||||
};
|
||||
defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
|
||||
defer if (symbol.source_location) |sl| getDebugInfoAllocator().free(sl.file_name);
|
||||
return printLineInfo(
|
||||
io,
|
||||
t,
|
||||
symbol.source_location,
|
||||
address,
|
||||
symbol.name orelse "???",
|
||||
symbol.compile_unit_name orelse debug_info.getModuleName(gpa, io, address) catch "???",
|
||||
symbol.compile_unit_name orelse debug_info.getModuleName(io, address) catch "???",
|
||||
);
|
||||
}
|
||||
fn printLineInfo(
|
||||
@@ -1608,14 +1600,13 @@ test "manage resources correctly" {
|
||||
return @returnAddress();
|
||||
}
|
||||
};
|
||||
const gpa = testing.allocator;
|
||||
const io = testing.io;
|
||||
|
||||
var discarding: Writer.Discarding = .init(&.{});
|
||||
var di: SelfInfo = .init;
|
||||
defer di.deinit(gpa);
|
||||
defer di.deinit(io);
|
||||
const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
|
||||
try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace());
|
||||
try printSourceAtAddress(io, &di, t, S.showMyTrace());
|
||||
}
|
||||
|
||||
/// This API helps you track where a value originated and where it was mutated,
|
||||
|
||||
@@ -55,7 +55,8 @@ pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void {
|
||||
pub fn deinit(unwinder: *SelfUnwinder) void {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
unwinder.cfi_vm.deinit(gpa);
|
||||
unwinder.expr_vm.deinit(gpa);
|
||||
unwinder.* = undefined;
|
||||
|
||||
@@ -11,7 +11,9 @@ pub const init: SelfInfo = .{
|
||||
.ranges = .empty,
|
||||
.unwind_cache = null,
|
||||
};
|
||||
pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
|
||||
pub fn deinit(si: *SelfInfo, io: Io) void {
|
||||
_ = io;
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
for (si.modules.items) |*mod| {
|
||||
unwind: {
|
||||
const u = &(mod.unwind orelse break :unwind catch break :unwind);
|
||||
@@ -28,7 +30,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
|
||||
if (si.unwind_cache) |cache| gpa.free(cache);
|
||||
}
|
||||
|
||||
pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, address, .exclusive);
|
||||
defer si.rwlock.unlock(io);
|
||||
|
||||
@@ -73,13 +76,15 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
}
|
||||
pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, address, .shared);
|
||||
defer si.rwlock.unlockShared(io);
|
||||
if (module.name.len == 0) return error.MissingDebugInfo;
|
||||
return module.name;
|
||||
}
|
||||
pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
|
||||
pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, address, .shared);
|
||||
defer si.rwlock.unlockShared(io);
|
||||
return module.load_offset;
|
||||
@@ -179,8 +184,9 @@ comptime {
|
||||
}
|
||||
}
|
||||
pub const UnwindContext = Dwarf.SelfUnwinder;
|
||||
pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
|
||||
pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
|
||||
comptime assert(can_unwind);
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
|
||||
{
|
||||
si.rwlock.lockSharedUncancelable(io);
|
||||
|
||||
@@ -6,7 +6,9 @@ pub const init: SelfInfo = .{
|
||||
.mutex = .init,
|
||||
.modules = .empty,
|
||||
};
|
||||
pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
|
||||
pub fn deinit(si: *SelfInfo, io: Io) void {
|
||||
_ = io;
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
for (si.modules.keys()) |*module| {
|
||||
unwind: {
|
||||
const u = &(module.unwind orelse break :unwind catch break :unwind);
|
||||
@@ -20,7 +22,8 @@ pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
|
||||
si.modules.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, address);
|
||||
defer si.mutex.unlock(io);
|
||||
|
||||
@@ -76,9 +79,8 @@ pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!st
|
||||
) catch null,
|
||||
};
|
||||
}
|
||||
pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
_ = si;
|
||||
_ = gpa;
|
||||
_ = io;
|
||||
// This function is marked as deprecated; however, it is significantly more
|
||||
// performant than `dladdr` (since the latter also does a very slow symbol
|
||||
@@ -87,7 +89,8 @@ pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Erro
|
||||
@ptrFromInt(address),
|
||||
) orelse return error.MissingDebugInfo);
|
||||
}
|
||||
pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
|
||||
pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, address);
|
||||
defer si.mutex.unlock(io);
|
||||
const header: *std.macho.mach_header_64 = @ptrFromInt(module.text_base);
|
||||
@@ -107,8 +110,8 @@ pub const UnwindContext = std.debug.Dwarf.SelfUnwinder;
|
||||
/// Unwind a frame using MachO compact unwind info (from `__unwind_info`).
|
||||
/// If the compact encoding can't encode a way to unwind a frame, it will
|
||||
/// defer unwinding to DWARF, in which case `__eh_frame` will be used if available.
|
||||
pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
|
||||
return unwindFrameInner(si, gpa, io, context) catch |err| switch (err) {
|
||||
pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
|
||||
return unwindFrameInner(si, io, context) catch |err| switch (err) {
|
||||
error.InvalidDebugInfo,
|
||||
error.MissingDebugInfo,
|
||||
error.UnsupportedDebugInfo,
|
||||
@@ -134,7 +137,8 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex
|
||||
=> return error.InvalidDebugInfo,
|
||||
};
|
||||
}
|
||||
fn unwindFrameInner(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) !usize {
|
||||
fn unwindFrameInner(si: *SelfInfo, io: Io, context: *UnwindContext) !usize {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
const module = try si.findModule(gpa, io, context.pc);
|
||||
defer si.mutex.unlock(io);
|
||||
|
||||
|
||||
@@ -1,39 +1,51 @@
|
||||
mutex: Io.Mutex,
|
||||
ntdll_handle: ?if (load_dll_notification_procs) *anyopaque else noreturn,
|
||||
notification_cookie: ?LDR.DLL_NOTIFICATION.COOKIE,
|
||||
modules: std.ArrayList(Module),
|
||||
module_name_arena: std.heap.ArenaAllocator.State,
|
||||
|
||||
pub const init: SelfInfo = .{
|
||||
.mutex = .init,
|
||||
.ntdll_handle = null,
|
||||
.notification_cookie = null,
|
||||
.modules = .empty,
|
||||
.module_name_arena = .{},
|
||||
};
|
||||
pub fn deinit(si: *SelfInfo, gpa: Allocator) void {
|
||||
for (si.modules.items) |*module| {
|
||||
di: {
|
||||
const di = &(module.di orelse break :di catch break :di);
|
||||
di.deinit(gpa);
|
||||
pub fn deinit(si: *SelfInfo, io: Io) void {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
if (si.notification_cookie) |cookie| unregister: {
|
||||
switch ((si.getNtdllProc(.LdrUnregisterDllNotification) catch break :unregister)(cookie)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| windows.unexpectedStatus(status) catch break :unregister,
|
||||
}
|
||||
}
|
||||
if (si.ntdll_handle) |handle| switch (windows.ntdll.LdrUnloadDll(handle)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| windows.unexpectedStatus(status) catch {},
|
||||
};
|
||||
for (si.modules.items) |*module| module.deinit(gpa, io);
|
||||
si.modules.deinit(gpa);
|
||||
|
||||
var module_name_arena = si.module_name_arena.promote(gpa);
|
||||
module_name_arena.deinit();
|
||||
}
|
||||
|
||||
pub fn getSymbol(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
pub fn getSymbol(si: *SelfInfo, io: Io, address: usize) Error!std.debug.Symbol {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
try si.mutex.lock(io);
|
||||
defer si.mutex.unlock(io);
|
||||
const module = try si.findModule(gpa, address);
|
||||
const di = try module.getDebugInfo(gpa, io);
|
||||
return di.getSymbol(gpa, address - module.base_address);
|
||||
return di.getSymbol(gpa, address - @intFromPtr(module.entry.DllBase));
|
||||
}
|
||||
pub fn getModuleName(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error![]const u8 {
|
||||
pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
try si.mutex.lock(io);
|
||||
defer si.mutex.unlock(io);
|
||||
const module = try si.findModule(gpa, address);
|
||||
return module.name;
|
||||
return module.name orelse {
|
||||
const name = try std.unicode.wtf16LeToWtf8Alloc(gpa, module.entry.BaseDllName.slice());
|
||||
module.name = name;
|
||||
return name;
|
||||
};
|
||||
}
|
||||
pub fn getModuleSlide(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!usize {
|
||||
pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
|
||||
const gpa = std.debug.getDebugInfoAllocator();
|
||||
try si.mutex.lock(io);
|
||||
defer si.mutex.unlock(io);
|
||||
const module = try si.findModule(gpa, address);
|
||||
@@ -141,18 +153,16 @@ pub const UnwindContext = struct {
|
||||
.history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
|
||||
};
|
||||
}
|
||||
pub fn deinit(ctx: *UnwindContext, gpa: Allocator) void {
|
||||
pub fn deinit(ctx: *UnwindContext) void {
|
||||
_ = ctx;
|
||||
_ = gpa;
|
||||
}
|
||||
pub fn getFp(ctx: *UnwindContext) usize {
|
||||
return ctx.cur.getRegs().bp;
|
||||
}
|
||||
};
|
||||
pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContext) Error!usize {
|
||||
pub fn unwindFrame(si: *SelfInfo, io: Io, context: *UnwindContext) Error!usize {
|
||||
_ = si;
|
||||
_ = io;
|
||||
_ = gpa;
|
||||
|
||||
const current_regs = context.cur.getRegs();
|
||||
var image_base: usize = undefined;
|
||||
@@ -188,16 +198,12 @@ pub fn unwindFrame(si: *SelfInfo, gpa: Allocator, io: Io, context: *UnwindContex
|
||||
}
|
||||
|
||||
const Module = struct {
|
||||
base_address: usize,
|
||||
size: u32,
|
||||
name: []const u8,
|
||||
handle: windows.HMODULE,
|
||||
|
||||
entry: *const LDR.DATA_TABLE_ENTRY,
|
||||
name: ?[]const u8,
|
||||
di: ?(Error!DebugInfo),
|
||||
|
||||
const DebugInfo = struct {
|
||||
arena: std.heap.ArenaAllocator.State,
|
||||
io: Io,
|
||||
coff_image_base: u64,
|
||||
mapped_file: ?MappedFile,
|
||||
dwarf: ?Dwarf,
|
||||
@@ -210,14 +216,19 @@ const Module = struct {
|
||||
section_view: []const u8,
|
||||
fn deinit(mf: *const MappedFile, io: Io) void {
|
||||
const process_handle = windows.GetCurrentProcess();
|
||||
assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(mf.section_view.ptr)) == .SUCCESS);
|
||||
switch (windows.ntdll.NtUnmapViewOfSection(
|
||||
process_handle,
|
||||
@constCast(mf.section_view.ptr),
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| windows.unexpectedStatus(status) catch {},
|
||||
}
|
||||
windows.CloseHandle(mf.section_handle);
|
||||
mf.file.close(io);
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(di: *DebugInfo, gpa: Allocator) void {
|
||||
const io = di.io;
|
||||
fn deinit(di: *DebugInfo, gpa: Allocator, io: Io) void {
|
||||
if (di.dwarf) |*dwarf| dwarf.deinit(gpa);
|
||||
if (di.pdb) |*pdb| {
|
||||
pdb.file_reader.file.close(io);
|
||||
@@ -262,7 +273,10 @@ const Module = struct {
|
||||
return .{
|
||||
.name = pdb.getSymbolName(module, vaddr - coff_section.virtual_address),
|
||||
.compile_unit_name = fs.path.basename(module.obj_file_name),
|
||||
.source_location = pdb.getLineNumberInfo(module, vaddr - coff_section.virtual_address) catch null,
|
||||
.source_location = pdb.getLineNumberInfo(
|
||||
module,
|
||||
vaddr - coff_section.virtual_address,
|
||||
) catch null,
|
||||
};
|
||||
}
|
||||
dwarf: {
|
||||
@@ -286,13 +300,19 @@ const Module = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn deinit(module: *Module, gpa: Allocator, io: Io) void {
|
||||
if (module.name) |name| gpa.free(name);
|
||||
if (module.di) |*di_or_err| if (di_or_err.*) |*di| di.deinit(gpa, io) else |_| {};
|
||||
module.* = undefined;
|
||||
}
|
||||
|
||||
fn getDebugInfo(module: *Module, gpa: Allocator, io: Io) Error!*DebugInfo {
|
||||
if (module.di == null) module.di = loadDebugInfo(module, gpa, io);
|
||||
return if (module.di.?) |*di| di else |err| err;
|
||||
}
|
||||
fn loadDebugInfo(module: *const Module, gpa: Allocator, io: Io) Error!DebugInfo {
|
||||
const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address);
|
||||
const mapped = mapped_ptr[0..module.size];
|
||||
const mapped_ptr: [*]const u8 = @ptrCast(module.entry.DllBase);
|
||||
const mapped = mapped_ptr[0..module.entry.SizeOfImage];
|
||||
var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo;
|
||||
|
||||
var arena_instance: std.heap.ArenaAllocator = .init(gpa);
|
||||
@@ -304,18 +324,15 @@ const Module = struct {
|
||||
// a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
|
||||
const mapped_file: ?DebugInfo.MappedFile = mapped: {
|
||||
if (!coff_obj.strtabRequired()) break :mapped null;
|
||||
var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
|
||||
name_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
|
||||
const process_handle = windows.GetCurrentProcess();
|
||||
const len = windows.kernel32.GetModuleFileNameExW(
|
||||
process_handle,
|
||||
module.handle,
|
||||
name_buffer[4..],
|
||||
windows.PATH_MAX_WIDE,
|
||||
);
|
||||
if (len == 0) return error.MissingDebugInfo;
|
||||
const name_w = name_buffer[0 .. len + 4 :0];
|
||||
const coff_file = Io.Threaded.dirOpenFileWtf16(null, name_w, .{}) catch |err| switch (err) {
|
||||
var path_buffer: [4 + windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
path_buffer[0..4].* = .{ '\\', '?', '?', '\\' }; // openFileAbsoluteW requires the prefix to be present
|
||||
const path_slice = module.entry.FullDllName.slice();
|
||||
@memcpy(path_buffer[4..][0..path_slice.len], path_slice);
|
||||
const coff_file = Io.Threaded.dirOpenFileWtf16(
|
||||
null,
|
||||
path_buffer[0 .. 4 + path_slice.len],
|
||||
.{},
|
||||
) catch |err| switch (err) {
|
||||
error.Canceled => |e| return e,
|
||||
error.Unexpected => |e| return e,
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
@@ -359,7 +376,8 @@ const Module = struct {
|
||||
null,
|
||||
null,
|
||||
.{ .READONLY = true },
|
||||
// The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default.
|
||||
// The documentation states that if no AllocationAttribute is specified,
|
||||
// then SEC_COMMIT is the default.
|
||||
// In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6.
|
||||
.{ .COMMIT = true },
|
||||
coff_file.handle,
|
||||
@@ -368,6 +386,7 @@ const Module = struct {
|
||||
errdefer windows.CloseHandle(section_handle);
|
||||
var coff_len: usize = 0;
|
||||
var section_view_ptr: ?[*]const u8 = null;
|
||||
const process_handle = windows.GetCurrentProcess();
|
||||
const map_section_rc = windows.ntdll.NtMapViewOfSection(
|
||||
section_handle,
|
||||
process_handle,
|
||||
@@ -381,7 +400,13 @@ const Module = struct {
|
||||
.{ .READONLY = true },
|
||||
);
|
||||
if (map_section_rc != .SUCCESS) return error.MissingDebugInfo;
|
||||
errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(section_view_ptr.?)) == .SUCCESS);
|
||||
errdefer switch (windows.ntdll.NtUnmapViewOfSection(
|
||||
process_handle,
|
||||
@constCast(section_view_ptr.?),
|
||||
)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| windows.unexpectedStatus(status) catch {},
|
||||
};
|
||||
const section_view = section_view_ptr.?[0..coff_len];
|
||||
coff_obj = coff.Coff.init(section_view, false) catch return error.InvalidDebugInfo;
|
||||
break :mapped .{
|
||||
@@ -496,7 +521,6 @@ const Module = struct {
|
||||
|
||||
return .{
|
||||
.arena = arena_instance.state,
|
||||
.io = io,
|
||||
.coff_image_base = coff_image_base,
|
||||
.mapped_file = mapped_file,
|
||||
.dwarf = opt_dwarf,
|
||||
@@ -509,52 +533,82 @@ const Module = struct {
|
||||
/// Assumes we already hold `si.mutex`.
|
||||
fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebugInfo, OutOfMemory, Unexpected }!*Module {
|
||||
for (si.modules.items) |*mod| {
|
||||
if (address >= mod.base_address and address < mod.base_address + mod.size) {
|
||||
return mod;
|
||||
const base = @intFromPtr(mod.entry.DllBase);
|
||||
if (address >= base and address < base + mod.entry.SizeOfImage) return mod;
|
||||
}
|
||||
try si.modules.ensureUnusedCapacity(gpa, 1);
|
||||
var entry: *LDR.DATA_TABLE_ENTRY = undefined;
|
||||
switch (windows.ntdll.LdrFindEntryForAddress(@ptrFromInt(address), &entry)) {
|
||||
.SUCCESS => {},
|
||||
.DLL_NOT_FOUND => return error.MissingDebugInfo,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
if (si.notification_cookie == null) {
|
||||
var notification_cookie: LDR.DLL_NOTIFICATION.COOKIE = undefined;
|
||||
switch ((try si.getNtdllProc(.LdrRegisterDllNotification))(
|
||||
.{},
|
||||
&dllNotification,
|
||||
si,
|
||||
¬ification_cookie,
|
||||
)) {
|
||||
.SUCCESS => si.notification_cookie = notification_cookie,
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
}
|
||||
const mod = si.modules.addOneAssumeCapacity();
|
||||
mod.* = .{ .entry = entry, .name = null, .di = null };
|
||||
return mod;
|
||||
}
|
||||
|
||||
// A new module might have been loaded; rebuild the list.
|
||||
{
|
||||
for (si.modules.items) |*mod| {
|
||||
const di = &(mod.di orelse continue catch continue);
|
||||
di.deinit(gpa);
|
||||
}
|
||||
si.modules.clearRetainingCapacity();
|
||||
|
||||
var module_name_arena = si.module_name_arena.promote(gpa);
|
||||
defer si.module_name_arena = module_name_arena.state;
|
||||
_ = module_name_arena.reset(.retain_capacity);
|
||||
|
||||
const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0);
|
||||
if (handle == windows.INVALID_HANDLE_VALUE) {
|
||||
return windows.unexpectedError(windows.GetLastError());
|
||||
}
|
||||
defer windows.CloseHandle(handle);
|
||||
var entry: windows.MODULEENTRY32 = undefined;
|
||||
entry.dwSize = @sizeOf(windows.MODULEENTRY32);
|
||||
var result = windows.kernel32.Module32First(handle, &entry);
|
||||
while (result != 0) : (result = windows.kernel32.Module32Next(handle, &entry)) {
|
||||
try si.modules.append(gpa, .{
|
||||
.base_address = @intFromPtr(entry.modBaseAddr),
|
||||
.size = entry.modBaseSize,
|
||||
.name = try module_name_arena.allocator().dupe(
|
||||
u8,
|
||||
std.mem.sliceTo(&entry.szModule, 0),
|
||||
),
|
||||
.handle = entry.hModule,
|
||||
.di = null,
|
||||
});
|
||||
inline fn getNtdllProc(
|
||||
si: *SelfInfo,
|
||||
comptime proc: std.meta.DeclEnum(windows.ntdll),
|
||||
) !@TypeOf(&@field(windows.ntdll, @tagName(proc))) {
|
||||
return if (load_dll_notification_procs)
|
||||
@ptrCast(try si.loadNtdllProc(@tagName(proc)))
|
||||
else
|
||||
&@field(windows.ntdll, @tagName(proc));
|
||||
}
|
||||
fn loadNtdllProc(si: *SelfInfo, name: []const u8) Io.UnexpectedError!*anyopaque {
|
||||
const ntdll_handle = si.ntdll_handle orelse ntdll_handle: {
|
||||
var ntdll_handle: *anyopaque = undefined;
|
||||
switch (windows.ntdll.LdrLoadDll(null, null, &.init(
|
||||
&.{ 'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l' },
|
||||
), &ntdll_handle)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
si.ntdll_handle = ntdll_handle;
|
||||
break :ntdll_handle ntdll_handle;
|
||||
};
|
||||
var proc_addr: *anyopaque = undefined;
|
||||
switch (windows.ntdll.LdrGetProcedureAddress(ntdll_handle, &.init(name), 0, &proc_addr)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
return proc_addr;
|
||||
}
|
||||
|
||||
for (si.modules.items) |*mod| {
|
||||
if (address >= mod.base_address and address < mod.base_address + mod.size) {
|
||||
return mod;
|
||||
}
|
||||
fn dllNotification(
|
||||
reason: LDR.DLL_NOTIFICATION.REASON,
|
||||
data: *const LDR.DLL_NOTIFICATION.DATA,
|
||||
context: ?*anyopaque,
|
||||
) callconv(.winapi) void {
|
||||
const si: *SelfInfo = @ptrCast(@alignCast(context));
|
||||
switch (reason) {
|
||||
.LOADED => {},
|
||||
.UNLOADED => {
|
||||
const io = std.Options.debug_io;
|
||||
si.mutex.lockUncancelable(io);
|
||||
defer si.mutex.unlock(io);
|
||||
for (si.modules.items, 0..) |*mod, mod_index| {
|
||||
if (mod.entry.DllBase != data.Unloaded.DllBase) continue;
|
||||
mod.deinit(std.debug.getDebugInfoAllocator(), io);
|
||||
_ = si.modules.swapRemove(mod_index);
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
@@ -563,12 +617,23 @@ const Allocator = std.mem.Allocator;
|
||||
const Dwarf = std.debug.Dwarf;
|
||||
const Pdb = std.debug.Pdb;
|
||||
const Error = std.debug.SelfInfoError;
|
||||
const assert = std.debug.assert;
|
||||
const coff = std.coff;
|
||||
const fs = std.fs;
|
||||
const windows = std.os.windows;
|
||||
const LDR = windows.LDR;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
const native_endian = builtin.target.cpu.arch.endian();
|
||||
const load_dll_notification_procs = builtin.abi == .msvc and switch (builtin.zig_backend) {
|
||||
.stage2_c => true,
|
||||
else => switch (builtin.output_mode) {
|
||||
.Exe => false,
|
||||
.Lib => switch (builtin.link_mode) {
|
||||
.static => true,
|
||||
.dynamic => false,
|
||||
},
|
||||
.Obj => true,
|
||||
},
|
||||
};
|
||||
|
||||
const SelfInfo = @This();
|
||||
|
||||
+3
-3
@@ -110,11 +110,11 @@ pub fn defaultQueryPageSize() usize {
|
||||
break :size @intCast(vm_info.page_size);
|
||||
},
|
||||
.windows => {
|
||||
var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
|
||||
var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined;
|
||||
switch (windows.ntdll.NtQuerySystemInformation(
|
||||
.SystemBasicInformation,
|
||||
.Basic,
|
||||
&sbi,
|
||||
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
|
||||
@sizeOf(windows.SYSTEM.BASIC_INFORMATION),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => break :size sbi.PageSize,
|
||||
|
||||
+568
-1125
@@ -24,6 +24,66 @@ pub const nls = @import("windows/nls.zig");
|
||||
|
||||
pub const current_process: HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1))));
|
||||
|
||||
pub const OBJECT = struct {
|
||||
// ref: um/winternl.h
|
||||
|
||||
pub const ATTRIBUTES = extern struct {
|
||||
Length: ULONG = @sizeOf(ATTRIBUTES),
|
||||
RootDirectory: ?HANDLE = null,
|
||||
ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
|
||||
Attributes: Flags = .{},
|
||||
SecurityDescriptor: ?*anyopaque = null,
|
||||
SecurityQualityOfService: ?*anyopaque = null,
|
||||
|
||||
// Valid values for the Attributes field
|
||||
pub const Flags = packed struct(ULONG) {
|
||||
Reserved0: u1 = 0,
|
||||
INHERIT: bool = false,
|
||||
Reserved2: u2 = 0,
|
||||
PERMANENT: bool = false,
|
||||
EXCLUSIVE: bool = false,
|
||||
/// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
|
||||
CASE_INSENSITIVE: bool = true,
|
||||
OPENIF: bool = false,
|
||||
OPENLINK: bool = false,
|
||||
KERNEL_HANDLE: bool = false,
|
||||
FORCE_ACCESS_CHECK: bool = false,
|
||||
IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
|
||||
DONT_REPARSE: bool = false,
|
||||
Reserved13: u19 = 0,
|
||||
|
||||
pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
|
||||
.INHERIT = true,
|
||||
.PERMANENT = true,
|
||||
.EXCLUSIVE = true,
|
||||
.CASE_INSENSITIVE = true,
|
||||
.OPENIF = true,
|
||||
.OPENLINK = true,
|
||||
.KERNEL_HANDLE = true,
|
||||
.FORCE_ACCESS_CHECK = true,
|
||||
.IGNORE_IMPERSONATED_DEVICEMAP = true,
|
||||
.DONT_REPARSE = true,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pub const INFORMATION_CLASS = enum(c_int) {
|
||||
Basic = 0,
|
||||
Name = 1,
|
||||
Type = 2,
|
||||
Types = 3,
|
||||
HandleFlag = 4,
|
||||
Session = 5,
|
||||
_,
|
||||
|
||||
pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len;
|
||||
};
|
||||
|
||||
pub const NAME_INFORMATION = extern struct {
|
||||
Name: UNICODE_STRING,
|
||||
};
|
||||
};
|
||||
|
||||
pub const FILE = struct {
|
||||
// ref: km/ntddk.h
|
||||
|
||||
@@ -73,6 +133,94 @@ pub const FILE = struct {
|
||||
|
||||
// ref: km/ntifs.h
|
||||
|
||||
pub const NAME_FLAGS = packed struct(UCHAR) {
|
||||
NTFS: bool = false,
|
||||
DOS: bool = false,
|
||||
Reserved2: u5 = 0,
|
||||
UNSPECIFIED: bool = false,
|
||||
};
|
||||
|
||||
pub const NOTIFY = struct {
|
||||
pub const CHANGE = packed struct(ULONG) {
|
||||
FILE_NAME: bool = false,
|
||||
DIR_NAME: bool = false,
|
||||
ATTRIBUTES: bool = false,
|
||||
SIZE: bool = false,
|
||||
LAST_WRITE: bool = false,
|
||||
LAST_ACCESS: bool = false,
|
||||
CREATION: bool = false,
|
||||
EA: bool = false,
|
||||
SECURITY: bool = false,
|
||||
STREAM_NAME: bool = false,
|
||||
STREAM_SIZE: bool = false,
|
||||
STREAM_WRITE: bool = false,
|
||||
Reserved12: u20 = 0,
|
||||
};
|
||||
|
||||
pub const INFORMATION = extern struct {
|
||||
NextEntryOffset: ULONG,
|
||||
Action: ULONG,
|
||||
FileNameLength: ULONG,
|
||||
FileName: [0]WCHAR,
|
||||
|
||||
pub fn fileName(info: *INFORMATION) []WCHAR {
|
||||
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
|
||||
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
|
||||
}
|
||||
};
|
||||
|
||||
pub const EXTENDED_INFORMATION = extern struct {
|
||||
NextEntryOffset: ULONG,
|
||||
Action: ULONG,
|
||||
CreationTime: LARGE_INTEGER,
|
||||
LastModificationTime: LARGE_INTEGER,
|
||||
LastChangeTime: LARGE_INTEGER,
|
||||
LastAccessTime: LARGE_INTEGER,
|
||||
AllocatedLength: LARGE_INTEGER,
|
||||
FileSize: LARGE_INTEGER,
|
||||
FileAttributes: ATTRIBUTE,
|
||||
u: extern union {
|
||||
ReparsePointTag: ULONG,
|
||||
EaSize: ULONG,
|
||||
},
|
||||
FileId: LARGE_INTEGER,
|
||||
ParentFileId: LARGE_INTEGER,
|
||||
FileNameLength: ULONG,
|
||||
FileName: [0]WCHAR,
|
||||
|
||||
pub fn fileName(info: *INFORMATION) []WCHAR {
|
||||
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
|
||||
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
|
||||
}
|
||||
};
|
||||
|
||||
pub const FULL_INFORMATION = extern struct {
|
||||
NextEntryOffset: ULONG,
|
||||
Action: ULONG,
|
||||
CreationTime: LARGE_INTEGER,
|
||||
LastModificationTime: LARGE_INTEGER,
|
||||
LastChangeTime: LARGE_INTEGER,
|
||||
LastAccessTime: LARGE_INTEGER,
|
||||
AllocatedLength: LARGE_INTEGER,
|
||||
FileSize: LARGE_INTEGER,
|
||||
FileAttributes: ATTRIBUTE,
|
||||
u: extern union {
|
||||
ReparsePointTag: ULONG,
|
||||
EaSize: ULONG,
|
||||
},
|
||||
FileId: LARGE_INTEGER,
|
||||
ParentFileId: LARGE_INTEGER,
|
||||
FileNameLength: ULONG,
|
||||
FileNameFlags: NAME_FLAGS,
|
||||
FileName: [0]WCHAR,
|
||||
|
||||
pub fn fileName(info: *INFORMATION) []WCHAR {
|
||||
const ptr: [*]WCHAR = @ptrCast(&info.FileName);
|
||||
return ptr[0..@divExact(info.FileNameLength, @sizeOf(WCHAR))];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const PIPE = struct {
|
||||
/// Define the `NamedPipeType` flags for `NtCreateNamedPipeFile`
|
||||
pub const TYPE = packed struct(ULONG) {
|
||||
@@ -357,6 +505,7 @@ pub const FILE = struct {
|
||||
IdAllExtdBothDirectory = 81,
|
||||
StreamReservation = 82,
|
||||
MupProvider = 83,
|
||||
_,
|
||||
|
||||
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
|
||||
};
|
||||
@@ -647,6 +796,19 @@ pub const FILE = struct {
|
||||
Mode: MODE,
|
||||
};
|
||||
};
|
||||
|
||||
// ref: km/
|
||||
};
|
||||
|
||||
pub const DIRECTORY = struct {
|
||||
pub const NOTIFY_INFORMATION_CLASS = enum(c_int) {
|
||||
Notify = 1,
|
||||
NotifyExtended = 2,
|
||||
NotifyFull = 3,
|
||||
_,
|
||||
|
||||
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
|
||||
};
|
||||
};
|
||||
|
||||
pub const CONSOLE = struct {
|
||||
@@ -880,141 +1042,229 @@ pub const CONSOLE = struct {
|
||||
|
||||
// ref: km/ntddk.h
|
||||
|
||||
pub const PROCESSINFOCLASS = enum(c_int) {
|
||||
BasicInformation = 0,
|
||||
QuotaLimits = 1,
|
||||
IoCounters = 2,
|
||||
VmCounters = 3,
|
||||
Times = 4,
|
||||
BasePriority = 5,
|
||||
RaisePriority = 6,
|
||||
DebugPort = 7,
|
||||
ExceptionPort = 8,
|
||||
AccessToken = 9,
|
||||
LdtInformation = 10,
|
||||
LdtSize = 11,
|
||||
DefaultHardErrorMode = 12,
|
||||
IoPortHandlers = 13,
|
||||
PooledUsageAndLimits = 14,
|
||||
WorkingSetWatch = 15,
|
||||
UserModeIOPL = 16,
|
||||
EnableAlignmentFaultFixup = 17,
|
||||
PriorityClass = 18,
|
||||
Wx86Information = 19,
|
||||
HandleCount = 20,
|
||||
AffinityMask = 21,
|
||||
PriorityBoost = 22,
|
||||
DeviceMap = 23,
|
||||
SessionInformation = 24,
|
||||
ForegroundInformation = 25,
|
||||
Wow64Information = 26,
|
||||
ImageFileName = 27,
|
||||
LUIDDeviceMapsEnabled = 28,
|
||||
BreakOnTermination = 29,
|
||||
DebugObjectHandle = 30,
|
||||
DebugFlags = 31,
|
||||
HandleTracing = 32,
|
||||
IoPriority = 33,
|
||||
ExecuteFlags = 34,
|
||||
TlsInformation = 35,
|
||||
Cookie = 36,
|
||||
ImageInformation = 37,
|
||||
CycleTime = 38,
|
||||
PagePriority = 39,
|
||||
InstrumentationCallback = 40,
|
||||
ThreadStackAllocation = 41,
|
||||
WorkingSetWatchEx = 42,
|
||||
ImageFileNameWin32 = 43,
|
||||
ImageFileMapping = 44,
|
||||
AffinityUpdateMode = 45,
|
||||
MemoryAllocationMode = 46,
|
||||
GroupInformation = 47,
|
||||
TokenVirtualizationEnabled = 48,
|
||||
OwnerInformation = 49,
|
||||
WindowInformation = 50,
|
||||
HandleInformation = 51,
|
||||
MitigationPolicy = 52,
|
||||
DynamicFunctionTableInformation = 53,
|
||||
HandleCheckingMode = 54,
|
||||
KeepAliveCount = 55,
|
||||
RevokeFileHandles = 56,
|
||||
WorkingSetControl = 57,
|
||||
HandleTable = 58,
|
||||
CheckStackExtentsMode = 59,
|
||||
CommandLineInformation = 60,
|
||||
ProtectionInformation = 61,
|
||||
MemoryExhaustion = 62,
|
||||
FaultInformation = 63,
|
||||
TelemetryIdInformation = 64,
|
||||
CommitReleaseInformation = 65,
|
||||
Reserved1Information = 66,
|
||||
Reserved2Information = 67,
|
||||
SubsystemProcess = 68,
|
||||
InPrivate = 70,
|
||||
RaiseUMExceptionOnInvalidHandleClose = 71,
|
||||
SubsystemInformation = 75,
|
||||
Win32kSyscallFilterInformation = 79,
|
||||
EnergyTrackingState = 82,
|
||||
NetworkIoCounters = 114,
|
||||
_,
|
||||
pub const SYSTEM = struct {
|
||||
pub const INFORMATION_CLASS = enum(c_int) {
|
||||
Basic = 0,
|
||||
Performance = 2,
|
||||
TimeOfDay = 3,
|
||||
Process = 5,
|
||||
ProcessorPerformance = 8,
|
||||
Interrupt = 23,
|
||||
Exception = 33,
|
||||
RegistryQuota = 37,
|
||||
Lookaside = 45,
|
||||
CodeIntegrity = 103,
|
||||
Policy = 134,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
|
||||
pub const BASIC_INFORMATION = extern struct {
|
||||
Reserved: ULONG,
|
||||
TimerResolution: ULONG,
|
||||
PageSize: ULONG,
|
||||
NumberOfPhysicalPages: ULONG,
|
||||
LowestPhysicalPageNumber: ULONG,
|
||||
HighestPhysicalPageNumber: ULONG,
|
||||
AllocationGranularity: ULONG,
|
||||
MinimumUserModeAddress: ULONG_PTR,
|
||||
MaximumUserModeAddress: ULONG_PTR,
|
||||
ActiveProcessorsAffinityMask: KAFFINITY,
|
||||
NumberOfProcessors: UCHAR,
|
||||
};
|
||||
};
|
||||
|
||||
pub const THREADINFOCLASS = enum(c_int) {
|
||||
BasicInformation = 0,
|
||||
Times = 1,
|
||||
Priority = 2,
|
||||
BasePriority = 3,
|
||||
AffinityMask = 4,
|
||||
ImpersonationToken = 5,
|
||||
DescriptorTableEntry = 6,
|
||||
EnableAlignmentFaultFixup = 7,
|
||||
EventPair_Reusable = 8,
|
||||
QuerySetWin32StartAddress = 9,
|
||||
ZeroTlsCell = 10,
|
||||
PerformanceCount = 11,
|
||||
AmILastThread = 12,
|
||||
IdealProcessor = 13,
|
||||
PriorityBoost = 14,
|
||||
SetTlsArrayAddress = 15,
|
||||
IsIoPending = 16,
|
||||
// Windows 2000+ from here
|
||||
HideFromDebugger = 17,
|
||||
// Windows XP+ from here
|
||||
BreakOnTermination = 18,
|
||||
SwitchLegacyState = 19,
|
||||
IsTerminated = 20,
|
||||
// Windows Vista+ from here
|
||||
LastSystemCall = 21,
|
||||
IoPriority = 22,
|
||||
CycleTime = 23,
|
||||
PagePriority = 24,
|
||||
ActualBasePriority = 25,
|
||||
TebInformation = 26,
|
||||
CSwitchMon = 27,
|
||||
// Windows 7+ from here
|
||||
CSwitchPmu = 28,
|
||||
Wow64Context = 29,
|
||||
GroupInformation = 30,
|
||||
UmsInformation = 31,
|
||||
CounterProfiling = 32,
|
||||
IdealProcessorEx = 33,
|
||||
// Windows 8+ from here
|
||||
CpuAccountingInformation = 34,
|
||||
// Windows 8.1+ from here
|
||||
SuspendCount = 35,
|
||||
// Windows 10+ from here
|
||||
HeterogeneousCpuPolicy = 36,
|
||||
ContainerId = 37,
|
||||
NameInformation = 38,
|
||||
SelectedCpuSets = 39,
|
||||
SystemThreadInformation = 40,
|
||||
ActualGroupAffinity = 41,
|
||||
DynamicCodePolicyInfo = 42,
|
||||
SubsystemInformation = 45,
|
||||
pub const PROCESS = struct {
|
||||
pub const INFORMATION = extern struct {
|
||||
hProcess: HANDLE,
|
||||
hThread: HANDLE,
|
||||
dwProcessId: DWORD,
|
||||
dwThreadId: DWORD,
|
||||
};
|
||||
|
||||
pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
|
||||
pub const INFOCLASS = enum(c_int) {
|
||||
BasicInformation = 0,
|
||||
QuotaLimits = 1,
|
||||
IoCounters = 2,
|
||||
VmCounters = 3,
|
||||
Times = 4,
|
||||
BasePriority = 5,
|
||||
RaisePriority = 6,
|
||||
DebugPort = 7,
|
||||
ExceptionPort = 8,
|
||||
AccessToken = 9,
|
||||
LdtInformation = 10,
|
||||
LdtSize = 11,
|
||||
DefaultHardErrorMode = 12,
|
||||
IoPortHandlers = 13,
|
||||
PooledUsageAndLimits = 14,
|
||||
WorkingSetWatch = 15,
|
||||
UserModeIOPL = 16,
|
||||
EnableAlignmentFaultFixup = 17,
|
||||
PriorityClass = 18,
|
||||
Wx86Information = 19,
|
||||
HandleCount = 20,
|
||||
AffinityMask = 21,
|
||||
PriorityBoost = 22,
|
||||
DeviceMap = 23,
|
||||
SessionInformation = 24,
|
||||
ForegroundInformation = 25,
|
||||
Wow64Information = 26,
|
||||
ImageFileName = 27,
|
||||
LUIDDeviceMapsEnabled = 28,
|
||||
BreakOnTermination = 29,
|
||||
DebugObjectHandle = 30,
|
||||
DebugFlags = 31,
|
||||
HandleTracing = 32,
|
||||
IoPriority = 33,
|
||||
ExecuteFlags = 34,
|
||||
TlsInformation = 35,
|
||||
Cookie = 36,
|
||||
ImageInformation = 37,
|
||||
CycleTime = 38,
|
||||
PagePriority = 39,
|
||||
InstrumentationCallback = 40,
|
||||
ThreadStackAllocation = 41,
|
||||
WorkingSetWatchEx = 42,
|
||||
ImageFileNameWin32 = 43,
|
||||
ImageFileMapping = 44,
|
||||
AffinityUpdateMode = 45,
|
||||
MemoryAllocationMode = 46,
|
||||
GroupInformation = 47,
|
||||
TokenVirtualizationEnabled = 48,
|
||||
OwnerInformation = 49,
|
||||
WindowInformation = 50,
|
||||
HandleInformation = 51,
|
||||
MitigationPolicy = 52,
|
||||
DynamicFunctionTableInformation = 53,
|
||||
HandleCheckingMode = 54,
|
||||
KeepAliveCount = 55,
|
||||
RevokeFileHandles = 56,
|
||||
WorkingSetControl = 57,
|
||||
HandleTable = 58,
|
||||
CheckStackExtentsMode = 59,
|
||||
CommandLineInformation = 60,
|
||||
ProtectionInformation = 61,
|
||||
MemoryExhaustion = 62,
|
||||
FaultInformation = 63,
|
||||
TelemetryIdInformation = 64,
|
||||
CommitReleaseInformation = 65,
|
||||
Reserved1Information = 66,
|
||||
Reserved2Information = 67,
|
||||
SubsystemProcess = 68,
|
||||
InPrivate = 70,
|
||||
RaiseUMExceptionOnInvalidHandleClose = 71,
|
||||
SubsystemInformation = 75,
|
||||
Win32kSyscallFilterInformation = 79,
|
||||
EnergyTrackingState = 82,
|
||||
NetworkIoCounters = 114,
|
||||
_,
|
||||
|
||||
pub const Max: @typeInfo(@This()).@"enum".tag_type = 117;
|
||||
};
|
||||
|
||||
pub const BASIC_INFORMATION = extern struct {
|
||||
ExitStatus: NTSTATUS,
|
||||
PebBaseAddress: *PEB,
|
||||
AffinityMask: ULONG_PTR,
|
||||
BasePriority: KPRIORITY,
|
||||
UniqueProcessId: ULONG_PTR,
|
||||
InheritedFromUniqueProcessId: ULONG_PTR,
|
||||
};
|
||||
|
||||
pub const VM_COUNTERS = extern struct {
|
||||
PeakVirtualSize: SIZE_T,
|
||||
VirtualSize: SIZE_T,
|
||||
PageFaultCount: ULONG,
|
||||
PeakWorkingSetSize: SIZE_T,
|
||||
WorkingSetSize: SIZE_T,
|
||||
QuotaPeakPagedPoolUsage: SIZE_T,
|
||||
QuotaPagedPoolUsage: SIZE_T,
|
||||
QuotaPeakNonPagedPoolUsage: SIZE_T,
|
||||
QuotaNonPagedPoolUsage: SIZE_T,
|
||||
PagefileUsage: SIZE_T,
|
||||
PeakPagefileUsage: SIZE_T,
|
||||
};
|
||||
};
|
||||
|
||||
pub const THREAD = struct {
|
||||
pub const INFOCLASS = enum(c_int) {
|
||||
BasicInformation = 0,
|
||||
Times = 1,
|
||||
Priority = 2,
|
||||
BasePriority = 3,
|
||||
AffinityMask = 4,
|
||||
ImpersonationToken = 5,
|
||||
DescriptorTableEntry = 6,
|
||||
EnableAlignmentFaultFixup = 7,
|
||||
EventPair_Reusable = 8,
|
||||
QuerySetWin32StartAddress = 9,
|
||||
ZeroTlsCell = 10,
|
||||
PerformanceCount = 11,
|
||||
AmILastThread = 12,
|
||||
IdealProcessor = 13,
|
||||
PriorityBoost = 14,
|
||||
SetTlsArrayAddress = 15,
|
||||
IsIoPending = 16,
|
||||
// Windows 2000+ from here
|
||||
HideFromDebugger = 17,
|
||||
// Windows XP+ from here
|
||||
BreakOnTermination = 18,
|
||||
SwitchLegacyState = 19,
|
||||
IsTerminated = 20,
|
||||
// Windows Vista+ from here
|
||||
LastSystemCall = 21,
|
||||
IoPriority = 22,
|
||||
CycleTime = 23,
|
||||
PagePriority = 24,
|
||||
ActualBasePriority = 25,
|
||||
TebInformation = 26,
|
||||
CSwitchMon = 27,
|
||||
// Windows 7+ from here
|
||||
CSwitchPmu = 28,
|
||||
Wow64Context = 29,
|
||||
GroupInformation = 30,
|
||||
UmsInformation = 31,
|
||||
CounterProfiling = 32,
|
||||
IdealProcessorEx = 33,
|
||||
// Windows 8+ from here
|
||||
CpuAccountingInformation = 34,
|
||||
// Windows 8.1+ from here
|
||||
SuspendCount = 35,
|
||||
// Windows 10+ from here
|
||||
HeterogeneousCpuPolicy = 36,
|
||||
ContainerId = 37,
|
||||
NameInformation = 38,
|
||||
SelectedCpuSets = 39,
|
||||
SystemThreadInformation = 40,
|
||||
ActualGroupAffinity = 41,
|
||||
DynamicCodePolicyInfo = 42,
|
||||
SubsystemInformation = 45,
|
||||
_,
|
||||
|
||||
pub const Max: @typeInfo(@This()).@"enum".tag_type = 60;
|
||||
};
|
||||
|
||||
pub const BASIC_INFORMATION = extern struct {
|
||||
ExitStatus: NTSTATUS,
|
||||
TebBaseAddress: PVOID,
|
||||
ClientId: CLIENT_ID,
|
||||
AffinityMask: KAFFINITY,
|
||||
Priority: KPRIORITY,
|
||||
BasePriority: KPRIORITY,
|
||||
};
|
||||
};
|
||||
|
||||
pub const MEMORY = struct {
|
||||
pub const BASIC_INFORMATION = extern struct {
|
||||
BaseAddress: PVOID,
|
||||
AllocationBase: PVOID,
|
||||
AllocationProtect: DWORD,
|
||||
PartitionId: WORD,
|
||||
RegionSize: SIZE_T,
|
||||
State: DWORD,
|
||||
Protect: DWORD,
|
||||
Type: DWORD,
|
||||
};
|
||||
};
|
||||
|
||||
// ref: km/ntifs.h
|
||||
@@ -2560,48 +2810,6 @@ pub fn GetProcessHeap() ?*HEAP {
|
||||
return peb().ProcessHeap;
|
||||
}
|
||||
|
||||
// ref: um/winternl.h
|
||||
|
||||
pub const OBJECT_ATTRIBUTES = extern struct {
|
||||
Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
RootDirectory: ?HANDLE = null,
|
||||
ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty),
|
||||
Attributes: ATTRIBUTES = .{},
|
||||
SecurityDescriptor: ?*anyopaque = null,
|
||||
SecurityQualityOfService: ?*anyopaque = null,
|
||||
|
||||
// Valid values for the Attributes field
|
||||
pub const ATTRIBUTES = packed struct(ULONG) {
|
||||
Reserved0: u1 = 0,
|
||||
INHERIT: bool = false,
|
||||
Reserved2: u2 = 0,
|
||||
PERMANENT: bool = false,
|
||||
EXCLUSIVE: bool = false,
|
||||
/// If name-lookup code should ignore the case of the ObjectName member rather than performing an exact-match search.
|
||||
CASE_INSENSITIVE: bool = true,
|
||||
OPENIF: bool = false,
|
||||
OPENLINK: bool = false,
|
||||
KERNEL_HANDLE: bool = false,
|
||||
FORCE_ACCESS_CHECK: bool = false,
|
||||
IGNORE_IMPERSONATED_DEVICEMAP: bool = false,
|
||||
DONT_REPARSE: bool = false,
|
||||
Reserved13: u19 = 0,
|
||||
|
||||
pub const VALID_ATTRIBUTES: ATTRIBUTES = .{
|
||||
.INHERIT = true,
|
||||
.PERMANENT = true,
|
||||
.EXCLUSIVE = true,
|
||||
.CASE_INSENSITIVE = true,
|
||||
.OPENIF = true,
|
||||
.OPENLINK = true,
|
||||
.KERNEL_HANDLE = true,
|
||||
.FORCE_ACCESS_CHECK = true,
|
||||
.IGNORE_IMPERSONATED_DEVICEMAP = true,
|
||||
.DONT_REPARSE = true,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// ref none
|
||||
|
||||
pub fn GetCurrentProcess() HANDLE {
|
||||
@@ -2623,297 +2831,13 @@ pub fn GetCurrentThreadId() DWORD {
|
||||
}
|
||||
|
||||
pub fn GetLastError() Win32Error {
|
||||
return @enumFromInt(teb().LastErrorValue);
|
||||
}
|
||||
/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
|
||||
/// It implements similar behavior to `DeviceIoControl` and is meant to serve
|
||||
/// as a direct substitute for that call.
|
||||
/// TODO work out if we need to expose other arguments to the underlying syscalls.
|
||||
pub fn DeviceIoControl(
|
||||
device: HANDLE,
|
||||
io_control_code: CTL_CODE,
|
||||
opts: struct {
|
||||
event: ?HANDLE = null,
|
||||
apc_routine: ?*const IO_APC_ROUTINE = null,
|
||||
apc_context: ?*anyopaque = null,
|
||||
io_status_block: ?*IO_STATUS_BLOCK = null,
|
||||
in: []const u8 = &.{},
|
||||
out: []u8 = &.{},
|
||||
},
|
||||
) NTSTATUS {
|
||||
var io_status_block: IO_STATUS_BLOCK = undefined;
|
||||
return switch (io_control_code.DeviceType) {
|
||||
.FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(
|
||||
device,
|
||||
opts.event,
|
||||
opts.apc_routine,
|
||||
opts.apc_context,
|
||||
opts.io_status_block orelse &io_status_block,
|
||||
io_control_code,
|
||||
if (opts.in.len > 0) opts.in.ptr else null,
|
||||
@intCast(opts.in.len),
|
||||
if (opts.out.len > 0) opts.out.ptr else null,
|
||||
@intCast(opts.out.len),
|
||||
),
|
||||
else => ntdll.NtDeviceIoControlFile(
|
||||
device,
|
||||
opts.event,
|
||||
opts.apc_routine,
|
||||
opts.apc_context,
|
||||
opts.io_status_block orelse &io_status_block,
|
||||
io_control_code,
|
||||
if (opts.in.len > 0) opts.in.ptr else null,
|
||||
@intCast(opts.in.len),
|
||||
if (opts.out.len > 0) opts.out.ptr else null,
|
||||
@intCast(opts.out.len),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
|
||||
var bytes: DWORD = undefined;
|
||||
if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) {
|
||||
switch (GetLastError()) {
|
||||
.IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
pub const CreateIoCompletionPortError = error{Unexpected};
|
||||
|
||||
pub fn CreateIoCompletionPort(
|
||||
file_handle: HANDLE,
|
||||
existing_completion_port: ?HANDLE,
|
||||
completion_key: usize,
|
||||
concurrent_thread_count: DWORD,
|
||||
) CreateIoCompletionPortError!HANDLE {
|
||||
const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
|
||||
switch (GetLastError()) {
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
};
|
||||
return handle;
|
||||
}
|
||||
|
||||
pub const PostQueuedCompletionStatusError = error{Unexpected};
|
||||
|
||||
pub fn PostQueuedCompletionStatus(
|
||||
completion_port: HANDLE,
|
||||
bytes_transferred_count: DWORD,
|
||||
completion_key: usize,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
) PostQueuedCompletionStatusError!void {
|
||||
if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
|
||||
switch (GetLastError()) {
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const GetQueuedCompletionStatusResult = enum {
|
||||
Normal,
|
||||
Aborted,
|
||||
Canceled,
|
||||
EOF,
|
||||
Timeout,
|
||||
};
|
||||
|
||||
pub fn GetQueuedCompletionStatus(
|
||||
completion_port: HANDLE,
|
||||
bytes_transferred_count: *DWORD,
|
||||
lpCompletionKey: *usize,
|
||||
lpOverlapped: *?*OVERLAPPED,
|
||||
dwMilliseconds: DWORD,
|
||||
) GetQueuedCompletionStatusResult {
|
||||
if (kernel32.GetQueuedCompletionStatus(
|
||||
completion_port,
|
||||
bytes_transferred_count,
|
||||
lpCompletionKey,
|
||||
lpOverlapped,
|
||||
dwMilliseconds,
|
||||
) == FALSE) {
|
||||
switch (GetLastError()) {
|
||||
.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
|
||||
.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Canceled,
|
||||
.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
|
||||
.WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout,
|
||||
else => |err| {
|
||||
if (std.debug.runtime_safety) {
|
||||
@setEvalBranchQuota(2500);
|
||||
std.debug.panic("unexpected error: {}\n", .{err});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
return GetQueuedCompletionStatusResult.Normal;
|
||||
}
|
||||
|
||||
pub const GetQueuedCompletionStatusError = error{
|
||||
Aborted,
|
||||
Canceled,
|
||||
EOF,
|
||||
Timeout,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn GetQueuedCompletionStatusEx(
|
||||
completion_port: HANDLE,
|
||||
completion_port_entries: []OVERLAPPED_ENTRY,
|
||||
timeout_ms: ?DWORD,
|
||||
alertable: bool,
|
||||
) GetQueuedCompletionStatusError!u32 {
|
||||
var num_entries_removed: u32 = 0;
|
||||
|
||||
const success = kernel32.GetQueuedCompletionStatusEx(
|
||||
completion_port,
|
||||
completion_port_entries.ptr,
|
||||
@as(ULONG, @intCast(completion_port_entries.len)),
|
||||
&num_entries_removed,
|
||||
timeout_ms orelse INFINITE,
|
||||
@intFromBool(alertable),
|
||||
);
|
||||
|
||||
if (success == FALSE) {
|
||||
return switch (GetLastError()) {
|
||||
.ABANDONED_WAIT_0 => error.Aborted,
|
||||
.OPERATION_ABORTED => error.Canceled,
|
||||
.HANDLE_EOF => error.EOF,
|
||||
.WAIT_TIMEOUT => error.Timeout,
|
||||
else => |err| unexpectedError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return num_entries_removed;
|
||||
return teb().LastErrorValue;
|
||||
}
|
||||
|
||||
pub fn CloseHandle(hObject: HANDLE) void {
|
||||
assert(ntdll.NtClose(hObject) == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
|
||||
return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
|
||||
}
|
||||
|
||||
pub fn sendmsg(
|
||||
s: ws2_32.SOCKET,
|
||||
msg: *ws2_32.WSAMSG_const,
|
||||
flags: u32,
|
||||
) i32 {
|
||||
var bytes_send: DWORD = undefined;
|
||||
if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
|
||||
return ws2_32.SOCKET_ERROR;
|
||||
} else {
|
||||
return @as(i32, @as(u31, @intCast(bytes_send)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
|
||||
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
|
||||
var bytes_send: DWORD = undefined;
|
||||
if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) {
|
||||
return ws2_32.SOCKET_ERROR;
|
||||
} else {
|
||||
return @as(i32, @as(u31, @intCast(bytes_send)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
|
||||
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
|
||||
var bytes_received: DWORD = undefined;
|
||||
var flags_inout = flags;
|
||||
if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) {
|
||||
return ws2_32.SOCKET_ERROR;
|
||||
} else {
|
||||
return @as(i32, @as(u31, @intCast(bytes_received)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
|
||||
return ws2_32.WSAPoll(fds, n, timeout);
|
||||
}
|
||||
|
||||
pub fn WSAIoctl(
|
||||
s: ws2_32.SOCKET,
|
||||
dwIoControlCode: DWORD,
|
||||
inBuffer: ?[]const u8,
|
||||
outBuffer: []u8,
|
||||
overlapped: ?*OVERLAPPED,
|
||||
completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
|
||||
) !DWORD {
|
||||
var bytes: DWORD = undefined;
|
||||
switch (ws2_32.WSAIoctl(
|
||||
s,
|
||||
dwIoControlCode,
|
||||
if (inBuffer) |i| i.ptr else null,
|
||||
if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0,
|
||||
outBuffer.ptr,
|
||||
@as(DWORD, @intCast(outBuffer.len)),
|
||||
&bytes,
|
||||
overlapped,
|
||||
completionRoutine,
|
||||
)) {
|
||||
0 => {},
|
||||
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
|
||||
else => |err| return unexpectedWSAError(err),
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
const GetModuleFileNameError = error{Unexpected};
|
||||
|
||||
pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
|
||||
const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
|
||||
if (rc == 0) {
|
||||
switch (GetLastError()) {
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
return buf_ptr[0..rc :0];
|
||||
}
|
||||
|
||||
pub const NtAllocateVirtualMemoryError = error{
|
||||
AccessDenied,
|
||||
InvalidParameter,
|
||||
NoMemory,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void {
|
||||
return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) {
|
||||
.SUCCESS => return,
|
||||
.ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied,
|
||||
.INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter,
|
||||
.NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory,
|
||||
else => |st| unexpectedStatus(st),
|
||||
};
|
||||
}
|
||||
|
||||
pub const NtFreeVirtualMemoryError = error{
|
||||
AccessDenied,
|
||||
InvalidParameter,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void {
|
||||
// TODO: If the return value is .INVALID_PAGE_PROTECTION, call RtlFlushSecureMemoryCache and try again.
|
||||
return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) {
|
||||
.SUCCESS => return,
|
||||
.ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied,
|
||||
.INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter,
|
||||
else => NtFreeVirtualMemoryError.Unexpected,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
|
||||
const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
|
||||
if (success == FALSE) {
|
||||
return switch (GetLastError()) {
|
||||
else => |err| unexpectedError(err),
|
||||
};
|
||||
switch (ntdll.NtClose(hObject)) {
|
||||
.SUCCESS => {},
|
||||
else => |status| unexpectedStatus(status) catch {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2963,114 +2887,75 @@ pub const CreateProcessFlags = packed struct(u32) {
|
||||
create_ignore_system_default: bool = false,
|
||||
};
|
||||
|
||||
pub const LoadLibraryError = error{
|
||||
FileNotFound,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
|
||||
return kernel32.LoadLibraryW(lpLibFileName) orelse {
|
||||
switch (GetLastError()) {
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.MOD_NOT_FOUND => return error.FileNotFound,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const LoadLibraryFlags = enum(DWORD) {
|
||||
none = 0,
|
||||
dont_resolve_dll_references = 0x00000001,
|
||||
load_ignore_code_authz_level = 0x00000010,
|
||||
load_library_as_datafile = 0x00000002,
|
||||
load_library_as_datafile_exclusive = 0x00000040,
|
||||
load_library_as_image_resource = 0x00000020,
|
||||
load_library_search_application_dir = 0x00000200,
|
||||
load_library_search_default_dirs = 0x00001000,
|
||||
load_library_search_dll_load_dir = 0x00000100,
|
||||
load_library_search_system32 = 0x00000800,
|
||||
load_library_search_user_dirs = 0x00000400,
|
||||
load_with_altered_search_path = 0x00000008,
|
||||
load_library_require_signed_target = 0x00000080,
|
||||
load_library_safe_current_dirs = 0x00002000,
|
||||
};
|
||||
|
||||
pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE {
|
||||
return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse {
|
||||
switch (GetLastError()) {
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.MOD_NOT_FOUND => return error.FileNotFound,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn FreeLibrary(hModule: HMODULE) void {
|
||||
assert(kernel32.FreeLibrary(hModule) != 0);
|
||||
}
|
||||
|
||||
pub fn QueryPerformanceFrequency() u64 {
|
||||
// "On systems that run Windows XP or later, the function will always succeed"
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
|
||||
var result: LARGE_INTEGER = undefined;
|
||||
assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0);
|
||||
// The kernel treats this integer as unsigned.
|
||||
return @as(u64, @bitCast(result));
|
||||
}
|
||||
|
||||
pub fn QueryPerformanceCounter() u64 {
|
||||
// "On systems that run Windows XP or later, the function will always succeed"
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
|
||||
var result: LARGE_INTEGER = undefined;
|
||||
assert(ntdll.RtlQueryPerformanceCounter(&result) != 0);
|
||||
// The kernel treats this integer as unsigned.
|
||||
return @as(u64, @bitCast(result));
|
||||
}
|
||||
|
||||
/// This is a workaround for the C backend until zig has the ability to put
|
||||
/// C code in inline assembly.
|
||||
extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque;
|
||||
extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque;
|
||||
extern fn zig_x86_windows_teb() callconv(.c) *anyopaque;
|
||||
extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque;
|
||||
|
||||
pub fn teb() *TEB {
|
||||
return switch (native_arch) {
|
||||
.thumb => if (builtin.zig_backend == .stage2_c)
|
||||
@ptrCast(@alignCast(zig_thumb_windows_teb()))
|
||||
else
|
||||
asm (
|
||||
\\ mrc p15, 0, %[ptr], c13, c0, 2
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
.aarch64 => if (builtin.zig_backend == .stage2_c)
|
||||
@ptrCast(@alignCast(zig_aarch64_windows_teb()))
|
||||
else
|
||||
asm (
|
||||
\\ mov %[ptr], x18
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
.x86 => if (builtin.zig_backend == .stage2_c)
|
||||
@ptrCast(@alignCast(zig_x86_windows_teb()))
|
||||
else
|
||||
asm (
|
||||
if (builtin.zig_backend == .stage2_c) return @ptrCast(@alignCast(struct {
|
||||
/// This is a workaround for the C backend until zig has the ability to put
|
||||
/// C code in inline assembly.
|
||||
extern fn zig_windows_teb() callconv(.c) *anyopaque;
|
||||
}.zig_windows_teb()));
|
||||
switch (native_arch) {
|
||||
.thumb => return asm (
|
||||
\\ mrc p15, 0, %[ptr], c13, c0, 2
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
.aarch64 => return asm (
|
||||
\\ mov %[ptr], x18
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
.x86 => {
|
||||
comptime assert(
|
||||
@offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x18,
|
||||
);
|
||||
return asm (
|
||||
\\ movl %%fs:0x18, %[ptr]
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
.x86_64 => if (builtin.zig_backend == .stage2_c)
|
||||
@ptrCast(@alignCast(zig_x86_64_windows_teb()))
|
||||
else
|
||||
asm (
|
||||
);
|
||||
},
|
||||
.x86_64 => {
|
||||
comptime assert(
|
||||
@offsetOf(TEB, "NtTib") + @offsetOf(@FieldType(TEB, "NtTib"), "Self") == 0x30,
|
||||
);
|
||||
return asm (
|
||||
\\ movq %%gs:0x30, %[ptr]
|
||||
: [ptr] "=r" (-> *TEB),
|
||||
),
|
||||
);
|
||||
},
|
||||
else => @compileError("unsupported arch"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peb() *PEB {
|
||||
if (builtin.zig_backend == .stage2_c) switch (native_arch) {
|
||||
.x86, .x86_64 => return @ptrCast(@alignCast(struct {
|
||||
/// This is a workaround for the C backend until zig has the ability to put
|
||||
/// C code in inline assembly.
|
||||
extern fn zig_windows_peb() callconv(.c) *anyopaque;
|
||||
}.zig_windows_peb())),
|
||||
else => {},
|
||||
} else switch (native_arch) {
|
||||
.aarch64 => {
|
||||
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
|
||||
return asm (
|
||||
\\ ldr %[ptr], [x18, #0x60]
|
||||
: [ptr] "=r" (-> *PEB),
|
||||
);
|
||||
},
|
||||
.x86 => {
|
||||
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
|
||||
return asm (
|
||||
\\ movl %%fs:0x30, %[ptr]
|
||||
: [ptr] "=r" (-> *PEB),
|
||||
);
|
||||
},
|
||||
.x86_64 => {
|
||||
comptime assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
|
||||
return asm (
|
||||
\\ movq %%gs:0x60, %[ptr]
|
||||
: [ptr] "=r" (-> *PEB),
|
||||
);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return teb().ProcessEnvironmentBlock;
|
||||
}
|
||||
|
||||
@@ -3089,20 +2974,6 @@ pub fn toSysTime(ns: Io.Timestamp) i64 {
|
||||
return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100);
|
||||
}
|
||||
|
||||
pub fn fileTimeToNanoSeconds(ft: FILETIME) Io.Timestamp {
|
||||
const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
return fromSysTime(hns);
|
||||
}
|
||||
|
||||
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
|
||||
pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME {
|
||||
const adjusted: u64 = @bitCast(toSysTime(ns));
|
||||
return .{
|
||||
.dwHighDateTime = @as(u32, @truncate(adjusted >> 32)),
|
||||
.dwLowDateTime = @as(u32, @truncate(adjusted)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
|
||||
/// redundant copy of the uppercase data.
|
||||
pub inline fn toUpperWtf16(c: u16) u16 {
|
||||
@@ -3126,7 +2997,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
|
||||
// endianness for the uppercasing
|
||||
const a_c_native = std.mem.littleToNative(u16, a_c);
|
||||
const b_c_native = std.mem.littleToNative(u16, b_c);
|
||||
if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
|
||||
if (a_c != b_c and toUpperWtf16(a_c_native) != toUpperWtf16(b_c_native)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3134,19 +3005,7 @@ pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
|
||||
}
|
||||
// Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
|
||||
// redundant copy of the uppercase data.
|
||||
const a_bytes = @as(u16, @intCast(a.len * 2));
|
||||
const a_string: UNICODE_STRING = .{
|
||||
.Length = a_bytes,
|
||||
.MaximumLength = a_bytes,
|
||||
.Buffer = @constCast(a.ptr),
|
||||
};
|
||||
const b_bytes = @as(u16, @intCast(b.len * 2));
|
||||
const b_string: UNICODE_STRING = .{
|
||||
.Length = b_bytes,
|
||||
.MaximumLength = b_bytes,
|
||||
.Buffer = @constCast(b.ptr),
|
||||
};
|
||||
return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
|
||||
return ntdll.RtlEqualUnicodeString(&.init(a), &.init(b), TRUE) == TRUE;
|
||||
}
|
||||
|
||||
/// Compares two WTF-8 strings using the equivalent functionality of
|
||||
@@ -3464,13 +3323,10 @@ pub const LONG = i32;
|
||||
pub const ULONG64 = u64;
|
||||
pub const ULONGLONG = u64;
|
||||
pub const LONGLONG = i64;
|
||||
pub const HLOCAL = HANDLE;
|
||||
pub const LANGID = c_ushort;
|
||||
pub const COLORREF = DWORD;
|
||||
|
||||
pub const WPARAM = usize;
|
||||
pub const LPARAM = LONG_PTR;
|
||||
pub const LRESULT = LONG_PTR;
|
||||
|
||||
pub const va_list = *opaque {};
|
||||
|
||||
@@ -3480,6 +3336,40 @@ pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWS
|
||||
pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
|
||||
pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
|
||||
|
||||
fn STRING(comptime C: type) type {
|
||||
return extern struct {
|
||||
Length: USHORT,
|
||||
MaximumLength: USHORT,
|
||||
Buffer: ?[*]C,
|
||||
|
||||
pub const empty: @This() = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
|
||||
|
||||
pub fn init(string: []const C) @This() {
|
||||
const len: USHORT = @intCast(@sizeOf(C) * string.len);
|
||||
return .{
|
||||
.Length = len,
|
||||
.MaximumLength = len,
|
||||
.Buffer = @constCast(string.ptr),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isEmpty(string: *const @This()) bool {
|
||||
return string.Length == 0;
|
||||
}
|
||||
|
||||
pub fn slice(string: *const @This()) []C {
|
||||
return if (string.isEmpty()) &.{} else string.Buffer.?[0..@divExact(string.Length, @sizeOf(C))];
|
||||
}
|
||||
|
||||
pub fn sliceZ(string: *const @This()) [:0]C {
|
||||
assert(string.Length + @sizeOf(C) <= string.MaximumLength);
|
||||
return string.Buffer.?[0..@divExact(string.Length, @sizeOf(C)) :0];
|
||||
}
|
||||
};
|
||||
}
|
||||
pub const ANSI_STRING = STRING(CHAR);
|
||||
pub const UNICODE_STRING = STRING(WCHAR);
|
||||
|
||||
pub const TRUE = 1;
|
||||
pub const FALSE = 0;
|
||||
|
||||
@@ -3509,111 +3399,14 @@ pub const OVERLAPPED = extern struct {
|
||||
hEvent: ?HANDLE,
|
||||
};
|
||||
|
||||
pub const OVERLAPPED_ENTRY = extern struct {
|
||||
lpCompletionKey: ULONG_PTR,
|
||||
lpOverlapped: *OVERLAPPED,
|
||||
Internal: ULONG_PTR,
|
||||
dwNumberOfBytesTransferred: DWORD,
|
||||
};
|
||||
|
||||
pub const MAX_PATH = 260;
|
||||
|
||||
pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) {
|
||||
FileBasicInfo = 0,
|
||||
FileStandardInfo = 1,
|
||||
FileNameInfo = 2,
|
||||
FileRenameInfo = 3,
|
||||
FileDispositionInfo = 4,
|
||||
FileAllocationInfo = 5,
|
||||
FileEndOfFileInfo = 6,
|
||||
FileStreamInfo = 7,
|
||||
FileCompressionInfo = 8,
|
||||
FileAttributeTagInfo = 9,
|
||||
FileIdBothDirectoryInfo = 10,
|
||||
FileIdBothDirectoryRestartInfo = 11,
|
||||
FileIoPriorityHintInfo = 12,
|
||||
FileRemoteProtocolInfo = 13,
|
||||
FileFullDirectoryInfo = 14,
|
||||
FileFullDirectoryRestartInfo = 15,
|
||||
FileStorageInfo = 16,
|
||||
FileAlignmentInfo = 17,
|
||||
FileIdInfo = 18,
|
||||
FileIdExtdDirectoryInfo = 19,
|
||||
FileIdExtdDirectoryRestartInfo = 20,
|
||||
};
|
||||
|
||||
pub const BY_HANDLE_FILE_INFORMATION = extern struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
dwVolumeSerialNumber: DWORD,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
nNumberOfLinks: DWORD,
|
||||
nFileIndexHigh: DWORD,
|
||||
nFileIndexLow: DWORD,
|
||||
};
|
||||
|
||||
pub const FILE_NAME_INFO = extern struct {
|
||||
FileNameLength: DWORD,
|
||||
FileName: [1]WCHAR,
|
||||
};
|
||||
|
||||
/// Return the normalized drive name. This is the default.
|
||||
pub const FILE_NAME_NORMALIZED = 0x0;
|
||||
|
||||
/// Return the opened file name (not normalized).
|
||||
pub const FILE_NAME_OPENED = 0x8;
|
||||
|
||||
/// Return the path with the drive letter. This is the default.
|
||||
pub const VOLUME_NAME_DOS = 0x0;
|
||||
|
||||
/// Return the path with a volume GUID path instead of the drive name.
|
||||
pub const VOLUME_NAME_GUID = 0x1;
|
||||
|
||||
/// Return the path with no drive information.
|
||||
pub const VOLUME_NAME_NONE = 0x4;
|
||||
|
||||
/// Return the path with the volume device path.
|
||||
pub const VOLUME_NAME_NT = 0x2;
|
||||
|
||||
pub const SECURITY_ATTRIBUTES = extern struct {
|
||||
nLength: DWORD,
|
||||
lpSecurityDescriptor: ?*anyopaque,
|
||||
bInheritHandle: BOOL,
|
||||
};
|
||||
|
||||
pub const PIPE_ACCESS_INBOUND = 0x00000001;
|
||||
pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
|
||||
pub const PIPE_ACCESS_DUPLEX = 0x00000003;
|
||||
|
||||
pub const PIPE_TYPE_BYTE = 0x00000000;
|
||||
pub const PIPE_TYPE_MESSAGE = 0x00000004;
|
||||
|
||||
pub const PIPE_READMODE_BYTE = 0x00000000;
|
||||
pub const PIPE_READMODE_MESSAGE = 0x00000002;
|
||||
|
||||
pub const PIPE_WAIT = 0x00000000;
|
||||
pub const PIPE_NOWAIT = 0x00000001;
|
||||
|
||||
pub const CREATE_ALWAYS = 2;
|
||||
pub const CREATE_NEW = 1;
|
||||
pub const OPEN_ALWAYS = 4;
|
||||
pub const OPEN_EXISTING = 3;
|
||||
pub const TRUNCATE_EXISTING = 5;
|
||||
|
||||
// flags for CreateEvent
|
||||
pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
|
||||
pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
|
||||
|
||||
pub const PROCESS_INFORMATION = extern struct {
|
||||
hProcess: HANDLE,
|
||||
hThread: HANDLE,
|
||||
dwProcessId: DWORD,
|
||||
dwThreadId: DWORD,
|
||||
};
|
||||
|
||||
pub const STARTUPINFOW = extern struct {
|
||||
cb: DWORD,
|
||||
lpReserved: ?LPWSTR,
|
||||
@@ -3650,50 +3443,7 @@ pub const STARTF_USESHOWWINDOW = 0x00000001;
|
||||
pub const STARTF_USESIZE = 0x00000002;
|
||||
pub const STARTF_USESTDHANDLES = 0x00000100;
|
||||
|
||||
pub const INFINITE = 4294967295;
|
||||
|
||||
pub const MAXIMUM_WAIT_OBJECTS = 64;
|
||||
|
||||
pub const WAIT_ABANDONED = 0x00000080;
|
||||
pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
|
||||
pub const WAIT_OBJECT_0 = 0x00000000;
|
||||
pub const WAIT_TIMEOUT = 0x00000102;
|
||||
pub const WAIT_FAILED = 0xFFFFFFFF;
|
||||
|
||||
pub const HANDLE_FLAG_INHERIT = 0x00000001;
|
||||
pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
|
||||
|
||||
pub const MOVEFILE_COPY_ALLOWED = 2;
|
||||
pub const MOVEFILE_CREATE_HARDLINK = 16;
|
||||
pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
|
||||
pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
|
||||
pub const MOVEFILE_REPLACE_EXISTING = 1;
|
||||
pub const MOVEFILE_WRITE_THROUGH = 8;
|
||||
|
||||
pub const FILE_BEGIN = 0;
|
||||
pub const FILE_CURRENT = 1;
|
||||
pub const FILE_END = 2;
|
||||
|
||||
pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD;
|
||||
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
|
||||
|
||||
pub const WIN32_FIND_DATAW = extern struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
dwReserved0: DWORD,
|
||||
dwReserved1: DWORD,
|
||||
cFileName: [260]u16,
|
||||
cAlternateFileName: [14]u16,
|
||||
};
|
||||
|
||||
pub const FILETIME = extern struct {
|
||||
dwLowDateTime: DWORD,
|
||||
dwHighDateTime: DWORD,
|
||||
};
|
||||
pub const THREAD_START_ROUTINE = fn (LPVOID) callconv(.winapi) DWORD;
|
||||
|
||||
pub const SYSTEM_INFO = extern struct {
|
||||
anon1: extern union {
|
||||
@@ -3716,7 +3466,6 @@ pub const SYSTEM_INFO = extern struct {
|
||||
|
||||
pub const HRESULT = c_long;
|
||||
|
||||
pub const KNOWNFOLDERID = GUID;
|
||||
pub const GUID = extern struct {
|
||||
Data1: u32,
|
||||
Data2: u16,
|
||||
@@ -3771,75 +3520,11 @@ test GUID {
|
||||
);
|
||||
}
|
||||
|
||||
pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
|
||||
|
||||
pub const KF_FLAG_DEFAULT = 0;
|
||||
pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
|
||||
pub const KF_FLAG_CREATE = 32768;
|
||||
pub const KF_FLAG_DONT_VERIFY = 16384;
|
||||
pub const KF_FLAG_DONT_UNEXPAND = 8192;
|
||||
pub const KF_FLAG_NO_ALIAS = 4096;
|
||||
pub const KF_FLAG_INIT = 2048;
|
||||
pub const KF_FLAG_DEFAULT_PATH = 1024;
|
||||
pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
|
||||
pub const KF_FLAG_SIMPLE_IDLIST = 256;
|
||||
pub const KF_FLAG_ALIAS_ONLY = -2147483648;
|
||||
|
||||
pub const S_OK = 0;
|
||||
pub const S_FALSE = 0x00000001;
|
||||
pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
|
||||
pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
|
||||
pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
|
||||
pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004)));
|
||||
pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005)));
|
||||
pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)));
|
||||
pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005)));
|
||||
pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006)));
|
||||
pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)));
|
||||
pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057)));
|
||||
|
||||
pub fn HRESULT_CODE(hr: HRESULT) Win32Error {
|
||||
return @enumFromInt(hr & 0xFFFF);
|
||||
}
|
||||
|
||||
pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||
pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
|
||||
pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||
pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
|
||||
pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
|
||||
pub const FILE_FLAG_OVERLAPPED = 0x40000000;
|
||||
pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
|
||||
pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
|
||||
pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
|
||||
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
|
||||
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
|
||||
|
||||
pub const RECT = extern struct {
|
||||
left: LONG,
|
||||
top: LONG,
|
||||
right: LONG,
|
||||
bottom: LONG,
|
||||
};
|
||||
|
||||
pub const SMALL_RECT = extern struct {
|
||||
Left: SHORT,
|
||||
Top: SHORT,
|
||||
Right: SHORT,
|
||||
Bottom: SHORT,
|
||||
};
|
||||
|
||||
pub const POINT = extern struct {
|
||||
x: LONG,
|
||||
y: LONG,
|
||||
};
|
||||
|
||||
pub const COORD = extern struct {
|
||||
X: SHORT,
|
||||
Y: SHORT,
|
||||
};
|
||||
|
||||
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
|
||||
|
||||
pub const TLS_OUT_OF_INDEXES = 4294967295;
|
||||
pub const IMAGE_TLS_DIRECTORY = extern struct {
|
||||
StartAddressOfRawData: usize,
|
||||
@@ -3854,8 +3539,6 @@ pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
|
||||
|
||||
pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void;
|
||||
|
||||
pub const PROV_RSA_FULL = 1;
|
||||
|
||||
pub const REGSAM = ACCESS_MASK;
|
||||
pub const LSTATUS = LONG;
|
||||
|
||||
@@ -3872,9 +3555,6 @@ pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005);
|
||||
pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006);
|
||||
pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007);
|
||||
|
||||
/// Open symbolic link.
|
||||
pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
|
||||
|
||||
pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
|
||||
QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
|
||||
Flags: ULONG,
|
||||
@@ -3975,38 +3655,6 @@ pub const REG = struct {
|
||||
pub const QWORD_LITTLE_ENDIAN: ULONG = 11;
|
||||
};
|
||||
|
||||
pub const FILE_NOTIFY_INFORMATION = extern struct {
|
||||
NextEntryOffset: DWORD,
|
||||
Action: DWORD,
|
||||
FileNameLength: DWORD,
|
||||
// Flexible array member
|
||||
// FileName: [1]WCHAR,
|
||||
};
|
||||
|
||||
pub const FILE_ACTION_ADDED = 0x00000001;
|
||||
pub const FILE_ACTION_REMOVED = 0x00000002;
|
||||
pub const FILE_ACTION_MODIFIED = 0x00000003;
|
||||
pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
|
||||
pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
|
||||
|
||||
pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void;
|
||||
|
||||
pub const FileNotifyChangeFilter = packed struct(DWORD) {
|
||||
file_name: bool = false,
|
||||
dir_name: bool = false,
|
||||
attributes: bool = false,
|
||||
size: bool = false,
|
||||
last_write: bool = false,
|
||||
last_access: bool = false,
|
||||
creation: bool = false,
|
||||
ea: bool = false,
|
||||
security: bool = false,
|
||||
stream_name: bool = false,
|
||||
stream_size: bool = false,
|
||||
stream_write: bool = false,
|
||||
_pad: u20 = 0,
|
||||
};
|
||||
|
||||
pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
|
||||
pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
|
||||
|
||||
@@ -4056,26 +3704,6 @@ pub const RTL_RUN_ONCE = extern struct {
|
||||
|
||||
pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
|
||||
|
||||
pub const COINIT = struct {
|
||||
pub const APARTMENTTHREADED = 2;
|
||||
pub const MULTITHREADED = 0;
|
||||
pub const DISABLE_OLE1DDE = 4;
|
||||
pub const SPEED_OVER_MEMORY = 8;
|
||||
};
|
||||
|
||||
pub const MEMORY_BASIC_INFORMATION = extern struct {
|
||||
BaseAddress: PVOID,
|
||||
AllocationBase: PVOID,
|
||||
AllocationProtect: DWORD,
|
||||
PartitionId: WORD,
|
||||
RegionSize: SIZE_T,
|
||||
State: DWORD,
|
||||
Protect: DWORD,
|
||||
Type: DWORD,
|
||||
};
|
||||
|
||||
pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION;
|
||||
|
||||
/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
|
||||
/// > prefix may be expanded to a longer string by the system at run time, and
|
||||
/// > this expansion applies to the total length.
|
||||
@@ -4544,14 +4172,6 @@ pub const UNW_FLAG_EHANDLER = 0x1;
|
||||
pub const UNW_FLAG_UHANDLER = 0x2;
|
||||
pub const UNW_FLAG_CHAININFO = 0x4;
|
||||
|
||||
pub const UNICODE_STRING = extern struct {
|
||||
Length: c_ushort,
|
||||
MaximumLength: c_ushort,
|
||||
Buffer: ?[*]WCHAR,
|
||||
|
||||
pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null };
|
||||
};
|
||||
|
||||
pub const ACTIVATION_CONTEXT_DATA = opaque {};
|
||||
pub const ASSEMBLY_STORAGE_MAP = opaque {};
|
||||
pub const FLS_CALLBACK_INFO = opaque {};
|
||||
@@ -4564,15 +4184,6 @@ pub const CLIENT_ID = extern struct {
|
||||
UniqueThread: HANDLE,
|
||||
};
|
||||
|
||||
pub const THREAD_BASIC_INFORMATION = extern struct {
|
||||
ExitStatus: NTSTATUS,
|
||||
TebBaseAddress: PVOID,
|
||||
ClientId: CLIENT_ID,
|
||||
AffinityMask: KAFFINITY,
|
||||
Priority: KPRIORITY,
|
||||
BasePriority: KPRIORITY,
|
||||
};
|
||||
|
||||
pub const TEB = extern struct {
|
||||
NtTib: NT_TIB,
|
||||
EnvironmentPointer: PVOID,
|
||||
@@ -4580,7 +4191,7 @@ pub const TEB = extern struct {
|
||||
ActiveRpcHandle: PVOID,
|
||||
ThreadLocalStoragePointer: PVOID,
|
||||
ProcessEnvironmentBlock: *PEB,
|
||||
LastErrorValue: ULONG,
|
||||
LastErrorValue: Win32Error,
|
||||
Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
|
||||
Reserved3: [1952]u8,
|
||||
TlsSlots: [64]PVOID,
|
||||
@@ -4793,7 +4404,7 @@ pub const PEB = extern struct {
|
||||
};
|
||||
|
||||
/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
|
||||
/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
|
||||
/// It is essentially the head of three double-linked lists of `LDR.DATA_TABLE_ENTRY` structures which each represent one loaded module.
|
||||
///
|
||||
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
|
||||
/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
|
||||
@@ -4825,24 +4436,89 @@ pub const PEB_LDR_DATA = extern struct {
|
||||
ShutdownThreadId: HANDLE,
|
||||
};
|
||||
|
||||
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
|
||||
/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
|
||||
pub const LDR_DATA_TABLE_ENTRY = extern struct {
|
||||
InLoadOrderLinks: LIST_ENTRY,
|
||||
InMemoryOrderLinks: LIST_ENTRY,
|
||||
InInitializationOrderLinks: LIST_ENTRY,
|
||||
DllBase: PVOID,
|
||||
EntryPoint: PVOID,
|
||||
SizeOfImage: ULONG,
|
||||
FullDllName: UNICODE_STRING,
|
||||
BaseDllName: UNICODE_STRING,
|
||||
Reserved5: [3]PVOID,
|
||||
DUMMYUNIONNAME: extern union {
|
||||
CheckSum: ULONG,
|
||||
Reserved6: PVOID,
|
||||
},
|
||||
TimeDateStamp: ULONG,
|
||||
pub const LDR = struct {
|
||||
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
|
||||
/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
|
||||
pub const DATA_TABLE_ENTRY = extern struct {
|
||||
InLoadOrderLinks: LIST_ENTRY,
|
||||
InMemoryOrderLinks: LIST_ENTRY,
|
||||
InInitializationOrderLinks: LIST_ENTRY,
|
||||
DllBase: PVOID,
|
||||
EntryPoint: PVOID,
|
||||
SizeOfImage: ULONG,
|
||||
FullDllName: UNICODE_STRING,
|
||||
BaseDllName: UNICODE_STRING,
|
||||
Reserved5: [3]PVOID,
|
||||
DUMMYUNIONNAME: extern union {
|
||||
CheckSum: ULONG,
|
||||
Reserved6: PVOID,
|
||||
},
|
||||
TimeDateStamp: ULONG,
|
||||
};
|
||||
|
||||
pub const DLL_NOTIFICATION = struct {
|
||||
pub const REASON = enum(ULONG) { LOADED = 1, UNLOADED = 2 };
|
||||
|
||||
pub const DATA = extern union {
|
||||
Loaded: LOADED,
|
||||
Unloaded: UNLOADED,
|
||||
|
||||
pub const LOADED = extern struct {
|
||||
Flags: REGISTER,
|
||||
FullDllName: *const UNICODE_STRING,
|
||||
BaseDllName: *const UNICODE_STRING,
|
||||
DllBase: PVOID,
|
||||
SizeOfImage: ULONG,
|
||||
};
|
||||
|
||||
pub const UNLOADED = extern struct {
|
||||
Flags: REGISTER,
|
||||
FullDllName: *const UNICODE_STRING,
|
||||
BaseDllName: *const UNICODE_STRING,
|
||||
DllBase: PVOID,
|
||||
SizeOfImage: ULONG,
|
||||
};
|
||||
};
|
||||
|
||||
pub const COOKIE = *opaque {};
|
||||
|
||||
pub const FUNCTION = fn (
|
||||
NotificationReason: REASON,
|
||||
NotificationData: *const DATA,
|
||||
Context: ?PVOID,
|
||||
) callconv(.winapi) void;
|
||||
|
||||
pub const REGISTER = packed struct(ULONG) {
|
||||
Reserved0: u32 = 0,
|
||||
};
|
||||
};
|
||||
|
||||
pub const GET_DLL_HANDLE_EX = packed struct(ULONG) {
|
||||
UNCHANGED_REFCOUNT: bool = false,
|
||||
PIN: bool = false,
|
||||
Reserved2: u30 = 0,
|
||||
};
|
||||
|
||||
pub const GET_PROCEDURE_ADDRESS = packed struct(ULONG) {
|
||||
DONT_RECORD_FORWARDER: bool = false,
|
||||
Reserved1: u31 = 0,
|
||||
};
|
||||
|
||||
pub const LOAD = packed struct(ULONG) {
|
||||
DONT_RESOLVE_DLL_REFERENCES: bool = false,
|
||||
LIBRARY_AS_DATAFILE: bool = false,
|
||||
PACKAGED_LIBRARY: bool = false,
|
||||
WITH_ALTERED_SEARCH_PATH: bool = false,
|
||||
IGNORE_CODE_AUTHZ_LEVEL: bool = false,
|
||||
LIBRARY_AS_IMAGE_RESOURCE: bool = false,
|
||||
LIBRARY_AS_DATAFILE_EXCLUSIVE: bool = false,
|
||||
LIBRARY_REQUERE_SIGNED_TARGET: bool = false,
|
||||
LIBRARY_SEARCH_DLL_LOAD_DIR: bool = false,
|
||||
LIBRARY_SEARCH_USER_DIRS: bool = false,
|
||||
LIBRARY_SEARCH_SYSTEM32: bool = false,
|
||||
LIBRARY_SEARCH_DEFAULT_DIRS: bool = false,
|
||||
};
|
||||
};
|
||||
|
||||
pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
|
||||
@@ -4956,86 +4632,6 @@ pub const MODULEINFO = extern struct {
|
||||
EntryPoint: LPVOID,
|
||||
};
|
||||
|
||||
pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
|
||||
FaultingPc: LPVOID,
|
||||
FaultingVa: LPVOID,
|
||||
};
|
||||
|
||||
pub const VM_COUNTERS = extern struct {
|
||||
PeakVirtualSize: SIZE_T,
|
||||
VirtualSize: SIZE_T,
|
||||
PageFaultCount: ULONG,
|
||||
PeakWorkingSetSize: SIZE_T,
|
||||
WorkingSetSize: SIZE_T,
|
||||
QuotaPeakPagedPoolUsage: SIZE_T,
|
||||
QuotaPagedPoolUsage: SIZE_T,
|
||||
QuotaPeakNonPagedPoolUsage: SIZE_T,
|
||||
QuotaNonPagedPoolUsage: SIZE_T,
|
||||
PagefileUsage: SIZE_T,
|
||||
PeakPagefileUsage: SIZE_T,
|
||||
};
|
||||
|
||||
pub const PROCESS_MEMORY_COUNTERS = extern struct {
|
||||
cb: DWORD,
|
||||
PageFaultCount: DWORD,
|
||||
PeakWorkingSetSize: SIZE_T,
|
||||
WorkingSetSize: SIZE_T,
|
||||
QuotaPeakPagedPoolUsage: SIZE_T,
|
||||
QuotaPagedPoolUsage: SIZE_T,
|
||||
QuotaPeakNonPagedPoolUsage: SIZE_T,
|
||||
QuotaNonPagedPoolUsage: SIZE_T,
|
||||
PagefileUsage: SIZE_T,
|
||||
PeakPagefileUsage: SIZE_T,
|
||||
};
|
||||
|
||||
pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
|
||||
cb: DWORD,
|
||||
PageFaultCount: DWORD,
|
||||
PeakWorkingSetSize: SIZE_T,
|
||||
WorkingSetSize: SIZE_T,
|
||||
QuotaPeakPagedPoolUsage: SIZE_T,
|
||||
QuotaPagedPoolUsage: SIZE_T,
|
||||
QuotaPeakNonPagedPoolUsage: SIZE_T,
|
||||
QuotaNonPagedPoolUsage: SIZE_T,
|
||||
PagefileUsage: SIZE_T,
|
||||
PeakPagefileUsage: SIZE_T,
|
||||
PrivateUsage: SIZE_T,
|
||||
};
|
||||
|
||||
pub const PERFORMANCE_INFORMATION = extern struct {
|
||||
cb: DWORD,
|
||||
CommitTotal: SIZE_T,
|
||||
CommitLimit: SIZE_T,
|
||||
CommitPeak: SIZE_T,
|
||||
PhysicalTotal: SIZE_T,
|
||||
PhysicalAvailable: SIZE_T,
|
||||
SystemCache: SIZE_T,
|
||||
KernelTotal: SIZE_T,
|
||||
KernelPaged: SIZE_T,
|
||||
KernelNonpaged: SIZE_T,
|
||||
PageSize: SIZE_T,
|
||||
HandleCount: DWORD,
|
||||
ProcessCount: DWORD,
|
||||
ThreadCount: DWORD,
|
||||
};
|
||||
|
||||
pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
|
||||
cb: DWORD,
|
||||
Reserved: DWORD,
|
||||
TotalSize: SIZE_T,
|
||||
TotalInUse: SIZE_T,
|
||||
PeakUsage: SIZE_T,
|
||||
};
|
||||
|
||||
pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL;
|
||||
pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL;
|
||||
|
||||
pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
|
||||
BasicInfo: PSAPI_WS_WATCH_INFORMATION,
|
||||
FaultingThreadId: ULONG_PTR,
|
||||
Flags: ULONG_PTR,
|
||||
};
|
||||
|
||||
pub const OSVERSIONINFOW = extern struct {
|
||||
dwOSVersionInfoSize: ULONG,
|
||||
dwMajorVersion: ULONG,
|
||||
@@ -5098,20 +4694,6 @@ pub const MOUNTMGR_VOLUME_PATHS = extern struct {
|
||||
MultiSz: [1]WCHAR,
|
||||
};
|
||||
|
||||
pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
|
||||
ObjectBasicInformation = 0,
|
||||
ObjectNameInformation = 1,
|
||||
ObjectTypeInformation = 2,
|
||||
ObjectTypesInformation = 3,
|
||||
ObjectHandleFlagInformation = 4,
|
||||
ObjectSessionInformation = 5,
|
||||
MaxObjectInfoClass,
|
||||
};
|
||||
|
||||
pub const OBJECT_NAME_INFORMATION = extern struct {
|
||||
Name: UNICODE_STRING,
|
||||
};
|
||||
|
||||
pub const SRWLOCK_INIT = SRWLOCK{};
|
||||
pub const SRWLOCK = extern struct {
|
||||
Ptr: ?PVOID = null,
|
||||
@@ -5122,17 +4704,6 @@ pub const CONDITION_VARIABLE = extern struct {
|
||||
Ptr: ?PVOID = null,
|
||||
};
|
||||
|
||||
pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
|
||||
pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
|
||||
|
||||
pub const CTRL_C_EVENT: DWORD = 0;
|
||||
pub const CTRL_BREAK_EVENT: DWORD = 1;
|
||||
pub const CTRL_CLOSE_EVENT: DWORD = 2;
|
||||
pub const CTRL_LOGOFF_EVENT: DWORD = 5;
|
||||
pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
|
||||
|
||||
pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL;
|
||||
|
||||
/// Processor feature enumeration.
|
||||
pub const PF = enum(DWORD) {
|
||||
/// On a Pentium, a floating-point precision error can occur in rare circumstances.
|
||||
@@ -5431,72 +5002,13 @@ pub const KUSER_SHARED_DATA = extern struct {
|
||||
/// Read-only user-mode address for the shared data.
|
||||
/// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
|
||||
/// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/
|
||||
pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000));
|
||||
pub const SharedUserData: *const KUSER_SHARED_DATA = @ptrFromInt(0x7FFE0000);
|
||||
|
||||
pub fn IsProcessorFeaturePresent(feature: PF) bool {
|
||||
if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false;
|
||||
return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1;
|
||||
}
|
||||
|
||||
pub const TH32CS_SNAPHEAPLIST = 0x00000001;
|
||||
pub const TH32CS_SNAPPROCESS = 0x00000002;
|
||||
pub const TH32CS_SNAPTHREAD = 0x00000004;
|
||||
pub const TH32CS_SNAPMODULE = 0x00000008;
|
||||
pub const TH32CS_SNAPMODULE32 = 0x00000010;
|
||||
pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
|
||||
pub const TH32CS_INHERIT = 0x80000000;
|
||||
|
||||
pub const MAX_MODULE_NAME32 = 255;
|
||||
pub const MODULEENTRY32 = extern struct {
|
||||
dwSize: DWORD,
|
||||
th32ModuleID: DWORD,
|
||||
th32ProcessID: DWORD,
|
||||
GlblcntUsage: DWORD,
|
||||
ProccntUsage: DWORD,
|
||||
modBaseAddr: *BYTE,
|
||||
modBaseSize: DWORD,
|
||||
hModule: HMODULE,
|
||||
szModule: [MAX_MODULE_NAME32 + 1]CHAR,
|
||||
szExePath: [MAX_PATH]CHAR,
|
||||
};
|
||||
|
||||
pub const SYSTEM_INFORMATION_CLASS = enum(c_int) {
|
||||
SystemBasicInformation = 0,
|
||||
SystemPerformanceInformation = 2,
|
||||
SystemTimeOfDayInformation = 3,
|
||||
SystemProcessInformation = 5,
|
||||
SystemProcessorPerformanceInformation = 8,
|
||||
SystemInterruptInformation = 23,
|
||||
SystemExceptionInformation = 33,
|
||||
SystemRegistryQuotaInformation = 37,
|
||||
SystemLookasideInformation = 45,
|
||||
SystemCodeIntegrityInformation = 103,
|
||||
SystemPolicyInformation = 134,
|
||||
};
|
||||
|
||||
pub const SYSTEM_BASIC_INFORMATION = extern struct {
|
||||
Reserved: ULONG,
|
||||
TimerResolution: ULONG,
|
||||
PageSize: ULONG,
|
||||
NumberOfPhysicalPages: ULONG,
|
||||
LowestPhysicalPageNumber: ULONG,
|
||||
HighestPhysicalPageNumber: ULONG,
|
||||
AllocationGranularity: ULONG,
|
||||
MinimumUserModeAddress: ULONG_PTR,
|
||||
MaximumUserModeAddress: ULONG_PTR,
|
||||
ActiveProcessorsAffinityMask: KAFFINITY,
|
||||
NumberOfProcessors: UCHAR,
|
||||
};
|
||||
|
||||
pub const PROCESS_BASIC_INFORMATION = extern struct {
|
||||
ExitStatus: NTSTATUS,
|
||||
PebBaseAddress: *PEB,
|
||||
AffinityMask: ULONG_PTR,
|
||||
BasePriority: KPRIORITY,
|
||||
UniqueProcessId: ULONG_PTR,
|
||||
InheritedFromUniqueProcessId: ULONG_PTR,
|
||||
};
|
||||
|
||||
// https://github.com/reactos/reactos/blob/master/sdk/include/ndk/pstypes.h#L977-L983
|
||||
pub const KERNEL_USER_TIMES = extern struct {
|
||||
CreationTime: LARGE_INTEGER,
|
||||
@@ -5505,75 +5017,6 @@ pub const KERNEL_USER_TIMES = extern struct {
|
||||
UserTime: LARGE_INTEGER,
|
||||
};
|
||||
|
||||
pub const ReadMemoryError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 {
|
||||
var nread: usize = 0;
|
||||
switch (ntdll.NtReadVirtualMemory(
|
||||
handle,
|
||||
addr,
|
||||
buffer.ptr,
|
||||
buffer.len,
|
||||
&nread,
|
||||
)) {
|
||||
.SUCCESS => return buffer[0..nread],
|
||||
// TODO: map errors
|
||||
else => |rc| return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const WriteMemoryError = error{
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize {
|
||||
var nwritten: usize = 0;
|
||||
switch (ntdll.NtWriteVirtualMemory(
|
||||
handle,
|
||||
addr,
|
||||
buffer.ptr,
|
||||
buffer.len,
|
||||
&nwritten,
|
||||
)) {
|
||||
.SUCCESS => return nwritten,
|
||||
// TODO: map errors
|
||||
else => |rc| return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const ProcessBaseAddressError = error{
|
||||
AccessDenied,
|
||||
InvalidHandle,
|
||||
Unexpected,
|
||||
} || ReadMemoryError;
|
||||
|
||||
/// Returns the base address of the process loaded into memory.
|
||||
pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE {
|
||||
var info: PROCESS_BASIC_INFORMATION = undefined;
|
||||
var nread: DWORD = 0;
|
||||
const rc = ntdll.NtQueryInformationProcess(
|
||||
handle,
|
||||
.BasicInformation,
|
||||
&info,
|
||||
@sizeOf(PROCESS_BASIC_INFORMATION),
|
||||
&nread,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.INVALID_HANDLE => return error.InvalidHandle,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
|
||||
var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined;
|
||||
const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf);
|
||||
const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr));
|
||||
return ppeb.ImageBaseAddress;
|
||||
}
|
||||
|
||||
pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameTooLong }!usize {
|
||||
// Each u8 in UTF-8/WTF-8 correlates to at most one u16 in UTF-16LE/WTF-16LE.
|
||||
if (wtf16le.len < wtf8.len) {
|
||||
|
||||
@@ -1,154 +1,29 @@
|
||||
const std = @import("../../std.zig");
|
||||
const windows = std.os.windows;
|
||||
|
||||
const ACCESS_MASK = windows.ACCESS_MASK;
|
||||
const BOOL = windows.BOOL;
|
||||
const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
|
||||
const COORD = windows.COORD;
|
||||
const DWORD = windows.DWORD;
|
||||
const FARPROC = windows.FARPROC;
|
||||
const FILETIME = windows.FILETIME;
|
||||
const HANDLE = windows.HANDLE;
|
||||
const HANDLER_ROUTINE = windows.HANDLER_ROUTINE;
|
||||
const HMODULE = windows.HMODULE;
|
||||
const LARGE_INTEGER = windows.LARGE_INTEGER;
|
||||
const LPCSTR = windows.LPCSTR;
|
||||
const LPCVOID = windows.LPCVOID;
|
||||
const LPCWSTR = windows.LPCWSTR;
|
||||
const LPTHREAD_START_ROUTINE = windows.LPTHREAD_START_ROUTINE;
|
||||
const LPVOID = windows.LPVOID;
|
||||
const LPWSTR = windows.LPWSTR;
|
||||
const MODULEENTRY32 = windows.MODULEENTRY32;
|
||||
const OVERLAPPED = windows.OVERLAPPED;
|
||||
const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY;
|
||||
const PROCESS_INFORMATION = windows.PROCESS_INFORMATION;
|
||||
const PROCESS = windows.PROCESS;
|
||||
const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE;
|
||||
const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
|
||||
const SIZE_T = windows.SIZE_T;
|
||||
const SRWLOCK = windows.SRWLOCK;
|
||||
const STARTUPINFOW = windows.STARTUPINFOW;
|
||||
const SYSTEM_INFO = windows.SYSTEM_INFO;
|
||||
const UCHAR = windows.UCHAR;
|
||||
const UINT = windows.UINT;
|
||||
const ULONG = windows.ULONG;
|
||||
const ULONG_PTR = windows.ULONG_PTR;
|
||||
const va_list = windows.va_list;
|
||||
const WCHAR = windows.WCHAR;
|
||||
const Win32Error = windows.Win32Error;
|
||||
const WORD = windows.WORD;
|
||||
|
||||
// I/O - Filesystem
|
||||
|
||||
pub extern "kernel32" fn ReadDirectoryChangesW(
|
||||
hDirectory: HANDLE,
|
||||
lpBuffer: [*]align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) u8,
|
||||
nBufferLength: DWORD,
|
||||
bWatchSubtree: BOOL,
|
||||
dwNotifyFilter: windows.FileNotifyChangeFilter,
|
||||
lpBytesReturned: ?*DWORD,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
lpCompletionRoutine: windows.LPOVERLAPPED_COMPLETION_ROUTINE,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtCancelIoFile.
|
||||
pub extern "kernel32" fn CancelIo(
|
||||
hFile: HANDLE,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`.
|
||||
// `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END`
|
||||
pub extern "kernel32" fn SetFilePointerEx(
|
||||
hFile: HANDLE,
|
||||
liDistanceToMove: LARGE_INTEGER,
|
||||
lpNewFilePointer: ?*LARGE_INTEGER,
|
||||
dwMoveMethod: DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtSetInformationFile + `FILE_BASIC_INFORMATION`
|
||||
pub extern "kernel32" fn SetFileTime(
|
||||
hFile: HANDLE,
|
||||
lpCreationTime: ?*const FILETIME,
|
||||
lpLastAccessTime: ?*const FILETIME,
|
||||
lpLastWriteTime: ?*const FILETIME,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn WriteFile(
|
||||
in_hFile: HANDLE,
|
||||
in_lpBuffer: [*]const u8,
|
||||
in_nNumberOfBytesToWrite: DWORD,
|
||||
out_lpNumberOfBytesWritten: ?*DWORD,
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`.
|
||||
pub extern "kernel32" fn SetFileCompletionNotificationModes(
|
||||
FileHandle: HANDLE,
|
||||
Flags: UCHAR,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn ReadFile(
|
||||
hFile: HANDLE,
|
||||
lpBuffer: LPVOID,
|
||||
nNumberOfBytesToRead: DWORD,
|
||||
lpNumberOfBytesRead: ?*DWORD,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn GetSystemDirectoryW(
|
||||
lpBuffer: LPWSTR,
|
||||
uSize: UINT,
|
||||
) callconv(.winapi) UINT;
|
||||
|
||||
// I/O - Kernel Objects
|
||||
|
||||
// TODO: Wrapper around NtRemoveIoCompletion.
|
||||
pub extern "kernel32" fn GetQueuedCompletionStatus(
|
||||
CompletionPort: HANDLE,
|
||||
lpNumberOfBytesTransferred: *DWORD,
|
||||
lpCompletionKey: *ULONG_PTR,
|
||||
lpOverlapped: *?*OVERLAPPED,
|
||||
dwMilliseconds: DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtRemoveIoCompletionEx.
|
||||
pub extern "kernel32" fn GetQueuedCompletionStatusEx(
|
||||
CompletionPort: HANDLE,
|
||||
lpCompletionPortEntries: [*]OVERLAPPED_ENTRY,
|
||||
ulCount: ULONG,
|
||||
ulNumEntriesRemoved: *ULONG,
|
||||
dwMilliseconds: DWORD,
|
||||
fAlertable: BOOL,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtSetIoCompletion with `IoStatus = .SUCCESS`.
|
||||
pub extern "kernel32" fn PostQueuedCompletionStatus(
|
||||
CompletionPort: HANDLE,
|
||||
dwNumberOfBytesTransferred: DWORD,
|
||||
dwCompletionKey: ULONG_PTR,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn GetOverlappedResult(
|
||||
hFile: HANDLE,
|
||||
lpOverlapped: *OVERLAPPED,
|
||||
lpNumberOfBytesTransferred: *DWORD,
|
||||
bWait: BOOL,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtCreateIoCompletion + NtSetInformationFile with FILE_COMPLETION_INFORMATION.
|
||||
// This would be better splitting into two functions.
|
||||
pub extern "kernel32" fn CreateIoCompletionPort(
|
||||
FileHandle: HANDLE,
|
||||
ExistingCompletionPort: ?HANDLE,
|
||||
CompletionKey: ULONG_PTR,
|
||||
NumberOfConcurrentThreads: DWORD,
|
||||
) callconv(.winapi) ?HANDLE;
|
||||
|
||||
// TODO: Wrapper around RtlReportSilentProcessExit + NtTerminateProcess.
|
||||
pub extern "kernel32" fn TerminateProcess(
|
||||
hProcess: HANDLE,
|
||||
uExitCode: UINT,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// Process Management
|
||||
|
||||
pub extern "kernel32" fn CreateProcessW(
|
||||
@@ -161,86 +36,21 @@ pub extern "kernel32" fn CreateProcessW(
|
||||
lpEnvironment: ?[*:0]const u16,
|
||||
lpCurrentDirectory: ?LPCWSTR,
|
||||
lpStartupInfo: *STARTUPINFOW,
|
||||
lpProcessInformation: *PROCESS_INFORMATION,
|
||||
lpProcessInformation: *PROCESS.INFORMATION,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`.
|
||||
pub extern "kernel32" fn GetExitCodeProcess(
|
||||
hProcess: HANDLE,
|
||||
lpExitCode: *DWORD,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn CreateToolhelp32Snapshot(
|
||||
dwFlags: DWORD,
|
||||
th32ProcessID: DWORD,
|
||||
) callconv(.winapi) HANDLE;
|
||||
|
||||
// Threading
|
||||
|
||||
// TODO: Already a wrapper for this, see `windows.GetCurrentThreadId`.
|
||||
pub extern "kernel32" fn GetCurrentThreadId() callconv(.winapi) DWORD;
|
||||
|
||||
// TODO: CreateRemoteThread with hProcess=NtCurrentProcess().
|
||||
pub extern "kernel32" fn CreateThread(
|
||||
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
dwStackSize: SIZE_T,
|
||||
lpStartAddress: LPTHREAD_START_ROUTINE,
|
||||
lpStartAddress: *const THREAD_START_ROUTINE,
|
||||
lpParameter: ?LPVOID,
|
||||
dwCreationFlags: DWORD,
|
||||
lpThreadId: ?*DWORD,
|
||||
) callconv(.winapi) ?HANDLE;
|
||||
|
||||
// Code Libraries/Modules
|
||||
|
||||
// TODO: Wrapper around LdrGetDllFullName.
|
||||
pub extern "kernel32" fn GetModuleFileNameW(
|
||||
hModule: ?HMODULE,
|
||||
lpFilename: [*]WCHAR,
|
||||
nSize: DWORD,
|
||||
) callconv(.winapi) DWORD;
|
||||
|
||||
extern "kernel32" fn K32GetModuleFileNameExW(
|
||||
hProcess: HANDLE,
|
||||
hModule: ?HMODULE,
|
||||
lpFilename: LPWSTR,
|
||||
nSize: DWORD,
|
||||
) callconv(.winapi) DWORD;
|
||||
pub const GetModuleFileNameExW = K32GetModuleFileNameExW;
|
||||
|
||||
// TODO: Wrapper around ntdll.LdrGetDllHandle, which is a wrapper around LdrGetDllHandleEx
|
||||
pub extern "kernel32" fn GetModuleHandleW(
|
||||
lpModuleName: ?LPCWSTR,
|
||||
) callconv(.winapi) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" fn Module32First(
|
||||
hSnapshot: HANDLE,
|
||||
lpme: *MODULEENTRY32,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn Module32Next(
|
||||
hSnapshot: HANDLE,
|
||||
lpme: *MODULEENTRY32,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
pub extern "kernel32" fn LoadLibraryW(
|
||||
lpLibFileName: LPCWSTR,
|
||||
) callconv(.winapi) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" fn LoadLibraryExW(
|
||||
lpLibFileName: LPCWSTR,
|
||||
hFile: ?HANDLE,
|
||||
dwFlags: DWORD,
|
||||
) callconv(.winapi) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" fn GetProcAddress(
|
||||
hModule: HMODULE,
|
||||
lpProcName: LPCSTR,
|
||||
) callconv(.winapi) ?FARPROC;
|
||||
|
||||
pub extern "kernel32" fn FreeLibrary(
|
||||
hModule: HMODULE,
|
||||
) callconv(.winapi) BOOL;
|
||||
|
||||
// Error Management
|
||||
|
||||
pub extern "kernel32" fn FormatMessageW(
|
||||
@@ -252,6 +62,3 @@ pub extern "kernel32" fn FormatMessageW(
|
||||
nSize: DWORD,
|
||||
Arguments: ?*va_list,
|
||||
) callconv(.winapi) DWORD;
|
||||
|
||||
// TODO: Getter for teb().LastErrorValue.
|
||||
pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error;
|
||||
|
||||
+175
-72
@@ -2,6 +2,7 @@ const std = @import("../../std.zig");
|
||||
const windows = std.os.windows;
|
||||
|
||||
const ACCESS_MASK = windows.ACCESS_MASK;
|
||||
const ANSI_STRING = windows.ANSI_STRING;
|
||||
const BOOL = windows.BOOL;
|
||||
const BOOLEAN = windows.BOOLEAN;
|
||||
const CONDITION_VARIABLE = windows.CONDITION_VARIABLE;
|
||||
@@ -9,6 +10,7 @@ const CONTEXT = windows.CONTEXT;
|
||||
const CRITICAL_SECTION = windows.CRITICAL_SECTION;
|
||||
const CTL_CODE = windows.CTL_CODE;
|
||||
const CURDIR = windows.CURDIR;
|
||||
const DIRECTORY = windows.DIRECTORY;
|
||||
const DWORD = windows.DWORD;
|
||||
const DWORD64 = windows.DWORD64;
|
||||
const ERESOURCE = windows.ERESOURCE;
|
||||
@@ -22,18 +24,19 @@ const IO_APC_ROUTINE = windows.IO_APC_ROUTINE;
|
||||
const IO_STATUS_BLOCK = windows.IO_STATUS_BLOCK;
|
||||
const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
|
||||
const LARGE_INTEGER = windows.LARGE_INTEGER;
|
||||
const LDR = windows.LDR;
|
||||
const LOGICAL = windows.LOGICAL;
|
||||
const LONG = windows.LONG;
|
||||
const LPCVOID = windows.LPCVOID;
|
||||
const LPVOID = windows.LPVOID;
|
||||
const MEM = windows.MEM;
|
||||
const NTSTATUS = windows.NTSTATUS;
|
||||
const OBJECT_ATTRIBUTES = windows.OBJECT_ATTRIBUTES;
|
||||
const OBJECT_INFORMATION_CLASS = windows.OBJECT_INFORMATION_CLASS;
|
||||
const OBJECT = windows.OBJECT;
|
||||
const PAGE = windows.PAGE;
|
||||
const PCWSTR = windows.PCWSTR;
|
||||
const PROCESSINFOCLASS = windows.PROCESSINFOCLASS;
|
||||
const PROCESS = windows.PROCESS;
|
||||
const PVOID = windows.PVOID;
|
||||
const PWSTR = windows.PWSTR;
|
||||
const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
|
||||
const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
|
||||
const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
|
||||
@@ -41,8 +44,8 @@ const SEC = windows.SEC;
|
||||
const SECTION_INHERIT = windows.SECTION_INHERIT;
|
||||
const SIZE_T = windows.SIZE_T;
|
||||
const SRWLOCK = windows.SRWLOCK;
|
||||
const SYSTEM_INFORMATION_CLASS = windows.SYSTEM_INFORMATION_CLASS;
|
||||
const THREADINFOCLASS = windows.THREADINFOCLASS;
|
||||
const SYSTEM = windows.SYSTEM;
|
||||
const THREAD = windows.THREAD;
|
||||
const ULONG = windows.ULONG;
|
||||
const ULONG_PTR = windows.ULONG_PTR;
|
||||
const UNICODE_STRING = windows.UNICODE_STRING;
|
||||
@@ -91,7 +94,7 @@ pub extern "ntdll" fn RtlCaptureContext(
|
||||
|
||||
pub extern "ntdll" fn NtSetInformationThread(
|
||||
ThreadHandle: HANDLE,
|
||||
ThreadInformationClass: THREADINFOCLASS,
|
||||
ThreadInformationClass: THREAD.INFOCLASS,
|
||||
ThreadInformation: *const anyopaque,
|
||||
ThreadInformationLength: ULONG,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
@@ -99,7 +102,7 @@ pub extern "ntdll" fn NtSetInformationThread(
|
||||
pub extern "ntdll" fn NtCreateFile(
|
||||
FileHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
AllocationSize: ?*const LARGE_INTEGER,
|
||||
FileAttributes: FILE.ATTRIBUTE,
|
||||
@@ -152,7 +155,7 @@ pub extern "ntdll" fn NtLockFile(
|
||||
pub extern "ntdll" fn NtOpenFile(
|
||||
FileHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
ShareAccess: FILE.SHARE,
|
||||
OpenOptions: FILE.MODE,
|
||||
@@ -233,7 +236,7 @@ pub extern "ntdll" fn NtUnlockFile(
|
||||
|
||||
pub extern "ntdll" fn NtQueryObject(
|
||||
Handle: HANDLE,
|
||||
ObjectInformationClass: OBJECT_INFORMATION_CLASS,
|
||||
ObjectInformationClass: OBJECT.INFORMATION_CLASS,
|
||||
ObjectInformation: ?PVOID,
|
||||
ObjectInformationLength: ULONG,
|
||||
ReturnLength: ?*ULONG,
|
||||
@@ -246,7 +249,7 @@ pub extern "ntdll" fn NtClose(
|
||||
pub extern "ntdll" fn NtCreateSection(
|
||||
SectionHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
|
||||
MaximumSize: ?*const LARGE_INTEGER,
|
||||
SectionPageProtection: PAGE,
|
||||
AllocationAttributes: SEC,
|
||||
@@ -331,7 +334,7 @@ pub extern "ntdll" fn NtWaitForSingleObject(
|
||||
|
||||
pub extern "ntdll" fn NtQueryInformationProcess(
|
||||
ProcessHandle: HANDLE,
|
||||
ProcessInformationClass: PROCESSINFOCLASS,
|
||||
ProcessInformationClass: PROCESS.INFOCLASS,
|
||||
ProcessInformation: *anyopaque,
|
||||
ProcessInformationLength: ULONG,
|
||||
ReturnLength: ?*ULONG,
|
||||
@@ -339,14 +342,14 @@ pub extern "ntdll" fn NtQueryInformationProcess(
|
||||
|
||||
pub extern "ntdll" fn NtQueryInformationThread(
|
||||
ThreadHandle: HANDLE,
|
||||
ThreadInformationClass: THREADINFOCLASS,
|
||||
ThreadInformationClass: THREAD.INFOCLASS,
|
||||
ThreadInformation: *anyopaque,
|
||||
ThreadInformationLength: ULONG,
|
||||
ReturnLength: ?*ULONG,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtQuerySystemInformation(
|
||||
SystemInformationClass: SYSTEM_INFORMATION_CLASS,
|
||||
SystemInformationClass: SYSTEM.INFORMATION_CLASS,
|
||||
SystemInformation: PVOID,
|
||||
SystemInformationLength: ULONG,
|
||||
ReturnLength: ?*ULONG,
|
||||
@@ -354,15 +357,99 @@ pub extern "ntdll" fn NtQuerySystemInformation(
|
||||
|
||||
// ref none
|
||||
|
||||
pub extern "ntdll" fn LdrAddRefDll(
|
||||
Flags: ULONG,
|
||||
DllHandle: PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrLoadDll(
|
||||
DllPath: ?PCWSTR,
|
||||
DllCharacteristics: ?*const ULONG,
|
||||
DllName: *const UNICODE_STRING,
|
||||
DllHandle: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrUnloadDll(
|
||||
DllHandle: PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn LdrFindEntryForAddress(
|
||||
DllHandle: PVOID,
|
||||
Entry: **LDR.DATA_TABLE_ENTRY,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetDllFullName(
|
||||
DllHandle: ?PVOID,
|
||||
FullDllName: *UNICODE_STRING,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetDllPath(
|
||||
DllName: PCWSTR,
|
||||
Flags: LDR.LOAD,
|
||||
DllPath: *PWSTR,
|
||||
SearchPaths: *PWSTR,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn LdrGetDllHandle(
|
||||
DllPath: ?PCWSTR,
|
||||
DllCharacteristics: ?*const ULONG,
|
||||
DllName: *const UNICODE_STRING,
|
||||
DllHandle: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetDllHandleByMapping(
|
||||
BaseAddress: PVOID,
|
||||
DllHandle: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetDllHandleByName(
|
||||
BaseDllName: *const UNICODE_STRING,
|
||||
FullDllName: *const UNICODE_STRING,
|
||||
DllHandle: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetDllHandleEx(
|
||||
Flags: LDR.GET_DLL_HANDLE_EX,
|
||||
DllPath: ?PCWSTR,
|
||||
DllCharacteristics: ?*const ULONG,
|
||||
DllName: *const UNICODE_STRING,
|
||||
DllHandle: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn LdrGetProcedureAddress(
|
||||
DllHandle: PVOID,
|
||||
ProcedureName: *const ANSI_STRING,
|
||||
ProcedureNumber: ULONG,
|
||||
ProcedureAddress: *PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetProcedureAddressEx(
|
||||
DllHandle: PVOID,
|
||||
ProcedureName: *const ANSI_STRING,
|
||||
ProcedureNumber: ULONG,
|
||||
ProcedureAddress: *PVOID,
|
||||
Flags: LDR.GET_PROCEDURE_ADDRESS,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrGetProcedureAddressForCaller(
|
||||
DllHandle: PVOID,
|
||||
ProcedureName: *const ANSI_STRING,
|
||||
ProcedureNumber: ULONG,
|
||||
ProcedureAddress: *PVOID,
|
||||
Flags: LDR.GET_PROCEDURE_ADDRESS,
|
||||
CallerAddress: PVOID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn LdrRegisterDllNotification(
|
||||
Flags: LDR.DLL_NOTIFICATION.REGISTER,
|
||||
NotificationFunction: *const LDR.DLL_NOTIFICATION.FUNCTION,
|
||||
Context: ?PVOID,
|
||||
Cookie: *LDR.DLL_NOTIFICATION.COOKIE,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn LdrUnregisterDllNotification(
|
||||
Cookie: LDR.DLL_NOTIFICATION.COOKIE,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtQueryAttributesFile(
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
FileAttributes: *FILE.BASIC_INFORMATION,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCreateEvent(
|
||||
EventHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
|
||||
EventType: EVENT_TYPE,
|
||||
InitialState: BOOLEAN,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
@@ -374,7 +461,7 @@ pub extern "ntdll" fn NtSetEvent(
|
||||
pub extern "ntdll" fn NtCreateKeyedEvent(
|
||||
KeyedEventHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: ?*const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: ?*const OBJECT.ATTRIBUTES,
|
||||
Flags: ULONG,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtReleaseKeyedEvent(
|
||||
@@ -390,10 +477,57 @@ pub extern "ntdll" fn NtWaitForKeyedEvent(
|
||||
Timeout: ?*const LARGE_INTEGER,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCancelSynchronousIoFile(
|
||||
ThreadHandle: HANDLE,
|
||||
IoRequestToCancel: ?*IO_STATUS_BLOCK,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtCancelIoFile(
|
||||
FileHandle: HANDLE,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtCancelIoFileEx(
|
||||
FileHandle: HANDLE,
|
||||
IoRequestToCancel: *const IO_STATUS_BLOCK,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
/// This function has been observed to return SUCCESS on timeout on Windows 10
|
||||
/// and TIMEOUT on Wine 10.0.
|
||||
///
|
||||
/// This function has been observed on Windows 11 such that positive interval
|
||||
/// is real time, which can cause waits to be interrupted by changing system
|
||||
/// time, however negative intervals are not affected by changes to system
|
||||
/// time.
|
||||
pub extern "ntdll" fn NtDelayExecution(
|
||||
Alertable: BOOLEAN,
|
||||
DelayInterval: *const LARGE_INTEGER,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtNotifyChangeDirectoryFileEx(
|
||||
FileHandle: HANDLE,
|
||||
Event: ?HANDLE,
|
||||
ApcRoutine: ?*const IO_APC_ROUTINE,
|
||||
ApcContext: ?*anyopaque,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
Buffer: *anyopaque,
|
||||
Length: ULONG,
|
||||
CompletionFilter: FILE.NOTIFY.CHANGE,
|
||||
WatchTree: BOOLEAN,
|
||||
DirectoryNotifyInformationClass: DIRECTORY.NOTIFY_INFORMATION_CLASS,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtOpenThread(
|
||||
ThreadHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
ClientId: *const windows.CLIENT_ID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCreateNamedPipeFile(
|
||||
FileHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
ShareAccess: FILE.SHARE,
|
||||
CreateDisposition: FILE.CREATE_DISPOSITION,
|
||||
@@ -437,7 +571,7 @@ pub extern "ntdll" fn NtUnmapViewOfSectionEx(
|
||||
pub extern "ntdll" fn NtOpenKey(
|
||||
KeyHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ObjectAttributes: *const OBJECT.ATTRIBUTES,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtQueueApcThread(
|
||||
@@ -470,6 +604,19 @@ pub extern "ntdll" fn NtProtectVirtualMemory(
|
||||
OldAccessProtection: *PAGE,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
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 NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
|
||||
ThreadIds: [*]const ULONG_PTR,
|
||||
ThreadCount: ULONG,
|
||||
Unknown1: ?*const anyopaque,
|
||||
Unknown2: ?*const anyopaque,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtYieldExecution() callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn RtlAddVectoredExceptionHandler(
|
||||
@@ -527,10 +674,6 @@ pub extern "ntdll" fn RtlQueryPerformanceCounter(
|
||||
pub extern "ntdll" fn RtlQueryPerformanceFrequency(
|
||||
PerformanceFrequency: *LARGE_INTEGER,
|
||||
) callconv(.winapi) BOOL;
|
||||
pub extern "ntdll" fn NtQueryPerformanceCounter(
|
||||
PerformanceCounter: *LARGE_INTEGER,
|
||||
PerformanceFrequency: ?*LARGE_INTEGER,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn RtlReAllocateHeap(
|
||||
HeapHandle: *HEAP,
|
||||
@@ -539,8 +682,17 @@ pub extern "ntdll" fn RtlReAllocateHeap(
|
||||
Size: SIZE_T,
|
||||
) callconv(.winapi) ?PVOID;
|
||||
|
||||
pub extern "ntdll" fn RtlReportSilentProcessExit(
|
||||
ProcessHandle: HANDLE,
|
||||
ExitStatus: NTSTATUS,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtTerminateProcess(
|
||||
ProcessHandle: ?HANDLE,
|
||||
ExitStatus: NTSTATUS,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn RtlSetCurrentDirectory_U(
|
||||
PathName: *UNICODE_STRING,
|
||||
PathName: *const UNICODE_STRING,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn RtlTryAcquireSRWLockExclusive(
|
||||
@@ -572,52 +724,3 @@ pub extern "ntdll" fn RtlWakeConditionVariable(
|
||||
pub extern "ntdll" fn RtlWakeAllConditionVariable(
|
||||
ConditionVariable: *CONDITION_VARIABLE,
|
||||
) callconv(.winapi) void;
|
||||
|
||||
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 NtAlertThread(ThreadHandle: HANDLE) callconv(.winapi) NTSTATUS;
|
||||
pub extern "ntdll" fn NtAlertMultipleThreadByThreadId(
|
||||
ThreadIds: [*]const ULONG_PTR,
|
||||
ThreadCount: ULONG,
|
||||
Unknown1: ?*const anyopaque,
|
||||
Unknown2: ?*const anyopaque,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtOpenThread(
|
||||
ThreadHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: *const OBJECT_ATTRIBUTES,
|
||||
ClientId: *const windows.CLIENT_ID,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCancelSynchronousIoFile(
|
||||
ThreadHandle: HANDLE,
|
||||
IoRequestToCancel: ?*IO_STATUS_BLOCK,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
/// This function has been observed to return SUCCESS on timeout on Windows 10
|
||||
/// and TIMEOUT on Wine 10.0.
|
||||
///
|
||||
/// This function has been observed on Windows 11 such that positive interval
|
||||
/// is real time, which can cause waits to be interrupted by changing system
|
||||
/// time, however negative intervals are not affected by changes to system
|
||||
/// time.
|
||||
pub extern "ntdll" fn NtDelayExecution(
|
||||
Alertable: BOOLEAN,
|
||||
DelayInterval: *const LARGE_INTEGER,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCancelIoFile(
|
||||
FileHandle: HANDLE,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
pub extern "ntdll" fn NtCancelIoFileEx(
|
||||
FileHandle: HANDLE,
|
||||
IoRequestToCancel: *const IO_STATUS_BLOCK,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
) callconv(.winapi) NTSTATUS;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// Codes are from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d
|
||||
pub const Win32Error = enum(u16) {
|
||||
pub const Win32Error = enum(u32) {
|
||||
/// The operation completed successfully.
|
||||
SUCCESS = 0,
|
||||
/// Incorrect function.
|
||||
|
||||
+4
-4
@@ -243,7 +243,7 @@ pub fn getBaseAddress() usize {
|
||||
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
||||
return @intFromPtr(&std.c._mh_execute_header);
|
||||
},
|
||||
.windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)),
|
||||
.windows => return @intFromPtr(windows.peb().ImageBaseAddress),
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
@@ -606,11 +606,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
|
||||
return @as(u64, @bitCast(physmem));
|
||||
},
|
||||
.windows => {
|
||||
var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
|
||||
var sbi: windows.SYSTEM.BASIC_INFORMATION = undefined;
|
||||
const rc = windows.ntdll.NtQuerySystemInformation(
|
||||
.SystemBasicInformation,
|
||||
.Basic,
|
||||
&sbi,
|
||||
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
|
||||
@sizeOf(windows.SYSTEM.BASIC_INFORMATION),
|
||||
null,
|
||||
);
|
||||
if (rc != .SUCCESS) {
|
||||
|
||||
@@ -86,7 +86,7 @@ pub const ResourceUsageStatistics = struct {
|
||||
.visionos,
|
||||
.watchos,
|
||||
=> @as(?std.posix.rusage, null),
|
||||
.windows => @as(?std.os.windows.VM_COUNTERS, null),
|
||||
.windows => @as(?std.os.windows.PROCESS.VM_COUNTERS, null),
|
||||
else => {},
|
||||
};
|
||||
};
|
||||
|
||||
+5
-9
@@ -473,10 +473,9 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn {
|
||||
std.Thread.maybeAttachSignalStack();
|
||||
std.debug.maybeEnableSegfaultHandler();
|
||||
|
||||
const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
|
||||
const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
|
||||
|
||||
std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, .global));
|
||||
std.os.windows.ntdll.RtlExitUserProcess(
|
||||
callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global),
|
||||
);
|
||||
}
|
||||
|
||||
fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn {
|
||||
@@ -647,9 +646,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal
|
||||
// values in their intended encoding from the PEB instead.
|
||||
std.Thread.maybeAttachSignalStack();
|
||||
std.debug.maybeEnableSegfaultHandler();
|
||||
const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
|
||||
const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
|
||||
return callMain(cmd_line_w, .global);
|
||||
return callMain(std.os.windows.peb().ProcessParameters.CommandLine.slice(), .global);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -776,8 +773,7 @@ fn call_wWinMain() std.os.windows.INT {
|
||||
// - With STARTF_USESHOWWINDOW unset:
|
||||
// - nShowCmd is always SW_SHOWDEFAULT
|
||||
const SW_SHOWDEFAULT = 10;
|
||||
const STARTF_USESHOWWINDOW = 1;
|
||||
if (peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) {
|
||||
if (peb.ProcessParameters.dwFlags & std.os.windows.STARTF_USESHOWWINDOW != 0) {
|
||||
break :nShowCmd @truncate(peb.ProcessParameters.dwShowWindow);
|
||||
}
|
||||
break :nShowCmd SW_SHOWDEFAULT;
|
||||
|
||||
@@ -100,11 +100,11 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
|
||||
REG.MULTI_SZ,
|
||||
=> {
|
||||
comptime assert(@sizeOf(std.os.windows.UNICODE_STRING) % 2 == 0);
|
||||
const unicode = @as(*std.os.windows.UNICODE_STRING, @ptrCast(&tmp_bufs[i]));
|
||||
const unicode: *std.os.windows.UNICODE_STRING = @ptrCast(&tmp_bufs[i]);
|
||||
unicode.* = .{
|
||||
.Length = 0,
|
||||
.MaximumLength = max_value_len - @sizeOf(std.os.windows.UNICODE_STRING),
|
||||
.Buffer = @as([*]u16, @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..])),
|
||||
.Buffer = @ptrCast(tmp_bufs[i][@sizeOf(std.os.windows.UNICODE_STRING)..]),
|
||||
};
|
||||
break :blk unicode;
|
||||
},
|
||||
@@ -159,8 +159,8 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
|
||||
REG.MULTI_SZ,
|
||||
=> {
|
||||
var buf = @field(args, field.name).value_buf;
|
||||
const entry = @as(*align(1) const std.os.windows.UNICODE_STRING, @ptrCast(table[i + 1].EntryContext));
|
||||
const len = try std.unicode.utf16LeToUtf8(buf, entry.Buffer.?[0 .. entry.Length / 2]);
|
||||
const entry: *const std.os.windows.UNICODE_STRING = @ptrCast(table[i + 1].EntryContext);
|
||||
const len = try std.unicode.utf16LeToUtf8(buf, entry.slice());
|
||||
buf[len] = 0;
|
||||
},
|
||||
|
||||
@@ -168,7 +168,7 @@ fn getCpuInfoFromRegistry(core: usize, args: anytype) !void {
|
||||
REG.DWORD_BIG_ENDIAN,
|
||||
REG.QWORD,
|
||||
=> {
|
||||
const entry = @as([*]align(1) const u8, @ptrCast(table[i + 1].EntryContext));
|
||||
const entry: [*]const u8 = @ptrCast(table[i + 1].EntryContext);
|
||||
switch (@field(args, field.name).value_type) {
|
||||
REG.DWORD, REG.DWORD_BIG_ENDIAN => {
|
||||
@memcpy(@field(args, field.name).value_buf[0..4], entry[0..4]);
|
||||
|
||||
@@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a
|
||||
|
||||
#if defined(zig_thumb)
|
||||
|
||||
static inline void* zig_thumb_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2);
|
||||
@@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) {
|
||||
|
||||
#elif defined(zig_aarch64)
|
||||
|
||||
static inline void* zig_aarch64_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readx18qword(0x0);
|
||||
@@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) {
|
||||
|
||||
#elif defined(zig_x86_32)
|
||||
|
||||
static inline void* zig_x86_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readfsdword(0x18);
|
||||
@@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) {
|
||||
return teb;
|
||||
}
|
||||
|
||||
static inline void* zig_windows_peb(void) {
|
||||
void* peb = 0;
|
||||
#if defined(zig_msvc)
|
||||
peb = (void*)__readfsdword(0x30);
|
||||
#elif defined(zig_gnuc_asm)
|
||||
__asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb));
|
||||
#endif
|
||||
return peb;
|
||||
}
|
||||
|
||||
#elif defined(zig_x86_64)
|
||||
|
||||
static inline void* zig_x86_64_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readgsqword(0x30);
|
||||
@@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) {
|
||||
return teb;
|
||||
}
|
||||
|
||||
static inline void* zig_windows_peb(void) {
|
||||
void* peb = 0;
|
||||
#if defined(zig_msvc)
|
||||
peb = (void*)__readgsqword(0x60);
|
||||
#elif defined(zig_gnuc_asm)
|
||||
__asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb));
|
||||
#endif
|
||||
return peb;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(zig_loongarch)
|
||||
|
||||
+24
-4
@@ -4148,7 +4148,7 @@ static inline void zig_msvc_atomic_store_i128(zig_i128 volatile* obj, zig_i128 a
|
||||
|
||||
#if defined(zig_thumb)
|
||||
|
||||
static inline void* zig_thumb_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)_MoveFromCoprocessor(15, 0, 13, 0, 2);
|
||||
@@ -4160,7 +4160,7 @@ static inline void* zig_thumb_windows_teb(void) {
|
||||
|
||||
#elif defined(zig_aarch64)
|
||||
|
||||
static inline void* zig_aarch64_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readx18qword(0x0);
|
||||
@@ -4172,7 +4172,7 @@ static inline void* zig_aarch64_windows_teb(void) {
|
||||
|
||||
#elif defined(zig_x86_32)
|
||||
|
||||
static inline void* zig_x86_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readfsdword(0x18);
|
||||
@@ -4182,9 +4182,19 @@ static inline void* zig_x86_windows_teb(void) {
|
||||
return teb;
|
||||
}
|
||||
|
||||
static inline void* zig_windows_peb(void) {
|
||||
void* peb = 0;
|
||||
#if defined(zig_msvc)
|
||||
peb = (void*)__readfsdword(0x30);
|
||||
#elif defined(zig_gnuc_asm)
|
||||
__asm__ ("movl %%fs:0x30, %[ptr]" : [ptr] "=r" (peb));
|
||||
#endif
|
||||
return peb;
|
||||
}
|
||||
|
||||
#elif defined(zig_x86_64)
|
||||
|
||||
static inline void* zig_x86_64_windows_teb(void) {
|
||||
static inline void* zig_windows_teb(void) {
|
||||
void* teb = 0;
|
||||
#if defined(zig_msvc)
|
||||
teb = (void*)__readgsqword(0x30);
|
||||
@@ -4194,6 +4204,16 @@ static inline void* zig_x86_64_windows_teb(void) {
|
||||
return teb;
|
||||
}
|
||||
|
||||
static inline void* zig_windows_peb(void) {
|
||||
void* peb = 0;
|
||||
#if defined(zig_msvc)
|
||||
peb = (void*)__readgsqword(0x60);
|
||||
#elif defined(zig_gnuc_asm)
|
||||
__asm__ ("movq %%gs:0x60, %[ptr]" : [ptr] "=r" (peb));
|
||||
#endif
|
||||
return peb;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(zig_loongarch)
|
||||
|
||||
@@ -4,17 +4,16 @@ const fatal = std.process.fatal;
|
||||
extern fn add(a: u32, b: u32, addr: *usize) u32;
|
||||
|
||||
pub fn main(init: std.process.Init) void {
|
||||
const gpa = init.gpa;
|
||||
const io = init.io;
|
||||
|
||||
var di: std.debug.SelfInfo = .init;
|
||||
defer di.deinit(gpa);
|
||||
defer di.deinit(io);
|
||||
|
||||
var add_addr: usize = undefined;
|
||||
_ = add(1, 2, &add_addr);
|
||||
|
||||
const symbol = di.getSymbol(gpa, io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err});
|
||||
defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
|
||||
const symbol = di.getSymbol(io, add_addr) catch |err| fatal("failed to get symbol: {t}", .{err});
|
||||
defer if (symbol.source_location) |sl| std.debug.getDebugInfoAllocator().free(sl.file_name);
|
||||
|
||||
if (symbol.name == null) fatal("failed to resolve symbol name", .{});
|
||||
if (symbol.compile_unit_name == null) fatal("failed to resolve compile unit", .{});
|
||||
|
||||
@@ -127,7 +127,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
|
||||
.hStdOutput = null,
|
||||
.hStdError = windows.peb().ProcessParameters.hStdError,
|
||||
};
|
||||
var proc_info: windows.PROCESS_INFORMATION = undefined;
|
||||
var proc_info: windows.PROCESS.INFORMATION = undefined;
|
||||
|
||||
if (windows.kernel32.CreateProcessW(
|
||||
@constCast(verify_path.ptr),
|
||||
@@ -141,7 +141,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
|
||||
&startup_info,
|
||||
&proc_info,
|
||||
) == 0) {
|
||||
std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.kernel32.GetLastError()});
|
||||
std.process.fatal("kernel32 CreateProcessW failed with {t}", .{windows.GetLastError()});
|
||||
}
|
||||
|
||||
windows.CloseHandle(proc_info.hThread);
|
||||
@@ -156,9 +156,15 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
|
||||
else => |status| return windows.unexpectedStatus(status),
|
||||
}
|
||||
|
||||
var exit_code: windows.DWORD = undefined;
|
||||
if (windows.kernel32.GetExitCodeProcess(child_proc, &exit_code) == 0) {
|
||||
return error.UnableToGetExitCode;
|
||||
var info: windows.PROCESS.BASIC_INFORMATION = undefined;
|
||||
switch (windows.ntdll.NtQueryInformationProcess(
|
||||
child_proc,
|
||||
.BasicInformation,
|
||||
&info,
|
||||
@sizeOf(windows.PROCESS.BASIC_INFORMATION),
|
||||
null,
|
||||
)) {
|
||||
.SUCCESS => return @intFromEnum(info.ExitStatus),
|
||||
else => return error.UnableToGetExitCode,
|
||||
}
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ fn testArgv(expected_args: []const [*:0]const u16) !void {
|
||||
defer arena_state.deinit();
|
||||
const allocator = arena_state.allocator();
|
||||
|
||||
const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine;
|
||||
const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)];
|
||||
const raw_args: std.process.Args = .{ .vector = cmd_line_w };
|
||||
const raw_args: std.process.Args = .{
|
||||
.vector = std.os.windows.peb().ProcessParameters.CommandLine.slice(),
|
||||
};
|
||||
const args = try raw_args.toSlice(allocator);
|
||||
var wtf8_buf = std.array_list.Managed(u8).init(allocator);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user