Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2476