summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/conf/NOTES12
-rw-r--r--sys/conf/files1
-rw-r--r--sys/conf/options3
-rw-r--r--sys/fs/msdosfs/msdosfs_fileno.c163
-rw-r--r--sys/fs/msdosfs/msdosfs_vfsops.c14
-rw-r--r--sys/fs/msdosfs/msdosfs_vnops.c46
-rw-r--r--sys/fs/msdosfs/msdosfsmount.h24
-rw-r--r--sys/modules/msdosfs/Makefile5
8 files changed, 253 insertions, 15 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index f944152..fd1b42a 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -843,6 +843,18 @@ options MSDOSFS_ICONV
options NTFS_ICONV
options UDF_ICONV
+# Experimental support for large MS-DOS filesystems.
+#
+# WARNING: This uses at least 32 bytes of kernel memory (which is not
+# reclaimed until the FS is unmounted) for each file on disk to map
+# between the 32-bit inode numbers used by VFS and the 64-bit pseudo-inode
+# numbers used internally by msdosfs. This is only safe to use in certain
+# controlled situations (e.g. read-only FS with less than 1 million files).
+# Since the mappings do not persist across unmounts (or reboots), these
+# filesystems are not suitable for exporting through NFS, or any other
+# application that requires fixed inode numbers.
+options MSDOSFS_LARGE
+
#####################################################################
# POSIX P1003.1B
diff --git a/sys/conf/files b/sys/conf/files
index a25b963..7237d64 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -880,6 +880,7 @@ fs/msdosfs/msdosfs_fat.c optional msdosfs
fs/msdosfs/msdosfs_lookup.c optional msdosfs
fs/msdosfs/msdosfs_vfsops.c optional msdosfs
fs/msdosfs/msdosfs_vnops.c optional msdosfs
+fs/msdosfs/msdosfs_fileno.c optional msdosfs_large
fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv
fs/ntfs/ntfs_compr.c optional ntfs
fs/ntfs/ntfs_ihash.c optional ntfs
diff --git a/sys/conf/options b/sys/conf/options
index 2fb6582..b593318 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -219,6 +219,9 @@ NFS_ROOT opt_nfsroot.h
NETSMB opt_netsmb.h
NETSMBCRYPTO opt_netsmb.h
+# Experimental support for large MS-DOS filesystems; SEE WARNING IN "NOTES"!
+MSDOSFS_LARGE opt_msdosfs.h
+
# Options used only in subr_param.c.
HZ opt_param.h
MAXFILES opt_param.h
diff --git a/sys/fs/msdosfs/msdosfs_fileno.c b/sys/fs/msdosfs/msdosfs_fileno.c
new file mode 100644
index 0000000..ad6d4a7
--- /dev/null
+++ b/sys/fs/msdosfs/msdosfs_fileno.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2003-2004 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Compress 64-bit file numbers into temporary, unique 32-bit file numbers.
+ * This is needed because the algorithm we use to calculate these numbers
+ * generates 64-bit quantities, but struct dirent's d_fileno member and
+ * struct vnodeattr's va_fileid member only have space for 32 bits.
+ *
+ * 32-bit file numbers are generated sequentially, and stored in a
+ * red-black tree, indexed on 64-bit file number. The mappings do not
+ * persist across reboots (or unmounts); anything that relies on this
+ * (e.g. NFS) will not work correctly. This scheme consumes 32 bytes
+ * of kernel memory per file (on i386), and it may be possible for a user
+ * to cause a panic by creating millions of tiny files.
+ *
+ * As an optimization, we split the file number space between statically
+ * allocated and dynamically allocated. File numbers less than
+ * FILENO_FIRST_DYN are left unchanged and do not have any tree nodes
+ * allocated to them.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <fs/msdosfs/bpb.h>
+#include <fs/msdosfs/bootsect.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/msdosfs/direntry.h>
+
+static MALLOC_DEFINE(M_MSDOSFSFILENO, "MSDOSFS fileno", "MSDOSFS fileno mapping node");
+
+static struct mtx fileno_mtx;
+MTX_SYSINIT(fileno, &fileno_mtx, "MSDOSFS fileno", MTX_DEF);
+
+RB_PROTOTYPE(msdosfs_filenotree, msdosfs_fileno, mf_tree,
+ msdosfs_fileno_compare)
+
+static int msdosfs_fileno_compare(struct msdosfs_fileno *,
+ struct msdosfs_fileno *);
+
+#define FILENO_FIRST_DYN 0xf0000000
+
+/* Initialize file number mapping structures. */
+void
+msdosfs_fileno_init(mp)
+ struct mount *mp;
+{
+ struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
+
+ RB_INIT(&pmp->pm_filenos);
+ pmp->pm_nfileno = FILENO_FIRST_DYN;
+ if (pmp->pm_HugeSectors > 0xffffffff /
+ (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1)
+ pmp->pm_flags |= MSDOSFS_LARGEFS;
+}
+
+/* Free 32-bit file number generation structures. */
+void
+msdosfs_fileno_free(mp)
+ struct mount *mp;
+{
+ struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
+ struct msdosfs_fileno *mf, *next;
+
+ for (mf = RB_MIN(msdosfs_filenotree, &pmp->pm_filenos); mf != NULL;
+ mf = next) {
+ next = RB_NEXT(msdosfs_filenotree, &pmp->pm_filenos, mf);
+ RB_REMOVE(msdosfs_filenotree, &pmp->pm_filenos, mf);
+ free(mf, M_MSDOSFSFILENO);
+ }
+}
+
+/* Map a 64-bit file number into a 32-bit one. */
+uint32_t
+msdosfs_fileno_map(mp, fileno)
+ struct mount *mp;
+ uint64_t fileno;
+{
+ struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
+ struct msdosfs_fileno key, *mf, *tmf;
+ uint32_t mapped;
+
+ if ((pmp->pm_flags & MSDOSFS_LARGEFS) == 0) {
+ KASSERT((uint32_t)fileno == fileno,
+ ("fileno >32 bits but not a large fs?"));
+ return ((uint32_t)fileno);
+ }
+ if (fileno < FILENO_FIRST_DYN)
+ return ((uint32_t)fileno);
+ mtx_lock(&fileno_mtx);
+ key.mf_fileno64 = fileno;
+ mf = RB_FIND(msdosfs_filenotree, &pmp->pm_filenos, &key);
+ if (mf != NULL) {
+ mapped = mf->mf_fileno32;
+ mtx_unlock(&fileno_mtx);
+ return (mapped);
+ }
+ if (pmp->pm_nfileno < FILENO_FIRST_DYN)
+ panic("msdosfs_fileno_map: wraparound");
+ mtx_unlock(&fileno_mtx);
+ mf = malloc(sizeof(*mf), M_MSDOSFSFILENO, M_WAITOK);
+ mtx_lock(&fileno_mtx);
+ tmf = RB_FIND(msdosfs_filenotree, &pmp->pm_filenos, &key);
+ if (tmf != NULL) {
+ mapped = tmf->mf_fileno32;
+ mtx_unlock(&fileno_mtx);
+ free(mf, M_MSDOSFSFILENO);
+ return (mapped);
+ }
+ mf->mf_fileno64 = fileno;
+ mapped = mf->mf_fileno32 = pmp->pm_nfileno++;
+ RB_INSERT(msdosfs_filenotree, &pmp->pm_filenos, mf);
+ mtx_unlock(&fileno_mtx);
+ return (mapped);
+}
+
+/* Compare by 64-bit file number. */
+static int
+msdosfs_fileno_compare(fa, fb)
+ struct msdosfs_fileno *fa, *fb;
+{
+
+ if (fa->mf_fileno64 > fb->mf_fileno64)
+ return (1);
+ else if (fa->mf_fileno64 < fb->mf_fileno64)
+ return (-1);
+ return (0);
+}
+
+RB_GENERATE(msdosfs_filenotree, msdosfs_fileno, mf_tree,
+ msdosfs_fileno_compare)
diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
index 3ac0fb6..5d0b3c5 100644
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -71,6 +71,8 @@
#include <fs/msdosfs/denode.h>
#include <fs/msdosfs/fat.h>
+#include "opt_msdosfs.h"
+
#define MSDOSFS_DFLTBSIZE 4096
#if 1 /*def PC98*/
@@ -227,6 +229,9 @@ msdosfs_mount(mp, path, data, ndp, td)
/*
* Process export requests.
*/
+ if ((args.export.ex_flags & MNT_EXPORTED) != 0 &&
+ (pmp->pm_flags & MSDOSFS_LARGEFS) != 0)
+ return (EOPNOTSUPP);
return (vfs_export(mp, &args.export));
}
}
@@ -415,6 +420,7 @@ mountmsdosfs(devvp, mp, td, argp)
pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
pmp->pm_HugeSectors = pmp->pm_Sectors;
}
+#ifndef MSDOSFS_LARGE
if (pmp->pm_HugeSectors > 0xffffffff /
(pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) {
/*
@@ -426,6 +432,7 @@ mountmsdosfs(devvp, mp, td, argp)
printf("mountmsdosfs(): disk too big, sorry\n");
goto error_exit;
}
+#endif /* !MSDOSFS_LARGE */
if (pmp->pm_RootDirEnts == 0) {
if (bsp->bs710.bsBootSectSig2 != BOOTSIG2
@@ -628,6 +635,10 @@ mountmsdosfs(devvp, mp, td, argp)
mp->mnt_flag |= MNT_LOCAL;
devvp->v_rdev->si_mountpoint = mp;
+#ifdef MSDOSFS_LARGE
+ msdosfs_fileno_init(mp);
+#endif
+
return 0;
error_exit:
@@ -720,6 +731,9 @@ msdosfs_unmount(mp, mntflags, td)
#endif
vrele(pmp->pm_devvp);
free(pmp->pm_inusemap, M_MSDOSFSFAT);
+#ifdef MSDOSFS_LARGE
+ msdosfs_fileno_free(mp);
+#endif
free(pmp, M_MSDOSFSMNT);
mp->mnt_data = (qaddr_t)0;
mp->mnt_flag &= ~MNT_LOCAL;
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
index 728fd10..05a296e 100644
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -77,6 +77,8 @@
#include <fs/msdosfs/denode.h>
#include <fs/msdosfs/fat.h>
+#include "opt_msdosfs.h"
+
#define DOS_FILESIZE_MAX 0xffffffff
/*
@@ -284,7 +286,7 @@ msdosfs_getattr(ap)
mode_t mode;
struct timespec ts;
u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
- u_long fileid;
+ uint64_t fileid;
getnanotime(&ts);
DETIMES(dep, &ts, &ts, &ts);
@@ -295,16 +297,22 @@ msdosfs_getattr(ap)
* doesn't work.
*/
if (dep->de_Attributes & ATTR_DIRECTORY) {
- fileid = cntobn(pmp, dep->de_StartCluster) * dirsperblk;
+ fileid = (uint64_t)cntobn(pmp, dep->de_StartCluster) *
+ dirsperblk;
if (dep->de_StartCluster == MSDOSFSROOT)
fileid = 1;
} else {
- fileid = cntobn(pmp, dep->de_dirclust) * dirsperblk;
+ fileid = (uint64_t)cntobn(pmp, dep->de_dirclust) *
+ dirsperblk;
if (dep->de_dirclust == MSDOSFSROOT)
- fileid = roottobn(pmp, 0) * dirsperblk;
- fileid += dep->de_diroffset / sizeof(struct direntry);
+ fileid = (uint64_t)roottobn(pmp, 0) * dirsperblk;
+ fileid += (uint64_t)dep->de_diroffset / sizeof(struct direntry);
}
- vap->va_fileid = fileid;
+#ifdef MSDOSFS_LARGE
+ vap->va_fileid = msdosfs_fileno_map(pmp->pm_mountp, fileid);
+#else
+ vap->va_fileid = (long)fileid;
+#endif
if ((dep->de_Attributes & ATTR_READONLY) == 0)
mode = S_IRWXU|S_IRWXG|S_IRWXO;
else
@@ -1453,7 +1461,7 @@ msdosfs_readdir(ap)
int blsize;
long on;
u_long cn;
- u_long fileno;
+ uint64_t fileno;
u_long dirsperblk;
long bias = 0;
daddr_t bn, lbn;
@@ -1525,11 +1533,17 @@ msdosfs_readdir(ap)
for (n = (int)offset / sizeof(struct direntry);
n < 2; n++) {
if (FAT32(pmp))
- dirbuf.d_fileno = cntobn(pmp,
+ fileno = (uint64_t)cntobn(pmp,
pmp->pm_rootdirblk)
* dirsperblk;
else
- dirbuf.d_fileno = 1;
+ fileno = 1;
+#ifdef MSDOSFS_LARGE
+ dirbuf.d_fileno = msdosfs_fileno_map(
+ pmp->pm_mountp, fileno);
+#else
+ dirbuf.d_fileno = (uint32_t)fileno;
+#endif
dirbuf.d_type = DT_DIR;
switch (n) {
case 0:
@@ -1640,19 +1654,25 @@ msdosfs_readdir(ap)
/* if this is the root directory */
if (fileno == MSDOSFSROOT)
if (FAT32(pmp))
- fileno = cntobn(pmp,
+ fileno = (uint64_t)cntobn(pmp,
pmp->pm_rootdirblk)
* dirsperblk;
else
fileno = 1;
else
- fileno = cntobn(pmp, fileno) * dirsperblk;
- dirbuf.d_fileno = fileno;
+ fileno = (uint64_t)cntobn(pmp, fileno) *
+ dirsperblk;
dirbuf.d_type = DT_DIR;
} else {
- dirbuf.d_fileno = offset / sizeof(struct direntry);
+ fileno = (uint64_t)offset / sizeof(struct direntry);
dirbuf.d_type = DT_REG;
}
+#ifdef MSDOSFS_LARGE
+ dirbuf.d_fileno = msdosfs_fileno_map(pmp->pm_mountp,
+ fileno);
+#else
+ dirbuf.d_fileno = (uint32_t)fileno;
+#endif
if (chksum != winChksum(dentp->deName)) {
dirbuf.d_namlen = dos2unixfn(dentp->deName,
(u_char *)dirbuf.d_name,
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
index 0ff712f..46f9cc5 100644
--- a/sys/fs/msdosfs/msdosfsmount.h
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -53,10 +53,14 @@
#ifdef _KERNEL
+#include <sys/tree.h>
+
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_MSDOSFSMNT);
#endif
+struct msdosfs_fileno;
+
/*
* Layout of the mount control block for a msdos filesystem.
*/
@@ -99,7 +103,20 @@ struct msdosfsmount {
void *pm_w2u; /* Unicode->Local iconv handle */
void *pm_u2d; /* Unicode->DOS iconv handle */
void *pm_d2u; /* DOS->Local iconv handle */
+ u_int32_t pm_nfileno; /* next 32-bit fileno */
+ RB_HEAD(msdosfs_filenotree, msdosfs_fileno) pm_filenos; /* 64<->32-bit fileno mapping */
+};
+
+/*
+ * A 64-bit file number and the 32-bit file number to which it is mapped,
+ * in a red-black tree node.
+ */
+struct msdosfs_fileno {
+ RB_ENTRY(msdosfs_fileno) mf_tree;
+ uint32_t mf_fileno32;
+ uint64_t mf_fileno64;
};
+
/* Byte offset in FAT on filesystem pmp, cluster cn */
#define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv)
@@ -202,6 +219,10 @@ int msdosfs_init(struct vfsconf *vfsp);
int msdosfs_uninit(struct vfsconf *vfsp);
int msdosfs_mountroot(void);
+void msdosfs_fileno_init(struct mount *);
+void msdosfs_fileno_free(struct mount *);
+uint32_t msdosfs_fileno_map(struct mount *, uint64_t);
+
#endif /* _KERNEL */
/*
@@ -219,7 +240,7 @@ struct msdosfs_args {
char *cs_win; /* Windows(Unicode) Charset */
char *cs_dos; /* DOS Charset */
char *cs_local; /* Local Charset */
- mode_t dirmask; /* dir mask to be applied for msdosfs perms */
+ mode_t dirmask; /* dir mask to be applied for msdosfs perms */
};
/*
@@ -236,6 +257,7 @@ struct msdosfs_args {
#define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */
#define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */
#define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */
+#define MSDOSFS_LARGEFS 0x10000000 /* perform fileno mapping */
#define MSDOSFS_ARGSMAGIC 0xe4eff300
diff --git a/sys/modules/msdosfs/Makefile b/sys/modules/msdosfs/Makefile
index ef8989f..9ee8451 100644
--- a/sys/modules/msdosfs/Makefile
+++ b/sys/modules/msdosfs/Makefile
@@ -3,9 +3,12 @@
.PATH: ${.CURDIR}/../../fs/msdosfs
KMOD= msdosfs
-SRCS= vnode_if.h \
+SRCS= opt_msdosfs.h vnode_if.h \
msdosfs_conv.c msdosfs_denode.c msdosfs_fat.c msdosfs_lookup.c \
msdosfs_vfsops.c msdosfs_vnops.c
EXPORT_SYMS= msdosfs_iconv
+opt_msdosfs.h:
+ touch ${.TARGET}
+
.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud