Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Trunk/i386/libsaio/hfs.c

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

Archive Download this file

Revision: 1996