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