X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/7e41aa883dd258f888d0470250eead40a53ef1f5..3903760236c30e3b5ace7a4eefac3a269d68957c:/bsd/hfs/hfscommon/Misc/FileExtentMapping.c?ds=inline diff --git a/bsd/hfs/hfscommon/Misc/FileExtentMapping.c b/bsd/hfs/hfscommon/Misc/FileExtentMapping.c deleted file mode 100644 index 31249e05b..000000000 --- a/bsd/hfs/hfscommon/Misc/FileExtentMapping.c +++ /dev/null @@ -1,2266 +0,0 @@ -/* - * Copyright (c) 2000-2014 Apple Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ - - -#include "../../hfs.h" -#include "../../hfs_format.h" -#include "../../hfs_endian.h" - -#include "../headers/FileMgrInternal.h" -#include "../headers/BTreesInternal.h" - -#include -#include - -/* -============================================================ -Public (Exported) Routines: -============================================================ - - ExtendFileC Allocate more space to a given file. - - CompareExtentKeys - Compare two extents file keys (a search key and a trial - key). Used by the BTree manager when searching for, - adding, or deleting keys in the extents file of an HFS - volume. - - CompareExtentKeysPlus - Compare two extents file keys (a search key and a trial - key). Used by the BTree manager when searching for, - adding, or deleting keys in the extents file of an HFS+ - volume. - - MapFileBlockC Convert (map) an offset within a given file into a - physical disk address. - - TruncateFileC Truncates the disk space allocated to a file. The file - space is truncated to a specified new physical EOF, rounded - up to the next allocation block boundry. There is an option - to truncate to the end of the extent containing the new EOF. - - FlushExtentFile - Flush the extents file for a given volume. - - SearchExtentFile - Search the FCB and extents file for an extent record that - contains a given file position (in bytes). - - -============================================================ -Internal Routines: -============================================================ - FindExtentRecord - Search the extents BTree for a particular extent record. - SearchExtentRecord - Search a given extent record to see if it contains a given - file position (in bytes). Used by SearchExtentFile. - ReleaseExtents - Deallocate all allocation blocks in all extents of an extent - data record. - TruncateExtents - Deallocate blocks and delete extent records for all allocation - blocks beyond a certain point in a file. The starting point - must be the first file allocation block for some extent record - for the file. - DeallocateFork - Deallocate all allocation blocks belonging to a given fork. - UpdateExtentRecord - If the extent record came from the extents file, write out - the updated record; otherwise, copy the updated record into - the FCB resident extent record. If the record has no extents, - and was in the extents file, then delete the record instead. -*/ - -#if CONFIG_HFS_STD -static const int64_t kTwoGigabytes = 0x80000000LL; -#endif - -enum -{ - kDataForkType = 0, - kResourceForkType = 0xFF, - - kPreviousRecord = -1 -}; - - -#if CONFIG_HFS_STD -static OSErr HFSPlusToHFSExtents( - const HFSPlusExtentRecord oldExtents, - HFSExtentRecord newExtents); -#endif - -static OSErr FindExtentRecord( - const ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock, - Boolean allowPrevious, - HFSPlusExtentKey *foundKey, - HFSPlusExtentRecord foundData, - u_int32_t *foundHint); - -static OSErr DeleteExtentRecord( - const ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock); - -static OSErr CreateExtentRecord( - ExtendedVCB *vcb, - HFSPlusExtentKey *key, - HFSPlusExtentRecord extents, - u_int32_t *hint); - - -static OSErr GetFCBExtentRecord( - const FCB *fcb, - HFSPlusExtentRecord extents); - -static OSErr SearchExtentRecord( - ExtendedVCB *vcb, - u_int32_t searchFABN, - const HFSPlusExtentRecord extentData, - u_int32_t extentDataStartFABN, - u_int32_t *foundExtentDataOffset, - u_int32_t *endingFABNPlusOne, - Boolean *noMoreExtents); - -static OSErr ReleaseExtents( - ExtendedVCB *vcb, - const HFSPlusExtentRecord extentRecord, - u_int32_t *numReleasedAllocationBlocks, - Boolean *releasedLastExtent); - -static OSErr DeallocateFork( - ExtendedVCB *vcb, - HFSCatalogNodeID fileID, - u_int8_t forkType, - HFSPlusExtentRecord catalogExtents, - Boolean * recordDeleted); - -static OSErr TruncateExtents( - ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock, - Boolean * recordDeleted); - -static OSErr UpdateExtentRecord ( - ExtendedVCB *vcb, - FCB *fcb, - int deleted, - const HFSPlusExtentKey *extentFileKey, - const HFSPlusExtentRecord extentData, - u_int32_t extentBTreeHint); - -static Boolean ExtentsAreIntegral( - const HFSPlusExtentRecord extentRecord, - u_int32_t mask, - u_int32_t *blocksChecked, - Boolean *checkedLastExtent); - -//_________________________________________________________________________________ -// -// Routine: FindExtentRecord -// -// Purpose: Search the extents BTree for an extent record matching the given -// FileID, fork, and starting file allocation block number. -// -// Inputs: -// vcb Volume to search -// forkType 0 = data fork, -1 = resource fork -// fileID File's FileID (CatalogNodeID) -// startBlock Starting file allocation block number -// allowPrevious If the desired record isn't found and this flag is set, -// then see if the previous record belongs to the same fork. -// If so, then return it. -// -// Outputs: -// foundKey The key data for the record actually found -// foundData The extent record actually found (NOTE: on an HFS volume, the -// fourth entry will be zeroes. -// foundHint The BTree hint to find the node again -//_________________________________________________________________________________ -static OSErr FindExtentRecord( - const ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock, - Boolean allowPrevious, - HFSPlusExtentKey *foundKey, - HFSPlusExtentRecord foundData, - u_int32_t *foundHint) -{ - FCB * fcb; - struct BTreeIterator *btIterator = NULL; - FSBufferDescriptor btRecord; - OSErr err; - u_int16_t btRecordSize; - - err = noErr; - if (foundHint) - *foundHint = 0; - fcb = GetFileControlBlock(vcb->extentsRefNum); - - MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); - if (btIterator == NULL) { - return memFullErr; // translates to ENOMEM - } - bzero(btIterator, sizeof(*btIterator)); - - /* HFS Plus / HFSX */ - if (vcb->vcbSigWord != kHFSSigWord) { - HFSPlusExtentKey * extentKeyPtr; - HFSPlusExtentRecord extentData; - - extentKeyPtr = (HFSPlusExtentKey*) &btIterator->key; - extentKeyPtr->keyLength = kHFSPlusExtentKeyMaximumLength; - extentKeyPtr->forkType = forkType; - extentKeyPtr->pad = 0; - extentKeyPtr->fileID = fileID; - extentKeyPtr->startBlock = startBlock; - - btRecord.bufferAddress = &extentData; - btRecord.itemSize = sizeof(HFSPlusExtentRecord); - btRecord.itemCount = 1; - - err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); - - if (err == btNotFound && allowPrevious) { - err = BTIterateRecord(fcb, kBTreePrevRecord, btIterator, &btRecord, &btRecordSize); - - // A previous record may not exist, so just return btNotFound (like we would if - // it was for the wrong file/fork). - if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long - err = btNotFound; - - if (err == noErr) { - // Found a previous record. Does it belong to the same fork of the same file? - if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType) - err = btNotFound; - } - } - - if (err == noErr) { - // 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)); - } - } -#if CONFIG_HFS_STD - else { - HFSExtentKey * extentKeyPtr; - HFSExtentRecord extentData; - - extentKeyPtr = (HFSExtentKey*) &btIterator->key; - extentKeyPtr->keyLength = kHFSExtentKeyMaximumLength; - extentKeyPtr->forkType = forkType; - extentKeyPtr->fileID = fileID; - extentKeyPtr->startBlock = startBlock; - - btRecord.bufferAddress = &extentData; - btRecord.itemSize = sizeof(HFSExtentRecord); - btRecord.itemCount = 1; - - err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); - - if (err == btNotFound && allowPrevious) { - err = BTIterateRecord(fcb, kBTreePrevRecord, btIterator, &btRecord, &btRecordSize); - - // A previous record may not exist, so just return btNotFound (like we would if - // it was for the wrong file/fork). - if (err == (OSErr) fsBTStartOfIterationErr) //¥¥ fsBTStartOfIterationErr is type unsigned long - err = btNotFound; - - if (err == noErr) { - // Found a previous record. Does it belong to the same fork of the same file? - if (extentKeyPtr->fileID != fileID || extentKeyPtr->forkType != forkType) - err = btNotFound; - } - } - - if (err == noErr) { - u_int16_t i; - - // 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; - foundData[1].blockCount = extentData[1].blockCount; - foundData[2].startBlock = extentData[2].startBlock; - foundData[2].blockCount = extentData[2].blockCount; - - for (i = 3; i < kHFSPlusExtentDensity; ++i) - { - foundData[i].startBlock = 0; - foundData[i].blockCount = 0; - } - } - } -#endif - - if (foundHint) - *foundHint = btIterator->hint.nodeNum; - - FREE(btIterator, M_TEMP); - return err; -} - - - -static OSErr CreateExtentRecord( - ExtendedVCB *vcb, - HFSPlusExtentKey *key, - HFSPlusExtentRecord extents, - u_int32_t *hint) -{ - struct BTreeIterator *btIterator = NULL; - FSBufferDescriptor btRecord; - u_int16_t btRecordSize; - int lockflags; - OSErr err; - - err = noErr; - *hint = 0; - - MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); - if (btIterator == NULL) { - return memFullErr; // translates to ENOMEM - } - 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); - - /* HFS+/HFSX */ - if (vcb->vcbSigWord != kHFSSigWord) { - btRecordSize = sizeof(HFSPlusExtentRecord); - btRecord.bufferAddress = extents; - btRecord.itemSize = btRecordSize; - btRecord.itemCount = 1; - - BlockMoveData(key, &btIterator->key, sizeof(HFSPlusExtentKey)); - } -#if CONFIG_HFS_STD - else { - /* HFS Standard */ - HFSExtentKey * keyPtr; - HFSExtentRecord data; - - btRecordSize = sizeof(HFSExtentRecord); - btRecord.bufferAddress = &data; - btRecord.itemSize = btRecordSize; - btRecord.itemCount = 1; - - keyPtr = (HFSExtentKey*) &btIterator->key; - keyPtr->keyLength = kHFSExtentKeyMaximumLength; - keyPtr->forkType = key->forkType; - keyPtr->fileID = key->fileID; - keyPtr->startBlock = key->startBlock; - - err = HFSPlusToHFSExtents(extents, data); - } -#endif - - if (err == noErr) - err = BTInsertRecord(GetFileControlBlock(vcb->extentsRefNum), btIterator, &btRecord, btRecordSize); - - if (err == noErr) - *hint = btIterator->hint.nodeNum; - - (void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum)); - - hfs_systemfile_unlock(vcb, lockflags); - - FREE (btIterator, M_TEMP); - return err; -} - - -static OSErr DeleteExtentRecord( - const ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock) -{ - struct BTreeIterator *btIterator = NULL; - OSErr err; - - err = noErr; - - MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); - if (btIterator == NULL) { - return memFullErr; // translates to ENOMEM - } - bzero(btIterator, sizeof(*btIterator)); - - /* HFS+ / HFSX */ - if (vcb->vcbSigWord != kHFSSigWord) { // HFS Plus volume - HFSPlusExtentKey * keyPtr; - - keyPtr = (HFSPlusExtentKey*) &btIterator->key; - keyPtr->keyLength = kHFSPlusExtentKeyMaximumLength; - keyPtr->forkType = forkType; - keyPtr->pad = 0; - keyPtr->fileID = fileID; - keyPtr->startBlock = startBlock; - } -#if CONFIG_HFS_STD - else { - /* HFS standard */ - HFSExtentKey * keyPtr; - - keyPtr = (HFSExtentKey*) &btIterator->key; - keyPtr->keyLength = kHFSExtentKeyMaximumLength; - keyPtr->forkType = forkType; - keyPtr->fileID = fileID; - keyPtr->startBlock = startBlock; - } -#endif - - err = BTDeleteRecord(GetFileControlBlock(vcb->extentsRefNum), btIterator); - (void) BTFlushPath(GetFileControlBlock(vcb->extentsRefNum)); - - - FREE(btIterator, M_TEMP); - return err; -} - - - -//_________________________________________________________________________________ -// -// Routine: MapFileBlock -// -// Function: Maps a file position into a physical disk address. -// -//_________________________________________________________________________________ - -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) - daddr64_t *startSector, // first sector (NOT an allocation block) - size_t *availableBytes) // number of contiguous bytes (up to numberOfBytes) -{ - OSErr err; - u_int32_t allocBlockSize; // Size of the volume's allocation block - u_int32_t sectorSize; - HFSPlusExtentKey foundKey; - HFSPlusExtentRecord foundData; - u_int32_t foundIndex; - u_int32_t hint; - u_int32_t firstFABN; // file allocation block of first block in found extent - u_int32_t nextFABN; // file allocation block of block after end of found extent - off_t dataEnd; // (offset) end of range that is contiguous - u_int32_t sectorsPerBlock; // Number of sectors per allocation block - u_int32_t startBlock; // volume allocation block corresponding to firstFABN - daddr64_t temp; - off_t tmpOff; - - allocBlockSize = vcb->blockSize; - sectorSize = VCBTOHFS(vcb)->hfs_logical_block_size; - - err = SearchExtentFile(vcb, fcb, offset, &foundKey, foundData, &foundIndex, &hint, &nextFABN); - if (err == noErr) { - startBlock = foundData[foundIndex].startBlock; - firstFABN = nextFABN - foundData[foundIndex].blockCount; - } - - if (err != noErr) - { - return err; - } - - // - // Determine the end of the available space. It will either be the end of the extent, - // or the file's PEOF, whichever is smaller. - // - dataEnd = (off_t)((off_t)(nextFABN) * (off_t)(allocBlockSize)); // Assume valid data through end of this extent - if (((off_t)fcb->ff_blocks * (off_t)allocBlockSize) < dataEnd) // Is PEOF shorter? - dataEnd = (off_t)fcb->ff_blocks * (off_t)allocBlockSize; // Yes, so only map up to PEOF - - // Compute the number of sectors in an allocation block - sectorsPerBlock = allocBlockSize / sectorSize; // sectors per allocation block - - // - // Compute the absolute sector number that contains the offset of the given file - // offset in sectors from start of the extent + - // offset in sectors from start of allocation block space - // - temp = (daddr64_t)((offset - (off_t)((off_t)(firstFABN) * (off_t)(allocBlockSize)))/sectorSize); - temp += (daddr64_t)startBlock * (daddr64_t)sectorsPerBlock; - - /* Add in any volume offsets */ - if (vcb->vcbSigWord == kHFSPlusSigWord) - temp += vcb->hfsPlusIOPosOffset / sectorSize; - else - temp += vcb->vcbAlBlSt; - - // Return the desired sector for file position "offset" - *startSector = temp; - - // - // Determine the number of contiguous bytes until the end of the extent - // (or the amount they asked for, whichever comes first). - // - if (availableBytes) - { - tmpOff = dataEnd - offset; - /* - * Disallow negative runs. - */ - if (tmpOff <= 0) { - return EINVAL; - } - - if (tmpOff > (off_t)(numberOfBytes)) { - *availableBytes = numberOfBytes; // more there than they asked for, so pin the output - } - else { - *availableBytes = tmpOff; - } - } - - return noErr; -} - - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: ReleaseExtents -// -// Function: Release the extents of a single extent data record. -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -static OSErr ReleaseExtents( - ExtendedVCB *vcb, - const HFSPlusExtentRecord extentRecord, - u_int32_t *numReleasedAllocationBlocks, - Boolean *releasedLastExtent) -{ - u_int32_t extentIndex; - u_int32_t numberOfExtents; - OSErr err = noErr; - - *numReleasedAllocationBlocks = 0; - *releasedLastExtent = false; - - if (vcb->vcbSigWord == kHFSPlusSigWord) - numberOfExtents = kHFSPlusExtentDensity; - else - numberOfExtents = kHFSExtentDensity; - - for( extentIndex = 0; extentIndex < numberOfExtents; extentIndex++) - { - u_int32_t numAllocationBlocks; - - // Loop over the extent record and release the blocks associated with each extent. - - numAllocationBlocks = extentRecord[extentIndex].blockCount; - if ( numAllocationBlocks == 0 ) - { - *releasedLastExtent = true; - break; - } - - err = BlockDeallocate( vcb, extentRecord[extentIndex].startBlock, numAllocationBlocks , 0); - if ( err != noErr ) - break; - - *numReleasedAllocationBlocks += numAllocationBlocks; // bump FABN to beg of next extent - } - - return( err ); -} - - - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: TruncateExtents -// -// Purpose: Delete extent records whose starting file allocation block number -// is greater than or equal to a given starting block number. The -// allocation blocks represented by the extents are deallocated. -// -// Inputs: -// vcb Volume to operate on -// fileID Which file to operate on -// startBlock Starting file allocation block number for first extent -// record to delete. -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -static OSErr TruncateExtents( - ExtendedVCB *vcb, - u_int8_t forkType, - u_int32_t fileID, - u_int32_t startBlock, - Boolean * recordDeleted) -{ - OSErr err; - u_int32_t numberExtentsReleased; - Boolean releasedLastExtent; - u_int32_t 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); - if (err != noErr) { - if (err == btNotFound) - err = noErr; - break; - } - - err = ReleaseExtents( vcb, extents, &numberExtentsReleased, &releasedLastExtent ); - if (err != noErr) break; - - err = DeleteExtentRecord(vcb, forkType, fileID, startBlock); - if (err != noErr) break; - - *recordDeleted = true; - startBlock += numberExtentsReleased; - } - hfs_systemfile_unlock(vcb, lockflags); - - return err; -} - - - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: DeallocateFork -// -// Function: De-allocates all disk space allocated to a specified fork. -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -static OSErr DeallocateFork( - ExtendedVCB *vcb, - HFSCatalogNodeID fileID, - u_int8_t forkType, - HFSPlusExtentRecord catalogExtents, - Boolean * recordDeleted) /* true if a record was deleted */ -{ - OSErr err; - u_int32_t numReleasedAllocationBlocks; - Boolean releasedLastExtent; - - // Release the catalog extents - err = ReleaseExtents( vcb, catalogExtents, &numReleasedAllocationBlocks, &releasedLastExtent ); - // Release the extra extents, if present - if (err == noErr && !releasedLastExtent) - err = TruncateExtents(vcb, forkType, fileID, numReleasedAllocationBlocks, recordDeleted); - - return( err ); -} - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: FlushExtentFile -// -// Function: Flushes the extent file for a specified volume -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -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. - - if (FTOC(fcb)->c_flag & C_MODIFIED) - { - MarkVCBDirty( vcb ); - // err = FlushVolumeControlBlock( vcb ); - } - } - - return( err ); -} - - -#if CONFIG_HFS_STD -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: CompareExtentKeys -// -// Function: Compares two extent file keys (a search key and a trial key) for -// an HFS volume. -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -__private_extern__ -int32_t CompareExtentKeys( const HFSExtentKey *searchKey, const HFSExtentKey *trialKey ) -{ - int32_t result; // ± 1 - - #if DEBUG_BUILD - if (searchKey->keyLength != kHFSExtentKeyMaximumLength) - DebugStr("HFS: search Key is wrong length"); - if (trialKey->keyLength != kHFSExtentKeyMaximumLength) - DebugStr("HFS: trial Key is wrong length"); - #endif - - result = -1; // assume searchKey < trialKey - - if (searchKey->fileID == trialKey->fileID) { - // - // FileNum's are equal; compare fork types - // - if (searchKey->forkType == trialKey->forkType) { - // - // Fork types are equal; compare allocation block number - // - if (searchKey->startBlock == trialKey->startBlock) { - // - // Everything is equal - // - result = 0; - } - else { - // - // Allocation block numbers differ; determine sign - // - if (searchKey->startBlock > trialKey->startBlock) - result = 1; - } - } - else { - // - // Fork types differ; determine sign - // - if (searchKey->forkType > trialKey->forkType) - result = 1; - } - } - else { - // - // FileNums differ; determine sign - // - if (searchKey->fileID > trialKey->fileID) - result = 1; - } - - return( result ); -} -#endif - - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: CompareExtentKeysPlus -// -// Function: Compares two extent file keys (a search key and a trial key) for -// an HFS volume. -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -__private_extern__ -int32_t CompareExtentKeysPlus( const HFSPlusExtentKey *searchKey, const HFSPlusExtentKey *trialKey ) -{ - int32_t result; // ± 1 - - #if DEBUG_BUILD - if (searchKey->keyLength != kHFSPlusExtentKeyMaximumLength) - DebugStr("HFS: search Key is wrong length"); - if (trialKey->keyLength != kHFSPlusExtentKeyMaximumLength) - DebugStr("HFS: trial Key is wrong length"); - #endif - - result = -1; // assume searchKey < trialKey - - if (searchKey->fileID == trialKey->fileID) { - // - // FileNum's are equal; compare fork types - // - if (searchKey->forkType == trialKey->forkType) { - // - // Fork types are equal; compare allocation block number - // - if (searchKey->startBlock == trialKey->startBlock) { - // - // Everything is equal - // - result = 0; - } - else { - // - // Allocation block numbers differ; determine sign - // - if (searchKey->startBlock > trialKey->startBlock) - result = 1; - } - } - else { - // - // Fork types differ; determine sign - // - if (searchKey->forkType > trialKey->forkType) - result = 1; - } - } - else { - // - // FileNums differ; determine sign - // - if (searchKey->fileID > trialKey->fileID) - result = 1; - } - - return( result ); -} - -static int -should_pin_blocks(hfsmount_t *hfsmp, FCB *fcb) -{ - if (!ISSET(hfsmp->hfs_flags, HFS_CS_HOTFILE_PIN) - || fcb->ff_cp == NULL || fcb->ff_cp->c_vp == NULL) { - return 0; - } - - int pin_blocks; - - // - // File system metadata should get pinned - // - if (vnode_issystem(fcb->ff_cp->c_vp)) { - return 1; - } - - // - // If a file is AutoCandidate, we should not pin its blocks because - // it was an automatically added file and this function is intended - // to pin new blocks being added to user-generated content. - // - if (fcb->ff_cp->c_attr.ca_recflags & kHFSAutoCandidateMask) { - return 0; - } - - // - // If a file is marked FastDevPinned it is an existing pinned file - // or a new file that should be pinned. - // - // If a file is marked FastDevCandidate it is a new file that is - // being written to for the first time so we don't want to pin it - // just yet as it may not meet the criteria (i.e. too large). - // - if ((fcb->ff_cp->c_attr.ca_recflags & (kHFSFastDevPinnedMask)) != 0) { - pin_blocks = 1; - } else { - pin_blocks = 0; - } - - return pin_blocks; -} - - - -static void -pin_blocks_if_needed(ExtendedVCB *vcb, FCB *fcb, u_int32_t startBlock, u_int32_t blockCount) -{ - if (!should_pin_blocks(vcb, fcb)) { - return; - } - - // ask CoreStorage to pin the new blocks being added to this file - if (hfs_pin_block_range((struct hfsmount *)vcb, HFS_PIN_IT, startBlock, blockCount, vfs_context_kernel()) == 0) { - struct vnode *vp = fcb->ff_cp->c_vp; - - // and make sure to keep our accounting in order - hfs_hotfile_adjust_blocks(vp, -blockCount); - } -} - - - -/* - * Add a file extent to a file. - * - * Used by hfs_extendfs to extend the volume allocation bitmap file. - * - */ -int -AddFileExtent(ExtendedVCB *vcb, FCB *fcb, u_int32_t startBlock, u_int32_t blockCount) -{ - HFSPlusExtentKey foundKey; - HFSPlusExtentRecord foundData; - u_int32_t foundIndex; - u_int32_t hint; - u_int32_t nextBlock; - int64_t peof; - int i; - int error; - - peof = (int64_t)(fcb->ff_blocks + blockCount) * (int64_t)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 if (error == 0) { - pin_blocks_if_needed(vcb, fcb, startBlock, blockCount); - } - - } else { - /* - * Add a new extent into existing record. - */ - foundData[foundIndex].startBlock = startBlock; - foundData[foundIndex].blockCount = blockCount; - error = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint); - if (error == 0) { - pin_blocks_if_needed(vcb, fcb, startBlock, blockCount); - } - } - (void) FlushExtentFile(vcb); - - return (error); -} - - -//_________________________________________________________________________________ -// -// Routine: Extendfile -// -// Function: Extends the disk space allocated to a file. -// -//_________________________________________________________________________________ - -OSErr ExtendFileC ( - ExtendedVCB *vcb, // volume that file resides on - FCB *fcb, // FCB of file to truncate - int64_t bytesToAdd, // number of bytes to allocate - u_int32_t blockHint, // desired starting allocation block - u_int32_t flags, // EFContig and/or EFAll - int64_t *actualBytesAdded) // number of bytes actually allocated -{ - OSErr err; - u_int32_t volumeBlockSize; - int64_t blocksToAdd; - int64_t bytesThisExtent; - HFSPlusExtentKey foundKey; - HFSPlusExtentRecord foundData; - u_int32_t foundIndex; - u_int32_t hint; - u_int32_t nextBlock; - u_int32_t startBlock; - Boolean allOrNothing; - Boolean forceContig; - Boolean wantContig; - Boolean useMetaZone; - Boolean needsFlush; - int allowFlushTxns; - u_int32_t actualStartBlock; - u_int32_t actualNumBlocks; - u_int32_t numExtentsPerRecord; - int64_t maximumBytes; - int64_t availbytes; - int64_t peof; - u_int32_t prevblocks; - uint32_t fastdev = 0; - - struct hfsmount *hfsmp = (struct hfsmount*)vcb; - allowFlushTxns = 0; - needsFlush = false; - *actualBytesAdded = 0; - volumeBlockSize = vcb->blockSize; - allOrNothing = ((flags & kEFAllMask) != 0); - forceContig = ((flags & kEFContigMask) != 0); - prevblocks = fcb->ff_blocks; - - if (vcb->vcbSigWord != kHFSSigWord) { - numExtentsPerRecord = kHFSPlusExtentDensity; - } -#if CONFIG_HFS_STD - else { - /* HFS Standard */ - numExtentsPerRecord = kHFSExtentDensity; - - /* Make sure the request and new PEOF are less than 2GB if HFS std*/ - if (bytesToAdd >= kTwoGigabytes) - goto HFS_Std_Overflow; - if ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes) - goto HFS_Std_Overflow; - } -#endif - - // - // Determine how many blocks need to be allocated. - // Round up the number of desired bytes to add. - // - blocksToAdd = howmany(bytesToAdd, volumeBlockSize); - bytesToAdd = (int64_t)((int64_t)blocksToAdd * (int64_t)volumeBlockSize); - - /* - * For deferred allocations just reserve the blocks. - */ - if ((flags & kEFDeferMask) - && (vcb->vcbSigWord == kHFSPlusSigWord) - && (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC) - && (blocksToAdd < hfs_freeblks(VCBTOHFS(vcb), 1))) { - hfs_lock_mount (hfsmp); - vcb->loanedBlocks += blocksToAdd; - hfs_unlock_mount(hfsmp); - - fcb->ff_unallocblocks += blocksToAdd; - FTOC(fcb)->c_blocks += blocksToAdd; - fcb->ff_blocks += blocksToAdd; - - /* - * We haven't touched the disk here; no blocks have been - * allocated and the volume will not be inconsistent if we - * don't update the catalog record immediately. - */ - FTOC(fcb)->c_flag |= C_MINOR_MOD; - *actualBytesAdded = bytesToAdd; - return (0); - } - /* - * Give back any unallocated blocks before doing real allocations. - */ - if (fcb->ff_unallocblocks > 0) { - u_int32_t loanedBlocks; - - loanedBlocks = fcb->ff_unallocblocks; - blocksToAdd += loanedBlocks; - bytesToAdd = (int64_t)blocksToAdd * (int64_t)volumeBlockSize; - FTOC(fcb)->c_blocks -= loanedBlocks; - fcb->ff_blocks -= loanedBlocks; - fcb->ff_unallocblocks = 0; - - hfs_lock_mount(hfsmp); - vcb->loanedBlocks -= loanedBlocks; - hfs_unlock_mount(hfsmp); - } - - // - // If the file's clump size is larger than the allocation block size, - // then set the maximum number of bytes to the requested number of bytes - // rounded up to a multiple of the clump size. - // - if ((vcb->vcbClpSiz > (int32_t)volumeBlockSize) - && (bytesToAdd < (int64_t)HFS_MAX_DEFERED_ALLOC) - && (flags & kEFNoClumpMask) == 0) { - maximumBytes = (int64_t)howmany(bytesToAdd, vcb->vcbClpSiz); - maximumBytes *= vcb->vcbClpSiz; - } else { - maximumBytes = bytesToAdd; - } - -#if CONFIG_HFS_STD - // - // Compute new physical EOF, rounded up to a multiple of a block. - // - if ( (vcb->vcbSigWord == kHFSSigWord) && // Too big? - ((((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd) >= kTwoGigabytes) ) { - if (allOrNothing) // Yes, must they have it all? - goto HFS_Std_Overflow; // Yes, can't have it - else { - --blocksToAdd; // No, give give 'em one block less - bytesToAdd -= volumeBlockSize; - } - } -#endif - - // - // If allocation is all-or-nothing, make sure there are - // enough free blocks on the volume (quick test). - // - if (allOrNothing && - (blocksToAdd > hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask))) { - err = dskFulErr; - goto ErrorExit; - } - - // - // See if there are already enough blocks allocated to the file. - // - peof = ((int64_t)fcb->ff_blocks * (int64_t)volumeBlockSize) + bytesToAdd; // potential new PEOF - err = SearchExtentFile(vcb, fcb, peof-1, &foundKey, foundData, &foundIndex, &hint, &nextBlock); - if (err == noErr) { - // 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; - goto Exit; - } - if (err != fxRangeErr) // Any real error? - goto ErrorExit; // Yes, so exit immediately - - // - // Adjust the PEOF to the end of the last extent. - // - peof = (int64_t)((int64_t)nextBlock * (int64_t)volumeBlockSize); // currently allocated PEOF - bytesThisExtent = (int64_t)(nextBlock - fcb->ff_blocks) * (int64_t)volumeBlockSize; - if (bytesThisExtent != 0) { - fcb->ff_blocks = nextBlock; - FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize); - FTOC(fcb)->c_flag |= C_MODIFIED; - bytesToAdd -= bytesThisExtent; - } - - // - // Allocate some more space. - // - // First try a contiguous allocation (of the whole amount). - // If that fails, get whatever we can. - // If forceContig, then take whatever we got - // else, keep getting bits and pieces (non-contig) - - /* - * Note that for sparse devices (like sparse bundle dmgs), we - * should only be aggressive with re-using once-allocated pieces - * if we're not dealing with system files. If we're trying to operate - * on behalf of a system file, we need the maximum contiguous amount - * possible. For non-system files we favor locality and fragmentation over - * contiguity as it can result in fewer blocks being needed from the underlying - * filesystem that the sparse image resides upon. - */ - err = noErr; - if ( (vcb->hfs_flags & HFS_HAS_SPARSE_DEVICE) - && (fcb->ff_cp->c_fileid >= kHFSFirstUserCatalogNodeID) - && (flags & kEFMetadataMask) == 0) { - /* - * We want locality over contiguity so by default we set wantContig to - * false unless we hit one of the circumstances below. - */ - wantContig = false; - if (hfs_isrbtree_active(VCBTOHFS(vcb))) { - /* - * If the red-black tree is acive, we can always find a suitable contiguous - * chunk. So if the user specifically requests contiguous files, we should - * honor that no matter what kind of device it is. - */ - if (forceContig) { - wantContig = true; - } - } - else { - /* - * If the red-black tree is not active, then only set wantContig to true - * if we have never done a contig scan on the device, which would populate - * the free extent cache. Note that the caller may explicitly unset the - * DID_CONTIG_SCAN bit in order to force us to vend a contiguous extent here - * if the caller wants to get a contiguous chunk. - */ - if ((vcb->hfs_flags & HFS_DID_CONTIG_SCAN) == 0) { - vcb->hfs_flags |= HFS_DID_CONTIG_SCAN; - wantContig = true; - } - } - } - else { - wantContig = true; - } - - if (should_pin_blocks(hfsmp, fcb)) - fastdev = HFS_ALLOC_FAST_DEV; - - useMetaZone = flags & kEFMetadataMask; - do { - if (blockHint != 0) - startBlock = blockHint; - else - startBlock = foundData[foundIndex].startBlock + foundData[foundIndex].blockCount; - - actualNumBlocks = 0; - actualStartBlock = 0; - - /* Find number of free blocks based on reserved block flag option */ - availbytes = (int64_t)hfs_freeblks(VCBTOHFS(vcb), flags & kEFReserveMask) * - (int64_t)volumeBlockSize; - if (availbytes <= 0) { - err = dskFulErr; - } else { - if (wantContig && (availbytes < bytesToAdd)) { - err = dskFulErr; - } - else { - uint32_t ba_flags = fastdev; - - if (wantContig) { - ba_flags |= HFS_ALLOC_FORCECONTIG; - } - if (useMetaZone) { - ba_flags |= HFS_ALLOC_METAZONE; - } - if (allowFlushTxns) { - ba_flags |= HFS_ALLOC_FLUSHTXN; - } - - err = BlockAllocate( - vcb, - startBlock, - howmany(MIN(bytesToAdd, availbytes), volumeBlockSize), - howmany(MIN(maximumBytes, availbytes), volumeBlockSize), - ba_flags, - &actualStartBlock, - &actualNumBlocks); - } - } - if (err == dskFulErr) { - if (forceContig) { - if (allowFlushTxns == 0) { - /* If we're forcing contiguity, re-try but allow plucking from recently freed regions */ - allowFlushTxns = 1; - wantContig = 1; - err = noErr; - continue; - } - else { - break; // AllocContig failed because not enough contiguous space - } - } - if (wantContig) { - // Couldn't get one big chunk, so get whatever we can. - err = noErr; - wantContig = false; - continue; - } - if (actualNumBlocks != 0) - err = noErr; - - if (useMetaZone == 0) { - /* Couldn't get anything so dip into metadat zone */ - err = noErr; - useMetaZone = 1; - continue; - } - - /* If we couldn't find what we needed without flushing the journal, then go ahead and do it now */ - if (allowFlushTxns == 0) { - allowFlushTxns = 1; - err = noErr; - continue; - } - - } - if (err == noErr) { - // 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. - foundData[foundIndex].blockCount += actualNumBlocks; - err = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint); - if (err != noErr) break; - } - else { - u_int16_t i; - - // Need to add a new extent. See if there is room in the current record. - if (foundData[foundIndex].blockCount != 0) // Is current extent free to use? - ++foundIndex; // No, so use the next one. - if (foundIndex == numExtentsPerRecord) { - // This record is full. Need to create a new one. - if (FTOC(fcb)->c_fileid == kHFSExtentsFileID) { - (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks, 0); - err = dskFulErr; // Oops. Can't extend extents file past first record. - break; - } - - foundKey.keyLength = kHFSPlusExtentKeyMaximumLength; - if (FORK_IS_RSRC(fcb)) - foundKey.forkType = kResourceForkType; - else - foundKey.forkType = kDataForkType; - foundKey.pad = 0; - foundKey.fileID = FTOC(fcb)->c_fileid; - foundKey.startBlock = nextBlock; - - foundData[0].startBlock = actualStartBlock; - foundData[0].blockCount = actualNumBlocks; - - // zero out remaining extents... - for (i = 1; i < kHFSPlusExtentDensity; ++i) - { - foundData[i].startBlock = 0; - foundData[i].blockCount = 0; - } - - foundIndex = 0; - - err = CreateExtentRecord(vcb, &foundKey, foundData, &hint); - if (err == fxOvFlErr) { - // We couldn't create an extent record because extents B-tree - // couldn't grow. Dellocate the extent just allocated and - // return a disk full error. - (void) BlockDeallocate(vcb, actualStartBlock, actualNumBlocks, 0); - err = dskFulErr; - } - if (err != noErr) break; - - needsFlush = true; // We need to update the B-tree header - } - else { - // Add a new extent into this record and update. - foundData[foundIndex].startBlock = actualStartBlock; - foundData[foundIndex].blockCount = actualNumBlocks; - err = UpdateExtentRecord(vcb, fcb, 0, &foundKey, foundData, hint); - if (err != noErr) break; - } - } - - // Figure out how many bytes were actually allocated. - // NOTE: BlockAllocate could have allocated more than we asked for. - // Don't set the PEOF beyond what our client asked for. - nextBlock += actualNumBlocks; - bytesThisExtent = (int64_t)((int64_t)actualNumBlocks * (int64_t)volumeBlockSize); - if (bytesThisExtent > bytesToAdd) { - bytesToAdd = 0; - } - else { - bytesToAdd -= bytesThisExtent; - maximumBytes -= bytesThisExtent; - } - fcb->ff_blocks += (bytesThisExtent / volumeBlockSize); - FTOC(fcb)->c_blocks += (bytesThisExtent / volumeBlockSize); - FTOC(fcb)->c_flag |= C_MODIFIED; - - // 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. - if (forceContig) { - if (bytesToAdd != 0) - err = dskFulErr; - break; // We've already got everything that's contiguous - } - } - } while (err == noErr && bytesToAdd); - -ErrorExit: -Exit: - 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_lock_mount (hfsmp); - HFS_UPDATE_NEXT_ALLOCATION(vcb, VCBTOHFS(vcb)->hfs_metazone_end + 1); - MarkVCBDirty(vcb); - hfs_unlock_mount(hfsmp); - } - } - if (prevblocks < fcb->ff_blocks) { - *actualBytesAdded = (int64_t)(fcb->ff_blocks - prevblocks) * (int64_t)volumeBlockSize; - } else { - *actualBytesAdded = 0; - } - - if (fastdev) { - hfs_hotfile_adjust_blocks(fcb->ff_cp->c_vp, - (int64_t)prevblocks - fcb->ff_blocks); - } - - if (needsFlush) - (void) FlushExtentFile(vcb); - - return err; - -#if CONFIG_HFS_STD -HFS_Std_Overflow: - err = fileBoundsErr; - goto ErrorExit; -#endif -} - - - -//_________________________________________________________________________________ -// -// Routine: TruncateFileC -// -// Function: Truncates the disk space allocated to a file. The file space is -// truncated to a specified new PEOF rounded up to the next allocation -// block boundry. If the 'TFTrunExt' option is specified, the file is -// truncated to the end of the extent containing the new PEOF. -// -//_________________________________________________________________________________ - -OSErr TruncateFileC ( - ExtendedVCB *vcb, // volume that file resides on - FCB *fcb, // FCB of file to truncate - int64_t peof, // new physical size for file - int deleted, // if nonzero, the file's catalog record has already been deleted. - int rsrc, // does this represent a resource fork or not? - uint32_t fileid, // the fileid of the file we're manipulating. - Boolean truncateToExtent) // if true, truncate to end of extent containing newPEOF - -{ - OSErr err; - u_int32_t nextBlock; // next file allocation block to consider - u_int32_t startBlock; // Physical (volume) allocation block number of start of a range - u_int32_t physNumBlocks; // Number of allocation blocks in file (according to PEOF) - u_int32_t numBlocks; - HFSPlusExtentKey key; // key for current extent record; key->keyLength == 0 if FCB's extent record - u_int32_t hint; // BTree hint corresponding to key - HFSPlusExtentRecord extentRecord; - u_int32_t extentIndex; - u_int32_t extentNextBlock; - u_int32_t numExtentsPerRecord; - int64_t temp64; - u_int8_t forkType; - Boolean extentChanged; // true if we actually changed an extent - Boolean recordDeleted; // true if an extent record got deleted - - recordDeleted = false; - - if (vcb->vcbSigWord == kHFSPlusSigWord) { - numExtentsPerRecord = kHFSPlusExtentDensity; - } - else { - numExtentsPerRecord = kHFSExtentDensity; - } - - if (rsrc) { - forkType = kResourceForkType; - } - else { - forkType = kDataForkType; - } - - temp64 = fcb->ff_blocks; - physNumBlocks = (u_int32_t)temp64; - - // - // Round newPEOF up to a multiple of the allocation block size. If new size is - // two gigabytes or more, then round down by one allocation block (??? really? - // shouldn't that be an error?). - // - nextBlock = howmany(peof, vcb->blockSize); // number of allocation blocks to remain in file - peof = (int64_t)((int64_t)nextBlock * (int64_t)vcb->blockSize); // number of bytes in those blocks - -#if CONFIG_HFS_STD - if ((vcb->vcbSigWord == kHFSSigWord) && (peof >= kTwoGigabytes)) { - #if DEBUG_BUILD - DebugStr("HFS: Trying to truncate a file to 2GB or more"); - #endif - err = fileBoundsErr; - goto ErrorExit; - } -#endif - - // - // Update FCB's length - // - /* - * XXX Any errors could cause ff_blocks and c_blocks to get out of sync... - */ - numBlocks = peof / vcb->blockSize; - if (!deleted) { - FTOC(fcb)->c_blocks -= (fcb->ff_blocks - numBlocks); - } - fcb->ff_blocks = numBlocks; - - // this catalog entry is modified and *must* get forced - // to disk when hfs_update() is called - if (!deleted) { - /* - * If the file is already C_NOEXISTS, then the catalog record - * has been removed from disk already. We wouldn't need to force - * another update - */ - FTOC(fcb)->c_flag |= C_MODIFIED; - } - // - // If the new PEOF is 0, then truncateToExtent has no meaning (we should always deallocate - // all storage). - // - if (peof == 0) { - int i; - - // Deallocate all the extents for this fork - err = DeallocateFork(vcb, fileid, forkType, fcb->fcbExtents, &recordDeleted); - if (err != noErr) goto ErrorExit; // got some error, so return it - - // Update the catalog extent record (making sure it's zeroed out) - if (err == noErr) { - for (i=0; i < kHFSPlusExtentDensity; i++) { - fcb->fcbExtents[i].startBlock = 0; - fcb->fcbExtents[i].blockCount = 0; - } - } - goto Done; - } - - // - // Find the extent containing byte (peof-1). This is the last extent we'll keep. - // (If truncateToExtent is true, we'll keep the whole extent; otherwise, we'll only - // keep up through peof). The search will tell us how many allocation blocks exist - // in the found extent plus all previous extents. - // - err = SearchExtentFile(vcb, fcb, peof-1, &key, extentRecord, &extentIndex, &hint, &extentNextBlock); - if (err != noErr) goto ErrorExit; - - extentChanged = false; // haven't changed the extent yet - - if (!truncateToExtent) { - // - // Shorten this extent. It may be the case that the entire extent gets - // freed here. - // - numBlocks = extentNextBlock - nextBlock; // How many blocks in this extent to free up - if (numBlocks != 0) { - // Compute first volume allocation block to free - startBlock = extentRecord[extentIndex].startBlock + extentRecord[extentIndex].blockCount - numBlocks; - // Free the blocks in bitmap - err = BlockDeallocate(vcb, startBlock, numBlocks, 0); - if (err != noErr) goto ErrorExit; - // Adjust length of this extent - extentRecord[extentIndex].blockCount -= numBlocks; - // If extent is empty, set start block to 0 - if (extentRecord[extentIndex].blockCount == 0) - extentRecord[extentIndex].startBlock = 0; - // Remember that we changed the extent record - extentChanged = true; - } - } - - // - // Now move to the next extent in the record, and set up the file allocation block number - // - nextBlock = extentNextBlock; // Next file allocation block to free - ++extentIndex; // Its index within the extent record - - // - // Release all following extents in this extent record. Update the record. - // - while (extentIndex < numExtentsPerRecord && extentRecord[extentIndex].blockCount != 0) { - numBlocks = extentRecord[extentIndex].blockCount; - // Deallocate this extent - err = BlockDeallocate(vcb, extentRecord[extentIndex].startBlock, numBlocks, 0); - if (err != noErr) goto ErrorExit; - // Update next file allocation block number - nextBlock += numBlocks; - // Zero out start and length of this extent to delete it from record - extentRecord[extentIndex].startBlock = 0; - extentRecord[extentIndex].blockCount = 0; - // Remember that we changed an extent - extentChanged = true; - // Move to next extent in record - ++extentIndex; - } - - // - // If any of the extents in the current record were changed, then update that - // record (in the FCB, or extents file). - // - if (extentChanged) { - err = UpdateExtentRecord(vcb, fcb, deleted, &key, extentRecord, hint); - if (err != noErr) goto ErrorExit; - } - - // - // If there are any following allocation blocks, then we need - // to seach for their extent records and delete those allocation - // blocks. - // - if (nextBlock < physNumBlocks) - err = TruncateExtents(vcb, forkType, fileid, nextBlock, &recordDeleted); - -Done: -ErrorExit: - if (recordDeleted) - (void) FlushExtentFile(vcb); - - return err; -} - - -/* - * HFS Plus only - * - */ -OSErr HeadTruncateFile ( - ExtendedVCB *vcb, - FCB *fcb, - u_int32_t headblks) -{ - HFSPlusExtentRecord extents; - HFSPlusExtentRecord tailExtents; - HFSCatalogNodeID fileID; - u_int8_t forkType; - u_int32_t blkcnt; - u_int32_t startblk; - u_int32_t 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, 0); - /* - * 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("hfs: HeadTruncateFile: problems deallocating %s (%d)\n", - FTOC(fcb)->c_desc.cd_nameptr ? (const char *)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 (;;) { - u_int32_t 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("hfs: HeadTruncateFile: problems finding extents %s (%d)\n", - FTOC(fcb)->c_desc.cd_nameptr ? (const char *)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, 0); - if (error) { - printf("hfs: HeadTruncateFile: problems deallocating %s (%d)\n", - FTOC(fcb)->c_desc.cd_nameptr ? (const char *)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("hfs: HeadTruncateFile: problems deallocating %s (%d)\n", - FTOC(fcb)->c_desc.cd_nameptr ? (const char *)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 -= headblks; - fcb->ff_blocks = blkcnt; - - FTOC(fcb)->c_flag |= C_MODIFIED; - FTOC(fcb)->c_touch_chgtime = TRUE; - - (void) FlushExtentFile(vcb); - } - -ErrorExit: - return MacToVFSError(error); -} - - - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: SearchExtentRecord (was XRSearch) -// -// Function: Searches extent record for the extent mapping a given file -// allocation block number (FABN). -// -// Input: searchFABN - desired FABN -// extentData - pointer to extent data record (xdr) -// extentDataStartFABN - beginning FABN for extent record -// -// Output: foundExtentDataOffset - offset to extent entry within xdr -// result = noErr, offset to extent mapping desired FABN -// result = FXRangeErr, offset to last extent in record -// endingFABNPlusOne - ending FABN +1 -// noMoreExtents - True if the extent was not found, and the -// extent record was not full (so don't bother -// looking in subsequent records); false otherwise. -// -// Result: noErr = ok -// FXRangeErr = desired FABN > last mapped FABN in record -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -static OSErr SearchExtentRecord( - ExtendedVCB *vcb, - u_int32_t searchFABN, - const HFSPlusExtentRecord extentData, - u_int32_t extentDataStartFABN, - u_int32_t *foundExtentIndex, - u_int32_t *endingFABNPlusOne, - Boolean *noMoreExtents) -{ - OSErr err = noErr; - u_int32_t extentIndex; - /* Set it to the HFS std value */ - u_int32_t numberOfExtents = kHFSExtentDensity; - u_int32_t numAllocationBlocks; - Boolean foundExtent; - - *endingFABNPlusOne = extentDataStartFABN; - *noMoreExtents = false; - foundExtent = false; - - /* Override numberOfExtents for HFS+/HFSX */ - if (vcb->vcbSigWord != kHFSSigWord) { - numberOfExtents = kHFSPlusExtentDensity; - } - - for( extentIndex = 0; extentIndex < numberOfExtents; ++extentIndex ) - { - - // Loop over the extent record and find the search FABN. - - numAllocationBlocks = extentData[extentIndex].blockCount; - if ( numAllocationBlocks == 0 ) - { - break; - } - - *endingFABNPlusOne += numAllocationBlocks; - - if( searchFABN < *endingFABNPlusOne ) - { - // Found the extent. - foundExtent = true; - break; - } - } - - if( foundExtent ) - { - // Found the extent. Note the extent offset - *foundExtentIndex = extentIndex; - } - else - { - // Did not find the extent. Set foundExtentDataOffset accordingly - if( extentIndex > 0 ) - { - *foundExtentIndex = extentIndex - 1; - } - else - { - *foundExtentIndex = 0; - } - - // If we found an empty extent, then set noMoreExtents. - if (extentIndex < numberOfExtents) - *noMoreExtents = true; - - // Finally, return an error to the caller - err = fxRangeErr; - } - - return( err ); -} - -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ -// Routine: SearchExtentFile (was XFSearch) -// -// Function: Searches extent file (including the FCB resident extent record) -// for the extent mapping a given file position. -// -// Input: vcb - VCB pointer -// fcb - FCB pointer -// filePosition - file position (byte address) -// -// Output: foundExtentKey - extent key record (xkr) -// If extent was found in the FCB's resident extent record, -// then foundExtentKey->keyLength will be set to 0. -// foundExtentData - extent data record(xdr) -// foundExtentIndex - index to extent entry in xdr -// result = 0, offset to extent mapping desired FABN -// result = FXRangeErr, offset to last extent in record -// (i.e., kNumExtentsPerRecord-1) -// extentBTreeHint - BTree hint for extent record -// kNoHint = Resident extent record -// endingFABNPlusOne - ending FABN +1 -// -// Result: -// noErr Found an extent that contains the given file position -// FXRangeErr Given position is beyond the last allocated extent -// (other) (some other internal I/O error) -//‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹‹ - -OSErr SearchExtentFile( - ExtendedVCB *vcb, - const FCB *fcb, - int64_t filePosition, - HFSPlusExtentKey *foundExtentKey, - HFSPlusExtentRecord foundExtentData, - u_int32_t *foundExtentIndex, - u_int32_t *extentBTreeHint, - u_int32_t *endingFABNPlusOne ) -{ - OSErr err; - u_int32_t filePositionBlock; - int64_t temp64; - Boolean noMoreExtents; - int lockflags; - - temp64 = filePosition / (int64_t)vcb->blockSize; - filePositionBlock = (u_int32_t)temp64; - - bcopy ( fcb->fcbExtents, foundExtentData, sizeof(HFSPlusExtentRecord)); - - // Search the resident FCB first. - err = SearchExtentRecord( vcb, filePositionBlock, foundExtentData, 0, - foundExtentIndex, endingFABNPlusOne, &noMoreExtents ); - - if( err == noErr ) { - // Found the extent. Set results accordingly - *extentBTreeHint = kNoHint; // no hint, because not in the BTree - foundExtentKey->keyLength = 0; // 0 = the FCB itself - - goto Exit; - } - - // Didn't find extent in FCB. If FCB's extent record wasn't full, there's no point - // in searching the extents file. Note that SearchExtentRecord left us pointing at - // the last valid extent (or the first one, if none were valid). This means we need - // to fill in the hint and key outputs, just like the "if" statement above. - if ( noMoreExtents ) { - *extentBTreeHint = kNoHint; // no hint, because not in the BTree - foundExtentKey->keyLength = 0; // 0 = the FCB itself - err = fxRangeErr; // There are no more extents, so must be beyond PEOF - goto Exit; - } - - // - // 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) { - // - // If we get here, the desired position is beyond the extents in the FCB, and there are no extents - // in the extents file. Return the FCB's extents and a range error. - // - *extentBTreeHint = kNoHint; - foundExtentKey->keyLength = 0; - err = GetFCBExtentRecord(fcb, foundExtentData); - // Note: foundExtentIndex and endingFABNPlusOne have already been set as a result of the very - // first SearchExtentRecord call in this function (when searching in the FCB's extents, and - // we got a range error). - - return fxRangeErr; - } - - // - // If we get here, there was either a BTree error, or we found an appropriate record. - // If we found a record, then search it for the correct index into the extents. - // - if (err == noErr) { - // Find appropriate index into extent record - err = SearchExtentRecord(vcb, filePositionBlock, foundExtentData, foundExtentKey->startBlock, - foundExtentIndex, endingFABNPlusOne, &noMoreExtents); - } - -Exit: - return err; -} - - - -//============================================================================ -// Routine: UpdateExtentRecord -// -// Function: Write new extent data to an existing extent record with a given key. -// If all of the extents are empty, and the extent record is in the -// extents file, then the record is deleted. -// -// Input: vcb - the volume containing the extents -// fcb - the file that owns the extents -// deleted - whether or not the file is already deleted -// extentFileKey - pointer to extent key record (xkr) -// If the key length is 0, then the extents are actually part -// of the catalog record, stored in the FCB. -// extentData - pointer to extent data record (xdr) -// extentBTreeHint - hint for given key, or kNoHint -// -// Result: noErr = ok -// (other) = error from BTree -//============================================================================ - -static OSErr UpdateExtentRecord (ExtendedVCB *vcb, FCB *fcb, int deleted, - const HFSPlusExtentKey *extentFileKey, - const HFSPlusExtentRecord extentData, - u_int32_t extentBTreeHint) -{ - OSErr err = noErr; - - if (extentFileKey->keyLength == 0) { // keyLength == 0 means the FCB's extent record - BlockMoveData(extentData, fcb->fcbExtents, sizeof(HFSPlusExtentRecord)); - if (!deleted) { - FTOC(fcb)->c_flag |= C_MODIFIED; - } - } - else { - struct BTreeIterator *btIterator = NULL; - FSBufferDescriptor btRecord; - u_int16_t btRecordSize; - FCB * btFCB; - int lockflags; - - // - // Need to find and change a record in Extents BTree - // - btFCB = GetFileControlBlock(vcb->extentsRefNum); - - MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); - if (btIterator == NULL) { - return memFullErr; // translates to ENOMEM - } - 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); - - /* HFS+/HFSX */ - if (vcb->vcbSigWord != kHFSSigWord) { // HFS Plus volume - HFSPlusExtentRecord foundData; // The extent data actually found - - BlockMoveData(extentFileKey, &btIterator->key, sizeof(HFSPlusExtentKey)); - - btIterator->hint.index = 0; - btIterator->hint.nodeNum = extentBTreeHint; - - btRecord.bufferAddress = &foundData; - btRecord.itemSize = sizeof(HFSPlusExtentRecord); - btRecord.itemCount = 1; - - err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator); - - if (err == noErr) { - BlockMoveData(extentData, &foundData, sizeof(HFSPlusExtentRecord)); - err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize); - } - (void) BTFlushPath(btFCB); - } -#if CONFIG_HFS_STD - else { - /* HFS Standard */ - HFSExtentKey * key; // Actual extent key used on disk in HFS - HFSExtentRecord foundData; // The extent data actually found - - key = (HFSExtentKey*) &btIterator->key; - key->keyLength = kHFSExtentKeyMaximumLength; - key->forkType = extentFileKey->forkType; - key->fileID = extentFileKey->fileID; - key->startBlock = extentFileKey->startBlock; - - btIterator->hint.index = 0; - btIterator->hint.nodeNum = extentBTreeHint; - - btRecord.bufferAddress = &foundData; - btRecord.itemSize = sizeof(HFSExtentRecord); - btRecord.itemCount = 1; - - err = BTSearchRecord(btFCB, btIterator, &btRecord, &btRecordSize, btIterator); - - if (err == noErr) - err = HFSPlusToHFSExtents(extentData, (HFSExtentDescriptor *)&foundData); - - if (err == noErr) - err = BTReplaceRecord(btFCB, btIterator, &btRecord, btRecordSize); - (void) BTFlushPath(btFCB); - - } -#endif - - hfs_systemfile_unlock(vcb, lockflags); - - FREE(btIterator, M_TEMP); - } - - return err; -} - - - -#if CONFIG_HFS_STD -static OSErr HFSPlusToHFSExtents( - const HFSPlusExtentRecord oldExtents, - HFSExtentRecord newExtents) -{ - OSErr err; - - err = noErr; - - // 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; - - #if DEBUG_BUILD - if (oldExtents[3].startBlock || oldExtents[3].blockCount) { - DebugStr("ExtentRecord with > 3 extents is invalid for HFS"); - err = fsDSIntErr; - } - #endif - - return err; -} -#endif - - - -static OSErr GetFCBExtentRecord( - const FCB *fcb, - HFSPlusExtentRecord extents) -{ - - BlockMoveData(fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord)); - - return noErr; -} - - -//_________________________________________________________________________________ -// -// Routine: ExtentsAreIntegral -// -// Purpose: Ensure that each extent can hold an integral number of nodes -// Called by the NodesAreContiguous function -//_________________________________________________________________________________ - -static Boolean ExtentsAreIntegral( - const HFSPlusExtentRecord extentRecord, - u_int32_t mask, - u_int32_t *blocksChecked, - Boolean *checkedLastExtent) -{ - u_int32_t blocks; - u_int32_t extentIndex; - - *blocksChecked = 0; - *checkedLastExtent = false; - - for(extentIndex = 0; extentIndex < kHFSPlusExtentDensity; extentIndex++) - { - blocks = extentRecord[extentIndex].blockCount; - - if ( blocks == 0 ) - { - *checkedLastExtent = true; - break; - } - - *blocksChecked += blocks; - - if (blocks & mask) - return false; - } - - return true; -} - - -//_________________________________________________________________________________ -// -// Routine: NodesAreContiguous -// -// Purpose: Ensure that all b-tree nodes are contiguous on disk -// Called by BTOpenPath during volume mount -//_________________________________________________________________________________ - -Boolean NodesAreContiguous( - ExtendedVCB *vcb, - FCB *fcb, - u_int32_t nodeSize) -{ - u_int32_t mask; - u_int32_t startBlock; - u_int32_t blocksChecked; - u_int32_t 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 || - (int64_t)((int64_t)blocksChecked * (int64_t)vcb->blockSize) >= (int64_t)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; -} -