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-2004 Apple Computer, Inc.
31 #include <hfs/hfs_format.h>
33 #define kBlockSize (0x200)
35 #define kMDBBaseOffset (2 * kBlockSize)
37 #define kBTreeCatalog (0)
38 #define kBTreeExtents (1)
40 static CICell gCurrentIH
;
41 static long long gAllocationOffset
;
42 static long gIsHFSPlus
;
43 static long gCaseSensitive
;
44 static long gBlockSize
;
45 static char gBTreeHeaderBuffer
[512];
46 static BTHeaderRec
*gBTHeaders
[2];
47 static char gHFSMdbVib
[kBlockSize
];
48 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
49 static char gHFSPlusHeader
[kBlockSize
];
50 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
51 static char gLinkTemp
[64];
52 static long long gVolID
;
55 static long ReadFile(void *file
, long *length
, void *base
, long offset
);
56 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
);
57 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
58 void *entry
, long dirID
, long *dirIndex
);
60 static long GetCatalogEntry(long *dirIndex
, char **name
,
61 long *flags
, long *time
);
62 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
64 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
66 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
67 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
68 char **key
, char **data
);
70 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
71 long offset
, long size
, void *buffer
, long cache
);
73 static long GetExtentStart(void *extents
, long index
);
74 static long GetExtentSize(void *extents
, long index
);
76 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
77 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
78 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
79 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
81 extern long FastRelString(char *str1
, char *str2
);
82 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
83 u_int16_t
*uniStr2
, u_int32_t len2
);
84 extern long BinaryUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
85 u_int16_t
*uniStr2
, u_int32_t len2
);
86 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
87 u_int8_t
*utf8p
, u_int32_t bufsize
);
88 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
89 u_int16_t
*ucslen
, u_int32_t bufsize
);
92 long HFSInitPartition(CICell ih
)
94 long extentSize
, extentFile
, nodeSize
;
97 if (ih
== gCurrentIH
) return 0;
99 printf("HFSInitPartition: %x\n", ih
);
101 gAllocationOffset
= 0;
107 // Look for the HFS MDB
108 Seek(ih
, kMDBBaseOffset
);
109 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
111 if (gHFSMDB
->drSigWord
== kHFSSigWord
) {
112 gAllocationOffset
= gHFSMDB
->drAlBlSt
* kBlockSize
;
114 // See if it is HFSPlus
115 if (gHFSMDB
->drEmbedSigWord
!= kHFSPlusSigWord
) {
117 gBlockSize
= gHFSMDB
->drAlBlkSiz
;
118 CacheInit(ih
, gBlockSize
);
121 // grab the 64 bit volume ID
122 bcopy(&gHFSMDB
->drFndrInfo
[6], &gVolID
, 8);
124 // Get the Catalog BTree node size.
125 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
126 extentSize
= gHFSMDB
->drCTFlSize
;
127 extentFile
= kHFSCatalogFileID
;
128 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
129 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
130 nodeSize
= ((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 + sizeof(BTNodeDescriptor
)))->nodeSize
;
132 // If the BTree node size is larger than the block size, reset the cache.
133 if (nodeSize
> gBlockSize
) CacheInit(ih
, nodeSize
);
138 // Calculate the offset to the embeded HFSPlus volume.
139 gAllocationOffset
+= (long long)gHFSMDB
->drEmbedExtent
.startBlock
* gHFSMDB
->drAlBlkSiz
;
142 // Look for the HFSPlus Header
143 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
144 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
146 //printf("checking signatures...\n");
147 // Not a HFS[+] volume.
148 if ((gHFSPlus
->signature
!= kHFSPlusSigWord
) &&
149 (gHFSPlus
->signature
!= kHFSXSigWord
)) return -1;
150 //printf("continuing with HFS\n");
153 gBlockSize
= gHFSPlus
->blockSize
;
154 CacheInit(ih
, gBlockSize
);
157 // grab the 64 bit volume ID
158 bcopy(&gHFSPlus
->finderInfo
[24], &gVolID
, 8);
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
)
176 return HFSReadFile(ih
, filePath
, (void *)kLoadAddr
, 0, 0);
179 extern long HFSReadFile(CICell ih
, char *filePath
, void *base
,
180 unsigned long offset
, unsigned long length
)
183 long dirID
, result
, flags
;
185 if (HFSInitPartition(ih
) == -1) return -1;
187 printf("%s HFS%s file: [%s] from %x.\n",
188 (((offset
== 0) && (length
== 0)) ? "Loading" : "Reading"),
189 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
191 dirID
= kHFSRootFolderID
;
192 // Skip a lead '\'. Start in the system folder if there are two.
193 if (filePath
[0] == '\\') {
194 if (filePath
[1] == '\\') {
195 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
196 else dirID
= gHFSMDB
->drFndrInfo
[5];
197 if (dirID
== 0) return -1;
203 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
204 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) return -1;
206 // Check file owner and permissions.
207 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) {
208 printf("%s: permissions incorrect\n", filePath
);
212 result
= ReadFile(entry
, &length
, base
, offset
);
213 if (result
== -1) return -1;
218 long HFSGetDirEntry(CICell ih
, char *dirPath
, long *dirIndex
, char **name
,
219 long *flags
, long *time
)
222 long dirID
, dirFlags
;
224 if (HFSInitPartition(ih
) == -1) return -1;
226 if (*dirIndex
== -1) return -1;
228 dirID
= kHFSRootFolderID
;
229 // Skip a lead '\'. Start in the system folder if there are two.
230 if (dirPath
[0] == '\\') {
231 if (dirPath
[1] == '\\') {
232 if (gIsHFSPlus
) dirID
= ((long *)gHFSPlus
->finderInfo
)[5];
233 else dirID
= gHFSMDB
->drFndrInfo
[5];
234 if (dirID
== 0) return -1;
240 if (*dirIndex
== 0) {
241 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
242 if (*dirIndex
== 0) *dirIndex
= -1;
243 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
246 GetCatalogEntry(dirIndex
, name
, flags
, time
);
247 if (*dirIndex
== 0) *dirIndex
= -1;
248 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
253 long HFSGetUUID(CICell ih
, char *uuidStr
)
255 if (HFSInitPartition(ih
) == -1) return -1;
256 if (gVolID
== 0LL) return -1;
258 return CreateUUIDString((uint8_t*)(&gVolID
), sizeof(gVolID
), uuidStr
);
264 static long ReadFile(void *file
, long *length
, void *base
, long offset
)
267 long fileID
, fileLength
;
268 HFSCatalogFile
*hfsFile
= file
;
269 HFSPlusCatalogFile
*hfsPlusFile
= file
;
272 fileID
= hfsPlusFile
->fileID
;
273 fileLength
= hfsPlusFile
->dataFork
.logicalSize
;
274 extents
= &hfsPlusFile
->dataFork
.extents
;
276 fileID
= hfsFile
->fileID
;
277 fileLength
= hfsFile
->dataLogicalSize
;
278 extents
= &hfsFile
->dataExtents
;
281 if (offset
> fileLength
) {
282 printf("Offset is too large.\n");
286 if ((*length
== 0) || ((offset
+ *length
) > fileLength
)) {
287 *length
= fileLength
- offset
;
290 if (*length
> kLoadSize
) {
291 printf("File is too large.\n");
295 *length
= ReadExtent((char *)extents
, fileLength
, fileID
,
296 offset
, *length
, base
, 0);
301 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
)
305 // Get information about the file.
306 switch (*(short *)entry
) {
307 case kHFSFolderRecord
:
308 *flags
= kFileTypeDirectory
;
309 tmpTime
= ((HFSCatalogFolder
*)entry
)->modifyDate
;
312 case kHFSPlusFolderRecord
:
313 *flags
= kFileTypeDirectory
|
314 (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
315 if (((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
!= 0) {
316 static int nwarnings
= 0;
317 if(nwarnings
++ < 25) // so we don't warn for all in an Extensions walk
318 printf("non-root file owner detected: %d\n",
319 ((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
);
320 *flags
|= kOwnerNotRoot
;
322 tmpTime
= ((HFSPlusCatalogFolder
*)entry
)->contentModDate
;
325 case kHFSFileRecord
:
326 *flags
= kFileTypeFlat
;
327 tmpTime
= ((HFSCatalogFile
*)entry
)->modifyDate
;
330 case kHFSPlusFileRecord
:
331 *flags
= kFileTypeFlat
|
332 (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
& kPermMask
);
333 if (((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
!= 0) {
334 printf("non-root file owner detected: %d\n",
335 ((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
);
336 *flags
|= kOwnerNotRoot
;
338 tmpTime
= ((HFSPlusCatalogFile
*)entry
)->contentModDate
;
341 case kHFSFileThreadRecord
:
342 case kHFSPlusFileThreadRecord
:
343 case kHFSFolderThreadRecord
:
344 case kHFSPlusFolderThreadRecord
:
345 *flags
= kFileTypeUnknown
;
351 // Convert base time from 1904 to 1970.
352 *time
= tmpTime
- 2082844800;
358 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
359 void *entry
, long dirID
, long *dirIndex
)
362 long result
, cnt
, subFolderID
= 0, tmpDirIndex
;
363 HFSPlusCatalogFile
*hfsPlusFile
;
365 // Copy the file name to gTempStr
367 while ((filePath
[cnt
] != '\\') && (filePath
[cnt
] != '\0')) cnt
++;
368 strncpy(gTempStr
, filePath
, cnt
);
370 // Move restPath to the right place.
371 if (filePath
[cnt
] != '\0') cnt
++;
372 restPath
= filePath
+ cnt
;
374 // gTempStr is a name in the current Dir.
375 // restPath is the rest of the path if any.
377 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
378 if (result
== -1) return -1;
380 GetCatalogEntryInfo(entry
, flags
, 0);
382 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
383 if (gIsHFSPlus
) subFolderID
= ((HFSPlusCatalogFolder
*)entry
)->folderID
;
384 else subFolderID
= ((HFSCatalogFolder
*)entry
)->folderID
;
387 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
)
388 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
389 subFolderID
, dirIndex
);
391 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
392 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
393 if ((hfsPlusFile
->userInfo
.fdType
== kHardLinkFileType
) &&
394 (hfsPlusFile
->userInfo
.fdCreator
== kHFSPlusCreator
)) {
395 sprintf(gLinkTemp
, "%s\\%s%d", HFSPLUSMETADATAFOLDER
,
396 HFS_INODE_PREFIX
, hfsPlusFile
->bsdInfo
.special
.iNodeNum
);
397 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
398 kHFSRootFolderID
, &tmpDirIndex
);
405 static long GetCatalogEntry(long *dirIndex
, char **name
,
406 long *flags
, long *time
)
408 long extentSize
, nodeSize
, curNode
, index
;
410 char *nodeBuf
, *testKey
, *entry
;
411 BTNodeDescriptor
*node
;
414 extent
= &gHFSPlus
->catalogFile
.extents
;
415 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
417 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
418 extentSize
= gHFSMDB
->drCTFlSize
;
421 nodeSize
= gBTHeaders
[kBTreeCatalog
]->nodeSize
;
422 nodeBuf
= (char *)malloc(nodeSize
);
423 node
= (BTNodeDescriptor
*)nodeBuf
;
425 index
= *dirIndex
% nodeSize
;
426 curNode
= *dirIndex
/ nodeSize
;
428 // Read the BTree node and get the record for index.
429 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
430 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
431 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
433 GetCatalogEntryInfo(entry
, flags
, time
);
435 // Get the file name.
437 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
438 ((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
,
442 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
443 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
449 if (index
== node
->numRecords
) {
451 curNode
= node
->fLink
;
453 *dirIndex
= curNode
* nodeSize
+ index
;
460 static long ReadCatalogEntry(char *fileName
, long dirID
,
461 void *entry
, long *dirIndex
)
464 char key
[sizeof(HFSPlusCatalogKey
)];
465 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
466 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
468 // Make the catalog key.
470 hfsPlusKey
->parentID
= dirID
;
471 length
= strlen(fileName
);
472 if (length
> 255) length
= 255;
473 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
474 &(hfsPlusKey
->nodeName
.length
), 512);
476 hfsKey
->parentID
= dirID
;
477 length
= strlen(fileName
);
478 if (length
> 31) length
= 31;
479 hfsKey
->nodeName
[0] = length
;
480 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
483 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
486 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
)
488 char key
[sizeof(HFSPlusExtentKey
)];
489 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
490 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
492 // Make the extents key.
494 hfsPlusKey
->forkType
= 0;
495 hfsPlusKey
->fileID
= fileID
;
496 hfsPlusKey
->startBlock
= startBlock
;
498 hfsKey
->forkType
= 0;
499 hfsKey
->fileID
= fileID
;
500 hfsKey
->startBlock
= startBlock
;
503 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
506 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
)
512 BTNodeDescriptor
*node
;
513 long nodeSize
, result
= 0, entrySize
= 0;
514 long curNode
, index
= 0, lowerBound
, upperBound
;
515 char *testKey
, *recordData
;
517 // Figure out which tree is being looked at.
518 if (btree
== kBTreeCatalog
) {
520 extent
= &gHFSPlus
->catalogFile
.extents
;
521 extentSize
= gHFSPlus
->catalogFile
.logicalSize
;
523 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
524 extentSize
= gHFSMDB
->drCTFlSize
;
526 extentFile
= kHFSCatalogFileID
;
529 extent
= &gHFSPlus
->extentsFile
.extents
;
530 extentSize
= gHFSPlus
->extentsFile
.logicalSize
;
532 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
533 extentSize
= gHFSMDB
->drXTFlSize
;
535 extentFile
= kHFSExtentsFileID
;
538 // Read the BTree Header if needed.
539 if (gBTHeaders
[btree
] == 0) {
540 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
541 gBTreeHeaderBuffer
+ btree
* 256, 0);
542 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
543 sizeof(BTNodeDescriptor
));
544 if ((gIsHFSPlus
&& btree
== kBTreeCatalog
) &&
545 (gBTHeaders
[btree
]->keyCompareType
== kHFSBinaryCompare
)) {
550 curNode
= gBTHeaders
[btree
]->rootNode
;
552 nodeSize
= gBTHeaders
[btree
]->nodeSize
;
553 nodeBuf
= (char *)malloc(nodeSize
);
554 node
= (BTNodeDescriptor
*)nodeBuf
;
557 // Read the current node.
558 ReadExtent(extent
, extentSize
, extentFile
,
559 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
561 // Find the matching key.
563 upperBound
= node
->numRecords
- 1;
564 while (lowerBound
<= upperBound
) {
565 index
= (lowerBound
+ upperBound
) / 2;
567 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
570 if (btree
== kBTreeCatalog
) {
571 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
573 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
576 if (btree
== kBTreeCatalog
) {
577 result
= CompareHFSCatalogKeys(key
, testKey
);
579 result
= CompareHFSExtentsKeys(key
, testKey
);
583 if (result
< 0) upperBound
= index
- 1; // search < trial
584 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
585 else break; // search = trial
590 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
593 // Found the closest key... Recurse on it if this is an index node.
594 if (node
->kind
== kBTIndexNode
) {
595 curNode
= *((long *)recordData
);
599 // Return error if the file was not found.
600 if (result
!= 0) return -1;
602 if (btree
== kBTreeCatalog
) {
603 switch (*(short *)recordData
) {
604 case kHFSFolderRecord
: entrySize
= 70; break;
605 case kHFSFileRecord
: entrySize
= 102; break;
606 case kHFSFolderThreadRecord
: entrySize
= 46; break;
607 case kHFSFileThreadRecord
: entrySize
= 46; break;
608 case kHFSPlusFolderRecord
: entrySize
= 88; break;
609 case kHFSPlusFileRecord
: entrySize
= 248; break;
610 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
611 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
614 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
615 else entrySize
= sizeof(HFSExtentRecord
);
618 bcopy(recordData
, entry
, entrySize
);
623 if (index
== node
->numRecords
) {
625 curNode
= node
->fLink
;
627 *dirIndex
= curNode
* nodeSize
+ index
;
635 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
636 char **key
, char **data
)
641 recordOffset
= *((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2)));
642 *key
= nodeBuffer
+ recordOffset
;
644 keySize
= *(short *)*key
;
645 *data
= *key
+ 2 + keySize
;
648 *data
= *key
+ 2 + keySize
- (keySize
& 1);
652 static long ReadExtent(char *extent
, long extentSize
,
653 long extentFile
, long offset
, long size
,
654 void *buffer
, long cache
)
656 long lastOffset
, blockNumber
, countedBlocks
= 0;
657 long nextExtent
= 0, sizeRead
= 0, readSize
;
658 long nextExtentBlock
, currentExtentBlock
= 0;
659 long long readOffset
;
660 long extentDensity
, sizeofExtent
, currentExtentSize
;
661 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
663 if (offset
>= extentSize
) return 0;
666 extentDensity
= kHFSPlusExtentDensity
;
667 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
669 extentDensity
= kHFSExtentDensity
;
670 sizeofExtent
= sizeof(HFSExtentDescriptor
);
673 lastOffset
= offset
+ size
;
674 while (offset
< lastOffset
) {
675 blockNumber
= offset
/ gBlockSize
;
677 // Find the extent for the offset.
678 for (; ; nextExtent
++) {
679 if (nextExtent
< extentDensity
) {
680 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
681 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
685 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
689 if (extentBuffer
== 0) {
690 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
691 if (extentBuffer
== 0) return -1;
694 nextExtentBlock
= nextExtent
/ extentDensity
;
695 if (currentExtentBlock
!= nextExtentBlock
) {
696 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
697 currentExtentBlock
= nextExtentBlock
;
700 currentExtentSize
= GetExtentSize(extentBuffer
,
701 nextExtent
% extentDensity
);
703 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
704 currentExtent
= extentBuffer
+ sizeofExtent
*
705 (nextExtent
% extentDensity
);
709 countedBlocks
+= currentExtentSize
;
712 readOffset
= ((blockNumber
- countedBlocks
) * gBlockSize
) +
713 (offset
% gBlockSize
);
715 readSize
= GetExtentSize(currentExtent
, 0) * gBlockSize
- readOffset
;
716 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
718 readOffset
+= (long long)GetExtentStart(currentExtent
, 0) * gBlockSize
;
720 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
723 sizeRead
+= readSize
;
725 bufferPos
+= readSize
;
728 if (extentBuffer
) free(extentBuffer
);
733 static long GetExtentStart(void *extents
, long index
)
736 HFSExtentDescriptor
*hfsExtents
= extents
;
737 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
739 if (gIsHFSPlus
) start
= hfsPlusExtents
[index
].startBlock
;
740 else start
= hfsExtents
[index
].startBlock
;
745 static long GetExtentSize(void *extents
, long index
)
748 HFSExtentDescriptor
*hfsExtents
= extents
;
749 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
751 if (gIsHFSPlus
) size
= hfsPlusExtents
[index
].blockCount
;
752 else size
= hfsExtents
[index
].blockCount
;
757 static long CompareHFSCatalogKeys(void *key
, void *testKey
)
759 HFSCatalogKey
*searchKey
, *trialKey
;
760 long result
, searchParentID
, trialParentID
;
765 searchParentID
= searchKey
->parentID
;
766 trialParentID
= trialKey
->parentID
;
768 // parent dirID is unsigned
769 if (searchParentID
> trialParentID
) result
= 1;
770 else if (searchParentID
< trialParentID
) result
= -1;
772 // parent dirID's are equal, compare names
773 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
779 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
)
781 HFSPlusCatalogKey
*searchKey
, *trialKey
;
782 long result
, searchParentID
, trialParentID
;
787 searchParentID
= searchKey
->parentID
;
788 trialParentID
= trialKey
->parentID
;
790 // parent dirID is unsigned
791 if (searchParentID
> trialParentID
) result
= 1;
792 else if (searchParentID
< trialParentID
) result
= -1;
794 // parent dirID's are equal, compare names
795 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
796 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
798 if (gCaseSensitive
) {
799 result
= BinaryUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
800 searchKey
->nodeName
.length
,
801 &trialKey
->nodeName
.unicode
[0],
802 trialKey
->nodeName
.length
);
804 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
805 searchKey
->nodeName
.length
,
806 &trialKey
->nodeName
.unicode
[0],
807 trialKey
->nodeName
.length
);
814 static long CompareHFSExtentsKeys(void *key
, void *testKey
)
816 HFSExtentKey
*searchKey
, *trialKey
;
822 // assume searchKey < trialKey
825 if (searchKey
->fileID
== trialKey
->fileID
) {
826 // FileNum's are equal; compare fork types
827 if (searchKey
->forkType
== trialKey
->forkType
) {
828 // Fork types are equal; compare allocation block number
829 if (searchKey
->startBlock
== trialKey
->startBlock
) {
830 // Everything is equal
833 // Allocation block numbers differ; determine sign
834 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
837 // Fork types differ; determine sign
838 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
841 // FileNums differ; determine sign
842 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;
848 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
)
850 HFSPlusExtentKey
*searchKey
, *trialKey
;
856 // assume searchKey < trialKey
859 if (searchKey
->fileID
== trialKey
->fileID
) {
860 // FileNum's are equal; compare fork types
861 if (searchKey
->forkType
== trialKey
->forkType
) {
862 // Fork types are equal; compare allocation block number
863 if (searchKey
->startBlock
== trialKey
->startBlock
) {
864 // Everything is equal
867 // Allocation block numbers differ; determine sign
868 if (searchKey
->startBlock
> trialKey
->startBlock
) result
= 1;
871 // Fork types differ; determine sign
872 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
875 // FileNums differ; determine sign
876 if (searchKey
->fileID
> trialKey
->fileID
) result
= 1;