2 * Copyright (c) 1999, 2002, 2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: Scavanger B-tree callback procs
30 Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved.
36 #include "BTreePrivate.h"
37 #include "Scavenger.h"
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
);
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).
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).
56 OSErr
SearchBTreeRecord(SFCB
*fcb
, const void* key
, UInt32 hint
, void* foundKey
, void* data
, UInt16
*dataSize
, UInt32
*newHint
)
58 FSBufferDescriptor btRecord
;
59 BTreeIterator searchIterator
;
60 BTreeIterator
*resultIterator
;
61 BTreeControlBlock
*btcb
;
65 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
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
);
76 btRecord
.itemSize
= sizeof(CatalogRecord
);
78 searchIterator
.hint
.writeCount
= 0; // clear these out for debugging...
79 searchIterator
.hint
.reserved1
= 0;
80 searchIterator
.hint
.reserved2
= 0;
82 searchIterator
.hint
.nodeNum
= hint
;
83 searchIterator
.hint
.index
= 0;
85 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
88 CopyMemory(key
, &searchIterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //¥¥ should we range check against maxkeylen?
90 resultIterator
= &btcb
->lastIterator
;
92 result
= BTSearchRecord( fcb
, &searchIterator
, kInvalidMRUCacheKey
, &btRecord
, dataSize
, resultIterator
);
96 *newHint
= resultIterator
->hint
.nodeNum
;
98 result
= CheckBTreeKey(&resultIterator
->key
, btcb
);
101 if (foundKey
!= NULL
)
102 CopyMemory(&resultIterator
->key
, foundKey
, CalcKeySize(btcb
, &resultIterator
->key
)); //¥¥ warning, this could overflow user's buffer!!!
104 if ( DEBUG_BUILD
&& !ValidHFSRecord(data
, btcb
, *dataSize
) )
105 DebugStr("\pSearchBTreeRecord: bad record?");
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.
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).
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).
131 OSErr
GetBTreeRecord(SFCB
*fcb
, SInt16 selectionIndex
, void* key
, void* data
, UInt16
*dataSize
, UInt32
*newHint
)
133 FSBufferDescriptor btRecord
;
134 BTreeIterator
*iterator
;
135 BTreeControlBlock
*btcb
;
140 // pick up our iterator in the BTCB for context...
142 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
143 iterator
= &btcb
->lastIterator
;
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
);
154 btRecord
.itemSize
= sizeof(CatalogRecord
);
156 // now we have to map index into next/prev operations...
158 if (selectionIndex
== 1)
160 operation
= kBTreeNextRecord
;
162 else if (selectionIndex
== -1)
164 operation
= kBTreePrevRecord
;
166 else if (selectionIndex
== 0)
168 operation
= kBTreeCurrentRecord
;
170 else if (selectionIndex
== (SInt16
) 0x8001)
172 operation
= kBTreeFirstRecord
;
174 else if (selectionIndex
== (SInt16
) 0x7FFF)
176 operation
= kBTreeLastRecord
;
178 else if (selectionIndex
> 1)
182 for (i
= 1; i
< selectionIndex
; ++i
)
184 result
= BTIterateRecord( fcb
, kBTreeNextRecord
, iterator
, &btRecord
, dataSize
);
187 operation
= kBTreeNextRecord
;
189 else // (selectionIndex < -1)
193 for (i
= -1; i
> selectionIndex
; --i
)
195 result
= BTIterateRecord( fcb
, kBTreePrevRecord
, iterator
, &btRecord
, dataSize
);
198 operation
= kBTreePrevRecord
;
201 result
= BTIterateRecord( fcb
, operation
, iterator
, &btRecord
, dataSize
);
205 *newHint
= iterator
->hint
.nodeNum
;
207 result
= CheckBTreeKey(&iterator
->key
, btcb
);
210 CopyMemory(&iterator
->key
, key
, CalcKeySize(btcb
, &iterator
->key
)); //¥¥ warning, this could overflow user's buffer!!!
212 if ( DEBUG_BUILD
&& !ValidHFSRecord(data
, btcb
, *dataSize
) )
213 DebugStr("\pGetBTreeRecord: bad record?");
223 OSErr
InsertBTreeRecord(SFCB
*fcb
, const void* key
, const void* data
, UInt16 dataSize
, UInt32
*newHint
)
225 FSBufferDescriptor btRecord
;
226 BTreeIterator iterator
;
227 BTreeControlBlock
*btcb
;
231 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
233 btRecord
.bufferAddress
= (void *)data
;
234 btRecord
.itemSize
= dataSize
;
235 btRecord
.itemCount
= 1;
237 iterator
.hint
.nodeNum
= 0; // no hint
239 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
242 CopyMemory(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //¥¥ should we range check against maxkeylen?
244 if ( DEBUG_BUILD
&& !ValidHFSRecord(data
, btcb
, dataSize
) )
245 DebugStr("\pInsertBTreeRecord: bad record?");
247 result
= BTInsertRecord( fcb
, &iterator
, &btRecord
, dataSize
);
249 *newHint
= iterator
.hint
.nodeNum
;
251 InvalidateBTreeIterator(fcb
); // invalidate current record markers
259 OSErr
DeleteBTreeRecord(SFCB
*fcb
, const void* key
)
261 BTreeIterator iterator
;
262 BTreeControlBlock
*btcb
;
266 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
268 iterator
.hint
.nodeNum
= 0; // no hint
270 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
273 CopyMemory(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //¥¥ should we range check against maxkeylen?
275 result
= BTDeleteRecord( fcb
, &iterator
);
277 InvalidateBTreeIterator(fcb
); // invalidate current record markers
285 OSErr
ReplaceBTreeRecord(SFCB
*fcb
, const void* key
, UInt32 hint
, void *newData
, UInt16 dataSize
, UInt32
*newHint
)
287 FSBufferDescriptor btRecord
;
288 BTreeIterator iterator
;
289 BTreeControlBlock
*btcb
;
293 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
295 btRecord
.bufferAddress
= newData
;
296 btRecord
.itemSize
= dataSize
;
297 btRecord
.itemCount
= 1;
299 iterator
.hint
.nodeNum
= hint
;
301 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
304 CopyMemory(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //¥¥ should we range check against maxkeylen?
306 if ( DEBUG_BUILD
&& !ValidHFSRecord(newData
, btcb
, dataSize
) )
307 DebugStr("\pReplaceBTreeRecord: bad record?");
309 result
= BTReplaceRecord( fcb
, &iterator
, &btRecord
, dataSize
);
311 *newHint
= iterator
.hint
.nodeNum
;
313 //¥¥Êdo we need to invalidate the iterator?
322 SetEndOfForkProc ( SFCB
*filePtr
, FSSize minEOF
, FSSize maxEOF
)
324 #pragma unused (maxEOF)
327 UInt32 actualSectorsAdded
;
329 UInt64 fileSize
; // in sectors
334 if ( minEOF
> filePtr
->fcbLogicalSize
)
336 bytesToAdd
= minEOF
- filePtr
->fcbLogicalSize
;
338 if (bytesToAdd
< filePtr
->fcbClumpSize
)
339 bytesToAdd
= filePtr
->fcbClumpSize
; //¥¥Êwhy not always be a mutiple of clump size ???
344 DebugStr("\pSetEndOfForkProc: minEOF is smaller than current size!");
348 vcb
= filePtr
->fcbVolume
;
350 flags
= kEFNoClumpMask
;
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
;
360 result
= ExtendFileC ( vcb
, filePtr
, (UInt32
)((bytesToAdd
+511)>>9), flags
, &actualSectorsAdded
);
361 ReturnIfError(result
);
363 filePtr
->fcbLogicalSize
= filePtr
->fcbPhysicalSize
; // new B-tree looks at fcbEOF
364 fileSize
= filePtr
->fcbLogicalSize
>> 9; // get size in sectors (for calls to ZeroFileBlocks)
367 // Make sure we got at least as much space as we needed
369 if (filePtr
->fcbLogicalSize
< minEOF
) {
370 Panic("\pSetEndOfForkProc: disk too full to extend B-tree file");
375 // Update the Alternate MDB or Alternate HFSPlusVolumeHeader
377 if ( vcb
->vcbSignature
== kHFSPlusSigWord
)
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
) )
387 result
= FlushAlternateVolumeControlBlock( vcb
, true );
389 // Zero newly allocated portion of HFS+ private file.
390 if ( result
== noErr
)
391 result
= ZeroFileBlocks( vcb
, filePtr
, (UInt32
)(fileSize
- actualSectorsAdded
), actualSectorsAdded
);
394 else if ( vcb
->vcbSignature
== kHFSSigWord
)
396 if ( filePtr
->fcbFileID
== kHFSExtentsFileID
)
398 // vcb->vcbXTAlBlks = filePtr->fcbPhysicalSize / vcb->vcbBlockSize;
400 result
= FlushAlternateVolumeControlBlock( vcb
, false );
401 if ( result
== noErr
)
402 result
= ZeroFileBlocks( vcb
, filePtr
, (UInt32
)(fileSize
- actualSectorsAdded
), actualSectorsAdded
);
404 else if ( filePtr
->fcbFileID
== kHFSCatalogFileID
|| filePtr
->fcbFileID
== kHFSRepairCatalogFileID
)
406 // vcb->vcbCTAlBlks = filePtr->fcbPhysicalSize / vcb->vcbBlockSize;
408 result
= FlushAlternateVolumeControlBlock( vcb
, false );
409 if ( result
== noErr
)
410 result
= ZeroFileBlocks( vcb
, filePtr
, (UInt32
)(fileSize
- actualSectorsAdded
), actualSectorsAdded
);
416 } // end SetEndOfForkProc
420 InvalidateBTreeIterator(SFCB
*fcb
)
422 BTreeControlBlock
*btcb
;
424 btcb
= (BTreeControlBlock
*) fcb
->fcbBtree
;
426 (void) BTInvalidateHint ( &btcb
->lastIterator
);
430 static OSErr
CheckBTreeKey(const BTreeKey
*key
, const BTreeControlBlock
*btcb
)
434 if ( btcb
->attributes
& kBTBigKeysMask
)
435 keyLen
= key
->length16
;
437 keyLen
= key
->length8
;
439 if ( (keyLen
< 6) || (keyLen
> btcb
->maxKeyLength
) )
442 DebugStr("\pCheckBTreeKey: bad key length!");
443 return fsBTInvalidKeyLengthErr
;
450 static Boolean
ValidHFSRecord(const void *record
, const BTreeControlBlock
*btcb
, UInt16 recordSize
)
454 if ( btcb
->maxKeyLength
== kHFSExtentKeyMaximumLength
)
456 return ( recordSize
== sizeof(HFSExtentRecord
) );
458 else if (btcb
->maxKeyLength
== kHFSPlusExtentKeyMaximumLength
)
460 return ( recordSize
== sizeof(HFSPlusExtentRecord
) );
462 else if (btcb
->maxKeyLength
== kAttributeKeyMaximumLength
)
464 HFSPlusAttrRecord
*attributeRecord
= (HFSPlusAttrRecord
*) record
;
466 switch (attributeRecord
->recordType
) {
467 case kHFSPlusAttrInlineData
:
470 case kHFSPlusAttrForkData
:
473 case kHFSPlusAttrExtents
:
477 else // Catalog record
479 CatalogRecord
*catalogRecord
= (CatalogRecord
*) record
;
481 switch(catalogRecord
->recordType
)
483 case kHFSFolderRecord
:
485 if ( recordSize
!= sizeof(HFSCatalogFolder
) )
487 if ( catalogRecord
->hfsFolder
.flags
!= 0 )
489 if ( catalogRecord
->hfsFolder
.valence
> 0x7FFF )
492 cNodeID
= catalogRecord
->hfsFolder
.folderID
;
494 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
499 case kHFSPlusFolderRecord
:
501 if ( recordSize
!= sizeof(HFSPlusCatalogFolder
) )
503 if ( (catalogRecord
->hfsPlusFolder
.flags
& (kHFSFileLockedMask
| kHFSThreadExistsMask
)) != 0 )
506 cNodeID
= catalogRecord
->hfsPlusFolder
.folderID
;
508 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
516 HFSExtentDescriptor
*dataExtent
;
517 HFSExtentDescriptor
*rsrcExtent
;
519 if ( recordSize
!= sizeof(HFSCatalogFile
) )
521 if ( (catalogRecord
->hfsFile
.flags
& ~(0x83)) != 0 )
524 cNodeID
= catalogRecord
->hfsFile
.fileID
;
529 // make sure 0 ² LEOF ² PEOF for both forks
531 if ( catalogRecord
->hfsFile
.dataLogicalSize
< 0 )
533 if ( catalogRecord
->hfsFile
.dataPhysicalSize
< catalogRecord
->hfsFile
.dataLogicalSize
)
535 if ( catalogRecord
->hfsFile
.rsrcLogicalSize
< 0 )
537 if ( catalogRecord
->hfsFile
.rsrcPhysicalSize
< catalogRecord
->hfsFile
.rsrcLogicalSize
)
540 dataExtent
= (HFSExtentDescriptor
*) &catalogRecord
->hfsFile
.dataExtents
;
541 rsrcExtent
= (HFSExtentDescriptor
*) &catalogRecord
->hfsFile
.rsrcExtents
;
543 for (i
= 0; i
< kHFSExtentDensity
; ++i
)
545 if ( (dataExtent
[i
].blockCount
> 0) && (dataExtent
[i
].startBlock
== 0) )
547 if ( (rsrcExtent
[i
].blockCount
> 0) && (rsrcExtent
[i
].startBlock
== 0) )
553 case kHFSPlusFileRecord
:
556 HFSPlusExtentDescriptor
*dataExtent
;
557 HFSPlusExtentDescriptor
*rsrcExtent
;
559 if ( recordSize
!= sizeof(HFSPlusCatalogFile
) )
562 cNodeID
= catalogRecord
->hfsPlusFile
.fileID
;
567 //¥¥ make sure 0 ² LEOF ² PEOF for both forks
569 dataExtent
= (HFSPlusExtentDescriptor
*) &catalogRecord
->hfsPlusFile
.dataFork
.extents
;
570 rsrcExtent
= (HFSPlusExtentDescriptor
*) &catalogRecord
->hfsPlusFile
.resourceFork
.extents
;
572 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
)
574 if ( (dataExtent
[i
].blockCount
> 0) && (dataExtent
[i
].startBlock
== 0) )
576 if ( (rsrcExtent
[i
].blockCount
> 0) && (rsrcExtent
[i
].startBlock
== 0) )
582 case kHFSFolderThreadRecord
:
583 case kHFSFileThreadRecord
:
585 if ( recordSize
!= sizeof(HFSCatalogThread
) )
588 cNodeID
= catalogRecord
->hfsThread
.parentID
;
589 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
592 if ( (catalogRecord
->hfsThread
.nodeName
[0] == 0) ||
593 (catalogRecord
->hfsThread
.nodeName
[0] > 31) )
598 case kHFSPlusFolderThreadRecord
:
599 case kHFSPlusFileThreadRecord
:
601 if ( recordSize
> sizeof(HFSPlusCatalogThread
) || recordSize
< (sizeof(HFSPlusCatalogThread
) - sizeof(HFSUniStr255
)))
604 cNodeID
= catalogRecord
->hfsPlusThread
.parentID
;
605 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
608 if ( (catalogRecord
->hfsPlusThread
.nodeName
.length
== 0) ||
609 (catalogRecord
->hfsPlusThread
.nodeName
.length
> 255) )
619 return true; // record appears to be OK