3946 lines
104 KiB
C++
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;
|
|
}
|