| author | |
| committer | |
| log | 747d46f22c2b5bef2d111564b5e4d362228004a2 |
| tree | d9a45754752c08ce8cd3ebc4e03d2a06347a3055 |
| parent | a2bb246db4c2bb88f402215d5db79a535dbff4b6 |
This commit proposes an initial draft of `GetPathNameByHandle` function
which wraps NT syscalls and strives to emulate (currently only
partially) the `kernel32.GetFinalPathNameByHandleW` function.4 files changed, 132 insertions(+), 24 deletions(-)
lib/std/os.zig+2-8| ... | ... | @@ -4060,7 +4060,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP |
| 4060 | 4060 | } |
| 4061 | 4061 | |
| 4062 | 4062 | /// Same as `realpath` except `pathname` is UTF16LE-encoded. |
| 4063 | /// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine | |
| 4064 | 4063 | pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 4065 | 4064 | const w = windows; |
| 4066 | 4065 | |
| ... | ... | @@ -4095,15 +4094,10 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat |
| 4095 | 4094 | defer w.CloseHandle(h_file); |
| 4096 | 4095 | |
| 4097 | 4096 | var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined; |
| 4098 | const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS); | |
| 4099 | ||
| 4100 | // Windows returns \\?\ prepended to the path. | |
| 4101 | // We strip it to make this function consistent across platforms. | |
| 4102 | const prefix = [_]u16{ '\\', '\\', '?', '\\' }; | |
| 4103 | const start_index = if (mem.startsWith(u16, wide_slice, &prefix)) prefix.len else 0; | |
| 4097 | const wide_slice = try w.GetFinalPathNameByHandle(h_file, wide_buf[0..]); | |
| 4104 | 4098 | |
| 4105 | 4099 | // Trust that Windows gives us valid UTF-16LE. |
| 4106 | const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable; | |
| 4100 | const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable; | |
| 4107 | 4101 | return out_buffer[0..end_index]; |
| 4108 | 4102 | } |
| 4109 | 4103 |
lib/std/os/windows.zig+70-16| ... | ... | @@ -903,24 +903,78 @@ pub const GetFinalPathNameByHandleError = error{ |
| 903 | 903 | Unexpected, |
| 904 | 904 | }; |
| 905 | 905 | |
| 906 | pub fn GetFinalPathNameByHandleW( | |
| 907 | hFile: HANDLE, | |
| 908 | buf_ptr: [*]u16, | |
| 909 | buf_len: DWORD, | |
| 910 | flags: DWORD, | |
| 911 | ) GetFinalPathNameByHandleError![:0]u16 { | |
| 912 | const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags); | |
| 913 | if (rc == 0) { | |
| 914 | switch (kernel32.GetLastError()) { | |
| 915 | .FILE_NOT_FOUND => return error.FileNotFound, | |
| 916 | .PATH_NOT_FOUND => return error.FileNotFound, | |
| 917 | .NOT_ENOUGH_MEMORY => return error.SystemResources, | |
| 918 | .FILENAME_EXCED_RANGE => return error.NameTooLong, | |
| 919 | .INVALID_PARAMETER => unreachable, | |
| 920 | else => |err| return unexpectedError(err), | |
| 906 | /// Returns canonical (normalized) path of handle. The output path assumes | |
| 907 | /// Win32 namespace, however, '\\?\' prefix is *not* prepended to the result. | |
| 908 | /// TODO support other namespaces/volume names. | |
| 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 | } | |
| 919 | ||
| 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]; | |
| 922 | ||
| 923 | // Since `NtQueryObject` returns a fully-qualified NT path, we need to translate | |
| 924 | // the result into a Win32/DOS path (e.g., \Device\HarddiskVolume4\foo would become | |
| 925 | // C:\foo). | |
| 926 | const dos_drive_letters = &[_]u16{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; | |
| 927 | var query_path = [_]u16{ '\\', 'D', 'o', 's', 'D', 'e', 'v', 'i', 'c', 'e', 's', '\\', 'C', ':' }; | |
| 928 | for (dos_drive_letters) |drive_letter| { | |
| 929 | const drive = &[_]u16{ drive_letter, ':' }; | |
| 930 | std.mem.copy(u16, query_path[12..], drive[0..]); | |
| 931 | ||
| 932 | var sym_handle: HANDLE = undefined; | |
| 933 | const len_bytes = @intCast(u16, query_path.len) * 2; | |
| 934 | var nt_name = UNICODE_STRING{ | |
| 935 | .Length = len_bytes, | |
| 936 | .MaximumLength = len_bytes, | |
| 937 | .Buffer = @intToPtr([*]u16, @ptrToInt(&query_path)), | |
| 938 | }; | |
| 939 | var attr = OBJECT_ATTRIBUTES{ | |
| 940 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | |
| 941 | .RootDirectory = null, | |
| 942 | .Attributes = 0, | |
| 943 | .ObjectName = &nt_name, | |
| 944 | .SecurityDescriptor = null, | |
| 945 | .SecurityQualityOfService = null, | |
| 946 | }; | |
| 947 | rc = ntdll.NtOpenSymbolicLinkObject(&sym_handle, SYMBOLIC_LINK_QUERY, attr); | |
| 948 | switch (rc) { | |
| 949 | .SUCCESS => {}, | |
| 950 | .OBJECT_NAME_NOT_FOUND => continue, | |
| 951 | else => return unexpectedStatus(rc), | |
| 921 | 952 | } |
| 953 | ||
| 954 | var link_buffer: [MAX_PATH]u8 = undefined; | |
| 955 | var link = UNICODE_STRING{ | |
| 956 | .Length = 0, | |
| 957 | .MaximumLength = MAX_PATH, | |
| 958 | .Buffer = @intToPtr([*]u16, @ptrToInt(&link_buffer[0])), | |
| 959 | }; | |
| 960 | rc = ntdll.NtQuerySymbolicLinkObject(sym_handle, &link, null); | |
| 961 | CloseHandle(sym_handle); | |
| 962 | switch (rc) { | |
| 963 | .SUCCESS => {}, | |
| 964 | else => return unexpectedStatus(rc), | |
| 965 | } | |
| 966 | ||
| 967 | const link_path = @as([*]const u16, link.Buffer)[0..link.Length / 2]; | |
| 968 | const idx = std.mem.indexOf(u16, object_path, link_path) orelse continue; | |
| 969 | ||
| 970 | std.mem.copy(u16, out_buffer[0..], drive[0..]); | |
| 971 | std.mem.copy(u16, out_buffer[2..], object_path[link_path.len..]); | |
| 972 | ||
| 973 | return out_buffer[0..object_path.len - link_path.len + 2]; | |
| 922 | 974 | } |
| 923 | return buf_ptr[0..rc :0]; | |
| 975 | ||
| 976 | // If we're here, that means there was no match so error out! | |
| 977 | unreachable; | |
| 924 | 978 | } |
| 925 | 979 | |
| 926 | 980 | pub const GetFileSizeError = error{Unexpected}; |
lib/std/os/windows/bits.zig+26| ... | ... | @@ -1573,3 +1573,29 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1; |
| 1573 | 1573 | |
| 1574 | 1574 | pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1; |
| 1575 | 1575 | pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2; |
| 1576 | ||
| 1577 | pub const OBJECT_INFORMATION_CLASS = extern enum { | |
| 1578 | ObjectBasicInformation, | |
| 1579 | ObjectNameInformation, | |
| 1580 | ObjectTypeInformation, | |
| 1581 | ObjectAllInformation, | |
| 1582 | ObjectDataInformation, | |
| 1583 | }; | |
| 1584 | pub const OBJECT_NAME_INFORMATION = extern struct { | |
| 1585 | Name: UNICODE_STRING, | |
| 1586 | }; | |
| 1587 | ||
| 1588 | pub const DIRECTORY_QUERY: DWORD = 0x0001; | |
| 1589 | pub const DIRECTORY_TRAVERSE: DWORD = 0x0002; | |
| 1590 | pub const DIRECTORY_CREATE_OBJECT: DWORD = 0x0004; | |
| 1591 | pub const DIRECTORY_CREATE_SUBDIRECTORY: DWORD = 0x0008; | |
| 1592 | pub const DIRECTORY_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED | 0xF; | |
| 1593 | ||
| 1594 | pub const OBJDIR_INFORMATION = extern struct { | |
| 1595 | ObjectName: UNICODE_STRING, | |
| 1596 | ObjectTypeName: UNICODE_STRING, | |
| 1597 | Data: [1]BYTE, | |
| 1598 | }; | |
| 1599 | ||
| 1600 | pub const SYMBOLIC_LINK_QUERY: DWORD = 0x0001; | |
| 1601 | pub const SYMBOLIC_LINK_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED | 0x1; | |
| \ No newline at end of file |
lib/std/os/windows/ntdll.zig+34| ... | ... | @@ -106,3 +106,37 @@ pub extern "NtDll" fn NtWaitForKeyedEvent( |
| 106 | 106 | Alertable: BOOLEAN, |
| 107 | 107 | Timeout: ?*LARGE_INTEGER, |
| 108 | 108 | ) callconv(.Stdcall) NTSTATUS; |
| 109 | ||
| 110 | pub extern "NtDll" fn NtQueryObject( | |
| 111 | Handle: HANDLE, | |
| 112 | ObjectInformationClass: OBJECT_INFORMATION_CLASS, | |
| 113 | ObjectInformation: *c_void, | |
| 114 | ObjectInformationLength: ULONG, | |
| 115 | ReturnLength: *ULONG, | |
| 116 | ) callconv(.Stdcall) NTSTATUS; | |
| 117 | ||
| 118 | pub extern "NtDll" fn NtOpenSymbolicLinkObject( | |
| 119 | pHandle: *HANDLE, | |
| 120 | DesiredAccess: DWORD, | |
| 121 | ObjectAttributes: OBJECT_ATTRIBUTES, | |
| 122 | ) callconv(.Stdcall) NTSTATUS; | |
| 123 | pub extern "NtDll" fn NtQuerySymbolicLinkObject( | |
| 124 | SymbolicLinkHandle: HANDLE, | |
| 125 | pLinkName: *UNICODE_STRING, | |
| 126 | pDataWritten: ?*ULONG, | |
| 127 | ) callconv(.Stdcall) NTSTATUS; | |
| 128 | ||
| 129 | pub extern "NtDll" fn NtOpenDirectoryObject( | |
| 130 | DirectoryObjectHandle: *HANDLE, | |
| 131 | DesiredAccess: DWORD, | |
| 132 | ObjectAttributes: OBJECT_ATTRIBUTES, | |
| 133 | ) callconv(.Stdcall) NTSTATUS; | |
| 134 | pub extern "NtDll" fn NtQueryDirectoryObject( | |
| 135 | DirectoryHandle: HANDLE, | |
| 136 | Buffer: ?*c_void, | |
| 137 | Length: ULONG, | |
| 138 | ReturnSingleEntry: BOOLEAN, | |
| 139 | RestartScan: BOOLEAN, | |
| 140 | Context: *ULONG, | |
| 141 | ReturnLength: *ULONG, | |
| 142 | ) callconv(.Stdcall) NTSTATUS; | |
| \ No newline at end of file |