]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (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. | |
1c79356b | 11 | * |
e5568f75 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
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. | |
1c79356b A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
23 | /* | |
24 | * hfs_endian.c | |
25 | * | |
26 | * This file implements endian swapping routines for the HFS/HFS Plus | |
27 | * volume format. | |
28 | */ | |
29 | ||
30 | #include <architecture/byte_order.h> | |
31 | ||
32 | #include "hfs_endian.h" | |
33 | #include "hfs_dbg.h" | |
3a60a9f5 | 34 | #include "hfscommon/headers/BTreesPrivate.h" |
1c79356b A |
35 | |
36 | #undef ENDIAN_DEBUG | |
37 | ||
3a60a9f5 A |
38 | /* |
39 | * Internal swapping routines | |
40 | * | |
41 | * These routines handle swapping the records of leaf and index nodes. The | |
42 | * layout of the keys and records varies depending on the kind of B-tree | |
43 | * (determined by fileID). | |
44 | * | |
45 | * The direction parameter must be kSwapBTNodeBigToHost or kSwapBTNodeHostToBig. | |
46 | * The kSwapBTNodeHeaderRecordOnly "direction" is not valid for these routines. | |
47 | */ | |
48 | static int hfs_swap_HFSPlusBTInternalNode (BlockDescriptor *src, HFSCatalogNodeID fileID, enum HFSBTSwapDirection direction); | |
49 | static int hfs_swap_HFSBTInternalNode (BlockDescriptor *src, HFSCatalogNodeID fileID, enum HFSBTSwapDirection direction); | |
1c79356b A |
50 | |
51 | /* | |
52 | * hfs_swap_HFSPlusForkData | |
1c79356b | 53 | */ |
3a60a9f5 | 54 | static void |
1c79356b A |
55 | hfs_swap_HFSPlusForkData ( |
56 | HFSPlusForkData *src | |
57 | ) | |
58 | { | |
59 | int i; | |
60 | ||
1c79356b A |
61 | src->logicalSize = SWAP_BE64 (src->logicalSize); |
62 | ||
63 | src->clumpSize = SWAP_BE32 (src->clumpSize); | |
64 | src->totalBlocks = SWAP_BE32 (src->totalBlocks); | |
65 | ||
66 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
67 | src->extents[i].startBlock = SWAP_BE32 (src->extents[i].startBlock); | |
68 | src->extents[i].blockCount = SWAP_BE32 (src->extents[i].blockCount); | |
69 | } | |
70 | } | |
71 | ||
72 | /* | |
73 | * hfs_swap_BTNode | |
74 | * | |
75 | * NOTE: This operation is not naturally symmetric. | |
76 | * We have to determine which way we're swapping things. | |
77 | */ | |
78 | int | |
79 | hfs_swap_BTNode ( | |
80 | BlockDescriptor *src, | |
3a60a9f5 A |
81 | vnode_t vp, |
82 | enum HFSBTSwapDirection direction | |
1c79356b A |
83 | ) |
84 | { | |
85 | BTNodeDescriptor *srcDesc = src->buffer; | |
86 | UInt16 *srcOffs = NULL; | |
3a60a9f5 | 87 | BTreeControlBlockPtr btcb = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr; |
1c79356b A |
88 | UInt32 i; |
89 | int error = 0; | |
90 | ||
1c79356b | 91 | #ifdef ENDIAN_DEBUG |
3a60a9f5 A |
92 | if (direction == kSwapBTNodeBigToHost) { |
93 | printf ("BE -> Native Swap\n"); | |
94 | } else if (direction == kSwapBTNodeHostToBig) { | |
95 | printf ("Native -> BE Swap\n"); | |
96 | } else if (direction == kSwapBTNodeHeaderRecordOnly) { | |
1c79356b A |
97 | printf ("Not swapping descriptors\n"); |
98 | } else { | |
3a60a9f5 | 99 | panic ("hfs_swap_BTNode: This is impossible"); |
1c79356b A |
100 | } |
101 | #endif | |
102 | ||
3a60a9f5 A |
103 | /* |
104 | * If we are doing a swap from on-disk to in-memory, then swap the node | |
105 | * descriptor and record offsets before we need to use them. | |
106 | */ | |
107 | if (direction == kSwapBTNodeBigToHost) { | |
1c79356b A |
108 | srcDesc->fLink = SWAP_BE32 (srcDesc->fLink); |
109 | srcDesc->bLink = SWAP_BE32 (srcDesc->bLink); | |
110 | ||
3a60a9f5 A |
111 | /* |
112 | * When first opening a BTree, we have to read the header node before the | |
113 | * control block is initialized. In this case, totalNodes will be zero, | |
114 | * so skip the bounds checking. | |
115 | */ | |
116 | if (btcb->totalNodes != 0) { | |
117 | if (srcDesc->fLink >= btcb->totalNodes) { | |
118 | printf("hfs_swap_BTNode: invalid forward link (0x%08X)\n", srcDesc->fLink); | |
119 | error = fsBTInvalidHeaderErr; | |
120 | goto fail; | |
121 | } | |
122 | if (srcDesc->bLink >= btcb->totalNodes) { | |
123 | printf("hfs_swap_BTNode: invalid backward link (0x%08X)\n", srcDesc->bLink); | |
124 | error = fsBTInvalidHeaderErr; | |
125 | goto fail; | |
126 | } | |
127 | } | |
128 | ||
129 | /* | |
130 | * Check srcDesc->kind. Don't swap it because it's only one byte. | |
131 | */ | |
132 | if (srcDesc->kind < kBTLeafNode || srcDesc->kind > kBTMapNode) { | |
133 | printf("hfs_swap_BTNode: invalid node kind (%d)\n", srcDesc->kind); | |
134 | error = fsBTInvalidHeaderErr; | |
135 | goto fail; | |
136 | } | |
137 | ||
138 | /* | |
139 | * Check srcDesc->height. Don't swap it because it's only one byte. | |
140 | */ | |
141 | if (srcDesc->height > btcb->treeDepth) { | |
142 | printf("hfs_swap_BTNode: invalid node height (%d)\n", srcDesc->height); | |
143 | error = fsBTInvalidHeaderErr; | |
144 | goto fail; | |
145 | } | |
146 | ||
1c79356b A |
147 | /* Don't swap srcDesc->reserved */ |
148 | ||
149 | srcDesc->numRecords = SWAP_BE16 (srcDesc->numRecords); | |
150 | ||
3a60a9f5 A |
151 | /* |
152 | * Swap the node offsets (including the free space one!). | |
153 | */ | |
1c79356b A |
154 | srcOffs = (UInt16 *)((char *)src->buffer + (src->blockSize - ((srcDesc->numRecords + 1) * sizeof (UInt16)))); |
155 | ||
3a60a9f5 A |
156 | /* |
157 | * Sanity check that the record offsets are within the node itself. | |
158 | */ | |
159 | if ((char *)srcOffs > ((char *)src->buffer + src->blockSize) || | |
160 | (char *)srcOffs < ((char *)src->buffer + sizeof(BTNodeDescriptor))) { | |
161 | printf("hfs_swap_BTNode: invalid record count (0x%04X)\n", srcDesc->numRecords); | |
162 | error = fsBTInvalidHeaderErr; | |
163 | goto fail; | |
1c79356b A |
164 | } |
165 | ||
3a60a9f5 A |
166 | /* |
167 | * Swap and sanity check each of the record offsets. | |
168 | */ | |
169 | for (i = 0; i <= srcDesc->numRecords; i++) { | |
1c79356b A |
170 | srcOffs[i] = SWAP_BE16 (srcOffs[i]); |
171 | ||
3a60a9f5 A |
172 | /* |
173 | * Sanity check: must be even, and within the node itself. | |
174 | * | |
175 | * We may be called to swap an unused node, which contains all zeroes. | |
176 | * This is why we allow the record offset to be zero. | |
177 | */ | |
178 | if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) { | |
179 | printf("hfs_swap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
180 | error = fsBTInvalidHeaderErr; | |
181 | goto fail; | |
182 | } | |
183 | ||
184 | /* | |
185 | * Make sure the offsets are strictly increasing. Note that we're looping over | |
186 | * them backwards, hence the order in the comparison. | |
187 | */ | |
188 | if ((i != 0) && (srcOffs[i] >= srcOffs[i-1])) { | |
189 | printf("hfs_swap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", | |
190 | srcDesc->numRecords-i-1, srcDesc->numRecords-i, srcOffs[i], srcOffs[i-1]); | |
191 | error = fsBTInvalidHeaderErr; | |
192 | goto fail; | |
1c79356b A |
193 | } |
194 | } | |
195 | } | |
196 | ||
3a60a9f5 A |
197 | /* |
198 | * Swap the records (ordered by frequency of access) | |
199 | */ | |
1c79356b A |
200 | if ((srcDesc->kind == kBTIndexNode) || |
201 | (srcDesc-> kind == kBTLeafNode)) { | |
202 | ||
3a60a9f5 A |
203 | if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) { |
204 | error = hfs_swap_HFSPlusBTInternalNode (src, VTOC(vp)->c_fileid, direction); | |
1c79356b | 205 | } else { |
3a60a9f5 | 206 | error = hfs_swap_HFSBTInternalNode (src, VTOC(vp)->c_fileid, direction); |
1c79356b A |
207 | } |
208 | ||
3a60a9f5 A |
209 | if (error) goto fail; |
210 | ||
1c79356b A |
211 | } else if (srcDesc-> kind == kBTMapNode) { |
212 | /* Don't swap the bitmaps, they'll be done in the bitmap routines */ | |
213 | ||
1c79356b | 214 | } else if (srcDesc-> kind == kBTHeaderNode) { |
3a60a9f5 A |
215 | /* The header's offset is hard-wired because we cannot trust the offset pointers. */ |
216 | BTHeaderRec *srcHead = (BTHeaderRec *)((char *)src->buffer + sizeof(BTNodeDescriptor)); | |
1c79356b A |
217 | |
218 | srcHead->treeDepth = SWAP_BE16 (srcHead->treeDepth); | |
219 | ||
220 | srcHead->rootNode = SWAP_BE32 (srcHead->rootNode); | |
221 | srcHead->leafRecords = SWAP_BE32 (srcHead->leafRecords); | |
222 | srcHead->firstLeafNode = SWAP_BE32 (srcHead->firstLeafNode); | |
223 | srcHead->lastLeafNode = SWAP_BE32 (srcHead->lastLeafNode); | |
224 | ||
225 | srcHead->nodeSize = SWAP_BE16 (srcHead->nodeSize); | |
226 | srcHead->maxKeyLength = SWAP_BE16 (srcHead->maxKeyLength); | |
227 | ||
228 | srcHead->totalNodes = SWAP_BE32 (srcHead->totalNodes); | |
229 | srcHead->freeNodes = SWAP_BE32 (srcHead->freeNodes); | |
230 | ||
231 | srcHead->clumpSize = SWAP_BE32 (srcHead->clumpSize); | |
232 | srcHead->attributes = SWAP_BE32 (srcHead->attributes); | |
233 | ||
234 | /* Don't swap srcHead->reserved1 */ | |
3a60a9f5 | 235 | /* Don't swap srcHead->btreeType; it's only one byte */ |
1c79356b A |
236 | /* Don't swap srcHead->reserved2 */ |
237 | /* Don't swap srcHead->reserved3 */ | |
238 | /* Don't swap bitmap */ | |
239 | } | |
240 | ||
3a60a9f5 A |
241 | /* |
242 | * If we are doing a swap from in-memory to on-disk, then swap the node | |
243 | * descriptor and record offsets after we're done using them. | |
244 | */ | |
245 | if (direction == kSwapBTNodeHostToBig) { | |
246 | /* | |
247 | * Sanity check and swap the forkward and backward links. | |
248 | */ | |
249 | if (srcDesc->fLink >= btcb->totalNodes) { | |
250 | printf("hfs_UNswap_BTNode: invalid forward link (0x%08X)\n", srcDesc->fLink); | |
251 | error = fsBTInvalidHeaderErr; | |
252 | goto fail; | |
253 | } | |
254 | if (srcDesc->bLink >= btcb->totalNodes) { | |
255 | printf("hfs_UNswap_BTNode: invalid backward link (0x%08X)\n", srcDesc->bLink); | |
256 | error = fsBTInvalidHeaderErr; | |
257 | goto fail; | |
258 | } | |
1c79356b A |
259 | srcDesc->fLink = SWAP_BE32 (srcDesc->fLink); |
260 | srcDesc->bLink = SWAP_BE32 (srcDesc->bLink); | |
261 | ||
3a60a9f5 A |
262 | /* |
263 | * Check srcDesc->kind. Don't swap it because it's only one byte. | |
264 | */ | |
265 | if (srcDesc->kind < kBTLeafNode || srcDesc->kind > kBTMapNode) { | |
266 | printf("hfs_UNswap_BTNode: invalid node kind (%d)\n", srcDesc->kind); | |
267 | error = fsBTInvalidHeaderErr; | |
268 | goto fail; | |
269 | } | |
270 | ||
271 | /* | |
272 | * Check srcDesc->height. Don't swap it because it's only one byte. | |
273 | */ | |
274 | if (srcDesc->height > btcb->treeDepth) { | |
275 | printf("hfs_UNswap_BTNode: invalid node height (%d)\n", srcDesc->height); | |
276 | error = fsBTInvalidHeaderErr; | |
277 | goto fail; | |
278 | } | |
279 | ||
1c79356b A |
280 | /* Don't swap srcDesc->reserved */ |
281 | ||
3a60a9f5 A |
282 | /* |
283 | * Swap the node offsets (including the free space one!). | |
284 | */ | |
1c79356b A |
285 | srcOffs = (UInt16 *)((char *)src->buffer + (src->blockSize - ((srcDesc->numRecords + 1) * sizeof (UInt16)))); |
286 | ||
3a60a9f5 A |
287 | /* |
288 | * Sanity check that the record offsets are within the node itself. | |
289 | */ | |
290 | if ((char *)srcOffs > ((char *)src->buffer + src->blockSize) || | |
291 | (char *)srcOffs < ((char *)src->buffer + sizeof(BTNodeDescriptor))) { | |
292 | printf("hfs_UNswap_BTNode: invalid record count (0x%04X)\n", srcDesc->numRecords); | |
293 | error = fsBTInvalidHeaderErr; | |
294 | goto fail; | |
1c79356b A |
295 | } |
296 | ||
3a60a9f5 A |
297 | /* |
298 | * Swap and sanity check each of the record offsets. | |
299 | */ | |
300 | for (i = 0; i <= srcDesc->numRecords; i++) { | |
301 | /* | |
302 | * Sanity check: must be even, and within the node itself. | |
303 | * | |
304 | * We may be called to swap an unused node, which contains all zeroes. | |
305 | * This is why we allow the record offset to be zero. | |
306 | */ | |
307 | if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) { | |
308 | printf("hfs_UNswap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
309 | error = fsBTInvalidHeaderErr; | |
310 | goto fail; | |
311 | } | |
312 | ||
313 | /* | |
314 | * Make sure the offsets are strictly increasing. Note that we're looping over | |
315 | * them backwards, hence the order in the comparison. | |
316 | */ | |
317 | if ((i < srcDesc->numRecords) && (srcOffs[i+1] >= srcOffs[i])) { | |
318 | printf("hfs_UNswap_BTNode: offsets %d and %d out of order (0x%04X, 0x%04X)\n", | |
319 | srcDesc->numRecords-i-2, srcDesc->numRecords-i-1, srcOffs[i+1], srcOffs[i]); | |
320 | error = fsBTInvalidHeaderErr; | |
321 | goto fail; | |
1c79356b A |
322 | } |
323 | ||
324 | srcOffs[i] = SWAP_BE16 (srcOffs[i]); | |
325 | } | |
326 | ||
327 | srcDesc->numRecords = SWAP_BE16 (srcDesc->numRecords); | |
328 | } | |
3a60a9f5 A |
329 | |
330 | fail: | |
331 | if (error) { | |
332 | /* | |
333 | * Log some useful information about where the corrupt node is. | |
334 | */ | |
335 | printf("node=%lld fileID=%u volume=%s device=%s\n", src->blockNum, VTOC(vp)->c_fileid, | |
336 | VTOVCB(vp)->vcbVN, vfs_statfs(vnode_mount(vp))->f_mntfromname); | |
337 | VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume; | |
338 | } | |
339 | ||
1c79356b A |
340 | return (error); |
341 | } | |
342 | ||
3a60a9f5 | 343 | static int |
1c79356b A |
344 | hfs_swap_HFSPlusBTInternalNode ( |
345 | BlockDescriptor *src, | |
346 | HFSCatalogNodeID fileID, | |
3a60a9f5 | 347 | enum HFSBTSwapDirection direction |
1c79356b A |
348 | ) |
349 | { | |
350 | BTNodeDescriptor *srcDesc = src->buffer; | |
351 | UInt16 *srcOffs = (UInt16 *)((char *)src->buffer + (src->blockSize - (srcDesc->numRecords * sizeof (UInt16)))); | |
3a60a9f5 | 352 | char *nextRecord; /* Points to start of record following current one */ |
1c79356b A |
353 | UInt32 i; |
354 | UInt32 j; | |
355 | ||
1c79356b A |
356 | if (fileID == kHFSExtentsFileID) { |
357 | HFSPlusExtentKey *srcKey; | |
358 | HFSPlusExtentDescriptor *srcRec; | |
3a60a9f5 | 359 | size_t recordSize; /* Size of the data part of the record, or node number for index nodes */ |
1c79356b | 360 | |
3a60a9f5 A |
361 | if (srcDesc->kind == kBTIndexNode) |
362 | recordSize = sizeof(UInt32); | |
363 | else | |
364 | recordSize = sizeof(HFSPlusExtentDescriptor); | |
365 | ||
1c79356b | 366 | for (i = 0; i < srcDesc->numRecords; i++) { |
3a60a9f5 | 367 | /* Point to the start of the record we're currently checking. */ |
1c79356b | 368 | srcKey = (HFSPlusExtentKey *)((char *)src->buffer + srcOffs[i]); |
3a60a9f5 A |
369 | |
370 | /* | |
371 | * Point to start of next (larger offset) record. We'll use this | |
372 | * to be sure the current record doesn't overflow into the next | |
373 | * record. | |
374 | */ | |
375 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
1c79356b | 376 | |
3a60a9f5 A |
377 | /* |
378 | * Make sure the key and data are within the buffer. Since both key | |
379 | * and data are fixed size, this is relatively easy. Note that this | |
380 | * relies on the keyLength being a constant; we verify the keyLength | |
381 | * below. | |
382 | */ | |
383 | if ((char *)srcKey + sizeof(HFSPlusExtentKey) + recordSize > nextRecord) { | |
384 | printf("hfs_swap_HFSPlusBTInternalNode: extents key #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
385 | return fsBTInvalidNodeErr; | |
386 | } | |
387 | ||
388 | if (direction == kSwapBTNodeBigToHost) | |
389 | srcKey->keyLength = SWAP_BE16 (srcKey->keyLength); | |
390 | if (srcKey->keyLength != sizeof(*srcKey) - sizeof(srcKey->keyLength)) { | |
391 | printf("hfs_swap_HFSPlusBTInternalNode: extents key #%d invalid length (%d)\n", srcDesc->numRecords-i-1, srcKey->keyLength); | |
392 | return fsBTInvalidNodeErr; | |
393 | } | |
394 | srcRec = (HFSPlusExtentDescriptor *)((char *)srcKey + srcKey->keyLength + sizeof(srcKey->keyLength)); | |
395 | if (direction == kSwapBTNodeHostToBig) | |
396 | srcKey->keyLength = SWAP_BE16 (srcKey->keyLength); | |
1c79356b | 397 | |
3a60a9f5 | 398 | /* Don't swap srcKey->forkType; it's only one byte */ |
1c79356b A |
399 | /* Don't swap srcKey->pad */ |
400 | ||
401 | srcKey->fileID = SWAP_BE32 (srcKey->fileID); | |
402 | srcKey->startBlock = SWAP_BE32 (srcKey->startBlock); | |
403 | ||
1c79356b | 404 | if (srcDesc->kind == kBTIndexNode) { |
3a60a9f5 | 405 | /* For index nodes, the record data is just a child node number. */ |
1c79356b | 406 | *((UInt32 *)srcRec) = SWAP_BE32 (*((UInt32 *)srcRec)); |
3a60a9f5 A |
407 | } else { |
408 | /* Swap the extent data */ | |
409 | for (j = 0; j < kHFSPlusExtentDensity; j++) { | |
410 | srcRec[j].startBlock = SWAP_BE32 (srcRec[j].startBlock); | |
411 | srcRec[j].blockCount = SWAP_BE32 (srcRec[j].blockCount); | |
412 | } | |
1c79356b A |
413 | } |
414 | } | |
415 | ||
416 | } else if (fileID == kHFSCatalogFileID) { | |
417 | HFSPlusCatalogKey *srcKey; | |
418 | SInt16 *srcPtr; | |
3a60a9f5 A |
419 | u_int16_t keyLength; |
420 | ||
1c79356b | 421 | for (i = 0; i < srcDesc->numRecords; i++) { |
3a60a9f5 | 422 | /* Point to the start of the record we're currently checking. */ |
1c79356b A |
423 | srcKey = (HFSPlusCatalogKey *)((char *)src->buffer + srcOffs[i]); |
424 | ||
3a60a9f5 A |
425 | /* |
426 | * Point to start of next (larger offset) record. We'll use this | |
427 | * to be sure the current record doesn't overflow into the next | |
428 | * record. | |
429 | */ | |
430 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
431 | ||
432 | /* | |
433 | * Make sure we can safely dereference the keyLength and parentID fields. */ | |
434 | if ((char *)srcKey + offsetof(HFSPlusCatalogKey, nodeName.unicode[0]) > nextRecord) { | |
435 | printf("hfs_swap_HFSPlusBTInternalNode: catalog key #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
436 | return fsBTInvalidNodeErr; | |
437 | } | |
438 | ||
439 | /* | |
440 | * Swap and sanity check the key length | |
441 | */ | |
442 | if (direction == kSwapBTNodeBigToHost) | |
443 | srcKey->keyLength = SWAP_BE16 (srcKey->keyLength); | |
444 | keyLength = srcKey->keyLength; /* Put it in a local (native order) because we use it several times */ | |
445 | if (direction == kSwapBTNodeHostToBig) | |
446 | srcKey->keyLength = SWAP_BE16 (keyLength); | |
1c79356b | 447 | |
3a60a9f5 A |
448 | /* Sanity check the key length */ |
449 | if (keyLength < kHFSPlusCatalogKeyMinimumLength || keyLength > kHFSPlusCatalogKeyMaximumLength) { | |
450 | printf("hfs_swap_HFSPlusBTInternalNode: catalog key #%d invalid length (%d)\n", srcDesc->numRecords-i-1, keyLength); | |
451 | return fsBTInvalidNodeErr; | |
452 | } | |
453 | ||
454 | /* | |
455 | * Make sure that we can safely dereference the record's type field or | |
456 | * an index node's child node number. | |
457 | */ | |
458 | srcPtr = (SInt16 *)((char *)srcKey + keyLength + sizeof(srcKey->keyLength)); | |
459 | if ((char *)srcPtr + sizeof(UInt32) > nextRecord) { | |
460 | printf("hfs_swap_HFSPlusBTInternalNode: catalog key #%d too big\n", srcDesc->numRecords-i-1); | |
461 | return fsBTInvalidNodeErr; | |
462 | } | |
463 | ||
1c79356b A |
464 | srcKey->parentID = SWAP_BE32 (srcKey->parentID); |
465 | ||
3a60a9f5 A |
466 | /* |
467 | * Swap and sanity check the key's node name | |
468 | */ | |
469 | if (direction == kSwapBTNodeBigToHost) | |
470 | srcKey->nodeName.length = SWAP_BE16 (srcKey->nodeName.length); | |
471 | /* Make sure name length is consistent with key length */ | |
472 | if (keyLength < sizeof(srcKey->parentID) + sizeof(srcKey->nodeName.length) + | |
473 | srcKey->nodeName.length*sizeof(srcKey->nodeName.unicode[0])) { | |
474 | printf("hfs_swap_HFSPlusBTInternalNode: catalog record #%d keyLength=%d expected=%d\n", | |
475 | srcDesc->numRecords-i, keyLength, sizeof(srcKey->parentID) + sizeof(srcKey->nodeName.length) + | |
476 | srcKey->nodeName.length*sizeof(srcKey->nodeName.unicode[0])); | |
477 | return fsBTInvalidNodeErr; | |
478 | } | |
1c79356b A |
479 | for (j = 0; j < srcKey->nodeName.length; j++) { |
480 | srcKey->nodeName.unicode[j] = SWAP_BE16 (srcKey->nodeName.unicode[j]); | |
481 | } | |
3a60a9f5 A |
482 | if (direction == kSwapBTNodeHostToBig) |
483 | srcKey->nodeName.length = SWAP_BE16 (srcKey->nodeName.length); | |
1c79356b | 484 | |
3a60a9f5 A |
485 | /* |
486 | * For index nodes, the record data is just the child's node number. | |
487 | * Skip over swapping the various types of catalog record. | |
488 | */ | |
1c79356b A |
489 | if (srcDesc->kind == kBTIndexNode) { |
490 | *((UInt32 *)srcPtr) = SWAP_BE32 (*((UInt32 *)srcPtr)); | |
491 | continue; | |
492 | } | |
493 | ||
3a60a9f5 A |
494 | /* Make sure the recordType is in native order before using it. */ |
495 | if (direction == kSwapBTNodeBigToHost) | |
496 | srcPtr[0] = SWAP_BE16 (srcPtr[0]); | |
1c79356b A |
497 | |
498 | if (srcPtr[0] == kHFSPlusFolderRecord) { | |
499 | HFSPlusCatalogFolder *srcRec = (HFSPlusCatalogFolder *)srcPtr; | |
3a60a9f5 A |
500 | if ((char *)srcRec + sizeof(*srcRec) > nextRecord) { |
501 | printf("hfs_swap_HFSPlusBTInternalNode: catalog folder record #%d too big\n", srcDesc->numRecords-i-1); | |
502 | return fsBTInvalidNodeErr; | |
503 | } | |
504 | ||
1c79356b A |
505 | srcRec->flags = SWAP_BE16 (srcRec->flags); |
506 | srcRec->valence = SWAP_BE32 (srcRec->valence); | |
507 | srcRec->folderID = SWAP_BE32 (srcRec->folderID); | |
508 | srcRec->createDate = SWAP_BE32 (srcRec->createDate); | |
509 | srcRec->contentModDate = SWAP_BE32 (srcRec->contentModDate); | |
510 | srcRec->attributeModDate = SWAP_BE32 (srcRec->attributeModDate); | |
511 | srcRec->accessDate = SWAP_BE32 (srcRec->accessDate); | |
512 | srcRec->backupDate = SWAP_BE32 (srcRec->backupDate); | |
513 | ||
514 | srcRec->bsdInfo.ownerID = SWAP_BE32 (srcRec->bsdInfo.ownerID); | |
515 | srcRec->bsdInfo.groupID = SWAP_BE32 (srcRec->bsdInfo.groupID); | |
516 | ||
3a60a9f5 A |
517 | /* Don't swap srcRec->bsdInfo.adminFlags; it's only one byte */ |
518 | /* Don't swap srcRec->bsdInfo.ownerFlags; it's only one byte */ | |
1c79356b A |
519 | |
520 | srcRec->bsdInfo.fileMode = SWAP_BE16 (srcRec->bsdInfo.fileMode); | |
521 | srcRec->bsdInfo.special.iNodeNum = SWAP_BE32 (srcRec->bsdInfo.special.iNodeNum); | |
522 | ||
523 | srcRec->textEncoding = SWAP_BE32 (srcRec->textEncoding); | |
524 | ||
525 | /* Don't swap srcRec->userInfo */ | |
526 | /* Don't swap srcRec->finderInfo */ | |
527 | /* Don't swap srcRec->reserved */ | |
528 | ||
529 | } else if (srcPtr[0] == kHFSPlusFileRecord) { | |
530 | HFSPlusCatalogFile *srcRec = (HFSPlusCatalogFile *)srcPtr; | |
3a60a9f5 A |
531 | if ((char *)srcRec + sizeof(*srcRec) > nextRecord) { |
532 | printf("hfs_swap_HFSPlusBTInternalNode: catalog file record #%d too big\n", srcDesc->numRecords-i-1); | |
533 | return fsBTInvalidNodeErr; | |
534 | } | |
1c79356b A |
535 | |
536 | srcRec->flags = SWAP_BE16 (srcRec->flags); | |
537 | ||
538 | srcRec->fileID = SWAP_BE32 (srcRec->fileID); | |
539 | ||
540 | srcRec->createDate = SWAP_BE32 (srcRec->createDate); | |
541 | srcRec->contentModDate = SWAP_BE32 (srcRec->contentModDate); | |
542 | srcRec->attributeModDate = SWAP_BE32 (srcRec->attributeModDate); | |
543 | srcRec->accessDate = SWAP_BE32 (srcRec->accessDate); | |
544 | srcRec->backupDate = SWAP_BE32 (srcRec->backupDate); | |
545 | ||
546 | srcRec->bsdInfo.ownerID = SWAP_BE32 (srcRec->bsdInfo.ownerID); | |
547 | srcRec->bsdInfo.groupID = SWAP_BE32 (srcRec->bsdInfo.groupID); | |
548 | ||
3a60a9f5 A |
549 | /* Don't swap srcRec->bsdInfo.adminFlags; it's only one byte */ |
550 | /* Don't swap srcRec->bsdInfo.ownerFlags; it's only one byte */ | |
1c79356b A |
551 | |
552 | srcRec->bsdInfo.fileMode = SWAP_BE16 (srcRec->bsdInfo.fileMode); | |
553 | srcRec->bsdInfo.special.iNodeNum = SWAP_BE32 (srcRec->bsdInfo.special.iNodeNum); | |
554 | ||
555 | srcRec->textEncoding = SWAP_BE32 (srcRec->textEncoding); | |
556 | ||
557 | /* Don't swap srcRec->reserved1 */ | |
558 | /* Don't swap srcRec->userInfo */ | |
559 | /* Don't swap srcRec->finderInfo */ | |
560 | /* Don't swap srcRec->reserved2 */ | |
561 | ||
562 | hfs_swap_HFSPlusForkData (&srcRec->dataFork); | |
563 | hfs_swap_HFSPlusForkData (&srcRec->resourceFork); | |
564 | ||
565 | } else if ((srcPtr[0] == kHFSPlusFolderThreadRecord) || | |
566 | (srcPtr[0] == kHFSPlusFileThreadRecord)) { | |
567 | ||
3a60a9f5 A |
568 | /* |
569 | * Make sure there is room for parentID and name length. | |
570 | */ | |
1c79356b | 571 | HFSPlusCatalogThread *srcRec = (HFSPlusCatalogThread *)srcPtr; |
3a60a9f5 A |
572 | if ((char *) &srcRec->nodeName.unicode[0] > nextRecord) { |
573 | printf("hfs_swap_HFSPlusBTInternalNode: catalog thread record #%d too big\n", srcDesc->numRecords-i-1); | |
574 | return fsBTInvalidNodeErr; | |
575 | } | |
576 | ||
1c79356b A |
577 | /* Don't swap srcRec->reserved */ |
578 | ||
579 | srcRec->parentID = SWAP_BE32 (srcRec->parentID); | |
580 | ||
3a60a9f5 A |
581 | if (direction == kSwapBTNodeBigToHost) |
582 | srcRec->nodeName.length = SWAP_BE16 (srcRec->nodeName.length); | |
583 | ||
584 | /* | |
585 | * Make sure there is room for the name in the buffer. | |
586 | * Then swap the characters of the name itself. | |
587 | */ | |
588 | if ((char *) &srcRec->nodeName.unicode[srcRec->nodeName.length] > nextRecord) { | |
589 | printf("hfs_swap_HFSPlusBTInternalNode: catalog thread record #%d name too big\n", srcDesc->numRecords-i-1); | |
590 | return fsBTInvalidNodeErr; | |
591 | } | |
1c79356b A |
592 | for (j = 0; j < srcRec->nodeName.length; j++) { |
593 | srcRec->nodeName.unicode[j] = SWAP_BE16 (srcRec->nodeName.unicode[j]); | |
594 | } | |
3a60a9f5 A |
595 | |
596 | if (direction == kSwapBTNodeHostToBig) | |
597 | srcRec->nodeName.length = SWAP_BE16 (srcRec->nodeName.length); | |
1c79356b A |
598 | |
599 | } else { | |
3a60a9f5 A |
600 | printf("hfs_swap_HFSPlusBTInternalNode: unrecognized catalog record type (0x%04X; record #%d)\n", srcPtr[0], srcDesc->numRecords-i-1); |
601 | return fsBTInvalidNodeErr; | |
1c79356b A |
602 | } |
603 | ||
3a60a9f5 A |
604 | /* We can swap the record type now that we're done using it. */ |
605 | if (direction == kSwapBTNodeHostToBig) | |
606 | srcPtr[0] = SWAP_BE16 (srcPtr[0]); | |
1c79356b A |
607 | } |
608 | ||
91447636 A |
609 | } else if (fileID == kHFSAttributesFileID) { |
610 | HFSPlusAttrKey *srcKey; | |
611 | HFSPlusAttrRecord *srcRec; | |
3a60a9f5 A |
612 | u_int16_t keyLength; |
613 | u_int32_t attrSize = 0; | |
614 | ||
91447636 | 615 | for (i = 0; i < srcDesc->numRecords; i++) { |
3a60a9f5 | 616 | /* Point to the start of the record we're currently checking. */ |
91447636 | 617 | srcKey = (HFSPlusAttrKey *)((char *)src->buffer + srcOffs[i]); |
3a60a9f5 A |
618 | |
619 | /* | |
620 | * Point to start of next (larger offset) record. We'll use this | |
621 | * to be sure the current record doesn't overflow into the next | |
622 | * record. | |
623 | */ | |
624 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
625 | ||
626 | /* Make sure there is room in the buffer for a minimal key */ | |
627 | if ((char *) &srcKey->attrName[1] > nextRecord) { | |
628 | printf("hfs_swap_HFSPlusBTInternalNode: attr key #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
629 | return fsBTInvalidNodeErr; | |
630 | } | |
91447636 | 631 | |
3a60a9f5 A |
632 | /* Swap the key length field */ |
633 | if (direction == kSwapBTNodeBigToHost) | |
634 | srcKey->keyLength = SWAP_BE16(srcKey->keyLength); | |
635 | keyLength = srcKey->keyLength; /* Keep a copy in native order */ | |
636 | if (direction == kSwapBTNodeHostToBig) | |
637 | srcKey->keyLength = SWAP_BE16(srcKey->keyLength); | |
638 | ||
639 | /* | |
640 | * Make sure that we can safely dereference the record's type field or | |
641 | * an index node's child node number. | |
642 | */ | |
643 | srcRec = (HFSPlusAttrRecord *)((char *)srcKey + keyLength + sizeof(srcKey->keyLength)); | |
644 | if ((char *)srcRec + sizeof(u_int32_t) > nextRecord) { | |
645 | printf("hfs_swap_HFSPlusBTInternalNode: attr key #%d too big (%d)\n", srcDesc->numRecords-i-1, keyLength); | |
646 | return fsBTInvalidNodeErr; | |
647 | } | |
91447636 A |
648 | |
649 | srcKey->fileID = SWAP_BE32(srcKey->fileID); | |
650 | srcKey->startBlock = SWAP_BE32(srcKey->startBlock); | |
3a60a9f5 A |
651 | |
652 | /* | |
653 | * Swap and check the attribute name | |
654 | */ | |
655 | if (direction == kSwapBTNodeBigToHost) | |
656 | srcKey->attrNameLen = SWAP_BE16(srcKey->attrNameLen); | |
657 | /* Sanity check the attribute name length */ | |
658 | if (srcKey->attrNameLen > kHFSMaxAttrNameLen || keyLength < (kHFSPlusAttrKeyMinimumLength + sizeof(u_int16_t)*srcKey->attrNameLen)) { | |
659 | printf("hfs_swap_HFSPlusBTInternalNode: attr key #%d keyLength=%d attrNameLen=%d\n", srcDesc->numRecords-i-1, keyLength, srcKey->attrNameLen); | |
660 | return fsBTInvalidNodeErr; | |
661 | } | |
91447636 A |
662 | for (j = 0; j < srcKey->attrNameLen; j++) |
663 | srcKey->attrName[j] = SWAP_BE16(srcKey->attrName[j]); | |
3a60a9f5 A |
664 | if (direction == kSwapBTNodeHostToBig) |
665 | srcKey->attrNameLen = SWAP_BE16(srcKey->attrNameLen); | |
91447636 | 666 | |
3a60a9f5 A |
667 | /* |
668 | * For index nodes, the record data is just the child's node number. | |
669 | * Skip over swapping the various types of attribute record. | |
670 | */ | |
91447636 A |
671 | if (srcDesc->kind == kBTIndexNode) { |
672 | *((UInt32 *)srcRec) = SWAP_BE32 (*((UInt32 *)srcRec)); | |
673 | continue; | |
674 | } | |
675 | ||
3a60a9f5 A |
676 | /* Swap the record data */ |
677 | if (direction == kSwapBTNodeBigToHost) | |
678 | srcRec->recordType = SWAP_BE32(srcRec->recordType); | |
91447636 A |
679 | switch (srcRec->recordType) { |
680 | case kHFSPlusAttrInlineData: | |
3a60a9f5 A |
681 | /* Is there room for the inline data header? */ |
682 | if ((char *) &srcRec->attrData.attrData[0] > nextRecord) { | |
683 | printf("hfs_swap_HFSPlusBTInternalNode: attr inline #%d too big\n", srcDesc->numRecords-i-1); | |
684 | return fsBTInvalidNodeErr; | |
685 | } | |
686 | ||
91447636 | 687 | /* We're not swapping the reserved fields */ |
3a60a9f5 A |
688 | |
689 | /* Swap the attribute size */ | |
690 | if (direction == kSwapBTNodeHostToBig) | |
691 | attrSize = srcRec->attrData.attrSize; | |
91447636 | 692 | srcRec->attrData.attrSize = SWAP_BE32(srcRec->attrData.attrSize); |
3a60a9f5 A |
693 | if (direction == kSwapBTNodeBigToHost) |
694 | attrSize = srcRec->attrData.attrSize; | |
695 | ||
696 | /* Is there room for the inline attribute data? */ | |
697 | if ((char *) &srcRec->attrData.attrData[attrSize] > nextRecord) { | |
698 | printf("hfs_swap_HFSPlusBTInternalNode: attr inline #%d too big (attrSize=%u)\n", srcDesc->numRecords-i-1, attrSize); | |
699 | return fsBTInvalidNodeErr; | |
700 | } | |
701 | ||
702 | /* Not swapping the attribute data itself */ | |
91447636 | 703 | break; |
3a60a9f5 | 704 | |
91447636 | 705 | case kHFSPlusAttrForkData: |
3a60a9f5 A |
706 | /* Is there room for the fork data record? */ |
707 | if ((char *)srcRec + sizeof(HFSPlusAttrForkData) > nextRecord) { | |
708 | printf("hfs_swap_HFSPlusBTInternalNode: attr fork data #%d too big\n", srcDesc->numRecords-i-1); | |
709 | return fsBTInvalidNodeErr; | |
710 | } | |
711 | ||
91447636 | 712 | /* We're not swapping the reserved field */ |
3a60a9f5 | 713 | |
91447636 A |
714 | hfs_swap_HFSPlusForkData(&srcRec->forkData.theFork); |
715 | break; | |
3a60a9f5 | 716 | |
91447636 | 717 | case kHFSPlusAttrExtents: |
3a60a9f5 A |
718 | /* Is there room for an extent record? */ |
719 | if ((char *)srcRec + sizeof(HFSPlusAttrExtents) > nextRecord) { | |
720 | printf("hfs_swap_HFSPlusBTInternalNode: attr extents #%d too big\n", srcDesc->numRecords-i-1); | |
721 | return fsBTInvalidNodeErr; | |
722 | } | |
723 | ||
91447636 | 724 | /* We're not swapping the reserved field */ |
3a60a9f5 | 725 | |
91447636 A |
726 | for (j = 0; j < kHFSPlusExtentDensity; j++) { |
727 | srcRec->overflowExtents.extents[j].startBlock = | |
728 | SWAP_BE32(srcRec->overflowExtents.extents[j].startBlock); | |
729 | srcRec->overflowExtents.extents[j].blockCount = | |
730 | SWAP_BE32(srcRec->overflowExtents.extents[j].blockCount); | |
731 | } | |
732 | break; | |
733 | } | |
3a60a9f5 A |
734 | if (direction == kSwapBTNodeHostToBig) |
735 | srcRec->recordType = SWAP_BE32(srcRec->recordType); | |
91447636 | 736 | } |
55e303ae | 737 | } else if (fileID > kHFSFirstUserCatalogNodeID) { |
3a60a9f5 | 738 | /* The only B-tree with a non-system CNID that we use is the hotfile B-tree */ |
55e303ae A |
739 | HotFileKey *srcKey; |
740 | UInt32 *srcRec; | |
741 | ||
742 | for (i = 0; i < srcDesc->numRecords; i++) { | |
3a60a9f5 | 743 | /* Point to the start of the record we're currently checking. */ |
55e303ae A |
744 | srcKey = (HotFileKey *)((char *)src->buffer + srcOffs[i]); |
745 | ||
3a60a9f5 A |
746 | /* |
747 | * Point to start of next (larger offset) record. We'll use this | |
748 | * to be sure the current record doesn't overflow into the next | |
749 | * record. | |
750 | */ | |
751 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
752 | ||
753 | /* Make sure there is room for the key (HotFileKey) and data (UInt32) */ | |
754 | if ((char *)srcKey + sizeof(HotFileKey) + sizeof(UInt32) > nextRecord) { | |
755 | printf("hfs_swap_HFSPlusBTInternalNode: hotfile #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
756 | return fsBTInvalidNodeErr; | |
757 | } | |
758 | ||
759 | /* Swap and sanity check the key length field */ | |
760 | if (direction == kSwapBTNodeBigToHost) | |
55e303ae | 761 | srcKey->keyLength = SWAP_BE16 (srcKey->keyLength); |
3a60a9f5 A |
762 | if (srcKey->keyLength != sizeof(*srcKey) - sizeof(srcKey->keyLength)) { |
763 | printf("hfs_swap_HFSPlusBTInternalNode: hotfile #%d incorrect keyLength %d\n", srcDesc->numRecords-i-1, srcKey->keyLength); | |
764 | return fsBTInvalidNodeErr; | |
765 | } | |
766 | srcRec = (u_int32_t *)((char *)srcKey + srcKey->keyLength + sizeof(srcKey->keyLength)); | |
767 | if (direction == kSwapBTNodeHostToBig) | |
55e303ae A |
768 | srcKey->keyLength = SWAP_BE16 (srcKey->keyLength); |
769 | ||
770 | /* Don't swap srcKey->forkType */ | |
771 | /* Don't swap srcKey->pad */ | |
772 | ||
773 | srcKey->temperature = SWAP_BE32 (srcKey->temperature); | |
774 | srcKey->fileID = SWAP_BE32 (srcKey->fileID); | |
775 | ||
776 | *((UInt32 *)srcRec) = SWAP_BE32 (*((UInt32 *)srcRec)); | |
777 | } | |
1c79356b | 778 | } else { |
3a60a9f5 | 779 | panic ("hfs_swap_HFSPlusBTInternalNode: fileID %u is not a system B-tree\n", fileID); |
1c79356b A |
780 | } |
781 | ||
55e303ae | 782 | |
1c79356b A |
783 | return (0); |
784 | } | |
785 | ||
3a60a9f5 | 786 | static int |
1c79356b A |
787 | hfs_swap_HFSBTInternalNode ( |
788 | BlockDescriptor *src, | |
789 | HFSCatalogNodeID fileID, | |
3a60a9f5 | 790 | enum HFSBTSwapDirection direction |
1c79356b A |
791 | ) |
792 | { | |
793 | BTNodeDescriptor *srcDesc = src->buffer; | |
794 | UInt16 *srcOffs = (UInt16 *)((char *)src->buffer + (src->blockSize - (srcDesc->numRecords * sizeof (UInt16)))); | |
3a60a9f5 | 795 | char *nextRecord; /* Points to start of record following current one */ |
1c79356b A |
796 | |
797 | UInt32 i; | |
798 | UInt32 j; | |
799 | ||
1c79356b A |
800 | if (fileID == kHFSExtentsFileID) { |
801 | HFSExtentKey *srcKey; | |
802 | HFSExtentDescriptor *srcRec; | |
3a60a9f5 | 803 | size_t recordSize; /* Size of the data part of the record, or node number for index nodes */ |
1c79356b | 804 | |
3a60a9f5 A |
805 | if (srcDesc->kind == kBTIndexNode) |
806 | recordSize = sizeof(UInt32); | |
807 | else | |
808 | recordSize = sizeof(HFSExtentDescriptor); | |
809 | ||
1c79356b | 810 | for (i = 0; i < srcDesc->numRecords; i++) { |
3a60a9f5 | 811 | /* Point to the start of the record we're currently checking. */ |
1c79356b A |
812 | srcKey = (HFSExtentKey *)((char *)src->buffer + srcOffs[i]); |
813 | ||
3a60a9f5 A |
814 | /* |
815 | * Point to start of next (larger offset) record. We'll use this | |
816 | * to be sure the current record doesn't overflow into the next | |
817 | * record. | |
818 | */ | |
819 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
820 | ||
821 | /* | |
822 | * Make sure the key and data are within the buffer. Since both key | |
823 | * and data are fixed size, this is relatively easy. Note that this | |
824 | * relies on the keyLength being a constant; we verify the keyLength | |
825 | * below. | |
826 | */ | |
827 | if ((char *)srcKey + sizeof(HFSExtentKey) + recordSize > nextRecord) { | |
828 | printf("hfs_swap_HFSBTInternalNode: extents key #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
829 | return fsBTInvalidNodeErr; | |
830 | } | |
831 | ||
832 | /* Don't swap srcKey->keyLength (it's only one byte), but do sanity check it */ | |
833 | if (srcKey->keyLength != sizeof(*srcKey) - sizeof(srcKey->keyLength)) { | |
834 | printf("hfs_swap_HFSBTInternalNode: extents key #%d invalid length (%d)\n", srcDesc->numRecords-i-1, srcKey->keyLength); | |
835 | return fsBTInvalidNodeErr; | |
836 | } | |
837 | ||
838 | /* Don't swap srcKey->forkType; it's only one byte */ | |
1c79356b A |
839 | |
840 | srcKey->fileID = SWAP_BE32 (srcKey->fileID); | |
841 | srcKey->startBlock = SWAP_BE16 (srcKey->startBlock); | |
842 | ||
843 | /* Point to record data (round up to even byte boundary) */ | |
844 | srcRec = (HFSExtentDescriptor *)((char *)srcKey + ((srcKey->keyLength + 2) & ~1)); | |
845 | ||
1c79356b | 846 | if (srcDesc->kind == kBTIndexNode) { |
3a60a9f5 | 847 | /* For index nodes, the record data is just a child node number. */ |
1c79356b | 848 | *((UInt32 *)srcRec) = SWAP_BE32 (*((UInt32 *)srcRec)); |
3a60a9f5 A |
849 | } else { |
850 | /* Swap the extent data */ | |
851 | for (j = 0; j < kHFSExtentDensity; j++) { | |
852 | srcRec[j].startBlock = SWAP_BE16 (srcRec[j].startBlock); | |
853 | srcRec[j].blockCount = SWAP_BE16 (srcRec[j].blockCount); | |
854 | } | |
1c79356b A |
855 | } |
856 | } | |
857 | ||
858 | } else if (fileID == kHFSCatalogFileID) { | |
859 | HFSCatalogKey *srcKey; | |
860 | SInt16 *srcPtr; | |
3a60a9f5 A |
861 | unsigned expectedKeyLength; |
862 | ||
1c79356b | 863 | for (i = 0; i < srcDesc->numRecords; i++) { |
3a60a9f5 | 864 | /* Point to the start of the record we're currently checking. */ |
1c79356b A |
865 | srcKey = (HFSCatalogKey *)((char *)src->buffer + srcOffs[i]); |
866 | ||
3a60a9f5 A |
867 | /* |
868 | * Point to start of next (larger offset) record. We'll use this | |
869 | * to be sure the current record doesn't overflow into the next | |
870 | * record. | |
871 | */ | |
872 | nextRecord = (char *)src->buffer + srcOffs[i-1]; | |
873 | ||
874 | /* | |
875 | * Make sure we can safely dereference the keyLength and parentID fields. | |
876 | * The value 8 below is 1 bytes for keyLength + 1 byte reserved + 4 bytes | |
877 | * for parentID + 1 byte for nodeName's length + 1 byte to round up the | |
878 | * record start to an even offset, which forms a minimal key. | |
879 | */ | |
880 | if ((char *)srcKey + 8 > nextRecord) { | |
881 | printf("hfs_swap_HFSBTInternalNode: catalog key #%d offset too big (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]); | |
882 | return fsBTInvalidNodeErr; | |
883 | } | |
884 | ||
885 | /* Don't swap srcKey->keyLength (it's only one byte), but do sanity check it */ | |
886 | if (srcKey->keyLength < kHFSCatalogKeyMinimumLength || srcKey->keyLength > kHFSCatalogKeyMaximumLength) { | |
887 | printf("hfs_swap_HFSBTInternalNode: catalog key #%d invalid length (%d)\n", srcDesc->numRecords-i-1, srcKey->keyLength); | |
888 | return fsBTInvalidNodeErr; | |
889 | } | |
890 | ||
1c79356b A |
891 | /* Don't swap srcKey->reserved */ |
892 | ||
893 | srcKey->parentID = SWAP_BE32 (srcKey->parentID); | |
894 | ||
895 | /* Don't swap srcKey->nodeName */ | |
3a60a9f5 A |
896 | |
897 | /* Make sure the keyLength is big enough for the key's content */ | |
898 | if (srcDesc->kind == kBTIndexNode) | |
899 | expectedKeyLength = sizeof(*srcKey) - sizeof(srcKey->keyLength); | |
900 | else | |
901 | expectedKeyLength = srcKey->nodeName[0] + kHFSCatalogKeyMinimumLength; | |
902 | if (srcKey->keyLength < expectedKeyLength) { | |
903 | printf("hfs_swap_HFSBTInternalNode: catalog record #%d keyLength=%u expected=%u\n", | |
904 | srcDesc->numRecords-i, srcKey->keyLength, expectedKeyLength); | |
905 | return fsBTInvalidNodeErr; | |
906 | } | |
1c79356b A |
907 | |
908 | /* Point to record data (round up to even byte boundary) */ | |
909 | srcPtr = (SInt16 *)((char *)srcKey + ((srcKey->keyLength + 2) & ~1)); | |
910 | ||
3a60a9f5 A |
911 | /* |
912 | * Make sure that we can safely dereference the record's type field or | |
913 | * and index node's child node number. | |
914 | */ | |
915 | if ((char *)srcPtr + sizeof(UInt32) > nextRecord) { | |
916 | printf("hfs_swap_HFSBTInternalNode: catalog key #%d too big\n", srcDesc->numRecords-i-1); | |
917 | return fsBTInvalidNodeErr; | |
918 | } | |
919 | ||
920 | /* | |
921 | * For index nodes, the record data is just the child's node number. | |
922 | * Skip over swapping the various types of catalog record. | |
923 | */ | |
1c79356b A |
924 | if (srcDesc->kind == kBTIndexNode) { |
925 | *((UInt32 *)srcPtr) = SWAP_BE32 (*((UInt32 *)srcPtr)); | |
926 | continue; | |
927 | } | |
928 | ||
3a60a9f5 A |
929 | /* Make sure the recordType is in native order before using it. */ |
930 | if (direction == kSwapBTNodeBigToHost) | |
931 | srcPtr[0] = SWAP_BE16 (srcPtr[0]); | |
1c79356b A |
932 | |
933 | if (srcPtr[0] == kHFSFolderRecord) { | |
934 | HFSCatalogFolder *srcRec = (HFSCatalogFolder *)srcPtr; | |
3a60a9f5 A |
935 | if ((char *)srcRec + sizeof(*srcRec) > nextRecord) { |
936 | printf("hfs_swap_HFSBTInternalNode: catalog folder record #%d too big\n", srcDesc->numRecords-i-1); | |
937 | return fsBTInvalidNodeErr; | |
938 | } | |
1c79356b A |
939 | |
940 | srcRec->flags = SWAP_BE16 (srcRec->flags); | |
941 | srcRec->valence = SWAP_BE16 (srcRec->valence); | |
942 | ||
943 | srcRec->folderID = SWAP_BE32 (srcRec->folderID); | |
944 | srcRec->createDate = SWAP_BE32 (srcRec->createDate); | |
945 | srcRec->modifyDate = SWAP_BE32 (srcRec->modifyDate); | |
946 | srcRec->backupDate = SWAP_BE32 (srcRec->backupDate); | |
947 | ||
948 | /* Don't swap srcRec->userInfo */ | |
949 | /* Don't swap srcRec->finderInfo */ | |
950 | /* Don't swap resserved array */ | |
951 | ||
952 | } else if (srcPtr[0] == kHFSFileRecord) { | |
953 | HFSCatalogFile *srcRec = (HFSCatalogFile *)srcPtr; | |
3a60a9f5 A |
954 | if ((char *)srcRec + sizeof(*srcRec) > nextRecord) { |
955 | printf("hfs_swap_HFSBTInternalNode: catalog file record #%d too big\n", srcDesc->numRecords-i-1); | |
956 | return fsBTInvalidNodeErr; | |
957 | } | |
1c79356b A |
958 | |
959 | srcRec->flags = srcRec->flags; | |
960 | srcRec->fileType = srcRec->fileType; | |
961 | ||
962 | /* Don't swap srcRec->userInfo */ | |
963 | ||
964 | srcRec->fileID = SWAP_BE32 (srcRec->fileID); | |
965 | ||
966 | srcRec->dataStartBlock = SWAP_BE16 (srcRec->dataStartBlock); | |
967 | srcRec->dataLogicalSize = SWAP_BE32 (srcRec->dataLogicalSize); | |
968 | srcRec->dataPhysicalSize = SWAP_BE32 (srcRec->dataPhysicalSize); | |
969 | ||
970 | srcRec->rsrcStartBlock = SWAP_BE16 (srcRec->rsrcStartBlock); | |
971 | srcRec->rsrcLogicalSize = SWAP_BE32 (srcRec->rsrcLogicalSize); | |
972 | srcRec->rsrcPhysicalSize = SWAP_BE32 (srcRec->rsrcPhysicalSize); | |
973 | ||
974 | srcRec->createDate = SWAP_BE32 (srcRec->createDate); | |
975 | srcRec->modifyDate = SWAP_BE32 (srcRec->modifyDate); | |
976 | srcRec->backupDate = SWAP_BE32 (srcRec->backupDate); | |
977 | ||
978 | /* Don't swap srcRec->finderInfo */ | |
979 | ||
980 | srcRec->clumpSize = SWAP_BE16 (srcRec->clumpSize); | |
981 | ||
982 | /* Swap the two sets of extents as an array of six (three each) UInt16 */ | |
983 | for (j = 0; j < kHFSExtentDensity * 2; j++) { | |
984 | srcRec->dataExtents[j].startBlock = SWAP_BE16 (srcRec->dataExtents[j].startBlock); | |
985 | srcRec->dataExtents[j].blockCount = SWAP_BE16 (srcRec->dataExtents[j].blockCount); | |
986 | } | |
987 | ||
988 | /* Don't swap srcRec->reserved */ | |
989 | ||
990 | } else if ((srcPtr[0] == kHFSFolderThreadRecord) || | |
991 | (srcPtr[0] == kHFSFileThreadRecord)) { | |
1c79356b | 992 | HFSCatalogThread *srcRec = (HFSCatalogThread *)srcPtr; |
3a60a9f5 A |
993 | |
994 | /* Make sure there is room for parentID and name length */ | |
995 | if ((char *) &srcRec->nodeName[1] > nextRecord) { | |
996 | printf("hfs_swap_HFSBTInternalNode: catalog thread record #%d too big\n", srcDesc->numRecords-i-1); | |
997 | return fsBTInvalidNodeErr; | |
998 | } | |
1c79356b A |
999 | |
1000 | /* Don't swap srcRec->reserved array */ | |
1001 | ||
1002 | srcRec->parentID = SWAP_BE32 (srcRec->parentID); | |
1003 | ||
1004 | /* Don't swap srcRec->nodeName */ | |
3a60a9f5 A |
1005 | |
1006 | /* Make sure there is room for the name in the buffer */ | |
1007 | if ((char *) &srcRec->nodeName[srcRec->nodeName[0]] > nextRecord) { | |
1008 | printf("hfs_swap_HFSBTInternalNode: catalog thread record #%d name too big\n", srcDesc->numRecords-i-1); | |
1009 | return fsBTInvalidNodeErr; | |
1010 | } | |
1c79356b | 1011 | } else { |
3a60a9f5 A |
1012 | printf("hfs_swap_HFSBTInternalNode: unrecognized catalog record type (0x%04X; record #%d)\n", srcPtr[0], srcDesc->numRecords-i-1); |
1013 | return fsBTInvalidNodeErr; | |
1c79356b A |
1014 | } |
1015 | ||
3a60a9f5 A |
1016 | /* We can swap the record type now that we're done using it */ |
1017 | if (direction == kSwapBTNodeHostToBig) | |
1018 | srcPtr[0] = SWAP_BE16 (srcPtr[0]); | |
1c79356b A |
1019 | } |
1020 | ||
1021 | } else { | |
3a60a9f5 | 1022 | panic ("hfs_swap_HFSBTInternalNode: fileID %u is not a system B-tree\n", fileID); |
1c79356b A |
1023 | } |
1024 | ||
1025 | return (0); | |
1026 | } |