Re: [PATCH v3] exfat: check disk status during buffer write

From: Namjae Jeon
Date: Wed Jul 31 2024 - 03:26:39 EST


2024년 7월 31일 (수) 오전 11:29, Dongliang Cui <dongliang.cui@xxxxxxxxxx>님이 작성:
>
> We found that when writing a large file through buffer write, if the
> disk is inaccessible, exFAT does not return an error normally, which
> leads to the writing process not stopping properly.
>
> To easily reproduce this issue, you can follow the steps below:
>
> 1. format a device to exFAT and then mount (with a full disk erase)
> 2. dd if=/dev/zero of=/exfat_mount/test.img bs=1M count=8192
> 3. eject the device
>
> You may find that the dd process does not stop immediately and may
> continue for a long time.
>
> The root cause of this issue is that during buffer write process,
> exFAT does not need to access the disk to look up directory entries
> or the FAT table (whereas FAT would do) every time data is written.
> Instead, exFAT simply marks the buffer as dirty and returns,
> delegating the writeback operation to the writeback process.
>
> If the disk cannot be accessed at this time, the error will only be
> returned to the writeback process, and the original process will not
> receive the error, so it cannot be returned to the user side.
>
> When the disk cannot be accessed normally, an error should be returned
> to stop the writing process.
>
> Signed-off-by: Dongliang Cui <dongliang.cui@xxxxxxxxxx>
> Signed-off-by: Zhiguo Niu <zhiguo.niu@xxxxxxxxxx>
> ---
> Changes in v3:
> - Implement .shutdown to monitor disk status.
> ---
> fs/exfat/exfat_fs.h | 10 ++++++++++
> fs/exfat/inode.c | 3 +++
> fs/exfat/super.c | 11 +++++++++++
> 3 files changed, 24 insertions(+)
>
> diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
> index ecc5db952deb..c6cf36070aa3 100644
> --- a/fs/exfat/exfat_fs.h
> +++ b/fs/exfat/exfat_fs.h
> @@ -148,6 +148,9 @@ enum {
> #define DIR_CACHE_SIZE \
> (DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
>
> +/* Superblock flags */
> +#define EXFAT_FLAGS_SHUTDOWN 1
> +
> struct exfat_dentry_namebuf {
> char *lfn;
> int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
> @@ -267,6 +270,8 @@ struct exfat_sb_info {
> unsigned int clu_srch_ptr; /* cluster search pointer */
> unsigned int used_clusters; /* number of used clusters */
>
> + unsigned long s_exfat_flags; /* Exfat superblock flags */
> +
> struct mutex s_lock; /* superblock lock */
> struct mutex bitmap_lock; /* bitmap lock */
> struct exfat_mount_options options;
> @@ -338,6 +343,11 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
> return container_of(inode, struct exfat_inode_info, vfs_inode);
> }
>
> +static inline int exfat_forced_shutdown(struct super_block *sb)
> +{
> + return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags);
> +}
> +
> /*
> * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
> * save ATTR_RO instead of ->i_mode.
> diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
> index dd894e558c91..b1b814183494 100644
> --- a/fs/exfat/inode.c
> +++ b/fs/exfat/inode.c
> @@ -452,6 +452,9 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
> {
> int ret;
>
> + if (unlikely(exfat_forced_shutdown(mapping->host->i_sb)))
> + return -EIO;
We need to add this to other write operations(exfat_create, unlink,
mkdir, rmdir, rename, setattr, writepages)
as well as exfat_write_begin().
Thanks.