| /* | |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdlib.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "winerror.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 | |
| { | |
| dev_t dev; | |
| ino_t ino; | |
| 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, { 0, (DWORD)(__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 ) | |
| { | |
| 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 = (len + 1 + strlenW( name )) * sizeof(WCHAR); | |
| nameW.MaximumLength = nameW.Length + sizeof(WCHAR); | |
| if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.Length ))) | |
| { | |
| SetLastError( ERROR_NOT_ENOUGH_MEMORY ); | |
| return 0; | |
| } | |
| MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer, len ); | |
| 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 ) | |
| { | |
| struct stat st; | |
| DeviceIoProc ret = NULL; | |
| int status, i, fd; | |
| status = wine_server_handle_to_fd( handle, 0, &fd, NULL ); | |
| if (status) | |
| { | |
| SetLastError( RtlNtStatusToDosError(status) ); | |
| return NULL; | |
| } | |
| if (fstat( fd, &st ) == -1) | |
| { | |
| wine_server_release_fd( handle, fd ); | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return NULL; | |
| } | |
| wine_server_release_fd( handle, fd ); | |
| RtlEnterCriticalSection( &vxd_section ); | |
| for (i = 0; i < MAX_VXD_MODULES; i++) | |
| { | |
| if (!vxd_modules[i].module) break; | |
| if (vxd_modules[i].dev == st.st_dev && vxd_modules[i].ino == st.st_ino) | |
| { | |
| 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 (strcmpW( 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 */ | |
| { | |
| struct stat st; | |
| int fd; | |
| /* get a file handle to the dummy file */ | |
| if (!(handle = open_vxd_handle( name ))) | |
| { | |
| FreeLibrary( module ); | |
| goto done; | |
| } | |
| wine_server_handle_to_fd( handle, 0, &fd, NULL ); | |
| if (fstat( fd, &st ) != -1) | |
| { | |
| vxd_modules[i].dev = st.st_dev; | |
| vxd_modules[i].ino = st.st_ino; | |
| } | |
| vxd_modules[i].module = module; | |
| vxd_modules[i].handle = handle; | |
| vxd_modules[i].proc = (DeviceIoProc)GetProcAddress( module, "DeviceIoControl" ); | |
| wine_server_release_fd( handle, fd ); | |
| goto done; | |
| } | |
| } | |
| ERR("too many open VxD modules, please report\n" ); | |
| CloseHandle( handle ); | |
| 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 VxDCall( DWORD service, CONTEXT86 *context ) | |
| { | |
| 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 (%08lx)\n", service); | |
| context->Eax = 0xffffffff; /* FIXME */ | |
| } | |
| } | |
| /*********************************************************************** | |
| * 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,%lx,%p,%ld,%p,%ld,%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 ) | |
| { | |
| DeviceIoProc proc = get_vxd_proc( hDevice ); | |
| if (proc) return proc( dwIoControlCode, lpvInBuffer, cbInBuffer, | |
| lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped ); | |
| return FALSE; | |
| } | |
| /* Not a VxD, let ntdll handle it */ | |
| if (lpOverlapped) | |
| { | |
| status = NtDeviceIoControlFile(hDevice, lpOverlapped->hEvent, | |
| NULL, NULL, (PIO_STATUS_BLOCK)lpOverlapped, | |
| dwIoControlCode, lpvInBuffer, cbInBuffer, | |
| lpvOutBuffer, cbOutBuffer); | |
| if (lpcbBytesReturned) *lpcbBytesReturned = lpOverlapped->InternalHigh; | |
| } | |
| else | |
| { | |
| IO_STATUS_BLOCK iosb; | |
| status = NtDeviceIoControlFile(hDevice, NULL, NULL, NULL, &iosb, | |
| dwIoControlCode, lpvInBuffer, cbInBuffer, | |
| lpvOutBuffer, cbOutBuffer); | |
| if (lpcbBytesReturned) *lpcbBytesReturned = iosb.Information; | |
| } | |
| if (status) SetLastError( RtlNtStatusToDosError(status) ); | |
| return !status; | |
| } |