Module Name: src
Committed By: perseant
Date: Wed Jul 24 00:38:27 UTC 2024
Modified Files:
src/sys/fs/exfatfs [perseant-exfatfs]: exfatfs_extern.c
exfatfs_extern.h exfatfs_tables.c exfatfs_tables.h exfatfs_vfsops.c
exfatfs_vnops.c
Log Message:
Actually use the upcase table when determining whether a file exists.
Forbid certain filenames that, while not listed in the exFAT spec,
nevertheless cause severe interoperability problems.
Mark the filesystem dirty when mounted read/write, and clean again
when the filesystem is unmounted.
Correctly terminate the FAT chain when a file is converted to FAT.
To generate a diff of this commit:
cvs rdiff -u -r1.1.2.5 -r1.1.2.6 src/sys/fs/exfatfs/exfatfs_extern.c
cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/fs/exfatfs/exfatfs_extern.h \
src/sys/fs/exfatfs/exfatfs_tables.c src/sys/fs/exfatfs/exfatfs_tables.h
cvs rdiff -u -r1.1.2.4 -r1.1.2.5 src/sys/fs/exfatfs/exfatfs_vfsops.c
cvs rdiff -u -r1.1.2.7 -r1.1.2.8 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_extern.c
diff -u src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.5 src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.6
--- src/sys/fs/exfatfs/exfatfs_extern.c:1.1.2.5 Fri Jul 19 16:19:15 2024
+++ src/sys/fs/exfatfs/exfatfs_extern.c Wed Jul 24 00:38:26 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_extern.c,v 1.1.2.5 2024/07/19 16:19:15 perseant Exp $ */
+/* $NetBSD: exfatfs_extern.c,v 1.1.2.6 2024/07/24 00:38:26 perseant Exp $ */
/*-
* Copyright (c) 2022 The NetBSD Foundation, Inc.
@@ -59,6 +59,7 @@ typedef struct uvvnode uvnode_t;
#include <fs/exfatfs/exfatfs_extern.h>
#include <fs/exfatfs/exfatfs_inode.h>
#include <fs/exfatfs/exfatfs_extern.h>
+#include <fs/exfatfs/exfatfs_tables.h>
#include <fs/exfatfs/exfatfs_vfsops.h>
/* #define EXFATFS_EXTERN_DEBUG */
@@ -258,6 +259,7 @@ int exfatfs_mountfs_shared(struct vnode
{
struct exfatfs *fs = NULL;
struct buf *bp;
+ uint16_t *uctable;
int error;
unsigned secshift;
const char *errstr;
@@ -265,6 +267,7 @@ int exfatfs_mountfs_shared(struct vnode
uint8_t boot_ignore[3] = { 106, 107, 112 };
int bn;
uint32_t sum, badsb;
+ off_t res, off;
DPRINTF(("exfatfs_mountfs_shared(%p, %u, %p)\n",
devvp, secsize, fsp));
@@ -369,11 +372,18 @@ int exfatfs_mountfs_shared(struct vnode
return EINVAL;
}
+ LIST_INIT(&fs->xf_newxip);
fs->xf_devvp = devvp;
fs->xf_mp = xmp;
- if (xmp != NULL)
+ if (xmp != NULL) {
xmp->xm_fs = fs;
- LIST_INIT(&fs->xf_newxip);
+
+ /* If mounting for write, mark the fs dirty */
+ if (!(xmp->xm_flags & EXFATFSMNT_RONLY)) {
+ fs->xf_VolumeFlags |= EXFATFS_VOLUME_DIRTY;
+ exfatfs_write_sb(fs);
+ }
+ }
exfatfs_finish_mountfs(fs);
@@ -382,7 +392,31 @@ int exfatfs_mountfs_shared(struct vnode
*/
read_rootdir(fs);
exfatfs_check_fence(fs);
-
+
+ /*
+ * Load the upcase table
+ */
+ uctable = malloc(GET_DSE_DATALENGTH(VTOXI(fs->xf_upcasevp))
+#ifdef _KERNEL
+ , M_EXFATFSBOOT, M_WAITOK
+#endif /* _KERNEL */
+ );
+ res = (off_t)GET_DSE_DATALENGTH(VTOXI(fs->xf_upcasevp));
+ for (off = 0; res > 0; off += EXFATFS_LSIZE(fs), res -= EXFATFS_LSIZE(fs)) {
+ bread(fs->xf_upcasevp, EXFATFS_B2L(fs, off),
+ EXFATFS_LSIZE(fs), 0, &bp);
+ memcpy(uctable + off, bp->b_data, MIN(res, EXFATFS_LSIZE(fs)));
+ brelse(bp, 0);
+ }
+ exfatfs_load_uctable(fs, uctable,
+ GET_DSE_DATALENGTH(VTOXI(fs->xf_upcasevp))
+ / sizeof(uint16_t));
+ free(uctable
+#ifdef _KERNEL
+ , M_EXFATFSBOOT
+#endif /* _KERNEL */
+ );
+
/*
* Initialize data structure for finding free clusters.
*/
@@ -1065,3 +1099,55 @@ int exfatfs_set_file_name(struct xfinode
return 0;
}
+
+/*
+ * Write the boot block to disk, and checksum the boot block set.
+ */
+int
+exfatfs_write_sb(struct exfatfs *fs)
+{
+ daddr_t base;
+ int i, error;
+ size_t j;
+ uint32_t cksum;
+ uint8_t boot_ignore[3] = { 106, 107, 112 };
+ struct buf *bp;
+
+ for (base = 0; base < 24; base += 12) {
+ /* Write superblock to disk */
+ if ((error = bread(fs->xf_devvp, base + 0, BSSIZE(fs),
+ 0, &bp)) != 0)
+ return error;
+ memcpy(bp->b_data, &fs->xf_exfatdfs,
+ sizeof(fs->xf_exfatdfs));
+ cksum = exfatfs_cksum32(0,
+ (uint8_t *)bp->b_data,
+ BSSIZE(fs), boot_ignore,
+ sizeof(boot_ignore));
+ bwrite(bp);
+
+ /* Checksum but do not write other sectors */
+ for (i = 1; i < 11; i++) {
+ if ((error = bread(fs->xf_devvp, base + i, BSSIZE(fs),
+ 0, &bp)) != 0)
+ return error;
+ cksum = exfatfs_cksum32(cksum,
+ (uint8_t *)bp->b_data,
+ BSSIZE(fs),
+ NULL, 0);
+ brelse(bp, 0);
+ }
+
+ /* Populate checksum block and write it */
+ bp = getblk(fs->xf_devvp, base + i, BSSIZE(fs)
+#ifdef _KERNEL
+ , 0, 0
+#endif /* _KERNEL */
+ );
+ for (j = 0; j < BSSIZE(fs) / sizeof(uint32_t); j++)
+ ((uint32_t *)bp->b_data)[j] = cksum;
+ bwrite(bp);
+ }
+
+ return 0;
+}
Index: src/sys/fs/exfatfs/exfatfs_extern.h
diff -u src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.2 src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.3
--- src/sys/fs/exfatfs/exfatfs_extern.h:1.1.2.2 Mon Jul 1 22:15:21 2024
+++ src/sys/fs/exfatfs/exfatfs_extern.h Wed Jul 24 00:38:26 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_extern.h,v 1.1.2.2 2024/07/01 22:15:21 perseant Exp $ */
+/* $NetBSD: exfatfs_extern.h,v 1.1.2.3 2024/07/24 00:38:26 perseant Exp $ */
/*-
* Copyright (c) 2022 The NetBSD Foundation, Inc.
@@ -51,5 +51,6 @@ int exfatfs_scandir(struct vnode *, off_
void *arg);
#define SCANDIR_STOP 0x00000001
#define SCANDIR_DONTFREE 0x00000002
+int exfatfs_write_sb(struct exfatfs *);
#endif /* EXFATFS_EXTERN_H_ */
Index: src/sys/fs/exfatfs/exfatfs_tables.c
diff -u src/sys/fs/exfatfs/exfatfs_tables.c:1.1.2.2 src/sys/fs/exfatfs/exfatfs_tables.c:1.1.2.3
--- src/sys/fs/exfatfs/exfatfs_tables.c:1.1.2.2 Mon Jul 1 22:15:21 2024
+++ src/sys/fs/exfatfs/exfatfs_tables.c Wed Jul 24 00:38:26 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_tables.c,v 1.1.2.2 2024/07/01 22:15:21 perseant Exp $ */
+/* $NetBSD: exfatfs_tables.c,v 1.1.2.3 2024/07/24 00:38:26 perseant Exp $ */
/*-
* Copyright (c) 2022 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: exfatfs_tables.c,v 1.1.2.2 2024/07/01 22:15:21 perseant Exp $");
+__KERNEL_RCSID(0, "$NetBSD: exfatfs_tables.c,v 1.1.2.3 2024/07/24 00:38:26 perseant Exp $");
#include <sys/types.h>
#include <sys/queue.h>
@@ -449,15 +449,44 @@ const uint16_t exfat_invalid_filename_ch
0x003C, 0x003E, 0x003F, 0x005C, 0x007C
};
+#define MAX_INVALID_LENGTH 4
+static uint16_t invalid_names[] = {
+ '.', 0, 0, 0,
+ '.', '.', 0, 0,
+ 'C', 'O', 'N', 0,
+ 'P', 'R', 'N', 0,
+ 'N', 'U', 'L', 0,
+ 'C', 'O', 'M', '1',
+ 'C', 'O', 'M', '2',
+ 'C', 'O', 'M', '3',
+ 'C', 'O', 'M', '4',
+ 'C', 'O', 'M', '5',
+ 'C', 'O', 'M', '6',
+ 'C', 'O', 'M', '7',
+ 'C', 'O', 'M', '8',
+ 'C', 'O', 'M', '9',
+ 'C', 'O', 'M', '0',
+ 'L', 'P', 'T', '1',
+ 'L', 'P', 'T', '2',
+ 'L', 'P', 'T', '3',
+ 'L', 'P', 'T', '4',
+ 'L', 'P', 'T', '5',
+ 'L', 'P', 'T', '6',
+ 'L', 'P', 'T', '7',
+ 'L', 'P', 'T', '8',
+ 0, 0, 0, 0
+};
+
/*
* Check whether a filename is valid.
* Returns 0 if valid, non-zero on error.
*/
int
-exfatfs_check_filename_ucs2(uint16_t *name, int len)
+exfatfs_check_filename_ucs2(struct exfatfs *fs, uint16_t *name, int len)
{
int i;
unsigned j;
+ uint16_t *ucs2cp;
for (i = 0; i < len; i++) {
for (j = 0; j < sizeof(exfat_invalid_filename_chars)
@@ -466,6 +495,19 @@ exfatfs_check_filename_ucs2(uint16_t *na
return -1;
}
}
+
+ /* Check name against list of invalid names */
+ /* XXX invalid filenames are not in the spec */
+ if (len <= MAX_INVALID_LENGTH) {
+ for (ucs2cp = invalid_names; *ucs2cp != 0;
+ ucs2cp += MAX_INVALID_LENGTH) {
+ if (exfatfs_upcase_cmp(fs, name, len,
+ ucs2cp, MAX_INVALID_LENGTH)
+ == 0)
+ return -2;
+ }
+ }
+
return 0;
}
@@ -473,7 +515,7 @@ MALLOC_JUSTDEFINE(M_EURO, "Exfatfs upcas
"Exfatfs upcase table list entry");
void
-exfatfs_load_uctable(struct exfatfs *fs, uint16_t *table, int len)
+exfatfs_load_uctable(struct exfatfs *fs, const uint16_t *table, int len)
{
uint16_t begin = 0;
int16_t ucoff;
@@ -511,6 +553,7 @@ exfatfs_load_uctable(struct exfatfs *fs,
begin = current;
}
}
+ ++current;
}
/* Finish up last entry, if any */
if (ucoff != 0) {
@@ -548,7 +591,7 @@ exfatfs_upcase(struct exfatfs *fs, uint1
STAILQ_FOREACH(europ, &fs->xf_eurolist, euro_list) {
if (europ->euro_begin > wc)
break;
- if (europ->euro_begin >= wc && wc < europ->euro_end)
+ if (europ->euro_begin <= wc && wc < europ->euro_end)
return wc + europ->euro_ucoff;
}
return wc;
Index: src/sys/fs/exfatfs/exfatfs_tables.h
diff -u src/sys/fs/exfatfs/exfatfs_tables.h:1.1.2.2 src/sys/fs/exfatfs/exfatfs_tables.h:1.1.2.3
--- src/sys/fs/exfatfs/exfatfs_tables.h:1.1.2.2 Mon Jul 1 22:15:21 2024
+++ src/sys/fs/exfatfs/exfatfs_tables.h Wed Jul 24 00:38:26 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_tables.h,v 1.1.2.2 2024/07/01 22:15:21 perseant Exp $ */
+/* $NetBSD: exfatfs_tables.h,v 1.1.2.3 2024/07/24 00:38:26 perseant Exp $ */
/*-
* Copyright (c) 2022, 2024 The NetBSD Foundation, Inc.
@@ -38,8 +38,8 @@ struct exfatfs_upcase_range_offset {
int16_t euro_ucoff; /* Offset of uppercase version of characters */
};
-int exfatfs_check_filename_ucs2(uint16_t *, int);
-void exfatfs_load_uctable(struct exfatfs *, uint16_t *, int);
+int exfatfs_check_filename_ucs2(struct exfatfs *, uint16_t *, int);
+void exfatfs_load_uctable(struct exfatfs *, const uint16_t *, int);
void exfatfs_destroy_uctable(struct exfatfs *);
void exfatfs_upcase_str(struct exfatfs *, uint16_t *, int);
int exfatfs_upcase_cmp(struct exfatfs *, uint16_t *, int, uint16_t *, int);
Index: src/sys/fs/exfatfs/exfatfs_vfsops.c
diff -u src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.4 src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.5
--- src/sys/fs/exfatfs/exfatfs_vfsops.c:1.1.2.4 Fri Jul 19 16:19:16 2024
+++ src/sys/fs/exfatfs/exfatfs_vfsops.c Wed Jul 24 00:38:26 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_vfsops.c,v 1.1.2.4 2024/07/19 16:19:16 perseant Exp $ */
+/* $NetBSD: exfatfs_vfsops.c,v 1.1.2.5 2024/07/24 00:38:26 perseant Exp $ */
/*-
* Copyright (c) 2022 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: exfatfs_vfsops.c,v 1.1.2.4 2024/07/19 16:19:16 perseant Exp $");
+__KERNEL_RCSID(0, "$NetBSD: exfatfs_vfsops.c,v 1.1.2.5 2024/07/24 00:38:26 perseant Exp $");
struct vm_page;
@@ -60,6 +60,7 @@ struct vm_page;
#include <fs/exfatfs/exfatfs_extern.h>
#include <fs/exfatfs/exfatfs_inode.h>
#include <fs/exfatfs/exfatfs_mount.h>
+#include <fs/exfatfs/exfatfs_tables.h>
#include <fs/exfatfs/exfatfs_vfsops.h>
/* #define EXFATFS_VFSOPS_DEBUG */
@@ -615,6 +616,11 @@ exfatfs_unmount(struct mount *mp, int mn
DPRINTF((" spec_node_setmountedfs...\n"));
if (fs->xf_devvp->v_type != VBAD)
spec_node_setmountedfs(fs->xf_devvp, NULL);
+ DPRINTF((" clear dirty and update free percent...\n"));
+ fs->xf_VolumeFlags &= ~EXFATFS_VOLUME_DIRTY;
+ fs->xf_PercentInUse = (fs->xf_ClusterCount - fs->xf_FreeClusterCount)
+ / fs->xf_ClusterCount;
+ exfatfs_write_sb(fs);
DPRINTF((" lock devvp...\n"));
vn_lock(fs->xf_devvp, LK_EXCLUSIVE | LK_RETRY);
DPRINTF((" close devvp...\n"));
@@ -622,6 +628,8 @@ exfatfs_unmount(struct mount *mp, int mn
xmp->xm_flags & EXFATFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED);
DPRINTF((" vput devvp...\n"));
vput(fs->xf_devvp);
+ DPRINTF((" free upcase table...\n"));
+ exfatfs_destroy_uctable(fs);
DPRINTF((" free bitmap...\n"));
exfatfs_bitmap_destroy(fs);
DPRINTF((" destroy lock...\n"));
Index: src/sys/fs/exfatfs/exfatfs_vnops.c
diff -u src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.7 src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.8
--- src/sys/fs/exfatfs/exfatfs_vnops.c:1.1.2.7 Fri Jul 19 16:19:16 2024
+++ src/sys/fs/exfatfs/exfatfs_vnops.c Wed Jul 24 00:38:27 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: exfatfs_vnops.c,v 1.1.2.7 2024/07/19 16:19:16 perseant Exp $ */
+/* $NetBSD: exfatfs_vnops.c,v 1.1.2.8 2024/07/24 00:38:27 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.7 2024/07/19 16:19:16 perseant Exp $");
+__KERNEL_RCSID(0, "$NetBSD: exfatfs_vnops.c,v 1.1.2.8 2024/07/24 00:38:27 perseant Exp $");
#include <sys/buf.h>
#include <sys/dirent.h>
@@ -1003,17 +1003,23 @@ exfatfs_alloc(struct vnode *dvp, struct
fs = dxip->xi_fs;
KASSERT(fs != NULL);
- /* Create a new inode */
- xip = exfatfs_newxfinode(fs, 0, 0);
-
/*
* Find an empty location in a directory. If there is none,
* extend the directory by enough to hold three entries.
* Remember the logical byte offset of the empty space.
*/
+
+ /* Convert filename to UCS2 */
ucs2len = exfatfs_utf8ucs2str(cnp->cn_nameptr, cnp->cn_namelen,
ucs2filename, EXFATFS_MAX_NAMELEN);
- exfatfs_upcase_str(fs, ucs2filename, ucs2len);
+ /* exfatfs_upcase_str(fs, ucs2filename, ucs2len); */
+
+ /* Check name for forbidden characters: from section 7.7.3 */
+ if (exfatfs_check_filename_ucs2(fs, ucs2filename, ucs2len) != 0)
+ return EINVAL;
+
+ /* Create a new inode */
+ xip = exfatfs_newxfinode(fs, 0, 0);
contig = 2 + howmany(ucs2len, EXFATFS_NAME_CHUNKSIZE);
DPRINTF(("alloc: namelen %lu -> ucs2len=%lu, contig=%lu\n",
@@ -2404,7 +2410,7 @@ detrunc(struct xfinode *xip, off_t bytes
* fragmented but has become so.
*/
static int
-rewrite_fat(struct xfinode *xip, uint32_t clustercount)
+rewrite_fat(struct xfinode *xip, uint32_t clustercount, int ioflags)
{
uint32_t lcn, pcn;
struct buf *bp = NULL;
@@ -2424,13 +2430,20 @@ rewrite_fat(struct xfinode *xip, uint32_
= (lcn == clustercount - 1
? 0xffffffff : pcn + 1);
if (EXFATFS_FATBLK(fs, pcn) != EXFATFS_FATBLK(fs, pcn + 1)) {
- bdwrite(bp);
+ if (ioflags)
+ bwrite(bp);
+ else
+ bdwrite(bp);
bp = NULL;
}
}
- if (bp != NULL)
- bdwrite(bp);
+ if (bp != NULL) {
+ if (ioflags)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+ }
return 0;
}
@@ -2543,7 +2556,7 @@ deextend(struct xfinode *xip, off_t byte
" with 0x%x != 0x%x+1\n",
INUM(xip), pcn, opcn));
CLR_DSE_NOFATCHAIN(xip);
- if ((error = rewrite_fat(xip, lcn)) != 0)
+ if ((error = rewrite_fat(xip, lcn, ioflags)) != 0)
return error;
}
if (!IS_DSE_NOFATCHAIN(xip)) {
@@ -2561,6 +2574,21 @@ deextend(struct xfinode *xip, off_t byte
bwrite(bp);
else
bdwrite(bp);
+
+ /* And it has no successor */
+ if ((error = bread(fs->xf_devvp,
+ EXFATFS_FATBLK(fs, pcn),
+ FATBSIZE(fs), 0, &bp)) != 0)
+ return error;
+ ((uint32_t *)bp->b_data)[EXFATFS_FATOFF(pcn)]
+ = 0xffffffff;
+ DPRINTF(("FAT %lu -> -1\n",
+ (unsigned long)pcn));
+ if (ioflags)
+ bwrite(bp);
+ else
+ bdwrite(bp);
+
bp = NULL;
}
}