2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include "../../hfs_macos_defs.h"
30 #include "../../hfs_format.h"
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>
40 struct ExtentsRecBuffer
{
42 ExtentRecord extentData
;
44 typedef struct ExtentsRecBuffer ExtentsRecBuffer
;
47 static u_int32_t
CheckExtents( void *extents
, u_int32_t blocks
, Boolean isHFSPlus
);
48 static OSErr
DeleteExtents( ExtendedVCB
*vcb
, u_int32_t fileNumber
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
);
49 static OSErr
MoveExtents( ExtendedVCB
*vcb
, u_int32_t srcFileID
, u_int32_t destFileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
);
52 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
55 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
56 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
);
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
63 OSErr
MoveData( ExtendedVCB
*vcb
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, int rsrc
) {
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
73 /* Copy the extent overflow blocks. */
74 err
= MoveExtents( vcb
, srcID
, destID
, 1, (u_int8_t
)0xff, 1);
76 if ( err
!= dskFulErr
) {
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.
84 err
= DeleteExtents( vcb
, destID
, 1, (u_int8_t
)0xff, 1);
85 ReturnIfError( err
); // we are doomed. Just QUIT!
90 /* Copy the extent overflow blocks. */
91 err
= MoveExtents( vcb
, srcID
, destID
, 1, 0, 1);
93 if ( err
!= dskFulErr
) {
96 err
= DeleteExtents( vcb
, destID
, 1, 0, 1);
97 ReturnIfError( err
); // we are doomed. Just QUIT!
103 /* Write out the catalog and extent overflow B-Tree changes */
104 err
= FlushCatalog( vcb
);
105 err
= FlushExtentFile( vcb
);
111 OSErr
ExchangeFileIDs( ExtendedVCB
*vcb
, ConstUTF8Param srcName
, ConstUTF8Param destName
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, u_int32_t srcHint
, u_int32_t destHint
)
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
;
121 Boolean isHFSPlus
= ( vcb
->vcbSigWord
== kHFSPlusSigWord
);
123 err
= BuildCatalogKeyUTF8(vcb
, srcID
, srcName
, kUndefinedStrLen
, &srcKey
, NULL
);
126 err
= BuildCatalogKeyUTF8(vcb
, destID
, destName
, kUndefinedStrLen
, &destKey
, NULL
);
131 //-- Step 1: Check the catalog nodes for extents
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
);
137 if ( srcData
.recordType
!= kHFSPlusFileRecord
)
138 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
140 //-- Check if there are any extents in the source file
141 //\80\80 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
);
146 //-- Check if there are any extents in the destination file
147 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
148 ReturnIfError( err
);
150 if ( destData
.recordType
!= kHFSPlusFileRecord
)
151 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
157 //-- Step 2: Exchange the Extent key in the extent file
159 //-- Exchange the extents key in the extent file
160 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
161 ReturnIfError( err
);
163 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
165 //-- Change the source extents file ids to our known bogus value
166 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, kHFSBogusExtentFileID
, 0,0, isHFSPlus
);
169 if ( err
!= dskFulErr
) {
173 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
174 ReturnIfError( err
); // we are doomed. Just QUIT!
176 err
= FlushCatalog( vcb
); // flush the catalog
177 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
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
);
186 if ( err
!= dskFulErr
)
189 ExUndo2aPlus
: err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
190 ReturnIfError( err
); // we are doomed. Just QUIT!
192 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
193 ReturnIfError( err
); // we are doomed. Just QUIT!
195 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
196 ReturnIfError( err
); // we are doomed. Just QUIT!
198 err
= FlushCatalog( vcb
); // flush the catalog
199 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
204 //-- Change the bogus extents file id's to the dest id's
205 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
208 if ( err
!= dskFulErr
)
211 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
212 ReturnIfError( err
); // we are doomed. Just QUIT!
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!
221 else if ( numSrcExtentBlocks
) // just the source file has extents
223 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
226 if ( err
!= dskFulErr
)
229 err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
230 ReturnIfError( err
); // we are doomed. Just QUIT!
235 else if ( numDestExtentBlocks
) // just the destination file has extents
237 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
240 if ( err
!= dskFulErr
)
243 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
244 ReturnIfError( err
); // we are doomed. Just QUIT!
250 //-- Step 3: Change the data in the catalog nodes
252 //-- find the source cnode and put dest info in it
253 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
257 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
258 CopyBigCatalogNodeInfo( &destData
, &srcData
);
260 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSPlusCatalogFile
), &srcHint
);
261 ReturnIfError( err
);
263 // find the destination cnode and put source info in it
264 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
268 CopyBigCatalogNodeInfo( &swapData
, &destData
);
269 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSPlusCatalogFile
), &destHint
);
270 ReturnIfError( err
);
275 //-- Step 1: Check the catalog nodes for extents
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
);
281 if ( srcData
.recordType
!= kHFSFileRecord
)
282 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
290 //\80\80 Do we save the found source node for later use?
293 //-- Check if there are any extents in the destination file
294 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
295 ReturnIfError( err
);
297 if ( destData
.recordType
!= kHFSFileRecord
)
298 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
304 //\80\80 Do we save the found destination node for later use?
307 //-- Step 2: Exchange the Extent key in the extent file
309 //-- Exchange the extents key in the extent file
310 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
311 ReturnIfError( err
);
313 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
315 //-- Change the source extents file ids to our known bogus value
316 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
319 if ( err
!= dskFulErr
)
322 ExUndo1a
: err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
323 ReturnIfError( err
); // we are doomed. Just QUIT!
325 err
= FlushCatalog( vcb
); // flush the catalog
326 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
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
);
334 if ( err
!= dskFulErr
)
337 ExUndo2a
: err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
338 ReturnIfError( err
); // we are doomed. Just QUIT!
340 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
341 ReturnIfError( err
); // we are doomed. Just QUIT!
346 //-- Change the bogus extents file id's to the dest id's
347 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
350 if ( err
!= dskFulErr
)
353 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
354 ReturnIfError( err
); // we are doomed. Just QUIT!
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!
363 else if ( numSrcExtentBlocks
) // just the source file has extents
365 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
368 if ( err
!= dskFulErr
)
371 err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
372 ReturnIfError( err
); // we are doomed. Just QUIT!
377 else if ( numDestExtentBlocks
) // just the destination file has extents
379 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
382 if ( err
!= dskFulErr
)
385 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
386 ReturnIfError( err
); // we are doomed. Just QUIT!
392 //-- Step 3: Change the data in the catalog nodes
394 //-- find the source cnode and put dest info in it
395 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
399 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
400 //\80\80 Asm source copies from the saved dest catalog node
401 CopyCatalogNodeInfo( &destData
, &srcData
);
403 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSCatalogFile
), &srcHint
);
404 ReturnIfError( err
);
407 // find the destination cnode and put source info in it
408 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
412 CopyCatalogNodeInfo( &swapData
, &destData
);
413 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSCatalogFile
), &destHint
);
414 ReturnIfError( err
);
420 //-- Step 4: Error Handling section
424 err
= FlushCatalog( vcb
); // flush the catalog
425 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
431 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
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
) );
443 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
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
;
451 static OSErr
MoveExtents( ExtendedVCB
*vcb
, u_int32_t srcFileID
, u_int32_t destFileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
454 ExtentsRecBuffer extentsBuffer
[kNumExtentsToCache
];
455 ExtentKey
* extentKeyPtr
;
456 ExtentRecord extentData
;
457 struct BTreeIterator
*btIterator
= NULL
;
458 struct BTreeIterator
*tmpIterator
= NULL
;
459 FSBufferDescriptor btRecord
;
461 u_int16_t btRecordSize
;
465 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
466 if (btIterator
== NULL
) {
467 return memFullErr
; // translates to ENOMEM
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
477 bzero(btIterator
, sizeof(*btIterator
));
478 bzero (tmpIterator
, sizeof(*tmpIterator
));
481 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
483 (void) BTInvalidateHint(btIterator
);
484 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
485 btRecord
.bufferAddress
= &extentData
;
486 btRecord
.itemCount
= 1;
488 //-- Collect the extent records
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
497 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
500 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
501 btKeySize
= sizeof(HFSPlusExtentKey
);
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;
511 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
512 btKeySize
= sizeof(HFSExtentKey
);
514 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
515 extentKeyPtr
->hfs
.forkType
= 0;
516 extentKeyPtr
->hfs
.fileID
= srcFileID
;
517 extentKeyPtr
->hfs
.startBlock
= 0;
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.
532 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
533 // set to destFileID.
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).
541 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
543 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
544 if (err
!= btNotFound
)
547 DebugStr("Unexpected error from SearchBTreeRecord");
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.
552 FREE (tmpIterator
, M_TEMP
);
553 FREE (btIterator
, M_TEMP
);
559 btRecord
.bufferAddress
= &extentData
;
560 btRecord
.itemCount
= 1;
562 for ( i
=0 ; i
<kNumExtentsToCache
; i
++ )
564 HFSCatalogNodeID foundFileID
= 0;
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
575 foundFileID
= extentKeyPtr
->hfsPlus
.fileID
;
579 foundFileID
= extentKeyPtr
->hfs
.fileID
;
582 if ( foundFileID
== srcFileID
) {
583 /* Check if we need to quit early. */
584 if (quitEarly
&& isHFSPlus
) {
585 if (extentKeyPtr
->hfsPlus
.forkType
!= forkType
) {
589 CopyExtentInfo(extentKeyPtr
, &extentData
, extentsBuffer
, i
);
592 /* The fileID's are of a different file. We're done here. */
599 //-- edit each extent key, and reinsert each extent record in the extent file
601 btRecordSize
= sizeof(HFSPlusExtentRecord
);
604 btRecordSize
= sizeof(HFSExtentRecord
);
607 for ( j
=0 ; j
<i
; j
++ )
611 extentsBuffer
[j
].extentKey
.hfsPlus
.fileID
= destFileID
; // change only the id in the key to dest ID
614 extentsBuffer
[j
].extentKey
.hfs
.fileID
= destFileID
; // change only the id in the key to dest ID
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
);
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
)
630 DebugStr("Can't insert record -- already exists");
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
643 if ( i
!= kNumExtentsToCache
) // if the buffer is not full, we must be done
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!
652 FREE (tmpIterator
, M_TEMP
);
653 FREE (btIterator
, M_TEMP
);
659 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
)
661 BlockMoveData( key
, &(buffer
[bufferCount
].extentKey
), sizeof( ExtentKey
) );
662 BlockMoveData( data
, &(buffer
[bufferCount
].extentData
), sizeof( ExtentRecord
) );
666 //-- Delete all extents in extent file that have the ID given.
667 static OSErr
DeleteExtents( ExtendedVCB
*vcb
, u_int32_t fileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
670 ExtentKey
* extentKeyPtr
;
671 ExtentRecord extentData
;
672 struct BTreeIterator
*btIterator
= NULL
;
673 struct BTreeIterator
*tmpIterator
= NULL
;
674 FSBufferDescriptor btRecord
;
675 u_int16_t btRecordSize
;
680 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
681 if (btIterator
== NULL
) {
682 return memFullErr
; // translates to ENOMEM
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
691 bzero(btIterator
, sizeof(*btIterator
));
692 bzero (tmpIterator
, sizeof(*tmpIterator
));
694 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
696 (void) BTInvalidateHint(btIterator
);
697 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
698 btRecord
.bufferAddress
= &extentData
;
699 btRecord
.itemCount
= 1;
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,
706 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
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;
716 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
718 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
719 extentKeyPtr
->hfs
.forkType
= forkType
;
720 extentKeyPtr
->hfs
.fileID
= fileID
;
721 extentKeyPtr
->hfs
.startBlock
= 0;
724 else return cmBadNews
;
727 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
728 if ( err
!= btNotFound
)
730 if (err
== noErr
) { // Did we find a bogus extent record?
731 err
= cmBadNews
; // Yes, so indicate things are messed up.
734 return err
; // Got some unexpected error, so return it
739 HFSCatalogNodeID foundFileID
= 0;
741 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, btIterator
, &btRecord
, &btRecordSize
);
744 if (err
== btNotFound
) // If we hit the end of the BTree
745 err
= noErr
; // then it's OK
747 break; // We're done now.
750 foundFileID
= extentKeyPtr
->hfsPlus
.fileID
;
754 foundFileID
= extentKeyPtr
->hfs
.fileID
;
758 if ( foundFileID
!= fileID
) {
759 break; // numbers don't match, we must be done
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
) {
768 *tmpIterator
= *btIterator
;
769 err
= BTDeleteRecord( fcb
, tmpIterator
);
774 FREE (tmpIterator
, M_TEMP
);
775 FREE (btIterator
, M_TEMP
);
781 // Check if there are extents represented in the extents overflow file.
782 static u_int32_t
CheckExtents( void *extents
, u_int32_t totalBlocks
, Boolean isHFSPlus
)
784 u_int32_t extentAllocationBlocks
;
788 if ( totalBlocks
== 0 )
791 extentAllocationBlocks
= 0;
795 for ( i
= 0 ; i
< kHFSPlusExtentDensity
; i
++ )
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)
805 for ( i
= 0 ; i
< kHFSExtentDensity
; i
++ )
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)
814 return( extentAllocationBlocks
);