diff --git a/Documentation/filesystems/exfat.rst b/Documentation/filesystems/exfat.rst new file mode 100644 index 000000000000..ce5c9344a7b2 --- /dev/null +++ b/Documentation/filesystems/exfat.rst @@ -0,0 +1,117 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================== +The Linux exFAT filesystem driver +================================== + + +.. Table of contents + + - Overview + - Utilities support + - Supported mount options + + +Overview +======== + +exFAT is a filesystem designed for removable storage and other devices that +need to store large files. The Linux exFAT filesystem driver provides read +and write support for exFAT volumes. + +To mount an exFAT volume, use the ``exfat`` filesystem type:: + + mount -t exfat /dev/sdX1 /mnt + + +Utilities support +================= + +The exfatprogs project provides userspace utilities for creating, checking, +repairing, inspecting, and tuning exFAT filesystems. Use exfatprogs when +creating or checking an exFAT filesystem. For example, use ``mkfs.exfat`` +to create a filesystem and ``fsck.exfat`` to check or repair one. + +The project is available at: + + https://github.com/exfatprogs/exfatprogs + + +Supported mount options +======================= + +The exFAT driver supports the following mount options: + +======================= ==================================================== +uid= +gid= Set the owner and group of all files and + directories. The default is the uid and gid of + the process mounting the filesystem. + +umask= Set the permission mask for files and directories. + The default is the umask of the process mounting + the filesystem. + +dmask= Set the permission mask for directories. + +fmask= Set the permission mask for files. + +allow_utime= Control the permission check for changing file + timestamps. Only permission bits 0022 are used. + Permission bit 0020 allows members of the file's + group to change timestamps, and permission bit 0002 + allows other users to change timestamps. The + default is derived from dmask (``~dmask & 0022``). + +iocharset=name Character set used to convert between user-visible + filenames and the UTF-16 character encoding used by + exFAT. The default is + CONFIG_EXFAT_DEFAULT_IOCHARSET, which is ``utf8`` + unless changed at kernel configuration time. Use + ``iocharset=utf8`` for UTF-8 filename handling. + +errors= Specify exFAT behavior on filesystem errors. The + value must be ``panic``, ``continue``, or + ``remount-ro``. These respectively panic, continue + without changing the filesystem, or remount the + filesystem read-only. The default is + ``remount-ro``. + +discard Issue discard/TRIM requests to the block device + when clusters are freed. This is disabled by + default. ``nodiscard`` disables it explicitly. + +keep_last_dots Keep trailing periods in path components during + lookup. Without this option, trailing periods are + stripped. Existing entries with trailing periods + can be accessed when this option is enabled, but + creating new entries with trailing periods is + rejected. + +sys_tz Use the system timezone as the UTC offset when an + exFAT timestamp does not contain a valid timezone + offset. This takes precedence over time_offset. + +time_offset=minutes Set the UTC offset, in minutes, used when an exFAT + timestamp does not contain a valid timezone offset. + Values from -1440 to 1440 are accepted. The default + is 0. This option is ignored when sys_tz is set. + +zero_size_dir Create directories with zero size and without + allocating a cluster. This is disabled by default; + the default behavior allocates a cluster for a new + directory. ``nozero_size_dir`` disables it + explicitly. +======================= ==================================================== + + +Deprecated mount options +------------------------ + +The following options are accepted for compatibility but should not be used: + +``utf8`` + Deprecated. Use ``iocharset=utf8`` instead. + +``debug``, ``namecase=``, ``codepage=`` + Deprecated and ignored by the exFAT driver. diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst index 7cce7cc28d12..1100130ccf0a 100644 --- a/Documentation/filesystems/index.rst +++ b/Documentation/filesystems/index.rst @@ -86,6 +86,7 @@ Documentation for filesystem implementations. ecryptfs efivarfs erofs + exfat ext2 ext3 ext4/index diff --git a/MAINTAINERS b/MAINTAINERS index 05d5fad5c191..5a6d6fbec69c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10031,6 +10031,7 @@ R: Yuezhang Mo L: exfat@lists.linux.dev S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git +F: Documentation/filesystems/exfat.rst F: fs/exfat/ EXPRESSWIRE PROTOCOL LIBRARY diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index fe73b1380c5d..46514b13bebd 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -678,11 +678,54 @@ enum exfat_validate_dentry_mode { ES_MODE_GET_BENIGN_SEC_ENTRY, }; -static bool exfat_validate_entry(unsigned int type, - enum exfat_validate_dentry_mode *mode) +static bool exfat_validate_vendor_alloc(struct super_block *sb, + struct exfat_dentry *ep) { + struct exfat_sb_info *sbi = EXFAT_SB(sb); + u8 flags = ep->dentry.vendor_alloc.flags; + u32 start_clu = le32_to_cpu(ep->dentry.vendor_alloc.start_clu); + u64 size = le64_to_cpu(ep->dentry.vendor_alloc.size); + u64 max_size = exfat_cluster_to_bytes(sbi, + (u64)EXFAT_DATA_CLUSTER_COUNT(sbi)); + u64 num_clusters; + + /* AllocationPossible is required for Vendor Allocation entries. */ + if (!(flags & ALLOC_POSSIBLE)) + return false; + + /* The null GUID does not identify a valid vendor allocation. */ + if (!memchr_inv(ep->dentry.vendor_alloc.vendor_guid, 0, + sizeof(ep->dentry.vendor_alloc.vendor_guid))) + return false; + + if (!start_clu) + return !size && !(flags & (ALLOC_NO_FAT_CHAIN ^ ALLOC_FAT_CHAIN)); + + if (!is_valid_cluster(sbi, start_clu) || size > max_size) + return false; + + if ((flags & ALLOC_NO_FAT_CHAIN) == ALLOC_NO_FAT_CHAIN) { + if (!size) + return false; + + num_clusters = DIV_ROUND_UP_ULL(size, sbi->cluster_size); + if (num_clusters > sbi->num_clusters - start_clu) + return false; + } + + return true; +} + +static bool exfat_validate_entry(struct super_block *sb, + struct exfat_dentry *ep, enum exfat_validate_dentry_mode *mode) +{ + unsigned int type = exfat_get_entry_type(ep); + if (type == TYPE_UNUSED || type == TYPE_DELETED) return false; + if (type == TYPE_VENDOR_ALLOC && + !exfat_validate_vendor_alloc(sb, ep)) + return false; switch (*mode) { case ES_MODE_GET_FILE_ENTRY: @@ -836,7 +879,7 @@ int exfat_get_dentry_set(struct exfat_entry_set_cache *es, /* validate cached dentries */ for (i = ES_IDX_STREAM; i < es->num_entries; i++) { ep = exfat_get_dentry_cached(es, i); - if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) + if (!exfat_validate_entry(sb, ep, &mode)) goto put_es; } return 0; @@ -1266,7 +1309,8 @@ static int exfat_get_volume_label_dentry(struct super_block *sb, es->bh = es->__bh; es->bh[0] = bh; es->num_bh = 1; - es->start_off = exfat_dentries_to_bytes(i) % sb->s_blocksize; + es->start_off = exfat_dentries_to_bytes(i) & + ((u32)sb->s_blocksize - 1); return 0; } diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 5acfe7ef57d6..eee573d1697c 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -7,6 +7,7 @@ #define _EXFAT_FS_H #include +#include #include #include #include @@ -232,6 +233,7 @@ struct exfat_sb_info { unsigned int num_FAT_sectors; /* num of FAT sectors */ unsigned int root_dir; /* root dir cluster */ unsigned int dentries_per_clu; /* num of dentries per cluster */ + unsigned int dentries_per_clu_bits; unsigned int vol_flags; /* volume flags */ unsigned int vol_flags_persistent; /* volume flags to retain */ struct buffer_head *boot_bh; /* buffer_head of BOOT sector */ @@ -293,9 +295,16 @@ struct exfat_inode_info { /* on-disk position of directory entry or 0 */ loff_t i_pos; - loff_t valid_size; + /* + * valid_size and zeroed_size are updated from multiple contexts that + * are not serialised against each other (page_mkwrite runs without + * i_rwsem, while buffered/DIO writes advance them under i_rwsem). Keep + * them atomic and only ever advance them with a cmpxchg loop so a + * concurrent update can never regress the value or tear on 32-bit. + */ + atomic64_t valid_size; /* block-aligned size zeroed in the page cache (>= valid_size) */ - loff_t zeroed_size; + atomic64_t zeroed_size; /* hash by i_location */ struct hlist_node i_hash_fat; struct inode vfs_inode; @@ -313,6 +322,54 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) return container_of(inode, struct exfat_inode_info, vfs_inode); } +static inline loff_t exfat_get_valid_size(struct exfat_inode_info *ei) +{ + return atomic64_read(&ei->valid_size); +} + +static inline loff_t exfat_get_zeroed_size(struct exfat_inode_info *ei) +{ + return atomic64_read(&ei->zeroed_size); +} + +static inline void exfat_set_valid_size(struct exfat_inode_info *ei, loff_t v) +{ + atomic64_set(&ei->valid_size, v); +} + +static inline void exfat_set_zeroed_size(struct exfat_inode_info *ei, loff_t v) +{ + atomic64_set(&ei->zeroed_size, v); +} + +/* + * Monotonically advance @v to at least @new. Returns true if the value was + * actually raised, so callers can decide whether to mark the inode dirty. + */ +static inline bool exfat_size_advance(atomic64_t *v, loff_t new) +{ + loff_t old = atomic64_read(v); + + do { + if (old >= new) + return false; + } while (!atomic64_try_cmpxchg(v, &old, new)); + + return true; +} + +static inline bool exfat_advance_valid_size(struct exfat_inode_info *ei, + loff_t new) +{ + return exfat_size_advance(&ei->valid_size, new); +} + +static inline bool exfat_advance_zeroed_size(struct exfat_inode_info *ei, + loff_t new) +{ + return exfat_size_advance(&ei->zeroed_size, new); +} + static inline int exfat_forced_shutdown(struct super_block *sb) { return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags); @@ -623,19 +680,29 @@ static inline int exfat_chain_advance(struct super_block *sb, struct exfat_chain *chain, unsigned int step) { unsigned int clu = chain->dir; + unsigned int i; - if (unlikely(chain->size < step)) - return -EIO; + if (chain->flags == ALLOC_NO_FAT_CHAIN) { + if (unlikely(chain->size < step)) + return -EIO; - if (exfat_cluster_walk(sb, &clu, step, chain->flags)) - return -EIO; + chain->size -= step; + chain->dir = chain->size ? clu + step : EXFAT_EOF_CLUSTER; + return 0; + } - chain->size -= step; + /* + * Only the FAT tells where a chain of this kind ends. The size can be + * smaller than the chain, see commit 1e5654de0f51 ("exfat: handle + * wrong stream entry size in exfat_readdir()"). + */ + for (i = 0; i < step && clu != EXFAT_EOF_CLUSTER; i++) { + if (exfat_get_next_cluster(sb, &clu)) + return -EIO; + } - if (chain->size == 0 && chain->flags == ALLOC_NO_FAT_CHAIN) - chain->dir = EXFAT_EOF_CLUSTER; - else - chain->dir = clu; + chain->size -= min(chain->size, step); + chain->dir = clu; return 0; } diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c index a8b11e2ce43f..9f4607fced7f 100644 --- a/fs/exfat/fatent.c +++ b/fs/exfat/fatent.c @@ -418,28 +418,47 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) return 0; } +/* + * Newly allocated clusters may still have dirty buffer_heads in the block + * device mapping, e.g. the dentries of a directory that was just removed. + * File data does not go through those buffers, so drop them; otherwise a + * later flush of the block device writes them over the new data. + */ +static void exfat_clean_bdev_aliases(struct super_block *sb, + unsigned int clu, unsigned int count) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + clean_bdev_aliases(sb->s_bdev, exfat_cluster_to_sector(sbi, clu), + (sector_t)count << sbi->sect_per_clus_bits); +} + int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, struct exfat_chain *p_chain, bool sync_bmap, bool contig) { int ret = -ENOSPC; unsigned int total_cnt; unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER; + unsigned int run_clu = EXFAT_EOF_CLUSTER, run_len = 0; struct super_block *sb = inode->i_sb; struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_lock(&sbi->bitmap_lock); + total_cnt = EXFAT_DATA_CLUSTER_COUNT(sbi); if (unlikely(total_cnt < sbi->used_clusters)) { exfat_fs_error_ratelimit(sb, "%s: invalid used clusters(t:%u,u:%u)\n", __func__, total_cnt, sbi->used_clusters); - return -EIO; + ret = -EIO; + goto unlock; } - if (num_alloc > total_cnt - sbi->used_clusters) - return -ENOSPC; - - mutex_lock(&sbi->bitmap_lock); + if (num_alloc > total_cnt - sbi->used_clusters) { + ret = -ENOSPC; + goto unlock; + } hint_clu = p_chain->dir; /* find new cluster */ @@ -509,15 +528,26 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, } } p_chain->size++; + sbi->used_clusters++; + + /* clean stale buffers once per contiguous run of clusters */ + if (run_len && new_clu == run_clu + run_len) { + run_len++; + } else { + if (run_len) + exfat_clean_bdev_aliases(sb, run_clu, run_len); + run_clu = new_clu; + run_len = 1; + } last_clu = new_clu; if (p_chain->size == num_alloc) { done: + exfat_clean_bdev_aliases(sb, run_clu, run_len); sbi->clu_srch_ptr = hint_clu; - sbi->used_clusters += p_chain->size; - mutex_unlock(&sbi->bitmap_lock); - return 0; + ret = 0; + goto unlock; } hint_clu = new_clu + 1; diff --git a/fs/exfat/file.c b/fs/exfat/file.c index 3867e78c2312..1386149916bb 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -17,11 +17,70 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" #include "iomap.h" +/* + * Unlike pagecache_isize_extended(), this function updates i_size while + * holding the straddling folio lock, and does not skip the straddling + * folio when the filesystem block size is >= PAGE_SIZE or when 'from' + * and 'to' fall within the same filesystem block. + * + * This ensures that the folio is marked RO and the post-EOF range is + * zeroed before readers or writeback can observe the updated i_size. + */ +static void exfat_pagecache_isize_extended(struct inode *inode, loff_t from, + loff_t to) +{ + struct folio *folio; + + if (!(from & (PAGE_SIZE - 1))) { + i_size_write(inode, to); + return; + } + + folio = filemap_lock_folio(inode->i_mapping, from >> PAGE_SHIFT); + + /* Folio not cached? Nothing to do */ + if (IS_ERR(folio)) { + i_size_write(inode, to); + return; + } + + /* + * See folio_clear_dirty_for_io() for details why folio_mark_dirty() + * is needed. + */ + if (folio_mkclean(folio)) + folio_mark_dirty(folio); + + /* + * Zero the post-EOF range before publishing the new size. Writeback + * normally does this, but the extension would expose the tail to + * readers before writeback gets a chance to zero it. + */ + if (folio_test_dirty(folio)) { + loff_t offset, end; + + offset = from - folio_pos(folio); + end = min_t(loff_t, to - folio_pos(folio), + folio_size(folio)); + folio_zero_segment(folio, offset, end); + } + + /* + * Publish the new size after zeroing the tail. Uptodate folios can + * be read without taking the folio lock. + */ + i_size_write(inode, to); + + folio_unlock(folio); + folio_put(folio); +} + static int exfat_cont_expand(struct inode *inode, loff_t size) { int ret; @@ -32,8 +91,6 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) struct exfat_chain clu; loff_t oldsize = i_size_read(inode); - truncate_pagecache(inode, oldsize); - ret = inode_newsize_ok(inode, size); if (ret) return ret; @@ -81,15 +138,12 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) out: inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - /* Expanded range not zeroed, do not update valid_size */ - i_size_write(inode, size); /* - * When extending file size, call truncate_pagecache() first, - * then update i_size, and call pagecache_isize_extended() - * to ensures the straddling folio is properly marked RO so - * page_mkwrite() is called and post-EOF area is zeroed. + * When extending file size, call exfat_pagecache_isize_extended() + * to update i_size and ensure the straddling folio is properly + * marked RO so page_mkwrite() is called and post-EOF area is zeroed. */ - pagecache_isize_extended(inode, oldsize, inode->i_size); + exfat_pagecache_isize_extended(inode, oldsize, size); inode->i_blocks = round_up(size, sbi->cluster_size) >> 9; mark_inode_dirty(inode); @@ -247,8 +301,10 @@ int __exfat_truncate(struct inode *inode) ei->start_clu = EXFAT_EOF_CLUSTER; } - if (i_size_read(inode) < ei->valid_size) - ei->valid_size = ei->zeroed_size = i_size_read(inode); + if (i_size_read(inode) < exfat_get_valid_size(ei)) { + exfat_set_valid_size(ei, i_size_read(inode)); + exfat_set_zeroed_size(ei, i_size_read(inode)); + } if (ei->type == TYPE_FILE) ei->attr |= EXFAT_ATTR_ARCHIVE; @@ -413,6 +469,7 @@ int exfat_setattr(const struct mnt_idmap *idmap, struct dentry *dentry, * about to be freed. */ inode_dio_wait(inode); + filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, attr->ia_size); /* @@ -420,6 +477,7 @@ int exfat_setattr(const struct mnt_idmap *idmap, struct dentry *dentry, * is already written by it, so mark_inode_dirty() is unneeded. */ exfat_truncate(inode); + filemap_invalidate_unlock(inode->i_mapping); } else mark_inode_dirty(inode); @@ -654,98 +712,42 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) } /* - * exfat_zero_new_range - zero [start, end) without overwriting uptodate blocks + * exfat_prepare_valid_range - populate and dirty folios covering [start, end) * - * Uptodate blocks may contain data written through a shared mapping beyond - * valid_size. + * Populate missing blocks via the read path, which zero-fills data + * beyond the current valid_size and skips uptodate blocks. + * Mark entire folios dirty so the newly valid range is written back. + * + * Call before advancing valid_size. */ -static int exfat_zero_new_range(struct inode *inode, loff_t start, loff_t end) +static int exfat_prepare_valid_range(struct inode *inode, loff_t start, + loff_t end) { struct address_space *mapping = inode->i_mapping; - unsigned int blocksize = i_blocksize(inode); - loff_t pos = start; - int err; + loff_t next, pos = start; + struct folio *folio; + pgoff_t index; + + if (pos >= end) + return 0; while (pos < end) { - loff_t next = min_t(loff_t, - round_down(pos, PAGE_SIZE) + PAGE_SIZE, end); - struct folio *folio; - loff_t bpos; + index = pos >> PAGE_SHIFT; + next = min(((loff_t)index + 1) << PAGE_SHIFT, end); - folio = filemap_get_folio(mapping, pos >> PAGE_SHIFT); - if (IS_ERR(folio)) { - err = iomap_zero_range(inode, pos, next - pos, NULL, - &exfat_iomap_ops, NULL, NULL); - if (err < 0) - return err; - pos = next; - continue; - } + balance_dirty_pages_ratelimited(mapping); - if (folio_test_uptodate(folio)) { - folio_lock(folio); - if (folio->mapping == mapping) - folio_mark_dirty(folio); - folio_unlock(folio); - folio_put(folio); - pos = next; - continue; - } + folio = read_mapping_folio(mapping, index, NULL); + if (IS_ERR(folio)) + return PTR_ERR(folio); - /* - * Zero not-uptodate block runs. iomap_zero_range() requires an - * unlocked folio, so recheck ->mapping after each call. - */ folio_lock(folio); - bpos = pos; - while (bpos < next) { - loff_t rstart, rend; - - if (folio->mapping != mapping) { - folio_unlock(folio); - err = iomap_zero_range(inode, bpos, next - bpos, - NULL, &exfat_iomap_ops, NULL, NULL); - if (err < 0) { - folio_put(folio); - return err; - } - folio_lock(folio); - break; - } - - if (iomap_is_partially_uptodate(folio, - offset_in_folio(folio, bpos), blocksize)) { - bpos += blocksize; - continue; - } - - rstart = bpos; - rend = min_t(loff_t, bpos + blocksize, next); - while (rend < next && - !iomap_is_partially_uptodate(folio, - offset_in_folio(folio, rend), blocksize)) - rend = min_t(loff_t, rend + blocksize, next); - - folio_unlock(folio); - err = iomap_zero_range(inode, rstart, rend - rstart, - NULL, &exfat_iomap_ops, NULL, NULL); - if (err < 0) { - folio_put(folio); - return err; - } - folio_lock(folio); - bpos = rend; - } - - /* - * Dirty only a fully uptodate folio. Dirtying a partial folio could - * write uninitialised cache contents over valid on-disk blocks. - */ - if (folio->mapping == mapping && folio_test_uptodate(folio)) + if (folio->mapping == mapping) { folio_mark_dirty(folio); + pos = next; + } folio_unlock(folio); folio_put(folio); - pos = next; } return 0; @@ -754,12 +756,14 @@ static int exfat_zero_new_range(struct inode *inode, loff_t start, loff_t end) static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size) { struct exfat_inode_info *ei = EXFAT_I(inode); - loff_t old_valid_size = ei->valid_size; + loff_t old_valid_size = exfat_get_valid_size(ei); int ret = 0; if (old_valid_size < new_valid_size) { + inode_dio_wait(inode); + /* Do not re-zero blocks already covered by zeroed_size. */ - loff_t gap_start = max(old_valid_size, ei->zeroed_size); + loff_t gap_start = max(old_valid_size, exfat_get_zeroed_size(ei)); if (i_size_read(inode) < new_valid_size) { /* @@ -781,23 +785,38 @@ static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size) if (gap_start < new_valid_size) unmap_mapping_range(inode->i_mapping, gap_start, new_valid_size - gap_start, 0); - ret = exfat_zero_new_range(inode, gap_start, new_valid_size); + ret = exfat_prepare_valid_range(inode, gap_start, new_valid_size); filemap_invalidate_unlock(inode->i_mapping); if (ret) { + filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, old_valid_size); exfat_truncate(inode); + filemap_invalidate_unlock(inode->i_mapping); return ret; } - ei->valid_size = new_valid_size; - if (ei->zeroed_size < round_up(new_valid_size, i_blocksize(inode))) - ei->zeroed_size = round_up(new_valid_size, i_blocksize(inode)); + exfat_set_valid_size(ei, new_valid_size); + exfat_advance_zeroed_size(ei, + round_up(new_valid_size, i_blocksize(inode))); mark_inode_dirty(inode); } return ret; } +/* + * The clusters for a write are allocated before the data is copied. Free the + * ones that a failed or short write has left beyond the end of the file. + */ +static void exfat_write_failed(struct inode *inode) +{ + inode_dio_wait(inode); + filemap_invalidate_lock(inode->i_mapping); + truncate_pagecache(inode, i_size_read(inode)); + exfat_truncate(inode); + filemap_invalidate_unlock(inode->i_mapping); +} + static ssize_t exfat_fallback_buffered_write(struct kiocb *iocb, struct iov_iter *from) { @@ -807,6 +826,8 @@ static ssize_t exfat_fallback_buffered_write(struct kiocb *iocb, iocb->ki_flags &= ~IOCB_DIRECT; + inode_dio_wait(file_inode(iocb->ki_filp)); + written = iomap_file_buffered_write(iocb, from, &exfat_write_iomap_ops, NULL, NULL); if (written < 0) @@ -854,8 +875,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct exfat_inode_info *ei = EXFAT_I(inode); - loff_t pos = iocb->ki_pos; - loff_t valid_size; + loff_t pos, valid_size, end; int err; if (unlikely(exfat_forced_shutdown(inode->i_sb))) @@ -863,11 +883,6 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) inode_lock(inode); - if (pos > i_size_read(inode)) - truncate_pagecache(inode, i_size_read(inode)); - - valid_size = ei->valid_size; - ret = generic_write_checks(iocb, iter); if (ret <= 0) goto unlock; @@ -878,6 +893,12 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) goto unlock; } + pos = iocb->ki_pos; + end = pos + iov_iter_count(iter); + if (pos > i_size_read(inode)) + truncate_pagecache(inode, i_size_read(inode)); + + valid_size = exfat_get_valid_size(ei); if (pos > valid_size) { ret = exfat_extend_valid_size(inode, pos); if (ret < 0 && ret != -ENOSPC) { @@ -887,21 +908,28 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) } if (ret < 0) goto unlock; + + pos = valid_size; } - if (iocb->ki_flags & IOCB_DIRECT) + if (iocb->ki_flags & IOCB_DIRECT) { ret = exfat_dio_write_iter(iocb, iter); - else + } else { + /* + * Prevent concurrent direct I/O and buffered I/O to the same file + * range. Wait for in-flight DIO to finish before dirtying pages. + */ + inode_dio_wait(inode); ret = iomap_file_buffered_write(iocb, iter, &exfat_write_iomap_ops, NULL, NULL); + } + if (ret != -EIOCBQUEUED && end > i_size_read(inode)) + exfat_write_failed(inode); if (ret < 0) goto unlock; inode_unlock(inode); - if (pos > valid_size) - pos = valid_size; - if (iocb->ki_pos > pos) { ssize_t err = generic_write_sync(iocb, iocb->ki_pos - pos); @@ -954,8 +982,10 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) fault_page_start = ((loff_t)vmf->pgoff) << PAGE_SHIFT; new_valid_size = min(mmap_valid_size, i_size_read(inode)); - if (ei->valid_size < new_valid_size) { - if (ei->zeroed_size < fault_page_start) { + if (exfat_get_valid_size(ei) < new_valid_size) { + loff_t zeroed_size = exfat_get_zeroed_size(ei); + + if (zeroed_size < fault_page_start) { int err; /* @@ -963,7 +993,7 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) * fault populated its folio and iomap_page_mkwrite() * will dirty it. */ - err = exfat_zero_new_range(inode, ei->zeroed_size, + err = exfat_prepare_valid_range(inode, zeroed_size, fault_page_start); if (err < 0) { inode_unlock(inode); @@ -976,9 +1006,9 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf) * at i_size recording blocks wholly beyond it could skip a * later required zeroing. */ - if (ei->zeroed_size < round_up(new_valid_size, i_blocksize(inode))) - ei->zeroed_size = round_up(new_valid_size, i_blocksize(inode)); - ei->valid_size = new_valid_size; + exfat_advance_zeroed_size(ei, + round_up(new_valid_size, i_blocksize(inode))); + exfat_set_valid_size(ei, new_valid_size); mark_inode_dirty(inode); } diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index ccd13630187e..5129df1e79b4 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -29,6 +29,7 @@ int __exfat_write_inode(struct inode *inode, int sync) struct exfat_sb_info *sbi = EXFAT_SB(sb); struct exfat_inode_info *ei = EXFAT_I(inode); bool is_dir = (ei->type == TYPE_DIR); + loff_t valid_size = exfat_get_valid_size(ei); struct timespec64 ts; if (inode->i_ino == EXFAT_ROOT_INO) @@ -81,7 +82,7 @@ int __exfat_write_inode(struct inode *inode, int sync) * preventing fsck from reporting "more clusters are allocated". */ on_disk_size = max_t(unsigned long long, i_size_read(inode), - ei->valid_size); + valid_size); if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_size = 0; @@ -89,7 +90,7 @@ int __exfat_write_inode(struct inode *inode, int sync) * valid_size on disk must reflect only confirmed data (up to i_size) * and must not exceed on_disk_size. */ - on_disk_valid_size = min_t(unsigned long long, ei->valid_size, + on_disk_valid_size = min_t(unsigned long long, valid_size, i_size_read(inode)); if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_valid_size = 0; @@ -258,15 +259,16 @@ static void exfat_readahead(struct readahead_control *rac) struct inode *inode = mapping->host; struct exfat_inode_info *ei = EXFAT_I(inode); loff_t pos = readahead_pos(rac); + loff_t valid_size = exfat_get_valid_size(ei); struct iomap_read_folio_ctx ctx = { .ops = &exfat_iomap_bio_read_ops, .rac = rac, }; /* Range cross valid_size, read it page by page. */ - if (ei->valid_size < i_size_read(inode) && - pos <= ei->valid_size && - ei->valid_size < pos + readahead_length(rac)) + if (valid_size < i_size_read(inode) && + pos <= valid_size && + valid_size < pos + readahead_length(rac)) return; iomap_readahead(&exfat_iomap_ops, &ctx, NULL); @@ -371,8 +373,8 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) ei->start_clu = info->start_clu; ei->flags = info->flags; ei->type = info->type; - ei->valid_size = info->valid_size; - ei->zeroed_size = info->valid_size; + exfat_set_valid_size(ei, info->valid_size); + exfat_set_zeroed_size(ei, info->valid_size); ei->version = 0; ei->hint_stat.eidx = 0; diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c index 8911aa84a730..e50ecbfbfbe0 100644 --- a/fs/exfat/iomap.c +++ b/fs/exfat/iomap.c @@ -46,6 +46,7 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length struct exfat_inode_info *ei = EXFAT_I(inode); unsigned int cluster, num_clusters; loff_t cluster_offset, cluster_length; + loff_t valid_size = exfat_get_valid_size(ei); int err; bool balloc = false; @@ -74,6 +75,17 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length err = exfat_map_cluster(inode, exfat_bytes_to_cluster(sbi, offset), &cluster, &num_clusters, may_alloc, &balloc); + if (err == -ENOSPC && !(flags & IOMAP_DIRECT)) { + /* + * There are not enough free clusters for the whole range. A + * buffered write can be short, so map what still fits. + */ + num_clusters = 1; + err = exfat_map_cluster(inode, + exfat_bytes_to_cluster(sbi, offset), + &cluster, &num_clusters, may_alloc, + &balloc); + } if (err) goto out; @@ -88,7 +100,7 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length if (may_alloc || flags & IOMAP_ZERO) { if (balloc) iomap->flags |= IOMAP_F_NEW; - else if (iomap->offset + iomap->length >= ei->valid_size) { + else if (iomap->offset + iomap->length >= valid_size) { /* * This is a write that starts at or extends beyond * the current valid_size. The region between the old @@ -114,10 +126,10 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length * return IOMAP_UNWRITTEN so the write path can * distinguish it from a real hole. */ - if (offset >= ei->valid_size) { + if (offset >= valid_size) { iomap->type = flags & IOMAP_REPORT ? IOMAP_HOLE : IOMAP_UNWRITTEN; - } else if (offset + iomap->length > ei->valid_size) { + } else if (offset + iomap->length > valid_size) { if (flags & IOMAP_REPORT) { /* * For SEEK_HOLE/SEEK_DATA, clip the length @@ -125,9 +137,9 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length * This ensures the caller gets the precise * hole position in byte units. */ - iomap->length = ei->valid_size - iomap->offset; + iomap->length = valid_size - iomap->offset; } else - iomap->length = round_up(ei->valid_size, + iomap->length = round_up(valid_size, i_blocksize(inode)) - iomap->offset; } @@ -148,7 +160,27 @@ static int exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length, static int exfat_write_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned int flags, struct iomap *iomap, struct iomap *srcmap) { - return __exfat_iomap_begin(inode, offset, length, flags, iomap, true); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t valid_size = exfat_get_valid_size(ei); + loff_t end; + int err; + + err = __exfat_iomap_begin(inode, offset, length, flags, iomap, true); + if (err || flags & (IOMAP_DIRECT | IOMAP_FAULT) || + iomap->flags & IOMAP_F_NEW) + return err; + + end = iomap->offset + iomap->length; + if (end >= valid_size || + round_up(end, i_blocksize(inode)) <= valid_size) + return 0; + + /* + * Zero the invalid bytes from valid_size to the end of the block + * to prevent stale data from being exposed through the page cache. + */ + return iomap_truncate_page(inode, valid_size, NULL, &exfat_iomap_ops, + NULL, NULL); } static DEFINE_IOMAP_ITER_NEXT(exfat_iomap_next, exfat_iomap_begin); @@ -157,6 +189,27 @@ const struct iomap_ops exfat_iomap_ops = { .iomap_next = exfat_iomap_next, }; +#ifdef CONFIG_SWAP +static int exfat_swap_iomap_begin(struct inode *inode, loff_t offset, + loff_t length, unsigned int flags, struct iomap *iomap, + struct iomap *srcmap) +{ + /* + * Swap activation needs the physical mappings of preallocated + * ranges. Do not report the VDL tail as a hole. + */ + return __exfat_iomap_begin(inode, offset, length, + flags & ~IOMAP_REPORT, iomap, false); +} + +static DEFINE_IOMAP_ITER_NEXT(exfat_swap_iomap_next, + exfat_swap_iomap_begin); + +static const struct iomap_ops exfat_swap_iomap_ops = { + .iomap_next = exfat_swap_iomap_next, +}; +#endif + /* * exfat_write_iomap_end - Update the state after write * @@ -175,10 +228,8 @@ static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, end = pos + written; - if (ei->valid_size < end) { - ei->valid_size = end; + if (exfat_advance_valid_size(ei, end)) dirtied = true; - } /* * IOMAP_F_ZERO_TAIL zeroes the remainder of the last block. Track that @@ -186,8 +237,7 @@ static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length, */ if (iomap->flags & IOMAP_F_ZERO_TAIL) end = round_up(end, i_blocksize(inode)); - if (ei->zeroed_size < end) - ei->zeroed_size = end; + exfat_advance_zeroed_size(ei, end); if (dirtied || iomap->flags & IOMAP_F_SIZE_CHANGED) mark_inode_dirty(inode); @@ -250,7 +300,7 @@ static void exfat_iomap_read_end_io(struct bio *bio) s64 valid_size; loff_t pos = folio_pos(folio); - valid_size = ei->valid_size; + valid_size = exfat_get_valid_size(ei); if (pos + iter.offset < valid_size && pos + iter.offset + iter.length > valid_size) folio_zero_segment(folio, offset_in_folio(folio, valid_size), @@ -275,5 +325,10 @@ const struct iomap_read_ops exfat_iomap_bio_read_ops = { int exfat_iomap_swap_activate(struct swap_info_struct *sis, struct file *file, sector_t *span) { - return iomap_swapfile_activate(sis, file, span, &exfat_iomap_ops); +#ifdef CONFIG_SWAP + return iomap_swapfile_activate(sis, file, span, + &exfat_swap_iomap_ops); +#else + return -EIO; +#endif } diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 5e7836af63b8..68ddb9c5504f 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -380,13 +380,13 @@ int exfat_find_empty_entry(struct inode *inode, /* directory inode should be updated in here */ i_size_write(inode, size); - ei->valid_size += sbi->cluster_size; + atomic64_add(sbi->cluster_size, &ei->valid_size); ei->flags = p_dir->flags; inode->i_blocks += sbi->cluster_size >> 9; } p_dir->dir = exfat_sector_to_cluster(sbi, es->bh[0]->b_blocknr); - p_dir->size -= dentry / sbi->dentries_per_clu; + p_dir->size -= dentry >> sbi->dentries_per_clu_bits; return dentry & (sbi->dentries_per_clu - 1); } @@ -638,14 +638,15 @@ static int exfat_find(struct inode *dir, const struct qstr *qname, /* adjust cdir to the optimized value */ cdir.dir = hint_opt.clu; if (cdir.flags & ALLOC_NO_FAT_CHAIN) - cdir.size -= dentry / sbi->dentries_per_clu; + cdir.size -= dentry >> sbi->dentries_per_clu_bits; dentry = hint_opt.eidx; info->dir = cdir; info->entry = dentry; info->num_subdirs = 0; - if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_2_ENTRIES)) + /* Validate the complete set, including recognized benign entries. */ + if (exfat_get_dentry_set(&es, sb, &cdir, dentry, ES_ALL_ENTRIES)) return -EIO; ep = exfat_get_dentry_cached(&es, ES_IDX_FILE); ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM); @@ -1248,7 +1249,7 @@ static int __exfat_rename(struct inode *old_parent_inode, } i_size_write(new_inode, 0); - new_ei->valid_size = 0; + exfat_set_valid_size(new_ei, 0); new_ei->start_clu = EXFAT_EOF_CLUSTER; new_ei->flags = ALLOC_NO_FAT_CHAIN; } diff --git a/fs/exfat/super.c b/fs/exfat/super.c index a9ea36ba2693..4943cef97741 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" @@ -69,16 +70,28 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } +static inline __u8 exfat_calc_perc_in_use(const struct exfat_sb_info *sbi) +{ + return (__u8)mul_u64_u32_div(sbi->used_clusters, 100, + EXFAT_DATA_CLUSTER_COUNT(sbi)); +} + static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags) { struct exfat_sb_info *sbi = EXFAT_SB(sb); struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data; + __u8 new_piu; /* retain persistent-flags */ new_flags |= sbi->vol_flags_persistent; + if (new_flags & VOLUME_DIRTY) + new_piu = 0xFF; + else + new_piu = exfat_calc_perc_in_use(sbi); + /* flags are not changed */ - if (sbi->vol_flags == new_flags) + if (sbi->vol_flags == new_flags && new_piu == p_boot->percent_in_use) return 0; sbi->vol_flags = new_flags; @@ -90,6 +103,7 @@ static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags) return 0; p_boot->vol_flags = cpu_to_le16(new_flags); + p_boot->percent_in_use = new_piu; set_buffer_uptodate(sbi->boot_bh); mark_buffer_dirty(sbi->boot_bh); @@ -505,8 +519,8 @@ static int exfat_read_boot_sector(struct super_block *sb) EXFAT_RESERVED_CLUSTERS; sbi->root_dir = le32_to_cpu(p_boot->root_cluster); - sbi->dentries_per_clu = 1 << - (sbi->cluster_size_bits - DENTRY_SIZE_BITS); + sbi->dentries_per_clu_bits = sbi->cluster_size_bits - DENTRY_SIZE_BITS; + sbi->dentries_per_clu = 1 << sbi->dentries_per_clu_bits; sbi->vol_flags = le16_to_cpu(p_boot->vol_flags); sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE); @@ -775,9 +789,13 @@ static int exfat_reconfigure(struct fs_context *fc) fc->sb_flags |= SB_NODIRATIME; sync_filesystem(sb); - mutex_lock(&sbi->s_lock); - exfat_clear_volume_dirty(sb); - mutex_unlock(&sbi->s_lock); + + if ((fc->sb_flags & (SB_FORCE | SB_RDONLY)) == SB_RDONLY && + !sb_rdonly(sb)) { + mutex_lock(&sbi->s_lock); + exfat_clear_volume_dirty(sb); + mutex_unlock(&sbi->s_lock); + } if (new_opts->allow_utime == (unsigned short)-1) new_opts->allow_utime = ~new_opts->fs_dmask & 0022;