//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1992.
//
//  File:       difat.cxx
//
//  Contents:   Double Indirected Fat Code
//
//  Classes:    None.
//
//  Functions:
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#include <msfhead.cxx>

#pragma hdrstop			//  Remove for MAC build

#include <difat.hxx>
#include <mread.hxx>

#ifndef REF
//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::CDIFat, public
//
//  Synopsis:   CDIFat copy constructor
//
//  Arguments:  [fatOld] -- reference to CDIFat to be copied.
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_CDIFat)
#endif

CDIFat::CDIFat(CDIFat *pfatOld)
        :   _pmsParent(NULL),
            _fv(SIDDIF)
{
}
#endif //!REF

//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::CDIFat, public
//
//  Synopsis:   CDIFat constructor
//
//  Arguments:  [pmsParent] -- Pointer to parent CMStream
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_CDIFat2)
#endif

CDIFat::CDIFat()
:   _pmsParent(NULL),
    _fv(SIDDIF)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat constructor\n"));
    _cfsTable = 0;
    msfDebugOut((DEB_ITRACE,"Out CDIFat constructor\n"));
}


//+---------------------------------------------------------------------------
//
//  Member:	CDIFat::Empty, public
//
//  Synopsis:	Empty all the control structures of this instance
//
//  Arguments:	None.
//
//  Returns:	void.
//
//  History:	04-Dec-92	PhilipLa	Created
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Empty)
#endif

void CDIFat::Empty(void)
{
    _fv.Empty();
    _pmsParent = NULL;
    _cfsTable = 0;
}


//+-------------------------------------------------------------------------
//
//  Method:     CIDFat::Flush, private
//
//  Synopsis:   Flush a sector to disk
//
//  Arguments:  [oSect] -- Indicated which sector to flush
//
//  Returns:    S_OK if call completed OK.
//
//  Algorithm:  Write sector up to parent mstream.
//
//  History:    11-May-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Flush)
#endif

SCODE CDIFat::Flush(void)
{
    return _fv.Flush();
}


//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::GetFatSect, public
//
//  Synopsis:   Given an offset into the Fat chain, return the sector
//              value for that FatSect.
//
//  Arguments:  [oSect] -- offset in Fat chain
//
//  Returns:    Sector value of FatSect.
//
//  Algorithm:  If sector is stored in the header, retrieve it from
//                  there.
//              If not, retrieve it from the FatVector.
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_GetFatSect)
#endif

SCODE CDIFat::GetFatSect(const FSINDEX oSect, SECT *psect)
{
    SCODE sc = S_OK;
    SECT sectReturn;

    msfDebugOut((DEB_ITRACE,"In CDIFat::GetFatSect(%lu)\n",oSect));
    if (oSect < CSECTFAT)
    {
        msfDebugOut((DEB_ITRACE,"Getting sect from header\n"));
        sectReturn = _pmsParent->GetHeader()->GetFatSect(oSect);
    }
    else
    {
        FSINDEX ipfs;
        FSOFFSET isect;

        SectToPair(oSect,&ipfs,&isect);

        msfAssert(ipfs < _cfsTable);

        CFatSect *pfs;
        msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs));
        sectReturn = pfs->GetSect(isect);
        _fv.ReleaseTable(ipfs);
    }

    msfDebugOut((DEB_ITRACE,"Out CDIFat::GetFatSect(%lu)=>%lu\n",oSect,sectReturn));
    *psect = sectReturn;

Err:
    return sc;
}



//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::SetFatSect, public
//
//  Synopsis:   Given an offset into the Fat chain, set the sector
//              value.
//
//  Arguments:  [oSect] -- Offset into fat chain
//              [sect] -- New sector value for that offset.
//
//  Returns:    S_OK if call completed OK.
//
//  Algorithm:  If the sector is stored in the header, set it and
//                  flush the header.
//              Otherwise, if the sector will not fit in the current
//                  CFatVector, resize it.
//              Set the sector in the FatVector and flush it.
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_SetFatSect)
#endif

