btrfs: don't mess with i_nlink of unlocked inode in rename()
old_inode is not locked; it's not safe to play with its link count. Instead of bumping it and calling btrfs_unlink_inode(), add a variant of the latter that does not do btrfs_drop_nlink()/ btrfs_update_inode(), call it instead of btrfs_inc_nlink()/ btrfs_unlink_inode() and do btrfs_update_inode() ourselves. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
c2db1073fd
commit
92986796d8
|
@ -2664,7 +2664,7 @@ failed:
|
||||||
* recovery code. It remove a link in a directory with a given name, and
|
* recovery code. It remove a link in a directory with a given name, and
|
||||||
* also drops the back refs in the inode to the directory
|
* also drops the back refs in the inode to the directory
|
||||||
*/
|
*/
|
||||||
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
static int __btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
struct inode *dir, struct inode *inode,
|
struct inode *dir, struct inode *inode,
|
||||||
const char *name, int name_len)
|
const char *name, int name_len)
|
||||||
|
@ -2739,12 +2739,25 @@ err:
|
||||||
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
||||||
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||||
btrfs_update_inode(trans, root, dir);
|
btrfs_update_inode(trans, root, dir);
|
||||||
btrfs_drop_nlink(inode);
|
|
||||||
ret = btrfs_update_inode(trans, root, inode);
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root,
|
||||||
|
struct inode *dir, struct inode *inode,
|
||||||
|
const char *name, int name_len)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = __btrfs_unlink_inode(trans, root, dir, inode, name, name_len);
|
||||||
|
if (!ret) {
|
||||||
|
btrfs_drop_nlink(inode);
|
||||||
|
ret = btrfs_update_inode(trans, root, inode);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* helper to check if there is any shared block in the path */
|
/* helper to check if there is any shared block in the path */
|
||||||
static int check_path_shared(struct btrfs_root *root,
|
static int check_path_shared(struct btrfs_root *root,
|
||||||
struct btrfs_path *path)
|
struct btrfs_path *path)
|
||||||
|
@ -6999,11 +7012,12 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
old_dentry->d_name.name,
|
old_dentry->d_name.name,
|
||||||
old_dentry->d_name.len);
|
old_dentry->d_name.len);
|
||||||
} else {
|
} else {
|
||||||
btrfs_inc_nlink(old_dentry->d_inode);
|
ret = __btrfs_unlink_inode(trans, root, old_dir,
|
||||||
ret = btrfs_unlink_inode(trans, root, old_dir,
|
|
||||||
old_dentry->d_inode,
|
old_dentry->d_inode,
|
||||||
old_dentry->d_name.name,
|
old_dentry->d_name.name,
|
||||||
old_dentry->d_name.len);
|
old_dentry->d_name.len);
|
||||||
|
if (!ret)
|
||||||
|
ret = btrfs_update_inode(trans, root, old_inode);
|
||||||
}
|
}
|
||||||
BUG_ON(ret);
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue