2 * Copyright (c) 2000-2003 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 2.0 (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@
23 * hfs.c - File System Module for HFS and HFS+.
25 * Copyright (c) 1999-2002 Apple Computer, Inc.
31 #include <hfs/hfs_format.h>
35 #define kBlockSize (0x200)
37 #define kMDBBaseOffset (2 * kBlockSize)
39 #define kBTreeCatalog (0)
40 #define kBTreeExtents (1)
44 static CICell gCurrentIH
;
45 static long long gAllocationOffset
;
46 static long gIsHFSPlus
;
47 static long gCaseSensitive
;
48 static long gBlockSize
;
49 static long gCacheBlockSize
;
50 static char *gBTreeHeaderBuffer
;
51 static BTHeaderRec
*gBTHeaders
[2];
52 static char *gHFSMdbVib
;
53 static HFSMasterDirectoryBlock
*gHFSMDB
;
54 static char *gHFSPlusHeader
;
55 static HFSPlusVolumeHeader
*gHFSPlus
;
56 static char *gLinkTemp
;
57 static char *gTempStr
;
61 static CICell gCurrentIH
;
62 static long long gAllocationOffset
;
63 static long gIsHFSPlus
;
64 static long gBlockSize
;
65 static long gCaseSensitive
;
66 static long gCacheBlockSize
;
67 static char gBTreeHeaderBuffer
[512];
68 static BTHeaderRec
*gBTHeaders
[2];
69 static char gHFSMdbVib
[kBlockSize
];
70 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
71 static char gHFSPlusHeader
[kBlockSize
];
72 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
73 static char gLinkTemp
[64];
75 #endif /* !__i386__ */
77 static long ReadFile(void *file
, long *length
, void *base
, long offset
);
78 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
,
79 FinderInfo
*finderInfo
, long *infoValid
);
80 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
81 void *entry
, long dirID
, long *dirIndex
);
83 static long GetCatalogEntry(long *dirIndex
, char **name
,
84 long *flags
, long *time
,
85 FinderInfo
*finderInfo
, long *infoValid
);
86 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
88 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
90 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
91 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
92 char **key
, char **data
);
94 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
95 long offset
, long size
, void *buffer
, long cache
);
97 static long GetExtentStart(void *extents
, long index
);
98 static long GetExtentSize(void *extents
, long index
);
100 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
101 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
102 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
103 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
105 extern long FastRelString(char *str1
, char *str2
);
106 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
107 u_int16_t
*uniStr2
, u_int32_t len2
);
108 extern long BinaryUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
109 u_int16_t
*uniStr2
, u_int32_t len2
);
113 SwapFinderInfo(FndrFileInfo
*dst
, FndrFileInfo
*src
)
115 dst
->fdType
= SWAP_BE32(src
->fdType
);
116 dst
->fdCreator
= SWAP_BE32(src
->fdCreator
);
117 dst
->fdFlags
= SWAP_BE16(src
->fdFlags
);
118 // Don't bother with location
121 long HFSInitPartition(CICell ih
)
123 long extentSize
, extentFile
, nodeSize
;
126 if (ih
== gCurrentIH
) {
128 CacheInit(ih
, gCacheBlockSize
);
134 if (!gTempStr
) gTempStr
= (char *)malloc(4096);
135 if (!gLinkTemp
) gLinkTemp
= (char *)malloc(64);
136 if (!gBTreeHeaderBuffer
) gBTreeHeaderBuffer
= (char *)malloc(512);
138 gHFSMdbVib
= (char *)malloc(kBlockSize
);
139 gHFSMDB
= (HFSMasterDirectoryBlock
*)gHFSMdbVib
;
141 if (!gHFSPlusHeader
) {
142 gHFSPlusHeader
= (char *)malloc(kBlockSize
);
143 gHFSPlus
= (HFSPlusVolumeHeader
*)gHFSPlusHeader
;
145 if (!gTempStr
|| !gLinkTemp
|| !gBTreeHeaderBuffer
||
146 !gHFSMdbVib
|| !gHFSPlusHeader
) return -1;
147 #endif /* __i386__ */
149 gAllocationOffset
= 0;
155 // Look for the HFS MDB
156 Seek(ih
, kMDBBaseOffset
);
157 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
159 if ( SWAP_BE16(gHFSMDB
->drSigWord
) == kHFSSigWord
) {
160 gAllocationOffset
= SWAP_BE16(gHFSMDB
->drAlBlSt
) * kBlockSize
;
162 // See if it is HFSPlus
163 if (SWAP_BE16(gHFSMDB
->drEmbedSigWord
) != kHFSPlusSigWord
) {
165 gCacheBlockSize
= gBlockSize
= SWAP_BE32(gHFSMDB
->drAlBlkSiz
);
166 CacheInit(ih
, gCacheBlockSize
);
169 // Get the Catalog BTree node size.
170 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
171 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
172 extentFile
= kHFSCatalogFileID
;
173 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
174 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
176 nodeSize
= SWAP_BE16(((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 +
177 sizeof(BTNodeDescriptor
)))->nodeSize
);
179 // If the BTree node size is larger than the block size, reset the cache.
180 if (nodeSize
> gBlockSize
) {
181 gCacheBlockSize
= nodeSize
;
182 CacheInit(ih
, gCacheBlockSize
);
188 // Calculate the offset to the embeded HFSPlus volume.
189 gAllocationOffset
+= (long long)SWAP_BE16(gHFSMDB
->drEmbedExtent
.startBlock
) *
190 SWAP_BE32(gHFSMDB
->drAlBlkSiz
);
193 // Look for the HFSPlus Header
194 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
195 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
197 // Not a HFS+ or HFSX volume.
198 if (SWAP_BE16(gHFSPlus
->signature
) != kHFSPlusSigWord
&&
199 SWAP_BE16(gHFSPlus
->signature
) != kHFSXSigWord
) {
200 verbose("HFS signature was not present.\n");
206 gCacheBlockSize
= gBlockSize
= SWAP_BE32(gHFSPlus
->blockSize
);
207 CacheInit(ih
, gCacheBlockSize
);
210 // Get the Catalog BTree node size.
211 extent
= &gHFSPlus
->catalogFile
.extents
;
212 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
213 extentFile
= kHFSCatalogFileID
;
215 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
216 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
218 nodeSize
= SWAP_BE16(((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 +
219 sizeof(BTNodeDescriptor
)))->nodeSize
);
221 // If the BTree node size is larger than the block size, reset the cache.
222 if (nodeSize
> gBlockSize
) {
223 gCacheBlockSize
= nodeSize
;
224 CacheInit(ih
, gCacheBlockSize
);
230 long HFSLoadFile(CICell ih
, char * filePath
)
232 return HFSReadFile(ih
, filePath
, (void *)gFSLoadAddress
, 0, 0);
235 long HFSReadFile(CICell ih
, char * filePath
, void *base
, unsigned long offset
, unsigned long length
)
238 long dirID
, result
, flags
;
240 verbose("Loading HFS%s file: [%s] from %x.\n",
241 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
243 if (HFSInitPartition(ih
) == -1) return -1;
245 dirID
= kHFSRootFolderID
;
246 // Skip a lead '\'. Start in the system folder if there are two.
247 if (filePath
[0] == '/') {
248 if (filePath
[1] == '/') {
249 if (gIsHFSPlus
) dirID
= SWAP_BE32(((long *)gHFSPlus
->finderInfo
)[5]);
250 else dirID
= SWAP_BE32(gHFSMDB
->drFndrInfo
[5]);
259 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
260 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) {
265 // Not yet for Intel. System.config/Default.table will fail this check.
266 // Check file owner and permissions.
267 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
270 result
= ReadFile(entry
, &length
, base
, offset
);
278 long HFSGetDirEntry(CICell ih
, char * dirPath
, long * dirIndex
, char ** name
,
279 long * flags
, long * time
,
280 FinderInfo
* finderInfo
, long * infoValid
)
283 long dirID
, dirFlags
;
285 if (HFSInitPartition(ih
) == -1) return -1;
287 if (*dirIndex
== -1) return -1;
289 dirID
= kHFSRootFolderID
;
290 // Skip a lead '\'. Start in the system folder if there are two.
291 if (dirPath
[0] == '/') {
292 if (dirPath
[1] == '/') {
293 if (gIsHFSPlus
) dirID
= SWAP_BE32(((long *)gHFSPlus
->finderInfo
)[5]);
294 else dirID
= SWAP_BE32(gHFSMDB
->drFndrInfo
[5]);
295 if (dirID
== 0) return -1;
301 if (*dirIndex
== 0) {
302 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
303 if (*dirIndex
== 0) *dirIndex
= -1;
304 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
307 GetCatalogEntry(dirIndex
, name
, flags
, time
, finderInfo
, infoValid
);
308 if (*dirIndex
== 0) *dirIndex
= -1;
309 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
315 HFSGetDescription(CICell ih
, char *str
, long strMaxLen
)
319 UInt32 firstLeafNode
;
324 if (HFSInitPartition(ih
) == -1) { return; }
326 /* Fill some crucial data structures by side effect. */
328 HFSGetDirEntry(ih
, "/", &dirIndex
, &name
, &flags
, &time
, 0, 0);
330 /* Now we can loook up the volume name node. */
331 nodeSize
= SWAP_BE16(gBTHeaders
[kBTreeCatalog
]->nodeSize
);
332 firstLeafNode
= SWAP_BE32(gBTHeaders
[kBTreeCatalog
]->firstLeafNode
);
334 dirIndex
= firstLeafNode
* nodeSize
;
336 GetCatalogEntry(&dirIndex
, &name
, &flags
, &time
, 0, 0);
338 strncpy(str
, name
, strMaxLen
);
339 str
[strMaxLen
] = '\0';
344 HFSGetFileBlock(CICell ih
, char *filePath
, unsigned long long *firstBlock
)
347 long dirID
, result
, flags
;
349 HFSCatalogFile
*hfsFile
= (void *)entry
;
350 HFSPlusCatalogFile
*hfsPlusFile
= (void *)entry
;
352 if (HFSInitPartition(ih
) == -1) return -1;
354 dirID
= kHFSRootFolderID
;
355 // Skip a lead '\'. Start in the system folder if there are two.
356 if (filePath
[0] == '/') {
357 if (filePath
[1] == '/') {
358 if (gIsHFSPlus
) dirID
= SWAP_BE32(((long *)gHFSPlus
->finderInfo
)[5]);
359 else dirID
= SWAP_BE32(gHFSMDB
->drFndrInfo
[5]);
368 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
369 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) {
370 printf("HFS: Resolve path %s failed\n", filePath
);
375 extents
= &hfsPlusFile
->dataFork
.extents
;
377 extents
= &hfsFile
->dataExtents
;
381 printf("extent start 0x%x\n", (unsigned long)GetExtentStart(extents
, 0));
382 printf("block size 0x%x\n", (unsigned long)gBlockSize
);
383 printf("Allocation offset 0x%x\n", (unsigned long)gAllocationOffset
);
385 *firstBlock
= ((unsigned long long)GetExtentStart(extents
, 0) * (unsigned long long) gBlockSize
+ gAllocationOffset
) / 512ULL;
392 static long ReadFile(void * file
, long * length
, void * base
, long offset
)
397 HFSCatalogFile
*hfsFile
= file
;
398 HFSPlusCatalogFile
*hfsPlusFile
= file
;
401 fileID
= SWAP_BE32(hfsPlusFile
->fileID
);
402 fileLength
= SWAP_BE64(hfsPlusFile
->dataFork
.logicalSize
);
403 extents
= &hfsPlusFile
->dataFork
.extents
;
405 fileID
= SWAP_BE32(hfsFile
->fileID
);
406 fileLength
= SWAP_BE32(hfsFile
->dataLogicalSize
);
407 extents
= &hfsFile
->dataExtents
;
410 if (offset
> fileLength
) {
411 printf("Offset is too large.\n");
415 if (*length
== 0 || (offset
+ *length
) > fileLength
) {
416 *length
= fileLength
- offset
;
421 if (*length
> kLoadSize
) {
422 printf("File is too large.\n");
427 *length
= ReadExtent((char *)extents
, fileLength
, fileID
,
428 offset
, *length
, (char *)base
, 0);
433 static long GetCatalogEntryInfo(void * entry
, long * flags
, long * time
,
434 FinderInfo
* finderInfo
, long * infoValid
)
439 // Get information about the file.
441 switch ( SWAP_BE16(*(short *)entry
) )
443 case kHFSFolderRecord
:
444 *flags
= kFileTypeDirectory
;
445 tmpTime
= SWAP_BE32(((HFSCatalogFolder
*)entry
)->modifyDate
);
448 case kHFSPlusFolderRecord
:
449 *flags
= kFileTypeDirectory
|
450 (SWAP_BE16(((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
) & kPermMask
);
451 if (SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
) != 0)
452 *flags
|= kOwnerNotRoot
;
453 tmpTime
= SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->contentModDate
);
456 case kHFSFileRecord
:
457 *flags
= kFileTypeFlat
;
458 tmpTime
= SWAP_BE32(((HFSCatalogFile
*)entry
)->modifyDate
);
460 SwapFinderInfo((FndrFileInfo
*)finderInfo
, &((HFSCatalogFile
*)entry
)->userInfo
);
465 case kHFSPlusFileRecord
:
466 *flags
= kFileTypeFlat
|
467 (SWAP_BE16(((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
) & kPermMask
);
468 if (SWAP_BE32(((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
) != 0)
469 *flags
|= kOwnerNotRoot
;
470 tmpTime
= SWAP_BE32(((HFSPlusCatalogFile
*)entry
)->contentModDate
);
472 SwapFinderInfo((FndrFileInfo
*)finderInfo
, &((HFSPlusCatalogFile
*)entry
)->userInfo
);
477 case kHFSFileThreadRecord
:
478 case kHFSPlusFileThreadRecord
:
479 case kHFSFolderThreadRecord
:
480 case kHFSPlusFolderThreadRecord
:
481 *flags
= kFileTypeUnknown
;
487 // Convert base time from 1904 to 1970.
488 *time
= tmpTime
- 2082844800;
490 if (infoValid
) *infoValid
= valid
;
495 static long ResolvePathToCatalogEntry(char * filePath
, long * flags
,
496 void * entry
, long dirID
, long * dirIndex
)
499 long result
, cnt
, subFolderID
, tmpDirIndex
;
500 HFSPlusCatalogFile
*hfsPlusFile
;
502 // Copy the file name to gTempStr
504 while ((filePath
[cnt
] != '/') && (filePath
[cnt
] != '\0')) cnt
++;
505 strlcpy(gTempStr
, filePath
, cnt
+1);
507 // Move restPath to the right place.
508 if (filePath
[cnt
] != '\0') cnt
++;
509 restPath
= filePath
+ cnt
;
511 // gTempStr is a name in the current Dir.
512 // restPath is the rest of the path if any.
514 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
519 GetCatalogEntryInfo(entry
, flags
, 0, 0, 0);
521 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
523 subFolderID
= SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->folderID
);
525 subFolderID
= SWAP_BE32(((HFSCatalogFolder
*)entry
)->folderID
);
527 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
528 subFolderID
, dirIndex
);
531 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
532 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
533 if ((SWAP_BE32(hfsPlusFile
->userInfo
.fdType
) == kHardLinkFileType
) &&
534 (SWAP_BE32(hfsPlusFile
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
535 sprintf(gLinkTemp
, "%s/%s%ld", HFSPLUSMETADATAFOLDER
,
536 HFS_INODE_PREFIX
, SWAP_BE32(hfsPlusFile
->bsdInfo
.special
.iNodeNum
));
537 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
538 kHFSRootFolderID
, &tmpDirIndex
);
545 static long GetCatalogEntry(long * dirIndex
, char ** name
,
546 long * flags
, long * time
,
547 FinderInfo
* finderInfo
, long * infoValid
)
549 long extentSize
, nodeSize
, curNode
, index
;
551 char *nodeBuf
, *testKey
, *entry
;
552 BTNodeDescriptor
*node
;
555 extent
= &gHFSPlus
->catalogFile
.extents
;
556 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
558 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
559 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
562 nodeSize
= SWAP_BE16(gBTHeaders
[kBTreeCatalog
]->nodeSize
);
563 nodeBuf
= (char *)malloc(nodeSize
);
564 node
= (BTNodeDescriptor
*)nodeBuf
;
566 index
= *dirIndex
% nodeSize
;
567 curNode
= *dirIndex
/ nodeSize
;
569 // Read the BTree node and get the record for index.
570 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
571 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
572 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
574 GetCatalogEntryInfo(entry
, flags
, time
, finderInfo
, infoValid
);
576 // Get the file name.
578 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
579 SWAP_BE16(((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
),
580 gTempStr
, 256, OSBigEndian
);
583 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
584 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
590 if (index
== SWAP_BE16(node
->numRecords
)) {
592 curNode
= SWAP_BE32(node
->fLink
);
594 *dirIndex
= curNode
* nodeSize
+ index
;
601 static long ReadCatalogEntry(char * fileName
, long dirID
,
602 void * entry
, long * dirIndex
)
605 char key
[sizeof(HFSPlusCatalogKey
)];
606 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
607 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
609 // Make the catalog key.
612 hfsPlusKey
->parentID
= SWAP_BE32(dirID
);
613 length
= strlen(fileName
);
614 if (length
> 255) length
= 255;
615 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
616 &(hfsPlusKey
->nodeName
.length
), 512, OSBigEndian
);
618 hfsKey
->parentID
= SWAP_BE32(dirID
);
619 length
= strlen(fileName
);
620 if (length
> 31) length
= 31;
621 hfsKey
->nodeName
[0] = length
;
622 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
625 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
628 static long ReadExtentsEntry(long fileID
, long startBlock
, void * entry
)
630 char key
[sizeof(HFSPlusExtentKey
)];
631 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
632 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
634 // Make the extents key.
636 hfsPlusKey
->forkType
= 0;
637 hfsPlusKey
->fileID
= SWAP_BE32(fileID
);
638 hfsPlusKey
->startBlock
= SWAP_BE32(startBlock
);
640 hfsKey
->forkType
= 0;
641 hfsKey
->fileID
= SWAP_BE32(fileID
);
642 hfsKey
->startBlock
= SWAP_BE16(startBlock
);
645 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
648 static long ReadBTreeEntry(long btree
, void * key
, char * entry
, long * dirIndex
)
654 BTNodeDescriptor
*node
;
655 long nodeSize
, result
= 0, entrySize
= 0;
656 long curNode
, index
= 0, lowerBound
, upperBound
;
657 char *testKey
, *recordData
;
659 // Figure out which tree is being looked at.
660 if (btree
== kBTreeCatalog
) {
662 extent
= &gHFSPlus
->catalogFile
.extents
;
663 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
665 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
666 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
668 extentFile
= kHFSCatalogFileID
;
671 extent
= &gHFSPlus
->extentsFile
.extents
;
672 extentSize
= SWAP_BE64(gHFSPlus
->extentsFile
.logicalSize
);
674 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
675 extentSize
= SWAP_BE32(gHFSMDB
->drXTFlSize
);
677 extentFile
= kHFSExtentsFileID
;
680 // Read the BTree Header if needed.
681 if (gBTHeaders
[btree
] == 0) {
682 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
683 gBTreeHeaderBuffer
+ btree
* 256, 0);
684 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
685 sizeof(BTNodeDescriptor
));
686 if ((gIsHFSPlus
&& btree
== kBTreeCatalog
) &&
687 (gBTHeaders
[btree
]->keyCompareType
== kHFSBinaryCompare
)) {
692 curNode
= SWAP_BE32(gBTHeaders
[btree
]->rootNode
);
693 nodeSize
= SWAP_BE16(gBTHeaders
[btree
]->nodeSize
);
694 nodeBuf
= (char *)malloc(nodeSize
);
695 node
= (BTNodeDescriptor
*)nodeBuf
;
698 // Read the current node.
699 ReadExtent(extent
, extentSize
, extentFile
,
700 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
702 // Find the matching key.
704 upperBound
= SWAP_BE16(node
->numRecords
) - 1;
705 while (lowerBound
<= upperBound
) {
706 index
= (lowerBound
+ upperBound
) / 2;
708 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
711 if (btree
== kBTreeCatalog
) {
712 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
714 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
717 if (btree
== kBTreeCatalog
) {
718 result
= CompareHFSCatalogKeys(key
, testKey
);
720 result
= CompareHFSExtentsKeys(key
, testKey
);
724 if (result
< 0) upperBound
= index
- 1; // search < trial
725 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
726 else break; // search = trial
731 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
734 // Found the closest key... Recurse on it if this is an index node.
735 if (node
->kind
== kBTIndexNode
) {
736 curNode
= SWAP_BE32( *((long *)recordData
) );
740 // Return error if the file was not found.
741 if (result
!= 0) { free(nodeBuf
); return -1; }
743 if (btree
== kBTreeCatalog
) {
744 switch (SWAP_BE16(*(short *)recordData
)) {
745 case kHFSFolderRecord
: entrySize
= 70; break;
746 case kHFSFileRecord
: entrySize
= 102; break;
747 case kHFSFolderThreadRecord
: entrySize
= 46; break;
748 case kHFSFileThreadRecord
: entrySize
= 46; break;
749 case kHFSPlusFolderRecord
: entrySize
= 88; break;
750 case kHFSPlusFileRecord
: entrySize
= 248; break;
751 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
752 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
755 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
756 else entrySize
= sizeof(HFSExtentRecord
);
759 bcopy(recordData
, entry
, entrySize
);
764 if (index
== SWAP_BE16(node
->numRecords
)) {
766 curNode
= SWAP_BE32(node
->fLink
);
768 *dirIndex
= curNode
* nodeSize
+ index
;
776 static void GetBTreeRecord(long index
, char * nodeBuffer
, long nodeSize
,
777 char ** key
, char ** data
)
782 recordOffset
= SWAP_BE16(*((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2))));
783 *key
= nodeBuffer
+ recordOffset
;
785 keySize
= SWAP_BE16(*(short *)*key
);
786 *data
= *key
+ 2 + keySize
;
789 *data
= *key
+ 2 + keySize
- (keySize
& 1);
793 static long ReadExtent(char * extent
, long extentSize
,
794 long extentFile
, long offset
, long size
,
795 void * buffer
, long cache
)
797 long lastOffset
, blockNumber
, countedBlocks
= 0;
798 long nextExtent
= 0, sizeRead
= 0, readSize
;
799 long nextExtentBlock
, currentExtentBlock
= 0;
800 long long readOffset
;
801 long extentDensity
, sizeofExtent
, currentExtentSize
;
802 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
804 if (offset
>= extentSize
) return 0;
807 extentDensity
= kHFSPlusExtentDensity
;
808 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
810 extentDensity
= kHFSExtentDensity
;
811 sizeofExtent
= sizeof(HFSExtentDescriptor
);
814 lastOffset
= offset
+ size
;
815 while (offset
< lastOffset
) {
816 blockNumber
= offset
/ gBlockSize
;
818 // Find the extent for the offset.
819 for (; ; nextExtent
++) {
820 if (nextExtent
< extentDensity
) {
821 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
822 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
826 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
830 if (extentBuffer
== 0) {
831 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
832 if (extentBuffer
== 0) return -1;
835 nextExtentBlock
= nextExtent
/ extentDensity
;
836 if (currentExtentBlock
!= nextExtentBlock
) {
837 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
838 currentExtentBlock
= nextExtentBlock
;
841 currentExtentSize
= GetExtentSize(extentBuffer
, nextExtent
% extentDensity
);
843 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
844 currentExtent
= extentBuffer
+ sizeofExtent
* (nextExtent
% extentDensity
);
848 countedBlocks
+= currentExtentSize
;
851 readOffset
= ((blockNumber
- countedBlocks
) * gBlockSize
) +
852 (offset
% gBlockSize
);
854 readSize
= GetExtentSize(currentExtent
, 0) * gBlockSize
- readOffset
;
855 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
857 readOffset
+= (long long)GetExtentStart(currentExtent
, 0) * gBlockSize
;
859 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
862 sizeRead
+= readSize
;
864 bufferPos
+= readSize
;
867 if (extentBuffer
) free(extentBuffer
);
872 static long GetExtentStart(void * extents
, long index
)
875 HFSExtentDescriptor
*hfsExtents
= extents
;
876 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
878 if (gIsHFSPlus
) start
= SWAP_BE32(hfsPlusExtents
[index
].startBlock
);
879 else start
= SWAP_BE16(hfsExtents
[index
].startBlock
);
884 static long GetExtentSize(void * extents
, long index
)
887 HFSExtentDescriptor
*hfsExtents
= extents
;
888 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
890 if (gIsHFSPlus
) size
= SWAP_BE32(hfsPlusExtents
[index
].blockCount
);
891 else size
= SWAP_BE16(hfsExtents
[index
].blockCount
);
896 static long CompareHFSCatalogKeys(void * key
, void * testKey
)
898 HFSCatalogKey
*searchKey
, *trialKey
;
899 long result
, searchParentID
, trialParentID
;
904 searchParentID
= SWAP_BE32(searchKey
->parentID
);
905 trialParentID
= SWAP_BE32(trialKey
->parentID
);
907 // parent dirID is unsigned
908 if (searchParentID
> trialParentID
) result
= 1;
909 else if (searchParentID
< trialParentID
) result
= -1;
911 // parent dirID's are equal, compare names
912 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
918 static long CompareHFSPlusCatalogKeys(void * key
, void * testKey
)
920 HFSPlusCatalogKey
*searchKey
, *trialKey
;
921 long result
, searchParentID
, trialParentID
;
926 searchParentID
= SWAP_BE32(searchKey
->parentID
);
927 trialParentID
= SWAP_BE32(trialKey
->parentID
);
929 // parent dirID is unsigned
930 if (searchParentID
> trialParentID
) result
= 1;
931 else if (searchParentID
< trialParentID
) result
= -1;
933 // parent dirID's are equal, compare names
934 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
935 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
937 if (gCaseSensitive
) {
938 result
= BinaryUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
939 SWAP_BE16(searchKey
->nodeName
.length
),
940 &trialKey
->nodeName
.unicode
[0],
941 SWAP_BE16(trialKey
->nodeName
.length
));
943 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
944 SWAP_BE16(searchKey
->nodeName
.length
),
945 &trialKey
->nodeName
.unicode
[0],
946 SWAP_BE16(trialKey
->nodeName
.length
));
953 static long CompareHFSExtentsKeys(void * key
, void * testKey
)
955 HFSExtentKey
*searchKey
, *trialKey
;
961 // assume searchKey < trialKey
964 if (searchKey
->fileID
== trialKey
->fileID
) {
965 // FileNum's are equal; compare fork types
966 if (searchKey
->forkType
== trialKey
->forkType
) {
967 // Fork types are equal; compare allocation block number
968 if (searchKey
->startBlock
== trialKey
->startBlock
) {
969 // Everything is equal
972 // Allocation block numbers differ; determine sign
973 if (SWAP_BE16(searchKey
->startBlock
) > SWAP_BE16(trialKey
->startBlock
))
977 // Fork types differ; determine sign
978 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
981 // FileNums differ; determine sign
982 if (SWAP_BE32(searchKey
->fileID
) > SWAP_BE32(trialKey
->fileID
))
989 static long CompareHFSPlusExtentsKeys(void * key
, void * testKey
)
991 HFSPlusExtentKey
*searchKey
, *trialKey
;
997 // assume searchKey < trialKey
1000 if (searchKey
->fileID
== trialKey
->fileID
) {
1001 // FileNum's are equal; compare fork types
1002 if (searchKey
->forkType
== trialKey
->forkType
) {
1003 // Fork types are equal; compare allocation block number
1004 if (searchKey
->startBlock
== trialKey
->startBlock
) {
1005 // Everything is equal
1008 // Allocation block numbers differ; determine sign
1009 if (SWAP_BE32(searchKey
->startBlock
) > SWAP_BE32(trialKey
->startBlock
))
1013 // Fork types differ; determine sign
1014 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
1017 // FileNums differ; determine sign
1018 if (SWAP_BE32(searchKey
->fileID
) > SWAP_BE32(trialKey
->fileID
))