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