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>
38 #define kBlockSize (0x200)
40 #define kMDBBaseOffset (2 * kBlockSize)
42 #define kBTreeCatalog (0)
43 #define kBTreeExtents (1)
47 static CICell gCurrentIH
;
48 static long long gAllocationOffset
;
49 static long gIsHFSPlus
;
50 static long gBlockSize
;
51 static long gCacheBlockSize
;
52 static char *gBTreeHeaderBuffer
;
53 static BTHeaderRec
*gBTHeaders
[2];
54 static char *gHFSMdbVib
;
55 static HFSMasterDirectoryBlock
*gHFSMDB
;
56 static char *gHFSPlusHeader
;
57 static HFSPlusVolumeHeader
*gHFSPlus
;
58 static char *gLinkTemp
;
59 static char *gTempStr
;
63 static CICell gCurrentIH
;
64 static long long gAllocationOffset
;
65 static long gIsHFSPlus
;
66 static long gBlockSize
;
67 static long gCacheBlockSize
;
68 static char gBTreeHeaderBuffer
[512];
69 static BTHeaderRec
*gBTHeaders
[2];
70 static char gHFSMdbVib
[kBlockSize
];
71 static HFSMasterDirectoryBlock
*gHFSMDB
=(HFSMasterDirectoryBlock
*)gHFSMdbVib
;
72 static char gHFSPlusHeader
[kBlockSize
];
73 static HFSPlusVolumeHeader
*gHFSPlus
=(HFSPlusVolumeHeader
*)gHFSPlusHeader
;
74 static char gLinkTemp
[64];
76 #endif /* !__i386__ */
78 static long ReadFile(void *file
, long *length
);
79 static long GetCatalogEntryInfo(void *entry
, long *flags
, long *time
);
80 static long ResolvePathToCatalogEntry(char *filePath
, long *flags
,
81 void *entry
, long dirID
, long *dirIndex
);
83 static long GetCatalogEntry(long *dirIndex
, char **name
,
84 long *flags
, long *time
);
85 static long ReadCatalogEntry(char *fileName
, long dirID
, void *entry
,
87 static long ReadExtentsEntry(long fileID
, long startBlock
, void *entry
);
89 static long ReadBTreeEntry(long btree
, void *key
, char *entry
, long *dirIndex
);
90 static void GetBTreeRecord(long index
, char *nodeBuffer
, long nodeSize
,
91 char **key
, char **data
);
93 static long ReadExtent(char *extent
, long extentSize
, long extentFile
,
94 long offset
, long size
, void *buffer
, long cache
);
96 static long GetExtentStart(void *extents
, long index
);
97 static long GetExtentSize(void *extents
, long index
);
99 static long CompareHFSCatalogKeys(void *key
, void *testKey
);
100 static long CompareHFSPlusCatalogKeys(void *key
, void *testKey
);
101 static long CompareHFSExtentsKeys(void *key
, void *testKey
);
102 static long CompareHFSPlusExtentsKeys(void *key
, void *testKey
);
104 extern long FastRelString(char *str1
, char *str2
);
105 extern long FastUnicodeCompare(u_int16_t
*uniStr1
, u_int32_t len1
,
106 u_int16_t
*uniStr2
, u_int32_t len2
);
107 extern void utf_encodestr(const u_int16_t
*ucsp
, int ucslen
,
108 u_int8_t
*utf8p
, u_int32_t bufsize
);
109 extern void utf_decodestr(const u_int8_t
*utf8p
, u_int16_t
*ucsp
,
110 u_int16_t
*ucslen
, u_int32_t bufsize
);
113 long HFSInitPartition(CICell ih
)
115 long extentSize
, extentFile
, nodeSize
;
118 if (ih
== gCurrentIH
) {
120 CacheInit(ih
, gCacheBlockSize
);
126 if (!gTempStr
) gTempStr
= (char *)malloc(4096);
127 if (!gLinkTemp
) gLinkTemp
= (char *)malloc(64);
128 if (!gBTreeHeaderBuffer
) gBTreeHeaderBuffer
= (char *)malloc(512);
130 gHFSMdbVib
= (char *)malloc(kBlockSize
);
131 gHFSMDB
= (HFSMasterDirectoryBlock
*)gHFSMdbVib
;
133 if (!gHFSPlusHeader
) {
134 gHFSPlusHeader
= (char *)malloc(kBlockSize
);
135 gHFSPlus
= (HFSPlusVolumeHeader
*)gHFSPlusHeader
;
137 if (!gTempStr
|| !gLinkTemp
|| !gBTreeHeaderBuffer
||
138 !gHFSMdbVib
|| !gHFSPlusHeader
) return -1;
139 #endif /* __i386__ */
141 gAllocationOffset
= 0;
146 // Look for the HFS MDB
147 Seek(ih
, kMDBBaseOffset
);
148 Read(ih
, (long)gHFSMdbVib
, kBlockSize
);
150 if ( SWAP_BE16(gHFSMDB
->drSigWord
) == kHFSSigWord
) {
151 gAllocationOffset
= SWAP_BE16(gHFSMDB
->drAlBlSt
) * kBlockSize
;
153 // See if it is HFSPlus
154 if (SWAP_BE16(gHFSMDB
->drEmbedSigWord
) != kHFSPlusSigWord
) {
156 gCacheBlockSize
= gBlockSize
= SWAP_BE32(gHFSMDB
->drAlBlkSiz
);
157 CacheInit(ih
, gCacheBlockSize
);
160 // Get the Catalog BTree node size.
161 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
162 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
163 extentFile
= kHFSCatalogFileID
;
164 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
165 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
167 nodeSize
= SWAP_BE16(((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 +
168 sizeof(BTNodeDescriptor
)))->nodeSize
);
170 // If the BTree node size is larger than the block size, reset the cache.
171 if (nodeSize
> gBlockSize
) {
172 gCacheBlockSize
= nodeSize
;
173 CacheInit(ih
, gCacheBlockSize
);
179 // Calculate the offset to the embeded HFSPlus volume.
180 gAllocationOffset
+= (long long)SWAP_BE16(gHFSMDB
->drEmbedExtent
.startBlock
) *
181 SWAP_BE32(gHFSMDB
->drAlBlkSiz
);
184 // Look for the HFSPlus Header
185 Seek(ih
, gAllocationOffset
+ kMDBBaseOffset
);
186 Read(ih
, (long)gHFSPlusHeader
, kBlockSize
);
188 // Not a HFS[+] volume.
189 if (SWAP_BE16(gHFSPlus
->signature
) != kHFSPlusSigWord
) {
190 verbose("HFS signature was not present.\n");
195 gCacheBlockSize
= gBlockSize
= SWAP_BE32(gHFSPlus
->blockSize
);
196 CacheInit(ih
, gCacheBlockSize
);
199 // Get the Catalog BTree node size.
200 extent
= &gHFSPlus
->catalogFile
.extents
;
201 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
202 extentFile
= kHFSCatalogFileID
;
204 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
205 gBTreeHeaderBuffer
+ kBTreeCatalog
* 256, 0);
207 nodeSize
= SWAP_BE16(((BTHeaderRec
*)(gBTreeHeaderBuffer
+ kBTreeCatalog
* 256 +
208 sizeof(BTNodeDescriptor
)))->nodeSize
);
210 // If the BTree node size is larger than the block size, reset the cache.
211 if (nodeSize
> gBlockSize
) {
212 gCacheBlockSize
= nodeSize
;
213 CacheInit(ih
, gCacheBlockSize
);
219 long HFSLoadFile(CICell ih
, char * filePath
)
222 long dirID
, result
, length
, flags
;
224 verbose("Loading HFS%s file: [%s] from %x.\n",
225 (gIsHFSPlus
? "+" : ""), filePath
, ih
);
227 if (HFSInitPartition(ih
) == -1) return -1;
229 dirID
= kHFSRootFolderID
;
230 // Skip a lead '\'. Start in the system folder if there are two.
231 if (filePath
[0] == '/') {
232 if (filePath
[1] == '/') {
233 if (gIsHFSPlus
) dirID
= SWAP_BE32(((long *)gHFSPlus
->finderInfo
)[5]);
234 else dirID
= SWAP_BE32(gHFSMDB
->drFndrInfo
[5]);
243 result
= ResolvePathToCatalogEntry(filePath
, &flags
, entry
, dirID
, 0);
244 if ((result
== -1) || ((flags
& kFileTypeMask
) != kFileTypeFlat
)) {
248 #if 0 // Not yet for Intel. System.config/Default.table will fail this check.
249 // Check file owner and permissions.
250 if (flags
& (kOwnerNotRoot
| kPermGroupWrite
| kPermOtherWrite
)) return -1;
253 result
= ReadFile(entry
, &length
);
261 long HFSGetDirEntry(CICell ih
, char * dirPath
, long * dirIndex
, char ** name
,
262 long * flags
, long * time
)
265 long dirID
, dirFlags
;
267 if (HFSInitPartition(ih
) == -1) return -1;
269 if (*dirIndex
== -1) return -1;
271 dirID
= kHFSRootFolderID
;
272 // Skip a lead '\'. Start in the system folder if there are two.
273 if (dirPath
[0] == '/') {
274 if (dirPath
[1] == '/') {
275 if (gIsHFSPlus
) dirID
= SWAP_BE32(((long *)gHFSPlus
->finderInfo
)[5]);
276 else dirID
= SWAP_BE32(gHFSMDB
->drFndrInfo
[5]);
277 if (dirID
== 0) return -1;
283 if (*dirIndex
== 0) {
284 ResolvePathToCatalogEntry(dirPath
, &dirFlags
, entry
, dirID
, dirIndex
);
285 if (*dirIndex
== 0) *dirIndex
= -1;
286 if ((dirFlags
& kFileTypeMask
) != kFileTypeUnknown
) return -1;
289 GetCatalogEntry(dirIndex
, name
, flags
, time
);
290 if (*dirIndex
== 0) *dirIndex
= -1;
291 if ((*flags
& kFileTypeMask
) == kFileTypeUnknown
) return -1;
298 static long ReadFile(void * file
, long * length
)
302 HFSCatalogFile
*hfsFile
= file
;
303 HFSPlusCatalogFile
*hfsPlusFile
= file
;
306 fileID
= SWAP_BE32(hfsPlusFile
->fileID
);
307 *length
= SWAP_BE64(hfsPlusFile
->dataFork
.logicalSize
);
308 extents
= &hfsPlusFile
->dataFork
.extents
;
310 fileID
= SWAP_BE32(hfsFile
->fileID
);
311 *length
= SWAP_BE32(hfsFile
->dataLogicalSize
);
312 extents
= &hfsFile
->dataExtents
;
315 if (*length
> kLoadSize
) {
316 printf("File is too large.\n");
321 *length
= ReadExtent((char *)extents
, *length
, fileID
,
322 0, *length
, (char *)gFSLoadAddress
, 0);
324 *length
= ReadExtent((char *)extents
, *length
, fileID
,
325 0, *length
, (char *)kLoadAddr
, 0);
331 static long GetCatalogEntryInfo(void * entry
, long * flags
, long * time
)
335 // Get information about the file.
337 switch ( SWAP_BE16(*(short *)entry
) )
339 case kHFSFolderRecord
:
340 *flags
= kFileTypeDirectory
;
341 tmpTime
= SWAP_BE32(((HFSCatalogFolder
*)entry
)->modifyDate
);
344 case kHFSPlusFolderRecord
:
345 *flags
= kFileTypeDirectory
|
346 (SWAP_BE16(((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.fileMode
) & kPermMask
);
347 if (SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->bsdInfo
.ownerID
) != 0)
348 *flags
|= kOwnerNotRoot
;
349 tmpTime
= SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->contentModDate
);
352 case kHFSFileRecord
:
353 *flags
= kFileTypeFlat
;
354 tmpTime
= SWAP_BE32(((HFSCatalogFile
*)entry
)->modifyDate
);
357 case kHFSPlusFileRecord
:
358 *flags
= kFileTypeFlat
|
359 (SWAP_BE16(((HFSPlusCatalogFile
*)entry
)->bsdInfo
.fileMode
) & kPermMask
);
360 if (SWAP_BE32(((HFSPlusCatalogFile
*)entry
)->bsdInfo
.ownerID
) != 0)
361 *flags
|= kOwnerNotRoot
;
362 tmpTime
= SWAP_BE32(((HFSPlusCatalogFile
*)entry
)->contentModDate
);
365 case kHFSFileThreadRecord
:
366 case kHFSPlusFileThreadRecord
:
367 case kHFSFolderThreadRecord
:
368 case kHFSPlusFolderThreadRecord
:
369 *flags
= kFileTypeUnknown
;
375 // Convert base time from 1904 to 1970.
376 *time
= tmpTime
- 2082844800;
382 static long ResolvePathToCatalogEntry(char * filePath
, long * flags
,
383 void * entry
, long dirID
, long * dirIndex
)
386 long result
, cnt
, subFolderID
, tmpDirIndex
;
387 HFSPlusCatalogFile
*hfsPlusFile
;
389 // Copy the file name to gTempStr
391 while ((filePath
[cnt
] != '/') && (filePath
[cnt
] != '\0')) cnt
++;
392 strlcpy(gTempStr
, filePath
, cnt
+1);
394 // Move restPath to the right place.
395 if (filePath
[cnt
] != '\0') cnt
++;
396 restPath
= filePath
+ cnt
;
398 // gTempStr is a name in the current Dir.
399 // restPath is the rest of the path if any.
401 result
= ReadCatalogEntry(gTempStr
, dirID
, entry
, dirIndex
);
406 GetCatalogEntryInfo(entry
, flags
, 0);
408 if ((*flags
& kFileTypeMask
) == kFileTypeDirectory
) {
410 subFolderID
= SWAP_BE32(((HFSPlusCatalogFolder
*)entry
)->folderID
);
412 subFolderID
= SWAP_BE32(((HFSCatalogFolder
*)entry
)->folderID
);
414 result
= ResolvePathToCatalogEntry(restPath
, flags
, entry
,
415 subFolderID
, dirIndex
);
418 if (gIsHFSPlus
&& ((*flags
& kFileTypeMask
) == kFileTypeFlat
)) {
419 hfsPlusFile
= (HFSPlusCatalogFile
*)entry
;
420 if ((SWAP_BE32(hfsPlusFile
->userInfo
.fdType
) == kHardLinkFileType
) &&
421 (SWAP_BE32(hfsPlusFile
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
422 sprintf(gLinkTemp
, "%s/%s%ld", HFSPLUSMETADATAFOLDER
,
423 HFS_INODE_PREFIX
, SWAP_BE32(hfsPlusFile
->bsdInfo
.special
.iNodeNum
));
424 result
= ResolvePathToCatalogEntry(gLinkTemp
, flags
, entry
,
425 kHFSRootFolderID
, &tmpDirIndex
);
432 static long GetCatalogEntry(long * dirIndex
, char ** name
,
433 long * flags
, long * time
)
435 long extentSize
, nodeSize
, curNode
, index
;
437 char *nodeBuf
, *testKey
, *entry
;
438 BTNodeDescriptor
*node
;
441 extent
= &gHFSPlus
->catalogFile
.extents
;
442 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
444 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
445 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
448 nodeSize
= SWAP_BE16(gBTHeaders
[kBTreeCatalog
]->nodeSize
);
449 nodeBuf
= (char *)malloc(nodeSize
);
450 node
= (BTNodeDescriptor
*)nodeBuf
;
452 index
= *dirIndex
% nodeSize
;
453 curNode
= *dirIndex
/ nodeSize
;
455 // Read the BTree node and get the record for index.
456 ReadExtent(extent
, extentSize
, kHFSCatalogFileID
,
457 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
458 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &entry
);
460 GetCatalogEntryInfo(entry
, flags
, time
);
462 // Get the file name.
464 utf_encodestr(((HFSPlusCatalogKey
*)testKey
)->nodeName
.unicode
,
465 SWAP_BE16(((HFSPlusCatalogKey
*)testKey
)->nodeName
.length
),
469 &((HFSCatalogKey
*)testKey
)->nodeName
[1],
470 ((HFSCatalogKey
*)testKey
)->nodeName
[0]);
476 if (index
== SWAP_BE16(node
->numRecords
)) {
478 curNode
= SWAP_BE32(node
->fLink
);
480 *dirIndex
= curNode
* nodeSize
+ index
;
487 static long ReadCatalogEntry(char * fileName
, long dirID
,
488 void * entry
, long * dirIndex
)
491 char key
[sizeof(HFSPlusCatalogKey
)];
492 HFSCatalogKey
*hfsKey
= (HFSCatalogKey
*)key
;
493 HFSPlusCatalogKey
*hfsPlusKey
= (HFSPlusCatalogKey
*)key
;
495 // Make the catalog key.
498 hfsPlusKey
->parentID
= SWAP_BE32(dirID
);
499 length
= strlen(fileName
);
500 if (length
> 255) length
= 255;
501 utf_decodestr(fileName
, hfsPlusKey
->nodeName
.unicode
,
502 &(hfsPlusKey
->nodeName
.length
), 512);
504 hfsKey
->parentID
= SWAP_BE32(dirID
);
505 length
= strlen(fileName
);
506 if (length
> 31) length
= 31;
507 hfsKey
->nodeName
[0] = length
;
508 strncpy(hfsKey
->nodeName
+ 1, fileName
, length
);
511 return ReadBTreeEntry(kBTreeCatalog
, &key
, entry
, dirIndex
);
514 static long ReadExtentsEntry(long fileID
, long startBlock
, void * entry
)
516 char key
[sizeof(HFSPlusExtentKey
)];
517 HFSExtentKey
*hfsKey
= (HFSExtentKey
*)key
;
518 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*)key
;
520 // Make the extents key.
522 hfsPlusKey
->forkType
= 0;
523 hfsPlusKey
->fileID
= SWAP_BE32(fileID
);
524 hfsPlusKey
->startBlock
= SWAP_BE32(startBlock
);
526 hfsKey
->forkType
= 0;
527 hfsKey
->fileID
= SWAP_BE32(fileID
);
528 hfsKey
->startBlock
= SWAP_BE16(startBlock
);
531 return ReadBTreeEntry(kBTreeExtents
, &key
, entry
, 0);
534 static long ReadBTreeEntry(long btree
, void * key
, char * entry
, long * dirIndex
)
540 BTNodeDescriptor
*node
;
541 long nodeSize
, result
= 0, entrySize
= 0;
542 long curNode
, index
= 0, lowerBound
, upperBound
;
543 char *testKey
, *recordData
;
545 // Figure out which tree is being looked at.
546 if (btree
== kBTreeCatalog
) {
548 extent
= &gHFSPlus
->catalogFile
.extents
;
549 extentSize
= SWAP_BE64(gHFSPlus
->catalogFile
.logicalSize
);
551 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drCTExtRec
;
552 extentSize
= SWAP_BE32(gHFSMDB
->drCTFlSize
);
554 extentFile
= kHFSCatalogFileID
;
557 extent
= &gHFSPlus
->extentsFile
.extents
;
558 extentSize
= SWAP_BE64(gHFSPlus
->extentsFile
.logicalSize
);
560 extent
= (HFSExtentDescriptor
*)&gHFSMDB
->drXTExtRec
;
561 extentSize
= SWAP_BE32(gHFSMDB
->drXTFlSize
);
563 extentFile
= kHFSExtentsFileID
;
566 // Read the BTree Header if needed.
567 if (gBTHeaders
[btree
] == 0) {
568 ReadExtent(extent
, extentSize
, extentFile
, 0, 256,
569 gBTreeHeaderBuffer
+ btree
* 256, 0);
570 gBTHeaders
[btree
] = (BTHeaderRec
*)(gBTreeHeaderBuffer
+ btree
* 256 +
571 sizeof(BTNodeDescriptor
));
574 curNode
= SWAP_BE32(gBTHeaders
[btree
]->rootNode
);
575 nodeSize
= SWAP_BE16(gBTHeaders
[btree
]->nodeSize
);
576 nodeBuf
= (char *)malloc(nodeSize
);
577 node
= (BTNodeDescriptor
*)nodeBuf
;
580 // Read the current node.
581 ReadExtent(extent
, extentSize
, extentFile
,
582 curNode
* nodeSize
, nodeSize
, nodeBuf
, 1);
584 // Find the matching key.
586 upperBound
= SWAP_BE16(node
->numRecords
) - 1;
587 while (lowerBound
<= upperBound
) {
588 index
= (lowerBound
+ upperBound
) / 2;
590 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
593 if (btree
== kBTreeCatalog
) {
594 result
= CompareHFSPlusCatalogKeys(key
, testKey
);
596 result
= CompareHFSPlusExtentsKeys(key
, testKey
);
599 if (btree
== kBTreeCatalog
) {
600 result
= CompareHFSCatalogKeys(key
, testKey
);
602 result
= CompareHFSExtentsKeys(key
, testKey
);
606 if (result
< 0) upperBound
= index
- 1; // search < trial
607 else if (result
> 0) lowerBound
= index
+ 1; // search > trial
608 else break; // search = trial
613 GetBTreeRecord(index
, nodeBuf
, nodeSize
, &testKey
, &recordData
);
616 // Found the closest key... Recurse on it if this is an index node.
617 if (node
->kind
== kBTIndexNode
) {
618 curNode
= SWAP_BE32( *((long *)recordData
) );
622 // Return error if the file was not found.
623 if (result
!= 0) { free(nodeBuf
); return -1; }
625 if (btree
== kBTreeCatalog
) {
626 switch (SWAP_BE16(*(short *)recordData
)) {
627 case kHFSFolderRecord
: entrySize
= 70; break;
628 case kHFSFileRecord
: entrySize
= 102; break;
629 case kHFSFolderThreadRecord
: entrySize
= 46; break;
630 case kHFSFileThreadRecord
: entrySize
= 46; break;
631 case kHFSPlusFolderRecord
: entrySize
= 88; break;
632 case kHFSPlusFileRecord
: entrySize
= 248; break;
633 case kHFSPlusFolderThreadRecord
: entrySize
= 264; break;
634 case kHFSPlusFileThreadRecord
: entrySize
= 264; break;
637 if (gIsHFSPlus
) entrySize
= sizeof(HFSPlusExtentRecord
);
638 else entrySize
= sizeof(HFSExtentRecord
);
641 bcopy(recordData
, entry
, entrySize
);
646 if (index
== SWAP_BE16(node
->numRecords
)) {
648 curNode
= SWAP_BE32(node
->fLink
);
650 *dirIndex
= curNode
* nodeSize
+ index
;
658 static void GetBTreeRecord(long index
, char * nodeBuffer
, long nodeSize
,
659 char ** key
, char ** data
)
664 recordOffset
= SWAP_BE16(*((short *)(nodeBuffer
+ (nodeSize
- 2 * index
- 2))));
665 *key
= nodeBuffer
+ recordOffset
;
667 keySize
= SWAP_BE16(*(short *)*key
);
668 *data
= *key
+ 2 + keySize
;
671 *data
= *key
+ 2 + keySize
- (keySize
& 1);
675 static long ReadExtent(char * extent
, long extentSize
,
676 long extentFile
, long offset
, long size
,
677 void * buffer
, long cache
)
679 long lastOffset
, blockNumber
, countedBlocks
= 0;
680 long nextExtent
= 0, sizeRead
= 0, readSize
;
681 long nextExtentBlock
, currentExtentBlock
= 0;
682 long long readOffset
;
683 long extentDensity
, sizeofExtent
, currentExtentSize
;
684 char *currentExtent
, *extentBuffer
= 0, *bufferPos
= buffer
;
686 if (offset
>= extentSize
) return 0;
689 extentDensity
= kHFSPlusExtentDensity
;
690 sizeofExtent
= sizeof(HFSPlusExtentDescriptor
);
692 extentDensity
= kHFSExtentDensity
;
693 sizeofExtent
= sizeof(HFSExtentDescriptor
);
696 lastOffset
= offset
+ size
;
697 while (offset
< lastOffset
) {
698 blockNumber
= offset
/ gBlockSize
;
700 // Find the extent for the offset.
701 for (; ; nextExtent
++) {
702 if (nextExtent
< extentDensity
) {
703 if ((countedBlocks
+GetExtentSize(extent
, nextExtent
)-1)<blockNumber
) {
704 countedBlocks
+= GetExtentSize(extent
, nextExtent
);
708 currentExtent
= extent
+ nextExtent
* sizeofExtent
;
712 if (extentBuffer
== 0) {
713 extentBuffer
= malloc(sizeofExtent
* extentDensity
);
714 if (extentBuffer
== 0) return -1;
717 nextExtentBlock
= nextExtent
/ extentDensity
;
718 if (currentExtentBlock
!= nextExtentBlock
) {
719 ReadExtentsEntry(extentFile
, countedBlocks
, extentBuffer
);
720 currentExtentBlock
= nextExtentBlock
;
723 currentExtentSize
= GetExtentSize(extentBuffer
, nextExtent
% extentDensity
);
725 if ((countedBlocks
+ currentExtentSize
- 1) >= blockNumber
) {
726 currentExtent
= extentBuffer
+ sizeofExtent
* (nextExtent
% extentDensity
);
730 countedBlocks
+= currentExtentSize
;
733 readOffset
= ((blockNumber
- countedBlocks
) * gBlockSize
) +
734 (offset
% gBlockSize
);
736 readSize
= GetExtentSize(currentExtent
, 0) * gBlockSize
- readOffset
;
737 if (readSize
> (size
- sizeRead
)) readSize
= size
- sizeRead
;
739 readOffset
+= (long long)GetExtentStart(currentExtent
, 0) * gBlockSize
;
741 CacheRead(gCurrentIH
, bufferPos
, gAllocationOffset
+ readOffset
,
744 sizeRead
+= readSize
;
746 bufferPos
+= readSize
;
749 if (extentBuffer
) free(extentBuffer
);
754 static long GetExtentStart(void * extents
, long index
)
757 HFSExtentDescriptor
*hfsExtents
= extents
;
758 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
760 if (gIsHFSPlus
) start
= SWAP_BE32(hfsPlusExtents
[index
].startBlock
);
761 else start
= SWAP_BE16(hfsExtents
[index
].startBlock
);
766 static long GetExtentSize(void * extents
, long index
)
769 HFSExtentDescriptor
*hfsExtents
= extents
;
770 HFSPlusExtentDescriptor
*hfsPlusExtents
= extents
;
772 if (gIsHFSPlus
) size
= SWAP_BE32(hfsPlusExtents
[index
].blockCount
);
773 else size
= SWAP_BE16(hfsExtents
[index
].blockCount
);
778 static long CompareHFSCatalogKeys(void * key
, void * testKey
)
780 HFSCatalogKey
*searchKey
, *trialKey
;
781 long result
, searchParentID
, trialParentID
;
786 searchParentID
= SWAP_BE32(searchKey
->parentID
);
787 trialParentID
= SWAP_BE32(trialKey
->parentID
);
789 // parent dirID is unsigned
790 if (searchParentID
> trialParentID
) result
= 1;
791 else if (searchParentID
< trialParentID
) result
= -1;
793 // parent dirID's are equal, compare names
794 result
= FastRelString(searchKey
->nodeName
, trialKey
->nodeName
);
800 static long CompareHFSPlusCatalogKeys(void * key
, void * testKey
)
802 HFSPlusCatalogKey
*searchKey
, *trialKey
;
803 long result
, searchParentID
, trialParentID
;
808 searchParentID
= SWAP_BE32(searchKey
->parentID
);
809 trialParentID
= SWAP_BE32(trialKey
->parentID
);
811 // parent dirID is unsigned
812 if (searchParentID
> trialParentID
) result
= 1;
813 else if (searchParentID
< trialParentID
) result
= -1;
815 // parent dirID's are equal, compare names
816 if ((searchKey
->nodeName
.length
== 0) || (trialKey
->nodeName
.length
== 0))
817 result
= searchKey
->nodeName
.length
- trialKey
->nodeName
.length
;
819 result
= FastUnicodeCompare(&searchKey
->nodeName
.unicode
[0],
820 SWAP_BE16(searchKey
->nodeName
.length
),
821 &trialKey
->nodeName
.unicode
[0],
822 SWAP_BE16(trialKey
->nodeName
.length
));
828 static long CompareHFSExtentsKeys(void * key
, void * testKey
)
830 HFSExtentKey
*searchKey
, *trialKey
;
836 // assume searchKey < trialKey
839 if (searchKey
->fileID
== trialKey
->fileID
) {
840 // FileNum's are equal; compare fork types
841 if (searchKey
->forkType
== trialKey
->forkType
) {
842 // Fork types are equal; compare allocation block number
843 if (searchKey
->startBlock
== trialKey
->startBlock
) {
844 // Everything is equal
847 // Allocation block numbers differ; determine sign
848 if (SWAP_BE16(searchKey
->startBlock
) > SWAP_BE16(trialKey
->startBlock
))
852 // Fork types differ; determine sign
853 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
856 // FileNums differ; determine sign
857 if (SWAP_BE32(searchKey
->fileID
) > SWAP_BE32(trialKey
->fileID
))
864 static long CompareHFSPlusExtentsKeys(void * key
, void * testKey
)
866 HFSPlusExtentKey
*searchKey
, *trialKey
;
872 // assume searchKey < trialKey
875 if (searchKey
->fileID
== trialKey
->fileID
) {
876 // FileNum's are equal; compare fork types
877 if (searchKey
->forkType
== trialKey
->forkType
) {
878 // Fork types are equal; compare allocation block number
879 if (searchKey
->startBlock
== trialKey
->startBlock
) {
880 // Everything is equal
883 // Allocation block numbers differ; determine sign
884 if (SWAP_BE32(searchKey
->startBlock
) > SWAP_BE32(trialKey
->startBlock
))
888 // Fork types differ; determine sign
889 if (searchKey
->forkType
> trialKey
->forkType
) result
= 1;
892 // FileNums differ; determine sign
893 if (SWAP_BE32(searchKey
->fileID
) > SWAP_BE32(trialKey
->fileID
))