]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/FileIDsServices.c
812f3e58c76d933bcd83fdfdcc5435ddb9a46cb4
[apple/xnu.git] / bsd / hfs / hfscommon / Catalog / FileIDsServices.c
1 /*
2 * Copyright (c) 2000-2004 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 #include "../../hfs_macos_defs.h"
24 #include "../../hfs_format.h"
25
26 #include "../headers/FileMgrInternal.h"
27 #include "../headers/HFSUnicodeWrappers.h"
28 #include "../headers/CatalogPrivate.h"
29
30
31 struct ExtentsRecBuffer {
32 ExtentKey extentKey;
33 ExtentRecord extentData;
34 };
35 typedef struct ExtentsRecBuffer ExtentsRecBuffer;
36
37
38 static UInt32 CheckExtents( void *extents, UInt32 blocks, Boolean isHFSPlus );
39 static OSErr DeleteExtents( ExtendedVCB *vcb, UInt32 fileNumber, Boolean isHFSPlus );
40 static OSErr MoveExtents( ExtendedVCB *vcb, UInt32 srcFileID, UInt32 destFileID, Boolean isHFSPlus );
41 static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
42 static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
43 static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount );
44
45
46
47 OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, UInt32 srcHint, UInt32 destHint )
48 {
49 CatalogKey srcKey; // 518 bytes
50 CatalogKey destKey; // 518 bytes
51 CatalogRecord srcData; // 520 bytes
52 CatalogRecord destData; // 520 bytes
53 CatalogRecord swapData; // 520 bytes
54 SInt16 numSrcExtentBlocks;
55 SInt16 numDestExtentBlocks;
56 OSErr err;
57 Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
58
59 err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, NULL);
60 ReturnIfError(err);
61
62 err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, NULL);
63 ReturnIfError(err);
64
65 if ( isHFSPlus )
66 {
67 //-- Step 1: Check the catalog nodes for extents
68
69 //-- locate the source file, test for extents in extent file, and copy the cat record for later
70 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
71 ReturnIfError( err );
72
73 if ( srcData.recordType != kHFSPlusFileRecord )
74 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
75
76 //-- Check if there are any extents in the source file
77 //\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
78 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
79 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents
80 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
81
82 //-- Check if there are any extents in the destination file
83 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
84 ReturnIfError( err );
85
86 if ( destData.recordType != kHFSPlusFileRecord )
87 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
88
89 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
90 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents
91 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
92
93 //-- Step 2: Exchange the Extent key in the extent file
94
95 //-- Exchange the extents key in the extent file
96 err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
97 ReturnIfError( err );
98
99 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
100 {
101 //-- Change the source extents file ids to our known bogus value
102 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
103 if ( err != noErr )
104 {
105 if ( err != dskFulErr )
106 return( err );
107 else
108 goto ExUndo1a;
109 }
110
111 //-- Change the destination extents file id's to the source id's
112 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
113 if ( err != noErr )
114 {
115 if ( err != dskFulErr )
116 return( err );
117
118 ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
119 ReturnIfError( err ); // we are doomed. Just QUIT!
120
121 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, isHFSPlus ); // Move the extents back
122 ReturnIfError( err ); // we are doomed. Just QUIT!
123
124 goto ExUndo1a;
125 }
126
127 //-- Change the bogus extents file id's to the dest id's
128 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, isHFSPlus );
129 if ( err != noErr )
130 {
131 if ( err != dskFulErr )
132 return( err );
133
134 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
135 ReturnIfError( err ); // we are doomed. Just QUIT!
136
137 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus ); // Move the extents back
138 ReturnIfError( err ); // we are doomed. Just QUIT!
139
140 goto ExUndo2aPlus;
141 }
142
143 }
144 else if ( numSrcExtentBlocks ) // just the source file has extents
145 {
146 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, isHFSPlus );
147 if ( err != noErr )
148 {
149 if ( err != dskFulErr )
150 return( err );
151
152 err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, isHFSPlus );
153 ReturnIfError( err ); // we are doomed. Just QUIT!
154
155 goto FlushAndReturn;
156 }
157 }
158 else if ( numDestExtentBlocks ) // just the destination file has extents
159 {
160 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, isHFSPlus );
161 if ( err != noErr )
162 {
163 if ( err != dskFulErr )
164 return( err );
165
166 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, isHFSPlus );
167 ReturnIfError( err ); // we are doomed. Just QUIT!
168
169 goto FlushAndReturn;
170 }
171 }
172
173 //-- Step 3: Change the data in the catalog nodes
174
175 //-- find the source cnode and put dest info in it
176 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
177 if ( err != noErr )
178 return( cmBadNews );
179
180 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
181 CopyBigCatalogNodeInfo( &destData, &srcData );
182
183 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint );
184 ReturnIfError( err );
185
186 // find the destination cnode and put source info in it
187 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
188 if ( err != noErr )
189 return( cmBadNews );
190
191 CopyBigCatalogNodeInfo( &swapData, &destData );
192 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint );
193 ReturnIfError( err );
194 }
195 else // HFS //
196 {
197 //-- Step 1: Check the catalog nodes for extents
198
199 //-- locate the source file, test for extents in extent file, and copy the cat record for later
200 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
201 ReturnIfError( err );
202
203 if ( srcData.recordType != kHFSFileRecord )
204 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
205
206 //-- Check if there are any extents in the source file
207 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
208 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents
209 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
210
211
212 //\80\80 Do we save the found source node for later use?
213
214
215 //-- Check if there are any extents in the destination file
216 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
217 ReturnIfError( err );
218
219 if ( destData.recordType != kHFSFileRecord )
220 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
221
222 numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
223 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents
224 numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
225
226 //\80\80 Do we save the found destination node for later use?
227
228
229 //-- Step 2: Exchange the Extent key in the extent file
230
231 //-- Exchange the extents key in the extent file
232 err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
233 ReturnIfError( err );
234
235 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
236 {
237 //-- Change the source extents file ids to our known bogus value
238 err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, isHFSPlus );
239 if ( err != noErr )
240 {
241 if ( err != dskFulErr )
242 return( err );
243
244 ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, isHFSPlus );
245 ReturnIfError( err ); // we are doomed. Just QUIT!
246
247 err = FlushCatalog( vcb ); // flush the catalog
248 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
249 return( dskFulErr );
250 }
251
252 //-- Change the destination extents file id's to the source id's
253 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
254 if ( err != noErr )
255 {
256 if ( err != dskFulErr )
257 return( err );
258
259 ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
260 ReturnIfError( err ); // we are doomed. Just QUIT!
261
262 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, isHFSPlus ); // Move the extents back
263 ReturnIfError( err ); // we are doomed. Just QUIT!
264
265 goto ExUndo1a;
266 }
267
268 //-- Change the bogus extents file id's to the dest id's
269 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, isHFSPlus );
270 if ( err != noErr )
271 {
272 if ( err != dskFulErr )
273 return( err );
274
275 err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
276 ReturnIfError( err ); // we are doomed. Just QUIT!
277
278 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus ); // Move the extents back
279 ReturnIfError( err ); // we are doomed. Just QUIT!
280
281 goto ExUndo2a;
282 }
283
284 }
285 else if ( numSrcExtentBlocks ) // just the source file has extents
286 {
287 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, isHFSPlus );
288 if ( err != noErr )
289 {
290 if ( err != dskFulErr )
291 return( err );
292
293 err = DeleteExtents( vcb, srcData.hfsFile.fileID, isHFSPlus );
294 ReturnIfError( err ); // we are doomed. Just QUIT!
295
296 goto FlushAndReturn;
297 }
298 }
299 else if ( numDestExtentBlocks ) // just the destination file has extents
300 {
301 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, isHFSPlus );
302 if ( err != noErr )
303 {
304 if ( err != dskFulErr )
305 return( err );
306
307 err = DeleteExtents( vcb, destData.hfsFile.fileID, isHFSPlus );
308 ReturnIfError( err ); // we are doomed. Just QUIT!
309
310 goto FlushAndReturn;
311 }
312 }
313
314 //-- Step 3: Change the data in the catalog nodes
315
316 //-- find the source cnode and put dest info in it
317 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
318 if ( err != noErr )
319 return( cmBadNews );
320
321 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
322 //\80\80 Asm source copies from the saved dest catalog node
323 CopyCatalogNodeInfo( &destData, &srcData );
324
325 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint );
326 ReturnIfError( err );
327
328
329 // find the destination cnode and put source info in it
330 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
331 if ( err != noErr )
332 return( cmBadNews );
333
334 CopyCatalogNodeInfo( &swapData, &destData );
335 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint );
336 ReturnIfError( err );
337 }
338
339 err = noErr;
340
341 //-- Step 4: Error Handling section
342
343
344 FlushAndReturn:
345 err = FlushCatalog( vcb ); // flush the catalog
346 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
347 return( err );
348 }
349
350
351 static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
352 {
353 dest->hfsFile.dataLogicalSize = src->hfsFile.dataLogicalSize;
354 dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize;
355 dest->hfsFile.rsrcLogicalSize = src->hfsFile.rsrcLogicalSize;
356 dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize;
357 dest->hfsFile.modifyDate = src->hfsFile.modifyDate;
358 BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) );
359 BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) );
360 }
361
362 static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
363 {
364 BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) );
365 BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) );
366 dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate;
367 }
368
369
370 static OSErr MoveExtents( ExtendedVCB *vcb, UInt32 srcFileID, UInt32 destFileID, Boolean isHFSPlus )
371 {
372 FCB * fcb;
373 ExtentsRecBuffer extentsBuffer[kNumExtentsToCache];
374 ExtentKey * extentKeyPtr;
375 ExtentRecord extentData;
376 BTreeIterator btIterator;
377 FSBufferDescriptor btRecord;
378 UInt16 btKeySize;
379 UInt16 btRecordSize;
380 SInt16 i, j;
381 OSErr err;
382
383
384 fcb = GetFileControlBlock(vcb->extentsRefNum);
385
386 (void) BTInvalidateHint(&btIterator);
387 extentKeyPtr = (ExtentKey*) &btIterator.key;
388 btRecord.bufferAddress = &extentData;
389 btRecord.itemCount = 1;
390
391 //-- Collect the extent records
392
393 //
394 // A search on the following key will cause the BTree to be positioned immediately
395 // before the first extent record for file #srcFileID, but not actually positioned
396 // on any record. This is because there cannot be an extent record with FABN = 0
397 // (the first extent of the fork, which would be in the catalog entry, not an extent
398 // record).
399 //
400 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
401 //
402 if (isHFSPlus) {
403 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
404 btKeySize = sizeof(HFSPlusExtentKey);
405
406 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
407 extentKeyPtr->hfsPlus.forkType = 0;
408 extentKeyPtr->hfsPlus.pad = 0;
409 extentKeyPtr->hfsPlus.fileID = srcFileID;
410 extentKeyPtr->hfsPlus.startBlock = 0;
411 }
412 else {
413 btRecord.itemSize = sizeof(HFSExtentRecord);
414 btKeySize = sizeof(HFSExtentKey);
415
416 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
417 extentKeyPtr->hfs.forkType = 0;
418 extentKeyPtr->hfs.fileID = srcFileID;
419 extentKeyPtr->hfs.startBlock = 0;
420 }
421
422 //
423 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent
424 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found
425 // records, but with destFileID as the file number in the key. Keep doing this sequence of
426 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
427 // no more extent records in the tree.
428 //
429 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
430 // set to destFileID.
431 //
432 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it
433 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series
434 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record
435 // we found, so that BTIterateRecord would get the next one (the first we haven't processed).
436 //
437
438 err = BTSearchRecord(fcb, &btIterator, &btRecord, &btRecordSize, &btIterator);
439
440 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
441 if (err != btNotFound)
442 {
443 if ( DEBUG_BUILD )
444 DebugStr("\pUnexpected error from SearchBTreeRecord");
445
446 if (err == noErr) // If we found such a bogus extent record, then the tree is really messed up
447 err = cmBadNews; // so return an error that conveys the disk is hosed.
448
449 return err;
450 }
451
452 do
453 {
454 btRecord.bufferAddress = &extentData;
455 btRecord.itemCount = 1;
456
457 for ( i=0 ; i<kNumExtentsToCache ; i++ )
458 {
459 HFSCatalogNodeID foundFileID;
460
461 err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize);
462 if ( err == btNotFound ) // Did we run out of extent records in the extents tree?
463 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus!
464 else if ( err != noErr )
465 return( err ); // must be ioError
466
467 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
468 if ( foundFileID == srcFileID )
469 {
470 CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
471 }
472 else
473 {
474 break;
475 }
476 }
477
478 //-- edit each extent key, and reinsert each extent record in the extent file
479 if (isHFSPlus)
480 btRecordSize = sizeof(HFSPlusExtentRecord);
481 else
482 btRecordSize = sizeof(HFSExtentRecord);
483
484 for ( j=0 ; j<i ; j++ )
485 {
486 BTreeIterator tmpIterator;
487
488 if (isHFSPlus)
489 extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; // change only the id in the key to dest ID
490 else
491 extentsBuffer[j].extentKey.hfs.fileID = destFileID; // change only the id in the key to dest ID
492
493 // get iterator and buffer descriptor ready...
494 (void) BTInvalidateHint(&tmpIterator);
495 BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator.key, btKeySize);
496 btRecord.bufferAddress = &(extentsBuffer[j].extentData);
497
498 err = BTInsertRecord(fcb, &tmpIterator, &btRecord, btRecordSize);
499 if ( err != noErr )
500 { // parse the error
501 if ( err == btExists )
502 {
503 if ( DEBUG_BUILD )
504 DebugStr("\pCan't insert record -- already exists");
505 return( cmBadNews );
506 }
507 else
508 return( err );
509 }
510 }
511
512 //-- okay, done with this buffered batch, go get the next set of extent records
513 // If our buffer is not full, we must be done, or recieved an error
514
515 if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done
516 {
517 err = DeleteExtents( vcb, srcFileID, isHFSPlus ); // Now delete all the extent entries with the sourceID
518 if ( DEBUG_BUILD && err != noErr )
519 DebugStr("\pError from DeleteExtents");
520 break; // we're done!
521 }
522 } while ( true );
523
524 return( err );
525 }
526
527
528 static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, UInt16 bufferCount )
529 {
530 BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
531 BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
532 }
533
534
535 //-- Delete all extents in extent file that have the ID given.
536 static OSErr DeleteExtents( ExtendedVCB *vcb, UInt32 fileID, Boolean isHFSPlus )
537 {
538 FCB * fcb;
539 ExtentKey * extentKeyPtr;
540 ExtentRecord extentData;
541 BTreeIterator btIterator;
542 FSBufferDescriptor btRecord;
543 UInt16 btRecordSize;
544 OSErr err;
545
546 fcb = GetFileControlBlock(vcb->extentsRefNum);
547
548 (void) BTInvalidateHint(&btIterator);
549 extentKeyPtr = (ExtentKey*) &btIterator.key;
550 btRecord.bufferAddress = &extentData;
551 btRecord.itemCount = 1;
552
553 // The algorithm is to position the BTree just before any extent records for fileID.
554 // Then just keep getting successive records. If the record is still for fileID,
555 // then delete it.
556
557 if (isHFSPlus) {
558 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
559
560 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
561 extentKeyPtr->hfsPlus.forkType = 0;
562 extentKeyPtr->hfsPlus.pad = 0;
563 extentKeyPtr->hfsPlus.fileID = fileID;
564 extentKeyPtr->hfsPlus.startBlock = 0;
565 }
566 else {
567 btRecord.itemSize = sizeof(HFSExtentRecord);
568
569 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
570 extentKeyPtr->hfs.forkType = 0;
571 extentKeyPtr->hfs.fileID = fileID;
572 extentKeyPtr->hfs.startBlock = 0;
573 }
574
575 err = BTSearchRecord(fcb, &btIterator, &btRecord, &btRecordSize, &btIterator);
576 if ( err != btNotFound )
577 {
578 if (err == noErr) { // Did we find a bogus extent record?
579 err = cmBadNews; // Yes, so indicate things are messed up.
580 }
581
582 return err; // Got some unexpected error, so return it
583 }
584
585 do
586 {
587 BTreeIterator tmpIterator;
588 HFSCatalogNodeID foundFileID;
589
590 err = BTIterateRecord(fcb, kBTreeNextRecord, &btIterator, &btRecord, &btRecordSize);
591 if ( err != noErr )
592 {
593 if (err == btNotFound) // If we hit the end of the BTree
594 err = noErr; // then it's OK
595
596 break; // We're done now.
597 }
598
599 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
600 if ( foundFileID != fileID )
601 break; // numbers don't match, we must be done
602
603 tmpIterator = btIterator;
604 err = BTDeleteRecord( fcb, &tmpIterator );
605 if (err != noErr)
606 break;
607 } while ( true );
608
609 return( err );
610 }
611
612
613 // Check if there are extents represented in the extents overflow file.
614 static UInt32 CheckExtents( void *extents, UInt32 totalBlocks, Boolean isHFSPlus )
615 {
616 UInt32 extentAllocationBlocks;
617 UInt16 i;
618
619
620 if ( totalBlocks == 0 )
621 return( 0 );
622
623 extentAllocationBlocks = 0;
624
625 if ( isHFSPlus )
626 {
627 for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ )
628 {
629 extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
630 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
631 return( 0 );
632 }
633 }
634 else
635 {
636 for ( i = 0 ; i < kHFSExtentDensity ; i++ )
637 {
638 extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount;
639 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
640 return( 0 );
641 }
642 }
643
644 return( extentAllocationBlocks );
645 }