Chameleon

Chameleon Svn Source Tree

Root/branches/rekursor/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 *dirIndex);
84
85static long GetCatalogEntry(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 *dirIndex);
90static long ReadExtentsEntry(long fileID, long startBlock, void *entry);
91
92static long ReadBTreeEntry(long btree, void *key, char *entry, 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 long dirID, result, flags;
268
269 if (HFSInitPartition(ih) == -1) return -1;
270
271 dirID = kHFSRootFolderID;
272 // Skip a lead '\'. Start in the system folder if there are two.
273 if (filePath[0] == '/') {
274 if (filePath[1] == '/') {
275 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
276 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
277 if (dirID == 0) {
278return -1;
279 }
280 filePath++;
281 }
282 filePath++;
283 }
284
285 result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
286 if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
287return -1;
288 }
289
290#if UNUSED
291 // Not yet for Intel. System.config/Default.table will fail this check.
292 // Check file owner and permissions.
293 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
294#endif
295
296 result = ReadFile(entry, &length, base, offset);
297 if (result == -1) {
298return -1;
299 }
300
301 verbose("Loaded HFS%s file: [%s] %d bytes from %x.\n",
302 (gIsHFSPlus ? "+" : ""), filePath, (uint32_t)length, ih);
303
304 return length;
305}
306
307long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex, char ** name,
308 long * flags, long * time,
309 FinderInfo * finderInfo, long * infoValid)
310{
311 char entry[512];
312 long dirID, dirFlags;
313
314 if (HFSInitPartition(ih) == -1) return -1;
315
316 if (*dirIndex == -1) return -1;
317
318 dirID = kHFSRootFolderID;
319 // Skip a lead '\'. Start in the system folder if there are two.
320 if (dirPath[0] == '/') {
321 if (dirPath[1] == '/') {
322 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
323 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
324 if (dirID == 0) return -1;
325 dirPath++;
326 }
327 dirPath++;
328 }
329
330 if (*dirIndex == 0) {
331 ResolvePathToCatalogEntry(dirPath, &dirFlags, entry, dirID, dirIndex);
332 if (*dirIndex == 0) *dirIndex = -1;
333 if ((dirFlags & kFileTypeMask) != kFileTypeUnknown) return -1;
334 }
335
336 GetCatalogEntry(dirIndex, name, flags, time, finderInfo, infoValid);
337 if (*dirIndex == 0) *dirIndex = -1;
338 if ((*flags & kFileTypeMask) == kFileTypeUnknown) return -1;
339
340 return 0;
341}
342
343void
344HFSGetDescription(CICell ih, char *str, long strMaxLen)
345{
346
347 UInt16 nodeSize;
348 UInt32 firstLeafNode;
349 long dirIndex;
350 char *name;
351 long flags, time;
352
353 if (HFSInitPartition(ih) == -1) { return; }
354
355 /* Fill some crucial data structures by side effect. */
356 dirIndex = 0;
357 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
358
359 /* Now we can loook up the volume name node. */
360 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
361 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
362
363 dirIndex = firstLeafNode * nodeSize;
364
365 GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
366
367 strncpy(str, name, strMaxLen);
368 str[strMaxLen] = '\0';
369}
370
371
372long
373HFSGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)
374{
375 char entry[512];
376 long dirID, result, flags;
377 void *extents;
378 HFSCatalogFile *hfsFile = (void *)entry;
379 HFSPlusCatalogFile *hfsPlusFile = (void *)entry;
380
381 if (HFSInitPartition(ih) == -1) return -1;
382
383 dirID = kHFSRootFolderID;
384 // Skip a lead '\'. Start in the system folder if there are two.
385 if (filePath[0] == '/') {
386 if (filePath[1] == '/') {
387 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
388 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
389 if (dirID == 0) {
390return -1;
391 }
392 filePath++;
393 }
394 filePath++;
395 }
396
397 result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
398 if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
399 printf("HFS: Resolve path %s failed\n", filePath);
400return -1;
401 }
402
403 if (gIsHFSPlus) {
404 extents = &hfsPlusFile->dataFork.extents;
405 } else {
406 extents = &hfsFile->dataExtents;
407 }
408
409#if DEBUG
410 printf("extent start 0x%x\n", (unsigned long)GetExtentStart(extents, 0));
411 printf("block size 0x%x\n", (unsigned long)gBlockSize);
412 printf("Allocation offset 0x%x\n", (unsigned long)gAllocationOffset);
413#endif
414 *firstBlock = ((unsigned long long)GetExtentStart(extents, 0) * (unsigned long long) gBlockSize + gAllocationOffset) / 512ULL;
415 return 0;
416}
417
418long HFSGetUUID(CICell ih, char *uuidStr)
419{
420 if (HFSInitPartition(ih) == -1) return -1;
421 if (gVolID == 0LL) return -1;
422
423 return CreateUUIDString((uint8_t*)(&gVolID), sizeof(gVolID), uuidStr);
424}
425
426// Private Functions
427
428static long ReadFile(void * file, uint64_t * length, void * base, uint64_t offset)
429{
430 void *extents;
431 long fileID;
432 uint64_t fileLength;
433 HFSCatalogFile *hfsFile = file;
434 HFSPlusCatalogFile *hfsPlusFile = file;
435
436 if (gIsHFSPlus) {
437 fileID = SWAP_BE32(hfsPlusFile->fileID);
438 fileLength = (uint64_t)SWAP_BE64(hfsPlusFile->dataFork.logicalSize);
439 extents = &hfsPlusFile->dataFork.extents;
440 } else {
441 fileID = SWAP_BE32(hfsFile->fileID);
442 fileLength = SWAP_BE32(hfsFile->dataLogicalSize);
443 extents = &hfsFile->dataExtents;
444 }
445
446 if (offset > fileLength) {
447 printf("Offset is too large.\n");
448 return -1;
449 }
450
451 if ((*length == 0) || ((offset + *length) > fileLength)) {
452 *length = fileLength - offset;
453 }
454
455/* if (*length > kLoadSize) {
456 printf("File is too large.\n");
457 return -1;
458 }*/
459
460 *length = ReadExtent((char *)extents, fileLength, fileID,
461 offset, *length, (char *)base, 0);
462
463 return 0;
464}
465
466static long GetCatalogEntryInfo(void * entry, long * flags, long * time,
467 FinderInfo * finderInfo, long * infoValid)
468{
469 long tmpTime = 0;
470 long valid = 0;
471
472 // Get information about the file.
473
474 switch ( SWAP_BE16(*(short *)entry) )
475 {
476 case kHFSFolderRecord :
477 *flags = kFileTypeDirectory;
478 tmpTime = SWAP_BE32(((HFSCatalogFolder *)entry)->modifyDate);
479 break;
480
481 case kHFSPlusFolderRecord :
482 *flags = kFileTypeDirectory |
483 (SWAP_BE16(((HFSPlusCatalogFolder *)entry)->bsdInfo.fileMode) & kPermMask);
484 if (SWAP_BE32(((HFSPlusCatalogFolder *)entry)->bsdInfo.ownerID) != 0)
485 *flags |= kOwnerNotRoot;
486 tmpTime = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->contentModDate);
487 break;
488
489 case kHFSFileRecord :
490 *flags = kFileTypeFlat;
491 tmpTime = SWAP_BE32(((HFSCatalogFile *)entry)->modifyDate);
492 if (finderInfo) {
493 SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSCatalogFile *)entry)->userInfo);
494 valid = 1;
495 }
496 break;
497
498 case kHFSPlusFileRecord :
499 *flags = kFileTypeFlat |
500 (SWAP_BE16(((HFSPlusCatalogFile *)entry)->bsdInfo.fileMode) & kPermMask);
501 if (SWAP_BE32(((HFSPlusCatalogFile *)entry)->bsdInfo.ownerID) != 0)
502 *flags |= kOwnerNotRoot;
503 tmpTime = SWAP_BE32(((HFSPlusCatalogFile *)entry)->contentModDate);
504 if (finderInfo) {
505 SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSPlusCatalogFile *)entry)->userInfo);
506 valid = 1;
507 }
508 break;
509
510 case kHFSFileThreadRecord :
511 case kHFSPlusFileThreadRecord :
512 case kHFSFolderThreadRecord :
513 case kHFSPlusFolderThreadRecord :
514 *flags = kFileTypeUnknown;
515 tmpTime = 0;
516 break;
517 }
518
519 if (time != 0) {
520 // Convert base time from 1904 to 1970.
521 *time = tmpTime - 2082844800;
522 }
523 if (infoValid) *infoValid = valid;
524
525 return 0;
526}
527
528static long ResolvePathToCatalogEntry(char * filePath, long * flags,
529 void * entry, long dirID, long * dirIndex)
530{
531 char *restPath;
532 long result, cnt, subFolderID = 0, tmpDirIndex;
533 HFSPlusCatalogFile *hfsPlusFile;
534
535 // Copy the file name to gTempStr
536 cnt = 0;
537 while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++;
538 strlcpy(gTempStr, filePath, cnt+1);
539
540 // Move restPath to the right place.
541 if (filePath[cnt] != '\0') cnt++;
542 restPath = filePath + cnt;
543
544 // gTempStr is a name in the current Dir.
545 // restPath is the rest of the path if any.
546
547 result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex);
548 if (result == -1) {
549return -1;
550 }
551
552 GetCatalogEntryInfo(entry, flags, 0, 0, 0);
553
554 if ((*flags & kFileTypeMask) == kFileTypeDirectory) {
555 if (gIsHFSPlus)
556 subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID);
557 else
558 subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID);
559 }
560
561 if ((*flags & kFileTypeMask) == kFileTypeDirectory)
562 result = ResolvePathToCatalogEntry(restPath, flags, entry,
563 subFolderID, dirIndex);
564
565 if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat)) {
566 hfsPlusFile = (HFSPlusCatalogFile *)entry;
567 if ((SWAP_BE32(hfsPlusFile->userInfo.fdType) == kHardLinkFileType) &&
568 (SWAP_BE32(hfsPlusFile->userInfo.fdCreator) == kHFSPlusCreator)) {
569 sprintf(gLinkTemp, "%s/%s%ld", HFSPLUSMETADATAFOLDER,
570 HFS_INODE_PREFIX, SWAP_BE32(hfsPlusFile->bsdInfo.special.iNodeNum));
571 result = ResolvePathToCatalogEntry(gLinkTemp, flags, entry,
572 kHFSRootFolderID, &tmpDirIndex);
573 }
574 }
575
576 return result;
577}
578
579static long GetCatalogEntry(long * dirIndex, char ** name,
580 long * flags, long * time,
581 FinderInfo * finderInfo, long * infoValid)
582{
583 long extentSize, nodeSize, curNode, index;
584 void *extent;
585 char *nodeBuf, *testKey, *entry;
586 BTNodeDescriptor *node;
587
588 if (gIsHFSPlus) {
589 extent = &gHFSPlus->catalogFile.extents;
590 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
591 } else {
592 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
593 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
594 }
595
596 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
597 nodeBuf = (char *)MALLOC(nodeSize);
598 node = (BTNodeDescriptor *)nodeBuf;
599
600 index = *dirIndex % nodeSize;
601 curNode = *dirIndex / nodeSize;
602
603 // Read the BTree node and get the record for index.
604 ReadExtent(extent, extentSize, kHFSCatalogFileID,
605 curNode * nodeSize, nodeSize, nodeBuf, 1);
606 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
607
608 GetCatalogEntryInfo(entry, flags, time, finderInfo, infoValid);
609
610 // Get the file name.
611 if (gIsHFSPlus) {
612 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
613 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
614 (u_int8_t *)gTempStr, 256, OSBigEndian);
615 } else {
616 strncpy(gTempStr,
617 (const char *)&((HFSCatalogKey *)testKey)->nodeName[1],
618 ((HFSCatalogKey *)testKey)->nodeName[0]);
619 gTempStr[((HFSCatalogKey *)testKey)->nodeName[0]] = '\0';
620 }
621 *name = gTempStr;
622
623 // Update dirIndex.
624 index++;
625 if (index == SWAP_BE16(node->numRecords)) {
626 index = 0;
627 curNode = SWAP_BE32(node->fLink);
628 }
629 *dirIndex = curNode * nodeSize + index;
630
631 free(nodeBuf);
632
633 return 0;
634}
635
636static long ReadCatalogEntry(char * fileName, long dirID,
637 void * entry, long * dirIndex)
638{
639 long length;
640 char key[sizeof(HFSPlusCatalogKey)];
641 HFSCatalogKey *hfsKey = (HFSCatalogKey *)key;
642 HFSPlusCatalogKey *hfsPlusKey = (HFSPlusCatalogKey *)key;
643
644 // Make the catalog key.
645 if ( gIsHFSPlus )
646 {
647 hfsPlusKey->parentID = SWAP_BE32(dirID);
648 length = strlen(fileName);
649 if (length > 255) length = 255;
650 utf_decodestr((u_int8_t *)fileName, hfsPlusKey->nodeName.unicode,
651 &(hfsPlusKey->nodeName.length), 512, OSBigEndian);
652 } else {
653 hfsKey->parentID = SWAP_BE32(dirID);
654 length = strlen(fileName);
655 if (length > 31) length = 31;
656 hfsKey->nodeName[0] = length;
657 strncpy((char *)(hfsKey->nodeName + 1), fileName, length);
658 }
659
660 return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex);
661}
662
663static long ReadExtentsEntry(long fileID, long startBlock, void * entry)
664{
665 char key[sizeof(HFSPlusExtentKey)];
666 HFSExtentKey *hfsKey = (HFSExtentKey *)key;
667 HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey *)key;
668
669 // Make the extents key.
670 if (gIsHFSPlus) {
671 hfsPlusKey->forkType = 0;
672 hfsPlusKey->fileID = SWAP_BE32(fileID);
673 hfsPlusKey->startBlock = SWAP_BE32(startBlock);
674 } else {
675 hfsKey->forkType = 0;
676 hfsKey->fileID = SWAP_BE32(fileID);
677 hfsKey->startBlock = SWAP_BE16(startBlock);
678 }
679
680 return ReadBTreeEntry(kBTreeExtents, &key, entry, 0);
681}
682
683static long ReadBTreeEntry(long btree, void * key, char * entry, long * dirIndex)
684{
685 long extentSize;
686 void *extent;
687 short extentFile;
688 char *nodeBuf;
689 BTNodeDescriptor *node;
690 long nodeSize, result = 0, entrySize = 0;
691 long curNode, index = 0, lowerBound, upperBound;
692 char *testKey, *recordData;
693
694 // Figure out which tree is being looked at.
695 if (btree == kBTreeCatalog) {
696 if (gIsHFSPlus) {
697 extent = &gHFSPlus->catalogFile.extents;
698 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
699 } else {
700 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
701 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
702 }
703 extentFile = kHFSCatalogFileID;
704 } else {
705 if (gIsHFSPlus) {
706 extent = &gHFSPlus->extentsFile.extents;
707 extentSize = SWAP_BE64(gHFSPlus->extentsFile.logicalSize);
708 } else {
709 extent = (HFSExtentDescriptor *)&gHFSMDB->drXTExtRec;
710 extentSize = SWAP_BE32(gHFSMDB->drXTFlSize);
711 }
712 extentFile = kHFSExtentsFileID;
713 }
714
715 // Read the BTree Header if needed.
716 if (gBTHeaders[btree] == 0) {
717 ReadExtent(extent, extentSize, extentFile, 0, 256,
718 gBTreeHeaderBuffer + btree * 256, 0);
719 gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 +
720 sizeof(BTNodeDescriptor));
721 if ((gIsHFSPlus && btree == kBTreeCatalog) &&
722 (gBTHeaders[btree]->keyCompareType == kHFSBinaryCompare)) {
723 gCaseSensitive = 1;
724 }
725 }
726
727 curNode = SWAP_BE32(gBTHeaders[btree]->rootNode);
728 nodeSize = SWAP_BE16(gBTHeaders[btree]->nodeSize);
729 nodeBuf = (char *)MALLOC(nodeSize);
730 node = (BTNodeDescriptor *)nodeBuf;
731
732 while (1) {
733 // Read the current node.
734 ReadExtent(extent, extentSize, extentFile,
735 curNode * nodeSize, nodeSize, nodeBuf, 1);
736
737 // Find the matching key.
738 lowerBound = 0;
739 upperBound = SWAP_BE16(node->numRecords) - 1;
740 while (lowerBound <= upperBound) {
741 index = (lowerBound + upperBound) / 2;
742
743 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
744
745 if (gIsHFSPlus) {
746 if (btree == kBTreeCatalog) {
747 result = CompareHFSPlusCatalogKeys(key, testKey);
748 } else {
749 result = CompareHFSPlusExtentsKeys(key, testKey);
750 }
751 } else {
752 if (btree == kBTreeCatalog) {
753 result = CompareHFSCatalogKeys(key, testKey);
754 } else {
755 result = CompareHFSExtentsKeys(key, testKey);
756 }
757 }
758
759 if (result < 0) upperBound = index - 1; // search < trial
760 else if (result > 0) lowerBound = index + 1; // search > trial
761 else break; // search = trial
762 }
763
764 if (result < 0) {
765 index = upperBound;
766 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
767 }
768
769 // Found the closest key... Recurse on it if this is an index node.
770 if (node->kind == kBTIndexNode) {
771 curNode = SWAP_BE32( *((long *)recordData) );
772 } else break;
773 }
774
775 // Return error if the file was not found.
776 if (result != 0) { free(nodeBuf); return -1; }
777
778 if (btree == kBTreeCatalog) {
779 switch (SWAP_BE16(*(short *)recordData)) {
780 case kHFSFolderRecord : entrySize = 70; break;
781 case kHFSFileRecord : entrySize = 102; break;
782 case kHFSFolderThreadRecord : entrySize = 46; break;
783 case kHFSFileThreadRecord : entrySize = 46; break;
784 case kHFSPlusFolderRecord : entrySize = 88; break;
785 case kHFSPlusFileRecord : entrySize = 248; break;
786 case kHFSPlusFolderThreadRecord : entrySize = 264; break;
787 case kHFSPlusFileThreadRecord : entrySize = 264; break;
788 }
789 } else {
790 if (gIsHFSPlus) entrySize = sizeof(HFSPlusExtentRecord);
791 else entrySize = sizeof(HFSExtentRecord);
792 }
793
794 bcopy(recordData, entry, entrySize);
795
796 // Update dirIndex.
797 if (dirIndex != 0) {
798 index++;
799 if (index == SWAP_BE16(node->numRecords)) {
800 index = 0;
801 curNode = SWAP_BE32(node->fLink);
802 }
803 *dirIndex = curNode * nodeSize + index;
804 }
805
806 free(nodeBuf);
807
808 return 0;
809}
810
811static void GetBTreeRecord(long index, char * nodeBuffer, long nodeSize,
812 char ** key, char ** data)
813{
814 long keySize;
815 long recordOffset;
816
817 recordOffset = SWAP_BE16(*((short *)(nodeBuffer + (nodeSize - 2 * index - 2))));
818 *key = nodeBuffer + recordOffset;
819 if (gIsHFSPlus) {
820 keySize = SWAP_BE16(*(short *)*key);
821 *data = *key + 2 + keySize;
822 } else {
823 keySize = **key;
824 *data = *key + 2 + keySize - (keySize & 1);
825 }
826}
827
828static long ReadExtent(char * extent, uint64_t extentSize,
829 long extentFile, uint64_t offset, uint64_t size,
830 void * buffer, long cache)
831{
832 uint64_t lastOffset;
833long long blockNumber, countedBlocks = 0;
834 long long nextExtent = 0, sizeRead = 0, readSize;
835 long long nextExtentBlock, currentExtentBlock = 0;
836 long long readOffset;
837 long long extentDensity, sizeofExtent, currentExtentSize;
838 char *currentExtent, *extentBuffer = 0, *bufferPos = buffer;
839
840 if (offset >= extentSize) return 0;
841
842 if (gIsHFSPlus) {
843 extentDensity = kHFSPlusExtentDensity;
844 sizeofExtent = sizeof(HFSPlusExtentDescriptor);
845 } else {
846 extentDensity = kHFSExtentDensity;
847 sizeofExtent = sizeof(HFSExtentDescriptor);
848 }
849
850 lastOffset = offset + size;
851 while (offset < lastOffset) {
852 blockNumber = offset / gBlockSize;
853
854 // Find the extent for the offset.
855 for (; ; nextExtent++) {
856 if (nextExtent < extentDensity) {
857 if ((countedBlocks+GetExtentSize(extent, nextExtent)-1)<blockNumber) {
858 countedBlocks += GetExtentSize(extent, nextExtent);
859 continue;
860 }
861
862 currentExtent = extent + nextExtent * sizeofExtent;
863 break;
864 }
865
866 if (extentBuffer == 0) {
867 extentBuffer = MALLOC(sizeofExtent * extentDensity);
868 if (extentBuffer == 0) return -1;
869 }
870
871 nextExtentBlock = nextExtent / extentDensity;
872 if (currentExtentBlock != nextExtentBlock) {
873 ReadExtentsEntry(extentFile, countedBlocks, extentBuffer);
874 currentExtentBlock = nextExtentBlock;
875 }
876
877 currentExtentSize = GetExtentSize(extentBuffer, nextExtent % extentDensity);
878
879 if ((countedBlocks + currentExtentSize - 1) >= blockNumber) {
880 currentExtent = extentBuffer + sizeofExtent * (nextExtent % extentDensity);
881 break;
882 }
883
884 countedBlocks += currentExtentSize;
885 }
886
887 readOffset = ((blockNumber - countedBlocks) * gBlockSize) +
888 (offset % gBlockSize);
889
890 readSize = GetExtentSize(currentExtent, 0) * gBlockSize - readOffset;
891 if (readSize > (size - sizeRead)) readSize = size - sizeRead;
892
893 readOffset += (long long)GetExtentStart(currentExtent, 0) * gBlockSize;
894
895 CacheRead(gCurrentIH, bufferPos, gAllocationOffset + readOffset,
896 readSize, cache);
897
898 sizeRead += readSize;
899 offset += readSize;
900 bufferPos += readSize;
901 }
902
903 if (extentBuffer) free(extentBuffer);
904
905 return sizeRead;
906}
907
908static long GetExtentStart(void * extents, long index)
909{
910 long start;
911 HFSExtentDescriptor *hfsExtents = extents;
912 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
913
914 if (gIsHFSPlus) start = SWAP_BE32(hfsPlusExtents[index].startBlock);
915 else start = SWAP_BE16(hfsExtents[index].startBlock);
916
917 return start;
918}
919
920static long GetExtentSize(void * extents, long index)
921{
922 long size;
923 HFSExtentDescriptor *hfsExtents = extents;
924 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
925
926 if (gIsHFSPlus) size = SWAP_BE32(hfsPlusExtents[index].blockCount);
927 else size = SWAP_BE16(hfsExtents[index].blockCount);
928
929 return size;
930}
931
932static long CompareHFSCatalogKeys(void * key, void * testKey)
933{
934 HFSCatalogKey *searchKey, *trialKey;
935 long result, searchParentID, trialParentID;
936
937 searchKey = key;
938 trialKey = testKey;
939
940 searchParentID = SWAP_BE32(searchKey->parentID);
941 trialParentID = SWAP_BE32(trialKey->parentID);
942
943 // parent dirID is unsigned
944 if (searchParentID > trialParentID) result = 1;
945 else if (searchParentID < trialParentID) result = -1;
946 else {
947 // parent dirID's are equal, compare names
948 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
949 }
950
951 return result;
952}
953
954static long CompareHFSPlusCatalogKeys(void * key, void * testKey)
955{
956 HFSPlusCatalogKey *searchKey, *trialKey;
957 long result, searchParentID, trialParentID;
958
959 searchKey = key;
960 trialKey = testKey;
961
962 searchParentID = SWAP_BE32(searchKey->parentID);
963 trialParentID = SWAP_BE32(trialKey->parentID);
964
965 // parent dirID is unsigned
966 if (searchParentID > trialParentID) result = 1;
967 else if (searchParentID < trialParentID) result = -1;
968 else {
969 // parent dirID's are equal, compare names
970 if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))
971 result = searchKey->nodeName.length - trialKey->nodeName.length;
972 else
973 if (gCaseSensitive) {
974 result = BinaryUnicodeCompare(&searchKey->nodeName.unicode[0],
975 SWAP_BE16(searchKey->nodeName.length),
976 &trialKey->nodeName.unicode[0],
977 SWAP_BE16(trialKey->nodeName.length));
978 } else {
979 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
980 SWAP_BE16(searchKey->nodeName.length),
981 &trialKey->nodeName.unicode[0],
982 SWAP_BE16(trialKey->nodeName.length), OSBigEndian);
983 }
984 }
985
986 return result;
987}
988
989static long CompareHFSExtentsKeys(void * key, void * testKey)
990{
991 HFSExtentKey *searchKey, *trialKey;
992 long result;
993
994 searchKey = key;
995 trialKey = testKey;
996
997 // assume searchKey < trialKey
998 result = -1;
999
1000 if (searchKey->fileID == trialKey->fileID) {
1001 // FileNum's are equal; compare fork types
1002 if (searchKey->forkType == trialKey->forkType) {
1003 // Fork types are equal; compare allocation block number
1004 if (searchKey->startBlock == trialKey->startBlock) {
1005 // Everything is equal
1006 result = 0;
1007 } else {
1008 // Allocation block numbers differ; determine sign
1009 if (SWAP_BE16(searchKey->startBlock) > SWAP_BE16(trialKey->startBlock))
1010 result = 1;
1011 }
1012 } else {
1013 // Fork types differ; determine sign
1014 if (searchKey->forkType > trialKey->forkType) result = 1;
1015 }
1016 } else {
1017 // FileNums differ; determine sign
1018 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
1019 result = 1;
1020 }
1021
1022 return result;
1023}
1024
1025static long CompareHFSPlusExtentsKeys(void * key, void * testKey)
1026{
1027 HFSPlusExtentKey *searchKey, *trialKey;
1028 long result;
1029
1030 searchKey = key;
1031 trialKey = testKey;
1032
1033 // assume searchKey < trialKey
1034 result = -1;
1035
1036 if (searchKey->fileID == trialKey->fileID) {
1037 // FileNum's are equal; compare fork types
1038 if (searchKey->forkType == trialKey->forkType) {
1039 // Fork types are equal; compare allocation block number
1040 if (searchKey->startBlock == trialKey->startBlock) {
1041 // Everything is equal
1042 result = 0;
1043 } else {
1044 // Allocation block numbers differ; determine sign
1045 if (SWAP_BE32(searchKey->startBlock) > SWAP_BE32(trialKey->startBlock))
1046 result = 1;
1047 }
1048 } else {
1049 // Fork types differ; determine sign
1050 if (searchKey->forkType > trialKey->forkType) result = 1;
1051 }
1052 } else {
1053 // FileNums differ; determine sign
1054 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
1055 result = 1;
1056 }
1057
1058 return result;
1059}
1060
1061

Archive Download this file

Revision: 5