2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 File: FileIDServices.c
25 Contains: File ID manipulating routines.
29 Written by: Deric Horn
31 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: xxx put other contact here xxx
39 Technology: xxx put technology here xxx
48 Change History (most recent first):
49 <MacOSX> 3/2/98 djb Fix extents corruption bug in MoveExtents (radar #2309434).
50 <MacOSX> 11/20/98 djb Add support for UTF-8 names.
51 <MacOSX> 4/2/98 djb Switch over to real BTree interface in MoveExtents and DeleteExtents.
52 <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file.
54 <CS21> 11/17/97 djb PrepareInputName routine now returns an error.
55 <CS20> 11/13/97 djb Radar #2001699 ResolveFileID needs to use CMNotFound error.
56 <CS19> 10/31/97 JL #2000184 - CreateFileThreadID and ExchangeFiles now return the
57 WDCBRecPtr or NULL for external file systems. ExchangeFiles no
58 longer returns length of FCB table to caller since that wasn't
60 <18> 10/23/97 DSH 1685058, Fix ExchangeFiles by invalidating the node cache before
62 <CS17> 10/19/97 msd Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
63 <CS16> 10/16/97 DSH Return badFidErr in ResolveFileID if LocateCatalogThread fails
64 <CS15> 10/15/97 DSH CreateFileThreadID(), remap btExists to fidExists.
65 <CS14> 9/7/97 djb Turn off some DebugStr calls.
66 <CS13> 9/4/97 msd Remove call to PropertyExchangeObjects.
67 <CS12> 8/14/97 djb Remove hard link support.
68 <CS11> 7/18/97 msd Include LowMemPriv.h.
69 <CS10> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
71 <CS9> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
72 <CS8> 6/24/97 djb Add hard link support to ResolveFileID and CreateFileIDRef.
73 <CS7> 6/20/97 msd Use contentModDate and attributeModDate fields instead of
75 <CS6> 6/13/97 djb Switch over from PrepareOutputName to ConvertUnicodeToHFSName.
76 PrepareInputName now takes an encoding.
77 <CS5> 5/28/97 msd Move the declaration of FindFileName to FilesInternal.i.
78 <CS4> 5/19/97 djb No longer need to invalidate vcbDirIDM field.
79 <CS3> 5/16/97 msd In ExchangeFiles, change srcNamePtr from char * to StringPtr
81 <CS2> 4/28/97 djb (DSH) Added VolumeWritable check back into CreateFileIDThread.
82 <CS1> 4/24/97 djb first checked in
83 <HFS23> 4/11/97 DSH Use extended VCB fields catalogRefNum, and extentsRefNum.
84 <HFS22> 4/9/97 msd Rewrite CreateFileThreadID so that it properly handles
85 pathnames, and doesn't overwrite the ioNamePtr. The data field
86 of FindFileNameGlueRec points to a CatalogNodeData, not
88 <HFS21> 4/4/97 djb Get in sync with volume format changes.
89 <HFS20> 3/31/97 djb Change ClearMem to ClearMemory.
90 <HFS19> 3/17/97 DSH C_FlushCache prototype to FilesInternal.h
91 <HFS18> 3/5/97 msd ExchangeFiles needs to call PropertyExchangeObjects.
92 <HFS17> 2/13/97 msd Fix MoveExtents and DeleteExtents to work with HFS+ extent
94 <HFS16> 1/31/97 msd In MoveExtents, when a record isn't found and you want the next
95 record in order, use the "next record" offset = 1 instead of
96 "current record" offset = 0. DeleteExtents would always exit
97 without doing anything because it was searching for an invalid
98 key. Removed several DebugStrs that were used as cheap code
100 <HFS15> 1/15/97 DSH Resolve wasn't passing the name back for HFS
101 <HFS14> 1/13/97 djb LocateCatalogThread now passes back the thread record size.
102 <HFS13> 1/11/97 DSH HFS+, fixed some Unicode/Pascal strings related bugs for use on
104 <HFS12> 1/9/97 DSH Fix ExchangeFiles extents
105 <HFS11> 1/6/97 DSH pass VCB in CloseFile() routine.
106 <HFS10> 1/6/97 djb Fixed ResolveFileID - it was not returning a directory ID!
107 <HFS9> 1/3/97 msd Fix prototype for C_FlushCache. Fix prototype for
109 <HFS8> 1/3/97 djb Integrate latest HFSVolumesPriv.h changes.
110 <HFS7> 1/2/97 DSH C port of ExchangeFileIDs
111 <HFS6> 12/20/96 djb Fixed bug in CreateFileID.
112 <HFS5> 12/19/96 DSH All refs to VCB are now refs to ExtendedVCB
113 <HFS4> 12/19/96 msd Use kFileThreadExistsMask (from HFSVolumesPriv.h) instead of
114 kFileThreadMask (from FilesInternal.h) since the latter was
115 incorrectly defined and has now been removed.
116 <HFS3> 12/19/96 djb Updated for new B-tree Manager interface.
117 <HFS2> 12/18/96 msd GetFileThreadID was using a bitwise-OR (|) instead of
118 bitwise-AND (&) to test for a bit being set.
119 <HFS1> 12/12/96 DSH first checked in
123 #include "../../hfs_macos_defs.h"
124 #include "../../hfs_format.h"
126 #include "../headers/FileMgrInternal.h"
127 #include "../headers/HFSUnicodeWrappers.h"
128 #include "../headers/CatalogPrivate.h"
131 struct ExtentsRecBuffer
{
133 ExtentRecord extentData
;
135 typedef struct ExtentsRecBuffer ExtentsRecBuffer
;
138 OSErr
CreateFileID( ExtendedVCB
*vcb
, HFSCatalogNodeID fileID
, CatalogName
*name
, HFSCatalogNodeID
*threadID
);
139 OSErr
GetFileThreadID( ExtendedVCB
*vcb
, HFSCatalogNodeID id
, const CatalogName
*name
, Boolean isHFSPlus
, UInt32
*threadID
);
141 UInt32
CheckExtents( void *extents
, UInt32 blocks
, Boolean isHFSPlus
);
142 OSErr
DeleteExtents( ExtendedVCB
*vcb
, UInt32 fileNumber
, Boolean isHFSPlus
);
143 OSErr
MoveExtents( ExtendedVCB
*vcb
, UInt32 srcFileID
, UInt32 destFileID
, Boolean isHFSPlus
);
144 void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
145 void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
);
147 void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, UInt16 bufferCount
);
148 extern void TrashFileBlocks( ExtendedVCB
*vcb
, UInt32 fileNumber
);
152 OSErr
ExchangeFileIDs( ExtendedVCB
*vcb
, ConstUTF8Param srcName
, ConstUTF8Param destName
, HFSCatalogNodeID srcID
, HFSCatalogNodeID destID
, UInt32 srcHint
, UInt32 destHint
)
154 CatalogKey srcKey
; // 518 bytes
155 CatalogRecord srcData
; // 520 bytes
156 CatalogKey destKey
; // 518 bytes
157 CatalogRecord destData
; // 520 bytes
158 CatalogRecord swapData
; // 520 bytes
159 SInt16 numSrcExtentBlocks
;
160 SInt16 numDestExtentBlocks
;
162 Boolean isHFSPlus
= ( vcb
->vcbSigWord
== kHFSPlusSigWord
);
164 TrashCatalogIterator(vcb
, srcID
); // invalidate any iterators for this parentID
165 TrashCatalogIterator(vcb
, destID
); // invalidate any iterators for this parentID
167 err
= BuildCatalogKeyUTF8(vcb
, srcID
, srcName
, kUndefinedStrLen
, &srcKey
, NULL
);
170 err
= BuildCatalogKeyUTF8(vcb
, destID
, destName
, kUndefinedStrLen
, &destKey
, NULL
);
175 //-- Step 1: Check the catalog nodes for extents
177 //-- locate the source file, test for extents in extent file, and copy the cat record for later
178 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
179 ReturnIfError( err
);
181 if ( srcData
.recordType
!= kHFSPlusFileRecord
)
182 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
184 //-- Check if there are any extents in the source file
185 //\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
186 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.dataFork
.extents
, srcData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
187 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
188 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.resourceFork
.extents
, srcData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
190 //-- Check if there are any extents in the destination file
191 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
192 ReturnIfError( err
);
194 if ( destData
.recordType
!= kHFSPlusFileRecord
)
195 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
197 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.dataFork
.extents
, destData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
198 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
199 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.resourceFork
.extents
, destData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
201 //-- Step 2: Exchange the Extent key in the extent file
203 //-- Exchange the extents key in the extent file
204 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
205 ReturnIfError( err
);
207 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
209 //-- Change the source extents file ids to our known bogus value
210 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, kHFSBogusExtentFileID
, isHFSPlus
);
213 if ( err
!= dskFulErr
)
219 //-- Change the destination extents file id's to the source id's
220 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
223 if ( err
!= dskFulErr
)
226 ExUndo2aPlus
: err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
227 ReturnIfError( err
); // we are doomed. Just QUIT!
229 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
); // Move the extents back
230 ReturnIfError( err
); // we are doomed. Just QUIT!
235 //-- Change the bogus extents file id's to the dest id's
236 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
239 if ( err
!= dskFulErr
)
242 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
243 ReturnIfError( err
); // we are doomed. Just QUIT!
245 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
); // Move the extents back
246 ReturnIfError( err
); // we are doomed. Just QUIT!
252 else if ( numSrcExtentBlocks
) // just the source file has extents
254 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
257 if ( err
!= dskFulErr
)
260 err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
261 ReturnIfError( err
); // we are doomed. Just QUIT!
266 else if ( numDestExtentBlocks
) // just the destination file has extents
268 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
271 if ( err
!= dskFulErr
)
274 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
275 ReturnIfError( err
); // we are doomed. Just QUIT!
281 //-- Step 3: Change the data in the catalog nodes
283 //-- find the source cnode and put dest info in it
284 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
288 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
289 CopyBigCatalogNodeInfo( &destData
, &srcData
);
291 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSPlusCatalogFile
), &srcHint
);
292 ReturnIfError( err
);
294 // find the destination cnode and put source info in it
295 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
299 CopyBigCatalogNodeInfo( &swapData
, &destData
);
300 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSPlusCatalogFile
), &destHint
);
301 ReturnIfError( err
);
305 //-- Step 1: Check the catalog nodes for extents
307 //-- locate the source file, test for extents in extent file, and copy the cat record for later
308 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
309 ReturnIfError( err
);
311 if ( srcData
.recordType
!= kHFSFileRecord
)
312 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
314 //-- Check if there are any extents in the source file
315 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.dataExtents
, srcData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
316 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
317 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.rsrcExtents
, srcData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
320 //\80\80 Do we save the found source node for later use?
323 //-- Check if there are any extents in the destination file
324 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
325 ReturnIfError( err
);
327 if ( destData
.recordType
!= kHFSFileRecord
)
328 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
330 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.dataExtents
, destData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
331 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
332 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.rsrcExtents
, destData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
334 //\80\80 Do we save the found destination node for later use?
337 //-- Step 2: Exchange the Extent key in the extent file
339 //-- Exchange the extents key in the extent file
340 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
341 ReturnIfError( err
);
343 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
345 //-- Change the source extents file ids to our known bogus value
346 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, kHFSBogusExtentFileID
, isHFSPlus
);
349 if ( err
!= dskFulErr
)
352 ExUndo1a
: err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
353 ReturnIfError( err
); // we are doomed. Just QUIT!
355 err
= FlushCatalog( vcb
); // flush the catalog
356 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
360 //-- Change the destination extents file id's to the source id's
361 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, isHFSPlus
);
364 if ( err
!= dskFulErr
)
367 ExUndo2a
: err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, isHFSPlus
);
368 ReturnIfError( err
); // we are doomed. Just QUIT!
370 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsFile
.fileID
, isHFSPlus
); // Move the extents back
371 ReturnIfError( err
); // we are doomed. Just QUIT!
376 //-- Change the bogus extents file id's to the dest id's
377 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsFile
.fileID
, isHFSPlus
);
380 if ( err
!= dskFulErr
)
383 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, isHFSPlus
);
384 ReturnIfError( err
); // we are doomed. Just QUIT!
386 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, isHFSPlus
); // Move the extents back
387 ReturnIfError( err
); // we are doomed. Just QUIT!
393 else if ( numSrcExtentBlocks
) // just the source file has extents
395 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, isHFSPlus
);
398 if ( err
!= dskFulErr
)
401 err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, isHFSPlus
);
402 ReturnIfError( err
); // we are doomed. Just QUIT!
407 else if ( numDestExtentBlocks
) // just the destination file has extents
409 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, isHFSPlus
);
412 if ( err
!= dskFulErr
)
415 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, isHFSPlus
);
416 ReturnIfError( err
); // we are doomed. Just QUIT!
422 //-- Step 3: Change the data in the catalog nodes
424 //-- find the source cnode and put dest info in it
425 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
429 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
430 //\80\80 Asm source copies from the saved dest catalog node
431 CopyCatalogNodeInfo( &destData
, &srcData
);
433 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSCatalogFile
), &srcHint
);
434 ReturnIfError( err
);
437 // find the destination cnode and put source info in it
438 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
442 CopyCatalogNodeInfo( &swapData
, &destData
);
443 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSCatalogFile
), &destHint
);
444 ReturnIfError( err
);
449 //-- Step 4: Error Handling section
453 err
= FlushCatalog( vcb
); // flush the catalog
454 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
459 void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
461 // dest->hfsFile.filStBlk = src->hfsFile.filStBlk;
462 dest
->hfsFile
.dataLogicalSize
= src
->hfsFile
.dataLogicalSize
;
463 dest
->hfsFile
.dataPhysicalSize
= src
->hfsFile
.dataPhysicalSize
;
464 // dest->hfsFile.filRStBlk = src->hfsFile.filRStBlk;
465 dest
->hfsFile
.rsrcLogicalSize
= src
->hfsFile
.rsrcLogicalSize
;
466 dest
->hfsFile
.rsrcPhysicalSize
= src
->hfsFile
.rsrcPhysicalSize
;
467 dest
->hfsFile
.modifyDate
= src
->hfsFile
.modifyDate
;
468 BlockMoveData( src
->hfsFile
.dataExtents
, dest
->hfsFile
.dataExtents
, sizeof(HFSExtentRecord
) );
469 BlockMoveData( src
->hfsFile
.rsrcExtents
, dest
->hfsFile
.rsrcExtents
, sizeof(HFSExtentRecord
) );
472 void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
474 BlockMoveData( &src
->hfsPlusFile
.dataFork
, &dest
->hfsPlusFile
.dataFork
, sizeof(HFSPlusForkData
) );
475 BlockMoveData( &src
->hfsPlusFile
.resourceFork
, &dest
->hfsPlusFile
.resourceFork
, sizeof(HFSPlusForkData
) );
476 dest
->hfsPlusFile
.contentModDate
= src
->hfsPlusFile
.contentModDate
;
480 OSErr
MoveExtents( ExtendedVCB
*vcb
, UInt32 srcFileID
, UInt32 destFileID
, Boolean isHFSPlus
)
483 ExtentsRecBuffer extentsBuffer
[kNumExtentsToCache
];
484 ExtentKey
* extentKeyPtr
;
485 ExtentRecord extentData
;
486 BTreeIterator btIterator
;
487 FSBufferDescriptor btRecord
;
494 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
496 (void) BTInvalidateHint(&btIterator
);
497 extentKeyPtr
= (ExtentKey
*) &btIterator
.key
;
498 btRecord
.bufferAddress
= &extentData
;
499 btRecord
.itemCount
= 1;
501 //-- Collect the extent records
504 // A search on the following key will cause the BTree to be positioned immediately
505 // before the first extent record for file #srcFileID, but not actually positioned
506 // on any record. This is because there cannot be an extent record with FABN = 0
507 // (the first extent of the fork, which would be in the catalog entry, not an extent
510 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
513 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
514 btKeySize
= sizeof(HFSPlusExtentKey
);
516 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
517 extentKeyPtr
->hfsPlus
.forkType
= 0;
518 extentKeyPtr
->hfsPlus
.pad
= 0;
519 extentKeyPtr
->hfsPlus
.fileID
= srcFileID
;
520 extentKeyPtr
->hfsPlus
.startBlock
= 0;
523 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
524 btKeySize
= sizeof(HFSExtentKey
);
526 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
527 extentKeyPtr
->hfs
.forkType
= 0;
528 extentKeyPtr
->hfs
.fileID
= srcFileID
;
529 extentKeyPtr
->hfs
.startBlock
= 0;
533 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent
534 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found
535 // records, but with destFileID as the file number in the key. Keep doing this sequence of
536 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
537 // no more extent records in the tree.
539 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
540 // set to destFileID.
542 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it
543 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series
544 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record
545 // we found, so that BTIterateRecord would get the next one (the first we haven't processed).
548 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
550 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
551 if (err
!= btNotFound
)
554 DebugStr("\pUnexpected error from SearchBTreeRecord");
556 if (err
== noErr
) // If we found such a bogus extent record, then the tree is really messed up
557 err
= cmBadNews
; // so return an error that conveys the disk is hosed.
564 btRecord
.bufferAddress
= &extentData
;
565 btRecord
.itemCount
= 1;
567 for ( i
=0 ; i
<kNumExtentsToCache
; i
++ )
569 HFSCatalogNodeID foundFileID
;
571 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &btIterator
, &btRecord
, &btRecordSize
);
572 if ( err
== btNotFound
) // Did we run out of extent records in the extents tree?
573 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus!
574 else if ( err
!= noErr
)
575 return( err
); // must be ioError
577 foundFileID
= isHFSPlus
? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
578 if ( foundFileID
== srcFileID
)
580 CopyExtentInfo(extentKeyPtr
, &extentData
, extentsBuffer
, i
);
588 //-- edit each extent key, and reinsert each extent record in the extent file
590 btRecordSize
= sizeof(HFSPlusExtentRecord
);
592 btRecordSize
= sizeof(HFSExtentRecord
);
594 for ( j
=0 ; j
<i
; j
++ )
596 BTreeIterator tmpIterator
;
599 extentsBuffer
[j
].extentKey
.hfsPlus
.fileID
= destFileID
; // change only the id in the key to dest ID
601 extentsBuffer
[j
].extentKey
.hfs
.fileID
= destFileID
; // change only the id in the key to dest ID
603 // get iterator and buffer descriptor ready...
604 (void) BTInvalidateHint(&tmpIterator
);
605 BlockMoveData(&(extentsBuffer
[j
].extentKey
), &tmpIterator
.key
, btKeySize
);
606 btRecord
.bufferAddress
= &(extentsBuffer
[j
].extentData
);
608 err
= BTInsertRecord(fcb
, &tmpIterator
, &btRecord
, btRecordSize
);
611 if ( err
== btExists
)
614 DebugStr("\pCan't insert record -- already exists");
622 //-- okay, done with this buffered batch, go get the next set of extent records
623 // If our buffer is not full, we must be done, or recieved an error
625 if ( i
!= kNumExtentsToCache
) // if the buffer is not full, we must be done
627 err
= DeleteExtents( vcb
, srcFileID
, isHFSPlus
); // Now delete all the extent entries with the sourceID
628 if ( DEBUG_BUILD
&& err
!= noErr
)
629 DebugStr("\pError from DeleteExtents");
630 break; // we're done!
638 void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, UInt16 bufferCount
)
640 BlockMoveData( key
, &(buffer
[bufferCount
].extentKey
), sizeof( ExtentKey
) );
641 BlockMoveData( data
, &(buffer
[bufferCount
].extentData
), sizeof( ExtentRecord
) );
645 //-- Delete all extents in extent file that have the ID given.
646 OSErr
DeleteExtents( ExtendedVCB
*vcb
, UInt32 fileID
, Boolean isHFSPlus
)
649 ExtentKey
* extentKeyPtr
;
650 ExtentRecord extentData
;
651 BTreeIterator btIterator
;
652 FSBufferDescriptor btRecord
;
656 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
658 (void) BTInvalidateHint(&btIterator
);
659 extentKeyPtr
= (ExtentKey
*) &btIterator
.key
;
660 btRecord
.bufferAddress
= &extentData
;
661 btRecord
.itemCount
= 1;
663 // The algorithm is to position the BTree just before any extent records for fileID.
664 // Then just keep getting successive records. If the record is still for fileID,
668 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
670 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
671 extentKeyPtr
->hfsPlus
.forkType
= 0;
672 extentKeyPtr
->hfsPlus
.pad
= 0;
673 extentKeyPtr
->hfsPlus
.fileID
= fileID
;
674 extentKeyPtr
->hfsPlus
.startBlock
= 0;
677 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
679 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
680 extentKeyPtr
->hfs
.forkType
= 0;
681 extentKeyPtr
->hfs
.fileID
= fileID
;
682 extentKeyPtr
->hfs
.startBlock
= 0;
685 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
686 if ( err
!= btNotFound
)
688 if (err
== noErr
) { // Did we find a bogus extent record?
689 err
= cmBadNews
; // Yes, so indicate things are messed up.
692 return err
; // Got some unexpected error, so return it
697 BTreeIterator tmpIterator
;
698 HFSCatalogNodeID foundFileID
;
700 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &btIterator
, &btRecord
, &btRecordSize
);
703 if (err
== btNotFound
) // If we hit the end of the BTree
704 err
= noErr
; // then it's OK
706 break; // We're done now.
709 foundFileID
= isHFSPlus
? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
710 if ( foundFileID
!= fileID
)
711 break; // numbers don't match, we must be done
713 tmpIterator
= btIterator
;
714 err
= BTDeleteRecord( fcb
, &tmpIterator
);
723 // Check if there are extents represented in the extents overflow file.
724 UInt32
CheckExtents( void *extents
, UInt32 totalBlocks
, Boolean isHFSPlus
)
726 UInt32 extentAllocationBlocks
;
730 if ( totalBlocks
== 0 )
733 extentAllocationBlocks
= 0;
737 for ( i
= 0 ; i
< kHFSPlusExtentDensity
; i
++ )
739 extentAllocationBlocks
+= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
740 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
746 for ( i
= 0 ; i
< kHFSExtentDensity
; i
++ )
748 extentAllocationBlocks
+= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
749 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
754 return( extentAllocationBlocks
);