msdosfs_vfsops.c revision 23134
123134Sbde/* $Id: msdosfs_vfsops.c,v 1.16 1997/02/22 09:40:48 peter Exp $ */ 22893Sdfr/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */ 32893Sdfr 42893Sdfr/*- 52893Sdfr * Copyright (C) 1994 Wolfgang Solfrank. 62893Sdfr * Copyright (C) 1994 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> 532893Sdfr#include <sys/namei.h> 542893Sdfr#include <sys/proc.h> 552893Sdfr#include <sys/kernel.h> 562893Sdfr#include <sys/vnode.h> 572893Sdfr#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ 582893Sdfr#include <sys/mount.h> 592893Sdfr#include <sys/buf.h> 602893Sdfr#include <sys/file.h> 612893Sdfr#include <sys/malloc.h> 622893Sdfr 632893Sdfr#include <msdosfs/bpb.h> 642893Sdfr#include <msdosfs/bootsect.h> 652893Sdfr#include <msdosfs/direntry.h> 662893Sdfr#include <msdosfs/denode.h> 672893Sdfr#include <msdosfs/msdosfsmount.h> 682893Sdfr#include <msdosfs/fat.h> 692893Sdfr 7012338Sbdestatic int mountmsdosfs __P((struct vnode *devvp, struct mount *mp, 7112338Sbde struct proc *p)); 7212338Sbdestatic int msdosfs_fhtovp __P((struct mount *, struct fid *, 7312338Sbde struct mbuf *, struct vnode **, int *, 7412338Sbde struct ucred **)); 7512338Sbdestatic int msdosfs_mount __P((struct mount *, char *, caddr_t, 7612338Sbde struct nameidata *, struct proc *)); 7712338Sbdestatic int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t, 7812338Sbde struct proc *)); 7912338Sbdestatic int msdosfs_root __P((struct mount *, struct vnode **)); 8012338Sbdestatic int msdosfs_start __P((struct mount *, int, struct proc *)); 8112338Sbdestatic int msdosfs_statfs __P((struct mount *, struct statfs *, 8212338Sbde struct proc *)); 8312338Sbdestatic int msdosfs_sync __P((struct mount *, int, struct ucred *, 8412338Sbde struct proc *)); 8512338Sbdestatic int msdosfs_unmount __P((struct mount *, int, struct proc *)); 8612338Sbdestatic int msdosfs_vget __P((struct mount *mp, ino_t ino, 8712338Sbde struct vnode **vpp)); 8812338Sbdestatic int msdosfs_vptofh __P((struct vnode *, struct fid *)); 8912338Sbde 902893Sdfr/* 918876Srgrimes * mp - path - addr in user space of mount point (ie /usr or whatever) 922893Sdfr * data - addr in user space of mount params including the name of the block 938876Srgrimes * special file to treat as a filesystem. 942893Sdfr */ 9512144Sphkstatic int 962893Sdfrmsdosfs_mount(mp, path, data, ndp, p) 972893Sdfr struct mount *mp; 982893Sdfr char *path; 992893Sdfr caddr_t data; 1002893Sdfr struct nameidata *ndp; 1012893Sdfr struct proc *p; 1022893Sdfr{ 1032893Sdfr struct vnode *devvp; /* vnode for blk device to mount */ 1042893Sdfr struct msdosfs_args args; /* will hold data from mount request */ 1052893Sdfr struct msdosfsmount *pmp; /* msdosfs specific mount control block */ 1062893Sdfr int error, flags; 1072893Sdfr u_int size; 1082893Sdfr struct ucred *cred, *scred; 1092893Sdfr struct vattr va; 1102893Sdfr 1112893Sdfr /* 1122893Sdfr * Copy in the args for the mount request. 1132893Sdfr */ 1143152Sphk error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args)); 1153152Sphk if (error) 1162893Sdfr return error; 1172893Sdfr 1182893Sdfr /* 1192893Sdfr * If they just want to update then be sure we can do what is 1202893Sdfr * asked. Can't change a filesystem from read/write to read only. 1212893Sdfr * Why? And if they've supplied a new device file name then we 1222893Sdfr * continue, otherwise return. 1232893Sdfr */ 1242893Sdfr if (mp->mnt_flag & MNT_UPDATE) { 1252893Sdfr pmp = (struct msdosfsmount *) mp->mnt_data; 1262893Sdfr error = 0; 1272893Sdfr if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { 1282893Sdfr flags = WRITECLOSE; 1292893Sdfr if (mp->mnt_flag & MNT_FORCE) 1302893Sdfr flags |= FORCECLOSE; 1312893Sdfr error = vflush(mp, NULLVP, flags); 1322893Sdfr } 1332893Sdfr if (!error && (mp->mnt_flag & MNT_RELOAD)) 1342893Sdfr /* not yet implemented */ 1352893Sdfr error = EINVAL; 1362893Sdfr if (error) 1372893Sdfr return error; 13823134Sbde if (pmp->pm_ronly && (mp->mnt_flag & MNT_WANTRDWR)) 1392893Sdfr pmp->pm_ronly = 0; 1402893Sdfr if (args.fspec == 0) { 1412893Sdfr /* 1422893Sdfr * Process export requests. 1432893Sdfr */ 1442893Sdfr return vfs_export(mp, &pmp->pm_export, &args.export); 1452893Sdfr } 1462893Sdfr } else 1472893Sdfr pmp = NULL; 1482893Sdfr 1492893Sdfr /* 1502893Sdfr * check to see that the user in owns the target directory. 1512893Sdfr * Note the very XXX trick to make sure we're checking as the 1522893Sdfr * real user -- were mount() executable by anyone, this wouldn't 1532893Sdfr * be a problem. 1542893Sdfr * 1552893Sdfr * XXX there should be one consistent error out. 1562893Sdfr */ 1572893Sdfr cred = crdup(p->p_ucred); /* XXX */ 1582893Sdfr cred->cr_uid = p->p_cred->p_ruid; /* XXX */ 1592893Sdfr error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); 1602893Sdfr if (error) { 1612893Sdfr crfree(cred); /* XXX */ 1622893Sdfr return error; 1632893Sdfr } 1642893Sdfr if (cred->cr_uid != 0) { 1652893Sdfr if (va.va_uid != cred->cr_uid) { 1662893Sdfr error = EACCES; 1672893Sdfr crfree(cred); /* XXX */ 1682893Sdfr return error; 1692893Sdfr } 1702893Sdfr 1712893Sdfr /* a user mounted it; we'll verify permissions when unmounting */ 1722893Sdfr mp->mnt_flag |= MNT_USER; 1732893Sdfr } 1742893Sdfr 1752893Sdfr /* 1762893Sdfr * Now, lookup the name of the block device this mount or name 1772893Sdfr * update request is to apply to. 1782893Sdfr */ 1792893Sdfr NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); 1802893Sdfr scred = p->p_ucred; /* XXX */ 1812893Sdfr p->p_ucred = cred; /* XXX */ 1822893Sdfr error = namei(ndp); 1832893Sdfr p->p_ucred = scred; /* XXX */ 1842893Sdfr crfree(cred); /* XXX */ 1852893Sdfr if (error != 0) 1862893Sdfr return error; 1878876Srgrimes 1882893Sdfr /* 1892893Sdfr * Be sure they've given us a block device to treat as a 1902893Sdfr * filesystem. And, that its major number is within the bdevsw 1912893Sdfr * table. 1922893Sdfr */ 1932893Sdfr devvp = ndp->ni_vp; 1942893Sdfr if (devvp->v_type != VBLK) { 1952893Sdfr vrele(devvp); 1962893Sdfr return ENOTBLK; 1972893Sdfr } 1982893Sdfr if (major(devvp->v_rdev) >= nblkdev) { 1992893Sdfr vrele(devvp); 2002893Sdfr return ENXIO; 2012893Sdfr } 2022893Sdfr 2032893Sdfr /* 2042893Sdfr * If this is an update, then make sure the vnode for the block 2052893Sdfr * special device is the same as the one our filesystem is in. 2062893Sdfr */ 2072893Sdfr if (mp->mnt_flag & MNT_UPDATE) { 2082893Sdfr if (devvp != pmp->pm_devvp) 2092893Sdfr error = EINVAL; 2102893Sdfr else 2112893Sdfr vrele(devvp); 2122893Sdfr } else { 2132893Sdfr 2142893Sdfr /* 2152893Sdfr * Well, it's not an update, it's a real mount request. 2162893Sdfr * Time to get dirty. 2172893Sdfr */ 2182893Sdfr error = mountmsdosfs(devvp, mp, p); 2192893Sdfr } 2202893Sdfr if (error) { 2212893Sdfr vrele(devvp); 2222893Sdfr return error; 2232893Sdfr } 2242893Sdfr 2252893Sdfr /* 2262893Sdfr * Copy in the name of the directory the filesystem is to be 2272893Sdfr * mounted on. Then copy in the name of the block special file 2282893Sdfr * representing the filesystem being mounted. And we clear the 2292893Sdfr * remainder of the character strings to be tidy. Set up the 2302893Sdfr * user id/group id/mask as specified by the user. Then, we try to 2312893Sdfr * fill in the filesystem stats structure as best we can with 2322893Sdfr * whatever applies from a dos file system. 2332893Sdfr */ 2342893Sdfr pmp = (struct msdosfsmount *) mp->mnt_data; 2352893Sdfr copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname, 2362893Sdfr sizeof(mp->mnt_stat.f_mntonname) - 1, &size); 2372893Sdfr bzero(mp->mnt_stat.f_mntonname + size, 2382893Sdfr sizeof(mp->mnt_stat.f_mntonname) - size); 2392893Sdfr copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); 2402893Sdfr bzero(mp->mnt_stat.f_mntfromname + size, 2412893Sdfr MNAMELEN - size); 2422893Sdfr pmp->pm_mounter = p->p_cred->p_ruid; 2432893Sdfr pmp->pm_gid = args.gid; 2442893Sdfr pmp->pm_uid = args.uid; 2452893Sdfr pmp->pm_mask = args.mask; 2462893Sdfr (void) msdosfs_statfs(mp, &mp->mnt_stat, p); 2472893Sdfr#ifdef MSDOSFS_DEBUG 2483311Sphk printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 2492893Sdfr#endif 2502893Sdfr return 0; 2512893Sdfr} 2522893Sdfr 25312144Sphkstatic int 2542893Sdfrmountmsdosfs(devvp, mp, p) 2552893Sdfr struct vnode *devvp; 2562893Sdfr struct mount *mp; 2572893Sdfr struct proc *p; 2582893Sdfr{ 2592893Sdfr int i; 2602893Sdfr int bpc; 2612893Sdfr int bit; 2622893Sdfr int error; 2632893Sdfr int needclose; 2642893Sdfr int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 2652893Sdfr dev_t dev = devvp->v_rdev; 2662893Sdfr union bootsector *bsp; 2672893Sdfr struct msdosfsmount *pmp = NULL; 2682893Sdfr struct buf *bp0 = NULL; 2692893Sdfr struct byte_bpb33 *b33; 2702893Sdfr struct byte_bpb50 *b50; 27116363Sasami#ifdef PC98 27216363Sasami u_int pc98_wrk; 27316363Sasami u_int Phy_Sector_Size; 27416363Sasami#endif 2752893Sdfr 2762893Sdfr /* 2772893Sdfr * Multiple mounts of the same block special file aren't allowed. 2782893Sdfr * Make sure no one else has the special file open. And flush any 2792893Sdfr * old buffers from this filesystem. Presumably this prevents us 2802893Sdfr * from running into buffers that are the wrong blocksize. 2812893Sdfr */ 2823152Sphk error = vfs_mountedon(devvp); 2833152Sphk if (error) 2842893Sdfr return error; 2852893Sdfr if (vcount(devvp) > 1) 2862893Sdfr return EBUSY; 2873152Sphk error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); 2883152Sphk if (error) 2892893Sdfr return error; 2902893Sdfr 2912893Sdfr /* 2922893Sdfr * Now open the block special file. 2932893Sdfr */ 2943152Sphk error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p); 2953152Sphk if (error) 2962893Sdfr return error; 2972893Sdfr needclose = 1; 2982893Sdfr#ifdef HDSUPPORT 2992893Sdfr /* 3002893Sdfr * Put this in when we support reading dos filesystems from 3012893Sdfr * partitioned harddisks. 3022893Sdfr */ 3032893Sdfr if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) { 3042893Sdfr } 3052893Sdfr#endif 3062893Sdfr 3072893Sdfr /* 3082893Sdfr * Read the boot sector of the filesystem, and then check the boot 3092893Sdfr * signature. If not a dos boot sector then error out. We could 3102893Sdfr * also add some checking on the bsOemName field. So far I've seen 3112893Sdfr * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0" 3122893Sdfr */ 31316363Sasami#ifdef PC98 31416363Sasami devvp->v_flag &= 0xffff; 31516363Sasami error = bread(devvp, 0, 1024, NOCRED, &bp0); 31616363Sasami#else 3173152Sphk error = bread(devvp, 0, 512, NOCRED, &bp0); 31816363Sasami#endif 3193152Sphk if (error) 3202893Sdfr goto error_exit; 3212893Sdfr bp0->b_flags |= B_AGE; 3222893Sdfr bsp = (union bootsector *) bp0->b_data; 3232893Sdfr b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB; 3242893Sdfr b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB; 3252893Sdfr#ifdef MSDOSFS_CHECKSIG 32616363Sasami#ifdef PC98 32716363Sasami if (bsp->bs50.bsBootSectSig != BOOTSIG && 32816363Sasami bsp->bs50.bsBootSectSig != 0 && /* PC98 DOS 3.3x */ 32916363Sasami bsp->bs50.bsBootSectSig != 15760 && /* PC98 DOS 5.0 */ 33016363Sasami bsp->bs50.bsBootSectSig != 64070) { /* PC98 DOS 3.3B */ 33116363Sasami#else 3322893Sdfr if (bsp->bs50.bsBootSectSig != BOOTSIG) { 33316363Sasami#endif 3342893Sdfr error = EINVAL; 3352893Sdfr goto error_exit; 3362893Sdfr } 3372893Sdfr#endif 3382893Sdfr if ( bsp->bs50.bsJump[0] != 0xe9 && 3392893Sdfr (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) { 3402893Sdfr error = EINVAL; 3412893Sdfr goto error_exit; 3422893Sdfr } 3432893Sdfr 3442893Sdfr pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); 3452893Sdfr bzero((caddr_t)pmp, sizeof *pmp); 3462893Sdfr pmp->pm_mountp = mp; 3472893Sdfr 3482893Sdfr /* 3492893Sdfr * Compute several useful quantities from the bpb in the 3502893Sdfr * bootsector. Copy in the dos 5 variant of the bpb then fix up 3512893Sdfr * the fields that are different between dos 5 and dos 3.3. 3522893Sdfr */ 3532893Sdfr pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 3542893Sdfr pmp->pm_SectPerClust = b50->bpbSecPerClust; 3552893Sdfr pmp->pm_ResSectors = getushort(b50->bpbResSectors); 3562893Sdfr pmp->pm_FATs = b50->bpbFATs; 3572893Sdfr pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 3582893Sdfr pmp->pm_Sectors = getushort(b50->bpbSectors); 3592893Sdfr pmp->pm_Media = b50->bpbMedia; 3602893Sdfr pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 3612893Sdfr pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 3622893Sdfr pmp->pm_Heads = getushort(b50->bpbHeads); 3632893Sdfr 3642893Sdfr /* XXX - We should probably check more values here */ 3652893Sdfr if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || 3662893Sdfr !pmp->pm_Heads || pmp->pm_Heads > 255 || 36716363Sasami#ifdef PC98 36816363Sasami !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { 36916363Sasami#else 3702893Sdfr !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { 37116363Sasami#endif 3722893Sdfr error = EINVAL; 3732893Sdfr goto error_exit; 3742893Sdfr } 3752893Sdfr 3762893Sdfr if (pmp->pm_Sectors == 0) { 3772893Sdfr pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 3782893Sdfr pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 3792893Sdfr } else { 3802893Sdfr pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 3812893Sdfr pmp->pm_HugeSectors = pmp->pm_Sectors; 3822893Sdfr } 38316363Sasami#ifdef PC98 /* for PC98 added Satoshi Yasuda */ 38416363Sasami Phy_Sector_Size = 512; 38516363Sasami if ((devvp->v_rdev>>8) == 2) { /* floppy check */ 38616363Sasami if (((devvp->v_rdev&077) == 2) && (pmp->pm_HugeSectors == 1232)) { 38716363Sasami Phy_Sector_Size = 1024; /* 2HD */ 38816363Sasami /* 38916363Sasami * 1024byte/sector support 39016363Sasami */ 39116363Sasami devvp->v_flag |= 0x10000; 39216363Sasami } else { 39316363Sasami if ((((devvp->v_rdev&077) == 3) /* 2DD 8 or 9 sector */ 39416363Sasami && (pmp->pm_HugeSectors == 1440)) /* 9 sector */ 39516363Sasami || (((devvp->v_rdev&077) == 4) 39616363Sasami && (pmp->pm_HugeSectors == 1280)) /* 8 sector */ 39716363Sasami || (((devvp->v_rdev&077) == 5) 39816363Sasami && (pmp->pm_HugeSectors == 2880))) { /* 1.44M */ 39916363Sasami Phy_Sector_Size = 512; 40016363Sasami } else { 40116363Sasami if (((devvp->v_rdev&077) != 1) 40216363Sasami && ((devvp->v_rdev&077) != 0)) { /* 2HC */ 40316363Sasami error = EINVAL; 40416363Sasami goto error_exit; 40516363Sasami } 40616363Sasami } 40716363Sasami } 40816363Sasami } 40916363Sasami pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size; 41016363Sasami pmp->pm_BytesPerSec = Phy_Sector_Size; 41116363Sasami pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk; 41216363Sasami pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk; 41316363Sasami pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk; 41416363Sasami pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk; 41516363Sasami pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk; 41616363Sasami pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk; 41716363Sasami#endif /* */ 4182893Sdfr pmp->pm_fatblk = pmp->pm_ResSectors; 4192893Sdfr pmp->pm_rootdirblk = pmp->pm_fatblk + 4202893Sdfr (pmp->pm_FATs * pmp->pm_FATsecs); 4212893Sdfr pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)) 4222893Sdfr / 4232893Sdfr pmp->pm_BytesPerSec;/* in sectors */ 4242893Sdfr pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 4252893Sdfr pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 4262893Sdfr pmp->pm_SectPerClust; 4272893Sdfr pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; 4282893Sdfr pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; 4292893Sdfr if (FAT12(pmp)) 4302893Sdfr /* 4312893Sdfr * This will usually be a floppy disk. This size makes sure 4322893Sdfr * that one fat entry will not be split across multiple 4332893Sdfr * blocks. 4342893Sdfr */ 4352893Sdfr pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; 4362893Sdfr else 4372893Sdfr /* 4382893Sdfr * This will usually be a hard disk. Reading or writing one 4392893Sdfr * block should be quite fast. 4402893Sdfr */ 4412893Sdfr pmp->pm_fatblocksize = MAXBSIZE; 4422893Sdfr pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; 4432893Sdfr 4442893Sdfr 4452893Sdfr if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0) 44615033Sgpalmer printf("mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length\n"); 4472893Sdfr 4482893Sdfr /* 4492893Sdfr * Compute mask and shift value for isolating cluster relative byte 4502893Sdfr * offsets and cluster numbers from a file offset. 4512893Sdfr */ 4522893Sdfr bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec; 4532893Sdfr pmp->pm_bpcluster = bpc; 4542893Sdfr pmp->pm_depclust = bpc / sizeof(struct direntry); 4552893Sdfr pmp->pm_crbomask = bpc - 1; 4562893Sdfr if (bpc == 0) { 4572893Sdfr error = EINVAL; 4582893Sdfr goto error_exit; 4592893Sdfr } 4602893Sdfr bit = 1; 4612893Sdfr for (i = 0; i < 32; i++) { 4622893Sdfr if (bit & bpc) { 4632893Sdfr if (bit ^ bpc) { 4642893Sdfr error = EINVAL; 4652893Sdfr goto error_exit; 4662893Sdfr } 4672893Sdfr pmp->pm_cnshift = i; 4682893Sdfr break; 4692893Sdfr } 4702893Sdfr bit <<= 1; 4712893Sdfr } 4722893Sdfr 47316363Sasami#ifdef PC98 47416363Sasami if (Phy_Sector_Size == 512) { 47516363Sasami pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ 47616363Sasami pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ 47716363Sasami } else { 47816363Sasami pmp->pm_brbomask = 0x03ff; 47916363Sasami pmp->pm_bnshift = 10; 48016363Sasami } 48116363Sasami#else 4822893Sdfr pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ 4832893Sdfr pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ 48416363Sasami#endif 4852893Sdfr 4862893Sdfr /* 4872893Sdfr * Release the bootsector buffer. 4882893Sdfr */ 4892893Sdfr brelse(bp0); 4902893Sdfr bp0 = NULL; 4912893Sdfr 4922893Sdfr /* 4932893Sdfr * Allocate memory for the bitmap of allocated clusters, and then 4942893Sdfr * fill it in. 4952893Sdfr */ 4962893Sdfr pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) 4972893Sdfr / N_INUSEBITS) 4982893Sdfr * sizeof(*pmp->pm_inusemap), 4992893Sdfr M_MSDOSFSFAT, M_WAITOK); 5002893Sdfr 5012893Sdfr /* 5022893Sdfr * fillinusemap() needs pm_devvp. 5032893Sdfr */ 5042893Sdfr pmp->pm_dev = dev; 5052893Sdfr pmp->pm_devvp = devvp; 5062893Sdfr 5072893Sdfr /* 5082893Sdfr * Have the inuse map filled in. 5092893Sdfr */ 5102893Sdfr error = fillinusemap(pmp); 5112893Sdfr if (error) 5122893Sdfr goto error_exit; 5132893Sdfr 5142893Sdfr /* 5152893Sdfr * If they want fat updates to be synchronous then let them suffer 5162893Sdfr * the performance degradation in exchange for the on disk copy of 5172893Sdfr * the fat being correct just about all the time. I suppose this 5182893Sdfr * would be a good thing to turn on if the kernel is still flakey. 5192893Sdfr */ 5202893Sdfr pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS; 5212893Sdfr 5222893Sdfr /* 5232893Sdfr * Finish up. 5242893Sdfr */ 5252893Sdfr pmp->pm_ronly = ronly; 5262893Sdfr if (ronly == 0) 5272893Sdfr pmp->pm_fmod = 1; 5282893Sdfr mp->mnt_data = (qaddr_t) pmp; 52923134Sbde mp->mnt_stat.f_fsid.val[0] = (long)dev; 53023134Sbde mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 5312893Sdfr devvp->v_specflags |= SI_MOUNTEDON; 5322893Sdfr 5332893Sdfr return 0; 5342893Sdfr 5352893Sdfrerror_exit:; 5362893Sdfr if (bp0) 5372893Sdfr brelse(bp0); 5382893Sdfr if (needclose) 5392893Sdfr (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, 5402893Sdfr NOCRED, p); 5412893Sdfr if (pmp) { 5422893Sdfr if (pmp->pm_inusemap) 5432893Sdfr free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); 5442893Sdfr free((caddr_t) pmp, M_MSDOSFSMNT); 5452893Sdfr mp->mnt_data = (qaddr_t) 0; 5462893Sdfr } 5472893Sdfr return error; 5482893Sdfr} 5492893Sdfr 55012144Sphkstatic int 5512893Sdfrmsdosfs_start(mp, flags, p) 5522893Sdfr struct mount *mp; 5532893Sdfr int flags; 5542893Sdfr struct proc *p; 5552893Sdfr{ 5562893Sdfr return 0; 5572893Sdfr} 5582893Sdfr 5592893Sdfr/* 5602893Sdfr * Unmount the filesystem described by mp. 5612893Sdfr */ 56212144Sphkstatic int 5632893Sdfrmsdosfs_unmount(mp, mntflags, p) 5642893Sdfr struct mount *mp; 5652893Sdfr int mntflags; 5662893Sdfr struct proc *p; 5672893Sdfr{ 5682893Sdfr int flags = 0; 5692893Sdfr int error; 5702893Sdfr struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; 5712893Sdfr 5722893Sdfr /* only the mounter, or superuser can unmount */ 5732893Sdfr if ((p->p_cred->p_ruid != pmp->pm_mounter) && 5742893Sdfr (error = suser(p->p_ucred, &p->p_acflag))) 5752893Sdfr return error; 5762893Sdfr 5772893Sdfr if (mntflags & MNT_FORCE) { 5782893Sdfr flags |= FORCECLOSE; 5792893Sdfr } 5803152Sphk error = vflush(mp, NULLVP, flags); 5813152Sphk if (error) 5822893Sdfr return error; 5832893Sdfr pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; 5842893Sdfr error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, 5852893Sdfr NOCRED, p); 5862893Sdfr vrele(pmp->pm_devvp); 5872893Sdfr free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); 5882893Sdfr free((caddr_t) pmp, M_MSDOSFSMNT); 5892893Sdfr mp->mnt_data = (qaddr_t) 0; 5902893Sdfr return error; 5912893Sdfr} 5922893Sdfr 59312144Sphkstatic int 5942893Sdfrmsdosfs_root(mp, vpp) 5952893Sdfr struct mount *mp; 5962893Sdfr struct vnode **vpp; 5972893Sdfr{ 5982893Sdfr struct denode *ndep; 5992893Sdfr struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data); 6002893Sdfr int error; 6012893Sdfr 6022893Sdfr error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep); 6032893Sdfr#ifdef MSDOSFS_DEBUG 6043311Sphk printf("msdosfs_root(); mp %p, pmp %p, ndep %p, vp %p\n", 6052893Sdfr mp, pmp, ndep, DETOV(ndep)); 6062893Sdfr#endif 6072893Sdfr if (error == 0) 6082893Sdfr *vpp = DETOV(ndep); 6092893Sdfr return error; 6102893Sdfr} 6112893Sdfr 61212144Sphkstatic int 6132893Sdfrmsdosfs_quotactl(mp, cmds, uid, arg, p) 6142893Sdfr struct mount *mp; 6152893Sdfr int cmds; 6162893Sdfr uid_t uid; 6172893Sdfr caddr_t arg; 6182893Sdfr struct proc *p; 6192893Sdfr{ 6203152Sphk return EOPNOTSUPP; 6212893Sdfr} 6222893Sdfr 62312144Sphkstatic int 6242893Sdfrmsdosfs_statfs(mp, sbp, p) 6252893Sdfr struct mount *mp; 6262893Sdfr struct statfs *sbp; 6272893Sdfr struct proc *p; 6282893Sdfr{ 6292893Sdfr struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; 6302893Sdfr 6312893Sdfr /* 6322893Sdfr * Fill in the stat block. 6332893Sdfr */ 6342893Sdfr sbp->f_bsize = pmp->pm_bpcluster; 6352893Sdfr sbp->f_iosize = pmp->pm_bpcluster; 6362893Sdfr sbp->f_blocks = pmp->pm_nmbrofclusters; 6372893Sdfr sbp->f_bfree = pmp->pm_freeclustercount; 6382893Sdfr sbp->f_bavail = pmp->pm_freeclustercount; 6392893Sdfr sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 6402893Sdfr sbp->f_ffree = 0; /* what to put in here? */ 6412893Sdfr 6422893Sdfr /* 6432893Sdfr * Copy the mounted on and mounted from names into the passed in 6442893Sdfr * stat block, if it is not the one in the mount structure. 6452893Sdfr */ 6462893Sdfr if (sbp != &mp->mnt_stat) { 64723134Sbde sbp->f_type = mp->mnt_vfc->vfc_typenum; 6482893Sdfr bcopy((caddr_t) mp->mnt_stat.f_mntonname, 6492893Sdfr (caddr_t) & sbp->f_mntonname[0], MNAMELEN); 6502893Sdfr bcopy((caddr_t) mp->mnt_stat.f_mntfromname, 6512893Sdfr (caddr_t) & sbp->f_mntfromname[0], MNAMELEN); 6522893Sdfr } 6532893Sdfr#if 0 6542893Sdfr strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); 6552893Sdfr sbp->f_fstypename[MFSNAMELEN] = '\0'; 6562893Sdfr#endif 6572893Sdfr return 0; 6582893Sdfr} 6592893Sdfr 66012144Sphkstatic int 6612893Sdfrmsdosfs_sync(mp, waitfor, cred, p) 6622893Sdfr struct mount *mp; 6632893Sdfr int waitfor; 6642893Sdfr struct ucred *cred; 6652893Sdfr struct proc *p; 6662893Sdfr{ 6672893Sdfr struct vnode *vp; 6682893Sdfr struct denode *dep; 6692893Sdfr struct msdosfsmount *pmp; 6702893Sdfr int error; 6712893Sdfr int allerror = 0; 6722893Sdfr 6732893Sdfr pmp = (struct msdosfsmount *) mp->mnt_data; 6742893Sdfr 6752893Sdfr /* 6762893Sdfr * If we ever switch to not updating all of the fats all the time, 6772893Sdfr * this would be the place to update them from the first one. 6782893Sdfr */ 6792893Sdfr if (pmp->pm_fmod) 6802893Sdfr if (pmp->pm_ronly) 6812893Sdfr panic("msdosfs_sync: rofs mod"); 6822893Sdfr else { 6832893Sdfr /* update fats here */ 6842893Sdfr } 6852893Sdfr 6862893Sdfr /* 6872893Sdfr * Go thru in memory denodes and write them out along with 6882893Sdfr * unwritten file blocks. 6892893Sdfr */ 69023134Sbde simple_lock(&mntvnode_slock); 6912893Sdfrloop: 6922893Sdfr for (vp = mp->mnt_vnodelist.lh_first; vp; 6932893Sdfr vp = vp->v_mntvnodes.le_next) { 6942893Sdfr if (vp->v_mount != mp) /* not ours anymore */ 6952893Sdfr goto loop; 69623134Sbde simple_lock(&vp->v_interlock); 6972893Sdfr dep = VTODE(vp); 6985083Sbde if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 && 69923134Sbde vp->v_dirtyblkhd.lh_first == NULL) { 70023134Sbde simple_unlock(&vp->v_interlock); 7012893Sdfr continue; 70223134Sbde } 70323134Sbde simple_unlock(&mntvnode_slock); 70423134Sbde error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); 70523134Sbde if (error) { 70623134Sbde simple_lock(&mntvnode_slock); 70723134Sbde if (error == ENOENT) 70823134Sbde goto loop; 70923134Sbde continue; 71023134Sbde } 7113152Sphk error = VOP_FSYNC(vp, cred, waitfor, p); 7123152Sphk if (error) 7132893Sdfr allerror = error; 71423134Sbde VOP_UNLOCK(vp, 0, p); 71523134Sbde vrele(vp); /* done with this one */ 71623134Sbde simple_lock(&mntvnode_slock); 7172893Sdfr } 71823134Sbde simple_unlock(&mntvnode_slock); 7192893Sdfr 7202893Sdfr /* 7212893Sdfr * Flush filesystem control info. 7222893Sdfr */ 7233152Sphk error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p); 7243152Sphk if (error) 7252893Sdfr allerror = error; 7262893Sdfr return allerror; 7272893Sdfr} 7282893Sdfr 72912144Sphkstatic int 7302893Sdfrmsdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) 7312893Sdfr struct mount *mp; 7322893Sdfr struct fid *fhp; 7332893Sdfr struct mbuf *nam; 7342893Sdfr struct vnode **vpp; 7352893Sdfr int *exflagsp; 7362893Sdfr struct ucred **credanonp; 7372893Sdfr{ 7382893Sdfr struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; 7392893Sdfr struct defid *defhp = (struct defid *) fhp; 7402893Sdfr struct denode *dep; 7412893Sdfr struct netcred *np; 7422893Sdfr int error; 7432893Sdfr 7442893Sdfr np = vfs_export_lookup(mp, &pmp->pm_export, nam); 7452893Sdfr if (np == NULL) 7462893Sdfr return EACCES; 7472893Sdfr error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, 7482893Sdfr NULL, &dep); 7492893Sdfr if (error) { 7502893Sdfr *vpp = NULLVP; 7512893Sdfr return error; 7522893Sdfr } 7532893Sdfr *vpp = DETOV(dep); 7542893Sdfr *exflagsp = np->netc_exflags; 7552893Sdfr *credanonp = &np->netc_anon; 7562893Sdfr return 0; 7572893Sdfr} 7582893Sdfr 7592893Sdfr 76012144Sphkstatic int 7612893Sdfrmsdosfs_vptofh(vp, fhp) 7622893Sdfr struct vnode *vp; 7632893Sdfr struct fid *fhp; 7642893Sdfr{ 7652893Sdfr struct denode *dep = VTODE(vp); 7662893Sdfr struct defid *defhp = (struct defid *) fhp; 7672893Sdfr 7682893Sdfr defhp->defid_len = sizeof(struct defid); 7692893Sdfr defhp->defid_dirclust = dep->de_dirclust; 7702893Sdfr defhp->defid_dirofs = dep->de_diroffset; 7712893Sdfr /* defhp->defid_gen = ip->i_gen; */ 7722893Sdfr return 0; 7732893Sdfr} 7742893Sdfr 77512144Sphkstatic int 7762893Sdfrmsdosfs_vget(mp, ino, vpp) 7772893Sdfr struct mount *mp; 7782893Sdfr ino_t ino; 7792893Sdfr struct vnode **vpp; 7802893Sdfr{ 7812893Sdfr return EOPNOTSUPP; 7822893Sdfr} 7832893Sdfr 78412145Sphkstatic struct vfsops msdosfs_vfsops = { 7852893Sdfr msdosfs_mount, 7862893Sdfr msdosfs_start, 7872893Sdfr msdosfs_unmount, 7882893Sdfr msdosfs_root, 7892893Sdfr msdosfs_quotactl, 7902893Sdfr msdosfs_statfs, 7912893Sdfr msdosfs_sync, 7922893Sdfr msdosfs_vget, 7932893Sdfr msdosfs_fhtovp, 7942893Sdfr msdosfs_vptofh, 7952893Sdfr msdosfs_init 7962893Sdfr}; 7972946Swollman 7982946SwollmanVFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0); 799