summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorChris Mason <mason@kernel.org>2026-10-16 21:17:04 +0000
committerAmir Goldstein <amir73il@gmail.com>2026-10-29 13:22:58 +0100
commiteb644ed5c6d9afdb1554c41cef1f39d150fbc554 (patch)
treef97725b6cf626ce66779295c50d8c17648c19b1a /scripts
parent50912a1715b8a7c614b654011a0db750e372b43e (diff)
downloadlinux-eb644ed5c6d9afdb1554c41cef1f39d150fbc554.tar.gz
linux-eb644ed5c6d9afdb1554c41cef1f39d150fbc554.zip
ovl: read the nlink xattr of every indexed non-dir
With nfs_export=on, data written through a file handle to a lower file, i.e. a file that exists only in a lower layer of the overlay, can be lost on the next mount. A lookup of the file by name then returns -EIO. Also, a file with one name reports st_nlink 2 once it has been copied to the upper layer. After such a file is removed, its file handle can still open it until the next mount. Before a lower file is changed for the first time, overlayfs copies it to the writable upper layer. This is called copy up. The index directory is $workdir/index, where $workdir is the directory given by the workdir= option. Each entry is named after the file handle of a lower inode, so that overlayfs can get from the handle to the copied up inode. With nfs_export=on, copy up indexes every non-directory: it copies the file to an entry in the index directory and then hard links that entry into the upper directory. The index entry is an extra link to the upper inode, so overlayfs stores the link count that users should see in the overlay.nlink xattr. A file can also be opened by handle, either by an NFS client or by a process with CAP_DAC_READ_SEARCH calling open_by_handle_at(). If the file is not in the dcache, its overlay dentry is disconnected, i.e. it has no parent yet. Copy up then creates the index entry but no hard link in the upper directory. The index entry has i_nlink 1 and holds the only copy of the data written to the file. ovl_get_nlink() reads the stored count. Its caller passes a default, which the function returns if there is no stored count. If the lower inode has i_nlink 1, the function returns the default without reading the xattr. That was correct as long as only lower files with multiple links were indexed. On the next mount, ovl_verify_index() looks for orphans, i.e. index entries of files whose last name was removed. For an index entry with i_nlink 1, it passes a default of 0 to ovl_get_nlink() and treats a result of 0 as an orphan. Here the lower inode also has i_nlink 1, so the result is the default. ovl_indexdir_cleanup() then replaces the entry with a whiteout, which in the index marks a file as deleted. The written data is lost. A lookup by name then returns -EIO, because a whiteout as the index entry of a lower file that still exists is a bad index. ovl_get_inode() gets the wrong count the same way. Its default is the i_nlink of the upper inode, which includes the index entry, so a copied up file with one name reports st_nlink 2. When that name is removed, the count drops to 1 rather than 0. ovl_nlink_end() replaces the index entry with a whiteout only at a count of 0, so the entry stays, and the handle of the removed file still opens it. Reproduced by writing through open_by_handle_at() to an uncached lower file with one link. After a remount the data was lost, and the name returned EIO. Fix this by adding an argument to ovl_get_nlink() that says whether the caller found an index entry, and by skipping the xattr only if the caller found none. Known gaps: - An index entry that a kernel without this patch left behind for a removed file now stays, and the handle of that file keeps working. The entry has i_nlink 1 and a stored count of 1, just like the entry of a file that was only written by handle. Without this patch, the next mount treated it as an orphan. Fixes: fbd2d2074bde ("ovl: index all non-dir on copy up for NFS export") Cc: stable@vger.kernel.org Signed-off-by: Chris Mason <mason@kernel.org> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Diffstat (limited to 'scripts')
0 files changed, 0 insertions, 0 deletions