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