Chameleon

Chameleon Svn Source Tree

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

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

Archive Download this file

Revision: 2453