SCODE CDIFat::SetFatSect(const FSINDEX oSect, const SECT sect)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::SetFatSect(%lu,%lu)\n",oSect,sect));
    SCODE sc = S_OK;

    if (oSect < CSECTFAT)
    {
        msfDebugOut((DEB_ITRACE,"Setting sect in header: %lu, %lu\n",oSect,sect));
        _pmsParent->GetHeader()->SetFatSect(oSect, sect);
    }
    else
    {
        FSINDEX ipfs;
        FSOFFSET isect;

        SectToPair(oSect,&ipfs,&isect);
        if (ipfs >= _cfsTable)
        {
            msfChk(Resize(_cfsTable + 1));
        }

        CFatSect *pfs;
        msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs));

        pfs->SetSect(isect, sect);
        _fv.ReleaseTable(ipfs);

        msfDebugOut((DEB_ITRACE,"In CDIFat::SetFatSect(%lu,%lu)\n",oSect,sect));
    }

Err:
    return sc;
}


//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::GetSect, public
//
//  Synopsis:   Given an offset into the DIFat chain, return the
//              sector value
//
//  Arguments:  [oSect] -- Offset into DIFat chain.
//
//  Returns:    Sector value for given offset.
//
//  Algorithm:  Retrieve the information from the NextFat fields of
//                  the CFatVector
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_GetSect)
#endif

SCODE CDIFat::GetSect(const FSINDEX oSect, SECT *psect)
{
    SCODE sc = S_OK;

    SECT sectReturn;

    msfDebugOut((DEB_ITRACE,"In CDIFat::GetSect(%lu)\n",oSect));
    msfAssert(oSect < _cfsTable);

    sectReturn = _pmsParent->GetHeader()->GetDifStart();

    //Possible optimization here:  Do some sort of check to
    //find the last page that's actually cached in the vector,
    //then start the loop from there.  Probably not a terribly
    //important optimization, but it is possible.
    for (FSINDEX oCurrent = 0; oCurrent < oSect; oCurrent++)
    {
        msfAssert(sectReturn != ENDOFCHAIN);
        
        CFatSect *pfs;
        msfChk(_fv.GetTableWithSect(oCurrent,
                                    FB_NONE,
                                    sectReturn,
                                    (void **)&pfs));
        
        sectReturn = pfs->GetNextFat(_fv.GetSectTable());
        _fv.ReleaseTable(oCurrent);
    }

    msfDebugOut((DEB_ITRACE,"Out CDIFat::GetSect(%lu)=>%lu\n",oSect,sectReturn));
    *psect = sectReturn;

Err:
    return sc;
}


//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::Init, public
//
//  Synopsis:   Init function for previously stored DIFat.
//
//  Arguments:  [cFatSect] -- Length of DIFat in sectors
//
//  Returns:    S_OK if call completed properly.
//
//  Algorithm:  *Finish This*
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Init)
#endif

SCODE CDIFat::Init(CMStream * pmsParent, const FSINDEX cFatSect)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::Init(%lu)\n",cFatSect));
    SCODE sc;

    _pmsParent = P_TO_BP(CMStream DFBASED *, pmsParent);
    ULONG cbSector = pmsParent->GetSectorSize();
    _fv.InitCommon(
            (FSOFFSET)(cbSector / sizeof(SECT)),
            (FSOFFSET)((cbSector / sizeof(SECT)) - 1));

    msfChk(_fv.Init(pmsParent, cFatSect));

    _cfsTable = cFatSect;

    msfDebugOut((DEB_ITRACE,"Out CDIFat::Init(%lu)\n",cFatSect));

Err:
    return sc;
}


//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::InitNew, public
//
//  Synopsis:   Init function for new DIFat
//
//  Arguments:  [pmsParent] -- Pointer to parent multistream
//
//  Returns:    S_OK if call completed successfully.
//
//  History:    11-May-92   PhilipLa    Created.
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_InitNew)
#endif

