]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Catalog/FileIDsServices.c
xnu-2050.9.2.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / Catalog / FileIDsServices.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include "../../hfs_macos_defs.h"
30 #include "../../hfs_format.h"
31
32 #include "../headers/FileMgrInternal.h"
33 #include "../headers/HFSUnicodeWrappers.h"
34 #include "../headers/CatalogPrivate.h"
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <libkern/libkern.h>
38
39
40 struct ExtentsRecBuffer {
41 ExtentKey extentKey;
42 ExtentRecord extentData;
43 };
44 typedef struct ExtentsRecBuffer ExtentsRecBuffer;
45
46
47 static u_int32_t CheckExtents( void *extents, u_int32_t blocks, Boolean isHFSPlus );
48 static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileNumber, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
49 static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus );
50 static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
51 static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest );
52 static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount );
53
54 /*
55 * This function moves the overflow extents associated with srcID into the file associated with dstID.
56 * We should have already verified that 'srcID' has overflow extents. So now we move all of the overflow
57 * extent records.
58 */
59 OSErr MoveData( ExtendedVCB *vcb, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, int rsrc) {
60
61 OSErr err;
62
63 /*
64 * Only the source file should have extents, so we just track those.
65 * We operate on the fork represented by the open FD that was used to call into this
66 * function
67 */
68 if (rsrc) {
69 /* Copy the extent overflow blocks. */
70 err = MoveExtents( vcb, srcID, destID, 1, (u_int8_t)0xff, 1);
71 if ( err != noErr ) {
72 if ( err != dskFulErr ) {
73 return( err );
74 }
75 /*
76 * In case of error, we would have probably run into problems
77 * growing the extents b-tree. Since the move is actually a copy + delete
78 * just delete the new entries. Same for below.
79 */
80 err = DeleteExtents( vcb, destID, 1, (u_int8_t)0xff, 1);
81 ReturnIfError( err ); // we are doomed. Just QUIT!
82 goto FlushAndReturn;
83 }
84 }
85 else {
86 /* Copy the extent overflow blocks. */
87 err = MoveExtents( vcb, srcID, destID, 1, 0, 1);
88 if ( err != noErr ) {
89 if ( err != dskFulErr ) {
90 return( err );
91 }
92 err = DeleteExtents( vcb, destID, 1, 0, 1);
93 ReturnIfError( err ); // we are doomed. Just QUIT!
94 goto FlushAndReturn;
95 }
96 }
97
98 FlushAndReturn:
99 /* Write out the catalog and extent overflow B-Tree changes */
100 err = FlushCatalog( vcb );
101 err = FlushExtentFile( vcb );
102
103 return( err );
104 }
105
106
107 OSErr ExchangeFileIDs( ExtendedVCB *vcb, ConstUTF8Param srcName, ConstUTF8Param destName, HFSCatalogNodeID srcID, HFSCatalogNodeID destID, u_int32_t srcHint, u_int32_t destHint )
108 {
109 CatalogKey srcKey; // 518 bytes
110 CatalogKey destKey; // 518 bytes
111 CatalogRecord srcData; // 520 bytes
112 CatalogRecord destData; // 520 bytes
113 CatalogRecord swapData; // 520 bytes
114 int16_t numSrcExtentBlocks;
115 int16_t numDestExtentBlocks;
116 OSErr err;
117 Boolean isHFSPlus = ( vcb->vcbSigWord == kHFSPlusSigWord );
118
119 err = BuildCatalogKeyUTF8(vcb, srcID, srcName, kUndefinedStrLen, &srcKey, NULL);
120 ReturnIfError(err);
121
122 err = BuildCatalogKeyUTF8(vcb, destID, destName, kUndefinedStrLen, &destKey, NULL);
123 ReturnIfError(err);
124
125 if ( isHFSPlus )
126 {
127 //-- Step 1: Check the catalog nodes for extents
128
129 //-- locate the source file, test for extents in extent file, and copy the cat record for later
130 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
131 ReturnIfError( err );
132
133 if ( srcData.recordType != kHFSPlusFileRecord )
134 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
135
136 //-- Check if there are any extents in the source file
137 //\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
138 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.dataFork.extents, srcData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
139 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents
140 numSrcExtentBlocks = CheckExtents( srcData.hfsPlusFile.resourceFork.extents, srcData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
141
142 //-- Check if there are any extents in the destination file
143 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
144 ReturnIfError( err );
145
146 if ( destData.recordType != kHFSPlusFileRecord )
147 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
148
149 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.dataFork.extents, destData.hfsPlusFile.dataFork.totalBlocks, isHFSPlus );
150 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents
151 numDestExtentBlocks = CheckExtents( destData.hfsPlusFile.resourceFork.extents, destData.hfsPlusFile.resourceFork.totalBlocks, isHFSPlus );
152
153 //-- Step 2: Exchange the Extent key in the extent file
154
155 //-- Exchange the extents key in the extent file
156 err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
157 ReturnIfError( err );
158
159 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
160 {
161 //-- Change the source extents file ids to our known bogus value
162 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, kHFSBogusExtentFileID, 0,0, isHFSPlus );
163 if ( err != noErr )
164 {
165 if ( err != dskFulErr )
166 return( err );
167 else
168 goto ExUndo1a;
169 }
170
171 //-- Change the destination extents file id's to the source id's
172 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
173 if ( err != noErr )
174 {
175 if ( err != dskFulErr )
176 return( err );
177
178 ExUndo2aPlus: err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
179 ReturnIfError( err ); // we are doomed. Just QUIT!
180
181 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back
182 ReturnIfError( err ); // we are doomed. Just QUIT!
183
184 goto ExUndo1a;
185 }
186
187 //-- Change the bogus extents file id's to the dest id's
188 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
189 if ( err != noErr )
190 {
191 if ( err != dskFulErr )
192 return( err );
193
194 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
195 ReturnIfError( err ); // we are doomed. Just QUIT!
196
197 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus ); // Move the extents back
198 ReturnIfError( err ); // we are doomed. Just QUIT!
199
200 goto ExUndo2aPlus;
201 }
202
203 }
204 else if ( numSrcExtentBlocks ) // just the source file has extents
205 {
206 err = MoveExtents( vcb, srcData.hfsPlusFile.fileID, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
207 if ( err != noErr )
208 {
209 if ( err != dskFulErr )
210 return( err );
211
212 err = DeleteExtents( vcb, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
213 ReturnIfError( err ); // we are doomed. Just QUIT!
214
215 goto FlushAndReturn;
216 }
217 }
218 else if ( numDestExtentBlocks ) // just the destination file has extents
219 {
220 err = MoveExtents( vcb, destData.hfsPlusFile.fileID, srcData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
221 if ( err != noErr )
222 {
223 if ( err != dskFulErr )
224 return( err );
225
226 err = DeleteExtents( vcb, destData.hfsPlusFile.fileID, 0, 0, isHFSPlus );
227 ReturnIfError( err ); // we are doomed. Just QUIT!
228
229 goto FlushAndReturn;
230 }
231 }
232
233 //-- Step 3: Change the data in the catalog nodes
234
235 //-- find the source cnode and put dest info in it
236 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
237 if ( err != noErr )
238 return( cmBadNews );
239
240 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
241 CopyBigCatalogNodeInfo( &destData, &srcData );
242
243 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSPlusCatalogFile), &srcHint );
244 ReturnIfError( err );
245
246 // find the destination cnode and put source info in it
247 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
248 if ( err != noErr )
249 return( cmBadNews );
250
251 CopyBigCatalogNodeInfo( &swapData, &destData );
252 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSPlusCatalogFile), &destHint );
253 ReturnIfError( err );
254 }
255 else // HFS //
256 {
257 //-- Step 1: Check the catalog nodes for extents
258
259 //-- locate the source file, test for extents in extent file, and copy the cat record for later
260 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
261 ReturnIfError( err );
262
263 if ( srcData.recordType != kHFSFileRecord )
264 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
265
266 //-- Check if there are any extents in the source file
267 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.dataExtents, srcData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
268 if ( numSrcExtentBlocks == 0 ) // then check the resource fork extents
269 numSrcExtentBlocks = CheckExtents( srcData.hfsFile.rsrcExtents, srcData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
270
271
272 //\80\80 Do we save the found source node for later use?
273
274
275 //-- Check if there are any extents in the destination file
276 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
277 ReturnIfError( err );
278
279 if ( destData.recordType != kHFSFileRecord )
280 return( cmFThdDirErr ); // Error "cmFThdDirErr = it is a directory"
281
282 numDestExtentBlocks = CheckExtents( destData.hfsFile.dataExtents, destData.hfsFile.dataPhysicalSize / vcb->blockSize, isHFSPlus );
283 if ( numDestExtentBlocks == 0 ) // then check the resource fork extents
284 numDestExtentBlocks = CheckExtents( destData.hfsFile.rsrcExtents, destData.hfsFile.rsrcPhysicalSize / vcb->blockSize, isHFSPlus );
285
286 //\80\80 Do we save the found destination node for later use?
287
288
289 //-- Step 2: Exchange the Extent key in the extent file
290
291 //-- Exchange the extents key in the extent file
292 err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
293 ReturnIfError( err );
294
295 if ( numSrcExtentBlocks && numDestExtentBlocks ) // if both files have extents
296 {
297 //-- Change the source extents file ids to our known bogus value
298 err = MoveExtents( vcb, srcData.hfsFile.fileID, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
299 if ( err != noErr )
300 {
301 if ( err != dskFulErr )
302 return( err );
303
304 ExUndo1a: err = DeleteExtents( vcb, kHFSBogusExtentFileID, 0, 0, isHFSPlus );
305 ReturnIfError( err ); // we are doomed. Just QUIT!
306
307 err = FlushCatalog( vcb ); // flush the catalog
308 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
309 return( dskFulErr );
310 }
311
312 //-- Change the destination extents file id's to the source id's
313 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
314 if ( err != noErr )
315 {
316 if ( err != dskFulErr )
317 return( err );
318
319 ExUndo2a: err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
320 ReturnIfError( err ); // we are doomed. Just QUIT!
321
322 err = MoveExtents( vcb, kHFSBogusExtentFileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back
323 ReturnIfError( err ); // we are doomed. Just QUIT!
324
325 goto ExUndo1a;
326 }
327
328 //-- Change the bogus extents file id's to the dest id's
329 err = MoveExtents( vcb, kHFSBogusExtentFileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
330 if ( err != noErr )
331 {
332 if ( err != dskFulErr )
333 return( err );
334
335 err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus );
336 ReturnIfError( err ); // we are doomed. Just QUIT!
337
338 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus ); // Move the extents back
339 ReturnIfError( err ); // we are doomed. Just QUIT!
340
341 goto ExUndo2a;
342 }
343
344 }
345 else if ( numSrcExtentBlocks ) // just the source file has extents
346 {
347 err = MoveExtents( vcb, srcData.hfsFile.fileID, destData.hfsFile.fileID, 0, 0, isHFSPlus );
348 if ( err != noErr )
349 {
350 if ( err != dskFulErr )
351 return( err );
352
353 err = DeleteExtents( vcb, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
354 ReturnIfError( err ); // we are doomed. Just QUIT!
355
356 goto FlushAndReturn;
357 }
358 }
359 else if ( numDestExtentBlocks ) // just the destination file has extents
360 {
361 err = MoveExtents( vcb, destData.hfsFile.fileID, srcData.hfsFile.fileID, 0, 0, isHFSPlus );
362 if ( err != noErr )
363 {
364 if ( err != dskFulErr )
365 return( err );
366
367 err = DeleteExtents( vcb, destData.hfsFile.fileID, 0, 0, isHFSPlus );
368 ReturnIfError( err ); // we are doomed. Just QUIT!
369
370 goto FlushAndReturn;
371 }
372 }
373
374 //-- Step 3: Change the data in the catalog nodes
375
376 //-- find the source cnode and put dest info in it
377 err = LocateCatalogNodeByKey( vcb, srcHint, &srcKey, &srcData, &srcHint );
378 if ( err != noErr )
379 return( cmBadNews );
380
381 BlockMoveData( &srcData, &swapData, sizeof(CatalogRecord) );
382 //\80\80 Asm source copies from the saved dest catalog node
383 CopyCatalogNodeInfo( &destData, &srcData );
384
385 err = ReplaceBTreeRecord( vcb->catalogRefNum, &srcKey, srcHint, &srcData, sizeof(HFSCatalogFile), &srcHint );
386 ReturnIfError( err );
387
388
389 // find the destination cnode and put source info in it
390 err = LocateCatalogNodeByKey( vcb, destHint, &destKey, &destData, &destHint );
391 if ( err != noErr )
392 return( cmBadNews );
393
394 CopyCatalogNodeInfo( &swapData, &destData );
395 err = ReplaceBTreeRecord( vcb->catalogRefNum, &destKey, destHint, &destData, sizeof(HFSCatalogFile), &destHint );
396 ReturnIfError( err );
397 }
398
399 err = noErr;
400
401 //-- Step 4: Error Handling section
402
403
404 FlushAndReturn:
405 err = FlushCatalog( vcb ); // flush the catalog
406 err = FlushExtentFile( vcb ); // flush the extent file (unneeded for common case, but it's cheap)
407 return( err );
408 }
409
410
411 static void CopyCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
412 {
413 dest->hfsFile.dataLogicalSize = src->hfsFile.dataLogicalSize;
414 dest->hfsFile.dataPhysicalSize = src->hfsFile.dataPhysicalSize;
415 dest->hfsFile.rsrcLogicalSize = src->hfsFile.rsrcLogicalSize;
416 dest->hfsFile.rsrcPhysicalSize = src->hfsFile.rsrcPhysicalSize;
417 dest->hfsFile.modifyDate = src->hfsFile.modifyDate;
418 BlockMoveData( src->hfsFile.dataExtents, dest->hfsFile.dataExtents, sizeof(HFSExtentRecord) );
419 BlockMoveData( src->hfsFile.rsrcExtents, dest->hfsFile.rsrcExtents, sizeof(HFSExtentRecord) );
420 }
421
422 static void CopyBigCatalogNodeInfo( CatalogRecord *src, CatalogRecord *dest )
423 {
424 BlockMoveData( &src->hfsPlusFile.dataFork, &dest->hfsPlusFile.dataFork, sizeof(HFSPlusForkData) );
425 BlockMoveData( &src->hfsPlusFile.resourceFork, &dest->hfsPlusFile.resourceFork, sizeof(HFSPlusForkData) );
426 dest->hfsPlusFile.contentModDate = src->hfsPlusFile.contentModDate;
427 }
428
429
430 static OSErr MoveExtents( ExtendedVCB *vcb, u_int32_t srcFileID, u_int32_t destFileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus )
431 {
432 FCB * fcb;
433 ExtentsRecBuffer extentsBuffer[kNumExtentsToCache];
434 ExtentKey * extentKeyPtr;
435 ExtentRecord extentData;
436 struct BTreeIterator *btIterator = NULL;
437 struct BTreeIterator *tmpIterator = NULL;
438 FSBufferDescriptor btRecord;
439 u_int16_t btKeySize;
440 u_int16_t btRecordSize;
441 int16_t i, j;
442 OSErr err;
443
444 MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
445 if (btIterator == NULL) {
446 return memFullErr; // translates to ENOMEM
447 }
448
449
450 MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
451 if (tmpIterator == NULL) {
452 FREE (btIterator, M_TEMP);
453 return memFullErr; // translates to ENOMEM
454 }
455
456 bzero(btIterator, sizeof(*btIterator));
457 bzero (tmpIterator, sizeof(*tmpIterator));
458
459
460 fcb = GetFileControlBlock(vcb->extentsRefNum);
461
462 (void) BTInvalidateHint(btIterator);
463 extentKeyPtr = (ExtentKey*) &btIterator->key;
464 btRecord.bufferAddress = &extentData;
465 btRecord.itemCount = 1;
466
467 //-- Collect the extent records
468
469 //
470 // A search on the following key will cause the BTree to be positioned immediately
471 // before the first extent record for file #srcFileID, but not actually positioned
472 // on any record. This is because there cannot be an extent record with FABN = 0
473 // (the first extent of the fork, which would be in the catalog entry, not an extent
474 // record).
475 //
476 // Using BTIterateRecord with kBTreeNextRecord will then get that first extent record.
477 //
478 if (isHFSPlus) {
479 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
480 btKeySize = sizeof(HFSPlusExtentKey);
481
482 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
483 extentKeyPtr->hfsPlus.forkType = forkType;
484 extentKeyPtr->hfsPlus.pad = 0;
485 extentKeyPtr->hfsPlus.fileID = srcFileID;
486 extentKeyPtr->hfsPlus.startBlock = 0;
487 }
488 else {
489 btRecord.itemSize = sizeof(HFSExtentRecord);
490 btKeySize = sizeof(HFSExtentKey);
491
492 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
493 extentKeyPtr->hfs.forkType = 0;
494 extentKeyPtr->hfs.fileID = srcFileID;
495 extentKeyPtr->hfs.startBlock = 0;
496 }
497
498 //
499 // We do an initial BTSearchRecord to position the BTree's iterator just before any extent
500 // records for srcFileID. We then do a few BTIterateRecord and BTInsertRecord of those found
501 // records, but with destFileID as the file number in the key. Keep doing this sequence of
502 // BTIterateRecord and BTInsertRecord until we find an extent for another file, or there are
503 // no more extent records in the tree.
504 //
505 // Basically, we're copying records kNumExtentsToCache at a time. The copies have their file ID
506 // set to destFileID.
507 //
508 // This depends on BTInsertRecord not effecting the iterator used by BTIterateRecord. If it
509 // _did_ effect the iterator, then we would need to do a BTSearchRecord before each series
510 // of BTIterateRecord. We'd need to set up the key for BTSearchRecord to find the last record
511 // we found, so that BTIterateRecord would get the next one (the first we haven't processed).
512 //
513
514 err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
515
516 // We expect a btNotFound here, since there shouldn't be an extent record with FABN = 0.
517 if (err != btNotFound)
518 {
519 if ( DEBUG_BUILD )
520 DebugStr("Unexpected error from SearchBTreeRecord");
521
522 if (err == noErr) // If we found such a bogus extent record, then the tree is really messed up
523 err = cmBadNews; // so return an error that conveys the disk is hosed.
524
525 FREE (tmpIterator, M_TEMP);
526 FREE (btIterator, M_TEMP);
527 return err;
528 }
529
530 do
531 {
532 btRecord.bufferAddress = &extentData;
533 btRecord.itemCount = 1;
534
535 for ( i=0 ; i<kNumExtentsToCache ; i++ )
536 {
537 HFSCatalogNodeID foundFileID;
538
539 err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
540 if ( err == btNotFound ) // Did we run out of extent records in the extents tree?
541 break; // if xkrFNum(A0) is cleared on this error, then this test is bogus!
542 else if ( err != noErr ) {
543 FREE (btIterator, M_TEMP);
544 FREE (tmpIterator, M_TEMP);
545 return( err ); // must be ioError
546 }
547 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
548 if ( foundFileID == srcFileID ) {
549 /* Check if we need to quit early. */
550 if (quitEarly && isHFSPlus) {
551 if (extentKeyPtr->hfsPlus.forkType != forkType) {
552 break;
553 }
554 }
555 CopyExtentInfo(extentKeyPtr, &extentData, extentsBuffer, i);
556 }
557 else{
558 /* The fileID's are of a different file. We're done here. */
559 break;
560 }
561 }
562
563
564
565 //-- edit each extent key, and reinsert each extent record in the extent file
566 if (isHFSPlus)
567 btRecordSize = sizeof(HFSPlusExtentRecord);
568 else
569 btRecordSize = sizeof(HFSExtentRecord);
570
571 for ( j=0 ; j<i ; j++ )
572 {
573
574 if (isHFSPlus)
575 extentsBuffer[j].extentKey.hfsPlus.fileID = destFileID; // change only the id in the key to dest ID
576 else
577 extentsBuffer[j].extentKey.hfs.fileID = destFileID; // change only the id in the key to dest ID
578
579 // get iterator and buffer descriptor ready...
580 (void) BTInvalidateHint(tmpIterator);
581 BlockMoveData(&(extentsBuffer[j].extentKey), &tmpIterator->key, btKeySize);
582 btRecord.bufferAddress = &(extentsBuffer[j].extentData);
583
584 err = BTInsertRecord(fcb, tmpIterator, &btRecord, btRecordSize);
585 if ( err != noErr ) {
586 /* Parse the error and free iterators */
587 FREE (btIterator, M_TEMP);
588 FREE (tmpIterator, M_TEMP);
589 if ( err == btExists )
590 {
591 if ( DEBUG_BUILD ) {
592 DebugStr("Can't insert record -- already exists");
593 }
594 return( cmBadNews );
595 }
596 else {
597 return( err );
598 }
599 }
600 }
601
602 //-- okay, done with this buffered batch, go get the next set of extent records
603 // If our buffer is not full, we must be done, or recieved an error
604
605 if ( i != kNumExtentsToCache ) // if the buffer is not full, we must be done
606 {
607 err = DeleteExtents( vcb, srcFileID, forkType, quitEarly, isHFSPlus ); // Now delete all the extent entries with the sourceID
608 if ( DEBUG_BUILD && err != noErr )
609 DebugStr("Error from DeleteExtents");
610 break; // we're done!
611 }
612 } while ( true );
613
614 FREE (tmpIterator, M_TEMP);
615 FREE (btIterator, M_TEMP);
616
617 return( err );
618 }
619
620
621 static void CopyExtentInfo( ExtentKey *key, ExtentRecord *data, ExtentsRecBuffer *buffer, u_int16_t bufferCount )
622 {
623 BlockMoveData( key, &(buffer[bufferCount].extentKey), sizeof( ExtentKey ) );
624 BlockMoveData( data, &(buffer[bufferCount].extentData), sizeof( ExtentRecord ) );
625 }
626
627
628 //-- Delete all extents in extent file that have the ID given.
629 static OSErr DeleteExtents( ExtendedVCB *vcb, u_int32_t fileID, int quitEarly, u_int8_t forkType, Boolean isHFSPlus )
630 {
631 FCB * fcb;
632 ExtentKey * extentKeyPtr;
633 ExtentRecord extentData;
634 struct BTreeIterator *btIterator = NULL;
635 struct BTreeIterator *tmpIterator = NULL;
636 FSBufferDescriptor btRecord;
637 u_int16_t btRecordSize;
638 OSErr err;
639
640
641 MALLOC (btIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
642 if (btIterator == NULL) {
643 return memFullErr; // translates to ENOMEM
644 }
645
646 MALLOC (tmpIterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
647 if (tmpIterator == NULL) {
648 FREE (btIterator, M_TEMP);
649 return memFullErr; // translates to ENOMEM
650 }
651
652 bzero(btIterator, sizeof(*btIterator));
653 bzero (tmpIterator, sizeof(*tmpIterator));
654
655 fcb = GetFileControlBlock(vcb->extentsRefNum);
656
657 (void) BTInvalidateHint(btIterator);
658 extentKeyPtr = (ExtentKey*) &btIterator->key;
659 btRecord.bufferAddress = &extentData;
660 btRecord.itemCount = 1;
661
662 // The algorithm is to position the BTree just before any extent records for fileID.
663 // Then just keep getting successive records. If the record is still for fileID,
664 // then delete it.
665
666 if (isHFSPlus) {
667 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
668
669 extentKeyPtr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
670 extentKeyPtr->hfsPlus.forkType = forkType;
671 extentKeyPtr->hfsPlus.pad = 0;
672 extentKeyPtr->hfsPlus.fileID = fileID;
673 extentKeyPtr->hfsPlus.startBlock = 0;
674 }
675 else {
676 btRecord.itemSize = sizeof(HFSExtentRecord);
677
678 extentKeyPtr->hfs.keyLength = kHFSExtentKeyMaximumLength;
679 extentKeyPtr->hfs.forkType = forkType;
680 extentKeyPtr->hfs.fileID = fileID;
681 extentKeyPtr->hfs.startBlock = 0;
682 }
683
684 err = BTSearchRecord(fcb, btIterator, &btRecord, &btRecordSize, btIterator);
685 if ( err != btNotFound )
686 {
687 if (err == noErr) { // Did we find a bogus extent record?
688 err = cmBadNews; // Yes, so indicate things are messed up.
689 }
690
691 return err; // Got some unexpected error, so return it
692 }
693
694 do
695 {
696 HFSCatalogNodeID foundFileID;
697
698 err = BTIterateRecord(fcb, kBTreeNextRecord, btIterator, &btRecord, &btRecordSize);
699 if ( err != noErr )
700 {
701 if (err == btNotFound) // If we hit the end of the BTree
702 err = noErr; // then it's OK
703
704 break; // We're done now.
705 }
706
707 foundFileID = isHFSPlus ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID;
708 if ( foundFileID != fileID ) {
709 break; // numbers don't match, we must be done
710 }
711 if (quitEarly && isHFSPlus) {
712 /* If we're only deleting one type of fork, then quit early if it doesn't match */
713 if (extentKeyPtr->hfsPlus.forkType != forkType) {
714 break;
715 }
716 }
717
718 *tmpIterator = *btIterator;
719 err = BTDeleteRecord( fcb, tmpIterator );
720 if (err != noErr)
721 break;
722 } while ( true );
723
724 FREE (tmpIterator, M_TEMP);
725 FREE (btIterator, M_TEMP);
726
727 return( err );
728 }
729
730
731 // Check if there are extents represented in the extents overflow file.
732 static u_int32_t CheckExtents( void *extents, u_int32_t totalBlocks, Boolean isHFSPlus )
733 {
734 u_int32_t extentAllocationBlocks;
735 u_int16_t i;
736
737
738 if ( totalBlocks == 0 )
739 return( 0 );
740
741 extentAllocationBlocks = 0;
742
743 if ( isHFSPlus )
744 {
745 for ( i = 0 ; i < kHFSPlusExtentDensity ; i++ )
746 {
747 extentAllocationBlocks += ((HFSPlusExtentDescriptor *)extents)[i].blockCount;
748 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
749 return( 0 );
750 }
751 }
752 else
753 {
754 for ( i = 0 ; i < kHFSExtentDensity ; i++ )
755 {
756 extentAllocationBlocks += ((HFSExtentDescriptor *)extents)[i].blockCount;
757 if ( extentAllocationBlocks >= totalBlocks ) // greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
758 return( 0 );
759 }
760 }
761
762 return( extentAllocationBlocks );
763 }