msdosfs_vfsops.c revision 87068
150477Speter/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_vfsops.c 87068 2001-11-28 18:29:16Z jhb $ */ 233548Sjkh/* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ 32893Sdfr 42893Sdfr/*- 533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 72893Sdfr * All rights reserved. 82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 92893Sdfr * 102893Sdfr * Redistribution and use in source and binary forms, with or without 112893Sdfr * modification, are permitted provided that the following conditions 122893Sdfr * are met: 132893Sdfr * 1. Redistributions of source code must retain the above copyright 142893Sdfr * notice, this list of conditions and the following disclaimer. 152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright 162893Sdfr * notice, this list of conditions and the following disclaimer in the 172893Sdfr * documentation and/or other materials provided with the distribution. 182893Sdfr * 3. All advertising materials mentioning features or use of this software 192893Sdfr * must display the following acknowledgement: 202893Sdfr * This product includes software developed by TooLs GmbH. 212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products 222893Sdfr * derived from this software without specific prior written permission. 232893Sdfr * 242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 342893Sdfr */ 352893Sdfr/* 362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com) 378876Srgrimes * 382893Sdfr * You can do anything you want with this software, just don't say you wrote 392893Sdfr * it, and don't remove this notice. 408876Srgrimes * 412893Sdfr * This software is provided "as is". 428876Srgrimes * 432893Sdfr * The author supplies this software to be publicly redistributed on the 442893Sdfr * understanding that the author is not responsible for the correct 452893Sdfr * functioning of this software in any circumstances and is not liable for 462893Sdfr * any damages caused by this software. 478876Srgrimes * 482893Sdfr * October 1992 492893Sdfr */ 502893Sdfr 512893Sdfr#include <sys/param.h> 522893Sdfr#include <sys/systm.h> 5340651Sbde#include <sys/conf.h> 542893Sdfr#include <sys/namei.h> 552893Sdfr#include <sys/proc.h> 562893Sdfr#include <sys/kernel.h> 572893Sdfr#include <sys/vnode.h> 582893Sdfr#include <sys/mount.h> 5960041Sphk#include <sys/bio.h> 602893Sdfr#include <sys/buf.h> 6124131Sbde#include <sys/fcntl.h> 622893Sdfr#include <sys/malloc.h> 6333548Sjkh#include <sys/stat.h> /* defines ALLPERMS */ 6471576Sjasone#include <sys/mutex.h> 652893Sdfr 6677162Sru#include <fs/msdosfs/bpb.h> 6777162Sru#include <fs/msdosfs/bootsect.h> 6877162Sru#include <fs/msdosfs/direntry.h> 6977162Sru#include <fs/msdosfs/denode.h> 7077162Sru#include <fs/msdosfs/msdosfsmount.h> 7177162Sru#include <fs/msdosfs/fat.h> 722893Sdfr 7358706Sdillon#define MSDOSFS_DFLTBSIZE 4096 7458706Sdillon 7556674Snyan#if 1 /*def PC98*/ 7656674Snyan/* 7756674Snyan * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 7856674Snyan * garbage or a random value :-{ 7956674Snyan * If you want to use that broken-signatured media, define the 8056674Snyan * following symbol even though PC/AT. 8156674Snyan * (ex. mount PC-98 DOS formatted FD on PC/AT) 8256674Snyan */ 8356674Snyan#define MSDOSFS_NOCHECKSIG 8456674Snyan#endif 8556674Snyan 8630354SphkMALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); 8730354Sphkstatic MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); 8830309Sphk 8933548Sjkhstatic int update_mp __P((struct mount *mp, struct msdosfs_args *argp)); 9012338Sbdestatic int mountmsdosfs __P((struct vnode *devvp, struct mount *mp, 9183366Sjulian struct thread *td, struct msdosfs_args *argp)); 9212338Sbdestatic int msdosfs_fhtovp __P((struct mount *, struct fid *, 9351138Salfred struct vnode **)); 9412338Sbdestatic int msdosfs_mount __P((struct mount *, char *, caddr_t, 9583366Sjulian struct nameidata *, struct thread *)); 9612338Sbdestatic int msdosfs_root __P((struct mount *, struct vnode **)); 9712338Sbdestatic int msdosfs_statfs __P((struct mount *, struct statfs *, 9883366Sjulian struct thread *)); 9912338Sbdestatic int msdosfs_sync __P((struct mount *, int, struct ucred *, 10083366Sjulian struct thread *)); 10183366Sjulianstatic int msdosfs_unmount __P((struct mount *, int, struct thread *)); 10212338Sbdestatic int msdosfs_vptofh __P((struct vnode *, struct fid *)); 10312338Sbde 10433548Sjkhstatic int 10533548Sjkhupdate_mp(mp, argp) 10633548Sjkh struct mount *mp; 10733548Sjkh struct msdosfs_args *argp; 10833548Sjkh{ 10933548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 11033548Sjkh int error; 11133548Sjkh 11233548Sjkh pmp->pm_gid = argp->gid; 11333548Sjkh pmp->pm_uid = argp->uid; 11433548Sjkh pmp->pm_mask = argp->mask & ALLPERMS; 11533548Sjkh pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; 11633768Sache if (pmp->pm_flags & MSDOSFSMNT_U2WTABLE) { 11733747Sache bcopy(argp->u2w, pmp->pm_u2w, sizeof(pmp->pm_u2w)); 11833768Sache bcopy(argp->d2u, pmp->pm_d2u, sizeof(pmp->pm_d2u)); 11933768Sache bcopy(argp->u2d, pmp->pm_u2d, sizeof(pmp->pm_u2d)); 12033768Sache } 12133768Sache if (pmp->pm_flags & MSDOSFSMNT_ULTABLE) { 12233762Sache bcopy(argp->ul, pmp->pm_ul, sizeof(pmp->pm_ul)); 12333768Sache bcopy(argp->lu, pmp->pm_lu, sizeof(pmp->pm_lu)); 12433768Sache } 12533548Sjkh 12633548Sjkh if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 12733548Sjkh pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 12833548Sjkh else if (!(pmp->pm_flags & 12933548Sjkh (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 13033548Sjkh struct vnode *rootvp; 13133548Sjkh 13233548Sjkh /* 13333548Sjkh * Try to divine whether to support Win'95 long filenames 13433548Sjkh */ 13533548Sjkh if (FAT32(pmp)) 13633548Sjkh pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 13733548Sjkh else { 13833548Sjkh if ((error = msdosfs_root(mp, &rootvp)) != 0) 13933548Sjkh return error; 14033548Sjkh pmp->pm_flags |= findwin95(VTODE(rootvp)) 14133548Sjkh ? MSDOSFSMNT_LONGNAME 14233548Sjkh : MSDOSFSMNT_SHORTNAME; 14333548Sjkh vput(rootvp); 14433548Sjkh } 14533548Sjkh } 14633548Sjkh return 0; 14733548Sjkh} 14833548Sjkh 1492893Sdfr/* 1508876Srgrimes * mp - path - addr in user space of mount point (ie /usr or whatever) 1512893Sdfr * data - addr in user space of mount params including the name of the block 1528876Srgrimes * special file to treat as a filesystem. 1532893Sdfr */ 15412144Sphkstatic int 15583366Sjulianmsdosfs_mount(mp, path, data, ndp, td) 1562893Sdfr struct mount *mp; 1572893Sdfr char *path; 1582893Sdfr caddr_t data; 1592893Sdfr struct nameidata *ndp; 16083366Sjulian struct thread *td; 1612893Sdfr{ 1622893Sdfr struct vnode *devvp; /* vnode for blk device to mount */ 1632893Sdfr struct msdosfs_args args; /* will hold data from mount request */ 16433548Sjkh /* msdosfs specific mount control block */ 16533548Sjkh struct msdosfsmount *pmp = NULL; 16633548Sjkh size_t size; 1672893Sdfr int error, flags; 16833548Sjkh mode_t accessmode; 1692893Sdfr 17033548Sjkh error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); 1713152Sphk if (error) 17233548Sjkh return (error); 17335202Sdt if (args.magic != MSDOSFS_ARGSMAGIC) 17433548Sjkh args.flags = 0; 1752893Sdfr /* 17633548Sjkh * If updating, check whether changing from read-only to 17733548Sjkh * read/write; if there is no device name, that's all we do. 1782893Sdfr */ 1792893Sdfr if (mp->mnt_flag & MNT_UPDATE) { 18033548Sjkh pmp = VFSTOMSDOSFS(mp); 1812893Sdfr error = 0; 18233548Sjkh if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { 1832893Sdfr flags = WRITECLOSE; 1842893Sdfr if (mp->mnt_flag & MNT_FORCE) 1852893Sdfr flags |= FORCECLOSE; 18676688Siedowse error = vflush(mp, 0, flags); 1872893Sdfr } 1882893Sdfr if (!error && (mp->mnt_flag & MNT_RELOAD)) 1892893Sdfr /* not yet implemented */ 19033548Sjkh error = EOPNOTSUPP; 1912893Sdfr if (error) 19233548Sjkh return (error); 19333548Sjkh if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { 19433548Sjkh /* 19533548Sjkh * If upgrade to read-write by non-root, then verify 19633548Sjkh * that user has necessary permissions on the device. 19733548Sjkh */ 19887067Sjhb if (suser_td(td)) { 19933548Sjkh devvp = pmp->pm_devvp; 20083366Sjulian vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); 20133548Sjkh error = VOP_ACCESS(devvp, VREAD | VWRITE, 20283366Sjulian td->td_proc->p_ucred, td); 20333548Sjkh if (error) { 20483366Sjulian VOP_UNLOCK(devvp, 0, td); 20533548Sjkh return (error); 20633548Sjkh } 20783366Sjulian VOP_UNLOCK(devvp, 0, td); 20833548Sjkh } 20933548Sjkh pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 21033548Sjkh } 2112893Sdfr if (args.fspec == 0) { 21287068Sjhb#ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ 21333548Sjkh if (args.flags & MSDOSFSMNT_MNTOPT) { 21433548Sjkh pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; 21533548Sjkh pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; 21633548Sjkh if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 21733548Sjkh pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 21833548Sjkh } 21933548Sjkh#endif 2202893Sdfr /* 2212893Sdfr * Process export requests. 2222893Sdfr */ 22375934Sphk return (vfs_export(mp, &args.export)); 2242893Sdfr } 2252893Sdfr } 2262893Sdfr /* 22733548Sjkh * Not an update, or updating the name: look up the name 22833548Sjkh * and verify that it refers to a sensible block device. 2292893Sdfr */ 23083366Sjulian NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, td); 2312893Sdfr error = namei(ndp); 23233548Sjkh if (error) 23333548Sjkh return (error); 23433548Sjkh devvp = ndp->ni_vp; 23554655Seivind NDFREE(ndp, NDF_ONLY_PNBUF); 2368876Srgrimes 23755756Sphk if (!vn_isdisk(devvp, &error)) { 2382893Sdfr vrele(devvp); 23955756Sphk return (error); 2402893Sdfr } 2412893Sdfr /* 24233548Sjkh * If mount by non-root, then verify that user has necessary 24333548Sjkh * permissions on the device. 2442893Sdfr */ 24587067Sjhb if (suser_td(td)) { 24633548Sjkh accessmode = VREAD; 24733548Sjkh if ((mp->mnt_flag & MNT_RDONLY) == 0) 24833548Sjkh accessmode |= VWRITE; 24983366Sjulian vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); 25083366Sjulian error = VOP_ACCESS(devvp, accessmode, td->td_proc->p_ucred, td); 25133548Sjkh if (error) { 25233548Sjkh vput(devvp); 25333548Sjkh return (error); 25433548Sjkh } 25583366Sjulian VOP_UNLOCK(devvp, 0, td); 25633548Sjkh } 25733548Sjkh if ((mp->mnt_flag & MNT_UPDATE) == 0) { 25883366Sjulian error = mountmsdosfs(devvp, mp, td, &args); 25933548Sjkh#ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 26033548Sjkh pmp = VFSTOMSDOSFS(mp); 26133548Sjkh#endif 26233548Sjkh } else { 2632893Sdfr if (devvp != pmp->pm_devvp) 26433548Sjkh error = EINVAL; /* XXX needs translation */ 2652893Sdfr else 2662893Sdfr vrele(devvp); 2672893Sdfr } 2682893Sdfr if (error) { 2692893Sdfr vrele(devvp); 27033548Sjkh return (error); 27133548Sjkh } 27233548Sjkh 27333548Sjkh error = update_mp(mp, &args); 27433548Sjkh if (error) { 27583366Sjulian msdosfs_unmount(mp, MNT_FORCE, td); 2762893Sdfr return error; 2772893Sdfr } 27833548Sjkh (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, 27933548Sjkh &size); 28033548Sjkh bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); 28183366Sjulian (void) msdosfs_statfs(mp, &mp->mnt_stat, td); 2822893Sdfr#ifdef MSDOSFS_DEBUG 2833311Sphk printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 2842893Sdfr#endif 28533548Sjkh return (0); 2862893Sdfr} 2872893Sdfr 28812144Sphkstatic int 28983366Sjulianmountmsdosfs(devvp, mp, td, argp) 2902893Sdfr struct vnode *devvp; 2912893Sdfr struct mount *mp; 29283366Sjulian struct thread *td; 29333548Sjkh struct msdosfs_args *argp; 2942893Sdfr{ 29533548Sjkh struct msdosfsmount *pmp; 29633548Sjkh struct buf *bp; 2972893Sdfr dev_t dev = devvp->v_rdev; 2982893Sdfr union bootsector *bsp; 2992893Sdfr struct byte_bpb33 *b33; 3002893Sdfr struct byte_bpb50 *b50; 30133548Sjkh struct byte_bpb710 *b710; 30233548Sjkh u_int8_t SecPerClust; 30355188Sbp u_long clusters; 30433548Sjkh int ronly, error; 3052893Sdfr 3062893Sdfr /* 30733548Sjkh * Disallow multiple mounts of the same device. 30833548Sjkh * Disallow mounting of a device that is currently in use 30933548Sjkh * (except for root, which might share swap device for miniroot). 31033548Sjkh * Flush out any old buffers remaining from a previous use. 3112893Sdfr */ 3123152Sphk error = vfs_mountedon(devvp); 3133152Sphk if (error) 31433548Sjkh return (error); 31533548Sjkh if (vcount(devvp) > 1 && devvp != rootvp) 31633548Sjkh return (EBUSY); 31783366Sjulian vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); 31883366Sjulian error = vinvalbuf(devvp, V_SAVE, td->td_proc->p_ucred, td, 0, 0); 31983366Sjulian VOP_UNLOCK(devvp, 0, td); 3203152Sphk if (error) 32133548Sjkh return (error); 3222893Sdfr 32333548Sjkh ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 32483366Sjulian vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); 32583366Sjulian error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, td); 32683366Sjulian VOP_UNLOCK(devvp, 0, td); 3273152Sphk if (error) 32833548Sjkh return (error); 32933548Sjkh 33033548Sjkh bp = NULL; /* both used in error_exit */ 33133548Sjkh pmp = NULL; 33233548Sjkh 3332893Sdfr /* 33433548Sjkh * Read the boot sector of the filesystem, and then check the 33533548Sjkh * boot signature. If not a dos boot sector then error out. 33656674Snyan * 33756674Snyan * NOTE: 2048 is a maximum sector size in current... 3382893Sdfr */ 33956674Snyan error = bread(devvp, 0, 2048, NOCRED, &bp); 3403152Sphk if (error) 3412893Sdfr goto error_exit; 34233548Sjkh bp->b_flags |= B_AGE; 34333548Sjkh bsp = (union bootsector *)bp->b_data; 34433548Sjkh b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 34533548Sjkh b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 34633548Sjkh b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; 34733548Sjkh 34856674Snyan#ifndef MSDOSFS_NOCHECKSIG 34987068Sjhb if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 35087068Sjhb || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 35187068Sjhb error = EINVAL; 35287068Sjhb goto error_exit; 35387068Sjhb } 35456674Snyan#endif 3552893Sdfr 35669781Sdwmalone pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 3572893Sdfr pmp->pm_mountp = mp; 3582893Sdfr 3592893Sdfr /* 3602893Sdfr * Compute several useful quantities from the bpb in the 3612893Sdfr * bootsector. Copy in the dos 5 variant of the bpb then fix up 3622893Sdfr * the fields that are different between dos 5 and dos 3.3. 3632893Sdfr */ 36433548Sjkh SecPerClust = b50->bpbSecPerClust; 3652893Sdfr pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 3662893Sdfr pmp->pm_ResSectors = getushort(b50->bpbResSectors); 3672893Sdfr pmp->pm_FATs = b50->bpbFATs; 3682893Sdfr pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 3692893Sdfr pmp->pm_Sectors = getushort(b50->bpbSectors); 3702893Sdfr pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 3712893Sdfr pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 3722893Sdfr pmp->pm_Heads = getushort(b50->bpbHeads); 37333548Sjkh pmp->pm_Media = b50->bpbMedia; 3742893Sdfr 37556674Snyan /* calculate the ratio of sector size to DEV_BSIZE */ 37656674Snyan pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 37756674Snyan 37887068Sjhb /* XXX - We should probably check more values here */ 37987068Sjhb if (!pmp->pm_BytesPerSec || !SecPerClust 38087068Sjhb || !pmp->pm_Heads || pmp->pm_Heads > 255 38116363Sasami#ifdef PC98 38287068Sjhb || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { 38316363Sasami#else 38487068Sjhb || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { 38516363Sasami#endif 38687068Sjhb error = EINVAL; 38787068Sjhb goto error_exit; 38887068Sjhb } 3892893Sdfr 3902893Sdfr if (pmp->pm_Sectors == 0) { 3912893Sdfr pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 3922893Sdfr pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 3932893Sdfr } else { 3942893Sdfr pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 3952893Sdfr pmp->pm_HugeSectors = pmp->pm_Sectors; 3962893Sdfr } 39735511Sdt if (pmp->pm_HugeSectors > 0xffffffff / 39835511Sdt (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { 39933548Sjkh /* 40033548Sjkh * We cannot deal currently with this size of disk 40133548Sjkh * due to fileid limitations (see msdosfs_getattr and 40233548Sjkh * msdosfs_readdir) 40333548Sjkh */ 40433548Sjkh error = EINVAL; 40535046Sache printf("mountmsdosfs(): disk too big, sorry\n"); 40633548Sjkh goto error_exit; 40733548Sjkh } 40833548Sjkh 40933548Sjkh if (pmp->pm_RootDirEnts == 0) { 41033548Sjkh if (bsp->bs710.bsBootSectSig2 != BOOTSIG2 41133548Sjkh || bsp->bs710.bsBootSectSig3 != BOOTSIG3 41233548Sjkh || pmp->pm_Sectors 41333548Sjkh || pmp->pm_FATsecs 41433548Sjkh || getushort(b710->bpbFSVers)) { 41533548Sjkh error = EINVAL; 41635046Sache printf("mountmsdosfs(): bad FAT32 filesystem\n"); 41733548Sjkh goto error_exit; 41833548Sjkh } 41933548Sjkh pmp->pm_fatmask = FAT32_MASK; 42033548Sjkh pmp->pm_fatmult = 4; 42133548Sjkh pmp->pm_fatdiv = 1; 42233548Sjkh pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 42333548Sjkh if (getushort(b710->bpbExtFlags) & FATMIRROR) 42433548Sjkh pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 42533548Sjkh else 42633548Sjkh pmp->pm_flags |= MSDOSFS_FATMIRROR; 42733548Sjkh } else 42833548Sjkh pmp->pm_flags |= MSDOSFS_FATMIRROR; 42933548Sjkh 43056674Snyan /* 43156674Snyan * Check a few values (could do some more): 43256674Snyan * - logical sector size: power of 2, >= block size 43356674Snyan * - sectors per cluster: power of 2, >= 1 43456674Snyan * - number of sectors: >= 1, <= size of partition 43556674Snyan */ 43656674Snyan if ( (SecPerClust == 0) 43756674Snyan || (SecPerClust & (SecPerClust - 1)) 43856674Snyan || (pmp->pm_BytesPerSec < DEV_BSIZE) 43956674Snyan || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 44056674Snyan || (pmp->pm_HugeSectors == 0) 44156674Snyan ) { 44256674Snyan error = EINVAL; 44356674Snyan goto error_exit; 44456674Snyan } 44533548Sjkh 44656674Snyan pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 44756674Snyan pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 44856674Snyan pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 44956674Snyan SecPerClust *= pmp->pm_BlkPerSec; 45056674Snyan 45156674Snyan pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 45256674Snyan 45333548Sjkh if (FAT32(pmp)) { 45433548Sjkh pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 45533548Sjkh pmp->pm_firstcluster = pmp->pm_fatblk 45633548Sjkh + (pmp->pm_FATs * pmp->pm_FATsecs); 45756674Snyan pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 45833548Sjkh } else { 45933548Sjkh pmp->pm_rootdirblk = pmp->pm_fatblk + 46033548Sjkh (pmp->pm_FATs * pmp->pm_FATsecs); 46133548Sjkh pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 46256674Snyan + DEV_BSIZE - 1) 46356674Snyan / DEV_BSIZE; /* in blocks */ 46433548Sjkh pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 46533548Sjkh } 46633548Sjkh 46755188Sbp pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 46855188Sbp SecPerClust + 1; 46956674Snyan pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 47033548Sjkh 47133548Sjkh if (pmp->pm_fatmask == 0) { 47233548Sjkh if (pmp->pm_maxcluster 47333548Sjkh <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 47433548Sjkh /* 47533548Sjkh * This will usually be a floppy disk. This size makes 47633548Sjkh * sure that one fat entry will not be split across 47733548Sjkh * multiple blocks. 47833548Sjkh */ 47933548Sjkh pmp->pm_fatmask = FAT12_MASK; 48033548Sjkh pmp->pm_fatmult = 3; 48133548Sjkh pmp->pm_fatdiv = 2; 48233548Sjkh } else { 48333548Sjkh pmp->pm_fatmask = FAT16_MASK; 48433548Sjkh pmp->pm_fatmult = 2; 48533548Sjkh pmp->pm_fatdiv = 1; 48633548Sjkh } 48733548Sjkh } 48855188Sbp 48955188Sbp clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 49055188Sbp if (pmp->pm_maxcluster >= clusters) { 49155188Sbp printf("Warning: number of clusters (%ld) exceeds FAT " 49255188Sbp "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 49355188Sbp pmp->pm_maxcluster = clusters - 1; 49455188Sbp } 49555188Sbp 49655188Sbp 4972893Sdfr if (FAT12(pmp)) 4982893Sdfr pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; 4992893Sdfr else 50058706Sdillon pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE; 50133548Sjkh 50256674Snyan pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 50356674Snyan pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 5042893Sdfr 5052893Sdfr /* 5062893Sdfr * Compute mask and shift value for isolating cluster relative byte 5072893Sdfr * offsets and cluster numbers from a file offset. 5082893Sdfr */ 50956674Snyan pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 51033548Sjkh pmp->pm_crbomask = pmp->pm_bpcluster - 1; 51133548Sjkh pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 51233548Sjkh 51333548Sjkh /* 51433548Sjkh * Check for valid cluster size 51533548Sjkh * must be a power of 2 51633548Sjkh */ 51733548Sjkh if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 5182893Sdfr error = EINVAL; 5192893Sdfr goto error_exit; 5202893Sdfr } 5212893Sdfr 52233548Sjkh /* 52333548Sjkh * Release the bootsector buffer. 52433548Sjkh */ 52533548Sjkh brelse(bp); 52633548Sjkh bp = NULL; 52733548Sjkh 52833548Sjkh /* 52933548Sjkh * Check FSInfo. 53033548Sjkh */ 53133548Sjkh if (pmp->pm_fsinfo) { 53233548Sjkh struct fsinfo *fp; 53333548Sjkh 53456674Snyan if ((error = bread(devvp, pmp->pm_fsinfo, fsi_size(pmp), 53556674Snyan NOCRED, &bp)) != 0) 53633548Sjkh goto error_exit; 53733548Sjkh fp = (struct fsinfo *)bp->b_data; 53833548Sjkh if (!bcmp(fp->fsisig1, "RRaA", 4) 53933548Sjkh && !bcmp(fp->fsisig2, "rrAa", 4) 54033548Sjkh && !bcmp(fp->fsisig3, "\0\0\125\252", 4) 54133548Sjkh && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) 54233548Sjkh pmp->pm_nxtfree = getulong(fp->fsinxtfree); 54333548Sjkh else 54433548Sjkh pmp->pm_fsinfo = 0; 54533548Sjkh brelse(bp); 54633548Sjkh bp = NULL; 54716363Sasami } 5482893Sdfr 5492893Sdfr /* 55033548Sjkh * Check and validate (or perhaps invalidate?) the fsinfo structure? XXX 5512893Sdfr */ 5522893Sdfr 5532893Sdfr /* 5542893Sdfr * Allocate memory for the bitmap of allocated clusters, and then 5552893Sdfr * fill it in. 5562893Sdfr */ 5572893Sdfr pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) 5582893Sdfr / N_INUSEBITS) 5592893Sdfr * sizeof(*pmp->pm_inusemap), 5602893Sdfr M_MSDOSFSFAT, M_WAITOK); 5612893Sdfr 5622893Sdfr /* 5632893Sdfr * fillinusemap() needs pm_devvp. 5642893Sdfr */ 5652893Sdfr pmp->pm_dev = dev; 5662893Sdfr pmp->pm_devvp = devvp; 5672893Sdfr 5682893Sdfr /* 5692893Sdfr * Have the inuse map filled in. 5702893Sdfr */ 57133548Sjkh if ((error = fillinusemap(pmp)) != 0) 5722893Sdfr goto error_exit; 5732893Sdfr 5742893Sdfr /* 5752893Sdfr * If they want fat updates to be synchronous then let them suffer 5762893Sdfr * the performance degradation in exchange for the on disk copy of 5772893Sdfr * the fat being correct just about all the time. I suppose this 5782893Sdfr * would be a good thing to turn on if the kernel is still flakey. 5792893Sdfr */ 58033548Sjkh if (mp->mnt_flag & MNT_SYNCHRONOUS) 58133548Sjkh pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 5822893Sdfr 5832893Sdfr /* 5842893Sdfr * Finish up. 5852893Sdfr */ 58633548Sjkh if (ronly) 58733548Sjkh pmp->pm_flags |= MSDOSFSMNT_RONLY; 58833548Sjkh else 5892893Sdfr pmp->pm_fmod = 1; 5902893Sdfr mp->mnt_data = (qaddr_t) pmp; 59150256Sbde mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 59223134Sbde mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 59323997Speter mp->mnt_flag |= MNT_LOCAL; 59466886Seivind devvp->v_rdev->si_mountpoint = mp; 5952893Sdfr 5962893Sdfr return 0; 5972893Sdfr 59833548Sjkherror_exit: 59933548Sjkh if (bp) 60033548Sjkh brelse(bp); 60183366Sjulian (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, NOCRED, td); 6022893Sdfr if (pmp) { 6032893Sdfr if (pmp->pm_inusemap) 60433548Sjkh free(pmp->pm_inusemap, M_MSDOSFSFAT); 60533548Sjkh free(pmp, M_MSDOSFSMNT); 60633548Sjkh mp->mnt_data = (qaddr_t)0; 6072893Sdfr } 60833548Sjkh return (error); 6092893Sdfr} 6102893Sdfr 6112893Sdfr/* 6122893Sdfr * Unmount the filesystem described by mp. 6132893Sdfr */ 61412144Sphkstatic int 61583366Sjulianmsdosfs_unmount(mp, mntflags, td) 6162893Sdfr struct mount *mp; 6172893Sdfr int mntflags; 61883366Sjulian struct thread *td; 6192893Sdfr{ 62033548Sjkh struct msdosfsmount *pmp; 62133548Sjkh int error, flags; 6222893Sdfr 62333548Sjkh flags = 0; 62433548Sjkh if (mntflags & MNT_FORCE) 6252893Sdfr flags |= FORCECLOSE; 62676688Siedowse error = vflush(mp, 0, flags); 6273152Sphk if (error) 6282893Sdfr return error; 62933548Sjkh pmp = VFSTOMSDOSFS(mp); 63066886Seivind pmp->pm_devvp->v_rdev->si_mountpoint = NULL; 63133548Sjkh#ifdef MSDOSFS_DEBUG 63233548Sjkh { 63333548Sjkh struct vnode *vp = pmp->pm_devvp; 63433548Sjkh 63533548Sjkh printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); 63633548Sjkh printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n", 63733548Sjkh vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); 63851486Sdillon printf("id %lu, mount %p, op %p\n", 63951486Sdillon vp->v_id, vp->v_mount, vp->v_op); 64033548Sjkh printf("freef %p, freeb %p, mount %p\n", 64171999Sphk TAILQ_NEXT(vp, v_freelist), vp->v_freelist.tqe_prev, 64233548Sjkh vp->v_mount); 64333548Sjkh printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", 64440790Speter TAILQ_FIRST(&vp->v_cleanblkhd), 64540790Speter TAILQ_FIRST(&vp->v_dirtyblkhd), 64633548Sjkh vp->v_numoutput, vp->v_type); 64733548Sjkh printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", 64833548Sjkh vp->v_socket, vp->v_tag, 64933548Sjkh ((u_int *)vp->v_data)[0], 65033548Sjkh ((u_int *)vp->v_data)[1]); 65133548Sjkh } 65233548Sjkh#endif 65334266Sjulian error = VOP_CLOSE(pmp->pm_devvp, 65434266Sjulian (pmp->pm_flags&MSDOSFSMNT_RONLY) ? FREAD : FREAD | FWRITE, 65583366Sjulian NOCRED, td); 6562893Sdfr vrele(pmp->pm_devvp); 65733548Sjkh free(pmp->pm_inusemap, M_MSDOSFSFAT); 65833548Sjkh free(pmp, M_MSDOSFSMNT); 65933548Sjkh mp->mnt_data = (qaddr_t)0; 66023997Speter mp->mnt_flag &= ~MNT_LOCAL; 66133548Sjkh return (error); 6622893Sdfr} 6632893Sdfr 66412144Sphkstatic int 6652893Sdfrmsdosfs_root(mp, vpp) 6662893Sdfr struct mount *mp; 6672893Sdfr struct vnode **vpp; 6682893Sdfr{ 66933548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 6702893Sdfr struct denode *ndep; 6712893Sdfr int error; 6722893Sdfr 6732893Sdfr#ifdef MSDOSFS_DEBUG 67433548Sjkh printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 6752893Sdfr#endif 67633548Sjkh error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 67733548Sjkh if (error) 67833548Sjkh return (error); 67933548Sjkh *vpp = DETOV(ndep); 68033548Sjkh return (0); 6812893Sdfr} 6822893Sdfr 68312144Sphkstatic int 68483366Sjulianmsdosfs_statfs(mp, sbp, td) 6852893Sdfr struct mount *mp; 6862893Sdfr struct statfs *sbp; 68783366Sjulian struct thread *td; 6882893Sdfr{ 68933548Sjkh struct msdosfsmount *pmp; 6902893Sdfr 69133548Sjkh pmp = VFSTOMSDOSFS(mp); 6922893Sdfr sbp->f_bsize = pmp->pm_bpcluster; 6932893Sdfr sbp->f_iosize = pmp->pm_bpcluster; 69455188Sbp sbp->f_blocks = pmp->pm_maxcluster + 1; 6952893Sdfr sbp->f_bfree = pmp->pm_freeclustercount; 6962893Sdfr sbp->f_bavail = pmp->pm_freeclustercount; 6972893Sdfr sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 6982893Sdfr sbp->f_ffree = 0; /* what to put in here? */ 6992893Sdfr if (sbp != &mp->mnt_stat) { 70023134Sbde sbp->f_type = mp->mnt_vfc->vfc_typenum; 70133548Sjkh bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 70233548Sjkh bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 7032893Sdfr } 70433548Sjkh strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); 70533548Sjkh return (0); 7062893Sdfr} 7072893Sdfr 70812144Sphkstatic int 70983366Sjulianmsdosfs_sync(mp, waitfor, cred, td) 7102893Sdfr struct mount *mp; 7112893Sdfr int waitfor; 7122893Sdfr struct ucred *cred; 71383366Sjulian struct thread *td; 7142893Sdfr{ 71533548Sjkh struct vnode *vp, *nvp; 7162893Sdfr struct denode *dep; 71733548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 71833548Sjkh int error, allerror = 0; 7192893Sdfr 7202893Sdfr /* 7212893Sdfr * If we ever switch to not updating all of the fats all the time, 7222893Sdfr * this would be the place to update them from the first one. 7232893Sdfr */ 72446568Speter if (pmp->pm_fmod != 0) { 72533548Sjkh if (pmp->pm_flags & MSDOSFSMNT_RONLY) 7262893Sdfr panic("msdosfs_sync: rofs mod"); 7272893Sdfr else { 7282893Sdfr /* update fats here */ 7292893Sdfr } 73046568Speter } 7312893Sdfr /* 73233548Sjkh * Write back each (modified) denode. 7332893Sdfr */ 73472200Sbmilekic mtx_lock(&mntvnode_mtx); 7352893Sdfrloop: 73685339Sdillon for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nvp) { 73733548Sjkh /* 73833548Sjkh * If the vnode that we are about to sync is no longer 73935511Sdt * associated with this mount point, start over. 74033548Sjkh */ 74133548Sjkh if (vp->v_mount != mp) 7422893Sdfr goto loop; 74385339Sdillon nvp = TAILQ_NEXT(vp, v_nmntvnodes); 74433548Sjkh 74578907Sjhb mtx_unlock(&mntvnode_mtx); 74672200Sbmilekic mtx_lock(&vp->v_interlock); 7472893Sdfr dep = VTODE(vp); 74835511Sdt if (vp->v_type == VNON || 74943305Sdillon ((dep->de_flag & 75035511Sdt (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 75143305Sdillon (TAILQ_EMPTY(&vp->v_dirtyblkhd) || waitfor == MNT_LAZY))) { 75272200Sbmilekic mtx_unlock(&vp->v_interlock); 75378907Sjhb mtx_lock(&mntvnode_mtx); 7542893Sdfr continue; 75523134Sbde } 75683366Sjulian error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 75723134Sbde if (error) { 75872200Sbmilekic mtx_lock(&mntvnode_mtx); 75923134Sbde if (error == ENOENT) 76023134Sbde goto loop; 76123134Sbde continue; 76223134Sbde } 76383366Sjulian error = VOP_FSYNC(vp, cred, waitfor, td); 7643152Sphk if (error) 7652893Sdfr allerror = error; 76683366Sjulian VOP_UNLOCK(vp, 0, td); 76735511Sdt vrele(vp); 76872200Sbmilekic mtx_lock(&mntvnode_mtx); 7692893Sdfr } 77072200Sbmilekic mtx_unlock(&mntvnode_mtx); 7712893Sdfr 7722893Sdfr /* 7732893Sdfr * Flush filesystem control info. 7742893Sdfr */ 77535511Sdt if (waitfor != MNT_LAZY) { 77683366Sjulian vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, td); 77783366Sjulian error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, td); 77835511Sdt if (error) 77935511Sdt allerror = error; 78083366Sjulian VOP_UNLOCK(pmp->pm_devvp, 0, td); 78135511Sdt } 78233548Sjkh return (allerror); 7832893Sdfr} 7842893Sdfr 78512144Sphkstatic int 78651138Salfredmsdosfs_fhtovp(mp, fhp, vpp) 7872893Sdfr struct mount *mp; 7882893Sdfr struct fid *fhp; 7892893Sdfr struct vnode **vpp; 7902893Sdfr{ 79133548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 7922893Sdfr struct defid *defhp = (struct defid *) fhp; 7932893Sdfr struct denode *dep; 7942893Sdfr int error; 7952893Sdfr 79633548Sjkh error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); 7972893Sdfr if (error) { 7982893Sdfr *vpp = NULLVP; 79933548Sjkh return (error); 8002893Sdfr } 8012893Sdfr *vpp = DETOV(dep); 80251138Salfred return (0); 80351138Salfred} 80451138Salfred 80551138Salfredstatic int 8062893Sdfrmsdosfs_vptofh(vp, fhp) 8072893Sdfr struct vnode *vp; 8082893Sdfr struct fid *fhp; 8092893Sdfr{ 81033548Sjkh struct denode *dep; 81133548Sjkh struct defid *defhp; 8122893Sdfr 81333548Sjkh dep = VTODE(vp); 81433548Sjkh defhp = (struct defid *)fhp; 8152893Sdfr defhp->defid_len = sizeof(struct defid); 8162893Sdfr defhp->defid_dirclust = dep->de_dirclust; 8172893Sdfr defhp->defid_dirofs = dep->de_diroffset; 81833548Sjkh /* defhp->defid_gen = dep->de_gen; */ 81933548Sjkh return (0); 8202893Sdfr} 8212893Sdfr 82212145Sphkstatic struct vfsops msdosfs_vfsops = { 8232893Sdfr msdosfs_mount, 82451068Salfred vfs_stdstart, 8252893Sdfr msdosfs_unmount, 8262893Sdfr msdosfs_root, 82751068Salfred vfs_stdquotactl, 8282893Sdfr msdosfs_statfs, 8292893Sdfr msdosfs_sync, 83051068Salfred vfs_stdvget, 8312893Sdfr msdosfs_fhtovp, 83275934Sphk vfs_stdcheckexp, 8332893Sdfr msdosfs_vptofh, 83454803Srwatson msdosfs_init, 83562227Sbp msdosfs_uninit, 83654803Srwatson vfs_stdextattrctl, 8372893Sdfr}; 8382946Swollman 83977577SruVFS_SET(msdosfs_vfsops, msdosfs, 0); 840