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