From e8a7246e8bbc0f890626ec5305500b7ed2a63876 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 25 Aug 2011 15:33:41 +0200 Subject: [PATCH] setupapi: Add support for creating WinSxS manifest files from fake dll resources. --- dlls/setupapi/fakedll.c | 282 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 1 deletion(-) diff --git a/dlls/setupapi/fakedll.c b/dlls/setupapi/fakedll.c index 0c39935bfc..e21507b7fc 100644 --- a/dlls/setupapi/fakedll.c +++ b/dlls/setupapi/fakedll.c @@ -1,7 +1,7 @@ /* * Creation of Wine fake dlls for apps that access the dll file directly. * - * Copyright 2006 Alexandre Julliard + * Copyright 2006, 2011 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -470,6 +470,278 @@ static HANDLE create_dest_file( const WCHAR *name ) return h; } +/* XML parsing code copied from ntdll */ + +typedef struct +{ + const char *ptr; + unsigned int len; +} xmlstr_t; + +typedef struct +{ + const char *ptr; + const char *end; +} xmlbuf_t; + +static inline BOOL xmlstr_cmp(const xmlstr_t* xmlstr, const char *str) +{ + return !strncmp(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; +} + +static inline BOOL isxmlspace( char ch ) +{ + return (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'); +} + +static BOOL next_xml_elem( xmlbuf_t *xmlbuf, xmlstr_t *elem ) +{ + const char *ptr; + + for (;;) + { + ptr = memchr(xmlbuf->ptr, '<', xmlbuf->end - xmlbuf->ptr); + if (!ptr) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + ptr++; + if (ptr + 3 < xmlbuf->end && ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') /* skip comment */ + { + for (ptr += 3; ptr + 3 <= xmlbuf->end; ptr++) + if (ptr[0] == '-' && ptr[1] == '-' && ptr[2] == '>') break; + + if (ptr + 3 > xmlbuf->end) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + xmlbuf->ptr = ptr + 3; + } + else break; + } + + xmlbuf->ptr = ptr; + while (ptr < xmlbuf->end && !isxmlspace(*ptr) && *ptr != '>' && (*ptr != '/' || ptr == xmlbuf->ptr)) + ptr++; + + elem->ptr = xmlbuf->ptr; + elem->len = ptr - xmlbuf->ptr; + xmlbuf->ptr = ptr; + return xmlbuf->ptr != xmlbuf->end; +} + +static BOOL next_xml_attr(xmlbuf_t* xmlbuf, xmlstr_t* name, xmlstr_t* value, + BOOL* error, BOOL* end) +{ + const char *ptr; + + *error = TRUE; + + while (xmlbuf->ptr < xmlbuf->end && isxmlspace(*xmlbuf->ptr)) + xmlbuf->ptr++; + + if (xmlbuf->ptr == xmlbuf->end) return FALSE; + + if (*xmlbuf->ptr == '/') + { + xmlbuf->ptr++; + if (xmlbuf->ptr == xmlbuf->end || *xmlbuf->ptr != '>') + return FALSE; + + xmlbuf->ptr++; + *end = TRUE; + *error = FALSE; + return FALSE; + } + + if (*xmlbuf->ptr == '>') + { + xmlbuf->ptr++; + *error = FALSE; + return FALSE; + } + + ptr = xmlbuf->ptr; + while (ptr < xmlbuf->end && *ptr != '=' && *ptr != '>' && !isxmlspace(*ptr)) ptr++; + + if (ptr == xmlbuf->end || *ptr != '=') return FALSE; + + name->ptr = xmlbuf->ptr; + name->len = ptr-xmlbuf->ptr; + xmlbuf->ptr = ptr; + + ptr++; + if (ptr == xmlbuf->end || (*ptr != '"' && *ptr != '\'')) return FALSE; + + value->ptr = ++ptr; + if (ptr == xmlbuf->end) return FALSE; + + ptr = memchr(ptr, ptr[-1], xmlbuf->end - ptr); + if (!ptr) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + + value->len = ptr - value->ptr; + xmlbuf->ptr = ptr + 1; + + if (xmlbuf->ptr == xmlbuf->end) return FALSE; + + *error = FALSE; + return TRUE; +} + +static void get_manifest_filename( const xmlstr_t *arch, const xmlstr_t *name, const xmlstr_t *key, + const xmlstr_t *version, WCHAR *buffer, DWORD size ) +{ + static const WCHAR trailerW[] = {'_','n','o','n','e','_','d','e','a','d','b','e','e','f',0}; + DWORD pos; + + pos = MultiByteToWideChar( CP_UTF8, 0, arch->ptr, arch->len, buffer, size ); + buffer[pos++] = '_'; + pos += MultiByteToWideChar( CP_UTF8, 0, name->ptr, name->len, buffer + pos, size - pos ); + buffer[pos++] = '_'; + pos += MultiByteToWideChar( CP_UTF8, 0, key->ptr, key->len, buffer + pos, size - pos ); + buffer[pos++] = '_'; + pos += MultiByteToWideChar( CP_UTF8, 0, version->ptr, version->len, buffer + pos, size - pos ); + memcpy( buffer + pos, trailerW, sizeof(trailerW) ); + strlwrW( buffer ); +} + +static BOOL create_winsxs_dll( const WCHAR *dll_name, const xmlstr_t *arch, const xmlstr_t *name, + const xmlstr_t *key, const xmlstr_t *version, + const void *dll_data, size_t dll_size ) +{ + static const WCHAR winsxsW[] = {'w','i','n','s','x','s','\\'}; + WCHAR *path; + const WCHAR *filename; + DWORD pos, written, path_len; + HANDLE handle; + BOOL ret = FALSE; + + if (!(filename = strrchrW( dll_name, '\\' ))) filename = dll_name; + else filename++; + + path_len = GetWindowsDirectoryW( NULL, 0 ) + 1 + sizeof(winsxsW)/sizeof(WCHAR) + + arch->len + name->len + key->len + version->len + 18 + strlenW( filename ) + 1; + + path = HeapAlloc( GetProcessHeap(), 0, path_len * sizeof(WCHAR) ); + pos = GetWindowsDirectoryW( path, path_len ); + path[pos++] = '\\'; + memcpy( path + pos, winsxsW, sizeof(winsxsW) ); + pos += sizeof(winsxsW) / sizeof(WCHAR); + get_manifest_filename( arch, name, key, version, path + pos, path_len - pos ); + pos += strlenW( path + pos ); + path[pos++] = '\\'; + strcpyW( path + pos, filename ); + handle = create_dest_file( path ); + if (handle && handle != INVALID_HANDLE_VALUE) + { + TRACE( "creating %s\n", debugstr_w(path) ); + ret = (WriteFile( handle, dll_data, dll_size, &written, NULL ) && written == dll_size); + if (!ret) ERR( "failed to write to %s (error=%u)\n", debugstr_w(path), GetLastError() ); + CloseHandle( handle ); + if (!ret) DeleteFileW( path ); + } + HeapFree( GetProcessHeap(), 0, path ); + return ret; +} + +static BOOL create_manifest( const xmlstr_t *arch, const xmlstr_t *name, const xmlstr_t *key, + const xmlstr_t *version, const void *data, DWORD len ) +{ + static const WCHAR winsxsW[] = {'w','i','n','s','x','s','\\','m','a','n','i','f','e','s','t','s','\\'}; + static const WCHAR extensionW[] = {'.','m','a','n','i','f','e','s','t',0}; + WCHAR *path; + DWORD pos, written, path_len; + HANDLE handle; + BOOL ret = FALSE; + + path_len = GetWindowsDirectoryW( NULL, 0 ) + 1 + sizeof(winsxsW)/sizeof(WCHAR) + + arch->len + name->len + key->len + version->len + 18 + sizeof(extensionW)/sizeof(WCHAR); + + path = HeapAlloc( GetProcessHeap(), 0, path_len * sizeof(WCHAR) ); + pos = GetWindowsDirectoryW( path, MAX_PATH ); + path[pos++] = '\\'; + memcpy( path + pos, winsxsW, sizeof(winsxsW) ); + pos += sizeof(winsxsW) / sizeof(WCHAR); + get_manifest_filename( arch, name, key, version, path + pos, MAX_PATH - pos ); + strcatW( path + pos, extensionW ); + handle = CreateFileW( path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); + if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND) + { + create_directories( path ); + handle = CreateFileW( path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL ); + } + + if (handle != INVALID_HANDLE_VALUE) + { + TRACE( "creating %s\n", debugstr_w(path) ); + ret = (WriteFile( handle, data, len, &written, NULL ) && written == len); + if (!ret) ERR( "failed to write to %s (error=%u)\n", debugstr_w(path), GetLastError() ); + CloseHandle( handle ); + if (!ret) DeleteFileW( path ); + } + HeapFree( GetProcessHeap(), 0, path ); + return ret; +} + +static void register_manifest( const WCHAR *dll_name, const char *manifest, DWORD len, + const void *dll_data, size_t dll_size ) +{ +#ifdef __i386__ + static const char current_arch[] = "x86"; +#elif defined __x86_64__ + static const char current_arch[] = "amd64"; +#else + static const char current_arch[] = "none"; +#endif + xmlbuf_t buffer; + xmlstr_t elem, attr_name, attr_value; + xmlstr_t name, version, arch, key; + BOOL end = FALSE, error; + + buffer.ptr = manifest; + buffer.end = manifest + len; + name.ptr = version.ptr = arch.ptr = key.ptr = NULL; + name.len = version.len = arch.len = key.len = 0; + + while (next_xml_elem( &buffer, &elem )) + { + if (!xmlstr_cmp( &elem, "assemblyIdentity" )) continue; + while (next_xml_attr( &buffer, &attr_name, &attr_value, &error, &end )) + { + if (xmlstr_cmp(&attr_name, "name")) name = attr_value; + else if (xmlstr_cmp(&attr_name, "version")) version = attr_value; + else if (xmlstr_cmp(&attr_name, "processorArchitecture")) arch = attr_value; + else if (xmlstr_cmp(&attr_name, "publicKeyToken")) key = attr_value; + } + if (!error && name.ptr && version.ptr && arch.ptr && key.ptr) + { + if (!arch.len) /* fixup the architecture */ + { + char *new_buffer = HeapAlloc( GetProcessHeap(), 0, len + sizeof(current_arch) ); + memcpy( new_buffer, manifest, arch.ptr - manifest ); + strcpy( new_buffer + (arch.ptr - manifest), current_arch ); + memcpy( new_buffer + strlen(new_buffer), arch.ptr, len - (arch.ptr - manifest) ); + arch.ptr = current_arch; + arch.len = strlen( current_arch ); + if (create_winsxs_dll( dll_name, &arch, &name, &key, &version, dll_data, dll_size )) + create_manifest( &arch, &name, &key, &version, new_buffer, len + arch.len ); + HeapFree( GetProcessHeap(), 0, new_buffer ); + } + else + { + if (create_winsxs_dll( dll_name, &arch, &name, &key, &version, dll_data, dll_size )) + create_manifest( &arch, &name, &key, &version, manifest, len ); + } + } + } +} + static BOOL CALLBACK register_resource( HMODULE module, LPCWSTR type, LPWSTR name, LONG_PTR arg ) { HRESULT *hr = (HRESULT *)arg; @@ -493,10 +765,18 @@ static void register_fake_dll( const WCHAR *name, const void *data, size_t size static const WCHAR atlW[] = {'a','t','l','.','d','l','l',0}; static const WCHAR moduleW[] = {'M','O','D','U','L','E',0}; static const WCHAR regtypeW[] = {'W','I','N','E','_','R','E','G','I','S','T','R','Y',0}; + static const WCHAR manifestW[] = {'W','I','N','E','_','M','A','N','I','F','E','S','T',0}; const IMAGE_RESOURCE_DIRECTORY *resdir; LDR_RESOURCE_INFO info; HRESULT hr = S_OK; HMODULE module = (HMODULE)((ULONG_PTR)data | 1); + HRSRC rsrc; + + if ((rsrc = FindResourceW( module, manifestW, MAKEINTRESOURCEW(RT_MANIFEST) ))) + { + char *manifest = LoadResource( module, rsrc ); + register_manifest( name, manifest, SizeofResource( module, rsrc ), data, size ); + } info.Type = (ULONG_PTR)regtypeW; if (LdrFindResourceDirectory_U( module, &info, 1, &resdir )) return;