]> git.saurik.com Git - apple/boot.git/blame - i386/libsaio/hfs.c
boot-93.tar.gz
[apple/boot.git] / i386 / libsaio / hfs.c
CommitLineData
75b89a82
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * hfs.c - File System Module for HFS and HFS+.
24 *
25 * Copyright (c) 1999-2002 Apple Computer, Inc.
26 *
27 * DRI: Josh de Cesare
28 */
29
30#include <sl.h>
31#include <hfs/hfs_format.h>
32
33#define kBlockSize (0x200)
34
35#define kMDBBaseOffset (2 * kBlockSize)
36
37#define kBTreeCatalog (0)
38#define kBTreeExtents (1)
39
40#ifdef __i386__
41
42static CICell gCurrentIH;
43static long long gAllocationOffset;
44static long gIsHFSPlus;
45static long gBlockSize;
46static long gCacheBlockSize;
47static char *gBTreeHeaderBuffer;
48static BTHeaderRec *gBTHeaders[2];
49static char *gHFSMdbVib;
50static HFSMasterDirectoryBlock *gHFSMDB;
51static char *gHFSPlusHeader;
52static HFSPlusVolumeHeader *gHFSPlus;
53static char *gLinkTemp;
54static char *gTempStr;
55
56#else /* !__i386__ */
57
58static CICell gCurrentIH;
59static long long gAllocationOffset;
60static long gIsHFSPlus;
61static long gBlockSize;
62static long gCacheBlockSize;
63static char gBTreeHeaderBuffer[512];
64static BTHeaderRec *gBTHeaders[2];
65static char gHFSMdbVib[kBlockSize];
66static HFSMasterDirectoryBlock *gHFSMDB =(HFSMasterDirectoryBlock*)gHFSMdbVib;
67static char gHFSPlusHeader[kBlockSize];
68static HFSPlusVolumeHeader *gHFSPlus =(HFSPlusVolumeHeader*)gHFSPlusHeader;
69static char gLinkTemp[64];
70
71#endif /* !__i386__ */
72
73static long ReadFile(void *file, long *length);
74static long GetCatalogEntryInfo(void *entry, long *flags, long *time);
75static long ResolvePathToCatalogEntry(char *filePath, long *flags,
76 void *entry, long dirID, long *dirIndex);
77
78static long GetCatalogEntry(long *dirIndex, char **name,
79 long *flags, long *time);
80static long ReadCatalogEntry(char *fileName, long dirID, void *entry,
81 long *dirIndex);
82static long ReadExtentsEntry(long fileID, long startBlock, void *entry);
83
84static long ReadBTreeEntry(long btree, void *key, char *entry, long *dirIndex);
85static void GetBTreeRecord(long index, char *nodeBuffer, long nodeSize,
86 char **key, char **data);
87
88static long ReadExtent(char *extent, long extentSize, long extentFile,
89 long offset, long size, void *buffer, long cache);
90
91static long GetExtentStart(void *extents, long index);
92static long GetExtentSize(void *extents, long index);
93
94static long CompareHFSCatalogKeys(void *key, void *testKey);
95static long CompareHFSPlusCatalogKeys(void *key, void *testKey);
96static long CompareHFSExtentsKeys(void *key, void *testKey);
97static long CompareHFSPlusExtentsKeys(void *key, void *testKey);
98
99extern long FastRelString(char *str1, char *str2);
100extern long FastUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
101 u_int16_t *uniStr2, u_int32_t len2);
102extern void utf_encodestr(const u_int16_t *ucsp, int ucslen,
103 u_int8_t *utf8p, u_int32_t bufsize);
104extern void utf_decodestr(const u_int8_t *utf8p, u_int16_t *ucsp,
105 u_int16_t *ucslen, u_int32_t bufsize);
106
107
108long HFSInitPartition(CICell ih)
109{
110 long extentSize, extentFile, nodeSize;
111 void *extent;
112
113 if (ih == gCurrentIH) {
114#ifdef __i386__
115 CacheInit(ih, gCacheBlockSize);
116#endif
117 return 0;
118 }
119
120 verbose("HFSInitPartition: %x\n", ih);
121
122#ifdef __i386__
123 if (!gTempStr) gTempStr = (char *)malloc(4096);
124 if (!gLinkTemp) gLinkTemp = (char *)malloc(64);
125 if (!gBTreeHeaderBuffer) gBTreeHeaderBuffer = (char *)malloc(512);
126 if (!gHFSMdbVib) {
127 gHFSMdbVib = (char *)malloc(kBlockSize);
128 gHFSMDB = (HFSMasterDirectoryBlock *)gHFSMdbVib;
129 }
130 if (!gHFSPlusHeader) {
131 gHFSPlusHeader = (char *)malloc(kBlockSize);
132 gHFSPlus = (HFSPlusVolumeHeader *)gHFSPlusHeader;
133 }
134 if (!gTempStr || !gLinkTemp || !gBTreeHeaderBuffer ||
135 !gHFSMdbVib || !gHFSPlusHeader) return -1;
136#endif /* __i386__ */
137
138 gAllocationOffset = 0;
139 gIsHFSPlus = 0;
140 gBTHeaders[0] = 0;
141 gBTHeaders[1] = 0;
142
143 // Look for the HFS MDB
144 Seek(ih, kMDBBaseOffset);
145 Read(ih, (long)gHFSMdbVib, kBlockSize);
146
147 if ( SWAP_BE16(gHFSMDB->drSigWord) == kHFSSigWord ) {
148 gAllocationOffset = SWAP_BE16(gHFSMDB->drAlBlSt) * kBlockSize;
149
150 // See if it is HFSPlus
151 if (SWAP_BE16(gHFSMDB->drEmbedSigWord) != kHFSPlusSigWord) {
152 // Normal HFS;
153 gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSMDB->drAlBlkSiz);
154 CacheInit(ih, gCacheBlockSize);
155 gCurrentIH = ih;
156
157 // Get the Catalog BTree node size.
158 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
159 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
160 extentFile = kHFSCatalogFileID;
161 ReadExtent(extent, extentSize, extentFile, 0, 256,
162 gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);
163
164 nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 +
165 sizeof(BTNodeDescriptor)))->nodeSize);
166
167 // If the BTree node size is larger than the block size, reset the cache.
168 if (nodeSize > gBlockSize) {
169 gCacheBlockSize = nodeSize;
170 CacheInit(ih, gCacheBlockSize);
171 }
172
173 return 0;
174 }
175
176 // Calculate the offset to the embeded HFSPlus volume.
177 gAllocationOffset += (long long)SWAP_BE16(gHFSMDB->drEmbedExtent.startBlock) *
178 SWAP_BE32(gHFSMDB->drAlBlkSiz);
179 }
180
181 // Look for the HFSPlus Header
182 Seek(ih, gAllocationOffset + kMDBBaseOffset);
183 Read(ih, (long)gHFSPlusHeader, kBlockSize);
184
185 // Not a HFS[+] volume.
186 if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord) return -1;
187
188 gIsHFSPlus = 1;
189 gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSPlus->blockSize);
190 CacheInit(ih, gCacheBlockSize);
191 gCurrentIH = ih;
192
193 // Get the Catalog BTree node size.
194 extent = &gHFSPlus->catalogFile.extents;
195 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
196 extentFile = kHFSCatalogFileID;
197
198 ReadExtent(extent, extentSize, extentFile, 0, 256,
199 gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);
200
201 nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 +
202 sizeof(BTNodeDescriptor)))->nodeSize);
203
204 // If the BTree node size is larger than the block size, reset the cache.
205 if (nodeSize > gBlockSize) {
206 gCacheBlockSize = nodeSize;
207 CacheInit(ih, gCacheBlockSize);
208 }
209
210 return 0;
211}
212
213long HFSLoadFile(CICell ih, char * filePath)
214{
215 char entry[512];
216 long dirID, result, length, flags;
217
218 if (HFSInitPartition(ih) == -1) return -1;
219
220 verbose("Loading HFS%s file: [%s] from %x.\n",
221 (gIsHFSPlus ? "+" : ""), filePath, ih);
222
223 dirID = kHFSRootFolderID;
224 // Skip a lead '\'. Start in the system folder if there are two.
225 if (filePath[0] == '/') {
226 if (filePath[1] == '/') {
227 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
228 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
229 if (dirID == 0) return -1;
230 filePath++;
231 }
232 filePath++;
233 }
234
235 result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
236 if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1;
237
238#if 0 // Not yet for Intel. System.config/Default.table will fail this check.
239 // Check file owner and permissions.
240 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
241#endif
242
243 result = ReadFile(entry, &length);
244 if (result == -1) return -1;
245
246 return length;
247}
248
249long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex, char ** name,
250 long * flags, long * time)
251{
252 char entry[512];
253 long dirID, dirFlags;
254
255 if (HFSInitPartition(ih) == -1) return -1;
256
257 if (*dirIndex == -1) return -1;
258
259 dirID = kHFSRootFolderID;
260 // Skip a lead '\'. Start in the system folder if there are two.
261 if (dirPath[0] == '/') {
262 if (dirPath[1] == '/') {
263 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
264 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
265 if (dirID == 0) return -1;
266 dirPath++;
267 }
268 dirPath++;
269 }
270
271 if (*dirIndex == 0) {
272 ResolvePathToCatalogEntry(dirPath, &dirFlags, entry, dirID, dirIndex);
273 if (*dirIndex == 0) *dirIndex = -1;
274 if ((dirFlags & kFileTypeMask) != kFileTypeUnknown) return -1;
275 }
276
277 GetCatalogEntry(dirIndex, name, flags, time);
278 if (*dirIndex == 0) *dirIndex = -1;
279 if ((*flags & kFileTypeMask) == kFileTypeUnknown) return -1;
280
281 return 0;
282}
283
284// Private Functions
285
286static long ReadFile(void * file, long * length)
287{
288 void *extents;
289 long fileID;
290 HFSCatalogFile *hfsFile = file;
291 HFSPlusCatalogFile *hfsPlusFile = file;
292
293 if (gIsHFSPlus) {
294 fileID = SWAP_BE32(hfsPlusFile->fileID);
295 *length = SWAP_BE64(hfsPlusFile->dataFork.logicalSize);
296 extents = &hfsPlusFile->dataFork.extents;
297 } else {
298 fileID = SWAP_BE32(hfsFile->fileID);
299 *length = SWAP_BE32(hfsFile->dataLogicalSize);
300 extents = &hfsFile->dataExtents;
301 }
302
303 if (*length > kLoadSize) {
304 printf("File is too large.\n");
305 return -1;
306 }
307
308#ifdef __i386__
309 *length = ReadExtent((char *)extents, *length, fileID,
310 0, *length, (char *)gFSLoadAddress, 0);
311#else
312 *length = ReadExtent((char *)extents, *length, fileID,
313 0, *length, (char *)kLoadAddr, 0);
314#endif
315
316 return 0;
317}
318
319static long GetCatalogEntryInfo(void * entry, long * flags, long * time)
320{
321 long tmpTime = 0;
322
323 // Get information about the file.
324
325 switch ( SWAP_BE16(*(short *)entry) )
326 {
327 case kHFSFolderRecord :
328 *flags = kFileTypeDirectory;
329 tmpTime = SWAP_BE32(((HFSCatalogFolder *)entry)->modifyDate);
330 break;
331
332 case kHFSPlusFolderRecord :
333 *flags = kFileTypeDirectory |
334 (SWAP_BE16(((HFSPlusCatalogFolder *)entry)->bsdInfo.fileMode) & kPermMask);
335 if (SWAP_BE32(((HFSPlusCatalogFolder *)entry)->bsdInfo.ownerID) != 0)
336 *flags |= kOwnerNotRoot;
337 tmpTime = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->contentModDate);
338 break;
339
340 case kHFSFileRecord :
341 *flags = kFileTypeFlat;
342 tmpTime = SWAP_BE32(((HFSCatalogFile *)entry)->modifyDate);
343 break;
344
345 case kHFSPlusFileRecord :
346 *flags = kFileTypeFlat |
347 (SWAP_BE16(((HFSPlusCatalogFile *)entry)->bsdInfo.fileMode) & kPermMask);
348 if (SWAP_BE32(((HFSPlusCatalogFile *)entry)->bsdInfo.ownerID) != 0)
349 *flags |= kOwnerNotRoot;
350 tmpTime = SWAP_BE32(((HFSPlusCatalogFile *)entry)->contentModDate);
351 break;
352
353 case kHFSFileThreadRecord :
354 case kHFSPlusFileThreadRecord :
355 case kHFSFolderThreadRecord :
356 case kHFSPlusFolderThreadRecord :
357 *flags = kFileTypeUnknown;
358 tmpTime = 0;
359 break;
360 }
361
362 if (time != 0) {
363 // Convert base time from 1904 to 1970.
364 *time = tmpTime - 2082844800;
365 }
366
367 return 0;
368}
369
370static long ResolvePathToCatalogEntry(char * filePath, long * flags,
371 void * entry, long dirID, long * dirIndex)
372{
373 char *restPath;
374 long result, cnt, subFolderID, tmpDirIndex;
375 HFSPlusCatalogFile *hfsPlusFile;
376
377 // Copy the file name to gTempStr
378 cnt = 0;
379 while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++;
380 strncpy(gTempStr, filePath, cnt);
381
382 // Move restPath to the right place.
383 if (filePath[cnt] != '\0') cnt++;
384 restPath = filePath + cnt;
385
386 // gTempStr is a name in the current Dir.
387 // restPath is the rest of the path if any.
388
389 result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex);
390 if (result == -1) return -1;
391
392 GetCatalogEntryInfo(entry, flags, 0);
393
394 if ((*flags & kFileTypeMask) == kFileTypeDirectory) {
395 if (gIsHFSPlus)
396 subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID);
397 else
398 subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID);
399
400 result = ResolvePathToCatalogEntry(restPath, flags, entry,
401 subFolderID, dirIndex);
402 }
403
404 if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat)) {
405 hfsPlusFile = (HFSPlusCatalogFile *)entry;
406 if ((SWAP_BE32(hfsPlusFile->userInfo.fdType) == kHardLinkFileType) &&
407 (SWAP_BE32(hfsPlusFile->userInfo.fdCreator) == kHFSPlusCreator)) {
408 sprintf(gLinkTemp, "%s/%s%ld", HFSPLUSMETADATAFOLDER,
409 HFS_INODE_PREFIX, SWAP_BE32(hfsPlusFile->bsdInfo.special.iNodeNum));
410 result = ResolvePathToCatalogEntry(gLinkTemp, flags, entry,
411 kHFSRootFolderID, &tmpDirIndex);
412 }
413 }
414
415 return result;
416}
417
418static long GetCatalogEntry(long * dirIndex, char ** name,
419 long * flags, long * time)
420{
421 long extentSize, nodeSize, curNode, index;
422 void *extent;
423 char *nodeBuf, *testKey, *entry;
424 BTNodeDescriptor *node;
425
426 if (gIsHFSPlus) {
427 extent = &gHFSPlus->catalogFile.extents;
428 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
429 } else {
430 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
431 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
432 }
433
434 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
435 nodeBuf = (char *)malloc(nodeSize);
436 node = (BTNodeDescriptor *)nodeBuf;
437
438 index = *dirIndex % nodeSize;
439 curNode = *dirIndex / nodeSize;
440
441 // Read the BTree node and get the record for index.
442 ReadExtent(extent, extentSize, kHFSCatalogFileID,
443 curNode * nodeSize, nodeSize, nodeBuf, 1);
444 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
445
446 GetCatalogEntryInfo(entry, flags, time);
447
448 // Get the file name.
449 if (gIsHFSPlus) {
450 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
451 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
452 gTempStr, 256);
453 } else {
454 strncpy(gTempStr,
455 &((HFSCatalogKey *)testKey)->nodeName[1],
456 ((HFSCatalogKey *)testKey)->nodeName[0]);
457 }
458 *name = gTempStr;
459
460 // Update dirIndex.
461 index++;
462 if (index == SWAP_BE16(node->numRecords)) {
463 index = 0;
464 curNode = SWAP_BE32(node->fLink);
465 }
466 *dirIndex = curNode * nodeSize + index;
467
468 free(nodeBuf);
469
470 return 0;
471}
472
473static long ReadCatalogEntry(char * fileName, long dirID,
474 void * entry, long * dirIndex)
475{
476 long length;
477 char key[sizeof(HFSPlusCatalogKey)];
478 HFSCatalogKey *hfsKey = (HFSCatalogKey *)key;
479 HFSPlusCatalogKey *hfsPlusKey = (HFSPlusCatalogKey *)key;
480
481 // Make the catalog key.
482 if ( gIsHFSPlus )
483 {
484 hfsPlusKey->parentID = SWAP_BE32(dirID);
485 length = strlen(fileName);
486 if (length > 255) length = 255;
487 utf_decodestr(fileName, hfsPlusKey->nodeName.unicode,
488 &(hfsPlusKey->nodeName.length), 512);
489 } else {
490 hfsKey->parentID = SWAP_BE32(dirID);
491 length = strlen(fileName);
492 if (length > 31) length = 31;
493 hfsKey->nodeName[0] = length;
494 strncpy(hfsKey->nodeName + 1, fileName, length);
495 }
496
497 return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex);
498}
499
500static long ReadExtentsEntry(long fileID, long startBlock, void * entry)
501{
502 char key[sizeof(HFSPlusExtentKey)];
503 HFSExtentKey *hfsKey = (HFSExtentKey *)key;
504 HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey *)key;
505
506 // Make the extents key.
507 if (gIsHFSPlus) {
508 hfsPlusKey->forkType = 0;
509 hfsPlusKey->fileID = SWAP_BE32(fileID);
510 hfsPlusKey->startBlock = SWAP_BE32(startBlock);
511 } else {
512 hfsKey->forkType = 0;
513 hfsKey->fileID = SWAP_BE32(fileID);
514 hfsKey->startBlock = SWAP_BE16(startBlock);
515 }
516
517 return ReadBTreeEntry(kBTreeExtents, &key, entry, 0);
518}
519
520static long ReadBTreeEntry(long btree, void * key, char * entry, long * dirIndex)
521{
522 long extentSize;
523 void *extent;
524 short extentFile;
525 char *nodeBuf;
526 BTNodeDescriptor *node;
527 long nodeSize, result = 0, entrySize = 0;
528 long curNode, index = 0, lowerBound, upperBound;
529 char *testKey, *recordData;
530
531 // Figure out which tree is being looked at.
532 if (btree == kBTreeCatalog) {
533 if (gIsHFSPlus) {
534 extent = &gHFSPlus->catalogFile.extents;
535 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
536 } else {
537 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
538 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
539 }
540 extentFile = kHFSCatalogFileID;
541 } else {
542 if (gIsHFSPlus) {
543 extent = &gHFSPlus->extentsFile.extents;
544 extentSize = SWAP_BE64(gHFSPlus->extentsFile.logicalSize);
545 } else {
546 extent = (HFSExtentDescriptor *)&gHFSMDB->drXTExtRec;
547 extentSize = SWAP_BE32(gHFSMDB->drXTFlSize);
548 }
549 extentFile = kHFSExtentsFileID;
550 }
551
552 // Read the BTree Header if needed.
553 if (gBTHeaders[btree] == 0) {
554 ReadExtent(extent, extentSize, extentFile, 0, 256,
555 gBTreeHeaderBuffer + btree * 256, 0);
556 gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 +
557 sizeof(BTNodeDescriptor));
558 }
559
560 curNode = SWAP_BE32(gBTHeaders[btree]->rootNode);
561 nodeSize = SWAP_BE16(gBTHeaders[btree]->nodeSize);
562 nodeBuf = (char *)malloc(nodeSize);
563 node = (BTNodeDescriptor *)nodeBuf;
564
565 while (1) {
566 // Read the current node.
567 ReadExtent(extent, extentSize, extentFile,
568 curNode * nodeSize, nodeSize, nodeBuf, 1);
569
570 // Find the matching key.
571 lowerBound = 0;
572 upperBound = SWAP_BE16(node->numRecords) - 1;
573 while (lowerBound <= upperBound) {
574 index = (lowerBound + upperBound) / 2;
575
576 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
577
578 if (gIsHFSPlus) {
579 if (btree == kBTreeCatalog) {
580 result = CompareHFSPlusCatalogKeys(key, testKey);
581 } else {
582 result = CompareHFSPlusExtentsKeys(key, testKey);
583 }
584 } else {
585 if (btree == kBTreeCatalog) {
586 result = CompareHFSCatalogKeys(key, testKey);
587 } else {
588 result = CompareHFSExtentsKeys(key, testKey);
589 }
590 }
591
592 if (result < 0) upperBound = index - 1; // search < trial
593 else if (result > 0) lowerBound = index + 1; // search > trial
594 else break; // search = trial
595 }
596
597 if (result < 0) {
598 index = upperBound;
599 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
600 }
601
602 // Found the closest key... Recurse on it if this is an index node.
603 if (node->kind == kBTIndexNode) {
604 curNode = SWAP_BE32( *((long *)recordData) );
605 } else break;
606 }
607
608 // Return error if the file was not found.
609 if (result != 0) { free(nodeBuf); return -1; }
610
611 if (btree == kBTreeCatalog) {
612 switch (SWAP_BE16(*(short *)recordData)) {
613 case kHFSFolderRecord : entrySize = 70; break;
614 case kHFSFileRecord : entrySize = 102; break;
615 case kHFSFolderThreadRecord : entrySize = 46; break;
616 case kHFSFileThreadRecord : entrySize = 46; break;
617 case kHFSPlusFolderRecord : entrySize = 88; break;
618 case kHFSPlusFileRecord : entrySize = 248; break;
619 case kHFSPlusFolderThreadRecord : entrySize = 264; break;
620 case kHFSPlusFileThreadRecord : entrySize = 264; break;
621 }
622 } else {
623 if (gIsHFSPlus) entrySize = sizeof(HFSPlusExtentRecord);
624 else entrySize = sizeof(HFSExtentRecord);
625 }
626
627 bcopy(recordData, entry, entrySize);
628
629 // Update dirIndex.
630 if (dirIndex != 0) {
631 index++;
632 if (index == SWAP_BE16(node->numRecords)) {
633 index = 0;
634 curNode = SWAP_BE32(node->fLink);
635 }
636 *dirIndex = curNode * nodeSize + index;
637 }
638
639 free(nodeBuf);
640
641 return 0;
642}
643
644static void GetBTreeRecord(long index, char * nodeBuffer, long nodeSize,
645 char ** key, char ** data)
646{
647 long keySize;
648 long recordOffset;
649
650 recordOffset = SWAP_BE16(*((short *)(nodeBuffer + (nodeSize - 2 * index - 2))));
651 *key = nodeBuffer + recordOffset;
652 if (gIsHFSPlus) {
653 keySize = SWAP_BE16(*(short *)*key);
654 *data = *key + 2 + keySize;
655 } else {
656 keySize = **key;
657 *data = *key + 2 + keySize - (keySize & 1);
658 }
659}
660
661static long ReadExtent(char * extent, long extentSize,
662 long extentFile, long offset, long size,
663 void * buffer, long cache)
664{
665 long lastOffset, blockNumber, countedBlocks = 0;
666 long nextExtent = 0, sizeRead = 0, readSize;
667 long nextExtentBlock, currentExtentBlock = 0;
668 long long readOffset;
669 long extentDensity, sizeofExtent, currentExtentSize;
670 char *currentExtent, *extentBuffer = 0, *bufferPos = buffer;
671
672 if (offset >= extentSize) return 0;
673
674 if (gIsHFSPlus) {
675 extentDensity = kHFSPlusExtentDensity;
676 sizeofExtent = sizeof(HFSPlusExtentDescriptor);
677 } else {
678 extentDensity = kHFSExtentDensity;
679 sizeofExtent = sizeof(HFSExtentDescriptor);
680 }
681
682 lastOffset = offset + size;
683 while (offset < lastOffset) {
684 blockNumber = offset / gBlockSize;
685
686 // Find the extent for the offset.
687 for (; ; nextExtent++) {
688 if (nextExtent < extentDensity) {
689 if ((countedBlocks+GetExtentSize(extent, nextExtent)-1)<blockNumber) {
690 countedBlocks += GetExtentSize(extent, nextExtent);
691 continue;
692 }
693
694 currentExtent = extent + nextExtent * sizeofExtent;
695 break;
696 }
697
698 if (extentBuffer == 0) {
699 extentBuffer = malloc(sizeofExtent * extentDensity);
700 if (extentBuffer == 0) return -1;
701 }
702
703 nextExtentBlock = nextExtent / extentDensity;
704 if (currentExtentBlock != nextExtentBlock) {
705 ReadExtentsEntry(extentFile, countedBlocks, extentBuffer);
706 currentExtentBlock = nextExtentBlock;
707 }
708
709 currentExtentSize = GetExtentSize(extentBuffer, nextExtent % extentDensity);
710
711 if ((countedBlocks + currentExtentSize - 1) >= blockNumber) {
712 currentExtent = extentBuffer + sizeofExtent * (nextExtent % extentDensity);
713 break;
714 }
715
716 countedBlocks += currentExtentSize;
717 }
718
719 readOffset = ((blockNumber - countedBlocks) * gBlockSize) +
720 (offset % gBlockSize);
721
722 readSize = GetExtentSize(currentExtent, 0) * gBlockSize - readOffset;
723 if (readSize > (size - sizeRead)) readSize = size - sizeRead;
724
725 readOffset += (long long)GetExtentStart(currentExtent, 0) * gBlockSize;
726
727 CacheRead(gCurrentIH, bufferPos, gAllocationOffset + readOffset,
728 readSize, cache);
729
730 sizeRead += readSize;
731 offset += readSize;
732 bufferPos += readSize;
733 }
734
735 if (extentBuffer) free(extentBuffer);
736
737 return sizeRead;
738}
739
740static long GetExtentStart(void * extents, long index)
741{
742 long start;
743 HFSExtentDescriptor *hfsExtents = extents;
744 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
745
746 if (gIsHFSPlus) start = SWAP_BE32(hfsPlusExtents[index].startBlock);
747 else start = SWAP_BE16(hfsExtents[index].startBlock);
748
749 return start;
750}
751
752static long GetExtentSize(void * extents, long index)
753{
754 long size;
755 HFSExtentDescriptor *hfsExtents = extents;
756 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
757
758 if (gIsHFSPlus) size = SWAP_BE32(hfsPlusExtents[index].blockCount);
759 else size = SWAP_BE16(hfsExtents[index].blockCount);
760
761 return size;
762}
763
764static long CompareHFSCatalogKeys(void * key, void * testKey)
765{
766 HFSCatalogKey *searchKey, *trialKey;
767 long result, searchParentID, trialParentID;
768
769 searchKey = key;
770 trialKey = testKey;
771
772 searchParentID = SWAP_BE32(searchKey->parentID);
773 trialParentID = SWAP_BE32(trialKey->parentID);
774
775 // parent dirID is unsigned
776 if (searchParentID > trialParentID) result = 1;
777 else if (searchParentID < trialParentID) result = -1;
778 else {
779 // parent dirID's are equal, compare names
780 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
781 }
782
783 return result;
784}
785
786static long CompareHFSPlusCatalogKeys(void * key, void * testKey)
787{
788 HFSPlusCatalogKey *searchKey, *trialKey;
789 long result, searchParentID, trialParentID;
790
791 searchKey = key;
792 trialKey = testKey;
793
794 searchParentID = SWAP_BE32(searchKey->parentID);
795 trialParentID = SWAP_BE32(trialKey->parentID);
796
797 // parent dirID is unsigned
798 if (searchParentID > trialParentID) result = 1;
799 else if (searchParentID < trialParentID) result = -1;
800 else {
801 // parent dirID's are equal, compare names
802 if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))
803 result = searchKey->nodeName.length - trialKey->nodeName.length;
804 else
805 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
806 SWAP_BE16(searchKey->nodeName.length),
807 &trialKey->nodeName.unicode[0],
808 SWAP_BE16(trialKey->nodeName.length));
809 }
810
811 return result;
812}
813
814static long CompareHFSExtentsKeys(void * key, void * testKey)
815{
816 HFSExtentKey *searchKey, *trialKey;
817 long result;
818
819 searchKey = key;
820 trialKey = testKey;
821
822 // assume searchKey < trialKey
823 result = -1;
824
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
831 result = 0;
832 } else {
833 // Allocation block numbers differ; determine sign
834 if (SWAP_BE16(searchKey->startBlock) > SWAP_BE16(trialKey->startBlock))
835 result = 1;
836 }
837 } else {
838 // Fork types differ; determine sign
839 if (searchKey->forkType > trialKey->forkType) result = 1;
840 }
841 } else {
842 // FileNums differ; determine sign
843 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
844 result = 1;
845 }
846
847 return result;
848}
849
850static long CompareHFSPlusExtentsKeys(void * key, void * testKey)
851{
852 HFSPlusExtentKey *searchKey, *trialKey;
853 long result;
854
855 searchKey = key;
856 trialKey = testKey;
857
858 // assume searchKey < trialKey
859 result = -1;
860
861 if (searchKey->fileID == trialKey->fileID) {
862 // FileNum's are equal; compare fork types
863 if (searchKey->forkType == trialKey->forkType) {
864 // Fork types are equal; compare allocation block number
865 if (searchKey->startBlock == trialKey->startBlock) {
866 // Everything is equal
867 result = 0;
868 } else {
869 // Allocation block numbers differ; determine sign
870 if (SWAP_BE32(searchKey->startBlock) > SWAP_BE32(trialKey->startBlock))
871 result = 1;
872 }
873 } else {
874 // Fork types differ; determine sign
875 if (searchKey->forkType > trialKey->forkType) result = 1;
876 }
877 } else {
878 // FileNums differ; determine sign
879 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
880 result = 1;
881 }
882
883 return result;
884}