Files
zig/lib/std/os/windows.zig
T
2025-12-14 18:45:07 -05:00

7297 lines
260 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! This file contains thin wrappers around Windows-specific APIs, with these
//! specific goals in mind:
//! * Convert "errno"-style error codes into Zig errors.
//! * When null-terminated or WTF16LE byte buffers are required, provide APIs which accept
//! slices as well as APIs which accept null-terminated WTF16LE byte buffers.
const builtin = @import("builtin");
const native_arch = builtin.cpu.arch;
const std = @import("../std.zig");
const Io = std.Io;
const mem = std.mem;
const assert = std.debug.assert;
const math = std.math;
const maxInt = std.math.maxInt;
const UnexpectedError = std.posix.UnexpectedError;
test {
if (builtin.os.tag == .windows) {
_ = @import("windows/test.zig");
}
}
pub const advapi32 = @import("windows/advapi32.zig");
pub const kernel32 = @import("windows/kernel32.zig");
pub const ntdll = @import("windows/ntdll.zig");
pub const ws2_32 = @import("windows/ws2_32.zig");
pub const crypt32 = @import("windows/crypt32.zig");
pub const nls = @import("windows/nls.zig");
pub const FILE = struct {
// ref: km/ntddk.h
pub const END_OF_FILE_INFORMATION = extern struct {
EndOfFile: LARGE_INTEGER,
};
pub const ALIGNMENT_INFORMATION = extern struct {
AlignmentRequirement: ULONG,
};
pub const NAME_INFORMATION = extern struct {
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
pub const DISPOSITION = packed struct(ULONG) {
DELETE: bool = false,
POSIX_SEMANTICS: bool = false,
FORCE_IMAGE_SECTION_CHECK: bool = false,
ON_CLOSE: bool = false,
IGNORE_READONLY_ATTRIBUTE: bool = false,
Reserved5: u27 = 0,
pub const DO_NOT_DELETE: DISPOSITION = .{};
pub const INFORMATION = extern struct {
DeleteFile: BOOLEAN,
pub const EX = extern struct {
Flags: DISPOSITION,
};
};
};
pub const FS_VOLUME_INFORMATION = extern struct {
VolumeCreationTime: LARGE_INTEGER,
VolumeSerialNumber: ULONG,
VolumeLabelLength: ULONG,
SupportsObjects: BOOLEAN,
VolumeLabel: [0]WCHAR,
pub fn getVolumeLabel(fvi: *const FS_VOLUME_INFORMATION) []const WCHAR {
return (&fvi).ptr[0..@divExact(fvi.VolumeLabelLength, @sizeOf(WCHAR))];
}
};
// ref: km/ntifs.h
pub const PIPE = struct {
/// Define the `NamedPipeType` flags for `NtCreateNamedPipeFile`
pub const TYPE = packed struct(ULONG) {
TYPE: enum(u1) {
BYTE_STREAM = 0b0,
MESSAGE = 0b1,
} = .BYTE_STREAM,
REMOTE_CLIENTS: enum(u1) {
ACCEPT = 0b0,
REJECT = 0b1,
} = .ACCEPT,
Reserved2: u30 = 0,
pub const VALID_MASK: TYPE = .{
.TYPE = .MESSAGE,
.REMOTE_CLIENTS = .REJECT,
};
};
/// Define the `CompletionMode` flags for `NtCreateNamedPipeFile`
pub const COMPLETION_MODE = packed struct(ULONG) {
OPERATION: enum(u1) {
QUEUE = 0b0,
COMPLETE = 0b1,
} = .QUEUE,
Reserved1: u31 = 0,
};
/// Define the `ReadMode` flags for `NtCreateNamedPipeFile`
pub const READ_MODE = packed struct(ULONG) {
MODE: enum(u1) {
BYTE_STREAM = 0b0,
MESSAGE = 0b1,
},
Reserved1: u31 = 0,
};
/// Define the `NamedPipeConfiguration` flags for `NtQueryInformationFile`
pub const CONFIGURATION = enum(ULONG) {
INBOUND = 0x00000000,
OUTBOUND = 0x00000001,
FULL_DUPLEX = 0x00000002,
};
/// Define the `NamedPipeState` flags for `NtQueryInformationFile`
pub const STATE = enum(ULONG) {
DISCONNECTED = 0x00000001,
LISTENING = 0x00000002,
CONNECTED = 0x00000003,
CLOSING = 0x00000004,
};
/// Define the `NamedPipeEnd` flags for `NtQueryInformationFile`
pub const END = enum(ULONG) {
CLIENT = 0x00000000,
SERVER = 0x00000001,
};
pub const INFORMATION = extern struct {
ReadMode: READ_MODE,
CompletionMode: COMPLETION_MODE,
};
pub const LOCAL_INFORMATION = extern struct {
NamedPipeType: TYPE,
NamedPipeConfiguration: CONFIGURATION,
MaximumInstances: ULONG,
CurrentInstances: ULONG,
InboundQuota: ULONG,
ReadDataAvailable: ULONG,
OutboundQuota: ULONG,
WriteQuotaAvailable: ULONG,
NamedPipeState: STATE,
NamedPipeEnd: END,
};
pub const REMOTE_INFORMATION = extern struct {
CollectDataTime: LARGE_INTEGER,
MaximumCollectionCount: ULONG,
};
pub const WAIT_FOR_BUFFER = extern struct {
Timeout: LARGE_INTEGER,
NameLength: ULONG,
TimeoutSpecified: BOOLEAN,
Name: [PATH_MAX_WIDE]WCHAR,
pub const WAIT_FOREVER: LARGE_INTEGER = std.math.minInt(LARGE_INTEGER);
pub fn init(opts: struct {
Timeout: ?LARGE_INTEGER = null,
Name: []const WCHAR,
}) WAIT_FOR_BUFFER {
var fpwfb: WAIT_FOR_BUFFER = .{
.Timeout = opts.Timeout orelse undefined,
.NameLength = @intCast(@sizeOf(WCHAR) * opts.Name.len),
.TimeoutSpecified = @intFromBool(opts.Timeout != null),
.Name = undefined,
};
@memcpy(fpwfb.Name[0..opts.Name.len], opts.Name);
return fpwfb;
}
pub fn getName(fpwfb: *const WAIT_FOR_BUFFER) []const WCHAR {
return fpwfb.Name[0..@divExact(fpwfb.NameLength, @sizeOf(WCHAR))];
}
pub fn toBuffer(fpwfb: *const WAIT_FOR_BUFFER) []const u8 {
const start: [*]const u8 = @ptrCast(fpwfb);
return start[0 .. @offsetOf(WAIT_FOR_BUFFER, "Name") + fpwfb.NameLength];
}
};
};
pub const ALL_INFORMATION = extern struct {
BasicInformation: BASIC_INFORMATION,
StandardInformation: STANDARD_INFORMATION,
InternalInformation: INTERNAL_INFORMATION,
EaInformation: EA_INFORMATION,
AccessInformation: ACCESS_INFORMATION,
PositionInformation: POSITION_INFORMATION,
ModeInformation: MODE.INFORMATION,
AlignmentInformation: ALIGNMENT_INFORMATION,
NameInformation: NAME_INFORMATION,
};
pub const INTERNAL_INFORMATION = extern struct {
IndexNumber: LARGE_INTEGER,
};
pub const EA_INFORMATION = extern struct {
EaSize: ULONG,
};
pub const ACCESS_INFORMATION = extern struct {
AccessFlags: ACCESS_MASK,
};
pub const RENAME_INFORMATION = extern struct {
Flags: FLAGS,
RootDirectory: ?HANDLE,
FileNameLength: ULONG,
FileName: [PATH_MAX_WIDE]WCHAR,
pub fn init(opts: struct {
Flags: FLAGS = .{},
RootDirectory: ?HANDLE = null,
FileName: []const WCHAR,
}) RENAME_INFORMATION {
var fri: RENAME_INFORMATION = .{
.Flags = opts.Flags,
.RootDirectory = opts.RootDirectory,
.FileNameLength = @intCast(@sizeOf(WCHAR) * opts.FileName.len),
.FileName = undefined,
};
@memcpy(fri.FileName[0..opts.FileName.len], opts.FileName);
return fri;
}
pub const FLAGS = packed struct(ULONG) {
REPLACE_IF_EXISTS: bool = false,
POSIX_SEMANTICS: bool = false,
SUPPRESS_PIN_STATE_INHERITANCE: bool = false,
SUPPRESS_STORAGE_RESERVE_INHERITANCE: bool = false,
AVAILABLE_SPACE: enum(u2) {
NO_PRESERVE = 0b00,
NO_INCREASE = 0b01,
NO_DECREASE = 0b10,
PRESERVE = 0b11,
} = .NO_PRESERVE,
IGNORE_READONLY_ATTRIBUTE: bool = false,
RESIZE_SR: enum(u2) {
NO_FORCE = 0b00,
FORCE_TARGET = 0b01,
FORCE_SOURCE = 0b10,
FORCE = 0b11,
} = .NO_FORCE,
Reserved9: u23 = 0,
};
pub fn getFileName(ri: *const RENAME_INFORMATION) []const WCHAR {
return ri.FileName[0..@divExact(ri.FileNameLength, @sizeOf(WCHAR))];
}
pub fn toBuffer(fri: *const RENAME_INFORMATION) []const u8 {
const start: [*]const u8 = @ptrCast(fri);
return start[0 .. @offsetOf(RENAME_INFORMATION, "FileName") + fri.FileNameLength];
}
};
// ref: km/wdm.h
pub const INFORMATION_CLASS = enum(c_int) {
Directory = 1,
FullDirectory = 2,
BothDirectory = 3,
Basic = 4,
Standard = 5,
Internal = 6,
Ea = 7,
Access = 8,
Name = 9,
Rename = 10,
Link = 11,
Names = 12,
Disposition = 13,
Position = 14,
FullEa = 15,
Mode = 16,
Alignment = 17,
All = 18,
Allocation = 19,
EndOfFile = 20,
AlternateName = 21,
Stream = 22,
Pipe = 23,
PipeLocal = 24,
PipeRemote = 25,
MailslotQuery = 26,
MailslotSet = 27,
Compression = 28,
ObjectId = 29,
Completion = 30,
MoveCluster = 31,
Quota = 32,
ReparsePoint = 33,
NetworkOpen = 34,
AttributeTag = 35,
Tracking = 36,
IdBothDirectory = 37,
IdFullDirectory = 38,
ValidDataLength = 39,
ShortName = 40,
IoCompletionNotification = 41,
IoStatusBlockRange = 42,
IoPriorityHint = 43,
SfioReserve = 44,
SfioVolume = 45,
HardLink = 46,
ProcessIdsUsingFile = 47,
NormalizedName = 48,
NetworkPhysicalName = 49,
IdGlobalTxDirectory = 50,
IsRemoteDevice = 51,
Unused = 52,
NumaNode = 53,
StandardLink = 54,
RemoteProtocol = 55,
RenameBypassAccessCheck = 56,
LinkBypassAccessCheck = 57,
VolumeName = 58,
Id = 59,
IdExtdDirectory = 60,
ReplaceCompletion = 61,
HardLinkFullId = 62,
IdExtdBothDirectory = 63,
DispositionEx = 64,
RenameEx = 65,
RenameExBypassAccessCheck = 66,
DesiredStorageClass = 67,
Stat = 68,
MemoryPartition = 69,
StatLx = 70,
CaseSensitive = 71,
LinkEx = 72,
LinkExBypassAccessCheck = 73,
StorageReserveId = 74,
CaseSensitiveForceAccessCheck = 75,
KnownFolder = 76,
StatBasic = 77,
Id64ExtdDirectory = 78,
Id64ExtdBothDirectory = 79,
IdAllExtdDirectory = 80,
IdAllExtdBothDirectory = 81,
StreamReservation = 82,
MupProvider = 83,
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
};
pub const BASIC_INFORMATION = extern struct {
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
FileAttributes: ATTRIBUTE,
};
pub const STANDARD_INFORMATION = extern struct {
AllocationSize: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
NumberOfLinks: ULONG,
DeletePending: BOOLEAN,
Directory: BOOLEAN,
};
pub const POSITION_INFORMATION = extern struct {
CurrentByteOffset: LARGE_INTEGER,
};
pub const FS_DEVICE_INFORMATION = extern struct {
DeviceType: DEVICE_TYPE,
Characteristics: ULONG,
};
// ref: um/WinBase.h
pub const ATTRIBUTE_TAG_INFO = extern struct {
FileAttributes: DWORD,
ReparseTag: IO_REPARSE_TAG,
};
// ref: um/winnt.h
pub const SHARE = packed struct(ULONG) {
/// The file can be opened for read access by other threads.
READ: bool = false,
/// The file can be opened for write access by other threads.
WRITE: bool = false,
/// The file can be opened for delete access by other threads.
DELETE: bool = false,
Reserved3: u29 = 0,
pub const VALID_FLAGS: SHARE = .{
.READ = true,
.WRITE = true,
.DELETE = true,
};
};
pub const ATTRIBUTE = packed struct(ULONG) {
/// The file is read only. Applications can read the file, but cannot write to or delete it.
READONLY: bool = false,
/// The file is hidden. Do not include it in an ordinary directory listing.
HIDDEN: bool = false,
/// The file is part of or used exclusively by an operating system.
SYSTEM: bool = false,
Reserved3: u1 = 0,
DIRECTORY: bool = false,
/// The file should be archived. Applications use this attribute to mark files for backup or removal.
ARCHIVE: bool = false,
DEVICE: bool = false,
/// The file does not have other attributes set. This attribute is valid only if used alone.
NORMAL: bool = false,
/// The file is being used for temporary storage.
TEMPORARY: bool = false,
SPARSE_FILE: bool = false,
REPARSE_POINT: bool = false,
COMPRESSED: bool = false,
/// The data of a file is not immediately available. This attribute indicates that file data is physically moved to offline storage.
/// This attribute is used by Remote Storage, the hierarchical storage management software. Applications should not arbitrarily change this attribute.
OFFLINE: bool = false,
NOT_CONTENT_INDEXED: bool = false,
/// The file or directory is encrypted. For a file, this means that all data in the file is encrypted. For a directory, this means that encryption is
/// the default for newly created files and subdirectories. For more information, see File Encryption.
///
/// This flag has no effect if `SYSTEM` is also specified.
///
/// This flag is not supported on Home, Home Premium, Starter, or ARM editions of Windows.
ENCRYPTED: bool = false,
INTEGRITY_STREAM: bool = false,
VIRTUAL: bool = false,
NO_SCRUB_DATA: bool = false,
EA_or_RECALL_ON_OPEN: bool = false,
PINNED: bool = false,
UNPINNED: bool = false,
Reserved21: u1 = 0,
RECALL_ON_DATA_ACCESS: bool = false,
Reserved23: u6 = 0,
STRICTLY_SEQUENTIAL: bool = false,
Reserved30: u2 = 0,
};
// ref: um/winternl.h
/// Define the create disposition values
pub const CREATE_DISPOSITION = enum(ULONG) {
/// If the file already exists, replace it with the given file. If it does not, create the given file.
SUPERSEDE = 0x00000000,
/// If the file already exists, open it instead of creating a new file. If it does not, fail the request and do not create a new file.
OPEN = 0x00000001,
/// If the file already exists, fail the request and do not create or open the given file. If it does not, create the given file.
CREATE = 0x00000002,
/// If the file already exists, open it. If it does not, create the given file.
OPEN_IF = 0x00000003,
/// If the file already exists, open it and overwrite it. If it does not, fail the request.
OVERWRITE = 0x00000004,
/// If the file already exists, open it and overwrite it. If it does not, create the given file.
OVERWRITE_IF = 0x00000005,
pub const MAXIMUM_DISPOSITION: CREATE_DISPOSITION = .OVERWRITE_IF;
};
/// Define the create/open option flags
pub const MODE = packed struct(ULONG) {
/// The file being created or opened is a directory file. With this flag, the CreateDisposition parameter must be set to `.CREATE`, `.FILE_OPEN`, or `.OPEN_IF`.
/// With this flag, other compatible CreateOptions flags include only the following: `SYNCHRONOUS_IO`, `WRITE_THROUGH`, `OPEN_FOR_BACKUP_INTENT`, and `OPEN_BY_FILE_ID`.
DIRECTORY_FILE: bool = false,
/// Applications that write data to the file must actually transfer the data into the file before any requested write operation is considered complete.
/// This flag is automatically set if the CreateOptions flag `NO_INTERMEDIATE_BUFFERING` is set.
WRITE_THROUGH: bool = false,
/// All accesses to the file are sequential.
SEQUENTIAL_ONLY: bool = false,
/// The file cannot be cached or buffered in a driver's internal buffers. This flag is incompatible with the DesiredAccess `FILE_APPEND_DATA` flag.
NO_INTERMEDIATE_BUFFERING: bool = false,
IO: enum(u2) {
/// All operations on the file are performed asynchronously.
ASYNCHRONOUS = 0b00,
/// All operations on the file are performed synchronously. Any wait on behalf of the caller is subject to premature termination from alerts.
/// This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess `SYNCHRONIZE` flag also must be set.
SYNCHRONOUS_ALERT = 0b01,
/// All operations on the file are performed synchronously. Waits in the system to synchronize I/O queuing and completion are not subject to alerts.
/// This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess `SYNCHRONIZE` flag also must be set.
SYNCHRONOUS_NONALERT = 0b10,
_,
pub const VALID_FLAGS: @This() = @enumFromInt(0b11);
} = .ASYNCHRONOUS,
/// The file being opened must not be a directory file or this call fails. The file object being opened can represent a data file, a logical, virtual, or physical
/// device, or a volume.
NON_DIRECTORY_FILE: bool = false,
/// Create a tree connection for this file in order to open it over the network. This flag is not used by device and intermediate drivers.
CREATE_TREE_CONNECTION: bool = false,
/// Complete this operation immediately with an alternate success code of `STATUS_OPLOCK_BREAK_IN_PROGRESS` if the target file is oplocked, rather than blocking
/// the caller's thread. If the file is oplocked, another caller already has access to the file. This flag is not used by device and intermediate drivers.
COMPLETE_IF_OPLOCKED: bool = false,
/// If the extended attributes on an existing file being opened indicate that the caller must understand EAs to properly interpret the file, fail this request
/// because the caller does not understand how to deal with EAs. This flag is irrelevant for device and intermediate drivers.
NO_EA_KNOWLEDGE: bool = false,
OPEN_REMOTE_INSTANCE: bool = false,
/// Accesses to the file can be random, so no sequential read-ahead operations should be performed on the file by FSDs or the system.
RANDOM_ACCESS: bool = false,
/// Delete the file when the last handle to it is passed to `NtClose`. If this flag is set, the `DELETE` flag must be set in the DesiredAccess parameter.
DELETE_ON_CLOSE: bool = false,
/// The file name that is specified by the `ObjectAttributes` parameter includes the 8-byte file reference number for the file. This number is assigned by and
/// specific to the particular file system. If the file is a reparse point, the file name will also include the name of a device. Note that the FAT file system
/// does not support this flag. This flag is not used by device and intermediate drivers.
OPEN_BY_FILE_ID: bool = false,
/// The file is being opened for backup intent. Therefore, the system should check for certain access rights and grant the caller the appropriate access to the
/// file before checking the DesiredAccess parameter against the file's security descriptor. This flag not used by device and intermediate drivers.
OPEN_FOR_BACKUP_INTENT: bool = false,
/// Suppress inheritance of `FILE_ATTRIBUTE.COMPRESSED` from the parent directory. This allows creation of a non-compressed file in a directory that is marked
/// compressed.
NO_COMPRESSION: bool = false,
/// The file is being opened and an opportunistic lock on the file is being requested as a single atomic operation. The file system checks for oplocks before it
/// performs the create operation and will fail the create with a return code of STATUS_CANNOT_BREAK_OPLOCK if the result would be to break an existing oplock.
/// For more information, see the Remarks section.
///
/// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This flag is not supported.
///
/// This flag is supported on the following file systems: NTFS, FAT, and exFAT.
OPEN_REQUIRING_OPLOCK: bool = false,
Reserved17: u3 = 0,
/// This flag allows an application to request a filter opportunistic lock to prevent other applications from getting share violations. If there are already open
/// handles, the create request will fail with STATUS_OPLOCK_NOT_GRANTED. For more information, see the Remarks section.
RESERVE_OPFILTER: bool = false,
/// Open a file with a reparse point and bypass normal reparse point processing for the file. For more information, see the Remarks section.
OPEN_REPARSE_POINT: bool = false,
/// Instructs any filters that perform offline storage or virtualization to not recall the contents of the file as a result of this open.
OPEN_NO_RECALL: bool = false,
/// This flag instructs the file system to capture the user associated with the calling thread. Any subsequent calls to `FltQueryVolumeInformation` or
/// `ZwQueryVolumeInformationFile` using the returned handle will assume the captured user, rather than the calling user at the time, for purposes of computing
/// the free space available to the caller. This applies to the following FsInformationClass values: `FileFsSizeInformation`, `FileFsFullSizeInformation`, and
/// `FileFsFullSizeInformationEx`.
OPEN_FOR_FREE_SPACE_QUERY: bool = false,
Reserved24: u8 = 0,
pub const VALID_OPTION_FLAGS: MODE = .{
.DIRECTORY_FILE = true,
.WRITE_THROUGH = true,
.SEQUENTIAL_ONLY = true,
.NO_INTERMEDIATE_BUFFERING = true,
.IO = .VALID_FLAGS,
.NON_DIRECTORY_FILE = true,
.CREATE_TREE_CONNECTION = true,
.COMPLETE_IF_OPLOCKED = true,
.NO_EA_KNOWLEDGE = true,
.OPEN_REMOTE_INSTANCE = true,
.RANDOM_ACCESS = true,
.DELETE_ON_CLOSE = true,
.OPEN_BY_FILE_ID = true,
.OPEN_FOR_BACKUP_INTENT = true,
.NO_COMPRESSION = true,
.OPEN_REQUIRING_OPLOCK = true,
.Reserved17 = 0b111,
.RESERVE_OPFILTER = true,
.OPEN_REPARSE_POINT = true,
.OPEN_NO_RECALL = true,
.OPEN_FOR_FREE_SPACE_QUERY = true,
};
pub const VALID_PIPE_OPTION_FLAGS: MODE = .{
.WRITE_THROUGH = true,
.IO = .VALID_FLAGS,
};
pub const VALID_MAILSLOT_OPTION_FLAGS: MODE = .{
.WRITE_THROUGH = true,
.IO = .VALID_FLAGS,
};
pub const VALID_SET_OPTION_FLAGS: MODE = .{
.WRITE_THROUGH = true,
.SEQUENTIAL_ONLY = true,
.IO = .VALID_FLAGS,
};
// ref: km/ntifs.h
pub const INFORMATION = extern struct {
/// The set of flags that specify the mode in which the file can be accessed. These flags are a subset of `MODE`.
Mode: MODE,
};
};
};
// 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 Max: @typeInfo(@This()).@"enum".tag_type = 117;
};
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 Max: @typeInfo(@This()).@"enum".tag_type = 60;
};
// ref: km/ntifs.h
pub const HEAP = opaque {
pub const FLAGS = packed struct(u8) {
/// Serialized access is not used when the heap functions access this heap. This option
/// applies to all subsequent heap function calls. Alternatively, you can specify this
/// option on individual heap function calls.
///
/// The low-fragmentation heap (LFH) cannot be enabled for a heap created with this option.
///
/// A heap created with this option cannot be locked.
NO_SERIALIZE: bool = false,
/// Specifies that the heap is growable. Must be specified if `HeapBase` is `NULL`.
GROWABLE: bool = false,
/// The system raises an exception to indicate failure (for example, an out-of-memory
/// condition) for calls to `HeapAlloc` and `HeapReAlloc` instead of returning `NULL`.
///
/// To ensure that exceptions are generated for all calls to an allocation function, specify
/// `GENERATE_EXCEPTIONS` in the call to `HeapCreate`. In this case, it is not necessary to
/// additionally specify `GENERATE_EXCEPTIONS` in the allocation function calls.
GENERATE_EXCEPTIONS: bool = false,
/// The allocated memory will be initialized to zero. Otherwise, the memory is not
/// initialized to zero.
ZERO_MEMORY: bool = false,
REALLOC_IN_PLACE_ONLY: bool = false,
TAIL_CHECKING_ENABLED: bool = false,
FREE_CHECKING_ENABLED: bool = false,
DISABLE_COALESCE_ON_FREE: bool = false,
pub const CLASS = enum(u4) {
/// process heap
PROCESS,
/// private heap
PRIVATE,
/// Kernel Heap
KERNEL,
/// GDI heap
GDI,
/// User heap
USER,
/// Console heap
CONSOLE,
/// User Desktop heap
USER_DESKTOP,
/// Csrss Shared heap
CSRSS_SHARED,
/// Csr Port heap
CSR_PORT,
_,
pub const MASK: CLASS = @enumFromInt(maxInt(@typeInfo(CLASS).@"enum".tag_type));
};
pub const CREATE = packed struct(ULONG) {
COMMON: FLAGS = .{},
SEGMENT_HEAP: bool = false,
/// Only applies to segment heap. Applies pointer obfuscation which is
/// generally excessive and unnecessary but is necessary for certain insecure
/// heaps in win32k.
///
/// Specifying HEAP_CREATE_HARDENED prevents the heap from using locks as
/// pointers would potentially be exposed in heap metadata lock variables.
/// Callers are therefore responsible for synchronizing access to hardened heaps.
HARDENED: bool = false,
Reserved10: u2 = 0,
CLASS: CLASS = @enumFromInt(0),
/// Create heap with 16 byte alignment (obsolete)
ALIGN_16: bool = false,
/// Create heap call tracing enabled (obsolete)
ENABLE_TRACING: bool = false,
/// Create heap with executable pages
///
/// All memory blocks that are allocated from this heap allow code execution, if the
/// hardware enforces data execution prevention. Use this flag heap in applications that
/// run code from the heap. If `ENABLE_EXECUTE` is not specified and an application
/// attempts to run code from a protected page, the application receives an exception
/// with the status code `STATUS_ACCESS_VIOLATION`.
ENABLE_EXECUTE: bool = false,
Reserved19: u13 = 0,
pub const VALID_MASK: CREATE = .{
.COMMON = .{
.NO_SERIALIZE = true,
.GROWABLE = true,
.GENERATE_EXCEPTIONS = true,
.ZERO_MEMORY = true,
.REALLOC_IN_PLACE_ONLY = true,
.TAIL_CHECKING_ENABLED = true,
.FREE_CHECKING_ENABLED = true,
.DISABLE_COALESCE_ON_FREE = true,
},
.CLASS = .MASK,
.ALIGN_16 = true,
.ENABLE_TRACING = true,
.ENABLE_EXECUTE = true,
.SEGMENT_HEAP = true,
.HARDENED = true,
};
};
pub const ALLOCATION = packed struct(ULONG) {
COMMON: FLAGS = .{},
SETTABLE_USER: packed struct(u4) {
VALUE: u1 = 0,
FLAGS: packed struct(u3) {
FLAG1: bool = false,
FLAG2: bool = false,
FLAG3: bool = false,
} = .{},
} = .{},
CLASS: CLASS = @enumFromInt(0),
Reserved16: u2 = 0,
TAG: u12 = 0,
Reserved30: u2 = 0,
};
};
pub const RTL_PARAMETERS = extern struct {
Length: ULONG,
SegmentReserve: SIZE_T,
SegmentCommit: SIZE_T,
DeCommitFreeBlockThreshold: SIZE_T,
DeCommitTotalFreeThreshold: SIZE_T,
MaximumAllocationSize: SIZE_T,
VirtualMemoryThreshold: SIZE_T,
InitialCommit: SIZE_T,
InitialReserve: SIZE_T,
CommitRoutine: *const COMMIT_ROUTINE,
Reserved: [2]SIZE_T = @splat(0),
pub const COMMIT_ROUTINE = fn (
Base: PVOID,
CommitAddress: *PVOID,
CommitSize: *SIZE_T,
) callconv(.winapi) NTSTATUS;
pub const SEGMENT = extern struct {
Version: VERSION,
Size: USHORT,
Flags: FLG,
MemorySource: MEMORY_SOURCE,
Reserved: [4]SIZE_T,
pub const VERSION = enum(USHORT) {
CURRENT = 3,
_,
};
pub const FLG = packed struct(ULONG) {
USE_PAGE_HEAP: bool = false,
NO_LFH: bool = false,
Reserved2: u30 = 0,
pub const VALID_FLAGS: FLG = .{
.USE_PAGE_HEAP = true,
.NO_LFH = true,
};
};
pub const MEMORY_SOURCE = extern struct {
Flags: ULONG,
MemoryTypeMask: TYPE,
NumaNode: ULONG,
u: extern union {
PartitionHandle: HANDLE,
Callbacks: *const VA_CALLBACKS,
},
Reserved: [2]SIZE_T = @splat(0),
pub const TYPE = enum(ULONG) {
Paged,
NonPaged,
@"64KPage",
LargePage,
HugePage,
Custom,
_,
pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len;
};
pub const VA_CALLBACKS = extern struct {
CallbackContext: HANDLE,
AllocateVirtualMemory: *const ALLOCATE_VIRTUAL_MEMORY_EX_CALLBACK,
FreeVirtualMemory: *const FREE_VIRTUAL_MEMORY_EX_CALLBACK,
QueryVirtualMemory: *const QUERY_VIRTUAL_MEMORY_CALLBACK,
pub const ALLOCATE_VIRTUAL_MEMORY_EX_CALLBACK = fn (
CallbackContext: HANDLE,
BaseAddress: *PVOID,
RegionSize: *SIZE_T,
AllocationType: ULONG,
PageProtection: ULONG,
ExtendedParameters: ?[*]MEM.EXTENDED_PARAMETER,
ExtendedParameterCount: ULONG,
) callconv(.c) NTSTATUS;
pub const FREE_VIRTUAL_MEMORY_EX_CALLBACK = fn (
CallbackContext: HANDLE,
ProcessHandle: HANDLE,
BaseAddress: *PVOID,
RegionSize: *SIZE_T,
FreeType: ULONG,
) callconv(.c) NTSTATUS;
pub const QUERY_VIRTUAL_MEMORY_CALLBACK = fn (
CallbackContext: HANDLE,
ProcessHandle: HANDLE,
BaseAddress: *PVOID,
MemoryInformationClass: MEMORY_INFO_CLASS,
MemoryInformation: PVOID,
MemoryInformationLength: SIZE_T,
ReturnLength: ?*SIZE_T,
) callconv(.c) NTSTATUS;
pub const MEMORY_INFO_CLASS = enum(c_int) {
Basic,
_,
};
};
};
};
};
};
pub const CTL_CODE = packed struct(ULONG) {
Method: METHOD,
Function: u12,
Access: FILE_ACCESS,
DeviceType: FILE_DEVICE,
pub const METHOD = enum(u2) {
BUFFERED = 0,
IN_DIRECT = 1,
OUT_DIRECT = 2,
NEITHER = 3,
};
pub const FILE_ACCESS = packed struct(u2) {
READ: bool = false,
WRITE: bool = false,
pub const ANY: FILE_ACCESS = .{ .READ = false, .WRITE = false };
pub const SPECIAL = ANY;
};
pub const FILE_DEVICE = enum(u16) {
BEEP = 0x00000001,
CD_ROM = 0x00000002,
CD_ROM_FILE_SYSTEM = 0x00000003,
CONTROLLER = 0x00000004,
DATALINK = 0x00000005,
DFS = 0x00000006,
DISK = 0x00000007,
DISK_FILE_SYSTEM = 0x00000008,
FILE_SYSTEM = 0x00000009,
INPORT_PORT = 0x0000000a,
KEYBOARD = 0x0000000b,
MAILSLOT = 0x0000000c,
MIDI_IN = 0x0000000d,
MIDI_OUT = 0x0000000e,
MOUSE = 0x0000000f,
MULTI_UNC_PROVIDER = 0x00000010,
NAMED_PIPE = 0x00000011,
NETWORK = 0x00000012,
NETWORK_BROWSER = 0x00000013,
NETWORK_FILE_SYSTEM = 0x00000014,
NULL = 0x00000015,
PARALLEL_PORT = 0x00000016,
PHYSICAL_NETCARD = 0x00000017,
PRINTER = 0x00000018,
SCANNER = 0x00000019,
SERIAL_MOUSE_PORT = 0x0000001a,
SERIAL_PORT = 0x0000001b,
SCREEN = 0x0000001c,
SOUND = 0x0000001d,
STREAMS = 0x0000001e,
TAPE = 0x0000001f,
TAPE_FILE_SYSTEM = 0x00000020,
TRANSPORT = 0x00000021,
UNKNOWN = 0x00000022,
VIDEO = 0x00000023,
VIRTUAL_DISK = 0x00000024,
WAVE_IN = 0x00000025,
WAVE_OUT = 0x00000026,
@"8042_PORT" = 0x00000027,
NETWORK_REDIRECTOR = 0x00000028,
BATTERY = 0x00000029,
BUS_EXTENDER = 0x0000002a,
MODEM = 0x0000002b,
VDM = 0x0000002c,
MASS_STORAGE = 0x0000002d,
SMB = 0x0000002e,
KS = 0x0000002f,
CHANGER = 0x00000030,
SMARTCARD = 0x00000031,
ACPI = 0x00000032,
DVD = 0x00000033,
FULLSCREEN_VIDEO = 0x00000034,
DFS_FILE_SYSTEM = 0x00000035,
DFS_VOLUME = 0x00000036,
SERENUM = 0x00000037,
TERMSRV = 0x00000038,
KSEC = 0x00000039,
FIPS = 0x0000003A,
INFINIBAND = 0x0000003B,
VMBUS = 0x0000003E,
CRYPT_PROVIDER = 0x0000003F,
WPD = 0x00000040,
BLUETOOTH = 0x00000041,
MT_COMPOSITE = 0x00000042,
MT_TRANSPORT = 0x00000043,
BIOMETRIC = 0x00000044,
PMI = 0x00000045,
EHSTOR = 0x00000046,
DEVAPI = 0x00000047,
GPIO = 0x00000048,
USBEX = 0x00000049,
CONSOLE = 0x00000050,
NFP = 0x00000051,
SYSENV = 0x00000052,
VIRTUAL_BLOCK = 0x00000053,
POINT_OF_SERVICE = 0x00000054,
STORAGE_REPLICATION = 0x00000055,
TRUST_ENV = 0x00000056,
UCM = 0x00000057,
UCMTCPCI = 0x00000058,
PERSISTENT_MEMORY = 0x00000059,
NVDIMM = 0x0000005a,
HOLOGRAPHIC = 0x0000005b,
SDFXHCI = 0x0000005c,
UCMUCSI = 0x0000005d,
PRM = 0x0000005e,
EVENT_COLLECTOR = 0x0000005f,
USB4 = 0x00000060,
SOUNDWIRE = 0x00000061,
MOUNTMGRCONTROLTYPE = 'm',
_,
};
};
pub const IOCTL = struct {
pub const MOUNTMGR = struct {
pub const QUERY_POINTS: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
pub const QUERY_DOS_VOLUME_PATH: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
};
};
pub const FSCTL = struct {
pub const SET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 41, .Method = .BUFFERED, .Access = .SPECIAL };
pub const GET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 42, .Method = .BUFFERED, .Access = .ANY };
pub const PIPE = struct {
pub const ASSIGN_EVENT: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 0, .Method = .BUFFERED, .Access = .ANY };
pub const DISCONNECT: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 1, .Method = .BUFFERED, .Access = .ANY };
pub const LISTEN: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
pub const PEEK: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 3, .Method = .BUFFERED, .Access = .{ .READ = true } };
pub const QUERY_EVENT: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 4, .Method = .BUFFERED, .Access = .ANY };
pub const TRANSCEIVE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 5, .Method = .NEITHER, .Access = .{ .READ = true, .WRITE = true } };
pub const WAIT: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 6, .Method = .BUFFERED, .Access = .ANY };
pub const IMPERSONATE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 7, .Method = .BUFFERED, .Access = .ANY };
pub const SET_CLIENT_PROCESS: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 8, .Method = .BUFFERED, .Access = .ANY };
pub const QUERY_CLIENT_PROCESS: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 9, .Method = .BUFFERED, .Access = .ANY };
pub const GET_PIPE_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 10, .Method = .BUFFERED, .Access = .ANY };
pub const SET_PIPE_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 11, .Method = .BUFFERED, .Access = .ANY };
pub const GET_CONNECTION_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
pub const SET_CONNECTION_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 13, .Method = .BUFFERED, .Access = .ANY };
pub const GET_HANDLE_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 14, .Method = .BUFFERED, .Access = .ANY };
pub const SET_HANDLE_ATTRIBUTE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 15, .Method = .BUFFERED, .Access = .ANY };
pub const FLUSH: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 16, .Method = .BUFFERED, .Access = .{ .WRITE = true } };
pub const INTERNAL_READ: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 2045, .Method = .BUFFERED, .Access = .{ .READ = true } };
pub const INTERNAL_WRITE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 2046, .Method = .BUFFERED, .Access = .{ .WRITE = true } };
pub const INTERNAL_TRANSCEIVE: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 2047, .Method = .NEITHER, .Access = .{ .READ = true, .WRITE = true } };
pub const INTERNAL_READ_OVFLOW: CTL_CODE = .{ .DeviceType = .NAMED_PIPE, .Function = 2048, .Method = .BUFFERED, .Access = .{ .READ = true } };
};
};
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
pub const IO_REPARSE_TAG = packed struct(ULONG) {
Value: u12,
Index: u4 = 0,
ReservedBits: u12 = 0,
/// Can have children if a directory.
IsDirectory: bool = false,
/// Represents another named entity in the system.
IsSurrogate: bool = false,
/// Must be `false` for non-Microsoft tags.
IsReserved: bool = false,
/// Owned by Microsoft.
IsMicrosoft: bool = false,
pub const RESERVED_INVALID: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsReserved = true, .Index = 0x8, .Value = 0x000 };
pub const MOUNT_POINT: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x003 };
pub const HSM: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsReserved = true, .Value = 0x004 };
pub const DRIVE_EXTENDER: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x005 };
pub const HSM2: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x006 };
pub const SIS: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x007 };
pub const WIM: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x008 };
pub const CSV: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x009 };
pub const DFS: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x00A };
pub const FILTER_MANAGER: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x00B };
pub const SYMLINK: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x00C };
pub const IIS_CACHE: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x010 };
pub const DFSR: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x012 };
pub const DEDUP: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x013 };
pub const APPXSTRM: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsReserved = true, .Value = 0x014 };
pub const NFS: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x014 };
pub const FILE_PLACEHOLDER: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x015 };
pub const DFM: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x016 };
pub const WOF: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x017 };
pub inline fn WCI(index: u1) IO_REPARSE_TAG {
return .{ .IsMicrosoft = true, .IsDirectory = index == 0x1, .Index = index, .Value = 0x018 };
}
pub const GLOBAL_REPARSE: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x0019 };
pub inline fn CLOUD(index: u4) IO_REPARSE_TAG {
return .{ .IsMicrosoft = true, .IsDirectory = true, .Index = index, .Value = 0x01A };
}
pub const APPEXECLINK: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x01B };
pub const PROJFS: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsDirectory = true, .Value = 0x01C };
pub const LX_SYMLINK: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x01D };
pub const STORAGE_SYNC: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x01E };
pub const WCI_TOMBSTONE: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x01F };
pub const UNHANDLED: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x020 };
pub const ONEDRIVE: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x021 };
pub const PROJFS_TOMBSTONE: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x022 };
pub const AF_UNIX: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x023 };
pub const LX_FIFO: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x024 };
pub const LX_CHR: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x025 };
pub const LX_BLK: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .Value = 0x026 };
pub const LX_STORAGE_SYNC_FOLDER: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsDirectory = true, .Value = 0x027 };
pub inline fn WCI_LINK(index: u1) IO_REPARSE_TAG {
return .{ .IsMicrosoft = true, .IsSurrogate = true, .Index = index, .Value = 0x027 };
}
pub const DATALESS_CIM: IO_REPARSE_TAG = .{ .IsMicrosoft = true, .IsSurrogate = true, .Value = 0x28 };
};
// ref: km/wdm.h
pub const ACCESS_MASK = packed struct(DWORD) {
SPECIFIC: Specific = .{ .bits = 0 },
STANDARD: Standard = .{},
Reserved21: u3 = 0,
ACCESS_SYSTEM_SECURITY: bool = false,
MAXIMUM_ALLOWED: bool = false,
Reserved26: u2 = 0,
GENERIC: Generic = .{},
pub const Specific = packed union {
bits: u16,
// ref: km/wdm.h
/// Define access rights to files and directories
FILE: File,
FILE_DIRECTORY: File.Directory,
FILE_PIPE: File.Pipe,
/// Registry Specific Access Rights.
KEY: Key,
/// Object Manager Object Type Specific Access Rights.
OBJECT_TYPE: ObjectType,
/// Object Manager Directory Specific Access Rights.
DIRECTORY: Directory,
/// Object Manager Symbolic Link Specific Access Rights.
SYMBOLIC_LINK: SymbolicLink,
/// Section Access Rights.
SECTION: Section,
/// Session Specific Access Rights.
SESSION: Session,
/// Process Specific Access Rights.
PROCESS: Process,
/// Thread Specific Access Rights.
THREAD: Thread,
/// Partition Specific Access Rights.
MEMORY_PARTITION: MemoryPartition,
/// Generic mappings for transaction manager rights.
TRANSACTIONMANAGER: TransactionManager,
/// Generic mappings for transaction rights.
TRANSACTION: Transaction,
/// Generic mappings for resource manager rights.
RESOURCEMANAGER: ResourceManager,
/// Generic mappings for enlistment rights.
ENLISTMENT: Enlistment,
/// Event Specific Access Rights.
EVENT: Event,
/// Semaphore Specific Access Rights.
SEMAPHORE: Semaphore,
// ref: km/ntifs.h
/// Token Specific Access Rights.
TOKEN: Token,
// um/winnt.h
/// Job Object Specific Access Rights.
JOB_OBJECT: JobObject,
/// Mutant Specific Access Rights.
MUTANT: Mutant,
/// Timer Specific Access Rights.
TIMER: Timer,
/// I/O Completion Specific Access Rights.
IO_COMPLETION: IoCompletion,
pub const File = packed struct(u16) {
READ_DATA: bool = false,
WRITE_DATA: bool = false,
APPEND_DATA: bool = false,
READ_EA: bool = false,
WRITE_EA: bool = false,
EXECUTE: bool = false,
Reserved6: u1 = 0,
READ_ATTRIBUTES: bool = false,
WRITE_ATTRIBUTES: bool = false,
Reserved9: u7 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .FILE = .{
.READ_DATA = true,
.WRITE_DATA = true,
.APPEND_DATA = true,
.READ_EA = true,
.WRITE_EA = true,
.EXECUTE = true,
.Reserved6 = maxInt(@FieldType(File, "Reserved6")),
.READ_ATTRIBUTES = true,
.WRITE_ATTRIBUTES = true,
} },
};
pub const GENERIC_READ: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .READ,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .FILE = .{
.READ_DATA = true,
.READ_ATTRIBUTES = true,
.READ_EA = true,
} },
};
pub const GENERIC_WRITE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .WRITE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .FILE = .{
.WRITE_DATA = true,
.WRITE_ATTRIBUTES = true,
.WRITE_EA = true,
.APPEND_DATA = true,
} },
};
pub const GENERIC_EXECUTE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .EXECUTE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .FILE = .{
.READ_ATTRIBUTES = true,
.EXECUTE = true,
} },
};
pub const Directory = packed struct(u16) {
LIST: bool = false,
ADD_FILE: bool = false,
ADD_SUBDIRECTORY: bool = false,
READ_EA: bool = false,
WRITE_EA: bool = false,
TRAVERSE: bool = false,
DELETE_CHILD: bool = false,
READ_ATTRIBUTES: bool = false,
WRITE_ATTRIBUTES: bool = false,
Reserved9: u7 = 0,
};
pub const Pipe = packed struct(u16) {
READ_DATA: bool = false,
WRITE_DATA: bool = false,
CREATE_PIPE_INSTANCE: bool = false,
Reserved3: u4 = 0,
READ_ATTRIBUTES: bool = false,
WRITE_ATTRIBUTES: bool = false,
Reserved9: u7 = 0,
};
};
pub const Key = packed struct(u16) {
/// Required to query the values of a registry key.
QUERY_VALUE: bool = false,
/// Required to create, delete, or set a registry value.
SET_VALUE: bool = false,
/// Required to create a subkey of a registry key.
CREATE_SUB_KEY: bool = false,
/// Required to enumerate the subkeys of a registry key.
ENUMERATE_SUB_KEYS: bool = false,
/// Required to request change notifications for a registry key or for subkeys of a registry key.
NOTIFY: bool = false,
/// Reserved for system use.
CREATE_LINK: bool = false,
Reserved6: u2 = 0,
/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
/// This flag is ignored by 32-bit Windows.
WOW64_64KEY: bool = false,
/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
/// This flag is ignored by 32-bit Windows.
WOW64_32KEY: bool = false,
Reserved10: u6 = 0,
pub const WOW64_RES: ACCESS_MASK = .{
.SPECIFIC = .{ .KEY = .{
.WOW64_32KEY = true,
.WOW64_64KEY = true,
} },
};
/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
pub const READ: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .READ,
.SYNCHRONIZE = false,
},
.SPECIFIC = .{ .KEY = .{
.QUERY_VALUE = true,
.ENUMERATE_SUB_KEYS = true,
.NOTIFY = true,
} },
};
/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
pub const WRITE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .WRITE,
.SYNCHRONIZE = false,
},
.SPECIFIC = .{ .KEY = .{
.SET_VALUE = true,
.CREATE_SUB_KEY = true,
} },
};
/// Equivalent to KEY_READ.
pub const EXECUTE = READ;
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .ALL,
.SYNCHRONIZE = false,
},
.SPECIFIC = .{ .KEY = .{
.QUERY_VALUE = true,
.SET_VALUE = true,
.CREATE_SUB_KEY = true,
.ENUMERATE_SUB_KEYS = true,
.NOTIFY = true,
.CREATE_LINK = true,
} },
};
};
pub const ObjectType = packed struct(u16) {
CREATE: bool = false,
Reserved1: u15 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .OBJECT_TYPE = .{
.CREATE = true,
} },
};
};
pub const Directory = packed struct(u16) {
QUERY: bool = false,
TRAVERSE: bool = false,
CREATE_OBJECT: bool = false,
CREATE_SUBDIRECTORY: bool = false,
Reserved3: u12 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .DIRECTORY = .{
.QUERY = true,
.TRAVERSE = true,
.CREATE_OBJECT = true,
.CREATE_SUBDIRECTORY = true,
} },
};
};
pub const SymbolicLink = packed struct(u16) {
QUERY: bool = false,
SET: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .SYMBOLIC_LINK = .{
.QUERY = true,
} },
};
pub const ALL_ACCESS_EX: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .SYMBOLIC_LINK = .{
.QUERY = true,
.SET = true,
.Reserved2 = maxInt(@FieldType(SymbolicLink, "Reserved2")),
} },
};
};
pub const Section = packed struct(u16) {
QUERY: bool = false,
MAP_WRITE: bool = false,
MAP_READ: bool = false,
MAP_EXECUTE: bool = false,
EXTEND_SIZE: bool = false,
/// not included in `ALL_ACCESS`
MAP_EXECUTE_EXPLICIT: bool = false,
Reserved6: u10 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .SECTION = .{
.QUERY = true,
.MAP_WRITE = true,
.MAP_READ = true,
.MAP_EXECUTE = true,
.EXTEND_SIZE = true,
} },
};
};
pub const Session = packed struct(u16) {
QUERY_ACCESS: bool = false,
MODIFY_ACCESS: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .SESSION = .{
.QUERY_ACCESS = true,
.MODIFY_ACCESS = true,
} },
};
};
pub const Process = packed struct(u16) {
TERMINATE: bool = false,
CREATE_THREAD: bool = false,
SET_SESSIONID: bool = false,
VM_OPERATION: bool = false,
VM_READ: bool = false,
VM_WRITE: bool = false,
DUP_HANDLE: bool = false,
CREATE_PROCESS: bool = false,
SET_QUOTA: bool = false,
SET_INFORMATION: bool = false,
QUERY_INFORMATION: bool = false,
SUSPEND_RESUME: bool = false,
QUERY_LIMITED_INFORMATION: bool = false,
SET_LIMITED_INFORMATION: bool = false,
Reserved14: u2 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .PROCESS = .{
.TERMINATE = true,
.CREATE_THREAD = true,
.SET_SESSIONID = true,
.VM_OPERATION = true,
.VM_READ = true,
.VM_WRITE = true,
.DUP_HANDLE = true,
.CREATE_PROCESS = true,
.SET_QUOTA = true,
.SET_INFORMATION = true,
.QUERY_INFORMATION = true,
.SUSPEND_RESUME = true,
.QUERY_LIMITED_INFORMATION = true,
.SET_LIMITED_INFORMATION = true,
.Reserved14 = maxInt(@FieldType(Process, "Reserved14")),
} },
};
};
pub const Thread = packed struct(u16) {
TERMINATE: bool = false,
SUSPEND_RESUME: bool = false,
ALERT: bool = false,
GET_CONTEXT: bool = false,
SET_CONTEXT: bool = false,
SET_INFORMATION: bool = false,
QUERY_INFORMATION: bool = false,
SET_THREAD_TOKEN: bool = false,
IMPERSONATE: bool = false,
DIRECT_IMPERSONATION: bool = false,
SET_LIMITED_INFORMATION: bool = false,
QUERY_LIMITED_INFORMATION: bool = false,
RESUME: bool = false,
Reserved13: u3 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .THREAD = .{
.TERMINATE = true,
.SUSPEND_RESUME = true,
.ALERT = true,
.GET_CONTEXT = true,
.SET_CONTEXT = true,
.SET_INFORMATION = true,
.QUERY_INFORMATION = true,
.SET_THREAD_TOKEN = true,
.IMPERSONATE = true,
.DIRECT_IMPERSONATION = true,
.SET_LIMITED_INFORMATION = true,
.QUERY_LIMITED_INFORMATION = true,
.RESUME = true,
.Reserved13 = maxInt(@FieldType(Thread, "Reserved13")),
} },
};
};
pub const MemoryPartition = packed struct(u16) {
QUERY_ACCESS: bool = false,
MODIFY_ACCESS: bool = false,
Required2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .MEMORY_PARTITION = .{
.QUERY_ACCESS = true,
.MODIFY_ACCESS = true,
} },
};
};
pub const TransactionManager = packed struct(u16) {
QUERY_INFORMATION: bool = false,
SET_INFORMATION: bool = false,
RECOVER: bool = false,
RENAME: bool = false,
CREATE_RM: bool = false,
/// The following right is intended for DTC's use only; it will be deprecated, and no one else should take a dependency on it.
BIND_TRANSACTION: bool = false,
Reserved6: u10 = 0,
pub const GENERIC_READ: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .READ },
.SPECIFIC = .{ .TRANSACTIONMANAGER = .{
.QUERY_INFORMATION = true,
} },
};
pub const GENERIC_WRITE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .WRITE },
.SPECIFIC = .{ .TRANSACTIONMANAGER = .{
.SET_INFORMATION = true,
.RECOVER = true,
.RENAME = true,
.CREATE_RM = true,
} },
};
pub const GENERIC_EXECUTE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .EXECUTE },
.SPECIFIC = .{ .TRANSACTIONMANAGER = .{} },
};
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .TRANSACTIONMANAGER = .{
.QUERY_INFORMATION = true,
.SET_INFORMATION = true,
.RECOVER = true,
.RENAME = true,
.CREATE_RM = true,
.BIND_TRANSACTION = true,
} },
};
};
pub const Transaction = packed struct(u16) {
QUERY_INFORMATION: bool = false,
SET_INFORMATION: bool = false,
ENLIST: bool = false,
COMMIT: bool = false,
ROLLBACK: bool = false,
PROPAGATE: bool = false,
RIGHT_RESERVED1: bool = false,
Reserved7: u9 = 0,
pub const GENERIC_READ: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .READ,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TRANSACTION = .{
.QUERY_INFORMATION = true,
} },
};
pub const GENERIC_WRITE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .WRITE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TRANSACTION = .{
.SET_INFORMATION = true,
.COMMIT = true,
.ENLIST = true,
.ROLLBACK = true,
.PROPAGATE = true,
} },
};
pub const GENERIC_EXECUTE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .EXECUTE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TRANSACTION = .{
.COMMIT = true,
.ROLLBACK = true,
} },
};
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TRANSACTION = .{
.QUERY_INFORMATION = true,
.SET_INFORMATION = true,
.COMMIT = true,
.ENLIST = true,
.ROLLBACK = true,
.PROPAGATE = true,
} },
};
pub const RESOURCE_MANAGER_RIGHTS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .{
.READ_CONTROL = true,
},
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TRANSACTION = .{
.QUERY_INFORMATION = true,
.SET_INFORMATION = true,
.ENLIST = true,
.ROLLBACK = true,
.PROPAGATE = true,
} },
};
};
pub const ResourceManager = packed struct(u16) {
QUERY_INFORMATION: bool = false,
SET_INFORMATION: bool = false,
RECOVER: bool = false,
ENLIST: bool = false,
GET_NOTIFICATION: bool = false,
REGISTER_PROTOCOL: bool = false,
COMPLETE_PROPAGATION: bool = false,
Reserved7: u9 = 0,
pub const GENERIC_READ: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .READ,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .RESOURCEMANAGER = .{
.QUERY_INFORMATION = true,
} },
};
pub const GENERIC_WRITE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .WRITE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .RESOURCEMANAGER = .{
.SET_INFORMATION = true,
.RECOVER = true,
.ENLIST = true,
.GET_NOTIFICATION = true,
.REGISTER_PROTOCOL = true,
.COMPLETE_PROPAGATION = true,
} },
};
pub const GENERIC_EXECUTE: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .EXECUTE,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .RESOURCEMANAGER = .{
.RECOVER = true,
.ENLIST = true,
.GET_NOTIFICATION = true,
.COMPLETE_PROPAGATION = true,
} },
};
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .RESOURCEMANAGER = .{
.QUERY_INFORMATION = true,
.SET_INFORMATION = true,
.RECOVER = true,
.ENLIST = true,
.GET_NOTIFICATION = true,
.REGISTER_PROTOCOL = true,
.COMPLETE_PROPAGATION = true,
} },
};
};
pub const Enlistment = packed struct(u16) {
QUERY_INFORMATION: bool = false,
SET_INFORMATION: bool = false,
RECOVER: bool = false,
SUBORDINATE_RIGHTS: bool = false,
SUPERIOR_RIGHTS: bool = false,
Reserved5: u11 = 0,
pub const GENERIC_READ: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .READ },
.SPECIFIC = .{ .ENLISTMENT = .{
.QUERY_INFORMATION = true,
} },
};
pub const GENERIC_WRITE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .WRITE },
.SPECIFIC = .{ .ENLISTMENT = .{
.SET_INFORMATION = true,
.RECOVER = true,
.SUBORDINATE_RIGHTS = true,
.SUPERIOR_RIGHTS = true,
} },
};
pub const GENERIC_EXECUTE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .EXECUTE },
.SPECIFIC = .{ .ENLISTMENT = .{
.RECOVER = true,
.SUBORDINATE_RIGHTS = true,
.SUPERIOR_RIGHTS = true,
} },
};
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .ENLISTMENT = .{
.QUERY_INFORMATION = true,
.SET_INFORMATION = true,
.RECOVER = true,
.SUBORDINATE_RIGHTS = true,
.SUPERIOR_RIGHTS = true,
} },
};
};
pub const Event = packed struct(u16) {
QUERY_STATE: bool = false,
MODIFY_STATE: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .EVENT = .{
.QUERY_STATE = true,
.MODIFY_STATE = true,
} },
};
};
pub const Semaphore = packed struct(u16) {
QUERY_STATE: bool = false,
MODIFY_STATE: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .SEMAPHORE = .{
.QUERY_STATE = true,
.MODIFY_STATE = true,
} },
};
};
pub const Token = packed struct(u16) {
ASSIGN_PRIMARY: bool = false,
DUPLICATE: bool = false,
IMPERSONATE: bool = false,
QUERY: bool = false,
QUERY_SOURCE: bool = false,
ADJUST_PRIVILEGES: bool = false,
ADJUST_GROUPS: bool = false,
ADJUST_DEFAULT: bool = false,
ADJUST_SESSIONID: bool = false,
Reserved9: u7 = 0,
pub const ALL_ACCESS_P: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .TOKEN = .{
.ASSIGN_PRIMARY = true,
.DUPLICATE = true,
.IMPERSONATE = true,
.QUERY = true,
.QUERY_SOURCE = true,
.ADJUST_PRIVILEGES = true,
.ADJUST_GROUPS = true,
.ADJUST_DEFAULT = true,
} },
};
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED },
.SPECIFIC = .{ .TOKEN = .{
.ASSIGN_PRIMARY = true,
.DUPLICATE = true,
.IMPERSONATE = true,
.QUERY = true,
.QUERY_SOURCE = true,
.ADJUST_PRIVILEGES = true,
.ADJUST_GROUPS = true,
.ADJUST_DEFAULT = true,
.ADJUST_SESSIONID = true,
} },
};
pub const READ: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .READ },
.SPECIFIC = .{ .TOKEN = .{
.QUERY = true,
} },
};
pub const WRITE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .WRITE },
.SPECIFIC = .{ .TOKEN = .{
.ADJUST_PRIVILEGES = true,
.ADJUST_GROUPS = true,
.ADJUST_DEFAULT = true,
} },
};
pub const EXECUTE: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .EXECUTE },
.SPECIFIC = .{ .TOKEN = .{} },
};
pub const TRUST_CONSTRAINT_MASK: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .READ },
.SPECIFIC = .{ .TOKEN = .{
.QUERY = true,
.QUERY_SOURCE = true,
} },
};
pub const TRUST_ALLOWED_MASK: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .READ },
.SPECIFIC = .{ .TOKEN = .{
.QUERY = true,
.QUERY_SOURCE = true,
.DUPLICATE = true,
.IMPERSONATE = true,
} },
};
};
pub const JobObject = packed struct(u16) {
ASSIGN_PROCESS: bool = false,
SET_ATTRIBUTES: bool = false,
QUERY: bool = false,
TERMINATE: bool = false,
SET_SECURITY_ATTRIBUTES: bool = false,
IMPERSONATE: bool = false,
Reserved6: u10 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .JOB_OBJECT = .{
.ASSIGN_PROCESS = true,
.SET_ATTRIBUTES = true,
.QUERY = true,
.TERMINATE = true,
.SET_SECURITY_ATTRIBUTES = true,
.IMPERSONATE = true,
} },
};
};
pub const Mutant = packed struct(u16) {
QUERY_STATE: bool = false,
Reserved1: u15 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .MUTANT = .{
.QUERY_STATE = true,
} },
};
};
pub const Timer = packed struct(u16) {
QUERY_STATE: bool = false,
MODIFY_STATE: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{
.RIGHTS = .REQUIRED,
.SYNCHRONIZE = true,
},
.SPECIFIC = .{ .TIMER = .{
.QUERY_STATE = true,
.MODIFY_STATE = true,
} },
};
};
pub const IoCompletion = packed struct(u16) {
Reserved0: u1 = 0,
MODIFY_STATE: bool = false,
Reserved2: u14 = 0,
pub const ALL_ACCESS: ACCESS_MASK = .{
.STANDARD = .{ .RIGHTS = .REQUIRED, .SYNCHRONIZE = true },
.SPECIFIC = .{ .IO_COMPLETION = .{
.Reserved0 = maxInt(@FieldType(IoCompletion, "Reserved0")),
.MODIFY_STATE = true,
} },
};
};
pub const RIGHTS_ALL: Specific = .{ .bits = maxInt(@FieldType(Specific, "bits")) };
};
pub const Standard = packed struct(u5) {
RIGHTS: Rights = .{},
SYNCHRONIZE: bool = false,
pub const RIGHTS_ALL: Standard = .{
.RIGHTS = .ALL,
.SYNCHRONIZE = true,
};
pub const Rights = packed struct(u4) {
DELETE: bool = false,
READ_CONTROL: bool = false,
WRITE_DAC: bool = false,
WRITE_OWNER: bool = false,
pub const REQUIRED: Rights = .{
.DELETE = true,
.READ_CONTROL = true,
.WRITE_DAC = true,
.WRITE_OWNER = true,
};
pub const READ: Rights = .{
.READ_CONTROL = true,
};
pub const WRITE: Rights = .{
.READ_CONTROL = true,
};
pub const EXECUTE: Rights = .{
.READ_CONTROL = true,
};
pub const ALL = REQUIRED;
};
};
pub const Generic = packed struct(u4) {
ALL: bool = false,
EXECUTE: bool = false,
WRITE: bool = false,
READ: bool = false,
};
};
pub const DEVICE_TYPE = packed struct(ULONG) {
FileDevice: CTL_CODE.FILE_DEVICE,
Reserved16: u16 = 0,
};
pub const FS_INFORMATION_CLASS = enum(c_int) {
Volume = 1,
Label = 2,
Size = 3,
Device = 4,
Attribute = 5,
Control = 6,
FullSize = 7,
ObjectId = 8,
DriverPath = 9,
VolumeFlags = 10,
SectorSize = 11,
DataCopy = 12,
MetadataSize = 13,
FullSizeEx = 14,
Guid = 15,
_,
pub const Maximum: @typeInfo(@This()).@"enum".tag_type = 1 + @typeInfo(@This()).@"enum".fields.len;
};
pub const SECTION_INHERIT = enum(c_int) {
Share = 1,
Unmap = 2,
};
pub const PAGE = packed struct(ULONG) {
NOACCESS: bool = false,
READONLY: bool = false,
READWRITE: bool = false,
WRITECOPY: bool = false,
EXECUTE: bool = false,
EXECUTE_READ: bool = false,
EXECUTE_READWRITE: bool = false,
EXECUTE_WRITECOPY: bool = false,
GUARD: bool = false,
NOCACHE: bool = false,
WRITECOMBINE: bool = false,
GRAPHICS_NOACCESS: bool = false,
GRAPHICS_READONLY: bool = false,
GRAPHICS_READWRITE: bool = false,
GRAPHICS_EXECUTE: bool = false,
GRAPHICS_EXECUTE_READ: bool = false,
GRAPHICS_EXECUTE_READWRITE: bool = false,
GRAPHICS_COHERENT: bool = false,
GRAPHICS_NOCACHE: bool = false,
Reserved19: u12 = 0,
REVERT_TO_FILE_MAP: bool = false,
};
pub const MEM = struct {
pub const ALLOCATE = packed struct(ULONG) {
Reserved0: u12 = 0,
COMMIT: bool = false,
RESERVE: bool = false,
REPLACE_PLACEHOLDER: bool = false,
Reserved15: u3 = 0,
RESERVE_PLACEHOLDER: bool = false,
RESET: bool = false,
TOP_DOWN: bool = false,
WRITE_WATCH: bool = false,
PHYSICAL: bool = false,
Reserved23: u1 = 0,
RESET_UNDO: bool = false,
Reserved25: u4 = 0,
LARGE_PAGES: bool = false,
Reserved30: u1 = 0,
@"4MB_PAGES": bool = false,
pub const @"64K_PAGES": ALLOCATE = .{
.LARGE_PAGES = true,
.PHYSICAL = true,
};
};
pub const FREE = packed struct(ULONG) {
COALESCE_PLACEHOLDERS: bool = false,
PRESERVE_PLACEHOLDER: bool = false,
Reserved2: u12 = 0,
DECOMMIT: bool = false,
RELEASE: bool = false,
FREE: bool = false,
Reserved17: u15 = 0,
};
pub const MAP = packed struct(ULONG) {
Reserved0: u13 = 0,
RESERVE: bool = false,
REPLACE_PLACEHOLDER: bool = false,
Reserved15: u14 = 0,
LARGE_PAGES: bool = false,
Reserved30: u2 = 0,
};
pub const UNMAP = packed struct(ULONG) {
WITH_TRANSIENT_BOOST: bool = false,
PRESERVE_PLACEHOLDER: bool = false,
Reserved2: u30 = 0,
};
pub const EXTENDED_PARAMETER = extern struct {
s: packed struct(ULONG64) {
Type: TYPE,
Reserved: u56,
},
u: extern union {
ULong64: ULONG64,
Pointer: PVOID,
Size: SIZE_T,
Handle: HANDLE,
ULong: ULONG,
},
pub const TYPE = enum(u8) {
InvalidType = 0,
AddressRequirements,
NumaNode,
PartitionHandle,
UserPhysicalHandle,
AttributeFlags,
ImageMachine,
_,
pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".fields.len;
};
};
};
pub const SEC = packed struct(ULONG) {
Reserved0: u17 = 0,
HUGE_PAGES: bool = false,
PARTITION_OWNER_HANDLE: bool = false,
@"64K_PAGES": bool = false,
Reserved19: u3 = 0,
FILE: bool = false,
IMAGE: bool = false,
PROTECTED_IMAGE: bool = false,
RESERVE: bool = false,
COMMIT: bool = false,
NOCACHE: bool = false,
Reserved29: u1 = 0,
WRITECOMBINE: bool = false,
LARGE_PAGES: bool = false,
pub const IMAGE_NO_EXECUTE: SEC = .{
.IMAGE = true,
.NOCACHE = true,
};
};
pub const ERESOURCE = opaque {};
// ref: shared/ntdef.h
pub const EVENT_TYPE = enum(c_int) {
Notification,
Synchronization,
};
pub const TIMER_TYPE = enum(c_int) {
Notification,
Synchronization,
};
pub const WAIT_TYPE = enum(c_int) {
All,
Any,
};
pub const LOGICAL = ULONG;
pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
// ref: um/heapapi.h
pub fn GetProcessHeap() ?*HEAP {
return peb().ProcessHeap;
}
// ref: um/winternl.h
pub const OBJECT_ATTRIBUTES = extern struct {
Length: ULONG,
RootDirectory: ?HANDLE,
ObjectName: *UNICODE_STRING,
Attributes: ATTRIBUTES,
SecurityDescriptor: ?*anyopaque,
SecurityQualityOfService: ?*anyopaque,
// 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 const OpenError = error{
IsDir,
NotDir,
FileNotFound,
NoDevice,
AccessDenied,
PipeBusy,
PathAlreadyExists,
Unexpected,
NameTooLong,
WouldBlock,
NetworkNotFound,
AntivirusInterference,
BadPathName,
};
pub const OpenFileOptions = struct {
access_mask: ACCESS_MASK,
dir: ?HANDLE = null,
sa: ?*SECURITY_ATTRIBUTES = null,
share_access: FILE.SHARE = .VALID_FLAGS,
creation: FILE.CREATE_DISPOSITION,
/// If true, tries to open path as a directory.
/// Defaults to false.
filter: Filter = .file_only,
/// If false, tries to open path as a reparse point without dereferencing it.
/// Defaults to true.
follow_symlinks: bool = true,
pub const Filter = enum {
/// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory.
file_only,
/// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file.
dir_only,
/// `OpenFile` does not discriminate between opening files and directories.
any,
};
};
pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) {
return error.IsDir;
}
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) {
return error.IsDir;
}
var result: HANDLE = undefined;
const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name: UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
const attr: OBJECT_ATTRIBUTES = .{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
.Attributes = .{
.INHERIT = if (options.sa) |sa| sa.bInheritHandle != FALSE else false,
},
.ObjectName = &nt_name,
.SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
.SecurityQualityOfService = null,
};
var io: IO_STATUS_BLOCK = undefined;
while (true) {
const rc = ntdll.NtCreateFile(
&result,
options.access_mask,
&attr,
&io,
null,
.{ .NORMAL = true },
options.share_access,
options.creation,
.{
.DIRECTORY_FILE = options.filter == .dir_only,
.NON_DIRECTORY_FILE = options.filter == .file_only,
.IO = if (options.follow_symlinks) .SYNCHRONOUS_NONALERT else .ASYNCHRONOUS,
.OPEN_REPARSE_POINT = !options.follow_symlinks,
},
null,
0,
);
switch (rc) {
.SUCCESS => return result,
.OBJECT_NAME_INVALID => return error.BadPathName,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
.INVALID_PARAMETER => unreachable,
.SHARING_VIOLATION => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.PIPE_BUSY => return error.PipeBusy,
.PIPE_NOT_AVAILABLE => return error.NoDevice,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
.USER_MAPPED_FILE => return error.AccessDenied,
.INVALID_HANDLE => unreachable,
.DELETE_PENDING => {
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. There is not really a sane way to handle
// this other than retrying the creation after the OS finishes
// the deletion.
_ = kernel32.SleepEx(1, TRUE);
continue;
},
.VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
else => return unexpectedStatus(rc),
}
}
}
pub fn GetCurrentProcess() HANDLE {
const process_pseudo_handle: usize = @bitCast(@as(isize, -1));
return @ptrFromInt(process_pseudo_handle);
}
pub fn GetCurrentProcessId() DWORD {
return @truncate(@intFromPtr(teb().ClientId.UniqueProcess));
}
pub fn GetCurrentThread() HANDLE {
const thread_pseudo_handle: usize = @bitCast(@as(isize, -2));
return @ptrFromInt(thread_pseudo_handle);
}
pub fn GetCurrentThreadId() DWORD {
return @truncate(@intFromPtr(teb().ClientId.UniqueThread));
}
pub fn GetLastError() Win32Error {
return @enumFromInt(teb().LastErrorValue);
}
pub const CreatePipeError = error{ Unexpected, SystemResources };
var npfs: ?HANDLE = null;
/// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls.
/// It implements similar behavior to `CreatePipe` and is meant to serve
/// as a direct substitute for that call.
pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
// Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to:
//
// \??\pipe\Win32Pipes.{pid}.{count}
//
// where `pid` is the process id and count is a incrementing counter.
// The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System
// and use that as the root directory for `NtCreateNamedPipeFile`.
// This object is visible under the NPFS but has no filename attached to it.
//
// This implementation replicates how `CreatePipe` works in modern Windows versions.
const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst);
const dev_handle = opt_dev_handle orelse blk: {
const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\");
const len: u16 = @truncate(str.len * @sizeOf(u16));
const name: UNICODE_STRING = .{
.Length = len,
.MaximumLength = len,
.Buffer = @ptrCast(@constCast(str)),
};
const attrs: OBJECT_ATTRIBUTES = .{
.ObjectName = @constCast(&name),
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = null,
.Attributes = .{},
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var iosb: IO_STATUS_BLOCK = undefined;
var handle: HANDLE = undefined;
switch (ntdll.NtCreateFile(
&handle,
.{
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{ .READ = true },
},
@constCast(&attrs),
&iosb,
null,
.{},
.VALID_FLAGS,
.OPEN,
.{ .IO = .SYNCHRONOUS_NONALERT },
null,
0,
)) {
.SUCCESS => {},
// Judging from the ReactOS sources this is technically possible.
.INSUFFICIENT_RESOURCES => return error.SystemResources,
.INVALID_PARAMETER => unreachable,
else => |e| return unexpectedStatus(e),
}
if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| {
CloseHandle(handle);
break :blk xchg.?;
} else break :blk handle;
};
const name: UNICODE_STRING = .{ .Buffer = null, .Length = 0, .MaximumLength = 0 };
var attrs: OBJECT_ATTRIBUTES = .{
.ObjectName = @constCast(&name),
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = dev_handle,
.Attributes = .{ .INHERIT = sattr.bInheritHandle != FALSE },
.SecurityDescriptor = sattr.lpSecurityDescriptor,
.SecurityQualityOfService = null,
};
// 120 second relative timeout in 100ns units.
const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100;
var iosb: IO_STATUS_BLOCK = undefined;
var read: HANDLE = undefined;
switch (ntdll.NtCreateNamedPipeFile(
&read,
.{
.SPECIFIC = .{ .FILE_PIPE = .{
.WRITE_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{ .READ = true },
},
&attrs,
&iosb,
.{ .READ = true, .WRITE = true },
.CREATE,
.{ .IO = .SYNCHRONOUS_NONALERT },
.{ .TYPE = .BYTE_STREAM },
.{ .MODE = .BYTE_STREAM },
.{ .OPERATION = .QUEUE },
1,
4096,
4096,
@constCast(&default_timeout),
)) {
.SUCCESS => {},
.INVALID_PARAMETER => unreachable,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
else => |e| return unexpectedStatus(e),
}
errdefer CloseHandle(read);
attrs.RootDirectory = read;
var write: HANDLE = undefined;
switch (ntdll.NtCreateFile(
&write,
.{
.SPECIFIC = .{ .FILE_PIPE = .{
.READ_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{ .WRITE = true },
},
&attrs,
&iosb,
null,
.{},
.VALID_FLAGS,
.OPEN,
.{
.IO = .SYNCHRONOUS_NONALERT,
.NON_DIRECTORY_FILE = true,
},
null,
0,
)) {
.SUCCESS => {},
.INVALID_PARAMETER => unreachable,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
else => |e| return unexpectedStatus(e),
}
rd.* = read;
wr.* = write;
}
pub const DeviceIoControlError = error{
AccessDenied,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Pending,
/// Attempted to connect a named pipe in the "closing" state, meaning a previous client has
/// has closed their handle but we have not yet disconnected the pipe.
PipeClosing,
/// Attempted to connect a named pipe in the "connected" state, meaning a client has already
/// opened the pipe; there is a good connection between client and server.
PipeAlreadyConnected,
/// Attempted to connect a non-blocking named pipe which is already listening for connections.
PipeAlreadyListening,
Unexpected,
};
/// 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 = &.{},
},
) DeviceIoControlError!void {
var io_status_block: IO_STATUS_BLOCK = undefined;
const rc = switch (io_control_code.DeviceType) {
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),
),
.FILE_SYSTEM => 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),
),
};
switch (rc) {
.SUCCESS => {},
.PIPE_CLOSING => return error.PipeClosing,
.PIPE_CONNECTED => return error.PipeAlreadyConnected,
.PIPE_LISTENING => return error.PipeAlreadyListening,
.PRIVILEGE_NOT_HELD => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
.INVALID_PARAMETER => unreachable,
.UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
.PENDING => return error.Pending,
else => return unexpectedStatus(rc),
}
}
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 SetHandleInformationError = error{Unexpected};
pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const RtlGenRandomError = error{
/// `RtlGenRandom` has been known to fail in situations where the system is under heavy load.
/// Unfortunately, it does not call `SetLastError`, so it is not possible to get more specific
/// error information; it could actually be due to an out-of-memory condition, for example.
SystemResources,
};
/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
/// https://github.com/rust-lang-nursery/rand/issues/111
/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
var total_read: usize = 0;
var buff: []u8 = output[0..];
const max_read_size: ULONG = maxInt(ULONG);
while (total_read < output.len) {
const to_read: ULONG = @min(buff.len, max_read_size);
if (advapi32.RtlGenRandom(buff.ptr, to_read) == 0) {
return error.SystemResources;
}
total_read += to_read;
buff = buff[to_read..];
}
}
pub const WaitForSingleObjectError = error{
WaitAbandoned,
WaitTimeOut,
Unexpected,
};
pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
return WaitForSingleObjectEx(handle, milliseconds, false);
}
pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void {
switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @intFromBool(alertable))) {
WAIT_ABANDONED => return error.WaitAbandoned,
WAIT_OBJECT_0 => return,
WAIT_TIMEOUT => return error.WaitTimeOut,
WAIT_FAILED => switch (GetLastError()) {
else => |err| return unexpectedError(err),
},
else => return error.Unexpected,
}
}
pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 {
assert(handles.len > 0 and handles.len <= MAXIMUM_WAIT_OBJECTS);
const nCount: DWORD = @as(DWORD, @intCast(handles.len));
switch (kernel32.WaitForMultipleObjectsEx(
nCount,
handles.ptr,
@intFromBool(waitAll),
milliseconds,
@intFromBool(alertable),
)) {
WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| {
const handle_index = n - WAIT_OBJECT_0;
assert(handle_index < nCount);
return handle_index;
},
WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| {
const handle_index = n - WAIT_ABANDONED_0;
assert(handle_index < nCount);
return error.WaitAbandoned;
},
WAIT_TIMEOUT => return error.WaitTimeOut,
WAIT_FAILED => switch (GetLastError()) {
else => |err| return unexpectedError(err),
},
else => return error.Unexpected,
}
}
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;
}
pub fn CloseHandle(hObject: HANDLE) void {
assert(ntdll.NtClose(hObject) == .SUCCESS);
}
pub const ReadFileError = error{
BrokenPipe,
/// The specified network name is no longer available.
ConnectionResetByPeer,
Canceled,
/// Unable to read file due to lock.
LockViolation,
/// Known to be possible when:
/// - Unable to read from disconnected virtual com port (Windows)
AccessDenied,
NotOpenForReading,
Unexpected,
};
/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
/// multiple non-atomic reads.
pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize {
while (true) {
const want_read_count: DWORD = @min(@as(DWORD, maxInt(DWORD)), buffer.len);
var amt_read: DWORD = undefined;
var overlapped_data: OVERLAPPED = undefined;
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
overlapped_data = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @as(u32, @truncate(off)),
.OffsetHigh = @as(u32, @truncate(off >> 32)),
},
},
.hEvent = null,
};
break :blk &overlapped_data;
} else null;
if (kernel32.ReadFile(in_hFile, buffer.ptr, want_read_count, &amt_read, overlapped) == 0) {
switch (GetLastError()) {
.IO_PENDING => unreachable,
.OPERATION_ABORTED => continue,
.BROKEN_PIPE => return 0,
.HANDLE_EOF => return 0,
.NETNAME_DELETED => return error.ConnectionResetByPeer,
.LOCK_VIOLATION => return error.LockViolation,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_HANDLE => return error.NotOpenForReading,
else => |err| return unexpectedError(err),
}
}
return amt_read;
}
}
pub const WriteFileError = error{
SystemResources,
Canceled,
BrokenPipe,
NotOpenForWriting,
/// The process cannot access the file because another process has locked
/// a portion of the file.
LockViolation,
/// The specified network name is no longer available.
ConnectionResetByPeer,
/// Known to be possible when:
/// - Unable to write to disconnected virtual com port (Windows)
AccessDenied,
Unexpected,
};
pub fn WriteFile(
handle: HANDLE,
bytes: []const u8,
offset: ?u64,
) WriteFileError!usize {
var bytes_written: DWORD = undefined;
var overlapped_data: OVERLAPPED = undefined;
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
overlapped_data = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @truncate(off),
.OffsetHigh = @truncate(off >> 32),
},
},
.hEvent = null,
};
break :blk &overlapped_data;
} else null;
const adjusted_len = math.cast(u32, bytes.len) orelse maxInt(u32);
if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
switch (GetLastError()) {
.INVALID_USER_BUFFER => return error.SystemResources,
.NOT_ENOUGH_MEMORY => return error.SystemResources,
.OPERATION_ABORTED => return error.Canceled,
.NOT_ENOUGH_QUOTA => return error.SystemResources,
.IO_PENDING => unreachable,
.NO_DATA => return error.BrokenPipe,
.INVALID_HANDLE => return error.NotOpenForWriting,
.LOCK_VIOLATION => return error.LockViolation,
.NETNAME_DELETED => return error.ConnectionResetByPeer,
.ACCESS_DENIED => return error.AccessDenied,
.WORKING_SET_QUOTA => return error.SystemResources,
else => |err| return unexpectedError(err),
}
}
return bytes_written;
}
pub const SetCurrentDirectoryError = error{
NameTooLong,
FileNotFound,
NotDir,
AccessDenied,
NoDevice,
BadPathName,
Unexpected,
};
pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void {
const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong;
var nt_name: UNICODE_STRING = .{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(path_name.ptr),
};
const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_INVALID => return error.BadPathName,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
.INVALID_PARAMETER => unreachable,
.ACCESS_DENIED => return error.AccessDenied,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.NOT_A_DIRECTORY => return error.NotDir,
else => return unexpectedStatus(rc),
}
}
pub const GetCurrentDirectoryError = error{
NameTooLong,
Unexpected,
};
/// The result is a slice of `buffer`, indexed from 0.
/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
var wtf16le_buf: [PATH_MAX_WIDE:0]u16 = undefined;
const result = kernel32.GetCurrentDirectoryW(wtf16le_buf.len + 1, &wtf16le_buf);
if (result == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
assert(result <= wtf16le_buf.len);
const wtf16le_slice = wtf16le_buf[0..result];
var end_index: usize = 0;
var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
while (it.nextCodepoint()) |codepoint| {
const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
if (end_index + seq_len >= buffer.len)
return error.NameTooLong;
end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
}
return buffer[0..end_index];
}
pub const CreateSymbolicLinkError = error{
AccessDenied,
PathAlreadyExists,
FileNotFound,
NameTooLong,
NoDevice,
NetworkNotFound,
BadPathName,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Unexpected,
};
/// Needs either:
/// - `SeCreateSymbolicLinkPrivilege` privilege
/// or
/// - Developer mode on Windows 10
/// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still
/// be created on the file system but will lack reparse processing data applied to it.
pub fn CreateSymbolicLink(
dir: ?HANDLE,
sym_link_path: []const u16,
target_path: [:0]const u16,
is_directory: bool,
) CreateSymbolicLinkError!void {
const SYMLINK_DATA = extern struct {
ReparseTag: IO_REPARSE_TAG,
ReparseDataLength: USHORT,
Reserved: USHORT,
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
Flags: ULONG,
};
const symlink_handle = OpenFile(sym_link_path, .{
.access_mask = .{
.STANDARD = .{ .SYNCHRONIZE = true },
.GENERIC = .{ .WRITE = true, .READ = true },
},
.dir = dir,
.creation = .CREATE,
.filter = if (is_directory) .dir_only else .file_only,
}) catch |err| switch (err) {
error.IsDir => return error.PathAlreadyExists,
error.NotDir => return error.Unexpected,
error.WouldBlock => return error.Unexpected,
error.PipeBusy => return error.Unexpected,
error.NoDevice => return error.Unexpected,
error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(symlink_handle);
// Relevant portions of the documentation:
// > Relative links are specified using the following conventions:
// > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
// > - Current working directoryrelative—for example, if the current working directory is
// > C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
// > Note: If you specify a current working directoryrelative link, it is created as an absolute
// > link, due to the way the current working directory is processed based on the user and the thread.
// 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 (hasCommonNtPrefix(u16, target_path)) {
// Already an NT path, no need to do anything to it
break :target_path target_path;
} else {
switch (getWin32PathType(u16, target_path)) {
// Rooted paths need to avoid getting put through wToPrefixedFileW
// (and they are treated as relative in this context)
// Note: It seems that rooted paths in symbolic links are relative to
// the drive that the symbolic exists on, not to the CWD's drive.
// So, if the symlink is on C:\ and the CWD is on D:\,
// it will still resolve the path relative to the root of
// the C:\ drive.
.rooted => break :target_path target_path,
// Keep relative paths relative, but anything else needs to get NT-prefixed.
else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
break :target_path target_path,
}
}
var prefixed_target_path = try wToPrefixedFileW(dir, target_path);
// We do this after prefixing to ensure that drive-relative paths are treated as absolute
is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
break :target_path prefixed_target_path.span();
};
// prepare reparse data buffer
var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
const symlink_data: SYMLINK_DATA = .{
.ReparseTag = .SYMLINK,
.ReparseDataLength = @intCast(buf_len - header_len),
.Reserved = 0,
.SubstituteNameOffset = @intCast(final_target_path.len * 2),
.SubstituteNameLength = @intCast(final_target_path.len * 2),
.PrintNameOffset = 0,
.PrintNameLength = @intCast(final_target_path.len * 2),
.Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
};
@memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
@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)));
_ = DeviceIoControl(symlink_handle, FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] }) catch |err| switch (err) {
error.PipeClosing => unreachable,
error.PipeAlreadyConnected => unreachable,
error.PipeAlreadyListening => unreachable,
error.Pending => unreachable,
else => |e| return e,
};
}
pub const ReadLinkError = error{
FileNotFound,
NetworkNotFound,
AccessDenied,
Unexpected,
NameTooLong,
BadPathName,
AntivirusInterference,
UnsupportedReparsePointType,
};
/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
/// is safe to reuse a single buffer for both.
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
const result_handle = OpenFile(sub_path_w, .{
.access_mask = .{
.SPECIFIC = .{ .FILE = .{
.READ_ATTRIBUTES = true,
} },
.STANDARD = .{ .SYNCHRONIZE = true },
},
.dir = dir,
.creation = .OPEN,
.follow_symlinks = false,
.filter = .any,
}) catch |err| switch (err) {
error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
error.WouldBlock => return error.Unexpected,
error.NoDevice => return error.FileNotFound,
error.PipeBusy => return error.AccessDenied,
else => |e| return e,
};
defer CloseHandle(result_handle);
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
_ = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] }) catch |err| switch (err) {
error.PipeClosing => unreachable,
error.PipeAlreadyConnected => unreachable,
error.PipeAlreadyListening => unreachable,
error.AccessDenied => return error.Unexpected,
error.UnrecognizedVolume => return error.Unexpected,
error.Pending => unreachable,
else => |e| return e,
};
const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;
switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
@as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.SYMLINK)) => {
const buf: *const SYMBOLIC_LINK_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);
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
return parseReadLinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
},
@as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.MOUNT_POINT)) => {
const buf: *const 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);
return parseReadLinkPath(path_buf[offset..][0..len], false, out_buffer);
},
else => return error.UnsupportedReparsePointType,
}
}
fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
path: {
if (is_relative) break :path;
return ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
error.NameTooLong => |e| return e,
error.NotNtPath => break :path,
};
}
if (out_buffer.len < path.len) return error.NameTooLong;
const dest = out_buffer[0..path.len];
@memcpy(dest, path);
return dest;
}
pub const DeleteFileError = error{
FileNotFound,
AccessDenied,
NameTooLong,
/// Also known as sharing violation.
FileBusy,
Unexpected,
NotDir,
IsDir,
DirNotEmpty,
NetworkNotFound,
};
pub const DeleteFileOptions = struct {
dir: ?HANDLE,
remove_dir: bool = false,
};
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
var nt_name: 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) {
// Can't remove the parent directory with an open handle.
return error.FileBusy;
}
var io: IO_STATUS_BLOCK = undefined;
var tmp_handle: HANDLE = undefined;
var rc = ntdll.NtCreateFile(
&tmp_handle,
.{ .STANDARD = .{
.RIGHTS = .{ .DELETE = true },
.SYNCHRONIZE = true,
} },
&.{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
.Attributes = .{},
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
},
&io,
null,
.{},
.VALID_FLAGS,
.OPEN,
.{
.DIRECTORY_FILE = options.remove_dir,
.NON_DIRECTORY_FILE = !options.remove_dir,
.OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead?
},
null,
0,
);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
.INVALID_PARAMETER => unreachable,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
.SHARING_VIOLATION => return error.FileBusy,
.ACCESS_DENIED => return error.AccessDenied,
.DELETE_PENDING => return,
else => return unexpectedStatus(rc),
}
defer CloseHandle(tmp_handle);
// FileDispositionInformationEx has varying levels of support:
// - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1
// (INVALID_INFO_CLASS is returned if not supported)
// - Requires the NTFS filesystem
// (on filesystems like FAT32, INVALID_PARAMETER is returned)
// - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1
// - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
// (NOT_SUPPORTED is returned if a flag is unsupported)
//
// The strategy here is just to try using FileDispositionInformationEx and fall back to
// FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
// Deletion with posix semantics if the filesystem supports it.
const info: FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
.DELETE = true,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
} };
rc = ntdll.NtSetInformationFile(
tmp_handle,
&io,
&info,
@sizeOf(FILE.DISPOSITION.INFORMATION.EX),
.DispositionEx,
);
switch (rc) {
.SUCCESS => return,
// The filesystem does not support FileDispositionInformationEx
.INVALID_PARAMETER,
// The operating system does not support FileDispositionInformationEx
.INVALID_INFO_CLASS,
// The operating system does not support one of the flags
.NOT_SUPPORTED,
=> break :need_fallback true,
// For all other statuses, fall down to the switch below to handle them.
else => break :need_fallback false,
}
};
if (need_fallback) {
// Deletion with file pending semantics, which requires waiting or moving
// files to get them removed (from here).
const file_dispo: FILE.DISPOSITION.INFORMATION = .{
.DeleteFile = TRUE,
};
rc = ntdll.NtSetInformationFile(
tmp_handle,
&io,
&file_dispo,
@sizeOf(FILE.DISPOSITION.INFORMATION),
.Disposition,
);
}
switch (rc) {
.SUCCESS => {},
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
.INVALID_PARAMETER => unreachable,
.CANNOT_DELETE => return error.AccessDenied,
.MEDIA_WRITE_PROTECTED => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
else => return unexpectedStatus(rc),
}
}
pub const RenameError = error{
IsDir,
NotDir,
FileNotFound,
NoDevice,
AccessDenied,
PipeBusy,
PathAlreadyExists,
Unexpected,
NameTooLong,
NetworkNotFound,
AntivirusInterference,
BadPathName,
RenameAcrossMountPoints,
} || UnexpectedError;
pub fn RenameFile(
/// May only be `null` if `old_path_w` is a fully-qualified absolute path.
old_dir_fd: ?HANDLE,
old_path_w: []const u16,
/// May only be `null` if `new_path_w` is a fully-qualified absolute path,
/// or if the file is not being moved to a different directory.
new_dir_fd: ?HANDLE,
new_path_w: []const u16,
replace_if_exists: bool,
) RenameError!void {
const src_fd = OpenFile(old_path_w, .{
.dir = old_dir_fd,
.access_mask = .{
.STANDARD = .{
.RIGHTS = .{ .DELETE = true },
.SYNCHRONIZE = true,
},
.GENERIC = .{ .WRITE = true },
},
.creation = .OPEN,
.filter = .any, // This function is supposed to rename both files and directories.
.follow_symlinks = false,
}) catch |err| switch (err) {
error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
else => |e| return e,
};
defer CloseHandle(src_fd);
var rc: NTSTATUS = undefined;
// FileRenameInformationEx has varying levels of support:
// - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
// (INVALID_INFO_CLASS is returned if not supported)
// - Requires the NTFS filesystem
// (on filesystems like FAT32, INVALID_PARAMETER is returned)
// - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1
// - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
// (NOT_SUPPORTED is returned if a flag is unsupported)
//
// The strategy here is just to try using FileRenameInformationEx and fall back to
// FileRenameInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
const rename_info: FILE.RENAME_INFORMATION = .init(.{
.Flags = .{
.REPLACE_IF_EXISTS = replace_if_exists,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
},
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
.FileName = new_path_w,
});
var io_status_block: IO_STATUS_BLOCK = undefined;
const rename_info_buf = rename_info.toBuffer();
rc = ntdll.NtSetInformationFile(
src_fd,
&io_status_block,
rename_info_buf.ptr,
@intCast(rename_info_buf.len), // already checked for error.NameTooLong
.RenameEx,
);
switch (rc) {
.SUCCESS => return,
// The filesystem does not support FileDispositionInformationEx
.INVALID_PARAMETER,
// The operating system does not support FileDispositionInformationEx
.INVALID_INFO_CLASS,
// The operating system does not support one of the flags
.NOT_SUPPORTED,
=> break :need_fallback true,
// For all other statuses, fall down to the switch below to handle them.
else => break :need_fallback false,
}
};
if (need_fallback) {
const rename_info: FILE.RENAME_INFORMATION = .init(.{
.Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir_fd,
.FileName = new_path_w,
});
var io_status_block: IO_STATUS_BLOCK = undefined;
const rename_info_buf = rename_info.toBuffer();
rc = ntdll.NtSetInformationFile(
src_fd,
&io_status_block,
rename_info_buf.ptr,
@intCast(rename_info_buf.len), // already checked for error.NameTooLong
.Rename,
);
}
switch (rc) {
.SUCCESS => {},
.INVALID_HANDLE => unreachable,
.INVALID_PARAMETER => unreachable,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.ACCESS_DENIED => return error.AccessDenied,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
.DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
else => return unexpectedStatus(rc),
}
}
pub const GetStdHandleError = error{
NoStandardHandleAttached,
Unexpected,
};
pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
if (handle == INVALID_HANDLE_VALUE) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return handle;
}
pub const SetFilePointerError = error{
Unseekable,
Unexpected,
};
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
// "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 = @as(LARGE_INTEGER, @bitCast(offset));
if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
switch (GetLastError()) {
.INVALID_FUNCTION => return error.Unseekable,
.NEGATIVE_SEEK => return error.Unseekable,
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
switch (GetLastError()) {
.INVALID_FUNCTION => return error.Unseekable,
.NEGATIVE_SEEK => return error.Unseekable,
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
switch (GetLastError()) {
.INVALID_FUNCTION => return error.Unseekable,
.NEGATIVE_SEEK => return error.Unseekable,
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with parameters to get the current offset.
pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
var result: LARGE_INTEGER = undefined;
if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
switch (GetLastError()) {
.INVALID_FUNCTION => return error.Unseekable,
.NEGATIVE_SEEK => return error.Unseekable,
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
// Based on the docs for FILE_BEGIN, it seems that the returned signed integer
// should be interpreted as an unsigned integer.
return @as(u64, @bitCast(result));
}
pub const QueryObjectNameError = error{
AccessDenied,
InvalidHandle,
NameTooLong,
Unexpected,
};
pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned));
// buffer size is specified in bytes
const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse maxInt(ULONG);
// last argument would return the length required for full_buffer, not exposed here
return switch (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];
},
.ACCESS_DENIED => error.AccessDenied,
.INVALID_HANDLE => error.InvalidHandle,
// triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
// or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
.INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong,
else => |e| unexpectedStatus(e),
};
}
test QueryObjectName {
if (builtin.os.tag != .windows)
return;
//any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const handle = tmp.dir.fd;
var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
const result_path = try QueryObjectName(handle, &out_buffer);
const required_len_in_u16 = result_path.len + @divExact(@intFromPtr(result_path.ptr) - @intFromPtr(&out_buffer), 2) + 1;
//insufficient size
try std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
//exactly-sufficient size
_ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
}
pub const GetFinalPathNameByHandleError = error{
AccessDenied,
BadPathName,
FileNotFound,
NameTooLong,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Unexpected,
};
/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
/// Defaults to DOS volume names.
pub const GetFinalPathNameByHandleFormat = struct {
volume_name: enum {
/// Format as DOS volume name
Dos,
/// Format as NT volume name
Nt,
} = .Dos,
};
/// Returns canonical (normalized) path of handle.
/// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
/// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
/// If DOS volume name format is selected, note that this function does *not* prepend
/// `\\?\` prefix to the resultant path.
pub fn GetFinalPathNameByHandle(
hFile: HANDLE,
fmt: GetFinalPathNameByHandleFormat,
out_buffer: []u16,
) GetFinalPathNameByHandleError![]u16 {
const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
// we assume InvalidHandle is close enough to FileNotFound in semantics
// to not further complicate the error set
error.InvalidHandle => return error.FileNotFound,
else => |e| return e,
};
switch (fmt.volume_name) {
.Nt => {
// the returned path is already in .Nt format
return final_path;
},
.Dos => {
// parse the string to separate volume path from file path
const device_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
// We aren't entirely sure of the structure of the path returned by
// QueryObjectName in all contexts/environments.
// This code is written to cover the various cases that have
// been encountered and solved appropriately. But note that there's
// no easy way to verify that they have all been tackled!
// (Unless you, the reader knows of one then please do action that!)
if (!mem.startsWith(u16, final_path, device_prefix)) {
// Wine seems to return NT namespaced paths starting with \??\ from QueryObjectName
// (e.g. `\??\Z:\some\path\to\a\file.txt`), in which case we can just strip the
// prefix to turn it into an absolute path.
// https://github.com/ziglang/zig/issues/26029
// https://bugs.winehq.org/show_bug.cgi?id=39569
return ntToWin32Namespace(final_path, out_buffer) catch |err| switch (err) {
error.NotNtPath => return error.Unexpected,
error.NameTooLong => |e| return e,
};
}
const file_path_begin_index = mem.indexOfPos(u16, final_path, device_prefix.len, &[_]u16{'\\'}) orelse unreachable;
const volume_name_u16 = final_path[0..file_path_begin_index];
const device_name_u16 = volume_name_u16[device_prefix.len..];
const file_name_u16 = final_path[file_path_begin_index..];
// MUP is Multiple UNC Provider, and indicates that the path is a UNC
// path. In this case, the canonical UNC path can be gotten by just
// dropping the \Device\Mup\ and making sure the path begins with \\
if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
out_buffer[0] = '\\';
@memmove(out_buffer[1..][0..file_name_u16.len], file_name_u16);
return out_buffer[0 .. 1 + file_name_u16.len];
}
// Get DOS volume name. DOS volume names are actually symbolic link objects to the
// actual NT volume. For example:
// (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
const MIN_SIZE = @sizeOf(MOUNTMGR_MOUNT_POINT) + MAX_PATH;
// We initialize the input buffer to all zeros for convenience since
// `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
var input_buf: [MIN_SIZE]u8 align(@alignOf(MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(MOUNTMGR_MOUNT_POINTS)) = undefined;
// This surprising path is a filesystem path to the mount manager on Windows.
// Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
// This is the NT namespaced version of \\.\MountPointManager
const mgmt_path_u16 = std.unicode.utf8ToUtf16LeStringLiteral("\\??\\MountPointManager");
const mgmt_handle = OpenFile(mgmt_path_u16, .{
.access_mask = .{ .STANDARD = .{ .SYNCHRONIZE = true } },
.creation = .OPEN,
}) catch |err| switch (err) {
error.IsDir => return error.Unexpected,
error.NotDir => return error.Unexpected,
error.NoDevice => return error.Unexpected,
error.AccessDenied => return error.Unexpected,
error.PipeBusy => return error.Unexpected,
error.PathAlreadyExists => return error.Unexpected,
error.WouldBlock => return error.Unexpected,
error.NetworkNotFound => return error.Unexpected,
error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(mgmt_handle);
var input_struct: *MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]);
input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
@memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf }) catch |err| switch (err) {
error.PipeClosing => unreachable,
error.PipeAlreadyConnected => unreachable,
error.PipeAlreadyListening => unreachable,
error.AccessDenied => return error.Unexpected,
error.Pending => unreachable,
else => |e| return e,
};
const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
const mount_points = @as(
[*]const MOUNTMGR_MOUNT_POINT,
@ptrCast(&mount_points_struct.MountPoints[0]),
)[0..mount_points_struct.NumberOfMountPoints];
for (mount_points) |mount_point| {
const symlink = @as(
[*]const u16,
@ptrCast(@alignCast(&output_buf[mount_point.SymbolicLinkNameOffset])),
)[0 .. mount_point.SymbolicLinkNameLength / 2];
// Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
// with traditional DOS drive letters, so pick the first one available.
var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
const prefix = prefix_buf[0..prefix_buf.len];
if (mem.startsWith(u16, symlink, prefix)) {
const drive_letter = symlink[prefix.len..];
if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
@memcpy(out_buffer[0..drive_letter.len], drive_letter);
@memmove(out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
const total_len = drive_letter.len + file_name_u16.len;
// Validate that DOS does not contain any spurious nul bytes.
if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
return error.BadPathName;
}
return out_buffer[0..total_len];
} else if (mountmgrIsVolumeName(symlink)) {
// If the symlink is a volume GUID like \??\Volume{383da0b0-717f-41b6-8c36-00500992b58d},
// then it is a volume mounted as a path rather than a drive letter. We need to
// query the mount manager again to get the DOS path for the volume.
// 49 is the maximum length accepted by mountmgrIsVolumeName
const vol_input_size = @sizeOf(MOUNTMGR_TARGET_NAME) + (49 * 2);
var vol_input_buf: [vol_input_size]u8 align(@alignOf(MOUNTMGR_TARGET_NAME)) = [_]u8{0} ** vol_input_size;
// Note: If the path exceeds MAX_PATH, the Disk Management GUI doesn't accept the full path,
// and instead if must be specified using a shortened form (e.g. C:\FOO~1\BAR~1\<...>).
// However, just to be sure we can handle any path length, we use PATH_MAX_WIDE here.
const min_output_size = @sizeOf(MOUNTMGR_VOLUME_PATHS) + (PATH_MAX_WIDE * 2);
var vol_output_buf: [min_output_size]u8 align(@alignOf(MOUNTMGR_VOLUME_PATHS)) = undefined;
var vol_input_struct: *MOUNTMGR_TARGET_NAME = @ptrCast(&vol_input_buf[0]);
vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
@memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf }) catch |err| switch (err) {
error.PipeClosing => unreachable,
error.PipeAlreadyConnected => unreachable,
error.PipeAlreadyListening => unreachable,
error.AccessDenied => return error.Unexpected,
error.Pending => unreachable,
else => |e| return e,
};
const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
const volume_path = std.mem.sliceTo(@as(
[*]const u16,
&volume_paths_struct.MultiSz,
)[0 .. volume_paths_struct.MultiSzLength / 2], 0);
if (out_buffer.len < volume_path.len + file_name_u16.len) return error.NameTooLong;
// `out_buffer` currently contains the memory of `file_name_u16`, so it can overlap with where
// we want to place the filename before returning. Here are the possible overlapping cases:
//
// out_buffer: [filename]
// dest: [___(a)___] [___(b)___]
//
// In the case of (a), we need to copy forwards, and in the case of (b) we need
// to copy backwards. We also need to do this before copying the volume path because
// it could overwrite the file_name_u16 memory.
const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len];
@memmove(file_name_dest, file_name_u16);
@memcpy(out_buffer[0..volume_path.len], volume_path);
const total_len = volume_path.len + file_name_u16.len;
// Validate that DOS does not contain any spurious nul bytes.
if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
return error.BadPathName;
}
return out_buffer[0..total_len];
}
}
// If we've ended up here, then something went wrong/is corrupted in the OS,
// so error out!
return error.FileNotFound;
},
}
}
/// Equivalent to the MOUNTMGR_IS_VOLUME_NAME macro in mountmgr.h
fn mountmgrIsVolumeName(name: []const u16) bool {
return (name.len == 48 or (name.len == 49 and name[48] == mem.nativeToLittle(u16, '\\'))) and
name[0] == mem.nativeToLittle(u16, '\\') and
(name[1] == mem.nativeToLittle(u16, '?') or name[1] == mem.nativeToLittle(u16, '\\')) and
name[2] == mem.nativeToLittle(u16, '?') and
name[3] == mem.nativeToLittle(u16, '\\') and
mem.startsWith(u16, name[4..], std.unicode.utf8ToUtf16LeStringLiteral("Volume{")) and
name[19] == mem.nativeToLittle(u16, '-') and
name[24] == mem.nativeToLittle(u16, '-') and
name[29] == mem.nativeToLittle(u16, '-') and
name[34] == mem.nativeToLittle(u16, '-') and
name[47] == mem.nativeToLittle(u16, '}');
}
test mountmgrIsVolumeName {
@setEvalBranchQuota(2000);
const L = std.unicode.utf8ToUtf16LeStringLiteral;
try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\\\.\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\foo")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58}")));
}
test GetFinalPathNameByHandle {
if (builtin.os.tag != .windows)
return;
//any file will do
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const handle = tmp.dir.fd;
var buffer: [PATH_MAX_WIDE]u16 = undefined;
//check with sufficient size
const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
const required_len_in_u16 = nt_path.len + @divExact(@intFromPtr(nt_path.ptr) - @intFromPtr(&buffer), 2) + 1;
//check with insufficient size
try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
//check with exactly-sufficient size
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
}
pub const GetFileSizeError = error{Unexpected};
pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
var file_size: LARGE_INTEGER = undefined;
if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return @as(u64, @bitCast(file_size));
}
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 TerminateProcessError = error{ AccessDenied, Unexpected };
pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
switch (GetLastError()) {
Win32Error.ACCESS_DENIED => return error.AccessDenied,
else => |err| return unexpectedError(err),
}
}
}
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 const VirtualProtectError = error{
InvalidAddress,
Unexpected,
};
pub fn VirtualProtect(lpAddress: ?LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: *DWORD) VirtualProtectError!void {
// ntdll takes an extra level of indirection here
var addr = lpAddress;
var size = dwSize;
switch (ntdll.NtProtectVirtualMemory(GetCurrentProcess(), &addr, &size, flNewProtect, lpflOldProtect)) {
.SUCCESS => {},
.INVALID_ADDRESS => return error.InvalidAddress,
else => |st| return unexpectedStatus(st),
}
}
pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: DWORD) VirtualProtectError!DWORD {
var old_prot: DWORD = undefined;
var out_addr = addr;
var out_size = size;
switch (ntdll.NtProtectVirtualMemory(
handle,
&out_addr,
&out_size,
new_prot,
&old_prot,
)) {
.SUCCESS => return old_prot,
.INVALID_ADDRESS => return error.InvalidAddress,
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const SetConsoleTextAttributeError = error{Unexpected};
pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
const success = kernel32.SetConsoleCtrlHandler(
handler_routine,
if (add) TRUE else FALSE,
);
if (success == FALSE) {
return switch (GetLastError()) {
else => |err| unexpectedError(err),
};
}
}
pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
if (success == FALSE) {
return switch (GetLastError()) {
else => |err| unexpectedError(err),
};
}
}
pub const CreateProcessError = error{
FileNotFound,
AccessDenied,
InvalidName,
NameTooLong,
InvalidExe,
SystemResources,
FileBusy,
Unexpected,
};
pub const CreateProcessFlags = packed struct(u32) {
debug_process: bool = false,
debug_only_this_process: bool = false,
create_suspended: bool = false,
detached_process: bool = false,
create_new_console: bool = false,
normal_priority_class: bool = false,
idle_priority_class: bool = false,
high_priority_class: bool = false,
realtime_priority_class: bool = false,
create_new_process_group: bool = false,
create_unicode_environment: bool = false,
create_separate_wow_vdm: bool = false,
create_shared_wow_vdm: bool = false,
create_forcedos: bool = false,
below_normal_priority_class: bool = false,
above_normal_priority_class: bool = false,
inherit_parent_affinity: bool = false,
inherit_caller_priority: bool = false,
create_protected_process: bool = false,
extended_startupinfo_present: bool = false,
process_mode_background_begin: bool = false,
process_mode_background_end: bool = false,
create_secure_process: bool = false,
_reserved: bool = false,
create_breakaway_from_job: bool = false,
create_preserve_code_authz_level: bool = false,
create_default_error_mode: bool = false,
create_no_window: bool = false,
profile_user: bool = false,
profile_kernel: bool = false,
profile_server: bool = false,
create_ignore_system_default: bool = false,
};
pub fn CreateProcessW(
lpApplicationName: ?LPCWSTR,
lpCommandLine: ?LPWSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: CreateProcessFlags,
lpEnvironment: ?*anyopaque,
lpCurrentDirectory: ?LPCWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
) CreateProcessError!void {
if (kernel32.CreateProcessW(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
) == 0) {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.DIRECTORY => return error.FileNotFound,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_PARAMETER => unreachable,
.INVALID_NAME => return error.InvalidName,
.FILENAME_EXCED_RANGE => return error.NameTooLong,
.SHARING_VIOLATION => return error.FileBusy,
// These are all the system errors that are mapped to ENOEXEC by
// the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
// (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
// or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
.BAD_FORMAT,
.INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
.INVALID_STACKSEG,
.INVALID_MODULETYPE,
.INVALID_EXE_SIGNATURE,
.EXE_MARKED_INVALID,
.BAD_EXE_FORMAT,
.ITERATED_DATA_EXCEEDS_64k,
.INVALID_MINALLOCSIZE,
.DYNLINK_FROM_INVALID_RING,
.IOPL_NOT_ENABLED,
.INVALID_SEGDPL,
.AUTODATASEG_EXCEEDS_64k,
.RING2SEG_MUST_BE_MOVABLE,
.RELOC_CHAIN_XEEDS_SEGLIM,
.INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
// This one is not mapped to ENOEXEC but it is possible, for example
// when calling CreateProcessW on a plain text file with a .exe extension
.EXE_MACHINE_TYPE_MISMATCH,
=> return error.InvalidExe,
.COMMITMENT_LIMIT => return error.SystemResources,
else => |err| return unexpectedError(err),
}
}
}
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));
}
pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*anyopaque, Context: ?*anyopaque) void {
assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
}
pub const SetFileTimeError = error{Unexpected};
pub fn SetFileTime(
hFile: HANDLE,
lpCreationTime: ?*const FILETIME,
lpLastAccessTime: ?*const FILETIME,
lpLastWriteTime: ?*const FILETIME,
) SetFileTimeError!void {
const rc = kernel32.SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
if (rc == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const LockFileError = error{
SystemResources,
WouldBlock,
} || UnexpectedError;
pub fn LockFile(
FileHandle: HANDLE,
Event: ?HANDLE,
ApcRoutine: ?*const IO_APC_ROUTINE,
ApcContext: ?*anyopaque,
IoStatusBlock: *IO_STATUS_BLOCK,
ByteOffset: *const LARGE_INTEGER,
Length: *const LARGE_INTEGER,
Key: ?*ULONG,
FailImmediately: BOOLEAN,
ExclusiveLock: BOOLEAN,
) !void {
const rc = ntdll.NtLockFile(
FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
ByteOffset,
Length,
Key,
FailImmediately,
ExclusiveLock,
);
switch (rc) {
.SUCCESS => return,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
.LOCK_NOT_GRANTED => return error.WouldBlock,
.ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
else => return unexpectedStatus(rc),
}
}
pub const UnlockFileError = error{
RangeNotLocked,
} || UnexpectedError;
pub fn UnlockFile(
FileHandle: HANDLE,
IoStatusBlock: *IO_STATUS_BLOCK,
ByteOffset: *const LARGE_INTEGER,
Length: *const LARGE_INTEGER,
Key: ULONG,
) !void {
const rc = ntdll.NtUnlockFile(FileHandle, IoStatusBlock, ByteOffset, Length, Key);
switch (rc) {
.SUCCESS => return,
.RANGE_NOT_LOCKED => return error.RangeNotLocked,
.ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
else => return unexpectedStatus(rc),
}
}
/// 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 (
\\ movl %%fs:0x18, %[ptr]
: [ptr] "=r" (-> *TEB),
),
.x86_64 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_x86_64_windows_teb()))
else
asm (
\\ movq %%gs:0x30, %[ptr]
: [ptr] "=r" (-> *TEB),
),
else => @compileError("unsupported arch"),
};
}
pub fn peb() *PEB {
return teb().ProcessEnvironmentBlock;
}
/// A file time is a 64-bit value that represents the number of 100-nanosecond
/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
/// Universal Time (UTC).
/// This function returns the number of nanoseconds since the canonical epoch,
/// which is the POSIX one (Jan 01, 1970 AD).
pub fn fromSysTime(hns: i64) Io.Timestamp {
const adjusted_epoch: i128 = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
return .fromNanoseconds(@intCast(adjusted_epoch * 100));
}
pub fn toSysTime(ns: Io.Timestamp) i64 {
const hns = @divFloor(ns.nanoseconds, 100);
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)),
};
}
/// Compares two WTF16 strings using the equivalent functionality of
/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
/// This function can be called on any target.
pub fn eqlIgnoreCaseWtf16(a: []const u16, b: []const u16) bool {
if (@inComptime() or builtin.os.tag != .windows) {
// This function compares the strings code unit by code unit (aka u16-to-u16),
// so any length difference implies inequality. In other words, there's no possible
// conversion that changes the number of WTF-16 code units needed for the uppercase/lowercase
// version in the conversion table since only codepoints <= max(u16) are eligible
// for conversion at all.
if (a.len != b.len) return false;
for (a, b) |a_c, b_c| {
// The slices are always WTF-16 LE, so need to convert the elements to native
// 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)) {
return false;
}
}
return true;
}
// 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;
}
/// Compares two WTF-8 strings using the equivalent functionality of
/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
/// This function can be called on any target.
/// Assumes `a` and `b` are valid WTF-8.
pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool {
// A length equality check is not possible here because there are
// some codepoints that have a different length uppercase UTF-8 representations
// than their lowercase counterparts, e.g. U+0250 (2 bytes) <-> U+2C6F (3 bytes).
// There are 7 such codepoints in the uppercase data used by Windows.
var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator();
var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator();
// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
// redundant copy of the uppercase data.
const upcaseImpl = switch (builtin.os.tag) {
.windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar,
else => nls.upcaseW,
};
while (true) {
const a_cp = a_wtf8_it.nextCodepoint() orelse break;
const b_cp = b_wtf8_it.nextCodepoint() orelse return false;
if (a_cp <= maxInt(u16) and b_cp <= maxInt(u16)) {
if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) {
return false;
}
} else if (a_cp != b_cp) {
return false;
}
}
// Make sure there are no leftover codepoints in b
if (b_wtf8_it.nextCodepoint() != null) return false;
return true;
}
fn testEqlIgnoreCase(comptime expect_eql: bool, comptime a: []const u8, comptime b: []const u8) !void {
try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWtf8(a, b));
try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWtf16(
std.unicode.utf8ToUtf16LeStringLiteral(a),
std.unicode.utf8ToUtf16LeStringLiteral(b),
));
try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWtf8(a, b));
try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWtf16(
std.unicode.utf8ToUtf16LeStringLiteral(a),
std.unicode.utf8ToUtf16LeStringLiteral(b),
));
}
test "eqlIgnoreCaseWtf16/Wtf8" {
try testEqlIgnoreCase(true, "\x01 a B Λ ɐ", "\x01 A b λ Ɐ");
// does not do case-insensitive comparison for codepoints >= U+10000
try testEqlIgnoreCase(false, "𐓏", "𐓷");
}
pub const PathSpace = struct {
data: [PATH_MAX_WIDE:0]u16,
len: usize,
pub fn span(self: *const PathSpace) [:0]const u16 {
return self.data[0..self.len :0];
}
};
/// The error type for `removeDotDirsSanitized`
pub const RemoveDotDirsError = error{TooManyParentDirs};
/// Removes '.' and '..' path components from a "sanitized relative path".
/// A "sanitized path" is one where:
/// 1) all forward slashes have been replaced with back slashes
/// 2) all repeating back slashes have been collapsed
/// 3) the path is a relative one (does not start with a back slash)
pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize {
std.debug.assert(path.len == 0 or path[0] != '\\');
var write_idx: usize = 0;
var read_idx: usize = 0;
while (read_idx < path.len) {
if (path[read_idx] == '.') {
if (read_idx + 1 == path.len)
return write_idx;
const after_dot = path[read_idx + 1];
if (after_dot == '\\') {
read_idx += 2;
continue;
}
if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) {
if (write_idx == 0) return error.TooManyParentDirs;
std.debug.assert(write_idx >= 2);
write_idx -= 1;
while (true) {
write_idx -= 1;
if (write_idx == 0) break;
if (path[write_idx] == '\\') {
write_idx += 1;
break;
}
}
if (read_idx + 2 == path.len)
return write_idx;
read_idx += 3;
continue;
}
}
// skip to the next path separator
while (true) : (read_idx += 1) {
if (read_idx == path.len)
return write_idx;
path[write_idx] = path[read_idx];
write_idx += 1;
if (path[read_idx] == '\\')
break;
}
read_idx += 1;
}
return write_idx;
}
/// Normalizes a Windows path with the following steps:
/// 1) convert all forward slashes to back slashes
/// 2) collapse duplicate back slashes
/// 3) remove '.' and '..' directory parts
/// Returns the length of the new path.
pub fn normalizePath(comptime T: type, path: []T) RemoveDotDirsError!usize {
mem.replaceScalar(T, path, '/', '\\');
const new_len = mem.collapseRepeatsLen(T, path, '\\');
const prefix_len: usize = init: {
if (new_len >= 1 and path[0] == '\\') break :init 1;
if (new_len >= 2 and path[1] == ':')
break :init if (new_len >= 3 and path[2] == '\\') @as(usize, 3) else @as(usize, 2);
break :init 0;
};
return prefix_len + try removeDotDirsSanitized(T, path[prefix_len..new_len]);
}
pub const Wtf8ToPrefixedFileWError = Wtf16ToPrefixedFileWError;
/// Same as `sliceToPrefixedFileW` but accepts a pointer
/// to a null-terminated WTF-8 encoded path.
/// https://wtf-8.codeberg.page/
pub fn cStrToPrefixedFileW(dir: ?HANDLE, s: [*:0]const u8) Wtf8ToPrefixedFileWError!PathSpace {
return sliceToPrefixedFileW(dir, mem.sliceTo(s, 0));
}
/// Same as `wToPrefixedFileW` but accepts a WTF-8 encoded path.
/// https://wtf-8.codeberg.page/
pub fn sliceToPrefixedFileW(dir: ?HANDLE, path: []const u8) Wtf8ToPrefixedFileWError!PathSpace {
var temp_path: PathSpace = undefined;
temp_path.len = std.unicode.wtf8ToWtf16Le(&temp_path.data, path) catch |err| switch (err) {
error.InvalidWtf8 => return error.BadPathName,
};
temp_path.data[temp_path.len] = 0;
return wToPrefixedFileW(dir, temp_path.span());
}
pub const Wtf16ToPrefixedFileWError = error{
AccessDenied,
BadPathName,
FileNotFound,
NameTooLong,
Unexpected,
};
/// Converts the `path` to WTF16, null-terminated. If the path contains any
/// namespace prefix, or is anything but a relative path (rooted, drive relative,
/// etc) the result will have the NT-style prefix `\??\`.
///
/// Similar to RtlDosPathNameToNtPathName_U with a few differences:
/// - Does not allocate on the heap.
/// - Relative paths are kept as relative unless they contain too many ..
/// components, in which case they are resolved against the `dir` if it
/// is non-null, or the CWD if it is null.
/// - Special case device names like COM1, NUL, etc are not handled specially (TODO)
/// - . and space are not stripped from the end of relative paths (potential TODO)
pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWError!PathSpace {
const nt_prefix = [_]u16{ '\\', '?', '?', '\\' };
if (hasCommonNtPrefix(u16, path)) {
// TODO: Figure out a way to design an API that can avoid the copy for NT,
// since it is always returned fully unmodified.
var path_space: PathSpace = undefined;
path_space.data[0..nt_prefix.len].* = nt_prefix;
const len_after_prefix = path.len - nt_prefix.len;
@memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
path_space.len = path.len;
path_space.data[path_space.len] = 0;
return path_space;
} else {
const path_type = getWin32PathType(u16, path);
var path_space: PathSpace = undefined;
if (path_type == .local_device) {
switch (getLocalDevicePathType(u16, path)) {
.verbatim => {
path_space.data[0..nt_prefix.len].* = nt_prefix;
const len_after_prefix = path.len - nt_prefix.len;
@memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
path_space.len = path.len;
path_space.data[path_space.len] = 0;
return path_space;
},
.local_device, .fake_verbatim => {
const path_byte_len = ntdll.RtlGetFullPathName_U(
path.ptr,
path_space.data.len * 2,
&path_space.data,
null,
);
if (path_byte_len == 0) {
// TODO: This may not be the right error
return error.BadPathName;
} else if (path_byte_len / 2 > path_space.data.len) {
return error.NameTooLong;
}
path_space.len = path_byte_len / 2;
// Both prefixes will be normalized but retained, so all
// we need to do now is replace them with the NT prefix
path_space.data[0..nt_prefix.len].* = nt_prefix;
return path_space;
},
}
}
relative: {
if (path_type == .relative) {
// TODO: Handle special case device names like COM1, AUX, NUL, CONIN$, CONOUT$, etc.
// See https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
// TODO: Potentially strip all trailing . and space characters from the
// end of the path. This is something that both RtlDosPathNameToNtPathName_U
// and RtlGetFullPathName_U do. Technically, trailing . and spaces
// are allowed, but such paths may not interact well with Windows (i.e.
// files with these paths can't be deleted from explorer.exe, etc).
// This could be something that normalizePath may want to do.
@memcpy(path_space.data[0..path.len], path);
// Try to normalize, but if we get too many parent directories,
// then we need to start over and use RtlGetFullPathName_U instead.
path_space.len = normalizePath(u16, path_space.data[0..path.len]) catch |err| switch (err) {
error.TooManyParentDirs => break :relative,
};
path_space.data[path_space.len] = 0;
return path_space;
}
}
// We now know we are going to return an absolute NT path, so
// we can unconditionally prefix it with the NT prefix.
path_space.data[0..nt_prefix.len].* = nt_prefix;
if (path_type == .root_local_device) {
// `\\.` and `\\?` always get converted to `\??\` exactly, so
// we can just stop here
path_space.len = nt_prefix.len;
path_space.data[path_space.len] = 0;
return path_space;
}
const path_buf_offset = switch (path_type) {
// UNC paths will always start with `\\`. However, we want to
// end up with something like `\??\UNC\server\share`, so to get
// RtlGetFullPathName to write into the spot we want the `server`
// part to end up, we need to provide an offset such that
// the `\\` part gets written where the `C\` of `UNC\` will be
// in the final NT path.
.unc_absolute => nt_prefix.len + 2,
else => nt_prefix.len,
};
const buf_len: u32 = @intCast(path_space.data.len - path_buf_offset);
const path_to_get: [:0]const u16 = path_to_get: {
// If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
// RtlGetFullPathName_U will resolve relative paths against the CWD for us.
if (path_type != .relative or dir == null) {
break :path_to_get path;
}
// We can also skip GetFinalPathNameByHandle if the handle matches
// the handle returned by fs.cwd()
if (dir.? == std.fs.cwd().fd) {
break :path_to_get path;
}
// At this point, we know we have a relative path that had too many
// `..` components to be resolved by normalizePath, so we need to
// convert it into an absolute path and let RtlGetFullPathName_U
// canonicalize it. We do this by getting the path of the `dir`
// and appending the relative path to it.
var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined;
const dir_path = GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf) catch |err| switch (err) {
// This mapping is not correct; it is actually expected
// that calling GetFinalPathNameByHandle might return
// error.UnrecognizedVolume, and in fact has been observed
// in the wild. The problem is that wToPrefixedFileW was
// never intended to make *any* OS syscall APIs. It's only
// supposed to convert a string to one that is eligible to
// be used in the ntdll syscalls.
//
// To solve this, this function needs to no longer call
// GetFinalPathNameByHandle under any conditions, or the
// calling function needs to get reworked to not need to
// call this function.
//
// This may involve making breaking API changes.
error.UnrecognizedVolume => return error.Unexpected,
else => |e| return e,
};
if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
return error.NameTooLong;
}
// We don't have to worry about potentially doubling up path separators
// here since RtlGetFullPathName_U will handle canonicalizing it.
dir_path_buf[dir_path.len] = '\\';
@memcpy(dir_path_buf[dir_path.len + 1 ..][0..path.len], path);
const full_len = dir_path.len + 1 + path.len;
dir_path_buf[full_len] = 0;
break :path_to_get dir_path_buf[0..full_len :0];
};
const path_byte_len = ntdll.RtlGetFullPathName_U(
path_to_get.ptr,
buf_len * 2,
path_space.data[path_buf_offset..].ptr,
null,
);
if (path_byte_len == 0) {
// TODO: This may not be the right error
return error.BadPathName;
} else if (path_byte_len / 2 > buf_len) {
return error.NameTooLong;
}
path_space.len = path_buf_offset + (path_byte_len / 2);
if (path_type == .unc_absolute) {
// Now add in the UNC, the `C` should overwrite the first `\` of the
// FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>`
std.debug.assert(path_space.data[path_buf_offset] == '\\');
std.debug.assert(path_space.data[path_buf_offset + 1] == '\\');
const unc = [_]u16{ 'U', 'N', 'C' };
path_space.data[nt_prefix.len..][0..unc.len].* = unc;
}
return path_space;
}
}
/// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type.
pub const Win32PathType = enum {
/// `\\server\share\foo`
unc_absolute,
/// `C:\foo`
drive_absolute,
/// `C:foo`
drive_relative,
/// `\foo`
rooted,
/// `foo`
relative,
/// `\\.\foo`, `\\?\foo`
local_device,
/// `\\.`, `\\?`
root_local_device,
};
/// Get the path type of a Win32 namespace path.
/// Similar to `RtlDetermineDosPathNameType_U`.
/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
if (path.len < 1) return .relative;
const windows_path = std.fs.path.PathType.windows;
if (windows_path.isSep(T, path[0])) {
// \x
if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
// \\. or \\?
if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
// exactly \\. or \\? with nothing trailing
if (path.len == 3) return .root_local_device;
// \\.\x or \\?\x
if (windows_path.isSep(T, path[3])) return .local_device;
}
// \\x
return .unc_absolute;
} else {
// Some choice has to be made about how non-ASCII code points as drive-letters are handled, since
// path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification
// for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code
// units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so
// checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16.
//
// `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers
// `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get
// classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded
// in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path).
//
// The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both
// WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI,
// drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you
// to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will
// allow you to set any WTF-16 code unit as a drive letter.
//
// Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g.
// `cd /D €:\` will work, filesystem functions still work, etc.
//
// The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't
// just check path[0], path[1], path[2].
const colon_i: usize = switch (T) {
u8 => i: {
const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative;
// Conveniently, 4-byte sequences in WTF-8 have the same starting code point
// as 2-code-unit sequences in WTF-16.
if (code_point_len > 3) return .relative;
break :i code_point_len;
},
u16 => 1,
else => @compileError("unsupported type: " ++ @typeName(T)),
};
// x
if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
// x:\
if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
// x:
return .drive_relative;
}
}
test getWin32PathType {
try std.testing.expectEqual(.relative, getWin32PathType(u8, ""));
try std.testing.expectEqual(.relative, getWin32PathType(u8, "x"));
try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\"));
try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//."));
try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?"));
try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?"));
try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x"));
try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x"));
try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x"));
// local device paths require a path separator after the root, otherwise it is considered a UNC path
try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x"));
try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x"));
try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//"));
try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x"));
try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x"));
try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x"));
try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/"));
try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:"));
try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc"));
try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c"));
try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\"));
try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc"));
try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c"));
// Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter
try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\"));
try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\")));
try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:"));
try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:")));
// But code points that are encoded as two WTF-16 code units are not
try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\"));
try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\")));
}
/// Returns true if the path starts with `\??\`, which is indicative of an NT path
/// but is not enough to fully distinguish between NT paths and Win32 paths, as
/// `\??\` is not actually a distinct prefix but rather the path to a special virtual
/// folder in the Object Manager.
///
/// For example, `\Device\HarddiskVolume2` and `\DosDevices\C:` are also NT paths but
/// cannot be distinguished as such by their prefix.
///
/// So, inferring whether a path is an NT path or a Win32 path is usually a mistake;
/// that information should instead be known ahead-of-time.
///
/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
pub fn hasCommonNtPrefix(comptime T: type, path: []const T) bool {
// Must be exactly \??\, forward slashes are not allowed
const expected_wtf8_prefix = "\\??\\";
const expected_prefix = switch (T) {
u8 => expected_wtf8_prefix,
u16 => std.unicode.wtf8ToWtf16LeStringLiteral(expected_wtf8_prefix),
else => @compileError("unsupported type: " ++ @typeName(T)),
};
return mem.startsWith(T, path, expected_prefix);
}
const LocalDevicePathType = enum {
/// `\\.\` (path separators can be `\` or `/`)
local_device,
/// `\\?\`
/// When converted to an NT path, everything past the prefix is left
/// untouched and `\\?\` is replaced by `\??\`.
verbatim,
/// `\\?\` without all path separators being `\`.
/// This seems to be recognized as a prefix, but the 'verbatim' aspect
/// is not respected (i.e. if `//?/C:/foo` is converted to an NT path,
/// it will become `\??\C:\foo` [it will be canonicalized and the //?/ won't
/// be treated as part of the final path])
fake_verbatim,
};
/// Only relevant for Win32 -> NT path conversion.
/// Asserts `path` is of type `Win32PathType.local_device`.
fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType {
if (std.debug.runtime_safety) {
std.debug.assert(getWin32PathType(T, path) == .local_device);
}
const backslash = mem.nativeToLittle(T, '\\');
const all_backslash = path[0] == backslash and
path[1] == backslash and
path[3] == backslash;
return switch (path[2]) {
mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim,
mem.nativeToLittle(T, '.') => .local_device,
else => unreachable,
};
}
/// Similar to `RtlNtPathNameToDosPathName` but does not do any heap allocation.
/// The possible transformations are:
/// \??\C:\Some\Path -> C:\Some\Path
/// \??\UNC\server\share\foo -> \\server\share\foo
/// If the path does not have the NT namespace prefix, then `error.NotNtPath` is returned.
///
/// Functionality is based on the ReactOS test cases found here:
/// https://github.com/reactos/reactos/blob/master/modules/rostests/apitests/ntdll/RtlNtPathNameToDosPathName.c
///
/// `path` should be encoded as WTF-16LE.
///
/// Supports in-place modification (`path` and `out` may refer to the same slice).
pub fn ntToWin32Namespace(path: []const u16, out: []u16) error{ NameTooLong, NotNtPath }![]u16 {
if (path.len > PATH_MAX_WIDE) return error.NameTooLong;
if (!hasCommonNtPrefix(u16, path)) return error.NotNtPath;
var dest_index: usize = 0;
var after_prefix = path[4..]; // after the `\??\`
// The prefix \??\UNC\ means this is a UNC path, in which case the
// `\??\UNC\` should be replaced by `\\` (two backslashes)
const is_unc = after_prefix.len >= 4 and
eqlIgnoreCaseWtf16(after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
std.fs.path.PathType.windows.isSep(u16, after_prefix[3]);
const win32_len = path.len - @as(usize, if (is_unc) 6 else 4);
if (out.len < win32_len) return error.NameTooLong;
if (is_unc) {
out[0] = comptime std.mem.nativeToLittle(u16, '\\');
dest_index += 1;
// We want to include the last `\` of `\??\UNC\`
after_prefix = path[7..];
}
@memmove(out[dest_index..][0..after_prefix.len], after_prefix);
return out[0..win32_len];
}
test ntToWin32Namespace {
const L = std.unicode.utf8ToUtf16LeStringLiteral;
var mutable_unc_path_buf = L("\\??\\UNC\\path1\\path2").*;
try std.testing.expectEqualSlices(u16, L("\\\\path1\\path2"), try ntToWin32Namespace(&mutable_unc_path_buf, &mutable_unc_path_buf));
var mutable_path_buf = L("\\??\\C:\\test\\").*;
try std.testing.expectEqualSlices(u16, L("C:\\test\\"), try ntToWin32Namespace(&mutable_path_buf, &mutable_path_buf));
var too_small_buf: [6]u16 = undefined;
try std.testing.expectError(error.NameTooLong, ntToWin32Namespace(L("\\??\\C:\\test"), &too_small_buf));
}
inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
return (s << 10) | p;
}
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
pub fn unexpectedError(err: Win32Error) UnexpectedError {
if (std.posix.unexpected_error_tracing) {
// 614 is the length of the longest windows error description
var buf_wstr: [614:0]WCHAR = undefined;
const len = kernel32.FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
null,
err,
MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
&buf_wstr,
buf_wstr.len,
null,
);
std.debug.print("error.Unexpected: GetLastError({d}): {f}\n", .{
err, std.unicode.fmtUtf16Le(buf_wstr[0..len]),
});
std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() });
}
return error.Unexpected;
}
pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError {
return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
}
/// Call this when you made a windows NtDll call
/// and you get an unexpected status.
pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
if (std.posix.unexpected_error_tracing) {
std.debug.print("error.Unexpected NTSTATUS=0x{x} ({s})\n", .{
@intFromEnum(status),
std.enums.tagName(NTSTATUS, status) orelse "<unnamed>",
});
std.debug.dumpCurrentStackTrace(.{ .first_address = @returnAddress() });
}
return error.Unexpected;
}
pub fn statusBug(status: NTSTATUS) UnexpectedError {
switch (builtin.mode) {
.Debug => std.debug.panic("programmer bug caused syscall status: 0x{x} ({s})", .{
@intFromEnum(status),
std.enums.tagName(NTSTATUS, status) orelse "<unnamed>",
}),
else => return error.Unexpected,
}
}
pub fn errorBug(err: Win32Error) UnexpectedError {
switch (builtin.mode) {
.Debug => std.debug.panic("programmer bug caused syscall error: 0x{x} ({s})", .{
@intFromEnum(err),
std.enums.tagName(Win32Error, err) orelse "<unnamed>",
}),
else => return error.Unexpected,
}
}
pub const Win32Error = @import("windows/win32error.zig").Win32Error;
pub const LANG = @import("windows/lang.zig");
pub const SUBLANG = @import("windows/sublang.zig");
/// The standard input device. Initially, this is the console input buffer, CONIN$.
pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
pub const BOOL = c_int;
pub const BOOLEAN = BYTE;
pub const BYTE = u8;
pub const CHAR = u8;
pub const UCHAR = u8;
pub const FLOAT = f32;
pub const HANDLE = *anyopaque;
pub const HCRYPTPROV = ULONG_PTR;
pub const ATOM = u16;
pub const HBRUSH = *opaque {};
pub const HCURSOR = *opaque {};
pub const HICON = *opaque {};
pub const HINSTANCE = *opaque {};
pub const HMENU = *opaque {};
pub const HMODULE = *opaque {};
pub const HWND = *opaque {};
pub const HDC = *opaque {};
pub const HGLRC = *opaque {};
pub const FARPROC = *opaque {};
pub const PROC = *opaque {};
pub const INT = c_int;
pub const LPCSTR = [*:0]const CHAR;
pub const LPCVOID = *const anyopaque;
pub const LPSTR = [*:0]CHAR;
pub const LPVOID = *anyopaque;
pub const LPWSTR = [*:0]WCHAR;
pub const LPCWSTR = [*:0]const WCHAR;
pub const PVOID = *anyopaque;
pub const PWSTR = [*:0]WCHAR;
pub const PCWSTR = [*:0]const WCHAR;
/// Allocated by SysAllocString, freed by SysFreeString
pub const BSTR = [*:0]WCHAR;
pub const SIZE_T = usize;
pub const UINT = c_uint;
pub const ULONG_PTR = usize;
pub const LONG_PTR = isize;
pub const DWORD_PTR = ULONG_PTR;
pub const WCHAR = u16;
pub const WORD = u16;
pub const DWORD = u32;
pub const DWORD64 = u64;
pub const LARGE_INTEGER = i64;
pub const ULARGE_INTEGER = u64;
pub const USHORT = u16;
pub const SHORT = i16;
pub const ULONG = u32;
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 WPARAM = usize;
pub const LPARAM = LONG_PTR;
pub const LRESULT = LONG_PTR;
pub const va_list = *opaque {};
pub const TCHAR = @compileError("Deprecated: choose between `CHAR` or `WCHAR` directly instead.");
pub const LPTSTR = @compileError("Deprecated: choose between `LPSTR` or `LPWSTR` directly instead.");
pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWSTR` directly instead.");
pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
pub const TRUE = 1;
pub const FALSE = 0;
pub const INVALID_HANDLE_VALUE: HANDLE = @ptrFromInt(maxInt(usize));
pub const INVALID_FILE_ATTRIBUTES: DWORD = maxInt(DWORD);
pub const IO_STATUS_BLOCK = extern struct {
// "DUMMYUNIONNAME" expands to "u"
u: extern union {
Status: NTSTATUS,
Pointer: ?*anyopaque,
},
Information: ULONG_PTR,
};
pub const OVERLAPPED = extern struct {
Internal: ULONG_PTR,
InternalHigh: ULONG_PTR,
DUMMYUNIONNAME: extern union {
DUMMYSTRUCTNAME: extern struct {
Offset: DWORD,
OffsetHigh: DWORD,
},
Pointer: ?PVOID,
},
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,
lpDesktop: ?LPWSTR,
lpTitle: ?LPWSTR,
dwX: DWORD,
dwY: DWORD,
dwXSize: DWORD,
dwYSize: DWORD,
dwXCountChars: DWORD,
dwYCountChars: DWORD,
dwFillAttribute: DWORD,
dwFlags: DWORD,
wShowWindow: WORD,
cbReserved2: WORD,
lpReserved2: ?*BYTE,
hStdInput: ?HANDLE,
hStdOutput: ?HANDLE,
hStdError: ?HANDLE,
};
pub const STARTF_FORCEONFEEDBACK = 0x00000040;
pub const STARTF_FORCEOFFFEEDBACK = 0x00000080;
pub const STARTF_PREVENTPINNING = 0x00002000;
pub const STARTF_RUNFULLSCREEN = 0x00000020;
pub const STARTF_TITLEISAPPID = 0x00001000;
pub const STARTF_TITLEISLINKNAME = 0x00000800;
pub const STARTF_UNTRUSTEDSOURCE = 0x00008000;
pub const STARTF_USECOUNTCHARS = 0x00000008;
pub const STARTF_USEFILLATTRIBUTE = 0x00000010;
pub const STARTF_USEHOTKEY = 0x00000200;
pub const STARTF_USEPOSITION = 0x00000004;
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 SYSTEM_INFO = extern struct {
anon1: extern union {
dwOemId: DWORD,
anon2: extern struct {
wProcessorArchitecture: WORD,
wReserved: WORD,
},
},
dwPageSize: DWORD,
lpMinimumApplicationAddress: LPVOID,
lpMaximumApplicationAddress: LPVOID,
dwActiveProcessorMask: DWORD_PTR,
dwNumberOfProcessors: DWORD,
dwProcessorType: DWORD,
dwAllocationGranularity: DWORD,
wProcessorLevel: WORD,
wProcessorRevision: WORD,
};
pub const HRESULT = c_long;
pub const KNOWNFOLDERID = GUID;
pub const GUID = extern struct {
Data1: u32,
Data2: u16,
Data3: u16,
Data4: [8]u8,
const hex_offsets = switch (builtin.target.cpu.arch.endian()) {
.big => [16]u6{
0, 2, 4, 6,
9, 11, 14, 16,
19, 21, 24, 26,
28, 30, 32, 34,
},
.little => [16]u6{
6, 4, 2, 0,
11, 9, 16, 14,
19, 21, 24, 26,
28, 30, 32, 34,
},
};
pub fn parse(s: []const u8) GUID {
assert(s[0] == '{');
assert(s[37] == '}');
return parseNoBraces(s[1 .. s.len - 1]) catch @panic("invalid GUID string");
}
pub fn parseNoBraces(s: []const u8) !GUID {
assert(s.len == 36);
assert(s[8] == '-');
assert(s[13] == '-');
assert(s[18] == '-');
assert(s[23] == '-');
var bytes: [16]u8 = undefined;
for (hex_offsets, 0..) |hex_offset, i| {
bytes[i] = (try std.fmt.charToDigit(s[hex_offset], 16)) << 4 |
try std.fmt.charToDigit(s[hex_offset + 1], 16);
}
return @as(GUID, @bitCast(bytes));
}
};
test GUID {
try std.testing.expectEqual(
GUID{
.Data1 = 0x01234567,
.Data2 = 0x89ab,
.Data3 = 0xef10,
.Data4 = "\x32\x54\x76\x98\xba\xdc\xfe\x91".*,
},
GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}"),
);
}
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,
EndAddressOfRawData: usize,
AddressOfIndex: usize,
AddressOfCallBacks: usize,
SizeOfZeroFill: u32,
Characteristics: u32,
};
pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
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;
pub const HKEY = *opaque {};
pub const HKEY_CLASSES_ROOT: HKEY = @ptrFromInt(0x80000000);
pub const HKEY_CURRENT_USER: HKEY = @ptrFromInt(0x80000001);
pub const HKEY_LOCAL_MACHINE: HKEY = @ptrFromInt(0x80000002);
pub const HKEY_USERS: HKEY = @ptrFromInt(0x80000003);
pub const HKEY_PERFORMANCE_DATA: HKEY = @ptrFromInt(0x80000004);
pub const HKEY_PERFORMANCE_TEXT: HKEY = @ptrFromInt(0x80000050);
pub const HKEY_PERFORMANCE_NLSTEXT: HKEY = @ptrFromInt(0x80000060);
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,
Name: ?PWSTR,
EntryContext: ?*anyopaque,
DefaultType: ULONG,
DefaultData: ?*anyopaque,
DefaultLength: ULONG,
};
pub const RTL_QUERY_REGISTRY_ROUTINE = ?*const fn (
PWSTR,
ULONG,
?*anyopaque,
ULONG,
?*anyopaque,
?*anyopaque,
) callconv(.winapi) NTSTATUS;
/// Path is a full path
pub const RTL_REGISTRY_ABSOLUTE = 0;
/// \Registry\Machine\System\CurrentControlSet\Services
pub const RTL_REGISTRY_SERVICES = 1;
/// \Registry\Machine\System\CurrentControlSet\Control
pub const RTL_REGISTRY_CONTROL = 2;
/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
pub const RTL_REGISTRY_WINDOWS_NT = 3;
/// \Registry\Machine\Hardware\DeviceMap
pub const RTL_REGISTRY_DEVICEMAP = 4;
/// \Registry\User\CurrentUser
pub const RTL_REGISTRY_USER = 5;
pub const RTL_REGISTRY_MAXIMUM = 6;
/// Low order bits are registry handle
pub const RTL_REGISTRY_HANDLE = 0x40000000;
/// Indicates the key node is optional
pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
/// Name is a subkey and remainder of table or until next subkey are value
/// names for that subkey to look at.
pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
/// Reset current key to original key for this and all following table entries.
pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
/// Fail if no match found for this table entry.
pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
/// Used to mark a table entry that has no value name, just wants a call out, not
/// an enumeration of all values.
pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
/// QueryRoutine field ignored. EntryContext field points to location to store value.
/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
/// allocated.
pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
/// Used to delete value keys after they are queried.
pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
/// of the stored registry value matches the type expected by the caller.
/// If the types do not match, the call fails.
pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
pub const REG = struct {
/// No value type
pub const NONE: ULONG = 0;
/// Unicode nul terminated string
pub const SZ: ULONG = 1;
/// Unicode nul terminated string (with environment variable references)
pub const EXPAND_SZ: ULONG = 2;
/// Free form binary
pub const BINARY: ULONG = 3;
/// 32-bit number
pub const DWORD: ULONG = 4;
/// 32-bit number (same as REG_DWORD)
pub const DWORD_LITTLE_ENDIAN: ULONG = 4;
/// 32-bit number
pub const DWORD_BIG_ENDIAN: ULONG = 5;
/// Symbolic Link (unicode)
pub const LINK: ULONG = 6;
/// Multiple Unicode strings
pub const MULTI_SZ: ULONG = 7;
/// Resource list in the resource map
pub const RESOURCE_LIST: ULONG = 8;
/// Resource list in the hardware description
pub const FULL_RESOURCE_DESCRIPTOR: ULONG = 9;
pub const RESOURCE_REQUIREMENTS_LIST: ULONG = 10;
/// 64-bit number
pub const QWORD: ULONG = 11;
/// 64-bit number (same as REG_QWORD)
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 CONSOLE_SCREEN_BUFFER_INFO = extern struct {
dwSize: COORD,
dwCursorPosition: COORD,
wAttributes: WORD,
srWindow: SMALL_RECT,
dwMaximumWindowSize: COORD,
};
pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
pub const FOREGROUND_BLUE = 1;
pub const FOREGROUND_GREEN = 2;
pub const FOREGROUND_RED = 4;
pub const FOREGROUND_INTENSITY = 8;
pub const LIST_ENTRY = extern struct {
Flink: *LIST_ENTRY,
Blink: *LIST_ENTRY,
};
pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
Type: WORD,
CreatorBackTraceIndex: WORD,
CriticalSection: *RTL_CRITICAL_SECTION,
ProcessLocksList: LIST_ENTRY,
EntryCount: DWORD,
ContentionCount: DWORD,
Flags: DWORD,
CreatorBackTraceIndexHigh: WORD,
SpareWORD: WORD,
};
pub const RTL_CRITICAL_SECTION = extern struct {
DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
LockCount: LONG,
RecursionCount: LONG,
OwningThread: HANDLE,
LockSemaphore: HANDLE,
SpinCount: ULONG_PTR,
};
pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
pub const INIT_ONCE = RTL_RUN_ONCE;
pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
pub const INIT_ONCE_FN = *const fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.winapi) BOOL;
pub const RTL_RUN_ONCE = extern struct {
Ptr: ?*anyopaque,
};
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.
/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
pub const PATH_MAX_WIDE = 32767;
/// > [Each file name component can be] up to the value returned in the
/// > lpMaximumComponentLength parameter of the GetVolumeInformation function
/// > (this value is commonly 255 characters)
/// from https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
///
/// > The value that is stored in the variable that *lpMaximumComponentLength points to is
/// > used to indicate that a specified file system supports long names. For example, for
/// > a FAT file system that supports long names, the function stores the value 255, rather
/// > than the previous 8.3 indicator. Long names can also be supported on systems that use
/// > the NTFS file system.
/// from https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
///
/// The assumption being made here is that while lpMaximumComponentLength may vary, it will never
/// be larger than 255.
///
/// TODO: More verification of this assumption.
pub const NAME_MAX = 255;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
pub const FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
pub const FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
pub const FORMAT_MESSAGE_FROM_STRING = 0x00000400;
pub const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
pub const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
pub const FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
pub const EXCEPTION_DATATYPE_MISALIGNMENT = 0x80000002;
pub const EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
pub const EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d;
pub const EXCEPTION_STACK_OVERFLOW = 0xc00000fd;
pub const EXCEPTION_CONTINUE_SEARCH = 0;
pub const EXCEPTION_RECORD = extern struct {
ExceptionCode: u32,
ExceptionFlags: u32,
ExceptionRecord: *EXCEPTION_RECORD,
ExceptionAddress: *anyopaque,
NumberParameters: u32,
ExceptionInformation: [15]usize,
};
pub const FLOATING_SAVE_AREA = switch (native_arch) {
.x86 => extern struct {
ControlWord: DWORD,
StatusWord: DWORD,
TagWord: DWORD,
ErrorOffset: DWORD,
ErrorSelector: DWORD,
DataOffset: DWORD,
DataSelector: DWORD,
RegisterArea: [80]BYTE,
Cr0NpxState: DWORD,
},
else => @compileError("FLOATING_SAVE_AREA only defined on x86"),
};
pub const M128A = switch (native_arch) {
.x86_64 => extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
else => @compileError("M128A only defined on x86_64"),
};
pub const XMM_SAVE_AREA32 = switch (native_arch) {
.x86_64 => extern struct {
ControlWord: WORD,
StatusWord: WORD,
TagWord: BYTE,
Reserved1: BYTE,
ErrorOpcode: WORD,
ErrorOffset: DWORD,
ErrorSelector: WORD,
Reserved2: WORD,
DataOffset: DWORD,
DataSelector: WORD,
Reserved3: WORD,
MxCsr: DWORD,
MxCsr_Mask: DWORD,
FloatRegisters: [8]M128A,
XmmRegisters: [16]M128A,
Reserved4: [96]BYTE,
},
else => @compileError("XMM_SAVE_AREA32 only defined on x86_64"),
};
pub const NEON128 = switch (native_arch) {
.thumb => extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
.aarch64 => extern union {
DUMMYSTRUCTNAME: extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
D: [2]f64,
S: [4]f32,
H: [8]WORD,
B: [16]BYTE,
},
else => @compileError("NEON128 only defined on aarch64"),
};
pub const CONTEXT = switch (native_arch) {
.x86 => extern struct {
ContextFlags: DWORD,
Dr0: DWORD,
Dr1: DWORD,
Dr2: DWORD,
Dr3: DWORD,
Dr6: DWORD,
Dr7: DWORD,
FloatSave: FLOATING_SAVE_AREA,
SegGs: DWORD,
SegFs: DWORD,
SegEs: DWORD,
SegDs: DWORD,
Edi: DWORD,
Esi: DWORD,
Ebx: DWORD,
Edx: DWORD,
Ecx: DWORD,
Eax: DWORD,
Ebp: DWORD,
Eip: DWORD,
SegCs: DWORD,
EFlags: DWORD,
Esp: DWORD,
SegSs: DWORD,
ExtendedRegisters: [512]BYTE,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{ .bp = ctx.Ebp, .ip = ctx.Eip, .sp = ctx.Esp };
}
},
.x86_64 => extern struct {
P1Home: DWORD64 align(16),
P2Home: DWORD64,
P3Home: DWORD64,
P4Home: DWORD64,
P5Home: DWORD64,
P6Home: DWORD64,
ContextFlags: DWORD,
MxCsr: DWORD,
SegCs: WORD,
SegDs: WORD,
SegEs: WORD,
SegFs: WORD,
SegGs: WORD,
SegSs: WORD,
EFlags: DWORD,
Dr0: DWORD64,
Dr1: DWORD64,
Dr2: DWORD64,
Dr3: DWORD64,
Dr6: DWORD64,
Dr7: DWORD64,
Rax: DWORD64,
Rcx: DWORD64,
Rdx: DWORD64,
Rbx: DWORD64,
Rsp: DWORD64,
Rbp: DWORD64,
Rsi: DWORD64,
Rdi: DWORD64,
R8: DWORD64,
R9: DWORD64,
R10: DWORD64,
R11: DWORD64,
R12: DWORD64,
R13: DWORD64,
R14: DWORD64,
R15: DWORD64,
Rip: DWORD64,
DUMMYUNIONNAME: extern union {
FltSave: XMM_SAVE_AREA32,
FloatSave: XMM_SAVE_AREA32,
DUMMYSTRUCTNAME: extern struct {
Header: [2]M128A,
Legacy: [8]M128A,
Xmm0: M128A,
Xmm1: M128A,
Xmm2: M128A,
Xmm3: M128A,
Xmm4: M128A,
Xmm5: M128A,
Xmm6: M128A,
Xmm7: M128A,
Xmm8: M128A,
Xmm9: M128A,
Xmm10: M128A,
Xmm11: M128A,
Xmm12: M128A,
Xmm13: M128A,
Xmm14: M128A,
Xmm15: M128A,
},
},
VectorRegister: [26]M128A,
VectorControl: DWORD64,
DebugControl: DWORD64,
LastBranchToRip: DWORD64,
LastBranchFromRip: DWORD64,
LastExceptionToRip: DWORD64,
LastExceptionFromRip: DWORD64,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{ .bp = ctx.Rbp, .ip = ctx.Rip, .sp = ctx.Rsp };
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Rip = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Rsp = sp;
}
},
.thumb => extern struct {
ContextFlags: ULONG,
R0: ULONG,
R1: ULONG,
R2: ULONG,
R3: ULONG,
R4: ULONG,
R5: ULONG,
R6: ULONG,
R7: ULONG,
R8: ULONG,
R9: ULONG,
R10: ULONG,
R11: ULONG,
R12: ULONG,
Sp: ULONG,
Lr: ULONG,
Pc: ULONG,
Cpsr: ULONG,
Fpcsr: ULONG,
Padding: ULONG,
DUMMYUNIONNAME: extern union {
Q: [16]NEON128,
D: [32]ULONGLONG,
S: [32]ULONG,
},
Bvr: [8]ULONG,
Bcr: [8]ULONG,
Wvr: [1]ULONG,
Wcr: [1]ULONG,
Padding2: [2]ULONG,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{
.bp = ctx.DUMMYUNIONNAME.S[11],
.ip = ctx.Pc,
.sp = ctx.Sp,
};
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Pc = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Sp = sp;
}
},
.aarch64 => extern struct {
ContextFlags: ULONG align(16),
Cpsr: ULONG,
DUMMYUNIONNAME: extern union {
DUMMYSTRUCTNAME: extern struct {
X0: DWORD64,
X1: DWORD64,
X2: DWORD64,
X3: DWORD64,
X4: DWORD64,
X5: DWORD64,
X6: DWORD64,
X7: DWORD64,
X8: DWORD64,
X9: DWORD64,
X10: DWORD64,
X11: DWORD64,
X12: DWORD64,
X13: DWORD64,
X14: DWORD64,
X15: DWORD64,
X16: DWORD64,
X17: DWORD64,
X18: DWORD64,
X19: DWORD64,
X20: DWORD64,
X21: DWORD64,
X22: DWORD64,
X23: DWORD64,
X24: DWORD64,
X25: DWORD64,
X26: DWORD64,
X27: DWORD64,
X28: DWORD64,
Fp: DWORD64,
Lr: DWORD64,
},
X: [31]DWORD64,
},
Sp: DWORD64,
Pc: DWORD64,
V: [32]NEON128,
Fpcr: DWORD,
Fpsr: DWORD,
Bcr: [8]DWORD,
Bvr: [8]DWORD64,
Wcr: [2]DWORD,
Wvr: [2]DWORD64,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{
.bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp,
.ip = ctx.Pc,
.sp = ctx.Sp,
};
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Pc = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Sp = sp;
}
},
else => @compileError("CONTEXT is not defined for this architecture"),
};
pub const RUNTIME_FUNCTION = switch (native_arch) {
.x86_64 => extern struct {
BeginAddress: DWORD,
EndAddress: DWORD,
UnwindData: DWORD,
},
.thumb => extern struct {
BeginAddress: DWORD,
DUMMYUNIONNAME: extern union {
UnwindData: DWORD,
DUMMYSTRUCTNAME: packed struct {
Flag: u2,
FunctionLength: u11,
Ret: u2,
H: u1,
Reg: u3,
R: u1,
L: u1,
C: u1,
StackAdjust: u10,
},
},
},
.aarch64 => extern struct {
BeginAddress: DWORD,
DUMMYUNIONNAME: extern union {
UnwindData: DWORD,
DUMMYSTRUCTNAME: packed struct {
Flag: u2,
FunctionLength: u11,
RegF: u3,
RegI: u4,
H: u1,
CR: u2,
FrameSize: u9,
},
},
},
else => @compileError("RUNTIME_FUNCTION is not defined for this architecture"),
};
pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {
.x86_64 => extern struct {
FloatingContext: [16]?*M128A,
IntegerContext: [16]?*ULONG64,
},
.thumb => extern struct {
R4: ?*DWORD,
R5: ?*DWORD,
R6: ?*DWORD,
R7: ?*DWORD,
R8: ?*DWORD,
R9: ?*DWORD,
R10: ?*DWORD,
R11: ?*DWORD,
Lr: ?*DWORD,
D8: ?*ULONGLONG,
D9: ?*ULONGLONG,
D10: ?*ULONGLONG,
D11: ?*ULONGLONG,
D12: ?*ULONGLONG,
D13: ?*ULONGLONG,
D14: ?*ULONGLONG,
D15: ?*ULONGLONG,
},
.aarch64 => extern struct {
X19: ?*DWORD64,
X20: ?*DWORD64,
X21: ?*DWORD64,
X22: ?*DWORD64,
X23: ?*DWORD64,
X24: ?*DWORD64,
X25: ?*DWORD64,
X26: ?*DWORD64,
X27: ?*DWORD64,
X28: ?*DWORD64,
Fp: ?*DWORD64,
Lr: ?*DWORD64,
D8: ?*DWORD64,
D9: ?*DWORD64,
D10: ?*DWORD64,
D11: ?*DWORD64,
D12: ?*DWORD64,
D13: ?*DWORD64,
D14: ?*DWORD64,
D15: ?*DWORD64,
},
else => @compileError("KNONVOLATILE_CONTEXT_POINTERS is not defined for this architecture"),
};
pub const EXCEPTION_POINTERS = extern struct {
ExceptionRecord: *EXCEPTION_RECORD,
ContextRecord: *CONTEXT,
};
pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(.winapi) c_long;
pub const EXCEPTION_DISPOSITION = i32;
pub const EXCEPTION_ROUTINE = *const fn (
ExceptionRecord: ?*EXCEPTION_RECORD,
EstablisherFrame: PVOID,
ContextRecord: *CONTEXT,
DispatcherContext: PVOID,
) callconv(.winapi) EXCEPTION_DISPOSITION;
pub const UNWIND_HISTORY_TABLE_SIZE = 12;
pub const UNWIND_HISTORY_TABLE_ENTRY = extern struct {
ImageBase: ULONG64,
FunctionEntry: *RUNTIME_FUNCTION,
};
pub const UNWIND_HISTORY_TABLE = extern struct {
Count: ULONG,
LocalHint: BYTE,
GlobalHint: BYTE,
Search: BYTE,
Once: BYTE,
LowAddress: ULONG64,
HighAddress: ULONG64,
Entry: [UNWIND_HISTORY_TABLE_SIZE]UNWIND_HISTORY_TABLE_ENTRY,
};
pub const UNW_FLAG_NHANDLER = 0x0;
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 ACTIVATION_CONTEXT_DATA = opaque {};
pub const ASSEMBLY_STORAGE_MAP = opaque {};
pub const FLS_CALLBACK_INFO = opaque {};
pub const RTL_BITMAP = opaque {};
pub const KAFFINITY = usize;
pub const KPRIORITY = i32;
pub const CLIENT_ID = extern struct {
UniqueProcess: HANDLE,
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,
ClientId: CLIENT_ID,
ActiveRpcHandle: PVOID,
ThreadLocalStoragePointer: PVOID,
ProcessEnvironmentBlock: *PEB,
LastErrorValue: ULONG,
Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
Reserved3: [1952]u8,
TlsSlots: [64]PVOID,
Reserved4: [8]u8,
Reserved5: [26]PVOID,
ReservedForOle: PVOID,
Reserved6: [4]PVOID,
TlsExpansionSlots: PVOID,
};
comptime {
// XXX: Without this check we cannot use `std.Io.Writer` on 16-bit platforms. `std.fmt.bufPrint` will hit the unreachable in `PEB.GdiHandleBuffer` without this guard.
if (builtin.os.tag == .windows) {
// Offsets taken from WinDbg info and Geoff Chappell[1] (RIP)
// [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
assert(@offsetOf(TEB, "NtTib") == 0x00);
if (@sizeOf(usize) == 4) {
assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C);
assert(@offsetOf(TEB, "ClientId") == 0x20);
assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28);
assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C);
assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
assert(@offsetOf(TEB, "LastErrorValue") == 0x34);
assert(@offsetOf(TEB, "TlsSlots") == 0xe10);
} else if (@sizeOf(usize) == 8) {
assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38);
assert(@offsetOf(TEB, "ClientId") == 0x40);
assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50);
assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58);
assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
assert(@offsetOf(TEB, "LastErrorValue") == 0x68);
assert(@offsetOf(TEB, "TlsSlots") == 0x1480);
}
}
}
pub const EXCEPTION_REGISTRATION_RECORD = extern struct {
Next: ?*EXCEPTION_REGISTRATION_RECORD,
Handler: ?*EXCEPTION_DISPOSITION,
};
pub const NT_TIB = extern struct {
ExceptionList: ?*EXCEPTION_REGISTRATION_RECORD,
StackBase: PVOID,
StackLimit: PVOID,
SubSystemTib: PVOID,
DUMMYUNIONNAME: extern union { FiberData: PVOID, Version: DWORD },
ArbitraryUserPointer: PVOID,
Self: ?*@This(),
};
/// Process Environment Block
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269
/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
pub const PEB = extern struct {
// Versions: All
InheritedAddressSpace: BOOLEAN,
// Versions: 3.51+
ReadImageFileExecOptions: BOOLEAN,
BeingDebugged: BOOLEAN,
// Versions: 5.2+ (previously was padding)
BitField: UCHAR,
// Versions: all
Mutant: HANDLE,
ImageBaseAddress: HMODULE,
Ldr: *PEB_LDR_DATA,
ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
SubSystemData: PVOID,
ProcessHeap: ?*HEAP,
// Versions: 5.1+
FastPebLock: *RTL_CRITICAL_SECTION,
// Versions: 5.2+
AtlThunkSListPtr: PVOID,
IFEOKey: PVOID,
// Versions: 6.0+
/// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm
CrossProcessFlags: ULONG,
// Versions: 6.0+
union1: extern union {
KernelCallbackTable: PVOID,
UserSharedInfoPtr: PVOID,
},
// Versions: 5.1+
SystemReserved: ULONG,
// Versions: 5.1, (not 5.2, not 6.0), 6.1+
AtlThunkSListPtr32: ULONG,
// Versions: 6.1+
ApiSetMap: PVOID,
// Versions: all
TlsExpansionCounter: ULONG,
// note: there is padding here on 64 bit
TlsBitmap: *RTL_BITMAP,
TlsBitmapBits: [2]ULONG,
ReadOnlySharedMemoryBase: PVOID,
// Versions: 1703+
SharedData: PVOID,
// Versions: all
ReadOnlyStaticServerData: *PVOID,
AnsiCodePageData: PVOID,
OemCodePageData: PVOID,
UnicodeCaseTableData: PVOID,
// Versions: 3.51+
NumberOfProcessors: ULONG,
NtGlobalFlag: ULONG,
// Versions: all
CriticalSectionTimeout: LARGE_INTEGER,
// End of Original PEB size
// Fields appended in 3.51:
HeapSegmentReserve: ULONG_PTR,
HeapSegmentCommit: ULONG_PTR,
HeapDeCommitTotalFreeThreshold: ULONG_PTR,
HeapDeCommitFreeBlockThreshold: ULONG_PTR,
NumberOfHeaps: ULONG,
MaximumNumberOfHeaps: ULONG,
ProcessHeaps: *PVOID,
// Fields appended in 4.0:
GdiSharedHandleTable: PVOID,
ProcessStarterHelper: PVOID,
GdiDCAttributeList: ULONG,
// note: there is padding here on 64 bit
LoaderLock: *RTL_CRITICAL_SECTION,
OSMajorVersion: ULONG,
OSMinorVersion: ULONG,
OSBuildNumber: USHORT,
OSCSDVersion: USHORT,
OSPlatformId: ULONG,
ImageSubSystem: ULONG,
ImageSubSystemMajorVersion: ULONG,
ImageSubSystemMinorVersion: ULONG,
// note: there is padding here on 64 bit
ActiveProcessAffinityMask: KAFFINITY,
GdiHandleBuffer: [
switch (@sizeOf(usize)) {
4 => 0x22,
8 => 0x3C,
else => unreachable,
}
]ULONG,
// Fields appended in 5.0 (Windows 2000):
PostProcessInitRoutine: PVOID,
TlsExpansionBitmap: *RTL_BITMAP,
TlsExpansionBitmapBits: [32]ULONG,
SessionId: ULONG,
// note: there is padding here on 64 bit
// Versions: 5.1+
AppCompatFlags: ULARGE_INTEGER,
AppCompatFlagsUser: ULARGE_INTEGER,
ShimData: PVOID,
// Versions: 5.0+
AppCompatInfo: PVOID,
CSDVersion: UNICODE_STRING,
// Fields appended in 5.1 (Windows XP):
ActivationContextData: *const ACTIVATION_CONTEXT_DATA,
ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA,
SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
MinimumStackCommit: ULONG_PTR,
// Fields appended in 5.2 (Windows Server 2003):
FlsCallback: *FLS_CALLBACK_INFO,
FlsListHead: LIST_ENTRY,
FlsBitmap: *RTL_BITMAP,
FlsBitmapBits: [4]ULONG,
FlsHighIndex: ULONG,
// Fields appended in 6.0 (Windows Vista):
WerRegistrationData: PVOID,
WerShipAssertPtr: PVOID,
// Fields appended in 6.1 (Windows 7):
pUnused: PVOID, // previously pContextData
pImageHeaderHash: PVOID,
/// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm
TracingFlags: ULONG,
// Fields appended in 6.2 (Windows 8):
CsrServerReadOnlySharedMemoryBase: ULONGLONG,
// Fields appended in 1511:
TppWorkerpListLock: ULONG,
TppWorkerpList: LIST_ENTRY,
WaitOnAddressHashTable: [0x80]PVOID,
// Fields appended in 1709:
TelemetryCoverageHeader: PVOID,
CloudFileFlags: ULONG,
};
/// 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.
///
/// 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
pub const PEB_LDR_DATA = extern struct {
// Versions: 3.51 and higher
/// The size in bytes of the structure
Length: ULONG,
/// TRUE if the structure is prepared.
Initialized: BOOLEAN,
SsHandle: PVOID,
InLoadOrderModuleList: LIST_ENTRY,
InMemoryOrderModuleList: LIST_ENTRY,
InInitializationOrderModuleList: LIST_ENTRY,
// Versions: 5.1 and higher
/// No known use of this field is known in Windows 8 and higher.
EntryInProgress: PVOID,
// Versions: 6.0 from Windows Vista SP1, and higher
ShutdownInProgress: BOOLEAN,
/// Though ShutdownThreadId is declared as a HANDLE,
/// it is indeed the thread ID as suggested by its name.
/// It is picked up from the UniqueThread member of the CLIENT_ID in the
/// TEB of the thread that asks to terminate the process.
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 RTL_USER_PROCESS_PARAMETERS = extern struct {
AllocationSize: ULONG,
Size: ULONG,
Flags: ULONG,
DebugFlags: ULONG,
ConsoleHandle: HANDLE,
ConsoleFlags: ULONG,
hStdInput: HANDLE,
hStdOutput: HANDLE,
hStdError: HANDLE,
CurrentDirectory: CURDIR,
DllPath: UNICODE_STRING,
ImagePathName: UNICODE_STRING,
CommandLine: UNICODE_STRING,
/// Points to a NUL-terminated sequence of NUL-terminated
/// WTF-16 LE encoded `name=value` sequences.
/// Example using string literal syntax:
/// `"NAME=value\x00foo=bar\x00\x00"`
Environment: [*:0]WCHAR,
dwX: ULONG,
dwY: ULONG,
dwXSize: ULONG,
dwYSize: ULONG,
dwXCountChars: ULONG,
dwYCountChars: ULONG,
dwFillAttribute: ULONG,
dwFlags: ULONG,
dwShowWindow: ULONG,
WindowTitle: UNICODE_STRING,
Desktop: UNICODE_STRING,
ShellInfo: UNICODE_STRING,
RuntimeInfo: UNICODE_STRING,
DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
};
pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
Flags: c_ushort,
Length: c_ushort,
TimeStamp: ULONG,
DosPath: UNICODE_STRING,
};
pub const PPS_POST_PROCESS_INIT_ROUTINE = ?*const fn () callconv(.winapi) void;
pub const FILE_DIRECTORY_INFORMATION = extern struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: FILE.ATTRIBUTE,
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
pub const FILE_BOTH_DIR_INFORMATION = extern struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: FILE.ATTRIBUTE,
FileNameLength: ULONG,
EaSize: ULONG,
ShortNameLength: CHAR,
ShortName: [12]WCHAR,
FileName: [1]WCHAR,
};
pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
/// Helper for iterating a byte buffer of FILE_*_INFORMATION structures (from
/// things like NtQueryDirectoryFile calls).
pub fn FileInformationIterator(comptime FileInformationType: type) type {
return struct {
byte_offset: usize = 0,
buf: []u8 align(@alignOf(FileInformationType)),
pub fn next(self: *@This()) ?*FileInformationType {
if (self.byte_offset >= self.buf.len) return null;
const cur: *FileInformationType = @ptrCast(@alignCast(&self.buf[self.byte_offset]));
if (cur.NextEntryOffset == 0) {
self.byte_offset = self.buf.len;
} else {
self.byte_offset += cur.NextEntryOffset;
}
return cur;
}
};
}
pub const IO_APC_ROUTINE = fn (?*anyopaque, *IO_STATUS_BLOCK, ULONG) callconv(.winapi) void;
pub const CURDIR = extern struct {
DosPath: UNICODE_STRING,
Handle: HANDLE,
};
pub const DUPLICATE_SAME_ACCESS = 2;
pub const MODULEINFO = extern struct {
lpBaseOfDll: LPVOID,
SizeOfImage: DWORD,
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 GetProcessMemoryInfoError = error{
AccessDenied,
InvalidHandle,
Unexpected,
};
pub fn GetProcessMemoryInfo(hProcess: HANDLE) GetProcessMemoryInfoError!VM_COUNTERS {
var vmc: VM_COUNTERS = undefined;
const rc = ntdll.NtQueryInformationProcess(hProcess, .VmCounters, &vmc, @sizeOf(VM_COUNTERS), null);
switch (rc) {
.SUCCESS => return vmc,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_HANDLE => return error.InvalidHandle,
.INVALID_PARAMETER => unreachable,
else => return unexpectedStatus(rc),
}
}
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,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
};
pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW;
pub const REPARSE_DATA_BUFFER = extern struct {
ReparseTag: IO_REPARSE_TAG,
ReparseDataLength: USHORT,
Reserved: USHORT,
DataBuffer: [1]UCHAR,
};
pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
Flags: ULONG,
PathBuffer: [1]WCHAR,
};
pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
PathBuffer: [1]WCHAR,
};
pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
pub const MOUNTMGR_MOUNT_POINT = extern struct {
SymbolicLinkNameOffset: ULONG,
SymbolicLinkNameLength: USHORT,
Reserved1: USHORT,
UniqueIdOffset: ULONG,
UniqueIdLength: USHORT,
Reserved2: USHORT,
DeviceNameOffset: ULONG,
DeviceNameLength: USHORT,
Reserved3: USHORT,
};
pub const MOUNTMGR_MOUNT_POINTS = extern struct {
Size: ULONG,
NumberOfMountPoints: ULONG,
MountPoints: [1]MOUNTMGR_MOUNT_POINT,
};
pub const MOUNTMGR_TARGET_NAME = extern struct {
DeviceNameLength: USHORT,
DeviceName: [1]WCHAR,
};
pub const MOUNTMGR_VOLUME_PATHS = extern struct {
MultiSzLength: ULONG,
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,
};
pub const CONDITION_VARIABLE_INIT = CONDITION_VARIABLE{};
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.
FLOATING_POINT_PRECISION_ERRATA = 0,
/// Floating-point operations are emulated using software emulator.
/// This function returns a nonzero value if floating-point operations are emulated; otherwise, it returns zero.
FLOATING_POINT_EMULATED = 1,
/// The atomic compare and exchange operation (cmpxchg) is available.
COMPARE_EXCHANGE_DOUBLE = 2,
/// The MMX instruction set is available.
MMX_INSTRUCTIONS_AVAILABLE = 3,
PPC_MOVEMEM_64BIT_OK = 4,
ALPHA_BYTE_INSTRUCTIONS = 5,
/// The SSE instruction set is available.
XMMI_INSTRUCTIONS_AVAILABLE = 6,
/// The 3D-Now instruction is available.
@"3DNOW_INSTRUCTIONS_AVAILABLE" = 7,
/// The RDTSC instruction is available.
RDTSC_INSTRUCTION_AVAILABLE = 8,
/// The processor is PAE-enabled.
PAE_ENABLED = 9,
/// The SSE2 instruction set is available.
XMMI64_INSTRUCTIONS_AVAILABLE = 10,
SSE_DAZ_MODE_AVAILABLE = 11,
/// Data execution prevention is enabled.
NX_ENABLED = 12,
/// The SSE3 instruction set is available.
SSE3_INSTRUCTIONS_AVAILABLE = 13,
/// The atomic compare and exchange 128-bit operation (cmpxchg16b) is available.
COMPARE_EXCHANGE128 = 14,
/// The atomic compare 64 and exchange 128-bit operation (cmp8xchg16) is available.
COMPARE64_EXCHANGE128 = 15,
/// The processor channels are enabled.
CHANNELS_ENABLED = 16,
/// The processor implements the XSAVI and XRSTOR instructions.
XSAVE_ENABLED = 17,
/// The VFP/Neon: 32 x 64bit register bank is present.
/// This flag has the same meaning as PF_ARM_VFP_EXTENDED_REGISTERS.
ARM_VFP_32_REGISTERS_AVAILABLE = 18,
/// This ARM processor implements the ARM v8 NEON instruction set.
ARM_NEON_INSTRUCTIONS_AVAILABLE = 19,
/// Second Level Address Translation is supported by the hardware.
SECOND_LEVEL_ADDRESS_TRANSLATION = 20,
/// Virtualization is enabled in the firmware and made available by the operating system.
VIRT_FIRMWARE_ENABLED = 21,
/// RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE instructions are available.
RDWRFSGBASE_AVAILABLE = 22,
/// _fastfail() is available.
FASTFAIL_AVAILABLE = 23,
/// The divide instruction_available.
ARM_DIVIDE_INSTRUCTION_AVAILABLE = 24,
/// The 64-bit load/store atomic instructions are available.
ARM_64BIT_LOADSTORE_ATOMIC = 25,
/// The external cache is available.
ARM_EXTERNAL_CACHE_AVAILABLE = 26,
/// The floating-point multiply-accumulate instruction is available.
ARM_FMAC_INSTRUCTIONS_AVAILABLE = 27,
RDRAND_INSTRUCTION_AVAILABLE = 28,
/// This ARM processor implements the ARM v8 instructions set.
ARM_V8_INSTRUCTIONS_AVAILABLE = 29,
/// This ARM processor implements the ARM v8 extra cryptographic instructions (i.e., AES, SHA1 and SHA2).
ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30,
/// This ARM processor implements the ARM v8 extra CRC32 instructions.
ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31,
RDTSCP_INSTRUCTION_AVAILABLE = 32,
RDPID_INSTRUCTION_AVAILABLE = 33,
/// This ARM processor implements the ARM v8.1 atomic instructions (e.g., CAS, SWP).
ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34,
MONITORX_INSTRUCTION_AVAILABLE = 35,
/// The SSSE3 instruction set is available.
SSSE3_INSTRUCTIONS_AVAILABLE = 36,
/// The SSE4_1 instruction set is available.
SSE4_1_INSTRUCTIONS_AVAILABLE = 37,
/// The SSE4_2 instruction set is available.
SSE4_2_INSTRUCTIONS_AVAILABLE = 38,
/// The AVX instruction set is available.
AVX_INSTRUCTIONS_AVAILABLE = 39,
/// The AVX2 instruction set is available.
AVX2_INSTRUCTIONS_AVAILABLE = 40,
/// The AVX512F instruction set is available.
AVX512F_INSTRUCTIONS_AVAILABLE = 41,
ERMS_AVAILABLE = 42,
/// This ARM processor implements the ARM v8.2 Dot Product (DP) instructions.
ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43,
/// This ARM processor implements the ARM v8.3 JavaScript conversion (JSCVT) instructions.
ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44,
/// This Arm processor implements the Arm v8.3 LRCPC instructions (for example, LDAPR). Note that certain Arm v8.2 CPUs may optionally support the LRCPC instructions.
ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE,
};
pub const MAX_WOW64_SHARED_ENTRIES = 16;
pub const PROCESSOR_FEATURE_MAX = 64;
pub const MAXIMUM_XSTATE_FEATURES = 64;
pub const KSYSTEM_TIME = extern struct {
LowPart: ULONG,
High1Time: LONG,
High2Time: LONG,
};
pub const NT_PRODUCT_TYPE = enum(INT) {
NtProductWinNt = 1,
NtProductLanManNt,
NtProductServer,
};
pub const ALTERNATIVE_ARCHITECTURE_TYPE = enum(INT) {
StandardDesign,
NEC98x86,
EndAlternatives,
};
pub const XSTATE_FEATURE = extern struct {
Offset: ULONG,
Size: ULONG,
};
pub const XSTATE_CONFIGURATION = extern struct {
EnabledFeatures: ULONG64,
Size: ULONG,
OptimizedSave: ULONG,
Features: [MAXIMUM_XSTATE_FEATURES]XSTATE_FEATURE,
};
/// Shared Kernel User Data
pub const KUSER_SHARED_DATA = extern struct {
TickCountLowDeprecated: ULONG,
TickCountMultiplier: ULONG,
InterruptTime: KSYSTEM_TIME,
SystemTime: KSYSTEM_TIME,
TimeZoneBias: KSYSTEM_TIME,
ImageNumberLow: USHORT,
ImageNumberHigh: USHORT,
NtSystemRoot: [260]WCHAR,
MaxStackTraceDepth: ULONG,
CryptoExponent: ULONG,
TimeZoneId: ULONG,
LargePageMinimum: ULONG,
AitSamplingValue: ULONG,
AppCompatFlag: ULONG,
RNGSeedVersion: ULONGLONG,
GlobalValidationRunlevel: ULONG,
TimeZoneBiasStamp: LONG,
NtBuildNumber: ULONG,
NtProductType: NT_PRODUCT_TYPE,
ProductTypeIsValid: BOOLEAN,
Reserved0: [1]BOOLEAN,
NativeProcessorArchitecture: USHORT,
NtMajorVersion: ULONG,
NtMinorVersion: ULONG,
ProcessorFeatures: [PROCESSOR_FEATURE_MAX]BOOLEAN,
Reserved1: ULONG,
Reserved3: ULONG,
TimeSlip: ULONG,
AlternativeArchitecture: ALTERNATIVE_ARCHITECTURE_TYPE,
BootId: ULONG,
SystemExpirationDate: LARGE_INTEGER,
SuiteMaskY: ULONG,
KdDebuggerEnabled: BOOLEAN,
DummyUnion1: extern union {
MitigationPolicies: UCHAR,
Alt: packed struct {
NXSupportPolicy: u2,
SEHValidationPolicy: u2,
CurDirDevicesSkippedForDlls: u2,
Reserved: u2,
},
},
CyclesPerYield: USHORT,
ActiveConsoleId: ULONG,
DismountCount: ULONG,
ComPlusPackage: ULONG,
LastSystemRITEventTickCount: ULONG,
NumberOfPhysicalPages: ULONG,
SafeBootMode: BOOLEAN,
DummyUnion2: extern union {
VirtualizationFlags: UCHAR,
Alt: packed struct {
ArchStartedInEl2: u1,
QcSlIsSupported: u1,
SpareBits: u6,
},
},
Reserved12: [2]UCHAR,
DummyUnion3: extern union {
SharedDataFlags: ULONG,
Alt: packed struct {
DbgErrorPortPresent: u1,
DbgElevationEnabled: u1,
DbgVirtEnabled: u1,
DbgInstallerDetectEnabled: u1,
DbgLkgEnabled: u1,
DbgDynProcessorEnabled: u1,
DbgConsoleBrokerEnabled: u1,
DbgSecureBootEnabled: u1,
DbgMultiSessionSku: u1,
DbgMultiUsersInSessionSku: u1,
DbgStateSeparationEnabled: u1,
SpareBits: u21,
},
},
DataFlagsPad: [1]ULONG,
TestRetInstruction: ULONGLONG,
QpcFrequency: LONGLONG,
SystemCall: ULONG,
Reserved2: ULONG,
SystemCallPad: [2]ULONGLONG,
DummyUnion4: extern union {
TickCount: KSYSTEM_TIME,
TickCountQuad: ULONG64,
Alt: extern struct {
ReservedTickCountOverlay: [3]ULONG,
TickCountPad: [1]ULONG,
},
},
Cookie: ULONG,
CookiePad: [1]ULONG,
ConsoleSessionForegroundProcessId: LONGLONG,
TimeUpdateLock: ULONGLONG,
BaselineSystemTimeQpc: ULONGLONG,
BaselineInterruptTimeQpc: ULONGLONG,
QpcSystemTimeIncrement: ULONGLONG,
QpcInterruptTimeIncrement: ULONGLONG,
QpcSystemTimeIncrementShift: UCHAR,
QpcInterruptTimeIncrementShift: UCHAR,
UnparkedProcessorCount: USHORT,
EnclaveFeatureMask: [4]ULONG,
TelemetryCoverageRound: ULONG,
UserModeGlobalLogger: [16]USHORT,
ImageFileExecutionOptions: ULONG,
LangGenerationCount: ULONG,
Reserved4: ULONGLONG,
InterruptTimeBias: ULONGLONG,
QpcBias: ULONGLONG,
ActiveProcessorCount: ULONG,
ActiveGroupCount: UCHAR,
Reserved9: UCHAR,
DummyUnion5: extern union {
QpcData: USHORT,
Alt: extern struct {
QpcBypassEnabled: UCHAR,
QpcShift: UCHAR,
},
},
TimeZoneBiasEffectiveStart: LARGE_INTEGER,
TimeZoneBiasEffectiveEnd: LARGE_INTEGER,
XState: XSTATE_CONFIGURATION,
FeatureConfigurationChangeStamp: KSYSTEM_TIME,
Spare: ULONG,
UserPointerAuthMask: ULONG64,
};
/// 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 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,
};
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 = GetProcessMemoryInfoError || 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) {
const utf16_len = std.unicode.calcUtf16LeLenImpl(wtf8, .can_encode_surrogate_half) catch
return error.BadPathName;
if (utf16_len > wtf16le.len)
return error.NameTooLong;
}
return std.unicode.wtf8ToWtf16Le(wtf16le, wtf8) catch |err| switch (err) {
error.InvalidWtf8 => return error.BadPathName,
};
}