]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
udf: Fix data integrity writeout issues
authorJan Kara <jack@suse.cz>
Mon, 27 Jul 2026 10:49:28 +0000 (12:49 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 27 Jul 2026 14:25:32 +0000 (16:25 +0200)
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>
fs/udf/dir.c
fs/udf/file.c
fs/udf/inode.c
fs/udf/super.c
fs/udf/udfdecl.h

index ebc9f6a379fe3a3533c2f499e9c1cbe7f3cee79d..425e3c1629350442fd23cf6bd8e44c8255410a00 100644 (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,
 };
index f7f1422de30fa49b90dbfe637439ca41bc8bb0a2..0748cc965117e222a8383c9b184cf8934e5989f4 100644 (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,
index 67bcf83758c8fb7c5ae2665ea449771787ecc823..05e61a65478c4c79f3c37165a0396177f79ddd60 100644 (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 @@ finish:
 
        /* 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,
index 7b85f5a2b79fc46df7ebbb2049d6d24908e31efc..e7e9f2a0d24e80e8c3c00952276b4c5e10a58a50 100644 (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,
index 6d951e05c0046a41207b1b5584987284f41b17de..86dc2d6a2ef1b807f8425aec9e32b7037db26c02 100644 (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);