Skip to content

Commit 18c0bec

Browse files
committed
os.isCygwinPty: Fix a bug, replace kernel32 call, and optimize
- Fixes the first few code units of the name being omitted (it was using `@sizeOf(FILE_NAME_INFO)` as the start of the name bytes, but that includes the length of the dummy [1]u16 field and padding; instead the start should be the offset of the dummy [1]u16 field) - Replaces kernel32.GetFileInformationByHandleEx call with ntdll.NtQueryInformationFile + Contributes towards #1840 - Checks that the handle is a named pipe first before querying and checking the name, which is a much faster call than NtQueryInformationFile (this was about a 10x speedup in my probably-not-so-good/take-it-with-a-grain-of-salt benchmarking)
1 parent 3da2ff8 commit 18c0bec

File tree

3 files changed

+69
-11
lines changed

3 files changed

+69
-11
lines changed

lib/std/os.zig

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3192,22 +3192,48 @@ pub fn isatty(handle: fd_t) bool {
31923192
pub fn isCygwinPty(handle: fd_t) bool {
31933193
if (builtin.os.tag != .windows) return false;
31943194

3195-
const size = @sizeOf(windows.FILE_NAME_INFO);
3196-
var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
3195+
// If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
3196+
// msys-[...]-ptyN-[...]
3197+
// cygwin-[...]-ptyN-[...]
3198+
//
3199+
// Example: msys-1888ae32e00d56aa-pty0-to-master
3200+
3201+
// First, just check that the handle is a named pipe.
3202+
// This allows us to avoid the more costly NtQueryInformationFile call
3203+
// for handles that aren't named pipes.
3204+
{
3205+
var io_status: windows.IO_STATUS_BLOCK = undefined;
3206+
var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
3207+
const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
3208+
switch (rc) {
3209+
.SUCCESS => {},
3210+
else => return false,
3211+
}
3212+
if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
3213+
}
31973214

3198-
if (windows.kernel32.GetFileInformationByHandleEx(
3199-
handle,
3200-
windows.FileNameInfo,
3201-
@ptrCast(*anyopaque, &name_info_bytes),
3202-
name_info_bytes.len,
3203-
) == 0) {
3204-
return false;
3215+
const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
3216+
// `NAME_MAX` UTF-16 code units (2 bytes each)
3217+
// Note: This buffer may not be long enough to handle *all* possible paths (PATH_MAX_WIDE would be necessary for that),
3218+
// but because we only care about certain paths and we know they must be within a reasonable length,
3219+
// we can use this smaller buffer and just return false on any error from NtQueryInformationFile.
3220+
const num_name_bytes = windows.MAX_PATH * 2;
3221+
var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
3222+
3223+
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
3224+
const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @intCast(u32, name_info_bytes.len), .FileNameInformation);
3225+
switch (rc) {
3226+
.SUCCESS => {},
3227+
.INVALID_PARAMETER => unreachable,
3228+
else => return false,
32053229
}
32063230

32073231
const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
3208-
const name_bytes = name_info_bytes[size .. size + @as(usize, name_info.FileNameLength)];
3232+
const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + @as(usize, name_info.FileNameLength)];
32093233
const name_wide = mem.bytesAsSlice(u16, name_bytes);
3210-
return mem.indexOf(u16, name_wide, &[_]u16{ 'm', 's', 'y', 's', '-' }) != null or
3234+
// Note: The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
3235+
return (mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
3236+
mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
32113237
mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
32123238
}
32133239

lib/std/os/windows.zig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,6 +2472,29 @@ pub const FILE_INFORMATION_CLASS = enum(c_int) {
24722472
FileMaximumInformation,
24732473
};
24742474

2475+
pub const FILE_FS_DEVICE_INFORMATION = extern struct {
2476+
DeviceType: DEVICE_TYPE,
2477+
Characteristics: ULONG,
2478+
};
2479+
2480+
pub const FS_INFORMATION_CLASS = enum(c_int) {
2481+
FileFsVolumeInformation = 1,
2482+
FileFsLabelInformation,
2483+
FileFsSizeInformation,
2484+
FileFsDeviceInformation,
2485+
FileFsAttributeInformation,
2486+
FileFsControlInformation,
2487+
FileFsFullSizeInformation,
2488+
FileFsObjectIdInformation,
2489+
FileFsDriverPathInformation,
2490+
FileFsVolumeFlagsInformation,
2491+
FileFsSectorSizeInformation,
2492+
FileFsDataCopyInformation,
2493+
FileFsMetadataSizeInformation,
2494+
FileFsFullSizeInformationEx,
2495+
FileFsMaximumInformation,
2496+
};
2497+
24752498
pub const OVERLAPPED = extern struct {
24762499
Internal: ULONG_PTR,
24772500
InternalHigh: ULONG_PTR,

lib/std/os/windows/ntdll.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const IO_STATUS_BLOCK = windows.IO_STATUS_BLOCK;
1818
const LARGE_INTEGER = windows.LARGE_INTEGER;
1919
const OBJECT_INFORMATION_CLASS = windows.OBJECT_INFORMATION_CLASS;
2020
const FILE_INFORMATION_CLASS = windows.FILE_INFORMATION_CLASS;
21+
const FS_INFORMATION_CLASS = windows.FS_INFORMATION_CLASS;
2122
const UNICODE_STRING = windows.UNICODE_STRING;
2223
const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
2324
const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION;
@@ -232,6 +233,14 @@ pub extern "ntdll" fn NtQueryObject(
232233
ReturnLength: ?*ULONG,
233234
) callconv(WINAPI) NTSTATUS;
234235

236+
pub extern "ntdll" fn NtQueryVolumeInformationFile(
237+
FileHandle: HANDLE,
238+
IoStatusBlock: *IO_STATUS_BLOCK,
239+
FsInformation: *anyopaque,
240+
Length: ULONG,
241+
FsInformationClass: FS_INFORMATION_CLASS,
242+
) callconv(WINAPI) NTSTATUS;
243+
235244
pub extern "ntdll" fn RtlWakeAddressAll(
236245
Address: ?*const anyopaque,
237246
) callconv(WINAPI) void;

0 commit comments

Comments
 (0)