#include #include #include "debug.h" #include "fileshar.h" #ifdef USE_DIRECTIO #include "directio.h" #endif #define MAXTASKS 10 #ifdef _WIN32 #define CurrentProcess() ((HANDLE) GetCurrentProcessId()) #else #define CurrentProcess() ((HANDLE) GetCurrentPDB()) #endif #ifdef _WIN32 #define HSHfromPSH(psh) (HSHFILE) psh #define PSHfromHSH(hsh) (PSHFILE) hsh #else #define HSHfromPSH(psh) (HSHFILE) GlobalPtrHandle(psh) #define PSHfromHSH(hsh) (PSHFILE) GlobalLock((HGLOBAL) hsh) #endif // // allow multiple processes to use the same file handle (as will happen on // win16 and chicago when an interface pointer is simply-marshalled to another // process that shares the same global address space). // // This will not happen on NT, but we retain the code structure. // #ifdef USE_DIRECTIO // use unbuffered i/o direct to the disk, rather than going through // mmio and the disk buffer. much faster for streaming reads and writes. // open via mmio if direct io not possible (eg mmio handler installed). #endif typedef struct { #ifndef DAYTONA TCHAR szFile[256]; DWORD dwOpenFlags; MMIOINFO mmioinfo; HANDLE htask; int i; LONG lOffset; HANDLE ahtask[MAXTASKS]; HMMIO ahmmio[MAXTASKS]; ULONG ulRef[MAXTASKS]; #else ULONG ulRef; #endif HMMIO hmmio; #ifdef USE_DIRECTIO CFileStream * pdio; #ifndef DAYTONA CFileStream * adio[MAXTASKS]; #endif #endif } SHFILE, FAR * PSHFILE; #ifdef DAYTONA #define GetProperTask(psh) (TRUE) #else extern "C" { extern LPTSTR FAR lstrzcpy (LPTSTR pszTgt, LPCTSTR pszSrc, size_t cch); extern LPSTR FAR lstrzcpyA (LPSTR pszTgt, LPCSTR pszSrc, size_t cch); extern LPWSTR FAR lstrzcpyW (LPWSTR pszTgt, LPCWSTR pszSrc, size_t cch); extern LPWSTR FAR lstrzcpyAtoW (LPWSTR pszTgt, LPCSTR pszSrc, size_t cch); extern LPSTR FAR lstrzcpyWtoA (LPSTR pszTgt, LPCWSTR pszSrc, size_t cch); } // extern "C" BOOL GetProperTask(PSHFILE psh) { HANDLE htask = CurrentProcess(); int i; if (htask == psh->htask) return #ifdef USE_DIRECTIO (psh->pdio != NULL) || #endif ((psh->hmmio != 0) && (psh->hmmio != (HMMIO) -1)); for (i = 0; i < MAXTASKS; i++) { if (psh->ahtask[i] == htask) { Success: psh->hmmio = psh->ahmmio[i]; psh->htask = htask; psh->i = i; #ifdef USE_DIRECTIO psh->pdio = psh->adio[i]; if (psh->pdio != NULL) { psh->pdio->Seek(psh->lOffset); return TRUE; } #endif mmioSeek(psh->hmmio, psh->lOffset, SEEK_SET); return (psh->hmmio != 0) && (psh->hmmio != (HMMIO) -1); } } for (i = 0; i < MAXTASKS; i++) { if (psh->ahtask[i] == 0) { DPF2("Re-opening handle %lx in task %x\n", psh, htask); #ifdef USE_DIRECTIO psh->adio[i] = new CFileStream; if (!psh->adio[i]->Open(psh->szFile, (psh->dwOpenFlags & OF_READWRITE), (psh->dwOpenFlags & OF_CREATE))) { delete psh->adio[i]; psh->adio[i] = NULL; #else { #endif psh->ahmmio[i] = mmioOpen(psh->szFile, NULL, psh->dwOpenFlags); psh->ahtask[i] = htask; if (psh->ahmmio[i] == 0) { DPF("mmioOpen failed in GetProperTask!\n"); return FALSE; } } goto Success; } } DPF("File handle open in too many tasks!\n"); return FALSE; } #endif HSHFILE WINAPI shfileOpen(LPTSTR szFileName, MMIOINFO FAR* lpmmioinfo, DWORD dwOpenFlags) { PSHFILE psh = (PSHFILE) GlobalAllocPtr(GPTR | GMEM_SHARE, sizeof(SHFILE)); if (!psh) return NULL; #ifndef DAYTONA lstrzcpy(psh->szFile, szFileName, NUMELMS(psh->szFile)); psh->dwOpenFlags = dwOpenFlags; #endif psh->hmmio = NULL; #ifdef USE_DIRECTIO if ( // Direct I/O is broken for reading the end of files on Chicago. Don't use it. #ifndef DIRECTIOFORREADINGALSO !(dwOpenFlags & OF_CREATE) || #endif !(psh->pdio = new CFileStream) || !psh->pdio->Open( szFileName, (dwOpenFlags & (OF_WRITE | OF_READWRITE)), (dwOpenFlags & OF_CREATE))) { if (psh->pdio) delete psh->pdio; psh->pdio = NULL; #else { #endif psh->hmmio = mmioOpen(szFileName, lpmmioinfo, dwOpenFlags); if (psh->hmmio == 0) { DPF("mmioOpen failed!\n"); GlobalFreePtr(psh); return NULL; } } //DPF("Opening handle %lx ('%s') in task %x, mode = %lx\n", psh, szFileName, CurrentProcess(), psh->dwOpenFlags); #ifndef DAYTONA psh->ahmmio[0] = psh->hmmio; psh->ahtask[0] = psh->htask = CurrentProcess(); psh->ulRef[0] = 1; // !!! 0? #ifdef USE_DIRECTIO psh->adio[0] = psh->pdio; #endif #else psh->ulRef = 1; // !!! 0? #endif return HSHfromPSH(psh); } UINT WINAPI shfileClose(HSHFILE hsh, UINT uFlags) { PSHFILE psh = PSHfromHSH(hsh); #ifndef DAYTONA int i; for (i = 0; i < MAXTASKS; i++) { if (psh->ahtask[i] && psh->ahmmio[i]) { DPF("Handle %lx closed with ref count %ld in task %x\n", psh, psh->ulRef[i], psh->ahtask[i]); #ifdef USE_DIRECTIO if (psh->adio[i]) { delete psh->adio[i]; psh->adio[i] = NULL; } else #endif mmioClose(psh->ahmmio[i], 0); } } #else #ifdef USE_DIRECTIO if (psh->pdio) { delete psh->pdio; psh->pdio = NULL; } else #endif if (psh->hmmio) { mmioClose(psh->hmmio, 0); } #endif GlobalFreePtr(psh); return 0; } #ifdef USE_DIRECTIO // if we are using direct io, we want to bypass the buffering // schemes that are layered on top of this module. Allow them to // determine if we are using direct io to do this. BOOL shfileIsDirect(HSHFILE hsh) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; return (psh->pdio != 0); } void shfileStreamStart(HSHFILE hsh) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return; if (psh->pdio == 0) { return; } psh->pdio->StartStreaming(); } void shfileStreamStop(HSHFILE hsh) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return; if (psh->pdio == 0) { return; } psh->pdio->StopStreaming(); } #endif LONG WINAPI shfileRead(HSHFILE hsh, HPSTR pch, LONG cch) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifndef DAYTONA psh->lOffset += cch; #endif #ifdef USE_DIRECTIO if (psh->pdio) { DWORD bytes; if (!psh->pdio->Read((LPBYTE)pch, cch, &bytes)) { return 0; } else { return bytes; } } else #endif return mmioRead(psh->hmmio, pch, cch); } LONG WINAPI shfileWrite(HSHFILE hsh, const char _huge* pch, LONG cch) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifndef DAYTONA psh->lOffset += cch; #endif #ifdef USE_DIRECTIO if (psh->pdio) { DWORD bytes; if (!psh->pdio->Write((LPBYTE)pch, cch, &bytes)) { return 0; } else { return bytes; } } else #endif return mmioWrite(psh->hmmio, pch, cch); } LONG WINAPI shfileSeek(HSHFILE hsh, LONG lOffset, int iOrigin) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifdef USE_DIRECTIO if (psh->pdio) { Assert(iOrigin != SEEK_END); if (iOrigin == SEEK_CUR) { lOffset += psh->pdio->GetCurrentPosition(); } psh->pdio->Seek(lOffset); #ifndef DAYTONA psh->lOffset = psh->pdio->GetCurrentPosition(); return psh->lOffset; #else return psh->pdio->GetCurrentPosition(); #endif } else #endif { #ifdef DAYTONA return mmioSeek(psh->hmmio, lOffset, iOrigin); #else psh->lOffset = mmioSeek(psh->hmmio, lOffset, iOrigin); return psh->lOffset; #endif } } LONG WINAPI shfileZero(HSHFILE hsh, LONG lBytes) { LPVOID pmem; LONG lToWrite = lBytes; #define ZERO_AT_ONCE 1024 pmem = GlobalAllocPtr(GPTR, ZERO_AT_ONCE); // We write out 1024 bytes at a time, with the odd bytes being written // in the last block. This is probably more efficient than writing the // "odd" bytes first, then looping for a known number of iterations to // write 1024 bytes at a time. if (pmem) { LONG cbWrite = ZERO_AT_ONCE; while (lToWrite > 0) { if (lToWrite < cbWrite) { cbWrite = lToWrite; } if (shfileWrite(hsh, (HPSTR) pmem, cbWrite) != cbWrite) { // The file write has failed. This leaves the file in // a bad state. It might be worth trying to position // the write pointer as though nothing had been written, // but this is problematic as there may be a serious // problem with the file itself. Simply abort writing... lBytes = -1; lToWrite = 0; break; } lToWrite -= cbWrite; } GlobalFreePtr(pmem); return lBytes; } else { DPF("Unable to allocate 1K of zeroed memory!\n"); shfileSeek(hsh, lBytes, SEEK_SET); return lBytes; } } LONG WINAPI shfileFlush(HSHFILE hsh, UINT uFlags) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifdef USE_DIRECTIO if (psh->pdio) { if (!psh->pdio->CommitAndWait()) { return MMIOERR_CANNOTWRITE; } } #endif return 0; } LONG WINAPI shfileAddRef(HSHFILE hsh) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifdef DAYTONA psh->ulRef++; #else ++psh->ulRef[psh->i]; DPF2("Handle %lx in task %x: ref++ == %ld\n", psh, psh->htask, psh->ulRef[psh->i]); #endif return 0; } LONG WINAPI shfileRelease(HSHFILE hsh) { PSHFILE psh = PSHfromHSH(hsh); if (!GetProperTask(psh)) return -1; #ifdef DAYTONA if (--psh->ulRef <= 0) #else if (--psh->ulRef[psh->i] <= 0) #endif { #ifndef DAYTONA DPF2("Closing handle %lx in task %x, pos = %lx\n", psh, psh->htask, psh->lOffset); #endif #ifdef USE_DIRECTIO if (psh->pdio) { delete psh->pdio; psh->pdio = 0; } else #endif { mmioClose(psh->hmmio, 0); } psh->hmmio = 0; #ifndef DAYTONA psh->ahmmio[psh->i] = 0; psh->ahtask[psh->i] = 0; psh->ulRef[psh->i] = 0; #ifdef USE_DIRECTIO psh->adio[psh->i] = 0; #endif psh->htask = 0; #endif } else { #ifndef DAYTONA DPF2("Handle %lx in task %x: ref-- == %ld\n", psh, psh->htask, psh->ulRef[psh->i]); #endif } return 0; } static BYTE bPad; MMRESULT WINAPI shfileDescend(HSHFILE hshfile, LPMMCKINFO lpck, const LPMMCKINFO lpckParent, UINT wFlags) { FOURCC ckidFind; // chunk ID to find (or NULL) FOURCC fccTypeFind; // form/list type to find (or NULL) /* figure out what chunk id and form/list type to search for */ if (wFlags & MMIO_FINDCHUNK) ckidFind = lpck->ckid, fccTypeFind = 0; else if (wFlags & MMIO_FINDRIFF) ckidFind = FOURCC_RIFF, fccTypeFind = lpck->fccType; else if (wFlags & MMIO_FINDLIST) ckidFind = FOURCC_LIST, fccTypeFind = lpck->fccType; else ckidFind = fccTypeFind = 0; lpck->dwFlags = 0L; while (TRUE) { UINT w; /* read the chunk header */ if (shfileRead(hshfile, (HPSTR) lpck, 2 * sizeof(DWORD)) != 2 * sizeof(DWORD)) return MMIOERR_CHUNKNOTFOUND; /* store the offset of the data part of the chunk */ if ((lpck->dwDataOffset = shfileSeek(hshfile, 0L, SEEK_CUR)) == -1) return MMIOERR_CANNOTSEEK; /* check for unreasonable chunk size */ /* see if the chunk is within the parent chunk (if given) */ if ((lpckParent != NULL) && (( lpck->dwDataOffset - 8L) >= (lpckParent->dwDataOffset + lpckParent->cksize))) return MMIOERR_CHUNKNOTFOUND; /* if the chunk if a 'RIFF' or 'LIST' chunk, read the * form type or list type */ if ((lpck->ckid == FOURCC_RIFF) || (lpck->ckid == FOURCC_LIST)) { if (shfileRead(hshfile, (HPSTR) &lpck->fccType, sizeof(DWORD)) != sizeof(DWORD)) return MMIOERR_CHUNKNOTFOUND; } else lpck->fccType = 0; /* if this is the chunk we're looking for, stop looking */ if ( ((ckidFind == 0) || (ckidFind == lpck->ckid)) && ((fccTypeFind == 0) || (fccTypeFind == lpck->fccType)) ) break; /* ascend out of the chunk and try again */ if ((w = shfileAscend(hshfile, lpck, 0)) != 0) return w; } return 0; } MMRESULT WINAPI shfileAscend(HSHFILE hshfile, LPMMCKINFO lpck, UINT wFlags) { if (lpck->dwFlags & MMIO_DIRTY) { /* refers to a chunk created by shfileCreateChunk(); * check that the chunk size that was written when * shfileCreateChunk() was called is the real chunk size; * if not, fix it */ DWORD dwOffset; // current offset in file DWORD dwActualSize; // actual size of chunk data if ((dwOffset = (DWORD)shfileSeek(hshfile, 0L, SEEK_CUR)) == -1) return MMIOERR_CANNOTSEEK; if ((LONG)(dwActualSize = dwOffset - lpck->dwDataOffset) < 0) return MMIOERR_CANNOTWRITE; if (LOWORD(dwActualSize) & 1) { /* chunk size is odd -- write a null pad byte */ if (shfileWrite(hshfile, (HPSTR) &bPad, sizeof(bPad)) != sizeof(bPad)) return MMIOERR_CANNOTWRITE; } if (lpck->cksize == (DWORD)dwActualSize) return 0; /* fix the chunk header */ lpck->cksize = dwActualSize; if (shfileSeek(hshfile, lpck->dwDataOffset - sizeof(DWORD), SEEK_SET) == -1) return MMIOERR_CANNOTSEEK; if (shfileWrite(hshfile, (HPSTR) &lpck->cksize, sizeof(DWORD)) != sizeof(DWORD)) return MMIOERR_CANNOTWRITE; } /* seek to the end of the chunk, past the null pad byte * (which is only there if chunk size is odd) */ if (shfileSeek(hshfile, lpck->dwDataOffset + lpck->cksize + (lpck->cksize & 1L), SEEK_SET) == -1) return MMIOERR_CANNOTSEEK; return 0; } MMRESULT WINAPI shfileCreateChunk(HSHFILE hshfile, LPMMCKINFO lpck, UINT wFlags) { int iBytes; // bytes to write DWORD dwOffset; // current offset in file /* store the offset of the data part of the chunk */ if ((dwOffset = (DWORD)shfileSeek(hshfile, 0L, SEEK_CUR)) == -1) return MMIOERR_CANNOTSEEK; lpck->dwDataOffset = dwOffset + 2 * sizeof(DWORD); /* figure out if a form/list type needs to be written */ if (wFlags & MMIO_CREATERIFF) lpck->ckid = FOURCC_RIFF, iBytes = 3 * sizeof(DWORD); else if (wFlags & MMIO_CREATELIST) lpck->ckid = FOURCC_LIST, iBytes = 3 * sizeof(DWORD); else iBytes = 2 * sizeof(DWORD); /* write the chunk header */ if (shfileWrite(hshfile, (HPSTR) lpck, (LONG) iBytes) != (LONG) iBytes) return MMIOERR_CANNOTWRITE; lpck->dwFlags = MMIO_DIRTY; return 0; }