authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-05 17:23:35+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-06 23:56:37+02:00
log747d46f22c2b5bef2d111564b5e4d362228004a2
treed9a45754752c08ce8cd3ebc4e03d2a06347a3055
parenta2bb246db4c2bb88f402215d5db79a535dbff4b6

Initial draft of GetFinalPathNameByHandle

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,7 +4060,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
4060}4060}
40614061
4062/// Same as `realpath` except `pathname` is UTF16LE-encoded.4062/// Same as `realpath` except `pathname` is UTF16LE-encoded.
4063/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
4064pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {4063pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4065 const w = windows;4064 const w = windows;
40664065
...@@ -4095,15 +4094,10 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat...@@ -4095,15 +4094,10 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
4095 defer w.CloseHandle(h_file);4094 defer w.CloseHandle(h_file);
40964095
4097 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;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);4097 const wide_slice = try w.GetFinalPathNameByHandle(h_file, wide_buf[0..]);
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;
41044098
4105 // Trust that Windows gives us valid UTF-16LE.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 return out_buffer[0..end_index];4101 return out_buffer[0..end_index];
4108}4102}
41094103
lib/std/os/windows.zig+70-16
...@@ -903,24 +903,78 @@ pub const GetFinalPathNameByHandleError = error{...@@ -903,24 +903,78 @@ pub const GetFinalPathNameByHandleError = error{
903 Unexpected,903 Unexpected,
904};904};
905905
906pub fn GetFinalPathNameByHandleW(906/// Returns canonical (normalized) path of handle. The output path assumes
907 hFile: HANDLE,907/// Win32 namespace, however, '\\?\' prefix is *not* prepended to the result.
908 buf_ptr: [*]u16,908/// TODO support other namespaces/volume names.
909 buf_len: DWORD,909pub fn GetFinalPathNameByHandle(hFile: HANDLE, out_buffer: []u16) GetFinalPathNameByHandleError![]u16 {
910 flags: DWORD,910 // The implementation is based on implementation found in Wine sources:
911) GetFinalPathNameByHandleError![:0]u16 {911 // [LINK]
912 const rc = kernel32.GetFinalPathNameByHandleW(hFile, buf_ptr, buf_len, flags);912 var buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + MAX_PATH * 2]u8 = undefined;
913 if (rc == 0) {913 var dummy: ULONG = undefined;
914 switch (kernel32.GetLastError()) {914 var rc = ntdll.NtQueryObject(hFile, OBJECT_INFORMATION_CLASS.ObjectNameInformation, &buffer, buffer.len, &dummy);
915 .FILE_NOT_FOUND => return error.FileNotFound,915 switch (rc) {
916 .PATH_NOT_FOUND => return error.FileNotFound,916 .SUCCESS => {},
917 .NOT_ENOUGH_MEMORY => return error.SystemResources,917 else => return unexpectedStatus(rc),
918 .FILENAME_EXCED_RANGE => return error.NameTooLong,918 }
919 .INVALID_PARAMETER => unreachable,919
920 else => |err| return unexpectedError(err),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}
925979
926pub const GetFileSizeError = error{Unexpected};980pub const GetFileSizeError = error{Unexpected};
lib/std/os/windows/bits.zig+26
...@@ -1573,3 +1573,29 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;...@@ -1573,3 +1573,29 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
15731573
1574pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;1574pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
1575pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;1575pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
1576
1577pub const OBJECT_INFORMATION_CLASS = extern enum {
1578 ObjectBasicInformation,
1579 ObjectNameInformation,
1580 ObjectTypeInformation,
1581 ObjectAllInformation,
1582 ObjectDataInformation,
1583};
1584pub const OBJECT_NAME_INFORMATION = extern struct {
1585 Name: UNICODE_STRING,
1586};
1587
1588pub const DIRECTORY_QUERY: DWORD = 0x0001;
1589pub const DIRECTORY_TRAVERSE: DWORD = 0x0002;
1590pub const DIRECTORY_CREATE_OBJECT: DWORD = 0x0004;
1591pub const DIRECTORY_CREATE_SUBDIRECTORY: DWORD = 0x0008;
1592pub const DIRECTORY_ALL_ACCESS: DWORD = STANDARD_RIGHTS_REQUIRED | 0xF;
1593
1594pub const OBJDIR_INFORMATION = extern struct {
1595 ObjectName: UNICODE_STRING,
1596 ObjectTypeName: UNICODE_STRING,
1597 Data: [1]BYTE,
1598};
1599
1600pub const SYMBOLIC_LINK_QUERY: DWORD = 0x0001;
1601pub 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,3 +106,37 @@ pub extern "NtDll" fn NtWaitForKeyedEvent(
106 Alertable: BOOLEAN,106 Alertable: BOOLEAN,
107 Timeout: ?*LARGE_INTEGER,107 Timeout: ?*LARGE_INTEGER,
108) callconv(.Stdcall) NTSTATUS;108) callconv(.Stdcall) NTSTATUS;
109
110pub 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
118pub extern "NtDll" fn NtOpenSymbolicLinkObject(
119 pHandle: *HANDLE,
120 DesiredAccess: DWORD,
121 ObjectAttributes: OBJECT_ATTRIBUTES,
122) callconv(.Stdcall) NTSTATUS;
123pub extern "NtDll" fn NtQuerySymbolicLinkObject(
124 SymbolicLinkHandle: HANDLE,
125 pLinkName: *UNICODE_STRING,
126 pDataWritten: ?*ULONG,
127) callconv(.Stdcall) NTSTATUS;
128
129pub extern "NtDll" fn NtOpenDirectoryObject(
130 DirectoryObjectHandle: *HANDLE,
131 DesiredAccess: DWORD,
132 ObjectAttributes: OBJECT_ATTRIBUTES,
133) callconv(.Stdcall) NTSTATUS;
134pub 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