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 gCaseSensitive
;
47 static long gBlockSize
;
48 static char gBTreeHeaderBuffer
[512];
49 static BTHeaderRec
*gBTHeaders
[2];
50 static char gHFSMdbVib
[kBlockSize
];
51 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
52 static char gHFSPlusHeader
[kBlockSize
];
53 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
54 static char gLinkTemp
[64];
57 static long ReadFile(void *file
, long *length
);
58 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
);
59 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
60 void *entry
, long dirID
, long *dirIndex
);
62 static long GetCatalogEntry(long *dirIndex
, char **name
,
63 long *flags
, long *time
);
64 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
66 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
68 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
69 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
70 char **key
, char **data
);
72 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
73 long offset
, long size
, void *buffer
, long cache
);
75 static long GetExtentStart(void *extents
, long index
);
76 static long GetExtentSize(void *extents
, long index
);
78 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
79 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
80 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
81 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
83 extern long FastRelString(char *str1
, char *str2
);
84 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
85 u_int16_t
*uniStr2
, u_int32_t len2
);
86 extern long BinaryUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
87 u_int16_t
*uniStr2
, u_int32_t len2
);
88 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
89 u_int8_t
*utf8p
, u_int32_t bufsize
);
90 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
91 u_int16_t
*ucslen
, u_int32_t bufsize
);
94 long HFSInitPartition(CICell ih
)
96 long extentSize
, extentFile
, nodeSize
;
99 if (ih
== gCurrentIH
) return 0;
101 printf("HFSInitPartition: %x\n", ih
);
103 gAllocationOffset
= 0;
109 // Look for the HFS MDB
110 Seek(ih
, kMDBBaseOffset
);
111 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
113 if (gHFSMDB
->drSigWord
== kHFSSigWord
) {
114 gAllocationOffset
= gHFSMDB
->drAlBlSt
* kBlockSize
;
116 // See if it is HFSPlus
117 if (gHFSMDB
->drEmbedSigWord
!= kHFSPlusSigWord
) {
119 gBlockSize
= gHFSMDB
->drAlBlkSiz
;
120 CacheInit(ih
, gBlockSize
);
123 // Get the Catalog BTree node size.
124 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
125 extentSize
= gHFSMDB
->drCTFlSize
;
126 extentFile
= kHFSCatalogFileID
;
127 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
128 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
129 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
131 // If the BTree node size is larger than the block size, reset the cache.
132 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
137 // Calculate the offset to the embeded HFSPlus volume.
138 gAllocationOffset
+= (long long)gHFSMDB
->drEmbedExtent
.startBlock
* gHFSMDB
->drAlBlkSiz
;
141 // Look for the HFSPlus Header
142 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
143 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
145 // Not a HFS[+] volume.
146 if ((gHFSPlus
->signature
!= kHFSPlusSigWord
) &&
147 (gHFSPlus
->signature
!= kHFSXSigWord
)) return -1;
150 gBlockSize
= gHFSPlus
->blockSize
;
151 CacheInit(ih
, gBlockSize
);
154 // Get the Catalog BTree node size.
155 extent
= &gHFSPlus
->catalogFile
.extents
;
156 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
157 extentFile
= kHFSCatalogFileID
;
158 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
159 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
160 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
162 // If the BTree node size is larger than the block size, reset the cache.
163 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
168 long HFSLoadFile(CICell ih
, char *filePath
)
171 long dirID
, result
, length
, flags
;
173 if (HFSInitPartition(ih
) == -1) return -1;
175 printf("Loading HFS%s file: [%s] from %x.\n",
176 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
178 dirID
= kHFSRootFolderID
;
179 // Skip a lead '\'. Start in the system folder if there are two.
180 if (filePath
[0] == '\\') {
181 if (filePath
[1] == '\\') {
182 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
183 else dirID
= gHFSMDB
->drFndrInfo
[5];
184 if (dirID
== 0) return -1;
190 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
191 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
193 // Check file owner and permissions.
194 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
196 result
= ReadFile(entry
, &length
);
197 if (result
== -1) return -1;
202 long HFSGetDirEntry(CICell ih
, char *dirPath
, long *dirIndex
, char **name
,
203 long *flags
, long *time
)
206 long dirID
, dirFlags
;
208 if (HFSInitPartition(ih
) == -1) return -1;
210 if (*dirIndex
== -1) return -1;
212 dirID
= kHFSRootFolderID
;
213 // Skip a lead '\'. Start in the system folder if there are two.
214 if (dirPath
[0] == '\\') {
215 if (dirPath
[1] == '\\') {
216 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
217 else dirID
= gHFSMDB
->drFndrInfo
[5];
218 if (dirID
== 0) return -1;
224 if (*dirIndex
== 0) {
225 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
226 if (*dirIndex
== 0) *dirIndex
= -1;
227 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
230 GetCatalogEntry(dirIndex
, name
, flags
, time
);
231 if (*dirIndex
== 0) *dirIndex
= -1;
232 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
240 static long ReadFile(void *file
, long *length
)
244 HFSCatalogFile
*hfsFile
= file
;
245 HFSPlusCatalogFile
*hfsPlusFile
= file
;
248 fileID
= hfsPlusFile
->fileID
;
249 *length
= hfsPlusFile
->dataFork
.logicalSize
;
250 extents
= &hfsPlusFile
->dataFork
.extents
;
252 fileID
= hfsFile
->fileID
;
253 *length
= hfsFile
->dataLogicalSize
;
254 extents
= &hfsFile
->dataExtents
;
257 if (*length
> kLoadSize
) {
258 printf("File is too large.\n");
262 *length
= ReadExtent((char *)extents
, *length
, fileID
,
263 0, *length
, (char *)kLoadAddr
, 0);
268 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
)
272 // Get information about the file.
273 switch (*(short *)entry
) {
274 case kHFSFolderRecord
:
275 *flags
= kFileTypeDirectory
;
276 tmpTime
= ((HFSCatalogFolder
*)entry
)->modifyDate
;
279 case kHFSPlusFolderRecord
:
280 *flags
= kFileTypeDirectory
|
281 (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
282 if (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
!= 0)
283 *flags
|= kOwnerNotRoot
;
284 tmpTime
= ((HFSPlusCatalogFolder
*)entry
)->contentModDate
;
287 case kHFSFileRecord
:
288 *flags
= kFileTypeFlat
;
289 tmpTime
= ((HFSCatalogFile
*)entry
)->modifyDate
;
292 case kHFSPlusFileRecord
:
293 *flags
= kFileTypeFlat
|
294 (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
295 if (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
!= 0)
296 *flags
|= kOwnerNotRoot
;
297 tmpTime
= ((HFSPlusCatalogFile
*)entry
)->contentModDate
;
300 case kHFSFileThreadRecord
:
301 case kHFSPlusFileThreadRecord
:
302 case kHFSFolderThreadRecord
:
303 case kHFSPlusFolderThreadRecord
:
304 *flags
= kFileTypeUnknown
;
310 // Convert base time from 1904 to 1970.
311 *time
= tmpTime
- 2082844800;
317 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
318 void *entry
, long dirID
, long *dirIndex
)
321 long result
, cnt
, subFolderID
, tmpDirIndex
;
322 HFSPlusCatalogFile
*hfsPlusFile
;
324 // Copy the file name to gTempStr
326 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
327 strncpy(gTempStr
, filePath
, cnt
);
329 // Move restPath to the right place.
330 if (filePath
[cnt
] != '\0') cnt
++;
331 restPath
= filePath
+ cnt
;
333 // gTempStr is a name in the current Dir.
334 // restPath is the rest of the path if any.
336 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
337 if (result
== -1) return -1;
339 GetCatalogEntryInfo(entry
, flags
, 0);
341 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
342 if (gIsHFSPlus
) subFolderID
= ((HFSPlusCatalogFolder
*)entry
)->folderID
;
343 else subFolderID
= ((HFSCatalogFolder
*)entry
)->folderID
;
346 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
)
347 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
348 subFolderID
, dirIndex
);
350 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
351 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
352 if ((hfsPlusFile
->userInfo
.fdType
== kHardLinkFileType
) &&
353 (hfsPlusFile
->userInfo
.fdCreator
== kHFSPlusCreator
)) {
354 sprintf(gLinkTemp
, "%s\\%s%d", HFSPLUSMETADATAFOLDER
,
355 HFS_INODE_PREFIX
, hfsPlusFile
->bsdInfo
.special
.iNodeNum
);
356 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
357 kHFSRootFolderID
, &tmpDirIndex
);
364 static long GetCatalogEntry(long *dirIndex
, char **name
,
365 long *flags
, long *time
)
367 long extentSize
, nodeSize
, curNode
, index
;
369 char *nodeBuf
, *testKey
, *entry
;
370 BTNodeDescriptor
*node
;
373 extent
= &gHFSPlus
->catalogFile
.extents
;
374 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
376 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
377 extentSize
= gHFSMDB
->drCTFlSize
;
380 nodeSize
= gBTHeaders
[kBTreeCatalog
]->nodeSize
;
381 nodeBuf
= (char *)malloc(nodeSize
);
382 node
= (BTNodeDescriptor
*)nodeBuf
;
384 index
= *dirIndex
% nodeSize
;
385 curNode
= *dirIndex
/ nodeSize
;
387 // Read the BTree node and get the record for index.
388 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
389 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
390 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
392 GetCatalogEntryInfo(entry
, flags
, time
);
394 // Get the file name.
396 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
397 ((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
,
401 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
402 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
408 if (index
== node
->numRecords
) {
410 curNode
= node
->fLink
;
412 *dirIndex
= curNode
* nodeSize
+ index
;
419 static long ReadCatalogEntry(char *fileName
, long dirID
,
420 void *entry
, long *dirIndex
)
423 char key
[sizeof(HFSPlusCatalogKey
)];
424 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
425 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
427 // Make the catalog key.
429 hfsPlusKey
->parentID
= dirID
;
430 length
= strlen(fileName
);
431 if (length
> 255) length
= 255;
432 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
433 &(hfsPlusKey
->nodeName
.length
), 512);
435 hfsKey
->parentID
= dirID
;
436 length
= strlen(fileName
);
437 if (length
> 31) length
= 31;
438 hfsKey
->nodeName
[0] = length
;
439 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
442 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
445 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
)
447 char key
[sizeof(HFSPlusExtentKey
)];
448 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
449 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
451 // Make the extents key.
453 hfsPlusKey
->forkType
= 0;
454 hfsPlusKey
->fileID
= fileID
;
455 hfsPlusKey
->startBlock
= startBlock
;
457 hfsKey
->forkType
= 0;
458 hfsKey
->fileID
= fileID
;
459 hfsKey
->startBlock
= startBlock
;
462 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
465 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
)
471 BTNodeDescriptor
*node
;
472 long nodeSize
, result
, entrySize
;
473 long curNode
, index
, lowerBound
, upperBound
;
474 char *testKey
, *recordData
;
476 // Figure out which tree is being looked at.
477 if (btree
== kBTreeCatalog
) {
479 extent
= &gHFSPlus
->catalogFile
.extents
;
480 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
482 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
483 extentSize
= gHFSMDB
->drCTFlSize
;
485 extentFile
= kHFSCatalogFileID
;
488 extent
= &gHFSPlus
->extentsFile
.extents
;
489 extentSize
= gHFSPlus
->extentsFile
.logicalSize
;
491 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
492 extentSize
= gHFSMDB
->drXTFlSize
;
494 extentFile
= kHFSExtentsFileID
;
497 // Read the BTree Header if needed.
498 if (gBTHeaders
[btree
] == 0) {
499 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
500 gBTreeHeaderBuffer
+ btree
* 256, 0);
501 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
502 sizeof(BTNodeDescriptor
));
503 if ((gIsHFSPlus
&& btree
== kBTreeCatalog
) &&
504 (gBTHeaders
[btree
]->keyCompareType
== kHFSBinaryCompare
)) {
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 (node
->kind
== 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 if (gCaseSensitive
) {
758 result
= BinaryUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
759 searchKey
->nodeName
.length
,
760 &trialKey
->nodeName
.unicode
[0],
761 trialKey
->nodeName
.length
);
763 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
764 searchKey
->nodeName
.length
,
765 &trialKey
->nodeName
.unicode
[0],
766 trialKey
->nodeName
.length
);
773 static long CompareHFSExtentsKeys(void *key
, void *testKey
)
775 HFSExtentKey
*searchKey
, *trialKey
;
781 // assume searchKey < trialKey
784 if (searchKey
->fileID
== trialKey
->fileID
) {
785 // FileNum's are equal; compare fork types
786 if (searchKey
->forkType
== trialKey
->forkType
) {
787 // Fork types are equal; compare allocation block number
788 if (searchKey
->startBlock
== trialKey
->startBlock
) {
789 // Everything is equal
792 // Allocation block numbers differ; determine sign
793 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
796 // Fork types differ; determine sign
797 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
800 // FileNums differ; determine sign
801 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;
807 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
)
809 HFSPlusExtentKey
*searchKey
, *trialKey
;
815 // assume searchKey < trialKey
818 if (searchKey
->fileID
== trialKey
->fileID
) {
819 // FileNum's are equal; compare fork types
820 if (searchKey
->forkType
== trialKey
->forkType
) {
821 // Fork types are equal; compare allocation block number
822 if (searchKey
->startBlock
== trialKey
->startBlock
) {
823 // Everything is equal
826 // Allocation block numbers differ; determine sign
827 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
830 // Fork types differ; determine sign
831 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
834 // FileNums differ; determine sign
835 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;