/*++
 *
 *  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));

}


