udf: don't let a stale metadata buffer overwrite expanded file data
Since commit 62333e480d12 ("udf: Fix data loss when converting inline
inodes to out of line"), udf_expand_file_adinicb() allocates the data
block itself with udf_map_block() and then writes the folio with
filemap_fdatawrite(). Writeback maps the block through
udf_get_block_wb(), which does not allocate, so the buffer is never new
and nothing calls clean_bdev_aliases() for the block, as the generic
code does for every block get_block() reports as new.
A recently freed block can still have a dirty buffer in the block device
page cache. The common case is the file entry of a deleted file:
udf_evict_inode() updates it with sync_inode_metadata(), which for an
inode that is not IS_SYNC only marks the buffer dirty, and then frees
the block. If that block becomes the data block of an expanded file,
the next writeback of the block device writes the old file entry over
the file's data.
On a fresh mkudffs image, creating 20 small files, deleting them and
then creating five files with "printf hello >f; truncate -s 3000 f"
leaves all five with a deleted file's extended file entry as their first
block after sync and remount. This happened in 400 of 400 runs, on
bitmap and table images alike. When the file is extended by writing
instead of truncating, the write dirties the data again and the outcome
depends on writeback order: the data was corrupted in about 7% of runs.
Clean the aliases of the new block, as block_write_begin() and
mpage_writepages() would have done.
Fixes: 62333e480d12 ("udf: Fix data loss when converting inline inodes to out of line")
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
Link: https://patch.msgid.link/20260926023512.3035718-1-matthias.goergens@gmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
1 file changed