2 * Copyright (c) 2000-2015 Apple 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 "FileMgrInternal.h"
33 #include "HFSUnicodeWrappers.h"
34 #include "CatalogPrivate.h"
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <libkern/libkern.h>
41 struct ExtentsRecBuffer
{
43 ExtentRecord extentData
;
45 typedef struct ExtentsRecBuffer ExtentsRecBuffer
;
48 static u_int32_t
CheckExtents( void *extents
, u_int32_t blocks
, Boolean isHFSPlus
);
49 static OSErr
DeleteExtents( ExtendedVCB
*vcb
, u_int32_t fileNumber
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
);
50 static OSErr
MoveExtents( ExtendedVCB
*vcb
, u_int32_t srcFileID
, u_int32_t destFileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
);
53 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
56 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
57 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
);
60 * This function moves the overflow extents associated with srcID into the file associated with dstID.
61 * We should have already verified that 'srcID' has overflow extents. So now we move all of the overflow
64 OSErr
MoveData( ExtendedVCB
*vcb
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, int rsrc
) {
69 * Only the source file should have extents, so we just track those.
70 * We operate on the fork represented by the open FD that was used to call into this
74 /* Copy the extent overflow blocks. */
75 err
= MoveExtents( vcb
, srcID
, destID
, 1, (u_int8_t
)0xff, 1);
77 if ( err
!= dskFulErr
) {
81 * In case of error, we would have probably run into problems
82 * growing the extents b-tree. Since the move is actually a copy + delete
83 * just delete the new entries. Same for below.
85 err
= DeleteExtents( vcb
, destID
, 1, (u_int8_t
)0xff, 1);
86 ReturnIfError( err
); // we are doomed. Just QUIT!
91 /* Copy the extent overflow blocks. */
92 err
= MoveExtents( vcb
, srcID
, destID
, 1, 0, 1);
94 if ( err
!= dskFulErr
) {
97 err
= DeleteExtents( vcb
, destID
, 1, 0, 1);
98 ReturnIfError( err
); // we are doomed. Just QUIT!
104 /* Write out the catalog and extent overflow B-Tree changes */
105 err
= FlushCatalog( vcb
);
106 err
= FlushExtentFile( vcb
);
112 OSErr
ExchangeFileIDs( ExtendedVCB
*vcb
, ConstUTF8Param srcName
, ConstUTF8Param destName
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, u_int32_t srcHint
, u_int32_t destHint
)
114 CatalogKey srcKey
; // 518 bytes
115 CatalogKey destKey
; // 518 bytes
116 CatalogRecord srcData
; // 520 bytes
117 CatalogRecord destData
; // 520 bytes
118 CatalogRecord swapData
; // 520 bytes
119 int16_t numSrcExtentBlocks
;
120 int16_t numDestExtentBlocks
;
122 Boolean isHFSPlus
= ( vcb
->vcbSigWord
== kHFSPlusSigWord
);
124 err
= BuildCatalogKeyUTF8(vcb
, srcID
, srcName
, kUndefinedStrLen
, &srcKey
);
127 err
= BuildCatalogKeyUTF8(vcb
, destID
, destName
, kUndefinedStrLen
, &destKey
);
132 //-- Step 1: Check the catalog nodes for extents
134 //-- locate the source file, test for extents in extent file, and copy the cat record for later
135 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
136 ReturnIfError( err
);
138 if ( srcData
.recordType
!= kHFSPlusFileRecord
)
139 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
141 //-- Check if there are any extents in the source file
142 //\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
143 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.dataFork
.extents
, srcData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
144 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
145 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.resourceFork
.extents
, srcData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
147 //-- Check if there are any extents in the destination file
148 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
149 ReturnIfError( err
);
151 if ( destData
.recordType
!= kHFSPlusFileRecord
)
152 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
154 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.dataFork
.extents
, destData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
155 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
156 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.resourceFork
.extents
, destData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
158 //-- Step 2: Exchange the Extent key in the extent file
160 //-- Exchange the extents key in the extent file
161 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
162 ReturnIfError( err
);
164 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
166 //-- Change the source extents file ids to our known bogus value
167 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, kHFSBogusExtentFileID
, 0,0, isHFSPlus
);
170 if ( err
!= dskFulErr
) {
174 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
175 ReturnIfError( err
); // we are doomed. Just QUIT!
177 err
= FlushCatalog( vcb
); // flush the catalog
178 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
183 //-- Change the destination extents file id's to the source id's
184 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
187 if ( err
!= dskFulErr
)
190 ExUndo2aPlus
: err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
191 ReturnIfError( err
); // we are doomed. Just QUIT!
193 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
194 ReturnIfError( err
); // we are doomed. Just QUIT!
196 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
197 ReturnIfError( err
); // we are doomed. Just QUIT!
199 err
= FlushCatalog( vcb
); // flush the catalog
200 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
205 //-- Change the bogus extents file id's to the dest id's
206 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
209 if ( err
!= dskFulErr
)
212 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
213 ReturnIfError( err
); // we are doomed. Just QUIT!
215 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
216 ReturnIfError( err
); // we are doomed. Just QUIT!
222 else if ( numSrcExtentBlocks
) // just the source file has extents
224 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
227 if ( err
!= dskFulErr
)
230 err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
231 ReturnIfError( err
); // we are doomed. Just QUIT!
236 else if ( numDestExtentBlocks
) // just the destination file has extents
238 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
241 if ( err
!= dskFulErr
)
244 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
245 ReturnIfError( err
); // we are doomed. Just QUIT!
251 //-- Step 3: Change the data in the catalog nodes
253 //-- find the source cnode and put dest info in it
254 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
258 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
259 CopyBigCatalogNodeInfo( &destData
, &srcData
);
261 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSPlusCatalogFile
), &srcHint
);
262 ReturnIfError( err
);
264 // find the destination cnode and put source info in it
265 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
269 CopyBigCatalogNodeInfo( &swapData
, &destData
);
270 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSPlusCatalogFile
), &destHint
);
271 ReturnIfError( err
);
276 //-- Step 1: Check the catalog nodes for extents
278 //-- locate the source file, test for extents in extent file, and copy the cat record for later
279 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
280 ReturnIfError( err
);
282 if ( srcData
.recordType
!= kHFSFileRecord
)
283 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
285 //-- Check if there are any extents in the source file
286 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.dataExtents
, srcData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
287 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
288 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.rsrcExtents
, srcData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
291 //\80\80 Do we save the found source node for later use?
294 //-- Check if there are any extents in the destination file
295 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
296 ReturnIfError( err
);
298 if ( destData
.recordType
!= kHFSFileRecord
)
299 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
301 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.dataExtents
, destData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
302 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
303 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.rsrcExtents
, destData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
305 //\80\80 Do we save the found destination node for later use?
308 //-- Step 2: Exchange the Extent key in the extent file
310 //-- Exchange the extents key in the extent file
311 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
312 ReturnIfError( err
);
314 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
316 //-- Change the source extents file ids to our known bogus value
317 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
320 if ( err
!= dskFulErr
)
323 ExUndo1a
: err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
324 ReturnIfError( err
); // we are doomed. Just QUIT!
326 err
= FlushCatalog( vcb
); // flush the catalog
327 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
331 //-- Change the destination extents file id's to the source id's
332 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
335 if ( err
!= dskFulErr
)
338 ExUndo2a
: err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
339 ReturnIfError( err
); // we are doomed. Just QUIT!
341 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
342 ReturnIfError( err
); // we are doomed. Just QUIT!
347 //-- Change the bogus extents file id's to the dest id's
348 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
351 if ( err
!= dskFulErr
)
354 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
355 ReturnIfError( err
); // we are doomed. Just QUIT!
357 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
358 ReturnIfError( err
); // we are doomed. Just QUIT!
364 else if ( numSrcExtentBlocks
) // just the source file has extents
366 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
369 if ( err
!= dskFulErr
)
372 err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
373 ReturnIfError( err
); // we are doomed. Just QUIT!
378 else if ( numDestExtentBlocks
) // just the destination file has extents
380 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
383 if ( err
!= dskFulErr
)
386 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
387 ReturnIfError( err
); // we are doomed. Just QUIT!
393 //-- Step 3: Change the data in the catalog nodes
395 //-- find the source cnode and put dest info in it
396 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
400 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
401 //\80\80 Asm source copies from the saved dest catalog node
402 CopyCatalogNodeInfo( &destData
, &srcData
);
404 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSCatalogFile
), &srcHint
);
405 ReturnIfError( err
);
408 // find the destination cnode and put source info in it
409 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
413 CopyCatalogNodeInfo( &swapData
, &destData
);
414 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSCatalogFile
), &destHint
);
415 ReturnIfError( err
);
421 //-- Step 4: Error Handling section
425 err
= FlushCatalog( vcb
); // flush the catalog
426 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
432 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
434 dest
->hfsFile
.dataLogicalSize
= src
->hfsFile
.dataLogicalSize
;
435 dest
->hfsFile
.dataPhysicalSize
= src
->hfsFile
.dataPhysicalSize
;
436 dest
->hfsFile
.rsrcLogicalSize
= src
->hfsFile
.rsrcLogicalSize
;
437 dest
->hfsFile
.rsrcPhysicalSize
= src
->hfsFile
.rsrcPhysicalSize
;
438 dest
->hfsFile
.modifyDate
= src
->hfsFile
.modifyDate
;
439 BlockMoveData( src
->hfsFile
.dataExtents
, dest
->hfsFile
.dataExtents
, sizeof(HFSExtentRecord
) );
440 BlockMoveData( src
->hfsFile
.rsrcExtents
, dest
->hfsFile
.rsrcExtents
, sizeof(HFSExtentRecord
) );
444 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
446 BlockMoveData( &src
->hfsPlusFile
.dataFork
, &dest
->hfsPlusFile
.dataFork
, sizeof(HFSPlusForkData
) );
447 BlockMoveData( &src
->hfsPlusFile
.resourceFork
, &dest
->hfsPlusFile
.resourceFork
, sizeof(HFSPlusForkData
) );
448 dest
->hfsPlusFile
.contentModDate
= src
->hfsPlusFile
.contentModDate
;
452 static OSErr
MoveExtents( ExtendedVCB
*vcb
, u_int32_t srcFileID
, u_int32_t destFileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
455 ExtentsRecBuffer extentsBuffer
[kNumExtentsToCache
];
456 ExtentKey
* extentKeyPtr
;
457 ExtentRecord extentData
;
458 struct BTreeIterator
*btIterator
= NULL
;
459 struct BTreeIterator
*tmpIterator
= NULL
;
460 FSBufferDescriptor btRecord
;
462 u_int16_t btRecordSize
;
466 btIterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
467 tmpIterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
469 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
471 (void) BTInvalidateHint(btIterator
);
472 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
473 btRecord
.bufferAddress
= &extentData
;
474 btRecord
.itemCount
= 1;
476 //-- Collect the extent records
479 // A search on the following key will cause the BTree to be positioned immediately
480 // before the first extent record for file #srcFileID, but not actually positioned
481 // on any record. This is because there cannot be an extent record with FABN = 0
482 // (the first extent of the fork, which would be in the catalog entry, not an extent
485 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
488 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
489 btKeySize
= sizeof(HFSPlusExtentKey
);
491 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
492 extentKeyPtr
->hfsPlus
.forkType
= forkType
;
493 extentKeyPtr
->hfsPlus
.pad
= 0;
494 extentKeyPtr
->hfsPlus
.fileID
= srcFileID
;
495 extentKeyPtr
->hfsPlus
.startBlock
= 0;
499 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
500 btKeySize
= sizeof(HFSExtentKey
);
502 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
503 extentKeyPtr
->hfs
.forkType
= 0;
504 extentKeyPtr
->hfs
.fileID
= srcFileID
;
505 extentKeyPtr
->hfs
.startBlock
= 0;
509 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
510 hfs_free(btIterator
, sizeof(*btIterator
));
516 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent
517 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found
518 // records, but with destFileID as the file number in the key. Keep doing this sequence of
519 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
520 // no more extent records in the tree.
522 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
523 // set to destFileID.
525 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it
526 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series
527 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record
528 // we found, so that BTIterateRecord would get the next one (the first we haven't processed).
531 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
533 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
534 if (err
!= btNotFound
)
536 hfs_debug("hfs: unexpected error from SearchBTreeRecord\n");
538 if (err
== noErr
) // If we found such a bogus extent record, then the tree is really messed up
539 err
= cmBadNews
; // so return an error that conveys the disk is hosed.
541 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
542 hfs_free(btIterator
, sizeof(*btIterator
));
548 btRecord
.bufferAddress
= &extentData
;
549 btRecord
.itemCount
= 1;
551 for ( i
=0 ; i
<kNumExtentsToCache
; i
++ )
553 HFSCatalogNodeID foundFileID
= 0;
555 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, btIterator
, &btRecord
, &btRecordSize
);
556 if ( err
== btNotFound
) // Did we run out of extent records in the extents tree?
557 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus!
558 else if ( err
!= noErr
) {
559 hfs_free(btIterator
, sizeof(*btIterator
));
560 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
561 return( err
); // must be ioError
564 foundFileID
= extentKeyPtr
->hfsPlus
.fileID
;
568 foundFileID
= extentKeyPtr
->hfs
.fileID
;
571 if ( foundFileID
== srcFileID
) {
572 /* Check if we need to quit early. */
573 if (quitEarly
&& isHFSPlus
) {
574 if (extentKeyPtr
->hfsPlus
.forkType
!= forkType
) {
578 CopyExtentInfo(extentKeyPtr
, &extentData
, extentsBuffer
, i
);
581 /* The fileID's are of a different file. We're done here. */
588 //-- edit each extent key, and reinsert each extent record in the extent file
590 btRecordSize
= sizeof(HFSPlusExtentRecord
);
593 btRecordSize
= sizeof(HFSExtentRecord
);
596 for ( j
=0 ; j
<i
; j
++ )
600 extentsBuffer
[j
].extentKey
.hfsPlus
.fileID
= destFileID
; // change only the id in the key to dest ID
603 extentsBuffer
[j
].extentKey
.hfs
.fileID
= destFileID
; // change only the id in the key to dest ID
606 // get iterator and buffer descriptor ready...
607 (void) BTInvalidateHint(tmpIterator
);
608 BlockMoveData(&(extentsBuffer
[j
].extentKey
), &tmpIterator
->key
, btKeySize
);
609 btRecord
.bufferAddress
= &(extentsBuffer
[j
].extentData
);
611 err
= BTInsertRecord(fcb
, tmpIterator
, &btRecord
, btRecordSize
);
612 if ( err
!= noErr
) {
613 /* Parse the error and free iterators */
614 hfs_free(btIterator
, sizeof(*btIterator
));
615 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
616 if ( err
== btExists
)
618 hfs_debug("hfs: can't insert record -- already exists\n");
627 //-- okay, done with this buffered batch, go get the next set of extent records
628 // If our buffer is not full, we must be done, or recieved an error
630 if ( i
!= kNumExtentsToCache
) // if the buffer is not full, we must be done
632 err
= DeleteExtents( vcb
, srcFileID
, quitEarly
, forkType
, isHFSPlus
); // Now delete all the extent entries with the sourceID
634 hfs_debug("hfs: error from DeleteExtents (%d)\n", err
);
635 break; // we're done!
639 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
640 hfs_free(btIterator
, sizeof(*btIterator
));
646 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
)
648 BlockMoveData( key
, &(buffer
[bufferCount
].extentKey
), sizeof( ExtentKey
) );
649 BlockMoveData( data
, &(buffer
[bufferCount
].extentData
), sizeof( ExtentRecord
) );
653 //-- Delete all extents in extent file that have the ID given.
654 static OSErr
DeleteExtents( ExtendedVCB
*vcb
, u_int32_t fileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
657 ExtentKey
* extentKeyPtr
;
658 ExtentRecord extentData
;
659 struct BTreeIterator
*btIterator
= NULL
;
660 struct BTreeIterator
*tmpIterator
= NULL
;
661 FSBufferDescriptor btRecord
;
662 u_int16_t btRecordSize
;
665 btIterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
666 tmpIterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
668 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
670 (void) BTInvalidateHint(btIterator
);
671 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
672 btRecord
.bufferAddress
= &extentData
;
673 btRecord
.itemCount
= 1;
675 // The algorithm is to position the BTree just before any extent records for fileID.
676 // Then just keep getting successive records. If the record is still for fileID,
680 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
682 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
683 extentKeyPtr
->hfsPlus
.forkType
= forkType
;
684 extentKeyPtr
->hfsPlus
.pad
= 0;
685 extentKeyPtr
->hfsPlus
.fileID
= fileID
;
686 extentKeyPtr
->hfsPlus
.startBlock
= 0;
690 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
692 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
693 extentKeyPtr
->hfs
.forkType
= forkType
;
694 extentKeyPtr
->hfs
.fileID
= fileID
;
695 extentKeyPtr
->hfs
.startBlock
= 0;
704 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
705 if ( err
!= btNotFound
)
707 if (err
== noErr
) { // Did we find a bogus extent record?
708 err
= cmBadNews
; // Yes, so indicate things are messed up.
716 HFSCatalogNodeID foundFileID
= 0;
718 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, btIterator
, &btRecord
, &btRecordSize
);
721 if (err
== btNotFound
) // If we hit the end of the BTree
722 err
= noErr
; // then it's OK
724 break; // We're done now.
727 foundFileID
= extentKeyPtr
->hfsPlus
.fileID
;
731 foundFileID
= extentKeyPtr
->hfs
.fileID
;
735 if ( foundFileID
!= fileID
) {
736 break; // numbers don't match, we must be done
738 if (quitEarly
&& isHFSPlus
) {
739 /* If we're only deleting one type of fork, then quit early if it doesn't match */
740 if (extentKeyPtr
->hfsPlus
.forkType
!= forkType
) {
745 *tmpIterator
= *btIterator
;
746 err
= BTDeleteRecord( fcb
, tmpIterator
);
753 hfs_free(tmpIterator
, sizeof(*tmpIterator
));
754 hfs_free(btIterator
, sizeof(*btIterator
));
760 // Check if there are extents represented in the extents overflow file.
761 static u_int32_t
CheckExtents( void *extents
, u_int32_t totalBlocks
, Boolean isHFSPlus
)
763 u_int32_t extentAllocationBlocks
;
767 if ( totalBlocks
== 0 )
770 extentAllocationBlocks
= 0;
774 for ( i
= 0 ; i
< kHFSPlusExtentDensity
; i
++ )
776 extentAllocationBlocks
+= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
777 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
784 for ( i
= 0 ; i
< kHFSExtentDensity
; i
++ )
786 extentAllocationBlocks
+= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
787 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
793 return( extentAllocationBlocks
);