authorgravatar for rohlemF@gmail.comRohlem <rohlemF@gmail.com> 2020-12-09 13:04:26+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-11 17:48:18-07:00
log450e4672017773e2eb7b6db16e17279fe85a7a11
tree771e453cf99462df9f28a4eaa89f5b4b1a70715d
parent4b280ac7e9730a30cb3a03350711a13cf45c5dfc

std.os.windows.GetFinalPathNameByHandle: reintroduce kernel32 for compatibility

The NtQueryInformationFile with .FileNormalizedNameInformation is only available in Windows 10 1803 (rs4) and later, however there is probably still another route we can go via ntdll.

1 files changed, 61 insertions(+), 7 deletions(-)

lib/std/os/windows.zig+61-7
...@@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig");...@@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig");
2828
29pub usingnamespace @import("windows/bits.zig");29pub usingnamespace @import("windows/bits.zig");
3030
31//version detection
32usingnamespace std.zig.system.windows;
33
31pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));34pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
3235
33pub const OpenError = error{36pub const OpenError = error{
...@@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {...@@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
953 return @bitCast(u64, result);956 return @bitCast(u64, result);
954}957}
955958
956pub const GetFinalPathNameByHandleError = error{959pub const GetFinalPathNameByHandleError = error {
957 BadPathName,960 BadPathName,
958 FileNotFound,961 FileNotFound,
959 NameTooLong,962 NameTooLong,
960 Unexpected,963 Unexpected,
961};964 }
965 || if((comptime builtin.os.tag != .windows) or (targetVersionIsAtLeast(WindowsVersion.win10_rs4) == true))
966 error {}
967 else
968 error {
969 AccessDenied,
970 SystemResources,
971 };
962972
963/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.973/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
964/// Defaults to DOS volume names.974/// Defaults to DOS volume names.
...@@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle(...@@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle(
981 fmt: GetFinalPathNameByHandleFormat,991 fmt: GetFinalPathNameByHandleFormat,
982 out_buffer: []u16,992 out_buffer: []u16,
983) GetFinalPathNameByHandleError![]u16 {993) GetFinalPathNameByHandleError![]u16 {
984 // Get normalized path; doesn't include volume name though.994
985 var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;995 var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
996
997 if ((comptime (targetVersionIsAtLeast(WindowsVersion.win10_rs4) != true)) //need explicit comptime, because error returns affect return type
998 and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4)) {
999 // TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation
1000 // with ntdll instead of calling into kernel32
1001 // (probably using some less-powerful query and looping over path segments)
1002 const flags: DWORD = FILE_NAME_NORMALIZED | switch(fmt.volume_name) {
1003 .Dos => @as(DWORD, VOLUME_NAME_DOS),
1004 .Nt => @as(DWORD, VOLUME_NAME_NT),
1005 };
1006 const wide_path_buffer = std.mem.bytesAsSlice(u16, path_buffer[0..]);
1007 const rc = kernel32.GetFinalPathNameByHandleW(hFile, wide_path_buffer.ptr, @intCast(u32, wide_path_buffer.len), flags);
1008 if (rc == 0) {
1009 switch (kernel32.GetLastError()) {
1010 .FILE_NOT_FOUND => return error.FileNotFound,
1011 .PATH_NOT_FOUND => return error.FileNotFound,
1012 .NOT_ENOUGH_MEMORY => return error.SystemResources,
1013 .FILENAME_EXCED_RANGE => return error.NameTooLong,
1014 .ACCESS_DENIED => return error.AccessDenied, //can happen in SMB sub-queries for parent path segments
1015 .INVALID_PARAMETER => unreachable,
1016 else => |err| return unexpectedError(err),
1017 }
1018 }
1019
1020 //in case of failure, rc == length of string INCLUDING null terminator,
1021 if (rc > wide_path_buffer.len) return error.NameTooLong;
1022 //in case of success, rc == length of string EXCLUDING null terminator
1023 const result_slice = switch(fmt.volume_name) {
1024 .Dos => blk: {
1025 const expected_prefix = [_]u16{'\\', '\\', '?', '\\'};
1026 if (!std.mem.eql(u16, expected_prefix[0..], wide_path_buffer[0..expected_prefix.len])) {
1027 return error.BadPathName;
1028 }
1029 break :blk wide_path_buffer[expected_prefix.len..rc:0];
1030 },
1031 //no prefix here
1032 .Nt => wide_path_buffer[0..rc:0],
1033 };
1034 if(result_slice.len > out_buffer.len) return error.NameTooLong;
1035 std.mem.copy(u16, out_buffer[0..], result_slice);
1036 return out_buffer[0..result_slice.len];
1037 }
1038
1039 // Get normalized path; doesn't include volume name though.
986 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);1040 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
9871041
988 // Get NT volume name.1042 // Get NT volume name.