windows-nt/Source/XPSP1/NT/enduser/stuff/itss/itsfs.cpp
2020-09-26 16:20:57 +08:00

3946 lines
104 KiB
C++

// ITSFS.CPP -- Implementation for the class CITFileSystem
#include "StdAfx.h"
//#include <stdio.h>
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;
}