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