/*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
hfs_swap_BTNode (
BlockDescriptor *src,
vnode_t vp,
- enum HFSBTSwapDirection direction
+ enum HFSBTSwapDirection direction,
+ u_int8_t allow_empty_node
)
{
BTNodeDescriptor *srcDesc = src->buffer;
u_int16_t *srcOffs = NULL;
BTreeControlBlockPtr btcb = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr;
- u_int32_t i;
+ u_int16_t i; /* index to match srcDesc->numRecords */
int error = 0;
#ifdef ENDIAN_DEBUG
if (direction == kSwapBTNodeBigToHost) {
- printf ("BE -> Native Swap\n");
+ printf ("hfs: BE -> Native Swap\n");
} else if (direction == kSwapBTNodeHostToBig) {
- printf ("Native -> BE Swap\n");
+ printf ("hfs: Native -> BE Swap\n");
} else if (direction == kSwapBTNodeHeaderRecordOnly) {
- printf ("Not swapping descriptors\n");
+ printf ("hfs: Not swapping descriptors\n");
} else {
panic ("hfs_swap_BTNode: This is impossible");
}
/*
* When first opening a BTree, we have to read the header node before the
* control block is initialized. In this case, totalNodes will be zero,
- * so skip the bounds checking.
+ * so skip the bounds checking. Also, we should ignore the header node when
+ * checking for invalid forwards and backwards links, since the header node's
+ * links can point back to itself legitimately.
*/
if (btcb->totalNodes != 0) {
if (srcDesc->fLink >= btcb->totalNodes) {
error = fsBTInvalidHeaderErr;
goto fail;
}
+
+ if ((src->blockNum != 0) && (srcDesc->fLink == (u_int32_t) src->blockNum)) {
+ printf("hfs_swap_BTNode: invalid forward link (0x%08x == 0x%08x)\n",
+ srcDesc->fLink, (u_int32_t) src->blockNum);
+ error = fsBTInvalidHeaderErr;
+ goto fail;
+ }
+ if ((src->blockNum != 0) && (srcDesc->bLink == (u_int32_t) src->blockNum)) {
+ printf("hfs_swap_BTNode: invalid backward link (0x%08x == 0x%08x)\n",
+ srcDesc->bLink, (u_int32_t) src->blockNum);
+ error = fsBTInvalidHeaderErr;
+ goto fail;
+ }
+
+
}
/*
* Sanity check: must be even, and within the node itself.
*
* We may be called to swap an unused node, which contains all zeroes.
- * This is why we allow the record offset to be zero.
+ * Unused nodes are expected only when allow_empty_node is true.
+ * If it is false and record offset is zero, return error.
*/
- if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) {
+ if ((srcOffs[i] & 1) || (
+ (allow_empty_node == false) && (srcOffs[i] == 0)) ||
+ (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) ||
+ (srcOffs[i] >= src->blockSize)) {
printf("hfs_swap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]);
error = fsBTInvalidHeaderErr;
goto fail;
if (direction == kSwapBTNodeHostToBig) {
/*
* Sanity check and swap the forward and backward links.
+ * Ignore the header node since its forward and backwards links can legitimately
+ * point to itself.
*/
if (srcDesc->fLink >= btcb->totalNodes) {
panic("hfs_UNswap_BTNode: invalid forward link (0x%08X)\n", srcDesc->fLink);
error = fsBTInvalidHeaderErr;
goto fail;
}
+ if ((src->blockNum != 0) && (srcDesc->fLink == (u_int32_t) src->blockNum)) {
+ panic ("hfs_UNswap_BTNode: invalid forward link (0x%08x == 0x%08x)\n",
+ srcDesc->fLink, (u_int32_t) src->blockNum);
+ error = fsBTInvalidHeaderErr;
+ goto fail;
+ }
+
if (srcDesc->bLink >= btcb->totalNodes) {
panic("hfs_UNswap_BTNode: invalid backward link (0x%08X)\n", srcDesc->bLink);
error = fsBTInvalidHeaderErr;
goto fail;
}
+ if ((src->blockNum != 0) && (srcDesc->bLink == (u_int32_t) src->blockNum)) {
+ panic ("hfs_UNswap_BTNode: invalid backward link (0x%08x == 0x%08x)\n",
+ srcDesc->bLink, (u_int32_t) src->blockNum);
+ error = fsBTInvalidHeaderErr;
+ goto fail;
+ }
+
+
srcDesc->fLink = SWAP_BE32 (srcDesc->fLink);
srcDesc->bLink = SWAP_BE32 (srcDesc->bLink);
* Sanity check: must be even, and within the node itself.
*
* We may be called to swap an unused node, which contains all zeroes.
+ * This can happen when the last record from a node gets deleted.
* This is why we allow the record offset to be zero.
+ * Unused nodes are expected only when allow_empty_node is true
+ * (the caller should set it to true for kSwapBTNodeBigToHost).
*/
- if ((srcOffs[i] & 1) || (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) || (srcOffs[i] >= src->blockSize)) {
+ if ((srcOffs[i] & 1) ||
+ ((allow_empty_node == false) && (srcOffs[i] == 0)) ||
+ (srcOffs[i] < sizeof(BTNodeDescriptor) && srcOffs[i] != 0) ||
+ (srcOffs[i] >= src->blockSize)) {
panic("hfs_UNswap_BTNode: record #%d invalid offset (0x%04X)\n", srcDesc->numRecords-i-1, srcOffs[i]);
error = fsBTInvalidHeaderErr;
goto fail;
/*
* Log some useful information about where the corrupt node is.
*/
- printf("node=%lld fileID=%u volume=%s device=%s\n", src->blockNum, VTOC(vp)->c_fileid,
+ printf("hfs: node=%lld fileID=%u volume=%s device=%s\n", src->blockNum, VTOC(vp)->c_fileid,
VTOVCB(vp)->vcbVN, vfs_statfs(vnode_mount(vp))->f_mntfromname);
hfs_mark_volume_inconsistent(VTOVCB(vp));
}
* to be sure the current record doesn't overflow into the next
* record.
*/
- nextRecord = (char *)src->buffer + srcOffs[i-1];
+ nextRecord = (char *)src->buffer + (uintptr_t)(srcOffs[i-1]);
/*
* Make sure we can safely dereference the keyLength and parentID fields.