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