SCODE CDIFat::InitNew(CMStream *pmsParent)
{
    _pmsParent = P_TO_BP(CMStream DFBASED *, pmsParent);

    ULONG cbSector = pmsParent->GetSectorSize();
    _fv.InitCommon(
            (FSOFFSET)(cbSector / sizeof(SECT)),
            (FSOFFSET)((cbSector / sizeof(SECT)) - 1));
    _fv.Init(pmsParent, 0);
    _cfsTable = 0;
    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::InitConvert, public
//
//  Synopsis:   Init function for conversion
//
//  Arguments:  [sectMax] -- Last used sector in existing file
//
//  Returns:    S_OK if call completed OK.
//
//  Algorithm:  *Finish This*
//
//  History:    02-Jun-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_InitConvert)
#endif

SCODE CDIFat::InitConvert(CMStream *pmsParent, SECT sectMax)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::InitConvert(%lu)\n",sectMax));
    SCODE sc;

    _pmsParent = P_TO_BP(CMStream DFBASED *, pmsParent);

    USHORT cbSector = pmsParent->GetSectorSize();
    FSOFFSET csectPer = cbSector / sizeof(SECT);

    FSINDEX csectFat = 0;
    FSINDEX csectFatLast;

    FSINDEX csectDif = 0;
    FSINDEX csectDifLast;
    do
    {
        //Number of fat sectors needed to represent:
        //    Number of Data Sectors (sectMax) +
        //    Number of Fat Sectors (csectFat) +
        //    Number of DIF sectors (csectDif) +
        //    Number of Directory Sectors (1)

        //We must use a loop here, since the fat must be large
        //    enough to represent itself and the DIFat.  See
        //    CFat::InitConvert for a more lengthy discussion of
        //    this method.

        csectFatLast = csectFat;

        csectFat = (sectMax + csectFatLast + csectDif + 1 + csectPer - 1) /
            csectPer;

        csectDifLast = csectDif;

        if (csectFat < CSECTFAT)
        {
            csectDif = 0;
        }
        else
        {
            FSOFFSET ciSect;

            SectToPair(csectFat, &csectDif, &ciSect);
            csectDif++;
        }
    }
    while ((csectDif != csectDifLast) || (csectFat != csectFatLast));


    _cfsTable = csectDif;

    cbSector = pmsParent->GetSectorSize();
    _fv.InitCommon(cbSector / sizeof(SECT), (cbSector / sizeof(SECT)) - 1);

    msfChk(_fv.Init(pmsParent, _cfsTable));

    pmsParent->GetHeader()->SetDifLength(_cfsTable);

    if (_cfsTable > 0)
    {
        pmsParent->GetHeader()->SetDifStart(sectMax);

        FSINDEX i;
        for (i = 0; i < _cfsTable; i++)
        {
            CFatSect *pfs;

            msfChk(_fv.GetTable(i, FB_NEW, &pfs));
            _fv.SetSect(i, sectMax);

            sectMax++;
            pfs->SetNextFat(_fv.GetSectTable(),sectMax);
            _fv.ReleaseTable(i);
        }
    }

    msfDebugOut((DEB_ITRACE,"Out CDIFat::InitConvert()\n"));

Err:
    return sc;
}


#ifndef REF

//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::Remap, public
//
//  Synopsis:   Remap a Fat sector for copy-on-write
//
//  Arguments:  [oSect] -- Offset into fat chain to be remapped.
//              [psectReturn] -- pointer to return value
//
//  Returns:    S_OK if call was successful.
//
//  Algorithm:  *Finish This*
//
//  History:    11-May-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Remap)
#endif

SCODE CDIFat::Remap(const FSINDEX oSect, SECT * psectReturn)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::Remap(%lu)\n",oSect));
    SCODE sc = S_OK;

    SECT sectNew;
    SECT sectTemp;
    msfChk(GetFatSect(oSect, &sectTemp));

    msfChk(_pmsParent->GetFat()->QueryRemapped(sectTemp));

    if (sc == S_FALSE)
    {
        msfChk(_pmsParent->GetFat()->GetFree(1, &sectNew, GF_READONLY));

        msfDebugOut((DEB_ITRACE,"Remapping fat sector %lu from %lu to %lu\n",oSect,sectTemp,sectNew));

        msfChk(SetFatSect(oSect, sectNew));
    }
    else
    {
        sectNew = ENDOFCHAIN;
    }
    msfDebugOut((DEB_ITRACE,"In CDIFat::Remap(%lu)=>%lu\n",oSect,sectNew));
    *psectReturn = sectNew;

Err:
    return sc;
}


//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::RemapSelf, public
//
//  Synopsis:   Remap the entire DIFat for copy-on-write.
//
//  Arguments:  None.
//
//  Returns:    S_OK if DIFat was remapped successfully.
//
//  Algorithm:  *Finish This*
//
//  History:    11-May-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_RemapSelf)
#endif

