Chameleon

Chameleon Svn Source Tree

Root/branches/azimutz/Chazi/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 "sl.h" //Azi:???
32#include <hfs/hfs_format.h>
33
34#include "hfs.h"
35
36#define kBlockSize (0x200)
37
38#define kMDBBaseOffset (2 * kBlockSize)
39
40#define kBTreeCatalog (0)
41#define kBTreeExtents (1)
42
43#ifdef __i386__
44
45static CICell gCurrentIH;
46static long long gAllocationOffset;
47static long gIsHFSPlus;
48static long gCaseSensitive;
49static long gBlockSize;
50static long gCacheBlockSize;
51static char *gBTreeHeaderBuffer;
52static BTHeaderRec *gBTHeaders[2];
53static char *gHFSMdbVib;
54static HFSMasterDirectoryBlock *gHFSMDB;
55static char *gHFSPlusHeader;
56static HFSPlusVolumeHeader *gHFSPlus;
57static char *gLinkTemp;
58static long long gVolID;
59static char *gTempStr;
60
61#else /* !__i386__ */
62
63static CICell gCurrentIH;
64static long long gAllocationOffset;
65static long gIsHFSPlus;
66static long gBlockSize;
67static long gCaseSensitive;
68static long gCacheBlockSize;
69static char gBTreeHeaderBuffer[512];
70static BTHeaderRec *gBTHeaders[2];
71static char gHFSMdbVib[kBlockSize];
72static HFSMasterDirectoryBlock *gHFSMDB =(HFSMasterDirectoryBlock*)gHFSMdbVib;
73static char gHFSPlusHeader[kBlockSize];
74static HFSPlusVolumeHeader *gHFSPlus =(HFSPlusVolumeHeader*)gHFSPlusHeader;
75static char gLinkTemp[64];
76static long long gVolID;
77
78#endif /* !__i386__ */
79
80static long ReadFile(void *file, uint64_t *length, void *base, uint64_t offset);
81static long GetCatalogEntryInfo(void *entry, long *flags, long *time,
82 FinderInfo *finderInfo, long *infoValid);
83static long ResolvePathToCatalogEntry(char *filePath, long *flags,
84 void *entry, long dirID, long long *dirIndex);
85
86static long GetCatalogEntry(long long *dirIndex, char **name,
87 long *flags, long *time,
88 FinderInfo *finderInfo, long *infoValid);
89static long ReadCatalogEntry(char *fileName, long dirID, void *entry,
90 long long *dirIndex);
91static long ReadExtentsEntry(long fileID, long startBlock, void *entry);
92
93static long ReadBTreeEntry(long btree, void *key, char *entry, long long *dirIndex);
94static void GetBTreeRecord(long index, char *nodeBuffer, long nodeSize,
95 char **key, char **data);
96
97static long ReadExtent(char *extent, uint64_t extentSize, long extentFile,
98 uint64_t offset, uint64_t size, void *buffer, long cache);
99
100static long GetExtentStart(void *extents, long index);
101static long GetExtentSize(void *extents, long index);
102
103static long CompareHFSCatalogKeys(void *key, void *testKey);
104static long CompareHFSPlusCatalogKeys(void *key, void *testKey);
105static long CompareHFSExtentsKeys(void *key, void *testKey);
106static long CompareHFSPlusExtentsKeys(void *key, void *testKey);
107
108extern long FastRelString(u_int8_t *str1, u_int8_t *str2);
109extern long BinaryUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
110 u_int16_t *uniStr2, u_int32_t len2);
111
112
113static void SwapFinderInfo(FndrFileInfo *dst, FndrFileInfo *src)
114{
115 dst->fdType = SWAP_BE32(src->fdType);
116 dst->fdCreator = SWAP_BE32(src->fdCreator);
117 dst->fdFlags = SWAP_BE16(src->fdFlags);
118 // Don't bother with location
119}
120
121void HFSFree(CICell ih)
122{
123 if(gCurrentIH == ih)
124 gCurrentIH = 0;
125 free(ih);
126}
127
128bool HFSProbe (const void *buf)
129{
130const HFSMasterDirectoryBlock *mdb;
131const HFSPlusVolumeHeader *header;
132mdb=(const HFSMasterDirectoryBlock *)(((const char*)buf)+kMDBBaseOffset);
133header=(const HFSPlusVolumeHeader *)(((const char*)buf)+kMDBBaseOffset);
134
135if ( SWAP_BE16(mdb->drSigWord) == kHFSSigWord )
136return true;
137if (SWAP_BE16(header->signature) != kHFSPlusSigWord &&
138 SWAP_BE16(header->signature) != kHFSXSigWord)
139return false;
140return true;
141}
142
143long HFSInitPartition(CICell ih)
144{
145 long extentSize, extentFile, nodeSize;
146 void *extent;
147
148 if (ih == gCurrentIH) {
149#ifdef __i386__
150 CacheInit(ih, gCacheBlockSize);
151#endif
152 return 0;
153 }
154
155#ifdef __i386__
156 if (!gTempStr) gTempStr = (char *)malloc(4096);
157 if (!gLinkTemp) gLinkTemp = (char *)malloc(64);
158 if (!gBTreeHeaderBuffer) gBTreeHeaderBuffer = (char *)malloc(512);
159 if (!gHFSMdbVib) {
160 gHFSMdbVib = (char *)malloc(kBlockSize);
161 gHFSMDB = (HFSMasterDirectoryBlock *)gHFSMdbVib;
162 }
163 if (!gHFSPlusHeader) {
164 gHFSPlusHeader = (char *)malloc(kBlockSize);
165 gHFSPlus = (HFSPlusVolumeHeader *)gHFSPlusHeader;
166 }
167 if (!gTempStr || !gLinkTemp || !gBTreeHeaderBuffer ||
168 !gHFSMdbVib || !gHFSPlusHeader) return -1;
169#endif /* __i386__ */
170
171 gAllocationOffset = 0;
172 gIsHFSPlus = 0;
173 gCaseSensitive = 0;
174 gBTHeaders[0] = 0;
175 gBTHeaders[1] = 0;
176
177 // Look for the HFS MDB
178 Seek(ih, kMDBBaseOffset);
179 Read(ih, (long)gHFSMdbVib, kBlockSize);
180
181 if ( SWAP_BE16(gHFSMDB->drSigWord) == kHFSSigWord ) {
182 gAllocationOffset = SWAP_BE16(gHFSMDB->drAlBlSt) * kBlockSize;
183
184 // See if it is HFSPlus
185 if (SWAP_BE16(gHFSMDB->drEmbedSigWord) != kHFSPlusSigWord) {
186 // Normal HFS;
187 gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSMDB->drAlBlkSiz);
188 CacheInit(ih, gCacheBlockSize);
189 gCurrentIH = ih;
190
191 // grab the 64 bit volume ID
192 bcopy(&gHFSMDB->drFndrInfo[6], &gVolID, 8);
193
194 // Get the Catalog BTree node size.
195 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
196 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
197 extentFile = kHFSCatalogFileID;
198 ReadExtent(extent, extentSize, extentFile, 0, 256,
199 gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);
200
201 nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 +
202 sizeof(BTNodeDescriptor)))->nodeSize);
203
204 // If the BTree node size is larger than the block size, reset the cache.
205 if (nodeSize > gBlockSize) {
206 gCacheBlockSize = nodeSize;
207 CacheInit(ih, gCacheBlockSize);
208 }
209
210 return 0;
211 }
212
213 // Calculate the offset to the embeded HFSPlus volume.
214 gAllocationOffset += (long long)SWAP_BE16(gHFSMDB->drEmbedExtent.startBlock) *
215 SWAP_BE32(gHFSMDB->drAlBlkSiz);
216 }
217
218 // Look for the HFSPlus Header
219 Seek(ih, gAllocationOffset + kMDBBaseOffset);
220 Read(ih, (long)gHFSPlusHeader, kBlockSize);
221
222 // Not a HFS+ or HFSX volume.
223 if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord &&
224 SWAP_BE16(gHFSPlus->signature) != kHFSXSigWord) {
225verbose("HFS signature was not present.\n");
226 gCurrentIH = 0;
227return -1;
228 }
229
230 gIsHFSPlus = 1;
231 gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSPlus->blockSize);
232 CacheInit(ih, gCacheBlockSize);
233 gCurrentIH = ih;
234
235ih->modTime = SWAP_BE32(gHFSPlus->modifyDate) - 2082844800;
236
237 // grab the 64 bit volume ID
238 bcopy(&gHFSPlus->finderInfo[24], &gVolID, 8);
239
240 // Get the Catalog BTree node size.
241 extent = &gHFSPlus->catalogFile.extents;
242 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
243 extentFile = kHFSCatalogFileID;
244
245 ReadExtent(extent, extentSize, extentFile, 0, 256,
246 gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);
247
248 nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 +
249 sizeof(BTNodeDescriptor)))->nodeSize);
250
251 // If the BTree node size is larger than the block size, reset the cache.
252 if (nodeSize > gBlockSize) {
253 gCacheBlockSize = nodeSize;
254 CacheInit(ih, gCacheBlockSize);
255 }
256
257 return 0;
258}
259
260long HFSLoadFile(CICell ih, char * filePath)
261{
262 return HFSReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);
263}
264
265long HFSReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)
266{
267 char entry[512];
268 char devStr[12];
269 long dirID, result, flags;
270
271 if (HFSInitPartition(ih) == -1) return -1;
272
273 dirID = kHFSRootFolderID;
274 // Skip a lead '\'. Start in the system folder if there are two.
275 if (filePath[0] == '/') {
276 if (filePath[1] == '/') {
277 if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);
278 else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);
279 if (dirID == 0) {
280return -1;
281 }
282 filePath++;
283 }
284 filePath++;
285 }
286
287 result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);
288 if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
289return -1;
290 }
291
292#if UNUSED
293 // Not yet for Intel. System.config/Default.table will fail this check.
294 // Check file owner and permissions.
295 if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;
296#endif
297
298 result = ReadFile(entry, &length, base, offset);
299 if (result == -1) {
300return -1;
301 }
302
303 getDeviceDescription(ih, devStr);
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;
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, 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;
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
513 case kHFSFileThreadRecord :
514 case kHFSPlusFileThreadRecord :
515 case kHFSFolderThreadRecord :
516 case kHFSPlusFolderThreadRecord :
517 *flags = kFileTypeUnknown;
518 tmpTime = 0;
519 break;
520 }
521
522 if (time != 0) {
523 // Convert base time from 1904 to 1970.
524 *time = tmpTime - 2082844800;
525 }
526 if (infoValid) *infoValid = valid;
527
528 return 0;
529}
530
531static long ResolvePathToCatalogEntry(char * filePath, long * flags,
532 void * entry, long dirID, long long * dirIndex)
533{
534 char *restPath;
535 long result, cnt, subFolderID = 0;
536 long long tmpDirIndex;
537 HFSPlusCatalogFile *hfsPlusFile;
538
539 // Copy the file name to gTempStr
540 cnt = 0;
541 while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++;
542 strlcpy(gTempStr, filePath, cnt+1);
543
544 // Move restPath to the right place.
545 if (filePath[cnt] != '\0') cnt++;
546 restPath = filePath + cnt;
547
548 // gTempStr is a name in the current Dir.
549 // restPath is the rest of the path if any.
550
551 result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex);
552 if (result == -1) {
553return -1;
554 }
555
556 GetCatalogEntryInfo(entry, flags, 0, 0, 0);
557
558 if ((*flags & kFileTypeMask) == kFileTypeDirectory) {
559 if (gIsHFSPlus)
560 subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID);
561 else
562 subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID);
563 }
564
565 if ((*flags & kFileTypeMask) == kFileTypeDirectory)
566 result = ResolvePathToCatalogEntry(restPath, flags, entry,
567 subFolderID, dirIndex);
568
569 if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat)) {
570 hfsPlusFile = (HFSPlusCatalogFile *)entry;
571 if ((SWAP_BE32(hfsPlusFile->userInfo.fdType) == kHardLinkFileType) &&
572 (SWAP_BE32(hfsPlusFile->userInfo.fdCreator) == kHFSPlusCreator)) {
573 sprintf(gLinkTemp, "%s/%s%ld", HFSPLUSMETADATAFOLDER,
574 HFS_INODE_PREFIX, SWAP_BE32(hfsPlusFile->bsdInfo.special.iNodeNum));
575 result = ResolvePathToCatalogEntry(gLinkTemp, flags, entry,
576 kHFSRootFolderID, &tmpDirIndex);
577 }
578 }
579
580 return result;
581}
582
583static long GetCatalogEntry(long long * dirIndex, char ** name,
584 long * flags, long * time,
585 FinderInfo * finderInfo, long * infoValid)
586{
587 long extentSize, nodeSize, curNode, index;
588 void *extent;
589 char *nodeBuf, *testKey, *entry;
590 BTNodeDescriptor *node;
591
592 if (gIsHFSPlus) {
593 extent = &gHFSPlus->catalogFile.extents;
594 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
595 } else {
596 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
597 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
598 }
599
600 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
601 nodeBuf = (char *)malloc(nodeSize);
602 node = (BTNodeDescriptor *)nodeBuf;
603
604 index = (long) (*dirIndex % nodeSize);
605 curNode = (long) (*dirIndex / nodeSize);
606
607 // Read the BTree node and get the record for index.
608 ReadExtent(extent, extentSize, kHFSCatalogFileID,
609 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
610 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
611
612 GetCatalogEntryInfo(entry, flags, time, finderInfo, infoValid);
613
614 // Get the file name.
615 if (gIsHFSPlus) {
616 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
617 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
618 (u_int8_t *)gTempStr, 256, OSBigEndian);
619 } else {
620 strncpy(gTempStr,
621 (const char *)&((HFSCatalogKey *)testKey)->nodeName[1],
622 ((HFSCatalogKey *)testKey)->nodeName[0]);
623 gTempStr[((HFSCatalogKey *)testKey)->nodeName[0]] = '\0';
624 }
625 *name = gTempStr;
626
627 // Update dirIndex.
628 index++;
629 if (index == SWAP_BE16(node->numRecords)) {
630 index = 0;
631 curNode = SWAP_BE32(node->fLink);
632 }
633 *dirIndex = (long long) curNode * nodeSize + index;
634
635 free(nodeBuf);
636
637 return 0;
638}
639
640static long ReadCatalogEntry(char * fileName, long dirID,
641 void * entry, long long * dirIndex)
642{
643 long length;
644 char key[sizeof(HFSPlusCatalogKey)];
645 HFSCatalogKey *hfsKey = (HFSCatalogKey *)key;
646 HFSPlusCatalogKey *hfsPlusKey = (HFSPlusCatalogKey *)key;
647
648 // Make the catalog key.
649 if ( gIsHFSPlus )
650 {
651 hfsPlusKey->parentID = SWAP_BE32(dirID);
652 length = strlen(fileName);
653 if (length > 255) length = 255;
654 utf_decodestr((u_int8_t *)fileName, hfsPlusKey->nodeName.unicode,
655 &(hfsPlusKey->nodeName.length), 512, OSBigEndian);
656 } else {
657 hfsKey->parentID = SWAP_BE32(dirID);
658 length = strlen(fileName);
659 if (length > 31) length = 31;
660 hfsKey->nodeName[0] = length;
661 strncpy((char *)(hfsKey->nodeName + 1), fileName, length);
662 }
663
664 return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex);
665}
666
667static long ReadExtentsEntry(long fileID, long startBlock, void * entry)
668{
669 char key[sizeof(HFSPlusExtentKey)];
670 HFSExtentKey *hfsKey = (HFSExtentKey *)key;
671 HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey *)key;
672
673 // Make the extents key.
674 if (gIsHFSPlus) {
675 hfsPlusKey->forkType = 0;
676 hfsPlusKey->fileID = SWAP_BE32(fileID);
677 hfsPlusKey->startBlock = SWAP_BE32(startBlock);
678 } else {
679 hfsKey->forkType = 0;
680 hfsKey->fileID = SWAP_BE32(fileID);
681 hfsKey->startBlock = SWAP_BE16(startBlock);
682 }
683
684 return ReadBTreeEntry(kBTreeExtents, &key, entry, 0);
685}
686
687static long ReadBTreeEntry(long btree, void * key, char * entry, long long * dirIndex)
688{
689 long extentSize;
690 void *extent;
691 short extentFile;
692 char *nodeBuf;
693 BTNodeDescriptor *node;
694 long nodeSize, result = 0, entrySize = 0;
695 long curNode, index = 0, lowerBound, upperBound;
696 char *testKey, *recordData;
697
698 // Figure out which tree is being looked at.
699 if (btree == kBTreeCatalog) {
700 if (gIsHFSPlus) {
701 extent = &gHFSPlus->catalogFile.extents;
702 extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);
703 } else {
704 extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;
705 extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);
706 }
707 extentFile = kHFSCatalogFileID;
708 } else {
709 if (gIsHFSPlus) {
710 extent = &gHFSPlus->extentsFile.extents;
711 extentSize = SWAP_BE64(gHFSPlus->extentsFile.logicalSize);
712 } else {
713 extent = (HFSExtentDescriptor *)&gHFSMDB->drXTExtRec;
714 extentSize = SWAP_BE32(gHFSMDB->drXTFlSize);
715 }
716 extentFile = kHFSExtentsFileID;
717 }
718
719 // Read the BTree Header if needed.
720 if (gBTHeaders[btree] == 0) {
721 ReadExtent(extent, extentSize, extentFile, 0, 256,
722 gBTreeHeaderBuffer + btree * 256, 0);
723 gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 +
724 sizeof(BTNodeDescriptor));
725 if ((gIsHFSPlus && btree == kBTreeCatalog) &&
726 (gBTHeaders[btree]->keyCompareType == kHFSBinaryCompare)) {
727 gCaseSensitive = 1;
728 }
729 }
730
731 curNode = SWAP_BE32(gBTHeaders[btree]->rootNode);
732 nodeSize = SWAP_BE16(gBTHeaders[btree]->nodeSize);
733 nodeBuf = (char *)malloc(nodeSize);
734 node = (BTNodeDescriptor *)nodeBuf;
735
736 while (1) {
737 // Read the current node.
738 ReadExtent(extent, extentSize, extentFile,
739 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
740
741 // Find the matching key.
742 lowerBound = 0;
743 upperBound = SWAP_BE16(node->numRecords) - 1;
744 while (lowerBound <= upperBound) {
745 index = (lowerBound + upperBound) / 2;
746
747 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
748
749 if (gIsHFSPlus) {
750 if (btree == kBTreeCatalog) {
751 result = CompareHFSPlusCatalogKeys(key, testKey);
752 } else {
753 result = CompareHFSPlusExtentsKeys(key, testKey);
754 }
755 } else {
756 if (btree == kBTreeCatalog) {
757 result = CompareHFSCatalogKeys(key, testKey);
758 } else {
759 result = CompareHFSExtentsKeys(key, testKey);
760 }
761 }
762
763 if (result < 0) upperBound = index - 1; // search < trial
764 else if (result > 0) lowerBound = index + 1; // search > trial
765 else break; // search = trial
766 }
767
768 if (result < 0) {
769 index = upperBound;
770 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);
771 }
772
773 // Found the closest key... Recurse on it if this is an index node.
774 if (node->kind == kBTIndexNode) {
775 curNode = SWAP_BE32( *((long *)recordData) );
776 } else break;
777 }
778
779 // Return error if the file was not found.
780 if (result != 0) { free(nodeBuf); return -1; }
781
782 if (btree == kBTreeCatalog) {
783 switch (SWAP_BE16(*(short *)recordData)) {
784 case kHFSFolderRecord : entrySize = 70; break;
785 case kHFSFileRecord : entrySize = 102; break;
786 case kHFSFolderThreadRecord : entrySize = 46; break;
787 case kHFSFileThreadRecord : entrySize = 46; break;
788 case kHFSPlusFolderRecord : entrySize = 88; break;
789 case kHFSPlusFileRecord : entrySize = 248; break;
790 case kHFSPlusFolderThreadRecord : entrySize = 264; break;
791 case kHFSPlusFileThreadRecord : entrySize = 264; break;
792 }
793 } else {
794 if (gIsHFSPlus) entrySize = sizeof(HFSPlusExtentRecord);
795 else entrySize = sizeof(HFSExtentRecord);
796 }
797
798 bcopy(recordData, entry, entrySize);
799
800 // Update dirIndex.
801 if (dirIndex != 0) {
802 index++;
803 if (index == SWAP_BE16(node->numRecords)) {
804 index = 0;
805 curNode = SWAP_BE32(node->fLink);
806 }
807 *dirIndex = (long long) curNode * nodeSize + index;
808 }
809
810 free(nodeBuf);
811
812 return 0;
813}
814
815static void GetBTreeRecord(long index, char * nodeBuffer, long nodeSize,
816 char ** key, char ** data)
817{
818 long keySize;
819 long recordOffset;
820
821 recordOffset = SWAP_BE16(*((short *)(nodeBuffer + (nodeSize - 2 * index - 2))));
822 *key = nodeBuffer + recordOffset;
823 if (gIsHFSPlus) {
824 keySize = SWAP_BE16(*(short *)*key);
825 *data = *key + 2 + keySize;
826 } else {
827 keySize = **key;
828 *data = *key + 2 + keySize - (keySize & 1);
829 }
830}
831
832static long ReadExtent(char * extent, uint64_t extentSize,
833 long extentFile, uint64_t offset, uint64_t size,
834 void * buffer, long cache)
835{
836 uint64_t lastOffset;
837long long blockNumber, countedBlocks = 0;
838 long long nextExtent = 0, sizeRead = 0, readSize;
839 long long nextExtentBlock, currentExtentBlock = 0;
840 long long readOffset;
841 long long extentDensity, sizeofExtent, currentExtentSize;
842 char *currentExtent, *extentBuffer = 0, *bufferPos = buffer;
843
844 if (offset >= extentSize) return 0;
845
846 if (gIsHFSPlus) {
847 extentDensity = kHFSPlusExtentDensity;
848 sizeofExtent = sizeof(HFSPlusExtentDescriptor);
849 } else {
850 extentDensity = kHFSExtentDensity;
851 sizeofExtent = sizeof(HFSExtentDescriptor);
852 }
853
854 lastOffset = offset + size;
855 while (offset < lastOffset) {
856 blockNumber = offset / gBlockSize;
857
858 // Find the extent for the offset.
859 for (; ; nextExtent++) {
860 if (nextExtent < extentDensity) {
861 if ((countedBlocks+GetExtentSize(extent, nextExtent)-1)<blockNumber) {
862 countedBlocks += GetExtentSize(extent, nextExtent);
863 continue;
864 }
865
866 currentExtent = extent + nextExtent * sizeofExtent;
867 break;
868 }
869
870 if (extentBuffer == 0) {
871 extentBuffer = malloc(sizeofExtent * extentDensity);
872 if (extentBuffer == 0) return -1;
873 }
874
875 nextExtentBlock = nextExtent / extentDensity;
876 if (currentExtentBlock != nextExtentBlock) {
877 ReadExtentsEntry(extentFile, countedBlocks, extentBuffer);
878 currentExtentBlock = nextExtentBlock;
879 }
880
881 currentExtentSize = GetExtentSize(extentBuffer, nextExtent % extentDensity);
882
883 if ((countedBlocks + currentExtentSize - 1) >= blockNumber) {
884 currentExtent = extentBuffer + sizeofExtent * (nextExtent % extentDensity);
885 break;
886 }
887
888 countedBlocks += currentExtentSize;
889 }
890
891 readOffset = ((blockNumber - countedBlocks) * gBlockSize) +
892 (offset % gBlockSize);
893
894// MacWen: fix overflow in multiplication by forcing 64bit multiplication
895 readSize = (long long)GetExtentSize(currentExtent, 0) * gBlockSize - readOffset;
896 if (readSize > (size - sizeRead)) readSize = size - sizeRead;
897
898 readOffset += (long long)GetExtentStart(currentExtent, 0) * gBlockSize;
899
900 CacheRead(gCurrentIH, bufferPos, gAllocationOffset + readOffset,
901 readSize, cache);
902
903 sizeRead += readSize;
904 offset += readSize;
905 bufferPos += readSize;
906 }
907
908 if (extentBuffer) free(extentBuffer);
909
910 return sizeRead;
911}
912
913static long GetExtentStart(void * extents, long index)
914{
915 long start;
916 HFSExtentDescriptor *hfsExtents = extents;
917 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
918
919 if (gIsHFSPlus) start = SWAP_BE32(hfsPlusExtents[index].startBlock);
920 else start = SWAP_BE16(hfsExtents[index].startBlock);
921
922 return start;
923}
924
925static long GetExtentSize(void * extents, long index)
926{
927 long size;
928 HFSExtentDescriptor *hfsExtents = extents;
929 HFSPlusExtentDescriptor *hfsPlusExtents = extents;
930
931 if (gIsHFSPlus) size = SWAP_BE32(hfsPlusExtents[index].blockCount);
932 else size = SWAP_BE16(hfsExtents[index].blockCount);
933
934 return size;
935}
936
937static long CompareHFSCatalogKeys(void * key, void * testKey)
938{
939 HFSCatalogKey *searchKey, *trialKey;
940 long result, searchParentID, trialParentID;
941
942 searchKey = key;
943 trialKey = testKey;
944
945 searchParentID = SWAP_BE32(searchKey->parentID);
946 trialParentID = SWAP_BE32(trialKey->parentID);
947
948 // parent dirID is unsigned
949 if (searchParentID > trialParentID) result = 1;
950 else if (searchParentID < trialParentID) result = -1;
951 else {
952 // parent dirID's are equal, compare names
953 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
954 }
955
956 return result;
957}
958
959static long CompareHFSPlusCatalogKeys(void * key, void * testKey)
960{
961 HFSPlusCatalogKey *searchKey, *trialKey;
962 long result, searchParentID, trialParentID;
963
964 searchKey = key;
965 trialKey = testKey;
966
967 searchParentID = SWAP_BE32(searchKey->parentID);
968 trialParentID = SWAP_BE32(trialKey->parentID);
969
970 // parent dirID is unsigned
971 if (searchParentID > trialParentID) result = 1;
972 else if (searchParentID < trialParentID) result = -1;
973 else {
974 // parent dirID's are equal, compare names
975 if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))
976 result = searchKey->nodeName.length - trialKey->nodeName.length;
977 else
978 if (gCaseSensitive) {
979 result = BinaryUnicodeCompare(&searchKey->nodeName.unicode[0],
980 SWAP_BE16(searchKey->nodeName.length),
981 &trialKey->nodeName.unicode[0],
982 SWAP_BE16(trialKey->nodeName.length));
983 } else {
984 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
985 SWAP_BE16(searchKey->nodeName.length),
986 &trialKey->nodeName.unicode[0],
987 SWAP_BE16(trialKey->nodeName.length), OSBigEndian);
988 }
989 }
990
991 return result;
992}
993
994static long CompareHFSExtentsKeys(void * key, void * testKey)
995{
996 HFSExtentKey *searchKey, *trialKey;
997 long result;
998
999 searchKey = key;
1000 trialKey = testKey;
1001
1002 // assume searchKey < trialKey
1003 result = -1;
1004
1005 if (searchKey->fileID == trialKey->fileID) {
1006 // FileNum's are equal; compare fork types
1007 if (searchKey->forkType == trialKey->forkType) {
1008 // Fork types are equal; compare allocation block number
1009 if (searchKey->startBlock == trialKey->startBlock) {
1010 // Everything is equal
1011 result = 0;
1012 } else {
1013 // Allocation block numbers differ; determine sign
1014 if (SWAP_BE16(searchKey->startBlock) > SWAP_BE16(trialKey->startBlock))
1015 result = 1;
1016 }
1017 } else {
1018 // Fork types differ; determine sign
1019 if (searchKey->forkType > trialKey->forkType) result = 1;
1020 }
1021 } else {
1022 // FileNums differ; determine sign
1023 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
1024 result = 1;
1025 }
1026
1027 return result;
1028}
1029
1030static long CompareHFSPlusExtentsKeys(void * key, void * testKey)
1031{
1032 HFSPlusExtentKey *searchKey, *trialKey;
1033 long result;
1034
1035 searchKey = key;
1036 trialKey = testKey;
1037
1038 // assume searchKey < trialKey
1039 result = -1;
1040
1041 if (searchKey->fileID == trialKey->fileID) {
1042 // FileNum's are equal; compare fork types
1043 if (searchKey->forkType == trialKey->forkType) {
1044 // Fork types are equal; compare allocation block number
1045 if (searchKey->startBlock == trialKey->startBlock) {
1046 // Everything is equal
1047 result = 0;
1048 } else {
1049 // Allocation block numbers differ; determine sign
1050 if (SWAP_BE32(searchKey->startBlock) > SWAP_BE32(trialKey->startBlock))
1051 result = 1;
1052 }
1053 } else {
1054 // Fork types differ; determine sign
1055 if (searchKey->forkType > trialKey->forkType) result = 1;
1056 }
1057 } else {
1058 // FileNums differ; determine sign
1059 if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))
1060 result = 1;
1061 }
1062
1063 return result;
1064}
1065
1066

Archive Download this file

Revision: 515