]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/FileIDsServices.c
xnu-123.5.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / Catalog / FileIDsServices.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 File: FileIDServices.c
24
25 Contains: File ID manipulating routines.
26
27 Version: HFS Plus 1.0
28
29 Written by: Deric Horn
30
31 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
32
33 File Ownership:
34
35 DRI: Deric Horn
36
37 Other Contact: xxx put other contact here xxx
38
39 Technology: xxx put technology here xxx
40
41 Writers:
42
43 (JL) Jim Luther
44 (msd) Mark Day
45 (djb) Don Brady
46 (DSH) Deric Horn
47
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.
53
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
59 ever needed.
60 <18> 10/23/97 DSH 1685058, Fix ExchangeFiles by invalidating the node cache before
61 switching the files.
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
70 collision
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
74 modifyDate.
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
80 (fixes warnings).
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
87 CatalogRecord.
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
93 records.
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
99 coverage.
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
103 HFS+ volumes.
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
108 TrashFileBlocks.
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
120
121 */
122
123 #include "../../hfs_macos_defs.h"
124 #include "../../hfs_format.h"
125
126 #include "../headers/FileMgrInternal.h"
127 #include "../headers/HFSUnicodeWrappers.h"
128 #include "../headers/CatalogPrivate.h"
129
130
131 struct ExtentsRecBuffer {
132 ExtentKey extentKey;
133 ExtentRecord extentData;
134 };
135 typedef struct ExtentsRecBuffer ExtentsRecBuffer;
136
137
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 );
140
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 );
146
147 void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount );
148 extern void TrashFileBlocks( ExtendedVCB *vcb, UInt32 fileNumber );
149
150
151
152 OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, UInt32 srcHint, UInt32 destHint )
153 {
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;
161 UInt32 textEncoding;
162 OSErr err;
163 Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
164
165 TrashCatalogIterator(vcb, srcID); // invalidate any iterators for this parentID
166 TrashCatalogIterator(vcb, destID); // invalidate any iterators for this parentID
167
168 err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, &textEncoding);
169 ReturnIfError(err);
170
171 err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, &textEncoding);
172 ReturnIfError(err);
173
174 if ( isHFSPlus )
175 {
176 //-- Step 1: Check the catalog nodes for extents
177
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 );
181
182 if ( srcData.recordType != kHFSPlusFileRecord )
183 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
184
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 );
190
191 //-- Check if there are any extents in the destination file
192 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
193 ReturnIfError( err );
194
195 if ( destData.recordType != kHFSPlusFileRecord )
196 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
197
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 );
201
202 //-- Step 2: Exchange the Extent key in the extent file
203
204 //-- Exchange the extents key in the extent file
205 err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
206 ReturnIfError( err );
207
208 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
209 {
210 //-- Change the source extents file ids to our known bogus value
211 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
212 if ( err != noErr )
213 {
214 if ( err != dskFulErr )
215 return( err );
216 else
217 goto ExUndo1a;
218 }
219
220 //-- Change the destination extents file id's to the source id's
221 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
222 if ( err != noErr )
223 {
224 if ( err != dskFulErr )
225 return( err );
226
227 ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
228 ReturnIfError( err ); // we are doomed. Just QUIT!
229
230 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, isHFSPlus ); // Move the extents back
231 ReturnIfError( err ); // we are doomed. Just QUIT!
232
233 goto ExUndo1a;
234 }
235
236 //-- Change the bogus extents file id's to the dest id's
237 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, isHFSPlus );
238 if ( err != noErr )
239 {
240 if ( err != dskFulErr )
241 return( err );
242
243 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
244 ReturnIfError( err ); // we are doomed. Just QUIT!
245
246 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus ); // Move the extents back
247 ReturnIfError( err ); // we are doomed. Just QUIT!
248
249 goto ExUndo2aPlus;
250 }
251
252 }
253 else if ( numSrcExtentBlocks ) // just the source file has extents
254 {
255 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus );
256 if ( err != noErr )
257 {
258 if ( err != dskFulErr )
259 return( err );
260
261 err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
262 ReturnIfError( err ); // we are doomed. Just QUIT!
263
264 goto FlushAndReturn;
265 }
266 }
267 else if ( numDestExtentBlocks ) // just the destination file has extents
268 {
269 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
270 if ( err != noErr )
271 {
272 if ( err != dskFulErr )
273 return( err );
274
275 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
276 ReturnIfError( err ); // we are doomed. Just QUIT!
277
278 goto FlushAndReturn;
279 }
280 }
281
282 //-- Step 3: Change the data in the catalog nodes
283
284 //-- find the source cnode and put dest info in it
285 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
286 if ( err != noErr )
287 return( cmBadNews );
288
289 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
290 CopyBigCatalogNodeInfo( &destData, &srcData );
291
292 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint );
293 ReturnIfError( err );
294
295 // find the destination cnode and put source info in it
296 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
297 if ( err != noErr )
298 return( cmBadNews );
299
300 CopyBigCatalogNodeInfo( &swapData, &destData );
301 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint );
302 ReturnIfError( err );
303 }
304 else // HFS //
305 {
306 //-- Step 1: Check the catalog nodes for extents
307
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 );
311
312 if ( srcData.recordType != kHFSFileRecord )
313 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
314
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 );
319
320
321 //\80\80 Do we save the found source node for later use?
322
323
324 //-- Check if there are any extents in the destination file
325 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
326 ReturnIfError( err );
327
328 if ( destData.recordType != kHFSFileRecord )
329 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
330
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 );
334
335 //\80\80 Do we save the found destination node for later use?
336
337
338 //-- Step 2: Exchange the Extent key in the extent file
339
340 //-- Exchange the extents key in the extent file
341 err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
342 ReturnIfError( err );
343
344 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
345 {
346 //-- Change the source extents file ids to our known bogus value
347 err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
348 if ( err != noErr )
349 {
350 if ( err != dskFulErr )
351 return( err );
352
353 ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
354 ReturnIfError( err ); // we are doomed. Just QUIT!
355
356 err = FlushCatalog( vcb ); // flush the catalog
357 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
358 return( dskFulErr );
359 }
360
361 //-- Change the destination extents file id's to the source id's
362 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
363 if ( err != noErr )
364 {
365 if ( err != dskFulErr )
366 return( err );
367
368 ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
369 ReturnIfError( err ); // we are doomed. Just QUIT!
370
371 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, isHFSPlus ); // Move the extents back
372 ReturnIfError( err ); // we are doomed. Just QUIT!
373
374 goto ExUndo1a;
375 }
376
377 //-- Change the bogus extents file id's to the dest id's
378 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, isHFSPlus );
379 if ( err != noErr )
380 {
381 if ( err != dskFulErr )
382 return( err );
383
384 err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
385 ReturnIfError( err ); // we are doomed. Just QUIT!
386
387 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus ); // Move the extents back
388 ReturnIfError( err ); // we are doomed. Just QUIT!
389
390 goto ExUndo2a;
391 }
392
393 }
394 else if ( numSrcExtentBlocks ) // just the source file has extents
395 {
396 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus );
397 if ( err != noErr )
398 {
399 if ( err != dskFulErr )
400 return( err );
401
402 err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
403 ReturnIfError( err ); // we are doomed. Just QUIT!
404
405 goto FlushAndReturn;
406 }
407 }
408 else if ( numDestExtentBlocks ) // just the destination file has extents
409 {
410 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
411 if ( err != noErr )
412 {
413 if ( err != dskFulErr )
414 return( err );
415
416 err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
417 ReturnIfError( err ); // we are doomed. Just QUIT!
418
419 goto FlushAndReturn;
420 }
421 }
422
423 //-- Step 3: Change the data in the catalog nodes
424
425 //-- find the source cnode and put dest info in it
426 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
427 if ( err != noErr )
428 return( cmBadNews );
429
430 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
431 //\80\80 Asm source copies from the saved dest catalog node
432 CopyCatalogNodeInfo( &destData, &srcData );
433
434 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint );
435 ReturnIfError( err );
436
437
438 // find the destination cnode and put source info in it
439 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
440 if ( err != noErr )
441 return( cmBadNews );
442
443 CopyCatalogNodeInfo( &swapData, &destData );
444 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint );
445 ReturnIfError( err );
446 }
447
448 err = noErr;
449
450 //-- Step 4: Error Handling section
451
452
453 FlushAndReturn:
454 err = FlushCatalog( vcb ); // flush the catalog
455 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
456 return( err );
457 }
458
459
460 void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
461 {
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) );
471 }
472
473 void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
474 {
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;
478 }
479
480
481 OSErr MoveExtents( ExtendedVCB *vcb, UInt32 srcFileID, UInt32 destFileID, Boolean isHFSPlus )
482 {
483 FCB * fcb;
484 ExtentsRecBuffer extentsBuffer[kNumExtentsToCache];
485 ExtentKey * extentKeyPtr;
486 ExtentRecord extentData;
487 BTreeIterator btIterator;
488 FSBufferDescriptor btRecord;
489 UInt16 btKeySize;
490 UInt16 btRecordSize;
491 SInt16 i, j;
492 OSErr err;
493
494
495 fcb = GetFileControlBlock(vcb->extentsRefNum);
496
497 (void) BTInvalidateHint(&btIterator);
498 extentKeyPtr = (ExtentKey*) &btIterator.key;
499 btRecord.bufferAddress = &extentData;
500 btRecord.itemCount = 1;
501
502 //-- Collect the extent records
503
504 //
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
509 // record).
510 //
511 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
512 //
513 if (isHFSPlus) {
514 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
515 btKeySize = sizeof(HFSPlusExtentKey);
516
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;
522 }
523 else {
524 btRecord.itemSize = sizeof(HFSExtentRecord);
525 btKeySize = sizeof(HFSExtentKey);
526
527 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
528 extentKeyPtr->hfs.forkType = 0;
529 extentKeyPtr->hfs.fileID = srcFileID;
530 extentKeyPtr->hfs.startBlock = 0;
531 }
532
533 //
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.
539 //
540 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
541 // set to destFileID.
542 //
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).
547 //
548
549 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
550
551 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
552 if (err != btNotFound)
553 {
554 if ( DEBUG_BUILD )
555 DebugStr("\pUnexpected error from SearchBTreeRecord");
556
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.
559
560 return err;
561 }
562
563 do
564 {
565 btRecord.bufferAddress = &extentData;
566 btRecord.itemCount = 1;
567
568 for ( i=0 ; i<kNumExtentsToCache ; i++ )
569 {
570 HFSCatalogNodeID foundFileID;
571
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
577
578 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
579 if ( foundFileID == srcFileID )
580 {
581 CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
582 }
583 else
584 {
585 break;
586 }
587 }
588
589 //-- edit each extent key, and reinsert each extent record in the extent file
590 if (isHFSPlus)
591 btRecordSize = sizeof(HFSPlusExtentRecord);
592 else
593 btRecordSize = sizeof(HFSExtentRecord);
594
595 for ( j=0 ; j<i ; j++ )
596 {
597 BTreeIterator tmpIterator;
598
599 if (isHFSPlus)
600 extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; // change only the id in the key to dest ID
601 else
602 extentsBuffer[j].extentKey.hfs.fileID = destFileID; // change only the id in the key to dest ID
603
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);
608
609 err = BTInsertRecord(fcb, &tmpIterator, &btRecord, btRecordSize);
610 if ( err != noErr )
611 { // parse the error
612 if ( err == btExists )
613 {
614 if ( DEBUG_BUILD )
615 DebugStr("\pCan't insert record -- already exists");
616 return( cmBadNews );
617 }
618 else
619 return( err );
620 }
621 }
622
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
625
626 if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done
627 {
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!
632 }
633 } while ( true );
634
635 return( err );
636 }
637
638
639 void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount )
640 {
641 BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
642 BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
643 }
644
645
646 //-- Delete all extents in extent file that have the ID given.
647 OSErr DeleteExtents( ExtendedVCB *vcb, UInt32 fileID, Boolean isHFSPlus )
648 {
649 FCB * fcb;
650 ExtentKey * extentKeyPtr;
651 ExtentRecord extentData;
652 BTreeIterator btIterator;
653 FSBufferDescriptor btRecord;
654 UInt16 btRecordSize;
655 OSErr err;
656
657 fcb = GetFileControlBlock(vcb->extentsRefNum);
658
659 (void) BTInvalidateHint(&btIterator);
660 extentKeyPtr = (ExtentKey*) &btIterator.key;
661 btRecord.bufferAddress = &extentData;
662 btRecord.itemCount = 1;
663
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,
666 // then delete it.
667
668 if (isHFSPlus) {
669 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
670
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;
676 }
677 else {
678 btRecord.itemSize = sizeof(HFSExtentRecord);
679
680 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
681 extentKeyPtr->hfs.forkType = 0;
682 extentKeyPtr->hfs.fileID = fileID;
683 extentKeyPtr->hfs.startBlock = 0;
684 }
685
686 err = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, &btRecord, &btRecordSize, &btIterator);
687 if ( err != btNotFound )
688 {
689 if (err == noErr) { // Did we find a bogus extent record?
690 err = cmBadNews; // Yes, so indicate things are messed up.
691 }
692
693 return err; // Got some unexpected error, so return it
694 }
695
696 do
697 {
698 BTreeIterator tmpIterator;
699 HFSCatalogNodeID foundFileID;
700
701 err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize);
702 if ( err != noErr )
703 {
704 if (err == btNotFound) // If we hit the end of the BTree
705 err = noErr; // then it's OK
706
707 break; // We're done now.
708 }
709
710 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
711 if ( foundFileID != fileID )
712 break; // numbers don't match, we must be done
713
714 tmpIterator = btIterator;
715 err = BTDeleteRecord( fcb, &tmpIterator );
716 if (err != noErr)
717 break;
718 } while ( true );
719
720 return( err );
721 }
722
723
724 // Check if there are extents represented in the extents overflow file.
725 UInt32 CheckExtents( void *extents, UInt32 totalBlocks, Boolean isHFSPlus )
726 {
727 UInt32 extentAllocationBlocks;
728 UInt16 i;
729
730
731 if ( totalBlocks == 0 )
732 return( 0 );
733
734 extentAllocationBlocks = 0;
735
736 if ( isHFSPlus )
737 {
738 for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ )
739 {
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)
742 return( 0 );
743 }
744 }
745 else
746 {
747 for ( i = 0 ; i < kHFSExtentDensity ; i++ )
748 {
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)
751 return( 0 );
752 }
753 }
754
755 return( extentAllocationBlocks );
756 }