2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * hfs.c - File System Module for HFS and HFS+.
32 * Copyright (c) 1999-2004 Apple Computer, Inc.
38 #include <hfs/hfs_format.h>
40 #define kBlockSize (0x200)
42 #define kMDBBaseOffset (2 * kBlockSize)
44 #define kBTreeCatalog (0)
45 #define kBTreeExtents (1)
47 static CICell gCurrentIH
;
48 static long long gAllocationOffset
;
49 static long gIsHFSPlus
;
50 static long gCaseSensitive
;
51 static unsigned long gBlockSize
;
52 static char gBTreeHeaderBuffer
[512];
53 static BTHeaderRec
*gBTHeaders
[2];
54 static char gHFSMdbVib
[kBlockSize
];
55 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
56 static char gHFSPlusHeader
[kBlockSize
];
57 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
58 static char gLinkTemp
[64];
59 static long long gVolID
;
62 static long ReadFile(void *file
, long *length
, void *base
, long offset
);
63 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
);
64 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
, void *entry
,
65 unsigned long dirID
, unsigned long *dirIndex
);
67 static long GetCatalogEntry(unsigned long *dirIndex
, char **name
,
68 long *flags
, long *time
);
69 static long ReadCatalogEntry(char *fileName
, unsigned long dirID
, void *entry
,
70 unsigned long *dirIndex
);
71 static long ReadExtentsEntry(unsigned long fileID
, unsigned long startBlock
, void *entry
);
73 static long ReadBTreeEntry(long btree
, void *key
, char *entry
,
74 unsigned long *dirIndex
);
75 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
76 char **key
, char **data
);
78 static long ReadExtent(void *extent
, u_int64_t extentSize
, unsigned long extentFile
,
79 u_int64_t offset
, long size
, void *buffer
, long cache
);
81 static unsigned long GetExtentStart(void *extents
, long index
);
82 static unsigned long GetExtentSize(void *extents
, long index
);
84 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
85 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
86 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
87 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
89 extern long FastRelString(char *str1
, char *str2
);
90 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
91 u_int16_t
*uniStr2
, u_int32_t len2
);
92 extern long BinaryUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
93 u_int16_t
*uniStr2
, u_int32_t len2
);
94 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
95 u_int8_t
*utf8p
, u_int32_t bufsize
);
96 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
97 u_int16_t
*ucslen
, u_int32_t bufsize
);
100 long HFSInitPartition(CICell ih
)
102 unsigned long extentFile
;
104 u_int64_t extentSize
;
107 if (ih
== gCurrentIH
) return 0;
109 printf("HFSInitPartition: %x\n", ih
);
111 gAllocationOffset
= 0;
117 // Look for the HFS MDB
118 Seek(ih
, (long long)kMDBBaseOffset
);
119 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
121 if (gHFSMDB
->drSigWord
== kHFSSigWord
) {
122 gAllocationOffset
= gHFSMDB
->drAlBlSt
* kBlockSize
;
124 // See if it is HFSPlus
125 if (gHFSMDB
->drEmbedSigWord
!= kHFSPlusSigWord
) {
127 gBlockSize
= gHFSMDB
->drAlBlkSiz
;
128 CacheInit(ih
, gBlockSize
);
131 // grab the 64 bit volume ID
132 bcopy(&gHFSMDB
->drFndrInfo
[6], &gVolID
, 8);
134 // Get the Catalog BTree node size.
135 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
136 extentSize
= gHFSMDB
->drCTFlSize
;
137 extentFile
= kHFSCatalogFileID
;
138 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
139 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
140 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
142 // If the BTree node size is larger than the block size, reset the cache.
143 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
148 // Calculate the offset to the embeded HFSPlus volume.
149 gAllocationOffset
+= (long long)gHFSMDB
->drEmbedExtent
.startBlock
* gHFSMDB
->drAlBlkSiz
;
152 // Look for the HFSPlus Header
153 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
154 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
156 //printf("checking signatures...\n");
157 // Not a HFS[+] volume.
158 if ((gHFSPlus
->signature
!= kHFSPlusSigWord
) &&
159 (gHFSPlus
->signature
!= kHFSXSigWord
)) return -1;
160 //printf("continuing with HFS\n");
163 gBlockSize
= gHFSPlus
->blockSize
;
164 CacheInit(ih
, gBlockSize
);
167 // grab the 64 bit volume ID
168 bcopy(&gHFSPlus
->finderInfo
[24], &gVolID
, 8);
170 // Get the Catalog BTree node size.
171 extent
= &gHFSPlus
->catalogFile
.extents
;
172 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
173 extentFile
= kHFSCatalogFileID
;
174 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
175 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
176 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
178 // If the BTree node size is larger than the block size, reset the cache.
179 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
184 long HFSLoadFile(CICell ih
, char *filePath
)
186 return HFSReadFile(ih
, filePath
, (void *)kLoadAddr
, 0, 0);
189 extern long HFSReadFile(CICell ih
, char *filePath
, void *base
,
190 unsigned long offset
, unsigned long length
)
196 if (HFSInitPartition(ih
) == -1) return -1;
198 printf("%s HFS%s file: [%s] from %x.\n",
199 (((offset
== 0) && (length
== 0)) ? "Loading" : "Reading"),
200 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
202 dirID
= kHFSRootFolderID
;
203 // Skip a lead '\'. Start in the system folder if there are two.
204 if (filePath
[0] == '\\') {
205 if (filePath
[1] == '\\') {
206 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
207 else dirID
= gHFSMDB
->drFndrInfo
[5];
208 if (dirID
== 0) return -1;
214 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
215 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
217 // Check file owner and permissions.
218 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) {
219 printf("%s: permissions incorrect\n", filePath
);
223 result
= ReadFile(entry
, &length
, base
, offset
);
224 if (result
== -1) return -1;
229 long HFSGetDirEntry(CICell ih
, char *dirPath
, unsigned long *dirIndex
,
230 char **name
, long *flags
, long *time
)
233 unsigned long dirID
, dirFlags
;
235 if (HFSInitPartition(ih
) == -1) return -1;
237 if (*dirIndex
== -1) return -1;
239 dirID
= kHFSRootFolderID
;
240 // Skip a lead '\'. Start in the system folder if there are two.
241 if (dirPath
[0] == '\\') {
242 if (dirPath
[1] == '\\') {
243 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
244 else dirID
= gHFSMDB
->drFndrInfo
[5];
245 if (dirID
== 0) return -1;
251 if (*dirIndex
== 0) {
252 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
253 if (*dirIndex
== 0) *dirIndex
= -1;
254 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
257 GetCatalogEntry(dirIndex
, name
, flags
, time
);
258 if (*dirIndex
== 0) *dirIndex
= -1;
259 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
264 long HFSGetUUID(CICell ih
, char *uuidStr
)
266 if (HFSInitPartition(ih
) == -1) return -1;
267 if (gVolID
== 0LL) return -1;
269 return CreateUUIDString((uint8_t*)(&gVolID
), sizeof(gVolID
), uuidStr
);
275 static long ReadFile(void *file
, long *length
, void *base
, long offset
)
278 unsigned long fileID
;
279 u_int64_t fileLength
;
280 HFSCatalogFile
*hfsFile
= file
;
281 HFSPlusCatalogFile
*hfsPlusFile
= file
;
284 fileID
= hfsPlusFile
->fileID
;
285 fileLength
= hfsPlusFile
->dataFork
.logicalSize
;
286 extents
= &hfsPlusFile
->dataFork
.extents
;
288 fileID
= hfsFile
->fileID
;
289 fileLength
= hfsFile
->dataLogicalSize
;
290 extents
= &hfsFile
->dataExtents
;
293 if (offset
> fileLength
) {
294 printf("Offset is too large.\n");
298 if ((*length
== 0) || ((offset
+ *length
) > fileLength
)) {
299 *length
= (long)(fileLength
- offset
);
302 if (*length
> kLoadSize
) {
303 printf("File is too large.\n");
307 *length
= ReadExtent(extents
, fileLength
, fileID
,
308 offset
, *length
, base
, 0);
313 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
)
317 // Get information about the file.
318 switch (*(short *)entry
) {
319 case kHFSFolderRecord
:
320 *flags
= kFileTypeDirectory
;
321 tmpTime
= ((HFSCatalogFolder
*)entry
)->modifyDate
;
324 case kHFSPlusFolderRecord
:
325 *flags
= kFileTypeDirectory
|
326 (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
327 if (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
!= 0) {
328 static int nwarnings
= 0;
329 if(nwarnings
++ < 25) // so we don't warn for all in an Extensions walk
330 printf("non-root file owner detected: %d\n",
331 ((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
);
332 *flags
|= kOwnerNotRoot
;
334 tmpTime
= ((HFSPlusCatalogFolder
*)entry
)->contentModDate
;
337 case kHFSFileRecord
:
338 *flags
= kFileTypeFlat
;
339 tmpTime
= ((HFSCatalogFile
*)entry
)->modifyDate
;
342 case kHFSPlusFileRecord
:
343 *flags
= kFileTypeFlat
|
344 (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
345 if (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
!= 0) {
346 printf("non-root file owner detected: %d\n",
347 ((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
);
348 *flags
|= kOwnerNotRoot
;
350 tmpTime
= ((HFSPlusCatalogFile
*)entry
)->contentModDate
;
353 case kHFSFileThreadRecord
:
354 case kHFSPlusFileThreadRecord
:
355 case kHFSFolderThreadRecord
:
356 case kHFSPlusFolderThreadRecord
:
357 *flags
= kFileTypeUnknown
;
363 // Convert base time from 1904 to 1970.
364 *time
= tmpTime
- 2082844800;
370 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
, void *entry
,
371 unsigned long dirID
, unsigned long *dirIndex
)
375 unsigned long subFolderID
= 0, tmpDirIndex
;
376 HFSPlusCatalogFile
*hfsPlusFile
;
378 // Copy the file name to gTempStr
380 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
381 strncpy(gTempStr
, filePath
, cnt
);
383 // Move restPath to the right place.
384 if (filePath
[cnt
] != '\0') cnt
++;
385 restPath
= filePath
+ cnt
;
387 // gTempStr is a name in the current Dir.
388 // restPath is the rest of the path if any.
390 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
391 if (result
== -1) return -1;
393 GetCatalogEntryInfo(entry
, flags
, 0);
395 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
396 if (gIsHFSPlus
) subFolderID
= ((HFSPlusCatalogFolder
*)entry
)->folderID
;
397 else subFolderID
= ((HFSCatalogFolder
*)entry
)->folderID
;
400 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
)
401 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
402 subFolderID
, dirIndex
);
404 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
405 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
406 if ((hfsPlusFile
->userInfo
.fdType
== kHardLinkFileType
) &&
407 (hfsPlusFile
->userInfo
.fdCreator
== kHFSPlusCreator
)) {
408 sprintf(gLinkTemp
, "%s\\%s%d", HFSPLUSMETADATAFOLDER
,
409 HFS_INODE_PREFIX
, hfsPlusFile
->bsdInfo
.special
.iNodeNum
);
410 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
411 kHFSRootFolderID
, &tmpDirIndex
);
418 static long GetCatalogEntry(unsigned long *dirIndex
, char **name
,
419 long *flags
, long *time
)
421 long nodeSize
, index
;
423 u_int64_t extentSize
;
425 char *nodeBuf
, *testKey
, *entry
;
426 BTNodeDescriptor
*node
;
429 extent
= &gHFSPlus
->catalogFile
.extents
;
430 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
432 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
433 extentSize
= gHFSMDB
->drCTFlSize
;
436 nodeSize
= gBTHeaders
[kBTreeCatalog
]->nodeSize
;
437 nodeBuf
= (char *)malloc(nodeSize
);
438 node
= (BTNodeDescriptor
*)nodeBuf
;
440 index
= *dirIndex
% nodeSize
;
441 curNode
= *dirIndex
/ nodeSize
;
443 // Read the BTree node and get the record for index.
444 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
445 curNode
* (u_int32_t
)nodeSize
, nodeSize
, nodeBuf
, 1);
446 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
448 GetCatalogEntryInfo(entry
, flags
, time
);
450 // Get the file name.
452 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
453 ((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
,
457 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
458 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
464 if (index
== node
->numRecords
) {
466 curNode
= node
->fLink
;
468 *dirIndex
= curNode
* nodeSize
+ index
;
475 static long ReadCatalogEntry(char *fileName
, unsigned long dirID
,
476 void *entry
, unsigned long *dirIndex
)
479 char key
[sizeof(HFSPlusCatalogKey
)];
480 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
481 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
483 // Make the catalog key.
485 hfsPlusKey
->parentID
= dirID
;
486 length
= strlen(fileName
);
487 if (length
> 255) length
= 255;
488 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
489 &(hfsPlusKey
->nodeName
.length
), 512);
491 hfsKey
->parentID
= dirID
;
492 length
= strlen(fileName
);
493 if (length
> 31) length
= 31;
494 hfsKey
->nodeName
[0] = length
;
495 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
498 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
501 static long ReadExtentsEntry(unsigned long fileID
, unsigned long startBlock
, void *entry
)
503 char key
[sizeof(HFSPlusExtentKey
)];
504 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
505 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
507 // Make the extents key.
509 hfsPlusKey
->forkType
= 0;
510 hfsPlusKey
->fileID
= fileID
;
511 hfsPlusKey
->startBlock
= startBlock
;
513 hfsKey
->forkType
= 0;
514 hfsKey
->fileID
= fileID
;
515 hfsKey
->startBlock
= startBlock
;
518 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
521 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, unsigned long *dirIndex
)
523 u_int64_t extentSize
;
525 unsigned long extentFile
;
527 BTNodeDescriptor
*node
;
528 long nodeSize
, result
= 0, entrySize
= 0;
529 long curNode
, index
= 0, lowerBound
, upperBound
;
530 char *testKey
, *recordData
= NULL
;
532 // Figure out which tree is being looked at.
533 if (btree
== kBTreeCatalog
) {
535 extent
= &gHFSPlus
->catalogFile
.extents
;
536 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
538 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
539 extentSize
= gHFSMDB
->drCTFlSize
;
541 extentFile
= kHFSCatalogFileID
;
544 extent
= &gHFSPlus
->extentsFile
.extents
;
545 extentSize
= gHFSPlus
->extentsFile
.logicalSize
;
547 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
548 extentSize
= gHFSMDB
->drXTFlSize
;
550 extentFile
= kHFSExtentsFileID
;
553 // Read the BTree Header if needed.
554 if (gBTHeaders
[btree
] == 0) {
555 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
556 gBTreeHeaderBuffer
+ btree
* 256, 0);
557 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
558 sizeof(BTNodeDescriptor
));
559 if ((gIsHFSPlus
&& btree
== kBTreeCatalog
) &&
560 (gBTHeaders
[btree
]->keyCompareType
== kHFSBinaryCompare
)) {
565 curNode
= gBTHeaders
[btree
]->rootNode
;
567 nodeSize
= gBTHeaders
[btree
]->nodeSize
;
568 nodeBuf
= (char *)malloc(nodeSize
);
569 node
= (BTNodeDescriptor
*)nodeBuf
;
572 // Read the current node.
573 ReadExtent(extent
, extentSize
, extentFile
,
574 (u_int64_t
)curNode
* (u_int64_t
)nodeSize
, nodeSize
, nodeBuf
, 1);
576 // Find the matching key.
578 upperBound
= node
->numRecords
- 1;
579 while (lowerBound
<= upperBound
) {
580 index
= (lowerBound
+ upperBound
) / 2;
582 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
585 if (btree
== kBTreeCatalog
) {
586 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
588 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
591 if (btree
== kBTreeCatalog
) {
592 result
= CompareHFSCatalogKeys(key
, testKey
);
594 result
= CompareHFSExtentsKeys(key
, testKey
);
598 if (result
< 0) upperBound
= index
- 1; // search < trial
599 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
600 else break; // search = trial
605 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
608 // Found the closest key... Recurse on it if this is an index node.
609 if (node
->kind
== kBTIndexNode
) {
610 curNode
= *((long *)recordData
);
614 // Return error if the file was not found.
615 if (result
!= 0) return -1;
617 if (btree
== kBTreeCatalog
) {
618 switch (*(short *)recordData
) {
619 case kHFSFolderRecord
: entrySize
= 70; break;
620 case kHFSFileRecord
: entrySize
= 102; break;
621 case kHFSFolderThreadRecord
: entrySize
= 46; break;
622 case kHFSFileThreadRecord
: entrySize
= 46; break;
623 case kHFSPlusFolderRecord
: entrySize
= 88; break;
624 case kHFSPlusFileRecord
: entrySize
= 248; break;
625 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
626 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
629 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
630 else entrySize
= sizeof(HFSExtentRecord
);
633 bcopy(recordData
, entry
, entrySize
);
638 if (index
== node
->numRecords
) {
640 curNode
= node
->fLink
;
642 *dirIndex
= curNode
* nodeSize
+ index
;
650 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
651 char **key
, char **data
)
656 recordOffset
= *((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2)));
657 *key
= nodeBuffer
+ recordOffset
;
659 keySize
= *(short *)*key
;
660 *data
= *key
+ 2 + keySize
;
663 *data
= *key
+ 2 + keySize
- (keySize
& 1);
667 static long ReadExtent(void *extent
, u_int64_t extentSize
, unsigned long extentFile
,
668 u_int64_t offset
, long size
, void *buffer
, long cache
)
670 u_int64_t lastOffset
;
671 unsigned long blockNumber
, countedBlocks
= 0, nextExtent
= 0, sizeRead
= 0;
673 unsigned long nextExtentBlock
, currentExtentBlock
= 0;
674 u_int64_t readOffset
;
675 long extentDensity
, sizeofExtent
, currentExtentSize
;
676 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
678 if (offset
>= extentSize
) return 0;
681 extentDensity
= kHFSPlusExtentDensity
;
682 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
684 extentDensity
= kHFSExtentDensity
;
685 sizeofExtent
= sizeof(HFSExtentDescriptor
);
688 lastOffset
= offset
+ size
;
689 while (offset
< lastOffset
) {
690 blockNumber
= offset
/ gBlockSize
;
692 // Find the extent for the offset.
693 for (; ; nextExtent
++) {
694 if (nextExtent
< extentDensity
) {
695 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
696 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
700 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
704 if (extentBuffer
== 0) {
705 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
706 if (extentBuffer
== 0) return -1;
709 nextExtentBlock
= nextExtent
/ extentDensity
;
710 if (currentExtentBlock
!= nextExtentBlock
) {
711 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
712 currentExtentBlock
= nextExtentBlock
;
715 currentExtentSize
= GetExtentSize(extentBuffer
,
716 nextExtent
% extentDensity
);
718 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
719 currentExtent
= extentBuffer
+ sizeofExtent
*
720 (nextExtent
% extentDensity
);
724 countedBlocks
+= currentExtentSize
;
727 readOffset
= ((u_int64_t
)(blockNumber
- countedBlocks
) * (u_int64_t
)gBlockSize
) +
728 (offset
% gBlockSize
);
730 readSize
= ((u_int64_t
)GetExtentSize(currentExtent
, 0) * (u_int64_t
)gBlockSize
) - readOffset
;
731 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
733 readOffset
+= (u_int64_t
)GetExtentStart(currentExtent
, 0) * gBlockSize
;
735 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
736 (long)readSize
, cache
);
738 // truncation: readSize capped pre-CacheRead
739 sizeRead
+= readSize
;
741 bufferPos
+= readSize
;
744 if (extentBuffer
) free(extentBuffer
);
749 static unsigned long GetExtentStart(void *extents
, long index
)
752 HFSExtentDescriptor
*hfsExtents
= extents
;
753 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
755 if (gIsHFSPlus
) start
= hfsPlusExtents
[index
].startBlock
;
756 else start
= hfsExtents
[index
].startBlock
;
761 static unsigned long GetExtentSize(void *extents
, long index
)
764 HFSExtentDescriptor
*hfsExtents
= extents
;
765 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
767 if (gIsHFSPlus
) size
= hfsPlusExtents
[index
].blockCount
;
768 else size
= hfsExtents
[index
].blockCount
;
773 static long CompareHFSCatalogKeys(void *key
, void *testKey
)
775 HFSCatalogKey
*searchKey
, *trialKey
;
777 unsigned long searchParentID
, trialParentID
;
782 searchParentID
= searchKey
->parentID
;
783 trialParentID
= trialKey
->parentID
;
785 // parent dirID is unsigned
786 if (searchParentID
> trialParentID
) result
= 1;
787 else if (searchParentID
< trialParentID
) result
= -1;
789 // parent dirID's are equal, compare names
790 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
796 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
)
798 HFSPlusCatalogKey
*searchKey
, *trialKey
;
800 unsigned long searchParentID
, trialParentID
;
805 searchParentID
= searchKey
->parentID
;
806 trialParentID
= trialKey
->parentID
;
808 // parent dirID is unsigned
809 if (searchParentID
> trialParentID
) result
= 1;
810 else if (searchParentID
< trialParentID
) result
= -1;
812 // parent dirID's are equal, compare names
813 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
814 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
816 if (gCaseSensitive
) {
817 result
= BinaryUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
818 searchKey
->nodeName
.length
,
819 &trialKey
->nodeName
.unicode
[0],
820 trialKey
->nodeName
.length
);
822 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
823 searchKey
->nodeName
.length
,
824 &trialKey
->nodeName
.unicode
[0],
825 trialKey
->nodeName
.length
);
832 static long CompareHFSExtentsKeys(void *key
, void *testKey
)
834 HFSExtentKey
*searchKey
, *trialKey
;
840 // assume searchKey < trialKey
843 if (searchKey
->fileID
== trialKey
->fileID
) {
844 // FileNum's are equal; compare fork types
845 if (searchKey
->forkType
== trialKey
->forkType
) {
846 // Fork types are equal; compare allocation block number
847 if (searchKey
->startBlock
== trialKey
->startBlock
) {
848 // Everything is equal
851 // Allocation block numbers differ; determine sign
852 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
855 // Fork types differ; determine sign
856 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
859 // FileNums differ; determine sign
860 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;
866 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
)
868 HFSPlusExtentKey
*searchKey
, *trialKey
;
874 // assume searchKey < trialKey
877 if (searchKey
->fileID
== trialKey
->fileID
) {
878 // FileNum's are equal; compare fork types
879 if (searchKey
->forkType
== trialKey
->forkType
) {
880 // Fork types are equal; compare allocation block number
881 if (searchKey
->startBlock
== trialKey
->startBlock
) {
882 // Everything is equal
885 // Allocation block numbers differ; determine sign
886 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
889 // Fork types differ; determine sign
890 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
893 // FileNums differ; determine sign
894 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;