diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_dir2_sf.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_dir2_sf.c | 63 |
1 files changed, 41 insertions, 22 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c index 96b45cd6c63f..e84af093b2ab 100644 --- a/fs/xfs/libxfs/xfs_dir2_sf.c +++ b/fs/xfs/libxfs/xfs_dir2_sf.c | |||
@@ -632,36 +632,49 @@ xfs_dir2_sf_check( | |||
632 | /* Verify the consistency of an inline directory. */ | 632 | /* Verify the consistency of an inline directory. */ |
633 | int | 633 | int |
634 | xfs_dir2_sf_verify( | 634 | xfs_dir2_sf_verify( |
635 | struct xfs_mount *mp, | 635 | struct xfs_inode *ip) |
636 | struct xfs_dir2_sf_hdr *sfp, | ||
637 | int size) | ||
638 | { | 636 | { |
637 | struct xfs_mount *mp = ip->i_mount; | ||
638 | struct xfs_dir2_sf_hdr *sfp; | ||
639 | struct xfs_dir2_sf_entry *sfep; | 639 | struct xfs_dir2_sf_entry *sfep; |
640 | struct xfs_dir2_sf_entry *next_sfep; | 640 | struct xfs_dir2_sf_entry *next_sfep; |
641 | char *endp; | 641 | char *endp; |
642 | const struct xfs_dir_ops *dops; | 642 | const struct xfs_dir_ops *dops; |
643 | struct xfs_ifork *ifp; | ||
643 | xfs_ino_t ino; | 644 | xfs_ino_t ino; |
644 | int i; | 645 | int i; |
645 | int i8count; | 646 | int i8count; |
646 | int offset; | 647 | int offset; |
648 | int size; | ||
649 | int error; | ||
647 | __uint8_t filetype; | 650 | __uint8_t filetype; |
648 | 651 | ||
652 | ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_LOCAL); | ||
653 | /* | ||
654 | * xfs_iread calls us before xfs_setup_inode sets up ip->d_ops, | ||
655 | * so we can only trust the mountpoint to have the right pointer. | ||
656 | */ | ||
649 | dops = xfs_dir_get_ops(mp, NULL); | 657 | dops = xfs_dir_get_ops(mp, NULL); |
650 | 658 | ||
659 | ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); | ||
660 | sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data; | ||
661 | size = ifp->if_bytes; | ||
662 | |||
651 | /* | 663 | /* |
652 | * Give up if the directory is way too short. | 664 | * Give up if the directory is way too short. |
653 | */ | 665 | */ |
654 | XFS_WANT_CORRUPTED_RETURN(mp, size > | 666 | if (size <= offsetof(struct xfs_dir2_sf_hdr, parent) || |
655 | offsetof(struct xfs_dir2_sf_hdr, parent)); | 667 | size < xfs_dir2_sf_hdr_size(sfp->i8count)) |
656 | XFS_WANT_CORRUPTED_RETURN(mp, size >= | 668 | return -EFSCORRUPTED; |
657 | xfs_dir2_sf_hdr_size(sfp->i8count)); | ||
658 | 669 | ||
659 | endp = (char *)sfp + size; | 670 | endp = (char *)sfp + size; |
660 | 671 | ||
661 | /* Check .. entry */ | 672 | /* Check .. entry */ |
662 | ino = dops->sf_get_parent_ino(sfp); | 673 | ino = dops->sf_get_parent_ino(sfp); |
663 | i8count = ino > XFS_DIR2_MAX_SHORT_INUM; | 674 | i8count = ino > XFS_DIR2_MAX_SHORT_INUM; |
664 | XFS_WANT_CORRUPTED_RETURN(mp, !xfs_dir_ino_validate(mp, ino)); | 675 | error = xfs_dir_ino_validate(mp, ino); |
676 | if (error) | ||
677 | return error; | ||
665 | offset = dops->data_first_offset; | 678 | offset = dops->data_first_offset; |
666 | 679 | ||
667 | /* Check all reported entries */ | 680 | /* Check all reported entries */ |
@@ -672,12 +685,12 @@ xfs_dir2_sf_verify( | |||
672 | * Check the fixed-offset parts of the structure are | 685 | * Check the fixed-offset parts of the structure are |
673 | * within the data buffer. | 686 | * within the data buffer. |
674 | */ | 687 | */ |
675 | XFS_WANT_CORRUPTED_RETURN(mp, | 688 | if (((char *)sfep + sizeof(*sfep)) >= endp) |
676 | ((char *)sfep + sizeof(*sfep)) < endp); | 689 | return -EFSCORRUPTED; |
677 | 690 | ||
678 | /* Don't allow names with known bad length. */ | 691 | /* Don't allow names with known bad length. */ |
679 | XFS_WANT_CORRUPTED_RETURN(mp, sfep->namelen > 0); | 692 | if (sfep->namelen == 0) |
680 | XFS_WANT_CORRUPTED_RETURN(mp, sfep->namelen < MAXNAMELEN); | 693 | return -EFSCORRUPTED; |
681 | 694 | ||
682 | /* | 695 | /* |
683 | * Check that the variable-length part of the structure is | 696 | * Check that the variable-length part of the structure is |
@@ -685,33 +698,39 @@ xfs_dir2_sf_verify( | |||
685 | * name component, so nextentry is an acceptable test. | 698 | * name component, so nextentry is an acceptable test. |
686 | */ | 699 | */ |
687 | next_sfep = dops->sf_nextentry(sfp, sfep); | 700 | next_sfep = dops->sf_nextentry(sfp, sfep); |
688 | XFS_WANT_CORRUPTED_RETURN(mp, endp >= (char *)next_sfep); | 701 | if (endp < (char *)next_sfep) |
702 | return -EFSCORRUPTED; | ||
689 | 703 | ||
690 | /* Check that the offsets always increase. */ | 704 | /* Check that the offsets always increase. */ |
691 | XFS_WANT_CORRUPTED_RETURN(mp, | 705 | if (xfs_dir2_sf_get_offset(sfep) < offset) |
692 | xfs_dir2_sf_get_offset(sfep) >= offset); | 706 | return -EFSCORRUPTED; |
693 | 707 | ||
694 | /* Check the inode number. */ | 708 | /* Check the inode number. */ |
695 | ino = dops->sf_get_ino(sfp, sfep); | 709 | ino = dops->sf_get_ino(sfp, sfep); |
696 | i8count += ino > XFS_DIR2_MAX_SHORT_INUM; | 710 | i8count += ino > XFS_DIR2_MAX_SHORT_INUM; |
697 | XFS_WANT_CORRUPTED_RETURN(mp, !xfs_dir_ino_validate(mp, ino)); | 711 | error = xfs_dir_ino_validate(mp, ino); |
712 | if (error) | ||
713 | return error; | ||
698 | 714 | ||
699 | /* Check the file type. */ | 715 | /* Check the file type. */ |
700 | filetype = dops->sf_get_ftype(sfep); | 716 | filetype = dops->sf_get_ftype(sfep); |
701 | XFS_WANT_CORRUPTED_RETURN(mp, filetype < XFS_DIR3_FT_MAX); | 717 | if (filetype >= XFS_DIR3_FT_MAX) |
718 | return -EFSCORRUPTED; | ||
702 | 719 | ||
703 | offset = xfs_dir2_sf_get_offset(sfep) + | 720 | offset = xfs_dir2_sf_get_offset(sfep) + |
704 | dops->data_entsize(sfep->namelen); | 721 | dops->data_entsize(sfep->namelen); |
705 | 722 | ||
706 | sfep = next_sfep; | 723 | sfep = next_sfep; |
707 | } | 724 | } |
708 | XFS_WANT_CORRUPTED_RETURN(mp, i8count == sfp->i8count); | 725 | if (i8count != sfp->i8count) |
709 | XFS_WANT_CORRUPTED_RETURN(mp, (void *)sfep == (void *)endp); | 726 | return -EFSCORRUPTED; |
727 | if ((void *)sfep != (void *)endp) | ||
728 | return -EFSCORRUPTED; | ||
710 | 729 | ||
711 | /* Make sure this whole thing ought to be in local format. */ | 730 | /* Make sure this whole thing ought to be in local format. */ |
712 | XFS_WANT_CORRUPTED_RETURN(mp, offset + | 731 | if (offset + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + |
713 | (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + | 732 | (uint)sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) |
714 | (uint)sizeof(xfs_dir2_block_tail_t) <= mp->m_dir_geo->blksize); | 733 | return -EFSCORRUPTED; |
715 | 734 | ||
716 | return 0; | 735 | return 0; |
717 | } | 736 | } |