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