]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/dfalib/SBTree.c
hfs-556.41.1.tar.gz
[apple/hfs.git] / fsck_hfs / dfalib / SBTree.c
1 /*
2 * Copyright (c) 1999, 2002, 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 File: SBTree.c
25
26 Contains: Scavanger B-tree callback procs
27
28 Version: HFS Plus 1.0
29
30 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
31
32 */
33
34
35 #include "BTree.h"
36 #include "BTreePrivate.h"
37 #include "Scavenger.h"
38
39
40
41 // local routines
42 static void InvalidateBTreeIterator ( SFCB *fcb );
43 static OSErr CheckBTreeKey(const BTreeKey *key, const BTreeControlBlock *btcb);
44 static Boolean ValidHFSRecord(const void *record, const BTreeControlBlock *btcb, UInt16 recordSize);
45
46
47 // This function determines the size of the output buffer depending on
48 // the type of the BTree. It should really be taking the buffer size
49 // as input instead of assuming it (4425231).
50 //
51 // This function may also truncate inline attribute record because caller
52 // sends HFSPlusAttrRecord as output buffer and this function also assumes
53 // the output buffer of size HFSPlusAttrRecord. It may therefore not be
54 // enough to copy entire inline attribute record (4425232).
55
56 OSErr SearchBTreeRecord(SFCB *fcb, const void* key, UInt32 hint, void* foundKey, void* data, UInt16 *dataSize, UInt32 *newHint)
57 {
58 FSBufferDescriptor btRecord;
59 BTreeIterator searchIterator;
60 BTreeIterator *resultIterator;
61 BTreeControlBlock *btcb;
62 OSStatus result;
63
64
65 btcb = (BTreeControlBlock*) fcb->fcbBtree;
66
67 btRecord.bufferAddress = data;
68 btRecord.itemCount = 1;
69 if ( btcb->maxKeyLength == kHFSExtentKeyMaximumLength )
70 btRecord.itemSize = sizeof(HFSExtentRecord);
71 else if ( btcb->maxKeyLength == kHFSPlusExtentKeyMaximumLength )
72 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
73 else if ( btcb->maxKeyLength == kHFSPlusAttrKeyMaximumLength )
74 btRecord.itemSize = sizeof(HFSPlusAttrRecord);
75 else
76 btRecord.itemSize = sizeof(CatalogRecord);
77
78 searchIterator.hint.writeCount = 0; // clear these out for debugging...
79 searchIterator.hint.reserved1 = 0;
80 searchIterator.hint.reserved2 = 0;
81
82 searchIterator.hint.nodeNum = hint;
83 searchIterator.hint.index = 0;
84
85 result = CheckBTreeKey((BTreeKey *) key, btcb);
86 ExitOnError(result);
87
88 CopyMemory(key, &searchIterator.key, CalcKeySize(btcb, (BTreeKey *) key)); //¥¥ should we range check against maxkeylen?
89
90 resultIterator = &btcb->lastIterator;
91
92 result = BTSearchRecord( fcb, &searchIterator, kInvalidMRUCacheKey, &btRecord, dataSize, resultIterator );
93 if (result == noErr)
94 {
95 if (newHint != NULL)
96 *newHint = resultIterator->hint.nodeNum;
97
98 result = CheckBTreeKey(&resultIterator->key, btcb);
99 ExitOnError(result);
100
101 if (foundKey != NULL)
102 CopyMemory(&resultIterator->key, foundKey, CalcKeySize(btcb, &resultIterator->key)); //¥¥ warning, this could overflow user's buffer!!!
103
104 if ( DEBUG_BUILD && !ValidHFSRecord(data, btcb, *dataSize) )
105 DebugStr("\pSearchBTreeRecord: bad record?");
106 }
107
108 ErrorExit:
109
110 return result;
111 }
112
113
114
115 // Note
116 // The new B-tree manager differs from the original b-tree in how it does iteration. We need
117 // to account for these differences here. We save an iterator in the BTree control block so
118 // that we have a context in which to perfrom the iteration. Also note that the old B-tree
119 // allowed you to specify any number relative to the last operation (including 0) whereas the
120 // new B-tree only allows next/previous.
121 //
122 // This function determines the size of the output buffer depending on
123 // the type of the BTree. It should really be taking the buffer size
124 // as input instead of assuming it (4425231).
125 //
126 // This function may also truncate inline attribute record because caller
127 // sends HFSPlusAttrRecord as output buffer and this function also assumes
128 // the output buffer of size HFSPlusAttrRecord. It may therefore not be
129 // enough to copy entire inline attribute record (4425232).
130
131 OSErr GetBTreeRecord(SFCB *fcb, SInt16 selectionIndex, void* key, void* data, UInt16 *dataSize, UInt32 *newHint)
132 {
133 FSBufferDescriptor btRecord;
134 BTreeIterator *iterator;
135 BTreeControlBlock *btcb;
136 OSStatus result;
137 UInt16 operation;
138
139
140 // pick up our iterator in the BTCB for context...
141
142 btcb = (BTreeControlBlock*) fcb->fcbBtree;
143 iterator = &btcb->lastIterator;
144
145 btRecord.bufferAddress = data;
146 btRecord.itemCount = 1;
147 if ( btcb->maxKeyLength == kHFSExtentKeyMaximumLength )
148 btRecord.itemSize = sizeof(HFSExtentRecord);
149 else if ( btcb->maxKeyLength == kHFSPlusExtentKeyMaximumLength )
150 btRecord.itemSize = sizeof(HFSPlusExtentRecord);
151 else if ( btcb->maxKeyLength == kHFSPlusAttrKeyMaximumLength )
152 btRecord.itemSize = sizeof(HFSPlusAttrRecord);
153 else
154 btRecord.itemSize = sizeof(CatalogRecord);
155
156 // now we have to map index into next/prev operations...
157
158 if (selectionIndex == 1)
159 {
160 operation = kBTreeNextRecord;
161 }
162 else if (selectionIndex == -1)
163 {
164 operation = kBTreePrevRecord;
165 }
166 else if (selectionIndex == 0)
167 {
168 operation = kBTreeCurrentRecord;
169 }
170 else if (selectionIndex == (SInt16) 0x8001)
171 {
172 operation = kBTreeFirstRecord;
173 }
174 else if (selectionIndex == (SInt16) 0x7FFF)
175 {
176 operation = kBTreeLastRecord;
177 }
178 else if (selectionIndex > 1)
179 {
180 UInt32 i;
181
182 for (i = 1; i < selectionIndex; ++i)
183 {
184 result = BTIterateRecord( fcb, kBTreeNextRecord, iterator, &btRecord, dataSize );
185 ExitOnError(result);
186 }
187 operation = kBTreeNextRecord;
188 }
189 else // (selectionIndex < -1)
190 {
191 SInt32 i;
192
193 for (i = -1; i > selectionIndex; --i)
194 {
195 result = BTIterateRecord( fcb, kBTreePrevRecord, iterator, &btRecord, dataSize );
196 ExitOnError(result);
197 }
198 operation = kBTreePrevRecord;
199 }
200
201 result = BTIterateRecord( fcb, operation, iterator, &btRecord, dataSize );
202
203 if (result == noErr)
204 {
205 *newHint = iterator->hint.nodeNum;
206
207 result = CheckBTreeKey(&iterator->key, btcb);
208 ExitOnError(result);
209
210 CopyMemory(&iterator->key, key, CalcKeySize(btcb, &iterator->key)); //¥¥ warning, this could overflow user's buffer!!!
211
212 if ( DEBUG_BUILD && !ValidHFSRecord(data, btcb, *dataSize) )
213 DebugStr("\pGetBTreeRecord: bad record?");
214
215 }
216
217 ErrorExit:
218
219 return result;
220 }
221
222
223 OSErr InsertBTreeRecord(SFCB *fcb, const void* key, const void* data, UInt16 dataSize, UInt32 *newHint)
224 {
225 FSBufferDescriptor btRecord;
226 BTreeIterator iterator;
227 BTreeControlBlock *btcb;
228 OSStatus result;
229
230
231 btcb = (BTreeControlBlock*) fcb->fcbBtree;
232
233 btRecord.bufferAddress = (void *)data;
234 btRecord.itemSize = dataSize;
235 btRecord.itemCount = 1;
236
237 iterator.hint.nodeNum = 0; // no hint
238
239 result = CheckBTreeKey((BTreeKey *) key, btcb);
240 ExitOnError(result);
241
242 CopyMemory(key, &iterator.key, CalcKeySize(btcb, (BTreeKey *) key)); //¥¥ should we range check against maxkeylen?
243
244 if ( DEBUG_BUILD && !ValidHFSRecord(data, btcb, dataSize) )
245 DebugStr("\pInsertBTreeRecord: bad record?");
246
247 result = BTInsertRecord( fcb, &iterator, &btRecord, dataSize );
248
249 *newHint = iterator.hint.nodeNum;
250
251 InvalidateBTreeIterator(fcb); // invalidate current record markers
252
253 ErrorExit:
254
255 return result;
256 }
257
258
259 OSErr DeleteBTreeRecord(SFCB *fcb, const void* key)
260 {
261 BTreeIterator iterator;
262 BTreeControlBlock *btcb;
263 OSStatus result;
264
265
266 btcb = (BTreeControlBlock*) fcb->fcbBtree;
267
268 iterator.hint.nodeNum = 0; // no hint
269
270 result = CheckBTreeKey((BTreeKey *) key, btcb);
271 ExitOnError(result);
272
273 CopyMemory(key, &iterator.key, CalcKeySize(btcb, (BTreeKey *) key)); //¥¥ should we range check against maxkeylen?
274
275 result = BTDeleteRecord( fcb, &iterator );
276
277 InvalidateBTreeIterator(fcb); // invalidate current record markers
278
279 ErrorExit:
280
281 return result;
282 }
283
284
285 OSErr ReplaceBTreeRecord(SFCB *fcb, const void* key, UInt32 hint, void *newData, UInt16 dataSize, UInt32 *newHint)
286 {
287 FSBufferDescriptor btRecord;
288 BTreeIterator iterator;
289 BTreeControlBlock *btcb;
290 OSStatus result;
291
292
293 btcb = (BTreeControlBlock*) fcb->fcbBtree;
294
295 btRecord.bufferAddress = newData;
296 btRecord.itemSize = dataSize;
297 btRecord.itemCount = 1;
298
299 iterator.hint.nodeNum = hint;
300
301 result = CheckBTreeKey((BTreeKey *) key, btcb);
302 ExitOnError(result);
303
304 CopyMemory(key, &iterator.key, CalcKeySize(btcb, (BTreeKey *) key)); //¥¥ should we range check against maxkeylen?
305
306 if ( DEBUG_BUILD && !ValidHFSRecord(newData, btcb, dataSize) )
307 DebugStr("\pReplaceBTreeRecord: bad record?");
308
309 result = BTReplaceRecord( fcb, &iterator, &btRecord, dataSize );
310
311 *newHint = iterator.hint.nodeNum;
312
313 //¥¥Êdo we need to invalidate the iterator?
314
315 ErrorExit:
316
317 return result;
318 }
319
320
321 OSStatus
322 SetEndOfForkProc ( SFCB *filePtr, FSSize minEOF, FSSize maxEOF )
323 {
324 #pragma unused (maxEOF)
325
326 OSStatus result;
327 UInt32 actualSectorsAdded;
328 UInt64 bytesToAdd;
329 UInt64 fileSize; // in sectors
330 SVCB * vcb;
331 UInt32 flags;
332
333
334 if ( minEOF > filePtr->fcbLogicalSize )
335 {
336 bytesToAdd = minEOF - filePtr->fcbLogicalSize;
337
338 if (bytesToAdd < filePtr->fcbClumpSize)
339 bytesToAdd = filePtr->fcbClumpSize; //¥¥Êwhy not always be a mutiple of clump size ???
340 }
341 else
342 {
343 if ( DEBUG_BUILD )
344 DebugStr("\pSetEndOfForkProc: minEOF is smaller than current size!");
345 return -1;
346 }
347
348 vcb = filePtr->fcbVolume;
349
350 flags = kEFNoClumpMask;
351
352 // Due to time contraints we force the new rebuilt catalog file to be contiguous.
353 // It's hard to handle catalog file in extents because we have to do a swap
354 // of the old catalog file with the rebuilt catalog file at the end of
355 // the rebuild process. Extent records use the file ID as part of the key so
356 // it would be messy to fix them after the swap.
357 if ( filePtr->fcbFileID == kHFSRepairCatalogFileID)
358 flags |= kEFNoExtOvflwMask;
359
360 result = ExtendFileC ( vcb, filePtr, (UInt32)((bytesToAdd+511)>>9), flags, &actualSectorsAdded );
361 ReturnIfError(result);
362
363 filePtr->fcbLogicalSize = filePtr->fcbPhysicalSize; // new B-tree looks at fcbEOF
364 fileSize = filePtr->fcbLogicalSize >> 9; // get size in sectors (for calls to ZeroFileBlocks)
365
366 //
367 // Make sure we got at least as much space as we needed
368 //
369 if (filePtr->fcbLogicalSize < minEOF) {
370 Panic("\pSetEndOfForkProc: disk too full to extend B-tree file");
371 return dskFulErr;
372 }
373
374 //
375 // Update the Alternate MDB or Alternate HFSPlusVolumeHeader
376 //
377 if ( vcb->vcbSignature == kHFSPlusSigWord )
378 {
379 // If any of the HFS+ private files change size, flush them back to the Alternate volume header
380 if ( (filePtr->fcbFileID == kHFSExtentsFileID)
381 || (filePtr->fcbFileID == kHFSCatalogFileID)
382 || (filePtr->fcbFileID == kHFSStartupFileID)
383 || (filePtr->fcbFileID == kHFSAttributesFileID)
384 || (filePtr->fcbFileID == kHFSRepairCatalogFileID) )
385 {
386 MarkVCBDirty( vcb );
387 result = FlushAlternateVolumeControlBlock( vcb, true );
388
389 // Zero newly allocated portion of HFS+ private file.
390 if ( result == noErr )
391 result = ZeroFileBlocks( vcb, filePtr, (UInt32)(fileSize - actualSectorsAdded), actualSectorsAdded );
392 }
393 }
394 else if ( vcb->vcbSignature == kHFSSigWord )
395 {
396 if ( filePtr->fcbFileID == kHFSExtentsFileID )
397 {
398 // vcb->vcbXTAlBlks = filePtr->fcbPhysicalSize / vcb->vcbBlockSize;
399 MarkVCBDirty( vcb );
400 result = FlushAlternateVolumeControlBlock( vcb, false );
401 if ( result == noErr )
402 result = ZeroFileBlocks( vcb, filePtr, (UInt32)(fileSize - actualSectorsAdded), actualSectorsAdded );
403 }
404 else if ( filePtr->fcbFileID == kHFSCatalogFileID || filePtr->fcbFileID == kHFSRepairCatalogFileID )
405 {
406 // vcb->vcbCTAlBlks = filePtr->fcbPhysicalSize / vcb->vcbBlockSize;
407 MarkVCBDirty( vcb );
408 result = FlushAlternateVolumeControlBlock( vcb, false );
409 if ( result == noErr )
410 result = ZeroFileBlocks( vcb, filePtr, (UInt32)(fileSize - actualSectorsAdded), actualSectorsAdded );
411 }
412 }
413
414 return result;
415
416 } // end SetEndOfForkProc
417
418
419 static void
420 InvalidateBTreeIterator(SFCB *fcb)
421 {
422 BTreeControlBlock *btcb;
423
424 btcb = (BTreeControlBlock*) fcb->fcbBtree;
425
426 (void) BTInvalidateHint ( &btcb->lastIterator );
427 }
428
429
430 static OSErr CheckBTreeKey(const BTreeKey *key, const BTreeControlBlock *btcb)
431 {
432 UInt16 keyLen;
433
434 if ( btcb->attributes & kBTBigKeysMask )
435 keyLen = key->length16;
436 else
437 keyLen = key->length8;
438
439 if ( (keyLen < 6) || (keyLen > btcb->maxKeyLength) )
440 {
441 if ( DEBUG_BUILD )
442 DebugStr("\pCheckBTreeKey: bad key length!");
443 return fsBTInvalidKeyLengthErr;
444 }
445
446 return noErr;
447 }
448
449
450 static Boolean ValidHFSRecord(const void *record, const BTreeControlBlock *btcb, UInt16 recordSize)
451 {
452 UInt32 cNodeID;
453
454 if ( btcb->maxKeyLength == kHFSExtentKeyMaximumLength )
455 {
456 return ( recordSize == sizeof(HFSExtentRecord) );
457 }
458 else if (btcb->maxKeyLength == kHFSPlusExtentKeyMaximumLength )
459 {
460 return ( recordSize == sizeof(HFSPlusExtentRecord) );
461 }
462 else if (btcb->maxKeyLength == kAttributeKeyMaximumLength )
463 {
464 HFSPlusAttrRecord *attributeRecord = (HFSPlusAttrRecord *) record;
465
466 switch (attributeRecord->recordType) {
467 case kHFSPlusAttrInlineData:
468 break;
469
470 case kHFSPlusAttrForkData:
471 break;
472
473 case kHFSPlusAttrExtents:
474 break;
475 }
476 }
477 else // Catalog record
478 {
479 CatalogRecord *catalogRecord = (CatalogRecord*) record;
480
481 switch(catalogRecord->recordType)
482 {
483 case kHFSFolderRecord:
484 {
485 if ( recordSize != sizeof(HFSCatalogFolder) )
486 return false;
487 if ( catalogRecord->hfsFolder.flags != 0 )
488 return false;
489 if ( catalogRecord->hfsFolder.valence > 0x7FFF )
490 return false;
491
492 cNodeID = catalogRecord->hfsFolder.folderID;
493
494 if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
495 return false;
496 }
497 break;
498
499 case kHFSPlusFolderRecord:
500 {
501 if ( recordSize != sizeof(HFSPlusCatalogFolder) )
502 return false;
503 if ( (catalogRecord->hfsPlusFolder.flags & (kHFSFileLockedMask | kHFSThreadExistsMask)) != 0 )
504 return false;
505
506 cNodeID = catalogRecord->hfsPlusFolder.folderID;
507
508 if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
509 return false;
510 }
511 break;
512
513 case kHFSFileRecord:
514 {
515 UInt16 i;
516 HFSExtentDescriptor *dataExtent;
517 HFSExtentDescriptor *rsrcExtent;
518
519 if ( recordSize != sizeof(HFSCatalogFile) )
520 return false;
521 if ( (catalogRecord->hfsFile.flags & ~(0x83)) != 0 )
522 return false;
523
524 cNodeID = catalogRecord->hfsFile.fileID;
525
526 if ( cNodeID < 16 )
527 return false;
528
529 // make sure 0 ² LEOF ² PEOF for both forks
530
531 if ( catalogRecord->hfsFile.dataLogicalSize < 0 )
532 return false;
533 if ( catalogRecord->hfsFile.dataPhysicalSize < catalogRecord->hfsFile.dataLogicalSize )
534 return false;
535 if ( catalogRecord->hfsFile.rsrcLogicalSize < 0 )
536 return false;
537 if ( catalogRecord->hfsFile.rsrcPhysicalSize < catalogRecord->hfsFile.rsrcLogicalSize )
538 return false;
539
540 dataExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.dataExtents;
541 rsrcExtent = (HFSExtentDescriptor*) &catalogRecord->hfsFile.rsrcExtents;
542
543 for (i = 0; i < kHFSExtentDensity; ++i)
544 {
545 if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
546 return false;
547 if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
548 return false;
549 }
550 }
551 break;
552
553 case kHFSPlusFileRecord:
554 {
555 UInt16 i;
556 HFSPlusExtentDescriptor *dataExtent;
557 HFSPlusExtentDescriptor *rsrcExtent;
558
559 if ( recordSize != sizeof(HFSPlusCatalogFile) )
560 return false;
561
562 cNodeID = catalogRecord->hfsPlusFile.fileID;
563
564 if ( cNodeID < 16 )
565 return false;
566
567 //¥¥ make sure 0 ² LEOF ² PEOF for both forks
568
569 dataExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.dataFork.extents;
570 rsrcExtent = (HFSPlusExtentDescriptor*) &catalogRecord->hfsPlusFile.resourceFork.extents;
571
572 for (i = 0; i < kHFSPlusExtentDensity; ++i)
573 {
574 if ( (dataExtent[i].blockCount > 0) && (dataExtent[i].startBlock == 0) )
575 return false;
576 if ( (rsrcExtent[i].blockCount > 0) && (rsrcExtent[i].startBlock == 0) )
577 return false;
578 }
579 }
580 break;
581
582 case kHFSFolderThreadRecord:
583 case kHFSFileThreadRecord:
584 {
585 if ( recordSize != sizeof(HFSCatalogThread) )
586 return false;
587
588 cNodeID = catalogRecord->hfsThread.parentID;
589 if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
590 return false;
591
592 if ( (catalogRecord->hfsThread.nodeName[0] == 0) ||
593 (catalogRecord->hfsThread.nodeName[0] > 31) )
594 return false;
595 }
596 break;
597
598 case kHFSPlusFolderThreadRecord:
599 case kHFSPlusFileThreadRecord:
600 {
601 if ( recordSize > sizeof(HFSPlusCatalogThread) || recordSize < (sizeof(HFSPlusCatalogThread) - sizeof(HFSUniStr255)))
602 return false;
603
604 cNodeID = catalogRecord->hfsPlusThread.parentID;
605 if ( (cNodeID == 0) || (cNodeID < 16 && cNodeID > 2) )
606 return false;
607
608 if ( (catalogRecord->hfsPlusThread.nodeName.length == 0) ||
609 (catalogRecord->hfsPlusThread.nodeName.length > 255) )
610 return false;
611 }
612 break;
613
614 default:
615 return false;
616 }
617 }
618
619 return true; // record appears to be OK
620 }
621