You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2675 lines
72 KiB
2675 lines
72 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1992.
|
|
//
|
|
// File: mstream.cxx
|
|
//
|
|
// Contents: Mstream operations
|
|
//
|
|
// Classes: None. (defined in mstream.hxx)
|
|
//
|
|
// History: 18-Jul-91 Philipla Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#include "msfhead.cxx"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <dirfunc.hxx>
|
|
#include <sstream.hxx>
|
|
#include <difat.hxx>
|
|
#include <time.h>
|
|
#include <mread.hxx>
|
|
#include <docfilep.hxx>
|
|
#include <df32.hxx>
|
|
#include <smalloc.hxx>
|
|
#include <filelkb.hxx>
|
|
|
|
|
|
#if DBG == 1
|
|
DECLARE_INFOLEVEL(msf)
|
|
#endif
|
|
|
|
#define MINPAGES 6
|
|
#define MAXPAGES 128
|
|
|
|
#define MINPAGESSCRATCH 2
|
|
#define MAXPAGESSCRATCH 16
|
|
|
|
//#define SECURETEST
|
|
|
|
const WCHAR wcsContents[] = L"CONTENTS"; //Name of contents stream for
|
|
// conversion
|
|
const WCHAR wcsRootEntry[] = L"Root Entry"; //Name of root directory
|
|
// entry
|
|
|
|
SCODE ILBFlush(ILockBytes *pilb, BOOL fFlushCache);
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetBuffer, public
|
|
//
|
|
// Synopsis: Gets a chunk of memory to use as a buffer
|
|
//
|
|
// Arguments: [cbMin] - Minimum size for buffer
|
|
// [cbMax] - Maximum size for buffer
|
|
// [ppb] - Buffer pointer return
|
|
// [pcbActual] - Actual buffer size return
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [ppb]
|
|
// [pcbActual]
|
|
//
|
|
// Algorithm: Attempt to dynamically allocate [cbMax] bytes
|
|
// If that fails, halve allocation size and retry
|
|
// If allocation size falls below [cbMin], fail
|
|
//
|
|
// History: 04-Mar-93 DrewB Created
|
|
//
|
|
// Notes: Buffer should be released with FreeBuffer
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE GetBuffer(ULONG cbMin, ULONG cbMax, BYTE **ppb, ULONG *pcbActual)
|
|
{
|
|
ULONG cbSize;
|
|
BYTE *pb;
|
|
|
|
msfDebugOut((DEB_ITRACE, "In GetBuffer(%u, %u, %p, %p)\n",
|
|
cbMin, cbMax, ppb, pcbActual));
|
|
msfAssert(cbMin > 0);
|
|
msfAssert(cbMax >= cbMin);
|
|
msfAssert(ppb != NULL);
|
|
msfAssert(pcbActual != NULL);
|
|
|
|
cbSize = cbMax;
|
|
for (;;)
|
|
{
|
|
pb = (BYTE *) DfMemAlloc(cbSize);
|
|
if (pb == NULL)
|
|
{
|
|
cbSize >>= 1;
|
|
if (cbSize < cbMin)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*pcbActual = cbSize;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*ppb = pb;
|
|
|
|
msfDebugOut((DEB_ITRACE, "Out GetBuffer => %p, %u\n", *ppb, *pcbActual));
|
|
return pb == NULL ? STG_E_INSUFFICIENTMEMORY : S_OK;
|
|
}
|
|
|
|
// Define the safe buffer size
|
|
//#define SCRATCHBUFFERSIZE SCRATCHSECTORSIZE
|
|
BYTE s_bufSafe[SCRATCHBUFFERSIZE];
|
|
LONG s_bufSafeRef = 0;
|
|
|
|
|
|
|
|
// Critical Section will be initiqalized in the shared memory allocator
|
|
// constructor and deleted in the SmAllocator destructor
|
|
CRITICAL_SECTION g_csScratchBuffer;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetSafeBuffer, public
|
|
//
|
|
// Synopsis: Gets a buffer by first trying GetBuffer and if that fails,
|
|
// returning a pointer to statically allocated storage.
|
|
// Guaranteed to return a pointer to some storage.
|
|
//
|
|
// Arguments: [cbMin] - Minimum buffer size
|
|
// [cbMax] - Maximum buffer size
|
|
// [ppb] - Buffer pointer return
|
|
// [pcbActual] - Actual buffer size return
|
|
//
|
|
// Modifies: [ppb]
|
|
// [pcbActual]
|
|
//
|
|
// History: 04-Mar-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void GetSafeBuffer(ULONG cbMin, ULONG cbMax, BYTE **ppb, ULONG *pcbActual)
|
|
{
|
|
msfAssert(cbMin > 0);
|
|
msfAssert(cbMin <= SCRATCHBUFFERSIZE &&
|
|
aMsg("Minimum too large for GetSafeBuffer"));
|
|
msfAssert(cbMax >= cbMin);
|
|
msfAssert(ppb != NULL);
|
|
|
|
// We want to minimize contention for the
|
|
// static buffer so we always try dynamic allocation, regardless
|
|
// of the size
|
|
if (
|
|
FAILED(GetBuffer(cbMin, cbMax, ppb, pcbActual)))
|
|
{
|
|
|
|
EnterCriticalSection(&g_csScratchBuffer);
|
|
msfAssert(s_bufSafeRef == 0 &&
|
|
aMsg("Tried to use scratch buffer twice"));
|
|
s_bufSafeRef = 1;
|
|
*ppb = s_bufSafe;
|
|
*pcbActual = min(cbMax, SCRATCHBUFFERSIZE);
|
|
}
|
|
msfAssert(*ppb != NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeBuffer, public
|
|
//
|
|
// Synopsis: Releases a buffer allocated by GetBuffer or GetSafeBuffer
|
|
//
|
|
// Arguments: [pb] - Buffer
|
|
//
|
|
// History: 04-Mar-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void FreeBuffer(BYTE *pb)
|
|
{
|
|
if (pb == s_bufSafe)
|
|
{
|
|
msfAssert((s_bufSafeRef == 1) && aMsg("Bad safe buffer ref count"));
|
|
s_bufSafeRef = 0;
|
|
LeaveCriticalSection(&g_csScratchBuffer);
|
|
}
|
|
else
|
|
DfMemFree(pb);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::CMStream, public
|
|
//
|
|
// Synopsis: CMStream constructor
|
|
//
|
|
// Arguments: [pplstParent] -- Pointer to ILockBytes pointer of parent
|
|
// [plGen] -- Pointer to LUID Generator to use.
|
|
// Note: May be NULL, in which case a new
|
|
// [uSectorShift] -- Sector shift for this MStream
|
|
//
|
|
// History: 18-Jul-91 PhilipLa Created.
|
|
// 05-Sep-95 MikeHill Initialize '_fMaintainFLBModifyTimestamp'.
|
|
// 26-Apr-99 RogerCh Removed _fMaintainFLBModifyTimestamp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
CMStream::CMStream(
|
|
IMalloc *pMalloc,
|
|
ILockBytes **pplstParent,
|
|
BOOL fIsScratch,
|
|
#if defined(USE_NOSCRATCH) || defined(USE_NOSNAPSHOT)
|
|
DFLAGS df,
|
|
#endif
|
|
USHORT uSectorShift)
|
|
:_uSectorShift(uSectorShift),
|
|
_uSectorSize(1 << uSectorShift),
|
|
_uSectorMask(_uSectorSize - 1),
|
|
_pplstParent(P_TO_BP(CBasedILockBytesPtrPtr, pplstParent)),
|
|
_fIsScratch(fIsScratch),
|
|
_fIsNoScratch(P_NOSCRATCH(df)),
|
|
_pmsScratch(NULL),
|
|
_fIsNoSnapshot(P_NOSNAPSHOT(df)),
|
|
_hdr(uSectorShift),
|
|
_fat(SIDFAT),
|
|
_fatMini(SIDMINIFAT),
|
|
_pMalloc(pMalloc)
|
|
{
|
|
_pmsShadow = NULL;
|
|
_pCopySectBuf = NULL;
|
|
#if DBG == 1
|
|
_uBufferRef = 0;
|
|
#endif
|
|
_fIsShadow = FALSE;
|
|
|
|
_ulParentSize = 0;
|
|
|
|
_pdsministream = NULL;
|
|
_pmpt = NULL;
|
|
_fBlockWrite = _fTruncate = _fBlockHeader = _fNewConvert = FALSE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::CMStream, public
|
|
//
|
|
// Synopsis: CMStream copy constructor
|
|
//
|
|
// Arguments: [ms] -- MStream to copy
|
|
//
|
|
// History: 04-Nov-92 PhilipLa Created.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
CMStream::CMStream(const CMStream *pms)
|
|
:_uSectorShift(pms->_uSectorShift),
|
|
_uSectorSize(pms->_uSectorSize),
|
|
_uSectorMask(pms->_uSectorMask),
|
|
_pplstParent(pms->_pplstParent),
|
|
_fIsScratch(pms->_fIsScratch),
|
|
_hdr(*(CMSFHeader *)&pms->_hdr),
|
|
_dir(*(CDirectory *)pms->GetDir()),
|
|
_fat(pms->GetFat()),
|
|
_fatMini(pms->GetMiniFat()),
|
|
_fatDif(pms->GetDIFat()),
|
|
_pdsministream(pms->_pdsministream),
|
|
_pmpt(pms->_pmpt),
|
|
_fBlockWrite(pms->_fBlockWrite),
|
|
_fTruncate(pms->_fTruncate),
|
|
_fBlockHeader(pms->_fBlockHeader),
|
|
_fNewConvert(pms->_fNewConvert),
|
|
_pmsShadow(NULL),
|
|
_fIsShadow(TRUE),
|
|
_pMalloc(pms->_pMalloc)
|
|
{
|
|
_pCopySectBuf = pms->_pCopySectBuf;
|
|
#if DBG == 1
|
|
_uBufferRef = pms->_uBufferRef;
|
|
#endif
|
|
_dir.SetParent(this);
|
|
_fat.SetParent(this);
|
|
_fatMini.SetParent(this);
|
|
_fatDif.SetParent(this);
|
|
|
|
_ulParentSize = 0;
|
|
_pmpt->AddRef();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::InitCommon, private
|
|
//
|
|
// Synopsis: Common code for initialization routines.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm: *Finish This*
|
|
//
|
|
// History: 20-May-92 PhilipLa Created.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::InitCommon(VOID)
|
|
{
|
|
msfDebugOut((DEB_ITRACE,"In CMStream InitCommon()\n"));
|
|
SCODE sc = S_OK;
|
|
|
|
#ifdef SECURE_BUFFER
|
|
memset(s_bufSecure, SECURECHAR, MINISTREAMSIZE);
|
|
#endif
|
|
|
|
CMSFPageTable *pmpt;
|
|
msfMem(pmpt = new (GetMalloc()) CMSFPageTable(
|
|
this,
|
|
(_fIsScratch) ? MINPAGESSCRATCH: MINPAGES,
|
|
(_fIsScratch) ? MAXPAGESSCRATCH: MAXPAGES));
|
|
_pmpt = P_TO_BP(CBasedMSFPageTablePtr, pmpt);
|
|
|
|
msfChk(pmpt->Init());
|
|
if (!_fIsScratch)
|
|
{
|
|
CMStream *pms;
|
|
msfMem(pms = (CMStream *) new (GetMalloc()) CMStream(this));
|
|
_pmsShadow = P_TO_BP(CBasedMStreamPtr, pms);
|
|
}
|
|
|
|
_stmcDir.Init(this, SIDDIR, NULL);
|
|
_stmcMiniFat.Init(this, SIDMINIFAT, NULL);
|
|
|
|
msfDebugOut((DEB_ITRACE,"Leaving CMStream InitCommon()\n"));
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::InitCopy, public
|
|
//
|
|
// Synopsis: Copy the structures from one multistream to yourself
|
|
//
|
|
// Arguments: [pms] -- Pointer to multistream to copy.
|
|
//
|
|
// History: 04-Dec-92 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void CMStream::InitCopy(CMStream *pms)
|
|
{
|
|
_stmcDir.Init(this, SIDDIR, NULL);
|
|
_stmcMiniFat.Init(this, SIDMINIFAT, NULL);
|
|
|
|
_fat.InitCopy(pms->GetFat());
|
|
_fatMini.InitCopy(pms->GetMiniFat());
|
|
_fatDif.InitCopy(pms->GetDIFat());
|
|
_dir.InitCopy(pms->GetDir());
|
|
|
|
_dir.SetParent(this);
|
|
_fat.SetParent(this);
|
|
_fatMini.SetParent(this);
|
|
_fatDif.SetParent(this);
|
|
|
|
memcpy(&_hdr, pms->GetHeader(), sizeof(CMSFHeader));
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Empty, public
|
|
//
|
|
// Synopsis: Empty all of the control structures of this CMStream
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: void.
|
|
//
|
|
// History: 04-Dec-92 PhilipLa Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void CMStream::Empty(void)
|
|
{
|
|
_fat.Empty();
|
|
_fatMini.Empty();
|
|
_fatDif.Empty();
|
|
_dir.Empty();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::~CMStream, public
|
|
//
|
|
// Synopsis: CMStream destructor
|
|
//
|
|
// History: 18-Jul-91 PhilipLa Created.
|
|
// 20-Jul-95 SusiA Modified to eliminate mutex in allocator
|
|
// Caller must already have the mutex.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
CMStream::~CMStream()
|
|
{
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream destructor\n"));
|
|
|
|
|
|
|
|
if (_pmsShadow != NULL)
|
|
{
|
|
_pmsShadow->~CMStream();
|
|
_pmsShadow->deleteNoMutex (BP_TO_P(CMStream *, _pmsShadow));
|
|
}
|
|
|
|
#if DBG == 1
|
|
msfAssert((_uBufferRef == 0) &&
|
|
aMsg("CopySect buffer left with positive refcount."));
|
|
#endif
|
|
g_smAllocator.FreeNoMutex(BP_TO_P(BYTE *, _pCopySectBuf));
|
|
|
|
|
|
if ((!_fIsShadow) && (_pdsministream != NULL))
|
|
{
|
|
_pdsministream->Release();
|
|
}
|
|
|
|
if (_pmpt != NULL)
|
|
{
|
|
_pmpt->Release();
|
|
}
|
|
|
|
msfDebugOut((DEB_ITRACE,"Leaving CMStream destructor\n"));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Init, public
|
|
//
|
|
// Synposis: Set up an mstream instance from an existing stream
|
|
//
|
|
// Effects: Modifies Fat and Directory
|
|
//
|
|
// Arguments: void.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
// Error of Fat or Dir setup otherwise.
|
|
//
|
|
// History: 18-Jul-91 PhilipLa Created.
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::Init(VOID)
|
|
{
|
|
ULONG ulTemp;
|
|
SCODE sc;
|
|
ULARGE_INTEGER ulOffset;
|
|
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::Init()\n"));
|
|
|
|
msfAssert(!_fIsScratch &&
|
|
aMsg("Called Init() on scratch multistream."));
|
|
|
|
ULONG ulSectorSize = HEADERSIZE;
|
|
IFileLockBytes *pfl;
|
|
if (SUCCEEDED((*_pplstParent)->QueryInterface(IID_IFileLockBytes,
|
|
(void**) &pfl)))
|
|
{
|
|
ulSectorSize = pfl->GetSectorSize();
|
|
pfl->Release();
|
|
}
|
|
|
|
ULISet32(ulOffset, 0);
|
|
|
|
if (ulSectorSize == sizeof(CMSFHeaderData))
|
|
{
|
|
sc = (*_pplstParent)->ReadAt(ulOffset, (BYTE *)_hdr.GetData(),
|
|
sizeof(CMSFHeaderData), &ulTemp);
|
|
}
|
|
else
|
|
{
|
|
void *pvBuf = TaskMemAlloc(ulSectorSize);
|
|
msfMem(pvBuf);
|
|
sc = (*_pplstParent)->ReadAt(ulOffset, pvBuf, ulSectorSize, &ulTemp);
|
|
if (SUCCEEDED(sc) && ulTemp >= sizeof(CMSFHeaderData))
|
|
memcpy (_hdr.GetData(), pvBuf, sizeof(CMSFHeaderData));
|
|
TaskMemFree (pvBuf);
|
|
}
|
|
|
|
if (sc == E_PENDING)
|
|
{
|
|
sc = STG_E_PENDINGCONTROL;
|
|
}
|
|
msfChk(sc);
|
|
|
|
//We need to mark the header as not dirty, since the constructor
|
|
// defaults it to the dirty state. This needs to happen before
|
|
// any possible failures, otherwise we can end up writing a
|
|
// brand new header over an existing file.
|
|
_hdr.ResetDirty();
|
|
|
|
_uSectorShift = _hdr.GetSectorShift();
|
|
_uSectorSize = 1 << _uSectorShift;
|
|
_uSectorMask = _uSectorSize - 1;
|
|
|
|
if (ulTemp != ulSectorSize)
|
|
{
|
|
msfErr(Err,STG_E_INVALIDHEADER);
|
|
}
|
|
|
|
msfChk(_hdr.Validate());
|
|
|
|
msfChk(InitCommon());
|
|
|
|
msfChk(_fatDif.Init(this, _hdr.GetDifLength()));
|
|
msfChk(_fat.Init(this, _hdr.GetFatLength(), 0));
|
|
|
|
FSINDEX fsiLen;
|
|
if (_uSectorShift > SECTORSHIFT512)
|
|
fsiLen = _hdr.GetDirLength ();
|
|
else
|
|
msfChk(_fat.GetLength(_hdr.GetDirStart(), &fsiLen));
|
|
msfChk(_dir.Init(this, fsiLen));
|
|
|
|
msfChk(_fatMini.Init(this, _hdr.GetMiniFatLength(), 0));
|
|
|
|
BYTE *pbBuf;
|
|
|
|
msfMem(pbBuf = (BYTE *) GetMalloc()->Alloc(GetSectorSize()));
|
|
_pCopySectBuf = P_TO_BP(CBasedBytePtr, pbBuf);
|
|
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulSize;
|
|
#else
|
|
ULONG ulSize;
|
|
#endif
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulSize));
|
|
CDirectStream *pdsTemp;
|
|
|
|
msfMem(pdsTemp = new(GetMalloc()) CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream = P_TO_BP(CBasedDirectStreamPtr, pdsTemp);
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulSize);
|
|
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::Init()\n"));
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::InitNew, public
|
|
//
|
|
// Synposis: Set up a brand new mstream instance
|
|
//
|
|
// Effects: Modifies FAT and Directory
|
|
//
|
|
// Arguments: [fDelay] -- If TRUE, then the parent LStream
|
|
// will be truncated at the time of first
|
|
// entrance to COW, and no writes to the
|
|
// LStream will happen before then.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// History: 18-Jul-91 PhilipLa Created.
|
|
// 12-Jun-92 PhilipLa Added fDelay.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::InitNew(BOOL fDelay, ULARGE_INTEGER uliSize)
|
|
{
|
|
SCODE sc;
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::InitNew()\n"));
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ULONGLONG ulParentSize = 0;
|
|
#else
|
|
ULONG ulParentSize = 0;
|
|
#endif
|
|
|
|
msfChk(InitCommon());
|
|
|
|
if (!_fIsScratch)
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
ulParentSize = uliSize.QuadPart;
|
|
#else
|
|
msfAssert (ULIGetHigh(uliSize) == 0);
|
|
ulParentSize = ULIGetLow(uliSize);
|
|
#endif
|
|
|
|
if (!fDelay && ulParentSize > 0)
|
|
{
|
|
ULARGE_INTEGER ulTmp;
|
|
|
|
ULISet32(ulTmp, 0);
|
|
(*_pplstParent)->SetSize(ulTmp);
|
|
}
|
|
}
|
|
|
|
_fBlockWrite = (ulParentSize == 0) ? FALSE : fDelay;
|
|
|
|
msfChk(_fatDif.InitNew(this));
|
|
msfChk(_fat.InitNew(this));
|
|
|
|
if (!_fIsScratch || _fIsNoScratch)
|
|
{
|
|
msfChk(_fatMini.InitNew(this));
|
|
}
|
|
|
|
if (!_fIsScratch)
|
|
{
|
|
|
|
msfChk(_dir.InitNew(this));
|
|
|
|
BYTE *pbBuf;
|
|
|
|
msfMem(pbBuf = (BYTE *) GetMalloc()->Alloc(GetSectorSize()));
|
|
_pCopySectBuf = P_TO_BP(CBasedBytePtr, pbBuf);
|
|
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulSize;
|
|
#else
|
|
ULONG ulSize;
|
|
#endif
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulSize));
|
|
|
|
CDirectStream *pdsTemp;
|
|
|
|
msfMem(pdsTemp = new(GetMalloc()) CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream = P_TO_BP(CBasedDirectStreamPtr, pdsTemp);
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulSize);
|
|
}
|
|
|
|
//If we have a zero length original file, this will create an
|
|
// empty Docfile on the disk. If the original file was
|
|
// not zero length, then the Flush operations will be skipped
|
|
// by _fBlockWrite and the file will be unmodified.
|
|
if (!_fBlockWrite)
|
|
{
|
|
msfChk(Flush(0));
|
|
|
|
}
|
|
|
|
_fTruncate = (ulParentSize != 0);
|
|
_fBlockWrite = fDelay;
|
|
|
|
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::InitNew()\n"));
|
|
return S_OK;
|
|
|
|
Err:
|
|
Empty();
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::ConvertILB, private
|
|
//
|
|
// Synopsis: Copy the first sector of the underlying ILockBytes
|
|
// out to the end.
|
|
//
|
|
// Arguments: [sectMax] -- Total number of sectors in the ILockBytes
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 03-Feb-93 PhilipLa Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::ConvertILB(SECT sectMax)
|
|
{
|
|
SCODE sc;
|
|
BYTE *pb;
|
|
ULONG cbNull;
|
|
|
|
GetSafeBuffer(GetSectorSize(), GetSectorSize(), &pb, &cbNull);
|
|
|
|
ULONG ulTemp;
|
|
|
|
ULARGE_INTEGER ulTmp;
|
|
ULISet32(ulTmp, 0);
|
|
|
|
msfHChk((*_pplstParent)->ReadAt(ulTmp, pb, GetSectorSize(), &ulTemp));
|
|
|
|
ULARGE_INTEGER ulNewPos;
|
|
#ifdef LARGE_DOCFILE
|
|
ulNewPos.QuadPart = (ULONGLONG)(sectMax) << GetSectorShift();
|
|
#else
|
|
ULISet32(ulNewPos, sectMax << GetSectorShift());
|
|
#endif
|
|
|
|
msfDebugOut((DEB_ITRACE,"Copying first sector out to %lu\n",
|
|
ULIGetLow(ulNewPos)));
|
|
|
|
msfHChk((*_pplstParent)->WriteAt(
|
|
ulNewPos,
|
|
pb,
|
|
GetSectorSize(),
|
|
&ulTemp));
|
|
|
|
Err:
|
|
FreeBuffer(pb);
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::InitConvert, public
|
|
//
|
|
// Synopsis: Init function used in conversion of files to multi
|
|
// streams.
|
|
//
|
|
// Arguments: [fDelayConvert] -- If true, the actual file is not
|
|
// touched until a BeginCopyOnWrite()
|
|
//
|
|
// Returns: S_OK if everything completed OK.
|
|
//
|
|
// Algorithm: *Finish This*
|
|
//
|
|
// History: 28-May-92 Philipla Created.
|
|
//
|
|
// Notes: We are allowed to fail here in low memory
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::InitConvert(BOOL fDelayConvert)
|
|
{
|
|
SCODE sc;
|
|
SECT sectMax;
|
|
CDfName const dfnContents(wcsContents);
|
|
|
|
msfAssert(!_fIsScratch &&
|
|
aMsg("Called InitConvert on scratch multistream"));
|
|
|
|
_fBlockWrite = fDelayConvert;
|
|
|
|
msfAssert(!_fBlockWrite &&
|
|
aMsg("Delayed conversion not supported in this release."));
|
|
|
|
msfChk(InitCommon());
|
|
|
|
STATSTG stat;
|
|
msfChk((*_pplstParent)->Stat(&stat, STATFLAG_NONAME));
|
|
|
|
msfDebugOut((DEB_ITRACE,"Size is: %lu\n",ULIGetLow(stat.cbSize)));
|
|
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
sectMax = (SECT) ((stat.cbSize.QuadPart + GetSectorSize() - 1) >>
|
|
GetSectorShift());
|
|
#else
|
|
sectMax = (ULIGetLow(stat.cbSize) + GetSectorSize() - 1) >>
|
|
GetSectorShift();
|
|
#endif
|
|
|
|
SECT sectMaxMini = 0;
|
|
BOOL fIsMini;
|
|
fIsMini = FALSE;
|
|
|
|
//If the CONTENTS stream will be in the Minifat, compute
|
|
// the number of Minifat sectors needed.
|
|
#ifdef LARGE_DOCFILE
|
|
if (stat.cbSize.QuadPart < MINISTREAMSIZE)
|
|
#else
|
|
if (ULIGetLow(stat.cbSize) < MINISTREAMSIZE)
|
|
#endif
|
|
{
|
|
sectMaxMini = (ULIGetLow(stat.cbSize) + MINISECTORSIZE - 1) >>
|
|
MINISECTORSHIFT;
|
|
fIsMini = TRUE;
|
|
}
|
|
|
|
BYTE *pbBuf;
|
|
|
|
msfMem(pbBuf = (BYTE *) GetMalloc()->Alloc(GetSectorSize()));
|
|
_pCopySectBuf = P_TO_BP(CBasedBytePtr, pbBuf);
|
|
|
|
msfChk(_fatDif.InitConvert(this, sectMax));
|
|
msfChk(_fat.InitConvert(this, sectMax));
|
|
msfChk(_dir.InitNew(this));
|
|
msfChk(fIsMini ? _fatMini.InitConvert(this, sectMaxMini)
|
|
: _fatMini.InitNew(this));
|
|
|
|
SID sid;
|
|
|
|
msfChk(CreateEntry(SIDROOT, &dfnContents, STGTY_STREAM, &sid));
|
|
#ifdef LARGE_STREAMS
|
|
msfChk(_dir.SetSize(sid, stat.cbSize.QuadPart));
|
|
#else
|
|
msfChk(_dir.SetSize(sid, ULIGetLow(stat.cbSize)));
|
|
#endif
|
|
|
|
if (!fIsMini)
|
|
msfChk(_dir.SetStart(sid, sectMax - 1));
|
|
else
|
|
{
|
|
msfChk(_dir.SetStart(sid, 0));
|
|
msfChk(_dir.SetStart(SIDMINISTREAM, sectMax - 1));
|
|
#ifdef LARGE_STREAMS
|
|
msfChk(_dir.SetSize(SIDMINISTREAM, stat.cbSize.QuadPart));
|
|
#else
|
|
msfChk(_dir.SetSize(SIDMINISTREAM, ULIGetLow(stat.cbSize)));
|
|
#endif
|
|
}
|
|
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulMiniSize;
|
|
#else
|
|
ULONG ulMiniSize;
|
|
#endif
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulMiniSize));
|
|
|
|
CDirectStream *pdsTemp;
|
|
|
|
msfMem(pdsTemp = new(GetMalloc()) CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream = P_TO_BP(CBasedDirectStreamPtr, pdsTemp);
|
|
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulMiniSize);
|
|
|
|
if (!_fBlockWrite)
|
|
{
|
|
msfChk(ConvertILB(sectMax));
|
|
|
|
msfChk(Flush(0));
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
Err:
|
|
Empty();
|
|
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::FlushHeader, public
|
|
//
|
|
// Synopsis: Flush the header to the LStream.
|
|
//
|
|
// Arguments: [uForce] -- Flag to determine if header should be
|
|
// flushed while in copy on write mode.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
// S_OK if the MStream is in copy on write mode or
|
|
// is Unconverted and the header was not flushed.
|
|
//
|
|
// Algorithm: Write the complete header out to the 0th position of
|
|
// the LStream.
|
|
//
|
|
// History: 11-Dec-91 PhilipLa Created.
|
|
// 18-Feb-92 PhilipLa Added copy on write support.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::FlushHeader(USHORT uForce)
|
|
{
|
|
ULONG ulTemp;
|
|
SCODE sc;
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::FlushHeader()\n"));
|
|
|
|
if (_fIsScratch || _fBlockWrite ||
|
|
((_fBlockHeader) && (!(uForce & HDR_FORCE))))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//If the header isn't dirty, we don't flush it unless forced to.
|
|
if (!(uForce & HDR_FORCE) && !(_hdr.IsDirty()))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
ULARGE_INTEGER ulOffset;
|
|
ULISet32(ulOffset, 0);
|
|
|
|
USHORT usSectorSize = GetSectorSize();
|
|
if (usSectorSize == HEADERSIZE || _fIsScratch)
|
|
{
|
|
sc = (*_pplstParent)->WriteAt(ulOffset, (BYTE *)_hdr.GetData(),
|
|
sizeof(CMSFHeaderData), &ulTemp);
|
|
}
|
|
else
|
|
{
|
|
msfAssert (_pCopySectBuf != NULL);
|
|
memset (_pCopySectBuf, 0, usSectorSize);
|
|
memcpy (_pCopySectBuf, _hdr.GetData(), sizeof(CMSFHeaderData));
|
|
sc = (*_pplstParent)->WriteAt(ulOffset, _pCopySectBuf,
|
|
usSectorSize, &ulTemp);
|
|
}
|
|
|
|
if (sc == E_PENDING)
|
|
{
|
|
sc = STG_E_PENDINGCONTROL;
|
|
}
|
|
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::FlushHeader()\n"));
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
_hdr.ResetDirty();
|
|
}
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::BeginCopyOnWrite, public
|
|
//
|
|
// Synopsis: Switch the multistream into copy on write mode
|
|
//
|
|
// Effects: Creates new in-core copies of the Fat, Directory, and
|
|
// header.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Requires: The multistream cannot already be in copy on write
|
|
// mode.
|
|
//
|
|
// Returns: S_OK if the call completed OK.
|
|
// STG_E_ACCESSDENIED if multistream was already in COW mode
|
|
//
|
|
// Algorithm: Retrieve and store size of parent LStream.
|
|
// If _fUnconverted & _fTruncate, call SetSize(0)
|
|
// on the parent LStream.
|
|
// If _fUnconverted, then flush all control structures.
|
|
// Copy all control structures, and switch in the shadow
|
|
// copies for current use.
|
|
// Return S_OK.
|
|
//
|
|
// History: 18-Feb-92 PhilipLa Created.
|
|
// 09-Jun-92 PhilipLa Added support for fUnconverted
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::BeginCopyOnWrite(DWORD const dwFlags)
|
|
{
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::BeginCopyOnWrite()\n"));
|
|
|
|
SCODE sc;
|
|
|
|
msfAssert(!_fBlockHeader &&
|
|
aMsg("Tried to reenter Copy-on-Write mode."));
|
|
|
|
msfAssert(!_fIsScratch &&
|
|
aMsg("Tried to enter Copy-on-Write mode in scratch."));
|
|
|
|
msfAssert(!_fIsNoScratch &&
|
|
aMsg("Copy-on-Write started for NoScratch."));
|
|
|
|
//_fBlockWrite is true if we have a delayed conversion or
|
|
// truncation.
|
|
if (_fBlockWrite)
|
|
{
|
|
|
|
//In the overwrite case, we don't want to release any
|
|
// disk space, so we skip this step.
|
|
if ((_fTruncate) && !(dwFlags & STGC_OVERWRITE) &&
|
|
(_pmsScratch == NULL))
|
|
{
|
|
ULARGE_INTEGER ulTmp;
|
|
ULISet32(ulTmp, 0);
|
|
msfHChk((*_pplstParent)->SetSize(ulTmp));
|
|
}
|
|
|
|
|
|
if (!(dwFlags & STGC_OVERWRITE))
|
|
{
|
|
_fBlockHeader = TRUE;
|
|
}
|
|
|
|
_fBlockWrite = FALSE;
|
|
msfChk(Flush(0));
|
|
|
|
_fBlockHeader = FALSE;
|
|
_fTruncate = FALSE;
|
|
}
|
|
|
|
STATSTG stat;
|
|
msfHChk((*_pplstParent)->Stat(&stat, STATFLAG_NONAME));
|
|
#ifdef LARGE_DOCFILE
|
|
_ulParentSize = stat.cbSize.QuadPart;
|
|
msfDebugOut((DEB_ITRACE,"Parent size at begin is %Lu\n",_ulParentSize));
|
|
#else
|
|
msfAssert(ULIGetHigh(stat.cbSize) == 0);
|
|
_ulParentSize = ULIGetLow(stat.cbSize);
|
|
|
|
msfDebugOut((DEB_ITRACE,"Parent size at begin is %lu\n",_ulParentSize));
|
|
#endif
|
|
|
|
if (_fIsNoSnapshot)
|
|
{
|
|
SECT sectNoSnapshot;
|
|
#ifdef LARGE_DOCFILE
|
|
sectNoSnapshot = (SECT) ((_ulParentSize - GetSectorSize() +
|
|
#else
|
|
sectNoSnapshot = (SECT) ((_ulParentSize - HEADERSIZE +
|
|
#endif
|
|
GetSectorSize() - 1) / GetSectorSize());
|
|
|
|
_fat.SetNoSnapshot(sectNoSnapshot);
|
|
}
|
|
|
|
//We flush out all of our current dirty pages - after this point,
|
|
// we know that any dirty pages should be remapped before being
|
|
// written out, assuming we aren't in overwrite mode.
|
|
msfChk(Flush(0));
|
|
|
|
if (!(dwFlags & STGC_OVERWRITE))
|
|
{
|
|
SECT sectTemp;
|
|
|
|
if (_pmsScratch == NULL)
|
|
{
|
|
msfChk(_fat.FindMaxSect(§Temp));
|
|
}
|
|
else
|
|
{
|
|
msfChk(_fat.FindLast(§Temp));
|
|
}
|
|
|
|
_pmsShadow->InitCopy(this);
|
|
|
|
_pmsShadow->_pdsministream = NULL;
|
|
|
|
_fat.SetCopyOnWrite(_pmsShadow->GetFat(), sectTemp);
|
|
|
|
_fBlockHeader = TRUE;
|
|
msfChk(_fatDif.RemapSelf());
|
|
|
|
if (_fIsNoSnapshot)
|
|
msfChk(_fat.ResizeNoSnapshot());
|
|
|
|
msfChk(_fatDif.Fixup(BP_TO_P(CMStream *, _pmsShadow)));
|
|
|
|
if (_fIsNoSnapshot)
|
|
_fat.ResetNoSnapshotFree();
|
|
#if DBG == 1
|
|
_fat.CheckFreeCount();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_fat.SetCopyOnWrite(NULL, 0);
|
|
}
|
|
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::BeginCopyOnWrite()\n"));
|
|
|
|
return S_OK;
|
|
|
|
Err:
|
|
_fBlockHeader = FALSE;
|
|
|
|
_pmsShadow->Empty();
|
|
_fat.ResetCopyOnWrite();
|
|
|
|
if (_fIsNoSnapshot)
|
|
_fat.ResetNoSnapshotFree();
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::EndCopyOnWrite, public
|
|
//
|
|
// Synopsis: End copy on write mode, either by committing the current
|
|
// changes (in which case a merge is performed), or by
|
|
// aborting the changes, in which case the persistent form
|
|
// of the multistream should be identical to its form
|
|
// before copy on write mode was entered.
|
|
//
|
|
// Effects: *Finish This*
|
|
//
|
|
// Arguments: [df] -- Flags to determine commit or abort status.
|
|
//
|
|
// Requires: The multistream must be in copy on write mode.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
// STG_E_ACCESSDENIED if MStream was not in COW mode.
|
|
//
|
|
// Algorithm: If aborting, delete all shadow structures,
|
|
// call SetSize() on parent LStream to restore
|
|
// original size, and switch active controls back
|
|
// to originals.
|
|
// If committing, delete all old structures, switch
|
|
// shadows into original position.
|
|
//
|
|
// History: 18-Feb-92 PhilipLa Created.
|
|
// 09-Jun-92 Philipla Added support for fUnconverted
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::EndCopyOnWrite(
|
|
DWORD const dwCommitFlags,
|
|
DFLAGS const df)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::EndCopyOnWrite(%lu)\n",df));
|
|
|
|
BOOL fFlush = FLUSH_CACHE(dwCommitFlags);
|
|
|
|
if (dwCommitFlags & STGC_OVERWRITE)
|
|
{
|
|
if (_pmsScratch != NULL)
|
|
{
|
|
msfChk(_fatDif.Fixup(NULL));
|
|
_fat.ResetCopyOnWrite();
|
|
}
|
|
msfChk(Flush(fFlush));
|
|
}
|
|
else
|
|
{
|
|
msfAssert(_fBlockHeader &&
|
|
aMsg("Tried to exit copy-on-write mode without entering."));
|
|
|
|
ULARGE_INTEGER ulParentSize = {0,0};
|
|
|
|
if (P_ABORT(df))
|
|
{
|
|
msfDebugOut((DEB_ITRACE,"Aborting Copy On Write mode\n"));
|
|
|
|
Empty();
|
|
|
|
InitCopy(BP_TO_P(CMStream *, _pmsShadow));
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ulParentSize.QuadPart = _ulParentSize;
|
|
#else
|
|
ULISetLow(ulParentSize, _ulParentSize);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
SECT sectMax;
|
|
|
|
msfChk(_fatDif.Fixup(BP_TO_P(CMStream *, _pmsShadow)));
|
|
|
|
msfChk(Flush(fFlush));
|
|
|
|
_fat.ResetCopyOnWrite();
|
|
|
|
msfChk(_fat.GetMaxSect(§Max));
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ulParentSize.QuadPart = ConvertSectOffset(sectMax, 0,
|
|
GetSectorShift());
|
|
#else
|
|
ULISetLow(ulParentSize, ConvertSectOffset(sectMax, 0,
|
|
GetSectorShift()));
|
|
#endif
|
|
|
|
msfChk(FlushHeader(HDR_FORCE));
|
|
msfVerify(SUCCEEDED(ILBFlush(*_pplstParent, fFlush)) &&
|
|
aMsg("CMStream::EndCopyOnWrite ILBFLush failed. "
|
|
"Non-fatal, hit Ok."));
|
|
}
|
|
|
|
//
|
|
// Shrink the file if the size got smaller.
|
|
// Don't shrink if we are in NoSnapshot mode, unless the NoSnapshot
|
|
// limit has been set to 0 by Consolidate.
|
|
//We don't ever expect this SetSize to fail, since it
|
|
// should never attempt to enlarge the file.
|
|
if (!_fIsNoSnapshot || 0 == _fat.GetNoSnapshot())
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
if (ulParentSize.QuadPart < _ulParentSize)
|
|
#else
|
|
if (ULIGetLow(ulParentSize) < _ulParentSize)
|
|
#endif
|
|
{
|
|
olHVerSucc((*_pplstParent)->SetSize(ulParentSize));
|
|
}
|
|
}
|
|
|
|
_pmsShadow->Empty();
|
|
_fBlockHeader = FALSE;
|
|
_fNewConvert = FALSE;
|
|
}
|
|
|
|
if (_pmsScratch != NULL)
|
|
{
|
|
//Let the no-scratch fat pick up whatever changed we've made.
|
|
_pmsScratch->InitScratch(this, FALSE);
|
|
}
|
|
|
|
if (!_fIsNoSnapshot)
|
|
{
|
|
//In no-snapshot mode, we can't let the file shrink, since
|
|
//we might blow away someone else's state.
|
|
_ulParentSize = 0;
|
|
}
|
|
|
|
|
|
{
|
|
SCODE sc2 = SetSize();
|
|
msfVerify((SUCCEEDED(sc2) || (sc2 == E_PENDING)) &&
|
|
aMsg("SetSize after copy-on-write failed."));
|
|
}
|
|
|
|
if (_fIsNoSnapshot)
|
|
{
|
|
_ulParentSize = 0;
|
|
_fat.SetNoSnapshot(0);
|
|
}
|
|
|
|
#if DBG == 1
|
|
STATSTG stat;
|
|
msfHChk((*_pplstParent)->Stat(&stat, STATFLAG_NONAME));
|
|
#ifndef LARGE_DOCFILE
|
|
msfAssert(ULIGetHigh(stat.cbSize) == 0);
|
|
#endif
|
|
msfDebugOut((DEB_ITRACE, "Parent size at end is %lu\n",
|
|
ULIGetLow(stat.cbSize)));
|
|
#endif
|
|
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::EndCopyOnWrite()\n"));
|
|
Err:
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Consolidate, public
|
|
//
|
|
// Synopsis: Fill in the holes of a file by moving blocks toward the front.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Algorithm: 1) Find a limit that all blocks should be moved below.
|
|
// The limit includes all the data, a new FAT, DIFAT and
|
|
// DirStream, and doesn't disturb the old fat/dif/dir blocks.
|
|
// 2) Move new fat/dif/dir sectors down. We do this with the
|
|
// cache's copy-on-write DIRTY behaviour.
|
|
// 3) Stream by stream move all the high sectors down.
|
|
//
|
|
// History: 11-Feb-1997 BChapman Created
|
|
//
|
|
// Notes: 1) This is called in the contex of Begin/End Copy-on-Write.
|
|
// 2) We assume throughout that the underlying free sector allocator
|
|
// returns the first free sector of the file.
|
|
// 3) The old fat/dif/dir may leave small holes in the finished file.
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::Consolidate(void)
|
|
{
|
|
//
|
|
// We don't support NOSCRATCH and this should have already
|
|
// been checked in the caller.
|
|
//
|
|
msfAssert(_pmsScratch == NULL);
|
|
|
|
SCODE sc=S_OK;
|
|
|
|
ULONG cAllocatedSects;
|
|
ULONG cDirEntries;
|
|
CDirEntry *pde=NULL;
|
|
|
|
SID sid;
|
|
ULONG sectLast = 0; // Last allocated Sector #.
|
|
ULONG csectFree = 0; // #sects that are free.
|
|
SECT sectLimit; // #sects we will try to shrink the file into.
|
|
ULONG csectLowDIF; // #DIF sects below sectLimit.
|
|
ULONG csectLowFAT; // #FAT sects below sectLimit.
|
|
ULONG csectLowControl; // #motion sensitive sects below sectLimit.
|
|
SECT *psectControl; // list of Dir and Mini Fat sectors
|
|
ULONG csectControl; // # of sects in psectControl
|
|
ULONG i;
|
|
SECT sectType;
|
|
|
|
//
|
|
// It is quite impossible to consolidate a file when the Snapshot
|
|
// limits are in effect. This routine is only called when we have
|
|
// confirmed there are no other "seperate" writers. So it should be
|
|
// safe to turn off the snapshot limits.
|
|
//
|
|
if(_fIsNoSnapshot)
|
|
{
|
|
_fat.SetNoSnapshot(0);
|
|
_fat.ResetNoSnapshotFree();
|
|
}
|
|
//
|
|
// Compute the Number of allocated sectors in the file.
|
|
// Ignore the header.
|
|
//
|
|
msfChk(_fat.FindLast(§Last));
|
|
sectLast--;
|
|
msfChk(_fat.CountSectType(&csectFree, 0, sectLast, FREESECT));
|
|
|
|
//
|
|
// Compute the expected size of the consolidated file. We will
|
|
// use this limit to determine when a sector is too high and needs
|
|
// to be copied down.
|
|
//
|
|
sectLimit = sectLast - csectFree;
|
|
|
|
//
|
|
// We will move the stream data sectors by copying them to free
|
|
// sectors and releasing the old sector.
|
|
// But, there are a class of sectors (control structures: FAT, DIF,
|
|
// miniFat, Directory Stream) that the old sector cannot be freed until
|
|
// end-copy-on-write.
|
|
// It is possible that the old (original) versions of these sectors
|
|
// could exist below sectLimit and with therefore be taking up "dead"
|
|
// space in the resulting file. So we need to adjust sectLimit.
|
|
//
|
|
// We are in Copy-On-Write mode so any FAT, DIF, Directory Stream, or
|
|
// MiniFat sector that is modified (by moving other sectors) will be
|
|
// copied to a free sector by the cache manager.
|
|
// It is difficult to know ahead of time which Low FAT sectors won't
|
|
// be touched, and therefore can avoid being copied, so we just assume
|
|
// we need to make a complete copy of the FAT, DIF, Directory Stream,
|
|
// and MiniFAT.
|
|
//
|
|
|
|
// Count the number of FAT (and DIFat) blocks up to sectLimit.
|
|
//
|
|
msfChk(_fat.CountSectType(&csectLowFAT, 0, sectLimit, FATSECT));
|
|
msfChk(_fat.CountSectType(&csectLowDIF, 0, sectLimit, DIFSECT));
|
|
|
|
//
|
|
// Build a list of sectors in the Directory Stream and MiniFat
|
|
//
|
|
msfChk(BuildConsolidationControlSectList(&psectControl, &csectControl));
|
|
|
|
//
|
|
// Sum all the copy-on-write control sectors below sectLimit.
|
|
//
|
|
csectLowControl = csectLowFAT + csectLowDIF;
|
|
for(i=0; i<csectControl; i++)
|
|
{
|
|
if(psectControl[i] < sectLimit)
|
|
++csectLowControl;
|
|
}
|
|
|
|
//
|
|
// Now we adjust sectLimit. (see large comment above)
|
|
// We want to increase it by csectLowControl # of sectors.
|
|
// But, advancing over new control sectors doesn't help make space
|
|
// so skip over those.
|
|
// Note: In a well packed file we can hit EOF while doing this.
|
|
//
|
|
for( ; csectLowControl > 0; ++sectLimit)
|
|
{
|
|
if(sectLimit >= sectLast)
|
|
{
|
|
delete [] psectControl;
|
|
return S_OK;
|
|
}
|
|
|
|
msfChkTo(Err_FreeList, _fat.GetNext(sectLimit, §Type));
|
|
if( FATSECT != sectType
|
|
&& DIFSECT != sectType
|
|
&& (! IsSectorInList(sectLimit, psectControl, csectControl)) )
|
|
{
|
|
--csectLowControl;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are done with the control sector list.
|
|
//
|
|
delete [] psectControl;
|
|
psectControl = NULL;
|
|
|
|
//
|
|
// At Last! We begin to move some data.
|
|
// Iterate through the directory.
|
|
// Remapping the sectors of each Stream to below sectLimit.
|
|
//
|
|
cDirEntries = _dir.GetNumDirEntries();
|
|
for(sid=0; sid<cDirEntries; sid++)
|
|
{
|
|
//
|
|
// Always get the directory entry "for-writing".
|
|
// This has the effect of remapping each sector of the directory
|
|
// stream down into the front of the file.
|
|
//
|
|
msfChk(_dir.GetDirEntry(sid, FB_DIRTY, &pde));
|
|
switch(pde->GetFlags())
|
|
{
|
|
case STGTY_LOCKBYTES:
|
|
case STGTY_PROPERTY:
|
|
case STGTY_STORAGE:
|
|
case STGTY_INVALID:
|
|
default:
|
|
break;
|
|
//
|
|
// Remap The Mini-stream
|
|
//
|
|
case STGTY_ROOT:
|
|
msfChkTo(Err_Release, ConsolidateStream(pde, sectLimit));
|
|
GetMiniStream()->EmptyCache();
|
|
break;
|
|
|
|
//
|
|
// Remap the regular streams.
|
|
// Don't remap streams in the mini-streams
|
|
//
|
|
case STGTY_STREAM:
|
|
#ifdef LARGE_STREAMS
|
|
if(pde->GetSize(_dir.IsLargeSector()) < MINISTREAMSIZE)
|
|
#else
|
|
if(pde->GetSize() < MINISTREAMSIZE)
|
|
#endif
|
|
break;
|
|
|
|
msfChkTo(Err_Release, ConsolidateStream(pde, sectLimit));
|
|
break;
|
|
}
|
|
_dir.ReleaseEntry(sid);
|
|
}
|
|
|
|
//
|
|
// If there are any remaining un-remapped FAT blocks, remap them now.
|
|
// (begin-copy-on-write already remapped the DIF).
|
|
//
|
|
msfChk(_fat.DirtyAll());
|
|
msfChk(_fatMini.DirtyAll());
|
|
|
|
return sc;
|
|
|
|
Err_FreeList:
|
|
delete [] psectControl;
|
|
return sc;
|
|
|
|
Err_Release:
|
|
_dir.ReleaseEntry(sid);
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::BuildConsolidationControlSectList, private
|
|
//
|
|
// Synopsis: Makes a list of all the sectors that are copy-on-write
|
|
// for the purpose of computing how much space we need to
|
|
// make to Consolidate a file.
|
|
//
|
|
// Arguments: [ppsectList] -- [out] pointer to a list of sectors.
|
|
// [pcsect] -- [out] count of sectors on the list.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 25-feb-1997 BChapman Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::BuildConsolidationControlSectList(
|
|
SECT **ppsectList,
|
|
ULONG *pcsect)
|
|
{
|
|
SECT sect;
|
|
SECT *psectList;
|
|
ULONG i, csect;
|
|
SCODE sc;
|
|
|
|
csect = _dir.GetNumDirSects() + _hdr.GetMiniFatLength();
|
|
msfMem(psectList = (SECT*) new SECT[csect]);
|
|
|
|
i = 0;
|
|
//
|
|
// Walk the Directory Stream FAT chain and record all
|
|
// the sector numbers.
|
|
//
|
|
sect = _hdr.GetDirStart();
|
|
while(sect != ENDOFCHAIN)
|
|
{
|
|
msfAssert(i < csect);
|
|
|
|
psectList[i++] = sect;
|
|
msfChkTo(Err_Free, _fat.GetNext(sect, §));
|
|
}
|
|
msfAssert(i == _dir.GetNumDirSects());
|
|
|
|
//
|
|
// Walk the MiniFat FAT chain and record all
|
|
// the sector numbers.
|
|
//
|
|
sect = _hdr.GetMiniFatStart();
|
|
while(sect != ENDOFCHAIN)
|
|
{
|
|
msfAssert(i < csect);
|
|
|
|
psectList[i++] = sect;
|
|
msfChkTo(Err_Free, _fat.GetNext(sect, §));
|
|
}
|
|
msfAssert((i == csect) && aMsg("Directory Stream + MiniFat too short\n"));
|
|
|
|
*ppsectList = psectList;
|
|
*pcsect = csect;
|
|
return S_OK;
|
|
|
|
Err_Free:
|
|
if (psectList != NULL)
|
|
delete [] psectList;
|
|
return sc;
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::IsSectorOnList, private
|
|
//
|
|
// Synopsis: Searches a list of sector values for a given value.
|
|
//
|
|
// Arguments: [sect] -- [in] value to search for.
|
|
// [psectList] -- [in] list of sectors.
|
|
// [csect] -- [in] count of sectors on the list.
|
|
//
|
|
// Returns: TRUE if the sector is on the list, otherwise FALSE.
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 25-feb-1997 BChapman Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CMStream::IsSectorInList(
|
|
SECT sect,
|
|
SECT *psectList,
|
|
ULONG csectList)
|
|
{
|
|
ULONG i;
|
|
for(i=0; i < csectList; i++)
|
|
{
|
|
if(sect == psectList[i])
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::ConsolidateStream, private
|
|
//
|
|
// Synopsis: Scan the stream FAT chain and remap sectors that are
|
|
// above the given limit.
|
|
//
|
|
// Arguments: [pde] -- Directory entry of the stream (DIRTY)
|
|
// [sectLimit] -- sector limit that all sectors should be below
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 18-feb-1997 BChapman Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::ConsolidateStream(
|
|
CDirEntry *pde, // Current CDirEntry Object (read-only)
|
|
SECT sectLimit) // Move all sectors below this sector#
|
|
{
|
|
SECT sectPrev, sectCurrent, sectNew;
|
|
ULONG cbLength;
|
|
SCODE sc=S_OK;
|
|
|
|
//
|
|
// This code should not be used in NOSCRATCH mode.
|
|
//
|
|
msfAssert(_pmsScratch == NULL);
|
|
|
|
//
|
|
// Check the first sector of the stream as a special case.
|
|
//
|
|
sectCurrent = pde->GetStart();
|
|
if(ENDOFCHAIN != sectCurrent && sectCurrent > sectLimit)
|
|
{
|
|
msfChk(_fat.GetFree(1, §New, GF_WRITE));
|
|
|
|
// This is only here because I don't understand GetFree().
|
|
msfAssert(ENDOFCHAIN != sectNew);
|
|
|
|
msfChk(MoveSect(ENDOFCHAIN, sectCurrent, sectNew));
|
|
sectCurrent = sectNew;
|
|
pde->SetStart(sectCurrent);
|
|
}
|
|
|
|
//
|
|
// Do the rest of the stream FAT chain.
|
|
//
|
|
sectPrev = sectCurrent;
|
|
while(ENDOFCHAIN != sectPrev)
|
|
{
|
|
msfChk(_fat.GetNext(sectPrev, §Current));
|
|
if(ENDOFCHAIN != sectCurrent && sectCurrent > sectLimit)
|
|
{
|
|
msfChk(_fat.GetFree(1, §New, GF_WRITE));
|
|
|
|
// This is only here because I don't understand GetFree().
|
|
msfAssert(ENDOFCHAIN != sectNew);
|
|
|
|
msfChk(MoveSect(sectPrev, sectCurrent, sectNew));
|
|
sectCurrent = sectNew;
|
|
}
|
|
sectPrev = sectCurrent;
|
|
}
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::MoveSect, private
|
|
//
|
|
// Synopsis: Move Data Sector for Consolidation Support.
|
|
//
|
|
// Arguments: [sectPrev] -- Previous sector, so the link can be updated.
|
|
// [sectOld] -- Location to copy from
|
|
// [sectNew] -- Location to copy to
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 20-Feb-1997 BChapman Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::MoveSect(
|
|
SECT sectPrev,
|
|
SECT sectOld,
|
|
SECT sectNew)
|
|
{
|
|
ULONG cb;
|
|
SCODE sc=S_OK;
|
|
ULARGE_INTEGER ulOff;
|
|
BYTE *pbScratch = BP_TO_P(BYTE HUGEP *, _pCopySectBuf);
|
|
|
|
//
|
|
// This code does not expect NOSCRATCH mode.
|
|
//
|
|
msfAssert(_pmsScratch == NULL);
|
|
|
|
//
|
|
// Copy the data from the old sector to the new sector.
|
|
//
|
|
ulOff.QuadPart = ConvertSectOffset(sectOld, 0, GetSectorShift());
|
|
msfChk((*_pplstParent)->ReadAt(ulOff,
|
|
pbScratch,
|
|
GetSectorSize(),
|
|
&cb));
|
|
|
|
ulOff.QuadPart = ConvertSectOffset(sectNew, 0, GetSectorShift());
|
|
msfChk((*_pplstParent)->WriteAt(ulOff,
|
|
pbScratch,
|
|
GetSectorSize(),
|
|
&cb));
|
|
|
|
//
|
|
// Update the previous sector's link (if this isn't the first sector)
|
|
//
|
|
if(ENDOFCHAIN != sectPrev)
|
|
{
|
|
msfChk(_fat.SetNext(sectPrev, sectNew));
|
|
}
|
|
|
|
//
|
|
// Update the link to the next sector.
|
|
//
|
|
SECT sectTemp;
|
|
msfChk(_fat.GetNext(sectOld, §Temp));
|
|
msfChk(_fat.SetNext(sectNew, sectTemp));
|
|
|
|
//
|
|
// Free the old sector.
|
|
//
|
|
msfChk(_fat.SetNext(sectOld, FREESECT));
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::CopySect, private
|
|
//
|
|
// Synopsis: Do a partial sector delta for copy-on-write support
|
|
//
|
|
// Arguments: [sectOld] -- Location to copy from
|
|
// [sectNew] -- Location to copy to
|
|
// [oStart] -- Offset into sector to begin delta
|
|
// [oEnd] -- Offset into sector to end delta
|
|
// [pb] -- Buffer to delta from
|
|
// [pulRetval] -- Return location for number of bytes written
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 22-Jan-93 PhilipLa Created
|
|
//
|
|
// Notes: [pb] may be unsafe memory
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
//This pragma is to avoid a C7 bug when building RETAIL
|
|
#if _MSC_VER == 700 && DBG == 0
|
|
#pragma function(memcpy)
|
|
#endif
|
|
|
|
SCODE CMStream::CopySect(
|
|
SECT sectOld,
|
|
SECT sectNew,
|
|
OFFSET oStart, // First byte of the sector to copy.
|
|
OFFSET oEnd, // Last byte of the sector to copy.
|
|
BYTE const HUGEP *pb,
|
|
ULONG *pulRetval)
|
|
{
|
|
SCODE sc;
|
|
|
|
ULONG cb;
|
|
ULARGE_INTEGER ulOff;
|
|
|
|
ULISetHigh(ulOff, 0);
|
|
|
|
BYTE HUGEP *pbScratch = BP_TO_P(BYTE HUGEP *, _pCopySectBuf);
|
|
|
|
#if DBG == 1
|
|
msfAssert((_uBufferRef == 0) &&
|
|
aMsg("Attempted to use CopySect buffer while refcount != 0"));
|
|
AtomicInc(&_uBufferRef);
|
|
#endif
|
|
|
|
msfAssert((pbScratch != NULL) && aMsg("No CopySect buffer found."));
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ulOff.QuadPart = ConvertSectOffset(sectOld, 0, GetSectorShift());
|
|
#else
|
|
ULISetLow(ulOff, ConvertSectOffset(sectOld, 0, GetSectorShift()));
|
|
#endif
|
|
msfHChk((*_pplstParent)->ReadAt(
|
|
ulOff,
|
|
pbScratch,
|
|
GetSectorSize(),
|
|
&cb));
|
|
|
|
//Now do delta in memory.
|
|
BYTE HUGEP *pstart;
|
|
pstart = pbScratch + oStart;
|
|
|
|
USHORT memLength;
|
|
memLength = oEnd - oStart + 1;
|
|
|
|
TRY
|
|
{
|
|
memcpy(pstart, pb, memLength);
|
|
}
|
|
CATCH(CException, e)
|
|
{
|
|
UNREFERENCED_PARM(e);
|
|
msfErr(Err, STG_E_INVALIDPOINTER);
|
|
}
|
|
END_CATCH
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ulOff.QuadPart = ConvertSectOffset(sectNew, 0, GetSectorShift());
|
|
#else
|
|
ULISetLow(ulOff, ConvertSectOffset(sectNew, 0, GetSectorShift()));
|
|
#endif
|
|
msfHChk((*_pplstParent)->WriteAt(
|
|
ulOff,
|
|
pbScratch,
|
|
GetSectorSize(),
|
|
&cb));
|
|
|
|
*pulRetval = memLength;
|
|
|
|
Err:
|
|
#if DBG == 1
|
|
AtomicDec(&_uBufferRef);
|
|
#endif
|
|
return sc;
|
|
}
|
|
|
|
|
|
//This returns the compiler to the default behavior
|
|
#if _MSC_VER == 700 && DBG == 0
|
|
#pragma intrinsic(memcpy)
|
|
#endif
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::MWrite, public
|
|
//
|
|
// Synposis: Do multiple sector writes
|
|
//
|
|
// Effects: Causes multiple stream writes. Modifies fat and directory
|
|
//
|
|
// Arguments: [ph] -- Handle of stream doing write
|
|
// [start] -- Starting sector to write
|
|
// [oStart] -- offset into sector to begin write at
|
|
// [end] -- Last sector to write
|
|
// [oEnd] -- offset into last sector to write to
|
|
// [buffer] -- Pointer to buffer into which data will be written
|
|
// [ulRetVal] -- location to return number of bytes written
|
|
//
|
|
// Returns: Error code of any failed call to parent write
|
|
// S_OK if call completed OK.
|
|
//
|
|
// Modifies: ulRetVal returns the number of bytes written
|
|
//
|
|
// Algorithm: Using a segment table, perform writes on parent stream
|
|
// until call is completed.
|
|
//
|
|
// History: 16-Aug-91 PhilipLa Created.
|
|
// 10-Sep-91 PhilipLa Converted to use sector table
|
|
// 11-Sep-91 PhilipLa Modified interface, modified to
|
|
// allow partial sector writes.
|
|
// 07-Jan-92 PhilipLa Converted to use handle.
|
|
// 18-Feb-92 PhilipLa Added copy on write support.
|
|
//
|
|
// Notes: [pvBuffer] may be unsafe memory
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::MWrite(
|
|
SID sid,
|
|
BOOL fIsMini,
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulOffset,
|
|
#else
|
|
ULONG ulOffset,
|
|
#endif
|
|
VOID const HUGEP *pvBuffer,
|
|
ULONG ulCount,
|
|
CStreamCache *pstmc,
|
|
ULONG *pulRetval)
|
|
{
|
|
SCODE sc;
|
|
BYTE const HUGEP *pbBuffer = (BYTE const HUGEP *) pvBuffer;
|
|
|
|
USHORT cbSector = GetSectorSize();
|
|
CFat *pfat = &_fat;
|
|
USHORT uShift = GetSectorShift();
|
|
ULONG ulLastBytes = 0;
|
|
|
|
ULARGE_INTEGER ulOff;
|
|
ULISetHigh(ulOff, 0);
|
|
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulOldSize = 0;
|
|
#else
|
|
ULONG ulOldSize = 0;
|
|
#endif
|
|
|
|
// Check if it's a small stream and whether this is a real or
|
|
// scratch multistream.
|
|
|
|
if ((fIsMini) &&
|
|
(!_fIsScratch) &&
|
|
(SIDMINISTREAM != sid))
|
|
{
|
|
msfAssert(sid <= MAXREGSID &&
|
|
aMsg("Invalid SID in MWrite"));
|
|
// This stream is stored in the ministream
|
|
|
|
cbSector = MINISECTORSIZE;
|
|
uShift = MINISECTORSHIFT;
|
|
pfat = GetMiniFat();
|
|
}
|
|
|
|
USHORT uMask = cbSector - 1;
|
|
|
|
SECT start = (SECT)(ulOffset >> uShift);
|
|
OFFSET oStart = (OFFSET)(ulOffset & uMask);
|
|
|
|
SECT end = (SECT)((ulOffset + ulCount - 1) >> uShift);
|
|
OFFSET oEnd = (OFFSET)((ulOffset + ulCount - 1) & uMask);
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::MWrite(%lu,%u,%lu,%u)\n",
|
|
start,oStart,end,oEnd));
|
|
|
|
ULONG bytecount;
|
|
ULONG total = 0;
|
|
|
|
msfChk(_dir.GetSize(sid, &ulOldSize));
|
|
|
|
|
|
//BEGIN COPYONWRITE
|
|
|
|
// Note that we don't do this for ministreams (the second pass through
|
|
// this code will take care of it).
|
|
|
|
msfAssert(!_fBlockWrite &&
|
|
aMsg("Called MWrite on Unconverted multistream"));
|
|
|
|
if ((_fBlockHeader) && (GetMiniFat() != pfat))
|
|
{
|
|
msfDebugOut((DEB_ITRACE,"**MWrite preparing for copy-on-write\n"));
|
|
|
|
SECT sectOldStart, sectNewStart, sectOldEnd, sectNewEnd;
|
|
|
|
SECT sectNew;
|
|
if (start != 0)
|
|
{
|
|
msfChk(pstmc->GetESect(start - 1, §New));
|
|
}
|
|
else
|
|
{
|
|
msfChk(_dir.GetStart(sid, §New));
|
|
}
|
|
|
|
msfChk(_fat.Remap(
|
|
sectNew,
|
|
(start == 0) ? 0 : 1,
|
|
(end - start + 1),
|
|
§OldStart,
|
|
§NewStart,
|
|
§OldEnd,
|
|
§NewEnd));
|
|
|
|
msfAssert(((end != start) || (sectNewStart == sectNewEnd)) &&
|
|
aMsg("Remap postcondition failed."));
|
|
|
|
if (sc != S_FALSE)
|
|
{
|
|
msfChk(pstmc->EmptyRegion(start, end));
|
|
}
|
|
|
|
if ((start == 0) && (sectNewStart != ENDOFCHAIN))
|
|
{
|
|
msfDebugOut((DEB_ITRACE,
|
|
"*** Remapped first sector. Changing directory.\n"));
|
|
msfChk(_dir.SetStart(sid, sectNewStart));
|
|
}
|
|
|
|
#ifdef LARGE_STREAMS
|
|
ULONGLONG ulSize = ulOldSize;
|
|
#else
|
|
ULONG ulSize = ulOldSize;
|
|
#endif
|
|
|
|
if (((oStart != 0) ||
|
|
((end == start) && (ulOffset + ulCount != ulSize)
|
|
&& ((USHORT)oEnd != (cbSector - 1)))) &&
|
|
(sectNewStart != ENDOFCHAIN))
|
|
{
|
|
//Partial first sector.
|
|
ULONG ulRetval;
|
|
|
|
msfChk(CopySect(
|
|
sectOldStart,
|
|
sectNewStart,
|
|
oStart,
|
|
(OFFSET) (end == start ? oEnd : (cbSector - 1)),
|
|
pbBuffer,
|
|
&ulRetval));
|
|
|
|
pbBuffer = pbBuffer + ulRetval;
|
|
total = total + ulRetval;
|
|
start++;
|
|
oStart = 0;
|
|
}
|
|
|
|
if (((end >= start) && ((USHORT)oEnd != cbSector - 1) &&
|
|
(ulCount + ulOffset != ulSize)) &&
|
|
(sectNewEnd != ENDOFCHAIN))
|
|
{
|
|
//Partial last sector.
|
|
|
|
msfAssert(((end != start) || (oStart == 0)) &&
|
|
aMsg("CopySect precondition failed."));
|
|
|
|
msfChk(CopySect(
|
|
sectOldEnd,
|
|
sectNewEnd,
|
|
0,
|
|
oEnd,
|
|
pbBuffer + ((end - start) << uShift) - oStart,
|
|
&ulLastBytes));
|
|
|
|
end--;
|
|
oEnd = cbSector - 1;
|
|
//We don't need to update pbBuffer, since the change
|
|
// is at the end.
|
|
}
|
|
}
|
|
|
|
|
|
// At this point, the entire block has been moved into the copy-on-write
|
|
// area of the multistream, and all partial writes have been done.
|
|
//END COPYONWRITE
|
|
|
|
msfAssert(end != 0xffffffffL);
|
|
|
|
if (end < start)
|
|
{
|
|
*pulRetval = total + ulLastBytes;
|
|
goto Err;
|
|
}
|
|
|
|
ULONG ulRunLength;
|
|
ulRunLength = end - start + 1;
|
|
|
|
USHORT offset;
|
|
offset = oStart;
|
|
|
|
while (TRUE)
|
|
{
|
|
SSegment segtab[CSEG + 1];
|
|
|
|
ULONG cSeg;
|
|
msfChk(pstmc->Contig(
|
|
start,
|
|
TRUE,
|
|
(SSegment STACKBASED *) segtab,
|
|
ulRunLength,
|
|
&cSeg));
|
|
|
|
msfAssert(cSeg <= CSEG);
|
|
|
|
USHORT oend = cbSector - 1;
|
|
ULONG i;
|
|
SECT sectStart;
|
|
for (USHORT iseg = 0; iseg < cSeg;)
|
|
{
|
|
sectStart = segtab[iseg].sectStart;
|
|
i = segtab[iseg].cSect;
|
|
if (i > ulRunLength)
|
|
i = ulRunLength;
|
|
|
|
ulRunLength -= i;
|
|
start += i;
|
|
|
|
iseg++;
|
|
if (ulRunLength == 0)
|
|
oend = oEnd;
|
|
|
|
ULONG ulSize = ((i - 1) << uShift) - offset + oend + 1;
|
|
|
|
msfDebugOut((
|
|
DEB_ITRACE,
|
|
"Calling lstream WriteAt(%lu,%p,%lu)\n",
|
|
ConvertSectOffset(sectStart,offset,uShift),
|
|
pbBuffer,
|
|
ulSize));
|
|
|
|
if (GetMiniFat() == pfat)
|
|
{
|
|
sc = _pdsministream->CDirectStream::WriteAt(
|
|
(sectStart << uShift) + offset,
|
|
pbBuffer, ulSize,
|
|
(ULONG STACKBASED *)&bytecount);
|
|
}
|
|
else
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
ulOff.QuadPart = ConvertSectOffset(sectStart, offset, uShift);
|
|
#else
|
|
ULISetLow(ulOff, ConvertSectOffset(sectStart, offset,
|
|
uShift));
|
|
#endif
|
|
sc = DfGetScode((*_pplstParent)->WriteAt(ulOff, pbBuffer,
|
|
ulSize, &bytecount));
|
|
}
|
|
|
|
total += bytecount;
|
|
|
|
//Check if this write is the last one in the stream,
|
|
// and that the stream ends as a partial sector.
|
|
//If so, fill out the remainder of the sector with
|
|
// something.
|
|
if ((0 == ulRunLength) && (total + ulOffset > ulOldSize) &&
|
|
(((total + ulOffset) & (GetSectorSize() - 1)) != 0))
|
|
{
|
|
//This is the last sector and the stream has grown.
|
|
ULONG csectOld = (ULONG)((ulOldSize + GetSectorSize() - 1) >>
|
|
GetSectorShift());
|
|
|
|
ULONG csectNew = (ULONG)((total + ulOffset + GetSectorSize() - 1) >>
|
|
GetSectorShift());
|
|
|
|
if (csectNew > csectOld)
|
|
{
|
|
msfAssert(!fIsMini &&
|
|
aMsg("Small stream grew in MWrite"));
|
|
|
|
SECT sectLast = sectStart + i - 1;
|
|
|
|
msfVerify(SUCCEEDED(SecureSect(
|
|
sectLast,
|
|
total + ulOffset,
|
|
FALSE)));
|
|
}
|
|
}
|
|
|
|
if (0 == ulRunLength || FAILED(sc))
|
|
{
|
|
break;
|
|
}
|
|
|
|
pbBuffer = pbBuffer + bytecount;
|
|
offset = 0;
|
|
}
|
|
|
|
if (0 == ulRunLength || FAILED(sc))
|
|
{
|
|
*pulRetval = total + ulLastBytes;
|
|
msfDebugOut((
|
|
DEB_ITRACE,
|
|
"Out CMStream::MWrite()=>%lu, retval = %lu\n",
|
|
sc,
|
|
total));
|
|
break;
|
|
}
|
|
}
|
|
|
|
Err:
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Flush, public
|
|
//
|
|
// Synopsis: Flush control structures.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 16-Dec-92 PhilipLa Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::Flush(BOOL fFlushCache)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
msfAssert(!_fBlockWrite &&
|
|
aMsg("Flush called on unconverted base."));
|
|
|
|
if ((!_fIsScratch) && (*_pplstParent != NULL))
|
|
{
|
|
msfChk(_pmpt->Flush());
|
|
msfChk(FlushHeader(HDR_NOFORCE));
|
|
msfChk(ILBFlush(*_pplstParent, fFlushCache));
|
|
}
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: ILBFlush
|
|
//
|
|
// Synopsis: Flush as thoroughly as possible
|
|
//
|
|
// Effects: Flushes ILockBytes
|
|
//
|
|
// Arguments: [pilb] - ILockBytes to flush
|
|
// [fFlushCache] - Flush thoroughly iff TRUE
|
|
//
|
|
// Returns: SCODE
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 12-Feb-93 AlexT Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE ILBFlush(ILockBytes *pilb, BOOL fFlushCache)
|
|
{
|
|
// Try to query interface to our own implementation
|
|
|
|
IFileLockBytes *pfl;
|
|
SCODE sc;
|
|
|
|
msfDebugOut((DEB_ITRACE, "In ILBFlushCache(%p)\n", pilb));
|
|
|
|
// Check for FileLockBytes
|
|
|
|
if (!fFlushCache ||
|
|
FAILED(DfGetScode(pilb->QueryInterface(IID_IFileLockBytes, (void **)&pfl))))
|
|
{
|
|
// Either we don't have to flush the cache or its not our ILockBytes
|
|
sc = DfGetScode(pilb->Flush());
|
|
}
|
|
else
|
|
{
|
|
// We have to flush the cache and its our ILockBytes
|
|
sc = DfGetScode(pfl->FlushCache());
|
|
pfl->Release();
|
|
}
|
|
|
|
msfDebugOut((DEB_ITRACE, "Out ILBFlushCache()\n"));
|
|
|
|
return(sc);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::SecureSect, public
|
|
//
|
|
// Synopsis: Zero out the unused portion of a sector
|
|
//
|
|
// Arguments: [sect] -- Sector to zero out
|
|
// [ulSize] -- Size of stream
|
|
// [fIsMini] -- TRUE if stream is in ministream
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 05-Apr-93 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::SecureSect(
|
|
const SECT sect,
|
|
#ifdef LARGE_STREAMS
|
|
const ULONGLONG ulSize,
|
|
#else
|
|
const ULONG ulSize,
|
|
#endif
|
|
const BOOL fIsMini)
|
|
{
|
|
#ifdef SECURE_TAIL
|
|
SCODE sc = S_OK;
|
|
BYTE *pb = NULL;
|
|
|
|
if (!_fIsScratch)
|
|
{
|
|
ULONG cbSect = fIsMini ? MINISECTORSIZE : GetSectorSize();
|
|
|
|
msfAssert(ulSize != 0);
|
|
|
|
ULONG ulOffset = (ULONG)(((ulSize - 1) % cbSect) + 1);
|
|
|
|
ULONG cb = cbSect - ulOffset;
|
|
|
|
msfAssert(cb != 0);
|
|
|
|
//We can use any initialized block of memory here. The header
|
|
// is available and is the correct size, so we use that.
|
|
#ifdef SECURE_BUFFER
|
|
pb = s_bufSecure;
|
|
#else
|
|
pb = (BYTE *)_hdr.GetData();
|
|
#endif
|
|
|
|
#ifdef SECURETEST
|
|
pb = (BYTE *) DfMemAlloc(cb);
|
|
if (pb != NULL)
|
|
memset(pb, 'Y', cb);
|
|
#endif
|
|
ULONG cbWritten;
|
|
|
|
if (!fIsMini)
|
|
{
|
|
ULARGE_INTEGER ulOff;
|
|
#ifdef LARGE_DOCFILE
|
|
ulOff.QuadPart = ConvertSectOffset(
|
|
sect,
|
|
(OFFSET)ulOffset,
|
|
GetSectorShift());
|
|
#else
|
|
ULISet32 (ulOff, ConvertSectOffset(
|
|
sect,
|
|
(OFFSET)ulOffset,
|
|
GetSectorShift()));
|
|
#endif
|
|
|
|
msfChk(DfGetScode((*_pplstParent)->WriteAt(
|
|
ulOff,
|
|
pb,
|
|
cb,
|
|
&cbWritten)));
|
|
}
|
|
else
|
|
{
|
|
msfChk(_pdsministream->WriteAt(
|
|
(sect << MINISECTORSHIFT) + ulOffset,
|
|
pb,
|
|
cb,
|
|
(ULONG STACKBASED *)&cbWritten));
|
|
}
|
|
|
|
if (cbWritten != cb)
|
|
{
|
|
sc = STG_E_WRITEFAULT;
|
|
}
|
|
}
|
|
|
|
Err:
|
|
#ifdef SECURETEST
|
|
DfMemFree(pb);
|
|
#endif
|
|
|
|
return sc;
|
|
#else
|
|
//On NT, our sectors get zeroed out by the file system, so we don't
|
|
// need this whole rigamarole.
|
|
return S_OK;
|
|
#endif // WIN32 == 200
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::SetFileLockBytesTime, public
|
|
//
|
|
// Synopsis: Set the IFileLockBytes time.
|
|
//
|
|
// Arguments: [tt] -- Timestamp requested (WT_CREATION, WT_MODIFICATION,
|
|
// WT_ACCESS)
|
|
// [nt] -- New timestamp
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm: Query for IFileLockBytes and call its SetTime member.
|
|
//
|
|
// History: 01-Sep-95 MikeHill Created.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::SetFileLockBytesTime(
|
|
WHICHTIME const tt,
|
|
TIME_T nt)
|
|
{
|
|
SCODE sc = S_OK;
|
|
ILockBytes *pilb = *_pplstParent;
|
|
IFileLockBytes *pfl;
|
|
|
|
|
|
if (pilb &&
|
|
(SUCCEEDED(pilb->QueryInterface(IID_IFileLockBytes, (void **)&pfl))))
|
|
{
|
|
|
|
sc = ((CFileStream *)pfl)->SetTime(tt, nt);
|
|
pfl->Release();
|
|
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::SetAllFileLockBytesTimes, public
|
|
//
|
|
// Synopsis: Set the IFileLockBytes time.
|
|
//
|
|
// Arguments:
|
|
// [atm] -- ACCESS time
|
|
// [mtm] -- MODIFICATION time
|
|
// [ctm] -- CREATION time
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm: Query for IFileLockBytes and call its SetAllTimes member.
|
|
//
|
|
// History: 29-Nov-95 SusiA Created.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::SetAllFileLockBytesTimes(
|
|
TIME_T atm,
|
|
TIME_T mtm,
|
|
TIME_T ctm)
|
|
{
|
|
SCODE sc = S_OK;
|
|
ILockBytes *pilb = *_pplstParent;
|
|
IFileLockBytes *pfl;
|
|
|
|
if (SUCCEEDED(pilb->QueryInterface( IID_IFileLockBytes, (void **)&pfl)))
|
|
{
|
|
|
|
sc = ((CFileStream *)pfl)->SetAllTimes(atm, mtm, ctm);
|
|
pfl->Release();
|
|
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::SetTime, public
|
|
//
|
|
// Synopsis: Set the time for a given handle
|
|
//
|
|
// Arguments: [sid] -- SID to retrieve time for
|
|
// [tt] -- Timestamp requested (WT_CREATION, WT_MODIFICATION,
|
|
// WT_ACCESS)
|
|
// [nt] -- New timestamp
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm: Call through to directory
|
|
//
|
|
// History: 01-Apr-92 PhilipLa Created.
|
|
// 14-Sep-92 PhilipLa inlined.
|
|
// <Missing history>
|
|
// 26-APR-99 RogerCh Removed faulty optimization
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::SetTime(
|
|
SID const sid,
|
|
WHICHTIME const tt,
|
|
TIME_T nt)
|
|
{
|
|
|
|
if ( sid == SIDROOT )
|
|
{
|
|
SCODE sc;
|
|
|
|
if( FAILED( sc = SetFileLockBytesTime( tt, nt )))
|
|
{
|
|
return sc;
|
|
}
|
|
}// if( sid == SIDROOT)
|
|
|
|
return _dir.SetTime(sid, tt, nt);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::SetAllTimes, public
|
|
//
|
|
// Synopsis: Set all the times for a given handle
|
|
//
|
|
// Arguments: [sid] -- SID to retrieve time for
|
|
// [atm] -- ACCESS time
|
|
// [mtm] -- MODIFICATION time
|
|
// [ctm] -- CREATION time
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm: Call through to directory
|
|
//
|
|
// History: 27-Nov-95 SusiA Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::SetAllTimes(
|
|
SID const sid,
|
|
TIME_T atm,
|
|
TIME_T mtm,
|
|
TIME_T ctm)
|
|
{
|
|
|
|
if ( sid == SIDROOT )
|
|
{
|
|
|
|
SCODE sc;
|
|
|
|
if( FAILED( sc = SetAllFileLockBytesTimes(atm, mtm, ctm )))
|
|
{
|
|
return sc;
|
|
}
|
|
}
|
|
return _dir.SetAllTimes(sid, atm, mtm, ctm);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::GetTime, public
|
|
//
|
|
// Synopsis: Get the time for a given handle
|
|
//
|
|
// Arguments: [sid] -- SID to retrieve time for
|
|
// [tt] -- Timestamp requested (WT_CREATION, WT_MODIFICATION,
|
|
// WT_ACCESS)
|
|
// [pnt] -- Pointer to return location
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// History: 01-Apr-92 PhilipLa Created.
|
|
// 14-Sep-92 PhilipLa inlined.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::GetTime(SID const sid,
|
|
WHICHTIME const tt,
|
|
TIME_T *pnt)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
if (sid == SIDROOT)
|
|
{
|
|
//Get timestamp from ILockBytes
|
|
STATSTG stat;
|
|
|
|
msfChk((*_pplstParent)->Stat(&stat, STATFLAG_NONAME));
|
|
|
|
if (tt == WT_CREATION)
|
|
{
|
|
*pnt = stat.ctime;
|
|
}
|
|
else if (tt == WT_MODIFICATION)
|
|
{
|
|
*pnt = stat.mtime;
|
|
}
|
|
else
|
|
{
|
|
*pnt = stat.atime;
|
|
}
|
|
}
|
|
else
|
|
sc = _dir.GetTime(sid, tt, pnt);
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::GetAllTimes, public
|
|
//
|
|
// Synopsis: Get the times for a given handle
|
|
//
|
|
// Arguments: [sid] -- SID to retrieve time for
|
|
// [patm] -- Pointer to the ACCESS time
|
|
// [pmtm] -- Pointer to the MODIFICATION time
|
|
// [pctm] -- Pointer to the CREATION time
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// History: 26-May-95 SusiA Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::GetAllTimes(SID const sid,
|
|
TIME_T *patm,
|
|
TIME_T *pmtm,
|
|
TIME_T *pctm)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
if (sid == SIDROOT)
|
|
{
|
|
//Get timestamp from ILockBytes
|
|
STATSTG stat;
|
|
|
|
msfChk((*_pplstParent)->Stat(&stat, STATFLAG_NONAME));
|
|
|
|
*pctm = stat.ctime;
|
|
*pmtm = stat.mtime;
|
|
*patm = stat.atime;
|
|
|
|
}
|
|
else
|
|
sc = _dir.GetAllTimes(sid, patm, pmtm, pctm);
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::InitScratch, public
|
|
//
|
|
// Synopsis: Set up a multistream for NoScratch operation
|
|
//
|
|
// Arguments: [pms] -- Pointer to base multistream
|
|
// [fNew] -- True if this is the first time the function has
|
|
// been called (init path), FALSE if merging behavior
|
|
// is required (EndCopyOnWrite)
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 02-Mar-95 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::InitScratch(CMStream *pms, BOOL fNew)
|
|
{
|
|
msfDebugOut((DEB_ITRACE, "In CMStream::InitScratch:%p()\n", this));
|
|
|
|
msfAssert(GetSectorSize() == SCRATCHSECTORSIZE);
|
|
msfAssert(_fIsNoScratch &&
|
|
aMsg("Called InitScratch on Multistream not in NoScratch mode"));
|
|
|
|
return _fatMini.InitScratch(pms->GetFat(), fNew);
|
|
}
|
|
|
|
#ifdef MULTIHEAP
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::GetMalloc, public
|
|
//
|
|
// Synopsis: Returns the allocator associated with this multistream
|
|
//
|
|
// History: 05-May-93 AlexT Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
IMalloc * CMStream::GetMalloc(VOID) const
|
|
{
|
|
return (IMalloc *) &g_smAllocator;
|
|
}
|
|
#endif
|