You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2560 lines
71 KiB
2560 lines
71 KiB
/*++
|
|
*
|
|
* 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"
|
|
|
|
#define DOS_FLAG_EXEC_OPEN 1 // see dos\v86\inc\dossym.inc
|
|
|
|
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,
|
|
IN int
|
|
);
|
|
|
|
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 = SafeLoadLibrary(L"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) {
|
|
DPM_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 (DPM_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 = DPM_GetFileSize(hFile, &liFileSize.HighPart);
|
|
|
|
liFilePointer.HighPart = 0;
|
|
liFilePointer.LowPart = DPM_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 (DPM_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) {
|
|
DPM_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 = DPM_SetFilePointer( hFile, 0, 0, FILE_CURRENT );
|
|
pCache->dwFileSize = DPM_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) {
|
|
DPM_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 (DPM_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 (( DPM_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 = DPM_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 = DPM_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, MAX_PATH);
|
|
}
|
|
|
|
|
|
/* 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, int cbNewPath)
|
|
{
|
|
PSTR pPathName, pPathNameSlash, pPathExt;
|
|
|
|
//
|
|
// If the string is not already in the buffer, copy it in
|
|
//
|
|
|
|
if (NewPath != pszPath) {
|
|
strncpy (NewPath, pszPath, cbNewPath);
|
|
NewPath[cbNewPath-1] = '\0';
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
i = min(i, MAX_PATH-2);
|
|
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;
|
|
}
|
|
|
|
if (Dos_Flag_Addr && (*Dos_Flag_Addr & DOS_FLAG_EXEC_OPEN)) {
|
|
dwWinAccess |= GENERIC_EXECUTE;
|
|
}
|
|
|
|
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) {
|
|
|
|
if (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) &&
|
|
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,sizeof(szMappedPath))){
|
|
|
|
lpFileName=&szMappedPath[0];
|
|
|
|
hFile = CreateFileOem(lpFileName,
|
|
dwWinAccess,
|
|
dwWinShareMode | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
if (GetLastError() != ERROR_ACCESS_DENIED) {
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
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];
|
|
strncpy(szLowerPath, pszPath, MAX_PATH);
|
|
szLowerPath[MAX_PATH-1] = '\0';
|
|
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 (!DPM_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 = GetFileAttributesOemSys(lpFileName, FALSE);
|
|
|
|
// 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 = GetFileAttributesOemSys(lpFileName, FALSE);
|
|
}
|
|
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 (SetFileAttributesOemSys(lpFileName, attributes, FALSE)) {
|
|
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 (!DPM_LockFile(Handle, cbOffset, 0, cbLength, 0)) {
|
|
return (0xFFFF0000 | GetLastError());
|
|
}
|
|
} else if (al == 1) { // unlock
|
|
|
|
if (!DPM_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 = DPM_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 = DPM_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));
|
|
|
|
}
|
|
|
|
|