diff --git a/dlls/wow64/file.c b/dlls/wow64/file.c index ca87fabe868..05c8a97de0c 100644 --- a/dlls/wow64/file.c +++ b/dlls/wow64/file.c @@ -26,12 +26,172 @@ #include "winbase.h" #include "winnt.h" #include "winternl.h" +#include "winioctl.h" #include "wow64_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(wow); +static FILE_OBJECTID_BUFFER windir_id, sysdir_id; + +static inline NTSTATUS get_file_id( HANDLE handle, FILE_OBJECTID_BUFFER *id ) +{ + IO_STATUS_BLOCK io; + + return NtFsControlFile( handle, 0, NULL, NULL, &io, FSCTL_GET_OBJECT_ID, NULL, 0, id, sizeof(*id) ); +} + +static inline ULONG starts_with_path( const WCHAR *name, ULONG name_len, const WCHAR *prefix ) +{ + ULONG len = wcslen( prefix ); + + if (name_len < len) return 0; + if (wcsnicmp( name, prefix, len )) return 0; + if (name_len > len && name[len] != '\\') return 0; + return len; +} + + +/*********************************************************************** + * init_file_redirects + */ +void init_file_redirects(void) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + IO_STATUS_BLOCK io; + HANDLE handle; + + InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL ); + RtlInitUnicodeString( &nameW, L"\\??\\C:\\windows" ); + if (!NtOpenFile( &handle, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, + FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | + FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE )) + { + get_file_id( handle, &windir_id ); + NtClose( handle ); + } + RtlInitUnicodeString( &nameW, L"\\??\\C:\\windows\\system32" ); + if (!NtOpenFile( &handle, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, + FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | + FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE )) + { + get_file_id( handle, &sysdir_id ); + NtClose( handle ); + } +} + + +/*********************************************************************** + * replace_path + * + * Helper for get_file_redirect(). + */ +static BOOL replace_path( OBJECT_ATTRIBUTES *attr, ULONG prefix_len, const WCHAR *match, + const WCHAR *replace_dir, const WCHAR *replace_name ) +{ + const WCHAR *name = attr->ObjectName->Buffer; + ULONG match_len, replace_len, len = attr->ObjectName->Length / sizeof(WCHAR); + UNICODE_STRING str; + WCHAR *p; + + if (!starts_with_path( name + prefix_len, len - prefix_len, match )) return FALSE; + + match_len = wcslen( match ); + replace_len = wcslen( replace_dir ); + if (replace_name) replace_len += wcslen( replace_name ); + str.Length = (len + replace_len - match_len) * sizeof(WCHAR); + str.MaximumLength = str.Length + sizeof(WCHAR); + if (!(p = str.Buffer = Wow64AllocateTemp( str.MaximumLength ))) return FALSE; + + memcpy( p, name, prefix_len * sizeof(WCHAR) ); + p += prefix_len; + wcscpy( p, replace_dir ); + p += wcslen(p); + if (replace_name) + { + wcscpy( p, replace_name ); + p += wcslen(p); + } + name += prefix_len + match_len; + len -= prefix_len + match_len; + memcpy( p, name, len * sizeof(WCHAR) ); + p[len] = 0; + *attr->ObjectName = str; + return TRUE; +} + + +/*********************************************************************** + * get_file_redirect + */ +BOOL get_file_redirect( OBJECT_ATTRIBUTES *attr ) +{ + static const WCHAR * const no_redirect[] = + { + L"system32\\catroot", L"system32\\catroot2", L"system32\\driversstore", + L"system32\\drivers\\etc", L"system32\\logfiles", L"system32\\spool" + }; + static const WCHAR windirW[] = L"\\??\\C:\\windows\\"; + const WCHAR *name = attr->ObjectName->Buffer; + unsigned int i, prefix_len = 0, len = attr->ObjectName->Length / sizeof(WCHAR); + const WCHAR *syswow64dir; + UNICODE_STRING redir; + + if (!len) return FALSE; + + if (!attr->RootDirectory) + { + prefix_len = wcslen( windirW ); + if (len < prefix_len || wcsnicmp( name, windirW, prefix_len )) return FALSE; + } + else + { + FILE_OBJECTID_BUFFER id; + + if (get_file_id( attr->RootDirectory, &id )) return FALSE; + if (memcmp( &id, &windir_id, sizeof(id) )) + { + if (memcmp( &id, &sysdir_id, sizeof(id) )) return FALSE; + if (NtCurrentTeb()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE; + if (name[0] == '\\') return FALSE; + + /* only check for paths that should NOT be redirected */ + for (i = 0; i < ARRAY_SIZE( no_redirect ); i++) + if (starts_with_path( name, len, no_redirect[i] + 9 /* "system32\\" */)) return FALSE; + + /* redirect everything else */ + syswow64dir = get_machine_wow64_dir( current_machine ); + redir.Length = (wcslen(syswow64dir) + 1 + len) * sizeof(WCHAR); + redir.MaximumLength = redir.Length + sizeof(WCHAR); + if (!(redir.Buffer = Wow64AllocateTemp( redir.MaximumLength ))) return FALSE; + wcscpy( redir.Buffer, syswow64dir ); + wcscat( redir.Buffer, L"\\" ); + memcpy( redir.Buffer + wcslen(redir.Buffer), name, len * sizeof(WCHAR) ); + redir.Buffer[redir.Length / sizeof(WCHAR)] = 0; + attr->RootDirectory = 0; + *attr->ObjectName = redir; + return TRUE; + } + } + + /* sysnative is redirected even when redirection is disabled */ + + if (replace_path( attr, prefix_len, L"sysnative", L"system32", NULL )) return TRUE; + + if (NtCurrentTeb()->TlsSlots[WOW64_TLS_FILESYSREDIR]) return FALSE; + + for (i = 0; i < ARRAY_SIZE( no_redirect ); i++) + if (starts_with_path( name + prefix_len, len - prefix_len, no_redirect[i] )) return FALSE; + + syswow64dir = get_machine_wow64_dir( current_machine ) + wcslen( windirW ); + if (replace_path( attr, prefix_len, L"system32", syswow64dir, NULL )) return TRUE; + if (replace_path( attr, prefix_len, L"regedit.exe", syswow64dir, L"\\regedit.exe" )) return TRUE; + return FALSE; +} + + /********************************************************************** * wow64_NtCancelIoFile */ @@ -90,7 +250,7 @@ NTSTATUS WINAPI wow64_NtCreateFile( UINT *args ) NTSTATUS status; *handle_ptr = 0; - status = NtCreateFile( &handle, access, objattr_32to64( &attr, attr32 ), + status = NtCreateFile( &handle, access, objattr_32to64_redirect( &attr, attr32 ), iosb_32to64( &io, io32 ), alloc_size, attributes, sharing, disposition, options, ea_buffer, ea_length ); put_handle( handle_ptr, handle ); @@ -188,7 +348,7 @@ NTSTATUS WINAPI wow64_NtDeleteFile( UINT *args ) struct object_attr64 attr; - return NtDeleteFile( objattr_32to64( &attr, attr32 )); + return NtDeleteFile( objattr_32to64_redirect( &attr, attr32 )); } @@ -279,7 +439,7 @@ NTSTATUS WINAPI wow64_NtOpenFile( UINT *args ) NTSTATUS status; *handle_ptr = 0; - status = NtOpenFile( &handle, access, objattr_32to64( &attr, attr32 ), + status = NtOpenFile( &handle, access, objattr_32to64_redirect( &attr, attr32 ), iosb_32to64( &io, io32 ), sharing, options ); put_handle( handle_ptr, handle ); put_iosb( io32, &io ); @@ -297,7 +457,7 @@ NTSTATUS WINAPI wow64_NtQueryAttributesFile( UINT *args ) struct object_attr64 attr; - return NtQueryAttributesFile( objattr_32to64( &attr, attr32 ), info ); + return NtQueryAttributesFile( objattr_32to64_redirect( &attr, attr32 ), info ); } @@ -365,7 +525,7 @@ NTSTATUS WINAPI wow64_NtQueryFullAttributesFile( UINT *args ) struct object_attr64 attr; - return NtQueryFullAttributesFile( objattr_32to64( &attr, attr32 ), info ); + return NtQueryFullAttributesFile( objattr_32to64_redirect( &attr, attr32 ), info ); } @@ -564,16 +724,22 @@ NTSTATUS WINAPI wow64_NtSetInformationFile( UINT *args ) case FileLinkInformation: /* FILE_LINK_INFORMATION */ if (len >= sizeof(FILE_RENAME_INFORMATION32)) { + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; FILE_RENAME_INFORMATION32 *info32 = ptr; FILE_RENAME_INFORMATION *info; ULONG size; - size = offsetof( FILE_RENAME_INFORMATION, FileName[info32->FileNameLength/sizeof(WCHAR)] ); + name.Buffer = info32->FileName; + name.Length = info32->FileNameLength; + InitializeObjectAttributes( &attr, &name, 0, LongToHandle( info32->RootDirectory ), 0 ); + get_file_redirect( &attr ); + size = offsetof( FILE_RENAME_INFORMATION, FileName[name.Length/sizeof(WCHAR)] ); info = Wow64AllocateTemp( size ); info->ReplaceIfExists = info32->ReplaceIfExists; - info->RootDirectory = LongToHandle( info32->RootDirectory ); - info->FileNameLength = info32->FileNameLength; - memcpy( info->FileName, info32->FileName, info->FileNameLength ); + info->RootDirectory = attr.RootDirectory; + info->FileNameLength = name.Length; + memcpy( info->FileName, name.Buffer, info->FileNameLength ); status = NtSetInformationFile( handle, iosb_32to64( &io, io32 ), info, size, class ); } else status = io.Status = STATUS_INVALID_PARAMETER_3; diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c index 0000db54c29..29d358595b0 100644 --- a/dlls/wow64/syscall.c +++ b/dlls/wow64/syscall.c @@ -379,6 +379,8 @@ static void process_init(void) load_cpu_dll(); + init_file_redirects(); + #undef GET_PTR } diff --git a/dlls/wow64/wow64_private.h b/dlls/wow64/wow64_private.h index 303914c540e..9f2f8b8af5b 100644 --- a/dlls/wow64/wow64_private.h +++ b/dlls/wow64/wow64_private.h @@ -31,6 +31,9 @@ ALL_SYSCALLS void * WINAPI Wow64AllocateTemp( SIZE_T size ) DECLSPEC_HIDDEN; void WINAPI Wow64ApcRoutine( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3, CONTEXT *context ) DECLSPEC_HIDDEN; +extern void init_file_redirects(void) DECLSPEC_HIDDEN; +extern BOOL get_file_redirect( OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; + extern USHORT native_machine DECLSPEC_HIDDEN; extern USHORT current_machine DECLSPEC_HIDDEN; extern ULONG_PTR args_alignment DECLSPEC_HIDDEN; @@ -170,6 +173,15 @@ static inline OBJECT_ATTRIBUTES *objattr_32to64( struct object_attr64 *out, cons return &out->attr; } +static inline OBJECT_ATTRIBUTES *objattr_32to64_redirect( struct object_attr64 *out, + const OBJECT_ATTRIBUTES32 *in ) +{ + OBJECT_ATTRIBUTES *attr = objattr_32to64( out, in ); + + if (attr) get_file_redirect( attr ); + return attr; +} + static inline void put_handle( ULONG *handle32, HANDLE handle ) { *handle32 = HandleToULong( handle );