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
);
50 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
51 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
52 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
);
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
59 OSErr
MoveData( ExtendedVCB
*vcb
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, int rsrc
) {
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
69 /* Copy the extent overflow blocks. */
70 err
= MoveExtents( vcb
, srcID
, destID
, 1, (u_int8_t
)0xff, 1);
72 if ( err
!= dskFulErr
) {
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.
80 err
= DeleteExtents( vcb
, destID
, 1, (u_int8_t
)0xff, 1);
81 ReturnIfError( err
); // we are doomed. Just QUIT!
86 /* Copy the extent overflow blocks. */
87 err
= MoveExtents( vcb
, srcID
, destID
, 1, 0, 1);
89 if ( err
!= dskFulErr
) {
92 err
= DeleteExtents( vcb
, destID
, 1, 0, 1);
93 ReturnIfError( err
); // we are doomed. Just QUIT!
99 /* Write out the catalog and extent overflow B-Tree changes */
100 err
= FlushCatalog( vcb
);
101 err
= FlushExtentFile( vcb
);
107 OSErr
ExchangeFileIDs( ExtendedVCB
*vcb
, ConstUTF8Param srcName
, ConstUTF8Param destName
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, u_int32_t srcHint
, u_int32_t destHint
)
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
;
117 Boolean isHFSPlus
= ( vcb
->vcbSigWord
== kHFSPlusSigWord
);
119 err
= BuildCatalogKeyUTF8(vcb
, srcID
, srcName
, kUndefinedStrLen
, &srcKey
, NULL
);
122 err
= BuildCatalogKeyUTF8(vcb
, destID
, destName
, kUndefinedStrLen
, &destKey
, NULL
);
127 //-- Step 1: Check the catalog nodes for extents
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
);
133 if ( srcData
.recordType
!= kHFSPlusFileRecord
)
134 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
136 //-- Check if there are any extents in the source file
137 //\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
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
);
142 //-- Check if there are any extents in the destination file
143 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
144 ReturnIfError( err
);
146 if ( destData
.recordType
!= kHFSPlusFileRecord
)
147 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
153 //-- Step 2: Exchange the Extent key in the extent file
155 //-- Exchange the extents key in the extent file
156 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
157 ReturnIfError( err
);
159 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
161 //-- Change the source extents file ids to our known bogus value
162 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, kHFSBogusExtentFileID
, 0,0, isHFSPlus
);
165 if ( err
!= dskFulErr
)
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
);
175 if ( err
!= dskFulErr
)
178 ExUndo2aPlus
: err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
179 ReturnIfError( err
); // we are doomed. Just QUIT!
181 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
182 ReturnIfError( err
); // we are doomed. Just QUIT!
187 //-- Change the bogus extents file id's to the dest id's
188 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
191 if ( err
!= dskFulErr
)
194 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
195 ReturnIfError( err
); // we are doomed. Just QUIT!
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!
204 else if ( numSrcExtentBlocks
) // just the source file has extents
206 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
209 if ( err
!= dskFulErr
)
212 err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
213 ReturnIfError( err
); // we are doomed. Just QUIT!
218 else if ( numDestExtentBlocks
) // just the destination file has extents
220 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
223 if ( err
!= dskFulErr
)
226 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, 0, 0, isHFSPlus
);
227 ReturnIfError( err
); // we are doomed. Just QUIT!
233 //-- Step 3: Change the data in the catalog nodes
235 //-- find the source cnode and put dest info in it
236 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
240 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
241 CopyBigCatalogNodeInfo( &destData
, &srcData
);
243 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSPlusCatalogFile
), &srcHint
);
244 ReturnIfError( err
);
246 // find the destination cnode and put source info in it
247 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
251 CopyBigCatalogNodeInfo( &swapData
, &destData
);
252 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSPlusCatalogFile
), &destHint
);
253 ReturnIfError( err
);
257 //-- Step 1: Check the catalog nodes for extents
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
);
263 if ( srcData
.recordType
!= kHFSFileRecord
)
264 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
272 //\80\80 Do we save the found source node for later use?
275 //-- Check if there are any extents in the destination file
276 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
277 ReturnIfError( err
);
279 if ( destData
.recordType
!= kHFSFileRecord
)
280 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
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
);
286 //\80\80 Do we save the found destination node for later use?
289 //-- Step 2: Exchange the Extent key in the extent file
291 //-- Exchange the extents key in the extent file
292 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
293 ReturnIfError( err
);
295 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
297 //-- Change the source extents file ids to our known bogus value
298 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
301 if ( err
!= dskFulErr
)
304 ExUndo1a
: err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, 0, 0, isHFSPlus
);
305 ReturnIfError( err
); // we are doomed. Just QUIT!
307 err
= FlushCatalog( vcb
); // flush the catalog
308 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
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
);
316 if ( err
!= dskFulErr
)
319 ExUndo2a
: err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
320 ReturnIfError( err
); // we are doomed. Just QUIT!
322 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
); // Move the extents back
323 ReturnIfError( err
); // we are doomed. Just QUIT!
328 //-- Change the bogus extents file id's to the dest id's
329 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
332 if ( err
!= dskFulErr
)
335 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
336 ReturnIfError( err
); // we are doomed. Just QUIT!
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!
345 else if ( numSrcExtentBlocks
) // just the source file has extents
347 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
350 if ( err
!= dskFulErr
)
353 err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
354 ReturnIfError( err
); // we are doomed. Just QUIT!
359 else if ( numDestExtentBlocks
) // just the destination file has extents
361 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
364 if ( err
!= dskFulErr
)
367 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, 0, 0, isHFSPlus
);
368 ReturnIfError( err
); // we are doomed. Just QUIT!
374 //-- Step 3: Change the data in the catalog nodes
376 //-- find the source cnode and put dest info in it
377 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
381 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
382 //\80\80 Asm source copies from the saved dest catalog node
383 CopyCatalogNodeInfo( &destData
, &srcData
);
385 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSCatalogFile
), &srcHint
);
386 ReturnIfError( err
);
389 // find the destination cnode and put source info in it
390 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
394 CopyCatalogNodeInfo( &swapData
, &destData
);
395 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSCatalogFile
), &destHint
);
396 ReturnIfError( err
);
401 //-- Step 4: Error Handling section
405 err
= FlushCatalog( vcb
); // flush the catalog
406 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
411 static void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
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
) );
422 static void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
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
;
430 static OSErr
MoveExtents( ExtendedVCB
*vcb
, u_int32_t srcFileID
, u_int32_t destFileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
433 ExtentsRecBuffer extentsBuffer
[kNumExtentsToCache
];
434 ExtentKey
* extentKeyPtr
;
435 ExtentRecord extentData
;
436 struct BTreeIterator
*btIterator
= NULL
;
437 struct BTreeIterator
*tmpIterator
= NULL
;
438 FSBufferDescriptor btRecord
;
440 u_int16_t btRecordSize
;
444 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
445 if (btIterator
== NULL
) {
446 return memFullErr
; // translates to ENOMEM
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
456 bzero(btIterator
, sizeof(*btIterator
));
457 bzero (tmpIterator
, sizeof(*tmpIterator
));
460 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
462 (void) BTInvalidateHint(btIterator
);
463 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
464 btRecord
.bufferAddress
= &extentData
;
465 btRecord
.itemCount
= 1;
467 //-- Collect the extent records
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
476 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
479 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
480 btKeySize
= sizeof(HFSPlusExtentKey
);
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;
489 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
490 btKeySize
= sizeof(HFSExtentKey
);
492 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
493 extentKeyPtr
->hfs
.forkType
= 0;
494 extentKeyPtr
->hfs
.fileID
= srcFileID
;
495 extentKeyPtr
->hfs
.startBlock
= 0;
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.
505 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
506 // set to destFileID.
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).
514 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
516 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
517 if (err
!= btNotFound
)
520 DebugStr("Unexpected error from SearchBTreeRecord");
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.
525 FREE (tmpIterator
, M_TEMP
);
526 FREE (btIterator
, M_TEMP
);
532 btRecord
.bufferAddress
= &extentData
;
533 btRecord
.itemCount
= 1;
535 for ( i
=0 ; i
<kNumExtentsToCache
; i
++ )
537 HFSCatalogNodeID foundFileID
;
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
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
) {
555 CopyExtentInfo(extentKeyPtr
, &extentData
, extentsBuffer
, i
);
558 /* The fileID's are of a different file. We're done here. */
565 //-- edit each extent key, and reinsert each extent record in the extent file
567 btRecordSize
= sizeof(HFSPlusExtentRecord
);
569 btRecordSize
= sizeof(HFSExtentRecord
);
571 for ( j
=0 ; j
<i
; j
++ )
575 extentsBuffer
[j
].extentKey
.hfsPlus
.fileID
= destFileID
; // change only the id in the key to dest ID
577 extentsBuffer
[j
].extentKey
.hfs
.fileID
= destFileID
; // change only the id in the key to dest ID
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
);
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
)
592 DebugStr("Can't insert record -- already exists");
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
605 if ( i
!= kNumExtentsToCache
) // if the buffer is not full, we must be done
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!
614 FREE (tmpIterator
, M_TEMP
);
615 FREE (btIterator
, M_TEMP
);
621 static void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, u_int16_t bufferCount
)
623 BlockMoveData( key
, &(buffer
[bufferCount
].extentKey
), sizeof( ExtentKey
) );
624 BlockMoveData( data
, &(buffer
[bufferCount
].extentData
), sizeof( ExtentRecord
) );
628 //-- Delete all extents in extent file that have the ID given.
629 static OSErr
DeleteExtents( ExtendedVCB
*vcb
, u_int32_t fileID
, int quitEarly
, u_int8_t forkType
, Boolean isHFSPlus
)
632 ExtentKey
* extentKeyPtr
;
633 ExtentRecord extentData
;
634 struct BTreeIterator
*btIterator
= NULL
;
635 struct BTreeIterator
*tmpIterator
= NULL
;
636 FSBufferDescriptor btRecord
;
637 u_int16_t btRecordSize
;
641 MALLOC (btIterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
);
642 if (btIterator
== NULL
) {
643 return memFullErr
; // translates to ENOMEM
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
652 bzero(btIterator
, sizeof(*btIterator
));
653 bzero (tmpIterator
, sizeof(*tmpIterator
));
655 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
657 (void) BTInvalidateHint(btIterator
);
658 extentKeyPtr
= (ExtentKey
*) &btIterator
->key
;
659 btRecord
.bufferAddress
= &extentData
;
660 btRecord
.itemCount
= 1;
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,
667 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
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;
676 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
678 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
679 extentKeyPtr
->hfs
.forkType
= forkType
;
680 extentKeyPtr
->hfs
.fileID
= fileID
;
681 extentKeyPtr
->hfs
.startBlock
= 0;
684 err
= BTSearchRecord(fcb
, btIterator
, &btRecord
, &btRecordSize
, btIterator
);
685 if ( err
!= btNotFound
)
687 if (err
== noErr
) { // Did we find a bogus extent record?
688 err
= cmBadNews
; // Yes, so indicate things are messed up.
691 return err
; // Got some unexpected error, so return it
696 HFSCatalogNodeID foundFileID
;
698 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, btIterator
, &btRecord
, &btRecordSize
);
701 if (err
== btNotFound
) // If we hit the end of the BTree
702 err
= noErr
; // then it's OK
704 break; // We're done now.
707 foundFileID
= isHFSPlus
? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
708 if ( foundFileID
!= fileID
) {
709 break; // numbers don't match, we must be done
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
) {
718 *tmpIterator
= *btIterator
;
719 err
= BTDeleteRecord( fcb
, tmpIterator
);
724 FREE (tmpIterator
, M_TEMP
);
725 FREE (btIterator
, M_TEMP
);
731 // Check if there are extents represented in the extents overflow file.
732 static u_int32_t
CheckExtents( void *extents
, u_int32_t totalBlocks
, Boolean isHFSPlus
)
734 u_int32_t extentAllocationBlocks
;
738 if ( totalBlocks
== 0 )
741 extentAllocationBlocks
= 0;
745 for ( i
= 0 ; i
< kHFSPlusExtentDensity
; i
++ )
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)
754 for ( i
= 0 ; i
< kHFSExtentDensity
; i
++ )
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)
762 return( extentAllocationBlocks
);