]>
Commit | Line | Data |
---|---|---|
75b89a82 A |
1 | /* |
2 | * Copyright (c) 2000 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 1.1 (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 | #define kBlockSize (0x200) | |
34 | ||
35 | #define kMDBBaseOffset (2 * kBlockSize) | |
36 | ||
37 | #define kBTreeCatalog (0) | |
38 | #define kBTreeExtents (1) | |
39 | ||
40 | #ifdef __i386__ | |
41 | ||
42 | static CICell gCurrentIH; | |
43 | static long long gAllocationOffset; | |
44 | static long gIsHFSPlus; | |
45 | static long gBlockSize; | |
46 | static long gCacheBlockSize; | |
47 | static char *gBTreeHeaderBuffer; | |
48 | static BTHeaderRec *gBTHeaders[2]; | |
49 | static char *gHFSMdbVib; | |
50 | static HFSMasterDirectoryBlock *gHFSMDB; | |
51 | static char *gHFSPlusHeader; | |
52 | static HFSPlusVolumeHeader *gHFSPlus; | |
53 | static char *gLinkTemp; | |
54 | static char *gTempStr; | |
55 | ||
56 | #else /* !__i386__ */ | |
57 | ||
58 | static CICell gCurrentIH; | |
59 | static long long gAllocationOffset; | |
60 | static long gIsHFSPlus; | |
61 | static long gBlockSize; | |
62 | static long gCacheBlockSize; | |
63 | static char gBTreeHeaderBuffer[512]; | |
64 | static BTHeaderRec *gBTHeaders[2]; | |
65 | static char gHFSMdbVib[kBlockSize]; | |
66 | static HFSMasterDirectoryBlock *gHFSMDB =(HFSMasterDirectoryBlock*)gHFSMdbVib; | |
67 | static char gHFSPlusHeader[kBlockSize]; | |
68 | static HFSPlusVolumeHeader *gHFSPlus =(HFSPlusVolumeHeader*)gHFSPlusHeader; | |
69 | static char gLinkTemp[64]; | |
70 | ||
71 | #endif /* !__i386__ */ | |
72 | ||
73 | static long ReadFile(void *file, long *length); | |
74 | static long GetCatalogEntryInfo(void *entry, long *flags, long *time); | |
75 | static long ResolvePathToCatalogEntry(char *filePath, long *flags, | |
76 | void *entry, long dirID, long *dirIndex); | |
77 | ||
78 | static long GetCatalogEntry(long *dirIndex, char **name, | |
79 | long *flags, long *time); | |
80 | static long ReadCatalogEntry(char *fileName, long dirID, void *entry, | |
81 | long *dirIndex); | |
82 | static long ReadExtentsEntry(long fileID, long startBlock, void *entry); | |
83 | ||
84 | static long ReadBTreeEntry(long btree, void *key, char *entry, long *dirIndex); | |
85 | static void GetBTreeRecord(long index, char *nodeBuffer, long nodeSize, | |
86 | char **key, char **data); | |
87 | ||
88 | static long ReadExtent(char *extent, long extentSize, long extentFile, | |
89 | long offset, long size, void *buffer, long cache); | |
90 | ||
91 | static long GetExtentStart(void *extents, long index); | |
92 | static long GetExtentSize(void *extents, long index); | |
93 | ||
94 | static long CompareHFSCatalogKeys(void *key, void *testKey); | |
95 | static long CompareHFSPlusCatalogKeys(void *key, void *testKey); | |
96 | static long CompareHFSExtentsKeys(void *key, void *testKey); | |
97 | static long CompareHFSPlusExtentsKeys(void *key, void *testKey); | |
98 | ||
99 | extern long FastRelString(char *str1, char *str2); | |
100 | extern long FastUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1, | |
101 | u_int16_t *uniStr2, u_int32_t len2); | |
102 | extern void utf_encodestr(const u_int16_t *ucsp, int ucslen, | |
103 | u_int8_t *utf8p, u_int32_t bufsize); | |
104 | extern void utf_decodestr(const u_int8_t *utf8p, u_int16_t *ucsp, | |
105 | u_int16_t *ucslen, u_int32_t bufsize); | |
106 | ||
107 | ||
108 | long HFSInitPartition(CICell ih) | |
109 | { | |
110 | long extentSize, extentFile, nodeSize; | |
111 | void *extent; | |
112 | ||
113 | if (ih == gCurrentIH) { | |
114 | #ifdef __i386__ | |
115 | CacheInit(ih, gCacheBlockSize); | |
116 | #endif | |
117 | return 0; | |
118 | } | |
119 | ||
120 | verbose("HFSInitPartition: %x\n", ih); | |
121 | ||
122 | #ifdef __i386__ | |
123 | if (!gTempStr) gTempStr = (char *)malloc(4096); | |
124 | if (!gLinkTemp) gLinkTemp = (char *)malloc(64); | |
125 | if (!gBTreeHeaderBuffer) gBTreeHeaderBuffer = (char *)malloc(512); | |
126 | if (!gHFSMdbVib) { | |
127 | gHFSMdbVib = (char *)malloc(kBlockSize); | |
128 | gHFSMDB = (HFSMasterDirectoryBlock *)gHFSMdbVib; | |
129 | } | |
130 | if (!gHFSPlusHeader) { | |
131 | gHFSPlusHeader = (char *)malloc(kBlockSize); | |
132 | gHFSPlus = (HFSPlusVolumeHeader *)gHFSPlusHeader; | |
133 | } | |
134 | if (!gTempStr || !gLinkTemp || !gBTreeHeaderBuffer || | |
135 | !gHFSMdbVib || !gHFSPlusHeader) return -1; | |
136 | #endif /* __i386__ */ | |
137 | ||
138 | gAllocationOffset = 0; | |
139 | gIsHFSPlus = 0; | |
140 | gBTHeaders[0] = 0; | |
141 | gBTHeaders[1] = 0; | |
142 | ||
143 | // Look for the HFS MDB | |
144 | Seek(ih, kMDBBaseOffset); | |
145 | Read(ih, (long)gHFSMdbVib, kBlockSize); | |
146 | ||
147 | if ( SWAP_BE16(gHFSMDB->drSigWord) == kHFSSigWord ) { | |
148 | gAllocationOffset = SWAP_BE16(gHFSMDB->drAlBlSt) * kBlockSize; | |
149 | ||
150 | // See if it is HFSPlus | |
151 | if (SWAP_BE16(gHFSMDB->drEmbedSigWord) != kHFSPlusSigWord) { | |
152 | // Normal HFS; | |
153 | gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSMDB->drAlBlkSiz); | |
154 | CacheInit(ih, gCacheBlockSize); | |
155 | gCurrentIH = ih; | |
156 | ||
157 | // Get the Catalog BTree node size. | |
158 | extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec; | |
159 | extentSize = SWAP_BE32(gHFSMDB->drCTFlSize); | |
160 | extentFile = kHFSCatalogFileID; | |
161 | ReadExtent(extent, extentSize, extentFile, 0, 256, | |
162 | gBTreeHeaderBuffer + kBTreeCatalog * 256, 0); | |
163 | ||
164 | nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 + | |
165 | sizeof(BTNodeDescriptor)))->nodeSize); | |
166 | ||
167 | // If the BTree node size is larger than the block size, reset the cache. | |
168 | if (nodeSize > gBlockSize) { | |
169 | gCacheBlockSize = nodeSize; | |
170 | CacheInit(ih, gCacheBlockSize); | |
171 | } | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | // Calculate the offset to the embeded HFSPlus volume. | |
177 | gAllocationOffset += (long long)SWAP_BE16(gHFSMDB->drEmbedExtent.startBlock) * | |
178 | SWAP_BE32(gHFSMDB->drAlBlkSiz); | |
179 | } | |
180 | ||
181 | // Look for the HFSPlus Header | |
182 | Seek(ih, gAllocationOffset + kMDBBaseOffset); | |
183 | Read(ih, (long)gHFSPlusHeader, kBlockSize); | |
184 | ||
185 | // Not a HFS[+] volume. | |
186 | if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord) return -1; | |
187 | ||
188 | gIsHFSPlus = 1; | |
189 | gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSPlus->blockSize); | |
190 | CacheInit(ih, gCacheBlockSize); | |
191 | gCurrentIH = ih; | |
192 | ||
193 | // Get the Catalog BTree node size. | |
194 | extent = &gHFSPlus->catalogFile.extents; | |
195 | extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize); | |
196 | extentFile = kHFSCatalogFileID; | |
197 | ||
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 | long HFSLoadFile(CICell ih, char * filePath) | |
214 | { | |
215 | char entry[512]; | |
216 | long dirID, result, length, flags; | |
217 | ||
218 | if (HFSInitPartition(ih) == -1) return -1; | |
219 | ||
220 | verbose("Loading HFS%s file: [%s] from %x.\n", | |
221 | (gIsHFSPlus ? "+" : ""), filePath, ih); | |
222 | ||
223 | dirID = kHFSRootFolderID; | |
224 | // Skip a lead '\'. Start in the system folder if there are two. | |
225 | if (filePath[0] == '/') { | |
226 | if (filePath[1] == '/') { | |
227 | if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]); | |
228 | else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]); | |
229 | if (dirID == 0) return -1; | |
230 | filePath++; | |
231 | } | |
232 | filePath++; | |
233 | } | |
234 | ||
235 | result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0); | |
236 | if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat)) return -1; | |
237 | ||
238 | #if 0 // Not yet for Intel. System.config/Default.table will fail this check. | |
239 | // Check file owner and permissions. | |
240 | if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1; | |
241 | #endif | |
242 | ||
243 | result = ReadFile(entry, &length); | |
244 | if (result == -1) return -1; | |
245 | ||
246 | return length; | |
247 | } | |
248 | ||
249 | long HFSGetDirEntry(CICell ih, char * dirPath, long * dirIndex, char ** name, | |
250 | long * flags, long * time) | |
251 | { | |
252 | char entry[512]; | |
253 | long dirID, dirFlags; | |
254 | ||
255 | if (HFSInitPartition(ih) == -1) return -1; | |
256 | ||
257 | if (*dirIndex == -1) return -1; | |
258 | ||
259 | dirID = kHFSRootFolderID; | |
260 | // Skip a lead '\'. Start in the system folder if there are two. | |
261 | if (dirPath[0] == '/') { | |
262 | if (dirPath[1] == '/') { | |
263 | if (gIsHFSPlus) dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]); | |
264 | else dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]); | |
265 | if (dirID == 0) return -1; | |
266 | dirPath++; | |
267 | } | |
268 | dirPath++; | |
269 | } | |
270 | ||
271 | if (*dirIndex == 0) { | |
272 | ResolvePathToCatalogEntry(dirPath, &dirFlags, entry, dirID, dirIndex); | |
273 | if (*dirIndex == 0) *dirIndex = -1; | |
274 | if ((dirFlags & kFileTypeMask) != kFileTypeUnknown) return -1; | |
275 | } | |
276 | ||
277 | GetCatalogEntry(dirIndex, name, flags, time); | |
278 | if (*dirIndex == 0) *dirIndex = -1; | |
279 | if ((*flags & kFileTypeMask) == kFileTypeUnknown) return -1; | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | // Private Functions | |
285 | ||
286 | static long ReadFile(void * file, long * length) | |
287 | { | |
288 | void *extents; | |
289 | long fileID; | |
290 | HFSCatalogFile *hfsFile = file; | |
291 | HFSPlusCatalogFile *hfsPlusFile = file; | |
292 | ||
293 | if (gIsHFSPlus) { | |
294 | fileID = SWAP_BE32(hfsPlusFile->fileID); | |
295 | *length = SWAP_BE64(hfsPlusFile->dataFork.logicalSize); | |
296 | extents = &hfsPlusFile->dataFork.extents; | |
297 | } else { | |
298 | fileID = SWAP_BE32(hfsFile->fileID); | |
299 | *length = SWAP_BE32(hfsFile->dataLogicalSize); | |
300 | extents = &hfsFile->dataExtents; | |
301 | } | |
302 | ||
303 | if (*length > kLoadSize) { | |
304 | printf("File is too large.\n"); | |
305 | return -1; | |
306 | } | |
307 | ||
308 | #ifdef __i386__ | |
309 | *length = ReadExtent((char *)extents, *length, fileID, | |
310 | 0, *length, (char *)gFSLoadAddress, 0); | |
311 | #else | |
312 | *length = ReadExtent((char *)extents, *length, fileID, | |
313 | 0, *length, (char *)kLoadAddr, 0); | |
314 | #endif | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static long GetCatalogEntryInfo(void * entry, long * flags, long * time) | |
320 | { | |
321 | long tmpTime = 0; | |
322 | ||
323 | // Get information about the file. | |
324 | ||
325 | switch ( SWAP_BE16(*(short *)entry) ) | |
326 | { | |
327 | case kHFSFolderRecord : | |
328 | *flags = kFileTypeDirectory; | |
329 | tmpTime = SWAP_BE32(((HFSCatalogFolder *)entry)->modifyDate); | |
330 | break; | |
331 | ||
332 | case kHFSPlusFolderRecord : | |
333 | *flags = kFileTypeDirectory | | |
334 | (SWAP_BE16(((HFSPlusCatalogFolder *)entry)->bsdInfo.fileMode) & kPermMask); | |
335 | if (SWAP_BE32(((HFSPlusCatalogFolder *)entry)->bsdInfo.ownerID) != 0) | |
336 | *flags |= kOwnerNotRoot; | |
337 | tmpTime = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->contentModDate); | |
338 | break; | |
339 | ||
340 | case kHFSFileRecord : | |
341 | *flags = kFileTypeFlat; | |
342 | tmpTime = SWAP_BE32(((HFSCatalogFile *)entry)->modifyDate); | |
343 | break; | |
344 | ||
345 | case kHFSPlusFileRecord : | |
346 | *flags = kFileTypeFlat | | |
347 | (SWAP_BE16(((HFSPlusCatalogFile *)entry)->bsdInfo.fileMode) & kPermMask); | |
348 | if (SWAP_BE32(((HFSPlusCatalogFile *)entry)->bsdInfo.ownerID) != 0) | |
349 | *flags |= kOwnerNotRoot; | |
350 | tmpTime = SWAP_BE32(((HFSPlusCatalogFile *)entry)->contentModDate); | |
351 | break; | |
352 | ||
353 | case kHFSFileThreadRecord : | |
354 | case kHFSPlusFileThreadRecord : | |
355 | case kHFSFolderThreadRecord : | |
356 | case kHFSPlusFolderThreadRecord : | |
357 | *flags = kFileTypeUnknown; | |
358 | tmpTime = 0; | |
359 | break; | |
360 | } | |
361 | ||
362 | if (time != 0) { | |
363 | // Convert base time from 1904 to 1970. | |
364 | *time = tmpTime - 2082844800; | |
365 | } | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | static long ResolvePathToCatalogEntry(char * filePath, long * flags, | |
371 | void * entry, long dirID, long * dirIndex) | |
372 | { | |
373 | char *restPath; | |
374 | long result, cnt, subFolderID, tmpDirIndex; | |
375 | HFSPlusCatalogFile *hfsPlusFile; | |
376 | ||
377 | // Copy the file name to gTempStr | |
378 | cnt = 0; | |
379 | while ((filePath[cnt] != '/') && (filePath[cnt] != '\0')) cnt++; | |
380 | strncpy(gTempStr, filePath, cnt); | |
381 | ||
382 | // Move restPath to the right place. | |
383 | if (filePath[cnt] != '\0') cnt++; | |
384 | restPath = filePath + cnt; | |
385 | ||
386 | // gTempStr is a name in the current Dir. | |
387 | // restPath is the rest of the path if any. | |
388 | ||
389 | result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex); | |
390 | if (result == -1) return -1; | |
391 | ||
392 | GetCatalogEntryInfo(entry, flags, 0); | |
393 | ||
394 | if ((*flags & kFileTypeMask) == kFileTypeDirectory) { | |
395 | if (gIsHFSPlus) | |
396 | subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID); | |
397 | else | |
398 | subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID); | |
399 | ||
400 | result = ResolvePathToCatalogEntry(restPath, flags, entry, | |
401 | subFolderID, dirIndex); | |
402 | } | |
403 | ||
404 | if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat)) { | |
405 | hfsPlusFile = (HFSPlusCatalogFile *)entry; | |
406 | if ((SWAP_BE32(hfsPlusFile->userInfo.fdType) == kHardLinkFileType) && | |
407 | (SWAP_BE32(hfsPlusFile->userInfo.fdCreator) == kHFSPlusCreator)) { | |
408 | sprintf(gLinkTemp, "%s/%s%ld", HFSPLUSMETADATAFOLDER, | |
409 | HFS_INODE_PREFIX, SWAP_BE32(hfsPlusFile->bsdInfo.special.iNodeNum)); | |
410 | result = ResolvePathToCatalogEntry(gLinkTemp, flags, entry, | |
411 | kHFSRootFolderID, &tmpDirIndex); | |
412 | } | |
413 | } | |
414 | ||
415 | return result; | |
416 | } | |
417 | ||
418 | static long GetCatalogEntry(long * dirIndex, char ** name, | |
419 | long * flags, long * time) | |
420 | { | |
421 | long extentSize, nodeSize, curNode, index; | |
422 | void *extent; | |
423 | char *nodeBuf, *testKey, *entry; | |
424 | BTNodeDescriptor *node; | |
425 | ||
426 | if (gIsHFSPlus) { | |
427 | extent = &gHFSPlus->catalogFile.extents; | |
428 | extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize); | |
429 | } else { | |
430 | extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec; | |
431 | extentSize = SWAP_BE32(gHFSMDB->drCTFlSize); | |
432 | } | |
433 | ||
434 | nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize); | |
435 | nodeBuf = (char *)malloc(nodeSize); | |
436 | node = (BTNodeDescriptor *)nodeBuf; | |
437 | ||
438 | index = *dirIndex % nodeSize; | |
439 | curNode = *dirIndex / nodeSize; | |
440 | ||
441 | // Read the BTree node and get the record for index. | |
442 | ReadExtent(extent, extentSize, kHFSCatalogFileID, | |
443 | curNode * nodeSize, nodeSize, nodeBuf, 1); | |
444 | GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry); | |
445 | ||
446 | GetCatalogEntryInfo(entry, flags, time); | |
447 | ||
448 | // Get the file name. | |
449 | if (gIsHFSPlus) { | |
450 | utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode, | |
451 | SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length), | |
452 | gTempStr, 256); | |
453 | } else { | |
454 | strncpy(gTempStr, | |
455 | &((HFSCatalogKey *)testKey)->nodeName[1], | |
456 | ((HFSCatalogKey *)testKey)->nodeName[0]); | |
457 | } | |
458 | *name = gTempStr; | |
459 | ||
460 | // Update dirIndex. | |
461 | index++; | |
462 | if (index == SWAP_BE16(node->numRecords)) { | |
463 | index = 0; | |
464 | curNode = SWAP_BE32(node->fLink); | |
465 | } | |
466 | *dirIndex = curNode * nodeSize + index; | |
467 | ||
468 | free(nodeBuf); | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
473 | static long ReadCatalogEntry(char * fileName, long dirID, | |
474 | void * entry, long * dirIndex) | |
475 | { | |
476 | long length; | |
477 | char key[sizeof(HFSPlusCatalogKey)]; | |
478 | HFSCatalogKey *hfsKey = (HFSCatalogKey *)key; | |
479 | HFSPlusCatalogKey *hfsPlusKey = (HFSPlusCatalogKey *)key; | |
480 | ||
481 | // Make the catalog key. | |
482 | if ( gIsHFSPlus ) | |
483 | { | |
484 | hfsPlusKey->parentID = SWAP_BE32(dirID); | |
485 | length = strlen(fileName); | |
486 | if (length > 255) length = 255; | |
487 | utf_decodestr(fileName, hfsPlusKey->nodeName.unicode, | |
488 | &(hfsPlusKey->nodeName.length), 512); | |
489 | } else { | |
490 | hfsKey->parentID = SWAP_BE32(dirID); | |
491 | length = strlen(fileName); | |
492 | if (length > 31) length = 31; | |
493 | hfsKey->nodeName[0] = length; | |
494 | strncpy(hfsKey->nodeName + 1, fileName, length); | |
495 | } | |
496 | ||
497 | return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex); | |
498 | } | |
499 | ||
500 | static long ReadExtentsEntry(long fileID, long startBlock, void * entry) | |
501 | { | |
502 | char key[sizeof(HFSPlusExtentKey)]; | |
503 | HFSExtentKey *hfsKey = (HFSExtentKey *)key; | |
504 | HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey *)key; | |
505 | ||
506 | // Make the extents key. | |
507 | if (gIsHFSPlus) { | |
508 | hfsPlusKey->forkType = 0; | |
509 | hfsPlusKey->fileID = SWAP_BE32(fileID); | |
510 | hfsPlusKey->startBlock = SWAP_BE32(startBlock); | |
511 | } else { | |
512 | hfsKey->forkType = 0; | |
513 | hfsKey->fileID = SWAP_BE32(fileID); | |
514 | hfsKey->startBlock = SWAP_BE16(startBlock); | |
515 | } | |
516 | ||
517 | return ReadBTreeEntry(kBTreeExtents, &key, entry, 0); | |
518 | } | |
519 | ||
520 | static long ReadBTreeEntry(long btree, void * key, char * entry, long * dirIndex) | |
521 | { | |
522 | long extentSize; | |
523 | void *extent; | |
524 | short extentFile; | |
525 | char *nodeBuf; | |
526 | BTNodeDescriptor *node; | |
527 | long nodeSize, result = 0, entrySize = 0; | |
528 | long curNode, index = 0, lowerBound, upperBound; | |
529 | char *testKey, *recordData; | |
530 | ||
531 | // Figure out which tree is being looked at. | |
532 | if (btree == kBTreeCatalog) { | |
533 | if (gIsHFSPlus) { | |
534 | extent = &gHFSPlus->catalogFile.extents; | |
535 | extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize); | |
536 | } else { | |
537 | extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec; | |
538 | extentSize = SWAP_BE32(gHFSMDB->drCTFlSize); | |
539 | } | |
540 | extentFile = kHFSCatalogFileID; | |
541 | } else { | |
542 | if (gIsHFSPlus) { | |
543 | extent = &gHFSPlus->extentsFile.extents; | |
544 | extentSize = SWAP_BE64(gHFSPlus->extentsFile.logicalSize); | |
545 | } else { | |
546 | extent = (HFSExtentDescriptor *)&gHFSMDB->drXTExtRec; | |
547 | extentSize = SWAP_BE32(gHFSMDB->drXTFlSize); | |
548 | } | |
549 | extentFile = kHFSExtentsFileID; | |
550 | } | |
551 | ||
552 | // Read the BTree Header if needed. | |
553 | if (gBTHeaders[btree] == 0) { | |
554 | ReadExtent(extent, extentSize, extentFile, 0, 256, | |
555 | gBTreeHeaderBuffer + btree * 256, 0); | |
556 | gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 + | |
557 | sizeof(BTNodeDescriptor)); | |
558 | } | |
559 | ||
560 | curNode = SWAP_BE32(gBTHeaders[btree]->rootNode); | |
561 | nodeSize = SWAP_BE16(gBTHeaders[btree]->nodeSize); | |
562 | nodeBuf = (char *)malloc(nodeSize); | |
563 | node = (BTNodeDescriptor *)nodeBuf; | |
564 | ||
565 | while (1) { | |
566 | // Read the current node. | |
567 | ReadExtent(extent, extentSize, extentFile, | |
568 | curNode * nodeSize, nodeSize, nodeBuf, 1); | |
569 | ||
570 | // Find the matching key. | |
571 | lowerBound = 0; | |
572 | upperBound = SWAP_BE16(node->numRecords) - 1; | |
573 | while (lowerBound <= upperBound) { | |
574 | index = (lowerBound + upperBound) / 2; | |
575 | ||
576 | GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData); | |
577 | ||
578 | if (gIsHFSPlus) { | |
579 | if (btree == kBTreeCatalog) { | |
580 | result = CompareHFSPlusCatalogKeys(key, testKey); | |
581 | } else { | |
582 | result = CompareHFSPlusExtentsKeys(key, testKey); | |
583 | } | |
584 | } else { | |
585 | if (btree == kBTreeCatalog) { | |
586 | result = CompareHFSCatalogKeys(key, testKey); | |
587 | } else { | |
588 | result = CompareHFSExtentsKeys(key, testKey); | |
589 | } | |
590 | } | |
591 | ||
592 | if (result < 0) upperBound = index - 1; // search < trial | |
593 | else if (result > 0) lowerBound = index + 1; // search > trial | |
594 | else break; // search = trial | |
595 | } | |
596 | ||
597 | if (result < 0) { | |
598 | index = upperBound; | |
599 | GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData); | |
600 | } | |
601 | ||
602 | // Found the closest key... Recurse on it if this is an index node. | |
603 | if (node->kind == kBTIndexNode) { | |
604 | curNode = SWAP_BE32( *((long *)recordData) ); | |
605 | } else break; | |
606 | } | |
607 | ||
608 | // Return error if the file was not found. | |
609 | if (result != 0) { free(nodeBuf); return -1; } | |
610 | ||
611 | if (btree == kBTreeCatalog) { | |
612 | switch (SWAP_BE16(*(short *)recordData)) { | |
613 | case kHFSFolderRecord : entrySize = 70; break; | |
614 | case kHFSFileRecord : entrySize = 102; break; | |
615 | case kHFSFolderThreadRecord : entrySize = 46; break; | |
616 | case kHFSFileThreadRecord : entrySize = 46; break; | |
617 | case kHFSPlusFolderRecord : entrySize = 88; break; | |
618 | case kHFSPlusFileRecord : entrySize = 248; break; | |
619 | case kHFSPlusFolderThreadRecord : entrySize = 264; break; | |
620 | case kHFSPlusFileThreadRecord : entrySize = 264; break; | |
621 | } | |
622 | } else { | |
623 | if (gIsHFSPlus) entrySize = sizeof(HFSPlusExtentRecord); | |
624 | else entrySize = sizeof(HFSExtentRecord); | |
625 | } | |
626 | ||
627 | bcopy(recordData, entry, entrySize); | |
628 | ||
629 | // Update dirIndex. | |
630 | if (dirIndex != 0) { | |
631 | index++; | |
632 | if (index == SWAP_BE16(node->numRecords)) { | |
633 | index = 0; | |
634 | curNode = SWAP_BE32(node->fLink); | |
635 | } | |
636 | *dirIndex = curNode * nodeSize + index; | |
637 | } | |
638 | ||
639 | free(nodeBuf); | |
640 | ||
641 | return 0; | |
642 | } | |
643 | ||
644 | static void GetBTreeRecord(long index, char * nodeBuffer, long nodeSize, | |
645 | char ** key, char ** data) | |
646 | { | |
647 | long keySize; | |
648 | long recordOffset; | |
649 | ||
650 | recordOffset = SWAP_BE16(*((short *)(nodeBuffer + (nodeSize - 2 * index - 2)))); | |
651 | *key = nodeBuffer + recordOffset; | |
652 | if (gIsHFSPlus) { | |
653 | keySize = SWAP_BE16(*(short *)*key); | |
654 | *data = *key + 2 + keySize; | |
655 | } else { | |
656 | keySize = **key; | |
657 | *data = *key + 2 + keySize - (keySize & 1); | |
658 | } | |
659 | } | |
660 | ||
661 | static long ReadExtent(char * extent, long extentSize, | |
662 | long extentFile, long offset, long size, | |
663 | void * buffer, long cache) | |
664 | { | |
665 | long lastOffset, blockNumber, countedBlocks = 0; | |
666 | long nextExtent = 0, sizeRead = 0, readSize; | |
667 | long nextExtentBlock, currentExtentBlock = 0; | |
668 | long long readOffset; | |
669 | long extentDensity, sizeofExtent, currentExtentSize; | |
670 | char *currentExtent, *extentBuffer = 0, *bufferPos = buffer; | |
671 | ||
672 | if (offset >= extentSize) return 0; | |
673 | ||
674 | if (gIsHFSPlus) { | |
675 | extentDensity = kHFSPlusExtentDensity; | |
676 | sizeofExtent = sizeof(HFSPlusExtentDescriptor); | |
677 | } else { | |
678 | extentDensity = kHFSExtentDensity; | |
679 | sizeofExtent = sizeof(HFSExtentDescriptor); | |
680 | } | |
681 | ||
682 | lastOffset = offset + size; | |
683 | while (offset < lastOffset) { | |
684 | blockNumber = offset / gBlockSize; | |
685 | ||
686 | // Find the extent for the offset. | |
687 | for (; ; nextExtent++) { | |
688 | if (nextExtent < extentDensity) { | |
689 | if ((countedBlocks+GetExtentSize(extent, nextExtent)-1)<blockNumber) { | |
690 | countedBlocks += GetExtentSize(extent, nextExtent); | |
691 | continue; | |
692 | } | |
693 | ||
694 | currentExtent = extent + nextExtent * sizeofExtent; | |
695 | break; | |
696 | } | |
697 | ||
698 | if (extentBuffer == 0) { | |
699 | extentBuffer = malloc(sizeofExtent * extentDensity); | |
700 | if (extentBuffer == 0) return -1; | |
701 | } | |
702 | ||
703 | nextExtentBlock = nextExtent / extentDensity; | |
704 | if (currentExtentBlock != nextExtentBlock) { | |
705 | ReadExtentsEntry(extentFile, countedBlocks, extentBuffer); | |
706 | currentExtentBlock = nextExtentBlock; | |
707 | } | |
708 | ||
709 | currentExtentSize = GetExtentSize(extentBuffer, nextExtent % extentDensity); | |
710 | ||
711 | if ((countedBlocks + currentExtentSize - 1) >= blockNumber) { | |
712 | currentExtent = extentBuffer + sizeofExtent * (nextExtent % extentDensity); | |
713 | break; | |
714 | } | |
715 | ||
716 | countedBlocks += currentExtentSize; | |
717 | } | |
718 | ||
719 | readOffset = ((blockNumber - countedBlocks) * gBlockSize) + | |
720 | (offset % gBlockSize); | |
721 | ||
722 | readSize = GetExtentSize(currentExtent, 0) * gBlockSize - readOffset; | |
723 | if (readSize > (size - sizeRead)) readSize = size - sizeRead; | |
724 | ||
725 | readOffset += (long long)GetExtentStart(currentExtent, 0) * gBlockSize; | |
726 | ||
727 | CacheRead(gCurrentIH, bufferPos, gAllocationOffset + readOffset, | |
728 | readSize, cache); | |
729 | ||
730 | sizeRead += readSize; | |
731 | offset += readSize; | |
732 | bufferPos += readSize; | |
733 | } | |
734 | ||
735 | if (extentBuffer) free(extentBuffer); | |
736 | ||
737 | return sizeRead; | |
738 | } | |
739 | ||
740 | static long GetExtentStart(void * extents, long index) | |
741 | { | |
742 | long start; | |
743 | HFSExtentDescriptor *hfsExtents = extents; | |
744 | HFSPlusExtentDescriptor *hfsPlusExtents = extents; | |
745 | ||
746 | if (gIsHFSPlus) start = SWAP_BE32(hfsPlusExtents[index].startBlock); | |
747 | else start = SWAP_BE16(hfsExtents[index].startBlock); | |
748 | ||
749 | return start; | |
750 | } | |
751 | ||
752 | static long GetExtentSize(void * extents, long index) | |
753 | { | |
754 | long size; | |
755 | HFSExtentDescriptor *hfsExtents = extents; | |
756 | HFSPlusExtentDescriptor *hfsPlusExtents = extents; | |
757 | ||
758 | if (gIsHFSPlus) size = SWAP_BE32(hfsPlusExtents[index].blockCount); | |
759 | else size = SWAP_BE16(hfsExtents[index].blockCount); | |
760 | ||
761 | return size; | |
762 | } | |
763 | ||
764 | static long CompareHFSCatalogKeys(void * key, void * testKey) | |
765 | { | |
766 | HFSCatalogKey *searchKey, *trialKey; | |
767 | long result, searchParentID, trialParentID; | |
768 | ||
769 | searchKey = key; | |
770 | trialKey = testKey; | |
771 | ||
772 | searchParentID = SWAP_BE32(searchKey->parentID); | |
773 | trialParentID = SWAP_BE32(trialKey->parentID); | |
774 | ||
775 | // parent dirID is unsigned | |
776 | if (searchParentID > trialParentID) result = 1; | |
777 | else if (searchParentID < trialParentID) result = -1; | |
778 | else { | |
779 | // parent dirID's are equal, compare names | |
780 | result = FastRelString(searchKey->nodeName, trialKey->nodeName); | |
781 | } | |
782 | ||
783 | return result; | |
784 | } | |
785 | ||
786 | static long CompareHFSPlusCatalogKeys(void * key, void * testKey) | |
787 | { | |
788 | HFSPlusCatalogKey *searchKey, *trialKey; | |
789 | long result, searchParentID, trialParentID; | |
790 | ||
791 | searchKey = key; | |
792 | trialKey = testKey; | |
793 | ||
794 | searchParentID = SWAP_BE32(searchKey->parentID); | |
795 | trialParentID = SWAP_BE32(trialKey->parentID); | |
796 | ||
797 | // parent dirID is unsigned | |
798 | if (searchParentID > trialParentID) result = 1; | |
799 | else if (searchParentID < trialParentID) result = -1; | |
800 | else { | |
801 | // parent dirID's are equal, compare names | |
802 | if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0)) | |
803 | result = searchKey->nodeName.length - trialKey->nodeName.length; | |
804 | else | |
805 | result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], | |
806 | SWAP_BE16(searchKey->nodeName.length), | |
807 | &trialKey->nodeName.unicode[0], | |
808 | SWAP_BE16(trialKey->nodeName.length)); | |
809 | } | |
810 | ||
811 | return result; | |
812 | } | |
813 | ||
814 | static long CompareHFSExtentsKeys(void * key, void * testKey) | |
815 | { | |
816 | HFSExtentKey *searchKey, *trialKey; | |
817 | long result; | |
818 | ||
819 | searchKey = key; | |
820 | trialKey = testKey; | |
821 | ||
822 | // assume searchKey < trialKey | |
823 | result = -1; | |
824 | ||
825 | if (searchKey->fileID == trialKey->fileID) { | |
826 | // FileNum's are equal; compare fork types | |
827 | if (searchKey->forkType == trialKey->forkType) { | |
828 | // Fork types are equal; compare allocation block number | |
829 | if (searchKey->startBlock == trialKey->startBlock) { | |
830 | // Everything is equal | |
831 | result = 0; | |
832 | } else { | |
833 | // Allocation block numbers differ; determine sign | |
834 | if (SWAP_BE16(searchKey->startBlock) > SWAP_BE16(trialKey->startBlock)) | |
835 | result = 1; | |
836 | } | |
837 | } else { | |
838 | // Fork types differ; determine sign | |
839 | if (searchKey->forkType > trialKey->forkType) result = 1; | |
840 | } | |
841 | } else { | |
842 | // FileNums differ; determine sign | |
843 | if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID)) | |
844 | result = 1; | |
845 | } | |
846 | ||
847 | return result; | |
848 | } | |
849 | ||
850 | static long CompareHFSPlusExtentsKeys(void * key, void * testKey) | |
851 | { | |
852 | HFSPlusExtentKey *searchKey, *trialKey; | |
853 | long result; | |
854 | ||
855 | searchKey = key; | |
856 | trialKey = testKey; | |
857 | ||
858 | // assume searchKey < trialKey | |
859 | result = -1; | |
860 | ||
861 | if (searchKey->fileID == trialKey->fileID) { | |
862 | // FileNum's are equal; compare fork types | |
863 | if (searchKey->forkType == trialKey->forkType) { | |
864 | // Fork types are equal; compare allocation block number | |
865 | if (searchKey->startBlock == trialKey->startBlock) { | |
866 | // Everything is equal | |
867 | result = 0; | |
868 | } else { | |
869 | // Allocation block numbers differ; determine sign | |
870 | if (SWAP_BE32(searchKey->startBlock) > SWAP_BE32(trialKey->startBlock)) | |
871 | result = 1; | |
872 | } | |
873 | } else { | |
874 | // Fork types differ; determine sign | |
875 | if (searchKey->forkType > trialKey->forkType) result = 1; | |
876 | } | |
877 | } else { | |
878 | // FileNums differ; determine sign | |
879 | if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID)) | |
880 | result = 1; | |
881 | } | |
882 | ||
883 | return result; | |
884 | } |