mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
This contains a set of nine exfat fixes covering shared writable mappings,
entry error handling, allocation cleanup, FITRIM bounds, and locking:
- Fix valid_size extension over shared writable mappings by lazily
zeroing page-cache gaps and ensuring racing stores cannot be
overwritten or extend beyond valid_size.
- Clean up partially written directory entries on add-entry failure and
safely free new directory clusters.
- Free a newly allocated directory cluster when zeroing it fails.
- Write the destination entry before removing the source entry during
rename, preserving the source if the destination write fails.
- Keep FITRIM within the requested range, even when the free-space search
wraps around the allocation bitmap.
- Fix truncated FS_IOC_GETFSLABEL results by passing the destination
buffer size in bytes to the UTF-16-to-NLS conversion.
- Fix overflow in the cluster-to-dentry calculation, which could cause
readdir to stop early on large volumes.
- Replace the per-inode truncate_lock with inode_lock, using shared
locking in bmap to serialize against concurrent truncation.
- Update the exfat mailing list address in MAINTAINERS.
-----BEGIN PGP SIGNATURE-----
iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmqJqrAWHGxpbmtpbmpl
b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCLIXEACnqHgNmrvIM6QtesU6iSVd6eGT
+7q+6ahkVv6dFks9KMc2hbzIs9VWmDpD3omU2OuqbJYiL3vuhv4+CwOnie6/5umc
3JCAG5jVOq5ee+7Tz6xZoxz2JxoxXm6LLmHXU0WALN4k8IsE7veGXAGkMgfnAQ44
lFDTByhvyf7vmXGHsz2FewOjdT6pdl4SUVAZQkvcZB9ujCqCWzx53s1+XZ2RCsT3
trepvTXQkxk2UyAKGs30JUiHDYwv01sYJCV84YKFBzTBCQ9AIIxKLCWlhYfERmLi
iL2nTBoNQPdDKiQK/R+axjkUABIQD3L4EhhqdLqcDBOFGsRlxUfd8pjyp+O5/yRw
Ev3ydGBDOULyU6Fmtx7mpzWm3EHklssMoGGuI9b81WcQc0PLp613egglgr1gKNZs
PaB74ea3ehSfHUIn2hF6Uli+OfZhXuHiOE/YKp6QTuer3rwUl0nJgIPDpkfWDuBH
hBGnTyiafMS0TVnd2yS0z8R/JipVpUniAPKLL5kRO3uIWcUwrRGjU8vfjsY5BSvU
W8BrYBu69zRQ603m/ARJ2gFU3jPmD7a/Su/m2GKaPjB/90RDbjRjcJ+r78pDfa1b
FgmL0cgyZg6uYFB0K/PCbSDsXA+x7xpjmOwZGgZNzrsFDQ+C9p5fizuOVG6N9MOg
zxOmKrpSSLYx0ahHgQ==
=kunx
-----END PGP SIGNATURE-----
Merge tag 'exfat-for-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon:
"A set of exfat fixes covering shared writable mappings, entry error
handling, allocation cleanup, FITRIM bounds, and locking:
- Fix valid_size extension over shared writable mappings by lazily
zeroing page-cache gaps and ensuring racing stores cannot be
overwritten or extend beyond valid_size
- Clean up partially written directory entries on add-entry failure
and safely free new directory clusters
- Free a newly allocated directory cluster when zeroing it fails
- Write the destination entry before removing the source entry during
rename, preserving the source if the destination write fails
- Keep FITRIM within the requested range, even when the free-space
search wraps around the allocation bitmap
- Fix truncated FS_IOC_GETFSLABEL results by passing the destination
buffer size in bytes to the UTF-16-to-NLS conversion
- Fix overflow in the cluster-to-dentry calculation, which could
cause readdir to stop early on large volumes
- Replace the per-inode truncate_lock with inode_lock, using shared
locking in bmap to serialize against concurrent truncation
- Update the exfat mailing list address in MAINTAINERS"
* tag 'exfat-for-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
exfat: replace truncate_lock with inode_lock
exfat: keep FITRIM within the requested range
exfat: fix truncated volume labels returned by FS_IOC_GETFSLABEL
exfat: fix overflow in cluster-to-dentry conversion
exfat: clean up new entry on add entry failure
exfat: free new directory cluster if zeroing fails
exfat: write moved entry before removing source
MAINTAINERS: update mailing list address for exfat
exfat: fix valid_size extension over a shared writable mapping
This commit is contained in:
commit
728785f8fb
|
|
@ -9774,7 +9774,7 @@ EXFAT FILE SYSTEM
|
|||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Sungjong Seo <sj1557.seo@samsung.com>
|
||||
R: Yuezhang Mo <yuezhang.mo@sony.com>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
L: exfat@lists.linux.dev
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git
|
||||
F: fs/exfat/
|
||||
|
|
|
|||
|
|
@ -340,14 +340,27 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
|
|||
mutex_lock(&sbi->bitmap_lock);
|
||||
|
||||
trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
|
||||
if (trim_begin == EXFAT_EOF_CLUSTER)
|
||||
/*
|
||||
* exfat_find_free_bitmap() may wrap around to the beginning of
|
||||
* the bitmap. Reject a cluster outside the requested range.
|
||||
*/
|
||||
if (trim_begin == EXFAT_EOF_CLUSTER ||
|
||||
trim_begin < clu_start || trim_begin > clu_end)
|
||||
goto unlock;
|
||||
|
||||
next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
|
||||
if (next_free_clu == EXFAT_EOF_CLUSTER)
|
||||
goto unlock;
|
||||
for (;;) {
|
||||
if (trim_end >= clu_end)
|
||||
break;
|
||||
|
||||
next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
|
||||
/*
|
||||
* Stop if the search wrapped around or moved beyond the requested
|
||||
* FITRIM range.
|
||||
*/
|
||||
if (next_free_clu == EXFAT_EOF_CLUSTER ||
|
||||
next_free_clu <= trim_end || next_free_clu > clu_end)
|
||||
break;
|
||||
|
||||
do {
|
||||
if (next_free_clu == trim_end + 1) {
|
||||
/* extend trim range for continuous free cluster */
|
||||
trim_end++;
|
||||
|
|
@ -368,17 +381,11 @@ int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
|
|||
trim_begin = trim_end = next_free_clu;
|
||||
}
|
||||
|
||||
if (next_free_clu >= clu_end)
|
||||
break;
|
||||
|
||||
if (fatal_signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
|
||||
} while (next_free_clu != EXFAT_EOF_CLUSTER &&
|
||||
next_free_clu > trim_end);
|
||||
}
|
||||
|
||||
/* try to trim remainder */
|
||||
count = trim_end - trim_begin + 1;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
|
|||
exfat_bytes_to_cluster(sbi, i_size_read(inode)), ei->flags);
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
max_dentries = min(MAX_EXFAT_DENTRIES,
|
||||
max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES,
|
||||
exfat_cluster_to_dentries(sbi, sbi->num_clusters));
|
||||
|
||||
clu_offset = exfat_dentries_to_cluster(sbi, dentry);
|
||||
|
|
@ -299,7 +299,13 @@ int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return exfat_zeroed_cluster(inode, clu->dir);
|
||||
ret = exfat_zeroed_cluster(inode, clu->dir);
|
||||
if (ret) {
|
||||
exfat_free_cluster(inode, clu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname)
|
||||
|
|
|
|||
|
|
@ -294,12 +294,10 @@ struct exfat_inode_info {
|
|||
/* on-disk position of directory entry or 0 */
|
||||
loff_t i_pos;
|
||||
loff_t valid_size;
|
||||
/* page-aligned size that has been zeroed out for mmap */
|
||||
/* block-aligned size zeroed in the page cache (>= valid_size) */
|
||||
loff_t zeroed_size;
|
||||
/* hash by i_location */
|
||||
struct hlist_node i_hash_fat;
|
||||
/* protect bmap against truncate */
|
||||
struct rw_semaphore truncate_lock;
|
||||
struct inode vfs_inode;
|
||||
/* File creation time */
|
||||
struct timespec64 i_crtime;
|
||||
|
|
@ -487,10 +485,10 @@ static inline u32 exfat_dentries_to_bytes(u32 dentry)
|
|||
/*
|
||||
* helpers for cluster size to dentry size conversion.
|
||||
*/
|
||||
static inline u32 exfat_cluster_to_dentries(struct exfat_sb_info *sbi,
|
||||
static inline u64 exfat_cluster_to_dentries(struct exfat_sb_info *sbi,
|
||||
u32 nr_clusters)
|
||||
{
|
||||
return nr_clusters << (sbi->cluster_size_bits - DENTRY_SIZE_BITS);
|
||||
return (u64)nr_clusters << (sbi->cluster_size_bits - DENTRY_SIZE_BITS);
|
||||
}
|
||||
|
||||
static inline u32 exfat_dentries_to_cluster(struct exfat_sb_info *sbi,
|
||||
|
|
|
|||
184
fs/exfat/file.c
184
fs/exfat/file.c
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/falloc.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <linux/iomap.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
|
@ -412,7 +413,6 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
* about to be freed.
|
||||
*/
|
||||
inode_dio_wait(inode);
|
||||
down_write(&EXFAT_I(inode)->truncate_lock);
|
||||
truncate_setsize(inode, attr->ia_size);
|
||||
|
||||
/*
|
||||
|
|
@ -420,7 +420,6 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
* is already written by it, so mark_inode_dirty() is unneeded.
|
||||
*/
|
||||
exfat_truncate(inode);
|
||||
up_write(&EXFAT_I(inode)->truncate_lock);
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
|
|
@ -565,7 +564,7 @@ static int exfat_ioctl_get_volume_label(struct super_block *sb, unsigned long ar
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = exfat_utf16_to_nls(sb, &uniname, label, uniname.name_len);
|
||||
ret = exfat_utf16_to_nls(sb, &uniname, label, sizeof(label));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -654,6 +653,104 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
|||
return blkdev_issue_flush(inode->i_sb->s_bdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* exfat_zero_new_range - zero [start, end) without overwriting uptodate blocks
|
||||
*
|
||||
* Uptodate blocks may contain data written through a shared mapping beyond
|
||||
* valid_size.
|
||||
*/
|
||||
static int exfat_zero_new_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;
|
||||
|
||||
while (pos < end) {
|
||||
loff_t next = min_t(loff_t,
|
||||
round_down(pos, PAGE_SIZE) + PAGE_SIZE, end);
|
||||
struct folio *folio;
|
||||
loff_t bpos;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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))
|
||||
folio_mark_dirty(folio);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
pos = next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size)
|
||||
{
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
|
|
@ -661,18 +758,41 @@ static int exfat_extend_valid_size(struct inode *inode, loff_t new_valid_size)
|
|||
int ret = 0;
|
||||
|
||||
if (old_valid_size < new_valid_size) {
|
||||
/* Do not re-zero blocks already covered by zeroed_size. */
|
||||
loff_t gap_start = max(old_valid_size, ei->zeroed_size);
|
||||
|
||||
if (i_size_read(inode) < new_valid_size) {
|
||||
i_size_write(inode, new_valid_size);
|
||||
mark_inode_dirty(inode);
|
||||
/*
|
||||
* Allocate clusters before increasing i_size. The gap
|
||||
* may already be zeroed, so the subsequent zeroing
|
||||
* can be skipped.
|
||||
*/
|
||||
ret = exfat_cont_expand(inode, new_valid_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iomap_zero_range(inode, old_valid_size,
|
||||
new_valid_size - old_valid_size, NULL,
|
||||
&exfat_write_iomap_ops, NULL, NULL);
|
||||
/*
|
||||
* Revoke writable PTEs while zeroing the gap. A racing mmap
|
||||
* store re-faults through exfat_page_mkwrite() after valid_size
|
||||
* is updated.
|
||||
*/
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
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);
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
if (ret) {
|
||||
truncate_setsize(inode, old_valid_size);
|
||||
exfat_truncate(inode);
|
||||
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));
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -825,39 +945,39 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
|||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
vm_fault_t ret;
|
||||
loff_t new_valid_size, mmap_valid_size;
|
||||
loff_t new_valid_size, mmap_valid_size, fault_page_start;
|
||||
|
||||
if (!inode_trylock(inode))
|
||||
return VM_FAULT_RETRY;
|
||||
|
||||
mmap_valid_size = ((loff_t)vmf->pgoff + 1) << PAGE_SHIFT;
|
||||
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 < mmap_valid_size) {
|
||||
if (ei->zeroed_size < fault_page_start) {
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Only zero the range that hasn't been zeroed yet for
|
||||
* this mmap write path. zeroed_size tracks the largest
|
||||
* page-aligned offset that has already been zeroed.
|
||||
*
|
||||
* This prevents unnecessarily zeroing out the entire
|
||||
* tail page on every page fault when userspace writes
|
||||
* data byte-by-byte through mmap (after a small
|
||||
* fallocate). It fixes data corruption in the tail page
|
||||
* while preserving the existing valid_size semantics.
|
||||
* Zero only the gap below the faulting page. The read
|
||||
* fault populated its folio and iomap_page_mkwrite()
|
||||
* will dirty it.
|
||||
*/
|
||||
err = iomap_zero_range(inode, ei->zeroed_size,
|
||||
mmap_valid_size - ei->zeroed_size, NULL,
|
||||
&exfat_iomap_ops, NULL, NULL);
|
||||
err = exfat_zero_new_range(inode, ei->zeroed_size,
|
||||
fault_page_start);
|
||||
if (err < 0) {
|
||||
inode_unlock(inode);
|
||||
return vmf_fs_error(err);
|
||||
}
|
||||
ei->zeroed_size = mmap_valid_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Track zeroed_size by block, not page, because writeback stops
|
||||
* 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;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
|
@ -866,7 +986,7 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
|||
file_update_time(vmf->vma->vm_file);
|
||||
|
||||
filemap_invalidate_lock_shared(inode->i_mapping);
|
||||
ret = iomap_page_mkwrite(vmf, &exfat_write_iomap_ops, NULL);
|
||||
ret = iomap_page_mkwrite(vmf, &exfat_iomap_ops, NULL);
|
||||
filemap_invalidate_unlock_shared(inode->i_mapping);
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
inode_unlock(inode);
|
||||
|
|
@ -876,7 +996,6 @@ static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
|||
|
||||
static const struct vm_operations_struct exfat_file_vm_ops = {
|
||||
.fault = filemap_fault,
|
||||
.map_pages = filemap_map_pages,
|
||||
.page_mkwrite = exfat_page_mkwrite,
|
||||
};
|
||||
|
||||
|
|
@ -887,21 +1006,6 @@ static int exfat_file_mmap_prepare(struct vm_area_desc *desc)
|
|||
if (unlikely(exfat_forced_shutdown(file_inode(desc->file)->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
if (vma_desc_test_all(desc, VMA_SHARED_BIT, VMA_MAYWRITE_BIT)) {
|
||||
struct inode *inode = file_inode(file);
|
||||
loff_t from, to;
|
||||
int err;
|
||||
|
||||
from = ((loff_t)desc->pgoff << PAGE_SHIFT);
|
||||
to = min_t(loff_t, i_size_read(inode),
|
||||
from + vma_desc_size(desc));
|
||||
if (EXFAT_I(inode)->valid_size < to) {
|
||||
err = exfat_extend_valid_size(inode, to);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
file_accessed(file);
|
||||
desc->vm_ops = &exfat_file_vm_ops;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -291,10 +291,9 @@ static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block)
|
|||
{
|
||||
sector_t blocknr;
|
||||
|
||||
/* exfat_get_cluster() assumes the requested blocknr isn't truncated. */
|
||||
down_read(&EXFAT_I(mapping->host)->truncate_lock);
|
||||
inode_lock_shared(mapping->host);
|
||||
blocknr = iomap_bmap(mapping, block, &exfat_iomap_ops);
|
||||
up_read(&EXFAT_I(mapping->host)->truncate_lock);
|
||||
inode_unlock_shared(mapping->host);
|
||||
return blocknr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -177,11 +177,18 @@ static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length,
|
|||
|
||||
if (ei->valid_size < end) {
|
||||
ei->valid_size = end;
|
||||
if (ei->zeroed_size < end)
|
||||
ei->zeroed_size = end;
|
||||
dirtied = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* IOMAP_F_ZERO_TAIL zeroes the remainder of the last block. Track that
|
||||
* block as zeroed so later valid_size extensions do not zero it again.
|
||||
*/
|
||||
if (iomap->flags & IOMAP_F_ZERO_TAIL)
|
||||
end = round_up(end, i_blocksize(inode));
|
||||
if (ei->zeroed_size < end)
|
||||
ei->zeroed_size = end;
|
||||
|
||||
if (dirtied || iomap->flags & IOMAP_F_SIZE_CHANGED)
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
|
|
|
|||
|
|
@ -471,6 +471,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
|||
struct exfat_entry_set_cache es;
|
||||
int clu_size = 0;
|
||||
unsigned int start_clu = EXFAT_FREE_CLUSTER;
|
||||
bool dir_allocated = false;
|
||||
|
||||
ret = exfat_resolve_path(inode, path, &uniname);
|
||||
if (ret)
|
||||
|
|
@ -497,6 +498,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
|||
}
|
||||
start_clu = clu.dir;
|
||||
clu_size = sbi->cluster_size;
|
||||
dir_allocated = true;
|
||||
}
|
||||
|
||||
/* update the directory entry */
|
||||
|
|
@ -507,8 +509,21 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
|||
exfat_init_ext_entry(&es, num_entries, &uniname, NULL, 0);
|
||||
|
||||
ret = exfat_put_dentry_set(&es, IS_DIRSYNC(inode));
|
||||
if (ret)
|
||||
if (ret) {
|
||||
int cleanup_ret;
|
||||
|
||||
cleanup_ret = exfat_get_dentry_set(&es, sb, &info->dir,
|
||||
dentry, ES_ALL_ENTRIES);
|
||||
if (!cleanup_ret) {
|
||||
exfat_remove_entries(inode, &es, ES_IDX_FILE, false);
|
||||
cleanup_ret = exfat_put_dentry_set(&es,
|
||||
IS_DIRSYNC(inode));
|
||||
}
|
||||
|
||||
if (!cleanup_ret && dir_allocated)
|
||||
exfat_free_cluster(inode, &clu);
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->entry = dentry;
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
|
|
@ -1116,11 +1131,6 @@ static int exfat_move_file(struct inode *parent_inode,
|
|||
exfat_init_ext_entry(&new_es, num_new_entries, p_uniname,
|
||||
&mov_es, num_extra_entries);
|
||||
|
||||
exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE, false);
|
||||
|
||||
ei->dir = newdir;
|
||||
ei->entry = newentry;
|
||||
|
||||
ret = exfat_put_dentry_set(&new_es, IS_DIRSYNC(parent_inode));
|
||||
if (ret) {
|
||||
/* Best-effort delete to avoid duplicate entries */
|
||||
|
|
@ -1134,6 +1144,11 @@ static int exfat_move_file(struct inode *parent_inode,
|
|||
goto put_mov_es;
|
||||
}
|
||||
|
||||
exfat_remove_entries(parent_inode, &mov_es, ES_IDX_FILE, false);
|
||||
|
||||
ei->dir = newdir;
|
||||
ei->entry = newentry;
|
||||
|
||||
return exfat_put_dentry_set(&mov_es, IS_DIRSYNC(parent_inode));
|
||||
|
||||
put_mov_es:
|
||||
|
|
|
|||
|
|
@ -195,7 +195,6 @@ static struct inode *exfat_alloc_inode(struct super_block *sb)
|
|||
if (!ei)
|
||||
return NULL;
|
||||
|
||||
init_rwsem(&ei->truncate_lock);
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user