git: 0152d453a08f - main - msdosfs deextend: validate pages of the partial buffer
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 13 Feb 2023 22:31:43 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=0152d453a08fa2bad694dc04a8184fce2b7faa10
commit 0152d453a08fa2bad694dc04a8184fce2b7faa10
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2023-02-11 18:09:30 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2023-02-13 22:29:42 +0000
msdosfs deextend: validate pages of the partial buffer
Suppose that the cluster size is larger than page size. If the buffer
at the old EOF (before extending) was partial and dirty, it cannot be
automatically neither written out nor validated by the buffer cache,
since extending buffer adds invalid pages at the end.
Correct the buffer state by calling vfs_bio_clrbuf() on it, to mark
newly added and zeroed pages as valid.
Note that UFS is immune to the problem because ffs_truncate() always
allocate the block and buffer for the last byte of the file.
PR: 269341
Reported by: asomers
In collaboration with: pho
Reviewed by: markj
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D38549
---
sys/fs/msdosfs/msdosfs_denode.c | 36 +++++++++++++++++++++++++++++++-----
1 file changed, 31 insertions(+), 5 deletions(-)
diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c
index 0b4135d60784..a82517acbdfe 100644
--- a/sys/fs/msdosfs/msdosfs_denode.c
+++ b/sys/fs/msdosfs/msdosfs_denode.c
@@ -497,6 +497,7 @@ deextend(struct denode *dep, u_long length, struct ucred *cred)
{
struct msdosfsmount *pmp = dep->de_pmp;
struct vnode *vp = DETOV(dep);
+ struct buf *bp;
u_long count;
int error;
@@ -523,16 +524,41 @@ deextend(struct denode *dep, u_long length, struct ucred *cred)
if (count > pmp->pm_freeclustercount)
return (ENOSPC);
error = extendfile(dep, count, NULL, NULL, DE_CLEAR);
- if (error) {
- /* truncate the added clusters away again */
- (void) detrunc(dep, dep->de_FileSize, 0, cred);
- return (error);
- }
+ if (error != 0)
+ goto rewind;
}
+
+ /*
+ * For the case of cluster size larger than the page size, we
+ * need to ensure that the possibly dirty partial buffer at
+ * the old end of file is not filled with invalid pages by
+ * extension. Otherwise it has a contradictory state of
+ * B_CACHE | B_DELWRI but with invalid pages, and cannot be
+ * neither written out nor validated.
+ *
+ * Fix it by proactively clearing extended pages.
+ */
+ error = bread(vp, de_cluster(pmp, dep->de_FileSize), pmp->pm_bpcluster,
+ NOCRED, &bp);
+ if (error != 0)
+ goto rewind;
+ vfs_bio_clrbuf(bp);
+ if (!DOINGASYNC(vp))
+ (void)bwrite(bp);
+ else if (vm_page_count_severe() || buf_dirty_count_severe())
+ bawrite(bp);
+ else
+ bdwrite(bp);
+
vnode_pager_setsize(vp, length);
dep->de_FileSize = length;
dep->de_flag |= DE_UPDATE | DE_MODIFIED;
return (deupdat(dep, !DOINGASYNC(vp)));
+
+rewind:
+ /* truncate the added clusters away again */
+ (void)detrunc(dep, dep->de_FileSize, 0, cred);
+ return (error);
}
/*