| ... | ... | @@ -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 | 163 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { |
| 159 | | if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) { |
| 160 | | switch (kernel32.GetLastError()) { |
| 161 | | else => |err| return unexpectedError(err), |
| 164 | // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to: |
| 165 | // |
| 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 | 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 | 1167 | return @as(u64, @bitCast(result)); |
| 1051 | 1168 | } |
| 1052 | 1169 | |
| 1053 | | pub fn QueryObjectName( |
| 1054 | | handle: HANDLE, |
| 1055 | | out_buffer: []u16, |
| 1056 | | ) ![]u16 { |
| 1170 | pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) ![]u16 { |
| 1057 | 1171 | const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; |
| 1058 | 1172 | |
| 1059 | 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 | 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 |
| 1063 | | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null); |
| 1064 | | switch (rc) { |
| 1065 | | .SUCCESS => { |
| 1176 | // last argument would return the length required for full_buffer, not exposed here |
| 1177 | return switch (ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) { |
| 1178 | .SUCCESS => blk: { |
| 1066 | 1179 | // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) |
| 1067 | 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 | 1182 | // resulting string length is specified in bytes |
| 1070 | 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, |
| 1074 | | .INVALID_HANDLE => return error.InvalidHandle, |
| 1186 | .ACCESS_DENIED => error.AccessDenied, |
| 1187 | .INVALID_HANDLE => error.InvalidHandle, |
| 1075 | 1188 | // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH), |
| 1076 | 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, |
| 1078 | | else => |e| return unexpectedStatus(e), |
| 1079 | | } |
| 1190 | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong, |
| 1191 | else => |e| unexpectedStatus(e), |
| 1192 | }; |
| 1080 | 1193 | } |
| 1081 | | test "QueryObjectName" { |
| 1194 | |
| 1195 | test QueryObjectName { |
| 1082 | 1196 | if (builtin.os.tag != .windows) |
| 1083 | 1197 | return; |
| 1084 | 1198 | |
| ... | ... | @@ -3186,6 +3300,25 @@ pub const FILE_ATTRIBUTE_SYSTEM = 0x4; |
| 3186 | 3300 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; |
| 3187 | 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 | 3322 | // flags for CreateEvent |
| 3190 | 3323 | pub const CREATE_EVENT_INITIAL_SET = 0x00000002; |
| 3191 | 3324 | pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; |
| ... | ... | @@ -4151,7 +4284,7 @@ pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; |
| 4151 | 4284 | pub const UNICODE_STRING = extern struct { |
| 4152 | 4285 | Length: c_ushort, |
| 4153 | 4286 | MaximumLength: c_ushort, |
| 4154 | | Buffer: [*]WCHAR, |
| 4287 | Buffer: ?[*]WCHAR, |
| 4155 | 4288 | }; |
| 4156 | 4289 | |
| 4157 | 4290 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; |