Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2337