diff options
author | Barry Naujok <bnaujok@sgi.com> | 2008-06-22 23:25:38 -0400 |
---|---|---|
committer | Niv Sardi <xaiki@debian.org> | 2008-07-28 02:59:01 -0400 |
commit | 90bb7ab077a63facbe3aa0b9e3763a0cb956a4c1 (patch) | |
tree | fae24e57f69a35c6de32910e2b5d75b7df0f3c04 /fs/xfs/xfs_dir2_node.c | |
parent | e5700704b2b0853c059e424284cceeff3032ea28 (diff) |
[XFS] Fix returning case-preserved name with CI node form directories
xfs_dir2_node_lookup() calls xfs_da_node_lookup_int() which iterates
through leaf blocks containing the matching hash value for the name being
looked up. Inside xfs_da_node_lookup_int(), it calls the
xfs_dir2_leafn_lookup_for_entry() for each leaf block.
xfs_dir2_leafn_lookup_for_entry() iterates through each matching
hash/offset pair doing a name comparison to find the matching dirent.
For CI mode, the state->extrablk retains the details of the block that has
the CI match so xfs_dir2_node_lookup() can return the case-preserved name.
The original implementation didn't retain the xfs_da_buf_t properly, so
the lookup was returning a bogus name to be stored in the dentry.
In the case of unlink, the bad name was passed and in debug mode, ASSERTed
when it can't find the entry.
SGI-PV: 983284
SGI-Modid: xfs-linux-melb:xfs-kern:31337a
Signed-off-by: Barry Naujok <bnaujok@sgi.com>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_dir2_node.c')
-rw-r--r-- | fs/xfs/xfs_dir2_node.c | 69 |
1 files changed, 43 insertions, 26 deletions
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index 1b5430223461..fa6c3a5ddbc6 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c | |||
@@ -549,7 +549,6 @@ xfs_dir2_leafn_lookup_for_entry( | |||
549 | xfs_dir2_data_entry_t *dep; /* data block entry */ | 549 | xfs_dir2_data_entry_t *dep; /* data block entry */ |
550 | xfs_inode_t *dp; /* incore directory inode */ | 550 | xfs_inode_t *dp; /* incore directory inode */ |
551 | int error; /* error return value */ | 551 | int error; /* error return value */ |
552 | int di = -1; /* data entry index */ | ||
553 | int index; /* leaf entry index */ | 552 | int index; /* leaf entry index */ |
554 | xfs_dir2_leaf_t *leaf; /* leaf structure */ | 553 | xfs_dir2_leaf_t *leaf; /* leaf structure */ |
555 | xfs_dir2_leaf_entry_t *lep; /* leaf entry */ | 554 | xfs_dir2_leaf_entry_t *lep; /* leaf entry */ |
@@ -577,7 +576,6 @@ xfs_dir2_leafn_lookup_for_entry( | |||
577 | if (state->extravalid) { | 576 | if (state->extravalid) { |
578 | curbp = state->extrablk.bp; | 577 | curbp = state->extrablk.bp; |
579 | curdb = state->extrablk.blkno; | 578 | curdb = state->extrablk.blkno; |
580 | di = state->extrablk.index; | ||
581 | } | 579 | } |
582 | /* | 580 | /* |
583 | * Loop over leaf entries with the right hash value. | 581 | * Loop over leaf entries with the right hash value. |
@@ -602,17 +600,27 @@ xfs_dir2_leafn_lookup_for_entry( | |||
602 | */ | 600 | */ |
603 | if (newdb != curdb) { | 601 | if (newdb != curdb) { |
604 | /* | 602 | /* |
605 | * If we had a block before, drop it. | 603 | * If we had a block before that we aren't saving |
604 | * for a CI name, drop it | ||
606 | */ | 605 | */ |
607 | if (curbp) | 606 | if (curbp && (args->cmpresult == XFS_CMP_DIFFERENT || |
607 | curdb != state->extrablk.blkno)) | ||
608 | xfs_da_brelse(tp, curbp); | 608 | xfs_da_brelse(tp, curbp); |
609 | /* | 609 | /* |
610 | * Read the data block. | 610 | * If needing the block that is saved with a CI match, |
611 | * use it otherwise read in the new data block. | ||
611 | */ | 612 | */ |
612 | error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp, | 613 | if (args->cmpresult != XFS_CMP_DIFFERENT && |
613 | newdb), -1, &curbp, XFS_DATA_FORK); | 614 | newdb == state->extrablk.blkno) { |
614 | if (error) | 615 | ASSERT(state->extravalid); |
615 | return error; | 616 | curbp = state->extrablk.bp; |
617 | } else { | ||
618 | error = xfs_da_read_buf(tp, dp, | ||
619 | xfs_dir2_db_to_da(mp, newdb), | ||
620 | -1, &curbp, XFS_DATA_FORK); | ||
621 | if (error) | ||
622 | return error; | ||
623 | } | ||
616 | xfs_dir2_data_check(dp, curbp); | 624 | xfs_dir2_data_check(dp, curbp); |
617 | curdb = newdb; | 625 | curdb = newdb; |
618 | } | 626 | } |
@@ -624,38 +632,47 @@ xfs_dir2_leafn_lookup_for_entry( | |||
624 | /* | 632 | /* |
625 | * Compare the entry and if it's an exact match, return | 633 | * Compare the entry and if it's an exact match, return |
626 | * EEXIST immediately. If it's the first case-insensitive | 634 | * EEXIST immediately. If it's the first case-insensitive |
627 | * match, store the inode number and continue looking. | 635 | * match, store the block & inode number and continue looking. |
628 | */ | 636 | */ |
629 | cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen); | 637 | cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen); |
630 | if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { | 638 | if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { |
639 | /* If there is a CI match block, drop it */ | ||
640 | if (args->cmpresult != XFS_CMP_DIFFERENT && | ||
641 | curdb != state->extrablk.blkno) | ||
642 | xfs_da_brelse(tp, state->extrablk.bp); | ||
631 | args->cmpresult = cmp; | 643 | args->cmpresult = cmp; |
632 | args->inumber = be64_to_cpu(dep->inumber); | 644 | args->inumber = be64_to_cpu(dep->inumber); |
633 | di = (int)((char *)dep - (char *)curbp->data); | 645 | *indexp = index; |
634 | error = EEXIST; | 646 | state->extravalid = 1; |
647 | state->extrablk.bp = curbp; | ||
648 | state->extrablk.blkno = curdb; | ||
649 | state->extrablk.index = (int)((char *)dep - | ||
650 | (char *)curbp->data); | ||
651 | state->extrablk.magic = XFS_DIR2_DATA_MAGIC; | ||
635 | if (cmp == XFS_CMP_EXACT) | 652 | if (cmp == XFS_CMP_EXACT) |
636 | goto out; | 653 | return XFS_ERROR(EEXIST); |
637 | } | 654 | } |
638 | } | 655 | } |
639 | /* Didn't find an exact match. */ | ||
640 | error = ENOENT; | ||
641 | ASSERT(index == be16_to_cpu(leaf->hdr.count) || | 656 | ASSERT(index == be16_to_cpu(leaf->hdr.count) || |
642 | (args->op_flags & XFS_DA_OP_OKNOENT)); | 657 | (args->op_flags & XFS_DA_OP_OKNOENT)); |
643 | out: | ||
644 | if (curbp) { | 658 | if (curbp) { |
645 | /* Giving back a data block. */ | 659 | if (args->cmpresult == XFS_CMP_DIFFERENT) { |
646 | state->extravalid = 1; | 660 | /* Giving back last used data block. */ |
647 | state->extrablk.bp = curbp; | 661 | state->extravalid = 1; |
648 | state->extrablk.index = di; | 662 | state->extrablk.bp = curbp; |
649 | state->extrablk.blkno = curdb; | 663 | state->extrablk.index = -1; |
650 | state->extrablk.magic = XFS_DIR2_DATA_MAGIC; | 664 | state->extrablk.blkno = curdb; |
665 | state->extrablk.magic = XFS_DIR2_DATA_MAGIC; | ||
666 | } else { | ||
667 | /* If the curbp is not the CI match block, drop it */ | ||
668 | if (state->extrablk.bp != curbp) | ||
669 | xfs_da_brelse(tp, curbp); | ||
670 | } | ||
651 | } else { | 671 | } else { |
652 | state->extravalid = 0; | 672 | state->extravalid = 0; |
653 | } | 673 | } |
654 | /* | ||
655 | * Return the index, that will be the deletion point for remove/replace. | ||
656 | */ | ||
657 | *indexp = index; | 674 | *indexp = index; |
658 | return XFS_ERROR(error); | 675 | return XFS_ERROR(ENOENT); |
659 | } | 676 | } |
660 | 677 | ||
661 | /* | 678 | /* |