Chameleon

Chameleon Svn Source Tree

Root/branches/rekursor/i386/libsaio/hfs.c

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

Archive Download this file

Revision: 4