Chameleon

Chameleon Svn Source Tree

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

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

Archive Download this file

Revision: 1913