Commit 41080b5a authored by Al Viro's avatar Al Viro

nfsd race fixes: ext2

* make ext2_new_inode() put the inode into icache in locked state
* do not unlock until the inode is fully set up; otherwise nfsd
might pick it in half-baked state.
* make sure that ext2_new_inode() does *not* lead to two inodes with the
same inumber hashed at the same time; otherwise a bogus fhandle coming
from nfsd might race with inode creation:

nfsd: iget_locked() creates inode
nfsd: try to read from disk, block on that.
ext2_new_inode(): allocate inode with that inumber
ext2_new_inode(): insert it into icache, set it up and dirty
ext2_write_inode(): get the relevant part of inode table in cache,
set the entry for our inode (and start writing to disk)
nfsd: get CPU again, look into inode table, see nice and sane on-disk
inode, set the in-core inode from it

oops - we have two in-core inodes with the same inumber live in icache,
both used for IO.  Welcome to fs corruption...
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 261bca86
...@@ -585,7 +585,10 @@ got: ...@@ -585,7 +585,10 @@ got:
spin_lock(&sbi->s_next_gen_lock); spin_lock(&sbi->s_next_gen_lock);
inode->i_generation = sbi->s_next_generation++; inode->i_generation = sbi->s_next_generation++;
spin_unlock(&sbi->s_next_gen_lock); spin_unlock(&sbi->s_next_gen_lock);
insert_inode_hash(inode); if (insert_inode_locked(inode) < 0) {
err = -EINVAL;
goto fail_drop;
}
if (DQUOT_ALLOC_INODE(inode)) { if (DQUOT_ALLOC_INODE(inode)) {
err = -EDQUOT; err = -EDQUOT;
...@@ -612,6 +615,7 @@ fail_drop: ...@@ -612,6 +615,7 @@ fail_drop:
DQUOT_DROP(inode); DQUOT_DROP(inode);
inode->i_flags |= S_NOQUOTA; inode->i_flags |= S_NOQUOTA;
inode->i_nlink = 0; inode->i_nlink = 0;
unlock_new_inode(inode);
iput(inode); iput(inode);
return ERR_PTR(err); return ERR_PTR(err);
......
...@@ -41,9 +41,11 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode) ...@@ -41,9 +41,11 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
int err = ext2_add_link(dentry, inode); int err = ext2_add_link(dentry, inode);
if (!err) { if (!err) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode);
return 0; return 0;
} }
inode_dec_link_count(inode); inode_dec_link_count(inode);
unlock_new_inode(inode);
iput(inode); iput(inode);
return err; return err;
} }
...@@ -170,6 +172,7 @@ out: ...@@ -170,6 +172,7 @@ out:
out_fail: out_fail:
inode_dec_link_count(inode); inode_dec_link_count(inode);
unlock_new_inode(inode);
iput (inode); iput (inode);
goto out; goto out;
} }
...@@ -178,6 +181,7 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir, ...@@ -178,6 +181,7 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
struct dentry *dentry) struct dentry *dentry)
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
int err;
if (inode->i_nlink >= EXT2_LINK_MAX) if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK; return -EMLINK;
...@@ -186,7 +190,14 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir, ...@@ -186,7 +190,14 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
inode_inc_link_count(inode); inode_inc_link_count(inode);
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
return ext2_add_nondir(dentry, inode); err = ext2_add_link(dentry, inode);
if (!err) {
d_instantiate(dentry, inode);
return 0;
}
inode_dec_link_count(inode);
iput(inode);
return err;
} }
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
...@@ -222,12 +233,14 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) ...@@ -222,12 +233,14 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
goto out_fail; goto out_fail;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
unlock_new_inode(inode);
out: out:
return err; return err;
out_fail: out_fail:
inode_dec_link_count(inode); inode_dec_link_count(inode);
inode_dec_link_count(inode); inode_dec_link_count(inode);
unlock_new_inode(inode);
iput(inode); iput(inode);
out_dir: out_dir:
inode_dec_link_count(dir); inode_dec_link_count(dir);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment