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