| ... | @@ -153,14 +153,131 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -153,14 +153,131 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 153 | } | 153 | } |
| 154 | } | 154 | } |
| 155 | | 155 | |
| 156 | pub const CreatePipeError = error{Unexpected}; | 156 | pub const CreatePipeError = error{ Unexpected, SystemResources }; |
| 157 | | 157 | |
| | 158 | var npfs: ?HANDLE = null; |
| | 159 | |
| | 160 | /// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls. |
| | 161 | /// It implements similar behavior to `CreatePipe` and is meant to serve |
| | 162 | /// as a direct substitute for that call. |
| 158 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { | 163 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { |
| 159 | if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) { | 164 | // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to: |
| 160 | switch (kernel32.GetLastError()) { | 165 | // |
| 161 | else => |err| return unexpectedError(err), | 166 | // \??\pipe\Win32Pipes.{pid}.{count} |
| | 167 | // |
| | 168 | // where `pid` is the process id and count is a incrementing counter. |
| | 169 | // The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System |
| | 170 | // and use that as the root directory for `NtCreateNamedPipeFile`. |
| | 171 | // This object is visible under the NPFS but has no filename attached to it. |
| | 172 | // |
| | 173 | // This implementation replicates how `CreatePipe` works in modern Windows versions. |
| | 174 | const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst); |
| | 175 | const dev_handle = opt_dev_handle orelse blk: { |
| | 176 | const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\"); |
| | 177 | const len: u16 = @truncate(str.len * @sizeOf(u16)); |
| | 178 | const name = UNICODE_STRING{ |
| | 179 | .Length = len, |
| | 180 | .MaximumLength = len, |
| | 181 | .Buffer = @constCast(@ptrCast(str)), |
| | 182 | }; |
| | 183 | const attrs = OBJECT_ATTRIBUTES{ |
| | 184 | .ObjectName = @constCast(&name), |
| | 185 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| | 186 | .RootDirectory = null, |
| | 187 | .Attributes = 0, |
| | 188 | .SecurityDescriptor = null, |
| | 189 | .SecurityQualityOfService = null, |
| | 190 | }; |
| | 191 | |
| | 192 | var iosb: IO_STATUS_BLOCK = undefined; |
| | 193 | var handle: HANDLE = undefined; |
| | 194 | switch (ntdll.NtCreateFile( |
| | 195 | &handle, |
| | 196 | GENERIC_READ | SYNCHRONIZE, |
| | 197 | @constCast(&attrs), |
| | 198 | &iosb, |
| | 199 | null, |
| | 200 | 0, |
| | 201 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| | 202 | FILE_OPEN, |
| | 203 | FILE_SYNCHRONOUS_IO_NONALERT, |
| | 204 | null, |
| | 205 | 0, |
| | 206 | )) { |
| | 207 | .SUCCESS => {}, |
| | 208 | // Judging from the ReactOS sources this is technically possible. |
| | 209 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| | 210 | .INVALID_PARAMETER => unreachable, |
| | 211 | else => |e| return unexpectedStatus(e), |
| 162 | } | 212 | } |
| | 213 | if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| { |
| | 214 | CloseHandle(handle); |
| | 215 | break :blk xchg.?; |
| | 216 | } else break :blk handle; |
| | 217 | }; |
| | 218 | |
| | 219 | const name = UNICODE_STRING{ .Buffer = null, .Length = 0, .MaximumLength = 0 }; |
| | 220 | var attrs = OBJECT_ATTRIBUTES{ |
| | 221 | .ObjectName = @constCast(&name), |
| | 222 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| | 223 | .RootDirectory = dev_handle, |
| | 224 | .Attributes = OBJ_CASE_INSENSITIVE, |
| | 225 | .SecurityDescriptor = sattr.lpSecurityDescriptor, |
| | 226 | .SecurityQualityOfService = null, |
| | 227 | }; |
| | 228 | if (sattr.bInheritHandle != 0) attrs.Attributes |= OBJ_INHERIT; |
| | 229 | |
| | 230 | // 120 second relative timeout in 100ns units. |
| | 231 | const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100; |
| | 232 | var iosb: IO_STATUS_BLOCK = undefined; |
| | 233 | var read: HANDLE = undefined; |
| | 234 | switch (ntdll.NtCreateNamedPipeFile( |
| | 235 | &read, |
| | 236 | GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, |
| | 237 | &attrs, |
| | 238 | &iosb, |
| | 239 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| | 240 | FILE_CREATE, |
| | 241 | FILE_SYNCHRONOUS_IO_NONALERT, |
| | 242 | FILE_PIPE_BYTE_STREAM_TYPE, |
| | 243 | FILE_PIPE_BYTE_STREAM_MODE, |
| | 244 | FILE_PIPE_QUEUE_OPERATION, |
| | 245 | 1, |
| | 246 | 4096, |
| | 247 | 4096, |
| | 248 | @constCast(&default_timeout), |
| | 249 | )) { |
| | 250 | .SUCCESS => {}, |
| | 251 | .INVALID_PARAMETER => unreachable, |
| | 252 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| | 253 | else => |e| return unexpectedStatus(e), |
| 163 | } | 254 | } |
| | 255 | errdefer CloseHandle(read); |
| | 256 | |
| | 257 | attrs.RootDirectory = read; |
| | 258 | |
| | 259 | var write: HANDLE = undefined; |
| | 260 | switch (ntdll.NtCreateFile( |
| | 261 | &write, |
| | 262 | FILE_GENERIC_WRITE, |
| | 263 | &attrs, |
| | 264 | &iosb, |
| | 265 | null, |
| | 266 | 0, |
| | 267 | FILE_SHARE_READ, |
| | 268 | FILE_OPEN, |
| | 269 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, |
| | 270 | null, |
| | 271 | 0, |
| | 272 | )) { |
| | 273 | .SUCCESS => {}, |
| | 274 | .INVALID_PARAMETER => unreachable, |
| | 275 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| | 276 | else => |e| return unexpectedStatus(e), |
| | 277 | } |
| | 278 | |
| | 279 | rd.* = read; |
| | 280 | wr.* = write; |
| 164 | } | 281 | } |
| 165 | | 282 | |
| 166 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { | 283 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { |
| ... | @@ -1050,35 +1167,32 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { | ... | @@ -1050,35 +1167,32 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { |
| 1050 | return @as(u64, @bitCast(result)); | 1167 | return @as(u64, @bitCast(result)); |
| 1051 | } | 1168 | } |
| 1052 | | 1169 | |
| 1053 | pub fn QueryObjectName( | 1170 | pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) ![]u16 { |
| 1054 | handle: HANDLE, | | |
| 1055 | out_buffer: []u16, | | |
| 1056 | ) ![]u16 { | | |
| 1057 | const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; | 1171 | const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; |
| 1058 | | 1172 | |
| 1059 | const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned)); | 1173 | const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned)); |
| 1060 | //buffer size is specified in bytes | 1174 | // buffer size is specified in bytes |
| 1061 | const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG); | 1175 | const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG); |
| 1062 | //last argument would return the length required for full_buffer, not exposed here | 1176 | // last argument would return the length required for full_buffer, not exposed here |
| 1063 | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null); | 1177 | return switch (ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) { |
| 1064 | switch (rc) { | 1178 | .SUCCESS => blk: { |
| 1065 | .SUCCESS => { | | |
| 1066 | // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) | 1179 | // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) |
| 1067 | // if the object was "unnamed", not sure if this can happen for file handles | 1180 | // if the object was "unnamed", not sure if this can happen for file handles |
| 1068 | if (info.Name.MaximumLength == 0) return error.Unexpected; | 1181 | if (info.Name.MaximumLength == 0) break :blk error.Unexpected; |
| 1069 | // resulting string length is specified in bytes | 1182 | // resulting string length is specified in bytes |
| 1070 | const path_length_unterminated = @divExact(info.Name.Length, 2); | 1183 | const path_length_unterminated = @divExact(info.Name.Length, 2); |
| 1071 | return info.Name.Buffer[0..path_length_unterminated]; | 1184 | break :blk info.Name.Buffer.?[0..path_length_unterminated]; |
| 1072 | }, | 1185 | }, |
| 1073 | .ACCESS_DENIED => return error.AccessDenied, | 1186 | .ACCESS_DENIED => error.AccessDenied, |
| 1074 | .INVALID_HANDLE => return error.InvalidHandle, | 1187 | .INVALID_HANDLE => error.InvalidHandle, |
| 1075 | // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH), | 1188 | // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH), |
| 1076 | // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL) | 1189 | // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL) |
| 1077 | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong, | 1190 | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong, |
| 1078 | else => |e| return unexpectedStatus(e), | 1191 | else => |e| unexpectedStatus(e), |
| 1079 | } | 1192 | }; |
| 1080 | } | 1193 | } |
| 1081 | test "QueryObjectName" { | 1194 | |
| | 1195 | test QueryObjectName { |
| 1082 | if (builtin.os.tag != .windows) | 1196 | if (builtin.os.tag != .windows) |
| 1083 | return; | 1197 | return; |
| 1084 | | 1198 | |
| ... | @@ -3186,6 +3300,25 @@ pub const FILE_ATTRIBUTE_SYSTEM = 0x4; | ... | @@ -3186,6 +3300,25 @@ pub const FILE_ATTRIBUTE_SYSTEM = 0x4; |
| 3186 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; | 3300 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; |
| 3187 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; | 3301 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; |
| 3188 | | 3302 | |
| | 3303 | pub const FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff; |
| | 3304 | pub const FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE; |
| | 3305 | pub const FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE; |
| | 3306 | pub const FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE; |
| | 3307 | |
| | 3308 | // Flags for NtCreateNamedPipeFile |
| | 3309 | // NamedPipeType |
| | 3310 | pub const FILE_PIPE_BYTE_STREAM_TYPE = 0x0; |
| | 3311 | pub const FILE_PIPE_MESSAGE_TYPE = 0x1; |
| | 3312 | pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS = 0x0; |
| | 3313 | pub const FILE_PIPE_REJECT_REMOTE_CLIENTS = 0x2; |
| | 3314 | pub const FILE_PIPE_TYPE_VALID_MASK = 0x3; |
| | 3315 | // CompletionMode |
| | 3316 | pub const FILE_PIPE_QUEUE_OPERATION = 0x0; |
| | 3317 | pub const FILE_PIPE_COMPLETE_OPERATION = 0x1; |
| | 3318 | // ReadMode |
| | 3319 | pub const FILE_PIPE_BYTE_STREAM_MODE = 0x0; |
| | 3320 | pub const FILE_PIPE_MESSAGE_MODE = 0x1; |
| | 3321 | |
| 3189 | // flags for CreateEvent | 3322 | // flags for CreateEvent |
| 3190 | pub const CREATE_EVENT_INITIAL_SET = 0x00000002; | 3323 | pub const CREATE_EVENT_INITIAL_SET = 0x00000002; |
| 3191 | pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; | 3324 | pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; |
| ... | @@ -4151,7 +4284,7 @@ pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; | ... | @@ -4151,7 +4284,7 @@ pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; |
| 4151 | pub const UNICODE_STRING = extern struct { | 4284 | pub const UNICODE_STRING = extern struct { |
| 4152 | Length: c_ushort, | 4285 | Length: c_ushort, |
| 4153 | MaximumLength: c_ushort, | 4286 | MaximumLength: c_ushort, |
| 4154 | Buffer: [*]WCHAR, | 4287 | Buffer: ?[*]WCHAR, |
| 4155 | }; | 4288 | }; |
| 4156 | | 4289 | |
| 4157 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; | 4290 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; |