150477Speter/* $FreeBSD$ */ 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 */ 35139776Simp/*- 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> 53171754Sbde#include <sys/buf.h> 5440651Sbde#include <sys/conf.h> 55177785Skib#include <sys/fcntl.h> 56171754Sbde#include <sys/iconv.h> 57171754Sbde#include <sys/kernel.h> 58171748Sbde#include <sys/lock.h> 59171754Sbde#include <sys/malloc.h> 60171754Sbde#include <sys/mount.h> 61171748Sbde#include <sys/mutex.h> 622893Sdfr#include <sys/namei.h> 63164033Srwatson#include <sys/priv.h> 642893Sdfr#include <sys/proc.h> 65171754Sbde#include <sys/stat.h> 662893Sdfr#include <sys/vnode.h> 672893Sdfr 68171754Sbde#include <geom/geom.h> 69171754Sbde#include <geom/geom_vfs.h> 70171754Sbde 71171754Sbde#include <fs/msdosfs/bootsect.h> 7277162Sru#include <fs/msdosfs/bpb.h> 7377162Sru#include <fs/msdosfs/direntry.h> 7477162Sru#include <fs/msdosfs/denode.h> 7577162Sru#include <fs/msdosfs/fat.h> 76171754Sbde#include <fs/msdosfs/msdosfsmount.h> 772893Sdfr 78204470Skibstatic const char msdosfs_lock_msg[] = "fatlk"; 79204470Skib 80172757Sbde/* Mount options that we support. */ 81138471Sphkstatic const char *msdosfs_opts[] = { 82172798Sbde "async", "noatime", "noclusterr", "noclusterw", 83172757Sbde "export", "force", "from", "sync", 84172757Sbde "cs_dos", "cs_local", "cs_win", "dirmask", 85172757Sbde "gid", "kiconv", "large", "longname", 86172757Sbde "longnames", "mask", "shortname", "shortnames", 87172757Sbde "uid", "win95", "nowin95", 88138471Sphk NULL 89138471Sphk}; 90138471Sphk 9156674Snyan#if 1 /*def PC98*/ 9256674Snyan/* 9356674Snyan * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 9456674Snyan * garbage or a random value :-{ 9556674Snyan * If you want to use that broken-signatured media, define the 9656674Snyan * following symbol even though PC/AT. 9756674Snyan * (ex. mount PC-98 DOS formatted FD on PC/AT) 9856674Snyan */ 9956674Snyan#define MSDOSFS_NOCHECKSIG 10056674Snyan#endif 10156674Snyan 102151897SrwatsonMALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure"); 103151897Srwatsonstatic MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table"); 10430309Sphk 105171757Sbdestruct iconv_functions *msdosfs_iconv; 106120492Sfjoe 107138471Sphkstatic int update_mp(struct mount *mp, struct thread *td); 108183754Sattiliostatic int mountmsdosfs(struct vnode *devvp, struct mount *mp); 109170188Strhodesstatic vfs_fhtovp_t msdosfs_fhtovp; 110138471Sphkstatic vfs_mount_t msdosfs_mount; 111101777Sphkstatic vfs_root_t msdosfs_root; 112101777Sphkstatic vfs_statfs_t msdosfs_statfs; 113101777Sphkstatic vfs_sync_t msdosfs_sync; 114101777Sphkstatic vfs_unmount_t msdosfs_unmount; 11512338Sbde 116134345Stjr/* Maximum length of a character set name (arbitrary). */ 117134345Stjr#define MAXCSLEN 64 118134345Stjr 11933548Sjkhstatic int 120166558Srodrigcupdate_mp(struct mount *mp, struct thread *td) 12133548Sjkh{ 12233548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 123138471Sphk void *dos, *win, *local; 124138471Sphk int error, v; 12533548Sjkh 126138471Sphk if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) { 127138471Sphk if (msdosfs_iconv != NULL) { 128138471Sphk error = vfs_getopt(mp->mnt_optnew, 129138471Sphk "cs_win", &win, NULL); 130138471Sphk if (!error) 131138471Sphk error = vfs_getopt(mp->mnt_optnew, 132138471Sphk "cs_local", &local, NULL); 133138471Sphk if (!error) 134138471Sphk error = vfs_getopt(mp->mnt_optnew, 135138471Sphk "cs_dos", &dos, NULL); 136138471Sphk if (!error) { 137138471Sphk msdosfs_iconv->open(win, local, &pmp->pm_u2w); 138138471Sphk msdosfs_iconv->open(local, win, &pmp->pm_w2u); 139138471Sphk msdosfs_iconv->open(dos, local, &pmp->pm_u2d); 140138471Sphk msdosfs_iconv->open(local, dos, &pmp->pm_d2u); 141138471Sphk } 142134345Stjr if (error != 0) 143134345Stjr return (error); 144134345Stjr } else { 145134345Stjr pmp->pm_w2u = NULL; 146134345Stjr pmp->pm_u2w = NULL; 147134345Stjr pmp->pm_d2u = NULL; 148134345Stjr pmp->pm_u2d = NULL; 149134345Stjr } 150134345Stjr } 151134345Stjr 152232483Skevlo if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1) 153138471Sphk pmp->pm_gid = v; 154232483Skevlo if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1) 155138471Sphk pmp->pm_uid = v; 156232483Skevlo if (vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v) == 1) 157138471Sphk pmp->pm_mask = v & ALLPERMS; 158232483Skevlo if (vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v) == 1) 159138471Sphk pmp->pm_dirmask = v & ALLPERMS; 160138471Sphk vfs_flagopt(mp->mnt_optnew, "shortname", 161138471Sphk &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 162152595Srodrigc vfs_flagopt(mp->mnt_optnew, "shortnames", 163152595Srodrigc &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 164138471Sphk vfs_flagopt(mp->mnt_optnew, "longname", 165138471Sphk &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 166152595Srodrigc vfs_flagopt(mp->mnt_optnew, "longnames", 167152595Srodrigc &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 168138471Sphk vfs_flagopt(mp->mnt_optnew, "kiconv", 169138471Sphk &pmp->pm_flags, MSDOSFSMNT_KICONV); 17033548Sjkh 171152610Srodrigc if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0) 172152610Srodrigc pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 173152610Srodrigc else 174138471Sphk pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95; 175138471Sphk 17633548Sjkh if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 17733548Sjkh pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 17833548Sjkh else if (!(pmp->pm_flags & 17933548Sjkh (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 18033548Sjkh struct vnode *rootvp; 18133548Sjkh 18233548Sjkh /* 18333548Sjkh * Try to divine whether to support Win'95 long filenames 18433548Sjkh */ 18533548Sjkh if (FAT32(pmp)) 18633548Sjkh pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 18733548Sjkh else { 188144058Sjeff if ((error = 189191990Sattilio msdosfs_root(mp, LK_EXCLUSIVE, &rootvp)) != 0) 19033548Sjkh return error; 191171757Sbde pmp->pm_flags |= findwin95(VTODE(rootvp)) ? 192171757Sbde MSDOSFSMNT_LONGNAME : MSDOSFSMNT_SHORTNAME; 19333548Sjkh vput(rootvp); 19433548Sjkh } 19533548Sjkh } 19633548Sjkh return 0; 19733548Sjkh} 19833548Sjkh 199138471Sphkstatic int 200230249Smckusickmsdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 201138471Sphk{ 202138471Sphk struct msdosfs_args args; 203213664Skib struct export_args exp; 204138471Sphk int error; 205138471Sphk 206138471Sphk if (data == NULL) 207138471Sphk return (EINVAL); 208138471Sphk error = copyin(data, &args, sizeof args); 209138471Sphk if (error) 210138471Sphk return (error); 211213664Skib vfs_oexport_conv(&args.export, &exp); 212138471Sphk 213138471Sphk ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); 214213664Skib ma = mount_arg(ma, "export", &exp, sizeof(exp)); 215138471Sphk ma = mount_argf(ma, "uid", "%d", args.uid); 216138471Sphk ma = mount_argf(ma, "gid", "%d", args.gid); 217138471Sphk ma = mount_argf(ma, "mask", "%d", args.mask); 218138471Sphk ma = mount_argf(ma, "dirmask", "%d", args.dirmask); 219138471Sphk 220171757Sbde ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname"); 221171757Sbde ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname"); 222171757Sbde ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95"); 223171757Sbde ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv"); 224138471Sphk 225171757Sbde ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN); 226171757Sbde ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN); 227171757Sbde ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN); 228138471Sphk 229138471Sphk error = kernel_mount(ma, flags); 230138471Sphk 231138471Sphk return (error); 232138471Sphk} 233138471Sphk 2342893Sdfr/* 2358876Srgrimes * mp - path - addr in user space of mount point (ie /usr or whatever) 2362893Sdfr * data - addr in user space of mount params including the name of the block 2378876Srgrimes * special file to treat as a filesystem. 2382893Sdfr */ 23912144Sphkstatic int 240191990Sattiliomsdosfs_mount(struct mount *mp) 2412893Sdfr{ 2422893Sdfr struct vnode *devvp; /* vnode for blk device to mount */ 243191990Sattilio struct thread *td; 24433548Sjkh /* msdosfs specific mount control block */ 24533548Sjkh struct msdosfsmount *pmp = NULL; 246132902Sphk struct nameidata ndp; 247138689Sphk int error, flags; 248184413Strasz accmode_t accmode; 249138471Sphk char *from; 2502893Sdfr 251191990Sattilio td = curthread; 252138471Sphk if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) 253138471Sphk return (EINVAL); 254138471Sphk 2552893Sdfr /* 25633548Sjkh * If updating, check whether changing from read-only to 25733548Sjkh * read/write; if there is no device name, that's all we do. 2582893Sdfr */ 2592893Sdfr if (mp->mnt_flag & MNT_UPDATE) { 26033548Sjkh pmp = VFSTOMSDOSFS(mp); 261158924Srodrigc if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { 262165022Srodrigc /* 263165022Srodrigc * Forbid export requests if filesystem has 264165022Srodrigc * MSDOSFS_LARGEFS flag set. 265165022Srodrigc */ 266165022Srodrigc if ((pmp->pm_flags & MSDOSFS_LARGEFS) != 0) { 267165022Srodrigc vfs_mount_error(mp, 268165022Srodrigc "MSDOSFS_LARGEFS flag set, cannot export"); 269138689Sphk return (EOPNOTSUPP); 270165022Srodrigc } 271138689Sphk } 272137036Sphk if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 273138471Sphk vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 274191990Sattilio error = VFS_SYNC(mp, MNT_WAIT); 275133287Sphk if (error) 276133287Sphk return (error); 2772893Sdfr flags = WRITECLOSE; 2782893Sdfr if (mp->mnt_flag & MNT_FORCE) 2792893Sdfr flags |= FORCECLOSE; 280132023Salfred error = vflush(mp, 0, flags, td); 281138471Sphk if (error) 282138471Sphk return (error); 283172883Sdelphij 284172883Sdelphij /* 285172883Sdelphij * Now the volume is clean. Mark it so while the 286172883Sdelphij * device is still rw. 287172883Sdelphij */ 288172883Sdelphij error = markvoldirty(pmp, 0); 289172883Sdelphij if (error) { 290172883Sdelphij (void)markvoldirty(pmp, 1); 291172883Sdelphij return (error); 292172883Sdelphij } 293172883Sdelphij 294172883Sdelphij /* Downgrade the device from rw to ro. */ 295137036Sphk DROP_GIANT(); 296137036Sphk g_topology_lock(); 297171551Sbde error = g_access(pmp->pm_cp, 0, -1, 0); 298137036Sphk g_topology_unlock(); 299137036Sphk PICKUP_GIANT(); 300172883Sdelphij if (error) { 301172883Sdelphij (void)markvoldirty(pmp, 1); 302171551Sbde return (error); 303172883Sdelphij } 304171551Sbde 305172883Sdelphij /* 306172883Sdelphij * Backing out after an error was painful in the 307172883Sdelphij * above. Now we are committed to succeeding. 308172883Sdelphij */ 309172883Sdelphij pmp->pm_fmod = 0; 310172883Sdelphij pmp->pm_flags |= MSDOSFSMNT_RONLY; 311172883Sdelphij MNT_ILOCK(mp); 312172883Sdelphij mp->mnt_flag |= MNT_RDONLY; 313172883Sdelphij MNT_IUNLOCK(mp); 314138471Sphk } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 315138471Sphk !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 31633548Sjkh /* 31733548Sjkh * If upgrade to read-write by non-root, then verify 31833548Sjkh * that user has necessary permissions on the device. 31933548Sjkh */ 320164033Srwatson devvp = pmp->pm_devvp; 321175202Sattilio vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 322164033Srwatson error = VOP_ACCESS(devvp, VREAD | VWRITE, 323171757Sbde td->td_ucred, td); 324164033Srwatson if (error) 325164033Srwatson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 326164033Srwatson if (error) { 327175294Sattilio VOP_UNLOCK(devvp, 0); 328164033Srwatson return (error); 32933548Sjkh } 330175294Sattilio VOP_UNLOCK(devvp, 0); 331137036Sphk DROP_GIANT(); 332137036Sphk g_topology_lock(); 333137036Sphk error = g_access(pmp->pm_cp, 0, 1, 0); 334137036Sphk g_topology_unlock(); 335137036Sphk PICKUP_GIANT(); 336137036Sphk if (error) 337137036Sphk return (error); 338123963Sbde 339172883Sdelphij pmp->pm_fmod = 1; 340172883Sdelphij pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 341172883Sdelphij MNT_ILOCK(mp); 342172883Sdelphij mp->mnt_flag &= ~MNT_RDONLY; 343172883Sdelphij MNT_IUNLOCK(mp); 344165836Srodrigc 345123963Sbde /* Now that the volume is modifiable, mark it dirty. */ 346123873Strhodes error = markvoldirty(pmp, 1); 347123873Strhodes if (error) 348172883Sdelphij return (error); 34933548Sjkh } 3502893Sdfr } 3512893Sdfr /* 35233548Sjkh * Not an update, or updating the name: look up the name 353125796Sbde * and verify that it refers to a sensible disk device. 3542893Sdfr */ 355138471Sphk if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) 356138471Sphk return (EINVAL); 357149720Sssouhlal NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); 358132902Sphk error = namei(&ndp); 35933548Sjkh if (error) 36033548Sjkh return (error); 361132902Sphk devvp = ndp.ni_vp; 362132902Sphk NDFREE(&ndp, NDF_ONLY_PNBUF); 3638876Srgrimes 36455756Sphk if (!vn_isdisk(devvp, &error)) { 365149720Sssouhlal vput(devvp); 36655756Sphk return (error); 3672893Sdfr } 3682893Sdfr /* 36933548Sjkh * If mount by non-root, then verify that user has necessary 37033548Sjkh * permissions on the device. 3712893Sdfr */ 372184413Strasz accmode = VREAD; 373164033Srwatson if ((mp->mnt_flag & MNT_RDONLY) == 0) 374184413Strasz accmode |= VWRITE; 375184413Strasz error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 376164033Srwatson if (error) 377164033Srwatson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 378164033Srwatson if (error) { 379164033Srwatson vput(devvp); 380164033Srwatson return (error); 38133548Sjkh } 38233548Sjkh if ((mp->mnt_flag & MNT_UPDATE) == 0) { 383183754Sattilio error = mountmsdosfs(devvp, mp); 38433548Sjkh#ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 38533548Sjkh pmp = VFSTOMSDOSFS(mp); 38633548Sjkh#endif 38733548Sjkh } else { 388204589Skib vput(devvp); 3892893Sdfr if (devvp != pmp->pm_devvp) 390204589Skib return (EINVAL); /* XXX needs translation */ 3912893Sdfr } 3922893Sdfr if (error) { 3932893Sdfr vrele(devvp); 39433548Sjkh return (error); 39533548Sjkh } 39633548Sjkh 397138471Sphk error = update_mp(mp, td); 39833548Sjkh if (error) { 399134345Stjr if ((mp->mnt_flag & MNT_UPDATE) == 0) 400191990Sattilio msdosfs_unmount(mp, MNT_FORCE); 4012893Sdfr return error; 4022893Sdfr } 403171757Sbde 404234025Smckusick if (devvp->v_type == VCHR && devvp->v_rdev != NULL) 405234025Smckusick devvp->v_rdev->si_mountpt = mp; 406138471Sphk vfs_mountedfrom(mp, from); 4072893Sdfr#ifdef MSDOSFS_DEBUG 408138471Sphk printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 4092893Sdfr#endif 41033548Sjkh return (0); 4112893Sdfr} 4122893Sdfr 41312144Sphkstatic int 414183754Sattiliomountmsdosfs(struct vnode *devvp, struct mount *mp) 4152893Sdfr{ 41633548Sjkh struct msdosfsmount *pmp; 41733548Sjkh struct buf *bp; 418189120Sjhb struct cdev *dev; 4192893Sdfr union bootsector *bsp; 4202893Sdfr struct byte_bpb33 *b33; 4212893Sdfr struct byte_bpb50 *b50; 42233548Sjkh struct byte_bpb710 *b710; 42333548Sjkh u_int8_t SecPerClust; 42455188Sbp u_long clusters; 425171757Sbde int ronly, error; 426137036Sphk struct g_consumer *cp; 427137036Sphk struct bufobj *bo; 4282893Sdfr 429189120Sjhb bp = NULL; /* This and pmp both used in error_exit. */ 430189120Sjhb pmp = NULL; 431171551Sbde ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 432189120Sjhb 433189120Sjhb dev = devvp->v_rdev; 434189120Sjhb dev_ref(dev); 435137036Sphk DROP_GIANT(); 436137036Sphk g_topology_lock(); 437171551Sbde error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1); 438137036Sphk g_topology_unlock(); 439137036Sphk PICKUP_GIANT(); 440175294Sattilio VOP_UNLOCK(devvp, 0); 4413152Sphk if (error) 442189120Sjhb goto error_exit; 44333548Sjkh 444137036Sphk bo = &devvp->v_bufobj; 44533548Sjkh 4462893Sdfr /* 44733548Sjkh * Read the boot sector of the filesystem, and then check the 44833548Sjkh * boot signature. If not a dos boot sector then error out. 44956674Snyan * 450171408Sbde * NOTE: 8192 is a magic size that works for ffs. 4512893Sdfr */ 452171408Sbde error = bread(devvp, 0, 8192, NOCRED, &bp); 4533152Sphk if (error) 4542893Sdfr goto error_exit; 45533548Sjkh bp->b_flags |= B_AGE; 45633548Sjkh bsp = (union bootsector *)bp->b_data; 45733548Sjkh b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 45833548Sjkh b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 459105655Sjhb b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 46033548Sjkh 46156674Snyan#ifndef MSDOSFS_NOCHECKSIG 46287068Sjhb if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 46387068Sjhb || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 46487068Sjhb error = EINVAL; 46587068Sjhb goto error_exit; 46687068Sjhb } 46756674Snyan#endif 4682893Sdfr 469111119Simp pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 4702893Sdfr pmp->pm_mountp = mp; 471137036Sphk pmp->pm_cp = cp; 472137036Sphk pmp->pm_bo = bo; 4732893Sdfr 474204470Skib lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0); 475204470Skib 4762893Sdfr /* 477171551Sbde * Initialize ownerships and permissions, since nothing else will 478173728Smaxim * initialize them iff we are mounting root. 479171551Sbde */ 480171551Sbde pmp->pm_uid = UID_ROOT; 481171551Sbde pmp->pm_gid = GID_WHEEL; 482171551Sbde pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | 483171551Sbde S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 484171551Sbde 485171551Sbde /* 486166340Srodrigc * Experimental support for large MS-DOS filesystems. 487166340Srodrigc * WARNING: This uses at least 32 bytes of kernel memory (which is not 488166340Srodrigc * reclaimed until the FS is unmounted) for each file on disk to map 489166340Srodrigc * between the 32-bit inode numbers used by VFS and the 64-bit 490166340Srodrigc * pseudo-inode numbers used internally by msdosfs. This is only 491166340Srodrigc * safe to use in certain controlled situations (e.g. read-only FS 492166340Srodrigc * with less than 1 million files). 493166340Srodrigc * Since the mappings do not persist across unmounts (or reboots), these 494166340Srodrigc * filesystems are not suitable for exporting through NFS, or any other 495166340Srodrigc * application that requires fixed inode numbers. 496166340Srodrigc */ 497171757Sbde vfs_flagopt(mp->mnt_optnew, "large", &pmp->pm_flags, MSDOSFS_LARGEFS); 498166340Srodrigc 499166340Srodrigc /* 5002893Sdfr * Compute several useful quantities from the bpb in the 5012893Sdfr * bootsector. Copy in the dos 5 variant of the bpb then fix up 5022893Sdfr * the fields that are different between dos 5 and dos 3.3. 5032893Sdfr */ 50433548Sjkh SecPerClust = b50->bpbSecPerClust; 5052893Sdfr pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 506113979Sjhb if (pmp->pm_BytesPerSec < DEV_BSIZE) { 507113979Sjhb error = EINVAL; 508113979Sjhb goto error_exit; 509113979Sjhb } 5102893Sdfr pmp->pm_ResSectors = getushort(b50->bpbResSectors); 5112893Sdfr pmp->pm_FATs = b50->bpbFATs; 5122893Sdfr pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 5132893Sdfr pmp->pm_Sectors = getushort(b50->bpbSectors); 5142893Sdfr pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 5152893Sdfr pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 5162893Sdfr pmp->pm_Heads = getushort(b50->bpbHeads); 51733548Sjkh pmp->pm_Media = b50->bpbMedia; 5182893Sdfr 51956674Snyan /* calculate the ratio of sector size to DEV_BSIZE */ 52056674Snyan pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 52156674Snyan 522176431Smarcel /* 523176431Smarcel * We don't check pm_Heads nor pm_SecPerTrack, because 524176431Smarcel * these may not be set for EFI file systems. We don't 525176431Smarcel * use these anyway, so we're unaffected if they are 526176431Smarcel * invalid. 527176431Smarcel */ 528176431Smarcel if (!pmp->pm_BytesPerSec || !SecPerClust) { 52987068Sjhb error = EINVAL; 53087068Sjhb goto error_exit; 53187068Sjhb } 5322893Sdfr 5332893Sdfr if (pmp->pm_Sectors == 0) { 5342893Sdfr pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 5352893Sdfr pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 5362893Sdfr } else { 5372893Sdfr pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 5382893Sdfr pmp->pm_HugeSectors = pmp->pm_Sectors; 5392893Sdfr } 540166340Srodrigc if (!(pmp->pm_flags & MSDOSFS_LARGEFS)) { 541171757Sbde if (pmp->pm_HugeSectors > 0xffffffff / 542166340Srodrigc (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { 543166340Srodrigc /* 544166340Srodrigc * We cannot deal currently with this size of disk 545166340Srodrigc * due to fileid limitations (see msdosfs_getattr and 546166340Srodrigc * msdosfs_readdir) 547166340Srodrigc */ 548166340Srodrigc error = EINVAL; 549166340Srodrigc vfs_mount_error(mp, 550166340Srodrigc "Disk too big, try '-o large' mount option"); 551166340Srodrigc goto error_exit; 552166340Srodrigc } 55333548Sjkh } 55433548Sjkh 55533548Sjkh if (pmp->pm_RootDirEnts == 0) { 556246216Skib if (pmp->pm_FATsecs 55733548Sjkh || getushort(b710->bpbFSVers)) { 55833548Sjkh error = EINVAL; 559227817Skib#ifdef MSDOSFS_DEBUG 56035046Sache printf("mountmsdosfs(): bad FAT32 filesystem\n"); 561227817Skib#endif 56233548Sjkh goto error_exit; 56333548Sjkh } 56433548Sjkh pmp->pm_fatmask = FAT32_MASK; 56533548Sjkh pmp->pm_fatmult = 4; 56633548Sjkh pmp->pm_fatdiv = 1; 56733548Sjkh pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 56833548Sjkh if (getushort(b710->bpbExtFlags) & FATMIRROR) 56933548Sjkh pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 57033548Sjkh else 57133548Sjkh pmp->pm_flags |= MSDOSFS_FATMIRROR; 57233548Sjkh } else 57333548Sjkh pmp->pm_flags |= MSDOSFS_FATMIRROR; 57433548Sjkh 57556674Snyan /* 57656674Snyan * Check a few values (could do some more): 57756674Snyan * - logical sector size: power of 2, >= block size 57856674Snyan * - sectors per cluster: power of 2, >= 1 57956674Snyan * - number of sectors: >= 1, <= size of partition 580113979Sjhb * - number of FAT sectors: >= 1 58156674Snyan */ 58256674Snyan if ( (SecPerClust == 0) 58356674Snyan || (SecPerClust & (SecPerClust - 1)) 58456674Snyan || (pmp->pm_BytesPerSec < DEV_BSIZE) 58556674Snyan || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 58656674Snyan || (pmp->pm_HugeSectors == 0) 587113979Sjhb || (pmp->pm_FATsecs == 0) 588206098Savg || (SecPerClust * pmp->pm_BlkPerSec > MAXBSIZE / DEV_BSIZE) 58956674Snyan ) { 59056674Snyan error = EINVAL; 59156674Snyan goto error_exit; 59256674Snyan } 59333548Sjkh 59456674Snyan pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 595171757Sbde pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 59656674Snyan pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 59756674Snyan SecPerClust *= pmp->pm_BlkPerSec; 59856674Snyan 59956674Snyan pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 60056674Snyan 60133548Sjkh if (FAT32(pmp)) { 60233548Sjkh pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 60333548Sjkh pmp->pm_firstcluster = pmp->pm_fatblk 60433548Sjkh + (pmp->pm_FATs * pmp->pm_FATsecs); 60556674Snyan pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 60633548Sjkh } else { 60733548Sjkh pmp->pm_rootdirblk = pmp->pm_fatblk + 60833548Sjkh (pmp->pm_FATs * pmp->pm_FATsecs); 60933548Sjkh pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 61056674Snyan + DEV_BSIZE - 1) 61156674Snyan / DEV_BSIZE; /* in blocks */ 61233548Sjkh pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 61333548Sjkh } 61433548Sjkh 61555188Sbp pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 61655188Sbp SecPerClust + 1; 617171757Sbde pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 61833548Sjkh 61933548Sjkh if (pmp->pm_fatmask == 0) { 62033548Sjkh if (pmp->pm_maxcluster 62133548Sjkh <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 62233548Sjkh /* 62333548Sjkh * This will usually be a floppy disk. This size makes 62433548Sjkh * sure that one fat entry will not be split across 62533548Sjkh * multiple blocks. 62633548Sjkh */ 62733548Sjkh pmp->pm_fatmask = FAT12_MASK; 62833548Sjkh pmp->pm_fatmult = 3; 62933548Sjkh pmp->pm_fatdiv = 2; 63033548Sjkh } else { 63133548Sjkh pmp->pm_fatmask = FAT16_MASK; 63233548Sjkh pmp->pm_fatmult = 2; 63333548Sjkh pmp->pm_fatdiv = 1; 63433548Sjkh } 63533548Sjkh } 63655188Sbp 63755188Sbp clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 63855188Sbp if (pmp->pm_maxcluster >= clusters) { 639227817Skib#ifdef MSDOSFS_DEBUG 64055188Sbp printf("Warning: number of clusters (%ld) exceeds FAT " 64155188Sbp "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 642227817Skib#endif 64355188Sbp pmp->pm_maxcluster = clusters - 1; 64455188Sbp } 64555188Sbp 6462893Sdfr if (FAT12(pmp)) 647171408Sbde pmp->pm_fatblocksize = 3 * 512; 6482893Sdfr else 649171408Sbde pmp->pm_fatblocksize = PAGE_SIZE; 650171408Sbde pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize, 651171408Sbde pmp->pm_BytesPerSec); 65256674Snyan pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 65356674Snyan pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 6542893Sdfr 6552893Sdfr /* 6562893Sdfr * Compute mask and shift value for isolating cluster relative byte 6572893Sdfr * offsets and cluster numbers from a file offset. 6582893Sdfr */ 65956674Snyan pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 66033548Sjkh pmp->pm_crbomask = pmp->pm_bpcluster - 1; 66133548Sjkh pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 66233548Sjkh 66333548Sjkh /* 66433548Sjkh * Check for valid cluster size 66533548Sjkh * must be a power of 2 66633548Sjkh */ 66733548Sjkh if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 6682893Sdfr error = EINVAL; 6692893Sdfr goto error_exit; 6702893Sdfr } 6712893Sdfr 67233548Sjkh /* 67333548Sjkh * Release the bootsector buffer. 67433548Sjkh */ 67533548Sjkh brelse(bp); 67633548Sjkh bp = NULL; 67733548Sjkh 67833548Sjkh /* 679171731Sbde * Check the fsinfo sector if we have one. Silently fix up our 680171731Sbde * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff) 681171731Sbde * or too large. Ignore fp->fsinfree for now, since we need to 682171731Sbde * read the entire FAT anyway to fill the inuse map. 68333548Sjkh */ 68433548Sjkh if (pmp->pm_fsinfo) { 68533548Sjkh struct fsinfo *fp; 68633548Sjkh 687171711Sbde if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 68856674Snyan NOCRED, &bp)) != 0) 68933548Sjkh goto error_exit; 69033548Sjkh fp = (struct fsinfo *)bp->b_data; 69133548Sjkh if (!bcmp(fp->fsisig1, "RRaA", 4) 69233548Sjkh && !bcmp(fp->fsisig2, "rrAa", 4) 693171406Sbde && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) { 69433548Sjkh pmp->pm_nxtfree = getulong(fp->fsinxtfree); 695171731Sbde if (pmp->pm_nxtfree > pmp->pm_maxcluster) 696125934Stjr pmp->pm_nxtfree = CLUST_FIRST; 697125934Stjr } else 69833548Sjkh pmp->pm_fsinfo = 0; 69933548Sjkh brelse(bp); 70033548Sjkh bp = NULL; 70116363Sasami } 7022893Sdfr 7032893Sdfr /* 704171731Sbde * Finish initializing pmp->pm_nxtfree (just in case the first few 705171731Sbde * sectors aren't properly reserved in the FAT). This completes 706171731Sbde * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 707171731Sbde * value if there is no fsinfo. We will use pmp->pm_nxtfree 708171731Sbde * internally even if there is no fsinfo. 7092893Sdfr */ 710171731Sbde if (pmp->pm_nxtfree < CLUST_FIRST) 711171731Sbde pmp->pm_nxtfree = CLUST_FIRST; 7122893Sdfr 7132893Sdfr /* 7142893Sdfr * Allocate memory for the bitmap of allocated clusters, and then 7152893Sdfr * fill it in. 7162893Sdfr */ 717126086Sbde pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS) 7182893Sdfr * sizeof(*pmp->pm_inusemap), 719111119Simp M_MSDOSFSFAT, M_WAITOK); 7202893Sdfr 7212893Sdfr /* 7222893Sdfr * fillinusemap() needs pm_devvp. 7232893Sdfr */ 7242893Sdfr pmp->pm_devvp = devvp; 725189120Sjhb pmp->pm_dev = dev; 7262893Sdfr 7272893Sdfr /* 7282893Sdfr * Have the inuse map filled in. 7292893Sdfr */ 730204471Skib MSDOSFS_LOCK_MP(pmp); 731204471Skib error = fillinusemap(pmp); 732204471Skib MSDOSFS_UNLOCK_MP(pmp); 733204471Skib if (error != 0) 7342893Sdfr goto error_exit; 7352893Sdfr 7362893Sdfr /* 7372893Sdfr * If they want fat updates to be synchronous then let them suffer 7382893Sdfr * the performance degradation in exchange for the on disk copy of 7392893Sdfr * the fat being correct just about all the time. I suppose this 7402893Sdfr * would be a good thing to turn on if the kernel is still flakey. 7412893Sdfr */ 74233548Sjkh if (mp->mnt_flag & MNT_SYNCHRONOUS) 74333548Sjkh pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 7442893Sdfr 7452893Sdfr /* 7462893Sdfr * Finish up. 7472893Sdfr */ 74833548Sjkh if (ronly) 74933548Sjkh pmp->pm_flags |= MSDOSFSMNT_RONLY; 750123873Strhodes else { 751172883Sdelphij if ((error = markvoldirty(pmp, 1)) != 0) { 752172883Sdelphij (void)markvoldirty(pmp, 0); 753123963Sbde goto error_exit; 754172883Sdelphij } 7552893Sdfr pmp->pm_fmod = 1; 756123873Strhodes } 757172697Salfred mp->mnt_data = pmp; 75850256Sbde mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 75923134Sbde mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 760162647Stegge MNT_ILOCK(mp); 76123997Speter mp->mnt_flag |= MNT_LOCAL; 762162647Stegge MNT_IUNLOCK(mp); 7632893Sdfr 764166340Srodrigc if (pmp->pm_flags & MSDOSFS_LARGEFS) 765166340Srodrigc msdosfs_fileno_init(mp); 766131523Stjr 7672893Sdfr return 0; 7682893Sdfr 76933548Sjkherror_exit: 77033548Sjkh if (bp) 77133548Sjkh brelse(bp); 772137036Sphk if (cp != NULL) { 773137036Sphk DROP_GIANT(); 774137036Sphk g_topology_lock(); 775183754Sattilio g_vfs_close(cp); 776137036Sphk g_topology_unlock(); 777137036Sphk PICKUP_GIANT(); 778137036Sphk } 7792893Sdfr if (pmp) { 780204576Skib lockdestroy(&pmp->pm_fatlock); 7812893Sdfr if (pmp->pm_inusemap) 78233548Sjkh free(pmp->pm_inusemap, M_MSDOSFSFAT); 78333548Sjkh free(pmp, M_MSDOSFSMNT); 784172697Salfred mp->mnt_data = NULL; 7852893Sdfr } 786189120Sjhb dev_rel(dev); 78733548Sjkh return (error); 7882893Sdfr} 7892893Sdfr 7902893Sdfr/* 7912893Sdfr * Unmount the filesystem described by mp. 7922893Sdfr */ 79312144Sphkstatic int 794191990Sattiliomsdosfs_unmount(struct mount *mp, int mntflags) 7952893Sdfr{ 79633548Sjkh struct msdosfsmount *pmp; 79733548Sjkh int error, flags; 7982893Sdfr 79933548Sjkh flags = 0; 80033548Sjkh if (mntflags & MNT_FORCE) 8012893Sdfr flags |= FORCECLOSE; 802191990Sattilio error = vflush(mp, 0, flags, curthread); 803188956Strasz if (error && error != ENXIO) 8042893Sdfr return error; 80533548Sjkh pmp = VFSTOMSDOSFS(mp); 806172883Sdelphij if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) { 807172883Sdelphij error = markvoldirty(pmp, 0); 808188956Strasz if (error && error != ENXIO) { 809172883Sdelphij (void)markvoldirty(pmp, 1); 810172883Sdelphij return (error); 811172883Sdelphij } 812172883Sdelphij } 813120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 814120492Sfjoe if (pmp->pm_w2u) 815120492Sfjoe msdosfs_iconv->close(pmp->pm_w2u); 816120492Sfjoe if (pmp->pm_u2w) 817120492Sfjoe msdosfs_iconv->close(pmp->pm_u2w); 818120492Sfjoe if (pmp->pm_d2u) 819120492Sfjoe msdosfs_iconv->close(pmp->pm_d2u); 820120492Sfjoe if (pmp->pm_u2d) 821120492Sfjoe msdosfs_iconv->close(pmp->pm_u2d); 822120492Sfjoe } 823123873Strhodes 82433548Sjkh#ifdef MSDOSFS_DEBUG 82533548Sjkh { 82633548Sjkh struct vnode *vp = pmp->pm_devvp; 827177493Sjeff struct bufobj *bo; 82833548Sjkh 829177493Sjeff bo = &vp->v_bufobj; 830177493Sjeff BO_LOCK(bo); 831103936Sjeff VI_LOCK(vp); 832142235Sphk vn_printf(vp, 833142235Sphk "msdosfs_umount(): just before calling VOP_CLOSE()\n"); 83433548Sjkh printf("freef %p, freeb %p, mount %p\n", 835234482Smckusick TAILQ_NEXT(vp, v_actfreelist), vp->v_actfreelist.tqe_prev, 83633548Sjkh vp->v_mount); 83733548Sjkh printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", 838136943Sphk TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd), 839136943Sphk TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd), 840136943Sphk vp->v_bufobj.bo_numoutput, vp->v_type); 841103936Sjeff VI_UNLOCK(vp); 842177493Sjeff BO_UNLOCK(bo); 84333548Sjkh } 84433548Sjkh#endif 845137036Sphk DROP_GIANT(); 846234025Smckusick if (pmp->pm_devvp->v_type == VCHR && pmp->pm_devvp->v_rdev != NULL) 847234025Smckusick pmp->pm_devvp->v_rdev->si_mountpt = NULL; 848137036Sphk g_topology_lock(); 849183754Sattilio g_vfs_close(pmp->pm_cp); 850137036Sphk g_topology_unlock(); 851137036Sphk PICKUP_GIANT(); 8522893Sdfr vrele(pmp->pm_devvp); 853189120Sjhb dev_rel(pmp->pm_dev); 85433548Sjkh free(pmp->pm_inusemap, M_MSDOSFSFAT); 855172883Sdelphij if (pmp->pm_flags & MSDOSFS_LARGEFS) 856166340Srodrigc msdosfs_fileno_free(mp); 857204470Skib lockdestroy(&pmp->pm_fatlock); 85833548Sjkh free(pmp, M_MSDOSFSMNT); 859172697Salfred mp->mnt_data = NULL; 860162647Stegge MNT_ILOCK(mp); 86123997Speter mp->mnt_flag &= ~MNT_LOCAL; 862162647Stegge MNT_IUNLOCK(mp); 863188956Strasz return (error); 8642893Sdfr} 8652893Sdfr 86612144Sphkstatic int 867191990Sattiliomsdosfs_root(struct mount *mp, int flags, struct vnode **vpp) 8682893Sdfr{ 86933548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 8702893Sdfr struct denode *ndep; 8712893Sdfr int error; 8722893Sdfr 8732893Sdfr#ifdef MSDOSFS_DEBUG 87433548Sjkh printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 8752893Sdfr#endif 87633548Sjkh error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 87733548Sjkh if (error) 87833548Sjkh return (error); 87933548Sjkh *vpp = DETOV(ndep); 88033548Sjkh return (0); 8812893Sdfr} 8822893Sdfr 88312144Sphkstatic int 884191990Sattiliomsdosfs_statfs(struct mount *mp, struct statfs *sbp) 8852893Sdfr{ 88633548Sjkh struct msdosfsmount *pmp; 8872893Sdfr 88833548Sjkh pmp = VFSTOMSDOSFS(mp); 8892893Sdfr sbp->f_bsize = pmp->pm_bpcluster; 8902893Sdfr sbp->f_iosize = pmp->pm_bpcluster; 89155188Sbp sbp->f_blocks = pmp->pm_maxcluster + 1; 8922893Sdfr sbp->f_bfree = pmp->pm_freeclustercount; 8932893Sdfr sbp->f_bavail = pmp->pm_freeclustercount; 894171757Sbde sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 8952893Sdfr sbp->f_ffree = 0; /* what to put in here? */ 89633548Sjkh return (0); 8972893Sdfr} 8982893Sdfr 899246921Skib/* 900246921Skib * If we have an FSInfo block, update it. 901246921Skib */ 90212144Sphkstatic int 903246921Skibmsdosfs_fsiflush(struct msdosfsmount *pmp, int waitfor) 904246921Skib{ 905246921Skib struct fsinfo *fp; 906246921Skib struct buf *bp; 907246921Skib int error; 908246921Skib 909246921Skib MSDOSFS_LOCK_MP(pmp); 910246921Skib if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) { 911246921Skib error = 0; 912246921Skib goto unlock; 913246921Skib } 914246921Skib error = bread(pmp->pm_devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 915246921Skib NOCRED, &bp); 916246921Skib if (error != 0) { 917246921Skib brelse(bp); 918246921Skib goto unlock; 919246921Skib } 920246921Skib fp = (struct fsinfo *)bp->b_data; 921246921Skib putulong(fp->fsinfree, pmp->pm_freeclustercount); 922246921Skib putulong(fp->fsinxtfree, pmp->pm_nxtfree); 923246921Skib pmp->pm_flags &= ~MSDOSFS_FSIMOD; 924246921Skib if (waitfor == MNT_WAIT) 925246921Skib error = bwrite(bp); 926246921Skib else 927246921Skib bawrite(bp); 928246921Skibunlock: 929246921Skib MSDOSFS_UNLOCK_MP(pmp); 930246921Skib return (error); 931246921Skib} 932246921Skib 933246921Skibstatic int 934191990Sattiliomsdosfs_sync(struct mount *mp, int waitfor) 9352893Sdfr{ 93633548Sjkh struct vnode *vp, *nvp; 937191990Sattilio struct thread *td; 9382893Sdfr struct denode *dep; 93933548Sjkh struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 94033548Sjkh int error, allerror = 0; 9412893Sdfr 942191990Sattilio td = curthread; 943191990Sattilio 9442893Sdfr /* 9452893Sdfr * If we ever switch to not updating all of the fats all the time, 9462893Sdfr * this would be the place to update them from the first one. 9472893Sdfr */ 94846568Speter if (pmp->pm_fmod != 0) { 94933548Sjkh if (pmp->pm_flags & MSDOSFSMNT_RONLY) 9502893Sdfr panic("msdosfs_sync: rofs mod"); 9512893Sdfr else { 9522893Sdfr /* update fats here */ 9532893Sdfr } 95446568Speter } 9552893Sdfr /* 95633548Sjkh * Write back each (modified) denode. 9572893Sdfr */ 9582893Sdfrloop: 959234386Smckusick MNT_VNODE_FOREACH_ALL(vp, mp, nvp) { 960234386Smckusick if (vp->v_type == VNON) { 961120785Sjeff VI_UNLOCK(vp); 962120785Sjeff continue; 963120785Sjeff } 9642893Sdfr dep = VTODE(vp); 965137008Sphk if ((dep->de_flag & 96635511Sdt (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 967136943Sphk (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 968137008Sphk waitfor == MNT_LAZY)) { 969103936Sjeff VI_UNLOCK(vp); 9702893Sdfr continue; 97123134Sbde } 97283366Sjulian error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 97323134Sbde if (error) { 97423134Sbde if (error == ENOENT) 97523134Sbde goto loop; 97623134Sbde continue; 97723134Sbde } 978140048Sphk error = VOP_FSYNC(vp, waitfor, td); 9793152Sphk if (error) 9802893Sdfr allerror = error; 981175294Sattilio VOP_UNLOCK(vp, 0); 982121874Skan vrele(vp); 9832893Sdfr } 9842893Sdfr 9852893Sdfr /* 9862893Sdfr * Flush filesystem control info. 9872893Sdfr */ 98835511Sdt if (waitfor != MNT_LAZY) { 989175202Sattilio vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 990140048Sphk error = VOP_FSYNC(pmp->pm_devvp, waitfor, td); 99135511Sdt if (error) 99235511Sdt allerror = error; 993175294Sattilio VOP_UNLOCK(pmp->pm_devvp, 0); 99435511Sdt } 995246921Skib 996246921Skib error = msdosfs_fsiflush(pmp, waitfor); 997246921Skib if (error != 0) 998246921Skib allerror = error; 99933548Sjkh return (allerror); 10002893Sdfr} 10012893Sdfr 1002170188Strhodesstatic int 1003222167Srmacklemmsdosfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) 1004170188Strhodes{ 1005170188Strhodes struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1006170188Strhodes struct defid *defhp = (struct defid *) fhp; 1007170188Strhodes struct denode *dep; 1008170188Strhodes int error; 1009170188Strhodes 1010170188Strhodes error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); 1011170188Strhodes if (error) { 1012170188Strhodes *vpp = NULLVP; 1013170188Strhodes return (error); 1014170188Strhodes } 1015170188Strhodes *vpp = DETOV(dep); 1016170188Strhodes vnode_create_vobject(*vpp, dep->de_FileSize, curthread); 1017170188Strhodes return (0); 1018170188Strhodes} 1019170188Strhodes 102012145Sphkstatic struct vfsops msdosfs_vfsops = { 1021170188Strhodes .vfs_fhtovp = msdosfs_fhtovp, 1022138471Sphk .vfs_mount = msdosfs_mount, 1023138471Sphk .vfs_cmount = msdosfs_cmount, 1024116271Sphk .vfs_root = msdosfs_root, 1025116271Sphk .vfs_statfs = msdosfs_statfs, 1026116271Sphk .vfs_sync = msdosfs_sync, 1027116271Sphk .vfs_unmount = msdosfs_unmount, 10282893Sdfr}; 10292946Swollman 103077577SruVFS_SET(msdosfs_vfsops, msdosfs, 0); 1031120492SfjoeMODULE_VERSION(msdosfs, 1); 1032