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