/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*
* @APPLE_LICENSE_HEADER_END@
*/
-/*
- File: FileExtentMapping.c
-
- Contains: xxx put contents here xxx
-
- Version: HFS Plus 1.0
-
- Written by: Dave Heller, Mark Day
-
- Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
-
- File Ownership:
-
- DRI: Mark Day
-
- Other Contact: xxx put other contact here xxx
-
- Technology: xxx put technology here xxx
-
- Writers:
-
- (DSH) Deric Horn
- (msd) Mark Day
- (djb) Don Brady
-
- Change History (most recent first):
- <MacOSX> 9/9/99 djb Fix fcbModifiedMask flag testing logic.
- <MacOSX> 8/25/98 djb Flush extents b-tree header if dirty (2371088).
- <MacOSX> 6/30/98 djb Add functions NodesAreContiguous and ExtentsAreIntegral (for radar #2249539).
- <MacOSX> 6/23/98 djb Changed DeallocFile to DeleteFile which now deletes the catalog record.
- Fixed UpdateExtentRecord to pass correct fcb to Btree routines. Fixed
- hfs+ bug in CreateExtentRecord (double dereference).
- <MacOSX> 5/20/98 djb In ExtendFileC don't lie about the peof! (radar #2230094).
- <MacOSX> 4/17/98 djb Add VCB locking.
- <MacOSX> 4/2/98 djb Switch over to real BTree interface (no more BTreeWrapper.c).
- <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
-
- <CS24> 1/23/98 msd Bug 2208024: AllocContig is actually allocating one extent even
- though there is not enough contiguous space.
- <CS23> 12/2/97 DSH GetFCBExtentRecord no longer static so DFA can use it.
- <CS22> 10/20/97 msd When allocating more space for a file, do the clump size
- calculations in ExtendFileC, not BlockAllocate. Undo change from
- <CS18>.
- <CS21> 10/17/97 msd Conditionalize DebugStrs.
- <CS20> 10/16/97 msd Simplify the code path for MapFileBlockC (logical to physical
- block mapping) in the typical case where the file isn't
- fragmented so badly that it has extents in the extents B-tree.
- Simplified some of the calculations for all cases.
- <CS19> 10/13/97 DSH FindExtentRecord & DeleteExtentRecord are also being used by DFA
- no longer static.
- <CS18> 10/6/97 msd When extending a file, set the physical EOF to include any extra
- space allocated due to a file's clump size.
- <CS17> 9/19/97 msd Remove the MapLogicalToPhysical SPI. It was never used and is
- not being tested anyway.
- <CS16> 9/5/97 msd In CompareExtentKeys and CompareExtentKeysPlus, use the symbolic
- constants for key length. Don't DebugStr unless DEBUG_BUILD is
- set.
- <CS15> 7/24/97 djb Add instrumentation to MapFileBlockC
- <CS14> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
- collision
- <CS13> 7/15/97 DSH AdjEOF() mark the FCB as modified. (1664389)
- <CS12> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
- <CS11> 7/3/97 msd Bug #1663518. Remove DebugStr when setting the FCB extent record
- for a volume control file.
- <CS10> 6/27/97 msd Moved enum kFirstFileRefnum to FilesInternal.
- <CS9> 6/24/97 djb Include "CatalogPrivate.h"
- <CS8> 6/16/97 msd Finish implementation of CreateLargeFile SPI.
- <CS7> 6/12/97 msd Add stub for CreateLargeFile SPI.
- <CS6> 6/5/97 msd Add MapLogicalToPhysical.
- <CS5> 6/2/97 msd In TruncateFileC, don't update the extent record unless it was
- actually changed (prevents extra updates when truncating to the
- end of the extent, and it is the last extent of the file.) Added
- an AdjustEOF routine called by the assembly AdjEOF routine. It
- copies the EOF, physical length, and extent information from one
- FCB to all other FCBs for that fork.
- <CS4> 5/20/97 DSH Removed const declaration in MapFileBlocC, const is benign when
- passing by value, and SC requires it to match prototype.
- <CS3> 5/15/97 msd Change enum kResourceForkType from -1 to 0xFF since it is now
- unsigned. Change all forkType parameters to UInt8.
- <CS2> 5/7/97 msd When checking for an unused extent descriptor, check the length,
- not the starting block.
- <CS1> 4/24/97 djb first checked in
- <HFS25> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
- <HFS24> 4/4/97 djb Get in sync with volume format changes.
- <HFS23> 3/17/97 DSH Casting to compile with SC.
- <HFS22> 2/26/97 msd Add instrumentation in ExtendFileC and TruncateFileC. In
- CompareExtentKeys and CompareExtentKeysPlus, make sure the key
- lengths are correct.
- <HFS21> 2/5/97 msd The comparison with fsBTStartOfIterationErr didn't work because
- the enum is an unsigned long; it is now casted to an OSErr
- before comparing.
- <HFS20> 1/31/97 msd In FindExtentRecord, turn an fsBTStartOfIterationErr error into
- btNotFound.
- <HFS19> 1/28/97 msd Fixed bug in MapFileBlockC where it returned the wrong number of
- bytes available at the given block number. This could
- potentially cause programs to read or write over other files.
- <HFS18> 1/16/97 djb Extent key compare procs now return SInt32. Fixed
- UpdateExtentRecord - it was passing a pointer to an ExtentKey
- pointer.
- <HFS17> 1/10/97 msd Change TruncateFileC to call DellocateFork when the new PEOF is
- 0. Fixes a fxRangeErr returned when no extents existed.
- <HFS16> 1/6/97 msd Previous change prevents extent records from being removed if
- the files new PEOF is in the local (FCB/catalog) extents.
- <HFS15> 1/3/97 djb Temp fix in TruncateFileC to prevent unwanted calls to
- TruncateExtents.
- <HFS14> 12/23/96 msd Previous change to SearchExtentFile didn't set up the outputs
- for hint and key when the FCB extent record wasn't full.
- <HFS13> 12/20/96 msd In SearchExtentFile, don't bother searching the extents file if
- the FCB's extent record wasn't full, or if the FCB was for the
- extents file itself. Modified SearchExtentRecord to return a
- Boolean to indicate that the record was not full.
- <HFS12> 12/19/96 DSH Changed refs from VCB to ExtendedVCB
- <HFS11> 12/19/96 djb Updated for new B-tree Manager interface.
- <HFS10> 12/12/96 djb Really use new SPI for GetCatalogNode.
- <HFS9> 12/12/96 djb Use new Catalog SPI for GetCatalogNode. Added Mark's changes to
- MapFileBlockC.
- <HFS8> 12/11/96 msd TruncateFileC must always release extents, even if PEOF hasn't
- changed (since allocation may have been rounded up due to clump
- size).
- <HFS7> 12/10/96 msd Check PRAGMA_LOAD_SUPPORTED before loading precompiled headers.
- <HFS6> 12/4/96 DSH Precompiled headers
- <HFS5> 11/26/96 msd Add an exported routine to grow the parallel FCB table to
- accomodate the HFS+ ExtentRecord.
- <HFS4> 11/26/96 msd Convert internal routines to use ExtentKey and ExtentRecord
- (instead of the raw HFS structures).
- <HFS3> 11/21/96 msd Added CompareExtentKeysPlus().
- <HFS2> 11/20/96 msd Finish porting FXM to C.
- <HFS1> 11/6/96 DKH first checked in
-
-*/
#include "../../hfs.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/BTreesInternal.h"
-#include "../headers/CatalogPrivate.h" // calling a private catalog routine (LocateCatalogNode)
#include <sys/malloc.h>
============================================================
Public (Exported) Routines:
============================================================
- DeAllocFile Deallocate all disk space allocated to a specified file.
- Both forks are deallocated.
ExtendFileC Allocate more space to a given file.
FlushExtentFile
Flush the extents file for a given volume.
- GrowParallelFCBs
- Make sure the parallel FCB entries are big enough to support
- the HFS+ ExtentRecord. If not, the array is grown and the
- pre-existing data copied over.
- AdjustEOF
- Copy EOF, physical length, and extent records from one FCB
- to all other FCBs for that fork. This is used when a file is
- grown or shrunk as the result of a Write, SetEOF, or Allocate.
- MapLogicalToPhysical
- Map some position in a file to a volume block number. Also
- returns the number of contiguous bytes that are mapped there.
- This is a queued HFSDispatch call that does the equivalent of
- MapFileBlockC, using a parameter block.
============================================================
Internal Routines:
kPreviousRecord = -1
};
-void HFSToHFSPlusExtents(
- const HFSExtentRecord oldExtents,
- HFSPlusExtentRecord newExtents);
-OSErr HFSPlusToHFSExtents(
+static OSErr HFSPlusToHFSExtents(
const HFSPlusExtentRecord oldExtents,
HFSExtentRecord newExtents);
-OSErr FindExtentRecord(
+static OSErr FindExtentRecord(
const ExtendedVCB *vcb,
UInt8 forkType,
UInt32 fileID,
HFSPlusExtentRecord foundData,
UInt32 *foundHint);
-OSErr DeleteExtentRecord(
+static OSErr DeleteExtentRecord(
const ExtendedVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt32 startBlock);
static OSErr CreateExtentRecord(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
UInt32 *hint);
-OSErr GetFCBExtentRecord(
+static OSErr GetFCBExtentRecord(
const FCB *fcb,
HFSPlusExtentRecord extents);
static OSErr SearchExtentFile(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
const FCB *fcb,
SInt64 filePosition,
HFSPlusExtentKey *foundExtentKey,
UInt32 *endingFABNPlusOne );
static OSErr SearchExtentRecord(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
UInt32 searchFABN,
const HFSPlusExtentRecord extentData,
UInt32 extentDataStartFABN,
Boolean * recordDeleted);
static OSErr UpdateExtentRecord (
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
FCB *fcb,
const HFSPlusExtentKey *extentFileKey,
const HFSPlusExtentRecord extentData,
// fourth entry will be zeroes.
// foundHint The BTree hint to find the node again
//_________________________________________________________________________________
-OSErr FindExtentRecord(
+static OSErr FindExtentRecord(
const ExtendedVCB *vcb,
UInt8 forkType,
UInt32 fileID,
UInt16 btRecordSize;
err = noErr;
- *foundHint = 0;
+ if (foundHint)
+ *foundHint = 0;
fcb = GetFileControlBlock(vcb->extentsRefNum);
MALLOC(btIterator, BTreeIterator *, sizeof(*btIterator), M_TEMP, M_WAITOK);
if (err == noErr) {
UInt16 i;
- // Copy the found key back for the caller
- foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
- foundKey->forkType = extentKeyPtr->forkType;
- foundKey->pad = 0;
- foundKey->fileID = extentKeyPtr->fileID;
- foundKey->startBlock = extentKeyPtr->startBlock;
-
- // Copy the found data back for the caller
+ // Copy the found key back for the caller
+ if (foundKey) {
+ foundKey->keyLength = kHFSPlusExtentKeyMaximumLength;
+ foundKey->forkType = extentKeyPtr->forkType;
+ foundKey->pad = 0;
+ foundKey->fileID = extentKeyPtr->fileID;
+ foundKey->startBlock = extentKeyPtr->startBlock;
+ }
+ // Copy the found data back for the caller
foundData[0].startBlock = extentData[0].startBlock;
foundData[0].blockCount = extentData[0].blockCount;
foundData[1].startBlock = extentData[1].startBlock;
}
if (err == noErr) {
- // Copy the found key back for the caller
- BlockMoveData(extentKeyPtr, foundKey, sizeof(HFSPlusExtentKey));
- // Copy the found data back for the caller
+ // Copy the found key back for the caller
+ if (foundKey)
+ BlockMoveData(extentKeyPtr, foundKey, sizeof(HFSPlusExtentKey));
+ // Copy the found data back for the caller
BlockMoveData(&extentData, foundData, sizeof(HFSPlusExtentRecord));
}
}
-
- *foundHint = btIterator->hint.nodeNum;
+
+ if (foundHint)
+ *foundHint = btIterator->hint.nodeNum;
FREE(btIterator, M_TEMP);
return err;
}
static OSErr CreateExtentRecord(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
HFSPlusExtentKey *key,
HFSPlusExtentRecord extents,
UInt32 *hint)
{
BTreeIterator * btIterator;
FSBufferDescriptor btRecord;
- UInt16 btRecordSize;
- OSErr err;
+ UInt16 btRecordSize;
+ int lockflags;
+ OSErr err;
err = noErr;
*hint = 0;
+
MALLOC(btIterator, BTreeIterator *, sizeof(*btIterator), M_TEMP, M_WAITOK);
bzero(btIterator, sizeof(*btIterator));
-
+
+ /*
+ * The lock taken by callers of ExtendFileC is speculative and
+ * only occurs when the file already has overflow extents. So
+ * We need to make sure we have the lock here. The extents
+ * btree lock can be nested (its recursive) so we always take
+ * it here.
+ */
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
+
if (vcb->vcbSigWord == kHFSSigWord) {
HFSExtentKey * keyPtr;
HFSExtentRecord data;
if (err == noErr)
*hint = btIterator->hint.nodeNum;
+ (void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum));
+
+ hfs_systemfile_unlock(vcb, lockflags);
+
FREE(btIterator, M_TEMP);
return err;
}
-OSErr DeleteExtentRecord(
+static OSErr DeleteExtentRecord(
const ExtendedVCB *vcb,
UInt8 forkType,
UInt32 fileID,
OSErr err;
err = noErr;
+
MALLOC(btIterator, BTreeIterator *, sizeof(*btIterator), M_TEMP, M_WAITOK);
bzero(btIterator, sizeof(*btIterator));
}
err = BTDeleteRecord(GetFileControlBlock(vcb->extentsRefNum), btIterator);
-
+ (void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum));
+
FREE(btIterator, M_TEMP);
return err;
}
//
// Function: Maps a file position into a physical disk address.
//
-// Input: A2.L - VCB pointer
-// (A1,D1.W) - FCB pointer
-// D4.L - number of bytes desired
-// D5.L - file position (byte address)
-//
-// Output: D3.L - physical start block
-// D6.L - number of contiguous bytes available (up to D4 bytes)
-// D0.L - result code <01Oct85>
-// 0 = ok
-// FXRangeErr = file position beyond mapped range <17Oct85>
-// FXOvFlErr = extents file overflow <17Oct85>
-// other = error <17Oct85>
-//
-// Called By: Log2Phys (read/write in place), Cache (map a file block).
//_________________________________________________________________________________
+__private_extern__
OSErr MapFileBlockC (
ExtendedVCB *vcb, // volume that file resides on
FCB *fcb, // FCB of file
size_t numberOfBytes, // number of contiguous bytes desired
off_t offset, // starting offset within file (in bytes)
- daddr_t *startSector, // first sector (NOT an allocation block)
+ daddr64_t *startSector, // first sector (NOT an allocation block)
size_t *availableBytes) // number of contiguous bytes (up to numberOfBytes)
{
OSErr err;
off_t dataEnd; // (offset) end of range that is contiguous
UInt32 sectorsPerBlock; // Number of sectors per allocation block
UInt32 startBlock; // volume allocation block corresponding to firstFABN
- daddr_t temp;
+ daddr64_t temp;
off_t tmpOff;
allocBlockSize = vcb->blockSize;
sectorSize = VCBTOHFS(vcb)->hfs_phys_block_size;
-
+
err = SearchExtentFile(vcb, fcb, offset, &foundKey, foundData, &foundIndex, &hint, &nextFABN);
if (err == noErr) {
startBlock = foundData[foundIndex].startBlock;
// offset in sectors from start of the extent +
// offset in sectors from start of allocation block space
//
- temp = (daddr_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/sectorSize);
+ temp = (daddr64_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/sectorSize);
temp += startBlock * sectorsPerBlock;
/* Add in any volume offsets */
// Determine the number of contiguous bytes until the end of the extent
// (or the amount they asked for, whichever comes first).
//
- tmpOff = dataEnd - offset;
- if (tmpOff > (off_t)(numberOfBytes))
- *availableBytes = numberOfBytes; // more there than they asked for, so pin the output
- else
- *availableBytes = tmpOff;
+ if (availableBytes)
+ {
+ tmpOff = dataEnd - offset;
+ if (tmpOff > (off_t)(numberOfBytes))
+ *availableBytes = numberOfBytes; // more there than they asked for, so pin the output
+ else
+ *availableBytes = tmpOff;
+ }
return noErr;
}
UInt32 hint;
HFSPlusExtentKey key;
HFSPlusExtentRecord extents;
+ int lockflags;
+
+ /*
+ * The lock taken by callers of TruncateFileC is speculative and
+ * only occurs when the file already has overflow extents. So
+ * We need to make sure we have the lock here. The extents
+ * btree lock can be nested (its recursive) so we always take
+ * it here.
+ */
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
while (true) {
err = FindExtentRecord(vcb, forkType, fileID, startBlock, false, &key, extents, &hint);
*recordDeleted = true;
startBlock += numberExtentsReleased;
}
+ hfs_systemfile_unlock(vcb, lockflags);
return err;
}
// Function: Flushes the extent file for a specified volume
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
+__private_extern__
OSErr FlushExtentFile( ExtendedVCB *vcb )
{
FCB * fcb;
OSErr err;
+ int lockflags;
fcb = GetFileControlBlock(vcb->extentsRefNum);
+
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
err = BTFlushPath(fcb);
+ hfs_systemfile_unlock(vcb, lockflags);
+
if ( err == noErr )
{
// If the FCB for the extent "file" is dirty, mark the VCB as dirty.
// an HFS volume.
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
+__private_extern__
SInt32 CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey )
{
SInt32 result; // ± 1
// an HFS volume.
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
+__private_extern__
SInt32 CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey )
{
SInt32 result; // ± 1
return( result );
}
+/*
+ * Add a file extent to a file.
+ *
+ * Used by hfs_extendfs to extend the volume allocation bitmap file.
+ *
+ */
+__private_extern__
+int
+AddFileExtent(ExtendedVCB *vcb, FCB *fcb, UInt32 startBlock, UInt32 blockCount)
+{
+ HFSPlusExtentKey foundKey;
+ HFSPlusExtentRecord foundData;
+ UInt32 foundIndex;
+ UInt32 hint;
+ UInt32 nextBlock;
+ SInt64 peof;
+ int i;
+ int error;
+
+ peof = (SInt64)(fcb->ff_blocks + blockCount) * (SInt64)vcb->blockSize;
+
+ error = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock);
+ if (error != fxRangeErr)
+ return (EBUSY);
+
+ /*
+ * Add new extent. See if there is room in the current record.
+ */
+ if (foundData[foundIndex].blockCount != 0)
+ ++foundIndex;
+ if (foundIndex == kHFSPlusExtentDensity) {
+ /*
+ * Existing record is full so create a new one.
+ */
+ foundKey.keyLength = kHFSPlusExtentKeyMaximumLength;
+ foundKey.forkType = kDataForkType;
+ foundKey.pad = 0;
+ foundKey.fileID = FTOC(fcb)->c_fileid;
+ foundKey.startBlock = nextBlock;
+
+ foundData[0].startBlock = startBlock;
+ foundData[0].blockCount = blockCount;
+
+ /* zero out remaining extents. */
+ for (i = 1; i < kHFSPlusExtentDensity; ++i) {
+ foundData[i].startBlock = 0;
+ foundData[i].blockCount = 0;
+ }
+
+ foundIndex = 0;
+
+ error = CreateExtentRecord(vcb, &foundKey, foundData, &hint);
+ if (error == fxOvFlErr)
+ error = dskFulErr;
+ } else {
+ /*
+ * Add a new extent into existing record.
+ */
+ foundData[foundIndex].startBlock = startBlock;
+ foundData[foundIndex].blockCount = blockCount;
+ error = UpdateExtentRecord(vcb, fcb, &foundKey, foundData, hint);
+ }
+ (void) FlushExtentFile(vcb);
+
+ return (error);
+}
//_________________________________________________________________________________
//
// Function: Extends the disk space allocated to a file.
//
-// Input: A2.L - VCB pointer
-// A1.L - pointer to FCB array
-// D1.W - file refnum
-// D3.B - option flags
-// kEFContigMask - force contiguous allocation
-// kEFAllMask - allocate all requested bytes or none
-// NOTE: You may not set both options.
-// D4.L - number of additional bytes to allocate
-//
-// Output: D0.W - result code
-// 0 = ok
-// -n = IO error
-// D6.L - number of bytes allocated
-//
-// Called by: FileAloc,FileWrite,SetEof
-//
-// Note: ExtendFile updates the PEOF in the FCB.
//_________________________________________________________________________________
+__private_extern__
OSErr ExtendFileC (
ExtendedVCB *vcb, // volume that file resides on
FCB *fcb, // FCB of file to truncate
Boolean allOrNothing;
Boolean forceContig;
Boolean wantContig;
+ Boolean useMetaZone;
Boolean needsFlush;
UInt32 actualStartBlock;
UInt32 actualNumBlocks;
// Determine how many blocks need to be allocated.
// Round up the number of desired bytes to add.
//
- blocksToAdd = FileBytesToBlocks(bytesToAdd, volumeBlockSize);
+ blocksToAdd = howmany(bytesToAdd, volumeBlockSize);
bytesToAdd = (SInt64)((SInt64)blocksToAdd * (SInt64)volumeBlockSize);
/*
&& (vcb->vcbSigWord == kHFSPlusSigWord)
&& (bytesToAdd < (SInt64)HFS_MAX_DEFERED_ALLOC)
&& (blocksToAdd < hfs_freeblks(VCBTOHFS(vcb), 1))) {
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ vcb->loanedBlocks += blocksToAdd;
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
+
fcb->ff_unallocblocks += blocksToAdd;
- vcb->loanedBlocks += blocksToAdd;
FTOC(fcb)->c_blocks += blocksToAdd;
fcb->ff_blocks += blocksToAdd;
- FTOC(fcb)->c_flag |= C_MODIFIED;
+ FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
*actualBytesAdded = bytesToAdd;
return (0);
}
* Give back any unallocated blocks before doing real allocations.
*/
if (fcb->ff_unallocblocks > 0) {
- blocksToAdd += fcb->ff_unallocblocks;
- bytesToAdd = (SInt64)blocksToAdd * (SInt64)volumeBlockSize;
+ u_int32_t loanedBlocks;
- vcb->loanedBlocks -= fcb->ff_unallocblocks;
- FTOC(fcb)->c_blocks -= fcb->ff_unallocblocks;
- fcb->ff_blocks -= fcb->ff_unallocblocks;
+ loanedBlocks = fcb->ff_unallocblocks;
+ blocksToAdd += loanedBlocks;
+ bytesToAdd = (SInt64)blocksToAdd * (SInt64)volumeBlockSize;
+ FTOC(fcb)->c_blocks -= loanedBlocks;
+ fcb->ff_blocks -= loanedBlocks;
fcb->ff_unallocblocks = 0;
+
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ vcb->loanedBlocks -= loanedBlocks;
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
}
//
// then set the maximum number of bytes to the requested number of bytes
// rounded up to a multiple of the clump size.
//
- if ((fcb->fcbClmpSize > volumeBlockSize)
+ if ((vcb->vcbClpSiz > (int32_t)volumeBlockSize)
&& (bytesToAdd < (SInt64)HFS_MAX_DEFERED_ALLOC)
&& (flags & kEFNoClumpMask) == 0) {
- maximumBytes = (SInt64)FileBytesToBlocks(bytesToAdd, fcb->fcbClmpSize);
- maximumBytes *= fcb->fcbClmpSize;
+ maximumBytes = (SInt64)howmany(bytesToAdd, vcb->vcbClpSiz);
+ maximumBytes *= vcb->vcbClpSiz;
} else {
maximumBytes = bytesToAdd;
}
//
// Compute new physical EOF, rounded up to a multiple of a block.
//
- if ((vcb->vcbSigWord == kHFSSigWord) && ((((SInt64)fcb->ff_blocks * (SInt64)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes)) // Too big?
+ if ( (vcb->vcbSigWord == kHFSSigWord) && // Too big?
+ ((((SInt64)fcb->ff_blocks * (SInt64)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes) ) {
if (allOrNothing) // Yes, must they have it all?
goto Overflow; // Yes, can't have it
else {
--blocksToAdd; // No, give give 'em one block less
bytesToAdd -= volumeBlockSize;
}
+ }
//
// If allocation is all-or-nothing, make sure there are
// Enough blocks are already allocated. Just update the FCB to reflect the new length.
fcb->ff_blocks = peof / volumeBlockSize;
FTOC(fcb)->c_blocks += (bytesToAdd / volumeBlockSize);
- FTOC(fcb)->c_flag |= C_MODIFIED;
+ FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
goto Exit;
}
if (err != fxRangeErr) // Any real error?
// else, keep getting bits and pieces (non-contig)
err = noErr;
wantContig = true;
+ useMetaZone = flags & kEFMetadataMask;
vcb->vcbFreeExtCnt = 0; /* For now, force rebuild of free extent list */
do {
if (blockHint != 0)
err = BlockAllocate(
vcb,
startBlock,
- MIN(bytesToAdd, availbytes),
- MIN(maximumBytes, availbytes),
+ howmany(MIN(bytesToAdd, availbytes), volumeBlockSize),
+ howmany(MIN(maximumBytes, availbytes), volumeBlockSize),
wantContig,
+ useMetaZone,
&actualStartBlock,
&actualNumBlocks);
}
}
} else {
- err = BlockAllocate(vcb, startBlock, bytesToAdd, maximumBytes,
- wantContig, &actualStartBlock, &actualNumBlocks);
+ err = BlockAllocate(vcb, startBlock, howmany(bytesToAdd, volumeBlockSize),
+ howmany(maximumBytes, volumeBlockSize), wantContig, useMetaZone,
+ &actualStartBlock, &actualNumBlocks);
}
if (err == dskFulErr) {
if (forceContig)
}
if (actualNumBlocks != 0)
err = noErr;
+ if (useMetaZone == 0) {
+ /* Couldn't get anything so dip into metadat zone */
+ err = noErr;
+ useMetaZone = 1;
+ continue;
+ }
}
if (err == noErr) {
+ if (actualNumBlocks != 0) {
+ // this catalog entry *must* get forced to disk when
+ // hfs_update() is called
+ FTOC(fcb)->c_flag |= C_FORCEUPDATE;
+ }
+
// Add the new extent to the existing extent record, or create a new one.
if ((actualStartBlock == startBlock) && (blockHint == 0)) {
// We grew the file's last extent, so just adjust the number of blocks.
}
fcb->ff_blocks += (bytesThisExtent / volumeBlockSize);
FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize);
- FTOC(fcb)->c_flag |= C_MODIFIED;
+ FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
// If contiguous allocation was requested, then we've already got one contiguous
// chunk. If we didn't get all we wanted, then adjust the error to disk full.
ErrorExit:
Exit:
- *actualBytesAdded = (SInt64)(fcb->ff_blocks - prevblocks) * (SInt64)volumeBlockSize;
+ if (VCBTOHFS(vcb)->hfs_flags & HFS_METADATA_ZONE) {
+ /* Keep the roving allocator out of the metadata zone. */
+ if (vcb->nextAllocation >= VCBTOHFS(vcb)->hfs_metazone_start &&
+ vcb->nextAllocation <= VCBTOHFS(vcb)->hfs_metazone_end) {
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ vcb->nextAllocation = VCBTOHFS(vcb)->hfs_metazone_end + 1;
+ vcb->vcbFlags |= 0xFF00;
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
+ }
+ }
+ if (prevblocks < fcb->ff_blocks) {
+ *actualBytesAdded = (SInt64)(fcb->ff_blocks - prevblocks) * (SInt64)volumeBlockSize;
+ } else {
+ *actualBytesAdded = 0;
+ }
if (needsFlush)
(void) FlushExtentFile(vcb);
// block boundry. If the 'TFTrunExt' option is specified, the file is
// truncated to the end of the extent containing the new PEOF.
//
-// Input: A2.L - VCB pointer
-// A1.L - pointer to FCB array
-// D1.W - file refnum
-// D2.B - option flags
-// TFTrunExt - truncate to the extent containing new PEOF
-// D3.L - new PEOF
-//
-// Output: D0.W - result code
-// 0 = ok
-// -n = IO error
-//
-// Note: TruncateFile updates the PEOF in the FCB.
//_________________________________________________________________________________
+__private_extern__
OSErr TruncateFileC (
ExtendedVCB *vcb, // volume that file resides on
FCB *fcb, // FCB of file to truncate
UInt8 forkType;
Boolean extentChanged; // true if we actually changed an extent
Boolean recordDeleted; // true if an extent record got deleted
-
recordDeleted = false;
// two gigabytes or more, then round down by one allocation block (??? really?
// shouldn't that be an error?).
//
- nextBlock = FileBytesToBlocks(peof, vcb->blockSize); // number of allocation blocks to remain in file
+ nextBlock = howmany(peof, vcb->blockSize); // number of allocation blocks to remain in file
peof = (SInt64)((SInt64)nextBlock * (SInt64)vcb->blockSize); // number of bytes in those blocks
if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= kTwoGigabytes)) {
#if DEBUG_BUILD
//
// Update FCB's length
//
+ /*
+ * XXX Any errors could cause ff_blocks and c_blocks to get out of sync...
+ */
numBlocks = peof / vcb->blockSize;
FTOC(fcb)->c_blocks -= (fcb->ff_blocks - numBlocks);
fcb->ff_blocks = numBlocks;
- FTOC(fcb)->c_flag |= C_MODIFIED;
+
+ // this catalog entry is modified and *must* get forced
+ // to disk when hfs_update() is called
+ FTOC(fcb)->c_flag |= C_MODIFIED | C_FORCEUPDATE;
//
// If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate
Done:
ErrorExit:
-
if (recordDeleted)
(void) FlushExtentFile(vcb);
}
+/*
+ * HFS Plus only
+ *
+ */
+__private_extern__
+OSErr HeadTruncateFile (
+ ExtendedVCB *vcb,
+ FCB *fcb,
+ UInt32 headblks)
+{
+ HFSPlusExtentRecord extents;
+ HFSPlusExtentRecord tailExtents;
+ HFSCatalogNodeID fileID;
+ UInt8 forkType;
+ UInt32 blkcnt;
+ UInt32 startblk;
+ UInt32 blksfreed;
+ int i, j;
+ int error = 0;
+ int lockflags;
+
+
+ if (vcb->vcbSigWord != kHFSPlusSigWord)
+ return (-1);
+
+ forkType = FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType;
+ fileID = FTOC(fcb)->c_fileid;
+ bzero(tailExtents, sizeof(tailExtents));
+
+ blksfreed = 0;
+ startblk = 0;
+
+ /*
+ * Process catalog resident extents
+ */
+ for (i = 0, j = 0; i < kHFSPlusExtentDensity; ++i) {
+ blkcnt = fcb->fcbExtents[i].blockCount;
+ if (blkcnt == 0)
+ break; /* end of extents */
+
+ if (blksfreed < headblks) {
+ error = BlockDeallocate(vcb, fcb->fcbExtents[i].startBlock, blkcnt);
+ /*
+ * Any errors after the first BlockDeallocate
+ * must be ignored so we can put the file in
+ * a known state.
+ */
+ if (error ) {
+ if (i == 0)
+ goto ErrorExit; /* uh oh */
+ else {
+ error = 0;
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ }
+ }
+
+ blksfreed += blkcnt;
+ fcb->fcbExtents[i].startBlock = 0;
+ fcb->fcbExtents[i].blockCount = 0;
+ } else {
+ tailExtents[j].startBlock = fcb->fcbExtents[i].startBlock;
+ tailExtents[j].blockCount = blkcnt;
+ ++j;
+ }
+ startblk += blkcnt;
+ }
+
+ if (blkcnt == 0)
+ goto CopyExtents;
+
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
+
+ /*
+ * Process overflow extents
+ */
+ for (;;) {
+ UInt32 extblks;
+
+ error = FindExtentRecord(vcb, forkType, fileID, startblk, false, NULL, extents, NULL);
+ if (error) {
+ /*
+ * Any errors after the first BlockDeallocate
+ * must be ignored so we can put the file in
+ * a known state.
+ */
+ if (error != btNotFound)
+ printf("HeadTruncateFile: problems finding extents %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ break;
+ }
+
+ for(i = 0, extblks = 0; i < kHFSPlusExtentDensity; ++i) {
+ blkcnt = extents[i].blockCount;
+ if (blkcnt == 0)
+ break; /* end of extents */
+
+ if (blksfreed < headblks) {
+ error = BlockDeallocate(vcb, extents[i].startBlock, blkcnt);
+ if (error) {
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ }
+ blksfreed += blkcnt;
+ } else {
+ tailExtents[j].startBlock = extents[i].startBlock;
+ tailExtents[j].blockCount = blkcnt;
+ ++j;
+ }
+ extblks += blkcnt;
+ }
+
+ error = DeleteExtentRecord(vcb, forkType, fileID, startblk);
+ if (error) {
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ }
+
+ if (blkcnt == 0)
+ break; /* all done */
+
+ startblk += extblks;
+ }
+ hfs_systemfile_unlock(vcb, lockflags);
+
+CopyExtents:
+ if (blksfreed) {
+ bcopy(tailExtents, fcb->fcbExtents, sizeof(tailExtents));
+ blkcnt = fcb->ff_blocks - headblks;
+ FTOC(fcb)->c_blocks -= blkcnt;
+ fcb->ff_blocks = blkcnt;
+
+ FTOC(fcb)->c_flag |= C_FORCEUPDATE;
+ FTOC(fcb)->c_touch_chgtime = TRUE;
+
+ (void) FlushExtentFile(vcb);
+ }
+
+ErrorExit:
+ return MacToVFSError(error);
+}
+
+
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
// Routine: SearchExtentRecord (was XRSearch)
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
static OSErr SearchExtentRecord(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
UInt32 searchFABN,
const HFSPlusExtentRecord extentData,
UInt32 extentDataStartFABN,
//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
static OSErr SearchExtentFile(
- const ExtendedVCB *vcb,
+ ExtendedVCB *vcb,
const FCB *fcb,
SInt64 filePosition,
HFSPlusExtentKey *foundExtentKey,
UInt32 filePositionBlock;
SInt64 temp64;
Boolean noMoreExtents;
+ int lockflags;
temp64 = filePosition / (SInt64)vcb->blockSize;
filePositionBlock = (UInt32)temp64;
//
// Find the desired record, or the previous record if it is the same fork
//
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
+
err = FindExtentRecord(vcb, FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType,
FTOC(fcb)->c_fileid, filePositionBlock, true, foundExtentKey, foundExtentData, extentBTreeHint);
+ hfs_systemfile_unlock(vcb, lockflags);
if (err == btNotFound) {
//
-//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
+//============================================================================
// Routine: UpdateExtentRecord
//
// Function: Write new extent data to an existing extent record with a given key.
//
// Result: noErr = ok
// (other) = error from BTree
-//\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b\8b
+//============================================================================
static OSErr UpdateExtentRecord (
- const ExtendedVCB *vcb,
- FCB *fcb,
- const HFSPlusExtentKey *extentFileKey,
- const HFSPlusExtentRecord extentData,
- UInt32 extentBTreeHint)
+ ExtendedVCB *vcb,
+ FCB *fcb,
+ const HFSPlusExtentKey *extentFileKey,
+ const HFSPlusExtentRecord extentData,
+ UInt32 extentBTreeHint)
{
OSErr err = noErr;
FSBufferDescriptor btRecord;
UInt16 btRecordSize;
FCB * btFCB;
+ int lockflags;
//
// Need to find and change a record in Extents BTree
//
btFCB = GetFileControlBlock(vcb->extentsRefNum);
+
MALLOC(btIterator, BTreeIterator *, sizeof(*btIterator), M_TEMP, M_WAITOK);
bzero(btIterator, sizeof(*btIterator));
+ /*
+ * The lock taken by callers of ExtendFileC/TruncateFileC is
+ * speculative and only occurs when the file already has
+ * overflow extents. So we need to make sure we have the lock
+ * here. The extents btree lock can be nested (its recursive)
+ * so we always take it here.
+ */
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
+
if (vcb->vcbSigWord == kHFSSigWord) {
HFSExtentKey * key; // Actual extent key used on disk in HFS
HFSExtentRecord foundData; // The extent data actually found
if (err == noErr)
err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
+ (void) BTFlushPath(btFCB);
}
else { // HFS Plus volume
HFSPlusExtentRecord foundData; // The extent data actually found
BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord));
err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize);
}
+ (void) BTFlushPath(btFCB);
}
+ hfs_systemfile_unlock(vcb, lockflags);
FREE(btIterator, M_TEMP);
}
-void HFSToHFSPlusExtents(
- const HFSExtentRecord oldExtents,
- HFSPlusExtentRecord newExtents)
-{
- UInt32 i;
- // copy the first 3 extents
- newExtents[0].startBlock = oldExtents[0].startBlock;
- newExtents[0].blockCount = oldExtents[0].blockCount;
- newExtents[1].startBlock = oldExtents[1].startBlock;
- newExtents[1].blockCount = oldExtents[1].blockCount;
- newExtents[2].startBlock = oldExtents[2].startBlock;
- newExtents[2].blockCount = oldExtents[2].blockCount;
-
- // zero out the remaining ones
- for (i = 3; i < kHFSPlusExtentDensity; ++i)
- {
- newExtents[i].startBlock = 0;
- newExtents[i].blockCount = 0;
- }
-}
-
-
-
-OSErr HFSPlusToHFSExtents(
+static OSErr HFSPlusToHFSExtents(
const HFSPlusExtentRecord oldExtents,
HFSExtentRecord newExtents)
{
-OSErr GetFCBExtentRecord(
+static OSErr GetFCBExtentRecord(
const FCB *fcb,
HFSPlusExtentRecord extents)
{
return true;
}
+
+
+//_________________________________________________________________________________
+//
+// Routine: NodesAreContiguous
+//
+// Purpose: Ensure that all b-tree nodes are contiguous on disk
+// Called by BTOpenPath during volume mount
+//_________________________________________________________________________________
+
+__private_extern__
+Boolean NodesAreContiguous(
+ ExtendedVCB *vcb,
+ FCB *fcb,
+ UInt32 nodeSize)
+{
+ UInt32 mask;
+ UInt32 startBlock;
+ UInt32 blocksChecked;
+ UInt32 hint;
+ HFSPlusExtentKey key;
+ HFSPlusExtentRecord extents;
+ OSErr result;
+ Boolean lastExtentReached;
+ int lockflags;
+
+
+ if (vcb->blockSize >= nodeSize)
+ return TRUE;
+
+ mask = (nodeSize / vcb->blockSize) - 1;
+
+ // check the local extents
+ (void) GetFCBExtentRecord(fcb, extents);
+ if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) )
+ return FALSE;
+
+ if ( lastExtentReached ||
+ (SInt64)((SInt64)blocksChecked * (SInt64)vcb->blockSize) >= (SInt64)fcb->ff_size)
+ return TRUE;
+
+ startBlock = blocksChecked;
+
+ lockflags = hfs_systemfile_lock(vcb, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
+
+ // check the overflow extents (if any)
+ while ( !lastExtentReached )
+ {
+ result = FindExtentRecord(vcb, kDataForkType, fcb->ff_cp->c_fileid, startBlock, FALSE, &key, extents, &hint);
+ if (result) break;
+
+ if ( !ExtentsAreIntegral(extents, mask, &blocksChecked, &lastExtentReached) ) {
+ hfs_systemfile_unlock(vcb, lockflags);
+ return FALSE;
+ }
+ startBlock += blocksChecked;
+ }
+ hfs_systemfile_unlock(vcb, lockflags);
+ return TRUE;
+}
+