From: "Alex Xu (Hello71)" Subject: [PATCH v3 3/5] setupapi: Use IOCTL_COPYCHUNK, avoid buffering whole file Message-Id: <20220127215056.243525-4-alex_y_xu@yahoo.ca> Date: Thu, 27 Jan 2022 16:50:54 -0500 In-Reply-To: <20220127215056.243525-1-alex_y_xu@yahoo.ca> References: <20220127215056.243525-1-alex_y_xu@yahoo.ca> --- dlls/setupapi/fakedll.c | 198 ++++++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 90 deletions(-) diff --git a/dlls/setupapi/fakedll.c b/dlls/setupapi/fakedll.c index a4b8df4dfb4..85c6f2500d8 100644 --- a/dlls/setupapi/fakedll.c +++ b/dlls/setupapi/fakedll.c @@ -19,8 +19,6 @@ */ #include -#include -#include #include #define COBJMACROS @@ -32,6 +30,7 @@ #include "winbase.h" #include "winuser.h" #include "winnt.h" +#include "winioctl.h" #include "winternl.h" #include "wine/debug.h" #include "wine/list.h" @@ -64,8 +63,6 @@ static const unsigned int file_alignment = 512; static const unsigned int section_alignment = 4096; static const unsigned int max_dll_name_len = 64; -static void *file_buffer; -static SIZE_T file_buffer_size; static unsigned int handled_count; static unsigned int handled_total; static WCHAR **handled_dlls; @@ -169,10 +166,11 @@ static int is_valid_ptr( const void *data, SIZE_T size, const void *ptr, SIZE_T } /* extract the 16-bit NE dll from a PE builtin */ -static void extract_16bit_image( IMAGE_NT_HEADERS *nt, void **data, SIZE_T *size ) +static void extract_16bit_image( void **data, SIZE_T *size ) { DWORD exp_size, *size_ptr; - IMAGE_DOS_HEADER *dos; + IMAGE_DOS_HEADER *dos = *data; + IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((char *)*data + dos->e_lfanew); IMAGE_EXPORT_DIRECTORY *exports; IMAGE_SECTION_HEADER *section = NULL; WORD *ordinals; @@ -205,60 +203,81 @@ static void extract_16bit_image( IMAGE_NT_HEADERS *nt, void **data, SIZE_T *size } } -/* read in the contents of a file into the global file buffer */ +/* open and check a fake dll file */ /* return 1 on success, 0 on nonexistent file, -1 on other error */ -static int read_file( const WCHAR *name, void **data, SIZE_T *size ) +static int read_file( const WCHAR *name, HANDLE *h ) { - struct stat st; - int fd, ret = -1; - size_t header_size; + char file_buffer[4096]; + DWORD bytes_read; IMAGE_DOS_HEADER *dos; IMAGE_NT_HEADERS *nt; - const size_t min_size = sizeof(*dos) + 32 + - FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader.MajorLinkerVersion ); - if ((fd = _wopen( name, O_RDONLY | O_BINARY )) == -1) return 0; - if (fstat( fd, &st ) == -1) goto done; - *size = st.st_size; - if (!file_buffer || st.st_size > file_buffer_size) - { - VirtualFree( file_buffer, 0, MEM_RELEASE ); - file_buffer = NULL; - file_buffer_size = st.st_size; - if (NtAllocateVirtualMemory( GetCurrentProcess(), &file_buffer, 0, &file_buffer_size, - MEM_COMMIT, PAGE_READWRITE )) goto done; - } + *h = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + if (*h == INVALID_HANDLE_VALUE) return 0; - /* check for valid fake dll file */ + if (!ReadFile( *h, file_buffer, sizeof(file_buffer), &bytes_read, NULL )) + return -1; + if (bytes_read < sizeof(dos)) return -1; - if (st.st_size < min_size) goto done; - header_size = min( st.st_size, 4096 ); - if (read( fd, file_buffer, header_size ) != header_size) goto done; - dos = file_buffer; - if (dos->e_magic != IMAGE_DOS_SIGNATURE) goto done; - if (dos->e_lfanew < sizeof(*dos) + 32) goto done; + dos = (IMAGE_DOS_HEADER *)file_buffer; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) return -1; + if (dos->e_lfanew < sizeof(dos) + 32) return -1; if (memcmp( dos + 1, builtin_signature, strlen(builtin_signature) + 1 ) && - memcmp( dos + 1, fakedll_signature, strlen(fakedll_signature) + 1 )) goto done; - if (dos->e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader.MajorLinkerVersion) > header_size) - goto done; - nt = (IMAGE_NT_HEADERS *)((char *)file_buffer + dos->e_lfanew); + memcmp( dos + 1, fakedll_signature, strlen(fakedll_signature) + 1 )) return -1; + if (dos->e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader.MajorLinkerVersion) > bytes_read) + return -1; + nt = (IMAGE_NT_HEADERS *)(file_buffer + dos->e_lfanew); if (nt->Signature == IMAGE_NT_SIGNATURE && nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) - { /* wrong 32/64 type, pretend it doesn't exist */ - ret = 0; - goto done; - } - if (st.st_size == header_size || - read( fd, (char *)file_buffer + header_size, - st.st_size - header_size ) == st.st_size - header_size) + return 0; + return 1; +} + +static BOOL write_fake_dll( const WCHAR *src_name, HANDLE src, HANDLE dest ) +{ + BOOL ret = FALSE; + + if (lstrlenW(src_name) > 2 && !wcscmp( src_name + lstrlenW(src_name) - 2, L"16" )) { - *data = file_buffer; - if (lstrlenW(name) > 2 && !wcscmp( name + lstrlenW(name) - 2, L"16" )) - extract_16bit_image( nt, data, size ); - ret = 1; + LARGE_INTEGER file_size; + HANDLE src_map; + if (!GetFileSizeEx( src, &file_size )) return FALSE; + src_map = CreateFileMappingA( src, NULL, PAGE_WRITECOPY, 0, 0, NULL ); + if (src_map) + { + void *src_view = MapViewOfFile( src_map, FILE_MAP_COPY, 0, 0, 0 ); + if (src_view) + { + DWORD bytes_written; + void *data = src_view; + SIZE_T size = file_size.QuadPart; + extract_16bit_image( &data, &size ); + ret = WriteFile( dest, data, size, &bytes_written, NULL ) && bytes_written == size; + UnmapViewOfFile( src_view ); + } + CloseHandle( src_map ); + } + } + else + { + /* can't call CopyFileW here because files are open with no share mode */ + /* wine extension: IOCTL_COPYCHUNK works even on local files */ + SRV_COPYCHUNK_COPY copychunk = { + .ChunkCount = 1, + .Chunk = { [0] = { .Length = 1UL<<30 } } + }; + SRV_COPYCHUNK_RESPONSE copychunk_resp; + DWORD count; + ret = DeviceIoControl( src, IOCTL_PREPARE_COPYCHUNK, NULL, 0, ©chunk.SourceFile, + sizeof(copychunk.SourceFile), &count, NULL ); + if (!ret) return FALSE; + do { + ret = DeviceIoControl( dest, IOCTL_COPYCHUNK, ©chunk, sizeof(copychunk), + ©chunk_resp, sizeof(copychunk_resp), &count, NULL ); + copychunk.Chunk[0].SourceOffset.QuadPart += copychunk_resp.TotalBytesWritten; + copychunk.Chunk[0].DestinationOffset.QuadPart += copychunk_resp.TotalBytesWritten; + } while (ret && copychunk_resp.TotalBytesWritten); } -done: - close( fd ); return ret; } @@ -415,12 +434,11 @@ static const WCHAR *enum_load_path( unsigned int idx ) } /* try to load a pre-compiled fake dll */ -static void *load_fake_dll( const WCHAR *name, SIZE_T *size ) +static int load_fake_dll( const WCHAR *name, HANDLE *h ) { const WCHAR *build_dir = _wgetenv( L"WINEBUILDDIR" ); const WCHAR *path; WCHAR *file, *ptr; - void *data = NULL; unsigned int i, pos, len, namelen, maxlen = 0; WCHAR *p; int res = 0; @@ -433,7 +451,7 @@ static void *load_fake_dll( const WCHAR *name, SIZE_T *size ) while ((path = enum_load_path( i++ ))) maxlen = max( maxlen, lstrlenW(path) ); maxlen += ARRAY_SIZE(pe_dir) + len + 1; - if (!(file = HeapAlloc( GetProcessHeap(), 0, maxlen * sizeof(WCHAR) ))) return NULL; + if (!(file = HeapAlloc( GetProcessHeap(), 0, maxlen * sizeof(WCHAR) ))) return -1; pos = maxlen - len - 1; lstrcpyW( file + pos, name ); @@ -449,7 +467,7 @@ static void *load_fake_dll( const WCHAR *name, SIZE_T *size ) ptr = prepend( ptr, ptr, namelen ); ptr = prepend( ptr, L"\\dlls", 5 ); ptr = prepend( ptr, build_dir, lstrlenW(build_dir) ); - if ((res = read_file( ptr, &data, size ))) goto done; + if ((res = read_file( ptr, h ))) goto done; /* now as a program */ ptr = file + pos; @@ -459,7 +477,7 @@ static void *load_fake_dll( const WCHAR *name, SIZE_T *size ) ptr = prepend( ptr, ptr, namelen ); ptr = prepend( ptr, L"\\programs", 9 ); ptr = prepend( ptr, build_dir, lstrlenW(build_dir) ); - if ((res = read_file( ptr, &data, size ))) goto done; + if ((res = read_file( ptr, h ))) goto done; } file[pos + len + 1] = 0; @@ -467,15 +485,14 @@ static void *load_fake_dll( const WCHAR *name, SIZE_T *size ) { ptr = prepend( file + pos, pe_dir, lstrlenW(pe_dir) ); ptr = prepend( ptr, path, lstrlenW(path) ); - if ((res = read_file( ptr, &data, size ))) break; + if ((res = read_file( ptr, h ))) break; ptr = prepend( file + pos, path, lstrlenW(path) ); - if ((res = read_file( ptr, &data, size ))) break; + if ((res = read_file( ptr, h ))) break; } done: HeapFree( GetProcessHeap(), 0, file ); - if (res == 1) return data; - return NULL; + return res; } /* create the fake dll destination file */ @@ -505,7 +522,7 @@ static HANDLE create_dest_file( const WCHAR *name, BOOL delete ) { if (GetLastError() == ERROR_PATH_NOT_FOUND) create_directories( name ); - h = CreateFileW( name, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL ); + h = CreateFileW( name, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL ); if (h == INVALID_HANDLE_VALUE) ERR( "failed to create %s (error=%u)\n", debugstr_w(name), GetLastError() ); } @@ -838,23 +855,30 @@ static BOOL CALLBACK register_resource( HMODULE module, LPCWSTR type, LPWSTR nam return TRUE; } -static void register_fake_dll( const WCHAR *name, const void *data, size_t size, struct list *delay_copy ) +static void register_fake_dll( const WCHAR *name, HANDLE h, struct list *delay_copy ) { const IMAGE_RESOURCE_DIRECTORY *resdir; LDR_RESOURCE_INFO info; HRESULT hr = S_OK; - HMODULE module = (HMODULE)((ULONG_PTR)data | 1); struct dll_data dll_data = { delay_copy, name, 0 }; WCHAR buffer[MAX_PATH]; const WCHAR *p; + HANDLE mapping; + HMODULE module; if (!(p = wcsrchr( name, '\\' ))) p = name; else p++; dll_data.src_len = p - name; + + mapping = CreateFileMappingA( h, NULL, PAGE_READONLY, 0, 0, NULL ); + if (!mapping) return; + module = (HMODULE)((ULONG_PTR)MapViewOfFile( mapping, FILE_MAP_COPY, 0, 0, 0 ) | 1); + if (!module) goto out; + EnumResourceNamesW( module, (WCHAR*)RT_MANIFEST, register_manifest, (LONG_PTR)&dll_data ); info.Type = (ULONG_PTR)L"WINE_REGISTRY"; - if (LdrFindResourceDirectory_U( module, &info, 1, &resdir )) return; + if (LdrFindResourceDirectory_U( module, &info, 1, &resdir )) goto out; if (!registrar) { @@ -869,7 +893,7 @@ static void register_fake_dll( const WCHAR *name, const void *data, size_t size, if (!registrar) { ERR( "failed to create IRegistrar: %x\n", hr ); - return; + goto out; } } @@ -880,22 +904,24 @@ static void register_fake_dll( const WCHAR *name, const void *data, size_t size, IRegistrar_AddReplacement( registrar, L"SystemRoot", buffer ); EnumResourceNamesW( module, L"WINE_REGISTRY", register_resource, (LONG_PTR)&hr ); if (FAILED(hr)) ERR( "failed to register %s: %x\n", debugstr_w(name), hr ); +out: + if (module) UnmapViewOfFile(module); + CloseHandle( mapping ); } /* copy a fake dll file to the dest directory */ static int install_fake_dll( WCHAR *dest, WCHAR *file, BOOL delete, struct list *delay_copy ) { int ret; - SIZE_T size; - void *data; - DWORD written; WCHAR *destname = dest + lstrlenW(dest); WCHAR *name = wcsrchr( file, '\\' ) + 1; WCHAR *end = name + lstrlenW(name); SIZE_T len = end - name; + HANDLE hsrc = INVALID_HANDLE_VALUE; - if (!(ret = read_file( file, &data, &size ))) + if (!(ret = read_file( file, &hsrc ))) { + if (hsrc != INVALID_HANDLE_VALUE) CloseHandle( hsrc ); *end = 0; return 0; } @@ -913,13 +939,14 @@ static int install_fake_dll( WCHAR *dest, WCHAR *file, BOOL delete, struct list { TRACE( "%s -> %s\n", debugstr_w(file), debugstr_w(dest) ); - ret = (WriteFile( h, data, size, &written, NULL ) && written == size); + ret = write_fake_dll( file, hsrc, h ); if (!ret) ERR( "failed to write to %s (error=%u)\n", debugstr_w(dest), GetLastError() ); - CloseHandle( h ); - if (ret) register_fake_dll( dest, data, size, delay_copy ); + if (ret) register_fake_dll( dest, h, delay_copy ); else DeleteFileW( dest ); + CloseHandle( h ); } } + if (hsrc != INVALID_HANDLE_VALUE) CloseHandle( hsrc ); *destname = 0; /* restore it for next file */ *end = 0; return ret; @@ -928,30 +955,25 @@ static int install_fake_dll( WCHAR *dest, WCHAR *file, BOOL delete, struct list static void delay_copy_files( struct list *delay_copy ) { struct delay_copy *copy, *next; - DWORD written; - SIZE_T size; - void *data; - HANDLE h; int ret; LIST_FOR_EACH_ENTRY_SAFE( copy, next, delay_copy, struct delay_copy, entry ) { + HANDLE h, h2; list_remove( ©->entry ); - ret = read_file( copy->src, &data, &size ); - if (ret != 1) - { - HeapFree( GetProcessHeap(), 0, copy ); - continue; - } + ret = read_file( copy->src, &h2 ); + if (ret != 1) goto next; h = create_dest_file( copy->dest, FALSE ); if (h && h != INVALID_HANDLE_VALUE) { - ret = (WriteFile( h, data, size, &written, NULL ) && written == size); + ret = write_fake_dll( copy->src, h2, h ); if (!ret) ERR( "failed to write to %s (error=%u)\n", debugstr_w(copy->dest), GetLastError() ); CloseHandle( h ); if (!ret) DeleteFileW( copy->dest ); } +next: + if (h2 != INVALID_HANDLE_VALUE) CloseHandle( h2 ); HeapFree( GetProcessHeap(), 0, copy ); } } @@ -1041,11 +1063,9 @@ static BOOL create_wildcard_dlls( const WCHAR *dirname, const WCHAR *wildcard, B BOOL create_fake_dll( const WCHAR *name, const WCHAR *source ) { struct list delay_copy = LIST_INIT( delay_copy ); - HANDLE h; + HANDLE h, h2; BOOL ret; - SIZE_T size; const WCHAR *filename; - void *buffer; BOOL delete = !wcscmp( source, L"-" ); /* '-' source means delete the file */ if (!(filename = wcsrchr( name, '\\' ))) filename = name; @@ -1064,12 +1084,10 @@ BOOL create_fake_dll( const WCHAR *name, const WCHAR *source ) if (!(h = create_dest_file( name, delete ))) return TRUE; /* not a fake dll */ if (h == INVALID_HANDLE_VALUE) return FALSE; - if ((buffer = load_fake_dll( source, &size ))) + if (load_fake_dll( source, &h2 )) { - DWORD written; - - ret = (WriteFile( h, buffer, size, &written, NULL ) && written == size); - if (ret) register_fake_dll( name, buffer, size, &delay_copy ); + ret = write_fake_dll( source, h2, h ); + if (ret) register_fake_dll( name, h, &delay_copy ); else ERR( "failed to write to %s (error=%u)\n", debugstr_w(name), GetLastError() ); } else @@ -1078,7 +1096,9 @@ BOOL create_fake_dll( const WCHAR *name, const WCHAR *source ) ret = build_fake_dll( h, name ); } + if (h2 != INVALID_HANDLE_VALUE) CloseHandle( h2 ); CloseHandle( h ); + if (!ret) DeleteFileW( name ); delay_copy_files( &delay_copy ); @@ -1091,8 +1111,6 @@ BOOL create_fake_dll( const WCHAR *name, const WCHAR *source ) */ void cleanup_fake_dlls(void) { - if (file_buffer) VirtualFree( file_buffer, 0, MEM_RELEASE ); - file_buffer = NULL; HeapFree( GetProcessHeap(), 0, handled_dlls ); handled_dlls = NULL; handled_count = handled_total = 0; -- 2.35.0