/*++ * * WOW v1.0 * * Copyright (c) 1993 Microsoft Corporation * * WKFILEIO.C * WOW32 KRNL FAST FILEIO ROUTINES * * History: * Routines removed from wkman.c * Created 1-Jan-1993 by Matt Felton (mattfe) * --*/ #include "precomp.h" #pragma hdrstop #include "dossvc.h" #include "demexp.h" #include "nt_vdd.h" MODNAME(wkfileio.c); extern DOSWOWDATA DosWowData; // Files which are mapped are kept in a single linked list // gpCacheHead -> the most recently accessed entry // BOOL fCacheInit = TRUE; // Set False When initialized PHMAPPEDFILEALIAS gpCacheHead = NULL; HMAPPEDFILEALIAS aMappedFileCache[MAX_MAPPED_FILES] = {0}; // File Handle To MappedFile Array DWORD dwTotalCacheBytes = 0; DWORD dwTotalCacheAccess = 0; #ifdef DEBUG INT fileiolevel = 12; INT fileoclevel = 8; #endif BOOL FASTCALL IsModuleSymantecInstall(HAND16 hMod16); // // named pipe stuff // BOOL LoadVdmRedir( VOID ); BOOL IsVdmRedirLoaded( VOID ); BOOL IsNamedPipeName( IN LPSTR Name ); PSTR TruncatePath83( IN OUT PSTR, IN PSTR ); CRITICAL_SECTION VdmLoadCritSec; // // invent some typedefs to avoid compiler warnings from GetProcAddress // typedef BOOL (*VR_READ_NAMED_PIPE_FUNC)( IN HANDLE Handle, IN LPBYTE Buffer, IN DWORD Buflen, OUT LPDWORD BytesRead, OUT LPDWORD Error ); typedef BOOL (*VR_WRITE_NAMED_PIPE_FUNC)( IN HANDLE Handle, IN LPBYTE Buffer, IN DWORD Buflen, OUT LPDWORD BytesRead ); typedef BOOL (*VR_IS_NAMED_PIPE_HANDLE_FUNC)( IN HANDLE Handle ); typedef BOOL (*VR_ADD_OPEN_NAMED_PIPE_INFO_FUNC)( IN HANDLE Handle, IN LPSTR lpFileName ); typedef LPSTR (*VR_CONVERT_LOCAL_NT_PIPE_NAME_FUNC)( OUT LPSTR Buffer OPTIONAL, IN LPSTR Name ); typedef BOOL (*VR_REMOVE_OPEN_NAMED_PIPE_INFO_FUNC)( IN HANDLE Handle ); typedef VOID (*VR_CANCEL_PIPE_IO_FUNC)( IN DWORD Thread ); // // prototypes for functions dynamically loaded from VDMREDIR.DLL // BOOL (*VrReadNamedPipe)( IN HANDLE Handle, IN LPBYTE Buffer, IN DWORD Buflen, OUT LPDWORD BytesRead, OUT LPDWORD Error ) = NULL; BOOL (*VrWriteNamedPipe)( IN HANDLE Handle, IN LPBYTE Buffer, IN DWORD Buflen, OUT LPDWORD BytesWritten ) = NULL; BOOL DefaultIsNamedPipeHandle( IN HANDLE Handle ); BOOL DefaultIsNamedPipeHandle( IN HANDLE Handle ) { return FALSE; } BOOL (*VrIsNamedPipeHandle)( IN HANDLE Handle ) = DefaultIsNamedPipeHandle; BOOL (*VrAddOpenNamedPipeInfo)( IN HANDLE Handle, IN LPSTR lpFileName ) = NULL; LPSTR (*VrConvertLocalNtPipeName)( OUT LPSTR Buffer OPTIONAL, IN LPSTR Name ) = NULL; BOOL (*VrRemoveOpenNamedPipeInfo)( IN HANDLE Handle ) = NULL; VOID DefaultVrCancelPipeIo( IN DWORD Thread ); VOID DefaultVrCancelPipeIo( IN DWORD Thread ) { (void)(Thread); } VOID (*VrCancelPipeIo)( IN DWORD Thread ) = DefaultVrCancelPipeIo; HANDLE hVdmRedir; BOOL VdmRedirLoaded = FALSE; BOOL LoadVdmRedir( VOID ) /*++ Routine Description: Load the VDMREDIR DLL if it is not already loaded. Called from OpenFile only. Since file operations cannot be performed on a file that has not been opened, it is safe to only call this function on open Arguments: None. Return Value: BOOL TRUE VdmRedir.DLL is loaded FALSE no it isn't --*/ { BOOL currentLoadState; // // need critical section - Windows apps end up being multi-threaded in // 32-bit world - might have simultaneous opens // EnterCriticalSection(&VdmLoadCritSec); if (!VdmRedirLoaded) { if ((hVdmRedir = LoadLibrary("VDMREDIR")) != NULL) { if ((VrReadNamedPipe = (VR_READ_NAMED_PIPE_FUNC)GetProcAddress(hVdmRedir, "VrReadNamedPipe")) == NULL) { goto closeAndReturn; } if ((VrWriteNamedPipe = (VR_WRITE_NAMED_PIPE_FUNC)GetProcAddress(hVdmRedir, "VrWriteNamedPipe")) == NULL) { goto closeAndReturn; } if ((VrIsNamedPipeHandle = (VR_IS_NAMED_PIPE_HANDLE_FUNC)GetProcAddress(hVdmRedir, "VrIsNamedPipeHandle")) == NULL) { goto closeAndReturn; } if ((VrAddOpenNamedPipeInfo = (VR_ADD_OPEN_NAMED_PIPE_INFO_FUNC)GetProcAddress(hVdmRedir, "VrAddOpenNamedPipeInfo")) == NULL) { goto closeAndReturn; } if ((VrConvertLocalNtPipeName = (VR_CONVERT_LOCAL_NT_PIPE_NAME_FUNC)GetProcAddress(hVdmRedir, "VrConvertLocalNtPipeName")) == NULL) { goto closeAndReturn; } if ((VrRemoveOpenNamedPipeInfo = (VR_REMOVE_OPEN_NAMED_PIPE_INFO_FUNC)GetProcAddress(hVdmRedir, "VrRemoveOpenNamedPipeInfo")) == NULL) { goto closeAndReturn; } if ((VrCancelPipeIo = (VR_CANCEL_PIPE_IO_FUNC)GetProcAddress(hVdmRedir, "VrCancelPipeIo")) == NULL) { VrCancelPipeIo = DefaultVrCancelPipeIo; closeAndReturn: CloseHandle(hVdmRedir); } else { VdmRedirLoaded = TRUE; } } } currentLoadState = VdmRedirLoaded; LeaveCriticalSection(&VdmLoadCritSec); return currentLoadState; } BOOL IsVdmRedirLoaded( VOID ) /*++ Routine Description: Checks current load state of VDMREDIR.DLL Arguments: None. Return Value: BOOL TRUE VdmRedir.DLL is loaded FALSE no it isn't --*/ { BOOL currentLoadState; EnterCriticalSection(&VdmLoadCritSec); currentLoadState = VdmRedirLoaded; LeaveCriticalSection(&VdmLoadCritSec); return currentLoadState; } BOOL IsNamedPipeName( IN LPSTR Name ) /*++ Routine Description: Lifted from VDMREDIR.DLL - we don't want to load the entire DLL if we need to check for a named pipe Checks if a string designates a named pipe. As criteria for the decision we use: \\computername\PIPE\... DOS (client-side) can only open a named pipe which is created at a server and must therefore be prefixed by a computername Arguments: Name - to check for (Dos) named pipe syntax Return Value: BOOL TRUE - Name refers to (local or remote) named pipe FALSE - Name doesn't look like name of pipe --*/ { int CharCount; if (IS_ASCII_PATH_SEPARATOR(*Name)) { ++Name; if (IS_ASCII_PATH_SEPARATOR(*Name)) { ++Name; CharCount = 0; while (*Name && !IS_ASCII_PATH_SEPARATOR(*Name)) { ++Name; ++CharCount; } if (!CharCount || !*Name) { // // Name is \\ or \\\ or just \\name which I don't understand, // so its not a named pipe - fail it // return FALSE; } // // bump name past next path separator. Note that we don't have to // check CharCount for max. length of a computername, because this // function is called only after the (presumed) named pipe has been // successfully opened, therefore we know that the name has been // validated // ++Name; } else { return FALSE; } // // We are at (after \ or \\\). Check if // is [Pp][Ii][Pp][Ee][\\/] // if (!WOW32_strnicmp(Name, "PIPE", 4)) { Name += 4; if (IS_ASCII_PATH_SEPARATOR(*Name)) { return TRUE; } } } return FALSE; } /* WK32WOWFileRead - Read a file * * * Entry - fh File Handle * bufsize Count to read * lpBuf Buffer Address * * Exit * SUCCESS * Count of bytes read * * FAILURE * system status code * Concept Borrowed from demFileRead * */ ULONG FASTCALL WK32WOWFileRead (PVDMFRAME pFrame) { PWOWFILEREAD16 parg16; LPBYTE pSrc; LPBYTE pDst; INT dwBytesRead; DWORD bufsize, dwError; LARGE_INTEGER liBytesLeft, liFileSize, liFilePointer; HANDLE hFile; PHMAPPEDFILEALIAS pCache = 0; PDOSSFT pSFT; GETARGPTR(pFrame, sizeof(*parg16), parg16); bufsize = FETCHDWORD(parg16->bufsize); dwBytesRead = bufsize; hFile = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); if (!hFile) { dwBytesRead = 0xffff0006; goto Return_dwBytesRead; } if (pSFT->SFT_Flags & 0x80) { // Is this a device handle? dwBytesRead = 0xffffffff; // Let DOS handle device handles. goto Return_dwBytesRead; // kernel QuickRead passes to DOS } // after any error (dx=ffff) // // It is legitimate to ask to read more bytes than are left in the // selector passed in, if the file is short enough to not actually // overrun the selector. In this case we don't want limit checking, // so zero is passed as the required size to GETVDMPTR(). // GETVDMPTR(parg16->lpBuf, 0, pDst); // If its the KRNL doing IO then find the File in the Cache if ( vptopPDB == parg16->lpPDB ) { if ( !(pCache = FINDMAPFILECACHE(hFile)) ){ // Cache Entry Not Found so Add it pCache = ALLOCMAPFILECACHE(); pCache->fAccess = W32MapViewOfFile( pCache, hFile); } if (pCache->fAccess) { // Calculate Starting Read Address in File pSrc = pCache->lpStartingAddressOfView + pCache->lFilePointer; dwBytesRead = bufsize; // Adjust Size so as to not read off the End of File if (pCache->lFilePointer > pCache->dwFileSize) { dwBytesRead = 0; } else { if (pCache->lFilePointer + dwBytesRead > pCache->dwFileSize) { dwBytesRead-=((pCache->lFilePointer+dwBytesRead)-pCache->dwFileSize); } } LOGDEBUG(fileiolevel, ("MapFileRead fh:%04X fh32:%08X pSrc:%08X Bytes:%08X pDsc %08X\n" ,FETCHWORD(parg16->fh),hFile, pSrc,dwBytesRead,FETCHDWORD(parg16->lpBuf))); // Could get PageIO Errors, especially reading over the network // So do try-except around the mapped read. try { RtlCopyMemory(pDst, pSrc, dwBytesRead); pCache->lFilePointer += dwBytesRead; dwTotalCacheBytes += dwBytesRead; dwTotalCacheAccess++; } except (TRUE) { SetFilePointer( hFile, pCache->lFilePointer, NULL, FILE_BEGIN ); FREEMAPFILECACHE(pCache->hfile32); pCache->hfile32 = hFile; pCache->fAccess = FALSE; pCache = 0; } } } if ((pCache == 0) || (pCache->fAccess == FALSE)) { // Do The File Read via the File System if (IsVdmRedirLoaded() && VrIsNamedPipeHandle(hFile)) { DWORD error; if (!VrReadNamedPipe(hFile, pDst, (DWORD)bufsize, &dwBytesRead, &error)) { dwBytesRead = error | 0xffff0000; } } else if (ReadFile (hFile, pDst, (DWORD)bufsize, &dwBytesRead, NULL) == FALSE){ // // In Win3.1 it is not an error to hit EOF during a read with buffer // smaller than the requested read. // AmiPro asks for more bytes than they allocated for the buffer. // dwError = GetLastError(); if(dwError == ERROR_NOACCESS) { liFileSize.LowPart = GetFileSize(hFile, &liFileSize.HighPart); liFilePointer.HighPart = 0; liFilePointer.LowPart = SetFilePointer(hFile, 0, &liFilePointer.HighPart, FILE_CURRENT ); if (liFileSize.QuadPart <= liFilePointer.QuadPart) { dwBytesRead = 0; } else { // how far to end of file? liBytesLeft.QuadPart = liFileSize.QuadPart - liFilePointer.QuadPart; // // If it should have worked, give up and assert // if (liBytesLeft.HighPart || liBytesLeft.LowPart >= bufsize) { WOW32ASSERTMSGF( FALSE, ("WK32WOWFileRead: ReadFile returned ERROR_NOACCESS but there is data to read,\n" "maybe invalid buffer %x:%4x size 0x%x (would fault on 3.1). Hit 'g' to\n" "return ERROR_NOT_ENOUGH_MEMORY.\n", HIWORD(parg16->lpBuf), LOWORD(parg16->lpBuf), bufsize)); dwBytesRead = ERROR_NOT_ENOUGH_MEMORY | 0xffff0000; } // else try again with the smaller request else if (ReadFile (hFile, pDst, liBytesLeft.LowPart, &dwBytesRead, NULL) == FALSE){ dwBytesRead = GetLastError() | 0xffff0000; } } } else { dwBytesRead = dwError | 0xffff0000; } } LOGDEBUG(fileiolevel, ("IOFileRead fh:%X fh32:%X Bytes req:%X read:%X pDsc %08X\n" ,FETCHWORD(parg16->fh),hFile,bufsize,dwBytesRead, FETCHDWORD(parg16->lpBuf))); } else { if ((dwTotalCacheBytes > CACHE_BYTE_THRESHOLD) || (dwTotalCacheAccess > CACHE_ACCESS_THRESHOLD) || (dwBytesRead > CACHE_READ_THRESHOLD)) { FlushMapFileCaches(); } } // // If the read was successful, let the emulator know that // these bytes have changed. // // On checked builds perform limit check now that we know the // actual number of bytes read. We wait until now to allow // for a requested read size which would overrun the selector, // but against a file which has few enough bytes remaining // that the selector isn't actually overrun. // if ((dwBytesRead & 0xffff0000) != 0xffff0000) { FLUSHVDMCODEPTR(parg16->lpBuf, (WORD)dwBytesRead, pDst); #ifdef DEBUG FREEVDMPTR(pDst); GETVDMPTR(parg16->lpBuf, dwBytesRead, pDst); #endif } FREEVDMPTR(pDst); Return_dwBytesRead: FREEARGPTR(parg16); return (dwBytesRead); } PHMAPPEDFILEALIAS FindMapFileCache(HANDLE hFile) { PHMAPPEDFILEALIAS pCache, prev; if (fCacheInit) { InitMapFileCache(); } pCache = gpCacheHead; prev = 0; while ( (pCache->hfile32 != hFile) && (pCache->hpfNext !=0) ) { prev = pCache; pCache = pCache->hpfNext; } // If we found it, then make sure its at the front of the list if (pCache->hfile32 == hFile) { if (prev != 0) { prev->hpfNext = pCache->hpfNext; pCache->hpfNext = gpCacheHead; gpCacheHead = pCache; } }else{ // If it was not found return error pCache = 0; } return(pCache); } PHMAPPEDFILEALIAS AllocMapFileCache() { PHMAPPEDFILEALIAS pCache, prev; if (fCacheInit) { InitMapFileCache(); } pCache = gpCacheHead; prev = 0; while ( (pCache->hpfNext != 0) && (pCache->hfile32 != 0) ) { prev = pCache; pCache = pCache->hpfNext; } if (prev != 0) { prev->hpfNext = pCache->hpfNext; pCache->hpfNext = gpCacheHead; gpCacheHead = pCache; } // If The found entry was in use, then Free if (pCache->hfile32 != 0) { FREEMAPFILECACHE(pCache->hfile32); } return(pCache); } VOID FreeMapFileCache(HANDLE hFile) { PHMAPPEDFILEALIAS pCache; if ( pCache = FINDMAPFILECACHE(hFile) ) { LOGDEBUG(fileiolevel,("FreeMapFileCache: hFile:%08x hMappedFileObject:%08X\n", hFile,pCache->hMappedFileObject)); if ( pCache->lpStartingAddressOfView != 0 ) { UnmapViewOfFile( pCache->lpStartingAddressOfView ); } if ( pCache->hMappedFileObject != 0) { CloseHandle( pCache->hMappedFileObject ); } if (pCache->fAccess) { SetFilePointer( hFile, pCache->lFilePointer, NULL, FILE_BEGIN ); } pCache->hfile32 = 0; pCache->hMappedFileObject = 0; pCache->lpStartingAddressOfView = 0; pCache->lFilePointer = 0; pCache->dwFileSize = 0; pCache->fAccess = FALSE; } } VOID InitMapFileCache() { PHMAPPEDFILEALIAS pCache; INT i; pCache = &aMappedFileCache[0]; gpCacheHead = 0; for ( i = 1; i <= MAX_MAPPED_FILES-1; i++ ) { pCache->hfile32 = 0; pCache->hMappedFileObject = 0; pCache->lpStartingAddressOfView = 0; pCache->lFilePointer = 0; pCache->dwFileSize = 0; pCache->fAccess = FALSE; pCache->hpfNext = gpCacheHead; gpCacheHead = pCache; pCache = &aMappedFileCache[i]; } fCacheInit = FALSE; } BOOL W32MapViewOfFile( PHMAPPEDFILEALIAS pCache, HANDLE hFile) { pCache->fAccess = FALSE; pCache->hfile32 = hFile; pCache->lpStartingAddressOfView = 0; pCache->hMappedFileObject = CreateFileMapping( hFile, 0, PAGE_READONLY, 0, 0, 0); if (pCache->hMappedFileObject != 0) { pCache->lpStartingAddressOfView = MapViewOfFile( pCache->hMappedFileObject, FILE_MAP_READ, 0, 0, 0); if (pCache->lpStartingAddressOfView != 0 ) { pCache->lFilePointer = SetFilePointer( hFile, 0, 0, FILE_CURRENT ); pCache->dwFileSize = GetFileSize(hFile, 0); pCache->fAccess = TRUE; // Assume Read Access } else { CloseHandle(pCache->hMappedFileObject); pCache->hMappedFileObject = 0; // so FreeMapFileCache doesn't double-close the handle } } return(pCache->fAccess); } /* FlushMapFileCaches * * Entry - None * * Exit - None * */ VOID FlushMapFileCaches() { PHMAPPEDFILEALIAS pCache; if (fCacheInit) { return; } WOW32ASSERT(gpCacheHead != NULL); pCache = gpCacheHead; dwTotalCacheBytes = dwTotalCacheAccess = 0; while ( (pCache->hpfNext !=0) ) { if (pCache->hfile32 != 0) { FREEMAPFILECACHE(pCache->hfile32); } pCache = pCache->hpfNext; } } /* WK32WOWFileWrite - Write to a file * * * Entry - fh File Handle * bufsize Count to read * lpBuf Buffer Address * * Exit * SUCCESS * Count of bytes read * * FAILURE * system status code * Concept Borrowed from demFileWrite * */ ULONG FASTCALL WK32WOWFileWrite (PVDMFRAME pFrame) { HANDLE hFile; DWORD dwBytesWritten; DWORD bufsize; PBYTE pb1; register PWOWFILEWRITE16 parg16; PHMAPPEDFILEALIAS pCache; PDOSSFT pSFT; GETARGPTR(pFrame, sizeof(*parg16), parg16); bufsize = FETCHDWORD(parg16->bufsize); if ( HIWORD(parg16->lpBuf) == 0 ) { pb1 = (PVOID)GetRModeVDMPointer(FETCHDWORD(parg16->lpBuf)); } else { GETVDMPTR(parg16->lpBuf, bufsize, pb1); } hFile = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); if (!hFile) { dwBytesWritten = 0xffff0006; // DOS Invalid Handle Error goto Cleanup; } if (pSFT->SFT_Flags & 0x80) { // Is this a device handle? dwBytesWritten = 0xffffffff; // Let DOS handle device handles. goto Cleanup; // kernel QuickWrite passes to DOS } // after any error (dx=ffff) // We don't Support Writing to Mapped Files if ( (pCache = FINDMAPFILECACHE(hFile)) && pCache->fAccess ) { if (pCache->lpStartingAddressOfView) { SetFilePointer( hFile, pCache->lFilePointer, NULL, FILE_BEGIN ); FREEMAPFILECACHE(hFile); } pCache->fAccess = FALSE; pCache->hfile32 = hFile; } // In DOS CX=0 truncates or extends the file to current file pointer. if (bufsize == 0){ if (SetEndOfFile(hFile) == FALSE) { dwBytesWritten = GetLastError() | 0xffff0000; LOGDEBUG(fileiolevel, ("IOFileWrite fh:%X fh32:%X SetEndOfFile failed pDsc %08X\n", FETCHWORD(parg16->fh),hFile,FETCHDWORD(parg16->lpBuf))); } else { dwBytesWritten = 0; LOGDEBUG(fileiolevel, ("IOFileWrite fh:%X fh32:%X truncated at current position pDsc %08X\n", FETCHWORD(parg16->fh),hFile,FETCHDWORD(parg16->lpBuf))); } } else { if (IsVdmRedirLoaded() && VrIsNamedPipeHandle(hFile)) { if (!VrWriteNamedPipe(hFile, pb1, (DWORD)bufsize, &dwBytesWritten)) { dwBytesWritten = GetLastError() | 0xffff0000; } } else { if (( WriteFile (hFile, pb1, (DWORD)bufsize, &dwBytesWritten, NULL)) == FALSE){ dwBytesWritten = GetLastError() | 0xffff0000; } } LOGDEBUG(fileiolevel, ("IOFileWrite fh:%X fh32:%X Bytes req:%X written:%X pDsc %08X\n", FETCHWORD(parg16->fh),hFile,bufsize,dwBytesWritten,FETCHDWORD(parg16->lpBuf))); } Cleanup: FREEVDMPTR(pb1); FREEARGPTR(parg16); return (dwBytesWritten); } /* WK32WOWFileLSeek - Change File Pointer * * * Entry - fh File Handle * fileOffset New Location * mode Positioning Method * 0 - File Absolute * 1 - Relative to Current Position * 2 - Relative to end of file * * Exit * SUCCESS * New Location * * FAILURE * system status code * */ ULONG FASTCALL WK32WOWFileLSeek (PVDMFRAME pFrame) { HANDLE hFile; ULONG dwLoc; PHMAPPEDFILEALIAS pCache; register PWOWFILELSEEK16 parg16; PDOSSFT pSFT; #if (FILE_BEGIN != 0 || FILE_CURRENT != 1 || FILE_END !=2) #error "Win32 values not DOS compatible" # #endif GETARGPTR(pFrame, sizeof(*parg16), parg16); hFile = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); if (!hFile) { FREEARGPTR(parg16); return(0xffff0006); } if (pSFT->SFT_Flags & 0x80) { // Is this a device handle? FREEARGPTR(parg16); // Let DOS handle device handles. return(0xffffffff); // kernel QuickLSeek passes to DOS } // after any error (dx=ffff) if ( (vptopPDB == parg16->lpPDB) && (pCache = FINDMAPFILECACHE(hFile)) && pCache->fAccess ) { // File Is in the Cache // Update our Seek Pointer LOGDEBUG(fileiolevel, ("CachedSeek fh:%04X Mode %04X pointer %08X\n",FETCHWORD(parg16->fh),FETCHWORD(parg16->mode),FETCHDWORD(parg16->fileOffset))); switch(FETCHWORD(parg16->mode)) { case FILE_BEGIN: pCache->lFilePointer = FETCHDWORD(parg16->fileOffset); break; case FILE_CURRENT: pCache->lFilePointer += (LONG)FETCHDWORD(parg16->fileOffset); break; case FILE_END: pCache->lFilePointer = pCache->dwFileSize + (LONG)FETCHDWORD(parg16->fileOffset); break; } dwLoc = pCache->lFilePointer; } else { DWORD dwLocHi = 0; // File is NOT in Cache so Just do normal Seek. if (((dwLoc = SetFilePointer (hFile, FETCHDWORD(parg16->fileOffset), &dwLocHi, (DWORD)FETCHWORD(parg16->mode))) == -1L) && (GetLastError() != NO_ERROR)) { dwLoc = GetLastError() | 0xffff0000; return(dwLoc); } if (dwLocHi) { // file pointer has been moved > FFFFFFFF. Truncate it dwLocHi = 0; if (((dwLoc = SetFilePointer (hFile, dwLoc, &dwLocHi, FILE_BEGIN)) == -1L) && (GetLastError() != NO_ERROR)) { dwLoc = GetLastError() | 0xffff0000; return(dwLoc); } } } FREEARGPTR(parg16); return (dwLoc); } BOOL IsDevice(PSTR pszFilePath) { PSTR pfile, pend; int length; UCHAR device_part[9]; PSYSDEV pSys; PUCHAR p; // Determine the start of the file part of the path. if (pfile = WOW32_strrchr(pszFilePath, '\\')) { pfile++; } else if (pszFilePath[0] && pszFilePath[1] == ':') { pfile = pszFilePath + 2; } else { pfile = pszFilePath; } // Compute length of pre-dot file name part. for (pend = pfile; *pend; pend++) { if (*pend == '.') { break; } } if (pend > pfile && *(pend - 1) == ':') { pend--; } length = (pend - pfile); if (length > 8) { return FALSE; } RtlFillMemory(device_part + length, 8 - length, ' '); RtlCopyMemory(device_part, pfile, length); device_part[8] = 0; WOW32_strupr(device_part); // Now go through the device chain comparing each entry with // the device part extracted from the file path. pSys = pDeviceChain; for (;;) { p = pSys->sdevDevName; if (RtlEqualMemory(device_part, p, 8)) { return TRUE; } if (LOWORD(pSys->sdevNext) == 0xFFFF) { break; } pSys = (PSYSDEV) GetRModeVDMPointer(pSys->sdevNext); } // If it wasn't in the chain then it's not a device. return FALSE; } PSTR NormalizeDosPath(PSTR pszPath, WORD wCurrentDriveNumber, PBOOL ItsANamedPipe) { static CHAR NewPath[MAX_PATH]; PSTR p; DWORD cbFilename; *ItsANamedPipe = FALSE; // Special case the NULL path. if (pszPath[0] == 0) { return pszPath; } // Apps can pass D:\\computer\share to int 21 open // Win 32 createfile can't cope with the leading drive letter // so remove it as necessary. if (WOW32_strncmp(pszPath+1,":\\\\",3) == 0) { pszPath++; pszPath++; } // // if the name specifies a named pipe, load VDMREDIR. If this fails return // an error // if (IsNamedPipeName(pszPath)) { if (!LoadVdmRedir()) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NULL; } *ItsANamedPipe = TRUE; // // convert \\\PIPE\foo\bar\etc to \\.\PIPE\... // if we already allocated a buffer for the slash conversion use // that else this call will allocate another buffer (we don't // want to write over DOS memory) // p = VrConvertLocalNtPipeName(NULL, pszPath); if (!p) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return p; } // if there is no drive letter at the beginning of the path // then prepend a drive letter and ':' to the beginning // of the path. if (pszPath[1] != ':' && !(IS_ASCII_PATH_SEPARATOR(pszPath[0]) && IS_ASCII_PATH_SEPARATOR(pszPath[1]))) { cbFilename = strlen( pszPath ) + 1; if( cbFilename > MAX_PATH - 2) { SetLastError(ERROR_PATH_NOT_FOUND); return NULL; } NewPath[0] = wCurrentDriveNumber + 'A'; NewPath[1] = ':'; RtlCopyMemory(NewPath + 2, pszPath, cbFilename); pszPath = NewPath; //return this value } return TruncatePath83(NewPath, pszPath); } /* TruncatePath83 - Takes as input a path and make sure it has an 8.3 file name * * Entry - pstr-> target buffer[MAX_PATH] * pstr-> string to convert * It is assumed that the string has at the very least a '?:' as * its first two characters, where ? is any drive letter. * * Exit * SUCCESS * return value-> converted string * * FAILURE * return value==NULL * */ PSTR TruncatePath83(PSTR NewPath, PSTR pszPath) { PSTR pPathName, pPathNameSlash, pPathExt; // // If the string is not already in the buffer, copy it in // if (NewPath != pszPath) { strcpy (NewPath, pszPath); } // // make sure file name and extension are 8.3 // pPathName = WOW32_strrchr(NewPath, '\\'); pPathNameSlash = WOW32_strrchr(NewPath, '/'); if ((NULL == pPathName) && (NULL == pPathNameSlash)) { pPathName = &NewPath[2]; // 1st char after '?:' } else { if (pPathNameSlash > pPathName) { pPathName = pPathNameSlash; } pPathName++; // 1st char in name } if (NULL != (pPathExt = WOW32_strchr(pPathName, '.'))) { // is there a period? pPathExt++; // 1st char in ext if (strlen(pPathExt) > 3) { // extension too big? pPathExt[3] = 0; // truncate extension } pPathExt--; // back to period if (pPathExt - pPathName > 8) { // is name too big? strcpy (&pPathName[8], pPathExt); // truncate file name } } else { if (strlen(pPathName) > 8) { // is name too big? pPathName[8] = 0; // truncate file name } } return(NewPath); } /* ExpandDosPath - Expands paths of the form "*.*" to "????????.???" * and merges in currentdirectory info * * N.B. This routine does not handle long file names * * Entry - pstr-> string to convert * * Exit * SUCCESS * return value-> converted string * * FAILURE * return value==NULL * */ PSTR ExpandDosPath(PSTR pszPathGiven) { static CHAR NewPath[MAX_PATH],TempPath[MAX_PATH]; // this is not reentrant USHORT usNewPathIndex = 0; USHORT usFillCount = 8; UCHAR ucCurrentChar, ucDrive; PSTR pszPath = TempPath; char *pFilePart; if (!pszPathGiven ) { return NULL; } // There is a bug in this routine where it is ignoring /. DOS treats them // same as \. As matches for \ are spread all over this routine, its // much safer to take an entry pass over the string and covert / to \. // sudeepb 29-Jun-1995 while (pszPathGiven[usNewPathIndex]) { if (pszPathGiven[usNewPathIndex] == '/') pszPath [usNewPathIndex] = '\\'; else pszPath [usNewPathIndex] = pszPathGiven[usNewPathIndex]; usNewPathIndex++; } pszPath [usNewPathIndex] = '\0'; // // copy filepath into NewPath, add in current drive, directory // if relative path name. // // Note: should be changed later to use GetFullPathName, since // it is equivalent, and should have the correct curr dir, // cur drive. be wary of trailing dots in GetFullPathName // ie. "*." is not the same as "*" // if (WOW32_strncmp(pszPath, "\\\\", 2)) { // should be drive letter ucDrive = *pszPath++; if ((*pszPath++ != ':') || (!isalpha(ucDrive))) { SetLastError(ERROR_PATH_NOT_FOUND); return NULL; } NewPath[0] = ucDrive; NewPath[1] = ':'; usNewPathIndex = 2; if (*pszPath != '\\') { NewPath[usNewPathIndex++] = '\\'; if (DosWowGetCurrentDirectory ((UCHAR) (toupper(ucDrive)-'A'+1), &NewPath[usNewPathIndex])) { return NULL; } usNewPathIndex = (USHORT)strlen(NewPath); if (usNewPathIndex > 3) { NewPath[usNewPathIndex++] = '\\'; } } pFilePart = WOW32_strrchr(pszPath, '\\'); if (pFilePart) { pFilePart++; } else { pFilePart = pszPath; } } else { // check for UNC name, if not UNC, path not found usNewPathIndex = 2; NewPath[0] = NewPath[1] = '\\'; pszPath += 2; pFilePart = WOW32_strrchr(pszPath, '\\'); if (!pFilePart) { SetLastError(ERROR_PATH_NOT_FOUND); return NULL; } pFilePart++; } while (pszPath < pFilePart && usNewPathIndex < MAX_PATH) { NewPath[usNewPathIndex++] = *pszPath++; } ucCurrentChar = *pszPath++; while ((usNewPathIndex < MAX_PATH) && (ucCurrentChar)) { if (ucCurrentChar == '*') { // // expand "*"s to "?" // while ((usFillCount > 0) && (usNewPathIndex < MAX_PATH)) { NewPath[usNewPathIndex++] = '?'; usFillCount--; } // // skip to next valid character after expansion // while ((ucCurrentChar != 0) && (ucCurrentChar != '.') && (ucCurrentChar != '\\')) { ucCurrentChar = *pszPath++; } } else { if (ucCurrentChar == '.') { usFillCount = 3; // fill count for .ext } else if (ucCurrentChar == '\\') { usFillCount = 8; // fill count for fn. } else { usFillCount--; } NewPath[usNewPathIndex++] = ucCurrentChar; // // get next character (except if no more are left) // if (ucCurrentChar) { ucCurrentChar = *pszPath++; } } } if (usNewPathIndex >= MAX_PATH) { SetLastError(ERROR_PATH_NOT_FOUND); return NULL; } NewPath[usNewPathIndex] = 0; // trailing zero return NewPath; } BOOL IsCdRomFile(PSTR pszPath) { UCHAR pszRootDir[MAX_PATH]; UCHAR file_system[MAX_PATH]; int i, j; // The given path is either a network path or has D: at the start. if (!pszPath[0]) { return FALSE; } if (pszPath[1] == ':') { pszRootDir[0] = pszPath[0]; pszRootDir[1] = ':'; pszRootDir[2] = '\\'; pszRootDir[3] = 0; } else if (IS_ASCII_PATH_SEPARATOR(pszPath[0]) && IS_ASCII_PATH_SEPARATOR(pszPath[1])) { j = 0; for (i = 2; pszPath[i]; i++) { if (IS_ASCII_PATH_SEPARATOR(pszPath[i])) { if (++j == 2) { break; } } } memcpy(pszRootDir, pszPath, i); pszRootDir[i] = '\\'; pszRootDir[i+1] = 0; } else { return FALSE; } if (GetVolumeInformationOem(pszRootDir, NULL, 0, NULL, NULL, NULL, file_system, MAX_PATH) && !WOW32_stricmp(file_system, "CDFS")) { return TRUE; } return FALSE; } /* WK32WOWFileOpen - Open a file * * * Entry - pszPath Path of file to open * wAccess Desired access * * Exit * SUCCESS * handle number * * FAILURE * system status code * -1 to indicate the the requested open was for device and * hence not attempted * */ ULONG FASTCALL WK32WOWFileOpen(PVDMFRAME pFrame) { PWOWFILEOPEN16 parg16; HANDLE hFile; ULONG ul; SHORT iDosHandle; PSTR pszPath; WORD wAccess; DWORD dwWinAccess; DWORD dwWinShareMode; WORD tmp; PBYTE pJFT; PDOSSFT pSft; PSTR lpFileName; BOOL ItsANamedPipe = FALSE; PHMAPPEDFILEALIAS pCache; PHMAPPEDFILEALIAS pTempCache; PTD ptd; PWCH pwch; BOOL first = TRUE; UNICODE_STRING UniFile; // // Get arguments. // GETARGPTR(pFrame, sizeof(*parg16), parg16); pszPath = SEGPTR(FETCHWORD(parg16->pszPathSegment), FETCHWORD(parg16->pszPathOffset)); wAccess = FETCHWORD(parg16->wAccess); // // If the path requested is a device then just pass it // through to DOS. // if (IsDevice(pszPath)) { FREEARGPTR(parg16); ul = 0xFFFFFFFF; // magic value to indicate that the open goto Done; // was not attempted. } if ((iDosHandle = VDDAllocateDosHandle(0, (PVOID *)&pSft, &pJFT)) < 0) { FREEARGPTR(parg16); ul = ERROR_TOO_MANY_OPEN_FILES | 0xFFFF0000; goto Done; } pCache = ALLOCMAPFILECACHE(); pCache->hfile32 = 0; pCache->fAccess = FALSE; // // Compute dwWinAccess and dwWinShareMode from wAccess. // tmp = wAccess&0x7; if (tmp == 0) { pCache->fAccess = TRUE; dwWinAccess = GENERIC_READ; } else if (tmp == 1) { dwWinAccess = GENERIC_WRITE; } else if (tmp == 2) { dwWinAccess = GENERIC_READ | GENERIC_WRITE; } else { FREEARGPTR(parg16); ul = ERROR_INVALID_ACCESS | 0xFFFF0000; goto Done; } tmp = wAccess&0x70; dwWinShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if (tmp == 0x10) { dwWinShareMode = 0; } else if (tmp == 0x20) { dwWinShareMode = FILE_SHARE_READ; } else if (tmp == 0x30) { dwWinShareMode = FILE_SHARE_WRITE; } // // open the file. If we think its a named pipe then use FILE_FLAG_OVERLAPPED // because the client might use DosReadAsyncNmPipe or DosWriteAsyncNmPipe // and the only way to accomplish that is to open the named pipe handle in // overlapped I/O mode now // WOW32ASSERT(DosWowData.lpCurDrv != (ULONG) NULL); lpFileName = NormalizeDosPath(pszPath, (WORD) (*(PUCHAR)DosWowData.lpCurDrv), &ItsANamedPipe); if (lpFileName) { // // This hack fixes the "Living Books" install program, which opens // a file DENY ALL, and then tries to reopen the same file. On DOS, // this succeeds if it is done from the same task, but it doesn't work // on NT. So here we open it without the sharing restrictions, since it // is anyway just a type of .INF file on the CD-ROM. // Currently, the test is very specific, but I can't think of a good // way to do this generically. // if ((dwWinShareMode == 0) && ((ptd = CURRENTPTD())->dwWOWCompatFlagsEx & WOWCFEX_SAMETASKFILESHARE) && (IsCdRomFile(lpFileName)) && (!WOW32_stricmp(pszPath, "install.txt"))) { dwWinShareMode = FILE_SHARE_READ; } hFile = CreateFileOem(lpFileName, dwWinAccess, dwWinShareMode | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, ItsANamedPipe ? FILE_FLAG_OVERLAPPED : 0, NULL ); // If the open failed, includes a request for WRITE, and was to // a CD-ROM then try again without the write request. Since // this is how DOS does it. if (hFile == INVALID_HANDLE_VALUE && dwWinAccess&GENERIC_WRITE && !ItsANamedPipe && IsCdRomFile(lpFileName)) { dwWinAccess &= ~GENERIC_WRITE; hFile = CreateFileOem(lpFileName, dwWinAccess, dwWinShareMode | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, ItsANamedPipe ? FILE_FLAG_OVERLAPPED : 0, NULL ); } // See if they are trying to open a .ini file, and if it doesn't exist, // copy it to the user's home dir from the system dir else if ((gpfnTermsrvCORIniFile != NULL) && (hFile == INVALID_HANDLE_VALUE) && WOW32_strstr(lpFileName,".INI")) { pwch = malloc_w((MAX_PATH + 1)*sizeof(WCHAR)); if (pwch) { UniFile.Buffer = pwch; UniFile.MaximumLength = (MAX_PATH+1)*sizeof(WCHAR); RtlMultiByteToUnicodeN(pwch, (MAX_PATH+1)*sizeof(WCHAR), NULL, lpFileName, strlen(lpFileName) + 1); if (RtlDosPathNameToNtPathName_U(pwch, &UniFile, NULL, NULL)) { gpfnTermsrvCORIniFile(&UniFile); RtlFreeHeap(RtlProcessHeap(), 0, UniFile.Buffer); hFile = CreateFileOem(lpFileName, dwWinAccess, dwWinShareMode, NULL, OPEN_EXISTING, 0, INVALID_HANDLE_VALUE ); } free_w(pwch); } } else { // If all attempts to open the file failed, it might be one of the // 9x special path, so try mapping it to NT special path // i.e. c:\winnt\startm~1 becomes c:\docume~1\alluse~1\startm~1 UCHAR szMappedPath[MAX_PATH]; if(!ItsANamedPipe && W32Map9xSpecialPath(lpFileName,szMappedPath) ){ lpFileName=&szMappedPath[0]; hFile = CreateFileOem(lpFileName, dwWinAccess, dwWinShareMode | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL ); } } } else { hFile = INVALID_HANDLE_VALUE; SetLastError(ERROR_FILE_NOT_FOUND); } if (hFile == INVALID_HANDLE_VALUE) { ul = GetLastError() | 0xFFFF0000; LOGDEBUG(fileoclevel,("WK32WOWFileOpen: %s mode:%02X failed error %d\n",pszPath, wAccess, GetLastError())); FREEARGPTR(parg16); if (ItsANamedPipe) { LocalFree(lpFileName); } pJFT[iDosHandle] = 0xFF; // undo VDDAllocateDosHandle pSft->SFT_Ref_Count--; goto Done; } else if (ItsANamedPipe) { // // we have to keep some info around when we open a named pipe // VrAddOpenNamedPipeInfo(hFile, lpFileName); } LOGDEBUG(fileoclevel,("WK32WOWFileOpen: %s hFile:%08X fh:%04X mode:%02X\n",pszPath, hFile,(WORD)iDosHandle,wAccess)); // Be defensive. If the app has managed to close the file via DOSEmulation // then we need to make sure we don't have the old file handle in our cache. if ( pTempCache = FINDMAPFILECACHE(hFile) ) { pTempCache->fAccess = FALSE; FREEMAPFILECACHE(hFile); } pCache->hfile32 = hFile; if ((vptopPDB == parg16->lpPDB) && (pCache->fAccess)) { W32MapViewOfFile( pCache, hFile); } else { FREEMAPFILECACHE(hFile); } // // Fill in the SFT. // VDDAssociateNtHandle(pSft, hFile, wAccess); // // Set the SFT flags appropriately for an open file // if (IsCharAlpha(lpFileName[0]) && (':' == lpFileName[1])) { UCHAR ch = toupper(lpFileName[0]) - 'A'; pSft->SFT_Flags = (USHORT)(ch) | (pSft->SFT_Flags & 0xff00); } FREEARGPTR(parg16); if (ItsANamedPipe) { LocalFree(lpFileName); pSft->SFT_Flags |= SFT_NAMED_PIPE; } ul = iDosHandle; Done: return ul; } /* WK32WOWFileCreate - Create a file * * * Entry - pszPath Path of file to create * * Exit * SUCCESS * handle number * * FAILURE * system status code * -1 to indicate the the requested open was for device and * hence not attempted * */ ULONG FASTCALL WK32WOWFileCreate(PVDMFRAME pFrame) { PWOWFILECREATE16 parg16; HANDLE hFile; ULONG ul; SHORT iDosHandle; PSTR pszPath; PBYTE pJFT; PDOSSFT pSft; PSTR lpFileName; ULONG attributes; BOOL ItsANamedPipe = FALSE; PTD ptd; BOOL bFirstTry = TRUE; // // Get arguments. // GETARGPTR(pFrame, sizeof(WOWFILECREATE16), parg16); pszPath = SEGPTR(FETCHWORD(parg16->pszPathSegment), FETCHWORD(parg16->pszPathOffset)); if (!(attributes = (DWORD) FETCHWORD(parg16->wAttributes) & 0x27)) { attributes = FILE_ATTRIBUTE_NORMAL; } // // If the path requested is a device then just pass it // through to DOS. // if (IsDevice(pszPath)) { FREEARGPTR(parg16); ul = 0xFFFFFFFF; // magic value to indicate that the open goto Done; // was not attempted. } if ((iDosHandle = VDDAllocateDosHandle(0, (PVOID *)&pSft, &pJFT)) < 0) { ul = ERROR_TOO_MANY_OPEN_FILES | 0xFFFF0000; goto Done; } // // open the file. If we think its a named pipe then use FILE_FLAG_OVERLAPPED // because the client might use DosReadAsyncNmPipe or DosWriteAsyncNmPipe // and the only way to accomplish that is to open the named pipe handle in // overlapped I/O mode now // WOW32ASSERT(DosWowData.lpCurDrv != (ULONG) NULL); lpFileName = NormalizeDosPath(pszPath, (WORD) (*(PUCHAR)DosWowData.lpCurDrv), &ItsANamedPipe); if (lpFileName) { Try_Create: hFile = CreateFileOem(lpFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, ItsANamedPipe ? attributes | FILE_FLAG_OVERLAPPED : attributes, NULL ); if ((hFile == INVALID_HANDLE_VALUE) && (bFirstTry) && (GetLastError() == ERROR_USER_MAPPED_FILE)) { // Some Windows Install Programs try to overwrite a .FON font file // during installation - without calling RemoveFontResource(); // If the font is in GDI32's cache the create will fail. if (RemoveFontResourceOem(lpFileName)) { LOGDEBUG(0,("WK32FileCreate: RemoveFontResource on %s \n", lpFileName)); SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0); } bFirstTry = FALSE; goto Try_Create; } } else { hFile = INVALID_HANDLE_VALUE; SetLastError(ERROR_FILE_NOT_FOUND); } if (hFile == INVALID_HANDLE_VALUE) { LOGDEBUG(fileoclevel,("WK32WOWFileCreate: %s failed error %d\n",pszPath, GetLastError())); if (ItsANamedPipe) { LocalFree(lpFileName); } pJFT[iDosHandle] = 0xFF; // undo VDDAllocateDosHandle pSft->SFT_Ref_Count--; ul = GetLastError() | 0xFFFF0000; goto Done; } else { if (ItsANamedPipe) { // // we have to keep some info around when we open a named pipe // VrAddOpenNamedPipeInfo(hFile, lpFileName); } // // Symantec Install 3.1 shipped with Q&A 4.0 wants to be sure it's the // only program running, so instead of nicely asking the user to close // other programs, it changes the shell= line in system.ini to its // install.exe, then restarts Windows and continues its installation. // To reverse this change, they sloppily restore a saved copy of // system.ini rather than use the API. Since the shell= line is // mapped to the registry, this sloppy method doesn't work. Later // when they want to create program groups, they try to start DDE // with the shell, and when that fails they read the shell= line // and start the specified program. On NT 4.0, that would be the // install program and things go poorly. On 3.51 they would eventually // give up and launch progman.exe, but since the shell has changed // this no longer works. // // We fix this by detecting their creation (overwriting) of system.ini // and at that point repairing the shell= value to Explorer.exe. This // operation is done by INSTBIN.EXE, module name INSTBIN, which is a // relief because I thought I would have to set WOWCFEX_RESTOREEXPLORER // for module name INSTALL (the primary Symantec Install EXE). // // Thanks to Bob Day for figuring out what the app was doing, I simply // came up with a workaround and implemented it. // // DaveHart 28-Jan-96 // WOW32ASSERTMSG(vptopPDB != parg16->lpPDB, "KRNL386 does create files, disable this assertion and add test below.\n"); if ((ptd = CURRENTPTD())->dwWOWCompatFlagsEx & WOWCFEX_RESTOREEXPLORER) { char szLowerPath[MAX_PATH]; strcpy(szLowerPath, pszPath); WOW32_strlwr(szLowerPath); if (WOW32_strstr(szLowerPath, szSystemDotIni)) { if (IsModuleSymantecInstall(ptd->hMod16)) { WritePrivateProfileString(szBoot, szShell, szExplorerDotExe, szSystemDotIni); LOGDEBUG(LOG_ALWAYS, ("Restored shell=Explorer.exe for Symantec Install hack.\n")); } } } } FREEARGPTR(parg16); LOGDEBUG(fileoclevel,("WK32WOWFileCreate: %s hFile:%08X fh:%04X\n",pszPath, hFile,(WORD)iDosHandle)); // // Fill in the SFT. // VDDAssociateNtHandle(pSft, hFile, 2); // // Set the SFT flags appropriately for an open file // if (IsCharAlpha(lpFileName[0]) && (':' == lpFileName[1])) { UCHAR ch = toupper(lpFileName[0]) - 'A'; pSft->SFT_Flags = (USHORT)(ch) | (pSft->SFT_Flags & 0xff00); } if (ItsANamedPipe) { LocalFree(lpFileName); pSft->SFT_Flags |= SFT_NAMED_PIPE; } ul = iDosHandle; Done: return ul; } /* WK32WOWFileClose - Close a file * * * Entry - hFile Handle of file to close * * Exit * SUCCESS * 0 * * FAILURE * Invalid handle status * -1 is returned if this handle is for a device. * */ ULONG FASTCALL WK32WOWFileClose(PVDMFRAME pFrame) { PWOWFILECLOSE16 parg16; PBYTE pJFT; HANDLE Handle; PDOSSFT pSFT; ULONG ul; GETARGPTR(pFrame, sizeof(*parg16), parg16); Handle = VDDRetrieveNtHandle(0, (SHORT) parg16->hFile, (PVOID *)&pSFT, &pJFT); if (!Handle || !pSFT->SFT_Ref_Count) { ul = ERROR_INVALID_HANDLE | 0xFFFF0000; goto Cleanup; } if (pSFT->SFT_Flags & 0x80) { // Is this a device handle? ul = 0xFFFFFFFF; // Let DOS handle device handles. goto Cleanup; } // Set the JFT entry to 0xFF to free it up. pJFT[FETCHWORD(parg16->hFile)] = 0xFF; // Decrement reference count. pSFT->SFT_Ref_Count--; // Close the handle if the reference count was set to zero. if (!pSFT->SFT_Ref_Count) { FREEMAPFILECACHE(Handle); LOGDEBUG(fileoclevel,("WK32WOWFileClose: Close Handle:%X fh32:%X\n", parg16->hFile, Handle)); if (!CloseHandle(Handle)) { ul = GetLastError() | 0xFFFF0000; goto Cleanup; } // // check if the handle being closed references a named pipe - we have to // delete some info that we keep for the open named pipe // if (!pSFT->SFT_Ref_Count && IsVdmRedirLoaded()) { VrRemoveOpenNamedPipeInfo(Handle); } } ul = 0; Cleanup: FREEARGPTR(parg16); return ul; } /* WK32WOWFileGetAttributes - Get file attributes * * * Entry - pszPath File to get attributes from * * Exit * SUCCESS * Attributes for file * * FAILURE * system status code * */ ULONG FASTCALL WK32WOWFileGetAttributes(PVDMFRAME pFrame) { PWOWFILEGETATTRIBUTES16 parg16; PSTR pszPath, lpFileName; ULONG attributes, l; BOOL ItsANamedPipe = FALSE; PWCH pwch; BOOL first = TRUE; UNICODE_STRING UniFile; GETARGPTR(pFrame, sizeof(*parg16), parg16); pszPath = SEGPTR(FETCHWORD(parg16->pszPathSegment), FETCHWORD(parg16->pszPathOffset)); FREEARGPTR(parg16); WOW32ASSERT(DosWowData.lpCurDrv != (ULONG) NULL); if (lpFileName = NormalizeDosPath(pszPath, (WORD) (*(PUCHAR)DosWowData.lpCurDrv), &ItsANamedPipe)) { attributes = GetFileAttributesOem(lpFileName); // See if they are trying to chmod a .ini file, and if so see if we // should copy it to the user's home dir if ((gpfnTermsrvCORIniFile != NULL) && (attributes == 0xffffffff) && WOW32_strstr(lpFileName,".INI")) { pwch = malloc_w((MAX_PATH + 1)*sizeof(WCHAR)); if (pwch) { UniFile.Buffer = pwch; UniFile.MaximumLength = (MAX_PATH+1)*sizeof(WCHAR); RtlMultiByteToUnicodeN(pwch, (MAX_PATH+1)*sizeof(WCHAR), NULL, lpFileName, strlen(lpFileName) + 1); if (RtlDosPathNameToNtPathName_U(pwch, &UniFile, NULL, NULL)) { gpfnTermsrvCORIniFile(&UniFile); RtlFreeHeap(RtlProcessHeap(), 0, UniFile.Buffer); attributes = GetFileAttributesOem(lpFileName); } free_w(pwch); } } } else { attributes = 0xFFFFFFFF; } if (ItsANamedPipe) { LocalFree(lpFileName); } if (attributes == 0xFFFFFFFF) { return (0xFFFF0000 | GetLastError()); } // Success! // Check to make sure that we didn't have a trailing backslash // on this one. In that case we should fail with PATH_NOT_FOUND. l = strlen(pszPath); if (l > 0 && IS_ASCII_PATH_SEPARATOR(pszPath[l - 1]) && l != 1 && !(l == 3 && pszPath[1] == ':')) { return (0xFFFF0000 | ERROR_PATH_NOT_FOUND); } if (attributes == FILE_ATTRIBUTE_NORMAL) attributes = 0; else attributes &= DOS_ATTR_MASK; // SudeepB - 28-Jul-1997 // // For CDFS, Win3.1/DOS/Win95, only return FILE_ATTRIBUTE_DIRECTORY (10) // for directories while WinNT returns // FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY (11). // Some VB controls that app setups use, depend on getting // FILE_ATTRIBUTE_DIRECTORY (10) only or otherwise are broken. // An example of this is Cliffs StudyWare series. if (attributes == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY)) { if(IsCdRomFile(lpFileName)) attributes = FILE_ATTRIBUTE_DIRECTORY; } return attributes; } /* WK32WOWFileSetAttributes - Set file attributes * * * Entry - pszPath File to get attributes from * * Exit * SUCCESS * Attributes for file * * FAILURE * system status code * */ ULONG FASTCALL WK32WOWFileSetAttributes(PVDMFRAME pFrame) { PWOWFILESETATTRIBUTES16 parg16; PSTR pszPath, lpFileName; ULONG attributes, l, dwReturn; BOOL ItsANamedPipe = FALSE; GETARGPTR(pFrame, sizeof(WOWFILESETATTRIBUTES16), parg16); pszPath = SEGPTR(FETCHWORD(parg16->pszPathSegment), FETCHWORD(parg16->pszPathOffset)); if (!(attributes = (DWORD) FETCHWORD(parg16->wAttributes))) { attributes = FILE_ATTRIBUTE_NORMAL; } FREEARGPTR(parg16); // Check to make sure that we didn't have a trailing backslash // on this one. In that case we should fail with PATH_NOT_FOUND. l = strlen(pszPath); WOW32ASSERT(DosWowData.lpCurDrv != (ULONG) NULL); if ((l > 0 && IS_ASCII_PATH_SEPARATOR(pszPath[l - 1]) && l != 1 && !(l == 3 && pszPath[1] == ':')) || !(lpFileName = NormalizeDosPath(pszPath, (WORD) (*(PUCHAR)DosWowData.lpCurDrv), &ItsANamedPipe))) { dwReturn = 0xFFFF0000 | ERROR_PATH_NOT_FOUND; } else { attributes &= DOS_ATTR_MASK; if (SetFileAttributesOem(lpFileName, attributes)) { dwReturn = 0; } else { dwReturn = 0xFFFF0000 | GetLastError(); } } if (ItsANamedPipe) { LocalFree(lpFileName); } return (dwReturn); } /* WK32WOWFileGetDateTime - Get file date and time * * * Entry - fh DOS file handle * * Exit * SUCCESS * date and time for file * * FAILURE * 0xFFFF * */ // this function lives in ntvdm.exe // see demlfn.c for details extern ULONG demGetFileTimeByHandle_WOW(HANDLE); ULONG FASTCALL WK32WOWFileGetDateTime(PVDMFRAME pFrame) { PWOWFILEGETDATETIME16 parg16; HANDLE Handle; PDOSSFT pSFT; GETARGPTR(pFrame, sizeof(*parg16), parg16); Handle = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); FREEARGPTR(parg16); if (!Handle || (pSFT->SFT_Flags & 0x80)) { // Let DOS handle device handles. return 0xFFFF; } return(demGetFileTimeByHandle_WOW(Handle)); } /* WK32WOWFileSetDateTime - Set file date and time * * * Entry - fh DOS file handle * date * time * * Exit * SUCCESS * date and time for file set * * FAILURE * 0xFFFF * */ ULONG FASTCALL WK32WOWFileSetDateTime(PVDMFRAME pFrame) { PWOWFILESETDATETIME16 parg16; HANDLE Handle; FILETIME LastWriteTime, LocalTime; USHORT wDate, wTime; PDOSSFT pSFT; GETARGPTR(pFrame, sizeof(WOWFILESETDATETIME16), parg16); Handle = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); wDate = FETCHWORD(parg16->date); wTime = FETCHWORD(parg16->time); FREEARGPTR(parg16); if (!Handle || (pSFT->SFT_Flags & 0x80) || // Let DOS handle device handles. !DosDateTimeToFileTime(wDate, wTime, &LocalTime) || !LocalFileTimeToFileTime(&LocalTime, &LastWriteTime) || !SetFileTime(Handle, NULL, NULL, &LastWriteTime)) { return 0xFFFF; } return (0); } /* WK32WOWFileLock - Locks or unlocks file data * * * Entry - fh DOS file handle * cbRegionOffset Start of file portion to lock or unlock * cbRegionLength Length of file portion to lock or unlock * al 0 for lock, 1 for unlock * * Exit * SUCCESS * 0 * * FAILURE * system status code * */ ULONG FASTCALL WK32WOWFileLock(PVDMFRAME pFrame) { PWOWFILELOCK16 parg16; HANDLE Handle; UCHAR al; DWORD cbOffset; DWORD cbLength; PDOSSFT pSFT; GETARGPTR(pFrame, sizeof(*parg16), parg16); Handle = VDDRetrieveNtHandle(0, (SHORT) parg16->fh, (PVOID *)&pSFT, NULL); if (pSFT->SFT_Flags & 0x80) { // Is this a device handle? FREEARGPTR(parg16); // Let DOS handle device handles. return 0xffffffff; // kernel QuickLock passes to DOS } // after any error except 21 (dx=ffff, ax!=21) al = FETCHWORD(parg16->ax) & 0xFF; cbOffset = FETCHDWORD(parg16->cbRegionOffset); cbLength = FETCHDWORD(parg16->cbRegionLength); FREEARGPTR(parg16); if (!Handle) { return (0xFFFF0000 | ERROR_INVALID_HANDLE); } if (al == 0) { // lock if (!LockFile(Handle, cbOffset, 0, cbLength, 0)) { return (0xFFFF0000 | GetLastError()); } } else if (al == 1) { // unlock if (!UnlockFile(Handle, cbOffset, 0, cbLength, 0)) { return (0xFFFF0000 | GetLastError()); } } else { // bad parameter return (0xFFFF0000 | ERROR_INVALID_FUNCTION); } return 0; } /* WK32WOWFindFirst - Path-Style Find First File * * Entry - lpDTA pointer to app's DTA * lpFile sz to path * wAttributes flags for search * * Exit * SUCCESS * 0 * * FAILURE * system status code * */ // this function (sitting in DEMLFN.C) checks to see if the path name // passed as a parameter is a SHORT path name, never mind it's existance extern BOOL demIsShortPathName(LPSTR, BOOL); ULONG FASTCALL WK32WOWFindFirst(PVDMFRAME pFrame) { PWOWFINDFIRST16 parg16; USHORT usSearchAttr; PVOID pDTA; PSTR ExpandName; PSTR pFile; BOOL ItsANamedPipe = FALSE; DWORD dwRet = 0xFFFF0000 | ERROR_PATH_NOT_FOUND; GETARGPTR(pFrame, sizeof(WOWFINDFIRST16), parg16); GETVDMPTR(FETCHDWORD(parg16->lpDTA), SIZEOF_DOSSRCHDTA, pDTA); pFile = SEGPTR(FETCHWORD(parg16->pszPathSegment), FETCHWORD(parg16->pszPathOffset) ); usSearchAttr = FETCHWORD(parg16->wAttributes); FREEARGPTR(parg16); WOW32ASSERT(DosWowData.lpCurDrv != (ULONG) NULL); pFile = NormalizeDosPath(pFile, (WORD) (*(PUCHAR)DosWowData.lpCurDrv), &ItsANamedPipe ); // // add in curr directory and expand the "*"s in the path to "?"s // ExpandName = ExpandDosPath (pFile); if (NULL != ExpandName && !demIsShortPathName(ExpandName, TRUE)) { ExpandName = NULL; SetLastError(ERROR_INVALID_NAME); } // // invoke dem to do the search // if (ExpandName) { // return NO_MORE_FILES for quicktime install etc that barf on // big directory or filenames that are longer than 64 bytes // the magic number 50 is calculated from 64 - 12 (for 8.3) - 1 (backslash) -1 // (terminating zero) LOGDEBUG(fileoclevel,("WK32WOWFindFirst: StrLen: %X\n", strlen(ExpandName))); if ( (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_LIMITFINDFIRSTLEN) && (strlen(ExpandName) > 50)) { dwRet = -1; SetLastError(ERROR_NO_MORE_FILES); } else { dwRet = demFileFindFirst (pDTA, ExpandName, usSearchAttr); } } else { dwRet = (DWORD)-1; } if (dwRet == -1) { dwRet = 0xFFFF0000 | GetLastError(); } else if (dwRet) { dwRet |= 0xFFFF0000; } FREEVDMPTR(pDTA); if (ItsANamedPipe) { LocalFree(pFile); } return (dwRet); } /* WK32WOWFindNext - Path-Style Find Next File * * Entry - lpDTA pointer to app's DTA * * Exit * SUCCESS * 0 * * FAILURE * system status code * */ ULONG FASTCALL WK32WOWFindNext(PVDMFRAME pFrame) { PWOWFINDNEXT16 parg16; PVOID pDTA; DWORD dwRet; GETARGPTR(pFrame, sizeof(WOWFINDNEXT16), parg16); GETVDMPTR(FETCHDWORD(parg16->lpDTA), SIZEOF_DOSSRCHDTA, pDTA); FREEARGPTR(parg16); if (dwRet = demFileFindNext (pDTA)) dwRet |= 0xFFFF0000; FREEVDMPTR(pDTA); return (dwRet); } BOOL FASTCALL IsModuleSymantecInstall(HAND16 hMod16) { VPVOID vpFilename = 0; PSZ pszFilename; CHAR szName[32]; CHAR szVersion[16]; BOOL bRet; // be sure stackalloc16() size matches stackfree16() size below bRet = ((vpFilename = stackalloc16(MAX_PATH)) && GetModuleFileName16(hMod16, vpFilename, MAX_PATH) && (pszFilename = GetPModeVDMPointer(vpFilename, MAX_PATH)) && WowGetProductNameVersion(pszFilename, szName, sizeof szName, szVersion, sizeof szVersion, NULL, NULL, 0) && ! WOW32_stricmp(szName, "Symantec Install for Windows") && RtlEqualMemory(szVersion, "3.1.0.", 6)); if(vpFilename) { stackfree16(vpFilename, MAX_PATH); } return (bRet); } // // these 3 functions are located in dos/dem/demlfn.c and exported // out of ntvdm.exe // extern ULONG demWOWLFNAllocateSearchHandle(HANDLE hFind); extern HANDLE demWOWLFNGetSearchHandle(USHORT DosHandle); extern BOOL demWOWLFNCloseSearchHandle(USHORT DosHandle); ULONG FASTCALL WK32FindFirstFile(PVDMFRAME pFrame) { // locate the handle which is a dword and a ptr to win32_find_data // which is a dword too. The handle's valid part is a low word // To avoid extra calls we check if the hi word of a handle is 0 // is it is -- then it's 16-bit handle and we retrieve 32-bit handle // from DEMLFN PFINDFIRSTFILE16 parg16; WIN32_FIND_DATA UNALIGNED* pFindData16; WIN32_FIND_DATA FindData32; HANDLE hFind; PSTR pszSearchFile; ULONG DosHandle = (ULONG)INVALID_HANDLE_VALUE; GETARGPTR(pFrame, sizeof(FINDFIRSTFILE16), parg16); GETPSZPTR(parg16->lpszSearchFile, pszSearchFile); GETVDMPTR(parg16->lpFindData, sizeof(WIN32_FIND_DATA), pFindData16); hFind = FindFirstFile(pszSearchFile, &FindData32); if (INVALID_HANDLE_VALUE != hFind) { // copy FindData into 16-bit land. Keep in mind that if we do a copy // of sizeof(WIN32_FIND_DATA) we may be writing over user's memory // since the size of a structure is not the same in 16-bit code! RtlCopyMemory(pFindData16, &FindData32, sizeof(DWORD)+ // dwFileAttributes sizeof(FILETIME)*3 + // FILETIME stuff sizeof(DWORD)*3 + // FileSize Low and High sizeof(DWORD)*2 + // dwReserved 0/1 sizeof(FindData32.cFileName) + sizeof(FindData32.cAlternateFileName)); // and now map the handle DosHandle = demWOWLFNAllocateSearchHandle(hFind); } FREEVDMPTR(pFindData16); FREEPSZPTR(pszSearchFile); FREEARGPTR(parg16); return(DosHandle); } ULONG FASTCALL WK32FindNextFile(PVDMFRAME pFrame) { PFINDNEXTFILE16 parg16; WIN32_FIND_DATA UNALIGNED* pFindData16; WIN32_FIND_DATA FindData32; HANDLE hFindFile; ULONG DosHandle; BOOL bSuccess; GETARGPTR(pFrame, sizeof(FINDNEXTFILE16), parg16); DosHandle = FETCHDWORD(parg16->hFindFile); GETVDMPTR(parg16->lpFindData, sizeof(WIN32_FIND_DATA), pFindData16); hFindFile = demWOWLFNGetSearchHandle((USHORT)DosHandle); bSuccess = FindNextFile(hFindFile, &FindData32); if (bSuccess) { RtlCopyMemory(pFindData16, &FindData32, sizeof(DWORD)+ // dwFileAttributes sizeof(FILETIME)*3 + // FILETIME stuff sizeof(DWORD)*3 + // FileSize Low and High sizeof(DWORD)*2 + // dwReserved 0/1 sizeof(FindData32.cFileName) + sizeof(FindData32.cAlternateFileName)); } FREEVDMPTR(pFindData16); FREEARGPTR(parg16); return((ULONG)bSuccess); } ULONG FASTCALL WK32FindClose(PVDMFRAME pFrame) { PFINDCLOSE16 parg16; ULONG DosHandle; GETARGPTR(pFrame, sizeof(FINDCLOSE16), parg16); DosHandle = FETCHDWORD(parg16->hFindFile); FREEARGPTR(parg16); // this also closes the real search handle via FindClose return ((ULONG)demWOWLFNCloseSearchHandle((USHORT)DosHandle)); }