mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 10:00:03 -04:00
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:
committed by
Christian Brauner
parent
917e583992
commit
1ec98214cc
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user