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