Chameleon

Chameleon Svn Source Tree

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

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

Archive Download this file

Revision: 2044