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