diff options
Diffstat (limited to 'private/ole32/stg/msf/fat.cxx')
-rw-r--r-- | private/ole32/stg/msf/fat.cxx | 3562 |
1 files changed, 3562 insertions, 0 deletions
diff --git a/private/ole32/stg/msf/fat.cxx b/private/ole32/stg/msf/fat.cxx new file mode 100644 index 000000000..68adba6f6 --- /dev/null +++ b/private/ole32/stg/msf/fat.cxx @@ -0,0 +1,3562 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1992. +// +// File: fat.cxx +// +// Contents: Allocation functions for MStream +// +// Classes: None. (defined in fat.hxx) +// +// History: 18-Jul-91 PhilipLa Created. +// +//-------------------------------------------------------------------------- + +#include "msfhead.cxx" + +#pragma hdrstop + +#include <difat.hxx> +#include <sstream.hxx> +#include <mread.hxx> + +#define USE_NOSCRATCHINMARKSECT + +#ifndef REF +//#define SECURETEST +#endif //!REF +//+--------------------------------------------------------------------------- +// +// Member: CFat::IsFree, private +// +// Synopsis: Check and see if a given sector is really free, given all +// the permutations of ways a sector can be assigned. +// +// Arguments: [sect] -- Sector to check +// +// Returns: S_OK if sector is really free. +// S_FALSE if sector is really allocated. +// Appropriate error in error case. +// +// Modifies: +// +// History: 30-Mar-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +inline SCODE CFat::IsFree(SECT sect) +{ + SCODE sc = S_OK; + SECT sectCurrent = FREESECT; + + msfDebugOut((DEB_ITRACE, "In CFat::IsFree:%p()\n", this)); + +#ifdef USE_NOSNAPSHOT + if (sect < _sectNoSnapshot) + { + return S_FALSE; + } + if ((_sectNoSnapshotFree != ENDOFCHAIN) && (sect < _sectNoSnapshotFree)) + { + return S_FALSE; + } +#endif + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + msfAssert((!_pmsParent->IsScratch()) && + aMsg("Scratch MS in Noscratch mode")); + + FSINDEX ipfs; + FSOFFSET isect; + + _pfatNoScratch->SectToPair(sect, &ipfs, &isect); + + if (ipfs < _pfatNoScratch->_cfsTable) + { + //We need to check the NoScratch fat to make sure + //that this sector isn't already allocated into + //scratch space for a stream. + + msfChk(_pfatNoScratch->GetNext( + sect, + §Current)); + } + } + //Since the no-scratch fat will have a complete copy of everything + // in the shadow fat, we don't need to check both. So only do this + // when we're not in no-scratch mode. + //The no-scratch fat also has a copy of the read-only GetFree() + // returned stuff, so we only need to do that check when not in + // no-scratch. + else + { +#endif + if (_cUnmarkedSects > 0) + { + //We are in copy-on-write mode. Lookup this + //sector in the DIF and make sure that it is + //actually free. + msfAssert((_sectLastUsed > 0) && + aMsg("Doing DIFat check when not in COW.")); + + msfChk(_pmsParent->GetDIFat()->Lookup( + sect, + §Current)); + } + + if ((sect < _sectLastUsed) && (sectCurrent == FREESECT)) + { + //We're in copy-on-write mode, so + // this sector may not be really free. + msfAssert(_sid != SIDMINIFAT && + aMsg("Minifat in Copy-on-Write mode")); + msfAssert(_pfatReal != NULL && + aMsg("Copy-On-Write mode without fat copy")); + msfAssert(_pfatReal->_cfsTable != 0 && + aMsg("Copy-on-Write mode with empty fat copy")); + msfAssert(_pfatReal->_pmsParent->IsShadow() && + aMsg("Copy-on-Write mode with non-shadow fat copy")); + + msfChk(_pfatReal->GetNext(sect, §Current)); + } +#ifdef USE_NOSCRATCH + } +#endif + + if (sectCurrent != FREESECT) + { + sc = S_FALSE; + } + + msfDebugOut((DEB_ITRACE, "Out CFat::IsFree\n")); + +Err: + return sc; +} + +//+------------------------------------------------------------------------- +// +// Method: CFatSect::Init, public +// +// Synopsis: CFatSect initialization function +// +// Effects: [uEntries] -- Number of entries in sector +// +// Algorithm: Allocate an array of SECT with size uEntries from +// the heap. +// +// History: 18-Jul-91 PhilipLa Created. +// 27-Dec-91 PhilipLa Converted to dynamic allocation +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFatSect_Init) +#endif + +SCODE FAT_CLASS CFatSect::Init(FSOFFSET uEntries) +{ + msfDebugOut((DEB_FAT,"In CFatSect constructor\n")); + + //This assumes that FREESECT is always 0xFFFFFFFF + memset(_asectEntry, 0xFF, uEntries * sizeof(SECT)); + + msfDebugOut((DEB_FAT,"Out CFatSect constructor\n")); + return S_OK; +} + + + +//+------------------------------------------------------------------------- +// +// Method: CFatSect::InitCopy, public +// +// Synopsis: Initialization function for copying FatSects +// +// Arguments: [fsOld] -- Reference to FatSect to be copies +// +// Returns: S_OK if call completed successfully. +// +// Algorithm: Allocate a new array of SECT and copy old +// information in. +// +// History: 06-Feb-92 PhilipLa Created. +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFatSect_InitCopy) +#endif + +SCODE FAT_CLASS CFatSect::InitCopy(USHORT uSize, CFatSect *pfsOld) +{ + msfDebugOut((DEB_FAT,"In CFatSect copy constructor\n")); + msfDebugOut((DEB_FAT,"This = %p, fsOld = %p\n",this, pfsOld)); + + msfDebugOut((DEB_FAT,"Sector size is %u sectors\n", uSize)); + + memcpy(_asectEntry, &pfsOld->_asectEntry, sizeof(SECT)*uSize); + msfDebugOut((DEB_FAT,"Out CFatSect copy constructor\n")); + return S_OK; +} + + +//+------------------------------------------------------------------------- +// +// Method: CFat::CFat, public +// +// Synopsis: CFat constructor. +// +// Arguments: [pmsParent] -- Pointer to parent multistream. +// +// Algorithm: Set uFatEntries to match parent MS header info. +// Initialize all member variables. +// +// History: 18-Jul-91 PhilipLa Created. +// 27-Dec-91 PhilipLa Converted to use FatVector +// 30-Dec-91 PhilipLa Converted to use variable size sect. +// +// Notes: +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_CFat) +#endif + +FAT_CLASS CFat::CFat(SID sid) +: _fv(sid), +#ifndef REF + _pfatReal(NULL), +#endif //!REF +#ifdef USE_NOSCRATCH + _pfatNoScratch(NULL), +#endif +#ifdef USE_NOSNAPSHOT + _sectNoSnapshot(0), + _sectNoSnapshotFree(ENDOFCHAIN), +#endif + _sid(sid), + _pmsParent(NULL), + _sectFirstFree(0), +#ifndef REF + _sectLastUsed(0), +#endif //!REF + _sectMax(ENDOFCHAIN) +{ +#ifndef REF + _cUnmarkedSects = 0; +#endif //!REF +} + + +#ifndef REF +//+------------------------------------------------------------------------- +// +// Method: CFat::CFat, public +// +// Synopsis: CFat copy constructor +// +// Arguments: [fatOld] -- Fat to be copied. +// +// Algorithm: Set member variables to match fat being copied. +// +// History: 27-Dec-91 PhilipLa Created. +// +// Notes: This is for use in transactioning. It is the only proper +// way to create a Shadow Fat. +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_CFat2) +#endif + +FAT_CLASS CFat::CFat(CFat *pfatOld) + : _pmsParent(pfatOld->_pmsParent), + _fv(pfatOld->_sid), + _pfatReal(NULL), + _sid(pfatOld->_sid), + _sectFirstFree(0), + _sectLastUsed(0), + _sectMax(ENDOFCHAIN), + _sectNoSnapshot(pfatOld->_sectNoSnapshot) +{ +#ifndef REF + _cUnmarkedSects = 0; +#endif //!REF +} + +//+------------------------------------------------------------------------- +// +// Method: CFat::InitCopy, public +// +// Synopsis: Init function for CFat copying +// +// Arguments: [fatOld] -- reference to CFat to be copied +// +// Returns: S_OK if call completed OK. +// +// Algorithm: *Finish This* +// +// History: 18-Feb-92 PhilipLa Created. +// +// Notes: +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_InitCopy) +#endif + +void FAT_CLASS CFat::InitCopy(CFat *pfatOld) +{ + msfDebugOut((DEB_FAT,"In CFat Copy Constructor\n")); + + _pmsParent = pfatOld->_pmsParent; + _uFatShift = _pmsParent->GetSectorShift() - 2; + _uFatMask = (_pmsParent->GetSectorSize() >> 2) - 1; + + _fv.InitCommon(1 << _uFatShift, 1 << _uFatShift); + + _cfsTable = pfatOld->_cfsTable; + + _fv.InitCopy(&pfatOld->_fv); + + _ulFreeSects = MAX_ULONG; + _sectFirstFree = pfatOld->_sectFirstFree; + _sectLastUsed = pfatOld->_sectLastUsed; + _sectMax = pfatOld->_sectMax; + + msfDebugOut((DEB_FAT,"Out CFat Copy Constructor\n")); +} +#endif //!REF + + +//+--------------------------------------------------------------------------- +// +// Member: CFat::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_CFat_Empty) +#endif + +void CFat::Empty(void) +{ + _fv.Empty(); + _pmsParent = NULL; +#ifndef REF + _pfatReal = NULL; +#endif //!REF + _cfsTable = 0; + _ulFreeSects = MAX_ULONG; + _sectFirstFree = 0; +#ifndef REF + _sectLastUsed = 0; +#endif //!REF + _sectMax = ENDOFCHAIN; +#ifndef REF + _cUnmarkedSects = 0; +#endif //!REF +} + + +//+------------------------------------------------------------------------- +// +// Method: CFat::~CFat, public +// +// Synopsis: CFat Destructor +// +// Algorithm: delete dynamically allocated storage +// +// History: 18-Jul-91 PhilipLa Created. +// 27-Jul-91 PhilipLa Converted to use FatVect. +// +// Notes: +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_1CFat) // inline? +#endif + +FAT_CLASS CFat::~CFat() +{ + msfDebugOut((DEB_FAT,"In CFat destructor. Size of fat is %lu\n",_cfsTable)); + + msfDebugOut((DEB_FAT,"Exiting CFat destructor\n")); +} + +#ifdef USE_NEW_GETFREE +struct SGetFreeStruct +{ + CVectBits *pfb; + + SECT sect; + CFatSect *pfs; + FSOFFSET isect; + FSINDEX ipfs; + + SECT sectLast; + FSINDEX ipfsLast; + FSOFFSET isectLast; + +#ifdef USE_NOSCRATCH +#ifdef USE_NOSCRATCHINMARKSECT + CFatSect *pfsNoScratch; + FSINDEX ipfsNoScratch; +#endif +#endif // USE_NOSCRATCH +}; + + + +//+--------------------------------------------------------------------------- +// +// Member: CFat::InitGetFreeStruct, private +// +// Synopsis: Initialize an SGetFreeStruct +// +// Arguments: [pgf] -- Pointer to structure to initialize +// +// History: 03-Nov-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +void CFat::InitGetFreeStruct(SGetFreeStruct *pgf) +{ + pgf->sectLast = ENDOFCHAIN; + pgf->pfs = NULL; +#ifdef USE_NOSCRATCH +#ifdef USE_NOSCRATCHINMARKSECT + pgf->pfsNoScratch = NULL; +#endif +#endif +} + + +//+--------------------------------------------------------------------------- +// +// Member: CFat::ReleaseGetFreeStruct, private +// +// Synopsis: Release an SGetFreeStruct +// +// Arguments: [pgf] -- Pointer to structure to release +// +// History: 03-Nov-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +void CFat::ReleaseGetFreeStruct(SGetFreeStruct *pgf) +{ + if (pgf->pfs != NULL) + { + _fv.ReleaseTable(pgf->ipfs); + } +#ifdef USE_NOSCRATCH +#ifdef USE_NOSCRATCHINMARKSECT + if (pgf->pfsNoScratch != NULL) + { + _pfatNoScratch->_fv.ReleaseTable(pgf->ipfsNoScratch); + } +#endif +#endif +} + +//+--------------------------------------------------------------------------- +// +// Member: CFat::MarkSect, private +// +// Synopsis: Mark a sector as used, for use from GetFree and GetFreeContig +// +// Arguments: [pgf] -- Pointer to SGetFreeStruct with information about +// which sector to mark. This structure also returns +// information to the caller, so that state can be +// preserved across multiple calls for optimization. +// +// Returns: Appropriate status code +// +// History: 27-Oct-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +inline SCODE CFat::MarkSect(SGetFreeStruct *pgf) +{ + msfAssert(_ulFreeSects != MAX_ULONG && + aMsg("Free sect count not set")); + + SCODE sc = S_OK; + + _ulFreeSects--; + + CVectBits * &pfb = pgf->pfb; + + SECT § = pgf->sect; + FSOFFSET &isect = pgf->isect; + FSINDEX &ipfs = pgf->ipfs; + CFatSect * &pfs = pgf->pfs; +#ifdef USE_NOSCRATCH +#ifdef USE_NOSCRATCHINMARKSECT + CFatSect * &pfsNoScratch = pgf->pfsNoScratch; + FSINDEX &ipfsNoScratch = pgf->ipfsNoScratch; +#endif +#endif + + SECT §Last = pgf->sectLast; + FSINDEX &ipfsLast = pgf->ipfsLast; + FSOFFSET &isectLast = pgf->isectLast; + + //If we're tracking the first free sector, we know it must be + // after this one, so set firstfree. + if (pfb != NULL) + { + pfb->firstfree = isect + 1; + } + + //Update _sectFirstFree, since the first free sector must be after then + // current one. + msfAssert(sect >= _sectFirstFree && + aMsg("Found free sector before _sectFirstFree")); + _sectFirstFree = sect + 1; + + //Mark the current sector as ENDOFCHAIN in the fat. + pfs->SetSect(isect, ENDOFCHAIN); + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { +#ifdef USE_NOSCRATCHINMARKSECT + //In no-scratch, update the no-scratch fat + //as well. + FSINDEX ipfsNoScratchChk; + FSOFFSET isectNoScratchChk; + _pfatNoScratch->SectToPair(sect, + &ipfsNoScratchChk, + &isectNoScratchChk); + + if ((pfsNoScratch != NULL) && (ipfsNoScratch != ipfsNoScratchChk)) + { + _pfatNoScratch->_fv.ReleaseTable(ipfsNoScratch); + pfsNoScratch = NULL; + } + + if (pfsNoScratch == NULL) + { + //SetNext call will grow the no-scratch fat if necessary. + msfChk(_pfatNoScratch->SetNext(sect, ENDOFCHAIN)); + msfChk(_pfatNoScratch->_fv.GetTable(ipfsNoScratchChk, + FB_DIRTY, + &pfsNoScratch)); + ipfsNoScratch = ipfsNoScratchChk; + } + else + { + pfsNoScratch->SetSect(isectNoScratchChk, ENDOFCHAIN); + _pfatNoScratch->_ulFreeSects--; + } +#else + //BUGBUG: SLOW + msfChk(_pfatNoScratch->SetNext(sect, ENDOFCHAIN)); +#endif + } +#endif + + //Dirty the current page. + if ((sectLast == ENDOFCHAIN) || (ipfs != ipfsLast)) + { + //We only need to make this call if we're touching a new + // page for the first time. + msfChk(_fv.SetDirty(ipfs)); + } + + //If we're building a chain, we want to update it here. + if (sectLast != ENDOFCHAIN) + { +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { +#ifdef USE_NOSCRATCHINMARKSECT + FSINDEX ipfsNoScratchChk; + FSOFFSET isectNoScratchChk; + _pfatNoScratch->SectToPair(sectLast, + &ipfsNoScratchChk, + &isectNoScratchChk); + + if (ipfsNoScratchChk == ipfsNoScratch) + { + msfAssert(pfsNoScratch != NULL); + pfsNoScratch->SetSect(isectNoScratchChk, sect); + } + else + { + msfChk(_pfatNoScratch->SetNext(sectLast, sect)); + } +#else + //BUGBUG: SLOW + msfChk(_pfatNoScratch->SetNext(sectLast, sect)); +#endif + } +#endif + //If we're in the same page, we can do this cheaply. + if (ipfsLast == ipfs) + { + pfs->SetSect(isectLast, sect); + } + else + { + //If we're NOT on the same page, we have to go grab the + // old one again, which is expensive. + //BUGBUG: Fix this too. + CFatSect *pfsLast; + + //Since we may only have one available page for the scratch MS, + // we need to release the current one before proceeding. + // This sucks. + if (_pmsParent->IsScratch()) + { + _fv.ReleaseTable(ipfs); + } + + msfChk(_fv.GetTable(ipfsLast, + FB_DIRTY, + &pfsLast)); + + pfsLast->SetSect(isectLast, sect); + _fv.ReleaseTable(ipfsLast); + + //Reacquire the current page if we're in the scratch. + if (_pmsParent->IsScratch()) + { + msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs)); + } + } + } + + return S_OK; +Err: + return sc; +} + + +//+------------------------------------------------------------------------- +// +// Member: CFat::GetFree, private +// +// Synposis: Locate and return a free sector in the FAT +// +// Effects: May modify full bit on full sectors +// +// Arguments: [psectRet] -- Pointer to return value +// +// Returns: S_OK if call completed successfully. +// +// Algorithm: Do a linear search of all tables until a free sector is +// found. If all tables are full, extend the FAT by one +// sector. +// +// History: 18-Jul-91 PhilipLa Created. +// 22-Jul-91 PhilipLa Added FAT extension. +// 17-Aug-91 PhilipLa Added full bits optimization +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetFree) +#endif + +#ifndef REF +SCODE FAT_CLASS CFat::GetFree(ULONG ulCount, SECT *psectRet, BOOL fReadOnly) +#else +SCODE FAT_CLASS CFat::GetFree(ULONG ulCount, SECT *psectRet) +#endif //!REF +{ + SGetFreeStruct gf; + + SECT § = gf.sect; + CVectBits * &pfb = gf.pfb; + FSOFFSET &isect = gf.isect; + FSINDEX &ipfs = gf.ipfs; + CFatSect * &pfs = gf.pfs; + SECT §Last = gf.sectLast; + FSINDEX &ipfsLast = gf.ipfsLast; + FSOFFSET &isectLast = gf.isectLast; + + InitGetFreeStruct(&gf); + + SCODE sc; + + *psectRet = ENDOFCHAIN; + + msfAssert((!_pmsParent->IsShadow()) && + aMsg("Modifying shadow fat.")); + + msfAssert(((!fReadOnly) || (ulCount == 1)) && + aMsg("Read-only GetFree called with ulCount != 1")); +#ifdef USE_NOSNAPSHOT + if (_sectNoSnapshotFree != ENDOFCHAIN) + { + //We're resizing our control structures to prepare for + // no-snapshot commit. + msfAssert((ulCount == 1) && + aMsg("No-snapshot GetFree called with ulCount != 1")); + msfAssert(fReadOnly && + aMsg("No-snapshot GetFree called without fReadOnly")); + *psectRet = _sectNoSnapshotFree++; + _cUnmarkedSects++; + if ((_ulFreeSects != 0) && (_ulFreeSects != MAX_ULONG)) + { + _ulFreeSects--; + } + if (*psectRet >= _sectMax) + { + _sectMax = *psectRet + 1; + } + return S_OK; + } +#endif + +#ifdef USE_NOSCRATCH + if ((fReadOnly) && (_pfatNoScratch != NULL)) + { + //As an optimization, and to prevent loops, we can dispatch + //this call directly to the no-scratch fat, since it has a + //complete picture of everything allocated in this fat. + + //Note that we don't need to pass the read-only flag to the + //no-scratch, since it's perfectly OK to scribble on it + //as much as we want. + + //Further note that if fReadOnly is true, then ulCount must be + // 1, so we can just work with the constant. + msfChk(_pfatNoScratch->GetFree(1, psectRet, GF_WRITE)); + if (_ulFreeSects != MAX_ULONG) + { + _ulFreeSects--; + } + + _cUnmarkedSects++; + if (*psectRet >= _sectMax) + { + _sectMax = *psectRet + 1; + } + return S_OK; + } +#endif + + while (TRUE) + { + //Make sure there are enough free sectors to hold the whole chain + // we're trying to allocate. + if (_ulFreeSects == MAX_ULONG) + { + msfChk(CountFree(&_ulFreeSects)); + } +#if DBG == 1 + else + { + CheckFreeCount(); + } +#endif + + while (ulCount > _ulFreeSects) + { +#if DBG == 1 && !defined(USE_NOSCRATCH) + ULONG ulFree = _ulFreeSects; +#endif + msfChk(Resize(_cfsTable + + ((ulCount - _ulFreeSects + _fv.GetSectTable() - 1) >> + _uFatShift))); +#if DBG == 1 && !defined(USE_NOSCRATCH) + msfAssert(_ulFreeSects > ulFree && + aMsg("Number of free sectors didn't increase after Resize.")); +#endif + + } + + //Starting at the first known free sector, loop until we find + // enough sectors to complete the chain. + FSOFFSET isectStart; + FSINDEX ipfsStart; + + SectToPair(_sectFirstFree, &ipfsStart, &isectStart); + + for (ipfs = ipfsStart; ipfs < _cfsTable; ipfs++) + { + //Only check a page if it is not known to be full. + pfb = _fv.GetBits(ipfs); + + if ((pfb == NULL) || (!pfb->full)) + { + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + + //Eliminate part of the search based on the first-known + // free entry for the page + if (pfb != NULL) + { + isectStart = pfb->firstfree; + } + + for (isect = isectStart; isect < _fv.GetSectTable(); isect++) + { + SECT sectCurrent = pfs->GetSect(isect); + sect = PairToSect(ipfs, isect); + + //If the sector is marked FREESECT, it still may not + // really be free. The IsFree function will return + // S_OK if it is OK to allocate. + if ((sectCurrent == FREESECT) && + ((sc = IsFree(sect)) == S_OK)) + { + //If fReadOnly is TRUE (meaning we're being called + // by the DIFat), then we don't want to actually + // mark anything in the current fat. We'll return + // the current sector and then be done with it. + if (fReadOnly) + { + _cUnmarkedSects++; + _ulFreeSects--; + } + else + { + //Mark the sector as free and return it. + msfChkTo(Err_Rel, MarkSect(&gf)); + } + + if (*psectRet == ENDOFCHAIN) + { + *psectRet = sect; + } + + ulCount--; + + if (ulCount == 0) + { + //If we're done, release the current table, + // update _sectMax, and return. + + if (sect >= _sectMax) + { + _sectMax = sect + 1; + } + + ReleaseGetFreeStruct(&gf); + return S_OK; + } + else + { + //Otherwise update the internal counters and + // keep going. + sectLast = sect; + ipfsLast = ipfs; + isectLast = isect; + } + } + else + { + msfChkTo(Err_Rel, sc); + } + + } + _fv.ReleaseTable(ipfs); + //If we are keeping a full bit, we can set it here, since + // we know we've allocated everything out of the current + // page. + if (pfb != NULL) + { + pfb->full = TRUE; + } + } + isectStart = 0; + } + + //Keep _sectMax up to date for SetSize purposes. + if (sectLast >= _sectMax) + { + _sectMax = sectLast + 1; + } + } + msfAssert(0 && + aMsg("GetFree exited improperly.")); + sc = STG_E_ABNORMALAPIEXIT; + +Err: + ReleaseGetFreeStruct(&gf); + return sc; + +Err_Rel: + ReleaseGetFreeStruct(&gf); + return sc; +} + + + + + + +//+--------------------------------------------------------------------------- +// +// Member: CFat::GetFreeContig, public +// +// Synopsis: Return a chain of free sectors, including contiguity +// information about the chain. +// +// Arguments: [ulCount] -- Number of sectors to allocate +// [aseg] -- Contig table to fill in +// [cSeg] -- Index of last used segment in contig table. +// [pcSegReturned] -- Pointer to return location for total +// number of segments returned. +// +// Returns: Appropriate status code +// +// History: 27-Oct-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +SCODE FAT_CLASS CFat::GetFreeContig(ULONG ulCount, + SSegment STACKBASED *aseg, + ULONG cSeg, + ULONG *pcSegReturned) +{ + SGetFreeStruct gf; + + SECT § = gf.sect; + CVectBits * &pfb = gf.pfb; + FSOFFSET &isect = gf.isect; + FSINDEX &ipfs = gf.ipfs; + CFatSect * &pfs = gf.pfs; + SECT §Last = gf.sectLast; + FSINDEX &ipfsLast = gf.ipfsLast; + FSOFFSET &isectLast = gf.isectLast; + + InitGetFreeStruct(&gf); + + FSINDEX ipfsFirstDirty; + + SCODE sc; + + //Variables used for tracking contiguity. + //cSegCurrent is the current segment being processed. + ULONG cSegCurrent = cSeg; + + //sectLast is the last sector returned. We start this with the + // last thing in the current contig table, and the MarkSect + // function will patch up the chain for us. + sectLast = aseg[cSeg].sectStart + aseg[cSeg].cSect - 1; + SectToPair(sectLast, &ipfsLast, &isectLast); + ipfsFirstDirty = ipfsLast; +#if DBG == 1 + SECT sectTest; + GetNext(sectLast, §Test); + msfAssert(sectTest == ENDOFCHAIN); +#endif + + msfAssert((!_pmsParent->IsShadow()) && + aMsg("Modifying shadow fat.")); + + while (TRUE) + { + //Make sure there are enough free sectors to hold the whole chain + // we're trying to allocate. + if (_ulFreeSects == MAX_ULONG) + { + msfChk(CountFree(&_ulFreeSects)); + } +#if DBG == 1 + else + { + CheckFreeCount(); + } +#endif + + while (ulCount > _ulFreeSects) + { +#if DBG == 1 && !defined(USE_NOSCRATCH) + ULONG ulFree = _ulFreeSects; +#endif + msfChk(Resize(_cfsTable + + ((ulCount - _ulFreeSects + _fv.GetSectTable() - 1) >> + _uFatShift))); +#if DBG == 1 && !defined(USE_NOSCRATCH) + msfAssert(_ulFreeSects > ulFree && + aMsg("Number of free sectors didn't increase after Resize.")); +#endif + + } + + //Starting at the first known free sector, loop until we find + // enough sectors to complete the chain. + FSOFFSET isectStart; + FSINDEX ipfsStart; + + SectToPair(_sectFirstFree, &ipfsStart, &isectStart); + + for (ipfs = ipfsStart; ipfs < _cfsTable; ipfs++) + { + //Only check a page if it is not known to be full. + pfb = _fv.GetBits(ipfs); + + if ((pfb == NULL) || (!pfb->full)) + { + //This little contortion is necessary because the + // page containing the first sector may not get marked + // as dirty by MarkSect. + if (ipfs == ipfsFirstDirty) + { + msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs)); + } + else + { + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + } + + //Eliminate part of the search based on the first-known + // free entry for the page + if (pfb != NULL) + { + isectStart = pfb->firstfree; + } + + for (isect = isectStart; isect < _fv.GetSectTable(); isect++) + { + SECT sectCurrent = pfs->GetSect(isect); + sect = PairToSect(ipfs, isect); + + //If the sector is marked FREESECT, it still may not + // really be free. The IsFree function will return + // TRUE if it is OK to allocate. + if ((sectCurrent == FREESECT) && + ((sc = IsFree(sect)) == S_OK)) + { + //Mark the sector as free and return it. + msfChkTo(Err_Rel, MarkSect(&gf)); + + ulCount--; + + //Update the contig table with this new information. + + if (cSegCurrent < CSEG) + { + if (sect != sectLast + 1) + { + //Use a new entry in the contig table. + cSegCurrent++; + if (cSegCurrent < CSEG) + { + aseg[cSegCurrent].ulOffset = + aseg[cSegCurrent - 1].ulOffset + + aseg[cSegCurrent - 1].cSect; + aseg[cSegCurrent].sectStart = sect; + aseg[cSegCurrent].cSect = 1; + } + } + else + { + //Grow the current segment + aseg[cSegCurrent].cSect++; + } + } + + if (ulCount == 0) + { + //If we're done, release the current table, + // update _sectMax, and return. + + if (sect >= _sectMax) + { + _sectMax = sect + 1; + } + + *pcSegReturned = cSegCurrent; + ReleaseGetFreeStruct(&gf); +#if DBG == 1 + _fv.ResetBits(); + CheckFreeCount(); +#endif + + return S_OK; + } + else + { + //Otherwise update the internal counters and + // keep going. + sectLast = sect; + ipfsLast = ipfs; + isectLast = isect; + } + } + else + { + msfChkTo(Err_Rel, sc); + } + + } + + _fv.ReleaseTable(ipfs); + //If we are keeping a full bit, we can set it here, since + // we know we've allocated everything out of the current + // page. + if (pfb != NULL) + { + pfb->full = TRUE; + } + } + isectStart = 0; + } + + //Keep _sectMax up to date for SetSize purposes. + if (sectLast >= _sectMax) + { + _sectMax = sectLast + 1; + } + } + msfAssert(0 && + aMsg("GetFree exited improperly.")); + sc = STG_E_ABNORMALAPIEXIT; + +Err: + ReleaseGetFreeStruct(&gf); + return sc; + +Err_Rel: + ReleaseGetFreeStruct(&gf); + return sc; +} + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::Contig, public +// +// Synposis: Create contiguous sector table +// +// Effects: Return contiguity information about a chain. +// +// Arguments: [aseg] -- Segment table to return data in +// [sect] -- Starting sector for table to begin +// [ulength] -- Runlength in sectors of table to produce +// [pcSeg] -- Returns count of segments in table +// +// Returns: Appropriate status code. +// +// Algorithm: Perform calls to CFat::GetNext(). Any call that is +// 1 higher than the previous represents contiguous blocks. +// Construct the Segment table on that basis. +// +// History: 16-Aug-91 PhilipLa Created. +// 20-May-92 AlexT Moved to CFat +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Contig) +#endif + +SCODE FAT_CLASS CFat::Contig( + SSegment STACKBASED *aseg, + BOOL fWrite, + SECT sectStart, + ULONG ulLengthStart, + ULONG *pcSeg) +{ + msfDebugOut((DEB_ITRACE,"In CFat::Contig(%lu,%lu)\n",sectStart,ulLengthStart)); + SCODE sc = S_OK; + + ULONG ulLength = ulLengthStart; + SECT sect = sectStart; + SECT stemp = sect; + ULONG ulCount = 1; + USHORT iseg = 0; + FSINDEX ipfs = (FSINDEX) -1; + FSINDEX ipfsOld = ipfs; + FSOFFSET isect; + CFatSect *pfs; + + msfAssert(sect != ENDOFCHAIN && + aMsg("Called Contig with ENDOFCHAIN start")); + + msfAssert((ulLength > 0) && aMsg("Bad length passed to Contig()")); + aseg[iseg].sectStart = sect; + aseg[iseg].cSect = 1; + aseg[iseg].ulOffset = 0; + ulLength--; + + while (TRUE) + { + msfAssert(sect != ENDOFCHAIN && + aMsg("Contig found premature ENDOFCHAIN")); + + SectToPair(sect, &ipfs, &isect); + + if (ipfs != ipfsOld) + { + if (ipfsOld != (FSINDEX) -1) + { + _fv.ReleaseTable(ipfsOld); + } + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + ipfsOld = ipfs; + } + + sect = pfs->GetSect(isect); + + if (sect == ENDOFCHAIN) + { + if ((ulLength < 1) || !fWrite) + break; + + //Allocate new sectors. +#if 0 + SECT sectNew; + msfChk(GetFree(ulLength, §New, GF_WRITE)); + msfChk(SetNext(stemp, sectNew)); + sect = sectNew; +#else + //GetFreeContig will allocate new sectors and fill up + // our contig table. After this call, the chain will + // be the proper length and the contig table will either + // contain the entire chain, or will be full. Either + // way we can exit immediately. + + if (ipfs != (FSINDEX) - 1) + { + _fv.ReleaseTable(ipfs); + } + + ULONG isegRet; + aseg[iseg].cSect = ulCount; + msfChk(GetFreeContig(ulLength, aseg, iseg, &isegRet)); + if (isegRet == CSEG) + { + aseg[isegRet].ulOffset = 0; + aseg[isegRet].cSect = 0; + aseg[isegRet].sectStart = FREESECT; + } + else + { + aseg[isegRet + 1].sectStart = ENDOFCHAIN; + } + *pcSeg = isegRet + 1; +#if DBG == 1 + SSegment segtab[CSEG + 1]; + ULONG cSeg; + Contig(segtab, FALSE, sectStart, ulLengthStart, &cSeg); + msfAssert(cSeg == *pcSeg); + for (USHORT i = 0; i < cSeg; i++) + { + msfAssert(segtab[i].sectStart == aseg[i].sectStart); + msfAssert(segtab[i].ulOffset == aseg[i].ulOffset); + msfAssert(segtab[i].cSect == aseg[i].cSect); + } +#endif + return S_OK; +#endif + } + + if (sect != (stemp + 1)) + { + if (ulLength < 1) + break; + + aseg[iseg].cSect = ulCount; + iseg++; + aseg[iseg].ulOffset = aseg[iseg - 1].ulOffset + ulCount; + aseg[iseg].sectStart = sect; + ulCount = 1; + stemp = sect; + } + else + { + ulCount++; + stemp = sect; + } + if (ulLength > 0) + ulLength--; + + if (iseg >= CSEG) + break; + } + if (ipfs != (FSINDEX) -1) + { + _fv.ReleaseTable(ipfs); + } + + if (iseg < CSEG) + { + aseg[iseg].cSect = ulCount; + aseg[iseg + 1].sectStart = ENDOFCHAIN; + } + else + { + aseg[iseg].ulOffset = 0; + aseg[iseg].cSect = 0; + aseg[iseg].sectStart = FREESECT; + } + + *pcSeg = iseg + 1; + msfDebugOut((DEB_ITRACE,"Exiting Contig()\n")); + +Err: + return sc; +} + + + +//+--------------------------------------------------------------------------- +// +// Member: CFat::ReserveSects, public +// +// Synopsis: Make sure the fat has at least a given number of free +// sectors in it. +// +// Arguments: [cSect] -- Number of sectors to reserve. +// +// Returns: Appropriate status code +// +// History: 18-Sep-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +SCODE CFat::ReserveSects(ULONG cSect) +{ + SCODE sc = S_OK; + + msfDebugOut((DEB_ITRACE, "In CFat::ReserveSects:%p()\n", this)); + + //Make sure there are enough free sectors to hold the whole chain + // we're trying to allocate. + if (_ulFreeSects == MAX_ULONG) + { + msfChk(CountFree(&_ulFreeSects)); + } +#if DBG == 1 + else + { + CheckFreeCount(); + } +#endif + + while (cSect > _ulFreeSects) + { +#if DBG == 1 && !defined(USE_NOSCRATCH) + ULONG ulFree = _ulFreeSects; +#endif + msfChk(Resize(_cfsTable + + ((cSect - _ulFreeSects + _fv.GetSectTable() - 1) >> + _uFatShift))); +#if DBG == 1 && !defined(USE_NOSCRATCH) + msfAssert(_ulFreeSects > ulFree && + aMsg("Number of free sectors didn't increase after Resize.")); +#endif + + } + + msfDebugOut((DEB_ITRACE, "Out CFat::ReserveSects\n")); +Err: + return sc; +} + +#else +//+------------------------------------------------------------------------- +// +// Member: CFat::GetFree, private +// +// Synposis: Locate and return a free sector in the FAT +// +// Effects: May modify full bit on full sectors +// +// Arguments: [psectRet] -- Pointer to return value +// +// Returns: S_OK if call completed successfully. +// +// Algorithm: Do a linear search of all tables until a free sector is +// found. If all tables are full, extend the FAT by one +// sector. +// +// History: 18-Jul-91 PhilipLa Created. +// 22-Jul-91 PhilipLa Added FAT extension. +// 17-Aug-91 PhilipLa Added full bits optimization +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetFree) +#endif + +#ifndef REF +SCODE FAT_CLASS CFat::GetFree(ULONG ulCount, SECT *psectRet, BOOL fReadOnly) +#else +SCODE FAT_CLASS CFat::GetFree(ULONG ulCount, SECT *psectRet) +#endif //!REF +{ + FSINDEX ipfs; + FSOFFSET isect; + SECT sectRetval = 0; + SCODE sc; + + SECT sectLast = ENDOFCHAIN; + FSINDEX ipfsLast; + FSOFFSET isectLast; + + *psectRet = ENDOFCHAIN; + +#ifndef REF + msfAssert((!_pmsParent->IsShadow()) && + aMsg("Modifying shadow fat.")); + + msfAssert(((!fReadOnly) || (ulCount == 1)) && + aMsg("Read-only GetFree called with ulCount != 1")); +#endif //!REF + +#ifdef USE_NOSCRATCH + if ((fReadOnly) && (_pfatNoScratch != NULL)) + { + //As an optimization, and to prevent loops, we can dispatch + //this call directly to the no-scratch fat, since it has a + //complete picture of everything allocated in this fat. + + //Note that we don't need to pass the read-only flag to the + //no-scratch, since it's perfectly OK to scribble on it + //as much as we want. + msfChk(_pfatNoScratch->GetFree(ulCount, psectRet, GF_WRITE)); + if (_ulFreeSects != MAX_ULONG) + { + _ulFreeSects -= ulCount; + } + + _cUnmarkedSects++; + if (*psectRet >= _sectMax) + { + _sectMax = *psectRet + 1; + } + return sc; + } +#endif + + while (TRUE) + { + if (_ulFreeSects == MAX_ULONG) + { + msfChk(CountFree(&_ulFreeSects)); + } +#if DBG == 1 + else + { + CheckFreeCount(); + } +#endif + + while (ulCount > _ulFreeSects) + { +#if DBG == 1 && !defined(USE_NOSCRATCH) + ULONG ulFree = _ulFreeSects; +#endif + msfChk(Resize(_cfsTable + + ((ulCount - _ulFreeSects + _fv.GetSectTable() - 1) >> + _uFatShift))); +#if DBG == 1 && !defined(USE_NOSCRATCH) + msfAssert(_ulFreeSects > ulFree && + aMsg("Number of free sectors didn't increase after Resize.")); +#endif + + } + + FSOFFSET isectStart; + FSINDEX ipfsStart; + + SectToPair(_sectFirstFree, &ipfsStart, &isectStart); + + for (ipfs = ipfsStart; ipfs < _cfsTable; ipfs++) + { + CVectBits *pfb = _fv.GetBits(ipfs); + if ((pfb == NULL) || (!pfb->full)) + { + CFatSect *pfs; + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + if (pfb != NULL) + { + isectStart = pfb->firstfree; + } + + for (isect = isectStart; isect < _fv.GetSectTable(); isect++) + { + SECT sectCurrent = pfs->GetSect(isect); + SECT sectNew = PairToSect(ipfs, isect); + +#ifndef REF + if (sectCurrent == FREESECT) + { + msfChkTo(Err_Rel, IsFree(sectNew)); + + if (sc == S_FALSE) + { + sectCurrent = ENDOFCHAIN; + } + } +#endif //!REF + + if (sectCurrent == FREESECT) + { + msfAssert(_ulFreeSects != MAX_ULONG && + aMsg("Free sect count not set")); + + _ulFreeSects--; + + sectRetval = sectNew; +#ifndef REF +#ifdef SECURE +#ifdef SECURETEST + if (_sid == SIDFAT) + { + BYTE *pb = (BYTE *) DfMemAlloc(SECTORSIZE); + if (pb) + memset(pb, 'Q', SECTORSIZE); + + //Write out sector. + ULONG cbWritten; + ULARGE_INTEGER ulOff; + ULISet32(ulOff, + ConvertSectOffset( + sectRetval, + 0, + _pmsParent->GetSectorShift())); + + _pmsParent->GetILB()->WriteAt( + ulOff, + pb, + SECTORSIZE, + &cbWritten); + + DfMemFree(pb); + } +#endif //SECURETEST +#endif //SECURE + if (fReadOnly) + { + _fv.ReleaseTable(ipfs); + *psectRet = sectRetval; + if (sectRetval >= _sectMax) + { + _sectMax = sectRetval + 1; + } + _cUnmarkedSects++; + return S_OK; + } +#endif //!REF + + if (pfb != NULL) + { + pfb->firstfree = isect + 1; + } + + msfAssert(sectRetval >= _sectFirstFree && + aMsg("Found free sector before _sectFirstFree")); + _sectFirstFree = sectRetval + 1; + + pfs->SetSect(isect, ENDOFCHAIN); + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + //In no-scratch, update the no-scratch fat + //as well. + msfChkTo(Err_Rel, _pfatNoScratch->SetNext( + sectRetval, ENDOFCHAIN)); + } +#endif + + msfChkTo(Err_Rel, _fv.SetDirty(ipfs)); + + if (sectLast != ENDOFCHAIN) + { + if (ipfsLast == ipfs) + { +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + //In no-scratch, update the no-scratch fat + //as well. + msfChkTo(Err_Rel, _pfatNoScratch->SetNext( + sectLast, sectRetval)); + } +#endif + pfs->SetSect(isectLast, sectRetval); + } + else + { + CFatSect *pfsLast; + + if (_pmsParent->IsScratch()) + { + _fv.ReleaseTable(ipfs); + } + + msfChkTo(Err_Rel, _fv.GetTable( + ipfsLast, + FB_DIRTY, + &pfsLast)); + + pfsLast->SetSect(isectLast, sectRetval); +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + //In no-scratch, update the no-scratch fat + //as well. + msfChkTo(Err_Rel, _pfatNoScratch->SetNext( + sectLast, sectRetval)); + } +#endif + _fv.ReleaseTable(ipfsLast); + + if (_pmsParent->IsScratch()) + { + msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs)); + } + } + } + + if (*psectRet == ENDOFCHAIN) + { + *psectRet = sectRetval; + } + + ulCount--; + + if (ulCount == 0) + { + _fv.ReleaseTable(ipfs); + + if (sectRetval >= _sectMax) + { + _sectMax = sectRetval + 1; + } + return S_OK; + } + else + { + sectLast = sectRetval; + ipfsLast = ipfs; + isectLast = isect; + } + } + } + _fv.ReleaseTable(ipfs); + if (pfb != NULL) + { + pfb->full = TRUE; + } + } + isectStart = 0; + } + if (sectRetval >= _sectMax) + { + _sectMax = sectRetval + 1; + } + } + msfAssert(0 && + aMsg("GetFree exited improperly.")); + sc = STG_E_ABNORMALAPIEXIT; + + Err: + return sc; + + Err_Rel: + _fv.ReleaseTable(ipfs); + return sc; +} + +//+------------------------------------------------------------------------- +// +// Member: CFat::Contig, public +// +// Synposis: Create contiguous sector table +// +// Effects: Return contiguity information about a chain. +// +// Arguments: [aseg] -- Segment table to return data in +// [sect] -- Starting sector for table to begin +// [ulength] -- Runlength in sectors of table to produce +// [pcSeg] -- Returns count of segments in table +// +// Returns: Appropriate status code. +// +// Algorithm: Perform calls to CFat::GetNext(). Any call that is +// 1 higher than the previous represents contiguous blocks. +// Construct the Segment table on that basis. +// +// History: 16-Aug-91 PhilipLa Created. +// 20-May-92 AlexT Moved to CFat +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Contig) +#endif + +SCODE FAT_CLASS CFat::Contig( + SSegment STACKBASED *aseg, + BOOL fWrite, + SECT sect, + ULONG ulLength, + ULONG *pcSeg) +{ + msfDebugOut((DEB_ITRACE,"In CFat::Contig(%lu,%lu)\n",sect,ulLength)); + SCODE sc = S_OK; + + SECT stemp = sect; + ULONG ulCount = 1; + USHORT iseg = 0; + FSINDEX ipfs = (FSINDEX) -1; + FSINDEX ipfsOld = ipfs; + FSOFFSET isect; + CFatSect *pfs; + + msfAssert(sect != ENDOFCHAIN && + aMsg("Called Contig with ENDOFCHAIN start")); + + msfAssert((ulLength > 0) && aMsg("Bad length passed to Contig()")); + aseg[iseg].sectStart = sect; + aseg[iseg].cSect = 1; + aseg[iseg].ulOffset = 0; + ulLength--; + + while (TRUE) + { + msfAssert(sect != ENDOFCHAIN && + aMsg("Contig found premature ENDOFCHAIN")); + + SectToPair(sect, &ipfs, &isect); + + if (ipfs != ipfsOld) + { + if (ipfsOld != (FSINDEX) -1) + { + _fv.ReleaseTable(ipfsOld); + } + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + ipfsOld = ipfs; + } + + sect = pfs->GetSect(isect); + + if (sect == ENDOFCHAIN) + { + if ((ulLength < 1) || !fWrite) + break; + + //Allocate new sectors. + + SECT sectNew; +#ifndef REF + msfChk(GetFree(ulLength, §New, GF_WRITE)); +#else + msfChk(GetFree(ulLength, §New)); +#endif //!REF + msfChk(SetNext(stemp, sectNew)); + sect = sectNew; + } + + if (sect != (stemp + 1)) + { + if (ulLength < 1) + break; + + aseg[iseg].cSect = ulCount; + iseg++; + aseg[iseg].ulOffset = aseg[iseg - 1].ulOffset + ulCount; + aseg[iseg].sectStart = sect; + ulCount = 1; + stemp = sect; + } + else + { + ulCount++; + stemp = sect; + } + if (ulLength > 0) + ulLength--; + + if (iseg >= CSEG) + break; + } + if (ipfs != (FSINDEX) -1) + { + _fv.ReleaseTable(ipfs); + } + + if (iseg < CSEG) + { + aseg[iseg].cSect = ulCount; + aseg[iseg + 1].sectStart = ENDOFCHAIN; + } + else + { + aseg[iseg].sectStart = FREESECT; + } + + *pcSeg = iseg + 1; + msfDebugOut((DEB_ITRACE,"Exiting Contig()\n")); + +Err: + return sc; +} + +#endif //USE_NEW_GETFREE + + +//+------------------------------------------------------------------------- +// +// Member: CFat::GetLength, public +// +// Synposis: Return the length of a fat chain. +// +// Arguments: [sect] -- Sector to begin count at. +// +// Returns: Length of the chain, in sectors +// +// Algorithm: Traverse the chain until ENDOFCHAIN is reached. +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetLength) +#endif + +SCODE FAT_CLASS CFat::GetLength(SECT sect, ULONG * pulRet) +{ + msfDebugOut((DEB_FAT,"In CFat::GetLength(%lu)\n",sect)); + SCODE sc = S_OK; + ULONG csect = 0; + + while (sect != ENDOFCHAIN) + { + msfChk(GetNext(sect, §)); + csect++; + } + + msfDebugOut((DEB_FAT,"FAT: GetLength returned %u\n",csect)); + *pulRet = csect; +Err: + return sc; +} + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::Init, public +// +// Synposis: Sets up a FAT, reading data from an existing stream +// +// Effects: Changes all _apfsTable entries, _cfsTable, and all +// flags fields +// +// Arguments: None. +// +// Returns: S_OK if call completed OK. +// +// Algorithm: Read size from first FAT in stream. +// Resize array to necessary size. +// Read in FAT sectors sequentially. +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Init) +#endif + +SCODE FAT_CLASS CFat::Init(CMStream *pmsParent, + FSINDEX cFatSect, + BOOL fConvert) +{ + SCODE sc; + + msfDebugOut((DEB_FAT,"CFat::setup thinks the FAT is size %lu\n",cFatSect)); + + _pmsParent = P_TO_BP(CBasedMStreamPtr, pmsParent); + + _uFatShift = pmsParent->GetSectorShift() - 2; + _uFatMask = (pmsParent->GetSectorSize() >> 2) - 1; + _fv.InitCommon(1 << _uFatShift, 1 << _uFatShift); + + msfChk(_fv.Init(pmsParent, cFatSect)); + + _cfsTable = cFatSect; + + USHORT cbSectorSize; + cbSectorSize = pmsParent->GetSectorSize(); + + _ulFreeSects = MAX_ULONG; + +Err: + return sc; +} + + +//+------------------------------------------------------------------------- +// +// Method: CFat::InitConvert, public +// +// Synopsis: Init function used for conversion +// +// Arguments: [sectData] -- number of sectors used by file +// +// Returns: S_OK if call completed OK. +// +// Algorithm: *Finish This* +// +// History: 28-May-92 PhilipLa Created. +// +// Notes: +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_InitConvert) +#endif + +SCODE FAT_CLASS CFat::InitConvert(CMStream *pmsParent, + SECT sectData) +{ + SCODE sc; + msfDebugOut((DEB_FAT,"Doing conversion\n")); + _pmsParent = P_TO_BP(CBasedMStreamPtr, pmsParent); + + msfAssert((sectData != 0) && + aMsg("Attempt to convert zero length file.")); + + SECT sectMax = 0; + FSINDEX csectFat = 0; + FSINDEX csectLast; + + _uFatShift = pmsParent->GetSectorShift() - 2; + _uFatMask = (pmsParent->GetSectorSize() >> 2) - 1; + _fv.InitCommon(1 << _uFatShift, 1 << _uFatShift); + + if (_sid == SIDFAT) + { + SECT sectTotal; + + //Since the fat needs to represent itself, we can't determine + // the actual number of sectors needed in one pass. We + // therefore loop, factoring in the number of fat sectors + // at each iteration, until we reach a stable state. + // + //As an example, consider the case where each fat sector represents + // 128 sectors and the file being converted is 128 sectors long. + // There will be no DIFat - therefore, we have 128 sectors needed + // on the first pass, which will require 1 fat sector to + // represent them. On the second pass, we discover that we + // actually need 2 fat sectors, since we now have 129 total + // sectors to allocate space for. The third pass will result + // in a stable state. + do + { + csectLast = csectFat; + sectTotal = sectData + pmsParent->GetHeader()->GetDifLength() + + csectFat + 1; + csectFat = (sectTotal + _fv.GetSectTable() - 1) >> _uFatShift; + } + while (csectLast != csectFat); + sectMax = sectData + pmsParent->GetHeader()->GetDifLength(); + } + else + { + //The minifat doesn't need to represent itself, so we can + // compute the number of sectors needed in one pass. + sectMax = sectData; + csectFat = (sectMax + _fv.GetSectTable() -1) >> _uFatShift; + } + + msfChk(_fv.Init(pmsParent, csectFat)); + + FSINDEX i; + + if (_sid == SIDMINIFAT) + { + SECT sectFirst; + msfChk(pmsParent->GetFat()->Allocate(csectFat, §First)); + + pmsParent->GetHeader()->SetMiniFatStart(sectFirst); + + pmsParent->GetHeader()->SetMiniFatLength(csectFat); + } + + + for (i = 0; i < csectFat; i++) + { + CFatSect *pfs; + + msfChk(_fv.GetTable(i, FB_NEW, &pfs)); + if (_sid == SIDFAT) + { + _fv.SetSect(i, sectMax + i); + pmsParent->GetDIFat()->SetFatSect(i, sectMax + i); + } + else + { +#ifdef USE_NOSCRATCH + msfAssert(_pfatNoScratch == NULL); +#endif + SECT sect; + msfChk(pmsParent->GetESect(_sid, i, §)); + _fv.SetSect(i, sect); + } + + _fv.ReleaseTable(i); + } + + + _cfsTable = csectFat; + + if (_sid != SIDMINIFAT) + { + + pmsParent->GetHeader()->SetFatLength(_cfsTable); + + SECT sect; + + if (sectData > 1) + { + for (sect = 0; sect < sectData - 2; sect++) + { + msfChk(SetNext(sect, sect + 1)); + } + + msfChk(SetNext(sectData - 2, ENDOFCHAIN)); + msfChk(SetNext(sectData - 1, 0)); + } + else + { + //In the event that the file to be converted is less + // than one sector long, we don't need to create a + // real chain, just a single terminated sector. + msfChk(SetNext(0, ENDOFCHAIN)); + } + + + for (sect = sectData; sect < sectMax; sect++) + { + msfChk(SetNext(sect, DIFSECT)); + } + + for (USHORT i = 0; i < csectFat; i++) + { + msfChk(SetNext(sectMax + i, FATSECT)); + } + + //Set up directory chain. + msfChk(SetNext(sectMax + i, ENDOFCHAIN)); + + pmsParent->GetHeader()->SetDirStart(sectMax + i); + + _ulFreeSects = (_cfsTable << _uFatShift) - (sectMax + csectFat + 1); + } + else + { + for (SECT sect = 0; sect < sectData -1; sect++) + { + msfChk(SetNext(sect, sect + 1)); + } + msfChk(SetNext(sectData - 1, ENDOFCHAIN)); + _ulFreeSects = (_cfsTable << _uFatShift) - sectData; + } + +#ifndef REF + if (!pmsParent->IsScratch()) + { +#endif //!REF + msfChk(pmsParent->SetSize()); +#ifndef REF + } +#endif //!REF + +Err: + return sc; +} + +//+------------------------------------------------------------------------- +// +// Member: CFat::InitNew, public +// +// Synposis: Sets up a FAT for a newly created multi-strean +// +// Effects: Changes all _apfsTable entries, _cfsTable, and all +// flags fields +// +// Arguments: [pmsparent] -- pointer to parent Mstream +// +// Returns: S_OK if call completed OK. +// +// Algorithm: Set parent pointer. +// Allocate 1 sector for FAT and 1 for Directory. +// +// History: 18-Jul-91 PhilipLa Created. +// 17-Aug-91 PhilipLa Added dirty bits optimization (Dump) +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_InitNew) +#endif + +SCODE FAT_CLASS CFat::InitNew(CMStream *pmsParent) +{ + msfDebugOut((DEB_FAT,"In CFat::InitNew()\n")); + SCODE sc; + + _pmsParent = P_TO_BP(CBasedMStreamPtr, pmsParent); + _uFatShift = pmsParent->GetSectorShift() - 2; + _uFatMask = (pmsParent->GetSectorSize() >> 2) - 1; + _fv.InitCommon(1 << _uFatShift, 1 << _uFatShift); + + FSINDEX count; + if (SIDMINIFAT == _sid) + count = pmsParent->GetHeader()->GetMiniFatLength(); + else + count = pmsParent->GetHeader()->GetFatLength(); + + msfDebugOut((DEB_FAT,"Setting up Fat of size %lu\n",count)); + + msfChk(_fv.Init(pmsParent, count)); + + _cfsTable = count; + + if (SIDFAT == _sid) + { + FSINDEX ipfs; + FSOFFSET isect; + CFatSect *pfs; + + SectToPair(pmsParent->GetHeader()->GetFatStart(), &ipfs, &isect); + msfChk(_fv.GetTable(ipfs, FB_NEW, &pfs)); + _fv.SetSect(ipfs, pmsParent->GetHeader()->GetFatStart()); + _fv.ReleaseTable(ipfs); + + msfChk(SetNext(pmsParent->GetHeader()->GetFatStart(), FATSECT)); + msfDebugOut((DEB_ITRACE,"Set sector %lu (FAT) to ENDOFCHAIN\n",pmsParent->GetHeader()->GetFatStart())); + + msfChk(SetNext(pmsParent->GetHeader()->GetDirStart(), ENDOFCHAIN)); + msfDebugOut((DEB_ITRACE,"Set sector %lu (DIR) to ENDOFCHAIN\n",pmsParent->GetHeader()->GetDirStart())); + _ulFreeSects = (count << _uFatShift) - 2; + } + else + { + _ulFreeSects = 0; + } + +#ifndef REF + if (!pmsParent->IsScratch()) + { +#endif //!REF + msfChk(pmsParent->SetSize()); +#ifndef REF + } +#endif //!REF + + msfDebugOut((DEB_FAT,"Exiting CFat::setupnew()\n")); + +Err: + return sc; +} + + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::Resize, private +// +// Synposis: Resize FAT, both in memory and in the file +// +// Effects: Modifies _cfsTable, _apfsTable, and all flags fields +// +// Arguments: [ulSize] -- New size (in # of tables) for FAT +// +// Returns: S_OK if call completed OK. +// +// Algorithm: Allocate new array of new size. +// Copy over all old pointers. +// Allocate new tables for any necessary. +// +// History: 18-Jul-91 PhilipLa Created. +// 18-Aug-91 PhilipLa Added dirty bits optimization +// +// Notes: BUGBUG: This routine currently cannot reduce the size of a +// fat. This functionality needs to be added eventually. +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Resize) +#endif + +SCODE FAT_CLASS CFat::Resize(ULONG ulSize) +{ + msfDebugOut((DEB_FAT,"In CFat::Resize(%lu)\n",ulSize)); + SCODE sc; + + if (ulSize == _cfsTable) + { + return S_OK; + } + + ULONG csect = _cfsTable; + + msfAssert(ulSize > _cfsTable && + aMsg("Attempted to shrink Fat")); + + + ULONG ipfs; + SECT sectNew; + + CFat *pfat = _pmsParent->GetFat(); + + +#ifndef REF + if ((!_pmsParent->IsScratch()) && (_sid == SIDFAT)) +#else + if (_sid == SIDFAT) +#endif //!REF + { + + //Make sure we have enough space for all of the sectors + // to be allocated. + + ULONG csectFat = ulSize - _cfsTable; + ULONG csectPerDif = (1 << _uFatShift) - 1; + ULONG csectDif = (csectFat + csectPerDif - 1) / csectPerDif; + + + //Assuming all the free sectors are at the end of the file, + // we need a file csectNew sectors long to hold them. + + ULONG csectOld, csectNew; + + msfChk(FindMaxSect(&csectOld)); + + // Every new sector added can conceivably cause a remap of + // another sector while in COW mode. + csectNew = csectOld + + ((csectFat + csectDif) * ((_sectLastUsed > 0) ? 2 : 1)); + + ULARGE_INTEGER cbSize; + + ULISet32(cbSize, ConvertSectOffset( + csectNew, + 0, + _pmsParent->GetSectorShift())); + + if (ULIGetLow(cbSize) > _pmsParent->GetParentSize()) + { + msfHChk(_pmsParent->GetILB()->SetSize(cbSize)); + } + + //If we are the fat, we have enough space in the file for + // ourselves at this point. + } +#ifndef REF + else if (_sid != SIDFAT) +#else + else +#endif //!REF + { + if (_cfsTable == 0) + { + msfChk(pfat->Allocate(ulSize, §New)); + _pmsParent->GetHeader()->SetMiniFatStart(sectNew); + } + else + { + sectNew = _pmsParent->GetHeader()->GetMiniFatStart(); + + SECT sectLast; + msfChk(pfat->GetESect(sectNew, ulSize - 1, §Last)); + + } + +#ifndef REF + if (!_pmsParent->IsScratch()) + { +#endif //!REF + msfChk(_pmsParent->SetSize()); +#ifndef REF + } +#endif //!REF + + + msfChk(pfat->GetSect(sectNew, csect, §New)); + + //If we are the Minifat, we have enough space in the underlying + // file for ourselves at this point. + } + + + _fv.Resize(ulSize); + + + for (ipfs = csect; ipfs < ulSize; ipfs++) + { + CFatSect *pfs; + msfChk(_fv.GetTable(ipfs, FB_NEW, &pfs)); + _cfsTable = ipfs + 1; + + + if (_sid == SIDFAT) + { +#ifdef USE_NOSNAPSHOT + if (_sectNoSnapshotFree != ENDOFCHAIN) + { + SECT sectStart, sectEnd; + + _pmsParent->GetHeader()->SetFatLength(_cfsTable); + + msfChk(GetFree(1, §New, GF_READONLY)); +#ifdef DIFAT_LOOKUP_ARRAY + _pmsParent->GetDIFat()->CacheUnmarkedSect(sectNew, + FATSECT, + ENDOFCHAIN); +#endif + msfChk(_pmsParent->GetDIFat()->SetFatSect(ipfs, sectNew)); + + msfAssert((_ulFreeSects != MAX_ULONG) && + aMsg("Free count not set in no-snapshot")); + + //We don't need to look at anything less than + //_sectNoSnapshotFree, since those are all by definition + //not free. + sectStart = max(PairToSect(ipfs, 0), _sectNoSnapshotFree); + sectEnd = PairToSect(ipfs, 0) + _fv.GetSectTable(); + + for (SECT sectChk = sectStart; + sectChk < sectEnd; + sectChk++) + { + if (IsFree(sectChk) == S_OK) + { + _ulFreeSects++; + } + } + CheckFreeCount(); + } + else + { +#endif //USE_NOSNAPSHOT +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + SECT sectStart, sectEnd; + + msfChk(GetFree(1, §New, GF_READONLY)); +#ifdef DIFAT_LOOKUP_ARRAY + _pmsParent->GetDIFat()->CacheUnmarkedSect(sectNew, + FATSECT, + ENDOFCHAIN); +#endif + if (_ulFreeSects != MAX_ULONG) + { + sectStart = PairToSect(ipfs, 0); + sectEnd = sectStart + _fv.GetSectTable(); + + for (SECT sectChk = sectStart; + sectChk < sectEnd; + sectChk++) + { + if (IsFree(sectChk) == S_OK) + { + _ulFreeSects++; + } + } + CheckFreeCount(); + } + } + else +#endif + { + _ulFreeSects += (1 << _uFatShift); +#ifndef REF + msfChk(pfat->GetFree(1, §New, GF_WRITE)); +#else + msfChk(pfat->GetFree(1, §New)); +#endif //!REF + msfChk(pfat->SetNext(sectNew, FATSECT)); + } + msfChk(_pmsParent->GetDIFat()->SetFatSect(ipfs, sectNew)); +#ifdef USE_NOSNAPSHOT + } +#endif + } + + msfAssert(sectNew != ENDOFCHAIN && + aMsg("Bad sector returned for fatsect.")); + + _fv.SetSect(ipfs, sectNew); + _fv.ReleaseTable(ipfs); + + if (_sid == SIDMINIFAT) + { + _ulFreeSects += (1 << _uFatShift); + msfChk(pfat->GetNext(sectNew, §New)); + } + } + + CheckFreeCount(); + + msfDebugOut((DEB_FAT,"CFat::Resize() - all new objects allocated\n")); + + if (SIDMINIFAT == _sid) + { + _pmsParent->GetHeader()->SetMiniFatLength(_cfsTable); + } + else + _pmsParent->GetHeader()->SetFatLength(_cfsTable); + + //This setsize should only shrink the file. +#if DBG == 1 + STATSTG stat; + + msfHChk(_pmsParent->GetILB()->Stat(&stat, STATFLAG_NONAME)); +#endif + +#ifndef REF + if (!_pmsParent->IsScratch()) + { +#endif //!REF + msfChk(_pmsParent->SetSize()); +#ifndef REF + } +#endif //!REF + +#ifdef USE_NOSCRATCH + if ((_pfatNoScratch != NULL) && (_ulFreeSects == MAX_ULONG)) + { + msfChk(CountFree(&_ulFreeSects)); + } +#endif + +#if DBG == 1 + STATSTG statNew; + + msfHChk(_pmsParent->GetILB()->Stat(&statNew, STATFLAG_NONAME)); + + msfAssert(ULIGetLow(statNew.cbSize) <= ULIGetLow(stat.cbSize)); +#endif + + msfDebugOut((DEB_FAT,"Out CFat::Resize(%lu)\n",ulSize)); + +Err: + msfAssert((_ulFreeSects != MAX_ULONG) && + aMsg("Free sect count not set after Resize().")); + return sc; +} + + + + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::Extend, private +// +// Synposis: Increase the size of an existing chain +// +// Effects: Modifies ulSize sectors within the fat. Causes one or +// more sector writes. +// +// Arguments: [sect] -- Sector ID of last sector in chain to be extended +// [ulSize] -- Number of sectors to add to chain +// +// Requires: sect must be at the end of a chain. +// +// Returns: S_OK if call completed OK. +// +// Algorithm: Use calls to GetFree to allocate chain. +// +// History: 18-Jul-91 PhilipLa Created. +// 17-Aug-91 PhilipLa Added dirty bits opt (Dump) +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Extend) +#endif + +SCODE FAT_CLASS CFat::Extend(SECT sect, ULONG ulSize) +{ + SCODE sc; + + msfDebugOut((DEB_FAT,"In CFat::Extend(%lu,%lu)\n",sect,ulSize)); + SECT sectTemp; + +#ifndef REF + msfChk(GetFree(ulSize, §Temp, GF_WRITE)); +#else + msfChk(GetFree(ulSize, §Temp)); +#endif //!REF + + //If this SetSize fails, clean up the new space and don't do anything + // to the original chain. + if (!_pmsParent->IsScratch()) + { + msfChkTo(EH_OOD, _pmsParent->SetSize()); + } + + //If this SetNext fail calls, we're screwed. Return the error but + // don't attempt to cleanup. + msfChk(SetNext(sect, sectTemp)); + + msfDebugOut((DEB_FAT,"Out CFat::Extend()\n")); + +Err: + return sc; +EH_OOD: + SetChainLength(sectTemp, 0); + return sc; +} + + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::GetNext, public +// +// Synposis: Returns the next sector in a chain, given a sector +// +// Arguments: [sect] -- Sector ID of any sector in a chain. +// +// Returns: Sector ID of next sector in chain, ENDOFCHAIN if at end +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetNext) +#endif + +SCODE FAT_CLASS CFat::GetNext(const SECT sect, SECT * psRet) +{ + SCODE sc; + + FSINDEX ipfs; + FSOFFSET isect; + + msfAssert(sect <= MAXREGSECT && + aMsg("Called GetNext() on invalid sector")); + + SectToPair(sect, &ipfs, &isect); + CFatSect *pfs; + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + + *psRet = pfs->GetSect(isect); + + _fv.ReleaseTable(ipfs); + + if (sect == *psRet) + { + msfAssert(sect != *psRet && + aMsg("Detected loop in fat chain.")); + return STG_E_ABNORMALAPIEXIT; + } + return S_OK; + +Err: + return sc; +} + + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::SetNext, private +// +// Synposis: Set the next sector in a chain +// +// Effects: Modifies a single entry within the fat. +// +// Arguments: [sectFirst] -- Sector ID of first sector +// [sectNext] -- Sector ID of next sector +// +// Returns: void +// +// History: 18-Jul-91 PhilipLa Created. +// 17-Aug-91 PhilipLa Added dirty bits optimization +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_SetNext) +#endif + +SCODE FAT_CLASS CFat::SetNext(SECT sectFirst, SECT sectNext) +{ + FSINDEX ipfs; + FSOFFSET isect; + SCODE sc; + +#ifndef REF + msfAssert((!_pmsParent->IsShadow()) && + aMsg("Modifying shadow fat.")); +#endif //!REF + + // creating infinite loops is a no-no + msfAssert(sectFirst != sectNext && + aMsg("Attempted to create loop in Fat chain")); + msfAssert(sectFirst <= MAXREGSECT && + aMsg("Called SetNext on invalid sector")); + + SectToPair(sectFirst, &ipfs, &isect); + + CFatSect *pfs; +#ifdef USE_NOSCRATCH + SECT sectCurrent; + + if (ipfs >= _cfsTable) + { + msfChk(Resize(ipfs + 1)); + } + + msfAssert(ipfs <= _cfsTable); +#endif + + msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs)); + +#ifdef USE_NOSCRATCH + sectCurrent = pfs->GetSect(isect); +#endif + + pfs->SetSect(isect,sectNext); + + _fv.ReleaseTable(ipfs); + + if (sectNext == FREESECT) + { + CVectBits *pfb; + pfb = _fv.GetBits(ipfs); + + if ((pfb != NULL) && + ((pfb->full == TRUE) || (isect < pfb->firstfree))) + { + pfb->full = FALSE; + pfb->firstfree = isect; + } + + if (sectFirst == _sectMax - 1) + { + _sectMax = ENDOFCHAIN; + } + if (sectFirst < _sectFirstFree) + { + _sectFirstFree = sectFirst; + } + + if (_ulFreeSects != MAX_ULONG) + { +#ifndef REF + SECT sectOld = FREESECT; + + msfChk(IsFree(sectFirst)); + + if (sc != S_FALSE) + { +#endif //!REF + _ulFreeSects++; +#ifndef REF + } +#endif //!REF + } + } +#ifdef USE_NOSCRATCH + else if (_pfatNoScratch != NULL) + { + //We need to update the noscratch fat as well. + msfChk(_pfatNoScratch->SetNext(sectFirst, sectNext)); + } + else if (sectFirst >= _sectMax) + { + _sectMax = sectFirst + 1; +#if DBG == 1 + SECT sectLast; + msfChk(FindLast(§Last)); + msfAssert(((_sectMax == sectLast) || (_sectMax == _sectLastUsed)) && + aMsg("_sectMax doesn't match actual last sector")); +#endif + } + + //If we're the no-scratch fat, then we may be marking a free sector + //as allocated due to the real fat allocating it. In this case, we + //need to update our free sector count. + if ((_sid == SIDMINIFAT) && (_pmsParent->IsScratch()) && + (sectCurrent == FREESECT) && (sectNext != FREESECT) && + (_ulFreeSects != MAX_ULONG)) + { + _ulFreeSects--; + } +#endif + +Err: + return sc; +} + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::CountFree, private +// +// Synposis: Count and return the number of free sectors in the Fat +// +// Arguments: void. +// +// Returns: void. +// +// Algorithm: Do a linear search of the Fat, counting free sectors. +// If a FatSect has its full bit set, it is not necessary +// to search that FatSect. +// +// History: 11-Sep-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_CountFree) +#endif + +SCODE FAT_CLASS CFat::CountFree(ULONG * pulRet) +{ + msfDebugOut((DEB_FAT,"In CFat::CountFree()\n")); + SCODE sc = S_OK; + + FSINDEX ipfs; + ULONG csectFree=0; + FSOFFSET isectStart; + FSINDEX ipfsStart; + + SectToPair(_sectFirstFree, &ipfsStart, &isectStart); + + for (ipfs = ipfsStart; ipfs < _cfsTable; ipfs++) + { + CVectBits *pfb = _fv.GetBits(ipfs); + + if ((pfb == NULL) || (!pfb->full)) + { + msfDebugOut((DEB_FAT,"Checking table %lu\n",ipfs)); + CFatSect *pfs; + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + + if (pfb != NULL) + { + isectStart = pfb->firstfree; + } + + FSOFFSET isect; + for (isect = isectStart; isect < _fv.GetSectTable(); isect++) + { + SECT sectCurrent = pfs->GetSect(isect); + SECT sectNew = PairToSect(ipfs, isect); + +#ifndef REF + if (sectCurrent == FREESECT) + { + msfChkTo(Err_Rel, IsFree(sectNew)); + + if (sc == S_FALSE) + { + sectCurrent = ENDOFCHAIN; + } + } +#endif //!REF + + + if (sectCurrent == FREESECT) + { + csectFree++; + } + } + _fv.ReleaseTable(ipfs); + } + isectStart = 0; + } + msfDebugOut((DEB_FAT,"Countfree returned %lu\n",csectFree)); + *pulRet = csectFree; + +Err: + return sc; +#ifndef REF + +Err_Rel: + _fv.ReleaseTable(ipfs); + return sc; +#endif //!REF +} + + +//+------------------------------------------------------------------------- +// +// Member: CFat::GetSect, public +// +// Synposis: Return the nth sector in a chain +// +// Arguments: [sect] -- Sector ID of beginning of chain +// [uNum] -- indicator of which sector is to be returned +// [psectReturn] -- Pointer to storage for return value +// +// Returns: S_OK. +// +// Algorithm: Linearly traverse chain until numth sector +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetSect) +#endif + +SCODE FAT_CLASS CFat::GetSect(SECT sect, ULONG ulNum, SECT * psectReturn) +{ + msfDebugOut((DEB_FAT,"In CFat::GetSect(%lu,%lu)\n",sect,ulNum)); + + SCODE sc = S_OK; + + if (ulNum == 0) + { + msfDebugOut((DEB_FAT,"Out CFat::GetSect()=>%lu\n",sect)); + } + else if ((SIDFAT == _sid) && + (_pmsParent->GetHeader()->GetFatStart() == sect)) + { + msfChk(_pmsParent->GetDIFat()->GetFatSect(ulNum, §)); + } + else for (ULONG i = 0; i < ulNum; i++) + { + msfChk(GetNext(sect, §)); + if (sect > MAXREGSECT) + { + //The stream isn't long enough, so stop. + msfAssert(sect == ENDOFCHAIN && + aMsg("Found invalid sector in fat chain.")); + break; + } + } + + *psectReturn = sect; + msfDebugOut((DEB_FAT,"Out CFat::GetSect()=>%lu\n",sect)); + +Err: + return sc; +} + + + +//+------------------------------------------------------------------------- +// +// Member: CFat::GetESect +// +// Synposis: Return the nth sector in a chain, extending the chain +// if necessary. +// +// Effects: Modifies fat (via Extend) if necessary +// +// Arguments: [sect] -- Sector ID of beginning of chain +// [ulNum] -- Indicates which sector is to be returned +// [psectReturn] -- Pointer to storage for return value +// +// Returns: S_OK if call completed OK. +// +// Algorithm: Linearly search chain until numth sector is found. If +// the chain terminates early, extend it as necessary. +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_GetESect) +#endif + +SCODE FAT_CLASS CFat::GetESect(SECT sect, ULONG ulNum, SECT *psectReturn) +{ + msfDebugOut((DEB_FAT,"In CFat::GetESect(%lu,%lu)\n",sect,ulNum)); + + SCODE sc = S_OK; + + ULONG i = 0; + while (i < ulNum) + { + SECT temp; + msfChk(GetNext(sect, &temp)); + + msfAssert(temp != FREESECT && + aMsg("FREESECT found in chain.")); + + if (temp == ENDOFCHAIN) + { + + //The stream isn't long enough, so extend it somehow. + ULONG need = ulNum - i; + + msfAssert((SIDMINIFAT == _sid || + sect != _pmsParent->GetHeader()->GetFatStart()) && + aMsg("Called GetESect on Fat chain")); + msfChk(Extend(sect,need)); + } + else + { + sect = temp; + i++; + } + } + + msfDebugOut((DEB_FAT,"Exiting GetESect with result %lu\n",sect)); + *psectReturn = sect; + +Err: + return sc; +} + + + +#ifndef REF +#if DBG == 1 + +//+------------------------------------------------------------------------- +// +// Member: CFat::checksanity, private +// +// Synposis: Print out a FAT chain. Used for debugging only. +// +// Arguments: [sectStart] -- sector to begin run at. +// +// Returns: void +// +// History: 18-Jul-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +SCODE FAT_CLASS CFat::checksanity(SECT sectStart) +{ + msfDebugOut((DEB_FAT,"Sanity Check (%i)\n\t",sectStart)); + SCODE sc = S_OK; + + while (sectStart != ENDOFCHAIN) + { + msfDebugOut((DEB_FAT | DEB_NOCOMPNAME,"%lu, ",sectStart)); + msfChk(GetNext(sectStart, §Start)); + } + msfDebugOut((DEB_FAT | DEB_NOCOMPNAME,"ENDOFCHAIN\n")); +Err: + return sc; +} + +#endif +#endif //!REF + +//+------------------------------------------------------------------------- +// +// Member: CFat::SetChainLength, private +// +// Synposis: Set the length of a fat chain. This is used to reduce +// the length of the chain only. To extend a chain, use +// Extend or GetESect +// +// Effects: Modifies the fat +// +// Arguments: [sectStart] -- Sector to begin at (head of chain) +// [uLength] -- New length for chain +// +// Returns: void. +// +// Algorithm: Traverse chain until uLength is reached or the chain +// terminates. If it terminates prematurely, return with +// no other action. Otherwise, deallocate all remaining +// sectors in the chain. +// +// History: 14-Aug-91 PhilipLa Created. +// +// Notes: +// +//--------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_SetChainLength) +#endif + +SCODE FAT_CLASS CFat::SetChainLength(SECT sectStart, ULONG ulLength) +{ + msfDebugOut((DEB_FAT,"In CFat::SetChainLength(%lu,%lu)\n",sectStart,ulLength)); + SCODE sc; + + if (sectStart == ENDOFCHAIN) return S_OK; + + for (USHORT ui = 1; ui < ulLength; ui++) + { + msfChk(GetNext(sectStart, §Start)); + if (sectStart == ENDOFCHAIN) return S_OK; + } + + msfAssert(sectStart != ENDOFCHAIN && + aMsg("Called SetChainLength is ENDOFCHAIN start")); + + SECT sectEnd; + sectEnd = sectStart; + + msfChk(GetNext(sectStart, §Start)); + + if (ulLength != 0) + { + msfChk(SetNext(sectEnd, ENDOFCHAIN)); + } + else + { + msfChk(SetNext(sectEnd, FREESECT)); + } + + while (sectStart != ENDOFCHAIN) + { + SECT sectTemp; + msfChk(GetNext(sectStart, §Temp)); + msfChk(SetNext(sectStart, FREESECT)); + sectStart = sectTemp; + } + + msfDebugOut((DEB_FAT,"Out CFat::SetChainLength()\n")); + +Err: + return sc; +} + + + +//+------------------------------------------------------------------------- +// +// Method: CFat::FindLast, private +// +// Synopsis: Find last used sector in a fat +// +// Returns: Location of last used sector +// +// Algorithm: Perform a backward linear search until a non-free +// sector is found. +// +// History: 18-Dec-91 PhilipLa Created. +// +// Notes: Used for shadow fats only. +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_FindLast) +#endif + +SCODE FAT_CLASS CFat::FindLast(SECT * psectRet) +{ + SCODE sc = S_OK; + FSINDEX ipfs = _cfsTable; + SECT sect = 0; + + while (ipfs > 0) + { + ipfs--; + + FSOFFSET isect = _fv.GetSectTable(); + + CFatSect *pfs; + msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); + + while (isect > 0) + { + isect--; + + SECT sectCurrent = pfs->GetSect(isect); + SECT sectNew = PairToSect(ipfs, isect); + if (sectCurrent == FREESECT) + { + msfChkTo(Err_Rel, IsFree(sectNew)); + if (sc == S_FALSE) + sectCurrent = ENDOFCHAIN; + } + + if (sectCurrent != FREESECT) + { + msfDebugOut((DEB_FAT,"FindLast returns %lu\n",PairToSect(ipfs,isect))); + sect = sectNew + 1; + break; + } + } + + _fv.ReleaseTable(ipfs); + if (sect != 0) + break; + } + +#ifdef USE_NOSNAPSHOT + //We need additional checks here since in some cases there aren't + //enough pages in the fat to hold _sectNoSnapshot (or _sectNoSnapshotFree). + //Returning too small a value could result in SetSizing the file to too + //small a size, which is data loss. + if (sect < _sectNoSnapshot) + { + sect = _sectNoSnapshot; + } + if ((_sectNoSnapshotFree != ENDOFCHAIN) && (sect < _sectNoSnapshotFree)) + { + sect = _sectNoSnapshotFree; + } +#endif + + *psectRet = sect; +Err: + return sc; +Err_Rel: + _fv.ReleaseTable(ipfs); + return sc; +} + + +#ifndef REF +//+------------------------------------------------------------------------- +// +// Method: CFat::Remap, public +// +// Synopsis: Remap a portion of a chain for copy-on-write +// +// Effects: +// +// Arguments: [sectStart] -- Sector marking the first sector in the chain +// [oStart] -- Offset in sectors at which to begin the remap +// [ulRunLength] -- Number of sectors to remap +// [psectOldStart] -- Returns old location of first remapped +// sector. +// [psectNewStart] -- Returns new location of first sector +// [psectOldEnd] -- Returns old location of last sector +// [psectNewEnd] -- Returns new location of last sector +// +// Returns: Appropriate status code. +// S_FALSE if everything succeeded but no sectors were +// remapped. +// +// History: 18-Feb-92 PhilipLa Created. +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_Remap) +#endif + +SCODE FAT_CLASS CFat::Remap( + SECT sectStart, + ULONG oStart, + ULONG ulRunLength, + SECT *psectOldStart, + SECT *psectNewStart, + SECT *psectOldEnd, + SECT *psectNewEnd) +{ + msfDebugOut((DEB_FAT,"In CFat::Remap(%lu, %lu, %lu)\n",sectStart,oStart,ulRunLength)); + + msfAssert(SIDMINIFAT != _sid && + aMsg("Called Remap on Minifat")); + msfAssert(ulRunLength != 0 && + aMsg("Called Remap with 0 runlength")); + msfAssert(!_pmsParent->IsScratch() && + aMsg("Called Remap on scratch.")); + + BOOL fRemapped = FALSE; + SCODE sc = S_OK; + SECT sectPrev; + SECT sect; + USHORT uCount = 0; + + + *psectNewStart = ENDOFCHAIN; + *psectNewEnd = ENDOFCHAIN; +#if DBG == 1 + *psectOldStart = ENDOFCHAIN; + *psectOldEnd = ENDOFCHAIN; +#endif + + if (oStart == 0) + { + sectPrev = ENDOFCHAIN; + sect = sectStart; + } + else + { + msfChk(GetESect(sectStart, oStart - 1, §Prev)); + msfChk(GetNext(sectPrev, §)); + } + + *psectOldStart = sect; + + while ((uCount < ulRunLength) && (sect != ENDOFCHAIN)) + { + if (uCount == ulRunLength - 1) + { + *psectOldEnd = sect; + } + + msfChk(QueryRemapped(sect)); + if (sc == S_FALSE) + { + SECT sectNew; + + msfChk(GetFree(1, §New, GF_WRITE)); + + msfDebugOut((DEB_ITRACE,"Remapping sector %lu to %lu\n",sect,sectNew)); + if (sectPrev != ENDOFCHAIN) + { + msfChk(SetNext(sectPrev, sectNew)); + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + msfChk(_pfatNoScratch->SetNext(sectPrev, sectNew)); + } +#endif + } + + msfAssert((sect != ENDOFCHAIN) && + aMsg("Remap precondition failed.")); + + SECT sectTemp; + msfChk(GetNext(sect, §Temp)); + msfChk(SetNext(sectNew, sectTemp)); + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + msfChk(_pfatNoScratch->SetNext(sectNew, sectTemp)); + } +#endif + + msfChk(SetNext(sect, FREESECT)); + + + fRemapped = TRUE; + + if (uCount == 0) + { + *psectNewStart = sectNew; + } + + if (uCount == ulRunLength - 1) + { + *psectNewEnd = sectNew; + } + + sect = sectNew; + } + sectPrev = sect; + msfChk(GetNext(sect, §)); + uCount++; + } + + if ((*psectNewStart != ENDOFCHAIN) && (oStart == 0)) + { + if (sectStart == _pmsParent->GetHeader()->GetDirStart()) + _pmsParent->GetHeader()->SetDirStart(*psectNewStart); + + if (sectStart == _pmsParent->GetHeader()->GetMiniFatStart()) + _pmsParent->GetHeader()->SetMiniFatStart(*psectNewStart); + } + + msfDebugOut((DEB_FAT,"Out CFat::Remap()=>%lu\n",*psectNewStart)); + +Err: + if ((sc == S_OK) && !fRemapped) + sc = S_FALSE; + return sc; +} +#endif //!REF + + + +//+------------------------------------------------------------------------- +// +// Method: CFat::FindMaxSect, private +// +// Synopsis: Return last used sector in current Fat. +// +// Arguments: None. +// +// Returns: Last used sector in current Fat +// +// History: 15-Dec-91 PhilipLa Created. +// +//-------------------------------------------------------------------------- + +#ifdef CODESEGMENTS +#pragma code_seg(SEG_CFat_FindMaxSect) +#endif + +SCODE FAT_CLASS CFat::FindMaxSect(SECT *psectRet) +{ + SCODE sc = S_OK; + +#ifdef USE_NOSCRATCH + if (_pfatNoScratch != NULL) + { + return _pfatNoScratch->FindMaxSect(psectRet); + } +#endif + + if (_sectMax == ENDOFCHAIN) + { + msfChk(FindLast(psectRet)); + } + else + { +#if DBG == 1 + SECT sectLast; + msfChk(FindLast(§Last)); +#ifndef REF + msfAssert(((_sectMax == sectLast) || (_sectMax == _sectLastUsed)) && + aMsg("_sectMax doesn't match actual last sector")); +#endif //!REF +#endif + *psectRet = _sectMax; + } + +#ifndef REF + if (*psectRet < _sectLastUsed) + { + *psectRet = _sectLastUsed; + } +#endif //!REF +Err: + return sc; +} + + + +#ifdef USE_NOSCRATCH +//+--------------------------------------------------------------------------- +// +// Member: CFat::InitScratch, public +// +// Synopsis: Initialize the fat based on another fat. This is used +// for NOSCRATCH mode. The fats may have different sector +// sizes. +// +// Arguments: [pfat] -- Pointer to fat to copy. +// [fNew] -- TRUE if this is being called on an empty fat. +// +// Returns: Appropriate status code +// +// History: 20-Mar-95 PhilipLa Created +// +//---------------------------------------------------------------------------- + +SCODE CFat::InitScratch(CFat *pfat, BOOL fNew) +{ + SCODE sc; + + msfAssert((_sid == SIDMINIFAT) && (_pmsParent->IsScratch()) && + aMsg("Called InitScratch on the wrong thing.")); + + USHORT cbSectorOriginal = pfat->_pmsParent->GetSectorSize(); + + USHORT cSectPerRealSect = _pmsParent->GetSectorSize() / + cbSectorOriginal; + + //This routine copies the fat from the multistream passed in into + // the _minifat_ of the current multistream. + + ULONG cfatOriginal = pfat->_cfsTable; + + //Our minifat must be large enough to hold all of the necessary + //sectors. Set the size appropriately. + ULONG cMiniFatSize = (cfatOriginal + cSectPerRealSect - 1) / + cSectPerRealSect; + + msfAssert(((!fNew) || (_cfsTable == 0)) && + aMsg("fNew TRUE when fat non-empty.")); + + msfAssert(((fNew) || (_pfatReal == pfat)) && + aMsg("Fat pointer changed between calls to InitScratch")); + + _pfatReal = P_TO_BP(CBasedFatPtr, pfat); + + if (cMiniFatSize > _cfsTable) + { + msfChk(Resize(cMiniFatSize)); + } + + ULONG ifs; + for (ifs = 0; ifs < cfatOriginal; ifs++) + { + CFatSect *pfs; + CFatSect *pfsCurrent; + + //Get the sector from the original fat + + msfChk(pfat->_fv.GetTable(ifs, FB_NONE, &pfs)); + msfAssert(pfs != NULL); + + //Write the sector into the appropriate place in this fat. + ULONG ifsCurrent; + ifsCurrent = ifs / cSectPerRealSect; + + OFFSET off; + off = (USHORT)((ifs % cSectPerRealSect) * cbSectorOriginal); + + msfChk(_fv.GetTable(ifsCurrent, FB_DIRTY, &pfsCurrent)); + + if (fNew) + { + memcpy((BYTE *)pfsCurrent + off, pfs, cbSectorOriginal); + } + else + { + //Merge the table into the current one. + for (USHORT i = 0; i < cbSectorOriginal / sizeof(SECT); i++) + { + SECT sectCurrent; + OFFSET offCurrent; + offCurrent = i + (off / sizeof(SECT)); + + sectCurrent = pfsCurrent->GetSect(offCurrent); + + if (sectCurrent != STREAMSECT) + { + pfsCurrent->SetSect(offCurrent, pfs->GetSect(i)); + } + } + } + + _fv.ReleaseTable(ifsCurrent); + pfat->_fv.ReleaseTable(ifs); + } + + msfAssert((pfat->_cUnmarkedSects == 0) && + aMsg("Fat being copied has changes in DIF")); + + _fv.ResetBits(); + _ulFreeSects = MAX_ULONG; + _sectFirstFree = 0; + _sectLastUsed = 0; + _sectMax = ENDOFCHAIN; +Err: + return sc; +} +#endif + + + +#ifdef USE_NOSNAPSHOT +//+--------------------------------------------------------------------------- +// +// Member: CFat::ResizeNoSnapshot, public +// +// Synopsis: Resize the fat to handle a no-snapshot commit. In +// no-snapshot mode it is possible that another commit has +// grown the file to the point where it cannot be contained in +// the existing fat in this open, so we need to grow it before +// we can continue with anything else. +// +// Arguments: None. +// +// Returns: Appropriate SCODE. +// +// History: 19-Jun-96 PhilipLa Created +// +//---------------------------------------------------------------------------- + +SCODE CFat::ResizeNoSnapshot(void) +{ + SCODE sc = S_OK; + FSINDEX ipfs; + FSOFFSET isect; + + //If we need to grow the fat to handle the previous commits (which is + // possible if other opens are doing a lot of writing), we do it here, + // since we know for certain that the first available sector is at + // _sectNoSnapshot. + SectToPair(_sectNoSnapshot, &ipfs, &isect); + if (ipfs >= _cfsTable) + { + //We know we have no free sectors, so we can set this here and + // Resize will end up with the correct value. + _ulFreeSects = 0; + + //This Resize will also cause a resize in the DIFat if necessary. + sc = Resize(ipfs + 1); + CheckFreeCount(); + } + return sc; +} +#endif + +#if DBG == 1 +void CFat::CheckFreeCount(void) +{ + SCODE sc; + if (_ulFreeSects != MAX_ULONG) + { + ULONG ulFree; + msfChk(CountFree(&ulFree)); + if (ulFree != _ulFreeSects) + { + msfDebugOut((DEB_ERROR, + "Free count mismatch. Cached: %lu, Real: %lu." + " Difference: %lu\n", + _ulFreeSects, + ulFree, + ulFree - _ulFreeSects)); + } + msfAssert((ulFree == _ulFreeSects) && + aMsg("Free count doesn't match cached value.")); + } +Err: + return; +} +#endif |