@@ -29,7 +29,8 @@ pub const gdi32 = @import("windows/gdi32.zig");
29
29
pub usingnamespace @import ("windows/bits.zig" );
30
30
31
31
//version detection
32
- usingnamespace std .zig .system .windows ;
32
+ const version = std .zig .system .windows ;
33
+ const WindowsVersion = version .WindowsVersion ;
33
34
34
35
pub const self_process_handle = @intToPtr (HANDLE , maxInt (usize ));
35
36
@@ -962,42 +963,44 @@ pub fn QueryObjectName(
962
963
var full_buffer : [@sizeOf (OBJECT_NAME_INFORMATION ) + PATH_MAX_WIDE * 2 ]u8 align (@alignOf (OBJECT_NAME_INFORMATION )) = undefined ;
963
964
var info = @ptrCast (* OBJECT_NAME_INFORMATION , & full_buffer );
964
965
//buffer size is specified in bytes
965
- const full_buffer_length = @intCast (ULONG , @sizeOf (OBJECT_NAME_INFORMATION ) + std .math .min (PATH_MAX_WIDE , (out_buffer .len + 1 ) * 2 ));
966
966
//last argument would return the length required for full_buffer, not exposed here
967
- const rc = ntdll .NtQueryObject (handle , .ObjectNameInformation , full_buffer [0.. ], full_buffer_length , null );
968
- return switch (rc ) {
969
- .SUCCESS = > if (@ptrCast (? [* ]WCHAR , info .Name .Buffer )) | buffer | blk : {
967
+ const rc = ntdll .NtQueryObject (handle , .ObjectNameInformation , & full_buffer , full_buffer .len , null );
968
+ switch (rc ) {
969
+ .SUCCESS = > {
970
+ // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
971
+ // if the object was "unnamed", not sure if this can happen for file handles
972
+ if (info .Name .MaximumLength == 0 ) return error .Unexpected ;
970
973
//resulting string length is specified in bytes
971
974
const path_length_unterminated = @divExact (info .Name .Length , 2 );
972
975
if (out_buffer .len < path_length_unterminated ) {
973
976
return error .NameTooLong ;
974
977
}
975
- std . mem .copy (WCHAR , out_buffer [0.. path_length_unterminated ], buffer [0.. path_length_unterminated : 0 ]);
976
- break : blk out_buffer [0.. path_length_unterminated ];
977
- } else error . Unexpected ,
978
- .ACCESS_DENIED = > error .AccessDenied ,
979
- .INVALID_HANDLE = > error .InvalidHandle ,
980
- .BUFFER_OVERFLOW , .BUFFER_TOO_SMALL = > error .NameTooLong ,
978
+ mem .copy (WCHAR , out_buffer [0.. path_length_unterminated ], info . Name . Buffer [0.. path_length_unterminated ]);
979
+ return out_buffer [0.. path_length_unterminated ];
980
+ },
981
+ .ACCESS_DENIED = > return error .AccessDenied ,
982
+ .INVALID_HANDLE = > return error .InvalidHandle ,
983
+ .BUFFER_OVERFLOW , .BUFFER_TOO_SMALL = > return error .NameTooLong ,
981
984
//name_buffer.len >= @sizeOf(OBJECT_NAME_INFORMATION) holds statically
982
985
.INFO_LENGTH_MISMATCH = > unreachable ,
983
- else = > | e | unexpectedStatus (e ),
984
- };
986
+ else = > | e | return unexpectedStatus (e ),
987
+ }
985
988
}
986
989
test "QueryObjectName" {
987
990
if (comptime builtin .os .tag != .windows )
988
991
return ;
989
992
990
993
//any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
991
- const file = try std .fs . openSelfExe (.{});
992
- defer file . close ();
993
- //make this large enough for the test runner exe path
994
- var out_buffer align ( 16 ) = std . mem . zeroes ([ 1 << 10 ]u16 ) ;
994
+ var tmp = std .testing . tmpDir (.{});
995
+ defer tmp . cleanup ();
996
+ const handle = tmp . dir . fd ;
997
+ var out_buffer : [ PATH_MAX_WIDE ]u16 = undefined ;
995
998
996
- var result_path = try QueryObjectName (file . handle , out_buffer [0 .. ] );
999
+ var result_path = try QueryObjectName (handle , & out_buffer );
997
1000
//insufficient size
998
- std .testing .expectError (error .NameTooLong , QueryObjectName (file . handle , out_buffer [0 .. result_path .len - 1 ]));
1001
+ std .testing .expectError (error .NameTooLong , QueryObjectName (handle , out_buffer [0 .. result_path .len - 1 ]));
999
1002
//exactly-sufficient size
1000
- _ = try QueryObjectName (file . handle , out_buffer [0.. result_path .len ]);
1003
+ _ = try QueryObjectName (handle , out_buffer [0.. result_path .len ]);
1001
1004
}
1002
1005
1003
1006
pub const GetFinalPathNameByHandleError = error {
@@ -1029,56 +1032,57 @@ pub fn GetFinalPathNameByHandle(
1029
1032
fmt : GetFinalPathNameByHandleFormat ,
1030
1033
out_buffer : []u16 ,
1031
1034
) GetFinalPathNameByHandleError ! []u16 {
1032
- var path_buffer : [std . math .max (@sizeOf (FILE_NAME_INFORMATION ), @sizeOf (OBJECT_NAME_INFORMATION )) + PATH_MAX_WIDE * 2 ]u8 align (@alignOf (FILE_NAME_INFORMATION )) = undefined ;
1035
+ var path_buffer : [math .max (@sizeOf (FILE_NAME_INFORMATION ), @sizeOf (OBJECT_NAME_INFORMATION )) + PATH_MAX_WIDE * 2 ]u8 align (@alignOf (FILE_NAME_INFORMATION )) = undefined ;
1033
1036
var volume_buffer : [@sizeOf (FILE_NAME_INFORMATION ) + MAX_PATH ]u8 align (@alignOf (FILE_NAME_INFORMATION )) = undefined ; // MAX_PATH bytes should be enough since it's Windows-defined name
1034
1037
1035
1038
var file_name_u16 : []const u16 = undefined ;
1036
1039
var volume_name_u16 : []const u16 = undefined ;
1037
- if ((comptime (targetVersionIsAtLeast (WindowsVersion .win10_rs4 ) != true )) //need explicit comptime, because error returns affect return type
1038
- and ! runtimeVersionIsAtLeast ( WindowsVersion . win10_rs4 ))
1039
- {
1040
- const final_path = QueryObjectName ( hFile , std . mem . bytesAsSlice ( u16 , path_buffer [0 .. ])) catch | err | return switch ( err ) {
1041
- error .InvalidHandle = > error .FileNotFound , //close enough?
1040
+ if ((comptime (std . builtin . os . version_range . windows . isAtLeast (WindowsVersion .win10_rs4 ) != true )) and ! version . detectRuntimeVersion (). isAtLeast ( WindowsVersion . win10_rs4 )) {
1041
+ const final_path = QueryObjectName ( hFile , mem . bytesAsSlice ( u16 , & path_buffer )) catch | err | return switch ( err ) {
1042
+ // we assume InvalidHandle is close enough to FileNotFound in semantics
1043
+ // to not further complicate the error set
1044
+ error .InvalidHandle = > error .FileNotFound ,
1042
1045
else = > | e | e ,
1043
1046
};
1044
1047
1045
1048
if (fmt .volume_name == .Nt ) {
1046
1049
if (out_buffer .len < final_path .len ) {
1047
1050
return error .NameTooLong ;
1048
1051
}
1049
- std . mem .copy (u16 , out_buffer [0 .. ] , final_path [0 .. ] );
1050
- return final_path ; //we can directly return the slice we received
1052
+ mem .copy (u16 , out_buffer , final_path );
1053
+ return out_buffer [0 .. final_path . len ];
1051
1054
}
1052
1055
1053
1056
//otherwise we need to parse the string for volume path for the .Dos logic below to work
1054
1057
const expected_prefix = std .unicode .utf8ToUtf16LeStringLiteral ("\\ Device\\ " );
1055
- if (! std .mem .eql (u16 , expected_prefix , final_path [0.. expected_prefix .len ])) {
1056
- //TODO find out if this can occur, and if we need to handle it differently
1057
- //(i.e. how to determine the end of a volume name)
1058
- return error .BadPathName ;
1059
- }
1060
- const index = std .mem .indexOfPos (u16 , final_path , expected_prefix .len , &[_ ]u16 {'\\ ' }) orelse unreachable ;
1058
+
1059
+ // TODO find out if a path can start with something besides `\Device\<volume name>`,
1060
+ // and if we need to handle it differently
1061
+ // (i.e. how to determine the start and end of the volume name in that case)
1062
+ if (! mem .eql (u16 , expected_prefix , final_path [0.. expected_prefix .len ])) return error .Unexpected ;
1063
+
1064
+ const index = mem .indexOfPos (u16 , final_path , expected_prefix .len , &[_ ]u16 {'\\ ' }) orelse unreachable ;
1061
1065
volume_name_u16 = final_path [0.. index ];
1062
1066
file_name_u16 = final_path [index .. ];
1063
1067
1064
1068
//fallthrough for fmt.volume_name != .Nt
1065
1069
} else {
1066
1070
// Get normalized path; doesn't include volume name though.
1067
- try QueryInformationFile (hFile , .FileNormalizedNameInformation , path_buffer [0 .. ] );
1068
- const file_name = @ptrCast (* const FILE_NAME_INFORMATION , & path_buffer [ 0 ] );
1069
- file_name_u16 = @ptrCast ([* ]const u16 , & file_name .FileName [ 0 ] )[0.. @divExact (file_name .FileNameLength , 2 )];
1071
+ try QueryInformationFile (hFile , .FileNormalizedNameInformation , & path_buffer );
1072
+ const file_name = @ptrCast (* const FILE_NAME_INFORMATION , & path_buffer );
1073
+ file_name_u16 = @ptrCast ([* ]const u16 , & file_name .FileName )[0.. @divExact (file_name .FileNameLength , 2 )];
1070
1074
1071
1075
// Get NT volume name.
1072
- try QueryInformationFile (hFile , .FileVolumeNameInformation , volume_buffer [0 .. ] );
1073
- const volume_name_info = @ptrCast (* const FILE_NAME_INFORMATION , & volume_buffer [ 0 ] );
1074
- volume_name_u16 = @ptrCast ([* ]const u16 , & volume_name_info .FileName [ 0 ] )[0.. @divExact (volume_name_info .FileNameLength , 2 )];
1076
+ try QueryInformationFile (hFile , .FileVolumeNameInformation , & volume_buffer );
1077
+ const volume_name_info = @ptrCast (* const FILE_NAME_INFORMATION , & volume_buffer );
1078
+ volume_name_u16 = @ptrCast ([* ]const u16 , & volume_name_info .FileName )[0.. @divExact (volume_name_info .FileNameLength , 2 )];
1075
1079
1076
1080
if (fmt .volume_name == .Nt ) {
1077
1081
// Nothing to do, we simply copy the bytes to the user-provided buffer.
1078
1082
if (out_buffer .len < volume_name_u16 .len + file_name_u16 .len ) return error .NameTooLong ;
1079
1083
1080
- std . mem .copy (u16 , out_buffer [0 .. ] , volume_name_u16 );
1081
- std . mem .copy (u16 , out_buffer [volume_name_u16 .len .. ], file_name_u16 );
1084
+ mem .copy (u16 , out_buffer , volume_name_u16 );
1085
+ mem .copy (u16 , out_buffer [volume_name_u16 .len .. ], file_name_u16 );
1082
1086
1083
1087
return out_buffer [0 .. volume_name_u16 .len + file_name_u16 .len ];
1084
1088
}
@@ -1123,7 +1127,7 @@ pub fn GetFinalPathNameByHandle(
1123
1127
input_struct .DeviceNameLength = @intCast (USHORT , volume_name_u16 .len * 2 );
1124
1128
@memcpy (input_buf [@sizeOf (MOUNTMGR_MOUNT_POINT ).. ], @ptrCast ([* ]const u8 , volume_name_u16 .ptr ), volume_name_u16 .len * 2 );
1125
1129
1126
- DeviceIoControl (mgmt_handle , IOCTL_MOUNTMGR_QUERY_POINTS , input_buf [0 .. ], output_buf [0 .. ] ) catch | err | switch (err ) {
1130
+ DeviceIoControl (mgmt_handle , IOCTL_MOUNTMGR_QUERY_POINTS , & input_buf , & output_buf ) catch | err | switch (err ) {
1127
1131
error .AccessDenied = > unreachable ,
1128
1132
else = > | e | return e ,
1129
1133
};
@@ -1143,22 +1147,20 @@ pub fn GetFinalPathNameByHandle(
1143
1147
1144
1148
// Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
1145
1149
// with traditional DOS drive letters, so pick the first one available.
1146
- const prefix_u8 = "\\ DosDevices\\ " ;
1147
- var prefix_buf_u16 : [prefix_u8 .len ]u16 = undefined ;
1148
- const prefix_len_u16 = std .unicode .utf8ToUtf16Le (prefix_buf_u16 [0.. ], prefix_u8 [0.. ]) catch unreachable ;
1149
- const prefix = prefix_buf_u16 [0.. prefix_len_u16 ];
1150
+ var prefix_buf = std .unicode .utf8ToUtf16LeStringLiteral ("\\ DosDevices\\ " );
1151
+ const prefix = prefix_buf [0.. prefix_buf .len ];
1150
1152
1151
- if (std . mem .startsWith (u16 , symlink , prefix )) {
1153
+ if (mem .startsWith (u16 , symlink , prefix )) {
1152
1154
const drive_letter = symlink [prefix .len .. ];
1153
1155
1154
1156
if (out_buffer .len < drive_letter .len + file_name_u16 .len ) return error .NameTooLong ;
1155
1157
1156
- std . mem .copy (u16 , out_buffer [0 .. ] , drive_letter );
1157
- std . mem .copy (u16 , out_buffer [drive_letter .len .. ], file_name_u16 );
1158
+ mem .copy (u16 , out_buffer , drive_letter );
1159
+ mem .copy (u16 , out_buffer [drive_letter .len .. ], file_name_u16 );
1158
1160
const total_len = drive_letter .len + file_name_u16 .len ;
1159
1161
1160
1162
// Validate that DOS does not contain any spurious nul bytes.
1161
- if (std . mem .indexOfScalar (u16 , out_buffer [0.. total_len ], 0 )) | _ | {
1163
+ if (mem .indexOfScalar (u16 , out_buffer [0.. total_len ], 0 )) | _ | {
1162
1164
return error .BadPathName ;
1163
1165
}
1164
1166
@@ -1178,15 +1180,14 @@ test "GetFinalPathNameByHandle" {
1178
1180
return ;
1179
1181
1180
1182
//any file will do
1181
- const file = try std .fs .openSelfExe (.{});
1182
- defer file .close ();
1183
- const handle = file .handle ;
1184
- //make this large enough for the test runner exe path
1185
- var buffer = std .mem .zeroes ([1 << 10 ]u16 );
1183
+ var tmp = std .testing .tmpDir (.{});
1184
+ defer tmp .cleanup ();
1185
+ const handle = tmp .dir .fd ;
1186
+ var buffer : [PATH_MAX_WIDE ]u16 = undefined ;
1186
1187
1187
1188
//check with sufficient size
1188
- const nt_length = (try GetFinalPathNameByHandle (handle , .{ .volume_name = .Nt }, buffer [0 .. ] )).len ;
1189
- const dos_length = (try GetFinalPathNameByHandle (handle , .{ .volume_name = .Dos }, buffer [0 .. ] )).len ;
1189
+ const nt_length = (try GetFinalPathNameByHandle (handle , .{ .volume_name = .Nt }, & buffer )).len ;
1190
+ const dos_length = (try GetFinalPathNameByHandle (handle , .{ .volume_name = .Dos }, & buffer )).len ;
1190
1191
1191
1192
//check with insufficient size
1192
1193
std .testing .expectError (error .NameTooLong , GetFinalPathNameByHandle (handle , .{ .volume_name = .Nt }, buffer [0 .. nt_length - 1 ]));
0 commit comments