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

Archive Download this file

Revision: 2121