windows-nt/Source/XPSP1/NT/base/mvdm/wow32/wkfileio.c

2547 lines
68 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
*
* 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 <something> (after \ or \\<name>\). Check if <something>
// 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 \\<this_computer>\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));
}