diff options
author | Darrick J. Wong <djwong@us.ibm.com> | 2012-04-29 18:31:10 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-04-29 18:31:10 -0400 |
commit | 814525f4df50a196464ce2c7abe91f693203060f (patch) | |
tree | 221de5b0331240ff6bc99a2dd7c48460e1305075 /fs/ext4 | |
parent | a9c4731780544d52b243bf46e4dd635c67fa9f84 (diff) |
ext4: calculate and verify inode checksums
This patch introduces to ext4 the ability to calculate and verify
inode checksums. This requires the use of a new ro compatibility flag
and some accompanying e2fsprogs patches to provide the relevant
features in tune2fs and e2fsck. The inode generation changes have
been integrated into this patch.
Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/ext4.h | 3 | ||||
-rw-r--r-- | fs/ext4/ialloc.c | 13 | ||||
-rw-r--r-- | fs/ext4/inode.c | 111 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 7 |
4 files changed, 126 insertions, 8 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5adbce519b66..ca96401b4b81 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -915,6 +915,9 @@ struct ext4_inode_info { | |||
915 | */ | 915 | */ |
916 | tid_t i_sync_tid; | 916 | tid_t i_sync_tid; |
917 | tid_t i_datasync_tid; | 917 | tid_t i_datasync_tid; |
918 | |||
919 | /* Precomputed uuid+inum+igen checksum for seeding inode checksums */ | ||
920 | __u32 i_csum_seed; | ||
918 | }; | 921 | }; |
919 | 922 | ||
920 | /* | 923 | /* |
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 409c2ee7750a..8207dfab2682 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c | |||
@@ -850,6 +850,19 @@ got: | |||
850 | inode->i_generation = sbi->s_next_generation++; | 850 | inode->i_generation = sbi->s_next_generation++; |
851 | spin_unlock(&sbi->s_next_gen_lock); | 851 | spin_unlock(&sbi->s_next_gen_lock); |
852 | 852 | ||
853 | /* Precompute checksum seed for inode metadata */ | ||
854 | if (EXT4_HAS_RO_COMPAT_FEATURE(sb, | ||
855 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { | ||
856 | __u32 csum; | ||
857 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
858 | __le32 inum = cpu_to_le32(inode->i_ino); | ||
859 | __le32 gen = cpu_to_le32(inode->i_generation); | ||
860 | csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, | ||
861 | sizeof(inum)); | ||
862 | ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, | ||
863 | sizeof(gen)); | ||
864 | } | ||
865 | |||
853 | ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ | 866 | ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ |
854 | ext4_set_inode_state(inode, EXT4_STATE_NEW); | 867 | ext4_set_inode_state(inode, EXT4_STATE_NEW); |
855 | 868 | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8bc21ecc1df5..16a67359b4c7 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -47,6 +47,73 @@ | |||
47 | 47 | ||
48 | #define MPAGE_DA_EXTENT_TAIL 0x01 | 48 | #define MPAGE_DA_EXTENT_TAIL 0x01 |
49 | 49 | ||
50 | static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw, | ||
51 | struct ext4_inode_info *ei) | ||
52 | { | ||
53 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
54 | __u16 csum_lo; | ||
55 | __u16 csum_hi = 0; | ||
56 | __u32 csum; | ||
57 | |||
58 | csum_lo = raw->i_checksum_lo; | ||
59 | raw->i_checksum_lo = 0; | ||
60 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && | ||
61 | EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) { | ||
62 | csum_hi = raw->i_checksum_hi; | ||
63 | raw->i_checksum_hi = 0; | ||
64 | } | ||
65 | |||
66 | csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)raw, | ||
67 | EXT4_INODE_SIZE(inode->i_sb)); | ||
68 | |||
69 | raw->i_checksum_lo = csum_lo; | ||
70 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && | ||
71 | EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) | ||
72 | raw->i_checksum_hi = csum_hi; | ||
73 | |||
74 | return csum; | ||
75 | } | ||
76 | |||
77 | static int ext4_inode_csum_verify(struct inode *inode, struct ext4_inode *raw, | ||
78 | struct ext4_inode_info *ei) | ||
79 | { | ||
80 | __u32 provided, calculated; | ||
81 | |||
82 | if (EXT4_SB(inode->i_sb)->s_es->s_creator_os != | ||
83 | cpu_to_le32(EXT4_OS_LINUX) || | ||
84 | !EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
85 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) | ||
86 | return 1; | ||
87 | |||
88 | provided = le16_to_cpu(raw->i_checksum_lo); | ||
89 | calculated = ext4_inode_csum(inode, raw, ei); | ||
90 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && | ||
91 | EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) | ||
92 | provided |= ((__u32)le16_to_cpu(raw->i_checksum_hi)) << 16; | ||
93 | else | ||
94 | calculated &= 0xFFFF; | ||
95 | |||
96 | return provided == calculated; | ||
97 | } | ||
98 | |||
99 | static void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw, | ||
100 | struct ext4_inode_info *ei) | ||
101 | { | ||
102 | __u32 csum; | ||
103 | |||
104 | if (EXT4_SB(inode->i_sb)->s_es->s_creator_os != | ||
105 | cpu_to_le32(EXT4_OS_LINUX) || | ||
106 | !EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
107 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) | ||
108 | return; | ||
109 | |||
110 | csum = ext4_inode_csum(inode, raw, ei); | ||
111 | raw->i_checksum_lo = cpu_to_le16(csum & 0xFFFF); | ||
112 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && | ||
113 | EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) | ||
114 | raw->i_checksum_hi = cpu_to_le16(csum >> 16); | ||
115 | } | ||
116 | |||
50 | static inline int ext4_begin_ordered_truncate(struct inode *inode, | 117 | static inline int ext4_begin_ordered_truncate(struct inode *inode, |
51 | loff_t new_size) | 118 | loff_t new_size) |
52 | { | 119 | { |
@@ -3644,6 +3711,39 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
3644 | if (ret < 0) | 3711 | if (ret < 0) |
3645 | goto bad_inode; | 3712 | goto bad_inode; |
3646 | raw_inode = ext4_raw_inode(&iloc); | 3713 | raw_inode = ext4_raw_inode(&iloc); |
3714 | |||
3715 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { | ||
3716 | ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize); | ||
3717 | if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > | ||
3718 | EXT4_INODE_SIZE(inode->i_sb)) { | ||
3719 | EXT4_ERROR_INODE(inode, "bad extra_isize (%u != %u)", | ||
3720 | EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize, | ||
3721 | EXT4_INODE_SIZE(inode->i_sb)); | ||
3722 | ret = -EIO; | ||
3723 | goto bad_inode; | ||
3724 | } | ||
3725 | } else | ||
3726 | ei->i_extra_isize = 0; | ||
3727 | |||
3728 | /* Precompute checksum seed for inode metadata */ | ||
3729 | if (EXT4_HAS_RO_COMPAT_FEATURE(sb, | ||
3730 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { | ||
3731 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
3732 | __u32 csum; | ||
3733 | __le32 inum = cpu_to_le32(inode->i_ino); | ||
3734 | __le32 gen = raw_inode->i_generation; | ||
3735 | csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, | ||
3736 | sizeof(inum)); | ||
3737 | ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, | ||
3738 | sizeof(gen)); | ||
3739 | } | ||
3740 | |||
3741 | if (!ext4_inode_csum_verify(inode, raw_inode, ei)) { | ||
3742 | EXT4_ERROR_INODE(inode, "checksum invalid"); | ||
3743 | ret = -EIO; | ||
3744 | goto bad_inode; | ||
3745 | } | ||
3746 | |||
3647 | inode->i_mode = le16_to_cpu(raw_inode->i_mode); | 3747 | inode->i_mode = le16_to_cpu(raw_inode->i_mode); |
3648 | inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); | 3748 | inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); |
3649 | inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); | 3749 | inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); |
@@ -3721,12 +3821,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
3721 | } | 3821 | } |
3722 | 3822 | ||
3723 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { | 3823 | if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { |
3724 | ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize); | ||
3725 | if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > | ||
3726 | EXT4_INODE_SIZE(inode->i_sb)) { | ||
3727 | ret = -EIO; | ||
3728 | goto bad_inode; | ||
3729 | } | ||
3730 | if (ei->i_extra_isize == 0) { | 3824 | if (ei->i_extra_isize == 0) { |
3731 | /* The extra space is currently unused. Use it. */ | 3825 | /* The extra space is currently unused. Use it. */ |
3732 | ei->i_extra_isize = sizeof(struct ext4_inode) - | 3826 | ei->i_extra_isize = sizeof(struct ext4_inode) - |
@@ -3738,8 +3832,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
3738 | if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) | 3832 | if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) |
3739 | ext4_set_inode_state(inode, EXT4_STATE_XATTR); | 3833 | ext4_set_inode_state(inode, EXT4_STATE_XATTR); |
3740 | } | 3834 | } |
3741 | } else | 3835 | } |
3742 | ei->i_extra_isize = 0; | ||
3743 | 3836 | ||
3744 | EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode); | 3837 | EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode); |
3745 | EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode); | 3838 | EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode); |
@@ -3963,6 +4056,8 @@ static int ext4_do_update_inode(handle_t *handle, | |||
3963 | raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize); | 4056 | raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize); |
3964 | } | 4057 | } |
3965 | 4058 | ||
4059 | ext4_inode_csum_set(inode, raw_inode, ei); | ||
4060 | |||
3966 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); | 4061 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); |
3967 | rc = ext4_handle_dirty_metadata(handle, NULL, bh); | 4062 | rc = ext4_handle_dirty_metadata(handle, NULL, bh); |
3968 | if (!err) | 4063 | if (!err) |
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 6eee25591b81..feba55a225a6 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -152,6 +152,13 @@ flags_out: | |||
152 | if (!inode_owner_or_capable(inode)) | 152 | if (!inode_owner_or_capable(inode)) |
153 | return -EPERM; | 153 | return -EPERM; |
154 | 154 | ||
155 | if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
156 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { | ||
157 | ext4_warning(sb, "Setting inode version is not " | ||
158 | "supported with metadata_csum enabled."); | ||
159 | return -ENOTTY; | ||
160 | } | ||
161 | |||
155 | err = mnt_want_write_file(filp); | 162 | err = mnt_want_write_file(filp); |
156 | if (err) | 163 | if (err) |
157 | return err; | 164 | return err; |