| ... | ... | @@ -28,6 +28,9 @@ pub const gdi32 = @import("windows/gdi32.zig"); |
| 28 | 28 | |
| 29 | 29 | pub usingnamespace @import("windows/bits.zig"); |
| 30 | 30 | |
| 31 | //version detection |
| 32 | usingnamespace std.zig.system.windows; |
| 33 | |
| 31 | 34 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); |
| 32 | 35 | |
| 33 | 36 | pub const OpenError = error{ |
| ... | ... | @@ -953,12 +956,19 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { |
| 953 | 956 | return @bitCast(u64, result); |
| 954 | 957 | } |
| 955 | 958 | |
| 956 | | pub const GetFinalPathNameByHandleError = error{ |
| 957 | | BadPathName, |
| 958 | | FileNotFound, |
| 959 | | NameTooLong, |
| 960 | | Unexpected, |
| 961 | | }; |
| 959 | pub const GetFinalPathNameByHandleError = error { |
| 960 | BadPathName, |
| 961 | FileNotFound, |
| 962 | NameTooLong, |
| 963 | Unexpected, |
| 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 | }; |
| 962 | 972 | |
| 963 | 973 | /// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`. |
| 964 | 974 | /// Defaults to DOS volume names. |
| ... | ... | @@ -981,8 +991,52 @@ pub fn GetFinalPathNameByHandle( |
| 981 | 991 | fmt: GetFinalPathNameByHandleFormat, |
| 982 | 992 | out_buffer: []u16, |
| 983 | 993 | ) GetFinalPathNameByHandleError![]u16 { |
| 984 | | // Get normalized path; doesn't include volume name though. |
| 994 | |
| 985 | 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 | 1040 | try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]); |
| 987 | 1041 | |
| 988 | 1042 | // Get NT volume name. |