/*++ Copyright (c) 1990 - 2000 Microsoft Corporation Module Name: filepool.cxx Abstract: Contains the routines for handling Filepools for the Spooler. Contains the C++ objects and the C wrapper functions. Author: Bryan Kilian (Bryankil) 5-Apr-2000 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "filepool.hxx" VOID FPCloseFiles(struct FileListItem * pItem, BOOL CloseShad); /*-- Interface Functions: CreateFilePool GetFileItemHandle GetNameFromHandle GetWriterFromHandle GetReaderFromHandle ReleasePoolHandle DestroyFilePool --*/ /********************* * CreateFilePool() * * Creates the Filepool. * * Arguments: * OUT FilePool : Pointer to a Filepool handle. * IN BasePath : String to use as a spool dir. * IN PreNumStr : String to use as a filename prefix. * IN SplExt : String to use as a spool extension. * IN ShdExt : String to use as a shadow file extension. * IN PoolTimeout : Dword in milliseconds before an idle pool item gets deleted. * IN MaxFiles : Maximum number of free files in the pool. * * The path gets created as \XXXXX * */ HRESULT CreateFilePool( HANDLE * FilePoolHandle, LPCTSTR BasePath, LPCTSTR PreNumStr, LPCTSTR SplExt, LPCTSTR ShdExt, DWORD PoolTimeout, DWORD MaxFiles ) { HRESULT RetVal = E_FAIL; class FilePool * FP = NULL; if (FilePoolHandle && BasePath && PreNumStr && SplExt && ShdExt) { MaxFiles = (MaxFiles > 9999) ? 9999 : MaxFiles; FP = new FilePool( PoolTimeout, MaxFiles ); if ( FP ) { RetVal = FP->AllocInit( BasePath, PreNumStr, SplExt, ShdExt ); if (SUCCEEDED(RetVal)) { *FilePoolHandle = (HANDLE) FP; } else { delete FP; *FilePoolHandle = INVALID_HANDLE_VALUE; } } else { RetVal = E_OUTOFMEMORY; } } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * GetFileItemHandle() * * Takes a FilePool Object and An empty Filehandle, and returns * an open File handle. Use ReleasePoolHandle Once you're finished * with the file. * * Arguments: * IN FilePool : Handle of a FilePool Object Created with CreateFilePool() * OUT FileItem : Pointer to a FileItem Handle. * IN FromFilename : String to use as a filename. * * Returns Error if it is passed in an invalid argument, or S_OK if it succeeds. * */ HRESULT GetFileItemHandle( HANDLE FilePoolHandle, HANDLE * FileItem, LPWSTR FromFilename ) { class FilePool * FP = NULL; HRESULT RetVal = E_FAIL; struct FileListItem * FLItem = NULL; FP = (class FilePool *)FilePoolHandle; RetVal = FP->GetWriteFileStruct(&FLItem, FromFilename); if (SUCCEEDED(RetVal)) { *FileItem = (HANDLE) FLItem; } else { *FileItem = INVALID_HANDLE_VALUE; } return RetVal; } /********************* * GetNameFromHandle() * * Takes a FileItem and a string pointer, and returns * an allocated string containing the name of the file. * * Arguments: * IN FileItem : Handle of a FileItem, retrieved with GetFileItemHandle() * OUT FileNameStr : Pointer to a string variable. * IN IsSpool : BOOL - TRUE - Returns the Spool Filename, FALSE returns the Shadow Filename * * Returns Error if it is passed in an invalid argument or if it cannot allocate the string, * or S_OK if it succeeds. * */ HRESULT GetNameFromHandle( HANDLE FileItem, PWSTR * FileNameStr, BOOL IsSpool ) { HRESULT RetVal = S_OK; struct FileListItem * FPI; if (FileItem && (FileItem != INVALID_HANDLE_VALUE)) { FPI = (struct FileListItem *)FileItem; if (FileNameStr && !*FileNameStr) { if (IsSpool) { *FileNameStr = AllocSplStr(FPI->SplFilename); } else { *FileNameStr = AllocSplStr(FPI->ShdFilename); } if (!*FileNameStr) { RetVal = E_OUTOFMEMORY; } } else { RetVal = E_INVALIDARG; } } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * GetCurrentWriter() * * Takes a FileItem and returns a FileHandle of the current WriteHandle if it exists. * * Arguments: * IN FileItem : FileItem Handle. * IN IsSpool : BOOL - TRUE: Returns the Spool file handle, FALSE returns the Shadow file handle. * * This will not create a writer if it is not yet created, and will also not update the object's flags. * * Return: Valid Handle if success or INVALID_HANDLE_VALUE. */ HANDLE GetCurrentWriter( HANDLE FileItem, BOOL IsSpool) { struct FileListItem * FPI; HANDLE RetVal = INVALID_HANDLE_VALUE; HANDLE Temp = INVALID_HANDLE_VALUE; if (FileItem) { FPI = (struct FileListItem *) FileItem; FPI->EnterCritSec(); if (IsSpool) { Temp = FPI->SplWriteHandle; } else { Temp = FPI->ShdWriteHandle; } FPI->LeaveCritSec(); if (Temp != INVALID_HANDLE_VALUE) { RetVal = Temp; } } return RetVal; } /********************* * GetFileCreationInfo * * Takes a FileItem and returns a bitmap. * * Arguments: * IN FileItem : FileItem Handle. * OUT BitMap * * Returns bitmap indicating which files needed to be opened. Bitmap reset when * the pool handle is released. * * Return: S_OK if success, otherwise an error value. */ HRESULT GetFileCreationInfo( HANDLE FileItem, PDWORD BitMap ) { struct FileListItem * FPI; *BitMap = 0; if (FileItem && (FileItem != INVALID_HANDLE_VALUE)) { FPI = (struct FileListItem *) FileItem; *BitMap = FPI->CreateInfo; return S_OK; } return E_FAIL; } /********************* * GetWriterFromHandle() * * Takes a FileItem and returns a FileHandle with an open Writer. * * Arguments: * IN FileItem : FileItem Handle. * OUT File : Pointer to a filehandle. * IN IsSpool : BOOL - TRUE: Returns the Spool file handle, FALSE returns the Shadow file handle. * * This will create a writer if it is not yet created, and will also update the object's flags. It will * return the current handle if there is one. * * Return: S_OK if success, otherwise an error value. */ HRESULT GetWriterFromHandle( HANDLE FileItem, HANDLE * File, BOOL IsSpool ) { HRESULT RetVal = S_OK; class FilePool * FP = NULL; struct FileListItem * FPI; if (FileItem && File) { FPI = (struct FileListItem *) FileItem; FP = FPI->FP; *File = INVALID_HANDLE_VALUE; FPI->EnterCritSec(); if (IsSpool) { if (FPI->SplWriteHandle == INVALID_HANDLE_VALUE) { RetVal = FP->CreateSplWriter(FPI); } } else { if (FPI->ShdWriteHandle == INVALID_HANDLE_VALUE) { RetVal = FP->CreateShdWriter(FPI); } } if (SUCCEEDED(RetVal)) { if (IsSpool) { *File = FPI->SplWriteHandle; FPI->Status |= FP_STATUS_SPL_WRITING; } else { *File = FPI->ShdWriteHandle; FPI->Status |= FP_STATUS_SHD_WRITING; } } else { RetVal = E_FAIL; } FPI->LeaveCritSec(); } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * GetReaderFromHandle() * * Takes a FileItem and returns a FileHandle with an open Reader. * * Arguments: * IN FileItem : FileItem Handle. * OUT File : Pointer to a filehandle. * * This will create a Reader if it is not yet created, and will also update the object's flags. It will * return the current handle if there is one. It will also return the writer if the system is finished with it. * * Return: S_OK if success, otherwise an error value. */ HRESULT GetReaderFromHandle( HANDLE FileItem, HANDLE * File ) { HRESULT RetVal = S_OK; class FilePool * FP = NULL; struct FileListItem * FPI; if (FileItem) { FPI = (struct FileListItem *) FileItem; FP = FPI->FP; // CriticalSection FPI->EnterCritSec(); if (!(FPI->Status & FP_STATUS_SPL_WRITING) && (FPI->SplWriteHandle != INVALID_HANDLE_VALUE) && (FPI->SplReadHandle == INVALID_HANDLE_VALUE) ) { // // We aren't writing this job anymore, we can reuse the // write handle for reading. // if (!(FPI->Status & FP_STATUS_SPL_READING)) { if (SetFilePointer(FPI->SplWriteHandle, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { *File = FPI->SplWriteHandle; FPI->Status |= FP_STATUS_SPL_READING; } else { RetVal = E_FAIL; } } else { *File = FPI->SplWriteHandle; } } else { // // We are still writing this job, so we need to use the readhandle. // if (FPI->SplReadHandle == INVALID_HANDLE_VALUE) { // // The Reader doesn't already exist, We need to create it. // RetVal = FP->CreateSplReader(FPI); } if (SUCCEEDED(RetVal)) { // // We now have a valid handle // *File = FPI->SplReadHandle; FPI->Status |= FP_STATUS_SPL_READING; } else { RetVal = E_FAIL; } } FPI->LeaveCritSec(); } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * FishedReading() * * Indicates to the object that we are finished with it for reading purposes. * * Arguments: * IN FileItem : Handle of a FileItem. * * Returns Error if it is passed in an invalid argument, or S_OK if it succeeds. * */ HRESULT FinishedReading( HANDLE FileItem ) { HRESULT RetVal = S_OK; struct FileListItem * FPI; if (FileItem) { FPI = (struct FileListItem *) FileItem; FPI->EnterCritSec(); FPI->Status &= ~FP_STATUS_SPL_READING; CloseFilesCheck(FPI, kCloseReadHandle); FPI->LeaveCritSec(); } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * FishedWriting() * * Indicates to the object that we are finished with it for writing purposes. * * Arguments: * IN FileItem : Handle of a FileItem. * IN IsSpool : BOOL - TRUE: Affects the Spl file, FALSE: Affects the Shd file. * * Returns Error if it is passed in an invalid argument, or S_OK if it succeeds. * */ HRESULT FinishedWriting( HANDLE FileItem, BOOL IsSpool ) { HRESULT RetVal = S_OK; struct FileListItem * FPI; if (FileItem) { FPI = (struct FileListItem *) FileItem; FPI->EnterCritSec(); if (IsSpool) { FPI->Status &= ~FP_STATUS_SPL_WRITING; CloseFilesCheck(FPI, kCloseWriteHandle); } else { FPI->Status &= ~FP_STATUS_SHD_WRITING; CloseFilesCheck(FPI, kCloseShdHandle); } FPI->LeaveCritSec(); } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * ReleasePoolHandle() * * Releases the pool item back to the pool to reuse. The pool will not reuse the item if all the * filehandles are closed, and also if we have reached our free files limit. * * Arguments: * IN OUT FileItem : Pointer to a FileItem Handle. * * Returns Error if it fails, or S_OK if it succeeds. FileItem gets set to INVALID_HANDLE_VALUE if it succeeds. * */ HRESULT ReleasePoolHandle( HANDLE * FileItem ) { HRESULT RetVal = S_OK; class FilePool * FP = NULL; struct FileListItem * FPI; if (FileItem && *FileItem ) { FPI = (struct FileListItem *) *FileItem; FP = FPI->FP; // // This should not be in the critsec, since we might delete // the critical section. // RetVal = FP->ReleasePoolHandle(FPI); if (SUCCEEDED(RetVal)) { *FileItem = INVALID_HANDLE_VALUE; } } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * RemoveFromFilePool() * * Removes the pool item from the pool completely and frees the associated memory. * * Arguments: * IN OUT FileItem : Pointer to a FileItem Handle. * IN Delete : BOOL, Tells us whether to delete the files or not. * * Returns Error if it fails, or S_OK if it succeeds. FileItem gets set to INVALID_HANDLE_VALUE if it succeeds. * */ HRESULT RemoveFromFilePool( HANDLE* FileItem, BOOL Delete ) { HRESULT RetVal = S_OK; class FilePool * FP = NULL; struct FileListItem * FPI; if (FileItem && *FileItem ) { FPI = (struct FileListItem *) *FileItem; FP = FPI->FP; RetVal = FP->RemoveFromPool(FPI, Delete); if (SUCCEEDED(RetVal)) { *FileItem = INVALID_HANDLE_VALUE; } } else { RetVal = E_INVALIDARG; } return RetVal; } /*++ Routine Name ForceCloseJobPoolFiles Routine Description: Closes all the handles to the SHD and SPL files, regardless if they are in use or not. Arguments: hFileItem - handle to file pool item Return Value: S_OK - all handles were closed or hFileItem is NULL other HRESULT - an error occurred --*/ HRESULT ForceCloseJobPoolFiles( HANDLE hFileItem ) { HRESULT hRet = S_OK; if (hFileItem) { // // Mark the files as ready to be closed. The function below // won't close the file handles ,if they are in use // CloseFiles(hFileItem, TRUE); // // Because CloseFile marked the handles as ready to be closed, the function // below will close the handle for writing to the spool file // if (FAILED(hRet = FinishedWriting(hFileItem, TRUE))) { DBGMSG(DBG_WARN, ("ForceCloseJobPoolFiles FinishedWriting on SPL file failed. HRESULT 0x%x\n", hRet)); } // // Because CloseFile marked the handles as ready to be closed, the function // below will close the handle for reading from the spool file // if (FAILED(hRet = FinishedReading(hFileItem))) { DBGMSG(DBG_WARN, ("ForceCloseJobPoolFiles FinishedReading failed. HRESULT 0x%x\n", hRet)); } // // Because CloseFile marked the handles as ready to be closed, the function // below will close the handle for writing to the shadow file // if (FAILED(hRet = FinishedWriting(hFileItem, FALSE))) { DBGMSG(DBG_WARN, ("ForceCloseJobPoolFiles FinishedWriting on SHD file failed. HRESULT 0x%x\n", hRet)); } } return hRet; } /********************* * CloseFiles() * * Closes the filehandles in the Pool Item (To save memory). * * Arguments: * IN FileItem : FileItem Handle. * IN CloseShad : Close the shadow file handle too. * * */ VOID CloseFiles( HANDLE FileItem, BOOL CloseShad ) { struct FileListItem * FPI; if (FileItem ) { FPI = (struct FileListItem *) FileItem; FPCloseFiles(FPI, CloseShad); } } /********************* * CloseFilesCheck() * * Checks to see if there are any file handles which were not * closed on the call to CloseFiles because they were in use. * If the status was set at that time we delete the files now. * Assumes we are holding the critical section. * * Arguments: * IN FileListItem : FileListItem pointer. * IN CloseHandle : Type of handle to be closed. * */ VOID CloseFilesCheck( FileListItem *pItem, ECloseHandle eCloseHandle ) { if (pItem) { switch (eCloseHandle) { case kCloseWriteHandle: if ((pItem->Status & FP_STATUS_SPL_WRITE_CLOSED) && pItem->SplWriteHandle != INVALID_HANDLE_VALUE) { __try { CloseHandle(pItem->SplWriteHandle); pItem->SplWriteHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing SplWriteHandle in CloseFilesCheck\n")); pItem->SplWriteHandle = INVALID_HANDLE_VALUE; } } break; case kCloseReadHandle: if ((pItem->Status & FP_STATUS_SPL_READ_CLOSED) && pItem->SplReadHandle != INVALID_HANDLE_VALUE) { __try { CloseHandle(pItem->SplReadHandle); pItem->SplReadHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing SplReadHandle in CloseFilesCheck\n")); pItem->SplReadHandle = INVALID_HANDLE_VALUE; } } break; case kCloseShdHandle: if ((pItem->Status & FP_STATUS_SHD_CLOSED) && pItem->ShdWriteHandle != INVALID_HANDLE_VALUE) { __try { CloseHandle(pItem->ShdWriteHandle); pItem->ShdWriteHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing ShdWriteHandle in CloseFilesCheck\n")); pItem->ShdWriteHandle = INVALID_HANDLE_VALUE; } } break; default: break; } } } /********************* * SetFileItemState * * Takes a FilePool Object and frees it, optionally deleting the files associated with it. * * Arguments: * hFileItem - The file item whose state we are going to change. * eState - The new state of the file item * * Returns Error if it is passed in an invalid argument, or S_OK if it succeeds. * */ HRESULT SetFileItemState( IN HANDLE hFileItem, IN EFileItemState eState ) { HRESULT hr = S_OK; hr = hFileItem ? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); if (SUCCEEDED(hr)) { hr = FPSetFileItemState(reinterpret_cast(hFileItem), eState); } return hr; } /********************* * DestroyFilePool() * * Takes a FilePool Object and frees it, optionally deleting the files associated with it. * * Arguments: * IN OUT FilePoolHandle : Pointer to existing Filepool. * IN DeleteFiles : BOOL - TRUE: Delete the Pool files. * * Returns Error if it is passed in an invalid argument, or S_OK if it succeeds. * */ HRESULT DestroyFilePool( HANDLE* FilePoolHandle, BOOL DeleteFiles ) { HRESULT RetVal = S_OK; class FilePool * FP = NULL; if (FilePoolHandle && *FilePoolHandle) { FP = (class FilePool *)FilePoolHandle; FP->DeleteEmptyFilesOnClose = DeleteFiles; delete FP; } else { RetVal = E_INVALIDARG; } return RetVal; } /********************* * TrimPool() * * Takes a FilePool Object and trim the free list, deleting old files. * * Arguments: * IN FilePool : Handle of a FilePool Object Created with CreateFilePool() * * Returns TRUE if there are files still in the free list, or FALSE if no files left. */ BOOL TrimPool( HANDLE FilePoolHandle ) { class FilePool * FP = NULL; BOOL Retval = FALSE; if (FilePoolHandle) { FP = (class FilePool *)FilePoolHandle; Retval = FP->TrimPool(); } return Retval; } /********************* * ChangeFilePoolBasePath() * * Takes a FilePool Object and changes the base path in it. This will be called * when the spool directory is changed. * * Arguments: * IN FilePool : Handle of a FilePool Object Created with CreateFilePool * IN BasePath : New base path to use * * Returns an HRESULT */ HRESULT ChangeFilePoolBasePath( IN HANDLE *FilePoolHandle, IN LPCTSTR BasePath ) { FilePool *FP = (FilePool *)FilePoolHandle; HRESULT RetVal = E_INVALIDARG; LPTSTR FileBase = NULL; if (FP) { if (FileBase = AllocSplStr(BasePath)) { FP->EnterCritSec(); FreeSplStr(FP->FileBase); FP->FileBase = FileBase; FP->LeaveCritSec(); RetVal = S_OK; } else { RetVal = E_OUTOFMEMORY; } } return RetVal; } /*-- Generic Utility Functions ConvertFileExt --*/ /********************* * ConvertFileExt() * * Utility function to change one extension to another. * Requires the extension to only be in the filename once. * The extensions being converted must be the same size. * * Arguments: * IN OUT FileName : String of Filename to be changed. * IN ExtFrom : Extension to change. * IN ExtTo : Extension to change it to. * */ HRESULT ConvertFileExt( PWSTR Filename, PCWSTR ExtFrom, PCWSTR ExtTo ) { HRESULT RetVal = E_FAIL; PWSTR Temp = NULL; DWORD cchExtTo = 0; if (Filename && ExtFrom && ExtTo && (wcslen(ExtFrom) == (cchExtTo = wcslen(ExtTo))) && ExtFrom[0]) { Temp = wcsstr(Filename, ExtFrom); if (Temp) { // // This should work in all our cases. // CopyMemory(Temp, ExtTo, cchExtTo * sizeof(WCHAR)); RetVal = S_OK; } } else { RetVal = E_INVALIDARG; } return RetVal; } /*-- FileItem Management Functions TruncateFiles --*/ /********************* * TruncateFiles() * * Takes a File Item and truncates any open files to 0 length. * * Arguments: * IN FileListItem : Item containing the files to truncate. * * */ VOID TruncateFiles( struct FileListItem * Item ) { BOOL Trunced = FALSE; if (Item) { Item->EnterCritSec(); // // Reinitialize cache data. // Item->CreateInfo = 0; // // Truncate the Shadow File // if (Item->ShdWriteHandle != INVALID_HANDLE_VALUE) { if (SetFilePointer(Item->ShdWriteHandle, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { if (!SetEndOfFile(Item->ShdWriteHandle)) { DBGMSG(DBG_WARN, ("FAILED to set SPL end of file to 0 %d.\n", GetLastError())); } } } // // Truncate the Spool File. If the writehandle is closed, use the readhandle. // if (Item->SplWriteHandle != INVALID_HANDLE_VALUE) { if (SetFilePointer(Item->SplWriteHandle, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { if (!SetEndOfFile(Item->SplWriteHandle)) { DBGMSG(DBG_WARN, ("FAILED to set SPL end of file to 0 %d.\n", GetLastError())); } } } else if (Item->SplReadHandle != INVALID_HANDLE_VALUE) { if (SetFilePointer(Item->SplReadHandle, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { if (!SetEndOfFile(Item->SplReadHandle)) { DBGMSG(DBG_WARN, ("FAILED to set SPL end of file to 0 %d.\n", GetLastError())); } } // There is only one open spool handle at this point so make that // open handle the writer. This is what the spooler needs when it // first requests a new pool item. This also saves file I/O if the // next use of this item doesn't need both writer and reader at the // same time. Item->SplWriteHandle = Item->SplReadHandle; Item->SplReadHandle = INVALID_HANDLE_VALUE; Trunced = TRUE; } if ((Item->SplReadHandle != INVALID_HANDLE_VALUE) && !Trunced) { //CloseHandle(Item->SplReadHandle); SetFilePointer(Item->SplReadHandle, 0, NULL, FILE_BEGIN); } Item->LeaveCritSec(); } } /********************* * FPCloseFiles() * * Takes a FileList item and closes the files associated with it, if they * are not in use. Else it just marks them as closed and it is the job of the * reader or writer to close the handles when it is done with them. * * Arguments: * IN FileListItem : FileItem with files to close. * IN CloseShad : BOOL - TRUE: Close the Shadow File too. * * */ VOID FPCloseFiles( struct FileListItem * pItem, BOOL CloseShad ) { if (pItem) { pItem->EnterCritSec(); if (pItem->SplWriteHandle != INVALID_HANDLE_VALUE) { if (pItem->Status & FP_STATUS_SPL_WRITING) { pItem->Status |= FP_STATUS_SPL_WRITE_CLOSED; } else { __try { CloseHandle(pItem->SplWriteHandle); pItem->SplWriteHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing SplWriteHandle in FPCloseFiles\n")); pItem->SplWriteHandle = INVALID_HANDLE_VALUE; } } } if (pItem->SplReadHandle != INVALID_HANDLE_VALUE) { if (pItem->Status & FP_STATUS_SPL_READING) { pItem->Status |= FP_STATUS_SPL_READ_CLOSED; } else { __try { CloseHandle(pItem->SplReadHandle); pItem->SplReadHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing SplReadHandle in FPCloseFiles\n")); pItem->SplReadHandle = INVALID_HANDLE_VALUE; } } } if ( CloseShad && pItem->ShdWriteHandle != INVALID_HANDLE_VALUE) { if (pItem->Status & FP_STATUS_SPL_WRITING) { pItem->Status |= FP_STATUS_SPL_WRITE_CLOSED; } else { __try { CloseHandle(pItem->ShdWriteHandle); pItem->ShdWriteHandle = INVALID_HANDLE_VALUE; } __except ( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("Hit an Exception Closing ShdWriteHandle in FPCloseFiles\n")); pItem->ShdWriteHandle = INVALID_HANDLE_VALUE; } } } pItem->LeaveCritSec(); } } /********************* * FPSetFileItemState * * Changes the state of a file pool item. Currently, it can just be marked not * to recycle the file pool object. * * Arguments: * pItem : The file list item. * eFileItemState : New state for the file item. * */ HRESULT FPSetFileItemState( IN FileListItem *pItem, IN EFileItemState eFileItemState ) { HRESULT hr = S_OK; pItem->EnterCritSec(); switch(eFileItemState) { case kDontRecycle: pItem->Status |= FP_STATUS_ITEM_DONT_RECYCLE; break; default: hr = HRESULT_FROM_WIN32(ERROR_INVALID_LEVEL); break; } pItem->LeaveCritSec(); return hr; } /********************* * DeletePoolFile() * * Takes a FilePool Object and trim the free list, deleting old files and freeing memory. * * Arguments: * IN OUT FileListItem : FileItem to delete the files from and free up. * * Closes the files, deletes them, frees the memory, and sets the pItem to NULL * */ VOID DeletePoolFile(struct FileListItem ** ppItem) { struct FileListItem * pItem; if (ppItem && *ppItem) { pItem = *ppItem; FPCloseFiles(pItem, TRUE); DeleteCriticalSection(&pItem->CritSec); if (pItem->SplFilename) { DeleteFile(pItem->SplFilename); FreeSplMem(pItem->SplFilename); } if (pItem->ShdFilename) { DeleteFile(pItem->ShdFilename); FreeSplMem(pItem->ShdFilename); } FreeSplMem(pItem); } *ppItem = NULL; } /*-- List Management Functions RemoveFromFPList AddToFPListEnd AddToFPListHead --*/ HRESULT RemoveFromFPList( struct FileListItem * Item, struct FileListItem ** Head, struct FileListItem ** Tail ) { struct FileListItem * Check = NULL; HRESULT RetVal = E_FAIL; // // Validate we have valid args. // if ( Item && Head && *Head && Tail && *Tail ) { for ( Check = *Head; Check && (Item != Check); Check = Check->FLNext ); if ( Check ) { if ( *Head == Check ) { *Head = Check->FLNext; } else { Check->FLPrev->FLNext = Check->FLNext; } if ( *Tail == Check ) { *Tail = Check->FLPrev; } else { Check->FLNext->FLPrev = Check->FLPrev; } Check->FLNext = NULL; Check->FLPrev = NULL; RetVal = S_OK; } else { // // you gave me a pointer to an item not in the list. // RetVal = E_POINTER; } } else { RetVal = E_INVALIDARG; } return RetVal; } HRESULT AddToFPListEnd( struct FileListItem * Item, struct FileListItem ** Head, struct FileListItem ** Tail ) { struct FileListItem * Check = NULL; HRESULT RetVal = E_FAIL; // // Validate we have valid args. // if ( Item && Head && Tail ) { if ( *Tail ) { // // There are items in the list, add something // onto the end. // Check = *Tail; Check->FLNext = Item; Item->FLPrev = Check; Item->FLNext = NULL; *Tail = Item; RetVal = S_OK; } else { if ( *Head ) { // // If we have a head and no tail, something // is seriously wrong. // RetVal = E_FAIL; } else { // // Adding the first item into a list. // Item->FLNext = NULL; Item->FLPrev = NULL; *Head = Item; *Tail = Item; RetVal = S_OK; } } } else { RetVal = E_INVALIDARG; } return RetVal; } HRESULT AddToFPListHead( struct FileListItem * Item, struct FileListItem ** Head, struct FileListItem ** Tail ) { HRESULT RetVal = S_OK; if ( Item && Head && Tail ) { if ( *Head ) { Item->FLNext = *Head; (*Head)->FLPrev = Item; } else { *Tail = Item; } *Head = Item; Item->FLPrev = NULL; } else { RetVal = E_INVALIDARG; } return RetVal; } VOID FreeFPList( struct FileListItem ** Head, BOOL DeleteFiles ) { struct FileListItem * Item = NULL; struct FileListItem * Next = NULL; if (Head && *Head) { Item = *Head; while (Item) { Next = Item->FLNext; Item->FLNext = NULL; Item->FLPrev = NULL; if (Item->SplFilename && DeleteFiles) { DeletePoolFile(&Item); Item = Next; } else { FPCloseFiles(Item, TRUE); DeleteCriticalSection(&Item->CritSec); if (Item->SplFilename) { FreeSplMem(Item->SplFilename); } if (Item->ShdFilename) { FreeSplMem(Item->ShdFilename); } FreeSplMem(Item); Item = Next; } } *Head = NULL; } } /*-- FilePool Class Functions: InitFilePoolVars FilePool ~FilePool EnterCritSec LeaveCritSec GetNextFileName CreatePoolFile GetWriteFileStruct ReleasePoolHandle DeletePoolFile operator delete --*/ /********************* * Filepool::FilePool() * * See CreateFilePool() Above. */ FilePool::FilePool( DWORD PTimeout, DWORD MaxFreeFiles ) : FreeFiles(NULL), EndFreeFiles(NULL), FileInUse(NULL), EndUsedFiles(NULL), CurrentNum(0), PoolTimeout(PTimeout), MaxFiles(MaxFreeFiles), FreeSize(0), UsedSize(0), DeleteEmptyFilesOnClose(FALSE) { SplModes.Mode = GENERIC_READ | GENERIC_WRITE; SplModes.Flags = FILE_ATTRIBUTE_NORMAL; SplModes.ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; SplModes.Disp = 0; ShdModes.Mode = GENERIC_WRITE; ShdModes.Flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN; ShdModes.ShareMode = 0; ShdModes.Disp = 0; } /********************* * Filepool::AllocInit() * * See CreateFilePool() Above. */ HRESULT FilePool::AllocInit( LPCTSTR BasePath, LPCTSTR PreNumStr, LPCTSTR SplExt, LPCTSTR ShdExt ) { HRESULT RetVal = S_OK; __try { InitializeCriticalSection(&FilePoolCritSec); } __except( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("FilePool: Failed to Initialise Critical Section.\n")); RetVal = E_FAIL; } if ((RetVal == S_OK) && (FileBase = AllocSplStr(BasePath)) && (FilePreNumStr = AllocSplStr(PreNumStr)) && (SplFileExt = AllocSplStr(SplExt)) && (ShdFileExt = AllocSplStr(ShdExt)) ) { // // We have all of our strings allocated. // } else { FreeSplStr(FileBase); FreeSplStr(FilePreNumStr); FreeSplStr(SplFileExt); FreeSplStr(ShdFileExt); RetVal = E_FAIL; } return RetVal; } /********************* * Filepool::FilePool() * * See CreateFilePool() Above. */ FilePool::~FilePool( ) { if (FileBase) { FreeSplMem(FileBase); } if (FilePreNumStr) { FreeSplMem(FilePreNumStr); } if (SplFileExt) { FreeSplMem(SplFileExt); } if (ShdFileExt) { FreeSplMem(ShdFileExt); } // // Free The lists here. // FreeFPList(&FileInUse, FALSE); FreeFPList(&FreeFiles, DeleteEmptyFilesOnClose); EndUsedFiles = NULL; EndFreeFiles = NULL; DeleteCriticalSection(&FilePoolCritSec); } VOID FilePool::EnterCritSec() { EnterCriticalSection(&FilePoolCritSec); } VOID FilePool::LeaveCritSec() { LeaveCriticalSection(&FilePoolCritSec); } VOID FileListItem::EnterCritSec() { EnterCriticalSection(&CritSec); } VOID FileListItem::LeaveCritSec() { LeaveCriticalSection(&CritSec); } /********************* * FilePool::GetNextFileName() * * Returns the next open Spool file name. Allocates memory. * * Arguments: * * Return: * A valid spool file name, or NULL. * */ LPWSTR FilePool::GetNextFileName(VOID) { DWORD SizeToAlloc = 0; PWSTR FName = NULL; SizeToAlloc = wcslen(FileBase) + 1 + wcslen(FilePreNumStr) + 5 + wcslen(SplFileExt) + 1; FName = (PWSTR)AllocSplMem(SizeToAlloc * sizeof(WCHAR)); if (FName) { DWORD NextNum; EnterCritSec(); NextNum = CurrentNum; CurrentNum++; if (CurrentNum > 99999) { CurrentNum = 0; } LeaveCritSec(); StringCchPrintf(FName, SizeToAlloc, L"%ws\\%ws%05d%ws", FileBase, FilePreNumStr, NextNum, SplFileExt); } return FName; } /********************* * FilePool::GetNextFileNameNoAlloc() * * Returns the next open Spool file name. Uses a buffer. * It is only used after a call to GetNextFileName. * * Arguments: * IN OUT Filename : Buffer to copy the name into. * IN cchFileName : Size of the buffer to copy into. * * Return: * None. * */ HRESULT FilePool::GetNextFileNameNoAlloc( IN OUT PWSTR Filename, IN SIZE_T cchFileName ) { DWORD NextNum; EnterCritSec(); NextNum = CurrentNum; CurrentNum++; if (CurrentNum > 99999) { CurrentNum = 0; } LeaveCritSec(); return StringCchPrintf(Filename, cchFileName, L"%ws\\%ws%05d%ws", FileBase, FilePreNumStr, NextNum, SplFileExt); } /********************* * Filepool::CreatePoolFile() * * Takes a pointer to a Filepool item and returns a new one. Can use a filename * passed in. * * Parameters: * OUT Item : Pointer to a File Item. * IN Filename : Optional Filename. Can be NULL. * * Returns S_OK if successful. */ HRESULT FilePool::CreatePoolFile( struct FileListItem ** Item, PWSTR Filename ) { HRESULT RetVal = E_FAIL; struct FileListItem * Temp = NULL; DWORD OldNum = 0; BOOL CritInitialized = FALSE; if ( Item ) { Temp = (struct FileListItem *)AllocSplMem(sizeof(struct FileListItem)); if ( Temp ) { Temp->FLNext = NULL; Temp->FLPrev = NULL; OldNum = CurrentNum; Temp->TimeStamp = 0; Temp->FP = this; Temp->CreateInfo = 0; __try { InitializeCriticalSection(&Temp->CritSec); CritInitialized = TRUE; } __except( EXCEPTION_EXECUTE_HANDLER ) { DBGMSG(DBG_WARN, ("FilePool: Failed to Initialise FL Critical Section.\n")); RetVal = E_FAIL; } if (CritInitialized) { if ( Filename ) { Temp->SplFilename = AllocSplStr(Filename); Temp->ShdFilename = AllocSplStr(Filename); if (Temp->SplFilename && Temp->ShdFilename) { *Item = Temp; ConvertFileExt(Temp->ShdFilename, SplFileExt, ShdFileExt); Temp->SplReadHandle = INVALID_HANDLE_VALUE; Temp->SplWriteHandle = INVALID_HANDLE_VALUE; Temp->ShdWriteHandle = INVALID_HANDLE_VALUE; RetVal = S_OK; } else { RetVal = E_OUTOFMEMORY; } } else { Temp->SplFilename = GetNextFileName(); if (Temp->SplFilename) { RetVal = S_OK; while (FileExists(Temp->SplFilename)) { // // Fundamental assumption here is the number of // characters occupied by the filename does not // change. // GetNextFileNameNoAlloc(Temp->SplFilename, wcslen(Temp->SplFilename) + 1); if (OldNum == CurrentNum) { // // We went right around. // RetVal = E_FAIL; break; } } if (SUCCEEDED(RetVal)) { Temp->ShdFilename = AllocSplStr(Temp->SplFilename); if (Temp->ShdFilename) { ConvertFileExt(Temp->ShdFilename, SplFileExt, ShdFileExt); Temp->SplReadHandle = INVALID_HANDLE_VALUE; Temp->SplWriteHandle = INVALID_HANDLE_VALUE; Temp->ShdWriteHandle = INVALID_HANDLE_VALUE; *Item = Temp; RetVal = S_OK; } } } } } } } else { RetVal = E_INVALIDARG; } if (FAILED(RetVal)) { if (Temp) { if (CritInitialized) { DeleteCriticalSection(&Temp->CritSec); } if (Temp->SplFilename) { FreeSplMem(Temp->SplFilename); } if (Temp->ShdFilename) { FreeSplMem(Temp->ShdFilename); } FreeSplMem(Temp); } } return RetVal; } /********************* * Filepool::GetWriteFileStruct() * * See GetFileItemHandle() Above. */ HRESULT FilePool::GetWriteFileStruct( struct FileListItem ** File, PWSTR Filename ) { struct FileListItem * Temp = NULL; HRESULT RetVal = S_OK; HRESULT OurRetVal = S_OK; EnterCritSec(); if ( FreeFiles && !Filename) { Temp = FreeFiles; RetVal = RemoveFromFPList( Temp, &FreeFiles, &EndFreeFiles ); if (SUCCEEDED(RetVal)) { FreeSize--; RetVal = AddToFPListEnd( Temp, &FileInUse, &EndUsedFiles); if (FAILED(RetVal)) { // // Bad things // DBGMSG(DBG_WARN, ("Could not add to List End %x\n", RetVal)); OurRetVal = E_FAIL; } else { UsedSize++; } } else { // // Find out what went wrong. // DBGMSG(DBG_WARN, ("Could not remove Item %x\n", RetVal)); Temp = NULL; OurRetVal = E_FAIL; } } else { LeaveCritSec(); RetVal = CreatePoolFile(&Temp, Filename); if ( FAILED(RetVal) ) { // // Bad Things // DBGMSG(DBG_WARN, ("Could not create Item %x\n", RetVal)); OurRetVal = E_FAIL; } else { EnterCritSec(); RetVal = AddToFPListEnd(Temp, &FileInUse, &EndUsedFiles); if ( FAILED(RetVal) ) { // // Bad Things // DBGMSG(DBG_WARN, ("Could not add to List End after create %x\n", RetVal)); OurRetVal = E_FAIL; } else { UsedSize++; } LeaveCritSec(); } EnterCritSec(); } LeaveCritSec(); if ( FAILED(OurRetVal) ) { // // Clean up. // if ( Temp ) { // // We weren't able to add the file to the structure, // This should never happen, but if it does, clean up // the memory we use. // DeletePoolFile(&Temp); } *File = NULL; } else { *File = Temp; } return OurRetVal; } /********************* * Filepool::ReleasePoolHandle() * * See ReleasePoolHandle() Above. */ HRESULT FilePool::ReleasePoolHandle( struct FileListItem * File ) { BOOL bDeletePoolFile = FALSE; HRESULT RetVal = S_OK; HRESULT RemRetVal = S_OK; if ((File->Status & FP_STATUS_SPL_READING) || (File->Status & FP_STATUS_SPL_WRITING) || (File->Status & FP_STATUS_SHD_WRITING)) { RetVal = E_FAIL; // // This is a pathological case as we will subsequently leak this handle. // Break. // DBGMSG(DBG_ERROR, ("Tried to release a file with handles in use\n")); } else { EnterCritSec(); RemRetVal = RemoveFromFPList(File, &FileInUse, &EndUsedFiles); if (SUCCEEDED(RemRetVal)) { UsedSize--; // // If the spool directory has changed we need to delete the pool file // if ( _wcsnicmp(FileBase, File->SplFilename, wcslen(FileBase)) ) { bDeletePoolFile = TRUE; } } LeaveCritSec(); if (SUCCEEDED(RemRetVal)) { if (bDeletePoolFile == TRUE || FreeSize >= MaxFiles || File->Status & FP_STATUS_ITEM_DONT_RECYCLE || ((File->SplWriteHandle == INVALID_HANDLE_VALUE) && (File->SplReadHandle == INVALID_HANDLE_VALUE))) { DeletePoolFile(&File); } else { File->TimeStamp = GetTickCount(); TruncateFiles(File); EnterCritSec(); RetVal = AddToFPListEnd(File, &FreeFiles, &EndFreeFiles); if (SUCCEEDED(RetVal)) { FreeSize++; } LeaveCritSec(); } } else { RetVal = E_INVALIDARG; } } return RetVal; } /********************* * Filepool::CreateSplReader() * *Used in GetReaderFromHandle. */ HRESULT FilePool::CreateSplReader( struct FileListItem * Item ) { HRESULT RetVal = S_OK; HANDLE Temp = INVALID_HANDLE_VALUE; Temp = CreateFile( Item->SplFilename, SplModes.Mode, SplModes.ShareMode, NULL, OPEN_EXISTING | SplModes.Disp, SplModes.Flags, NULL); if (Temp && (Temp != INVALID_HANDLE_VALUE)) { Item->SplReadHandle = Temp; Item->CreateInfo |= FP_SPL_READER_CREATED; } else { RetVal = E_FAIL; } return RetVal; } /********************* * Filepool::CreateSplWriter() * * Does the CreateFile for the Spool File. Used for GetWriterFromHandle(). */ HRESULT FilePool::CreateSplWriter( struct FileListItem * Item ) { HRESULT RetVal = S_OK; HANDLE Temp = INVALID_HANDLE_VALUE; Temp = CreateFile( Item->SplFilename, SplModes.Mode, SplModes.ShareMode, NULL, CREATE_ALWAYS | SplModes.Disp, SplModes.Flags, NULL); if (Temp && (Temp != INVALID_HANDLE_VALUE)) { Item->SplWriteHandle = Temp; Item->CreateInfo |= FP_SPL_WRITER_CREATED; } else { RetVal = E_FAIL; } return RetVal; } /********************* * Filepool::CreateShdWriter() * * Does the CreateFile for the ShadowFile. Used for GetWriterFromHandle(). */ HRESULT FilePool::CreateShdWriter( struct FileListItem * Item ) { HRESULT RetVal = S_OK; HANDLE Temp = INVALID_HANDLE_VALUE; Temp = CreateFile( Item->ShdFilename, ShdModes.Mode, ShdModes.ShareMode, NULL, CREATE_ALWAYS | ShdModes.Disp, ShdModes.Flags, NULL); if (Temp && (Temp != INVALID_HANDLE_VALUE)) { Item->ShdWriteHandle = Temp; Item->CreateInfo |= FP_SHD_CREATED; } else { RetVal = E_FAIL; } return RetVal; } /********************* * Filepool::RemoveFromPool() * * See RemoveFromFilePool() Above. */ HRESULT FilePool::RemoveFromPool( struct FileListItem * File, BOOL Delete ) { HRESULT RemRetVal = S_OK; if ((File->Status & FP_STATUS_SPL_READING) || (File->Status & FP_STATUS_SPL_WRITING) || (File->Status & FP_STATUS_SHD_WRITING)) { RemRetVal = E_FAIL; // // This is a pathological case as it will cause us to leak a KM handle. // Hard break here. // DBGMSG(DBG_ERROR, ("Tried to release a file with handles in use\n")); } else { EnterCritSec(); RemRetVal = RemoveFromFPList(File, &FileInUse, &EndUsedFiles); if (SUCCEEDED(RemRetVal)) { UsedSize--; } LeaveCritSec(); if (FAILED(RemRetVal)) { EnterCritSec(); RemRetVal = RemoveFromFPList(File, &FreeFiles, &EndFreeFiles); if (SUCCEEDED(RemRetVal)) { FreeSize--; } LeaveCritSec(); } if (SUCCEEDED(RemRetVal)) { if (Delete) { DeletePoolFile(&File); } else { FPCloseFiles(File, TRUE); DeleteCriticalSection(&File->CritSec); if (File->SplFilename) { FreeSplMem(File->SplFilename); } if (File->ShdFilename) { FreeSplMem(File->ShdFilename); } FreeSplMem(File); } } } return RemRetVal; } /********************* * Filepool::TrimPool() * * See TrimPool() Above. */ BOOL FilePool::TrimPool( VOID ) { DWORD Time = 0; BOOL GotToTrim = TRUE; struct FileListItem * Temp = NULL; struct FileListItem * DeleteFiles = NULL; DWORD TrimCount = 0; BOOL bFilesToTrim = FALSE; Time = GetTickCount(); EnterCritSec(); DeleteFiles = FreeFiles; while (FreeFiles) { if ((FreeFiles->TimeStamp > Time) || (Time - FreeFiles->TimeStamp > PoolTimeout)) { // // Walk forward until you reach a file that is not too old. // FreeFiles = FreeFiles->FLNext; TrimCount++; } else { if (FreeFiles->FLPrev) { // // There are files to delete. // FreeFiles->FLPrev->FLNext = NULL; FreeFiles->FLPrev = NULL; } else { // // No files have timed out. // DeleteFiles = NULL; } break; } } if (!FreeFiles) { EndFreeFiles = NULL; } // // We need to decrease the FreeSize by the number of elements we are just about // to trim off it. // FreeSize -= TrimCount; // // We should trim files the next time around if there is a FreeFiles list. // bFilesToTrim = FreeFiles != NULL; LeaveCritSec(); // // Now outside the critsec do the deletions. // while (DeleteFiles) { struct FileListItem * Temp = DeleteFiles; DeleteFiles = DeleteFiles->FLNext; DeletePoolFile(&Temp); } return bFilesToTrim; } void* FilePool::operator new( size_t n ) { return AllocSplMem(n); } void FilePool::operator delete( void* p, size_t n ) { if (p) { FreeSplMem(p); } }