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