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

Archive Download this file

Revision: 2734