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.
1050 lines
27 KiB
1050 lines
27 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1992.
|
|
//
|
|
// File: mstream.cxx
|
|
//
|
|
// Contents: Mstream operations
|
|
//
|
|
// Classes: None. (defined in mstream.hxx)
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#include "msfhead.cxx"
|
|
|
|
|
|
#include "h/dirfunc.hxx"
|
|
#include "h/sstream.hxx"
|
|
#include "h/difat.hxx"
|
|
#include "h/msfiter.hxx"
|
|
#include <time.h>
|
|
#include "mread.hxx"
|
|
#include "h/docfilep.hxx"
|
|
|
|
|
|
#if DEVL == 1
|
|
|
|
DECLARE_INFOLEVEL(msf, DEB_ERROR)
|
|
|
|
#endif
|
|
|
|
#define MINPAGES 6
|
|
#define MAXPAGES 128
|
|
|
|
extern "C" WCHAR const wcsContents[] =
|
|
{'C','O','N','T','E','N','T','S','\0'};
|
|
extern "C" CDfName const dfnContents(wcsContents);
|
|
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
|
|
//
|
|
// Notes: Buffer should be released with FreeBuffer
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE GetBuffer(USHORT cbMin, USHORT cbMax, BYTE **ppb, USHORT *pcbActual)
|
|
{
|
|
USHORT cbSize;
|
|
BYTE *pb;
|
|
|
|
msfDebugOut((DEB_ITRACE, "In GetBuffer(%hu, %hu, %p, %p)\n",
|
|
cbMin, cbMax, ppb, pcbActual));
|
|
msfAssert(cbMin > 0);
|
|
msfAssert(cbMax >= cbMin);
|
|
msfAssert(ppb != NULL);
|
|
msfAssert(pcbActual != NULL);
|
|
|
|
cbSize = cbMax;
|
|
for (;;)
|
|
{
|
|
pb = new BYTE[cbSize];
|
|
if (pb == NULL)
|
|
{
|
|
cbSize >>= 1;
|
|
if (cbSize < cbMin)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*pcbActual = cbSize;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*ppb = pb;
|
|
|
|
msfDebugOut((DEB_ITRACE, "Out GetBuffer => %p, %hu\n", *ppb, *pcbActual));
|
|
return pb == NULL ? STG_E_INSUFFICIENTMEMORY : S_OK;
|
|
}
|
|
|
|
// Define the safe buffer size
|
|
#define SCRATCHBUFFERSIZE ((USHORT) 4096)
|
|
static BYTE s_buf[SCRATCHBUFFERSIZE];
|
|
static LONG s_bufRef = 0;
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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]
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void GetSafeBuffer(USHORT cbMin, USHORT cbMax, BYTE **ppb, USHORT *pcbActual)
|
|
{
|
|
msfAssert(cbMin > 0);
|
|
msfAssert(cbMin <= SCRATCHBUFFERSIZE &&
|
|
aMsg("Minimum too large for GetSafeBuffer"));
|
|
msfAssert(cbMax >= cbMin);
|
|
msfAssert(ppb != NULL);
|
|
msfAssert(s_bufRef == 0 &&
|
|
aMsg("Tried to use scratch buffer twice"));
|
|
|
|
if (cbMax <= SCRATCHBUFFERSIZE ||
|
|
FAILED(GetBuffer(cbMin, cbMax, ppb, pcbActual)))
|
|
{
|
|
s_bufRef = 1;
|
|
*ppb = s_buf;
|
|
*pcbActual = min(cbMax, SCRATCHBUFFERSIZE);
|
|
}
|
|
msfAssert(*ppb != NULL);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FreeBuffer, public
|
|
//
|
|
// Synopsis: Releases a buffer allocated by GetBuffer or GetSafeBuffer
|
|
//
|
|
// Arguments: [pb] - Buffer
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void FreeBuffer(BYTE *pb)
|
|
{
|
|
if (pb == s_buf)
|
|
{
|
|
msfAssert((s_bufRef == 1) && aMsg("Bad safe buffer ref count"));
|
|
s_bufRef = 0;
|
|
}
|
|
else
|
|
delete [] 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
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
CMStream::CMStream(
|
|
ILockBytes **pplstParent,
|
|
USHORT uSectorShift)
|
|
:_uSectorShift(uSectorShift),
|
|
_uSectorSize( (USHORT) (1 << uSectorShift) ),
|
|
_uSectorMask((USHORT) (_uSectorSize - 1)),
|
|
_pplstParent(pplstParent),
|
|
_hdr(uSectorShift),
|
|
_fatDif( (USHORT) (1<<uSectorShift) ),
|
|
_fat(SIDFAT, (USHORT) (1<<uSectorShift), uSectorShift),
|
|
_dir((USHORT) (1 << uSectorShift)),
|
|
_fatMini(SIDMINIFAT, (USHORT) (1 << uSectorShift), uSectorShift)
|
|
{
|
|
|
|
_pdsministream = NULL;
|
|
_pmpt = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::InitCommon, private
|
|
//
|
|
// Synopsis: Common code for initialization routines.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: S_OK if call completed OK.
|
|
//
|
|
// Algorithm:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::InitCommon(VOID)
|
|
{
|
|
msfDebugOut((DEB_ITRACE,"In CMStream InitCommon()\n"));
|
|
SCODE sc = S_OK;
|
|
|
|
msfAssert(_pmpt == NULL);
|
|
msfMem(_pmpt = new CMSFPageTable(this, MINPAGES, MAXPAGES));
|
|
msfChk(_pmpt->Init());
|
|
|
|
msfDebugOut((DEB_ITRACE,"Leaving CMStream InitCommon()\n"));
|
|
|
|
return sc;
|
|
|
|
Err:
|
|
delete _pmpt;
|
|
_pmpt = NULL;
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::GetESect, private
|
|
//
|
|
// Synopsis: For a given SID and sect, return the location of that
|
|
// sector in the multistream.
|
|
//
|
|
// Arguments: [sid] -- SID of sector to locate
|
|
// [sect] -- Offset into chain to locate
|
|
// [psect] -- Pointer to return location.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::GetESect(SID sid, SECT sect, SECT *psect)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
SECT start;
|
|
|
|
if (sid == SIDFAT)
|
|
{
|
|
msfChk(_fatDif.GetFatSect(sect, &start));
|
|
}
|
|
else if (sid == SIDDIF)
|
|
{
|
|
msfChk(_fatDif.GetSect(sect, &start));
|
|
}
|
|
else
|
|
{
|
|
start = GetStart(sid);
|
|
msfChk(_fat.GetESect(start, sect, &start));
|
|
}
|
|
|
|
*psect = start;
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Empty, public
|
|
//
|
|
// Synopsis: Empty all of the control structures of this CMStream
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: void.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void CMStream::Empty(void)
|
|
{
|
|
_fat.Empty();
|
|
_fatMini.Empty();
|
|
_fatDif.Empty();
|
|
_dir.Empty();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::~CMStream, public
|
|
//
|
|
// Synopsis: CMStream destructor
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CMStream::~CMStream()
|
|
{
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream destructor\n"));
|
|
|
|
|
|
if (_pdsministream != NULL)
|
|
{
|
|
_pdsministream->Release();
|
|
}
|
|
|
|
if (_pmpt != NULL)
|
|
{
|
|
_pmpt->Release();
|
|
}
|
|
|
|
msfDebugOut((DEB_ITRACE,"Leaving CMStream destructor\n"));
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::GetIterator, public
|
|
//
|
|
// Synposis: Create a new iterator for a given handle.
|
|
//
|
|
// Effects: Creates a new CMSFIterator
|
|
//
|
|
// Arguments: [sidParent] -- SID of entry to iterate over
|
|
// [ppitRetval] -- Location for return of iterator pointer
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// Algorithm: Create new iterator with parent of 'this' and nsi as given
|
|
// by handle.
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::GetIterator(
|
|
SID const sidParent,
|
|
CMSFIterator **ppitRetval)
|
|
{
|
|
SCODE sc;
|
|
|
|
msfDebugOut((DEB_TRACE,"In CMStream::GetIterator()\n"));
|
|
|
|
SID sidChild;
|
|
msfChk(_dir.GetChild(sidParent, &sidChild));
|
|
|
|
msfDebugOut((DEB_ITRACE, "Getting an iterator for SID = %lu, "
|
|
"sidChild is %lu\n", sidParent, sidChild));
|
|
msfMem(*ppitRetval = new CMSFIterator(GetDir(), sidChild));
|
|
msfDebugOut((DEB_TRACE,"Leaving CMStream::GetIterator()\n"));
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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.
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::Init(VOID)
|
|
{
|
|
ULONG ulTemp;
|
|
SCODE sc;
|
|
ULARGE_INTEGER ulOffset;
|
|
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::Init()\n"));
|
|
|
|
|
|
msfChk(InitCommon());
|
|
|
|
|
|
ULISet32(ulOffset, 0);
|
|
msfHChk((*_pplstParent)->ReadAt(ulOffset, (BYTE *)(&_hdr),
|
|
sizeof(CMSFHeader), &ulTemp));
|
|
_hdr.ByteSwap(); // swap to memory/machine format if neccessary
|
|
|
|
_uSectorShift = _hdr.GetSectorShift();
|
|
_uSectorSize = (USHORT) (1 << _uSectorShift);
|
|
_uSectorMask = (USHORT) (_uSectorSize - 1);
|
|
|
|
if (ulTemp != sizeof(CMSFHeader))
|
|
{
|
|
msfErr(Err,STG_E_INVALIDHEADER);
|
|
}
|
|
|
|
msfChk(_hdr.Validate());
|
|
|
|
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));
|
|
|
|
ULONGLONG ulSize;
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulSize));
|
|
msfMem(_pdsministream = new CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulSize);
|
|
|
|
msfDebugOut((DEB_TRACE,"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.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::InitNew(VOID)
|
|
{
|
|
SCODE sc;
|
|
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::InitNew()\n"));
|
|
|
|
|
|
msfChk(InitCommon());
|
|
|
|
ULARGE_INTEGER ulTmp;
|
|
|
|
ulTmp.QuadPart = 0;
|
|
(*_pplstParent)->SetSize(ulTmp);
|
|
|
|
msfChk(_fatDif.InitNew(this));
|
|
msfChk(_fat.InitNew(this));
|
|
msfChk(_dir.InitNew(this));
|
|
|
|
msfChk(_fatMini.InitNew(this));
|
|
|
|
ULONGLONG ulSize;
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulSize));
|
|
|
|
msfMem(_pdsministream = new CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulSize);
|
|
|
|
|
|
msfChk(Flush(0));
|
|
|
|
|
|
msfDebugOut((DEB_TRACE,"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
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::ConvertILB(SECT sectMax)
|
|
{
|
|
SCODE sc;
|
|
BYTE *pb;
|
|
USHORT cbNull;
|
|
|
|
GetSafeBuffer(GetSectorSize(), GetSectorSize(), &pb, &cbNull);
|
|
|
|
ULONG ulTemp;
|
|
|
|
ULARGE_INTEGER ulTmp;
|
|
ULISet32(ulTmp, 0);
|
|
|
|
msfHChk((*_pplstParent)->ReadAt(ulTmp, pb, GetSectorSize(), &ulTemp));
|
|
|
|
ULARGE_INTEGER ulNewPos;
|
|
ulNewPos.QuadPart = (ULONGLONG)(sectMax) << GetSectorShift();
|
|
|
|
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*
|
|
//
|
|
// Notes: We are allowed to fail here in low memory
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::InitConvert(VOID)
|
|
{
|
|
SCODE sc;
|
|
|
|
SECT sectMax;
|
|
|
|
msfChk(InitCommon());
|
|
|
|
STATSTG stat;
|
|
(*_pplstParent)->Stat(&stat, STATFLAG_NONAME);
|
|
|
|
|
|
sectMax = (SECT)((stat.cbSize.QuadPart + GetSectorSize() - 1) >>
|
|
GetSectorShift());
|
|
|
|
SECT sectMaxMini = 0;
|
|
BOOL fIsMini;
|
|
fIsMini = FALSE;
|
|
|
|
//If the CONTENTS stream will be in the Minifat, compute
|
|
// the number of Minifat sectors needed.
|
|
if (stat.cbSize.QuadPart < MINISTREAMSIZE)
|
|
{
|
|
sectMaxMini = (SECT)((stat.cbSize.QuadPart +
|
|
MINISECTORSIZE - 1) >> MINISECTORSHIFT);
|
|
fIsMini = TRUE;
|
|
}
|
|
|
|
|
|
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));
|
|
msfChk(_dir.SetSize(sid, stat.cbSize.QuadPart));
|
|
|
|
if (!fIsMini)
|
|
msfChk(_dir.SetStart(sid, sectMax - 1));
|
|
else
|
|
{
|
|
msfChk(_dir.SetStart(sid, 0));
|
|
msfChk(_dir.SetStart(SIDMINISTREAM, sectMax - 1));
|
|
msfChk(_dir.SetSize(SIDMINISTREAM, stat.cbSize.QuadPart));
|
|
}
|
|
|
|
ULONGLONG ulMiniSize;
|
|
msfChk(_dir.GetSize(SIDMINISTREAM, &ulMiniSize));
|
|
msfMem(_pdsministream = new CDirectStream(MINISTREAM_LUID));
|
|
_pdsministream->InitSystem(this, SIDMINISTREAM, ulMiniSize);
|
|
|
|
msfChk(ConvertILB(sectMax));
|
|
|
|
msfChk(Flush(0));
|
|
|
|
return S_OK;
|
|
|
|
Err:
|
|
Empty();
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: CMStream::GetName, public
|
|
//
|
|
// Synopsis: Given a handle, return the current name of that entry
|
|
//
|
|
// Arguments: [sid] -- SID to find name for.
|
|
//
|
|
// Returns: Pointer to name.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::GetName(SID const sid, CDfName *pdfn)
|
|
{
|
|
return _dir.GetName(sid, pdfn);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::FlushHeader(USHORT uForce)
|
|
{
|
|
ULONG ulTemp;
|
|
SCODE sc;
|
|
|
|
UNREFERENCED_PARM(uForce);
|
|
msfDebugOut((DEB_ITRACE,"In CMStream::FlushHeader()\n"));
|
|
|
|
ULARGE_INTEGER ulOffset;
|
|
ULISet32(ulOffset, 0);
|
|
_hdr.ByteSwap(); // swap to disk format if neccessary
|
|
sc = DfGetScode((*_pplstParent)->
|
|
WriteAt(ulOffset, (BYTE *)(&_hdr),
|
|
sizeof(CMSFHeader), &ulTemp));
|
|
_hdr.ByteSwap(); // swap to memort/machine format if neccessary
|
|
msfDebugOut((DEB_ITRACE,"Out CMStream::FlushHeader()\n"));
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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.
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
SCODE CMStream::MWrite(
|
|
SID sid,
|
|
BOOL fIsMini,
|
|
ULONGLONG ulOffset,
|
|
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;
|
|
ulOff.QuadPart = 0;
|
|
|
|
ULONGLONG ulOldSize = 0;
|
|
|
|
// Check if it's a small stream and whether this is a real
|
|
// multistream.
|
|
|
|
if ((fIsMini) &&
|
|
(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 = (USHORT) (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));
|
|
|
|
|
|
|
|
msfAssert(end != 0xffffffffL);
|
|
|
|
if (end < start)
|
|
{
|
|
*pulRetval = total + ulLastBytes;
|
|
goto Err;
|
|
}
|
|
|
|
ULONG ulRunLength;
|
|
ulRunLength = end - start + 1;
|
|
SECT sectSidStart;
|
|
|
|
USHORT offset;
|
|
offset = oStart;
|
|
|
|
while (TRUE)
|
|
{
|
|
SSegment segtab[CSEG + 1];
|
|
SECT sect;
|
|
|
|
if (start > pstmc->GetOffset())
|
|
{
|
|
msfChk(pfat->GetESect(
|
|
pstmc->GetSect(),
|
|
start - pstmc->GetOffset(),
|
|
§));
|
|
}
|
|
else if (start == pstmc->GetOffset())
|
|
{
|
|
sect = pstmc->GetSect();
|
|
}
|
|
else
|
|
{
|
|
msfChk(_dir.GetStart(sid, §SidStart));
|
|
msfChk(pfat->GetESect(sectSidStart, start, §));
|
|
}
|
|
|
|
msfChk(pfat->Contig(
|
|
(SSegment STACKBASED *) segtab,
|
|
sect,
|
|
ulRunLength));
|
|
|
|
USHORT oend = (USHORT) (cbSector - 1);
|
|
ULONG i = 0;
|
|
SECT sectStart = ENDOFCHAIN;
|
|
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 (segtab[iseg].sectStart == ENDOFCHAIN)
|
|
{
|
|
msfAssert(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
|
|
{
|
|
ulOff.QuadPart = ConvertSectOffset(sectStart, offset,
|
|
uShift);
|
|
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 = (SECT)((ulOldSize + GetSectorSize() - 1) >>
|
|
GetSectorShift());
|
|
|
|
ULONG csectNew = (SECT)((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;
|
|
}
|
|
|
|
pstmc->SetCache(start -1, sectStart + i - 1);
|
|
|
|
if (0 == ulRunLength || FAILED(sc))
|
|
{
|
|
*pulRetval = total + ulLastBytes;
|
|
msfDebugOut((
|
|
DEB_TRACE,
|
|
"Out CMStream::MWrite()=>%lu, retval = %lu\n",
|
|
sc,
|
|
total));
|
|
break;
|
|
}
|
|
}
|
|
|
|
Err:
|
|
// We need this flush of the directory structures because we may have
|
|
// remapped the first sector in a chain.
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMStream::Flush, public
|
|
//
|
|
// Synopsis: Flush control structures.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
SCODE CMStream::Flush(BOOL fFlushCache)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
msfChk(_dir.Flush());
|
|
msfChk(_fatMini.Flush());
|
|
msfChk(_fat.Flush());
|
|
msfChk(_fatDif.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:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
SCODE ILBFlush(ILockBytes *pilb, BOOL fFlushCache)
|
|
{
|
|
SCODE sc;
|
|
UNREFERENCED_PARM(fFlushCache); // no cache used here
|
|
|
|
msfDebugOut((DEB_ITRACE, "In ILBFlushCache(%p)\n", pilb));
|
|
|
|
sc = DfGetScode(pilb->Flush());
|
|
|
|
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:
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMStream::SecureSect(
|
|
const SECT sect,
|
|
const ULONGLONG ulSize,
|
|
const BOOL fIsMini)
|
|
{
|
|
SCODE sc = S_OK;
|
|
BYTE *pb = NULL;
|
|
|
|
ULONG cbSect = fIsMini ? MINISECTORSIZE : GetSectorSize();
|
|
|
|
msfAssert(ulSize != 0);
|
|
|
|
OFFSET ulOffset = (OFFSET)((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.
|
|
pb = (BYTE *)&_hdr;
|
|
|
|
ULONG cbWritten;
|
|
|
|
if (!fIsMini)
|
|
{
|
|
ULARGE_INTEGER ulOff;
|
|
ulOff.QuadPart = ConvertSectOffset( sect, ulOffset,
|
|
GetSectorShift());
|
|
|
|
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:
|
|
|
|
return sc;
|
|
}
|