| ... | ... | @@ -907,18 +907,61 @@ pub const GetFinalPathNameByHandleError = error{ |
| 907 | 907 | /// Win32 namespace, however, '\\?\' prefix is *not* prepended to the result. |
| 908 | 908 | /// TODO support other namespaces/volume names. |
| 909 | 909 | pub fn GetFinalPathNameByHandle(hFile: HANDLE, out_buffer: []u16) GetFinalPathNameByHandleError![]u16 { |
| 910 | | // The implementation is based on implementation found in Wine sources: |
| 911 | | // [LINK] |
| 912 | | var buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + MAX_PATH * 2]u8 = undefined; |
| 913 | | var dummy: ULONG = undefined; |
| 914 | | var rc = ntdll.NtQueryObject(hFile, OBJECT_INFORMATION_CLASS.ObjectNameInformation, &buffer, buffer.len, &dummy); |
| 915 | | switch (rc) { |
| 916 | | .SUCCESS => {}, |
| 917 | | else => return unexpectedStatus(rc), |
| 918 | | } |
| 910 | // The implementation is based on implementation found in Wine sources, however, |
| 911 | // we make the following tweaks. First of all, if `NtQueryInformationFile` supports |
| 912 | // `FILE_INFORMATION_CLASS.FileNormalizedNameInformation` and `FileVolumeNameInformation`, |
| 913 | // we use those two calls to generate a valid Win32/DOS path. Otherwise, we fallback to |
| 914 | // more widely supported `NtQueryObject` generic routine. |
| 915 | // Wine source: https://source.winehq.com/git/wine.git/blob/HEAD:/dlls/kernelbase/file.c#l1708 |
| 916 | var buffer: [PATH_MAX_WIDE]u16 = undefined; |
| 917 | const object_path = blk: { |
| 918 | var path_buffer: [PATH_MAX_WIDE * 2]u8 = undefined; |
| 919 | var io: IO_STATUS_BLOCK = undefined; |
| 920 | var rc = ntdll.NtQueryInformationFile(hFile, &io, &path_buffer, path_buffer.len, FILE_INFORMATION_CLASS.FileNormalizedNameInformation); |
| 921 | switch (rc) { |
| 922 | .SUCCESS => { |
| 923 | var nt_volume_buffer: [MAX_PATH]u8 = undefined; |
| 924 | rc = ntdll.NtQueryInformationFile(hFile, &io, &nt_volume_buffer, nt_volume_buffer.len, FILE_INFORMATION_CLASS.FileVolumeNameInformation); |
| 925 | switch (rc) { |
| 926 | .SUCCESS => { |
| 927 | const file_name = @ptrCast(*const FILE_NAME_INFORMATION, @alignCast(@alignOf(FILE_NAME_INFORMATION), &path_buffer[0])); |
| 928 | const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0..file_name.FileNameLength / 2]; |
| 929 | |
| 930 | if (file_name_u16.len > PATH_MAX_WIDE) return error.NameTooLong; |
| 931 | |
| 932 | const volume_name = @ptrCast(*const FILE_NAME_INFORMATION, @alignCast(@alignOf(FILE_NAME_INFORMATION), &nt_volume_buffer[0])); |
| 933 | const volume_name_u16 = @ptrCast([*]const u16, &volume_name.FileName[0])[0..volume_name.FileNameLength / 2]; |
| 934 | |
| 935 | std.mem.copy(u16, buffer[0..], volume_name_u16); |
| 936 | std.mem.copy(u16, buffer[volume_name_u16.len..], file_name_u16); |
| 937 | |
| 938 | break :blk buffer[0..volume_name_u16.len + file_name_u16.len]; |
| 939 | }, |
| 940 | .INVALID_PARAMETER => {}, // fall through |
| 941 | else => return unexpectedStatus(rc), |
| 942 | } |
| 943 | }, |
| 944 | .INVALID_PARAMETER => {}, // fall through |
| 945 | else => return unexpectedStatus(rc), |
| 946 | } |
| 919 | 947 | |
| 920 | | const object_name = @ptrCast(*const OBJECT_NAME_INFORMATION, @alignCast(@alignOf(OBJECT_NAME_INFORMATION), &buffer)); |
| 921 | | const object_path = @as([*]const u16, object_name.Name.Buffer)[0..object_name.Name.Length / 2]; |
| 948 | var dummy: ULONG = undefined; |
| 949 | rc = ntdll.NtQueryObject(hFile, OBJECT_INFORMATION_CLASS.ObjectNameInformation, &path_buffer, path_buffer.len, &dummy); |
| 950 | switch (rc) { |
| 951 | .SUCCESS => {}, |
| 952 | else => return unexpectedStatus(rc), |
| 953 | } |
| 954 | |
| 955 | const object_name = @ptrCast(*const OBJECT_NAME_INFORMATION, @alignCast(@alignOf(OBJECT_NAME_INFORMATION), &buffer)); |
| 956 | |
| 957 | // Apparently, `NtQueryObject` has a bug when signalling an error condition. |
| 958 | // To check for error, i.e., FileNotFound, we check if the result Buffer is non null |
| 959 | // and Length is greater than zero. |
| 960 | // Source: https://stackoverflow.com/questions/65170/how-to-get-name-associated-with-open-handle |
| 961 | if (object_name.Name.Length == 0) return error.FileNotFound; |
| 962 | |
| 963 | break :blk @as([*]const u16, object_name.Name.Buffer)[0..object_name.Name.Length / 2]; |
| 964 | }; |
| 922 | 965 | |
| 923 | 966 | // Since `NtQueryObject` returns a fully-qualified NT path, we need to translate |
| 924 | 967 | // the result into a Win32/DOS path (e.g., \Device\HarddiskVolume4\foo would become |
| ... | ... | @@ -927,7 +970,7 @@ pub fn GetFinalPathNameByHandle(hFile: HANDLE, out_buffer: []u16) GetFinalPathNa |
| 927 | 970 | var query_path = [_]u16{ '\\', 'D', 'o', 's', 'D', 'e', 'v', 'i', 'c', 'e', 's', '\\', 'C', ':' }; |
| 928 | 971 | for (dos_drive_letters) |drive_letter| { |
| 929 | 972 | const drive = &[_]u16{ drive_letter, ':' }; |
| 930 | | std.mem.copy(u16, query_path[12..], drive[0..]); |
| 973 | std.mem.copy(u16, query_path[query_path.len - 2..], drive[0..]); |
| 931 | 974 | |
| 932 | 975 | var sym_handle: HANDLE = undefined; |
| 933 | 976 | const len_bytes = @intCast(u16, query_path.len) * 2; |
| ... | ... | @@ -944,7 +987,7 @@ pub fn GetFinalPathNameByHandle(hFile: HANDLE, out_buffer: []u16) GetFinalPathNa |
| 944 | 987 | .SecurityDescriptor = null, |
| 945 | 988 | .SecurityQualityOfService = null, |
| 946 | 989 | }; |
| 947 | | rc = ntdll.NtOpenSymbolicLinkObject(&sym_handle, SYMBOLIC_LINK_QUERY, attr); |
| 990 | var rc = ntdll.NtOpenSymbolicLinkObject(&sym_handle, SYMBOLIC_LINK_QUERY, attr); |
| 948 | 991 | switch (rc) { |
| 949 | 992 | .SUCCESS => {}, |
| 950 | 993 | .OBJECT_NAME_NOT_FOUND => continue, |
| ... | ... | @@ -967,6 +1010,7 @@ pub fn GetFinalPathNameByHandle(hFile: HANDLE, out_buffer: []u16) GetFinalPathNa |
| 967 | 1010 | const link_path = @as([*]const u16, link.Buffer)[0..link.Length / 2]; |
| 968 | 1011 | const idx = std.mem.indexOf(u16, object_path, link_path) orelse continue; |
| 969 | 1012 | |
| 1013 | // TODO check the provided buffer is actually big enough. |
| 970 | 1014 | std.mem.copy(u16, out_buffer[0..], drive[0..]); |
| 971 | 1015 | std.mem.copy(u16, out_buffer[2..], object_path[link_path.len..]); |
| 972 | 1016 | |