@@ -1000,19 +1000,13 @@ test "QueryObjectName" {
1000
1000
_ = try QueryObjectName (file .handle , out_buffer [0.. result_path .len ]);
1001
1001
}
1002
1002
1003
- pub const GetFinalPathNameByHandleError = error {
1004
- BadPathName ,
1005
- FileNotFound ,
1006
- NameTooLong ,
1007
- Unexpected ,
1008
- }
1009
- || if ((comptime builtin .os .tag != .windows ) or (targetVersionIsAtLeast (WindowsVersion .win10_rs4 ) == true ))
1010
- error {}
1011
- else
1012
- error {
1013
- AccessDenied ,
1014
- SystemResources ,
1015
- };
1003
+ pub const GetFinalPathNameByHandleError = error {
1004
+ AccessDenied ,
1005
+ BadPathName ,
1006
+ FileNotFound ,
1007
+ NameTooLong ,
1008
+ Unexpected ,
1009
+ };
1016
1010
1017
1011
/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
1018
1012
/// Defaults to DOS volume names.
@@ -1035,75 +1029,64 @@ pub fn GetFinalPathNameByHandle(
1035
1029
fmt : GetFinalPathNameByHandleFormat ,
1036
1030
out_buffer : []u16 ,
1037
1031
) 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 ;
1033
+ 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
1038
1034
1039
- var path_buffer : [@sizeOf ( FILE_NAME_INFORMATION ) + PATH_MAX_WIDE * 2 ] u8 align ( @alignOf ( FILE_NAME_INFORMATION )) = undefined ;
1040
-
1035
+ var file_name_u16 : [] const u16 = undefined ;
1036
+ var volume_name_u16 : [] const u16 = undefined ;
1041
1037
if ((comptime (targetVersionIsAtLeast (WindowsVersion .win10_rs4 ) != true )) //need explicit comptime, because error returns affect return type
1042
- and ! runtimeVersionIsAtLeast (WindowsVersion .win10_rs4 )) {
1043
- // TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation
1044
- // with ntdll instead of calling into kernel32
1045
- // (probably using some less-powerful query and looping over path segments)
1046
- const flags : DWORD = FILE_NAME_NORMALIZED | switch (fmt .volume_name ) {
1047
- .Dos = > @as (DWORD , VOLUME_NAME_DOS ),
1048
- .Nt = > @as (DWORD , VOLUME_NAME_NT ),
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?
1042
+ else = > | e | e ,
1049
1043
};
1050
- const wide_path_buffer = std .mem .bytesAsSlice (u16 , path_buffer [0.. ]);
1051
- const rc = kernel32 .GetFinalPathNameByHandleW (hFile , wide_path_buffer .ptr , @intCast (u32 , wide_path_buffer .len ), flags );
1052
- if (rc == 0 ) {
1053
- switch (kernel32 .GetLastError ()) {
1054
- .FILE_NOT_FOUND = > return error .FileNotFound ,
1055
- .PATH_NOT_FOUND = > return error .FileNotFound ,
1056
- .NOT_ENOUGH_MEMORY = > return error .SystemResources ,
1057
- .FILENAME_EXCED_RANGE = > return error .NameTooLong ,
1058
- .ACCESS_DENIED = > return error .AccessDenied , //can happen in SMB sub-queries for parent path segments
1059
- .INVALID_PARAMETER = > unreachable ,
1060
- else = > | err | return unexpectedError (err ),
1044
+
1045
+ if (fmt .volume_name == .Nt ) {
1046
+ if (out_buffer .len < final_path .len ) {
1047
+ return error .NameTooLong ;
1061
1048
}
1049
+ std .mem .copy (u16 , out_buffer [0.. ], final_path [0.. ]);
1050
+ return final_path ; //we can directly return the slice we received
1062
1051
}
1063
1052
1064
- //in case of failure, rc == length of string INCLUDING null terminator,
1065
- if (rc > wide_path_buffer .len ) return error .NameTooLong ;
1066
- //in case of success, rc == length of string EXCLUDING null terminator
1067
- const result_slice = switch (fmt .volume_name ) {
1068
- .Dos = > blk : {
1069
- const expected_prefix = [_ ]u16 {'\\ ' , '\\ ' , '?' , '\\ ' };
1070
- if (! std .mem .eql (u16 , expected_prefix [0.. ], wide_path_buffer [0.. expected_prefix .len ])) {
1071
- return error .BadPathName ;
1072
- }
1073
- break :blk wide_path_buffer [expected_prefix .len .. rc :0 ];
1074
- },
1075
- //no prefix here
1076
- .Nt = > wide_path_buffer [0.. rc :0 ],
1077
- };
1078
- if (result_slice .len > out_buffer .len ) return error .NameTooLong ;
1079
- std .mem .copy (u16 , out_buffer [0.. ], result_slice );
1080
- return out_buffer [0.. result_slice .len ];
1081
- }
1082
-
1083
- // Get normalized path; doesn't include volume name though.
1084
- try QueryInformationFile (hFile , .FileNormalizedNameInformation , path_buffer [0.. ]);
1085
-
1086
- // Get NT volume name.
1087
- 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
1088
- try QueryInformationFile (hFile , .FileVolumeNameInformation , volume_buffer [0.. ]);
1053
+ //otherwise we need to parse the string for volume path for the .Dos logic below to work
1054
+ 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 ;
1061
+ volume_name_u16 = final_path [0.. index ];
1062
+ file_name_u16 = final_path [index .. ];
1089
1063
1090
- const file_name = @ptrCast (* const FILE_NAME_INFORMATION , & path_buffer [0 ]);
1091
- const file_name_u16 = @ptrCast ([* ]const u16 , & file_name .FileName [0 ])[0 .. file_name .FileNameLength / 2 ];
1064
+ //fallthrough for fmt.volume_name != .Nt
1065
+ } else {
1066
+ // 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 )];
1092
1070
1093
- const volume_name = @ptrCast (* const FILE_NAME_INFORMATION , & volume_buffer [0 ]);
1071
+ // 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 )];
1094
1075
1095
- switch (fmt .volume_name ) {
1096
- .Nt = > {
1076
+ if (fmt .volume_name == .Nt ) {
1097
1077
// Nothing to do, we simply copy the bytes to the user-provided buffer.
1098
- const volume_name_u16 = @ptrCast ([* ]const u16 , & volume_name .FileName [0 ])[0 .. volume_name .FileNameLength / 2 ];
1099
-
1100
1078
if (out_buffer .len < volume_name_u16 .len + file_name_u16 .len ) return error .NameTooLong ;
1101
1079
1102
1080
std .mem .copy (u16 , out_buffer [0.. ], volume_name_u16 );
1103
1081
std .mem .copy (u16 , out_buffer [volume_name_u16 .len .. ], file_name_u16 );
1104
1082
1105
1083
return out_buffer [0 .. volume_name_u16 .len + file_name_u16 .len ];
1106
- },
1084
+ }
1085
+ //fallthrough for fmt.volume_name != .Nt
1086
+ }
1087
+
1088
+ switch (fmt .volume_name ) {
1089
+ .Nt = > unreachable , //handled above
1107
1090
.Dos = > {
1108
1091
// Get DOS volume name. DOS volume names are actually symbolic link objects to the
1109
1092
// actual NT volume. For example:
@@ -1137,8 +1120,8 @@ pub fn GetFinalPathNameByHandle(
1137
1120
1138
1121
var input_struct = @ptrCast (* MOUNTMGR_MOUNT_POINT , & input_buf [0 ]);
1139
1122
input_struct .DeviceNameOffset = @sizeOf (MOUNTMGR_MOUNT_POINT );
1140
- input_struct .DeviceNameLength = @intCast (USHORT , volume_name . FileNameLength );
1141
- @memcpy (input_buf [@sizeOf (MOUNTMGR_MOUNT_POINT ).. ], @ptrCast ([* ]const u8 , & volume_name . FileName [ 0 ] ), volume_name . FileNameLength );
1123
+ input_struct .DeviceNameLength = @intCast (USHORT , volume_name_u16 . len * 2 );
1124
+ @memcpy (input_buf [@sizeOf (MOUNTMGR_MOUNT_POINT ).. ], @ptrCast ([* ]const u8 , volume_name_u16 . ptr ), volume_name_u16 . len * 2 );
1142
1125
1143
1126
DeviceIoControl (mgmt_handle , IOCTL_MOUNTMGR_QUERY_POINTS , input_buf [0.. ], output_buf [0.. ]) catch | err | switch (err ) {
1144
1127
error .AccessDenied = > unreachable ,
0 commit comments