diff options
-rw-r--r-- | sys/conf/NOTES | 12 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/conf/options | 3 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_fileno.c | 163 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_vfsops.c | 14 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_vnops.c | 46 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfsmount.h | 24 | ||||
-rw-r--r-- | sys/modules/msdosfs/Makefile | 5 |
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> |