SCODE CDIFat::RemapSelf(VOID)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::RemapSelf()\n"));
    SECT sectNew, sect;
    SCODE sc;
    CFatSect *pfs;
    FSINDEX i = 0;

    if (_cfsTable > 0)
    {
        sect = _pmsParent->GetHeader()->GetDifStart();

        msfChk(_fv.GetTable(0, FB_DIRTY, &pfs));
        msfChkTo(Err_Rel0, _pmsParent->GetFat()->GetFree(1, &sectNew, GF_READONLY));

        _pmsParent->GetHeader()->SetDifStart(sectNew);

        _fv.SetSect(0, sectNew);

        _fv.ReleaseTable(0);
    }


    for (i = 1; i < _cfsTable; i++)
    {
        CFatSect *pfs2;

        msfChk(_fv.GetTable(i - 1, FB_DIRTY, &pfs));
        msfChkTo(Err_Rel1, _fv.GetTable(i, FB_DIRTY, &pfs2));

        msfChkTo(Err_Rel, _pmsParent->GetFat()->GetFree(1, &sectNew, GF_READONLY));

        pfs->SetNextFat(_fv.GetSectTable(), sectNew);
        _fv.SetSect(i, sectNew);

        _fv.ReleaseTable(i - 1);
        _fv.ReleaseTable(i);
    }
    msfDebugOut((DEB_ITRACE,"In CDIFat::RemapSelf()\n"));

Err:
    return sc;

Err_Rel0:
    _fv.ReleaseTable(0);
    return sc;
Err_Rel:
    _fv.ReleaseTable(i);
Err_Rel1:
    _fv.ReleaseTable(i - 1);
    return sc;
}
#endif //!REF



//+-------------------------------------------------------------------------
//
//  Method:     CDIFat::Resize, private
//
//  Synopsis:   Resize an existing DIFat.
//
//  Arguments:  [fsiSize] -- New size for object
//
//  Returns:    Nothing right now.
//
//  Algorithm:  *Finish This*
//
//  History:    11-May-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Resize)
#endif

SCODE CDIFat::Resize(FSINDEX fsiSize)
{
    msfDebugOut((DEB_ITRACE,"In CDIFat::Resize(%lu)\n",fsiSize));
    msfAssert(fsiSize == _cfsTable + 1);

    SCODE sc;

    msfChk(_fv.Resize(fsiSize));
    ULONG ipfs;
    ipfs = fsiSize - 1;

    CFatSect *pfs;
    msfChk(_fv.GetTable(ipfs, FB_NEW, &pfs));

    FSINDEX csect;
    csect = _cfsTable;
    _cfsTable = fsiSize;

    SECT sectNew;

#ifndef REF
    msfChk(_pmsParent->GetFat()->GetFree(1, &sectNew, GF_WRITE));
#else
    msfChk(_pmsParent->GetFat()->GetFree(1, &sectNew));
#endif //!REF
    msfChk(_pmsParent->GetFat()->SetNext(sectNew, DIFSECT));

#ifndef REF
#if DBG == 1
    STATSTG stat;

    msfHChk(_pmsParent->GetILB()->Stat(&stat, STATFLAG_NONAME));
    msfAssert(
            ConvertSectOffset(sectNew + 1, 0, _pmsParent->GetSectorShift()) <=
            ULIGetLow(stat.cbSize));
#endif
#endif //!REF


    _fv.SetSect(ipfs, sectNew);
    _fv.ReleaseTable(ipfs);

    if (csect == 0)
    {
        _pmsParent->GetHeader()->SetDifStart(sectNew);
    }
    else
    {
        CFatSect *pfs;
        msfChk(_fv.GetTable(csect - 1, FB_DIRTY, &pfs));

        pfs->SetNextFat(_fv.GetSectTable(),sectNew);
        _fv.ReleaseTable(csect - 1);
    }

    _pmsParent->GetHeader()->SetDifLength(_cfsTable);

    msfDebugOut((DEB_ITRACE,"Out CDIFat::Resize(%lu)\n",fsiSize));

Err:
    return sc;
}



