On Mon, Mar 23, 2020 at 3:21 PM Miklos Szeredi <miklos@xxxxxxxxxx> wrote: > > On Mon, Mar 23, 2020 at 2:24 PM Amir Goldstein <amir73il@xxxxxxxxx> wrote: > > IDGI. coming from vfs_unlink() and vfs_rename() it doesn't look like > > it is possible for victim inode not to have a hashed alias, so the > > alias test seems futile. > > Yeah, needs a comment: both ovl_remove_upper() and > ovl_remove_and_whiteout() unhash the dentry before returning, so > d_find_alias() will find another hashed dentry or none. Except that doesn't seem to be true for the overwriting rename case... Attached patch should work for both. Thanks, Miklos
--- fs/overlayfs/dir.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -819,6 +819,27 @@ static bool ovl_pure_upper(struct 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; + + 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; @@ -856,7 +877,7 @@ static int ovl_do_remove(struct dentry * if (is_dir) clear_nlink(dentry->d_inode); else - drop_nlink(dentry->d_inode); + ovl_drop_nlink(dentry); } ovl_nlink_end(dentry); @@ -1201,7 +1222,7 @@ static int ovl_rename(struct inode *oldd 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) ||