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

Archive Download this file

Revision: 1972