diff options
| author | Rohlem <rohlemF@gmail.com> | 2020-12-09 13:04:26 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-01-11 17:48:18 -0700 |
| commit | 450e4672017773e2eb7b6db16e17279fe85a7a11 (patch) | |
| tree | 771e453cf99462df9f28a4eaa89f5b4b1a70715d /lib | |
| parent | 4b280ac7e9730a30cb3a03350711a13cf45c5dfc (diff) | |
| download | zig-450e4672017773e2eb7b6db16e17279fe85a7a11.tar.gz zig-450e4672017773e2eb7b6db16e17279fe85a7a11.zip | |
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.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/std/os/windows.zig | 68 |
1 files changed, 61 insertions, 7 deletions
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 57755ffa49..2a103bdd64 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig"); pub usingnamespace @import("windows/bits.zig"); +//version detection +usingnamespace std.zig.system.windows; + pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); pub const OpenError = error{ @@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { return @bitCast(u64, result); } -pub const GetFinalPathNameByHandleError = error{ - BadPathName, - FileNotFound, - NameTooLong, - Unexpected, -}; +pub const GetFinalPathNameByHandleError = error { + BadPathName, + FileNotFound, + NameTooLong, + Unexpected, + } + || if((comptime builtin.os.tag != .windows) or (targetVersionIsAtLeast(WindowsVersion.win10_rs4) == true)) + error {} + else + error { + AccessDenied, + SystemResources, + }; /// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`. /// Defaults to DOS volume names. @@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle( fmt: GetFinalPathNameByHandleFormat, out_buffer: []u16, ) GetFinalPathNameByHandleError![]u16 { - // Get normalized path; doesn't include volume name though. + var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; + + if ((comptime (targetVersionIsAtLeast(WindowsVersion.win10_rs4) != true)) //need explicit comptime, because error returns affect return type + and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4)) { + // TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation + // with ntdll instead of calling into kernel32 + // (probably using some less-powerful query and looping over path segments) + const flags: DWORD = FILE_NAME_NORMALIZED | switch(fmt.volume_name) { + .Dos => @as(DWORD, VOLUME_NAME_DOS), + .Nt => @as(DWORD, VOLUME_NAME_NT), + }; + const wide_path_buffer = std.mem.bytesAsSlice(u16, path_buffer[0..]); + const rc = kernel32.GetFinalPathNameByHandleW(hFile, wide_path_buffer.ptr, @intCast(u32, wide_path_buffer.len), flags); + if (rc == 0) { + switch (kernel32.GetLastError()) { + .FILE_NOT_FOUND => return error.FileNotFound, + .PATH_NOT_FOUND => return error.FileNotFound, + .NOT_ENOUGH_MEMORY => return error.SystemResources, + .FILENAME_EXCED_RANGE => return error.NameTooLong, + .ACCESS_DENIED => return error.AccessDenied, //can happen in SMB sub-queries for parent path segments + .INVALID_PARAMETER => unreachable, + else => |err| return unexpectedError(err), + } + } + + //in case of failure, rc == length of string INCLUDING null terminator, + if (rc > wide_path_buffer.len) return error.NameTooLong; + //in case of success, rc == length of string EXCLUDING null terminator + const result_slice = switch(fmt.volume_name) { + .Dos => blk: { + const expected_prefix = [_]u16{'\\', '\\', '?', '\\'}; + if (!std.mem.eql(u16, expected_prefix[0..], wide_path_buffer[0..expected_prefix.len])) { + return error.BadPathName; + } + break :blk wide_path_buffer[expected_prefix.len..rc:0]; + }, + //no prefix here + .Nt => wide_path_buffer[0..rc:0], + }; + if(result_slice.len > out_buffer.len) return error.NameTooLong; + std.mem.copy(u16, out_buffer[0..], result_slice); + return out_buffer[0..result_slice.len]; + } + + // Get normalized path; doesn't include volume name though. try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]); // Get NT volume name. |
