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@
23 * hfs.c - File System Module for HFS and HFS+.
25 * Copyright (c) 1999-2000 Apple Computer, Inc.
34 #include <bsd/hfs/hfscommon/headers/HFSBtreesPriv.h>
35 #include <bsd/hfs/hfscommon/headers/system/MacOSTypes.h>
36 #include <bsd/hfs/hfscommon/headers/system/MacOSStubs.h>
37 #include <bsd/hfs/hfscommon/headers/HFSVolumes.h>
38 #define kBTIndexNode kIndexNode
39 #define BTHeaderRec HeaderRec
40 #define GetNodeType(node) (node->type)
42 #define GetNodeType(node) (node->kind)
43 #include <bsd/hfs/hfs_format.h>
48 #define kBlockSize (0x200)
50 #define kMDBBaseOffset (2 * kBlockSize)
52 #define kBTreeCatalog (0)
53 #define kBTreeExtents (1)
55 static CICell gCurrentIH
;
56 static long long gAllocationOffset
;
57 static long gIsHFSPlus
;
58 static long gBlockSize
;
59 static char gBTreeHeaderBuffer
[512];
60 static BTHeaderRec
*gBTHeaders
[2];
61 static char gHFSMdbVib
[kBlockSize
];
62 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
63 static char gHFSPlusHeader
[kBlockSize
];
64 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
65 static char gLinkTemp
[64];
68 static long ReadFile(void *file
, long *length
);
69 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
70 void *entry
, long dirID
, long *dirIndex
);
72 static long GetCatalogEntry(long *dirIndex
, char **name
,
73 long *flags
, long *time
);
74 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
76 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
78 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
79 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
80 char **key
, char **data
);
82 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
83 long offset
, long size
, void *buffer
, long cache
);
85 static long GetExtentStart(void *extents
, long index
);
86 static long GetExtentSize(void *extents
, long index
);
88 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
89 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
90 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
91 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
93 extern long FastRelString(char *str1
, char *str2
);
94 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
95 u_int16_t
*uniStr2
, u_int32_t len2
);
96 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
97 u_int8_t
*utf8p
, u_int32_t bufsize
);
98 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
99 u_int16_t
*ucslen
, u_int32_t bufsize
);
102 long HFSInitPartition(CICell ih
)
104 long extentSize
, extentFile
, nodeSize
;
107 if (ih
== gCurrentIH
) return 0;
109 printf("HFSInitPartition: %x\n", ih
);
111 gAllocationOffset
= 0;
116 // Look for the HFS MDB
117 Seek(ih
, kMDBBaseOffset
);
118 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
120 if (gHFSMDB
->drSigWord
== kHFSSigWord
) {
121 gAllocationOffset
= gHFSMDB
->drAlBlSt
* kBlockSize
;
123 // See if it is HFSPlus
124 if (gHFSMDB
->drEmbedSigWord
!= kHFSPlusSigWord
) {
126 gBlockSize
= gHFSMDB
->drAlBlkSiz
;
127 CacheInit(ih
, gBlockSize
);
130 // Get the Catalog BTree node size.
131 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
132 extentSize
= gHFSMDB
->drCTFlSize
;
133 extentFile
= kHFSCatalogFileID
;
134 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
135 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
136 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
138 // If the BTree node size is larger than the block size, reset the cache.
139 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
144 // Calculate the offset to the embeded HFSPlus volume.
145 gAllocationOffset
+= (long long)gHFSMDB
->drEmbedExtent
.startBlock
* gHFSMDB
->drAlBlkSiz
;
148 // Look for the HFSPlus Header
149 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
150 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
152 // Not a HFS[+] volume.
153 if (gHFSPlus
->signature
!= kHFSPlusSigWord
) return -1;
156 gBlockSize
= gHFSPlus
->blockSize
;
157 CacheInit(ih
, gBlockSize
);
160 // Get the Catalog BTree node size.
161 extent
= &gHFSPlus
->catalogFile
.extents
;
162 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
163 extentFile
= kHFSCatalogFileID
;
164 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
165 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
166 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
168 // If the BTree node size is larger than the block size, reset the cache.
169 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
174 long HFSLoadFile(CICell ih
, char *filePath
)
177 long dirID
, result
, length
, flags
;
179 if (HFSInitPartition(ih
) == -1) return -1;
181 printf("Loading HFS%s file: [%s] from %x.\n",
182 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
184 dirID
= kHFSRootFolderID
;
185 // Skip a lead '\'. Start in the system folder if there are two.
186 if (filePath
[0] == '\\') {
187 if (filePath
[1] == '\\') {
188 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
189 else dirID
= gHFSMDB
->drFndrInfo
[5];
190 if (dirID
== 0) return -1;
196 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
197 if ((result
== -1) || (flags
!= kFlatFileType
)) return -1;
199 result
= ReadFile(entry
, &length
);
200 if (result
== -1) return -1;
205 long HFSGetDirEntry(CICell ih
, char *dirPath
, long *dirIndex
, char **name
,
206 long *flags
, long *time
)
209 long dirID
, dirFlags
;
211 if (HFSInitPartition(ih
) == -1) return -1;
213 if (*dirIndex
== -1) return -1;
215 dirID
= kHFSRootFolderID
;
216 // Skip a lead '\'. Start in the system folder if there are two.
217 if (dirPath
[0] == '\\') {
218 if (dirPath
[1] == '\\') {
219 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
220 else dirID
= gHFSMDB
->drFndrInfo
[5];
221 if (dirID
== 0) return -1;
227 if (*dirIndex
== 0) {
228 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
229 if (*dirIndex
== 0) *dirIndex
= -1;
230 if (dirFlags
!= kUnknownFileType
) return -1;
233 GetCatalogEntry(dirIndex
, name
, flags
, time
);
234 if (*dirIndex
== 0) *dirIndex
= -1;
235 if (*flags
== kUnknownFileType
) return -1;
243 static long ReadFile(void *file
, long *length
)
247 HFSCatalogFile
*hfsFile
= file
;
248 HFSPlusCatalogFile
*hfsPlusFile
= file
;
251 fileID
= hfsPlusFile
->fileID
;
252 *length
= hfsPlusFile
->dataFork
.logicalSize
;
253 extents
= &hfsPlusFile
->dataFork
.extents
;
255 fileID
= hfsFile
->fileID
;
256 *length
= hfsFile
->dataLogicalSize
;
257 extents
= &hfsFile
->dataExtents
;
260 if (*length
> kLoadSize
) {
261 printf("File is too large.\n");
265 *length
= ReadExtent((char *)extents
, *length
, fileID
,
266 0, *length
, (char *)kLoadAddr
, 0);
271 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
272 void *entry
, long dirID
, long *dirIndex
)
275 long result
, cnt
, subFolderID
, tmpDirIndex
;
276 HFSCatalogFolder
*hfsFolder
;
277 HFSPlusCatalogFolder
*hfsPlusFolder
;
278 HFSPlusCatalogFile
*hfsPlusFile
;
280 hfsFolder
= (HFSCatalogFolder
*)entry
;
281 hfsPlusFolder
= (HFSPlusCatalogFolder
*)entry
;
283 // Copy the file name to gTempStr
285 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
286 strncpy(gTempStr
, filePath
, cnt
);
288 // Move restPath to the right place.
289 if (filePath
[cnt
] != '\0') cnt
++;
290 restPath
= filePath
+ cnt
;
292 // gTempStr is a name in the current Dir.
293 // restPath is the rest of the path if any.
295 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
296 if (result
== -1) return -1;
299 if (hfsPlusFolder
->recordType
== kHFSPlusFolderRecord
) {
300 *flags
= kDirectoryFileType
;
301 subFolderID
= hfsPlusFolder
->folderID
;
302 } else if (hfsPlusFolder
->recordType
== kHFSPlusFileRecord
) {
303 *flags
= kFlatFileType
;
304 } else if (hfsPlusFolder
->recordType
== kHFSPlusFolderThreadRecord
) {
305 *flags
= kUnknownFileType
;
308 if (hfsFolder
->recordType
== kHFSFolderRecord
) {
309 *flags
= kDirectoryFileType
;
310 subFolderID
= hfsFolder
->folderID
;
311 } else if (hfsFolder
->recordType
== kHFSFileRecord
) {
312 *flags
= kFlatFileType
;
313 } else if (hfsFolder
->recordType
== kHFSFolderThreadRecord
) {
314 *flags
= kUnknownFileType
;
318 if (*flags
== kDirectoryFileType
)
319 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
320 subFolderID
, dirIndex
);
322 if (gIsHFSPlus
&& (*flags
== kFlatFileType
)) {
323 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
324 if ((hfsPlusFile
->userInfo
.fdType
== kHardLinkFileType
) &&
325 (hfsPlusFile
->userInfo
.fdCreator
== kHFSPlusCreator
)) {
326 sprintf(gLinkTemp
, "%s\\%s%d", HFSPLUSMETADATAFOLDER
,
327 HFS_INODE_PREFIX
, hfsPlusFile
->bsdInfo
.special
.iNodeNum
);
328 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
329 kHFSRootFolderID
, &tmpDirIndex
);
336 static long GetCatalogEntry(long *dirIndex
, char **name
,
337 long *flags
, long *time
)
339 long extentSize
, nodeSize
, curNode
, index
, tmpTime
;
341 char *nodeBuf
, *testKey
, *recordData
;
342 BTNodeDescriptor
*node
;
345 extent
= &gHFSPlus
->catalogFile
.extents
;
346 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
348 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
349 extentSize
= gHFSMDB
->drCTFlSize
;
352 nodeSize
= gBTHeaders
[kBTreeCatalog
]->nodeSize
;
353 nodeBuf
= (char *)malloc(nodeSize
);
354 node
= (BTNodeDescriptor
*)nodeBuf
;
356 index
= *dirIndex
% nodeSize
;
357 curNode
= *dirIndex
/ nodeSize
;
359 // Read the BTree node and get the record for index.
360 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
361 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
362 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
364 // Get the kind of file.
365 switch (*(short *)recordData
) {
366 case kHFSFolderRecord
:
367 *flags
= kDirectoryFileType
;
368 tmpTime
= ((HFSCatalogFolder
*)recordData
)->modifyDate
;
371 case kHFSPlusFolderRecord
:
372 *flags
= kDirectoryFileType
;
373 tmpTime
= ((HFSPlusCatalogFolder
*)recordData
)->contentModDate
;
376 case kHFSFileRecord
:
377 *flags
= kFlatFileType
;
378 tmpTime
= ((HFSCatalogFile
*)recordData
)->modifyDate
;
381 case kHFSPlusFileRecord
:
382 *flags
= kFlatFileType
;
383 tmpTime
= ((HFSPlusCatalogFile
*)recordData
)->contentModDate
;
386 case kHFSFileThreadRecord
:
387 case kHFSPlusFileThreadRecord
:
388 case kHFSFolderThreadRecord
:
389 case kHFSPlusFolderThreadRecord
:
390 *flags
= kUnknownFileType
;
395 // Convert base time from 1904 to 1970.
396 *time
= tmpTime
- 2082844800;
398 // Get the file name.
400 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
401 ((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
,
405 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
406 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
412 if (index
== node
->numRecords
) {
414 curNode
= node
->fLink
;
416 *dirIndex
= curNode
* nodeSize
+ index
;
423 static long ReadCatalogEntry(char *fileName
, long dirID
,
424 void *entry
, long *dirIndex
)
427 char key
[sizeof(HFSPlusCatalogKey
)];
428 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
429 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
431 // Make the catalog key.
433 hfsPlusKey
->parentID
= dirID
;
434 length
= strlen(fileName
);
435 if (length
> 255) length
= 255;
436 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
437 &(hfsPlusKey
->nodeName
.length
), 512);
439 hfsKey
->parentID
= dirID
;
440 length
= strlen(fileName
);
441 if (length
> 31) length
= 31;
442 hfsKey
->nodeName
[0] = length
;
443 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
446 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
449 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
)
451 char key
[sizeof(HFSPlusExtentKey
)];
452 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
453 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
455 // Make the extents key.
457 hfsPlusKey
->forkType
= 0;
458 hfsPlusKey
->fileID
= fileID
;
459 hfsPlusKey
->startBlock
= startBlock
;
461 hfsKey
->forkType
= 0;
462 hfsKey
->fileID
= fileID
;
463 hfsKey
->startBlock
= startBlock
;
466 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
469 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
)
475 BTNodeDescriptor
*node
;
476 long nodeSize
, result
, entrySize
;
477 long curNode
, index
, lowerBound
, upperBound
;
478 char *testKey
, *recordData
;
480 // Figure out which tree is being looked at.
481 if (btree
== kBTreeCatalog
) {
483 extent
= &gHFSPlus
->catalogFile
.extents
;
484 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
486 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
487 extentSize
= gHFSMDB
->drCTFlSize
;
489 extentFile
= kHFSCatalogFileID
;
492 extent
= &gHFSPlus
->extentsFile
.extents
;
493 extentSize
= gHFSPlus
->extentsFile
.logicalSize
;
495 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
496 extentSize
= gHFSMDB
->drXTFlSize
;
498 extentFile
= kHFSExtentsFileID
;
501 // Read the BTree Header if needed.
502 if (gBTHeaders
[btree
] == 0) {
503 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
504 gBTreeHeaderBuffer
+ btree
* 256, 0);
505 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
506 sizeof(BTNodeDescriptor
));
509 curNode
= gBTHeaders
[btree
]->rootNode
;
511 nodeSize
= gBTHeaders
[btree
]->nodeSize
;
512 nodeBuf
= (char *)malloc(nodeSize
);
513 node
= (BTNodeDescriptor
*)nodeBuf
;
516 // Read the current node.
517 ReadExtent(extent
, extentSize
, extentFile
,
518 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
520 // Find the matching key.
522 upperBound
= node
->numRecords
- 1;
523 while (lowerBound
<= upperBound
) {
524 index
= (lowerBound
+ upperBound
) / 2;
526 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
529 if (btree
== kBTreeCatalog
) {
530 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
532 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
535 if (btree
== kBTreeCatalog
) {
536 result
= CompareHFSCatalogKeys(key
, testKey
);
538 result
= CompareHFSExtentsKeys(key
, testKey
);
542 if (result
< 0) upperBound
= index
- 1; // search < trial
543 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
544 else break; // search = trial
549 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
552 // Found the closest key... Recurse on it if this is an index node.
553 if (GetNodeType(node
) == kBTIndexNode
) {
554 curNode
= *((long *)recordData
);
558 // Return error if the file was not found.
559 if (result
!= 0) return -1;
561 if (btree
== kBTreeCatalog
) {
562 switch (*(short *)recordData
) {
563 case kHFSFolderRecord
: entrySize
= 70; break;
564 case kHFSFileRecord
: entrySize
= 102; break;
565 case kHFSFolderThreadRecord
: entrySize
= 46; break;
566 case kHFSFileThreadRecord
: entrySize
= 46; break;
567 case kHFSPlusFolderRecord
: entrySize
= 88; break;
568 case kHFSPlusFileRecord
: entrySize
= 248; break;
569 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
570 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
573 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
574 else entrySize
= sizeof(HFSExtentRecord
);
577 bcopy(recordData
, entry
, entrySize
);
582 if (index
== node
->numRecords
) {
584 curNode
= node
->fLink
;
586 *dirIndex
= curNode
* nodeSize
+ index
;
594 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
595 char **key
, char **data
)
600 recordOffset
= *((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2)));
601 *key
= nodeBuffer
+ recordOffset
;
603 keySize
= *(short *)*key
;
604 *data
= *key
+ 2 + keySize
;
607 *data
= *key
+ 2 + keySize
- (keySize
& 1);
611 static long ReadExtent(char *extent
, long extentSize
,
612 long extentFile
, long offset
, long size
,
613 void *buffer
, long cache
)
615 long lastOffset
, blockNumber
, countedBlocks
= 0;
616 long nextExtent
= 0, sizeRead
= 0, readSize
;
617 long nextExtentBlock
, currentExtentBlock
= 0;
618 long long readOffset
;
619 long extentDensity
, sizeofExtent
, currentExtentSize
;
620 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
622 if (offset
>= extentSize
) return 0;
625 extentDensity
= kHFSPlusExtentDensity
;
626 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
628 extentDensity
= kHFSExtentDensity
;
629 sizeofExtent
= sizeof(HFSExtentDescriptor
);
632 lastOffset
= offset
+ size
;
633 while (offset
< lastOffset
) {
634 blockNumber
= offset
/ gBlockSize
;
636 // Find the extent for the offset.
637 for (; ; nextExtent
++) {
638 if (nextExtent
< extentDensity
) {
639 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
640 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
644 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
648 if (extentBuffer
== 0) {
649 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
650 if (extentBuffer
== 0) return -1;
653 nextExtentBlock
= nextExtent
/ extentDensity
;
654 if (currentExtentBlock
!= nextExtentBlock
) {
655 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
656 currentExtentBlock
= nextExtentBlock
;
659 currentExtentSize
= GetExtentSize(extentBuffer
,
660 nextExtent
% extentDensity
);
662 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
663 currentExtent
= extentBuffer
+ sizeofExtent
*
664 (nextExtent
% extentDensity
);
668 countedBlocks
+= currentExtentSize
;
671 readOffset
= ((blockNumber
- countedBlocks
) * gBlockSize
) +
672 (offset
% gBlockSize
);
674 readSize
= GetExtentSize(currentExtent
, 0) * gBlockSize
- readOffset
;
675 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
677 readOffset
+= (long long)GetExtentStart(currentExtent
, 0) * gBlockSize
;
679 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
682 sizeRead
+= readSize
;
684 bufferPos
+= readSize
;
687 if (extentBuffer
) free(extentBuffer
);
692 static long GetExtentStart(void *extents
, long index
)
695 HFSExtentDescriptor
*hfsExtents
= extents
;
696 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
698 if (gIsHFSPlus
) start
= hfsPlusExtents
[index
].startBlock
;
699 else start
= hfsExtents
[index
].startBlock
;
704 static long GetExtentSize(void *extents
, long index
)
707 HFSExtentDescriptor
*hfsExtents
= extents
;
708 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
710 if (gIsHFSPlus
) size
= hfsPlusExtents
[index
].blockCount
;
711 else size
= hfsExtents
[index
].blockCount
;
716 static long CompareHFSCatalogKeys(void *key
, void *testKey
)
718 HFSCatalogKey
*searchKey
, *trialKey
;
719 long result
, searchParentID
, trialParentID
;
724 searchParentID
= searchKey
->parentID
;
725 trialParentID
= trialKey
->parentID
;
727 // parent dirID is unsigned
728 if (searchParentID
> trialParentID
) result
= 1;
729 else if (searchParentID
< trialParentID
) result
= -1;
731 // parent dirID's are equal, compare names
732 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
738 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
)
740 HFSPlusCatalogKey
*searchKey
, *trialKey
;
741 long result
, searchParentID
, trialParentID
;
746 searchParentID
= searchKey
->parentID
;
747 trialParentID
= trialKey
->parentID
;
749 // parent dirID is unsigned
750 if (searchParentID
> trialParentID
) result
= 1;
751 else if (searchParentID
< trialParentID
) result
= -1;
753 // parent dirID's are equal, compare names
754 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
755 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
757 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
758 searchKey
->nodeName
.length
,
759 &trialKey
->nodeName
.unicode
[0],
760 trialKey
->nodeName
.length
);
766 static long CompareHFSExtentsKeys(void *key
, void *testKey
)
768 HFSExtentKey
*searchKey
, *trialKey
;
774 // assume searchKey < trialKey
777 if (searchKey
->fileID
== trialKey
->fileID
) {
778 // FileNum's are equal; compare fork types
779 if (searchKey
->forkType
== trialKey
->forkType
) {
780 // Fork types are equal; compare allocation block number
781 if (searchKey
->startBlock
== trialKey
->startBlock
) {
782 // Everything is equal
785 // Allocation block numbers differ; determine sign
786 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
789 // Fork types differ; determine sign
790 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
793 // FileNums differ; determine sign
794 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;
800 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
)
802 HFSPlusExtentKey
*searchKey
, *trialKey
;
808 // assume searchKey < trialKey
811 if (searchKey
->fileID
== trialKey
->fileID
) {
812 // FileNum's are equal; compare fork types
813 if (searchKey
->forkType
== trialKey
->forkType
) {
814 // Fork types are equal; compare allocation block number
815 if (searchKey
->startBlock
== trialKey
->startBlock
) {
816 // Everything is equal
819 // Allocation block numbers differ; determine sign
820 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
823 // Fork types differ; determine sign
824 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
827 // FileNums differ; determine sign
828 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;