From: Bolan Chen <bolanchen123@gmail.com> --- dlls/ntdll/actctx.c | 315 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index 47464d40b37..ca41b2bf16e 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -27,6 +27,8 @@ #include <stdlib.h> #include "ntstatus.h" +#include "winbase.h" +#include "winnt.h" #include "winternl.h" #include "ddk/ntddk.h" #include "ddk/wdm.h" @@ -3326,6 +3328,206 @@ static NTSTATUS lookup_winsxs(struct actctx_loader* acl, struct assembly_identit return io.Status; } +static BOOL is_mixed_clr_module( void *module ) +{ + const IMAGE_COR20_HEADER *cor; + ULONG size; + + if (!RtlImageNtHeader( module )) return FALSE; + cor = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &size ); + if (!cor || size < sizeof(*cor)) return FALSE; + + return !(cor->flags & COMIMAGE_FLAGS_ILONLY); +} + +static void parse_probing_elem( xmlbuf_t *xmlbuf, const struct xml_elem *parent, WCHAR **private_path ) +{ + struct xml_attr attr; + BOOL end = FALSE; + + while (next_xml_attr( xmlbuf, &attr, &end )) + { + if (xml_attr_cmp( &attr, L"privatePath" )) + { + if (!*private_path && !(*private_path = xmlstrdupW( &attr.value ))) + set_error( xmlbuf ); + } else if (!is_xmlns_attr( &attr )) + WARN( "unknown config attr %s\n", debugstr_xml_attr( &attr ) ); + } + + if (!end) parse_expect_end_elem( xmlbuf, parent ); +} + +static void parse_assembly_binding_elem( xmlbuf_t *xmlbuf, const struct xml_elem *parent, WCHAR **private_path ) +{ + struct xml_attr attr; + struct xml_elem elem; + BOOL end = FALSE; + + while (next_xml_attr( xmlbuf, &attr, &end )); + if (end) return; + + while (next_xml_elem( xmlbuf, &elem, parent )) + { + if (xml_elem_cmp( &elem, L"probing", asmv1W )) + parse_probing_elem( xmlbuf, &elem, private_path ); + else + parse_unknown_elem( xmlbuf, &elem ); + } +} + +static void parse_runtime_elem( xmlbuf_t *xmlbuf, const struct xml_elem *parent, WCHAR **private_path ) +{ + struct xml_attr attr; + struct xml_elem elem; + BOOL end = FALSE; + + while (next_xml_attr( xmlbuf, &attr, &end )); + if (end) return; + + while (next_xml_elem( xmlbuf, &elem, parent )) + { + if (xml_elem_cmp( &elem, L"assemblyBinding", asmv1W )) + parse_assembly_binding_elem( xmlbuf, &elem, private_path ); + else + parse_unknown_elem( xmlbuf, &elem ); + } +} + + +static NTSTATUS parse_config_file( xmlbuf_t *xmlbuf, WCHAR **private_path ) +{ + struct xml_elem elem; + struct xml_elem parent = {}; + BOOL end = FALSE; + + xmlbuf->error = FALSE; + xmlbuf->ns_pos = 0; + *private_path = NULL; + + if (!next_xml_elem( xmlbuf, &elem, &parent )) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + if (xmlstr_cmp( &elem.name, L"?xml" ) && + (!parse_xml_header( xmlbuf ) || !next_xml_elem(xmlbuf, &elem, &parent))) + return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + if (!xmlstr_cmp( &elem.name, L"configuration" ) || elem.ns.len) + { + FIXME( "root element is %s, expected <configuration>", debugstr_xml_elem( &elem ) ); + return STATUS_SXS_ASSEMBLY_NOT_FOUND; + } + + parse_expect_no_attr( xmlbuf, &end ); + if (!end) + { + while (next_xml_elem( xmlbuf, &elem, &parent )) + { + if (xmlstr_cmp( &elem.name, L"runtime" ) && !elem.ns.len) + parse_runtime_elem( xmlbuf, &elem, private_path ); + else + parse_unknown_elem( xmlbuf, &elem ); + } + } + + if (xmlbuf->error) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + if (next_xml_elem( xmlbuf, &elem, &parent )) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + if (xmlbuf->ptr != xmlbuf->end) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + return STATUS_SUCCESS; +} + +static NTSTATUS open_config_file( LPCWSTR filename, HANDLE file, WCHAR **private_path ) +{ + FILE_END_OF_FILE_INFORMATION info; + IO_STATUS_BLOCK io; + HANDLE mapping; + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER size; + LARGE_INTEGER offset; + NTSTATUS status; + SIZE_T count; + INT buffer_len; + void *base; + xmlbuf_t xmlbuf; + int unicode_tests; + + TRACE( "loading config file %s\n", debugstr_w(filename) ); + + InitializeObjectAttributes( &attr, NULL, OBJ_CASE_INSENSITIVE, 0, NULL ); + size.QuadPart = 0; + status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ, + &attr, &size, PAGE_READONLY, SEC_COMMIT, file ); + if (status != STATUS_SUCCESS) return status; + + offset.QuadPart = 0; + count = 0; + base = NULL; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &base, 0, 0, &offset, + &count, ViewShare, 0, PAGE_READONLY ); + NtClose( mapping ); + if (status != STATUS_SUCCESS) return status; + + status = NtQueryInformationFile( file, &io, &info, sizeof(info), FileEndOfFileInformation ); + if (status == STATUS_SUCCESS) + { + if (info.EndOfFile.QuadPart > INT_MAX) + { + /* the config file seems to be malformed, ignore */ + status = STATUS_SXS_ASSEMBLY_NOT_FOUND; + goto done; + } + buffer_len = info.EndOfFile.QuadPart; + unicode_tests = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE; + if (RtlIsTextUnicode( base, buffer_len, &unicode_tests )) + { + xmlbuf.ptr = base; + xmlbuf.end = xmlbuf.ptr + buffer_len / sizeof(WCHAR); + status = parse_config_file( &xmlbuf, private_path ); + } + else if (unicode_tests & IS_TEXT_UNICODE_REVERSE_SIGNATURE) + { + const WCHAR *buf = base; + WCHAR *new_buff; + unsigned int i; + + if (!(new_buff = RtlAllocateHeap( GetProcessHeap(), 0, buffer_len ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + for (i = 0; i < buffer_len / sizeof(WCHAR); i++) + new_buff[i] = RtlUshortByteSwap( buf[i] ); + xmlbuf.ptr = new_buff; + xmlbuf.end = xmlbuf.ptr + buffer_len / sizeof(WCHAR); + status = parse_config_file( &xmlbuf, private_path ); + RtlFreeHeap( GetProcessHeap(), 0, new_buff ); + } + else + { + DWORD len; + WCHAR *new_buff; + + /* let's assume utf-8 for now */ + RtlUTF8ToUnicodeN( NULL, 0, &len, base, buffer_len ); + if (!(new_buff = RtlAllocateHeap( GetProcessHeap(), 0, len ))) + { + status = STATUS_NO_MEMORY; + goto done; + } + RtlUTF8ToUnicodeN( new_buff, len, &len, base, buffer_len ); + xmlbuf.ptr = new_buff; + xmlbuf.end = xmlbuf.ptr + len / sizeof(WCHAR); + status = parse_config_file( &xmlbuf, private_path ); + RtlFreeHeap( GetProcessHeap(), 0, new_buff ); + } + } + + done: + NtUnmapViewOfSection( GetCurrentProcess(), base ); + return status; +} + + static NTSTATUS open_manifest_file( struct actctx_loader *acl, struct assembly_identity *ai, const WCHAR *lang, const WCHAR *directory, WCHAR *buffer, DWORD len ) { @@ -3368,6 +3570,92 @@ done: return status; } +static NTSTATUS probe_private_path( struct actctx_loader *acl, struct assembly_identity *ai ) +{ + DWORD total; + DWORD len; + HANDLE file; + UNICODE_STRING nameW; + WCHAR *private_path = NULL; + WCHAR *directory = NULL; + WCHAR *buffer = NULL; + WCHAR *path = NULL; + const WCHAR *lang = ai->language; + NTSTATUS status; + + if (acl->actctx->config.type != ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE) + return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + nameW.Buffer = NULL; + if (!RtlDosPathNameToNtPathName_U( acl->actctx->config.info, &nameW, NULL, NULL )) + return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + status = open_nt_file( &file, &nameW ); + RtlFreeUnicodeString( &nameW ); + if (status) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + status = open_config_file( acl->actctx->config.info, file, &private_path ); + NtClose( file ); + + if ( status != STATUS_SUCCESS ) + { + RtlFreeHeap( GetProcessHeap(), 0, private_path ); + return status; + } + + if (!private_path) return STATUS_SXS_ASSEMBLY_NOT_FOUND; + + TRACE( "got privatePath %s from %s\n", debugstr_w( private_path ), + debugstr_w( acl->actctx->config.info ) ); + + if (!lang || !wcsicmp( lang, L"neutral" ) || !wcscmp( lang, L"*")) lang = L""; + + len = wcslen( acl->actctx->appdir.info ); + total = len + 2 * wcslen( private_path ) + 2 * wcslen( ai->name ) + wcslen( lang ) + 12; + + if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, total * sizeof(WCHAR) ))) + { + RtlFreeHeap( GetProcessHeap(), 0, private_path ); + return STATUS_NO_MEMORY; + } + + if (!(directory = build_assembly_dir( ai ))) + { + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + RtlFreeHeap( GetProcessHeap(), 0, private_path ); + return STATUS_NO_MEMORY; + } + + status = STATUS_SXS_ASSEMBLY_NOT_FOUND; + path = private_path; + + while (*path) + { + WCHAR *p = NULL; + WCHAR *end = wcschr( path, ';' ); + SIZE_T path_len = end ? end - path : wcslen( path ); + if (path_len) + { + memcpy( buffer, acl->actctx->appdir.info, len * sizeof(WCHAR) ); + p = buffer + len; + memcpy ( p, path, path_len * sizeof(WCHAR) ); + p += path_len; + if (p > buffer && p[-1] != '\\') *p++ = '\\'; + *p = 0; + status = open_manifest_file( acl, ai, lang, directory, buffer, total ); + if (status != STATUX_SXS_ASSEMBLY_NOT_FOUND) break; + if (!end) break; + path = end + 1; + } + } + + RtlFreeHeap( GetProcessHeap(), 0, buffer); + RtlFreeHeap( GetProcessHeap(), 0, directory ); + RtlFreeHeap( GetProcessHeap(), 0, private_path ); + return status; +} + + static NTSTATUS lookup_assembly(struct actctx_loader* acl, struct assembly_identity* ai) { @@ -3396,6 +3684,13 @@ static NTSTATUS lookup_assembly(struct actctx_loader* acl, return STATUS_NO_MEMORY; } + /* Lookup in <dir>\<privatePath>\name.dll + * <dir>\<privatePath>\name.manifest + * + * If the module is a mixed CLR module + */ + + /* Lookup in <dir>\name.dll * <dir>\name.manifest * <dir>\name\name.dll @@ -5292,6 +5587,10 @@ NTSTATUS WINAPI RtlCreateActivationContext( ACTIVATION_CONTEXT **new_actctx, con const ACTCTXW *pActCtx = ptr; /* FIXME: not the right structure */ const WCHAR *directory = NULL; ACTIVATION_CONTEXT *actctx; + HANDLE config_file = 0; + HMODULE config_module; + UNICODE_STRING config_name; + UNICODE_STRING config_nameW; UNICODE_STRING nameW; ULONG lang = 0; NTSTATUS status = STATUS_NO_MEMORY; @@ -5322,6 +5621,22 @@ NTSTATUS WINAPI RtlCreateActivationContext( ACTIVATION_CONTEXT **new_actctx, con actctx->ref_count = 1; actctx->config.type = ACTIVATION_CONTEXT_PATH_TYPE_NONE; actctx->config.info = NULL; + config_module = NtCurrentTeb()->Peb->ImageBaseAddress; + if (status = get_module_filename( config_module, &config_name, sizeof(L".config") )) + goto error; + wcscat( config_name.Buffer, L".config" ); + config_nameW.Buffer = NULL; + if (RtlDosPathNameToNtPathName_U( config_name.Buffer, &config_nameW, NULL, NULL )) { + if (!open_nt_file( &config_file, &config_nameW )) { + actctx->config.info = config_name.Buffer; + actctx->config.type = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE; + NtClose(config_file); + } else + RtlFreeUnicodeString(&config_name); + } else + RtlFreeUnicodeString(&config_name); + RtlFreeUnicodeString(&config_nameW); + actctx->appdir.type = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE; if (pActCtx->dwFlags & ACTCTX_FLAG_APPLICATION_NAME_VALID) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10753