aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2016-07-21 19:51:05 -0400
committerDave Chinner <david@fromorbit.com>2016-07-21 19:51:05 -0400
commit160ae76fa1a2ee2345cb66eb343e24a34d2f051d (patch)
tree695641e7d4e829855525797715fc982ae4f76f05
parentf021bd071f06b545926b1031348873b05442139f (diff)
libxfs: directory node splitting does not have an extra block
xfsprogs source commit 4280e59dcbc4cd8e01585efe788a68eb378048e8 xfs_da3_split() has to handle all three versions of the directory/attribute btree structure. The attr tree is v1, the dir tre is v2 or v3. The main difference between the v1 and v2/3 trees is the way tree nodes are split - in the v1 tree we can require a double split to occur because the object to be inserted may be larger than the space made by splitting a leaf. In this case we need to do a double split - one to split the full leaf, then another to allocate an empty leaf block in the correct location for the new entry. This does not happen with dir (v2/v3) formats as the objects being inserted are always guaranteed to fit into the new space in the split blocks. Indeed, for directories they *may* be an extra block on this buffer pointer. However, it's guaranteed not to be a leaf block (i.e. a directory data block) - the directory code only ever places hash index or free space blocks in this pointer (as a cursor of sorts), and so to use it as a directory data block will immediately corrupt the directory. The problem is that the code assumes that there may be extra blocks that we need to link into the tree once we've split the root, but this is not true for either dir or attr trees, because the extra attr block is always consumed by the last node split before we split the root. Hence the linking in an extra block is always wrong at the root split level, and this manifests itself in repair as a directory corruption in a repaired directory, leaving the directory rebuild incomplete. This is a dir v2 zero-day bug - it was in the initial dir v2 commit that was made back in February 1998. Fix this by ensuring the linking of the blocks after the root split never tries to make use of the extra blocks that may be held in the cursor. They are held there for other purposes and should never be touched by the root splitting code. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r--fs/xfs/libxfs/xfs_da_btree.c59
1 files changed, 29 insertions, 30 deletions
diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c
index 097bf7717d80..0f1f165f4048 100644
--- a/fs/xfs/libxfs/xfs_da_btree.c
+++ b/fs/xfs/libxfs/xfs_da_btree.c
@@ -356,7 +356,6 @@ xfs_da3_split(
356 struct xfs_da_state_blk *newblk; 356 struct xfs_da_state_blk *newblk;
357 struct xfs_da_state_blk *addblk; 357 struct xfs_da_state_blk *addblk;
358 struct xfs_da_intnode *node; 358 struct xfs_da_intnode *node;
359 struct xfs_buf *bp;
360 int max; 359 int max;
361 int action = 0; 360 int action = 0;
362 int error; 361 int error;
@@ -397,7 +396,9 @@ xfs_da3_split(
397 break; 396 break;
398 } 397 }
399 /* 398 /*
400 * Entry wouldn't fit, split the leaf again. 399 * Entry wouldn't fit, split the leaf again. The new
400 * extrablk will be consumed by xfs_da3_node_split if
401 * the node is split.
401 */ 402 */
402 state->extravalid = 1; 403 state->extravalid = 1;
403 if (state->inleaf) { 404 if (state->inleaf) {
@@ -446,6 +447,14 @@ xfs_da3_split(
446 return 0; 447 return 0;
447 448
448 /* 449 /*
450 * xfs_da3_node_split() should have consumed any extra blocks we added
451 * during a double leaf split in the attr fork. This is guaranteed as
452 * we can't be here if the attr fork only has a single leaf block.
453 */
454 ASSERT(state->extravalid == 0 ||
455 state->path.blk[max].magic == XFS_DIR2_LEAFN_MAGIC);
456
457 /*
449 * Split the root node. 458 * Split the root node.
450 */ 459 */
451 ASSERT(state->path.active == 0); 460 ASSERT(state->path.active == 0);
@@ -457,43 +466,33 @@ xfs_da3_split(
457 } 466 }
458 467
459 /* 468 /*
460 * Update pointers to the node which used to be block 0 and 469 * Update pointers to the node which used to be block 0 and just got
461 * just got bumped because of the addition of a new root node. 470 * bumped because of the addition of a new root node. Note that the
462 * There might be three blocks involved if a double split occurred, 471 * original block 0 could be at any position in the list of blocks in
463 * and the original block 0 could be at any position in the list. 472 * the tree.
464 * 473 *
465 * Note: the magic numbers and sibling pointers are in the same 474 * Note: the magic numbers and sibling pointers are in the same physical
466 * physical place for both v2 and v3 headers (by design). Hence it 475 * place for both v2 and v3 headers (by design). Hence it doesn't matter
467 * doesn't matter which version of the xfs_da_intnode structure we use 476 * which version of the xfs_da_intnode structure we use here as the
468 * here as the result will be the same using either structure. 477 * result will be the same using either structure.
469 */ 478 */
470 node = oldblk->bp->b_addr; 479 node = oldblk->bp->b_addr;
471 if (node->hdr.info.forw) { 480 if (node->hdr.info.forw) {
472 if (be32_to_cpu(node->hdr.info.forw) == addblk->blkno) { 481 ASSERT(be32_to_cpu(node->hdr.info.forw) == addblk->blkno);
473 bp = addblk->bp; 482 node = addblk->bp->b_addr;
474 } else {
475 ASSERT(state->extravalid);
476 bp = state->extrablk.bp;
477 }
478 node = bp->b_addr;
479 node->hdr.info.back = cpu_to_be32(oldblk->blkno); 483 node->hdr.info.back = cpu_to_be32(oldblk->blkno);
480 xfs_trans_log_buf(state->args->trans, bp, 484 xfs_trans_log_buf(state->args->trans, addblk->bp,
481 XFS_DA_LOGRANGE(node, &node->hdr.info, 485 XFS_DA_LOGRANGE(node, &node->hdr.info,
482 sizeof(node->hdr.info))); 486 sizeof(node->hdr.info)));
483 } 487 }
484 node = oldblk->bp->b_addr; 488 node = oldblk->bp->b_addr;
485 if (node->hdr.info.back) { 489 if (node->hdr.info.back) {
486 if (be32_to_cpu(node->hdr.info.back) == addblk->blkno) { 490 ASSERT(be32_to_cpu(node->hdr.info.back) == addblk->blkno);
487 bp = addblk->bp; 491 node = addblk->bp->b_addr;
488 } else {
489 ASSERT(state->extravalid);
490 bp = state->extrablk.bp;
491 }
492 node = bp->b_addr;
493 node->hdr.info.forw = cpu_to_be32(oldblk->blkno); 492 node->hdr.info.forw = cpu_to_be32(oldblk->blkno);
494 xfs_trans_log_buf(state->args->trans, bp, 493 xfs_trans_log_buf(state->args->trans, addblk->bp,
495 XFS_DA_LOGRANGE(node, &node->hdr.info, 494 XFS_DA_LOGRANGE(node, &node->hdr.info,
496 sizeof(node->hdr.info))); 495 sizeof(node->hdr.info)));
497 } 496 }
498 addblk->bp = NULL; 497 addblk->bp = NULL;
499 return 0; 498 return 0;