//+------------------------------------------------------------------------- // // 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 #include #include #define USE_NOSCRATCHINMARKSECT //#define SECURETEST //+--------------------------------------------------------------------------- // // 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 // 15-Mar-97 BChapman Calls IsSectType() // //---------------------------------------------------------------------------- inline SCODE CFat::IsFree(SECT sect) { return IsSectType(sect, FREESECT); } // //+--------------------------------------------------------------------------- // // Member: CFat::IsSectType, private // // Synopsis: Check and see if a given sector is really of a given type, // given all the permutations of ways a sector can be assigned. // If we are looking for FREESECT there are extra checks. // // Arguments: [sect] -- Sector to check // [sectType] -- Sector type to check against FREE, FAT, DIF. // // Returns: S_OK if sector is really free. // S_FALSE if sector is really allocated. // Appropriate error in error case. // // Modifies: // // History: 18-Feb-97 BChapman Created // //---------------------------------------------------------------------------- inline SCODE CFat::IsSectType(SECT sect, SECT sectType) { SCODE sc = S_OK; SECT sectCurrent = sectType; #ifdef DEBUG_ISSECTTYPE msfDebugOut((DEB_ITRACE, "In CFat::IsSectType:%p(%x, %x)\n", this, sect, sectType)); #endif if((FREESECT == sectType)) { if (sect < _sectNoSnapshot) { return S_FALSE; } if ((_sectNoSnapshotFree != ENDOFCHAIN) && (sect < _sectNoSnapshotFree)) { return S_FALSE; } } 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 { 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 (FREESECT == sectType) { 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)); } } } if (sectCurrent != sectType) { sc = S_FALSE; } #ifdef DEBUG_ISSECTTYPE msfDebugOut((DEB_ITRACE, "Out CFat::IsSectType\n")); #endif 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 // //-------------------------------------------------------------------------- SCODE 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. // //-------------------------------------------------------------------------- SCODE 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: // //-------------------------------------------------------------------------- CFat::CFat(SID sid) : _fv(sid), _pfatReal(NULL), _pfatNoScratch(NULL), _sectNoSnapshot(0), _sectNoSnapshotFree(ENDOFCHAIN), _sid(sid), _pmsParent(NULL), _sectFirstFree(0), _sectLastUsed(0), _sectMax(ENDOFCHAIN) { _cUnmarkedSects = 0; } //+------------------------------------------------------------------------- // // 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. // //-------------------------------------------------------------------------- CFat::CFat(CFat *pfatOld) : _pmsParent(pfatOld->_pmsParent), _fv(pfatOld->_sid), _pfatReal(NULL), _sid(pfatOld->_sid), _sectFirstFree(0), _sectLastUsed(0), _sectMax(ENDOFCHAIN), _sectNoSnapshot(pfatOld->_sectNoSnapshot) { _cUnmarkedSects = 0; } //+------------------------------------------------------------------------- // // 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: // //-------------------------------------------------------------------------- void CFat::InitCopy(CFat *pfatOld) { msfDebugOut((DEB_FAT,"In CFat Copy Constructor\n")); _pmsParent = pfatOld->_pmsParent; _uFatShift = pfatOld->_uFatShift; _uFatMask = pfatOld->_uFatMask; _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; _ipfsRangeLocks = pfatOld->_ipfsRangeLocks; _isectRangeLocks = pfatOld->_isectRangeLocks; msfDebugOut((DEB_FAT,"Out CFat Copy Constructor\n")); } //+--------------------------------------------------------------------------- // // Member: CFat::Empty, public // // Synopsis: Empty all the control structures of this instance // // Arguments: None. // // Returns: void. // // History: 04-Dec-92 PhilipLa Created // //---------------------------------------------------------------------------- void CFat::Empty(void) { _fv.Empty(); _pmsParent = NULL; _pfatReal = NULL; _cfsTable = 0; _ulFreeSects = MAX_ULONG; _sectFirstFree = 0; _sectLastUsed = 0; _sectMax = ENDOFCHAIN; _cUnmarkedSects = 0; } //+------------------------------------------------------------------------- // // 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: // //-------------------------------------------------------------------------- CFat::~CFat() { msfDebugOut((DEB_FAT,"In CFat destructor. Size of fat is %lu\n",_cfsTable)); msfDebugOut((DEB_FAT,"Exiting CFat destructor\n")); } struct SGetFreeStruct { CVectBits *pfb; SECT sect; CFatSect *pfs; FSOFFSET isect; FSINDEX ipfs; SECT sectLast; FSINDEX ipfsLast; FSOFFSET isectLast; #ifdef USE_NOSCRATCHINMARKSECT CFatSect *pfsNoScratch; FSINDEX ipfsNoScratch; #endif }; //+--------------------------------------------------------------------------- // // 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_NOSCRATCHINMARKSECT pgf->pfsNoScratch = NULL; #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_NOSCRATCHINMARKSECT if (pgf->pfsNoScratch != NULL) { _pfatNoScratch->_fv.ReleaseTable(pgf->ipfsNoScratch); } #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_NOSCRATCHINMARKSECT CFatSect * &pfsNoScratch = pgf->pfsNoScratch; FSINDEX &ipfsNoScratch = pgf->ipfsNoScratch; #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); 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 msfChk(_pfatNoScratch->SetNext(sect, ENDOFCHAIN)); // slow, unoptimized #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) { 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 msfChk(_pfatNoScratch->SetNext(sectLast, sect)); //slow,unoptimized #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 can be expensive if the page table is full CFatSect *pfsLast; //Since we may only have one available page for the scratch MS, // we need to release the current one before proceeding. // 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: // //--------------------------------------------------------------------------- SCODE CFat::GetFree(ULONG ulCount, SECT *psectRet, BOOL fReadOnly) { 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")); 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; } 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; } 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. // the vectbits pointer may have changed due to a resize pfb = _fv.GetBits(ipfs); 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 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. // the vectbits pointer may have changed due to a resize pfb = _fv.GetBits(ipfs); 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: // //--------------------------------------------------------------------------- SCODE 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 = NULL; 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; } if (pfs != NULL) 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 = 0; 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; } //+------------------------------------------------------------------------- // // 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: // //--------------------------------------------------------------------------- SCODE CFat::GetLength(SECT sect, ULONG * pulRet) { msfDebugOut((DEB_FAT,"In CFat::GetLength(%lu)\n",sect)); SCODE sc = S_OK; ULONG csect = 0; const ULONG csectMax = PairToSect(_cfsTable+1,0); while (sect != ENDOFCHAIN) { msfChk(GetNext(sect, §)); csect++; if (csect > csectMax) // infinite loop in the chain msfErr (Err, STG_E_DOCFILECORRUPT); } 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: // //--------------------------------------------------------------------------- SCODE 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(); InitRangeLocksSector(); _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: // //-------------------------------------------------------------------------- SCODE 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); InitRangeLocksSector (); 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 { msfAssert(_pfatNoScratch == NULL); 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 i2 = 0; i2 < csectFat; i2++) { msfChk(SetNext(sectMax + i2, 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; } if (!pmsParent->IsScratch()) { msfChk(pmsParent->SetSize()); } 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: // //--------------------------------------------------------------------------- SCODE 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; InitRangeLocksSector(); 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; } if (!pmsParent->IsScratch()) { msfChk(pmsParent->SetSize()); } 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: This routine currently cannot reduce the size of a fat. // //--------------------------------------------------------------------------- SCODE CFat::Resize(ULONG ulSize) { msfDebugOut((DEB_FAT,"In CFat::Resize(%lu)\n",ulSize)); SCODE sc = S_OK; if (ulSize == _cfsTable) { return S_OK; } ULONG csect = _cfsTable; msfAssert(ulSize > _cfsTable && aMsg("Attempted to shrink Fat")); // 512byte sector docfiles are restricted to 2G for now if (_pmsParent->GetSectorShift() == SECTORSHIFT512 && ulSize > _ipfsRangeLocks) return STG_E_DOCFILETOOLARGE; ULONG ipfs; SECT sectNew = 0; CFat *pfat = _pmsParent->GetFat(); if ((!_pmsParent->IsScratch()) && (_sid == SIDFAT)) { //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; #ifdef LARGE_DOCFILE cbSize.QuadPart = ConvertSectOffset( csectNew, 0, _pmsParent->GetSectorShift()); #else ULISet32(cbSize, ConvertSectOffset( csectNew, 0, _pmsParent->GetSectorShift())); #endif #ifdef LARGE_DOCFILE if (cbSize.QuadPart > _pmsParent->GetParentSize()) #else if (ULIGetLow(cbSize) > _pmsParent->GetParentSize()) #endif { msfHChk(_pmsParent->GetILB()->SetSize(cbSize)); } //If we are the fat, we have enough space in the file for // ourselves at this point. } else if (_sid != SIDFAT) { 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)); } if (!_pmsParent->IsScratch()) { msfChk(_pmsParent->SetSize()); } 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) { if (ipfs == _ipfsRangeLocks) { CVectBits *pfb; pfs->SetSect(_isectRangeLocks, ENDOFCHAIN); pfb = _fv.GetBits(_ipfsRangeLocks); if (pfb != NULL && pfb->full == FALSE && _isectRangeLocks == pfb->firstfree) { pfb->firstfree = _isectRangeLocks + 1; } _ulFreeSects--; } if (_sectNoSnapshotFree != ENDOFCHAIN) { SECT sectStart, sectEnd; _pmsParent->GetHeader()->SetFatLength(_cfsTable); msfChk(GetFree(1, §New, GF_READONLY)); _pmsParent->GetDIFat()->CacheUnmarkedSect(sectNew, FATSECT, ENDOFCHAIN); 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 { if (_pfatNoScratch != NULL) { SECT sectStart, sectEnd; msfChk(GetFree(1, §New, GF_READONLY)); _pmsParent->GetDIFat()->CacheUnmarkedSect(sectNew, FATSECT, ENDOFCHAIN); 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 { _ulFreeSects += (1 << _uFatShift); msfChk(pfat->GetFree(1, §New, GF_WRITE)); msfChk(pfat->SetNext(sectNew, FATSECT)); } msfChk(_pmsParent->GetDIFat()->SetFatSect(ipfs, sectNew)); } } 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 if (!_pmsParent->IsScratch()) { msfChk(_pmsParent->SetSize()); } if ((_pfatNoScratch != NULL) && (_ulFreeSects == MAX_ULONG)) { msfChk(CountFree(&_ulFreeSects)); } #if DBG == 1 STATSTG statNew; msfHChk(_pmsParent->GetILB()->Stat(&statNew, STATFLAG_NONAME)); #ifdef LARGE_DOCFILE if (ulSize < _ipfsRangeLocks && !(_pmsParent->IsInCOW())) msfAssert(statNew.cbSize.QuadPart <= stat.cbSize.QuadPart); #else if (!(_pmsParent->IsInCOW()) msfAssert(ULIGetLow(statNew.cbSize) <= ULIGetLow(stat.cbSize)); #endif #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: // //--------------------------------------------------------------------------- SCODE CFat::Extend(SECT sect, ULONG ulSize) { SCODE sc; msfDebugOut((DEB_FAT,"In CFat::Extend(%lu,%lu)\n",sect,ulSize)); SECT sectTemp; msfChk(GetFree(ulSize, §Temp, GF_WRITE)); //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 in trouble. 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: // //--------------------------------------------------------------------------- SCODE 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: // //--------------------------------------------------------------------------- SCODE CFat::SetNext(SECT sectFirst, SECT sectNext) { FSINDEX ipfs; FSOFFSET isect; SCODE sc; msfAssert((!_pmsParent->IsShadow()) && aMsg("Modifying shadow fat.")); // 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; SECT sectCurrent; if (ipfs >= _cfsTable) { msfChk(Resize(ipfs + 1)); } msfAssert(ipfs <= _cfsTable); msfChk(_fv.GetTable(ipfs, FB_DIRTY, &pfs)); sectCurrent = pfs->GetSect(isect); 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) { SECT sectOld = FREESECT; msfChk(IsFree(sectFirst)); if (sc != S_FALSE) { _ulFreeSects++; } sc = S_OK; // Don't return S_FALSE. } } 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--; } 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: This includes all the FREESECT's in the tail of the last FAT // block (past the last allocated sector) as free. So it is possible // That CountFree() could be greater than FindLast()! //--------------------------------------------------------------------------- SCODE 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); if (sectCurrent == FREESECT) { msfChkTo(Err_Rel, IsFree(sectNew)); if (sc == S_FALSE) { sectCurrent = ENDOFCHAIN; } } if (sectCurrent == FREESECT) { csectFree++; } } _fv.ReleaseTable(ipfs); } isectStart = 0; } msfDebugOut((DEB_FAT,"Countfree returned %lu\n",csectFree)); *pulRet = csectFree; Err: return sc; Err_Rel: _fv.ReleaseTable(ipfs); return sc; } //+------------------------------------------------------------------------- // // Member: CFat::CountSectType, private // // Synposis: Count and return the number of sectors of a given // type in the Fat // // Arguments: [out] count The returned count of sectors. // [in] sectStart The first sector of the range to examine. // [in] sectEnd The last sector of the range to examine. // [in] sectType The type of sector looked for. // // Returns: SCODE. // // Algorithm: Do a linear search of the Fat, counting sectors of the given // type in the given range. // // History: 18-Feb-97 BChapman Created. // // Notes: // //--------------------------------------------------------------------------- SCODE CFat::CountSectType( ULONG * pulRet, SECT sectStart, SECT sectEnd, SECT sectType) { msfDebugOut((DEB_FAT,"In CFat::CountSect(0x%x, 0x%x, 0x%x)\n", sectStart, sectEnd, sectType)); SCODE sc = S_OK; FSINDEX ipfs; ULONG csectType=0; FSOFFSET isectStart; // starting index into the current FAT FSOFFSET isectEnd; // ending index into the current FAT FSOFFSET isectFirstStart; // starting index into the first FAT FSOFFSET isectLastEnd; // ending index into the last FAT FSINDEX ipfsStart; // Starting FAT block number FSINDEX ipfsEnd; // Ending FAT block number SectToPair(sectStart, &ipfsStart, &isectFirstStart); SectToPair(sectEnd, &ipfsEnd, &isectLastEnd); for (ipfs = ipfsStart; ipfs <= ipfsEnd; ipfs++) { // // If we are counting FREESECTS and this FAT blocks is // known to be full then just skip it. // if(FREESECT == sectType) { CVectBits *pfb = _fv.GetBits(ipfs); if ((pfb != NULL) && pfb->full) continue; } msfDebugOut((DEB_FAT,"Checking table %lu\n",ipfs)); CFatSect *pfs; msfChk(_fv.GetTable(ipfs, FB_NONE, &pfs)); // // If this is the first FAT block in the given range use the given // starting offset-index. Otherwise start at the beginning. // if(ipfs == ipfsStart) isectStart = isectFirstStart; else isectStart = 0; // // If this is the last FAT block in the given range use the given // ending fooset-index, otherwise scan to the end of the block. // if(ipfs == ipfsEnd) isectEnd = isectLastEnd; else isectEnd = _fv.GetSectTable(); FSOFFSET isect; for (isect = isectStart; isect < isectEnd; isect++) { SECT sectCurrent = pfs->GetSect(isect); SECT sectNew = PairToSect(ipfs, isect); if (sectCurrent == sectType) { msfChkTo(Err_Rel, IsSectType(sectNew, sectType)); if (sc != S_FALSE) { csectType++; } } } _fv.ReleaseTable(ipfs); isectStart = 0; } msfDebugOut((DEB_FAT,"CountSectType returned %lu\n",csectType)); *pulRet = csectType; Err: return sc; Err_Rel: _fv.ReleaseTable(ipfs); return sc; } //+------------------------------------------------------------------------- // // 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: // //--------------------------------------------------------------------------- SCODE 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: // //--------------------------------------------------------------------------- SCODE 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; } #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 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 //+------------------------------------------------------------------------- // // 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: // //--------------------------------------------------------------------------- SCODE 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 (ULONG 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::DirtyAll, public // // Synopsis: Dirty every sector in the FAT. This had the effect in // consolidation of copying the whole FAT down lower in the // file. // // Returns: SCODE success or failure code. // // Algorithm: Load each FAT sector into the cache "for-writing". // // History: 24-Feb-1997 BChapman Created. // // Notes: For use // //-------------------------------------------------------------------------- SCODE CFat::DirtyAll() { SCODE sc = S_OK; CFatSect *pfs; FSINDEX i; for(i=0; i<_cfsTable; i++) { msfChk(_fv.GetTable(i, FB_DIRTY, &pfs)); _fv.ReleaseTable(i); } 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. // //-------------------------------------------------------------------------- SCODE 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 (ipfs == _ipfsRangeLocks && isect == _isectRangeLocks) sectCurrent = FREESECT; if (sectCurrent != FREESECT) { msfDebugOut((DEB_FAT,"FindLast returns %lu\n",PairToSect(ipfs,isect))); sect = sectNew + 1; break; } } _fv.ReleaseTable(ipfs); if (sect != 0) break; } //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; } *psectRet = sect; Err: return sc; Err_Rel: _fv.ReleaseTable(ipfs); return sc; } //+------------------------------------------------------------------------- // // 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. // //-------------------------------------------------------------------------- SCODE 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 = ENDOFCHAIN; SECT sect; ULONG 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)); if (_pfatNoScratch != NULL) { msfChk(_pfatNoScratch->SetNext(sectPrev, sectNew)); } } msfAssert((sect != ENDOFCHAIN) && aMsg("Remap precondition failed.")); SECT sectTemp; msfChk(GetNext(sect, §Temp)); msfChk(SetNext(sectNew, sectTemp)); if (_pfatNoScratch != NULL) { msfChk(_pfatNoScratch->SetNext(sectNew, sectTemp)); } 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; } //+------------------------------------------------------------------------- // // 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. // //-------------------------------------------------------------------------- SCODE CFat::FindMaxSect(SECT *psectRet) { SCODE sc = S_OK; if (_pfatNoScratch != NULL) { return _pfatNoScratch->FindMaxSect(psectRet); } if (_sectMax == ENDOFCHAIN) { msfChk(FindLast(psectRet)); } else { #if DBG == 1 SECT sectLast; msfChk(FindLast(§Last)); msfAssert(((_sectMax == sectLast) || (_sectMax == _sectLastUsed)) && aMsg("_sectMax doesn't match actual last sector")); #endif *psectRet = _sectMax; } if (*psectRet < _sectLastUsed) { *psectRet = _sectLastUsed; } Err: return sc; } //+--------------------------------------------------------------------------- // // 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 = S_OK; 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; } //+--------------------------------------------------------------------------- // // 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; } #if DBG == 1 void CFat::CheckFreeCount(void) { SCODE sc; // check is disabled if fat grows above >1G if ((_ulFreeSects != MAX_ULONG) && (_cfsTable < _ipfsRangeLocks/2)) { ULONG ulFree; msfChk(CountFree(&ulFree)); if (ulFree != _ulFreeSects) { msfDebugOut((DEB_ERROR, "Free count mismatch. Cached: %lu, Real: %lu." " Difference: %li\n", _ulFreeSects, ulFree, ulFree - _ulFreeSects)); } msfAssert((ulFree == _ulFreeSects) && aMsg("Free count doesn't match cached value.")); } Err: return; } #endif