summaryrefslogtreecommitdiffstats
path: root/fs/gfs2
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2021-02-12 13:22:38 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2021-03-12 22:15:19 -0500
commit4a378d8a0d9606e97bddb0389cbc2009c6fc006c (patch)
tree8e0e11fb736c3a1911935d5b030918bcebc6a0fa /fs/gfs2
parent60606ecad1881566ae1c76e2b921b6c07407e2cf (diff)
downloadlinux-4a378d8a0d9606e97bddb0389cbc2009c6fc006c.tar.bz2
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>
Diffstat (limited to 'fs/gfs2')
-rw-r--r--fs/gfs2/glops.c22
1 files changed, 14 insertions, 8 deletions
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 8e32d569c8bf..ef0b583c3417 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -394,18 +394,24 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
const struct gfs2_dinode *str = buf;
struct timespec64 atime;
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)))
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_inode.i_mode = be32_to_cpu(str->di_mode);
- ip->i_inode.i_rdev = 0;
- switch (ip->i_inode.i_mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
- ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
- be32_to_cpu(str->di_minor));
- break;
+ ip->i_inode.i_mode = mode;
+ if (is_new) {
+ ip->i_inode.i_rdev = 0;
+ switch (mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
+ be32_to_cpu(str->di_minor));
+ break;
+ }
}
i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid));