1/* 2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include "../../hfs_macos_defs.h" 30#include "../../hfs_format.h" 31 32#include "../headers/FileMgrInternal.h" 33#include "../headers/HFSUnicodeWrappers.h" 34#include "../headers/CatalogPrivate.h" 35#include <sys/kernel.h> 36#include <sys/malloc.h> 37#include <libkern/libkern.h> 38 39 40struct ExtentsRecBuffer { 41 ExtentKey extentKey; 42 ExtentRecord extentData; 43}; 44typedef struct ExtentsRecBuffer ExtentsRecBuffer; 45 46 47static u_int32_t CheckExtents( void *extents, u_int32_t blocks, Boolean isHFSPlus ); 48static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileNumber, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ); 49static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ); 50static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ); 51static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ); 52static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount ); 53 54/* 55 * This function moves the overflow extents associated with srcID into the file associated with dstID. 56 * We should have already verified that 'srcID' has overflow extents. So now we move all of the overflow 57 * extent records. 58 */ 59OSErr MoveData( ExtendedVCB *vcb, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, int rsrc) { 60 61 OSErr err; 62 63 /* 64 * Only the source file should have extents, so we just track those. 65 * We operate on the fork represented by the open FD that was used to call into this 66 * function 67 */ 68 if (rsrc) { 69 /* Copy the extent overflow blocks. */ 70 err = MoveExtents( vcb, srcID, destID, 1, (u_int8_t)0xff, 1); 71 if ( err != noErr ) { 72 if ( err != dskFulErr ) { 73 return( err ); 74 } 75 /* 76 * In case of error, we would have probably run into problems 77 * growing the extents b-tree. Since the move is actually a copy + delete 78 * just delete the new entries. Same for below. 79 */ 80 err = DeleteExtents( vcb, destID, 1, (u_int8_t)0xff, 1); 81 ReturnIfError( err ); // we are doomed. Just QUIT! 82 goto FlushAndReturn; 83 } 84 } 85 else { 86 /* Copy the extent overflow blocks. */ 87 err = MoveExtents( vcb, srcID, destID, 1, 0, 1); 88 if ( err != noErr ) { 89 if ( err != dskFulErr ) { 90 return( err ); 91 } 92 err = DeleteExtents( vcb, destID, 1, 0, 1); 93 ReturnIfError( err ); // we are doomed. Just QUIT! 94 goto FlushAndReturn; 95 } 96 } 97 98FlushAndReturn: 99 /* Write out the catalog and extent overflow B-Tree changes */ 100 err = FlushCatalog( vcb ); 101 err = FlushExtentFile( vcb ); 102 103 return( err ); 104} 105 106 107OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, u_int32_t srcHint, u_int32_t destHint ) 108{ 109 CatalogKey srcKey; // 518 bytes 110 CatalogKey destKey; // 518 bytes 111 CatalogRecord srcData; // 520 bytes 112 CatalogRecord destData; // 520 bytes 113 CatalogRecord swapData; // 520 bytes 114 int16_t numSrcExtentBlocks; 115 int16_t numDestExtentBlocks; 116 OSErr err; 117 Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord ); 118 119 err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, NULL); 120 ReturnIfError(err); 121 122 err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, NULL); 123 ReturnIfError(err); 124 125 if ( isHFSPlus ) 126 { 127 //-- Step 1: Check the catalog nodes for extents 128 129 //-- locate the source file, test for extents in extent file, and copy the cat record for later 130 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); 131 ReturnIfError( err ); 132 133 if ( srcData.recordType != kHFSPlusFileRecord ) 134 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" 135 136 //-- Check if there are any extents in the source file 137 //�� I am only checling the extents in the low 32 bits, routine will fail if files extents after 2 gig are in overflow 138 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus ); 139 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents 140 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus ); 141 142 //-- Check if there are any extents in the destination file 143 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); 144 ReturnIfError( err ); 145 146 if ( destData.recordType != kHFSPlusFileRecord ) 147 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" 148 149 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus ); 150 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents 151 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus ); 152 153 //-- Step 2: Exchange the Extent key in the extent file 154 155 //-- Exchange the extents key in the extent file 156 err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); 157 ReturnIfError( err ); 158 159 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents 160 { 161 //-- Change the source extents file ids to our known bogus value 162 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, 0,0, isHFSPlus ); 163 if ( err != noErr ) 164 { 165 if ( err != dskFulErr ) 166 return( err ); 167 else 168 goto ExUndo1a; 169 } 170 171 //-- Change the destination extents file id's to the source id's 172 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 173 if ( err != noErr ) 174 { 175 if ( err != dskFulErr ) 176 return( err ); 177 178ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 179 ReturnIfError( err ); // we are doomed. Just QUIT! 180 181 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back 182 ReturnIfError( err ); // we are doomed. Just QUIT! 183 184 goto ExUndo1a; 185 } 186 187 //-- Change the bogus extents file id's to the dest id's 188 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 189 if ( err != noErr ) 190 { 191 if ( err != dskFulErr ) 192 return( err ); 193 194 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 195 ReturnIfError( err ); // we are doomed. Just QUIT! 196 197 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back 198 ReturnIfError( err ); // we are doomed. Just QUIT! 199 200 goto ExUndo2aPlus; 201 } 202 203 } 204 else if ( numSrcExtentBlocks ) // just the source file has extents 205 { 206 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 207 if ( err != noErr ) 208 { 209 if ( err != dskFulErr ) 210 return( err ); 211 212 err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 213 ReturnIfError( err ); // we are doomed. Just QUIT! 214 215 goto FlushAndReturn; 216 } 217 } 218 else if ( numDestExtentBlocks ) // just the destination file has extents 219 { 220 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 221 if ( err != noErr ) 222 { 223 if ( err != dskFulErr ) 224 return( err ); 225 226 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); 227 ReturnIfError( err ); // we are doomed. Just QUIT! 228 229 goto FlushAndReturn; 230 } 231 } 232 233 //-- Step 3: Change the data in the catalog nodes 234 235 //-- find the source cnode and put dest info in it 236 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); 237 if ( err != noErr ) 238 return( cmBadNews ); 239 240 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) ); 241 CopyBigCatalogNodeInfo( &destData, &srcData ); 242 243 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint ); 244 ReturnIfError( err ); 245 246 // find the destination cnode and put source info in it 247 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); 248 if ( err != noErr ) 249 return( cmBadNews ); 250 251 CopyBigCatalogNodeInfo( &swapData, &destData ); 252 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint ); 253 ReturnIfError( err ); 254 } 255 else // HFS // 256 { 257 //-- Step 1: Check the catalog nodes for extents 258 259 //-- locate the source file, test for extents in extent file, and copy the cat record for later 260 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); 261 ReturnIfError( err ); 262 263 if ( srcData.recordType != kHFSFileRecord ) 264 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" 265 266 //-- Check if there are any extents in the source file 267 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus ); 268 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents 269 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus ); 270 271 272 //�� Do we save the found source node for later use? 273 274 275 //-- Check if there are any extents in the destination file 276 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); 277 ReturnIfError( err ); 278 279 if ( destData.recordType != kHFSFileRecord ) 280 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory" 281 282 numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus ); 283 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents 284 numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus ); 285 286 //�� Do we save the found destination node for later use? 287 288 289 //-- Step 2: Exchange the Extent key in the extent file 290 291 //-- Exchange the extents key in the extent file 292 err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); 293 ReturnIfError( err ); 294 295 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents 296 { 297 //-- Change the source extents file ids to our known bogus value 298 err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); 299 if ( err != noErr ) 300 { 301 if ( err != dskFulErr ) 302 return( err ); 303 304ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus ); 305 ReturnIfError( err ); // we are doomed. Just QUIT! 306 307 err = FlushCatalog( vcb ); // flush the catalog 308 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) 309 return( dskFulErr ); 310 } 311 312 //-- Change the destination extents file id's to the source id's 313 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); 314 if ( err != noErr ) 315 { 316 if ( err != dskFulErr ) 317 return( err ); 318 319ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); 320 ReturnIfError( err ); // we are doomed. Just QUIT! 321 322 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back 323 ReturnIfError( err ); // we are doomed. Just QUIT! 324 325 goto ExUndo1a; 326 } 327 328 //-- Change the bogus extents file id's to the dest id's 329 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); 330 if ( err != noErr ) 331 { 332 if ( err != dskFulErr ) 333 return( err ); 334 335 err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus ); 336 ReturnIfError( err ); // we are doomed. Just QUIT! 337 338 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back 339 ReturnIfError( err ); // we are doomed. Just QUIT! 340 341 goto ExUndo2a; 342 } 343 344 } 345 else if ( numSrcExtentBlocks ) // just the source file has extents 346 { 347 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); 348 if ( err != noErr ) 349 { 350 if ( err != dskFulErr ) 351 return( err ); 352 353 err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); 354 ReturnIfError( err ); // we are doomed. Just QUIT! 355 356 goto FlushAndReturn; 357 } 358 } 359 else if ( numDestExtentBlocks ) // just the destination file has extents 360 { 361 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); 362 if ( err != noErr ) 363 { 364 if ( err != dskFulErr ) 365 return( err ); 366 367 err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus ); 368 ReturnIfError( err ); // we are doomed. Just QUIT! 369 370 goto FlushAndReturn; 371 } 372 } 373 374 //-- Step 3: Change the data in the catalog nodes 375 376 //-- find the source cnode and put dest info in it 377 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint ); 378 if ( err != noErr ) 379 return( cmBadNews ); 380 381 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) ); 382 //�� Asm source copies from the saved dest catalog node 383 CopyCatalogNodeInfo( &destData, &srcData ); 384 385 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint ); 386 ReturnIfError( err ); 387 388 389 // find the destination cnode and put source info in it 390 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint ); 391 if ( err != noErr ) 392 return( cmBadNews ); 393 394 CopyCatalogNodeInfo( &swapData, &destData ); 395 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint ); 396 ReturnIfError( err ); 397 } 398 399 err = noErr; 400 401 //-- Step 4: Error Handling section 402 403 404FlushAndReturn: 405 err = FlushCatalog( vcb ); // flush the catalog 406 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap) 407 return( err ); 408} 409 410 411static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ) 412{ 413 dest->hfsFile.dataLogicalSize = src->hfsFile.dataLogicalSize; 414 dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize; 415 dest->hfsFile.rsrcLogicalSize = src->hfsFile.rsrcLogicalSize; 416 dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize; 417 dest->hfsFile.modifyDate = src->hfsFile.modifyDate; 418 BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) ); 419 BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) ); 420} 421 422static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest ) 423{ 424 BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) ); 425 BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) ); 426 dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate; 427} 428 429 430static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ) 431{ 432 FCB * fcb; 433 ExtentsRecBuffer extentsBuffer[kNumExtentsToCache]; 434 ExtentKey * extentKeyPtr; 435 ExtentRecord extentData; 436 struct BTreeIterator *btIterator = NULL; 437 struct BTreeIterator *tmpIterator = NULL; 438 FSBufferDescriptor btRecord; 439 u_int16_t btKeySize; 440 u_int16_t btRecordSize; 441 int16_t i, j; 442 OSErr err; 443 444 MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 445 if (btIterator == NULL) { 446 return memFullErr; // translates to ENOMEM 447 } 448 449 450 MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 451 if (tmpIterator == NULL) { 452 FREE (btIterator, M_TEMP); 453 return memFullErr; // translates to ENOMEM 454 } 455 456 bzero(btIterator, sizeof(*btIterator)); 457 bzero (tmpIterator, sizeof(*tmpIterator)); 458 459 460 fcb = GetFileControlBlock(vcb->extentsRefNum); 461 462 (void) BTInvalidateHint(btIterator); 463 extentKeyPtr = (ExtentKey*) &btIterator->key; 464 btRecord.bufferAddress = &extentData; 465 btRecord.itemCount = 1; 466 467 //-- Collect the extent records 468 469 // 470 // A search on the following key will cause the BTree to be positioned immediately 471 // before the first extent record for file #srcFileID, but not actually positioned 472 // on any record. This is because there cannot be an extent record with FABN = 0 473 // (the first extent of the fork, which would be in the catalog entry, not an extent 474 // record). 475 // 476 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record. 477 // 478 if (isHFSPlus) { 479 btRecord.itemSize = sizeof(HFSPlusExtentRecord); 480 btKeySize = sizeof(HFSPlusExtentKey); 481 482 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; 483 extentKeyPtr->hfsPlus.forkType = forkType; 484 extentKeyPtr->hfsPlus.pad = 0; 485 extentKeyPtr->hfsPlus.fileID = srcFileID; 486 extentKeyPtr->hfsPlus.startBlock = 0; 487 } 488 else { 489 btRecord.itemSize = sizeof(HFSExtentRecord); 490 btKeySize = sizeof(HFSExtentKey); 491 492 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; 493 extentKeyPtr->hfs.forkType = 0; 494 extentKeyPtr->hfs.fileID = srcFileID; 495 extentKeyPtr->hfs.startBlock = 0; 496 } 497 498 // 499 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent 500 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found 501 // records, but with destFileID as the file number in the key. Keep doing this sequence of 502 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are 503 // no more extent records in the tree. 504 // 505 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID 506 // set to destFileID. 507 // 508 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it 509 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series 510 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record 511 // we found, so that BTIterateRecord would get the next one (the first we haven't processed). 512 // 513 514 err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); 515 516 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0. 517 if (err != btNotFound) 518 { 519 if ( DEBUG_BUILD ) 520 DebugStr("Unexpected error from SearchBTreeRecord"); 521 522 if (err == noErr) // If we found such a bogus extent record, then the tree is really messed up 523 err = cmBadNews; // so return an error that conveys the disk is hosed. 524 525 FREE (tmpIterator, M_TEMP); 526 FREE (btIterator, M_TEMP); 527 return err; 528 } 529 530 do 531 { 532 btRecord.bufferAddress = &extentData; 533 btRecord.itemCount = 1; 534 535 for ( i=0 ; i<kNumExtentsToCache ; i++ ) 536 { 537 HFSCatalogNodeID foundFileID; 538 539 err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize); 540 if ( err == btNotFound ) // Did we run out of extent records in the extents tree? 541 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus! 542 else if ( err != noErr ) { 543 FREE (btIterator, M_TEMP); 544 FREE (tmpIterator, M_TEMP); 545 return( err ); // must be ioError 546 } 547 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID; 548 if ( foundFileID == srcFileID ) { 549 /* Check if we need to quit early. */ 550 if (quitEarly && isHFSPlus) { 551 if (extentKeyPtr->hfsPlus.forkType != forkType) { 552 break; 553 } 554 } 555 CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i); 556 } 557 else{ 558 /* The fileID's are of a different file. We're done here. */ 559 break; 560 } 561 } 562 563 564 565 //-- edit each extent key, and reinsert each extent record in the extent file 566 if (isHFSPlus) 567 btRecordSize = sizeof(HFSPlusExtentRecord); 568 else 569 btRecordSize = sizeof(HFSExtentRecord); 570 571 for ( j=0 ; j<i ; j++ ) 572 { 573 574 if (isHFSPlus) 575 extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; // change only the id in the key to dest ID 576 else 577 extentsBuffer[j].extentKey.hfs.fileID = destFileID; // change only the id in the key to dest ID 578 579 // get iterator and buffer descriptor ready... 580 (void) BTInvalidateHint(tmpIterator); 581 BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator->key, btKeySize); 582 btRecord.bufferAddress = &(extentsBuffer[j].extentData); 583 584 err = BTInsertRecord(fcb, tmpIterator, &btRecord, btRecordSize); 585 if ( err != noErr ) { 586 /* Parse the error and free iterators */ 587 FREE (btIterator, M_TEMP); 588 FREE (tmpIterator, M_TEMP); 589 if ( err == btExists ) 590 { 591 if ( DEBUG_BUILD ) { 592 DebugStr("Can't insert record -- already exists"); 593 } 594 return( cmBadNews ); 595 } 596 else { 597 return( err ); 598 } 599 } 600 } 601 602 //-- okay, done with this buffered batch, go get the next set of extent records 603 // If our buffer is not full, we must be done, or recieved an error 604 605 if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done 606 { 607 err = DeleteExtents( vcb, srcFileID, forkType, quitEarly, isHFSPlus ); // Now delete all the extent entries with the sourceID 608 if ( DEBUG_BUILD && err != noErr ) 609 DebugStr("Error from DeleteExtents"); 610 break; // we're done! 611 } 612 } while ( true ); 613 614 FREE (tmpIterator, M_TEMP); 615 FREE (btIterator, M_TEMP); 616 617 return( err ); 618} 619 620 621static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount ) 622{ 623 BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) ); 624 BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) ); 625} 626 627 628//-- Delete all extents in extent file that have the ID given. 629static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus ) 630{ 631 FCB * fcb; 632 ExtentKey * extentKeyPtr; 633 ExtentRecord extentData; 634 struct BTreeIterator *btIterator = NULL; 635 struct BTreeIterator *tmpIterator = NULL; 636 FSBufferDescriptor btRecord; 637 u_int16_t btRecordSize; 638 OSErr err; 639 640 641 MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 642 if (btIterator == NULL) { 643 return memFullErr; // translates to ENOMEM 644 } 645 646 MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); 647 if (tmpIterator == NULL) { 648 FREE (btIterator, M_TEMP); 649 return memFullErr; // translates to ENOMEM 650 } 651 652 bzero(btIterator, sizeof(*btIterator)); 653 bzero (tmpIterator, sizeof(*tmpIterator)); 654 655 fcb = GetFileControlBlock(vcb->extentsRefNum); 656 657 (void) BTInvalidateHint(btIterator); 658 extentKeyPtr = (ExtentKey*) &btIterator->key; 659 btRecord.bufferAddress = &extentData; 660 btRecord.itemCount = 1; 661 662 // The algorithm is to position the BTree just before any extent records for fileID. 663 // Then just keep getting successive records. If the record is still for fileID, 664 // then delete it. 665 666 if (isHFSPlus) { 667 btRecord.itemSize = sizeof(HFSPlusExtentRecord); 668 669 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength; 670 extentKeyPtr->hfsPlus.forkType = forkType; 671 extentKeyPtr->hfsPlus.pad = 0; 672 extentKeyPtr->hfsPlus.fileID = fileID; 673 extentKeyPtr->hfsPlus.startBlock = 0; 674 } 675 else { 676 btRecord.itemSize = sizeof(HFSExtentRecord); 677 678 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength; 679 extentKeyPtr->hfs.forkType = forkType; 680 extentKeyPtr->hfs.fileID = fileID; 681 extentKeyPtr->hfs.startBlock = 0; 682 } 683 684 err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator); 685 if ( err != btNotFound ) 686 { 687 if (err == noErr) { // Did we find a bogus extent record? 688 err = cmBadNews; // Yes, so indicate things are messed up. 689 } 690 691 return err; // Got some unexpected error, so return it 692 } 693 694 do 695 { 696 HFSCatalogNodeID foundFileID; 697 698 err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize); 699 if ( err != noErr ) 700 { 701 if (err == btNotFound) // If we hit the end of the BTree 702 err = noErr; // then it's OK 703 704 break; // We're done now. 705 } 706 707 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID; 708 if ( foundFileID != fileID ) { 709 break; // numbers don't match, we must be done 710 } 711 if (quitEarly && isHFSPlus) { 712 /* If we're only deleting one type of fork, then quit early if it doesn't match */ 713 if (extentKeyPtr->hfsPlus.forkType != forkType) { 714 break; 715 } 716 } 717 718 *tmpIterator = *btIterator; 719 err = BTDeleteRecord( fcb, tmpIterator ); 720 if (err != noErr) 721 break; 722 } while ( true ); 723 724 FREE (tmpIterator, M_TEMP); 725 FREE (btIterator, M_TEMP); 726 727 return( err ); 728} 729 730 731// Check if there are extents represented in the extents overflow file. 732static u_int32_t CheckExtents( void *extents, u_int32_t totalBlocks, Boolean isHFSPlus ) 733{ 734 u_int32_t extentAllocationBlocks; 735 u_int16_t i; 736 737 738 if ( totalBlocks == 0 ) 739 return( 0 ); 740 741 extentAllocationBlocks = 0; 742 743 if ( isHFSPlus ) 744 { 745 for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ ) 746 { 747 extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount; 748 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump) 749 return( 0 ); 750 } 751 } 752 else 753 { 754 for ( i = 0 ; i < kHFSExtentDensity ; i++ ) 755 { 756 extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount; 757 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump) 758 return( 0 ); 759 } 760 } 761 762 return( extentAllocationBlocks ); 763} 764