diff options
author | Theodore Ts'o <tytso@mit.edu> | 2017-03-25 17:22:47 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2017-03-25 17:22:47 -0400 |
commit | dac7a4b4b1f664934e8b713f529b629f67db313c (patch) | |
tree | a0c53abc4e4e65e87db9074460a8ca8de6eef700 /fs/ext4/xattr.c | |
parent | cd9cb405e0b948363811dc74dbb2890f56f2cb87 (diff) |
ext4: lock the xattr block before checksuming it
We must lock the xattr block before calculating or verifying the
checksum in order to avoid spurious checksum failures.
https://bugzilla.kernel.org/show_bug.cgi?id=193661
Reported-by: Colin Ian King <colin.king@canonical.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/ext4/xattr.c')
-rw-r--r-- | fs/ext4/xattr.c | 65 |
1 files changed, 31 insertions, 34 deletions
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 67636acf7624..996e7900d4c8 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c | |||
@@ -131,31 +131,26 @@ static __le32 ext4_xattr_block_csum(struct inode *inode, | |||
131 | } | 131 | } |
132 | 132 | ||
133 | static int ext4_xattr_block_csum_verify(struct inode *inode, | 133 | static int ext4_xattr_block_csum_verify(struct inode *inode, |
134 | sector_t block_nr, | 134 | struct buffer_head *bh) |
135 | struct ext4_xattr_header *hdr) | ||
136 | { | 135 | { |
137 | if (ext4_has_metadata_csum(inode->i_sb) && | 136 | struct ext4_xattr_header *hdr = BHDR(bh); |
138 | (hdr->h_checksum != ext4_xattr_block_csum(inode, block_nr, hdr))) | 137 | int ret = 1; |
139 | return 0; | ||
140 | return 1; | ||
141 | } | ||
142 | |||
143 | static void ext4_xattr_block_csum_set(struct inode *inode, | ||
144 | sector_t block_nr, | ||
145 | struct ext4_xattr_header *hdr) | ||
146 | { | ||
147 | if (!ext4_has_metadata_csum(inode->i_sb)) | ||
148 | return; | ||
149 | 138 | ||
150 | hdr->h_checksum = ext4_xattr_block_csum(inode, block_nr, hdr); | 139 | if (ext4_has_metadata_csum(inode->i_sb)) { |
140 | lock_buffer(bh); | ||
141 | ret = (hdr->h_checksum == ext4_xattr_block_csum(inode, | ||
142 | bh->b_blocknr, hdr)); | ||
143 | unlock_buffer(bh); | ||
144 | } | ||
145 | return ret; | ||
151 | } | 146 | } |
152 | 147 | ||
153 | static inline int ext4_handle_dirty_xattr_block(handle_t *handle, | 148 | static void ext4_xattr_block_csum_set(struct inode *inode, |
154 | struct inode *inode, | 149 | struct buffer_head *bh) |
155 | struct buffer_head *bh) | ||
156 | { | 150 | { |
157 | ext4_xattr_block_csum_set(inode, bh->b_blocknr, BHDR(bh)); | 151 | if (ext4_has_metadata_csum(inode->i_sb)) |
158 | return ext4_handle_dirty_metadata(handle, inode, bh); | 152 | BHDR(bh)->h_checksum = ext4_xattr_block_csum(inode, |
153 | bh->b_blocknr, BHDR(bh)); | ||
159 | } | 154 | } |
160 | 155 | ||
161 | static inline const struct xattr_handler * | 156 | static inline const struct xattr_handler * |
@@ -233,7 +228,7 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) | |||
233 | if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) || | 228 | if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) || |
234 | BHDR(bh)->h_blocks != cpu_to_le32(1)) | 229 | BHDR(bh)->h_blocks != cpu_to_le32(1)) |
235 | return -EFSCORRUPTED; | 230 | return -EFSCORRUPTED; |
236 | if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh))) | 231 | if (!ext4_xattr_block_csum_verify(inode, bh)) |
237 | return -EFSBADCRC; | 232 | return -EFSBADCRC; |
238 | error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size, | 233 | error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size, |
239 | bh->b_data); | 234 | bh->b_data); |
@@ -618,23 +613,22 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, | |||
618 | } | 613 | } |
619 | } | 614 | } |
620 | 615 | ||
616 | ext4_xattr_block_csum_set(inode, bh); | ||
621 | /* | 617 | /* |
622 | * Beware of this ugliness: Releasing of xattr block references | 618 | * Beware of this ugliness: Releasing of xattr block references |
623 | * from different inodes can race and so we have to protect | 619 | * from different inodes can race and so we have to protect |
624 | * from a race where someone else frees the block (and releases | 620 | * from a race where someone else frees the block (and releases |
625 | * its journal_head) before we are done dirtying the buffer. In | 621 | * its journal_head) before we are done dirtying the buffer. In |
626 | * nojournal mode this race is harmless and we actually cannot | 622 | * nojournal mode this race is harmless and we actually cannot |
627 | * call ext4_handle_dirty_xattr_block() with locked buffer as | 623 | * call ext4_handle_dirty_metadata() with locked buffer as |
628 | * that function can call sync_dirty_buffer() so for that case | 624 | * that function can call sync_dirty_buffer() so for that case |
629 | * we handle the dirtying after unlocking the buffer. | 625 | * we handle the dirtying after unlocking the buffer. |
630 | */ | 626 | */ |
631 | if (ext4_handle_valid(handle)) | 627 | if (ext4_handle_valid(handle)) |
632 | error = ext4_handle_dirty_xattr_block(handle, inode, | 628 | error = ext4_handle_dirty_metadata(handle, inode, bh); |
633 | bh); | ||
634 | unlock_buffer(bh); | 629 | unlock_buffer(bh); |
635 | if (!ext4_handle_valid(handle)) | 630 | if (!ext4_handle_valid(handle)) |
636 | error = ext4_handle_dirty_xattr_block(handle, inode, | 631 | error = ext4_handle_dirty_metadata(handle, inode, bh); |
637 | bh); | ||
638 | if (IS_SYNC(inode)) | 632 | if (IS_SYNC(inode)) |
639 | ext4_handle_sync(handle); | 633 | ext4_handle_sync(handle); |
640 | dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1)); | 634 | dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1)); |
@@ -863,13 +857,14 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, | |||
863 | ext4_xattr_cache_insert(ext4_mb_cache, | 857 | ext4_xattr_cache_insert(ext4_mb_cache, |
864 | bs->bh); | 858 | bs->bh); |
865 | } | 859 | } |
860 | ext4_xattr_block_csum_set(inode, bs->bh); | ||
866 | unlock_buffer(bs->bh); | 861 | unlock_buffer(bs->bh); |
867 | if (error == -EFSCORRUPTED) | 862 | if (error == -EFSCORRUPTED) |
868 | goto bad_block; | 863 | goto bad_block; |
869 | if (!error) | 864 | if (!error) |
870 | error = ext4_handle_dirty_xattr_block(handle, | 865 | error = ext4_handle_dirty_metadata(handle, |
871 | inode, | 866 | inode, |
872 | bs->bh); | 867 | bs->bh); |
873 | if (error) | 868 | if (error) |
874 | goto cleanup; | 869 | goto cleanup; |
875 | goto inserted; | 870 | goto inserted; |
@@ -967,10 +962,11 @@ inserted: | |||
967 | ce->e_reusable = 0; | 962 | ce->e_reusable = 0; |
968 | ea_bdebug(new_bh, "reusing; refcount now=%d", | 963 | ea_bdebug(new_bh, "reusing; refcount now=%d", |
969 | ref); | 964 | ref); |
965 | ext4_xattr_block_csum_set(inode, new_bh); | ||
970 | unlock_buffer(new_bh); | 966 | unlock_buffer(new_bh); |
971 | error = ext4_handle_dirty_xattr_block(handle, | 967 | error = ext4_handle_dirty_metadata(handle, |
972 | inode, | 968 | inode, |
973 | new_bh); | 969 | new_bh); |
974 | if (error) | 970 | if (error) |
975 | goto cleanup_dquot; | 971 | goto cleanup_dquot; |
976 | } | 972 | } |
@@ -1020,11 +1016,12 @@ getblk_failed: | |||
1020 | goto getblk_failed; | 1016 | goto getblk_failed; |
1021 | } | 1017 | } |
1022 | memcpy(new_bh->b_data, s->base, new_bh->b_size); | 1018 | memcpy(new_bh->b_data, s->base, new_bh->b_size); |
1019 | ext4_xattr_block_csum_set(inode, new_bh); | ||
1023 | set_buffer_uptodate(new_bh); | 1020 | set_buffer_uptodate(new_bh); |
1024 | unlock_buffer(new_bh); | 1021 | unlock_buffer(new_bh); |
1025 | ext4_xattr_cache_insert(ext4_mb_cache, new_bh); | 1022 | ext4_xattr_cache_insert(ext4_mb_cache, new_bh); |
1026 | error = ext4_handle_dirty_xattr_block(handle, | 1023 | error = ext4_handle_dirty_metadata(handle, inode, |
1027 | inode, new_bh); | 1024 | new_bh); |
1028 | if (error) | 1025 | if (error) |
1029 | goto cleanup; | 1026 | goto cleanup; |
1030 | } | 1027 | } |