exfat: remove duplicate write inode for truncating file
authorYuezhang Mo <Yuezhang.Mo@sony.com>
Wed, 29 Jun 2022 02:39:59 +0000 (10:39 +0800)
committerNamjae Jeon <linkinjeon@kernel.org>
Mon, 1 Aug 2022 01:14:06 +0000 (10:14 +0900)
This commit moves updating file attributes and timestamps before
calling __exfat_write_inode(), so that all updates of the inode
had been written by __exfat_write_inode(), mark_inode_dirty() is
unneeded.

Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
Reviewed-by: Andy Wu <Andy.Wu@sony.com>
Reviewed-by: Aoyama Wataru <wataru.aoyama@sony.com>
Reviewed-by: Daniel Palmer <daniel.palmer@sony.com>
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/exfat/file.c
fs/exfat/inode.c

index 08e5ffd..4e0793f 100644 (file)
@@ -148,8 +148,17 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
        if (ei->type == TYPE_FILE)
                ei->attr |= ATTR_ARCHIVE;
 
-       /* update the directory entry */
-       inode->i_mtime = current_time(inode);
+       /*
+        * update the directory entry
+        *
+        * If the directory entry is updated by mark_inode_dirty(), the
+        * directory entry will be written after a writeback cycle of
+        * updating the bitmap/FAT, which may result in clusters being
+        * freed but referenced by the directory entry in the event of a
+        * sudden power failure.
+        * __exfat_write_inode() is called for directory entry, bitmap
+        * and FAT to be written in a same writeback.
+        */
        if (__exfat_write_inode(inode, inode_needs_sync(inode)))
                return -EIO;
 
@@ -202,12 +211,6 @@ void exfat_truncate(struct inode *inode, loff_t size)
        if (err)
                goto write_size;
 
-       inode->i_ctime = inode->i_mtime = current_time(inode);
-       if (IS_DIRSYNC(inode))
-               exfat_sync_inode(inode);
-       else
-               mark_inode_dirty(inode);
-
        inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >>
                                inode->i_blkbits;
 write_size:
@@ -289,6 +292,12 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                        attr->ia_valid &= ~ATTR_MODE;
        }
 
+       if (attr->ia_valid & ATTR_SIZE)
+               inode->i_mtime = inode->i_ctime = current_time(inode);
+
+       setattr_copy(&init_user_ns, inode, attr);
+       exfat_truncate_atime(&inode->i_atime);
+
        if (attr->ia_valid & ATTR_SIZE) {
                error = exfat_block_truncate_page(inode, attr->ia_size);
                if (error)
@@ -296,13 +305,15 @@ int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 
                down_write(&EXFAT_I(inode)->truncate_lock);
                truncate_setsize(inode, attr->ia_size);
+
+               /*
+                * __exfat_write_inode() is called from exfat_truncate(), inode
+                * is already written by it, so mark_inode_dirty() is unneeded.
+                */
                exfat_truncate(inode, attr->ia_size);
                up_write(&EXFAT_I(inode)->truncate_lock);
-       }
-
-       setattr_copy(&init_user_ns, inode, attr);
-       exfat_truncate_atime(&inode->i_atime);
-       mark_inode_dirty(inode);
+       } else
+               mark_inode_dirty(inode);
 
 out:
        return error;
index ebc64fa..3acfbec 100644 (file)
@@ -369,6 +369,7 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
 
        if (to > i_size_read(inode)) {
                truncate_pagecache(inode, i_size_read(inode));
+               inode->i_mtime = inode->i_ctime = current_time(inode);
                exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned);
        }
 }