summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2019-04-09 23:37:08 -0400
committerBen Hutchings <ben@decadent.org.uk>2020-06-11 19:05:53 +0100
commit51890201da4d654f6ca131bc45a0e892bb10de1d (patch)
tree18bcbfe09da40210dced818d0d2e2ff379311181
parentf9c6ffd9190290d3d2502c297e3df4aef3d034c0 (diff)
downloadlinux-stable-51890201da4d654f6ca131bc45a0e892bb10de1d.tar.gz
linux-stable-51890201da4d654f6ca131bc45a0e892bb10de1d.tar.bz2
linux-stable-51890201da4d654f6ca131bc45a0e892bb10de1d.zip
ext4: protect journal inode's blocks using block_validity
commit 345c0dbf3a30872d9b204db96b5857cd00808cae upstream. Add the blocks which belong to the journal inode to block_validity's system zone so attempts to deallocate or overwrite the journal due a corrupted file system where the journal blocks are also claimed by another inode. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=202879 Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org [bwh: Backported to 3.16: - Use EXT4_HAS_COMPAT_FEATURE() - Use EIO instead of EFSCORRUPTED] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--fs/ext4/block_validity.c49
-rw-r--r--fs/ext4/inode.c5
2 files changed, 54 insertions, 0 deletions
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index 41eb9dcfac7e..afa29592d0cc 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -137,6 +137,48 @@ static void debug_print_tree(struct ext4_sb_info *sbi)
printk("\n");
}
+static int ext4_protect_reserved_inode(struct super_block *sb, u32 ino)
+{
+ struct inode *inode;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_map_blocks map;
+ u32 i = 0, err = 0, num, n;
+
+ if ((ino < EXT4_ROOT_INO) ||
+ (ino > le32_to_cpu(sbi->s_es->s_inodes_count)))
+ return -EINVAL;
+ inode = ext4_iget(sb, ino, EXT4_IGET_SPECIAL);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+ num = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+ while (i < num) {
+ map.m_lblk = i;
+ map.m_len = num - i;
+ n = ext4_map_blocks(NULL, inode, &map, 0);
+ if (n < 0) {
+ err = n;
+ break;
+ }
+ if (n == 0) {
+ i++;
+ } else {
+ if (!ext4_data_block_valid(sbi, map.m_pblk, n)) {
+ ext4_error(sb, "blocks %llu-%llu from inode %u "
+ "overlap system zone", map.m_pblk,
+ map.m_pblk + map.m_len - 1, ino);
+ err = -EIO;
+ break;
+ }
+ err = add_system_zone(sbi, map.m_pblk, n);
+ if (err < 0)
+ break;
+ i += n;
+ }
+ }
+ iput(inode);
+ return err;
+}
+
int ext4_setup_system_zone(struct super_block *sb)
{
ext4_group_t ngroups = ext4_get_groups_count(sb);
@@ -171,6 +213,13 @@ int ext4_setup_system_zone(struct super_block *sb)
if (ret)
return ret;
}
+ if (EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL) &&
+ sbi->s_es->s_journal_inum) {
+ ret = ext4_protect_reserved_inode(sb,
+ le32_to_cpu(sbi->s_es->s_journal_inum));
+ if (ret)
+ return ret;
+ }
if (test_opt(sb, DEBUG))
debug_print_tree(EXT4_SB(sb));
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 66aed5f01784..67d8c6a7b182 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -411,6 +411,11 @@ static int __check_block_validity(struct inode *inode, const char *func,
unsigned int line,
struct ext4_map_blocks *map)
{
+ if (EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
+ EXT4_FEATURE_COMPAT_HAS_JOURNAL) &&
+ (inode->i_ino ==
+ le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum)))
+ return 0;
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk,
map->m_len)) {
ext4_error_inode(inode, func, line, map->m_pblk,