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 Reviewed-by: Andy Wu Reviewed-by: Aoyama Wataru Reviewed-by: Daniel Palmer --- fs/exfat/file.c | 37 ++++++++++++++++++++++++------------- fs/exfat/inode.c | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 08e5ffd78b10..4e0793f35e8f 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -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; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index ebc64fa5c2de..3acfbec1a0d4 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -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); } } -- 2.25.1