gfs2: be careful with inode refresh

1) gfs2_dinode_in() should *not* touch ->i_rdev on live inodes; even
"zero and immediately reread the same value from dinode" is broken -
have it overlap with ->release() of char device and you can get all
kinds of bogus behaviour.

2) mismatch on inode type on live inodes should be treated as fs
corruption rather than blindly setting ->i_mode.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro
2021-02-12 13:22:38 -05:00
parent 60606ecad1
commit 4a378d8a0d

View File

@ -394,18 +394,24 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
const struct gfs2_dinode *str = buf; const struct gfs2_dinode *str = buf;
struct timespec64 atime; struct timespec64 atime;
u16 height, depth; u16 height, depth;
umode_t mode = be32_to_cpu(str->di_mode);
bool is_new = ip->i_inode.i_flags & I_NEW;
if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr))) if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
goto corrupt; goto corrupt;
if (unlikely(!is_new && inode_wrong_type(&ip->i_inode, mode)))
goto corrupt;
ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino); ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino);
ip->i_inode.i_mode = be32_to_cpu(str->di_mode); ip->i_inode.i_mode = mode;
ip->i_inode.i_rdev = 0; if (is_new) {
switch (ip->i_inode.i_mode & S_IFMT) { ip->i_inode.i_rdev = 0;
case S_IFBLK: switch (mode & S_IFMT) {
case S_IFCHR: case S_IFBLK:
ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major), case S_IFCHR:
be32_to_cpu(str->di_minor)); ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
break; be32_to_cpu(str->di_minor));
break;
}
} }
i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid)); i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid));