[PATCH v3 3/5] setupapi: Use IOCTL_COPYCHUNK, avoid buffering whole file
Alex Xu (Hello71)
alex_y_xu at yahoo.ca
Mon Aug 23 10:56:51 CDT 2021
---
dlls/setupapi/fakedll.c | 202 ++++++++++++++++++++++------------------
1 file changed, 110 insertions(+), 92 deletions(-)
diff --git a/dlls/setupapi/fakedll.c b/dlls/setupapi/fakedll.c
index 203d7c393ce..c17a1c7e2c5 100644
--- a/dlls/setupapi/fakedll.c
+++ b/dlls/setupapi/fakedll.c
@@ -19,8 +19,6 @@
*/
#include <stdarg.h>
-#include <fcntl.h>
-#include <sys/stat.h>
#include <unistd.h>
#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;
+ /* 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);
+ }
+ else
+ {
+ 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 );
+ }
}
-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 + ARRAY_SIZE(L".fake");
- if (!(file = HeapAlloc( GetProcessHeap(), 0, maxlen * sizeof(WCHAR) ))) return NULL;
+ if (!(file = HeapAlloc( GetProcessHeap(), 0, maxlen * sizeof(WCHAR) ))) return -1;
pos = maxlen - len - ARRAY_SIZE(L".fake");
lstrcpyW( file + pos, name );
@@ -449,9 +467,9 @@ 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;
lstrcpyW( file + pos + len + 1, L".fake" );
- if ((res = read_file( ptr, &data, size ))) goto done;
+ if ((res = read_file( ptr, h ))) goto done;
/* now as a program */
ptr = file + pos;
@@ -461,9 +479,9 @@ 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;
lstrcpyW( file + pos + len + 1, L".fake" );
- if ((res = read_file( ptr, &data, size ))) goto done;
+ if ((res = read_file( ptr, h ))) goto done;
}
file[pos + len + 1] = 0;
@@ -471,15 +489,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 */
@@ -509,7 +526,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() );
}
@@ -842,23 +859,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)
{
@@ -873,7 +897,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;
}
}
@@ -884,23 +908,25 @@ 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, const WCHAR *ext, 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 (ext) lstrcpyW( end, ext );
- if (!(ret = read_file( file, &data, &size )))
+ if (!(ret = read_file( file, &hsrc )))
{
+ if (hsrc != INVALID_HANDLE_VALUE) CloseHandle( hsrc );
*end = 0;
return 0;
}
@@ -918,13 +944,14 @@ static int install_fake_dll( WCHAR *dest, WCHAR *file, const WCHAR *ext, BOOL de
{
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;
@@ -933,30 +960,25 @@ static int install_fake_dll( WCHAR *dest, WCHAR *file, const WCHAR *ext, BOOL de
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 );
}
}
@@ -1051,11 +1073,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;
@@ -1074,12 +1094,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
@@ -1088,7 +1106,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 );
@@ -1101,8 +1121,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.33.0
More information about the wine-devel
mailing list