|  | /* | 
|  | * 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> | 
|  | #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 | 
|  | { | 
|  | 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, { (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 ) | 
|  | { | 
|  | 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 (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 */ | 
|  | { | 
|  | 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 WINAPI __regs_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 */ | 
|  | } | 
|  | } | 
|  | #ifdef DEFINE_REGS_ENTRYPOINT | 
|  | DEFINE_REGS_ENTRYPOINT( VxDCall, 4, 4 ); | 
|  | #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,%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) | 
|  | { | 
|  | if (HIWORD(dwIoControlCode) == FILE_DEVICE_FILE_SYSTEM) | 
|  | status = NtFsControlFile(hDevice, lpOverlapped->hEvent, | 
|  | NULL, NULL, (PIO_STATUS_BLOCK)lpOverlapped, | 
|  | dwIoControlCode, lpvInBuffer, cbInBuffer, | 
|  | lpvOutBuffer, cbOutBuffer); | 
|  | else | 
|  | 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; | 
|  |  | 
|  | 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; | 
|  | } |