Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: HEAD