udf: Fix data integrity writeout issues

UDF could fail to properly write out inode on fsync(2) due to races
with WB_SYNC_NONE writeback. Several racing fsyncs could also result in
some fsync returning earlier than all metadata buffers were properly
persisted.

Fix all these issues by using new .sync_inode_metadata method which
makes sure all inode related metadata is written to disk during any
WB_SYNC_ALL writeback.

Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20260727104923.3828017-30-jack@suse.cz
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Jan Kara
2026-07-27 12:49:28 +02:00
committed by Christian Brauner
parent 917e583992
commit 1ec98214cc
5 changed files with 33 additions and 21 deletions

View File

@@ -157,6 +157,6 @@ const struct file_operations udf_dir_operations = {
.read = generic_read_dir,
.iterate_shared = udf_readdir,
.unlocked_ioctl = udf_ioctl,
.fsync = udf_fsync,
.fsync = simple_fsync,
.setlease = generic_setlease,
};

View File

@@ -198,13 +198,6 @@ static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
return mmb_fsync(file,
&UDF_I(file->f_mapping->host)->i_metadata_bhs,
start, end, datasync);
}
const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl,
@@ -212,7 +205,7 @@ const struct file_operations udf_file_operations = {
.mmap = udf_file_mmap,
.write_iter = udf_file_write_iter,
.release = udf_release_file,
.fsync = udf_fsync,
.fsync = simple_fsync,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = generic_file_llseek,

View File

@@ -142,7 +142,9 @@ void udf_evict_inode(struct inode *inode)
if (!inode->i_nlink) {
want_delete = 1;
udf_setsize(inode, 0);
udf_update_inode(inode, IS_SYNC(inode));
udf_update_inode(inode, 0);
if (IS_SYNC(inode))
udf_sync_inode_metadata(inode, NULL);
}
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB &&
inode->i_size != iinfo->i_lenExtents) {
@@ -1710,6 +1712,30 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
}
int udf_sync_inode_metadata(struct inode *inode, struct writeback_control *wbc)
{
struct buffer_head *bh;
int err = 0;
bh = sb_getblk(inode->i_sb,
udf_get_lb_pblock(inode->i_sb,
&UDF_I(inode)->i_location, 0));
if (!bh)
return -EIO;
sync_dirty_buffer(bh);
if (buffer_write_io_error(bh)) {
udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
inode->i_ino);
err = -EIO;
goto out;
}
err = mmb_sync(&UDF_I(inode)->i_metadata_bhs);
out:
brelse(bh);
return err;
}
static int udf_sync_inode(struct inode *inode)
{
return udf_update_inode(inode, 1);
@@ -1732,7 +1758,6 @@ static int udf_update_inode(struct inode *inode, int do_sync)
uint32_t udfperms;
uint16_t icbflags;
uint16_t crclen;
int err = 0;
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
struct udf_inode_info *iinfo = UDF_I(inode);
@@ -1937,17 +1962,10 @@ static int udf_update_inode(struct inode *inode, int do_sync)
/* write the data blocks */
mark_buffer_dirty(bh);
if (do_sync) {
sync_dirty_buffer(bh);
if (buffer_write_io_error(bh)) {
udf_warn(inode->i_sb, "IO error syncing udf inode [%08llx]\n",
inode->i_ino);
err = -EIO;
}
}
brelse(bh);
set_inode_metadata_writeback(inode);
return err;
return 0;
}
struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,

View File

@@ -211,6 +211,7 @@ static const struct super_operations udf_sb_ops = {
.alloc_inode = udf_alloc_inode,
.free_inode = udf_free_in_core_inode,
.write_inode = udf_write_inode,
.sync_inode_metadata = udf_sync_inode_metadata,
.evict_inode = udf_evict_inode,
.put_super = udf_put_super,
.sync_fs = udf_sync_fs,

View File

@@ -137,7 +137,6 @@ static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
/* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);
/* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
@@ -158,6 +157,7 @@ extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
int udf_sync_inode_metadata(struct inode *, struct writeback_control *wbc);
extern int inode_bmap(struct inode *inode, sector_t block,
struct extent_position *pos, struct kernel_lb_addr *eloc,
uint32_t *elen, sector_t *offset, int8_t *etype);