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