1 | /*␊ |
2 | * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.␊ |
3 | *␊ |
4 | * @APPLE_LICENSE_HEADER_START@␊ |
5 | * ␊ |
6 | * The contents of this file constitute Original Code as defined in and␊ |
7 | * are subject to the Apple Public Source License Version 2.0 (the␊ |
8 | * "License"). You may not use this file except in compliance with the␊ |
9 | * License. Please obtain a copy of the License at␊ |
10 | * http://www.apple.com/publicsource and read it before using this file.␊ |
11 | * ␊ |
12 | * This Original Code and all software distributed under the License are␊ |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER␊ |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,␊ |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,␊ |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the␊ |
17 | * License for the specific language governing rights and limitations␊ |
18 | * under the License.␊ |
19 | * ␊ |
20 | * @APPLE_LICENSE_HEADER_END@␊ |
21 | */␊ |
22 | /*␊ |
23 | * hfs.c - File System Module for HFS and HFS+.␊ |
24 | *␊ |
25 | * Copyright (c) 1999-2002 Apple Computer, Inc.␊ |
26 | *␊ |
27 | * DRI: Josh de Cesare␊ |
28 | */␊ |
29 | ␊ |
30 | #include <sl.h>␊ |
31 | #include <hfs/hfs_format.h>␊ |
32 | ␊ |
33 | #include "hfs.h"␊ |
34 | ␊ |
35 | #define kBlockSize (0x200)␊ |
36 | ␊ |
37 | #define kMDBBaseOffset (2 * kBlockSize)␊ |
38 | ␊ |
39 | #define kBTreeCatalog (0)␊ |
40 | #define kBTreeExtents (1)␊ |
41 | ␊ |
42 | #ifdef __i386__␊ |
43 | ␊ |
44 | static CICell gCurrentIH;␊ |
45 | static long long gAllocationOffset;␊ |
46 | static long gIsHFSPlus;␊ |
47 | static long gCaseSensitive;␊ |
48 | static long gBlockSize;␊ |
49 | static long gCacheBlockSize;␊ |
50 | static char *gBTreeHeaderBuffer;␊ |
51 | static BTHeaderRec *gBTHeaders[2];␊ |
52 | static char *gHFSMdbVib;␊ |
53 | static HFSMasterDirectoryBlock *gHFSMDB;␊ |
54 | static char *gHFSPlusHeader;␊ |
55 | static HFSPlusVolumeHeader *gHFSPlus;␊ |
56 | static char *gLinkTemp;␊ |
57 | static long long gVolID;␊ |
58 | static char *gTempStr;␊ |
59 | ␊ |
60 | #else /* !__i386__ */␊ |
61 | ␊ |
62 | static CICell gCurrentIH;␊ |
63 | static long long gAllocationOffset;␊ |
64 | static long gIsHFSPlus;␊ |
65 | static long gBlockSize;␊ |
66 | static long gCaseSensitive;␊ |
67 | static long gCacheBlockSize;␊ |
68 | static char gBTreeHeaderBuffer[512];␊ |
69 | static BTHeaderRec *gBTHeaders[2];␊ |
70 | static char gHFSMdbVib[kBlockSize];␊ |
71 | static HFSMasterDirectoryBlock *gHFSMDB =(HFSMasterDirectoryBlock*)gHFSMdbVib;␊ |
72 | static char gHFSPlusHeader[kBlockSize];␊ |
73 | static HFSPlusVolumeHeader *gHFSPlus =(HFSPlusVolumeHeader*)gHFSPlusHeader;␊ |
74 | static char gLinkTemp[64];␊ |
75 | static long long gVolID;␊ |
76 | ␊ |
77 | #endif /* !__i386__ */␊ |
78 | ␊ |
79 | static long ReadFile(void *file, uint64_t *length, void *base, uint64_t offset);␊ |
80 | static long GetCatalogEntryInfo(void *entry, long *flags, long *time, FinderInfo *finderInfo, long *infoValid);␊ |
81 | static long ResolvePathToCatalogEntry(char *filePath, long *flags, void *entry, long dirID, long long *dirIndex);␊ |
82 | ␊ |
83 | static long GetCatalogEntry(long long *dirIndex, char **name, long *flags, long *time, FinderInfo *finderInfo, long *infoValid);␊ |
84 | static long ReadCatalogEntry(char *fileName, long dirID, void *entry, long long *dirIndex);␊ |
85 | static long ReadExtentsEntry(long fileID, long startBlock, void *entry);␊ |
86 | ␊ |
87 | static long ReadBTreeEntry(long btree, void *key, char *entry, long long *dirIndex);␊ |
88 | static void GetBTreeRecord(long index, char *nodeBuffer, long nodeSize, char **key, char **data);␊ |
89 | ␊ |
90 | static long ReadExtent(char *extent, uint64_t extentSize, long extentFile, uint64_t offset, uint64_t size, void *buffer, long cache);␊ |
91 | ␊ |
92 | static long GetExtentStart(void *extents, long index);␊ |
93 | static long GetExtentSize(void *extents, long index);␊ |
94 | ␊ |
95 | static long CompareHFSCatalogKeys(void *key, void *testKey);␊ |
96 | static long CompareHFSPlusCatalogKeys(void *key, void *testKey);␊ |
97 | static long CompareHFSExtentsKeys(void *key, void *testKey);␊ |
98 | static long CompareHFSPlusExtentsKeys(void *key, void *testKey);␊ |
99 | ␊ |
100 | extern long FastRelString(u_int8_t *str1, u_int8_t *str2);␊ |
101 | extern long BinaryUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1, u_int16_t *uniStr2, u_int32_t len2);␊ |
102 | ␊ |
103 | ␊ |
104 | //==============================================================================␊ |
105 | ␊ |
106 | static void SwapFinderInfo(FndrFileInfo *dst, FndrFileInfo *src)␊ |
107 | {␊ |
108 | dst->fdType = SWAP_BE32(src->fdType);␊ |
109 | dst->fdCreator = SWAP_BE32(src->fdCreator);␊ |
110 | dst->fdFlags = SWAP_BE16(src->fdFlags);␊ |
111 | // Don't bother with location␊ |
112 | }␊ |
113 | ␊ |
114 | ␊ |
115 | //==============================================================================␊ |
116 | ␊ |
117 | void HFSFree(CICell ih)␊ |
118 | {␊ |
119 | if(gCurrentIH == ih)␊ |
120 | gCurrentIH = 0;␊ |
121 | free(ih);␊ |
122 | }␊ |
123 | ␊ |
124 | ␊ |
125 | //==============================================================================␊ |
126 | ␊ |
127 | bool HFSProbe (const void *buf)␊ |
128 | {␊ |
129 | const HFSMasterDirectoryBlock *mdb;␊ |
130 | const HFSPlusVolumeHeader *header;␊ |
131 | mdb = (const HFSMasterDirectoryBlock *)(((const char*)buf)+kMDBBaseOffset);␊ |
132 | header = (const HFSPlusVolumeHeader *)(((const char*)buf)+kMDBBaseOffset);␊ |
133 | ␉␊ |
134 | ␉if ( SWAP_BE16(mdb->drSigWord) == kHFSSigWord )␊ |
135 | ␉␉return true;␊ |
136 | ␊ |
137 | ␉if (SWAP_BE16(header->signature) != kHFSPlusSigWord && SWAP_BE16(header->signature) != kHFSXSigWord)␊ |
138 | ␉␉return false;␊ |
139 | ␉return true;␊ |
140 | }␊ |
141 | ␊ |
142 | ␊ |
143 | //==============================================================================␊ |
144 | ␊ |
145 | long HFSInitPartition(CICell ih)␊ |
146 | {␊ |
147 | ␉long extentSize, extentFile, nodeSize;␊ |
148 | ␉void *extent;␊ |
149 | ␊ |
150 | ␉if (ih == gCurrentIH)␊ |
151 | ␉{␊ |
152 | #ifdef __i386__␊ |
153 | ␉CacheInit(ih, gCacheBlockSize);␊ |
154 | #endif␊ |
155 | ␉return 0;␊ |
156 | ␉}␊ |
157 | ␊ |
158 | #ifdef __i386__␊ |
159 | ␉if (!gTempStr) gTempStr = (char *)malloc(4096);␊ |
160 | ␉if (!gLinkTemp) gLinkTemp = (char *)malloc(64);␊ |
161 | ␉if (!gBTreeHeaderBuffer) gBTreeHeaderBuffer = (char *)malloc(512);␊ |
162 | ␉if (!gHFSMdbVib)␊ |
163 | ␉{␊ |
164 | gHFSMdbVib = (char *)malloc(kBlockSize);␊ |
165 | gHFSMDB = (HFSMasterDirectoryBlock *)gHFSMdbVib;␊ |
166 | ␉}␊ |
167 | ␉if (!gHFSPlusHeader)␊ |
168 | ␉{␊ |
169 | ␉␉gHFSPlusHeader = (char *)malloc(kBlockSize);␊ |
170 | ␉␉gHFSPlus = (HFSPlusVolumeHeader *)gHFSPlusHeader;␊ |
171 | ␉}␊ |
172 | ␉if (!gTempStr || !gLinkTemp || !gBTreeHeaderBuffer || !gHFSMdbVib || !gHFSPlusHeader) return -1;␊ |
173 | #endif /* __i386__ */␊ |
174 | ␊ |
175 | gAllocationOffset = 0;␊ |
176 | gIsHFSPlus = 0;␊ |
177 | gCaseSensitive = 0;␊ |
178 | gBTHeaders[0] = 0;␊ |
179 | gBTHeaders[1] = 0;␊ |
180 | ␊ |
181 | // Look for the HFS MDB␊ |
182 | Seek(ih, kMDBBaseOffset);␊ |
183 | Read(ih, (long)gHFSMdbVib, kBlockSize);␊ |
184 | ␊ |
185 | ␉if (SWAP_BE16(gHFSMDB->drSigWord) == kHFSSigWord)␊ |
186 | ␉{␊ |
187 | ␉␉gAllocationOffset = SWAP_BE16(gHFSMDB->drAlBlSt) * kBlockSize;␊ |
188 | ␊ |
189 | ␉␉// See if it is HFSPlus␊ |
190 | ␉␉if (SWAP_BE16(gHFSMDB->drEmbedSigWord) != kHFSPlusSigWord) {␊ |
191 | ␉␉// Normal HFS;␊ |
192 | gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSMDB->drAlBlkSiz);␊ |
193 | CacheInit(ih, gCacheBlockSize);␊ |
194 | gCurrentIH = ih;␊ |
195 | ␊ |
196 | // grab the 64 bit volume ID␊ |
197 | bcopy(&gHFSMDB->drFndrInfo[6], &gVolID, 8);␊ |
198 | ␊ |
199 | // Get the Catalog BTree node size.␊ |
200 | extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;␊ |
201 | extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);␊ |
202 | extentFile = kHFSCatalogFileID;␊ |
203 | ReadExtent(extent, extentSize, extentFile, 0, 256, gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);␊ |
204 | ␊ |
205 | nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 + ␊ |
206 | sizeof(BTNodeDescriptor)))->nodeSize);␊ |
207 | ␊ |
208 | // If the BTree node size is larger than the block size, reset the cache.␊ |
209 | ␉␉if (nodeSize > gBlockSize)␊ |
210 | ␉␉{␊ |
211 | ␉␉␉gCacheBlockSize = nodeSize;␊ |
212 | ␉␉␉CacheInit(ih, gCacheBlockSize);␊ |
213 | ␉␉}␊ |
214 | ␊ |
215 | ␉␉return 0;␊ |
216 | ␉}␊ |
217 | ␊ |
218 | // Calculate the offset to the embeded HFSPlus volume.␊ |
219 | gAllocationOffset += (long long)SWAP_BE16(gHFSMDB->drEmbedExtent.startBlock) *␊ |
220 | SWAP_BE32(gHFSMDB->drAlBlkSiz);␊ |
221 | }␊ |
222 | ␊ |
223 | // Look for the HFSPlus Header␊ |
224 | Seek(ih, gAllocationOffset + kMDBBaseOffset);␊ |
225 | Read(ih, (long)gHFSPlusHeader, kBlockSize);␊ |
226 | ␊ |
227 | // Not a HFS+ or HFSX volume.␊ |
228 | if (SWAP_BE16(gHFSPlus->signature) != kHFSPlusSigWord && SWAP_BE16(gHFSPlus->signature) != kHFSXSigWord)␊ |
229 | ␉{␊ |
230 | ␉␉verbose("HFS signature was not present.\n");␊ |
231 | ␉␉gCurrentIH = 0;␊ |
232 | ␉␉return -1;␊ |
233 | ␉}␊ |
234 | ␊ |
235 | gIsHFSPlus = 1;␊ |
236 | gCacheBlockSize = gBlockSize = SWAP_BE32(gHFSPlus->blockSize);␊ |
237 | CacheInit(ih, gCacheBlockSize);␊ |
238 | gCurrentIH = ih;␊ |
239 | ␊ |
240 | ␉ih->modTime = SWAP_BE32(gHFSPlus->modifyDate) - 2082844800;␊ |
241 | ␉␊ |
242 | // grab the 64 bit volume ID␊ |
243 | bcopy(&gHFSPlus->finderInfo[24], &gVolID, 8);␊ |
244 | ␊ |
245 | // Get the Catalog BTree node size.␊ |
246 | extent = &gHFSPlus->catalogFile.extents;␊ |
247 | extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);␊ |
248 | extentFile = kHFSCatalogFileID;␊ |
249 | ␊ |
250 | ReadExtent(extent, extentSize, extentFile, 0, 256,␊ |
251 | gBTreeHeaderBuffer + kBTreeCatalog * 256, 0);␊ |
252 | ␊ |
253 | nodeSize = SWAP_BE16(((BTHeaderRec *)(gBTreeHeaderBuffer + kBTreeCatalog * 256 +␊ |
254 | sizeof(BTNodeDescriptor)))->nodeSize);␊ |
255 | ␊ |
256 | // If the BTree node size is larger than the block size, reset the cache.␊ |
257 | ␉if (nodeSize > gBlockSize)␊ |
258 | ␉{␊ |
259 | ␉␉gCacheBlockSize = nodeSize;␊ |
260 | ␉␉CacheInit(ih, gCacheBlockSize);␊ |
261 | ␉}␊ |
262 | ␊ |
263 | ␉return 0;␊ |
264 | }␊ |
265 | ␊ |
266 | ␊ |
267 | //==============================================================================␊ |
268 | ␊ |
269 | long HFSLoadFile(CICell ih, char * filePath)␊ |
270 | {␊ |
271 | return HFSReadFile(ih, filePath, (void *)gFSLoadAddress, 0, 0);␊ |
272 | }␊ |
273 | ␊ |
274 | long HFSReadFile(CICell ih, char * filePath, void *base, uint64_t offset, uint64_t length)␊ |
275 | {␊ |
276 | char entry[512];␊ |
277 | char devStr[12];␊ |
278 | long dirID, result, flags =0;␊ |
279 | ␊ |
280 | ␉if (HFSInitPartition(ih) == -1)␊ |
281 | ␉{␊ |
282 | ␉␉return -1;␊ |
283 | ␉}␊ |
284 | ␊ |
285 | dirID = kHFSRootFolderID;␊ |
286 | // Skip a lead '\'. Start in the system folder if there are two.␊ |
287 | if (filePath[0] == '/')␊ |
288 | ␉{␊ |
289 | if (filePath[1] == '/')␊ |
290 | ␉␉{␊ |
291 | ␉␉if (gIsHFSPlus)␊ |
292 | ␉␉␉{␊ |
293 | ␉␉␉␉dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);␊ |
294 | ␉␉␉}␊ |
295 | ␉␉else␊ |
296 | ␉␉␉{␊ |
297 | ␉␉␉␉dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);␊ |
298 | ␉␉␉}␊ |
299 | ␊ |
300 | ␉␉␉if (dirID == 0)␊ |
301 | ␉␉␉{␊ |
302 | ␉␉␉␉return -1;␊ |
303 | ␉␉␉}␊ |
304 | ␉␉␉filePath++;␊ |
305 | ␉␉}␊ |
306 | ␉␉filePath++;␊ |
307 | ␉}␊ |
308 | ␊ |
309 | ␉result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);␊ |
310 | ␉if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat))␊ |
311 | ␉{␊ |
312 | ␉␉return -1;␊ |
313 | ␉}␊ |
314 | ␊ |
315 | #if UNUSED␊ |
316 | // Not yet for Intel. System.config/Default.table will fail this check.␊ |
317 | // Check file owner and permissions.␊ |
318 | if (flags & (kOwnerNotRoot | kPermGroupWrite | kPermOtherWrite)) return -1;␊ |
319 | #endif␊ |
320 | ␊ |
321 | result = ReadFile(entry, &length, base, offset);␊ |
322 | if (result == -1) {␊ |
323 | ␉return -1;␊ |
324 | }␊ |
325 | ␊ |
326 | getDeviceDescription(ih, devStr);␊ |
327 | /*␊ |
328 | verbose("Read HFS%s file: [%s/%s] %d bytes.\n",␊ |
329 | (gIsHFSPlus ? "+" : ""), devStr, filePath, (uint32_t)length);␊ |
330 | */␊ |
331 | return length;␊ |
332 | }␊ |
333 | ␊ |
334 | long HFSGetDirEntry(CICell ih, char * dirPath, long long * dirIndex, char ** name, long * flags, long * time, FinderInfo * finderInfo, long * infoValid)␊ |
335 | {␊ |
336 | ␉char entry[512];␊ |
337 | ␊ |
338 | ␉long dirID, dirFlags = 0;␊ |
339 | ␊ |
340 | ␉if (HFSInitPartition(ih) == -1)␊ |
341 | ␉{␊ |
342 | ␉␉return -1;␊ |
343 | ␉}␊ |
344 | ␊ |
345 | ␉if (*dirIndex == -1)␊ |
346 | ␉{␊ |
347 | ␉␉return -1;␊ |
348 | ␉}␊ |
349 | ␊ |
350 | ␉dirID = kHFSRootFolderID;␊ |
351 | ␊ |
352 | ␉// Skip a lead '\'. Start in the system folder if there are two.␊ |
353 | ␉if (dirPath[0] == '/')␊ |
354 | ␉{␊ |
355 | ␉␉if (dirPath[1] == '/')␊ |
356 | ␉␉{␊ |
357 | ␉␉␉if (gIsHFSPlus)␊ |
358 | ␉␉␉{␊ |
359 | ␉␉␉␉dirID = SWAP_BE32(((long *)gHFSPlus->finderInfo)[5]);␊ |
360 | ␉␉␉}␊ |
361 | ␉␉␉else ␊ |
362 | ␉␉␉{␊ |
363 | ␉␉␉␉dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);␊ |
364 | ␉␉␉}␊ |
365 | ␊ |
366 | ␉␉␉if (dirID == 0)␊ |
367 | ␉␉␉{␊ |
368 | ␉␉␉␉return -1;␊ |
369 | ␉␉␉}␊ |
370 | ␊ |
371 | ␉␉␉dirPath++;␊ |
372 | ␉␉}␊ |
373 | ␊ |
374 | ␉␉dirPath++;␊ |
375 | ␉}␊ |
376 | ␊ |
377 | ␉if (*dirIndex == 0)␊ |
378 | ␉{␊ |
379 | ␉␉ResolvePathToCatalogEntry(dirPath, &dirFlags, entry, dirID, dirIndex);␊ |
380 | ␊ |
381 | ␉␉if (*dirIndex == 0)␊ |
382 | ␉␉{␊ |
383 | ␉␉␉*dirIndex = -1;␊ |
384 | ␉␉}␊ |
385 | ␊ |
386 | ␉␉if ((dirFlags & kFileTypeMask) != kFileTypeUnknown)␊ |
387 | ␉␉{␊ |
388 | ␉␉␉return -1;␊ |
389 | ␉␉}␊ |
390 | ␉}␊ |
391 | ␊ |
392 | ␉GetCatalogEntry(dirIndex, name, flags, time, finderInfo, infoValid);␊ |
393 | ␊ |
394 | ␉if (*dirIndex == 0)␊ |
395 | ␉{␊ |
396 | ␉␉*dirIndex = -1;␊ |
397 | ␉}␊ |
398 | ␊ |
399 | ␉if ((*flags & kFileTypeMask) == kFileTypeUnknown)␊ |
400 | ␉{␊ |
401 | ␉␉return -1;␊ |
402 | ␉}␊ |
403 | ␊ |
404 | ␉return 0;␊ |
405 | }␊ |
406 | ␊ |
407 | ␊ |
408 | //==============================================================================␊ |
409 | ␊ |
410 | void HFSGetDescription(CICell ih, char *str, long strMaxLen)␊ |
411 | {␊ |
412 | ␊ |
413 | UInt16 nodeSize;␊ |
414 | UInt32 firstLeafNode;␊ |
415 | long long dirIndex;␊ |
416 | char *name;␊ |
417 | long flags =0, time;␊ |
418 | ␊ |
419 | ␉if (HFSInitPartition(ih) == -1)␊ |
420 | ␉{␊ |
421 | ␉␉return;␊ |
422 | ␉}␊ |
423 | ␊ |
424 | ␉/* Fill some crucial data structures by side effect. */␊ |
425 | ␉dirIndex = 0;␊ |
426 | ␉HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);␊ |
427 | ␊ |
428 | ␉/* Now we can loook up the volume name node. */␊ |
429 | ␉nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);␊ |
430 | ␉firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);␊ |
431 | ␊ |
432 | ␉dirIndex = (long long) firstLeafNode * nodeSize;␊ |
433 | ␊ |
434 | ␉GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);␊ |
435 | ␊ |
436 | ␉strncpy(str, name, strMaxLen);␊ |
437 | ␉str[strMaxLen] = '\0';␊ |
438 | }␊ |
439 | ␊ |
440 | ␊ |
441 | //==============================================================================␊ |
442 | ␊ |
443 | long HFSGetFileBlock(CICell ih, char *filePath, unsigned long long *firstBlock)␊ |
444 | {␊ |
445 | ␉char entry[512];␊ |
446 | ␉long dirID, result, flags =0;␊ |
447 | ␉void *extents;␊ |
448 | ␉HFSCatalogFile *hfsFile = (void *)entry;␊ |
449 | ␉HFSPlusCatalogFile *hfsPlusFile = (void *)entry;␊ |
450 | ␊ |
451 | ␉if (HFSInitPartition(ih) == -1) return -1;␊ |
452 | ␊ |
453 | ␉dirID = kHFSRootFolderID;␊ |
454 | ␉// Skip a lead '\'. Start in the system folder if there are two.␊ |
455 | ␉if (filePath[0] == '/')␊ |
456 | ␉{␊ |
457 | ␉␉if (filePath[1] == '/')␊ |
458 | ␉␉{␊ |
459 | ␉␉␉if (gIsHFSPlus)␊ |
460 | ␉␉␉{␊ |
461 | ␉␉␉␉dirID = SWAP_BE32(((long *) gHFSPlus->finderInfo)[5]);␊ |
462 | ␉␉␉}␊ |
463 | ␉␉␉else␊ |
464 | ␉␉␉{␊ |
465 | ␉␉␉␉dirID = SWAP_BE32(gHFSMDB->drFndrInfo[5]);␊ |
466 | ␉␉␉}␊ |
467 | ␊ |
468 | ␉␉␉if (dirID == 0)␊ |
469 | ␉␉␉{␊ |
470 | ␉␉␉␉return -1;␊ |
471 | ␉␉␉}␊ |
472 | ␉␉␉filePath++;␊ |
473 | ␉␉}␊ |
474 | ␉␉filePath++;␊ |
475 | ␉}␊ |
476 | ␊ |
477 | ␉result = ResolvePathToCatalogEntry(filePath, &flags, entry, dirID, 0);␊ |
478 | ␊ |
479 | ␉if ((result == -1) || ((flags & kFileTypeMask) != kFileTypeFlat))␊ |
480 | ␉{␊ |
481 | ␉␉printf("HFS: Resolve path %s failed\n", filePath);␊ |
482 | ␉␉return -1;␊ |
483 | ␉}␊ |
484 | ␊ |
485 | ␉if (gIsHFSPlus)␊ |
486 | ␉{␊ |
487 | ␉␉extents = &hfsPlusFile->dataFork.extents;␊ |
488 | ␉}␊ |
489 | ␉else␊ |
490 | ␉{␊ |
491 | ␉␉extents = &hfsFile->dataExtents;␊ |
492 | ␉}␊ |
493 | ␊ |
494 | #if DEBUG␊ |
495 | ␉printf("extent start 0x%x\n", (unsigned long)GetExtentStart(extents, 0));␊ |
496 | ␉printf("block size 0x%x\n", (unsigned long)gBlockSize);␊ |
497 | ␉printf("Allocation offset 0x%x\n", (unsigned long)gAllocationOffset);␊ |
498 | #endif␊ |
499 | ␉*firstBlock = ((unsigned long long)GetExtentStart(extents, 0) * (unsigned long long) gBlockSize + gAllocationOffset) / 512ULL;␊ |
500 | ␉return 0;␊ |
501 | }␊ |
502 | ␊ |
503 | ␊ |
504 | //==============================================================================␊ |
505 | ␊ |
506 | long HFSGetUUID(CICell ih, char *uuidStr)␊ |
507 | {␊ |
508 | if (HFSInitPartition(ih) == -1)␊ |
509 | ␉␉return -1;␊ |
510 | ␊ |
511 | if (gVolID == 0LL)␊ |
512 | ␉␉return -1;␊ |
513 | ␊ |
514 | return CreateUUIDString((uint8_t*)(&gVolID), sizeof(gVolID), uuidStr);␊ |
515 | }␊ |
516 | ␊ |
517 | ␊ |
518 | //==============================================================================␊ |
519 | // Private Functions␊ |
520 | ␊ |
521 | static long ReadFile(void * file, uint64_t * length, void * base, uint64_t offset)␊ |
522 | {␊ |
523 | void␉␉␉␉*extents;␊ |
524 | long␉␉␉␉fileID;␊ |
525 | uint64_t␉␉␉fileLength;␊ |
526 | HFSCatalogFile␉␉*hfsFile = file;␊ |
527 | HFSPlusCatalogFile␉*hfsPlusFile = file;␊ |
528 | ␊ |
529 | ␉if (gIsHFSPlus)␊ |
530 | ␉{␊ |
531 | ␉␉fileID = SWAP_BE32(hfsPlusFile->fileID);␊ |
532 | ␉␉fileLength = (uint64_t)SWAP_BE64(hfsPlusFile->dataFork.logicalSize);␊ |
533 | ␉␉extents = &hfsPlusFile->dataFork.extents;␊ |
534 | ␉}␊ |
535 | ␉else␊ |
536 | ␉{␊ |
537 | ␉␉fileID = SWAP_BE32(hfsFile->fileID);␊ |
538 | ␉␉fileLength = SWAP_BE32(hfsFile->dataLogicalSize);␊ |
539 | ␉␉extents = &hfsFile->dataExtents;␊ |
540 | ␉}␊ |
541 | ␊ |
542 | ␉if (offset > fileLength)␊ |
543 | ␉{␊ |
544 | ␉␉printf("Offset is too large.\n");␊ |
545 | ␉␉return -1;␊ |
546 | ␉}␊ |
547 | ␊ |
548 | ␉if ((*length == 0) || ((offset + *length) > fileLength))␊ |
549 | ␉{␊ |
550 | ␉␉*length = fileLength - offset;␊ |
551 | ␉}␊ |
552 | ␊ |
553 | /*␊ |
554 | ␉if (*length > kLoadSize)␊ |
555 | ␉{␊ |
556 | ␉␉printf("File is too large.\n");␊ |
557 | ␉␉return -1;␊ |
558 | ␉}␊ |
559 | */␊ |
560 | ␊ |
561 | ␉*length = ReadExtent((char *)extents, fileLength, fileID, offset, *length, (char *)base, 0);␊ |
562 | ␊ |
563 | ␉return 0;␊ |
564 | }␊ |
565 | ␊ |
566 | ␊ |
567 | //==============================================================================␊ |
568 | ␊ |
569 | static long GetCatalogEntryInfo(void * entry, long * flags, long * time, FinderInfo * finderInfo, long * infoValid)␊ |
570 | {␊ |
571 | ␉long tmpTime = 0;␊ |
572 | ␉long valid = 0;␊ |
573 | ␊ |
574 | ␉// Get information about the file.␊ |
575 | ␊ |
576 | ␉switch (SWAP_BE16(*(short *)entry))␊ |
577 | ␉{␊ |
578 | ␉case kHFSFolderRecord :␊ |
579 | ␉␉*flags = kFileTypeDirectory;␊ |
580 | ␉␉tmpTime = SWAP_BE32(((HFSCatalogFolder *)entry)->modifyDate);␊ |
581 | ␉␉break;␊ |
582 | ␊ |
583 | ␉case kHFSPlusFolderRecord :␊ |
584 | ␉␉*flags = kFileTypeDirectory | (SWAP_BE16(((HFSPlusCatalogFolder *)entry)->bsdInfo.fileMode) & kPermMask);␊ |
585 | ␉␉if (SWAP_BE32(((HFSPlusCatalogFolder *)entry)->bsdInfo.ownerID) != 0)␊ |
586 | ␉␉*flags |= kOwnerNotRoot;␊ |
587 | ␉␉tmpTime = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->contentModDate);␊ |
588 | ␉␉break;␊ |
589 | ␊ |
590 | ␉case kHFSFileRecord :␊ |
591 | ␉␉*flags = kFileTypeFlat;␊ |
592 | ␉␉tmpTime = SWAP_BE32(((HFSCatalogFile *)entry)->modifyDate);␊ |
593 | ␉␉if (finderInfo)␊ |
594 | ␉␉{␊ |
595 | ␉␉␉SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSCatalogFile *)entry)->userInfo);␊ |
596 | ␉␉␉valid = 1;␊ |
597 | ␉␉}␊ |
598 | ␉␉break;␊ |
599 | ␊ |
600 | ␉case kHFSPlusFileRecord :␊ |
601 | ␉␉*flags = kFileTypeFlat | (SWAP_BE16(((HFSPlusCatalogFile *)entry)->bsdInfo.fileMode) & kPermMask);␊ |
602 | ␉␉if (SWAP_BE32(((HFSPlusCatalogFile *)entry)->bsdInfo.ownerID) != 0)␊ |
603 | ␉␉*flags |= kOwnerNotRoot;␊ |
604 | ␉␉tmpTime = SWAP_BE32(((HFSPlusCatalogFile *)entry)->contentModDate);␊ |
605 | ␉␉if (finderInfo)␊ |
606 | ␉␉{␊ |
607 | ␉␉␉SwapFinderInfo((FndrFileInfo *)finderInfo, &((HFSPlusCatalogFile *)entry)->userInfo);␊ |
608 | ␉␉␉valid = 1;␊ |
609 | ␉␉}␊ |
610 | ␉␉break;␊ |
611 | ␊ |
612 | ␉case kHFSFileThreadRecord :␊ |
613 | ␉case kHFSPlusFileThreadRecord :␊ |
614 | ␉case kHFSFolderThreadRecord :␊ |
615 | ␉case kHFSPlusFolderThreadRecord :␊ |
616 | ␉␉*flags = kFileTypeUnknown;␊ |
617 | ␉␉tmpTime = 0;␊ |
618 | ␉␉break;␊ |
619 | ␉default:␊ |
620 | ␉␉break;␊ |
621 | ␉}␊ |
622 | ␊ |
623 | ␉if (time != 0)␊ |
624 | ␉{␊ |
625 | ␉␉// Convert base time from 1904 to 1970.␊ |
626 | ␉␉*time = tmpTime - 2082844800;␊ |
627 | ␉}␊ |
628 | ␊ |
629 | ␉if (infoValid)␊ |
630 | ␉{␊ |
631 | ␉␉*infoValid = valid;␊ |
632 | ␉}␊ |
633 | ␊ |
634 | ␉return 0;␊ |
635 | }␊ |
636 | ␊ |
637 | ␊ |
638 | //==============================================================================␊ |
639 | ␊ |
640 | static long ResolvePathToCatalogEntry(char * filePath, long * flags, void * entry, long dirID, long long * dirIndex)␊ |
641 | {␊ |
642 | ␉char␉␉␉*restPath;␊ |
643 | ␉long␉␉␉result, cnt, subFolderID = 0;␊ |
644 | ␉long long␉␉tmpDirIndex;␊ |
645 | ␉HFSPlusCatalogFile␉*hfsPlusFile;␊ |
646 | ␊ |
647 | ␉// Copy the file name to gTempStr␊ |
648 | ␉cnt = 0;␊ |
649 | ␉while ((filePath[cnt] != '/') && (filePath[cnt] != '\0'))␊ |
650 | ␉{␊ |
651 | ␉␉cnt++;␊ |
652 | ␉}␊ |
653 | ␊ |
654 | ␉strlcpy(gTempStr, filePath, cnt+1);␊ |
655 | ␊ |
656 | ␉// Move restPath to the right place.␊ |
657 | ␉if (filePath[cnt] != '\0')␊ |
658 | ␉{␊ |
659 | ␉␉cnt++;␊ |
660 | ␉}␊ |
661 | ␊ |
662 | ␉restPath = filePath + cnt;␊ |
663 | ␊ |
664 | ␉// gTempStr is a name in the current Dir.␊ |
665 | ␉// restPath is the rest of the path if any.␊ |
666 | ␊ |
667 | ␉result = ReadCatalogEntry(gTempStr, dirID, entry, dirIndex);␊ |
668 | ␊ |
669 | ␉if (result == -1)␊ |
670 | ␉{␊ |
671 | ␉␉return -1;␊ |
672 | ␉}␊ |
673 | ␊ |
674 | ␉GetCatalogEntryInfo(entry, flags, 0, 0, 0);␊ |
675 | ␊ |
676 | ␉if ((*flags & kFileTypeMask) == kFileTypeDirectory)␊ |
677 | ␉{␊ |
678 | ␉␉if (gIsHFSPlus)␊ |
679 | ␉␉{␊ |
680 | ␉␉␉subFolderID = SWAP_BE32(((HFSPlusCatalogFolder *)entry)->folderID);␊ |
681 | ␉␉}␊ |
682 | ␉␉else␊ |
683 | ␉␉{␊ |
684 | ␉␉␉subFolderID = SWAP_BE32(((HFSCatalogFolder *)entry)->folderID);␊ |
685 | ␉␉}␊ |
686 | ␉}␊ |
687 | ␊ |
688 | ␉if ((*flags & kFileTypeMask) == kFileTypeDirectory)␊ |
689 | ␉{␊ |
690 | ␉␉result = ResolvePathToCatalogEntry(restPath, flags, entry, subFolderID, dirIndex);␊ |
691 | ␉}␊ |
692 | ␊ |
693 | ␉if (gIsHFSPlus && ((*flags & kFileTypeMask) == kFileTypeFlat))␊ |
694 | ␉{␊ |
695 | ␉␉hfsPlusFile = (HFSPlusCatalogFile *)entry;␊ |
696 | ␊ |
697 | ␉␉if ((SWAP_BE32(hfsPlusFile->userInfo.fdType) == kHardLinkFileType) &&␊ |
698 | ␉␉␉(SWAP_BE32(hfsPlusFile->userInfo.fdCreator) == kHFSPlusCreator))␊ |
699 | ␉␉{␊ |
700 | ␉␉␉sprintf(gLinkTemp, "%s/%s%ld", HFSPLUSMETADATAFOLDER, HFS_INODE_PREFIX, SWAP_BE32(hfsPlusFile->bsdInfo.special.iNodeNum));␊ |
701 | ␉␉␉result = ResolvePathToCatalogEntry(gLinkTemp, flags, entry, kHFSRootFolderID, &tmpDirIndex);␊ |
702 | ␉␉}␊ |
703 | ␉}␊ |
704 | ␊ |
705 | ␉return result;␊ |
706 | }␊ |
707 | ␊ |
708 | ␊ |
709 | //==============================================================================␊ |
710 | ␊ |
711 | static long GetCatalogEntry(long long * dirIndex, char ** name, long * flags, long * time, FinderInfo * finderInfo, long * infoValid)␊ |
712 | {␊ |
713 | ␉long extentSize, nodeSize, curNode, index;␊ |
714 | ␉void *extent;␊ |
715 | ␉char *nodeBuf, *testKey, *entry;␊ |
716 | ␉BTNodeDescriptor *node;␊ |
717 | ␊ |
718 | ␉if (gIsHFSPlus)␊ |
719 | ␉{␊ |
720 | ␉␉extent = &gHFSPlus->catalogFile.extents;␊ |
721 | ␉␉extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);␊ |
722 | ␉}␊ |
723 | ␉else␊ |
724 | ␉{␊ |
725 | ␉␉extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;␊ |
726 | ␉␉extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);␊ |
727 | ␉}␊ |
728 | ␊ |
729 | ␉nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);␊ |
730 | ␉nodeBuf = (char *)malloc(nodeSize);␊ |
731 | ␉node = (BTNodeDescriptor *)nodeBuf;␊ |
732 | ␊ |
733 | index = (long) (*dirIndex % nodeSize);␊ |
734 | curNode = (long) (*dirIndex / nodeSize);␊ |
735 | ␊ |
736 | // Read the BTree node and get the record for index.␊ |
737 | ReadExtent(extent, extentSize, kHFSCatalogFileID,␊ |
738 | (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);␊ |
739 | GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);␊ |
740 | ␊ |
741 | GetCatalogEntryInfo(entry, flags, time, finderInfo, infoValid);␊ |
742 | ␊ |
743 | ␉// Get the file name.␊ |
744 | ␉if (gIsHFSPlus)␊ |
745 | ␉{␊ |
746 | ␉␉utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,␊ |
747 | SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),␊ |
748 | (u_int8_t *)gTempStr, 256, OSBigEndian);␊ |
749 | ␉}␊ |
750 | ␉else␊ |
751 | ␉{␊ |
752 | ␉␉strncpy(gTempStr, (const char *)&((HFSCatalogKey *)testKey)->nodeName[1], ␊ |
753 | ␉␉␉␉((HFSCatalogKey *)testKey)->nodeName[0]);␊ |
754 | ␊ |
755 | ␉␉gTempStr[((HFSCatalogKey *)testKey)->nodeName[0]] = '\0';␊ |
756 | ␉}␊ |
757 | *name = gTempStr;␊ |
758 | ␊ |
759 | // Update dirIndex.␊ |
760 | index++;␊ |
761 | ␊ |
762 | if (index == SWAP_BE16(node->numRecords))␊ |
763 | ␉{␊ |
764 | ␉␉index = 0;␊ |
765 | ␉␉curNode = SWAP_BE32(node->fLink);␊ |
766 | }␊ |
767 | *dirIndex = (long long) curNode * nodeSize + index;␊ |
768 | ␊ |
769 | ␉free(nodeBuf);␊ |
770 | ␊ |
771 | ␉return 0;␊ |
772 | }␊ |
773 | ␊ |
774 | ␊ |
775 | //==============================================================================␊ |
776 | ␊ |
777 | static long ReadCatalogEntry(char * fileName, long dirID, void * entry, long long * dirIndex)␊ |
778 | {␊ |
779 | ␉long length;␊ |
780 | ␉char key[sizeof(HFSPlusCatalogKey)];␊ |
781 | ␉HFSCatalogKey *hfsKey = (HFSCatalogKey *)key;␊ |
782 | ␉HFSPlusCatalogKey *hfsPlusKey = (HFSPlusCatalogKey *)key;␊ |
783 | ␊ |
784 | ␉// Make the catalog key.␊ |
785 | ␉if (gIsHFSPlus)␊ |
786 | ␉{␊ |
787 | ␉␉hfsPlusKey->parentID = SWAP_BE32(dirID);␊ |
788 | ␉␉length = strlen(fileName);␊ |
789 | ␊ |
790 | ␉␉if (length > 255)␊ |
791 | ␉␉{␊ |
792 | ␉␉␉length = 255;␊ |
793 | ␉␉}␊ |
794 | ␊ |
795 | ␉␉utf_decodestr((u_int8_t *)fileName, hfsPlusKey->nodeName.unicode, &(hfsPlusKey->nodeName.length), 512, OSBigEndian);␊ |
796 | ␉}␊ |
797 | ␉else␊ |
798 | ␉{␊ |
799 | ␉␉hfsKey->parentID = SWAP_BE32(dirID);␊ |
800 | ␉␉length = strlen(fileName);␊ |
801 | ␊ |
802 | ␉␉if (length > 31)␊ |
803 | ␉␉{␊ |
804 | ␉␉␉length = 31;␊ |
805 | ␉␉}␊ |
806 | ␊ |
807 | ␉␉hfsKey->nodeName[0] = length;␊ |
808 | ␉␉strncpy((char *)(hfsKey->nodeName + 1), fileName, length);␊ |
809 | ␉}␊ |
810 | ␊ |
811 | ␉return ReadBTreeEntry(kBTreeCatalog, &key, entry, dirIndex);␊ |
812 | }␊ |
813 | ␊ |
814 | ␊ |
815 | //==============================================================================␊ |
816 | ␊ |
817 | static long ReadExtentsEntry(long fileID, long startBlock, void * entry)␊ |
818 | {␊ |
819 | ␉char key[sizeof(HFSPlusExtentKey)];␊ |
820 | ␉HFSExtentKey *hfsKey = (HFSExtentKey *)key;␊ |
821 | ␉HFSPlusExtentKey *hfsPlusKey = (HFSPlusExtentKey *)key;␊ |
822 | ␊ |
823 | ␉// Make the extents key.␊ |
824 | ␉if (gIsHFSPlus)␊ |
825 | ␉{␊ |
826 | ␉␉hfsPlusKey->forkType = 0;␊ |
827 | ␉␉hfsPlusKey->fileID = SWAP_BE32(fileID);␊ |
828 | ␉␉hfsPlusKey->startBlock = SWAP_BE32(startBlock);␊ |
829 | ␉}␊ |
830 | ␉else␊ |
831 | ␉{␊ |
832 | ␉␉hfsKey->forkType = 0;␊ |
833 | ␉␉hfsKey->fileID = SWAP_BE32(fileID);␊ |
834 | ␉␉hfsKey->startBlock = SWAP_BE16(startBlock);␊ |
835 | ␉}␊ |
836 | ␊ |
837 | ␉return ReadBTreeEntry(kBTreeExtents, &key, entry, 0);␊ |
838 | }␊ |
839 | ␊ |
840 | ␊ |
841 | //==============================================================================␊ |
842 | ␊ |
843 | static long ReadBTreeEntry(long btree, void * key, char * entry, long long * dirIndex)␊ |
844 | {␊ |
845 | ␉long extentSize;␊ |
846 | ␉void *extent;␊ |
847 | ␉short extentFile;␊ |
848 | ␉char *nodeBuf;␊ |
849 | ␉BTNodeDescriptor *node;␊ |
850 | ␉long nodeSize, result = 0, entrySize = 0;␊ |
851 | ␉long curNode, index = 0, lowerBound, upperBound;␊ |
852 | ␉char *testKey, *recordData =0;␊ |
853 | ␊ |
854 | ␉// Figure out which tree is being looked at.␊ |
855 | ␉if (btree == kBTreeCatalog)␊ |
856 | ␉{␊ |
857 | ␉␉if (gIsHFSPlus) ␊ |
858 | ␉␉{␊ |
859 | ␉␉␉extent = &gHFSPlus->catalogFile.extents;␊ |
860 | ␉␉␉extentSize = SWAP_BE64(gHFSPlus->catalogFile.logicalSize);␊ |
861 | ␉␉}␊ |
862 | ␉␉else␊ |
863 | ␉␉{␊ |
864 | ␉␉␉extent = (HFSExtentDescriptor *)&gHFSMDB->drCTExtRec;␊ |
865 | ␉␉␉extentSize = SWAP_BE32(gHFSMDB->drCTFlSize);␊ |
866 | ␉␉}␊ |
867 | ␉␉extentFile = kHFSCatalogFileID;␊ |
868 | ␉}␊ |
869 | ␉else␊ |
870 | ␉{␊ |
871 | ␉␉if (gIsHFSPlus)␊ |
872 | ␉␉{␊ |
873 | ␉␉␉extent = &gHFSPlus->extentsFile.extents;␊ |
874 | ␉␉␉extentSize = SWAP_BE64(gHFSPlus->extentsFile.logicalSize);␊ |
875 | ␉␉}␊ |
876 | ␉␉else␊ |
877 | ␉␉{␊ |
878 | ␉␉␉extent = (HFSExtentDescriptor *)&gHFSMDB->drXTExtRec;␊ |
879 | ␉␉␉extentSize = SWAP_BE32(gHFSMDB->drXTFlSize);␊ |
880 | ␉␉}␊ |
881 | ␉␉extentFile = kHFSExtentsFileID;␊ |
882 | ␉}␊ |
883 | ␊ |
884 | ␉// Read the BTree Header if needed.␊ |
885 | ␉if (gBTHeaders[btree] == 0)␊ |
886 | ␉{␊ |
887 | ␉␉ReadExtent(extent, extentSize, extentFile, 0, 256, gBTreeHeaderBuffer + btree * 256, 0);␊ |
888 | ␉␉gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 + sizeof(BTNodeDescriptor));␊ |
889 | ␊ |
890 | ␉␉if ((gIsHFSPlus && btree == kBTreeCatalog) && (gBTHeaders[btree]->keyCompareType == kHFSBinaryCompare))␊ |
891 | ␉␉{␊ |
892 | ␉␉␉gCaseSensitive = 1;␊ |
893 | ␉␉}␊ |
894 | ␉}␊ |
895 | ␊ |
896 | ␉curNode = SWAP_BE32(gBTHeaders[btree]->rootNode);␊ |
897 | ␉nodeSize = SWAP_BE16(gBTHeaders[btree]->nodeSize);␊ |
898 | ␉nodeBuf = (char *)malloc(nodeSize);␊ |
899 | ␉node = (BTNodeDescriptor *)nodeBuf;␊ |
900 | ␊ |
901 | ␉while (1)␊ |
902 | ␉{␊ |
903 | ␉␉// Read the current node.␊ |
904 | ␉ReadExtent(extent, extentSize, extentFile, (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);␊ |
905 | ␊ |
906 | ␉␉// Find the matching key.␊ |
907 | ␉␉lowerBound = 0;␊ |
908 | ␉␉upperBound = SWAP_BE16(node->numRecords) - 1;␊ |
909 | ␊ |
910 | ␉␉while (lowerBound <= upperBound)␊ |
911 | ␉␉{␊ |
912 | ␉␉␉index = (lowerBound + upperBound) / 2;␊ |
913 | ␊ |
914 | ␉␉␉GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);␊ |
915 | ␊ |
916 | ␉␉␉if (gIsHFSPlus)␊ |
917 | ␉␉␉{␊ |
918 | ␉␉␉␉if (btree == kBTreeCatalog)␊ |
919 | ␉␉␉␉{␊ |
920 | ␉␉␉␉␉result = CompareHFSPlusCatalogKeys(key, testKey);␊ |
921 | ␉␉␉␉}␊ |
922 | ␉␉␉␉else␊ |
923 | ␉␉␉␉{␊ |
924 | ␉␉␉␉␉result = CompareHFSPlusExtentsKeys(key, testKey);␊ |
925 | ␉␉␉␉}␊ |
926 | ␉␉␉}␊ |
927 | ␉␉␉else␊ |
928 | ␉␉␉{␊ |
929 | ␉␉␉␉if (btree == kBTreeCatalog)␊ |
930 | ␉␉␉␉{␊ |
931 | ␉␉␉␉␉result = CompareHFSCatalogKeys(key, testKey);␊ |
932 | ␉␉␉␉}␊ |
933 | ␉␉␉␉else␊ |
934 | ␉␉␉␉{␊ |
935 | ␉␉␉␉␉result = CompareHFSExtentsKeys(key, testKey);␊ |
936 | ␉␉␉␉}␊ |
937 | ␉␉␉}␊ |
938 | ␊ |
939 | ␉␉␉if (result < 0)␊ |
940 | ␉␉␉{␊ |
941 | ␉␉␉␉upperBound = index - 1;␉// search < trial␊ |
942 | ␉␉␉}␊ |
943 | ␉␉␉else if (result > 0)␊ |
944 | ␉␉␉{␊ |
945 | ␉␉␉␉lowerBound = index + 1;␉// search > trial␊ |
946 | ␉␉␉}␊ |
947 | ␉␉␉else␊ |
948 | ␉␉␉{␊ |
949 | ␉␉␉␉break;␉// search = trial␊ |
950 | ␉␉␉}␊ |
951 | ␉␉}␊ |
952 | ␊ |
953 | ␉␉if (result < 0)␊ |
954 | ␉␉{␊ |
955 | ␉␉␉index = upperBound;␊ |
956 | ␉␉␉GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &recordData);␊ |
957 | ␉␉}␊ |
958 | ␊ |
959 | ␉␉// Found the closest key... Recurse on it if this is an index node.␊ |
960 | ␉␉if (node->kind == kBTIndexNode)␊ |
961 | ␉␉{␊ |
962 | ␉␉␉curNode = SWAP_BE32( *((long *)recordData) );␊ |
963 | ␉␉}␊ |
964 | ␉␉else␊ |
965 | ␉␉{␊ |
966 | ␉␉␉break;␊ |
967 | ␉␉}␊ |
968 | ␉}␊ |
969 | ␊ |
970 | ␉// Return error if the file was not found.␊ |
971 | ␉if (result != 0)␊ |
972 | ␉{␊ |
973 | ␉␉free(nodeBuf);␊ |
974 | ␉␉return -1;␊ |
975 | ␉}␊ |
976 | ␊ |
977 | ␉if (btree == kBTreeCatalog)␊ |
978 | ␉{␊ |
979 | ␉␉switch (SWAP_BE16(*(short *)recordData))␊ |
980 | ␉␉{␊ |
981 | ␉␉␉case kHFSFolderRecord : entrySize = 70;␊ |
982 | ␉␉␉␉break;␊ |
983 | ␉␉␉case kHFSFileRecord : entrySize = 102;␊ |
984 | ␉␉␉␉break;␊ |
985 | ␉␉␉case kHFSFolderThreadRecord : entrySize = 46;␊ |
986 | ␉␉␉␉break;␊ |
987 | ␉␉␉case kHFSFileThreadRecord : entrySize = 46;␊ |
988 | ␉␉␉␉break;␊ |
989 | ␉␉␉case kHFSPlusFolderRecord : entrySize = 88;␊ |
990 | ␉␉␉␉break;␊ |
991 | ␉␉␉case kHFSPlusFileRecord : entrySize = 248;␊ |
992 | ␉␉␉␉break;␊ |
993 | ␉␉␉case kHFSPlusFolderThreadRecord : entrySize = 264;␊ |
994 | ␉␉␉␉break;␊ |
995 | ␉␉␉case kHFSPlusFileThreadRecord : entrySize = 264;␊ |
996 | ␉␉␉␉break;␊ |
997 | ␉␉}␊ |
998 | ␉}␊ |
999 | ␉else␊ |
1000 | ␉{␊ |
1001 | ␉␉if (gIsHFSPlus)␊ |
1002 | ␉␉{␊ |
1003 | ␉␉␉entrySize = sizeof(HFSPlusExtentRecord);␊ |
1004 | ␉␉}␊ |
1005 | ␉␉else␊ |
1006 | ␉␉{␊ |
1007 | ␉␉␉entrySize = sizeof(HFSExtentRecord);␊ |
1008 | ␉␉}␊ |
1009 | ␉}␊ |
1010 | ␊ |
1011 | ␉bcopy(recordData, entry, entrySize);␊ |
1012 | ␊ |
1013 | ␉// Update dirIndex.␊ |
1014 | ␉if (dirIndex != 0)␊ |
1015 | ␉{␊ |
1016 | ␉␉index++;␊ |
1017 | ␉␉if (index == SWAP_BE16(node->numRecords))␊ |
1018 | ␉␉{␊ |
1019 | ␉␉␉index = 0;␊ |
1020 | ␉␉␉curNode = SWAP_BE32(node->fLink);␊ |
1021 | ␉␉}␊ |
1022 | ␉␉*dirIndex = (long long) curNode * nodeSize + index;␊ |
1023 | ␉}␊ |
1024 | ␊ |
1025 | ␉free(nodeBuf);␊ |
1026 | ␊ |
1027 | ␉return 0;␊ |
1028 | }␊ |
1029 | ␊ |
1030 | ␊ |
1031 | //==============================================================================␊ |
1032 | ␊ |
1033 | static void GetBTreeRecord(long index, char * nodeBuffer, long nodeSize, char ** key, char ** data)␊ |
1034 | {␊ |
1035 | ␉long keySize;␊ |
1036 | ␉long recordOffset;␊ |
1037 | ␊ |
1038 | ␉recordOffset = SWAP_BE16(*((short *)(nodeBuffer + (nodeSize - 2 * index - 2))));␊ |
1039 | ␉*key = nodeBuffer + recordOffset;␊ |
1040 | ␉if (gIsHFSPlus)␊ |
1041 | ␉{␊ |
1042 | ␉␉keySize = SWAP_BE16(*(short *)*key);␊ |
1043 | ␉␉*data = *key + 2 + keySize;␊ |
1044 | ␉}␊ |
1045 | ␉else␊ |
1046 | ␉{␊ |
1047 | ␉␉keySize = **key;␊ |
1048 | ␉␉*data = *key + 2 + keySize - (keySize & 1);␊ |
1049 | ␉}␊ |
1050 | }␊ |
1051 | ␊ |
1052 | ␊ |
1053 | //==============================================================================␊ |
1054 | ␊ |
1055 | static long ReadExtent(char * extent, uint64_t extentSize, long extentFile, uint64_t offset, uint64_t size, void * buffer, long cache)␊ |
1056 | {␊ |
1057 | ␉uint64_t lastOffset;␊ |
1058 | ␉long long blockNumber, countedBlocks = 0;␊ |
1059 | ␉long long nextExtent = 0, sizeRead = 0, readSize;␊ |
1060 | ␉long long nextExtentBlock, currentExtentBlock = 0;␊ |
1061 | ␉long long readOffset;␊ |
1062 | ␉long long extentDensity, sizeofExtent, currentExtentSize;␊ |
1063 | ␉char *currentExtent, *extentBuffer = 0, *bufferPos = buffer;␊ |
1064 | ␊ |
1065 | ␉if (offset >= extentSize)␊ |
1066 | ␉{␊ |
1067 | ␉␉return 0;␊ |
1068 | ␉}␊ |
1069 | ␊ |
1070 | ␉if (gIsHFSPlus)␊ |
1071 | ␉{␊ |
1072 | ␉␉extentDensity = kHFSPlusExtentDensity;␊ |
1073 | ␉␉sizeofExtent = sizeof(HFSPlusExtentDescriptor);␊ |
1074 | ␉}␊ |
1075 | ␉else␊ |
1076 | ␉{␊ |
1077 | ␉␉extentDensity = kHFSExtentDensity;␊ |
1078 | ␉␉sizeofExtent = sizeof(HFSExtentDescriptor);␊ |
1079 | ␉}␊ |
1080 | ␊ |
1081 | ␉lastOffset = offset + size;␊ |
1082 | ␊ |
1083 | ␉while (offset < lastOffset)␊ |
1084 | ␉{␊ |
1085 | ␉␉blockNumber = offset / gBlockSize;␊ |
1086 | ␊ |
1087 | ␉␉// Find the extent for the offset.␊ |
1088 | ␉␉for (; ; nextExtent++)␊ |
1089 | ␉␉{␊ |
1090 | ␉␉␉if (nextExtent < extentDensity)␊ |
1091 | ␉␉␉{␊ |
1092 | ␉␉␉␉if ((countedBlocks+GetExtentSize(extent, nextExtent)-1)<blockNumber)␊ |
1093 | ␉␉␉␉{␊ |
1094 | ␉␉␉␉␉countedBlocks += GetExtentSize(extent, nextExtent);␊ |
1095 | ␉␉␉␉␉continue;␊ |
1096 | ␉␉␉␉}␊ |
1097 | ␊ |
1098 | ␉␉␉␉currentExtent = extent + nextExtent * sizeofExtent;␊ |
1099 | ␉␉␉␉break;␊ |
1100 | ␉␉␉}␊ |
1101 | ␊ |
1102 | ␉␉␉if (extentBuffer == 0)␊ |
1103 | ␉␉␉{␊ |
1104 | ␉␉␉␉extentBuffer = malloc(sizeofExtent * extentDensity);␊ |
1105 | ␊ |
1106 | ␉␉␉␉if (extentBuffer == 0)␊ |
1107 | ␉␉␉␉{␊ |
1108 | ␉␉␉␉␉return -1;␊ |
1109 | ␉␉␉␉}␊ |
1110 | ␉␉␉}␊ |
1111 | ␊ |
1112 | ␉␉␉nextExtentBlock = nextExtent / extentDensity;␊ |
1113 | ␊ |
1114 | ␉␉␉if (currentExtentBlock != nextExtentBlock)␊ |
1115 | ␉␉␉{␊ |
1116 | ␉␉␉␉ReadExtentsEntry(extentFile, countedBlocks, extentBuffer);␊ |
1117 | ␉␉␉␉currentExtentBlock = nextExtentBlock;␊ |
1118 | ␉␉␉}␊ |
1119 | ␊ |
1120 | ␉␉␉currentExtentSize = GetExtentSize(extentBuffer, nextExtent % extentDensity);␊ |
1121 | ␊ |
1122 | ␉␉␉if ((countedBlocks + currentExtentSize - 1) >= blockNumber)␊ |
1123 | ␉␉␉{␊ |
1124 | ␉␉␉␉currentExtent = extentBuffer + sizeofExtent * (nextExtent % extentDensity);␊ |
1125 | ␉␉␉␉break;␊ |
1126 | ␉␉␉}␊ |
1127 | ␊ |
1128 | ␉␉␉countedBlocks += currentExtentSize;␊ |
1129 | ␉␉}␊ |
1130 | ␊ |
1131 | ␉␉readOffset = ((blockNumber - countedBlocks) * gBlockSize) + (offset % gBlockSize);␊ |
1132 | ␊ |
1133 | ␉␉// MacWen: fix overflow in multiplication by forcing 64bit multiplication␊ |
1134 | ␉␉readSize = (long long)GetExtentSize(currentExtent, 0) * gBlockSize - readOffset;␊ |
1135 | ␉␉if (readSize > (size - sizeRead))␊ |
1136 | ␉␉{␊ |
1137 | ␉␉␉readSize = size - sizeRead;␊ |
1138 | ␉␉}␊ |
1139 | ␊ |
1140 | ␉␉readOffset += (long long)GetExtentStart(currentExtent, 0) * gBlockSize;␊ |
1141 | ␊ |
1142 | ␉␉CacheRead(gCurrentIH, bufferPos, gAllocationOffset + readOffset, readSize, cache);␊ |
1143 | ␊ |
1144 | ␉␉sizeRead += readSize;␊ |
1145 | ␉␉offset += readSize;␊ |
1146 | ␉␉bufferPos += readSize;␊ |
1147 | ␉}␊ |
1148 | ␊ |
1149 | ␉if (extentBuffer)␊ |
1150 | ␉{␊ |
1151 | ␉␉free(extentBuffer);␊ |
1152 | ␉}␊ |
1153 | ␊ |
1154 | ␉return sizeRead;␊ |
1155 | }␊ |
1156 | ␊ |
1157 | ␊ |
1158 | //==============================================================================␊ |
1159 | ␊ |
1160 | static long GetExtentStart(void * extents, long index)␊ |
1161 | {␊ |
1162 | ␉long start;␊ |
1163 | ␊ |
1164 | ␉HFSExtentDescriptor␉␉*hfsExtents␉␉= extents;␊ |
1165 | ␉HFSPlusExtentDescriptor␉*hfsPlusExtents␉= extents;␊ |
1166 | ␊ |
1167 | ␉if (gIsHFSPlus)␊ |
1168 | ␉{␊ |
1169 | ␉␉start = SWAP_BE32(hfsPlusExtents[index].startBlock);␊ |
1170 | ␉}␊ |
1171 | ␉else␊ |
1172 | ␉{␊ |
1173 | ␉␉start = SWAP_BE16(hfsExtents[index].startBlock);␊ |
1174 | ␉}␊ |
1175 | ␊ |
1176 | ␉return start;␊ |
1177 | }␊ |
1178 | ␊ |
1179 | ␊ |
1180 | //==============================================================================␊ |
1181 | ␊ |
1182 | static long GetExtentSize(void * extents, long index)␊ |
1183 | {␊ |
1184 | ␉long size;␊ |
1185 | ␉HFSExtentDescriptor *hfsExtents = extents;␊ |
1186 | ␉HFSPlusExtentDescriptor *hfsPlusExtents = extents;␊ |
1187 | ␊ |
1188 | ␉if (gIsHFSPlus)␊ |
1189 | ␉{␊ |
1190 | ␉␉size = SWAP_BE32(hfsPlusExtents[index].blockCount);␊ |
1191 | ␉}␊ |
1192 | ␉else␊ |
1193 | ␉{␊ |
1194 | ␉␉size = SWAP_BE16(hfsExtents[index].blockCount);␊ |
1195 | ␉}␊ |
1196 | ␊ |
1197 | ␉return size;␊ |
1198 | }␊ |
1199 | ␊ |
1200 | ␊ |
1201 | //==============================================================================␊ |
1202 | ␊ |
1203 | static long CompareHFSCatalogKeys(void * key, void * testKey)␊ |
1204 | {␊ |
1205 | ␉HFSCatalogKey *searchKey, *trialKey;␊ |
1206 | ␉long result, searchParentID, trialParentID;␊ |
1207 | ␊ |
1208 | ␉searchKey = key;␊ |
1209 | ␉trialKey = testKey;␊ |
1210 | ␊ |
1211 | ␉searchParentID = SWAP_BE32(searchKey->parentID);␊ |
1212 | ␉trialParentID = SWAP_BE32(trialKey->parentID);␊ |
1213 | ␊ |
1214 | ␉// parent dirID is unsigned␊ |
1215 | ␉if (searchParentID > trialParentID)␊ |
1216 | ␉{␊ |
1217 | ␉␉result = 1;␊ |
1218 | ␉}␊ |
1219 | ␉else if (searchParentID < trialParentID)␊ |
1220 | ␉{␊ |
1221 | ␉␉result = -1;␊ |
1222 | ␉}␊ |
1223 | ␉else␊ |
1224 | ␉{␊ |
1225 | ␉␉// parent dirID's are equal, compare names␊ |
1226 | ␉␉result = FastRelString(searchKey->nodeName, trialKey->nodeName);␊ |
1227 | ␉}␊ |
1228 | ␊ |
1229 | ␉return result;␊ |
1230 | }␊ |
1231 | ␊ |
1232 | ␊ |
1233 | //==============================================================================␊ |
1234 | ␊ |
1235 | static long CompareHFSPlusCatalogKeys(void * key, void * testKey)␊ |
1236 | {␊ |
1237 | ␉HFSPlusCatalogKey *searchKey, *trialKey;␊ |
1238 | ␉long result, searchParentID, trialParentID;␊ |
1239 | ␊ |
1240 | ␉searchKey = key;␊ |
1241 | ␉trialKey = testKey;␊ |
1242 | ␊ |
1243 | ␉searchParentID = SWAP_BE32(searchKey->parentID);␊ |
1244 | ␉trialParentID = SWAP_BE32(trialKey->parentID);␊ |
1245 | ␊ |
1246 | ␉// parent dirID is unsigned␊ |
1247 | ␉if (searchParentID > trialParentID)␊ |
1248 | ␉{␊ |
1249 | ␉␉result = 1;␊ |
1250 | ␉}␊ |
1251 | ␉else if (searchParentID < trialParentID) ␊ |
1252 | ␉{␊ |
1253 | ␉␉result = -1;␊ |
1254 | ␉}␊ |
1255 | ␉else␊ |
1256 | ␉{␊ |
1257 | ␉␉// parent dirID's are equal, compare names␊ |
1258 | ␉␉if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))␊ |
1259 | ␉␉{␊ |
1260 | ␉␉␉result = searchKey->nodeName.length - trialKey->nodeName.length;␊ |
1261 | ␉␉}␊ |
1262 | ␉␉else if (gCaseSensitive)␊ |
1263 | ␉␉{␊ |
1264 | ␉␉␉result = BinaryUnicodeCompare(&searchKey->nodeName.unicode[0],␊ |
1265 | SWAP_BE16(searchKey->nodeName.length),␊ |
1266 | &trialKey->nodeName.unicode[0],␊ |
1267 | SWAP_BE16(trialKey->nodeName.length));␊ |
1268 | ␉␉}␊ |
1269 | ␉␉else␊ |
1270 | ␉␉{␊ |
1271 | ␉␉␉result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],␊ |
1272 | SWAP_BE16(searchKey->nodeName.length),␊ |
1273 | &trialKey->nodeName.unicode[0],␊ |
1274 | SWAP_BE16(trialKey->nodeName.length), OSBigEndian);␊ |
1275 | ␉␉}␊ |
1276 | ␉}␊ |
1277 | ␊ |
1278 | ␉return result;␊ |
1279 | }␊ |
1280 | ␊ |
1281 | ␊ |
1282 | //==============================================================================␊ |
1283 | ␊ |
1284 | static long CompareHFSExtentsKeys(void * key, void * testKey)␊ |
1285 | {␊ |
1286 | ␉HFSExtentKey *searchKey, *trialKey;␊ |
1287 | ␉long result;␊ |
1288 | ␊ |
1289 | ␉searchKey = key;␊ |
1290 | ␉trialKey = testKey;␊ |
1291 | ␊ |
1292 | ␉// assume searchKey < trialKey␊ |
1293 | ␉result = -1; ␊ |
1294 | ␊ |
1295 | ␉if (searchKey->fileID == trialKey->fileID)␊ |
1296 | ␉{␊ |
1297 | ␉␉// FileNum's are equal; compare fork types␊ |
1298 | ␉␉if (searchKey->forkType == trialKey->forkType)␊ |
1299 | ␉␉{␊ |
1300 | ␉␉␉// Fork types are equal; compare allocation block number␊ |
1301 | ␉␉␉if (searchKey->startBlock == trialKey->startBlock)␊ |
1302 | ␉␉␉{␊ |
1303 | ␉␉␉␉// Everything is equal␊ |
1304 | ␉␉␉␉result = 0;␊ |
1305 | ␉␉␉}␊ |
1306 | ␉␉␉else␊ |
1307 | ␉␉␉{␊ |
1308 | ␉␉␉␉// Allocation block numbers differ; determine sign␊ |
1309 | ␉␉␉␉if (SWAP_BE16(searchKey->startBlock) > SWAP_BE16(trialKey->startBlock))␊ |
1310 | ␉␉␉␉{␊ |
1311 | ␉␉␉␉␉result = 1;␊ |
1312 | ␉␉␉␉}␊ |
1313 | ␉␉␉}␊ |
1314 | ␉}␊ |
1315 | ␉␉else␊ |
1316 | ␉␉{␊ |
1317 | ␉␉␉// Fork types differ; determine sign␊ |
1318 | ␉␉␉if (searchKey->forkType > trialKey->forkType)␊ |
1319 | ␉␉␉{␊ |
1320 | ␉␉␉␉result = 1;␊ |
1321 | ␉␉␉}␊ |
1322 | ␉␉}␊ |
1323 | ␉}␊ |
1324 | ␉else␊ |
1325 | ␉{␊ |
1326 | ␉␉// FileNums differ; determine sign␊ |
1327 | ␉␉if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))␊ |
1328 | ␉␉{␊ |
1329 | ␉␉␉result = 1;␊ |
1330 | ␉␉}␊ |
1331 | ␉}␊ |
1332 | ␊ |
1333 | ␉return result;␊ |
1334 | }␊ |
1335 | ␊ |
1336 | ␊ |
1337 | //==============================================================================␊ |
1338 | ␊ |
1339 | static long CompareHFSPlusExtentsKeys(void * key, void * testKey)␊ |
1340 | {␊ |
1341 | ␉HFSPlusExtentKey *searchKey, *trialKey;␊ |
1342 | ␊ |
1343 | ␉long result = -1; // assume searchKey < trialKey␊ |
1344 | ␊ |
1345 | ␉searchKey = key;␊ |
1346 | ␉trialKey = testKey;␊ |
1347 | ␊ |
1348 | ␉if (searchKey->fileID == trialKey->fileID)␊ |
1349 | ␉{␊ |
1350 | ␉␉// FileNum's are equal; compare fork types␊ |
1351 | ␉␉if (searchKey->forkType == trialKey->forkType)␊ |
1352 | ␉␉{␊ |
1353 | ␉␉␉// Fork types are equal; compare allocation block number␊ |
1354 | ␉␉␉if (searchKey->startBlock == trialKey->startBlock)␊ |
1355 | ␉␉␉{␊ |
1356 | ␉␉␉␉// Everything is equal␊ |
1357 | ␉␉␉␉result = 0;␊ |
1358 | ␉␉␉}␊ |
1359 | ␉␉␉else␊ |
1360 | ␉␉␉{␊ |
1361 | ␉␉␉␉// Allocation block numbers differ; determine sign␊ |
1362 | ␉␉␉␉if (SWAP_BE32(searchKey->startBlock) > SWAP_BE32(trialKey->startBlock))␊ |
1363 | ␉␉␉␉{␊ |
1364 | ␉␉␉␉␉result = 1;␊ |
1365 | ␉␉␉␉}␊ |
1366 | ␉␉␉}␊ |
1367 | ␉␉}␊ |
1368 | ␉␉else␊ |
1369 | ␉␉{␊ |
1370 | ␉␉␉// Fork types differ; determine sign␊ |
1371 | ␉␉␉if (searchKey->forkType > trialKey->forkType)␊ |
1372 | ␉␉␉{␊ |
1373 | ␉␉␉␉result = 1;␊ |
1374 | ␉␉␉}␊ |
1375 | ␉␉}␊ |
1376 | ␉}␊ |
1377 | ␉else␊ |
1378 | ␉{␊ |
1379 | ␉␉// FileNums differ; determine sign␊ |
1380 | ␉␉if (SWAP_BE32(searchKey->fileID) > SWAP_BE32(trialKey->fileID))␊ |
1381 | ␉␉{␊ |
1382 | ␉␉␉result = 1;␊ |
1383 | ␉␉}␊ |
1384 | ␉}␊ |
1385 | ␊ |
1386 | ␉return result;␊ |
1387 | }␊ |
1388 | ␊ |
1389 | |