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. Detect too low i_nlink before unlink/rename and set the overlay nlink to the upper inode nlink (minus one for an index entry). Reported-by: Phasip <phasip@xxxxxxxxx> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/overlayfs/util.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) Miklos, This fix passed the reported reproducers (with index=off), overlay/034 with (index=on) and overlay/034 with s/LOWER/UPPER: -lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER +lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK As well as the rest of overlay/quick group. I will post the overlay/034 fork as a separate test later. Thanks, Amir. diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 36b60788ee47..e894a14857c7 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -734,7 +734,9 @@ static void ovl_cleanup_index(struct dentry *dentry) int ovl_nlink_start(struct dentry *dentry) { struct inode *inode = d_inode(dentry); + struct inode *iupper = ovl_inode_upper(inode); const struct cred *old_cred; + int index_nlink; int err; if (WARN_ON(!inode)) @@ -764,7 +766,26 @@ int ovl_nlink_start(struct dentry *dentry) if (err) return err; - if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode)) + if (d_is_dir(dentry)) + goto out; + + /* index adds +1 to upper nlink */ + index_nlink = !!ovl_test_flag(OVL_INDEX, inode); + if (iupper && (iupper->i_nlink - index_nlink) > inode->i_nlink) { + pr_warn_ratelimited("inode nlink too low (%pd2, ino=%lu, nlink=%u, upper_nlink=%u)\n", + dentry, inode->i_ino, inode->i_nlink, + iupper->i_nlink - index_nlink); + /* + * Upper hardlinks were added while overlay is mounted. + * Unlinking all overlay hardlinks would drop overlay nlink to + * zero before all upper inodes are unlinked. As a safety + * measure, when that situation is detected, set the overlay + * nlink to the upper inode nlink minus one for the index entry. + */ + set_nlink(d_inode(dentry), iupper->i_nlink - index_nlink); + } + + if (!index_nlink) goto out; old_cred = ovl_override_creds(dentry->d_sb); -- 2.17.1