mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 08:00:52 +07:00
ovl: fix WARN_ON nlink drop to zero
Changes to underlying layers should not cause WARN_ON(), but this repro does: mkdir w l u mnt sudo mount -t overlay -o workdir=w,lowerdir=l,upperdir=u overlay mnt touch mnt/h ln u/h u/k rm -rf mnt/k rm -rf mnt/h dmesg ------------[ cut here ]------------ WARNING: CPU: 1 PID: 116244 at fs/inode.c:302 drop_nlink+0x28/0x40 After upper hardlinks were added while overlay is mounted, unlinking all overlay hardlinks drops overlay nlink to zero before all upper inodes are unlinked. After unlink/rename prevent i_nlink from going to zero if there are still hashed aliases (i.e. cached hard links to the victim) remaining. Reported-by: Phasip <phasip@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
a5a84682ec
commit
83552eacdf
@ -822,6 +822,28 @@ static bool ovl_pure_upper(struct dentry *dentry)
|
||||
!ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
|
||||
}
|
||||
|
||||
static void ovl_drop_nlink(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct dentry *alias;
|
||||
|
||||
/* Try to find another, hashed alias */
|
||||
spin_lock(&inode->i_lock);
|
||||
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
|
||||
if (alias != dentry && !d_unhashed(alias))
|
||||
break;
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
/*
|
||||
* Changes to underlying layers may cause i_nlink to lose sync with
|
||||
* reality. In this case prevent the link count from going to zero
|
||||
* prematurely.
|
||||
*/
|
||||
if (inode->i_nlink > !!alias)
|
||||
drop_nlink(inode);
|
||||
}
|
||||
|
||||
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
||||
{
|
||||
int err;
|
||||
@ -859,7 +881,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
||||
if (is_dir)
|
||||
clear_nlink(dentry->d_inode);
|
||||
else
|
||||
drop_nlink(dentry->d_inode);
|
||||
ovl_drop_nlink(dentry);
|
||||
}
|
||||
ovl_nlink_end(dentry);
|
||||
|
||||
@ -1204,7 +1226,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
|
||||
if (new_is_dir)
|
||||
clear_nlink(d_inode(new));
|
||||
else
|
||||
drop_nlink(d_inode(new));
|
||||
ovl_drop_nlink(new);
|
||||
}
|
||||
|
||||
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||
|
||||
|
Loading…
Reference in New Issue
Block a user