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
;
163 Boolean isHFSPlus
= ( vcb
->vcbSigWord
== kHFSPlusSigWord
);
165 TrashCatalogIterator(vcb
, srcID
); // invalidate any iterators for this parentID
166 TrashCatalogIterator(vcb
, destID
); // invalidate any iterators for this parentID
168 err
= BuildCatalogKeyUTF8(vcb
, srcID
, srcName
, kUndefinedStrLen
, &srcKey
, &textEncoding
);
171 err
= BuildCatalogKeyUTF8(vcb
, destID
, destName
, kUndefinedStrLen
, &destKey
, &textEncoding
);
176 //-- Step 1: Check the catalog nodes for extents
178 //-- locate the source file, test for extents in extent file, and copy the cat record for later
179 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
180 ReturnIfError( err
);
182 if ( srcData
.recordType
!= kHFSPlusFileRecord
)
183 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
185 //-- Check if there are any extents in the source file
186 //\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
187 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.dataFork
.extents
, srcData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
188 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
189 numSrcExtentBlocks
= CheckExtents( srcData
.hfsPlusFile
.resourceFork
.extents
, srcData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
191 //-- Check if there are any extents in the destination file
192 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
193 ReturnIfError( err
);
195 if ( destData
.recordType
!= kHFSPlusFileRecord
)
196 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
198 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.dataFork
.extents
, destData
.hfsPlusFile
.dataFork
.totalBlocks
, isHFSPlus
);
199 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
200 numDestExtentBlocks
= CheckExtents( destData
.hfsPlusFile
.resourceFork
.extents
, destData
.hfsPlusFile
.resourceFork
.totalBlocks
, isHFSPlus
);
202 //-- Step 2: Exchange the Extent key in the extent file
204 //-- Exchange the extents key in the extent file
205 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
206 ReturnIfError( err
);
208 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
210 //-- Change the source extents file ids to our known bogus value
211 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, kHFSBogusExtentFileID
, isHFSPlus
);
214 if ( err
!= dskFulErr
)
220 //-- Change the destination extents file id's to the source id's
221 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
224 if ( err
!= dskFulErr
)
227 ExUndo2aPlus
: err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
228 ReturnIfError( err
); // we are doomed. Just QUIT!
230 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
); // Move the extents back
231 ReturnIfError( err
); // we are doomed. Just QUIT!
236 //-- Change the bogus extents file id's to the dest id's
237 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
240 if ( err
!= dskFulErr
)
243 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
244 ReturnIfError( err
); // we are doomed. Just QUIT!
246 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
); // Move the extents back
247 ReturnIfError( err
); // we are doomed. Just QUIT!
253 else if ( numSrcExtentBlocks
) // just the source file has extents
255 err
= MoveExtents( vcb
, srcData
.hfsPlusFile
.fileID
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
258 if ( err
!= dskFulErr
)
261 err
= DeleteExtents( vcb
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
262 ReturnIfError( err
); // we are doomed. Just QUIT!
267 else if ( numDestExtentBlocks
) // just the destination file has extents
269 err
= MoveExtents( vcb
, destData
.hfsPlusFile
.fileID
, srcData
.hfsPlusFile
.fileID
, isHFSPlus
);
272 if ( err
!= dskFulErr
)
275 err
= DeleteExtents( vcb
, destData
.hfsPlusFile
.fileID
, isHFSPlus
);
276 ReturnIfError( err
); // we are doomed. Just QUIT!
282 //-- Step 3: Change the data in the catalog nodes
284 //-- find the source cnode and put dest info in it
285 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
289 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
290 CopyBigCatalogNodeInfo( &destData
, &srcData
);
292 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSPlusCatalogFile
), &srcHint
);
293 ReturnIfError( err
);
295 // find the destination cnode and put source info in it
296 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
300 CopyBigCatalogNodeInfo( &swapData
, &destData
);
301 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSPlusCatalogFile
), &destHint
);
302 ReturnIfError( err
);
306 //-- Step 1: Check the catalog nodes for extents
308 //-- locate the source file, test for extents in extent file, and copy the cat record for later
309 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
310 ReturnIfError( err
);
312 if ( srcData
.recordType
!= kHFSFileRecord
)
313 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
315 //-- Check if there are any extents in the source file
316 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.dataExtents
, srcData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
317 if ( numSrcExtentBlocks
== 0 ) // then check the resource fork extents
318 numSrcExtentBlocks
= CheckExtents( srcData
.hfsFile
.rsrcExtents
, srcData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
321 //\80\80 Do we save the found source node for later use?
324 //-- Check if there are any extents in the destination file
325 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
326 ReturnIfError( err
);
328 if ( destData
.recordType
!= kHFSFileRecord
)
329 return( cmFThdDirErr
); // Error "cmFThdDirErr = it is a directory"
331 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.dataExtents
, destData
.hfsFile
.dataPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
332 if ( numDestExtentBlocks
== 0 ) // then check the resource fork extents
333 numDestExtentBlocks
= CheckExtents( destData
.hfsFile
.rsrcExtents
, destData
.hfsFile
.rsrcPhysicalSize
/ vcb
->blockSize
, isHFSPlus
);
335 //\80\80 Do we save the found destination node for later use?
338 //-- Step 2: Exchange the Extent key in the extent file
340 //-- Exchange the extents key in the extent file
341 err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
342 ReturnIfError( err
);
344 if ( numSrcExtentBlocks
&& numDestExtentBlocks
) // if both files have extents
346 //-- Change the source extents file ids to our known bogus value
347 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, kHFSBogusExtentFileID
, isHFSPlus
);
350 if ( err
!= dskFulErr
)
353 ExUndo1a
: err
= DeleteExtents( vcb
, kHFSBogusExtentFileID
, isHFSPlus
);
354 ReturnIfError( err
); // we are doomed. Just QUIT!
356 err
= FlushCatalog( vcb
); // flush the catalog
357 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
361 //-- Change the destination extents file id's to the source id's
362 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, isHFSPlus
);
365 if ( err
!= dskFulErr
)
368 ExUndo2a
: err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, isHFSPlus
);
369 ReturnIfError( err
); // we are doomed. Just QUIT!
371 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, srcData
.hfsFile
.fileID
, isHFSPlus
); // Move the extents back
372 ReturnIfError( err
); // we are doomed. Just QUIT!
377 //-- Change the bogus extents file id's to the dest id's
378 err
= MoveExtents( vcb
, kHFSBogusExtentFileID
, destData
.hfsFile
.fileID
, isHFSPlus
);
381 if ( err
!= dskFulErr
)
384 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, isHFSPlus
);
385 ReturnIfError( err
); // we are doomed. Just QUIT!
387 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, isHFSPlus
); // Move the extents back
388 ReturnIfError( err
); // we are doomed. Just QUIT!
394 else if ( numSrcExtentBlocks
) // just the source file has extents
396 err
= MoveExtents( vcb
, srcData
.hfsFile
.fileID
, destData
.hfsFile
.fileID
, isHFSPlus
);
399 if ( err
!= dskFulErr
)
402 err
= DeleteExtents( vcb
, srcData
.hfsFile
.fileID
, isHFSPlus
);
403 ReturnIfError( err
); // we are doomed. Just QUIT!
408 else if ( numDestExtentBlocks
) // just the destination file has extents
410 err
= MoveExtents( vcb
, destData
.hfsFile
.fileID
, srcData
.hfsFile
.fileID
, isHFSPlus
);
413 if ( err
!= dskFulErr
)
416 err
= DeleteExtents( vcb
, destData
.hfsFile
.fileID
, isHFSPlus
);
417 ReturnIfError( err
); // we are doomed. Just QUIT!
423 //-- Step 3: Change the data in the catalog nodes
425 //-- find the source cnode and put dest info in it
426 err
= LocateCatalogNodeByKey( vcb
, srcHint
, &srcKey
, &srcData
, &srcHint
);
430 BlockMoveData( &srcData
, &swapData
, sizeof(CatalogRecord
) );
431 //\80\80 Asm source copies from the saved dest catalog node
432 CopyCatalogNodeInfo( &destData
, &srcData
);
434 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &srcKey
, srcHint
, &srcData
, sizeof(HFSCatalogFile
), &srcHint
);
435 ReturnIfError( err
);
438 // find the destination cnode and put source info in it
439 err
= LocateCatalogNodeByKey( vcb
, destHint
, &destKey
, &destData
, &destHint
);
443 CopyCatalogNodeInfo( &swapData
, &destData
);
444 err
= ReplaceBTreeRecord( vcb
->catalogRefNum
, &destKey
, destHint
, &destData
, sizeof(HFSCatalogFile
), &destHint
);
445 ReturnIfError( err
);
450 //-- Step 4: Error Handling section
454 err
= FlushCatalog( vcb
); // flush the catalog
455 err
= FlushExtentFile( vcb
); // flush the extent file (unneeded for common case, but it's cheap)
460 void CopyCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
462 // dest->hfsFile.filStBlk = src->hfsFile.filStBlk;
463 dest
->hfsFile
.dataLogicalSize
= src
->hfsFile
.dataLogicalSize
;
464 dest
->hfsFile
.dataPhysicalSize
= src
->hfsFile
.dataPhysicalSize
;
465 // dest->hfsFile.filRStBlk = src->hfsFile.filRStBlk;
466 dest
->hfsFile
.rsrcLogicalSize
= src
->hfsFile
.rsrcLogicalSize
;
467 dest
->hfsFile
.rsrcPhysicalSize
= src
->hfsFile
.rsrcPhysicalSize
;
468 dest
->hfsFile
.modifyDate
= src
->hfsFile
.modifyDate
;
469 BlockMoveData( src
->hfsFile
.dataExtents
, dest
->hfsFile
.dataExtents
, sizeof(HFSExtentRecord
) );
470 BlockMoveData( src
->hfsFile
.rsrcExtents
, dest
->hfsFile
.rsrcExtents
, sizeof(HFSExtentRecord
) );
473 void CopyBigCatalogNodeInfo( CatalogRecord
*src
, CatalogRecord
*dest
)
475 BlockMoveData( &src
->hfsPlusFile
.dataFork
, &dest
->hfsPlusFile
.dataFork
, sizeof(HFSPlusForkData
) );
476 BlockMoveData( &src
->hfsPlusFile
.resourceFork
, &dest
->hfsPlusFile
.resourceFork
, sizeof(HFSPlusForkData
) );
477 dest
->hfsPlusFile
.contentModDate
= src
->hfsPlusFile
.contentModDate
;
481 OSErr
MoveExtents( ExtendedVCB
*vcb
, UInt32 srcFileID
, UInt32 destFileID
, Boolean isHFSPlus
)
484 ExtentsRecBuffer extentsBuffer
[kNumExtentsToCache
];
485 ExtentKey
* extentKeyPtr
;
486 ExtentRecord extentData
;
487 BTreeIterator btIterator
;
488 FSBufferDescriptor btRecord
;
495 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
497 (void) BTInvalidateHint(&btIterator
);
498 extentKeyPtr
= (ExtentKey
*) &btIterator
.key
;
499 btRecord
.bufferAddress
= &extentData
;
500 btRecord
.itemCount
= 1;
502 //-- Collect the extent records
505 // A search on the following key will cause the BTree to be positioned immediately
506 // before the first extent record for file #srcFileID, but not actually positioned
507 // on any record. This is because there cannot be an extent record with FABN = 0
508 // (the first extent of the fork, which would be in the catalog entry, not an extent
511 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
514 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
515 btKeySize
= sizeof(HFSPlusExtentKey
);
517 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
518 extentKeyPtr
->hfsPlus
.forkType
= 0;
519 extentKeyPtr
->hfsPlus
.pad
= 0;
520 extentKeyPtr
->hfsPlus
.fileID
= srcFileID
;
521 extentKeyPtr
->hfsPlus
.startBlock
= 0;
524 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
525 btKeySize
= sizeof(HFSExtentKey
);
527 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
528 extentKeyPtr
->hfs
.forkType
= 0;
529 extentKeyPtr
->hfs
.fileID
= srcFileID
;
530 extentKeyPtr
->hfs
.startBlock
= 0;
534 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent
535 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found
536 // records, but with destFileID as the file number in the key. Keep doing this sequence of
537 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
538 // no more extent records in the tree.
540 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
541 // set to destFileID.
543 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it
544 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series
545 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record
546 // we found, so that BTIterateRecord would get the next one (the first we haven't processed).
549 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
551 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
552 if (err
!= btNotFound
)
555 DebugStr("\pUnexpected error from SearchBTreeRecord");
557 if (err
== noErr
) // If we found such a bogus extent record, then the tree is really messed up
558 err
= cmBadNews
; // so return an error that conveys the disk is hosed.
565 btRecord
.bufferAddress
= &extentData
;
566 btRecord
.itemCount
= 1;
568 for ( i
=0 ; i
<kNumExtentsToCache
; i
++ )
570 HFSCatalogNodeID foundFileID
;
572 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &btIterator
, &btRecord
, &btRecordSize
);
573 if ( err
== btNotFound
) // Did we run out of extent records in the extents tree?
574 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus!
575 else if ( err
!= noErr
)
576 return( err
); // must be ioError
578 foundFileID
= isHFSPlus
? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
579 if ( foundFileID
== srcFileID
)
581 CopyExtentInfo(extentKeyPtr
, &extentData
, extentsBuffer
, i
);
589 //-- edit each extent key, and reinsert each extent record in the extent file
591 btRecordSize
= sizeof(HFSPlusExtentRecord
);
593 btRecordSize
= sizeof(HFSExtentRecord
);
595 for ( j
=0 ; j
<i
; j
++ )
597 BTreeIterator tmpIterator
;
600 extentsBuffer
[j
].extentKey
.hfsPlus
.fileID
= destFileID
; // change only the id in the key to dest ID
602 extentsBuffer
[j
].extentKey
.hfs
.fileID
= destFileID
; // change only the id in the key to dest ID
604 // get iterator and buffer descriptor ready...
605 (void) BTInvalidateHint(&tmpIterator
);
606 BlockMoveData(&(extentsBuffer
[j
].extentKey
), &tmpIterator
.key
, btKeySize
);
607 btRecord
.bufferAddress
= &(extentsBuffer
[j
].extentData
);
609 err
= BTInsertRecord(fcb
, &tmpIterator
, &btRecord
, btRecordSize
);
612 if ( err
== btExists
)
615 DebugStr("\pCan't insert record -- already exists");
623 //-- okay, done with this buffered batch, go get the next set of extent records
624 // If our buffer is not full, we must be done, or recieved an error
626 if ( i
!= kNumExtentsToCache
) // if the buffer is not full, we must be done
628 err
= DeleteExtents( vcb
, srcFileID
, isHFSPlus
); // Now delete all the extent entries with the sourceID
629 if ( DEBUG_BUILD
&& err
!= noErr
)
630 DebugStr("\pError from DeleteExtents");
631 break; // we're done!
639 void CopyExtentInfo( ExtentKey
*key
, ExtentRecord
*data
, ExtentsRecBuffer
*buffer
, UInt16 bufferCount
)
641 BlockMoveData( key
, &(buffer
[bufferCount
].extentKey
), sizeof( ExtentKey
) );
642 BlockMoveData( data
, &(buffer
[bufferCount
].extentData
), sizeof( ExtentRecord
) );
646 //-- Delete all extents in extent file that have the ID given.
647 OSErr
DeleteExtents( ExtendedVCB
*vcb
, UInt32 fileID
, Boolean isHFSPlus
)
650 ExtentKey
* extentKeyPtr
;
651 ExtentRecord extentData
;
652 BTreeIterator btIterator
;
653 FSBufferDescriptor btRecord
;
657 fcb
= GetFileControlBlock(vcb
->extentsRefNum
);
659 (void) BTInvalidateHint(&btIterator
);
660 extentKeyPtr
= (ExtentKey
*) &btIterator
.key
;
661 btRecord
.bufferAddress
= &extentData
;
662 btRecord
.itemCount
= 1;
664 // The algorithm is to position the BTree just before any extent records for fileID.
665 // Then just keep getting successive records. If the record is still for fileID,
669 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
671 extentKeyPtr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
672 extentKeyPtr
->hfsPlus
.forkType
= 0;
673 extentKeyPtr
->hfsPlus
.pad
= 0;
674 extentKeyPtr
->hfsPlus
.fileID
= fileID
;
675 extentKeyPtr
->hfsPlus
.startBlock
= 0;
678 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
680 extentKeyPtr
->hfs
.keyLength
= kHFSExtentKeyMaximumLength
;
681 extentKeyPtr
->hfs
.forkType
= 0;
682 extentKeyPtr
->hfs
.fileID
= fileID
;
683 extentKeyPtr
->hfs
.startBlock
= 0;
686 err
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
, &btRecord
, &btRecordSize
, &btIterator
);
687 if ( err
!= btNotFound
)
689 if (err
== noErr
) { // Did we find a bogus extent record?
690 err
= cmBadNews
; // Yes, so indicate things are messed up.
693 return err
; // Got some unexpected error, so return it
698 BTreeIterator tmpIterator
;
699 HFSCatalogNodeID foundFileID
;
701 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &btIterator
, &btRecord
, &btRecordSize
);
704 if (err
== btNotFound
) // If we hit the end of the BTree
705 err
= noErr
; // then it's OK
707 break; // We're done now.
710 foundFileID
= isHFSPlus
? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
711 if ( foundFileID
!= fileID
)
712 break; // numbers don't match, we must be done
714 tmpIterator
= btIterator
;
715 err
= BTDeleteRecord( fcb
, &tmpIterator
);
724 // Check if there are extents represented in the extents overflow file.
725 UInt32
CheckExtents( void *extents
, UInt32 totalBlocks
, Boolean isHFSPlus
)
727 UInt32 extentAllocationBlocks
;
731 if ( totalBlocks
== 0 )
734 extentAllocationBlocks
= 0;
738 for ( i
= 0 ; i
< kHFSPlusExtentDensity
; i
++ )
740 extentAllocationBlocks
+= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
741 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
747 for ( i
= 0 ; i
< kHFSExtentDensity
; i
++ )
749 extentAllocationBlocks
+= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
750 if ( extentAllocationBlocks
>= totalBlocks
) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
755 return( extentAllocationBlocks
);