linux/fs/overlayfs
Souvik Banerjee 5e5912d7db ovl: use linked upper dentry in copy-up tmpfile
[ Upstream commit e348eecd4d ]

ovl_copy_up_tmpfile() stores the disconnected O_TMPFILE dentry as the
overlay's upper dentry reference via ovl_inode_update().  vfs_tmpfile()
allocated this dentry via d_alloc(parentpath->dentry, &slash_name), so
d_name is "/" and d_parent is c->workdir.  Local upper filesystems
(ext4, btrfs, xfs, ...) immediately rename it to "#<inum>" via
d_mark_tmpfile() inside their ->tmpfile() op; FUSE and virtiofs do
not, so both fields stay that way.  Neither identifies the destination
directory and filename where ovl_do_link() actually linked the file.

When the upper filesystem implements ->d_revalidate() (e.g. FUSE or
virtiofs), ovl_revalidate_real() calls it with the dentry's parent
inode and a snapshot of d_name.  The server tries to look up "/" inside
c->workdir, fails, and overlayfs reports -ESTALE.

This causes persistent ESTALE errors for any file that was copied up via
the tmpfile path, breaking dpkg, apt, and other tools that do
rename-over-existing on overlayfs with a FUSE/virtiofs upper.

Before commit 6b52243f63 ("ovl: fold copy-up helpers into callers"),
the tmpfile copy-up path used a dedicated helper ovl_link_tmpfile()
that captured the linked destination dentry returned by ovl_do_link():

    err = ovl_do_link(temp, udir, upper);
    ...
    if (!err)
        *newdentry = dget(upper);

and published it via ovl_inode_update(d_inode(c->dentry), newdentry).
The fold inlined ovl_do_link() into ovl_copy_up_tmpfile() but dropped
the dget(upper) capture, and rewrote the publish line as
ovl_inode_update(d_inode(c->dentry), dget(temp)) — where temp is the
disconnected O_TMPFILE dentry.

Fix by keeping a reference to the linked destination dentry after
ovl_do_link() succeeds, and publishing that dentry at the existing
ovl_inode_update() call site.  The non-tmpfile/workdir path continues to
publish the renamed temporary dentry.

Reproducer:
  - Mount overlayfs with virtiofs (or a FUSE fs whose server advertises
    FUSE_TMPFILE) as upper
  - Run: dpkg -i <any .deb>
  - Observe: "error installing new file '...': Stale file handle"

Fixes: 6b52243f63 ("ovl: fold copy-up helpers into callers")
Cc: stable@vger.kernel.org # v4.20+
Signed-off-by: Souvik Banerjee <souvik@amlalabs.com>
Link: https://patch.msgid.link/20260501232735.2610824-1-souvik@amlalabs.com
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
History 2026-08-23 14:17:23 +02:00
..
copy_up.c ovl: use linked upper dentry in copy-up tmpfile 2026-08-23 14:17:23 +02:00
dir.c ovl: pass ofs to creation operations 2025-03-13 12:50:55 +01:00
export.c ovl: update of dentry revalidate flags after copy up 2023-07-23 13:47:10 +02:00
file.c ovl: fix incorrect fdput() on aio completion 2023-09-23 11:10:01 +02:00
inode.c ovl: use wrappers to all vfs_*xattr() calls 2025-03-13 12:50:55 +01:00
Kconfig docs: fix broken references to text files 2020-04-20 15:35:59 -06:00
Makefile treewide: Add SPDX license identifier - Makefile/Kconfig 2019-05-21 10:50:46 +02:00
namei.c ovl: use wrappers to all vfs_*xattr() calls 2025-03-13 12:50:55 +01:00
overlayfs.h ovl: pass ofs to creation operations 2025-03-13 12:50:55 +01:00
ovl_entry.h ovl: check type and offset of struct vfsmount in ovl_entry 2023-08-26 14:23:23 +02:00
readdir.c ovl: Fix uninit-value in ovl_fill_real 2026-03-04 07:19:47 -05:00
super.c ovl: pass ofs to creation operations 2025-03-13 12:50:55 +01:00
util.c ovl: Check for NULL d_inode() in ovl_dentry_upper() 2025-07-10 15:57:34 +02:00