From 18540f65cde320c499eb8bcb630ade9f9fa977fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller?= Date: Tue, 30 Nov 2021 16:32:34 +0300 Subject: [PATCH] ntdll: Implement opening files through nt device paths. --- dlls/ntdll/tests/file.c | 25 +++++++- dlls/ntdll/unix/file.c | 134 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index af67e8d75bf..608b64a32ad 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -137,18 +137,22 @@ static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) static void create_file_test(void) { + static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0}; static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t', '\\','f','a','i','l','i','n','g',0}; + static const WCHAR systemrootExplorerW[] = {'\\','S','y','s','t','e','m','R','o','o','t', + '\\','e','x','p','l','o','r','e','r','.','e','x','e',0}; static const WCHAR questionmarkInvalidNameW[] = {'a','f','i','l','e','?',0}; static const WCHAR pipeInvalidNameW[] = {'a','|','b',0}; static const WCHAR pathInvalidNtW[] = {'\\','\\','?','\\',0}; static const WCHAR pathInvalidNt2W[] = {'\\','?','?','\\',0}; static const WCHAR pathInvalidDosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0}; static const char testdata[] = "Hello World"; + static const WCHAR sepW[] = {'\\',0}; FILE_NETWORK_OPEN_INFORMATION info; NTSTATUS status; HANDLE dir, file; - WCHAR path[MAX_PATH]; + WCHAR path[MAX_PATH], temp[MAX_PATH]; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; UNICODE_STRING nameW; @@ -328,6 +332,25 @@ static void create_file_test(void) status = pNtQueryFullAttributesFile( &attr, &info ); ok( status == STATUS_OBJECT_NAME_INVALID, "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + + GetWindowsDirectoryW( path, MAX_PATH ); + path[2] = 0; + ok( QueryDosDeviceW( path, temp, MAX_PATH ), + "QueryDosDeviceW failed with error %lx\n", GetLastError() ); + lstrcatW( temp, sepW ); + lstrcatW( temp, path+3 ); + lstrcatW( temp, sepW ); + lstrcatW( temp, notepadW ); + + pRtlInitUnicodeString( &nameW, temp ); + status = pNtQueryFullAttributesFile( &attr, &info ); + ok( status == STATUS_SUCCESS, + "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + + pRtlInitUnicodeString( &nameW, systemrootExplorerW ); + status = pNtQueryFullAttributesFile( &attr, &info ); + ok( status == STATUS_SUCCESS, + "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); } static void open_file_test(void) diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index ba9cd8d9031..c3ddf72b44b 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4494,7 +4494,7 @@ static NTSTATUS nt_to_unix_file_name_no_root( FILE_OBJECT *fileobj, char **unix_ /****************************************************************************** - * nt_to_unix_file_name + * nt_to_unix_file_name_internal * * Convert a file name from NT namespace to Unix namespace. * @@ -4502,7 +4502,7 @@ static NTSTATUS nt_to_unix_file_name_no_root( FILE_OBJECT *fileobj, char **unix_ * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is * returned, but the unix name is still filled in properly. */ -NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition ) +NTSTATUS nt_to_unix_file_name_internal( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition ) { HANDLE rootdir = attr->RootDirectory; enum server_fd_type type; @@ -4581,6 +4581,136 @@ reparse: } +/* read the contents of an NT symlink object */ +static NTSTATUS read_nt_symlink( HANDLE root, UNICODE_STRING *name, WCHAR *target, size_t length ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING targetW; + NTSTATUS status; + HANDLE handle; + + attr.Length = sizeof(attr); + attr.RootDirectory = root; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr ))) + { + targetW.Buffer = target; + targetW.MaximumLength = (length - 1) * sizeof(WCHAR); + status = NtQuerySymbolicLinkObject( handle, &targetW, NULL ); + NtClose( handle ); + } + + return status; +} + +/* try to find dos device based on nt device name */ +static NTSTATUS nt_to_dos_device( WCHAR *name, size_t length, WCHAR *device_ret ) +{ + static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s',0}; + UNICODE_STRING dosdevW = { sizeof(dosdevicesW) - sizeof(WCHAR), sizeof(dosdevicesW), (WCHAR *)dosdevicesW }; + WCHAR symlinkW[MAX_DIR_ENTRY_LEN]; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + char data[1024]; + HANDLE handle; + ULONG ctx = 0; + + DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &dosdevW; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr ); + if (status) return STATUS_BAD_DEVICE_TYPE; + + while (!NtQueryDirectoryObject( handle, info, sizeof(data), TRUE, FALSE, &ctx, NULL )) + { + if (read_nt_symlink( handle, &info->ObjectName, symlinkW, MAX_DIR_ENTRY_LEN )) continue; + if (wcsnicmp( symlinkW, name, length )) continue; + if (info->ObjectName.Length != 2 * sizeof(WCHAR) || info->ObjectName.Buffer[1] != ':') continue; + + *device_ret = info->ObjectName.Buffer[0]; + NtClose( handle ); + return STATUS_SUCCESS; + } + + NtClose( handle ); + return STATUS_BAD_DEVICE_TYPE; +} + +/****************************************************************************** + * nt_to_unix_file_name + * + * Convert a file name from NT namespace to Unix namespace. + * + * If disposition is not FILE_OPEN or FILE_OVERWRITE, the last path + * element doesn't have to exist; in that case STATUS_NO_SUCH_FILE is + * returned, but the unix name is still filled in properly. + */ +NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition ) +{ + static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t','\\',0}; + static const WCHAR dosprefixW[] = {'\\','?','?','\\'}; + static const WCHAR deviceW[] = {'\\','D','e','v','i','c','e','\\',0}; + WCHAR *name, *ptr, *prefix, buffer[3] = {'c',':',0}; + UNICODE_STRING dospathW, *nameW; + OBJECT_ATTRIBUTES attr_copy; + size_t offset, name_len; + NTSTATUS status; + + if (attr->RootDirectory) return nt_to_unix_file_name_internal( attr, name_ret, disposition ); + + nameW = attr->ObjectName; + + if (nameW->Length >= sizeof(deviceW) - sizeof(WCHAR) + && !wcsnicmp( nameW->Buffer, deviceW, ARRAY_SIZE(deviceW) - 1 )) + { + offset = sizeof(deviceW) / sizeof(WCHAR); + while (offset * sizeof(WCHAR) < nameW->Length && nameW->Buffer[ offset ] != '\\') offset++; + if ((status = nt_to_dos_device( nameW->Buffer, offset, buffer ))) return status; + prefix = buffer; + } + else if (nameW->Length >= sizeof(systemrootW) - sizeof(WCHAR) && + !wcsnicmp( nameW->Buffer, systemrootW, ARRAY_SIZE(systemrootW) - 1 )) + { + offset = (sizeof(systemrootW) - 1) / sizeof(WCHAR); + prefix = user_shared_data->NtSystemRoot; + } + else + return nt_to_unix_file_name_internal( attr, name_ret, disposition ); + + name_len = sizeof(dosprefixW) + wcslen(prefix) * sizeof(WCHAR) + + sizeof(WCHAR) /* '\\' */ + nameW->Length - offset * sizeof(WCHAR) + sizeof(WCHAR); + if (!(name = malloc( name_len ))) + return STATUS_NO_MEMORY; + + ptr = name; + memcpy( ptr, dosprefixW, sizeof(dosprefixW) ); + ptr += sizeof(dosprefixW) / sizeof(WCHAR); + wcscpy( ptr, prefix ); + ptr += wcslen(ptr); + *ptr++ = '\\'; + memcpy( ptr, nameW->Buffer + offset, nameW->Length - offset * sizeof(WCHAR) ); + ptr[ nameW->Length / sizeof(WCHAR) - offset ] = 0; + + dospathW.Buffer = name; + dospathW.Length = wcslen( name ) * sizeof(WCHAR); + attr_copy = *attr; + attr_copy.ObjectName = &dospathW; + status = nt_to_unix_file_name_internal( &attr_copy, name_ret, disposition ); + + free( name ); + return status; +} + /****************************************************************************** * wine_nt_to_unix_file_name * -- 2.39.1