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