Module Name: src Committed By: perseant Date: Fri Jul 12 23:46:54 UTC 2024
Modified Files: src/sys/fs/exfatfs [perseant-exfatfs]: exfatfs_dirent.h exfatfs_vnops.c Log Message: Ensure that all of a given file's directory entries reside in the same sector, if possible. This provides atomicity of entry-set writes, avoiding a situation whereby a file could become invalid if (say) its access time was in the process of being updated when power was lost. Note however that file entry sets with names longer than 210 characters cannot fit into a single sector. To generate a diff of this commit: cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/fs/exfatfs/exfatfs_dirent.h cvs rdiff -u -r1.1.2.5 -r1.1.2.6 src/sys/fs/exfatfs/exfatfs_vnops.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/fs/exfatfs/exfatfs_dirent.h diff -u src/sys/fs/exfatfs/exfatfs_dirent.h:1.1.2.2 src/sys/fs/exfatfs/exfatfs_dirent.h:1.1.2.3 --- src/sys/fs/exfatfs/exfatfs_dirent.h:1.1.2.2 Mon Jul 1 22:15:21 2024 +++ src/sys/fs/exfatfs/exfatfs_dirent.h Fri Jul 12 23:46:54 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_dirent.h,v 1.1.2.2 2024/07/01 22:15:21 perseant Exp $ */ +/* $NetBSD: exfatfs_dirent.h,v 1.1.2.3 2024/07/12 23:46:54 perseant Exp $ */ /*- * Copyright (c) 2022, 2024 The NetBSD Foundation, Inc. @@ -64,6 +64,9 @@ struct exfatfs_dirent { uint64_t xd_dataLength; /* Bytes 24..32 */ }; +/* Not in the spec: FILE but not in use */ +#define XD_ENTRYTYPE_FILLER 0x05 + struct exfatfs_dirent_plus { struct exfatfs_dirent de; unsigned long serial; Index: src/sys/fs/exfatfs/exfatfs_vnops.c diff -u src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.5 src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.6 --- src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.5 Wed Jul 3 18:57:42 2024 +++ src/sys/fs/exfatfs/exfatfs_vnops.c Fri Jul 12 23:46:54 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: exfatfs_vnops.c,v 1.1.2.5 2024/07/03 18:57:42 perseant Exp $ */ +/* $NetBSD: exfatfs_vnops.c,v 1.1.2.6 2024/07/12 23:46:54 perseant Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: exfatfs_vnops.c,v 1.1.2.5 2024/07/03 18:57:42 perseant Exp $"); +__KERNEL_RCSID(0, "$NetBSD: exfatfs_vnops.c,v 1.1.2.6 2024/07/12 23:46:54 perseant Exp $"); #include <sys/buf.h> #include <sys/dirent.h> @@ -819,8 +819,9 @@ int exfatfs_findempty(struct vnode *dvp, struct xfinode *xip) { off_t off = 0, r = -1, newsize, so; - int i; + int i, filling, modified; daddr_t blkno; + size_t bytes; struct exfatfs_dirent *dirent; struct xfinode *dxip; struct exfatfs *fs; @@ -837,6 +838,7 @@ exfatfs_findempty(struct vnode *dvp, str fs = dxip->xi_fs; KASSERT(fs != NULL); len = GET_DFE_SECONDARY_COUNT(xip) + 1; + bytes = EXFATFS_DIRENT2BYTES(fs, len); KASSERT(len > 2); KASSERT(len <= EXFATFS_MAXDIRENT); @@ -856,9 +858,31 @@ exfatfs_findempty(struct vnode *dvp, str if ((error = bread(fs->xf_devvp, blkno, SECSIZE(fs), 0, &bp)) != 0) return error; + filling = 0; + modified = 0; for (so = 0; so < SECSIZE(fs); so += sizeof(*dirent)) { dirent = (struct exfatfs_dirent *) (((char *)bp->b_data) + so); + /* + * If our entry will fit into a disk sector, + * but it could no longer fit in *this* sector + * even if the rest of the sector were empty, + * skip to the next, clearing any intervening EOD + * markers as we go. + */ + if (r < 0 && !filling && bytes <= SECSIZE(fs) + && so + bytes > SECSIZE(fs)) { + filling = 1; + } + if (filling) { + if (ISEOD(dirent)) { + dirent->xd_entryType = + XD_ENTRYTYPE_FILLER; + modified = 1; + } + continue; + } + if (ISINUSE(dirent)) { contig = 0; r = -1; @@ -869,7 +893,11 @@ exfatfs_findempty(struct vnode *dvp, str } ++contig; if (contig >= len) { - brelse(bp, 0); + if (modified) + bdwrite(bp); + else + brelse(bp, 0); + DPRINTF(("dir 0x%lx has %d empty" " entries at byte %lld" " (entry %u) bn 0x%lx..0x%lx" @@ -884,7 +912,10 @@ exfatfs_findempty(struct vnode *dvp, str } } } - brelse(bp, 0); + if (modified) + bdwrite(bp); + else + brelse(bp, 0); } /* r = -1; */ /* XXX never reallocate XXX */ @@ -908,6 +939,7 @@ exfatfs_findempty(struct vnode *dvp, str /* Space is now allocated. Adjust validDataLength if necessary. */ /* exFAT requires ValidDataLength == DataLength for directories */ + SET_DSE_DATALENGTH(dxip, roundup2(newsize, CLUSTERSIZE(fs))); SET_DSE_VALIDDATALENGTH(dxip, GET_DSE_DATALENGTH(dxip)); uvm_vnp_setsize(dvp, GET_DSE_VALIDDATALENGTH(dxip));