2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Interface glue for new B-tree manager.
29 Copyright: © 1996-1998 by Apple Computer, Inc., all rights reserved.
35 Other Contact: Mark Day
37 Technology: xxx put technology here xxx
45 Change History (most recent first):
46 <MacOSX> 8/10/98 djb Removed all references to btcb global iterator (lastIterator).
47 <MacOSX> 04/02/98 djb GetBTreeRecord is only used for MacOS builds.
48 <MacOSX> 03/31/98 djb Sync up with final HFSVolumes.h header file.
49 <CS18> 9/4/97 msd Fix ValidHFSRecord to determine the type of B-tree by FileID,
50 not record size. Add better checking for attribute b-tree keys.
51 <CS17> 8/22/97 djb Get blockReadFromDisk flag from GetCacheBlock call.
52 <CS16> 8/14/97 djb Remove reserved field checks in ValidHFSRecord (radar #1649593).
53 Only call if ValidHFSRecord HFS_DIAGNOSTIC is true.
54 <CS15> 8/11/97 djb Bug 1670441. In SetEndOfForkProc, don't DebugStr if the disk is
56 <CS14> 7/25/97 DSH Pass heuristicHint to BTSearchRecord from SearchBTreeRecord.
57 <CS13> 7/24/97 djb CallBackProcs now take a file refNum instead of an FCB.
58 GetBlockProc now reports if block came from disk.
59 <CS12> 7/22/97 djb Move all trace points to BTree.c file.
60 <CS11> 7/21/97 djb LogEndTime now takes an error code.
61 <CS10> 7/16/97 DSH FilesInternal.x -> FileMgrInternal.x to avoid name collision
62 <CS9> 7/15/97 msd Bug #1664103. OpenBTree is not propagating errors from
64 <CS8> 7/9/97 djb Remove maxCNID check from ValidHFSRecord (radar #1649593).
65 <CS7> 6/13/97 djb In ValidHFSRecord HFSPlus threads names can be > 31 chars.
66 <CS6> 6/2/97 DSH Also flush AlternateVolumeHeader whenever Attributes or Startup
68 <CS5> 5/28/97 msd In ValidHFSRecord, check for attribute keys.
69 <CS4> 5/19/97 djb Move summary traces from GetBTreeRecord to BTIterateRecord.
70 <CS3> 5/9/97 djb Get in sync with new FilesInternal.i.
71 <CS2> 5/7/97 djb Add summary traces to B-tree SPI.
72 <CS1> 4/24/97 djb first checked in
73 <HFS18> 4/16/97 djb Always use new B-tree code.
74 <HFS17> 4/4/97 djb Remove clumpSize test from ValidHFSRecord.
75 <HFS16> 4/4/97 djb Get in sync with volume format changes.
76 <HFS15> 3/17/97 DSH Casting for SC, BlockProcs are now not static.
77 <HFS14> 3/3/97 djb Call trash block after closing btree!
78 <HFS13> 2/19/97 djb Add support for accessing bigger B-tree nodes.
79 <HFS12> 2/6/97 msd In CheckBTreeKey, remove test and DebugStr for parent ID being
81 <HFS11> 1/23/97 DSH SetEndOfForkProc now calls through to update the Alternate MDB
83 <HFS10> 1/16/97 djb Switched to dynamic lengths for BufferDescriptor length field in
84 SearchBTreeRecord and GetBTreeRecord. Round up to clump size in
86 <HFS9> 1/15/97 djb Don't return errors for bad file ids in key.
87 <HFS8> 1/13/97 djb Adding support for getting current record. ValidHFSRecord now
88 supports variable sized thread records.
89 <HFS7> 1/9/97 djb Call CheckBTreeKey before using key length in a BlockMoveData
91 <HFS6> 1/6/97 djb Implement SetEndOfForkProc.
92 <HFS5> 1/6/97 djb Added HFS Plus support to CheckBTreeKey and ValidHFSRecord.
93 <HFS4> 1/3/97 djb Added support for large keys. Integrated latest HFSVolumesPriv.h
95 <HFS3> 12/23/96 djb Fixed problem in SearchBTreeRecord (dataSize is an output so it
96 was undefined). Added some debugging code.
97 <HFS2> 12/20/96 msd Fix OpenBTree to use the real data type for the key compare proc
98 pointer (not void *). Fixed problem in SearchBTreeRecord that
99 assigns a pointer to a buffer size field (forgot to dereference
101 <HFS1> 12/19/96 djb first checked in
105 #include "../headers/BTreesPrivate.h"
110 // B-tree callbacks...
111 #if TARGET_API_MAC_OS8
112 OSStatus
GetBlockProc ( FileReference fileRefNum
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
113 OSStatus
ReleaseBlockProc ( FileReference fileRefNum
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
114 OSStatus
SetBlockSizeProc ( FileReference fileRefNum
, ByteCount blockSize
, ItemCount minBlockCount
);
119 static OSErr
CheckBTreeKey(const BTreeKey
*key
, const BTreeControlBlock
*btcb
);
120 static Boolean
ValidHFSRecord(const void *record
, const BTreeControlBlock
*btcb
, UInt16 recordSize
);
125 OSErr
SearchBTreeRecord(FileReference refNum
, const void* key
, UInt32 hint
, void* foundKey
, void* data
, UInt16
*dataSize
, UInt32
*newHint
)
127 FSBufferDescriptor btRecord
;
128 BTreeIterator searchIterator
;
130 BTreeControlBlock
*btcb
;
134 fcb
= GetFileControlBlock(refNum
);
135 btcb
= (BTreeControlBlock
*) fcb
->fcbBTCBPtr
;
137 btRecord
.bufferAddress
= data
;
138 btRecord
.itemCount
= 1;
139 if ( btcb
->maxKeyLength
== kHFSExtentKeyMaximumLength
)
140 btRecord
.itemSize
= sizeof(HFSExtentRecord
);
141 else if ( btcb
->maxKeyLength
== kHFSPlusExtentKeyMaximumLength
)
142 btRecord
.itemSize
= sizeof(HFSPlusExtentRecord
);
144 btRecord
.itemSize
= sizeof(CatalogRecord
);
146 searchIterator
.hint
.writeCount
= 0; // clear these out for debugging...
147 searchIterator
.hint
.reserved1
= 0;
148 searchIterator
.hint
.reserved2
= 0;
150 searchIterator
.hint
.nodeNum
= hint
;
151 searchIterator
.hint
.index
= 0;
153 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
156 BlockMoveData(key
, &searchIterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //\80\80 should we range check against maxkeylen?
158 // We only optimize for catalog records
159 if( btRecord
.itemSize
== sizeof(CatalogRecord
) )
161 UInt32 heuristicHint
;
163 Ptr hintCachePtr
= FCBTOVCB(fcb
)->hintCachePtr
;
165 // We pass a 2nd hint/guess into BTSearchRecord. The heuristicHint is a mapping of
166 // dirID and nodeNumber, in hopes that the current search will be in the same node
167 // as the last search with the same parentID.
168 result
= GetMRUCacheBlock( ((HFSCatalogKey
*)key
)->parentID
, hintCachePtr
, (Ptr
*)&cachedHint
);
169 heuristicHint
= (result
== noErr
) ? *cachedHint
: kInvalidMRUCacheKey
;
171 result
= BTSearchRecord( fcb
, &searchIterator
, heuristicHint
, &btRecord
, dataSize
, &searchIterator
);
173 InsertMRUCacheBlock( hintCachePtr
, ((HFSCatalogKey
*)key
)->parentID
, (Ptr
) &(searchIterator
.hint
.nodeNum
) );
177 result
= BTSearchRecord( fcb
, &searchIterator
, kInvalidMRUCacheKey
, &btRecord
, dataSize
, &searchIterator
);
182 *newHint
= searchIterator
.hint
.nodeNum
;
184 result
= CheckBTreeKey(&searchIterator
.key
, btcb
);
187 BlockMoveData(&searchIterator
.key
, foundKey
, CalcKeySize(btcb
, &searchIterator
.key
)); //\80\80 warning, this could overflow user's buffer!!!
189 if ( DEBUG_BUILD
&& !ValidHFSRecord(data
, btcb
, *dataSize
) )
190 DebugStr("\pSearchBTreeRecord: bad record?");
200 OSErr
InsertBTreeRecord(FileReference refNum
, void* key
, void* data
, UInt16 dataSize
, UInt32
*newHint
)
202 FSBufferDescriptor btRecord
;
203 BTreeIterator iterator
;
205 BTreeControlBlock
*btcb
;
209 fcb
= GetFileControlBlock(refNum
);
210 btcb
= (BTreeControlBlock
*) fcb
->fcbBTCBPtr
;
212 btRecord
.bufferAddress
= data
;
213 btRecord
.itemSize
= dataSize
;
214 btRecord
.itemCount
= 1;
216 iterator
.hint
.nodeNum
= 0; // no hint
218 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
221 BlockMoveData(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //\80\80 should we range check against maxkeylen?
223 if ( DEBUG_BUILD
&& !ValidHFSRecord(data
, btcb
, dataSize
) )
224 DebugStr("\pInsertBTreeRecord: bad record?");
226 result
= BTInsertRecord( fcb
, &iterator
, &btRecord
, dataSize
);
228 *newHint
= iterator
.hint
.nodeNum
;
236 OSErr
DeleteBTreeRecord(FileReference refNum
, void* key
)
238 BTreeIterator iterator
;
240 BTreeControlBlock
*btcb
;
244 fcb
= GetFileControlBlock(refNum
);
245 btcb
= (BTreeControlBlock
*) fcb
->fcbBTCBPtr
;
247 iterator
.hint
.nodeNum
= 0; // no hint
249 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
252 BlockMoveData(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //\80\80 should we range check against maxkeylen?
254 result
= BTDeleteRecord( fcb
, &iterator
);
262 OSErr
ReplaceBTreeRecord(FileReference refNum
, const void* key
, UInt32 hint
, void *newData
, UInt16 dataSize
, UInt32
*newHint
)
264 FSBufferDescriptor btRecord
;
265 BTreeIterator iterator
;
267 BTreeControlBlock
*btcb
;
271 fcb
= GetFileControlBlock(refNum
);
272 btcb
= (BTreeControlBlock
*) fcb
->fcbBTCBPtr
;
274 btRecord
.bufferAddress
= newData
;
275 btRecord
.itemSize
= dataSize
;
276 btRecord
.itemCount
= 1;
278 iterator
.hint
.nodeNum
= hint
;
280 result
= CheckBTreeKey((BTreeKey
*) key
, btcb
);
283 BlockMoveData(key
, &iterator
.key
, CalcKeySize(btcb
, (BTreeKey
*) key
)); //\80\80 should we range check against maxkeylen?
285 if ( DEBUG_BUILD
&& !ValidHFSRecord(newData
, btcb
, dataSize
) )
286 DebugStr("\pReplaceBTreeRecord: bad record?");
288 result
= BTReplaceRecord( fcb
, &iterator
, &btRecord
, dataSize
);
290 *newHint
= iterator
.hint
.nodeNum
;
292 //\80\80 do we need to invalidate the iterator?
301 static OSErr
CheckBTreeKey(const BTreeKey
*key
, const BTreeControlBlock
*btcb
)
305 if ( btcb
->attributes
& kBTBigKeysMask
)
306 keyLen
= key
->length16
;
308 keyLen
= key
->length8
;
310 if ( (keyLen
< 6) || (keyLen
> btcb
->maxKeyLength
) )
313 DebugStr("\pCheckBTreeKey: bad key length!");
314 return fsBTInvalidKeyLengthErr
;
321 static Boolean
ValidHFSRecord(const void *record
, const BTreeControlBlock
*btcb
, UInt16 recordSize
)
325 if ( btcb
->maxKeyLength
== kHFSExtentKeyMaximumLength
)
327 return ( recordSize
== sizeof(HFSExtentRecord
) );
329 else if (btcb
->maxKeyLength
== kHFSPlusExtentKeyMaximumLength
)
331 return ( recordSize
== sizeof(HFSPlusExtentRecord
) );
333 else // Catalog record
335 CatalogRecord
*catalogRecord
= (CatalogRecord
*) record
;
337 switch(catalogRecord
->recordType
)
339 case kHFSFolderRecord
:
341 if ( recordSize
!= sizeof(HFSCatalogFolder
) )
343 if ( catalogRecord
->hfsFolder
.flags
!= 0 )
345 if ( catalogRecord
->hfsFolder
.valence
> 0x7FFF )
348 cNodeID
= catalogRecord
->hfsFolder
.folderID
;
350 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
355 case kHFSPlusFolderRecord
:
357 if ( recordSize
!= sizeof(HFSPlusCatalogFolder
) )
359 if ( catalogRecord
->hfsPlusFolder
.flags
!= 0 )
361 if ( catalogRecord
->hfsPlusFolder
.valence
> 0x7FFF )
364 cNodeID
= catalogRecord
->hfsPlusFolder
.folderID
;
366 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
374 HFSExtentDescriptor
*dataExtent
;
375 HFSExtentDescriptor
*rsrcExtent
;
377 if ( recordSize
!= sizeof(HFSCatalogFile
) )
379 if ( (catalogRecord
->hfsFile
.flags
& ~(0x83)) != 0 )
382 cNodeID
= catalogRecord
->hfsFile
.fileID
;
387 // make sure 0 ¾ LEOF ¾ PEOF for both forks
389 if ( catalogRecord
->hfsFile
.dataLogicalSize
< 0 )
391 if ( catalogRecord
->hfsFile
.dataPhysicalSize
< catalogRecord
->hfsFile
.dataLogicalSize
)
393 if ( catalogRecord
->hfsFile
.rsrcLogicalSize
< 0 )
395 if ( catalogRecord
->hfsFile
.rsrcPhysicalSize
< catalogRecord
->hfsFile
.rsrcLogicalSize
)
398 dataExtent
= (HFSExtentDescriptor
*) &catalogRecord
->hfsFile
.dataExtents
;
399 rsrcExtent
= (HFSExtentDescriptor
*) &catalogRecord
->hfsFile
.rsrcExtents
;
402 for (i
= 0; i
< kHFSExtentDensity
; ++i
)
404 if ( (dataExtent
[i
].blockCount
> 0) && (dataExtent
[i
].startBlock
== 0) )
406 if ( (rsrcExtent
[i
].blockCount
> 0) && (rsrcExtent
[i
].startBlock
== 0) )
413 case kHFSPlusFileRecord
:
416 HFSPlusExtentDescriptor
*dataExtent
;
417 HFSPlusExtentDescriptor
*rsrcExtent
;
419 if ( recordSize
!= sizeof(HFSPlusCatalogFile
) )
421 if ( (catalogRecord
->hfsPlusFile
.flags
& ~(0x83)) != 0 )
424 cNodeID
= catalogRecord
->hfsPlusFile
.fileID
;
429 // make sure 0 ¾ LEOF ¾ PEOF for both forks
431 dataExtent
= (HFSPlusExtentDescriptor
*) &catalogRecord
->hfsPlusFile
.dataFork
.extents
;
432 rsrcExtent
= (HFSPlusExtentDescriptor
*) &catalogRecord
->hfsPlusFile
.resourceFork
.extents
;
435 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
)
437 if ( (dataExtent
[i
].blockCount
> 0) && (dataExtent
[i
].startBlock
== 0) )
439 if ( (rsrcExtent
[i
].blockCount
> 0) && (rsrcExtent
[i
].startBlock
== 0) )
446 case kHFSFolderThreadRecord
:
447 case kHFSFileThreadRecord
:
449 if ( recordSize
!= sizeof(HFSCatalogThread
) )
452 cNodeID
= catalogRecord
->hfsThread
.parentID
;
453 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
456 if ( (catalogRecord
->hfsThread
.nodeName
[0] == 0) ||
457 (catalogRecord
->hfsThread
.nodeName
[0] > 31) )
462 case kHFSPlusFolderThreadRecord
:
463 case kHFSPlusFileThreadRecord
:
465 if ( recordSize
> sizeof(HFSPlusCatalogThread
) || recordSize
< (sizeof(HFSPlusCatalogThread
) - sizeof(HFSUniStr255
)))
468 cNodeID
= catalogRecord
->hfsPlusThread
.parentID
;
469 if ( (cNodeID
== 0) || (cNodeID
< 16 && cNodeID
> 2) )
472 if ( (catalogRecord
->hfsPlusThread
.nodeName
.length
== 0) ||
473 (catalogRecord
->hfsPlusThread
.nodeName
.length
> 255) )
483 return true; // record appears to be OK