#ifndef REF
//+---------------------------------------------------------------------------
//
//  Member:	CDIFat::Lookup, public
//
//  Synopsis:	Lookup a sector and see if it is in the DIFat.  If it
//              is, return FATSECT if it is allocated to a fat sector
//              or DIFSECT if it is allocated to the DIF.
//
//  Arguments:	[sect] -- Sector to look up
//              [psectRet] -- Location for return value
//              [fReadOnly] -- If FALSE, decrement the dirty count
//                              after returning the page.
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	23-Feb-93	PhilipLa	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Lookup)
#endif

SCODE CDIFat::Lookup(const SECT sect, SECT *psectRet)
{
    SCODE sc = S_OK;

    *psectRet = FREESECT;

    for (FSINDEX i = 0; i < _pmsParent->GetHeader()->GetDifLength(); i++)
    {
        SECT sectDif;

        msfChk(GetSect(i, &sectDif));
        if (sectDif == sect)
        {
            *psectRet = DIFSECT;
            return S_OK;
        }
    }

    for (i = 0; i < _pmsParent->GetHeader()->GetFatLength(); i++)
    {
        SECT sectFat;

        msfChk(GetFatSect(i, &sectFat));
        if (sectFat == sect)
        {
            *psectRet = FATSECT;
            return S_OK;
        }
    }

Err:
    return sc;
}



//+---------------------------------------------------------------------------
//
//  Member:	CDIFat::Fixup, public
//
//  Synopsis:	Fixup the Fat after a copy-on-write commit.
//
//  Arguments:	[pmsShadow] -- Pointer to shadow multistream
//
//  Returns:	Appropriate status code
//
//  Modifies:	
//
//  History:	23-Feb-93	PhilipLa	Created
//
//  Notes:	
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CDIFat_Fixup)
#endif

SCODE CDIFat::Fixup(CMStream *pmsShadow)
{
    SCODE sc = S_OK;

    BOOL fChanged;

    CFat *pfat = _pmsParent->GetFat();

    CDIFat *pdifOld = pmsShadow->GetDIFat();
    FSINDEX cFatOld = pmsShadow->GetHeader()->GetFatLength();
    FSINDEX cDifOld = pmsShadow->GetHeader()->GetDifLength();

    for (FSINDEX i = 0; i < _pmsParent->GetHeader()->GetDifLength(); i++)
    {
        SECT sectNew;
        SECT sectOld = ENDOFCHAIN;
        SECT sectCurrent;

        msfChk(GetSect(i, &sectNew));

        if (i < cDifOld)
            msfChk(pdifOld->GetSect(i, &sectOld));

        if ((sectNew != sectOld) || (sectOld == ENDOFCHAIN))
        {
            msfChk(pfat->GetNext(sectNew, &sectCurrent));
            if (sectCurrent != DIFSECT)
            {
                msfChk(pfat->SetNext(sectNew, DIFSECT));
            }

            if (sectOld != ENDOFCHAIN)
            {
                msfChk(pfat->GetNext(sectOld, &sectCurrent));
                if (sectCurrent != FREESECT)
                {
                    msfChk(pfat->SetNext(sectOld, FREESECT));
                }
            }
        }
    }


    do
    {
        fChanged = FALSE;

        for (i = 0; i < _pmsParent->GetHeader()->GetFatLength(); i++)
        {
            SECT sectNew;
            SECT sectCurrent;
            SECT sectOld = ENDOFCHAIN;

            msfChk(GetFatSect(i, &sectNew));

            if (i < cFatOld)
                msfChk(pdifOld->GetFatSect(i, &sectOld));

            if ((sectNew != sectOld) || (sectOld == ENDOFCHAIN))
            {
                //Sector has been remapped.
                msfChk(pfat->GetNext(sectNew, &sectCurrent));

                if (sectCurrent != FATSECT)
                {
                    msfChk(pfat->SetNext(sectNew, FATSECT));
                    fChanged = TRUE;
                }

                if (sectOld != ENDOFCHAIN)
                {
                    msfChk(pfat->GetNext(sectOld, &sectCurrent));
                    if (sectCurrent != FREESECT)
                    {
                        msfChk(pfat->SetNext(sectOld, FREESECT));
                        fChanged = TRUE;
                    }
                }
            }
        }

    }
    while (fChanged);


Err:
    return sc;
}
#endif //!REF

