// ITSFS.CPP -- Implementation for the class CITFileSystem #include "StdAfx.h" //#include const WCHAR *pwscSpaceNameListStream = L"::DataSpace/NameList"; const WCHAR *pwcsSpaceNameStorage = L"::DataSpace/Storage/"; const WCHAR *pwcsSpaceContentSuffix = L"/Content"; const WCHAR *pwcsTransformListSuffix = L"/Transform/List"; const WCHAR *pwcsSpanInfoSuffix = L"/SpanInfo"; const WCHAR *pwcsTransformSubStorage = L"/Transform/"; const WCHAR *pwcsControlDataSuffix = L"/ControlData"; const WCHAR *pwcsInstanceSubStorage = L"/InstanceData/"; const WCHAR *pwcsTransformStorage = L"::Transform/"; const WCHAR *pwcsUncompressedSpace = L"Uncompressed"; const WCHAR *pwcsLZXSpace = L"MSCompressed"; // Creators: HRESULT __stdcall CITFileSystem::CreateITFileSystem (IUnknown *punkOuter, const WCHAR * pwcsName, DWORD grfMode, PITS_Control_Data pControlData, LCID lcid, IStorage ** ppstgOpen ) { CSyncWith sw(g_csITFS); ILockBytes *pLKB = NULL; HRESULT hr = CFSLockBytes::Create(NULL, pwcsName, (grfMode & (~SHARE_MASK) & (~RW_ACCESS_MASK)) | STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, &pLKB ); if (SUCCEEDED(hr)) { hr = CreateITFSOnLockBytes(punkOuter, pLKB, grfMode, pControlData, lcid, ppstgOpen); pLKB->Release(); // if the Create on LockBytes succeeded, it AddRef'd pLKB. } return hr; } HRESULT __stdcall CITFileSystem::CreateITFSOnLockBytes (IUnknown *punkOuter, ILockBytes * pLKB, DWORD grfMode, PITS_Control_Data pControlData, LCID lcid, IStorage **ppstgOpen ) { CSyncWith sw(g_csITFS); CITFileSystem *pITFS = New CITFileSystem(punkOuter); if (!pITFS) { pLKB->Release(); return STG_E_INSUFFICIENTMEMORY; } IITFileSystem *pIITFileSystem = NULL; IStorageITEx *pStorage = NULL; pITFS->AddRef(); // Because of the reference counting tricks // within InitCreateOnLockBytes. HRESULT hr = pITFS->m_ImpITFileSystem.InitCreateOnLockBytes (pLKB, grfMode, pControlData, lcid); if (SUCCEEDED(hr)) { pIITFileSystem = (IITFileSystem *) &pITFS->m_ImpITFileSystem; // If the OpenStorage call below succeeds, it will AddRef pITFS. hr = pIITFileSystem->OpenStorage(NULL, L"/", grfMode, &pStorage); pITFS->Release(); // To match the AddRef call above. } else delete pITFS; *ppstgOpen = pStorage; return hr; } IITFileSystem *CITFileSystem::CImpITFileSystem::FindFileSystem(const WCHAR *pwcsPath) { CSyncWith sw(g_csITFS); CImpITFileSystem *pITFS = (CImpITFileSystem *) g_pImpITFileSystemList; for (; pITFS; pITFS = (CImpITFileSystem *) pITFS->NextObject()) if (!wcsicmp_0x0409(pwcsPath, pITFS->m_awszFileName)) { pITFS->AddRef(); return pITFS; } return NULL; } HRESULT __stdcall CITFileSystem::IsITFile(const WCHAR * pwcsName) { CSyncWith sw(g_csITFS); IITFileSystem *pITFS = CImpITFileSystem::FindFileSystem(pwcsName); if (pITFS) { pITFS->Release(); return S_OK; } ILockBytes *pLKB = NULL; ITSFileHeader ITFH; HRESULT hr = CFSLockBytes::Open(NULL, pwcsName, STGM_READ | STGM_SHARE_DENY_NONE, &pLKB); if (SUCCEEDED(hr)) hr = IsITLockBytes(pLKB); else hr = S_FALSE; if (pLKB) pLKB->Release(); return hr; } HRESULT __stdcall CITFileSystem::IsITLockBytes(ILockBytes * pLKB) { CSyncWith sw(g_csITFS); ITSFileHeader ITFH; ULONG cbRead = 0; HRESULT hr = pLKB->ReadAt(CULINT(0).Uli(), &ITFH, sizeof(ITFH), &cbRead); if ( hr != S_OK || cbRead < sizeof(ITSFileHeaderV2) || ITFH.uMagic != MAGIC_ITS_FILE || ITFH.uFormatVersion < FirstReleasedVersion || ITFH.uFormatVersion > CurrentFileFormatVersion ) hr = S_FALSE; else if ( ITFH.uFormatVersion == CurrentFileFormatVersion && cbRead == sizeof(ITSFileHeader) ) hr = S_OK; else if (ITFH.uFormatVersion == FirstReleasedVersion) { ITFH.offPathMgrOrigin = 0; hr = S_OK; } else { RonM_ASSERT(FALSE); // If this assert fires, we have a old version // that isn't being handled. hr = S_FALSE; } return hr; } HRESULT __stdcall CITFileSystem::QueryFileStampAndLocale (const WCHAR *pwcsName, DWORD *pFileStamp, DWORD *pFileLocale) { CSyncWith sw(g_csITFS); IITFileSystem *pITFS = CImpITFileSystem::FindFileSystem(pwcsName); if (pITFS) { HRESULT hr = pITFS->QueryFileStampAndLocale(pFileStamp, pFileLocale); pITFS->Release(); return hr; } ILockBytes *pLKB = NULL; ITSFileHeader ITFH; HRESULT hr = CFSLockBytes::Open(NULL, pwcsName, STGM_READ | STGM_SHARE_DENY_NONE, &pLKB); if (SUCCEEDED(hr)) { hr = QueryLockByteStampAndLocale(pLKB, pFileStamp, pFileLocale); pLKB->Release(); } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::QueryFileStampAndLocale (DWORD *pFileStamp, DWORD *pFileLocale) { if (pFileStamp) *pFileStamp = m_itfsh.dwStamp; if (pFileLocale) *pFileLocale = m_itfsh.lcid; return S_OK; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::CountWrites() { InterlockedIncrement((long *) &m_itfsh.dwStamp); return S_OK; } HRESULT __stdcall CITFileSystem::QueryLockByteStampAndLocale (ILockBytes * plkbyt, DWORD *pFileStamp, DWORD *pFileLocale) { CSyncWith sw(g_csITFS); ITSFileHeader ITFH; ULONG cbRead = 0; HRESULT hr = plkbyt->ReadAt(CULINT(0).Uli(), &ITFH, sizeof(ITFH), &cbRead); if (!SUCCEEDED(hr)) return hr; if ( cbRead < sizeof(ITSFileHeaderV2) || ITFH.uMagic != MAGIC_ITS_FILE || ITFH.uFormatVersion < FirstReleasedVersion || ITFH.uFormatVersion > CurrentFileFormatVersion ) return STG_E_INVALIDHEADER; else if ( ITFH.uFormatVersion == CurrentFileFormatVersion && cbRead == sizeof(ITSFileHeader) ) { } else if (ITFH.uFormatVersion == FirstReleasedVersion) { } else { RonM_ASSERT(FALSE); // If this assert fires, we have a old version // that isn't being handled. return STG_E_INVALIDHEADER; } if (pFileStamp) *pFileStamp = ITFH.dwStamp; if (pFileLocale) *pFileLocale = ITFH.lcid; return S_OK; } HRESULT __stdcall CITFileSystem::OpenITFileSystem(IUnknown *punkOuter, const WCHAR * pwcsName, DWORD grfMode, IStorageITEx ** ppstgOpen ) { HRESULT hr; IStorage *pStorage = NULL; CSyncWith sw(g_csITFS); IITFileSystem *pIITFS = CImpITFileSystem::FindFileSystem(pwcsName); if (pIITFS) { if (!pIITFS->IsCompacting()) { hr = pIITFS->OpenStorage(punkOuter, L"/", grfMode, ppstgOpen); } else { hr = E_FAIL; } pIITFS->Release(); return hr; } ILockBytes *pLKB = NULL; hr = CFSLockBytes::Open(NULL, pwcsName, grfMode, &pLKB); if (SUCCEEDED(hr)) { hr = OpenITFSOnLockBytes(punkOuter, pLKB, grfMode, ppstgOpen); pLKB->Release(); // If OpenITFSOnLockBytes succeeds, it will AddRef pLKB. } return hr; } HRESULT __stdcall CITFileSystem::OpenITFSOnLockBytes (IUnknown *punkOuter, ILockBytes * plkbyt, DWORD grfMode, IStorageITEx ** ppstgOpen ) { CSyncWith sw(g_csITFS); CITFileSystem *pITFS = New CITFileSystem(punkOuter); if (!pITFS) return STG_E_INSUFFICIENTMEMORY; IStorage *pStorage = NULL; pITFS->AddRef(); // Because of the reference counting tricks // within InitCreateOnLockBytes. HRESULT hr = pITFS->m_ImpITFileSystem.InitOpenOnLockBytes(plkbyt, grfMode); if (SUCCEEDED(hr)) { // If the OpenStorage call below succeeds, it will AddRef pITFS. hr = pITFS->m_ImpITFileSystem.OpenStorage(NULL, L"/", grfMode, ppstgOpen); pITFS->Release(); // To match the AddRef above } else delete pITFS; return hr; } HRESULT __stdcall CITFileSystem::SetITFSTimes (WCHAR const * pwcsName, FILETIME const * pctime, FILETIME const * patime, FILETIME const * pmtime ) { CSyncWith sw(g_csITFS); IITFileSystem *pITFS = CImpITFileSystem::FindFileSystem(pwcsName); if (pITFS) { HRESULT hr = pITFS->SetITFSTimes(pctime, patime, pmtime); pITFS->Release(); return S_OK; } ILockBytes *pLKB = NULL; HRESULT hr = CFSLockBytes::Open(NULL, pwcsName, STGM_READ, &pLKB); if (SUCCEEDED(hr)) if (S_OK == IsITLockBytes(pLKB)) hr = ((CFSLockBytes::CImpILockBytes *)pLKB)->SetTimes(pctime, patime, pmtime); else hr = STG_E_FILENOTFOUND; if (pLKB) pLKB->Release(); return hr; } HRESULT __stdcall CITFileSystem::DefaultControlData(PITS_Control_Data *ppControlData) { *ppControlData = NULL; PITSFS_Control_Data pCD = PITSFS_Control_Data(OLEHeap()->Alloc(sizeof(ITSFS_Control_Data))); if (!pCD) return STG_E_INSUFFICIENTMEMORY; pCD->cdwFollowing = 6; pCD->cdwITFS_Control = 5; pCD->dwMagic = MAGIC_ITSFS_CONTROL; pCD->dwVersion = 1; pCD->cbDirectoryBlock = 8192; pCD->cMinCacheEntries = 20; pCD->fFlags = fDefaultIsCompression; *ppControlData = PITS_Control_Data(pCD); return NO_ERROR; } // Constructor: CITFileSystem::CImpITFileSystem::CImpITFileSystem (CITFileSystem *pITFileSystem, IUnknown *punkOuter) : IITFileSystem(pITFileSystem, punkOuter) { ZeroMemory(&m_itfsh, sizeof(m_itfsh)); m_itfsh.cbHeaderSize = sizeof(m_itfsh); m_itfsh.clsidFreeList = CLSID_NULL; m_itfsh.clsidPathMgr = CLSID_NULL; m_pLKBMedium = NULL; m_pPathManager = NULL; m_pSysPathManager = NULL; m_pFreeListManager = NULL; m_pTransformServices = NULL; m_pActiveStorageList = NULL; m_pActiveLockBytesList = NULL; m_fHeaderIsDirty = FALSE; m_fInitialed = FALSE; m_fReadOnly = FALSE; m_pwscDataSpaceNames = NULL; m_papTransformDescriptors = NULL; m_pStrmSpaceNames = NULL; m_cFSObjectRefs = 0; m_cwcFileName = 0; m_awszFileName[0] = 0; } // Destructor: CITFileSystem::CImpITFileSystem::~CImpITFileSystem(void) { // The code below is necessary to balance calls we've made to this->Release() // to hide circular references. The current set of circular references are: // // ** The free list Stream // ** The path manager LockBytes // ** The transform services object // ** Each storage whose name begins with ":" // ** Each stream whose name begins with ":" // ** Each active data space RonM_ASSERT(m_cFSObjectRefs != UINT(~0)); if (!~m_cFSObjectRefs) return; #ifdef _DEBUG LONG cRefs = #endif // _DEBUG AddRef(); // To avoid recursive destruction when we eliminate the last // circular reference to this file system. RonM_ASSERT(cRefs == 1); for (; m_cFSObjectRefs--;) // Accounting for the hidden circular references. this->AddRef(); // RonM_ASSERT(!m_pActiveStorageList); // RonM_ASSERT(!m_pActiveLockBytesList); DEBUGDEF(HRESULT hr) DEBUGDEF(ULONG c); if (m_papTransformDescriptors) { RonM_ASSERT(m_pStrmSpaceNames); RonM_ASSERT(m_pwscDataSpaceNames); UINT cSpaces = m_pwscDataSpaceNames[1]; for (UINT iSpace = 0; iSpace < cSpaces; iSpace++) if (m_papTransformDescriptors[iSpace]) DeactivateDataSpace(iSpace); delete [] m_papTransformDescriptors; } if (m_pwscDataSpaceNames) delete [] m_pwscDataSpaceNames; if (m_pStrmSpaceNames) m_pStrmSpaceNames->Release(); if (m_fInitialed) { RonM_ASSERT(m_pLKBMedium); RonM_ASSERT(m_pPathManager); RonM_ASSERT(m_pFreeListManager); RonM_ASSERT(m_pTransformServices); #ifdef _DEBUG hr = #endif // _DEBUG m_pPathManager->FlushToLockBytes(); RonM_ASSERT(SUCCEEDED(hr)); #ifdef _DEBUG hr = #endif // _DEBUG FlushToLockBytes(); RonM_ASSERT(SUCCEEDED(hr)); } if (m_pTransformServices) m_pTransformServices->Release(); if (m_pFreeListManager) { #ifdef _DEBUG c = #endif // _DEBUG m_pFreeListManager->Release(); RonM_ASSERT(!c); } if (m_pPathManager) { #ifdef _DEBUG c = #endif // _DEBUG m_pPathManager->Release(); RonM_ASSERT(!c); } if (m_pSysPathManager) { #ifdef _DEBUG c = #endif // _DEBUG m_pSysPathManager->Release(); RonM_ASSERT(!c); } if (m_pLKBMedium) m_pLKBMedium->Release(); } IStorageITEx *CITFileSystem::CImpITFileSystem::FindActiveStorage (const WCHAR *pwcsPath) { for (CImpITUnknown *pStg = m_pActiveStorageList; pStg; pStg = pStg->NextObject() ) if (((IIT_IStorageITEx *)pStg)->IsNamed(pwcsPath)) { pStg->AddRef(); return (IStorageITEx *) pStg; } return NULL; } ILockBytes *CITFileSystem::CImpITFileSystem::FindActiveLockBytes(const WCHAR *pwcsPath) { return ::FindMatchingLockBytes(pwcsPath, (CImpITUnknown *) m_pActiveLockBytesList); } // Initialers: HRESULT __stdcall CITFileSystem::CImpITFileSystem::InitCreateOnLockBytes (ILockBytes * plkbyt, DWORD grfMode, PITS_Control_Data pControlData, LCID lcid) { // First we bind to the lockbyte object passed to us. // If this call fails, the containing create function will delete this // object instance, and that will cause the lockbyte object to be released. if (!plkbyt) return STG_E_INVALIDPOINTER; ITSFS_Control_Data *pITCD = (ITSFS_Control_Data *) pControlData; if (!pITCD) { // We use _alloca here because it allocates memory on the stack, // and those allocations automatically get cleaned up when we // exit this function. So no explicit deallocation is necessary. // That's important given that sometimes we have control data // passed in to us, and sometimes we create it dynamically. pITCD = PITSFS_Control_Data(_alloca(sizeof(ITSFS_Control_Data))); if (!pITCD) return STG_E_INSUFFICIENTMEMORY; pITCD->cdwFollowing = 6; pITCD->cdwITFS_Control = 5; pITCD->dwMagic = MAGIC_ITSFS_CONTROL; pITCD->dwVersion = ITSFS_CONTROL_VERSION; pITCD->cbDirectoryBlock = DEFAULT_DIR_BLOCK_SIZE; pITCD->cMinCacheEntries = DEFAULT_MIN_CACHE_ENTRIES; pITCD->fFlags = fDefaultIsCompression; } else if (pITCD->cdwFollowing < 6 || pITCD->cdwITFS_Control < 5 || pITCD->dwMagic != MAGIC_ITSFS_CONTROL || pITCD->dwVersion != ITSFS_CONTROL_VERSION ) return STG_E_INVALIDPARAMETER; m_pLKBMedium = plkbyt; m_pLKBMedium->AddRef(); // We pick up the name of the file which contains this file system from // the LockBytes object. STATSTG statstg; ZeroMemory(&statstg, sizeof(statstg)); HRESULT hr = plkbyt->Stat(&statstg, STATFLAG_DEFAULT); RonM_ASSERT(hr == S_OK); if (hr != S_OK) return hr; // Now we must set initial values for the header structure. // This structure will be stored at offset zero in m_pLKBMedium. m_itfsh.uMagic = MAGIC_ITS_FILE; m_itfsh.cbHeaderSize = sizeof(ITSFileHeader); m_itfsh.uFormatVersion = CurrentFileFormatVersion; m_itfsh.fFlags = pITCD->fFlags & fDefaultIsCompression; m_itfsh.dwStamp = m_StartingFileStamp = statstg.mtime.dwLowDateTime; m_itfsh.lcid = lcid; m_itfsh.offFreeListData = 0; m_itfsh.cbFreeListData = 0; m_itfsh.offPathMgrData = 0; m_itfsh.cbPathMgrData = 0; m_itfsh.offPathMgrOrigin = 0; m_cwcFileName = wcsLen(statstg.pwcsName); if (m_cwcFileName >= MAX_PATH) // Note that an empty name is valid. { OLEHeap()->Free(statstg.pwcsName); return STG_E_INVALIDNAME; } CopyMemory(m_awszFileName, statstg.pwcsName, (m_cwcFileName + 1) * sizeof(WCHAR)); m_fHeaderIsDirty = TRUE; OLEHeap()->Free(statstg.pwcsName); // At this point we haven't created a Path Manager for this file system. // The Path Manager is the mechanism we used to keep track of the items // in the file system. However when we create the Path Manager, we must // record its location in the file system header instead. To keep the // program logic clean we're going to create a special variation on the // Path Manager interface called SysPathManager. The SysPathManager // object uses the file system header to keep track of two objects -- // the Path Manager and the Free List Manager. hr = CSystemPathManager::Create(NULL, this, &m_pSysPathManager); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; // SysPathManager works exactly like the PathManager object except // that it recognizes only the names L"F" and L"P", respectively, // to denote the Free List object and the Path Database object. // Now we must create a FreeList manager for this file system. hr = CFreeList::CreateFreeList(this, m_itfsh.cbHeaderSize, &m_pFreeListManager); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; // Since we want to support multiple free list managers in the future, // we record the class id of the current free list manager in the // header for the file system. Then when we open this file system // sometime later, we can be assured of connecting it to the correct // free list manager implementation. hr = m_pFreeListManager->GetClassID(&m_itfsh.clsidFreeList); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; ILockBytes *pLKB_PathDataBase = NULL; hr = OpenLockBytes(NULL, L"P", &pLKB_PathDataBase); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; // The call to NewPathDatabase will either bind pLKB_PathDataBase to the new // Path Manager, or Release it if it fails. hr = CPathManager1::NewPathDatabase(NULL, pLKB_PathDataBase, pITCD->cbDirectoryBlock, pITCD->cMinCacheEntries, &m_pPathManager ); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; hr = m_pPathManager->GetClassID(&m_itfsh.clsidPathMgr); if (!SUCCEEDED(hr)) return hr; m_fInitialed = TRUE; PathInfo PI; ZeroMemory(&PI, sizeof(PI)); wcsCpy(PI.awszStreamPath, L"/"); PI.cwcStreamPath = 1; PI.clsidStorage = CLSID_NULL; MarkActive(g_pImpITFileSystemList); hr = m_pPathManager->CreateEntry(&PI, NULL, FALSE); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; wcsCpy(PI.awszStreamPath, pwcsSpaceNameStorage); wcsCat(PI.awszStreamPath, pwcsLZXSpace); wcsCat(PI.awszStreamPath, pwcsSpaceContentSuffix); ILockBytes *pLKBDefault = NULL; hr = CreateLockBytes(NULL, (const WCHAR *)PI.awszStreamPath, pwcsUncompressedSpace, FALSE, &pLKBDefault ); if (!SUCCEEDED(hr)) return hr; pLKBDefault->Release(); pLKBDefault = NULL; hr = CreateSpaceNameList(); if (!SUCCEEDED(hr)) return hr; hr = CreateDefaultDataSpace(pITCD); if (!SUCCEEDED(hr)) return hr; hr = CTransformServices::Create(NULL, this, &m_pTransformServices); if (hr == S_OK) { Release(); // To compensate for the circular ref from FreeList Stream Release(); // To compensate for the circular ref from m_pTransformServices m_cFSObjectRefs += 2; // Note: The destructor for the file system will do m_cFSObjectRefs AddRef // calls to compensate for these Release calls. We do the release calls // here so that the file system won't be kept alive forever by its // circular references. } hr = ActivateDataSpace(1); // To flush out any errors in the control data // we've created for the default LZX data space. Container()->MoveInFrontOf(NULL); // BugBug: This put the current object list in clean order // for deallocation at process detach time. However // we'll need further adjustments as we add more // data spaces. return hr; } HRESULT CITFileSystem::CImpITFileSystem::CreateDefaultDataSpace(PITSFS_Control_Data pITCD) { XformControlData *pXFCD = NULL; if (pITCD->cdwFollowing > pITCD->cdwITFS_Control + 1) { // We have some trailing data. Let's assume that it's // control data for the LZX transform. pXFCD = PXformControlData(pITCD + 1); } else { // We don't have any LZX control data in *pITCD. So we // must get default control data from the transform // factory. XformControlData *pCD = NULL; ITransformFactory *pXFF = NULL; HRESULT hr = CLZX_TransformFactory::Create(NULL, IID_ITransformFactory, (void **)&pXFF); if (!SUCCEEDED(hr)) return hr; hr = pXFF->DefaultControlData(&pCD); pXFF->Release(); if (!SUCCEEDED(hr)) return hr; // Now that we've got the default control data, we must // copy into a local stack buffer. The use of _alloca means // that we do not have to explicitly deallocate pXFCD. That's // important given that sometimes the control data will be // passed in as part of *pITCD and sometimes it will be // created dynamically. UINT cbData = sizeof(DWORD) * (1 + pCD->cdwControlData); pXFCD = PXformControlData(_alloca(cbData)); if (!pXFCD) { OLEHeap()->Free(pCD); return STG_E_INSUFFICIENTMEMORY; } CopyMemory(pXFCD, pCD, cbData); OLEHeap()->Free(pCD); } WCHAR awcsPath[MAX_PATH]; wcsCpy(awcsPath, pwcsSpaceNameStorage); wcsCat(awcsPath, pwcsLZXSpace); UINT cbPrefix = wcsLen(awcsPath); wcsCat(awcsPath, pwcsTransformListSuffix); IStream *pStrm = NULL; ULONG cbWritten = 0; WCHAR awcsClassID[CWC_GUID_STRING_BUFFER]; UINT cbResult = StringFromGUID2(CLSID_LZX_Transform, awcsClassID, CWC_GUID_STRING_BUFFER); if (cbResult == 0) return STG_E_UNKNOWN; HRESULT hr = WriteToStream((const WCHAR *) awcsPath, awcsClassID, wcsLen(awcsClassID)); if (hr == S_OK) { awcsPath[cbPrefix] = 0; wcsCat(awcsPath, pwcsSpanInfoSuffix); ULARGE_INTEGER uli; uli.LowPart = 0; uli.HighPart = 0; hr = WriteToStream((const WCHAR *) awcsPath, &uli, sizeof(uli)); } if (hr == S_OK) { awcsPath[cbPrefix] = 0; wcsCat(awcsPath, pwcsControlDataSuffix); hr = WriteToStream((const WCHAR *) awcsPath, pXFCD, sizeof(XformControlData) + sizeof(DWORD) * pXFCD->cdwControlData ); } return hr; } HRESULT CITFileSystem::CImpITFileSystem::WriteToStream (const WCHAR *pwcsStreamPath, PVOID pvData, ULONG cbData) { IStreamITEx *pStrm = NULL; HRESULT hr = CreateStream(NULL, pwcsStreamPath, STGM_READWRITE, &pStrm); if (hr != S_OK) return hr; ULONG cbWritten = 0; hr = pStrm->Write(pvData, cbData, &cbWritten); pStrm->Release(); pStrm = FALSE; if (hr == S_FALSE || cbWritten != cbData) hr = STG_E_WRITEFAULT; return hr; } HRESULT CITFileSystem::CImpITFileSystem::CreateSpaceNameList() { USHORT cwcUncompressedSpace = (USHORT) wcsLen(pwcsUncompressedSpace); USHORT cwcDefaultSpace = (USHORT) wcsLen(pwcsLZXSpace); RonM_ASSERT(sizeof(USHORT) == 2 && sizeof(WCHAR) == 2); USHORT cwcNameListSize = cwcUncompressedSpace + cwcDefaultSpace + 6; m_pwscDataSpaceNames = New WCHAR[cwcNameListSize]; m_papTransformDescriptors = New PTransformDescriptor[2]; if (m_papTransformDescriptors) { m_papTransformDescriptors[0] = NULL; // To make the destructor code m_papTransformDescriptors[1] = NULL; // operate correctly. } if (!m_pwscDataSpaceNames || !m_papTransformDescriptors) return STG_E_INSUFFICIENTMEMORY; /* The m_pwscDataSpaceNames array has a prefix consisting of: USHORT cwcTotal; -- Total length of *m_pwscDataSpaceNames USHORT cDataSpaces; -- Number of names in *m_pwscDataSpaceNames Then each name has this format: USHORT cwcDataSpaceName; WCHAR awcDataSpaceName[cwcDataSpaceName]; WCHAR wcNULL == NULL; The position of a name in the sequence determines its ordinal value. */ m_pwscDataSpaceNames[0] = cwcNameListSize; m_pwscDataSpaceNames[1] = 2; m_pwscDataSpaceNames[2] = cwcUncompressedSpace; m_pwscDataSpaceNames[3 + cwcUncompressedSpace] = 0; m_pwscDataSpaceNames[4 + cwcUncompressedSpace] = cwcDefaultSpace; m_pwscDataSpaceNames[5 + cwcUncompressedSpace + cwcDefaultSpace] = 0; CopyMemory(m_pwscDataSpaceNames + 3, pwcsUncompressedSpace, cwcUncompressedSpace * sizeof(WCHAR)); CopyMemory(m_pwscDataSpaceNames + cwcUncompressedSpace + 5, pwcsLZXSpace, cwcDefaultSpace * sizeof(WCHAR) ); HRESULT hr = CreateStream(NULL, pwscSpaceNameListStream, STGM_READWRITE, &m_pStrmSpaceNames); if (!SUCCEEDED(hr)) return hr; return FlushSpaceNameList(); } HRESULT CITFileSystem::CImpITFileSystem::FlushSpaceNameList() { ULONG cb = m_pwscDataSpaceNames[0] * sizeof(WCHAR); ULONG cbWritten = 0; HRESULT hr = m_pStrmSpaceNames->Write(m_pwscDataSpaceNames, cb, &cbWritten); if (!SUCCEEDED(hr)) return hr; if (cbWritten != cb) return STG_E_WRITEFAULT; return NO_ERROR; } HRESULT CITFileSystem::CImpITFileSystem::OpenSpaceNameList() { HRESULT hr = OpenStream(NULL, pwscSpaceNameListStream, STGM_READWRITE, &m_pStrmSpaceNames); if (!SUCCEEDED(hr)) return hr; USHORT cwc; ULONG cbRead; ULONG cb = sizeof(cwc); RonM_ASSERT(sizeof(USHORT) == sizeof(WCHAR)); hr= m_pStrmSpaceNames->Read(&cwc, cb, &cbRead); if (!SUCCEEDED(hr)) return hr; if (cbRead != cb) return STG_E_READFAULT; m_pwscDataSpaceNames = New WCHAR[cwc]; m_pwscDataSpaceNames[0] = cwc; cb = sizeof(WCHAR) * (cwc - 1); hr = m_pStrmSpaceNames->Read(m_pwscDataSpaceNames + 1, cb, &cbRead); if (!SUCCEEDED(hr)) return hr; if (cbRead != cb) return STG_E_READFAULT; m_papTransformDescriptors = New PTransformDescriptor[m_pwscDataSpaceNames[1]]; if (!m_papTransformDescriptors) return STG_E_INSUFFICIENTMEMORY; ZeroMemory(m_papTransformDescriptors, sizeof(PTransformDescriptor) * m_pwscDataSpaceNames[1]); return NO_ERROR; } HRESULT CITFileSystem::CImpITFileSystem::FindSpaceName(const WCHAR *pwcsSpaceName) { RonM_ASSERT(m_pwscDataSpaceNames); DEBUGDEF(const WCHAR *pwcLimit = m_pwscDataSpaceNames + m_pwscDataSpaceNames[0]) const WCHAR *pwcNext = m_pwscDataSpaceNames + 2; USHORT cSpaces = m_pwscDataSpaceNames[1]; USHORT iSpace = 0; for (; iSpace < cSpaces; iSpace++) { RonM_ASSERT(pwcNext < pwcLimit); USHORT cwc = *pwcNext++; RonM_ASSERT(pwcNext < pwcLimit); if (!wcsicmp_0x0409(pwcNext, pwcsSpaceName)) return iSpace; pwcNext += cwc + 1; RonM_ASSERT(pwcNext <= pwcLimit); } return STG_E_INVALIDNAME; } HRESULT CITFileSystem::CImpITFileSystem::AddSpaceName(const WCHAR *pwcsSpaceName) { if (!CStorage::ValidStreamName(pwcsSpaceName)) return STG_E_INVALIDNAME; HRESULT hr = FindSpaceName(pwcsSpaceName); if (SUCCEEDED(hr)) return STG_E_INVALIDNAME; UINT cwcNewSpace = wcsLen(pwcsSpaceName); USHORT cwcOld = m_pwscDataSpaceNames[0]; WCHAR *pwcNext = m_pwscDataSpaceNames + 2; USHORT cSpaces = m_pwscDataSpaceNames[1]; USHORT iSpace = 0; if (cSpaces == MAX_SPACES) return STG_E_INSUFFICIENTMEMORY; for (; iSpace < cSpaces; iSpace++) { USHORT cwc = *pwcNext++; if (!cwc) { WCHAR *pwcsNew = New WCHAR[cwcNewSpace + cwcOld]; if (!pwcsNew) return STG_E_INSUFFICIENTMEMORY; pwcNext[-1] = (WCHAR)cwcNewSpace; UINT offset = UINT(pwcNext - m_pwscDataSpaceNames); CopyMemory(pwcsNew, m_pwscDataSpaceNames, offset * sizeof(WCHAR)); CopyMemory(pwcsNew + offset, pwcsSpaceName, cwcNewSpace * sizeof(WCHAR)); CopyMemory(pwcsNew + offset + cwcNewSpace, pwcNext, sizeof(WCHAR) * (cwcOld - offset) ); delete [] m_pwscDataSpaceNames; m_pwscDataSpaceNames = pwcsNew; HRESULT hr = FlushSpaceNameList(); if (!SUCCEEDED(hr)) return hr; // BugBug: Should also delete the space name here RonM_ASSERT(m_papTransformDescriptors[iSpace] == NULL); return iSpace; } pwcNext += cwc + 1; } WCHAR *pwcsNew = New WCHAR[2 + cwcNewSpace + cwcOld]; if (!pwcsNew) return STG_E_INSUFFICIENTMEMORY; TransformDescriptor **ppTXDNew = New PTransformDescriptor[cSpaces + 1]; if (!ppTXDNew) { delete [] pwcsNew; return STG_E_INSUFFICIENTMEMORY; } CopyMemory(ppTXDNew, m_papTransformDescriptors, cSpaces * sizeof(PTransformDescriptor)); m_papTransformDescriptors[cSpaces] = NULL; CopyMemory(pwcsNew, m_pwscDataSpaceNames, cwcOld * sizeof(WCHAR)); CopyMemory(pwcsNew + cwcOld + 1, pwcsSpaceName, cwcNewSpace * sizeof(WCHAR)); pwcsNew[cwcOld ] = (WCHAR)cwcNewSpace; pwcsNew[cwcOld + cwcNewSpace + 1] = 0; pwcsNew[1 ] = cSpaces + 1; delete m_papTransformDescriptors; m_papTransformDescriptors = ppTXDNew; delete [] m_pwscDataSpaceNames; m_pwscDataSpaceNames = pwcsNew; hr = FlushSpaceNameList(); if (!SUCCEEDED(hr)) return hr; // BugBug: Should also delete the space name here return cSpaces; } HRESULT CITFileSystem::CImpITFileSystem::DeleteSpaceName(const WCHAR *pwcsSpaceName) { HRESULT hr = FindSpaceName(pwcsSpaceName); if (!SUCCEEDED(hr)) return hr; ULONG iSpace = hr; if (iSpace < 2) return STG_E_INVALIDNAME; RonM_ASSERT(hr < m_pwscDataSpaceNames[1]); TransformDescriptor *pTD = m_papTransformDescriptors[iSpace]; RonM_ASSERT(!pTD || (pTD->pLockBytesChain == NULL && pTD->apTransformInstance[0] == NULL)); if (pTD) delete [] PBYTE(pTD); pTD = NULL; m_papTransformDescriptors[iSpace] = NULL; USHORT cwcName = (USHORT) wcsLen(pwcsSpaceName); WCHAR *pwcLimit = m_pwscDataSpaceNames + m_pwscDataSpaceNames[0]; WCHAR *pwcNext = m_pwscDataSpaceNames + 2; for (;hr--;) { USHORT cwc = *pwcNext++; pwcNext += cwc + 1; } *pwcNext++ = 0; WCHAR *pwcTrailing = pwcNext + cwcName; CopyMemory(pwcNext, pwcNext + cwcName, DWORD((pwcLimit - pwcTrailing) * sizeof(WCHAR))); return FlushSpaceNameList(); } HRESULT __stdcall CITFileSystem::CImpITFileSystem::InitOpenOnLockBytes (ILockBytes * plkbyt, DWORD grfMode) { m_pLKBMedium = plkbyt; plkbyt->AddRef(); // We pick up the name of the file which contains this file system from // the LockBytes object. STATSTG statstg; ZeroMemory(&statstg, sizeof(statstg)); HRESULT hr = plkbyt->Stat(&statstg, STATFLAG_DEFAULT); RonM_ASSERT(hr == S_OK); if (hr != S_OK) return hr; m_cwcFileName = wcsLen(statstg.pwcsName); if (m_cwcFileName >= MAX_PATH) // Note that an empty name is valid. { OLEHeap()->Free(statstg.pwcsName); return STG_E_INVALIDNAME; } CopyMemory(m_awszFileName, statstg.pwcsName, (m_cwcFileName + 1) * sizeof(WCHAR)); OLEHeap()->Free(statstg.pwcsName); m_fReadOnly = (statstg.grfMode & RW_ACCESS_MASK) == STGM_READ; ULONG cbRead = 0; hr = m_pLKBMedium->ReadAt(CULINT(0).Uli(), &m_itfsh, sizeof(m_itfsh), &cbRead); if (!SUCCEEDED(hr)) return hr; if ( cbRead < sizeof(ITSFileHeaderV2) || m_itfsh.uMagic != MAGIC_ITS_FILE || m_itfsh.uFormatVersion < FirstReleasedVersion || m_itfsh.uFormatVersion > CurrentFileFormatVersion ||(m_itfsh.fFlags & ~VALID_OPEN_FLAGS) ) return STG_E_INVALIDHEADER; else if ( m_itfsh.uFormatVersion == CurrentFileFormatVersion && cbRead == sizeof(ITSFileHeader) ) { } else if (m_itfsh.uFormatVersion == FirstReleasedVersion) { m_itfsh.offPathMgrOrigin = 0; } else { RonM_ASSERT(FALSE); // If this assert fires, we have a old version // that isn't being handled. return STG_E_INVALIDHEADER; } m_StartingFileStamp = m_itfsh.dwStamp; hr = CSystemPathManager::Create(NULL, this, &m_pSysPathManager); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; RonM_ASSERT(m_itfsh.clsidFreeList == CLSID_IFreeListManager_1); hr = CFreeList::AttachFreeList(this, &m_pFreeListManager); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; ILockBytes *pLKB_PathDatabase = NULL; hr = OpenLockBytes(NULL, L"P", &pLKB_PathDatabase); RonM_ASSERT(SUCCEEDED(hr)); if (!SUCCEEDED(hr)) return hr; hr = CPathManager1::LoadPathDatabase(NULL, pLKB_PathDatabase, &m_pPathManager); RonM_ASSERT(SUCCEEDED(hr)); if (hr == S_OK) { m_fInitialed = TRUE; MarkActive(g_pImpITFileSystemList); } hr = OpenSpaceNameList(); if (!SUCCEEDED(hr)) return hr; hr = CTransformServices::Create(NULL, this, &m_pTransformServices); if (hr == S_OK) { Release(); // To compensate for the circular ref from FreeList Stream Release(); // To compensate for the circular ref from m_pTransformServices m_cFSObjectRefs += 2; // Note: The destructor for the file system will do m_cFSObjectRefs AddRef // calls to compensate for these Release calls. We do the release calls // here so that the file system won't be kept alive forever by its // circular references. } Container()->MoveInFrontOf(((IITTransformServices *) m_pTransformServices)->Container()); return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::SetITFSTimes (FILETIME const * pctime, FILETIME const * patime, FILETIME const * pmtime ) { return ((CFSLockBytes::CImpILockBytes *) m_pLKBMedium) ->SetTimes(pctime, patime, pmtime); } // IUnknown methods: STDMETHODIMP_(ULONG) CITFileSystem::CImpITFileSystem::Release(void) { // The actual work for the Release function is done by // CImpITUnknown::Release() and ~CImpITFileSystem. // // We bracket that work as a critical section active file systems // are kept in a linked list. A release operation may remove // this file system from that list, and we need to guard against // having someone find a reference to this storage just before // we destroy it. CSyncWith sw(g_csITFS); ULONG ulCnt = CImpITUnknown::Release(); return ulCnt; } // IITFileSystem methods: CITCriticalSection& CITFileSystem::CImpITFileSystem::CriticalSection() { return m_cs; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::FlushToLockBytes() { // This routine copies the free list and the file header to the // lockbyte medium. It does not flush the path database. We expect // most calls to this interface will come from the Path Manager. RonM_ASSERT(m_fInitialed); RonM_ASSERT(m_pStrmSpaceNames); if (m_pFreeListManager->IsDirty() == S_OK) { HRESULT hr = m_pFreeListManager->RecordFreeList(); if (!SUCCEEDED(hr)) return hr; } if (m_fHeaderIsDirty || m_itfsh.dwStamp != m_StartingFileStamp) { ULONG cbWritten = 0; HRESULT hr = S_FALSE; switch (m_itfsh.uFormatVersion) { case RelativeOffsetVersion: hr = m_pLKBMedium->WriteAt(CULINT(0).Uli(), &m_itfsh, sizeof(m_itfsh), &cbWritten); if (hr == S_OK && cbWritten != sizeof(m_itfsh)) hr = STG_E_WRITEFAULT; break; case FirstReleasedVersion: RonM_ASSERT(m_itfsh.offPathMgrOrigin == 0); hr = m_pLKBMedium->WriteAt(CULINT(0).Uli(), &m_itfsh, sizeof(ITSFileHeaderV2), &cbWritten); if (hr == S_OK && cbWritten != sizeof(ITSFileHeaderV2)) hr = STG_E_WRITEFAULT; break; default: RonM_ASSERT(FALSE); // We have an old format version number that // isn't being handled! } if (!SUCCEEDED(hr)) return hr; m_fHeaderIsDirty = FALSE; } return NO_ERROR; } void CITFileSystem::CImpITFileSystem::CopyPath(PathInfo &PI, const WCHAR *pwcsPath) { PI.cwcStreamPath = wcsLen(pwcsPath); RonM_ASSERT(PI.cwcStreamPath < MAX_PATH); CopyMemory(PI.awszStreamPath, pwcsPath, sizeof(WCHAR) * (1 + PI.cwcStreamPath)); } HRESULT __stdcall CITFileSystem::CImpITFileSystem::CreateStorage (IUnknown *pUnkOuter, const WCHAR *pwcsPathPrefix, DWORD grfMode, IStorageITEx **ppStg ) { CSyncWith sw(g_csITFS); PathInfo PI, PIPrev; CopyPath(PI, pwcsPathPrefix); PI.uStateBits = 0; PI.iLockedBytesSegment = 0; PI.clsidStorage = CLSID_NULL; PI.cUnrecordedChanges = 0; RonM_ASSERT(PI.awszStreamPath[PI.cwcStreamPath - 1] == L'/'); HRESULT hr = m_pPathManager->CreateEntry(&PI, &PIPrev, FALSE); if (!SUCCEEDED(hr)) return hr; hr = CStorage::OpenStorage(pUnkOuter, this, &PI, grfMode, ppStg); if (!SUCCEEDED(hr)) m_pPathManager->DeleteEntry(&PI); else if (pwcsPathPrefix[0] != L'/') { ((IIT_IStorageITEx *) *ppStg)->Container()->MarkSecondary(); if (m_cFSObjectRefs!= UINT(~0)) { m_cFSObjectRefs++; this->Release(); // To account for circular refs through ":" storages. } } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::OpenStorage (IUnknown *pUnkOuter, const WCHAR *pwcsPathPrefix, DWORD grfMode, IStorageITEx **ppstg ) { CSyncWith sw(g_csITFS); IStorageITEx *pStorage = FindActiveStorage(pwcsPathPrefix); if (pStorage) { *ppstg = pStorage; return NO_ERROR; } PathInfo PI, PIPrev; CopyPath(PI, pwcsPathPrefix); RonM_ASSERT(PI.awszStreamPath[PI.cwcStreamPath - 1] == L'/'); HRESULT hr = m_pPathManager->FindEntry(&PI); if (hr == S_OK) hr = CStorage::OpenStorage(pUnkOuter, this, &PI, grfMode, ppstg); else { // Some storages don't have explicit entries in the path database. // So we'll look to see if anything has a prefix which matches // pwcsPathPrefix. HRESULT hr2 = NO_ERROR; BOOL fStorage = FALSE; IEnumSTATSTG *pEnum = NULL; hr2 = m_pPathManager->EnumFromObject(NULL, pwcsPathPrefix, wcsLen(pwcsPathPrefix), IID_IEnumSTATSTG, (PVOID *) &pEnum ); if (hr2 == S_OK) { STATSTG statstg; hr2 = pEnum->Next(1, &statstg, NULL); if (hr2 == S_OK) { const WCHAR *pwcPrefix = pwcsPathPrefix; const WCHAR *pwcEnum = statstg.pwcsName; for (; ; ) { WCHAR wcPrefix = WC_To_0x0409_Lower(*pwcPrefix++); WCHAR wcEnum = WC_To_0x0409_Lower(*pwcEnum++); if (wcPrefix == wcEnum && wcPrefix) continue; fStorage = wcPrefix == 0; RonM_ASSERT(wcEnum || wcPrefix); break; } OLEHeap()->Free(statstg.pwcsName); } pEnum->Release(); } if (fStorage) { PI.clsidStorage = CLSID_NULL; PI.uStateBits = 0; // BugBug: We don't yet set iLockedBytesSegment for this storage. // The problem is finding a prefix storage which has an // entry in the path database. hr = CStorage::OpenStorage(pUnkOuter, this, &PI, grfMode, ppstg);; } else hr = STG_E_FILENOTFOUND; } if (hr == S_OK && pwcsPathPrefix[0] != L'/') { ((IIT_IStorageITEx *) *ppstg)->Container()->MarkSecondary(); if (m_cFSObjectRefs!= UINT(~0)) { m_cFSObjectRefs++; this->Release(); // To account for circular references through ":" storages. } } return hr; } HRESULT CITFileSystem::CImpITFileSystem::CreateTransformedLockBytes (IUnknown *pUnkOuter, const WCHAR *pwcsPath, const WCHAR *pwcsDataSpaceName, BOOL fOverwrite, ILockBytes **ppLKB ) { RonM_ASSERT(pwcsPath[0] != L':'); HRESULT hr = FindSpaceName(pwcsDataSpaceName); if (!SUCCEEDED(hr)) return hr; ULONG iSpace = hr; BOOL fNewActivation = (m_papTransformDescriptors[iSpace] == NULL); if (fNewActivation) { hr = ActivateDataSpace(iSpace); if (!SUCCEEDED(hr)) return hr; } TransformDescriptor *pTD = m_papTransformDescriptors[iSpace]; RonM_ASSERT(pTD); ILockBytes *pLKB = CTransformedLockBytes::FindTransformedLockBytes(pwcsPath, pTD); if (pLKB) { RonM_ASSERT(!fNewActivation); pLKB->Release(); return STG_E_INUSE; } PathInfo PI, PIPrev; CopyPath(PI, pwcsPath); PI.ullcbOffset = 0; PI.ullcbData = 0; PI.uStateBits = 0; PI.iLockedBytesSegment = iSpace; PI.cUnrecordedChanges = 0; RonM_ASSERT(PI.awszStreamPath[PI.cwcStreamPath - 1] != L'/'); PIPrev.cwcStreamPath = 0; // Setup to detect overwrite condition. hr = m_pPathManager->CreateEntry(&PI, &PIPrev, fOverwrite); if (SUCCEEDED(hr)) { #if 0 // BugBug: Need to add a method handle this for transforms if (PIPrev.cwcStreamPath) // Did we overwrite an existing file? m_pFreeListManager->PutFreeSpace(PIPrev.ullcbOffset, PIPrev.ullcbData); #endif // 0 hr = CTransformedLockBytes::Open(NULL, &PI, pTD, this, ppLKB); } return hr; } HRESULT CITFileSystem::CImpITFileSystem::ActivateDataSpace(ULONG iSpace) { RonM_ASSERT(iSpace == 1); // Since we have only the default space implemented // BugBug: The code below only supports the default data space. RonM_ASSERT(iSpace < m_pwscDataSpaceNames[1]); RonM_ASSERT(m_papTransformDescriptors[iSpace] == NULL); WCHAR awszPath[MAX_PATH]; wcsCpy(awszPath, pwcsSpaceNameStorage); wcsCat(awszPath, pwcsLZXSpace); UINT cwcPrefix = wcsLen(awszPath); wcsCat(awszPath, pwcsSpanInfoSuffix); IStreamITEx *pStrm = NULL; HRESULT hr = OpenStream(NULL, (const WCHAR *) awszPath, STGM_READ, &pStrm); if (!SUCCEEDED(hr)) return hr; ULARGE_INTEGER ulicbSpan; ULONG cbRead = 0; hr = pStrm->Read(&ulicbSpan, sizeof(ulicbSpan), &cbRead); pStrm->Release(); pStrm = NULL; if (hr == S_FALSE || cbRead != sizeof(ulicbSpan)) hr = STG_E_READFAULT; if (!SUCCEEDED(hr)) return hr; awszPath[cwcPrefix] = 0; wcsCat(awszPath, pwcsControlDataSuffix); hr = OpenStream(NULL, (const WCHAR *) awszPath, STGM_READ, &pStrm); if (!SUCCEEDED(hr)) return hr; UINT cdwData = 0; hr = pStrm->Read(&cdwData, sizeof(cdwData), &cbRead); if (hr == S_FALSE || cbRead != sizeof(cdwData)) hr = STG_E_READFAULT; if (!SUCCEEDED(hr)) return hr; PXformControlData pXFCD = PXformControlData(_alloca(sizeof(DWORD) * (cdwData + 1))); if (!pXFCD) { pStrm->Release(); return STG_E_INSUFFICIENTMEMORY; } pXFCD->cdwControlData = cdwData; hr = pStrm->Read(&(pXFCD->adwControlData), cdwData * sizeof(DWORD), &cbRead); pStrm->Release(); pStrm = NULL; if (hr == S_FALSE || cbRead != cdwData * sizeof(DWORD)) hr = STG_E_READFAULT; if (!SUCCEEDED(hr)) return hr; awszPath[cwcPrefix] = 0; wcsCat(awszPath, pwcsSpaceContentSuffix); ILockBytes *pLKB = NULL; hr = OpenLockBytes(NULL, (const WCHAR *) awszPath, &pLKB); if (!SUCCEEDED(hr)) return hr; ITransformInstance *pITxInst; if (!SUCCEEDED(hr = CNull_TransformInstance::CreateFromILockBytes (NULL, pLKB, &pITxInst))) return hr; ((IITTransformInstance *) pITxInst)->Container()->MarkSecondary(); ITransformFactory *pITxFactory; hr = CLZX_TransformFactory::Create(NULL, IID_ITransformFactory, (void **)&pITxFactory); if (SUCCEEDED(hr)) { m_pTransformServices->AddRef(); // Because we're passing the Transform services // object to the transform factory. UINT cTransforms = 1; // Really should get this from transform list. TransformDescriptor *pTD = TransformDescriptor::Create(iSpace, cTransforms); if (pTD) { CLSID clsid = CLSID_LZX_Transform; hr = pITxFactory->CreateTransformInstance (pITxInst, // Container data span for transformed data ulicbSpan, // Untransformed size of data pXFCD, // Control data for this instance &clsid, // Transform Class ID pwcsLZXSpace, // Data space name for this instance m_pTransformServices, // Utility routines NULL, // Interface to get enciphering keys &pTD->apTransformInstance[0] // Out: Instance transform interface ); pITxFactory->Release(); if (SUCCEEDED(hr)) m_papTransformDescriptors[iSpace] = pTD; else delete pTD; } else hr = STG_E_INSUFFICIENTMEMORY; } else pITxInst->Release(); return hr; } HRESULT CITFileSystem::CImpITFileSystem::DeactivateDataSpace(ULONG iSpace) { RonM_ASSERT(iSpace < m_pwscDataSpaceNames[1]); RonM_ASSERT(m_papTransformDescriptors[iSpace]); RonM_ASSERT(iSpace == 1); // While we have just the "MSCompressed" space. TransformDescriptor *pTD = m_papTransformDescriptors[iSpace]; RonM_ASSERT(pTD->iSpace != 0); RonM_ASSERT(pTD->pLockBytesChain == NULL); if (IsWriteable() == S_OK) { UINT cTransforms = pTD->cTransformLayers; ULARGE_INTEGER *pacbSpans = (ULARGE_INTEGER *) _alloca(cTransforms * sizeof(ULARGE_INTEGER)); if (!pacbSpans) return STG_E_INSUFFICIENTMEMORY; HRESULT hr = NO_ERROR; for (UINT i = 0; i < cTransforms; i++) { hr = pTD->apTransformInstance[i]->SpaceSize(pacbSpans + i); if (!SUCCEEDED(hr)) return hr; } WCHAR awcsPath[MAX_PATH]; wcsCpy(awcsPath, pwcsSpaceNameStorage); wcsCat(awcsPath, pwcsLZXSpace); wcsCat(awcsPath, pwcsSpanInfoSuffix); IStreamITEx *pStrm = NULL; hr = OpenStream(NULL, awcsPath, STGM_READWRITE, &pStrm); if (!SUCCEEDED(hr)) return hr; ULONG cbSpanSizes = cTransforms * sizeof(ULARGE_INTEGER); ULONG cbWritten = 0; hr = pStrm->Write(pacbSpans, cbSpanSizes, &cbWritten); pStrm->Release(); pStrm = NULL; if (hr == S_FALSE || cbWritten != cbSpanSizes) hr = STG_E_WRITEFAULT; if (!SUCCEEDED(hr)) return hr; } pTD->apTransformInstance[0]->Release(); delete pTD; m_papTransformDescriptors[iSpace] = NULL; return NO_ERROR; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::CreateLockBytes (IUnknown *pUnkOuter, const WCHAR *pwcsPath, const WCHAR *pwcsDataSpaceName, BOOL fOverwrite, ILockBytes **ppLKB ) { CSyncWith sw(m_cs); if (wcsicmp_0x0409(pwcsDataSpaceName, pwcsUncompressedSpace)) { RonM_ASSERT(pwcsPath[0] != L':'); HRESULT hr = CreateTransformedLockBytes(pUnkOuter, pwcsPath, pwcsDataSpaceName, fOverwrite, ppLKB ); return hr; } ILockBytes *pLKB = FindActiveLockBytes(pwcsPath); if (pLKB) { pLKB->Release(); return STG_E_INUSE; } PathInfo PI, PIPrev; CopyPath(PI, pwcsPath); PI.ullcbOffset = 0; PI.ullcbData = 0; PI.uStateBits = 0; PI.iLockedBytesSegment = 0; PI.cUnrecordedChanges = 0; RonM_ASSERT(PI.awszStreamPath[PI.cwcStreamPath - 1] != L'/'); PIPrev.cwcStreamPath = 0; // Setup to detect overwrite condition. HRESULT hr = m_pPathManager->CreateEntry(&PI, &PIPrev, fOverwrite); if (SUCCEEDED(hr)) { if (PIPrev.cwcStreamPath) // Did we overwrite an existing file? m_pFreeListManager->PutFreeSpace(PIPrev.ullcbOffset, PIPrev.ullcbData); hr = CSegmentLockBytes::OpenSegment(pUnkOuter, this, m_pLKBMedium, &PI, ppLKB); } if (hr == S_OK && pwcsPath[0] != L'/') { ((IITLockBytes *) *ppLKB)->Container()->MarkSecondary(); if (m_cFSObjectRefs!= UINT(~0)) { m_cFSObjectRefs++; this->Release(); // To account for circular references through ":" streams } } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::OpenLockBytes (IUnknown *pUnkOuter, const WCHAR *pwcsPath, ILockBytes **ppLKB) { CSyncWith sw(m_cs); ILockBytes *pLKB = FindActiveLockBytes(pwcsPath); if (pLKB) { *ppLKB = pLKB; return NO_ERROR; } PathInfo PI; CopyPath(PI, pwcsPath); RonM_ASSERT(PI.awszStreamPath[PI.cwcStreamPath - 1] != L'/'); IITPathManager *pPathManager = PathMgr(&PI); HRESULT hr = pPathManager->FindEntry(&PI); if (hr == S_OK) { RonM_ASSERT(pwcsPath[0] != L':' || PI.iLockedBytesSegment == 0); if (PI.iLockedBytesSegment != 0) hr = OpenTransformedLockbytes(&PI, ppLKB); else { if (PI.ullcbData.NonZero() && (pPathManager == m_pPathManager)) PI.ullcbOffset += m_itfsh.offPathMgrOrigin; hr = CSegmentLockBytes::OpenSegment(pUnkOuter, this, m_pLKBMedium, &PI, ppLKB); } if (hr == S_OK && pwcsPath[0] != L'/') { ((IITLockBytes *) *ppLKB)->Container()->MarkSecondary(); if (m_cFSObjectRefs!= UINT(~0)) { m_cFSObjectRefs++; this->Release(); // To account for circular reference // through "F", "P", or a stream whose // path begins with ":". } } } else if (hr == S_FALSE) hr = STG_E_FILENOTFOUND; return hr; } HRESULT CITFileSystem::CImpITFileSystem::OpenTransformedLockbytes (PathInfo *pPI, ILockBytes **ppLKB) { RonM_ASSERT(pPI->cwcStreamPath > 0 && pPI->awszStreamPath[0] != L':'); UINT iSpace = pPI->iLockedBytesSegment; RonM_ASSERT(iSpace < m_pwscDataSpaceNames[1]); BOOL fNewActivation = !m_papTransformDescriptors[iSpace]; if (fNewActivation) { HRESULT hr = ActivateDataSpace(iSpace); if (!SUCCEEDED(hr)) return hr; } TransformDescriptor *pTD = m_papTransformDescriptors[iSpace]; RonM_ASSERT(pTD); ILockBytes *pLKB = CTransformedLockBytes::FindTransformedLockBytes(pPI->awszStreamPath, pTD); if (pLKB) { RonM_ASSERT(!fNewActivation); *ppLKB = pLKB; return NO_ERROR; } return CTransformedLockBytes::Open(NULL, pPI, pTD, this, ppLKB); } HRESULT __stdcall CITFileSystem::CImpITFileSystem::CreateStream (IUnknown *pUnkOuter, const WCHAR *pwcsPath, DWORD grfMode, IStreamITEx **ppStrm ) { return CreateStream(pUnkOuter, pwcsPath, (m_itfsh.fFlags & fDefaultIsCompression) ? (pwcsPath[0] == L':'? pwcsUncompressedSpace : pwcsLZXSpace) : pwcsUncompressedSpace, grfMode, ppStrm ); } HRESULT __stdcall CITFileSystem::CImpITFileSystem::CreateStream (IUnknown *pUnkOuter, const WCHAR * pwcsName, const WCHAR *pwcsDataSpaceName, DWORD grfMode, IStreamITEx ** ppstm ) { CSyncWith sw(m_cs); ILockBytes *pLKB; HRESULT hr = CreateLockBytes(NULL, pwcsName, pwcsDataSpaceName, !(grfMode & STGM_FAILIFTHERE), &pLKB ); if (SUCCEEDED(hr)) { hr = CStream::OpenStream(pUnkOuter, pLKB, grfMode, ppstm); if (!SUCCEEDED(hr)) pLKB->Release(); else if (((IITLockBytes *) pLKB)->Container()->IsSecondary()) ((IITStreamITEx *) *ppstm)->Container()->MarkSecondary(); } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::OpenStream (IUnknown *pUnkOuter, const WCHAR *pwcsPath, DWORD grfMode, IStreamITEx **ppStream ) { CSyncWith sw(m_cs); ILockBytes *pLKB; HRESULT hr = OpenLockBytes(NULL, pwcsPath, &pLKB); if (SUCCEEDED(hr)) { hr = CStream::OpenStream(pUnkOuter, pLKB, grfMode, ppStream); if (!SUCCEEDED(hr)) pLKB->Release(); else if (((IITLockBytes *) pLKB)->Container()->IsSecondary()) ((IITStreamITEx *) *ppStream)->Container()->MarkSecondary(); } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::ConnectStorage(CImpITUnknown *pStg) { CSyncWith sw(m_cs); pStg->MarkActive(m_pActiveStorageList); return NO_ERROR; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::ConnectLockBytes(CImpITUnknown *pStg) { CSyncWith sw(m_cs); pStg->MarkActive(m_pActiveLockBytesList); return NO_ERROR; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::LookForActivity (WCHAR const *pwcsName, IEnumSTATSTG *pEnum) { HRESULT hr = S_OK; BOOL fFinished = FALSE; BOOL fInUse = FALSE; // The strategy we use is to enumerate paths until we find one that doesn't // have a prefix which matches pwcsName. for (;;) { STATSTG statstg; statstg.pwcsName = NULL; HRESULT hr2 = pEnum->Next(1, &statstg, NULL); if (hr2 != S_OK) { RonM_ASSERT(statstg.pwcsName == NULL); if (hr2 != S_FALSE) hr = hr2; break; } RonM_ASSERT(statstg.pwcsName); // Now we'll compare the two paths to see if we still have a prefix match. const WCHAR *pwcPrefix = pwcsName; const WCHAR *pwcEnum = statstg.pwcsName; for (; ; ) { WCHAR wcPrefix = WC_To_0x0409_Lower(*pwcPrefix++); WCHAR wcEnum = WC_To_0x0409_Lower(*pwcEnum++); if (wcPrefix == wcEnum && wcPrefix) continue; // We've either found a mismatch or we've exhausted the // prefix string. if(wcPrefix || (wcEnum && wcEnum != L'/')) { fFinished = TRUE; // Not a storage prefix break; } // Next we look to see if this path is currently being used. // We can't delete storages or streams that are active. if (statstg.pwcsName[lstrlenW(statstg.pwcsName)-1] == L'/') { IStorageITEx *pStorage = FindActiveStorage(statstg.pwcsName); if (pStorage) { pStorage->Release(); fInUse = TRUE; fFinished = TRUE; } } else { // For lockbyte objects we need to check the // chain of uncompressed lockbyte objects and // then perhaps the chain for each active data space. ILockBytes *pLKB = FindActiveLockBytes(statstg.pwcsName); if (!pLKB) { // Not found as an uncompressed lockbyte. // So we iterate through the data spaces. // BugBug: Need to abstract the space count and hide the // way that we store this information. UINT cSpaces = m_pwscDataSpaceNames[1]; for (; cSpaces--; ) { TransformDescriptor *pTD = m_papTransformDescriptors[cSpaces]; if (!pTD) continue; pLKB = FindMatchingLockBytes (statstg.pwcsName, (CImpITUnknown *) pTD->pLockBytesChain ); if (pLKB) break; } } if (pLKB) { pLKB->Release(); fInUse = TRUE; fFinished = TRUE; } } break; } OLEHeap()->Free(statstg.pwcsName); if (fFinished) break; } if (fInUse) hr = STG_E_INUSE; return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::DeleteItem(WCHAR const *pwcsName) { // For a stream we remove one item. // For a storage we'll remove the storage and everything it contains! CSyncWith sw(m_cs); // To avoid race conditions... IEnumSTATSTG *pEnum = NULL; HRESULT hr = m_pPathManager->EnumFromObject(NULL, pwcsName, wcsLen(pwcsName), IID_IEnumSTATSTG, (PVOID *) &pEnum ); if (!SUCCEEDED(hr)) return hr; // First we look to see if this item or one of its descendants are being used. // We can't delete an item that's in use. hr = LookForActivity(pwcsName, pEnum); if (!SUCCEEDED(hr)) { pEnum->Release(); return hr; } // The strategy we use is to enumerate paths until we find one that doesn't // have a prefix which matches pwcsName. pEnum->Reset(); BOOL fFinished = FALSE; for (;;) { STATSTG statstg; statstg.pwcsName = NULL; HRESULT hr2 = pEnum->Next(1, &statstg, NULL); if (hr2 != S_OK) { RonM_ASSERT(statstg.pwcsName == NULL); if (hr2 != S_FALSE) hr = hr2; break; } RonM_ASSERT(statstg.pwcsName); // Now we'll compare the two paths to see if we still have a prefix match. const WCHAR *pwcPrefix = pwcsName; const WCHAR *pwcEnum = statstg.pwcsName; for (; ; ) { WCHAR wcPrefix = WC_To_0x0409_Lower(*pwcPrefix++); WCHAR wcEnum = WC_To_0x0409_Lower(*pwcEnum++); if (wcPrefix == wcEnum && wcPrefix) continue; // We've either found a mismatch or we've exhausted the // prefix string. if(wcPrefix || (wcEnum && wcEnum != L'/')) { fFinished = TRUE; // Not a storage prefix break; } // At this point we know that we've got a prefix match and that the // matching path is not being used by anybody. // Now we can delete the corresponding path manager entry. PathInfo PI; CopyPath(PI, statstg.pwcsName); hr = m_pPathManager->DeleteEntry(&PI); if (!SUCCEEDED(hr)) { fFinished = TRUE; break; } // If this path was for a uncompressed stream, we need to tell the // free list manager about the data space we've just released for reuse. if (PI.awszStreamPath[PI.cwcStreamPath-1] != L'/') if (PI.iLockedBytesSegment == 0) { hr = m_pFreeListManager->PutFreeSpace(PI.ullcbOffset, PI.ullcbData); if (!SUCCEEDED(hr)) fFinished = TRUE; } // BugBug Need to make a similar call to the transformed space... break; } OLEHeap()->Free(statstg.pwcsName); if (fFinished) break; } pEnum->Release(); return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::RenameItem(WCHAR const *pwcsOldName, WCHAR const *pwcsNewName) { // For a stream we remove one item. // For a storage we'll remove the storage and everything it contains! CSyncWith sw(m_cs); // To avoid race conditions... IEnumSTATSTG *pEnum = NULL; HRESULT hr = m_pPathManager->EnumFromObject(NULL, pwcsOldName, wcsLen(pwcsOldName), IID_IEnumSTATSTG, (PVOID *) &pEnum ); if (!SUCCEEDED(hr)) return hr; // First we look to see if this item or one of its descendants are being used. // We can't delete an item that's in use. hr = LookForActivity(pwcsOldName, pEnum); if (!SUCCEEDED(hr)) { pEnum->Release(); return hr; } // The strategy we use is to enumerate paths until we find one that doesn't // have a prefix which matches pwcsName. pEnum->Reset(); BOOL fFinished = FALSE; UINT cwcOldName = lstrlenW(pwcsOldName); UINT cwcNewName = lstrlenW(pwcsNewName); WCHAR awcsBuffer[MAX_PATH]; for (;;) { STATSTG statstg; statstg.pwcsName = NULL; HRESULT hr2 = pEnum->Next(1, &statstg, NULL); if (hr2 != S_OK) { RonM_ASSERT(statstg.pwcsName == NULL); if (hr2 != S_FALSE) hr = hr2; break; } RonM_ASSERT(statstg.pwcsName); // Now we'll compare the two paths to see if we still have a prefix match. const WCHAR *pwcPrefix = pwcsOldName; const WCHAR *pwcEnum = statstg.pwcsName; for (; ; ) { WCHAR wcPrefix = WC_To_0x0409_Lower(*pwcPrefix++); WCHAR wcEnum = WC_To_0x0409_Lower(*pwcEnum++); if (wcPrefix == wcEnum && wcPrefix) continue; // We've either found a mismatch or we've exhausted the // prefix string. if(wcPrefix || (wcEnum && wcEnum != L'/')) { fFinished = TRUE; // Not a storage prefix break; } // At this point we know that we've got a prefix match and that the // matching path is not being used by anybody. // Now we look to see if the new name would be too long. PWCHAR pwcsSuffix = statstg.pwcsName + cwcOldName; UINT cwcSuffix = lstrlenW(pwcsSuffix); if (cwcSuffix + cwcNewName >= MAX_PATH) { hr = STG_E_INVALIDNAME; fFinished = TRUE; break; } // Now we construct the new name and look to see if it already exists. // We don't allow rename operations to destroy any existing items. wcsCpy(awcsBuffer, pwcsNewName); wcsCat(awcsBuffer, pwcsSuffix); PathInfo PI; CopyPath(PI, awcsBuffer); hr2 = m_pPathManager->FindEntry(&PI); if (hr2 == S_OK) { hr = STG_E_FILEALREADYEXISTS; fFinished = TRUE; break; } CopyPath(PI, statstg.pwcsName); hr = m_pPathManager->DeleteEntry(&PI); if (!SUCCEEDED(hr)) { fFinished = TRUE; break; } CopyPath(PI, awcsBuffer); hr = m_pPathManager->CreateEntry(&PI, NULL, FALSE); if (!SUCCEEDED(hr)) { // This is bad. We've removed the old entry, but we haven't been // able to create the new entry. So we've lost a item. The best // we can do in this situation is to reclaim the space occupied // by the item. if (PI.awszStreamPath[PI.cwcStreamPath-1] != L'/') if (PI.iLockedBytesSegment == 0) m_pFreeListManager->PutFreeSpace(PI.ullcbOffset, PI.ullcbData); // BugBug Need to make a similar call to the transformed space... fFinished = TRUE; break; } break; } OLEHeap()->Free(statstg.pwcsName); if (fFinished) break; } pEnum->Release(); return hr; RonM_ASSERT(FALSE); return E_NOTIMPL; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::UpdatePathInfo(PathInfo *pPathInfo) { CSyncWith sw(m_cs); RonM_ASSERT(pPathInfo->cwcStreamPath); // No empty paths allowed! PathInfo pi = *pPathInfo; if ( pi.awszStreamPath[pPathInfo->cwcStreamPath - 1] != L'/' && pi.awszStreamPath[pPathInfo->cwcStreamPath - 1] != L'\\' && pi.iLockedBytesSegment == 0 // Uncompressed dataspace. && pi.ullcbData.NonZero() ) { // This is a stream in the L"Uncompressed" dataspace. // So we've got to adjust for our offset origin. pi.ullcbOffset -= m_itfsh.offPathMgrOrigin; } HRESULT hr = PathMgr(pPathInfo)->UpdateEntry(&pi); if ( hr != S_OK && pPathInfo->cwcStreamPath && pPathInfo->awszStreamPath[pPathInfo->cwcStreamPath - 1] == L'/' ) { // Since some storage entries don't have explicit entries in // the path database, we must be prepared to add them when // get an update request for a storage path. PathInfo PI; hr = PathMgr(pPathInfo)->CreateEntry(pPathInfo, &PI, FALSE); } if (hr == S_OK) pPathInfo->cUnrecordedChanges = 0; return hr; } IITPathManager *CITFileSystem::CImpITFileSystem::PathMgr(PathInfo *pPathInfo) { return ( pPathInfo->cwcStreamPath == 1 && (pPathInfo->awszStreamPath[0] == L'F' || pPathInfo->awszStreamPath[0] == L'P') )? m_pSysPathManager : m_pPathManager; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::GetITFSTimes (FILETIME *pctime, FILETIME *patime, FILETIME *pmtime ) { STATSTG statstg; HRESULT hr = m_pLKBMedium->Stat(&statstg, STATFLAG_NONAME); if (!SUCCEEDED(hr)) return hr; if (pctime) { pctime->dwLowDateTime = statstg.ctime.dwLowDateTime; pctime->dwHighDateTime = statstg.ctime.dwHighDateTime; } if (patime) { patime->dwLowDateTime = statstg.atime.dwLowDateTime; patime->dwHighDateTime = statstg.atime.dwHighDateTime; } if (pmtime) { pmtime->dwLowDateTime = statstg.mtime.dwLowDateTime; pmtime->dwHighDateTime = statstg.mtime.dwHighDateTime; } return NO_ERROR; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::ReallocEntry (PathInfo *pPathInfo, CULINT ullcbNew, BOOL fCopyContent) { CSyncWith sw(m_cs); CULINT ullBaseNew; HRESULT hr = m_pFreeListManager->GetFreeSpace(&ullBaseNew, &ullcbNew); if (SUCCEEDED(hr) & fCopyContent) { CULINT ullcb; ullcb = pPathInfo->ullcbData; if (ullcb > ullcbNew) ullcb = ullcbNew; hr = IITLockBytes::CopyLockBytes(m_pLKBMedium, pPathInfo->ullcbOffset, pPathInfo->ullcbOffset + ullcb, m_pLKBMedium, ullBaseNew ); } if (SUCCEEDED(hr)) { hr = m_pFreeListManager->PutFreeSpace(pPathInfo->ullcbOffset, pPathInfo->ullcbData); RonM_ASSERT(SUCCEEDED(hr)); pPathInfo->ullcbOffset = ullBaseNew; pPathInfo->ullcbData = ullcbNew; } else { HRESULT hr = m_pFreeListManager->PutFreeSpace(ullBaseNew, ullcbNew); RonM_ASSERT(SUCCEEDED(hr)); } IITPathManager *pPathManager = PathMgr(pPathInfo); if (SUCCEEDED(hr)) if (m_pSysPathManager == pPathManager) m_pSysPathManager->UpdateEntry(pPathInfo); else if (++(pPathInfo->cUnrecordedChanges) > PENDING_CHANGE_LIMIT) UpdatePathInfo(pPathInfo); return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::ReallocInPlace (PathInfo *pPathInfo, CULINT ullcbNew) { CSyncWith sw(m_cs); HRESULT hr = NO_ERROR; if (!(pPathInfo->ullcbData.NonZero())) // Currently empty? { if (ullcbNew.NonZero()) hr = ReallocEntry(pPathInfo, ullcbNew, FALSE); return hr; } if (pPathInfo->ullcbData == ullcbNew) return hr; IITPathManager *pPathManager = PathMgr(pPathInfo); if (pPathInfo->ullcbData > ullcbNew) { CULINT ullBaseTrailing, ullcbTrailing; ullBaseTrailing = pPathInfo->ullcbOffset + ullcbNew; ullcbTrailing = pPathInfo->ullcbData - ullcbNew; // Shrinking always works... hr = m_pFreeListManager->PutFreeSpace(ullBaseTrailing, ullcbTrailing); RonM_ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { pPathInfo->ullcbData = ullcbNew; if (m_pSysPathManager == pPathManager) m_pSysPathManager->UpdateEntry(pPathInfo); else if (++(pPathInfo->cUnrecordedChanges) > PENDING_CHANGE_LIMIT) UpdatePathInfo(pPathInfo); } return hr; } ullcbNew -= pPathInfo->ullcbData; hr = m_pFreeListManager->GetFreeSpaceAt (pPathInfo->ullcbOffset + pPathInfo->ullcbData, &ullcbNew); if (hr == S_OK) { pPathInfo->ullcbData += ullcbNew; if (m_pSysPathManager == pPathManager) m_pSysPathManager->UpdateEntry(pPathInfo); else if (++(pPathInfo->cUnrecordedChanges) > PENDING_CHANGE_LIMIT) UpdatePathInfo(pPathInfo); } return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::EnumeratePaths (WCHAR const *pwcsPathPrefix, IEnumSTATSTG **ppEnumStatStg) { CSyncWith sw(m_cs); HRESULT hr = CEnumFSItems::NewFSEnumerator (this, pwcsPathPrefix, wcsLen(pwcsPathPrefix), ppEnumStatStg); return hr; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::IsWriteable() { return m_fReadOnly? S_FALSE : S_OK; } HRESULT __stdcall CITFileSystem::CImpITFileSystem::FSObjectReleased() { if (m_cFSObjectRefs != UINT(-1)) { RonM_ASSERT(m_cFSObjectRefs); AddRef(); // Because this object is about to disappear. m_cFSObjectRefs--; } return NO_ERROR; } HRESULT CITFileSystem::CImpITFileSystem::CSystemPathManager::Create (IUnknown *punkOuter, CImpITFileSystem *pITFileSystem, IITPathManager **ppPathMgr) { CSystemPathManager *pSysPathMgr = New CSystemPathManager(punkOuter); return FinishSetup(pSysPathMgr? pSysPathMgr->m_PathManager.Init(pITFileSystem) : STG_E_INSUFFICIENTMEMORY, pSysPathMgr, IID_PathManager, (PPVOID)ppPathMgr ); } CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::CImpIPathManager(CSystemPathManager *pBackObj, IUnknown *punkOuter) : IITPathManager(pBackObj, punkOuter) { m_pIITFS = NULL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::Init(CImpITFileSystem *pITFileSystem) { m_pIITFS = pITFileSystem; return NO_ERROR; } // IPersist Method: HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::GetClassID(CLSID __RPC_FAR *pClassID) { *pClassID = CLSID_SystemPathManager; return NO_ERROR; } // IITPathManager interfaces: HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::FlushToLockBytes() { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::FindEntry (PPathInfo pPI ) { RonM_ASSERT(pPI->cwcStreamPath == 1); pPI->uStateBits = 0; pPI->iLockedBytesSegment = 0; pPI->cUnrecordedChanges = 0; switch(pPI->awszStreamPath[0]) { case L'F': pPI->ullcbOffset = m_pIITFS->m_itfsh.offFreeListData; pPI->ullcbData = m_pIITFS->m_itfsh. cbFreeListData; return NO_ERROR; case L'P': pPI->ullcbOffset = m_pIITFS->m_itfsh.offPathMgrData; pPI->ullcbData = m_pIITFS->m_itfsh. cbPathMgrData; return NO_ERROR; default: RonM_ASSERT(FALSE); return STG_E_DOCFILECORRUPT; } } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::CreateEntry(PPathInfo pPINew, PPathInfo pPIOld, BOOL fReplace ) { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::DeleteEntry(PPathInfo pPI ) { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::UpdateEntry(PPathInfo pPI ) { RonM_ASSERT(pPI->cwcStreamPath == 1); RonM_ASSERT(pPI->iLockedBytesSegment == 0); switch(pPI->awszStreamPath[0]) { case L'F': m_pIITFS->m_itfsh.offFreeListData = pPI->ullcbOffset; m_pIITFS->m_itfsh. cbFreeListData = pPI->ullcbData; pPI->cUnrecordedChanges = 0; m_pIITFS->m_fHeaderIsDirty = TRUE; return NO_ERROR; case L'P': m_pIITFS->m_itfsh.offPathMgrData = pPI->ullcbOffset; m_pIITFS->m_itfsh. cbPathMgrData = pPI->ullcbData; pPI->cUnrecordedChanges = 0; m_pIITFS->m_fHeaderIsDirty = TRUE; return NO_ERROR; default: RonM_ASSERT(FALSE); return STG_E_DOCFILECORRUPT; } } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::EnumFromObject(IUnknown *punkOuter, const WCHAR *pwszPrefix, UINT cwcPrefix, REFIID riid, PVOID *ppv ) { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::GetPathDB(IStreamITEx *pTempPDBStrm, BOOL fCompact) { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CSystemPathManager:: CImpIPathManager::ForceClearDirty() { RonM_ASSERT(FALSE); // To detect unexpected calls to this interface. return E_NOTIMPL; } CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::CImpIEnumSTATSTG (CEnumFSItems *pBackObj, IUnknown *punkOuter) : IITEnumSTATSTG(pBackObj, punkOuter) { m_pEnumPathMgr = NULL; m_pITFS = NULL; } CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::~CImpIEnumSTATSTG() { if (m_pEnumPathMgr) m_pEnumPathMgr->Release(); if (m_pITFS) m_pITFS->Release(); } HRESULT CITFileSystem::CImpITFileSystem::CEnumFSItems::NewFSEnumerator (CImpITFileSystem *pITFS, const WCHAR *pwszPathPrefix, UINT cwcPathPrefix, IEnumSTATSTG **ppEnumSTATSTG ) { CEnumFSItems *pEnumFS = New CEnumFSItems(NULL); CSyncWith sw(pITFS->m_cs); return FinishSetup(pEnumFS? pEnumFS->m_ImpEnumSTATSTG.InitFSEnumerator (pITFS, pwszPathPrefix, cwcPathPrefix) : STG_E_INSUFFICIENTMEMORY, pEnumFS, IID_IEnumSTATSTG, (PPVOID) ppEnumSTATSTG ); } HRESULT CITFileSystem::CImpITFileSystem::CEnumFSItems::NewCloneOf (CImpIEnumSTATSTG *pImpEnumFS, IEnumSTATSTG **ppEnumSTATSTG ) { CEnumFSItems *pEnumFS = New CEnumFSItems(NULL); CSyncWith sw(pImpEnumFS->m_pITFS->m_cs); return FinishSetup(pEnumFS? pEnumFS->m_ImpEnumSTATSTG.InitNewCloneOf(pImpEnumFS) : STG_E_INSUFFICIENTMEMORY, pEnumFS, IID_IEnumSTATSTG, (PPVOID) ppEnumSTATSTG ); } HRESULT CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::InitNewCloneOf (CImpIEnumSTATSTG *pImpEnumFS) { m_pITFS = pImpEnumFS->m_pITFS; m_pITFS->AddRef(); return pImpEnumFS->m_pEnumPathMgr->Clone(&m_pEnumPathMgr); } HRESULT CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::InitFSEnumerator (CImpITFileSystem *pITFS, const WCHAR *pwszPathPrefix, UINT cwcPathPrefix) { m_pITFS = pITFS; m_pITFS->AddRef(); HRESULT hr = m_pITFS->m_pPathManager->EnumFromObject (NULL, pwszPathPrefix, cwcPathPrefix, IID_IEnumSTATSTG, (PVOID *) &m_pEnumPathMgr ); if (SUCCEEDED(hr)) ((IITEnumSTATSTG *) m_pEnumPathMgr)->Container()->MarkSecondary(); return hr; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::Next( /* [in] */ ULONG celt, /* [in] */ STATSTG __RPC_FAR *rgelt, /* [out] */ ULONG __RPC_FAR *pceltFetched) { return m_pEnumPathMgr->Next(celt, rgelt, pceltFetched); } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::Skip( /* [in] */ ULONG celt) { return m_pEnumPathMgr->Skip(celt); } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::Reset( void) { return m_pEnumPathMgr->Reset(); } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::Clone( /* [out] */ IEnumSTATSTG __RPC_FAR *__RPC_FAR *ppenum) { return CEnumFSItems::NewCloneOf(this, ppenum); } HRESULT CITFileSystem::CImpITFileSystem::DeactivateSpace(UINT iSpace) { return DeactivateDataSpace(iSpace); } HRESULT CITFileSystem::CImpITFileSystem::GetFirstRecord(SEntry *prec, IStreamITEx *pRecTblStrm, int cTblRecsInCache, int cTblRecsTotal, SEntry *pRecTblCache) { HRESULT hr = NO_ERROR; RonM_ASSERT(cTblRecsTotal > 0); if (cTblRecsTotal == cTblRecsInCache) { CopyMemory(prec, (LPVOID)&pRecTblCache[0], sizeof(SEntry)); } else { LARGE_INTEGER li = CLINT(0).Li(); ULONG cbRead; if (SUCCEEDED(hr = pRecTblStrm->Seek(li, STREAM_SEEK_SET, NULL))) { if (SUCCEEDED(hr = pRecTblStrm->Read(prec, sizeof(SEntry), &cbRead))) { RonM_ASSERT(cbRead == sizeof(SEntry)); } } } return hr; } HRESULT CITFileSystem::CImpITFileSystem::GetNextRecord(ULONG ulCurRec, SEntry *prec, IStreamITEx *pRecTblStrm, int cTblRecsInCache, int cTblRecsTotal, SEntry *pRecTblCache) { RonM_ASSERT(cTblRecsTotal > 0); HRESULT hr = NO_ERROR; if (cTblRecsTotal == cTblRecsInCache) { if (ulCurRec == cTblRecsInCache) return S_FALSE; CopyMemory(prec, (LPVOID)&pRecTblCache[ulCurRec + 1], sizeof(SEntry)); } else { ULONG cbRead; if (SUCCEEDED(hr = pRecTblStrm->Read(prec, sizeof(SEntry), &cbRead))) { RonM_ASSERT(cbRead == sizeof(SEntry)); } } return S_OK; } HRESULT CITFileSystem::CImpITFileSystem::UpdatePathDB(IStreamITEx *pRecTblStrm, int cTblRecsInCache, int cTblRecsTotal, SEntry *pRecTblCache, CITSortRecords *pSort) { HRESULT hr = NO_ERROR; if (cTblRecsTotal == 0) return hr; IITEnumSTATSTG *pEnumPathMgr = NULL; ULONG ulCurEnumIndex; ULONG celtFetched = 1; UINT iDataSpace = FindSpaceName(pwcsUncompressedSpace); PathInfo pathInfo; if (SUCCEEDED(hr = m_pPathManager->EnumFromObject(NULL, L"//", 1, IID_IEnumSTATSTG, (PVOID *) &pEnumPathMgr))) { hr = pEnumPathMgr->GetFirstEntryInSeq(&pathInfo); ulCurEnumIndex = 0; } if (cTblRecsTotal == cTblRecsInCache) { for (int iRec = 0; SUCCEEDED(hr) && (iRec < cTblRecsInCache); iRec++) { SEntry *pe = pRecTblCache + iRec; RonM_ASSERT(pe->ulEntryID > ulCurEnumIndex); ULONG cEntriesToSkip = (pe->ulEntryID - ulCurEnumIndex); for (int iEnum = 0; SUCCEEDED(hr) && (celtFetched > 0) && (iEnum < cEntriesToSkip); iEnum++) { hr = pEnumPathMgr->GetNextEntryInSeq(1, &pathInfo, &celtFetched); ulCurEnumIndex += 1; }//for RonM_ASSERT(pe->ulEntryID == ulCurEnumIndex); RonM_ASSERT (pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'/' && pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'\\' && pathInfo.iLockedBytesSegment == 0 // Uncompressed dataspace. && pathInfo.ullcbData.NonZero()); RonM_ASSERT(pathInfo.ullcbOffset >= pe->ullcbOffset); if (pathInfo.ullcbOffset != pe->ullcbOffset) { //wprintf(L"Name = %s, offset=%d newOffset =%d size= %d entryid= %d\n", pathInfo.awszStreamPath, (int)pathInfo.ullcbOffset.Ull(), (int)pe->ullcbOffset.Ull(), (int)pathInfo.ullcbData.Ull(), (int)pe->ulEntryID); pathInfo.ullcbOffset = pe->ullcbOffset; if (SUCCEEDED(hr = PathMgr(&pathInfo)->UpdateEntry(&pathInfo))) pathInfo.cUnrecordedChanges = 0; } }// update all records } else { BOOL fEndLoop = FALSE; int iCurBlk = -1; ULONG cRecsToRead; while (!fEndLoop && SUCCEEDED(hr)) { if (SUCCEEDED(hr = pSort->ReadNextSortedBlk(&iCurBlk, (LPBYTE)pRecTblCache, &cRecsToRead, &fEndLoop)) && !fEndLoop) { for (int iRec = 0; SUCCEEDED(hr) && (iRec < cRecsToRead); iRec++) { SEntry *pe = pRecTblCache + iRec; RonM_ASSERT(pe->ulEntryID > ulCurEnumIndex); ULONG cEntriesToSkip = (pe->ulEntryID - ulCurEnumIndex); for (int iEnum = 0; SUCCEEDED(hr) && (iEnum < cEntriesToSkip); iEnum++) { hr = pEnumPathMgr->GetNextEntryInSeq(1, &pathInfo, &celtFetched); ulCurEnumIndex += 1; }//for RonM_ASSERT(pe->ulEntryID == ulCurEnumIndex); RonM_ASSERT (pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'/' && pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'\\' && pathInfo.iLockedBytesSegment == 0 // Uncompressed dataspace. && pathInfo.ullcbData.NonZero()); RonM_ASSERT(pathInfo.ullcbOffset >= pe->ullcbOffset); if (pathInfo.ullcbOffset != pe->ullcbOffset) { //wprintf(L"Name = %s, offset=%d newOffset =%d size= %d entryid= %d\n", pathInfo.awszStreamPath, (int)pathInfo.ullcbOffset.Ull(), (int)pe->ullcbOffset.Ull(), (int)pathInfo.ullcbData.Ull(), (int)pe->ulEntryID); pathInfo.ullcbOffset = pe->ullcbOffset; if (SUCCEEDED(hr = PathMgr(&pathInfo)->UpdateEntry(&pathInfo))) pathInfo.cUnrecordedChanges = 0; } }// update all records }//read next file block in sorted sequence }//while }//else if (pEnumPathMgr) pEnumPathMgr->Release(); return hr; } HRESULT CITFileSystem::CImpITFileSystem::SortRecTable(ESortField eSField, IStreamITEx *pRecTblStrm, int cTblRecsInCache, int cTblRecsTotal, SEntry *pRecTblCache, CITSortRecords **ppSort) { HRESULT hr = NO_ERROR; if (*ppSort == NULL) { *ppSort = (CITSortRecords *)New CITSortRecords; if (*ppSort == NULL) return STG_E_INSUFFICIENTMEMORY; } (*ppSort)->Initialize(pRecTblStrm, eSField, cTblRecsTotal, MAX_TABLE_RECS_INCACHE, sizeof(SEntry)); if (*ppSort && cTblRecsTotal) { if (cTblRecsTotal == cTblRecsInCache) { (*ppSort)->QSort((LPBYTE)pRecTblCache, 0, cTblRecsInCache - 1, &CompareEntries); VerifyData((LPBYTE)pRecTblCache, cTblRecsTotal, eSField, TRUE); } else { hr = (*ppSort)->FileSort(&CompareEntries); } } return hr; } int CITFileSystem::CImpITFileSystem::CompareEntries(SEntry e1, SEntry e2, ESortField eSField) { if (eSField == SORT_BY_ENTRYID) { if (e1.ulEntryID < e2.ulEntryID) return -1; else if (e1.ulEntryID > e2.ulEntryID) return 1; else return 0; //return ((long)e1.ulEntryID - (long)e2.ulEntryID); } else { if (e1.ullcbOffset < e2.ullcbOffset) return -1; else if (e1.ullcbOffset > e2.ullcbOffset) return 1; else return 0; } } HRESULT CITFileSystem::CImpITFileSystem::AppendToRecTbl(SEntry *prec, IStreamITEx *pRecTblStrm, int *pcTblRecsInCache, int *pcTblRecsTotal, SEntry *pRecTblCache) { HRESULT hr = NO_ERROR; //printf("Entry[%d] offset= %d, entryid = %d, size = %d\n", // prec->ulEntryID, (int)prec->ullcbOffset.Ull(), // (int)prec->ulEntryID, (int)prec->ullcbData.Ull()); if (*pcTblRecsInCache >= MAX_TABLE_RECS_INCACHE) { ULONG cbWritten; if (SUCCEEDED(hr = pRecTblStrm->Write((LPVOID)pRecTblCache, sizeof(SEntry)* (*pcTblRecsInCache), &cbWritten))) { RonM_ASSERT(cbWritten == sizeof(SEntry)*(*pcTblRecsInCache)); *pcTblRecsInCache = 0; } } CopyMemory(&pRecTblCache[(*pcTblRecsInCache)++], prec, sizeof(SEntry)); (*pcTblRecsTotal)++; return hr; } HRESULT CITFileSystem::CImpITFileSystem::InitRecTable(IStreamITEx **ppRecTblStrm, int *pcTblRecsInCache, int *pcTblRecsTotal, SEntry **ppRecTblCache) { *pcTblRecsInCache = 0; *pcTblRecsTotal = 0; *ppRecTblCache = NULL; *ppRecTblStrm = NULL; *ppRecTblCache = (SEntry *) New BYTE[sizeof(SEntry) * MAX_TABLE_RECS_INCACHE]; if (*ppRecTblCache == NULL) return STG_E_INSUFFICIENTMEMORY; HRESULT hr = CreateTempStm(ppRecTblStrm); return hr; } HRESULT CITFileSystem::CImpITFileSystem::CreateTempStm(IStreamITEx **ppRecTblStrm) { ILockBytes *pLKB = NULL; *ppRecTblStrm = NULL; HRESULT hr = CFSLockBytes::CreateTemp(NULL, &pLKB); if (hr == S_OK) { hr = CStream::OpenStream(NULL,pLKB, STGM_READWRITE, (IStreamITEx **) ppRecTblStrm); if (hr != S_OK) pLKB->Release(); else { //(*ppRecTblStrm)->AddRef(); LARGE_INTEGER uli = CLINT(0).Li(); hr = (*ppRecTblStrm)->Seek(uli, STREAM_SEEK_SET, NULL); } } return hr; } HRESULT CITFileSystem::CImpITFileSystem::BuildUpEntryTable(ULONG *pulRecNum, IStreamITEx *pRecTblStrm, int *pcTblRecsInCache, int *pcTblRecsTotal, SEntry *pRecTblCache, BOOL *pfNeedFileSort) { //init out param *pcTblRecsTotal = *pcTblRecsInCache = 0; *pulRecNum = 0; IITEnumSTATSTG *pEnumPathMgr; HRESULT hr = NO_ERROR; if (SUCCEEDED(hr = m_pPathManager->EnumFromObject(NULL, L"//", 1, IID_IEnumSTATSTG, (PVOID *) &pEnumPathMgr))) { SEntry srec; ULONG celtFetched = 1; PathInfo pathInfo; UINT iDataSpace = FindSpaceName(pwcsUncompressedSpace); if (SUCCEEDED(hr = pEnumPathMgr->GetFirstEntryInSeq(&pathInfo))) { if (pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'/' && pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'\\' && pathInfo.iLockedBytesSegment == 0 // Uncompressed dataspace. && pathInfo.ullcbData.NonZero() ) { srec.ullcbOffset = pathInfo.ullcbOffset; srec.ullcbData = pathInfo.ullcbData ; srec.ulEntryID = 0; AppendToRecTbl(&srec, pRecTblStrm, pcTblRecsInCache, pcTblRecsTotal, pRecTblCache); } (*pulRecNum) += 1; while (SUCCEEDED(hr) && (celtFetched > 0)) { if (SUCCEEDED(hr = pEnumPathMgr->GetNextEntryInSeq(1, &pathInfo, &celtFetched)) && (celtFetched > 0)) { if (pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'/' && pathInfo.awszStreamPath[pathInfo.cwcStreamPath - 1] != L'\\' && pathInfo.iLockedBytesSegment == 0 // Uncompressed dataspace. && pathInfo.ullcbData.NonZero()) { srec.ullcbOffset = pathInfo.ullcbOffset; srec.ullcbData = pathInfo.ullcbData ; srec.ulEntryID = *pulRecNum; AppendToRecTbl(&srec, pRecTblStrm, pcTblRecsInCache, pcTblRecsTotal, pRecTblCache); } (*pulRecNum) += 1; } } } *pfNeedFileSort = (*pcTblRecsTotal > *pcTblRecsInCache); pEnumPathMgr->Release(); } return hr; } void CITFileSystem::CImpITFileSystem::SetCompaction(BOOL fSet) { if (fSet) m_itfsh.fFlags |= Compacting; else m_itfsh.fFlags &= ~Compacting; m_fHeaderIsDirty = TRUE; } HRESULT CITFileSystem::CImpITFileSystem::Compact(ECompactionLev iLev) { IStreamITEx *pRecTblStrm = NULL; SEntry *pRecTblCache = NULL; CITSortRecords *pSort = NULL; int cTblRecsInCache; int cTblRecsTotal; BOOL fNeedFileSort; ULONG cRecsToRead; ULONG ulRecNum; HRESULT hr; if(SUCCEEDED(hr = InitRecTable(&pRecTblStrm, &cTblRecsInCache, &cTblRecsTotal, &pRecTblCache))) { if(SUCCEEDED(hr = BuildUpEntryTable(&ulRecNum, pRecTblStrm, &cTblRecsInCache, &cTblRecsTotal, pRecTblCache, &fNeedFileSort))) { //write the remaining buffer to the stream if (fNeedFileSort) { ULONG cbWritten; if (SUCCEEDED(hr) && SUCCEEDED(hr = pRecTblStrm->Write((LPVOID)pRecTblCache, sizeof(SEntry)*cTblRecsInCache, &cbWritten))) { RonM_ASSERT(cbWritten == (sizeof(SEntry) * cTblRecsInCache)); cTblRecsInCache = 0; } } #if 1 //test code if (!fNeedFileSort) VerifyData((LPBYTE)pRecTblCache, cTblRecsTotal, SORT_BY_ENTRYID, TRUE); else { int cNumBlks = cTblRecsTotal/MAX_TABLE_RECS_INCACHE; int cEntriesInLastBlk; if (cEntriesInLastBlk = cTblRecsTotal % MAX_TABLE_RECS_INCACHE) { cNumBlks ++; } ULONG cbToRead, cbRead; LARGE_INTEGER uli = CLINT(0).Li(); hr = pRecTblStrm->Seek(uli, STREAM_SEEK_SET, NULL); for (int iCurBlk = 0; (iCurBlk < cNumBlks) && SUCCEEDED(hr); iCurBlk++) { //read block in sorted sequence if (iCurBlk == (cNumBlks -1)) cbToRead = cEntriesInLastBlk * sizeof(SEntry); else cbToRead = MAX_TABLE_RECS_INCACHE * sizeof(SEntry); if (SUCCEEDED(hr = pRecTblStrm->Read((LPBYTE)pRecTblCache, cbToRead, &cbRead))) { RonM_ASSERT(cbRead == cbToRead); VerifyData((LPBYTE)pRecTblCache, cbToRead/sizeof(SEntry), SORT_BY_ENTRYID, iCurBlk == 0); } }//for } //end test code #endif ESortField eSortType = SORT_BY_OFFSET; if (cTblRecsTotal && SUCCEEDED(hr = SortRecTable(eSortType, pRecTblStrm, cTblRecsInCache, cTblRecsTotal, pRecTblCache, &pSort))) { IStreamITEx *pTempDataStrm, *pTempPDBStrm; if (SUCCEEDED(hr = CreateTempStm(&pTempDataStrm)) && SUCCEEDED(hr = CreateTempStm(&pTempPDBStrm))) { cRecsToRead = MAX_TABLE_RECS_INCACHE; CULINT ullCompactedOffset = CULINT(0); if (fNeedFileSort) { BOOL fEndLoop = FALSE; int iCurBlk = -1; while (!fEndLoop && SUCCEEDED(hr)) { if (SUCCEEDED(hr = pSort->ReadNextSortedBlk(&iCurBlk, (LPBYTE)pRecTblCache, &cRecsToRead, &fEndLoop))) { //compact data pointed by read block and update block if (!fEndLoop && SUCCEEDED(hr = CompactData((LPBYTE)pRecTblCache, cRecsToRead, &ullCompactedOffset, pTempDataStrm))) { VerifyData((LPBYTE)pRecTblCache, cRecsToRead, SORT_BY_OFFSET, TRUE); hr = pSort->WriteBlk(iCurBlk, (LPBYTE)pRecTblCache, cRecsToRead); } }//read next file block in sorted sequence }//while } else if (SUCCEEDED(hr)) { //compact data pointed by path entries in cached block hr = CompactData((LPBYTE)pRecTblCache, cTblRecsInCache, &ullCompactedOffset, pTempDataStrm); VerifyData((LPBYTE)pRecTblCache, cTblRecsInCache, SORT_BY_OFFSET, TRUE); } if (SUCCEEDED(hr)) { eSortType = SORT_BY_ENTRYID; if (SUCCEEDED(hr = SortRecTable(eSortType, pRecTblStrm, cTblRecsInCache, cTblRecsTotal, pRecTblCache, &pSort))) { if (SUCCEEDED(hr = UpdatePathDB(pRecTblStrm, cTblRecsInCache, cTblRecsTotal, pRecTblCache, pSort))) { //commit all the updates to the disk and reload m_pPathManager hr = m_pPathManager->FlushToLockBytes(); //get a copy of updated path DB with or without compaction if (SUCCEEDED(hr) && SUCCEEDED(hr = GetPathDB(pTempPDBStrm, iLev == COMPACT_DATA_AND_PATH))) { //putting all pieces together //updated header, empty free list, updated path DB and compacted data hr = CompactFileSystem(pTempPDBStrm, pTempDataStrm); } } } } pTempDataStrm->Release(); pTempPDBStrm->Release(); }//CreateTmpStrm }//SortRecTable }//BuildUpEntryTable }//InitRecTable if (pSort) delete pSort; if (pRecTblStrm) pRecTblStrm->Release(); if (pRecTblCache) delete pRecTblCache; return hr; } HRESULT __stdcall CITFileSystem::Compact(const WCHAR * pwcsName, ECompactionLev iLev) { CSyncWith sw(g_csITFS); HRESULT hr; CImpITFileSystem *pITFS = (CImpITFileSystem *)CImpITFileSystem::FindFileSystem(pwcsName); if(!pITFS) { ILockBytes *pLKB = NULL; if (SUCCEEDED(hr = CFSLockBytes::Open(NULL, pwcsName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pLKB ))) { CITFileSystem *pITFSout = New CITFileSystem(NULL); if (!pITFSout) { return STG_E_INSUFFICIENTMEMORY; } pITFS = &(pITFSout->m_ImpITFileSystem); pITFS->AddRef(); pITFS->AddRef(); //To match reference count trick in InitOpenOnLockBytes pITFSout->AddRef(); hr = pITFS->InitOpenOnLockBytes(pLKB, STGM_READWRITE | STGM_SHARE_EXCLUSIVE); pLKB->Release(); pITFSout->Release(); pITFS->SetCompaction(TRUE); } } else { pITFS->Release(); return E_FAIL; } if (SUCCEEDED(hr)) { hr = pITFS->Compact(iLev); } if (pITFS) { pITFS->SetCompaction(FALSE); pITFS->Release(); pITFS->Release(); } return hr; } /* HRESULT CITFileSystem::CImpITFileSystem::SaveTransformedSpace(IStreamITEx **ppTempStrm, ULARGE_INTEGER *pcbSaved) { HRESULT hr = NO_ERROR; WCHAR awszStreamPath[256]; //Copy tranformed space to a temporary stream wcsCpy(awszStreamPath, pwcsSpaceNameStorage); wcsCat(awszStreamPath, pwcsLZXSpace); wcsCat(awszStreamPath, pwcsSpaceContentSuffix); *pcbSaved = CULINT(0).Uli(); *ppTempStrm = NULL; IStreamITEx *pXStream; if (SUCCEEDED(hr = OpenStream(NULL, awszStreamPath, 0, &pXStream))) { STATSTG statstg; if (SUCCEEDED(hr = pXStream->Stat(&statstg, STATFLAG_NONAME))) { *pcbSaved = statstg.cbSize; if (SUCCEEDED(hr = CreateTempStm(ppTempStrm))) { ULARGE_INTEGER cbRead, cbWritten; if (SUCCEEDED(hr = pXStream->CopyTo(*ppTempStrm, statstg.cbSize, &cbRead, &cbWritten))) { RonM_ASSERT(CULINT(cbRead) == CULINT(cbWritten)); }//CopyTo }//CreateTempStm }//Stat pXStream->Release(); }//OpenStream return hr; } */ HRESULT CITFileSystem::CImpITFileSystem::DumpStream(IStreamITEx *pTempStrm, LPSTR pFileName) { HRESULT hr = NO_ERROR; #if 0 //test code FILE *file = fopen(pFileName, "w" ); if (file != NULL) { RonM_ASSERT(pTempStrm != NULL); HRESULT hr = NO_ERROR; BYTE lpBuf[2048]; if (SUCCEEDED(hr = pTempStrm->Seek(CLINT(0).Li(), STREAM_SEEK_SET, 0))) { ULONG cbToRead = sizeof(lpBuf); ULONG cbWritten; ULONG cbRead = cbToRead; while (SUCCEEDED(hr) && (cbRead == cbToRead)) { if (SUCCEEDED(hr = pTempStrm->Read(lpBuf, cbToRead, &cbRead))) { cbWritten = fwrite(lpBuf, sizeof(BYTE), cbRead, file); RonM_ASSERT(cbRead == cbWritten); if (cbRead != cbWritten) hr = E_FAIL; }//ReadAt }//while }//seek fclose(file); } else hr = E_FAIL; #endif //end test code return hr; } HRESULT CITFileSystem::CImpITFileSystem::CopyStream(IStreamITEx *pTempStrm, CULINT *pullCompactedOffset) { RonM_ASSERT(pTempStrm != NULL); HRESULT hr = NO_ERROR; BYTE lpBuf[2048]; if (SUCCEEDED(hr = pTempStrm->Seek(CLINT(0).Li(), STREAM_SEEK_SET, 0))) { ULONG cbToRead = sizeof(lpBuf); ULONG cbWritten; ULONG cbRead = cbToRead; while (SUCCEEDED(hr) && (cbRead == cbToRead)) { if (SUCCEEDED(hr = pTempStrm->Read(lpBuf, cbToRead, &cbRead))) { if (SUCCEEDED(hr = m_pLKBMedium->WriteAt((*pullCompactedOffset).Uli(), lpBuf, cbRead, &cbWritten))) { RonM_ASSERT(cbRead == cbWritten); *pullCompactedOffset += cbWritten; }//WriteAt }//ReadAt }//while }//seek return hr; } HRESULT CITFileSystem::CImpITFileSystem:: CompactFileSystem(IStreamITEx *pTempPDBStrm, IStreamITEx *pTempDataStrm) { STATSTG statstg1, statstg2; HRESULT hr; if (SUCCEEDED(hr = pTempPDBStrm->Stat(&statstg1, STATFLAG_NONAME)) && SUCCEEDED(hr = pTempDataStrm->Stat(&statstg2, STATFLAG_NONAME))) { ULONG cbRead = 0; ULARGE_INTEGER cbFreeListSize; m_pFreeListManager->SetFreeListEmpty(); m_pFreeListManager->GetSizeMax(&cbFreeListSize); //upgrade the version no. m_itfsh.uFormatVersion = CurrentFileFormatVersion; m_itfsh.cbHeaderSize = sizeof(m_itfsh); m_itfsh.offFreeListData = m_itfsh.cbHeaderSize; m_itfsh.cbFreeListData = cbFreeListSize; m_itfsh.offPathMgrData = m_itfsh.offFreeListData + m_itfsh.cbFreeListData; m_itfsh.cbPathMgrData = CULINT(statstg1.cbSize); m_itfsh.offPathMgrOrigin = m_itfsh.offPathMgrData + m_itfsh.cbPathMgrData; if (SUCCEEDED(hr = m_pLKBMedium->WriteAt(CULINT(0).Uli(), &m_itfsh, sizeof(m_itfsh), &cbRead))) { //write free list ULARGE_INTEGER cbSpaceSize = (m_itfsh.cbHeaderSize + CULINT(cbFreeListSize) + CULINT(statstg1.cbSize) + CULINT(statstg2.cbSize)).Uli(); m_pFreeListManager->SetSpaceSize(cbSpaceSize); hr = m_pFreeListManager->RecordFreeList(); RonM_ASSERT(hr == S_OK); if (SUCCEEDED(hr)) { m_pPathManager->ForceClearDirty(); #if 0 //test code hr = DumpStream(pTempPDBStrm, "c:\\test.PDB"); hr = DumpStream(pTempDataStrm, "c:\\test.DAT"); #endif//end test code CULINT ullOffset = m_itfsh.offPathMgrData; if (SUCCEEDED(hr = CopyStream(pTempPDBStrm, &ullOffset))) { RonM_ASSERT((ullOffset - m_itfsh.offPathMgrData) == CULINT(statstg1.cbSize)); ullOffset = m_itfsh.offPathMgrOrigin; if (SUCCEEDED(hr = CopyStream(pTempDataStrm, &ullOffset))) { RonM_ASSERT((ullOffset - m_itfsh.offPathMgrOrigin) == CULINT(statstg2.cbSize)); hr = m_pLKBMedium->SetSize(cbSpaceSize); } }//copying temp path DB stream }//copying free list }//copying header }//getting size of path database return hr; } HRESULT CITFileSystem::CImpITFileSystem::GetPathDB(IStreamITEx *pTempPDBStrm, BOOL fCompact) { return m_pPathManager->GetPathDB(pTempPDBStrm, fCompact); } HRESULT CITFileSystem::CImpITFileSystem::ForceClearDirty() { return m_pPathManager->ForceClearDirty(); } HRESULT CITFileSystem::CImpITFileSystem::CompactData(LPBYTE pRecTableCache, ULONG cEntries, CULINT *pullCompactedOffset, IStreamITEx *pTempDataStrm) { SEntry *pe; BYTE lpBuf[4096]; HRESULT hr = NO_ERROR; ULONG cEntriesCompacted = 0, iEntry = 0; CULINT cbToReadTotal; ULONG ceFollowing; ULARGE_INTEGER uli; SEntry *pe1, *pe2; while (iEntry < cEntries) { //Finding all the consequtive compacted entries BOOL fDone = FALSE; ceFollowing = 0; pe1 = (SEntry *)(pRecTableCache + iEntry * sizeof(SEntry)); cbToReadTotal = pe1->ullcbData; while (!fDone) { pe2 = (SEntry *)(pRecTableCache + (iEntry + ceFollowing + 1) * sizeof(SEntry)); if (pe2->ullcbOffset == (pe1->ullcbOffset + pe1->ullcbData)) { ceFollowing++; cbToReadTotal += pe2->ullcbData; pe1 = pe2; } else fDone = TRUE; } pe = (SEntry *)(pRecTableCache + iEntry * sizeof(SEntry)); uli = (pe->ullcbOffset + m_itfsh.offPathMgrOrigin).Uli(); RonM_ASSERT(*pullCompactedOffset <= CULINT(uli)); ULONG cbToRead; ULONG cbReadTotal = 0; ULONG cbWritten; ULONG cbRead; while ((cbReadTotal != cbToReadTotal) && SUCCEEDED(hr)) { cbToRead = (long)(cbToReadTotal - CULINT(cbReadTotal)).Ull(); if (cbToRead >= sizeof(lpBuf)) cbToRead = sizeof(lpBuf); if (SUCCEEDED(hr = m_pLKBMedium->ReadAt(uli, lpBuf, cbToRead, &cbRead))) { RonM_ASSERT(cbRead == cbToRead); if (SUCCEEDED(hr = pTempDataStrm->Write(lpBuf, cbRead, &cbWritten))) { RonM_ASSERT(cbRead == cbWritten); cbReadTotal += cbToRead; uli = (CULINT(uli) + cbWritten).Uli(); }//WriteAt }//ReadAt }//while RonM_ASSERT(cbReadTotal == cbToReadTotal); pe->ullcbOffset = *pullCompactedOffset; memCpy(pRecTableCache + iEntry * sizeof(SEntry), pe, sizeof(SEntry)); *pullCompactedOffset += cbToReadTotal; SEntry *pePrev = pe; //modify the path info for following compacted entries for (int i = iEntry + 1; i < (iEntry + ceFollowing + 1); i++) { pe = (SEntry *)(pRecTableCache + i * sizeof(SEntry)); pe->ullcbOffset = pePrev->ullcbOffset + pePrev->ullcbData; memCpy(pRecTableCache + i * sizeof(SEntry), pe, sizeof(SEntry)); pePrev = pe; } iEntry += (ceFollowing + 1); }//while return hr; } void CITFileSystem::CImpITFileSystem::VerifyData(LPBYTE pRecTableCache, ULONG cEntries, ESortField eSType, BOOL fReset) { #ifdef _DEBUG static int cEntry; static CULINT ullLastEntryOffset = CULINT(0); static UINT ulLastEntryID = 0; if (fReset) { cEntry = 0; ullLastEntryOffset = CULINT(0); ulLastEntryID = 0; } //printf("Verifying original entries from file After Sort\n"); for (int iEntry = 0; iEntry < cEntries; iEntry++, cEntry++) { SEntry *pe = (SEntry *)(pRecTableCache + iEntry * sizeof(SEntry)); //printf("Entry[%d] offset= %d, entryid = %d, size = %d\n", // cEntry, (int)pe->ullcbOffset.Ull(), (int)pe->ulEntryID, (int)pe->ullcbData.Ull()); //RonM_ASSERT(cEntry == pe->ulEntryID); if (eSType == SORT_BY_OFFSET) RonM_ASSERT(pe->ullcbOffset >= ullLastEntryOffset); else RonM_ASSERT(pe->ulEntryID >= ulLastEntryID); ullLastEntryOffset = pe->ullcbOffset; ulLastEntryID = pe->ulEntryID; } //printf("** End Verifying original entries **\n"); #endif } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::GetNextEntryInSeq (ULONG celt, PathInfo *rgelt, ULONG *pceltFetched) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CITFileSystem::CImpITFileSystem::CEnumFSItems::CImpIEnumSTATSTG::GetFirstEntryInSeq (PathInfo *rgelt) { return E_NOTIMPL; }