//+-------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1992.
//
//  File:           mstream.cxx
//
//  Contents:       Mstream operations
//
//  Classes:        None. (defined in mstream.hxx)
//
//--------------------------------------------------------------------

#include "msfhead.cxx"


#include <dirfunc.hxx>
#include <sstream.hxx>
#include <difat.hxx>
#include <msfiter.hxx>
#include <time.h>
#include <mread.hxx>
#include <docfilep.hxx>


#if DEVL == 1

DECLARE_INFOLEVEL(msf)

#endif

#define MINPAGES 6
#define MAXPAGES 12


extern WCHAR const wcsContents[] = {'C','O','N','T','E','N','T','S','\0'};
extern 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 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
//
//--------------------------------------------------------------------------


MSTREAM_NEAR CMStream::CMStream(
        ILockBytes **pplstParent,
        USHORT uSectorShift)
:_uSectorShift(uSectorShift),
 _uSectorSize(1 << uSectorShift),
 _uSectorMask(_uSectorSize - 1),
 _pplstParent(pplstParent),
 _hdr(uSectorShift),
 _fatDif(1 << uSectorShift),
 _fat(SIDFAT, 1 << uSectorShift, uSectorShift),
 _dir(1 << uSectorShift),
 _fatMini(SIDMINIFAT, 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:  *Finish This*
//
//--------------------------------------------------------------------------

SCODE MSTREAM_NEAR CMStream::InitCommon(VOID)
{
    msfDebugOut((DEB_ITRACE,"In CMStream InitCommon()\n"));
    SCODE sc = S_OK;

    msfMem(_pmpt = new CMSFPageTable(this, MINPAGES, MAXPAGES));
    msfChk(_pmpt->Init());


    msfDebugOut((DEB_ITRACE,"Leaving CMStream InitCommon()\n"));

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
//
//--------------------------------------------------------------------------

MSTREAM_NEAR 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 MSTREAM_NEAR 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 MSTREAM_NEAR 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));

    _uSectorShift = _hdr.GetSectorShift();
    _uSectorSize = 1 << _uSectorShift;
    _uSectorMask = _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;
    msfChk(_fat.GetLength(_hdr.GetDirStart(), &fsiLen));
    msfChk(_dir.Init(this, fsiLen));

        msfChk(_fatMini.Init(this, _hdr.GetMiniFatLength(), 0));

        ULONG 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;

	ULISet32(ulTmp, 0);
        (*_pplstParent)->SetSize(ulTmp);

    msfChk(_fatDif.InitNew(this));
    msfChk(_fat.InitNew(this));
    msfChk(_dir.InitNew(this));

        msfChk(_fatMini.InitNew(this));
        
        ULONG 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 MSTREAM_NEAR 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;
    ULISet32(ulNewPos, sectMax << GetSectorShift());

    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*
//
//  Notes:	We are allowed to fail here in low memory
//
//--------------------------------------------------------------------------

SCODE CMStream::InitConvert(VOID)
{
    ULONG sc;

    SECT sectMax;

    
    msfChk(InitCommon());

    STATSTG stat;
    (*_pplstParent)->Stat(&stat, STATFLAG_NONAME);
    msfAssert(ULIGetHigh(stat.cbSize) == 0);
    msfDebugOut((DEB_ITRACE,"Size is: %lu\n",ULIGetLow(stat.cbSize)));


    sectMax = (ULIGetLow(stat.cbSize) + GetSectorSize() - 1) >>
        GetSectorShift();

    SECT sectMaxMini;
    BOOL fIsMini;
    fIsMini = FALSE;

    //If the CONTENTS stream will be in the Minifat, compute
    //  the number of Minifat sectors needed.
    if (ULIGetLow(stat.cbSize) < MINISTREAMSIZE)
    {
        sectMaxMini = (ULIGetLow(stat.cbSize) + 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, ULIGetLow(stat.cbSize)));

    if (!fIsMini)
        msfChk(_dir.SetStart(sid, sectMax - 1));
    else
    {
        msfChk(_dir.SetStart(sid, 0));
        msfChk(_dir.SetStart(SIDMINISTREAM, sectMax - 1));
        msfChk(_dir.SetSize(SIDMINISTREAM, ULIGetLow(stat.cbSize)));
    }

    ULONG 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 MSTREAM_NEAR 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 MSTREAM_NEAR CMStream::FlushHeader(USHORT uForce)
{
    ULONG ulTemp;
    SCODE sc;

    msfDebugOut((DEB_ITRACE,"In CMStream::FlushHeader()\n"));


    ULARGE_INTEGER ulOffset;
    ULISet32(ulOffset, 0);
    sc = DfGetScode((*_pplstParent)->WriteAt(ulOffset, (BYTE *)(&_hdr),
                                             sizeof(CMSFHeader), &ulTemp));
    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 MSTREAM_NEAR CMStream::MWrite(
        SID sid,
        BOOL fIsMini,
        ULONG 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;
    ULISetHigh(ulOff, 0);

    ULONG ulOldSize = 0;
    
    //  Check if it's a small stream and whether this is a real or
    //  scratch 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 = 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(),
                    &sect));
        }
        else if (start == pstmc->GetOffset())
        {
            sect = pstmc->GetSect();
        }
        else
        {
            msfChk(_dir.GetStart(sid, &sectSidStart));
            msfChk(pfat->GetESect(sectSidStart, start, &sect));
        }

        msfChk(pfat->Contig(
                (SSegment STACKBASED *) segtab,
                sect,
                ulRunLength));

        USHORT oend = cbSector - 1;
        ULONG i;
        SECT sectStart;
        for (USHORT iseg = 0; iseg < CSEG;)
        {
            sectStart = segtab[iseg].sectStart;
            i = segtab[iseg].cSect;
            ulRunLength -= i;
            start += i;

            iseg++;
            if (segtab[iseg].sectStart == ENDOFCHAIN)
                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
            {
                ULISetLow(ulOff, 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 = (ulOldSize + GetSectorSize() - 1) >>
                    GetSectorShift();

                ULONG csectNew = (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;

    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 ULONG ulSize,
        const BOOL fIsMini)
{
    SCODE sc = S_OK;
    BYTE *pb = NULL;
    
        ULONG cbSect = fIsMini ? MINISECTORSIZE : GetSectorSize();
        
        msfAssert(ulSize != 0);
        
        ULONG ulOffset = ((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;
            ULISet32(ulOff, 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;
}
