X-Git-Url: https://git.saurik.com/apple/hfs.git/blobdiff_plain/de8ee0119e51802e7b74e261b628b6de53b34e6c..ccb1535577c019312b69b95a60bb75c8a3ee22a3:/core/FileIDsServices.c diff --git a/core/FileIDsServices.c b/core/FileIDsServices.c new file mode 100644 index 0000000..aba8940 --- /dev/null +++ b/core/FileIDsServices.c @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2000-2015 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_macos_defs.h" +#include "hfs_format.h" + +#include "FileMgrInternal.h" +#include "HFSUnicodeWrappers.h" +#include "CatalogPrivate.h" +#include +#include +#include + +#include "hfs_dbg.h" + +struct ExtentsRecBuffer { + ExtentKey extentKey; + ExtentRecord extentData; +}; +typedef struct ExtentsRecBuffer ExtentsRecBuffer; + + +static u_int32_t CheckExtents( void *extents, u_int32_t blocks, Boolean isHFSPlus ); +static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileNumber, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ); +static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ); + +#if CONFIG_HFS_STD +static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ); +#endif + +static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ); +static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount ); + +/* + * This function moves the overflow extents associated with srcID into the file associated with dstID. + * We should have already verified that 'srcID' has overflow extents. So now we move all of the overflow + * extent records. + */ +OSErr MoveData( ExtendedVCB *vcb, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, int rsrc) { + + OSErr err; + + /* + * Only the source file should have extents, so we just track those. + * We operate on the fork represented by the open FD that was used to call into this + * function + */ + if (rsrc) { + /* Copy the extent overflow blocks. */ + err = MoveExtents( vcb, srcID, destID, 1, (u_int8_t)0xff, 1); + if ( err != noErr ) { + if ( err != dskFulErr ) { + return( err ); + } + /* + * In case of error, we would have probably run into problems + * growing the extents b-tree. Since the move is actually a copy + delete + * just delete the new entries. Same for below. + */ + err = DeleteExtents( vcb, destID, 1, (u_int8_t)0xff, 1); + ReturnIfError( err ); // we are doomed. Just QUIT! + goto FlushAndReturn; + } + } + else { + /* Copy the extent overflow blocks. */ + err = MoveExtents( vcb, srcID, destID, 1, 0, 1); + if ( err != noErr ) { + if ( err != dskFulErr ) { + return( err ); + } + err = DeleteExtents( vcb, destID, 1, 0, 1); + ReturnIfError( err ); // we are doomed. Just QUIT! + goto FlushAndReturn; + } + } + +FlushAndReturn: + /* Write out the catalog and extent overflow B-Tree changes */ + err = FlushCatalog( vcb ); + err = FlushExtentFile( vcb ); + + return( err ); +} + + +OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, u_int32_t srcHint, u_int32_t destHint ) +{ + CatalogKey srcKey; // 518 bytes + CatalogKey destKey; // 518 bytes + CatalogRecord srcData; // 520 bytes + CatalogRecord destData; // 520 bytes + CatalogRecord swapData; // 520 bytes + int16_t numSrcExtentBlocks; + int16_t numDestExtentBlocks; + OSErr err; + Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord ); + + err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey); + ReturnIfError(err); + + err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey); + ReturnIfError(err); + + if ( isHFSPlus ) + { + //-- Step 1: Check the catalog nodes for extents + + //-- locate the source file, test for extents in extent file, and copy the cat record for later + err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); + ReturnIfError( err ); + + if ( srcData.recordType != kHFSPlusFileRecord ) + return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" + + //-- Check if there are any extents in the source file + //€€ I am only checling the extents in the low 32 bits, routine will fail if files extents after 2 gig are in overflow + numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus ); + if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents + numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus ); + + //-- Check if there are any extents in the destination file + err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); + ReturnIfError( err ); + + if ( destData.recordType != kHFSPlusFileRecord ) + return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" + + numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus ); + if ( numDestExtentBlocks == 0 ) // then check the resource fork extents + numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus ); + + //-- Step 2: Exchange the Extent key in the extent file + + //-- Exchange the extents key in the extent file + err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); + + if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents + { + //-- Change the source extents file ids to our known bogus value + err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, 0,0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) { + return( err ); + } + else { + err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = FlushCatalog( vcb ); // flush the catalog + err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) + return( dskFulErr ); + } + } + + //-- Change the destination extents file id's to the source id's + err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + +ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = FlushCatalog( vcb ); // flush the catalog + err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) + return( dskFulErr ); + + } + + //-- Change the bogus extents file id's to the dest id's + err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto ExUndo2aPlus; + } + + } + else if ( numSrcExtentBlocks ) // just the source file has extents + { + err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto FlushAndReturn; + } + } + else if ( numDestExtentBlocks ) // just the destination file has extents + { + err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto FlushAndReturn; + } + } + + //-- Step 3: Change the data in the catalog nodes + + //-- find the source cnode and put dest info in it + err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); + if ( err != noErr ) + return( cmBadNews ); + + BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) ); + CopyBigCatalogNodeInfo( &destData, &srcData ); + + err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint ); + ReturnIfError( err ); + + // find the destination cnode and put source info in it + err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); + if ( err != noErr ) + return( cmBadNews ); + + CopyBigCatalogNodeInfo( &swapData, &destData ); + err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint ); + ReturnIfError( err ); + } +#if CONFIG_HFS_STD + else // HFS // + { + //-- Step 1: Check the catalog nodes for extents + + //-- locate the source file, test for extents in extent file, and copy the cat record for later + err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); + ReturnIfError( err ); + + if ( srcData.recordType != kHFSFileRecord ) + return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" + + //-- Check if there are any extents in the source file + numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus ); + if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents + numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus ); + + + //€€ Do we save the found source node for later use? + + + //-- Check if there are any extents in the destination file + err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); + ReturnIfError( err ); + + if ( destData.recordType != kHFSFileRecord ) + return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" + + numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus ); + if ( numDestExtentBlocks == 0 ) // then check the resource fork extents + numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus ); + + //€€ Do we save the found destination node for later use? + + + //-- Step 2: Exchange the Extent key in the extent file + + //-- Exchange the extents key in the extent file + err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); + + if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents + { + //-- Change the source extents file ids to our known bogus value + err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + +ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = FlushCatalog( vcb ); // flush the catalog + err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) + return( dskFulErr ); + } + + //-- Change the destination extents file id's to the source id's + err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + +ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto ExUndo1a; + } + + //-- Change the bogus extents file id's to the dest id's + err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto ExUndo2a; + } + + } + else if ( numSrcExtentBlocks ) // just the source file has extents + { + err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto FlushAndReturn; + } + } + else if ( numDestExtentBlocks ) // just the destination file has extents + { + err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); + if ( err != noErr ) + { + if ( err != dskFulErr ) + return( err ); + + err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus ); + ReturnIfError( err ); // we are doomed. Just QUIT! + + goto FlushAndReturn; + } + } + + //-- Step 3: Change the data in the catalog nodes + + //-- find the source cnode and put dest info in it + err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); + if ( err != noErr ) + return( cmBadNews ); + + BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) ); + //€€ Asm source copies from the saved dest catalog node + CopyCatalogNodeInfo( &destData, &srcData ); + + err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint ); + ReturnIfError( err ); + + + // find the destination cnode and put source info in it + err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); + if ( err != noErr ) + return( cmBadNews ); + + CopyCatalogNodeInfo( &swapData, &destData ); + err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint ); + ReturnIfError( err ); + } +#endif + + err = noErr; + + //-- Step 4: Error Handling section + + +FlushAndReturn: + err = FlushCatalog( vcb ); // flush the catalog + err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) + return( err ); +} + + +#if CONFIG_HFS_STD +static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ) +{ + dest->hfsFile.dataLogicalSize = src->hfsFile.dataLogicalSize; + dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize; + dest->hfsFile.rsrcLogicalSize = src->hfsFile.rsrcLogicalSize; + dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize; + dest->hfsFile.modifyDate = src->hfsFile.modifyDate; + BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) ); + BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) ); +} +#endif + +static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ) +{ + BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) ); + BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) ); + dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate; +} + + +static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ) +{ + FCB * fcb; + ExtentsRecBuffer extentsBuffer[kNumExtentsToCache]; + ExtentKey * extentKeyPtr; + ExtentRecord extentData; + struct BTreeIterator *btIterator = NULL; + struct BTreeIterator *tmpIterator = NULL; + FSBufferDescriptor btRecord; + u_int16_t btKeySize; + u_int16_t btRecordSize; + int16_t i, j; + OSErr err; + + btIterator = hfs_mallocz(sizeof(struct BTreeIterator)); + tmpIterator = hfs_mallocz(sizeof(struct BTreeIterator)); + + fcb = GetFileControlBlock(vcb->extentsRefNum); + + (void) BTInvalidateHint(btIterator); + extentKeyPtr = (ExtentKey*) &btIterator->key; + btRecord.bufferAddress = &extentData; + btRecord.itemCount = 1; + + //-- Collect the extent records + + // + // A search on the following key will cause the BTree to be positioned immediately + // before the first extent record for file #srcFileID, but not actually positioned + // on any record. This is because there cannot be an extent record with FABN = 0 + // (the first extent of the fork, which would be in the catalog entry, not an extent + // record). + // + // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record. + // + if (isHFSPlus) { + btRecord.itemSize = sizeof(HFSPlusExtentRecord); + btKeySize = sizeof(HFSPlusExtentKey); + + extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; + extentKeyPtr->hfsPlus.forkType = forkType; + extentKeyPtr->hfsPlus.pad = 0; + extentKeyPtr->hfsPlus.fileID = srcFileID; + extentKeyPtr->hfsPlus.startBlock = 0; + } +#if CONFIG_HFS_STD + else { + btRecord.itemSize = sizeof(HFSExtentRecord); + btKeySize = sizeof(HFSExtentKey); + + extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; + extentKeyPtr->hfs.forkType = 0; + extentKeyPtr->hfs.fileID = srcFileID; + extentKeyPtr->hfs.startBlock = 0; + } +#else + else { + hfs_free(tmpIterator, sizeof(*tmpIterator)); + hfs_free(btIterator, sizeof(*btIterator)); + return cmBadNews; + } +#endif + + // + // We do an initial BTSearchRecord to position the BTree's iterator just before any extent + // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found + // records, but with destFileID as the file number in the key. Keep doing this sequence of + // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are + // no more extent records in the tree. + // + // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID + // set to destFileID. + // + // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it + // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series + // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record + // we found, so that BTIterateRecord would get the next one (the first we haven't processed). + // + + err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); + + // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0. + if (err != btNotFound) + { + hfs_debug("hfs: unexpected error from SearchBTreeRecord\n"); + + if (err == noErr) // If we found such a bogus extent record, then the tree is really messed up + err = cmBadNews; // so return an error that conveys the disk is hosed. + + hfs_free(tmpIterator, sizeof(*tmpIterator)); + hfs_free(btIterator, sizeof(*btIterator)); + return err; + } + + do + { + btRecord.bufferAddress = &extentData; + btRecord.itemCount = 1; + + for ( i=0 ; ihfsPlus.fileID; + } +#if CONFIG_HFS_STD + else { + foundFileID = extentKeyPtr->hfs.fileID; + } +#endif + if ( foundFileID == srcFileID ) { + /* Check if we need to quit early. */ + if (quitEarly && isHFSPlus) { + if (extentKeyPtr->hfsPlus.forkType != forkType) { + break; + } + } + CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i); + } + else{ + /* The fileID's are of a different file. We're done here. */ + break; + } + } + + + + //-- edit each extent key, and reinsert each extent record in the extent file + if (isHFSPlus) + btRecordSize = sizeof(HFSPlusExtentRecord); +#if CONFIG_HFS_STD + else + btRecordSize = sizeof(HFSExtentRecord); +#endif + + for ( j=0 ; jkey, btKeySize); + btRecord.bufferAddress = &(extentsBuffer[j].extentData); + + err = BTInsertRecord(fcb, tmpIterator, &btRecord, btRecordSize); + if ( err != noErr ) { + /* Parse the error and free iterators */ + hfs_free(btIterator, sizeof(*btIterator)); + hfs_free(tmpIterator, sizeof(*tmpIterator)); + if ( err == btExists ) + { + hfs_debug("hfs: can't insert record -- already exists\n"); + return( cmBadNews ); + } + else { + return( err ); + } + } + } + + //-- okay, done with this buffered batch, go get the next set of extent records + // If our buffer is not full, we must be done, or recieved an error + + if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done + { + err = DeleteExtents( vcb, srcFileID, quitEarly, forkType, isHFSPlus ); // Now delete all the extent entries with the sourceID + if (err != noErr ) + hfs_debug("hfs: error from DeleteExtents (%d)\n", err); + break; // we're done! + } + } while ( true ); + + hfs_free(tmpIterator, sizeof(*tmpIterator)); + hfs_free(btIterator, sizeof(*btIterator)); + + return( err ); +} + + +static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount ) +{ + BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) ); + BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) ); +} + + +//-- Delete all extents in extent file that have the ID given. +static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ) +{ + FCB * fcb; + ExtentKey * extentKeyPtr; + ExtentRecord extentData; + struct BTreeIterator *btIterator = NULL; + struct BTreeIterator *tmpIterator = NULL; + FSBufferDescriptor btRecord; + u_int16_t btRecordSize; + OSErr err; + + btIterator = hfs_mallocz(sizeof(struct BTreeIterator)); + tmpIterator = hfs_mallocz(sizeof(struct BTreeIterator)); + + fcb = GetFileControlBlock(vcb->extentsRefNum); + + (void) BTInvalidateHint(btIterator); + extentKeyPtr = (ExtentKey*) &btIterator->key; + btRecord.bufferAddress = &extentData; + btRecord.itemCount = 1; + + // The algorithm is to position the BTree just before any extent records for fileID. + // Then just keep getting successive records. If the record is still for fileID, + // then delete it. + + if (isHFSPlus) { + btRecord.itemSize = sizeof(HFSPlusExtentRecord); + + extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; + extentKeyPtr->hfsPlus.forkType = forkType; + extentKeyPtr->hfsPlus.pad = 0; + extentKeyPtr->hfsPlus.fileID = fileID; + extentKeyPtr->hfsPlus.startBlock = 0; + } +#if CONFIG_HFS_STD + else { + btRecord.itemSize = sizeof(HFSExtentRecord); + + extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; + extentKeyPtr->hfs.forkType = forkType; + extentKeyPtr->hfs.fileID = fileID; + extentKeyPtr->hfs.startBlock = 0; + } +#else + else { + err = cmBadNews; + goto exit; + } +#endif + + err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); + if ( err != btNotFound ) + { + if (err == noErr) { // Did we find a bogus extent record? + err = cmBadNews; // Yes, so indicate things are messed up. + } + + goto exit; + } + + do + { + HFSCatalogNodeID foundFileID = 0; + + err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize); + if ( err != noErr ) + { + if (err == btNotFound) // If we hit the end of the BTree + err = noErr; // then it's OK + + break; // We're done now. + } + if (isHFSPlus) { + foundFileID = extentKeyPtr->hfsPlus.fileID; + } +#if CONFIG_HFS_STD + else { + foundFileID = extentKeyPtr->hfs.fileID; + } +#endif + + if ( foundFileID != fileID ) { + break; // numbers don't match, we must be done + } + if (quitEarly && isHFSPlus) { + /* If we're only deleting one type of fork, then quit early if it doesn't match */ + if (extentKeyPtr->hfsPlus.forkType != forkType) { + break; + } + } + + *tmpIterator = *btIterator; + err = BTDeleteRecord( fcb, tmpIterator ); + if (err != noErr) + break; + } while ( true ); + +exit: + + hfs_free(tmpIterator, sizeof(*tmpIterator)); + hfs_free(btIterator, sizeof(*btIterator)); + + return( err ); +} + + +// Check if there are extents represented in the extents overflow file. +static u_int32_t CheckExtents( void *extents, u_int32_t totalBlocks, Boolean isHFSPlus ) +{ + u_int32_t extentAllocationBlocks; + u_int16_t i; + + + if ( totalBlocks == 0 ) + return( 0 ); + + extentAllocationBlocks = 0; + + if ( isHFSPlus ) + { + for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ ) + { + extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount; + if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump) + return( 0 ); + } + } +#if CONFIG_HFS_STD + else + { + for ( i = 0 ; i < kHFSExtentDensity ; i++ ) + { + extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount; + if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump) + return( 0 ); + } + } +#endif + + return( extentAllocationBlocks ); +}