Skip to content

Commit 83552ea

Browse files
author
Miklos Szeredi
committed
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 <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent a5a8468 commit 83552ea

File tree

1 file changed

+24
-2
lines changed

1 file changed

+24
-2
lines changed

fs/overlayfs/dir.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,28 @@ static bool ovl_pure_upper(struct dentry *dentry)
822822
!ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry));
823823
}
824824

825+
static void ovl_drop_nlink(struct dentry *dentry)
826+
{
827+
struct inode *inode = d_inode(dentry);
828+
struct dentry *alias;
829+
830+
/* Try to find another, hashed alias */
831+
spin_lock(&inode->i_lock);
832+
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
833+
if (alias != dentry && !d_unhashed(alias))
834+
break;
835+
}
836+
spin_unlock(&inode->i_lock);
837+
838+
/*
839+
* Changes to underlying layers may cause i_nlink to lose sync with
840+
* reality. In this case prevent the link count from going to zero
841+
* prematurely.
842+
*/
843+
if (inode->i_nlink > !!alias)
844+
drop_nlink(inode);
845+
}
846+
825847
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
826848
{
827849
int err;
@@ -859,7 +881,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
859881
if (is_dir)
860882
clear_nlink(dentry->d_inode);
861883
else
862-
drop_nlink(dentry->d_inode);
884+
ovl_drop_nlink(dentry);
863885
}
864886
ovl_nlink_end(dentry);
865887

@@ -1204,7 +1226,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
12041226
if (new_is_dir)
12051227
clear_nlink(d_inode(new));
12061228
else
1207-
drop_nlink(d_inode(new));
1229+
ovl_drop_nlink(new);
12081230
}
12091231

12101232
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||

0 commit comments

Comments
 (0)