/* * Win32 VxD functions * * Copyright 1998 Marcus Meissner * Copyright 1998 Ulrich Weigand * Copyright 1998 Patrik Stridvall * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include <stdlib.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <sys/types.h> #ifdef HAVE_SYS_STAT_H # include <sys/stat.h> #endif #include <string.h> #include <stdarg.h> #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winternl.h" #include "winioctl.h" #include "kernel_private.h" #include "wine/library.h" #include "wine/unicode.h" #include "wine/server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(vxd); typedef BOOL (WINAPI *DeviceIoProc)(DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPOVERLAPPED); typedef DWORD (WINAPI *VxDCallProc)(DWORD, CONTEXT86 *); struct vxd_module { LARGE_INTEGER index; HANDLE handle; HMODULE module; DeviceIoProc proc; }; struct vxdcall_service { WCHAR name[12]; DWORD service; HMODULE module; VxDCallProc proc; }; #define MAX_VXD_MODULES 32 static struct vxd_module vxd_modules[MAX_VXD_MODULES]; static struct vxdcall_service vxd_services[] = { { {'v','m','m','.','v','x','d',0}, 0x0001, NULL, NULL }, { {'v','w','i','n','3','2','.','v','x','d',0}, 0x002a, NULL, NULL } }; #define NB_VXD_SERVICES (sizeof(vxd_services)/sizeof(vxd_services[0])) static CRITICAL_SECTION vxd_section; static CRITICAL_SECTION_DEBUG critsect_debug = { 0, 0, &vxd_section, { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, 0, 0, { (DWORD_PTR)(__FILE__ ": vxd_section") } }; static CRITICAL_SECTION vxd_section = { &critsect_debug, -1, 0, 0, 0, 0 }; /* create a file handle to represent a VxD, by opening a dummy file in the wineserver directory */ static HANDLE open_vxd_handle( LPCWSTR name ) { static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'}; const char *dir = wine_get_server_dir(); int len; HANDLE ret; NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING nameW; IO_STATUS_BLOCK io; len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 ); nameW.Length = sizeof(prefixW) + (len + strlenW( name )) * sizeof(WCHAR); nameW.MaximumLength = nameW.Length + sizeof(WCHAR); if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.MaximumLength ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return 0; } memcpy( nameW.Buffer, prefixW, sizeof(prefixW) ); MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer + sizeof(prefixW)/sizeof(WCHAR), len ); len += sizeof(prefixW) / sizeof(WCHAR); nameW.Buffer[len-1] = '/'; strcpyW( nameW.Buffer + len, name ); attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.Attributes = 0; attr.ObjectName = &nameW; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; status = NtCreateFile( &ret, 0, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 ); if (status) { ret = 0; SetLastError( RtlNtStatusToDosError(status) ); } RtlFreeUnicodeString( &nameW ); return ret; } /* retrieve the DeviceIoControl function for a Vxd given a file handle */ static DeviceIoProc get_vxd_proc( HANDLE handle ) { DeviceIoProc ret = NULL; int status, i; IO_STATUS_BLOCK io; FILE_INTERNAL_INFORMATION info; status = NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation ); if (status) { SetLastError( RtlNtStatusToDosError(status) ); return NULL; } RtlEnterCriticalSection( &vxd_section ); for (i = 0; i < MAX_VXD_MODULES; i++) { if (!vxd_modules[i].module) break; if (vxd_modules[i].index.QuadPart == info.IndexNumber.QuadPart) { if (!(ret = vxd_modules[i].proc)) SetLastError( ERROR_INVALID_FUNCTION ); goto done; } } /* FIXME: Here we could go through the directory to find the VxD name and load it. */ /* Let's wait to find out if there are actually apps out there that try to share */ /* VxD handles between processes, before we go to the trouble of implementing it. */ ERR( "handle %p not found in module list, inherited from another process?\n", handle ); done: RtlLeaveCriticalSection( &vxd_section ); return ret; } /* load a VxD and return a file handle to it */ HANDLE VXD_Open( LPCWSTR filenameW, DWORD access, SECURITY_ATTRIBUTES *sa ) { static const WCHAR dotVxDW[] = {'.','v','x','d',0}; int i; HANDLE handle; HMODULE module; WCHAR *p, name[16]; if (!(GetVersion() & 0x80000000)) /* there are no VxDs on NT */ { SetLastError( ERROR_FILE_NOT_FOUND ); return 0; } /* normalize the filename */ if (strlenW( filenameW ) >= sizeof(name)/sizeof(WCHAR) - 4 || strchrW( filenameW, '/' ) || strchrW( filenameW, '\\' )) { SetLastError( ERROR_FILE_NOT_FOUND ); return 0; } strcpyW( name, filenameW ); strlwrW( name ); p = strchrW( name, '.' ); if (!p) strcatW( name, dotVxDW ); else if (strcmpiW( p, dotVxDW )) /* existing extension has to be .vxd */ { SetLastError( ERROR_FILE_NOT_FOUND ); return 0; } /* try to load the module first */ if (!(module = LoadLibraryW( name ))) { FIXME( "Unknown/unsupported VxD %s. Try setting Windows version to 'nt40' or 'win31'.\n", debugstr_w(name) ); SetLastError( ERROR_FILE_NOT_FOUND ); return 0; } /* register the module in the global list if necessary */ RtlEnterCriticalSection( &vxd_section ); for (i = 0; i < MAX_VXD_MODULES; i++) { if (vxd_modules[i].module == module) { handle = vxd_modules[i].handle; goto done; /* already registered */ } if (!vxd_modules[i].module) /* new one, register it */ { IO_STATUS_BLOCK io; FILE_INTERNAL_INFORMATION info; /* get a file handle to the dummy file */ if (!(handle = open_vxd_handle( name ))) { FreeLibrary( module ); goto done; } if (!NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation )) vxd_modules[i].index = info.IndexNumber; vxd_modules[i].module = module; vxd_modules[i].handle = handle; vxd_modules[i].proc = (DeviceIoProc)GetProcAddress( module, "DeviceIoControl" ); goto done; } } ERR("too many open VxD modules, please report\n" ); FreeLibrary( module ); handle = 0; done: RtlLeaveCriticalSection( &vxd_section ); if (!DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle, 0, (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle), DUP_HANDLE_SAME_ACCESS )) handle = 0; return handle; } /*********************************************************************** * VxDCall0 (KERNEL32.1) * VxDCall1 (KERNEL32.2) * VxDCall2 (KERNEL32.3) * VxDCall3 (KERNEL32.4) * VxDCall4 (KERNEL32.5) * VxDCall5 (KERNEL32.6) * VxDCall6 (KERNEL32.7) * VxDCall7 (KERNEL32.8) * VxDCall8 (KERNEL32.9) */ void WINAPI __regs_VxDCall( DWORD service, CONTEXT86 *context ) { unsigned int i; VxDCallProc proc = NULL; RtlEnterCriticalSection( &vxd_section ); for (i = 0; i < NB_VXD_SERVICES; i++) { if (HIWORD(service) != vxd_services[i].service) continue; if (!vxd_services[i].module) /* need to load it */ { if ((vxd_services[i].module = LoadLibraryW( vxd_services[i].name ))) vxd_services[i].proc = (VxDCallProc)GetProcAddress( vxd_services[i].module, "VxDCall" ); } proc = vxd_services[i].proc; break; } RtlLeaveCriticalSection( &vxd_section ); if (proc) context->Eax = proc( service, context ); else { FIXME( "Unknown/unimplemented VxD (%08x)\n", service); context->Eax = 0xffffffff; /* FIXME */ } } #ifdef DEFINE_REGS_ENTRYPOINT DEFINE_REGS_ENTRYPOINT( VxDCall, 1 ) #endif /*********************************************************************** * OpenVxDHandle (KERNEL32.@) * * This function is supposed to return the corresponding Ring 0 * ("kernel") handle for a Ring 3 handle in Win9x. * Evidently, Wine will have problems with this. But we try anyway, * maybe it helps... */ HANDLE WINAPI OpenVxDHandle(HANDLE hHandleRing3) { FIXME( "(%p), stub! (returning Ring 3 handle instead of Ring 0)\n", hHandleRing3); return hHandleRing3; } /**************************************************************************** * DeviceIoControl (KERNEL32.@) * This is one of those big ugly nasty procedure which can do * a million and one things when it comes to devices. It can also be * used for VxD communication. * * A return value of FALSE indicates that something has gone wrong which * GetLastError can decipher. */ BOOL WINAPI DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpvInBuffer, DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbBytesReturned, LPOVERLAPPED lpOverlapped) { NTSTATUS status; TRACE( "(%p,%x,%p,%d,%p,%d,%p,%p)\n", hDevice,dwIoControlCode,lpvInBuffer,cbInBuffer, lpvOutBuffer,cbOutBuffer,lpcbBytesReturned,lpOverlapped ); /* Check if this is a user defined control code for a VxD */ if (HIWORD( dwIoControlCode ) == 0 && (GetVersion() & 0x80000000)) { DeviceIoProc proc = get_vxd_proc( hDevice ); if (proc) return proc( dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped ); } /* Not a VxD, let ntdll handle it */ if (lpOverlapped) { LPVOID cvalue = ((ULONG_PTR)lpOverlapped->hEvent & 1) ? NULL : lpOverlapped; lpOverlapped->Internal = STATUS_PENDING; lpOverlapped->InternalHigh = 0; if (HIWORD(dwIoControlCode) == FILE_DEVICE_FILE_SYSTEM) status = NtFsControlFile(hDevice, lpOverlapped->hEvent, NULL, cvalue, (PIO_STATUS_BLOCK)lpOverlapped, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer); else status = NtDeviceIoControlFile(hDevice, lpOverlapped->hEvent, NULL, cvalue, (PIO_STATUS_BLOCK)lpOverlapped, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer); if (lpcbBytesReturned) *lpcbBytesReturned = lpOverlapped->InternalHigh; } else { IO_STATUS_BLOCK iosb; if (HIWORD(dwIoControlCode) == FILE_DEVICE_FILE_SYSTEM) status = NtFsControlFile(hDevice, NULL, NULL, NULL, &iosb, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer); else status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &iosb, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer); if (lpcbBytesReturned) *lpcbBytesReturned = iosb.Information; } if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; }