2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * hfs.c - File System Module for HFS and HFS+.
28 * Copyright (c) 1999-2002 Apple Computer, Inc.
34 #include <hfs/hfs_format.h>
36 #define kBlockSize (0x200)
38 #define kMDBBaseOffset (2 * kBlockSize)
40 #define kBTreeCatalog (0)
41 #define kBTreeExtents (1)
43 static CICell gCurrentIH
;
44 static long long gAllocationOffset
;
45 static long gIsHFSPlus
;
46 static long gBlockSize
;
47 static char gBTreeHeaderBuffer
[512];
48 static BTHeaderRec
*gBTHeaders
[2];
49 static char gHFSMdbVib
[kBlockSize
];
50 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
51 static char gHFSPlusHeader
[kBlockSize
];
52 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
53 static char gLinkTemp
[64];
56 static long ReadFile(void *file
, long *length
);
57 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
);
58 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
59 void *entry
, long dirID
, long *dirIndex
);
61 static long GetCatalogEntry(long *dirIndex
, char **name
,
62 long *flags
, long *time
);
63 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
65 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
67 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
68 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
69 char **key
, char **data
);
71 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
72 long offset
, long size
, void *buffer
, long cache
);
74 static long GetExtentStart(void *extents
, long index
);
75 static long GetExtentSize(void *extents
, long index
);
77 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
78 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
79 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
80 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
82 extern long FastRelString(char *str1
, char *str2
);
83 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
84 u_int16_t
*uniStr2
, u_int32_t len2
);
85 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
86 u_int8_t
*utf8p
, u_int32_t bufsize
);
87 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
88 u_int16_t
*ucslen
, u_int32_t bufsize
);
91 long HFSInitPartition(CICell ih
)
93 long extentSize
, extentFile
, nodeSize
;
96 if (ih
== gCurrentIH
) return 0;
98 printf("HFSInitPartition: %x\n", ih
);
100 gAllocationOffset
= 0;
105 // Look for the HFS MDB
106 Seek(ih
, kMDBBaseOffset
);
107 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
109 if (gHFSMDB
->drSigWord
== kHFSSigWord
) {
110 gAllocationOffset
= gHFSMDB
->drAlBlSt
* kBlockSize
;
112 // See if it is HFSPlus
113 if (gHFSMDB
->drEmbedSigWord
!= kHFSPlusSigWord
) {
115 gBlockSize
= gHFSMDB
->drAlBlkSiz
;
116 CacheInit(ih
, gBlockSize
);
119 // Get the Catalog BTree node size.
120 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
121 extentSize
= gHFSMDB
->drCTFlSize
;
122 extentFile
= kHFSCatalogFileID
;
123 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
124 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
125 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
127 // If the BTree node size is larger than the block size, reset the cache.
128 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
133 // Calculate the offset to the embeded HFSPlus volume.
134 gAllocationOffset
+= (long long)gHFSMDB
->drEmbedExtent
.startBlock
* gHFSMDB
->drAlBlkSiz
;
137 // Look for the HFSPlus Header
138 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
139 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
141 // Not a HFS[+] volume.
142 if (gHFSPlus
->signature
!= kHFSPlusSigWord
) return -1;
145 gBlockSize
= gHFSPlus
->blockSize
;
146 CacheInit(ih
, gBlockSize
);
149 // Get the Catalog BTree node size.
150 extent
= &gHFSPlus
->catalogFile
.extents
;
151 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
152 extentFile
= kHFSCatalogFileID
;
153 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
154 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
155 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
157 // If the BTree node size is larger than the block size, reset the cache.
158 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
163 long HFSLoadFile(CICell ih
, char *filePath
)
166 long dirID
, result
, length
, flags
;
168 if (HFSInitPartition(ih
) == -1) return -1;
170 printf("Loading HFS%s file: [%s] from %x.\n",
171 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
173 dirID
= kHFSRootFolderID
;
174 // Skip a lead '\'. Start in the system folder if there are two.
175 if (filePath
[0] == '\\') {
176 if (filePath
[1] == '\\') {
177 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
178 else dirID
= gHFSMDB
->drFndrInfo
[5];
179 if (dirID
== 0) return -1;
185 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
186 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
188 // Check file owner and permissions.
189 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
191 result
= ReadFile(entry
, &length
);
192 if (result
== -1) return -1;
197 long HFSGetDirEntry(CICell ih
, char *dirPath
, long *dirIndex
, char **name
,
198 long *flags
, long *time
)
201 long dirID
, dirFlags
;
203 if (HFSInitPartition(ih
) == -1) return -1;
205 if (*dirIndex
== -1) return -1;
207 dirID
= kHFSRootFolderID
;
208 // Skip a lead '\'. Start in the system folder if there are two.
209 if (dirPath
[0] == '\\') {
210 if (dirPath
[1] == '\\') {
211 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
212 else dirID
= gHFSMDB
->drFndrInfo
[5];
213 if (dirID
== 0) return -1;
219 if (*dirIndex
== 0) {
220 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
221 if (*dirIndex
== 0) *dirIndex
= -1;
222 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
225 GetCatalogEntry(dirIndex
, name
, flags
, time
);
226 if (*dirIndex
== 0) *dirIndex
= -1;
227 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
235 static long ReadFile(void *file
, long *length
)
239 HFSCatalogFile
*hfsFile
= file
;
240 HFSPlusCatalogFile
*hfsPlusFile
= file
;
243 fileID
= hfsPlusFile
->fileID
;
244 *length
= hfsPlusFile
->dataFork
.logicalSize
;
245 extents
= &hfsPlusFile
->dataFork
.extents
;
247 fileID
= hfsFile
->fileID
;
248 *length
= hfsFile
->dataLogicalSize
;
249 extents
= &hfsFile
->dataExtents
;
252 if (*length
> kLoadSize
) {
253 printf("File is too large.\n");
257 *length
= ReadExtent((char *)extents
, *length
, fileID
,
258 0, *length
, (char *)kLoadAddr
, 0);
263 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
)
267 // Get information about the file.
268 switch (*(short *)entry
) {
269 case kHFSFolderRecord
:
270 *flags
= kFileTypeDirectory
;
271 tmpTime
= ((HFSCatalogFolder
*)entry
)->modifyDate
;
274 case kHFSPlusFolderRecord
:
275 *flags
= kFileTypeDirectory
|
276 (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
277 if (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
!= 0)
278 *flags
|= kOwnerNotRoot
;
279 tmpTime
= ((HFSPlusCatalogFolder
*)entry
)->contentModDate
;
282 case kHFSFileRecord
:
283 *flags
= kFileTypeFlat
;
284 tmpTime
= ((HFSCatalogFile
*)entry
)->modifyDate
;
287 case kHFSPlusFileRecord
:
288 *flags
= kFileTypeFlat
|
289 (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
290 if (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
!= 0)
291 *flags
|= kOwnerNotRoot
;
292 tmpTime
= ((HFSPlusCatalogFile
*)entry
)->contentModDate
;
295 case kHFSFileThreadRecord
:
296 case kHFSPlusFileThreadRecord
:
297 case kHFSFolderThreadRecord
:
298 case kHFSPlusFolderThreadRecord
:
299 *flags
= kFileTypeUnknown
;
305 // Convert base time from 1904 to 1970.
306 *time
= tmpTime
- 2082844800;
312 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
313 void *entry
, long dirID
, long *dirIndex
)
316 long result
, cnt
, subFolderID
, tmpDirIndex
;
317 HFSPlusCatalogFile
*hfsPlusFile
;
319 // Copy the file name to gTempStr
321 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
322 strncpy(gTempStr
, filePath
, cnt
);
324 // Move restPath to the right place.
325 if (filePath
[cnt
] != '\0') cnt
++;
326 restPath
= filePath
+ cnt
;
328 // gTempStr is a name in the current Dir.
329 // restPath is the rest of the path if any.
331 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
332 if (result
== -1) return -1;
334 GetCatalogEntryInfo(entry
, flags
, 0);
336 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
337 if (gIsHFSPlus
) subFolderID
= ((HFSPlusCatalogFolder
*)entry
)->folderID
;
338 else subFolderID
= ((HFSCatalogFolder
*)entry
)->folderID
;
341 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
)
342 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
343 subFolderID
, dirIndex
);
345 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
346 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
347 if ((hfsPlusFile
->userInfo
.fdType
== kHardLinkFileType
) &&
348 (hfsPlusFile
->userInfo
.fdCreator
== kHFSPlusCreator
)) {
349 sprintf(gLinkTemp
, "%s\\%s%d", HFSPLUSMETADATAFOLDER
,
350 HFS_INODE_PREFIX
, hfsPlusFile
->bsdInfo
.special
.iNodeNum
);
351 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
352 kHFSRootFolderID
, &tmpDirIndex
);
359 static long GetCatalogEntry(long *dirIndex
, char **name
,
360 long *flags
, long *time
)
362 long extentSize
, nodeSize
, curNode
, index
;
364 char *nodeBuf
, *testKey
, *entry
;
365 BTNodeDescriptor
*node
;
368 extent
= &gHFSPlus
->catalogFile
.extents
;
369 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
371 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
372 extentSize
= gHFSMDB
->drCTFlSize
;
375 nodeSize
= gBTHeaders
[kBTreeCatalog
]->nodeSize
;
376 nodeBuf
= (char *)malloc(nodeSize
);
377 node
= (BTNodeDescriptor
*)nodeBuf
;
379 index
= *dirIndex
% nodeSize
;
380 curNode
= *dirIndex
/ nodeSize
;
382 // Read the BTree node and get the record for index.
383 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
384 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
385 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
387 GetCatalogEntryInfo(entry
, flags
, time
);
389 // Get the file name.
391 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
392 ((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
,
396 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
397 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
403 if (index
== node
->numRecords
) {
405 curNode
= node
->fLink
;
407 *dirIndex
= curNode
* nodeSize
+ index
;
414 static long ReadCatalogEntry(char *fileName
, long dirID
,
415 void *entry
, long *dirIndex
)
418 char key
[sizeof(HFSPlusCatalogKey
)];
419 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
420 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
422 // Make the catalog key.
424 hfsPlusKey
->parentID
= dirID
;
425 length
= strlen(fileName
);
426 if (length
> 255) length
= 255;
427 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
428 &(hfsPlusKey
->nodeName
.length
), 512);
430 hfsKey
->parentID
= dirID
;
431 length
= strlen(fileName
);
432 if (length
> 31) length
= 31;
433 hfsKey
->nodeName
[0] = length
;
434 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
437 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
440 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
)
442 char key
[sizeof(HFSPlusExtentKey
)];
443 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
444 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
446 // Make the extents key.
448 hfsPlusKey
->forkType
= 0;
449 hfsPlusKey
->fileID
= fileID
;
450 hfsPlusKey
->startBlock
= startBlock
;
452 hfsKey
->forkType
= 0;
453 hfsKey
->fileID
= fileID
;
454 hfsKey
->startBlock
= startBlock
;
457 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
460 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
)
466 BTNodeDescriptor
*node
;
467 long nodeSize
, result
, entrySize
;
468 long curNode
, index
, lowerBound
, upperBound
;
469 char *testKey
, *recordData
;
471 // Figure out which tree is being looked at.
472 if (btree
== kBTreeCatalog
) {
474 extent
= &gHFSPlus
->catalogFile
.extents
;
475 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
477 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
478 extentSize
= gHFSMDB
->drCTFlSize
;
480 extentFile
= kHFSCatalogFileID
;
483 extent
= &gHFSPlus
->extentsFile
.extents
;
484 extentSize
= gHFSPlus
->extentsFile
.logicalSize
;
486 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
487 extentSize
= gHFSMDB
->drXTFlSize
;
489 extentFile
= kHFSExtentsFileID
;
492 // Read the BTree Header if needed.
493 if (gBTHeaders
[btree
] == 0) {
494 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
495 gBTreeHeaderBuffer
+ btree
* 256, 0);
496 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
497 sizeof(BTNodeDescriptor
));
500 curNode
= gBTHeaders
[btree
]->rootNode
;
502 nodeSize
= gBTHeaders
[btree
]->nodeSize
;
503 nodeBuf
= (char *)malloc(nodeSize
);
504 node
= (BTNodeDescriptor
*)nodeBuf
;
507 // Read the current node.
508 ReadExtent(extent
, extentSize
, extentFile
,
509 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
511 // Find the matching key.
513 upperBound
= node
->numRecords
- 1;
514 while (lowerBound
<= upperBound
) {
515 index
= (lowerBound
+ upperBound
) / 2;
517 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
520 if (btree
== kBTreeCatalog
) {
521 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
523 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
526 if (btree
== kBTreeCatalog
) {
527 result
= CompareHFSCatalogKeys(key
, testKey
);
529 result
= CompareHFSExtentsKeys(key
, testKey
);
533 if (result
< 0) upperBound
= index
- 1; // search < trial
534 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
535 else break; // search = trial
540 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
543 // Found the closest key... Recurse on it if this is an index node.
544 if (node
->kind
== kBTIndexNode
) {
545 curNode
= *((long *)recordData
);
549 // Return error if the file was not found.
550 if (result
!= 0) return -1;
552 if (btree
== kBTreeCatalog
) {
553 switch (*(short *)recordData
) {
554 case kHFSFolderRecord
: entrySize
= 70; break;
555 case kHFSFileRecord
: entrySize
= 102; break;
556 case kHFSFolderThreadRecord
: entrySize
= 46; break;
557 case kHFSFileThreadRecord
: entrySize
= 46; break;
558 case kHFSPlusFolderRecord
: entrySize
= 88; break;
559 case kHFSPlusFileRecord
: entrySize
= 248; break;
560 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
561 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
564 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
565 else entrySize
= sizeof(HFSExtentRecord
);
568 bcopy(recordData
, entry
, entrySize
);
573 if (index
== node
->numRecords
) {
575 curNode
= node
->fLink
;
577 *dirIndex
= curNode
* nodeSize
+ index
;
585 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
586 char **key
, char **data
)
591 recordOffset
= *((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2)));
592 *key
= nodeBuffer
+ recordOffset
;
594 keySize
= *(short *)*key
;
595 *data
= *key
+ 2 + keySize
;
598 *data
= *key
+ 2 + keySize
- (keySize
& 1);
602 static long ReadExtent(char *extent
, long extentSize
,
603 long extentFile
, long offset
, long size
,
604 void *buffer
, long cache
)
606 long lastOffset
, blockNumber
, countedBlocks
= 0;
607 long nextExtent
= 0, sizeRead
= 0, readSize
;
608 long nextExtentBlock
, currentExtentBlock
= 0;
609 long long readOffset
;
610 long extentDensity
, sizeofExtent
, currentExtentSize
;
611 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
613 if (offset
>= extentSize
) return 0;
616 extentDensity
= kHFSPlusExtentDensity
;
617 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
619 extentDensity
= kHFSExtentDensity
;
620 sizeofExtent
= sizeof(HFSExtentDescriptor
);
623 lastOffset
= offset
+ size
;
624 while (offset
< lastOffset
) {
625 blockNumber
= offset
/ gBlockSize
;
627 // Find the extent for the offset.
628 for (; ; nextExtent
++) {
629 if (nextExtent
< extentDensity
) {
630 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
631 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
635 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
639 if (extentBuffer
== 0) {
640 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
641 if (extentBuffer
== 0) return -1;
644 nextExtentBlock
= nextExtent
/ extentDensity
;
645 if (currentExtentBlock
!= nextExtentBlock
) {
646 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
647 currentExtentBlock
= nextExtentBlock
;
650 currentExtentSize
= GetExtentSize(extentBuffer
,
651 nextExtent
% extentDensity
);
653 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
654 currentExtent
= extentBuffer
+ sizeofExtent
*
655 (nextExtent
% extentDensity
);
659 countedBlocks
+= currentExtentSize
;
662 readOffset
= ((blockNumber
- countedBlocks
) * gBlockSize
) +
663 (offset
% gBlockSize
);
665 readSize
= GetExtentSize(currentExtent
, 0) * gBlockSize
- readOffset
;
666 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
668 readOffset
+= (long long)GetExtentStart(currentExtent
, 0) * gBlockSize
;
670 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
673 sizeRead
+= readSize
;
675 bufferPos
+= readSize
;
678 if (extentBuffer
) free(extentBuffer
);
683 static long GetExtentStart(void *extents
, long index
)
686 HFSExtentDescriptor
*hfsExtents
= extents
;
687 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
689 if (gIsHFSPlus
) start
= hfsPlusExtents
[index
].startBlock
;
690 else start
= hfsExtents
[index
].startBlock
;
695 static long GetExtentSize(void *extents
, long index
)
698 HFSExtentDescriptor
*hfsExtents
= extents
;
699 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
701 if (gIsHFSPlus
) size
= hfsPlusExtents
[index
].blockCount
;
702 else size
= hfsExtents
[index
].blockCount
;
707 static long CompareHFSCatalogKeys(void *key
, void *testKey
)
709 HFSCatalogKey
*searchKey
, *trialKey
;
710 long result
, searchParentID
, trialParentID
;
715 searchParentID
= searchKey
->parentID
;
716 trialParentID
= trialKey
->parentID
;
718 // parent dirID is unsigned
719 if (searchParentID
> trialParentID
) result
= 1;
720 else if (searchParentID
< trialParentID
) result
= -1;
722 // parent dirID's are equal, compare names
723 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
729 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
)
731 HFSPlusCatalogKey
*searchKey
, *trialKey
;
732 long result
, searchParentID
, trialParentID
;
737 searchParentID
= searchKey
->parentID
;
738 trialParentID
= trialKey
->parentID
;
740 // parent dirID is unsigned
741 if (searchParentID
> trialParentID
) result
= 1;
742 else if (searchParentID
< trialParentID
) result
= -1;
744 // parent dirID's are equal, compare names
745 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
746 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
748 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
749 searchKey
->nodeName
.length
,
750 &trialKey
->nodeName
.unicode
[0],
751 trialKey
->nodeName
.length
);
757 static long CompareHFSExtentsKeys(void *key
, void *testKey
)
759 HFSExtentKey
*searchKey
, *trialKey
;
765 // assume searchKey < trialKey
768 if (searchKey
->fileID
== trialKey
->fileID
) {
769 // FileNum's are equal; compare fork types
770 if (searchKey
->forkType
== trialKey
->forkType
) {
771 // Fork types are equal; compare allocation block number
772 if (searchKey
->startBlock
== trialKey
->startBlock
) {
773 // Everything is equal
776 // Allocation block numbers differ; determine sign
777 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
780 // Fork types differ; determine sign
781 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
784 // FileNums differ; determine sign
785 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;
791 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
)
793 HFSPlusExtentKey
*searchKey
, *trialKey
;
799 // assume searchKey < trialKey
802 if (searchKey
->fileID
== trialKey
->fileID
) {
803 // FileNum's are equal; compare fork types
804 if (searchKey
->forkType
== trialKey
->forkType
) {
805 // Fork types are equal; compare allocation block number
806 if (searchKey
->startBlock
== trialKey
->startBlock
) {
807 // Everything is equal
810 // Allocation block numbers differ; determine sign
811 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
814 // Fork types differ; determine sign
815 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
818 // FileNums differ; determine sign
819 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;