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