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