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