You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3149 lines
88 KiB
3149 lines
88 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
|
|
//#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
|