diff options
Diffstat (limited to 'fs/btrfs/tree-checker.c')
-rw-r--r-- | fs/btrfs/tree-checker.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index d7f4a3468945..ae4361cc6db5 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c | |||
@@ -678,6 +678,97 @@ static int check_dev_item(struct btrfs_fs_info *fs_info, | |||
678 | return 0; | 678 | return 0; |
679 | } | 679 | } |
680 | 680 | ||
681 | /* Inode item error output has the same format as dir_item_err() */ | ||
682 | #define inode_item_err(fs_info, eb, slot, fmt, ...) \ | ||
683 | dir_item_err(fs_info, eb, slot, fmt, __VA_ARGS__) | ||
684 | |||
685 | static int check_inode_item(struct btrfs_fs_info *fs_info, | ||
686 | struct extent_buffer *leaf, | ||
687 | struct btrfs_key *key, int slot) | ||
688 | { | ||
689 | struct btrfs_inode_item *iitem; | ||
690 | u64 super_gen = btrfs_super_generation(fs_info->super_copy); | ||
691 | u32 valid_mask = (S_IFMT | S_ISUID | S_ISGID | S_ISVTX | 0777); | ||
692 | u32 mode; | ||
693 | |||
694 | if ((key->objectid < BTRFS_FIRST_FREE_OBJECTID || | ||
695 | key->objectid > BTRFS_LAST_FREE_OBJECTID) && | ||
696 | key->objectid != BTRFS_ROOT_TREE_DIR_OBJECTID && | ||
697 | key->objectid != BTRFS_FREE_INO_OBJECTID) { | ||
698 | generic_err(fs_info, leaf, slot, | ||
699 | "invalid key objectid: has %llu expect %llu or [%llu, %llu] or %llu", | ||
700 | key->objectid, BTRFS_ROOT_TREE_DIR_OBJECTID, | ||
701 | BTRFS_FIRST_FREE_OBJECTID, | ||
702 | BTRFS_LAST_FREE_OBJECTID, | ||
703 | BTRFS_FREE_INO_OBJECTID); | ||
704 | return -EUCLEAN; | ||
705 | } | ||
706 | if (key->offset != 0) { | ||
707 | inode_item_err(fs_info, leaf, slot, | ||
708 | "invalid key offset: has %llu expect 0", | ||
709 | key->offset); | ||
710 | return -EUCLEAN; | ||
711 | } | ||
712 | iitem = btrfs_item_ptr(leaf, slot, struct btrfs_inode_item); | ||
713 | |||
714 | /* Here we use super block generation + 1 to handle log tree */ | ||
715 | if (btrfs_inode_generation(leaf, iitem) > super_gen + 1) { | ||
716 | inode_item_err(fs_info, leaf, slot, | ||
717 | "invalid inode generation: has %llu expect (0, %llu]", | ||
718 | btrfs_inode_generation(leaf, iitem), | ||
719 | super_gen + 1); | ||
720 | return -EUCLEAN; | ||
721 | } | ||
722 | /* Note for ROOT_TREE_DIR_ITEM, mkfs could set its transid 0 */ | ||
723 | if (btrfs_inode_transid(leaf, iitem) > super_gen + 1) { | ||
724 | inode_item_err(fs_info, leaf, slot, | ||
725 | "invalid inode generation: has %llu expect [0, %llu]", | ||
726 | btrfs_inode_transid(leaf, iitem), super_gen + 1); | ||
727 | return -EUCLEAN; | ||
728 | } | ||
729 | |||
730 | /* | ||
731 | * For size and nbytes it's better not to be too strict, as for dir | ||
732 | * item its size/nbytes can easily get wrong, but doesn't affect | ||
733 | * anything in the fs. So here we skip the check. | ||
734 | */ | ||
735 | mode = btrfs_inode_mode(leaf, iitem); | ||
736 | if (mode & ~valid_mask) { | ||
737 | inode_item_err(fs_info, leaf, slot, | ||
738 | "unknown mode bit detected: 0x%x", | ||
739 | mode & ~valid_mask); | ||
740 | return -EUCLEAN; | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * S_IFMT is not bit mapped so we can't completely rely on is_power_of_2, | ||
745 | * but is_power_of_2() can save us from checking FIFO/CHR/DIR/REG. | ||
746 | * Only needs to check BLK, LNK and SOCKS | ||
747 | */ | ||
748 | if (!is_power_of_2(mode & S_IFMT)) { | ||
749 | if (!S_ISLNK(mode) && !S_ISBLK(mode) && !S_ISSOCK(mode)) { | ||
750 | inode_item_err(fs_info, leaf, slot, | ||
751 | "invalid mode: has 0%o expect valid S_IF* bit(s)", | ||
752 | mode & S_IFMT); | ||
753 | return -EUCLEAN; | ||
754 | } | ||
755 | } | ||
756 | if (S_ISDIR(mode) && btrfs_inode_nlink(leaf, iitem) > 1) { | ||
757 | inode_item_err(fs_info, leaf, slot, | ||
758 | "invalid nlink: has %u expect no more than 1 for dir", | ||
759 | btrfs_inode_nlink(leaf, iitem)); | ||
760 | return -EUCLEAN; | ||
761 | } | ||
762 | if (btrfs_inode_flags(leaf, iitem) & ~BTRFS_INODE_FLAG_MASK) { | ||
763 | inode_item_err(fs_info, leaf, slot, | ||
764 | "unknown flags detected: 0x%llx", | ||
765 | btrfs_inode_flags(leaf, iitem) & | ||
766 | ~BTRFS_INODE_FLAG_MASK); | ||
767 | return -EUCLEAN; | ||
768 | } | ||
769 | return 0; | ||
770 | } | ||
771 | |||
681 | /* | 772 | /* |
682 | * Common point to switch the item-specific validation. | 773 | * Common point to switch the item-specific validation. |
683 | */ | 774 | */ |
@@ -711,6 +802,9 @@ static int check_leaf_item(struct btrfs_fs_info *fs_info, | |||
711 | case BTRFS_DEV_ITEM_KEY: | 802 | case BTRFS_DEV_ITEM_KEY: |
712 | ret = check_dev_item(fs_info, leaf, key, slot); | 803 | ret = check_dev_item(fs_info, leaf, key, slot); |
713 | break; | 804 | break; |
805 | case BTRFS_INODE_ITEM_KEY: | ||
806 | ret = check_inode_item(fs_info, leaf, key, slot); | ||
807 | break; | ||
714 | } | 808 | } |
715 | return ret; | 809 | return ret; |
716 | } | 810 | } |