1c3aac50fSPeter Wemm /* $FreeBSD$ */ 2952a6212SJordan K. Hubbard /* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ 327a0bc89SDoug Rabson 427a0bc89SDoug Rabson /*- 5952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 6952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 727a0bc89SDoug Rabson * All rights reserved. 827a0bc89SDoug Rabson * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 927a0bc89SDoug Rabson * 1027a0bc89SDoug Rabson * Redistribution and use in source and binary forms, with or without 1127a0bc89SDoug Rabson * modification, are permitted provided that the following conditions 1227a0bc89SDoug Rabson * are met: 1327a0bc89SDoug Rabson * 1. Redistributions of source code must retain the above copyright 1427a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer. 1527a0bc89SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 1627a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer in the 1727a0bc89SDoug Rabson * documentation and/or other materials provided with the distribution. 1827a0bc89SDoug Rabson * 3. All advertising materials mentioning features or use of this software 1927a0bc89SDoug Rabson * must display the following acknowledgement: 2027a0bc89SDoug Rabson * This product includes software developed by TooLs GmbH. 2127a0bc89SDoug Rabson * 4. The name of TooLs GmbH may not be used to endorse or promote products 2227a0bc89SDoug Rabson * derived from this software without specific prior written permission. 2327a0bc89SDoug Rabson * 2427a0bc89SDoug Rabson * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2527a0bc89SDoug Rabson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2627a0bc89SDoug Rabson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2727a0bc89SDoug Rabson * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2827a0bc89SDoug Rabson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2927a0bc89SDoug Rabson * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 3027a0bc89SDoug Rabson * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3127a0bc89SDoug Rabson * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3227a0bc89SDoug Rabson * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3327a0bc89SDoug Rabson * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3427a0bc89SDoug Rabson */ 35d167cf6fSWarner Losh /*- 3627a0bc89SDoug Rabson * Written by Paul Popelka (paulp@uts.amdahl.com) 3727a0bc89SDoug Rabson * 3827a0bc89SDoug Rabson * You can do anything you want with this software, just don't say you wrote 3927a0bc89SDoug Rabson * it, and don't remove this notice. 4027a0bc89SDoug Rabson * 4127a0bc89SDoug Rabson * This software is provided "as is". 4227a0bc89SDoug Rabson * 4327a0bc89SDoug Rabson * The author supplies this software to be publicly redistributed on the 4427a0bc89SDoug Rabson * understanding that the author is not responsible for the correct 4527a0bc89SDoug Rabson * functioning of this software in any circumstances and is not liable for 4627a0bc89SDoug Rabson * any damages caused by this software. 4727a0bc89SDoug Rabson * 4827a0bc89SDoug Rabson * October 1992 4927a0bc89SDoug Rabson */ 5027a0bc89SDoug Rabson 5127a0bc89SDoug Rabson #include <sys/param.h> 5227a0bc89SDoug Rabson #include <sys/systm.h> 536becd1c8SBruce Evans #include <sys/buf.h> 5465baf8f0SBruce Evans #include <sys/conf.h> 5557b4252eSKonstantin Belousov #include <sys/fcntl.h> 566becd1c8SBruce Evans #include <sys/iconv.h> 576becd1c8SBruce Evans #include <sys/kernel.h> 581103771dSBruce Evans #include <sys/lock.h> 596becd1c8SBruce Evans #include <sys/malloc.h> 606becd1c8SBruce Evans #include <sys/mount.h> 611103771dSBruce Evans #include <sys/mutex.h> 6227a0bc89SDoug Rabson #include <sys/namei.h> 63acd3428bSRobert Watson #include <sys/priv.h> 6427a0bc89SDoug Rabson #include <sys/proc.h> 656becd1c8SBruce Evans #include <sys/stat.h> 6627a0bc89SDoug Rabson #include <sys/vnode.h> 6727a0bc89SDoug Rabson 689a135592SPoul-Henning Kamp #include <geom/geom.h> 699a135592SPoul-Henning Kamp #include <geom/geom_vfs.h> 709a135592SPoul-Henning Kamp 716becd1c8SBruce Evans #include <fs/msdosfs/bootsect.h> 726becd1c8SBruce Evans #include <fs/msdosfs/bpb.h> 736becd1c8SBruce Evans #include <fs/msdosfs/direntry.h> 746becd1c8SBruce Evans #include <fs/msdosfs/denode.h> 756becd1c8SBruce Evans #include <fs/msdosfs/fat.h> 766becd1c8SBruce Evans #include <fs/msdosfs/msdosfsmount.h> 776becd1c8SBruce Evans 7823b6c230SKonstantin Belousov static const char msdosfs_lock_msg[] = "fatlk"; 7923b6c230SKonstantin Belousov 807c3fc9deSBruce Evans /* Mount options that we support. */ 816a4b48f4SPoul-Henning Kamp static const char *msdosfs_opts[] = { 82cb65c1eeSBruce Evans "async", "noatime", "noclusterr", "noclusterw", 837c3fc9deSBruce Evans "export", "force", "from", "sync", 847c3fc9deSBruce Evans "cs_dos", "cs_local", "cs_win", "dirmask", 857c3fc9deSBruce Evans "gid", "kiconv", "large", "longname", 867c3fc9deSBruce Evans "longnames", "mask", "shortname", "shortnames", 877c3fc9deSBruce Evans "uid", "win95", "nowin95", 886a4b48f4SPoul-Henning Kamp NULL 896a4b48f4SPoul-Henning Kamp }; 906a4b48f4SPoul-Henning Kamp 9101f6cfbaSYoshihiro Takahashi #if 1 /*def PC98*/ 9201f6cfbaSYoshihiro Takahashi /* 9301f6cfbaSYoshihiro Takahashi * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 9401f6cfbaSYoshihiro Takahashi * garbage or a random value :-{ 9501f6cfbaSYoshihiro Takahashi * If you want to use that broken-signatured media, define the 9601f6cfbaSYoshihiro Takahashi * following symbol even though PC/AT. 9701f6cfbaSYoshihiro Takahashi * (ex. mount PC-98 DOS formatted FD on PC/AT) 9801f6cfbaSYoshihiro Takahashi */ 9901f6cfbaSYoshihiro Takahashi #define MSDOSFS_NOCHECKSIG 10001f6cfbaSYoshihiro Takahashi #endif 10101f6cfbaSYoshihiro Takahashi 1025bb84bc8SRobert Watson MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure"); 1035bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table"); 10455166637SPoul-Henning Kamp 1052d7c6b27SBruce Evans struct iconv_functions *msdosfs_iconv; 106c4f02a89SMax Khon 1076a4b48f4SPoul-Henning Kamp static int update_mp(struct mount *mp, struct thread *td); 1080d7935fdSAttilio Rao static int mountmsdosfs(struct vnode *devvp, struct mount *mp); 1091be5bc74STom Rhodes static vfs_fhtovp_t msdosfs_fhtovp; 1106a4b48f4SPoul-Henning Kamp static vfs_mount_t msdosfs_mount; 1119bf1a756SPoul-Henning Kamp static vfs_root_t msdosfs_root; 1129bf1a756SPoul-Henning Kamp static vfs_statfs_t msdosfs_statfs; 1139bf1a756SPoul-Henning Kamp static vfs_sync_t msdosfs_sync; 1149bf1a756SPoul-Henning Kamp static vfs_unmount_t msdosfs_unmount; 115af482601SBruce Evans 1161a9415afSTim J. Robbins /* Maximum length of a character set name (arbitrary). */ 1171a9415afSTim J. Robbins #define MAXCSLEN 64 1181a9415afSTim J. Robbins 119952a6212SJordan K. Hubbard static int 120dc9a617aSCraig Rodrigues update_mp(struct mount *mp, struct thread *td) 121952a6212SJordan K. Hubbard { 122952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1236a4b48f4SPoul-Henning Kamp void *dos, *win, *local; 1246a4b48f4SPoul-Henning Kamp int error, v; 125952a6212SJordan K. Hubbard 1266a4b48f4SPoul-Henning Kamp if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) { 1276a4b48f4SPoul-Henning Kamp if (msdosfs_iconv != NULL) { 1286a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1296a4b48f4SPoul-Henning Kamp "cs_win", &win, NULL); 1306a4b48f4SPoul-Henning Kamp if (!error) 1316a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1326a4b48f4SPoul-Henning Kamp "cs_local", &local, NULL); 1336a4b48f4SPoul-Henning Kamp if (!error) 1346a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1356a4b48f4SPoul-Henning Kamp "cs_dos", &dos, NULL); 1366a4b48f4SPoul-Henning Kamp if (!error) { 1371a9415afSTim J. Robbins msdosfs_iconv->open(win, local, &pmp->pm_u2w); 1381a9415afSTim J. Robbins msdosfs_iconv->open(local, win, &pmp->pm_w2u); 1391a9415afSTim J. Robbins msdosfs_iconv->open(dos, local, &pmp->pm_u2d); 1401a9415afSTim J. Robbins msdosfs_iconv->open(local, dos, &pmp->pm_d2u); 1416a4b48f4SPoul-Henning Kamp } 1421a9415afSTim J. Robbins if (error != 0) 1431a9415afSTim J. Robbins return (error); 144c4f02a89SMax Khon } else { 145c4f02a89SMax Khon pmp->pm_w2u = NULL; 146c4f02a89SMax Khon pmp->pm_u2w = NULL; 147c4f02a89SMax Khon pmp->pm_d2u = NULL; 148c4f02a89SMax Khon pmp->pm_u2d = NULL; 1497391f611SAndrey A. Chernov } 1501a9415afSTim J. Robbins } 1511a9415afSTim J. Robbins 1526a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v)) 1536a4b48f4SPoul-Henning Kamp pmp->pm_gid = v; 1546a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v)) 1556a4b48f4SPoul-Henning Kamp pmp->pm_uid = v; 1566a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v)) 1576a4b48f4SPoul-Henning Kamp pmp->pm_mask = v & ALLPERMS; 1586a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v)) 1596a4b48f4SPoul-Henning Kamp pmp->pm_dirmask = v & ALLPERMS; 1606a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "shortname", 1616a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1624ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "shortnames", 1634ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1646a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "longname", 1656a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1664ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "longnames", 1674ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1686a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "kiconv", 1696a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_KICONV); 1706a4b48f4SPoul-Henning Kamp 171d75b2048SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0) 1726a4b48f4SPoul-Henning Kamp pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 173d75b2048SCraig Rodrigues else 174d75b2048SCraig Rodrigues pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95; 175952a6212SJordan K. Hubbard 176952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 177952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 178952a6212SJordan K. Hubbard else if (!(pmp->pm_flags & 179952a6212SJordan K. Hubbard (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 180952a6212SJordan K. Hubbard struct vnode *rootvp; 181952a6212SJordan K. Hubbard 182952a6212SJordan K. Hubbard /* 183952a6212SJordan K. Hubbard * Try to divine whether to support Win'95 long filenames 184952a6212SJordan K. Hubbard */ 185952a6212SJordan K. Hubbard if (FAT32(pmp)) 186952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 187952a6212SJordan K. Hubbard else { 188d9b2d9f7SJeff Roberson if ((error = 189dfd233edSAttilio Rao msdosfs_root(mp, LK_EXCLUSIVE, &rootvp)) != 0) 190952a6212SJordan K. Hubbard return error; 1912d7c6b27SBruce Evans pmp->pm_flags |= findwin95(VTODE(rootvp)) ? 1922d7c6b27SBruce Evans MSDOSFSMNT_LONGNAME : MSDOSFSMNT_SHORTNAME; 193952a6212SJordan K. Hubbard vput(rootvp); 194952a6212SJordan K. Hubbard } 195952a6212SJordan K. Hubbard } 196952a6212SJordan K. Hubbard return 0; 197952a6212SJordan K. Hubbard } 198952a6212SJordan K. Hubbard 1996a4b48f4SPoul-Henning Kamp static int 200dfd233edSAttilio Rao msdosfs_cmount(struct mntarg *ma, void *data, int flags) 2016a4b48f4SPoul-Henning Kamp { 2026a4b48f4SPoul-Henning Kamp struct msdosfs_args args; 2036a4b48f4SPoul-Henning Kamp int error; 2046a4b48f4SPoul-Henning Kamp 2056a4b48f4SPoul-Henning Kamp if (data == NULL) 2066a4b48f4SPoul-Henning Kamp return (EINVAL); 2076a4b48f4SPoul-Henning Kamp error = copyin(data, &args, sizeof args); 2086a4b48f4SPoul-Henning Kamp if (error) 2096a4b48f4SPoul-Henning Kamp return (error); 2106a4b48f4SPoul-Henning Kamp 2116a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); 2126a4b48f4SPoul-Henning Kamp ma = mount_arg(ma, "export", &args.export, sizeof args.export); 2136a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "uid", "%d", args.uid); 2146a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "gid", "%d", args.gid); 2156a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "mask", "%d", args.mask); 2166a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "dirmask", "%d", args.dirmask); 2176a4b48f4SPoul-Henning Kamp 2186a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname"); 2196a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname"); 2206a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95"); 2216a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv"); 2226a4b48f4SPoul-Henning Kamp 2236a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN); 2246a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN); 2256a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN); 2266a4b48f4SPoul-Henning Kamp 2276a4b48f4SPoul-Henning Kamp error = kernel_mount(ma, flags); 2286a4b48f4SPoul-Henning Kamp 2296a4b48f4SPoul-Henning Kamp return (error); 2306a4b48f4SPoul-Henning Kamp } 2316a4b48f4SPoul-Henning Kamp 23227a0bc89SDoug Rabson /* 23327a0bc89SDoug Rabson * mp - path - addr in user space of mount point (ie /usr or whatever) 23427a0bc89SDoug Rabson * data - addr in user space of mount params including the name of the block 23527a0bc89SDoug Rabson * special file to treat as a filesystem. 23627a0bc89SDoug Rabson */ 2377fefffeeSPoul-Henning Kamp static int 238dfd233edSAttilio Rao msdosfs_mount(struct mount *mp) 23927a0bc89SDoug Rabson { 24027a0bc89SDoug Rabson struct vnode *devvp; /* vnode for blk device to mount */ 241dfd233edSAttilio Rao struct thread *td; 242952a6212SJordan K. Hubbard /* msdosfs specific mount control block */ 243952a6212SJordan K. Hubbard struct msdosfsmount *pmp = NULL; 2445e8c582aSPoul-Henning Kamp struct nameidata ndp; 245269c902fSPoul-Henning Kamp int error, flags; 24615bc6b2bSEdward Tomasz Napierala accmode_t accmode; 2476a4b48f4SPoul-Henning Kamp char *from; 24827a0bc89SDoug Rabson 249dfd233edSAttilio Rao td = curthread; 2506a4b48f4SPoul-Henning Kamp if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) 2516a4b48f4SPoul-Henning Kamp return (EINVAL); 2526a4b48f4SPoul-Henning Kamp 25327a0bc89SDoug Rabson /* 254952a6212SJordan K. Hubbard * If updating, check whether changing from read-only to 255952a6212SJordan K. Hubbard * read/write; if there is no device name, that's all we do. 25627a0bc89SDoug Rabson */ 25727a0bc89SDoug Rabson if (mp->mnt_flag & MNT_UPDATE) { 2582d7c6b27SBruce Evans pmp = VFSTOMSDOSFS(mp); 2595eb304a9SCraig Rodrigues if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { 260e9022ef8SCraig Rodrigues /* 261e9022ef8SCraig Rodrigues * Forbid export requests if filesystem has 262e9022ef8SCraig Rodrigues * MSDOSFS_LARGEFS flag set. 263e9022ef8SCraig Rodrigues */ 264e9022ef8SCraig Rodrigues if ((pmp->pm_flags & MSDOSFS_LARGEFS) != 0) { 265e9022ef8SCraig Rodrigues vfs_mount_error(mp, 266e9022ef8SCraig Rodrigues "MSDOSFS_LARGEFS flag set, cannot export"); 267269c902fSPoul-Henning Kamp return (EOPNOTSUPP); 268e9022ef8SCraig Rodrigues } 269269c902fSPoul-Henning Kamp } 2709a135592SPoul-Henning Kamp if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 2716a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 272dfd233edSAttilio Rao error = VFS_SYNC(mp, MNT_WAIT); 273e83f1423SPoul-Henning Kamp if (error) 274e83f1423SPoul-Henning Kamp return (error); 27527a0bc89SDoug Rabson flags = WRITECLOSE; 27627a0bc89SDoug Rabson if (mp->mnt_flag & MNT_FORCE) 27727a0bc89SDoug Rabson flags |= FORCECLOSE; 278f257b7a5SAlfred Perlstein error = vflush(mp, 0, flags, td); 2796a4b48f4SPoul-Henning Kamp if (error) 2806a4b48f4SPoul-Henning Kamp return (error); 2813247c9ddSXin LI 2823247c9ddSXin LI /* 2833247c9ddSXin LI * Now the volume is clean. Mark it so while the 2843247c9ddSXin LI * device is still rw. 2853247c9ddSXin LI */ 2863247c9ddSXin LI error = markvoldirty(pmp, 0); 2873247c9ddSXin LI if (error) { 2883247c9ddSXin LI (void)markvoldirty(pmp, 1); 2893247c9ddSXin LI return (error); 2903247c9ddSXin LI } 2913247c9ddSXin LI 2923247c9ddSXin LI /* Downgrade the device from rw to ro. */ 2939a135592SPoul-Henning Kamp DROP_GIANT(); 2949a135592SPoul-Henning Kamp g_topology_lock(); 2954eb3abf0SBruce Evans error = g_access(pmp->pm_cp, 0, -1, 0); 2969a135592SPoul-Henning Kamp g_topology_unlock(); 2979a135592SPoul-Henning Kamp PICKUP_GIANT(); 2983247c9ddSXin LI if (error) { 2993247c9ddSXin LI (void)markvoldirty(pmp, 1); 3004eb3abf0SBruce Evans return (error); 3013247c9ddSXin LI } 3024eb3abf0SBruce Evans 3033247c9ddSXin LI /* 3043247c9ddSXin LI * Backing out after an error was painful in the 3053247c9ddSXin LI * above. Now we are committed to succeeding. 3063247c9ddSXin LI */ 3073247c9ddSXin LI pmp->pm_fmod = 0; 3083247c9ddSXin LI pmp->pm_flags |= MSDOSFSMNT_RONLY; 3093247c9ddSXin LI MNT_ILOCK(mp); 3103247c9ddSXin LI mp->mnt_flag |= MNT_RDONLY; 3113247c9ddSXin LI MNT_IUNLOCK(mp); 3126a4b48f4SPoul-Henning Kamp } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 3136a4b48f4SPoul-Henning Kamp !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 314952a6212SJordan K. Hubbard /* 315952a6212SJordan K. Hubbard * If upgrade to read-write by non-root, then verify 316952a6212SJordan K. Hubbard * that user has necessary permissions on the device. 317952a6212SJordan K. Hubbard */ 318952a6212SJordan K. Hubbard devvp = pmp->pm_devvp; 319cb05b60aSAttilio Rao vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 320952a6212SJordan K. Hubbard error = VOP_ACCESS(devvp, VREAD | VWRITE, 321a854ed98SJohn Baldwin td->td_ucred, td); 322acd3428bSRobert Watson if (error) 323acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 324952a6212SJordan K. Hubbard if (error) { 32522db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 326952a6212SJordan K. Hubbard return (error); 327952a6212SJordan K. Hubbard } 32822db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 3299a135592SPoul-Henning Kamp DROP_GIANT(); 3309a135592SPoul-Henning Kamp g_topology_lock(); 3319a135592SPoul-Henning Kamp error = g_access(pmp->pm_cp, 0, 1, 0); 3329a135592SPoul-Henning Kamp g_topology_unlock(); 3339a135592SPoul-Henning Kamp PICKUP_GIANT(); 3349a135592SPoul-Henning Kamp if (error) 3359a135592SPoul-Henning Kamp return (error); 336cede1f56STom Rhodes 3373247c9ddSXin LI pmp->pm_fmod = 1; 3383247c9ddSXin LI pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 3393247c9ddSXin LI MNT_ILOCK(mp); 3403247c9ddSXin LI mp->mnt_flag &= ~MNT_RDONLY; 3413247c9ddSXin LI MNT_IUNLOCK(mp); 34282c59ec6SCraig Rodrigues 34382c59ec6SCraig Rodrigues /* Now that the volume is modifiable, mark it dirty. */ 34482c59ec6SCraig Rodrigues error = markvoldirty(pmp, 1); 34582c59ec6SCraig Rodrigues if (error) 34682c59ec6SCraig Rodrigues return (error); 34782c59ec6SCraig Rodrigues } 348952a6212SJordan K. Hubbard } 34927a0bc89SDoug Rabson /* 350952a6212SJordan K. Hubbard * Not an update, or updating the name: look up the name 351e9827c6dSBruce Evans * and verify that it refers to a sensible disk device. 35227a0bc89SDoug Rabson */ 3536a4b48f4SPoul-Henning Kamp if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) 3546a4b48f4SPoul-Henning Kamp return (EINVAL); 35575d7ba93SSuleiman Souhlal NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); 3565e8c582aSPoul-Henning Kamp error = namei(&ndp); 357952a6212SJordan K. Hubbard if (error) 358952a6212SJordan K. Hubbard return (error); 3595e8c582aSPoul-Henning Kamp devvp = ndp.ni_vp; 3605e8c582aSPoul-Henning Kamp NDFREE(&ndp, NDF_ONLY_PNBUF); 361952a6212SJordan K. Hubbard 362ba4ad1fcSPoul-Henning Kamp if (!vn_isdisk(devvp, &error)) { 36375d7ba93SSuleiman Souhlal vput(devvp); 364ba4ad1fcSPoul-Henning Kamp return (error); 36527a0bc89SDoug Rabson } 36627a0bc89SDoug Rabson /* 367952a6212SJordan K. Hubbard * If mount by non-root, then verify that user has necessary 368952a6212SJordan K. Hubbard * permissions on the device. 36927a0bc89SDoug Rabson */ 37015bc6b2bSEdward Tomasz Napierala accmode = VREAD; 371952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_RDONLY) == 0) 37215bc6b2bSEdward Tomasz Napierala accmode |= VWRITE; 37315bc6b2bSEdward Tomasz Napierala error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 374acd3428bSRobert Watson if (error) 375acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 376952a6212SJordan K. Hubbard if (error) { 377952a6212SJordan K. Hubbard vput(devvp); 378952a6212SJordan K. Hubbard return (error); 379952a6212SJordan K. Hubbard } 380952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_UPDATE) == 0) { 3810d7935fdSAttilio Rao error = mountmsdosfs(devvp, mp); 382952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 383952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 384952a6212SJordan K. Hubbard #endif 385952a6212SJordan K. Hubbard } else { 38627a0bc89SDoug Rabson if (devvp != pmp->pm_devvp) 387952a6212SJordan K. Hubbard error = EINVAL; /* XXX needs translation */ 38827a0bc89SDoug Rabson else 38975d7ba93SSuleiman Souhlal vput(devvp); 39027a0bc89SDoug Rabson } 39127a0bc89SDoug Rabson if (error) { 39227a0bc89SDoug Rabson vrele(devvp); 393952a6212SJordan K. Hubbard return (error); 394952a6212SJordan K. Hubbard } 395952a6212SJordan K. Hubbard 3966a4b48f4SPoul-Henning Kamp error = update_mp(mp, td); 397952a6212SJordan K. Hubbard if (error) { 3981a9415afSTim J. Robbins if ((mp->mnt_flag & MNT_UPDATE) == 0) 399dfd233edSAttilio Rao msdosfs_unmount(mp, MNT_FORCE); 40027a0bc89SDoug Rabson return error; 40127a0bc89SDoug Rabson } 4026a4b48f4SPoul-Henning Kamp 4036a4b48f4SPoul-Henning Kamp vfs_mountedfrom(mp, from); 40427a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 4056a4b48f4SPoul-Henning Kamp printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 40627a0bc89SDoug Rabson #endif 407952a6212SJordan K. Hubbard return (0); 40827a0bc89SDoug Rabson } 40927a0bc89SDoug Rabson 4107fefffeeSPoul-Henning Kamp static int 4110d7935fdSAttilio Rao mountmsdosfs(struct vnode *devvp, struct mount *mp) 41227a0bc89SDoug Rabson { 413952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 414952a6212SJordan K. Hubbard struct buf *bp; 415c72ae142SJohn Baldwin struct cdev *dev; 41627a0bc89SDoug Rabson union bootsector *bsp; 41727a0bc89SDoug Rabson struct byte_bpb33 *b33; 41827a0bc89SDoug Rabson struct byte_bpb50 *b50; 419952a6212SJordan K. Hubbard struct byte_bpb710 *b710; 420952a6212SJordan K. Hubbard u_int8_t SecPerClust; 421499d3ffaSBoris Popov u_long clusters; 422952a6212SJordan K. Hubbard int ronly, error; 4239a135592SPoul-Henning Kamp struct g_consumer *cp; 4249a135592SPoul-Henning Kamp struct bufobj *bo; 42527a0bc89SDoug Rabson 426c72ae142SJohn Baldwin bp = NULL; /* This and pmp both used in error_exit. */ 427c72ae142SJohn Baldwin pmp = NULL; 4284eb3abf0SBruce Evans ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 429c72ae142SJohn Baldwin 430c72ae142SJohn Baldwin dev = devvp->v_rdev; 431c72ae142SJohn Baldwin dev_ref(dev); 4329a135592SPoul-Henning Kamp DROP_GIANT(); 4339a135592SPoul-Henning Kamp g_topology_lock(); 4344eb3abf0SBruce Evans error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1); 4359a135592SPoul-Henning Kamp g_topology_unlock(); 4369a135592SPoul-Henning Kamp PICKUP_GIANT(); 43722db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 438c3c6d51eSPoul-Henning Kamp if (error) 439c72ae142SJohn Baldwin goto error_exit; 440952a6212SJordan K. Hubbard 4419a135592SPoul-Henning Kamp bo = &devvp->v_bufobj; 442952a6212SJordan K. Hubbard 44327a0bc89SDoug Rabson /* 444952a6212SJordan K. Hubbard * Read the boot sector of the filesystem, and then check the 445952a6212SJordan K. Hubbard * boot signature. If not a dos boot sector then error out. 44601f6cfbaSYoshihiro Takahashi * 44793fe42b6SBruce Evans * NOTE: 8192 is a magic size that works for ffs. 44827a0bc89SDoug Rabson */ 44993fe42b6SBruce Evans error = bread(devvp, 0, 8192, NOCRED, &bp); 450c3c6d51eSPoul-Henning Kamp if (error) 45127a0bc89SDoug Rabson goto error_exit; 452952a6212SJordan K. Hubbard bp->b_flags |= B_AGE; 453952a6212SJordan K. Hubbard bsp = (union bootsector *)bp->b_data; 45427a0bc89SDoug Rabson b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 45527a0bc89SDoug Rabson b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 45601a4d019SJohn Baldwin b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 457952a6212SJordan K. Hubbard 45801f6cfbaSYoshihiro Takahashi #ifndef MSDOSFS_NOCHECKSIG 459952a6212SJordan K. Hubbard if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 460952a6212SJordan K. Hubbard || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 46127a0bc89SDoug Rabson error = EINVAL; 46227a0bc89SDoug Rabson goto error_exit; 46327a0bc89SDoug Rabson } 46401f6cfbaSYoshihiro Takahashi #endif 46527a0bc89SDoug Rabson 466a163d034SWarner Losh pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 46727a0bc89SDoug Rabson pmp->pm_mountp = mp; 4689a135592SPoul-Henning Kamp pmp->pm_cp = cp; 4699a135592SPoul-Henning Kamp pmp->pm_bo = bo; 47027a0bc89SDoug Rabson 47123b6c230SKonstantin Belousov lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0); 47223b6c230SKonstantin Belousov 47327a0bc89SDoug Rabson /* 4744eb3abf0SBruce Evans * Initialize ownerships and permissions, since nothing else will 47523c1e989SMaxim Konovalov * initialize them iff we are mounting root. 4764eb3abf0SBruce Evans */ 4774eb3abf0SBruce Evans pmp->pm_uid = UID_ROOT; 4784eb3abf0SBruce Evans pmp->pm_gid = GID_WHEEL; 4794eb3abf0SBruce Evans pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | 4804eb3abf0SBruce Evans S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 4814eb3abf0SBruce Evans 4824eb3abf0SBruce Evans /* 483f458f2a5SCraig Rodrigues * Experimental support for large MS-DOS filesystems. 484f458f2a5SCraig Rodrigues * WARNING: This uses at least 32 bytes of kernel memory (which is not 485f458f2a5SCraig Rodrigues * reclaimed until the FS is unmounted) for each file on disk to map 486f458f2a5SCraig Rodrigues * between the 32-bit inode numbers used by VFS and the 64-bit 487f458f2a5SCraig Rodrigues * pseudo-inode numbers used internally by msdosfs. This is only 488f458f2a5SCraig Rodrigues * safe to use in certain controlled situations (e.g. read-only FS 489f458f2a5SCraig Rodrigues * with less than 1 million files). 490f458f2a5SCraig Rodrigues * Since the mappings do not persist across unmounts (or reboots), these 491f458f2a5SCraig Rodrigues * filesystems are not suitable for exporting through NFS, or any other 492f458f2a5SCraig Rodrigues * application that requires fixed inode numbers. 493f458f2a5SCraig Rodrigues */ 4942d7c6b27SBruce Evans vfs_flagopt(mp->mnt_optnew, "large", &pmp->pm_flags, MSDOSFS_LARGEFS); 495f458f2a5SCraig Rodrigues 496f458f2a5SCraig Rodrigues /* 49727a0bc89SDoug Rabson * Compute several useful quantities from the bpb in the 49827a0bc89SDoug Rabson * bootsector. Copy in the dos 5 variant of the bpb then fix up 49927a0bc89SDoug Rabson * the fields that are different between dos 5 and dos 3.3. 50027a0bc89SDoug Rabson */ 501952a6212SJordan K. Hubbard SecPerClust = b50->bpbSecPerClust; 50227a0bc89SDoug Rabson pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 503696f22f0SJohn Baldwin if (pmp->pm_BytesPerSec < DEV_BSIZE) { 504696f22f0SJohn Baldwin error = EINVAL; 505696f22f0SJohn Baldwin goto error_exit; 506696f22f0SJohn Baldwin } 50727a0bc89SDoug Rabson pmp->pm_ResSectors = getushort(b50->bpbResSectors); 50827a0bc89SDoug Rabson pmp->pm_FATs = b50->bpbFATs; 50927a0bc89SDoug Rabson pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 51027a0bc89SDoug Rabson pmp->pm_Sectors = getushort(b50->bpbSectors); 51127a0bc89SDoug Rabson pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 51227a0bc89SDoug Rabson pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 51327a0bc89SDoug Rabson pmp->pm_Heads = getushort(b50->bpbHeads); 514952a6212SJordan K. Hubbard pmp->pm_Media = b50->bpbMedia; 51527a0bc89SDoug Rabson 51601f6cfbaSYoshihiro Takahashi /* calculate the ratio of sector size to DEV_BSIZE */ 51701f6cfbaSYoshihiro Takahashi pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 51801f6cfbaSYoshihiro Takahashi 519043ec583SMarcel Moolenaar /* 520043ec583SMarcel Moolenaar * We don't check pm_Heads nor pm_SecPerTrack, because 521043ec583SMarcel Moolenaar * these may not be set for EFI file systems. We don't 522043ec583SMarcel Moolenaar * use these anyway, so we're unaffected if they are 523043ec583SMarcel Moolenaar * invalid. 524043ec583SMarcel Moolenaar */ 525043ec583SMarcel Moolenaar if (!pmp->pm_BytesPerSec || !SecPerClust) { 52627a0bc89SDoug Rabson error = EINVAL; 52727a0bc89SDoug Rabson goto error_exit; 52827a0bc89SDoug Rabson } 52927a0bc89SDoug Rabson 53027a0bc89SDoug Rabson if (pmp->pm_Sectors == 0) { 53127a0bc89SDoug Rabson pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 53227a0bc89SDoug Rabson pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 53327a0bc89SDoug Rabson } else { 53427a0bc89SDoug Rabson pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 53527a0bc89SDoug Rabson pmp->pm_HugeSectors = pmp->pm_Sectors; 53627a0bc89SDoug Rabson } 537f458f2a5SCraig Rodrigues if (!(pmp->pm_flags & MSDOSFS_LARGEFS)) { 538c681be37SDmitrij Tejblum if (pmp->pm_HugeSectors > 0xffffffff / 539c681be37SDmitrij Tejblum (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { 540952a6212SJordan K. Hubbard /* 541952a6212SJordan K. Hubbard * We cannot deal currently with this size of disk 542952a6212SJordan K. Hubbard * due to fileid limitations (see msdosfs_getattr and 543952a6212SJordan K. Hubbard * msdosfs_readdir) 544952a6212SJordan K. Hubbard */ 545952a6212SJordan K. Hubbard error = EINVAL; 546f458f2a5SCraig Rodrigues vfs_mount_error(mp, 547f458f2a5SCraig Rodrigues "Disk too big, try '-o large' mount option"); 548952a6212SJordan K. Hubbard goto error_exit; 549952a6212SJordan K. Hubbard } 550f458f2a5SCraig Rodrigues } 551952a6212SJordan K. Hubbard 552952a6212SJordan K. Hubbard if (pmp->pm_RootDirEnts == 0) { 55320c5ba36SPeter Edwards if (pmp->pm_Sectors 554952a6212SJordan K. Hubbard || pmp->pm_FATsecs 555952a6212SJordan K. Hubbard || getushort(b710->bpbFSVers)) { 556952a6212SJordan K. Hubbard error = EINVAL; 557aaf0bb19SAndrey A. Chernov printf("mountmsdosfs(): bad FAT32 filesystem\n"); 558952a6212SJordan K. Hubbard goto error_exit; 559952a6212SJordan K. Hubbard } 560952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT32_MASK; 561952a6212SJordan K. Hubbard pmp->pm_fatmult = 4; 562952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 563952a6212SJordan K. Hubbard pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 564952a6212SJordan K. Hubbard if (getushort(b710->bpbExtFlags) & FATMIRROR) 565952a6212SJordan K. Hubbard pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 566952a6212SJordan K. Hubbard else 567952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 568952a6212SJordan K. Hubbard } else 569952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 570952a6212SJordan K. Hubbard 571952a6212SJordan K. Hubbard /* 572952a6212SJordan K. Hubbard * Check a few values (could do some more): 573952a6212SJordan K. Hubbard * - logical sector size: power of 2, >= block size 574952a6212SJordan K. Hubbard * - sectors per cluster: power of 2, >= 1 575952a6212SJordan K. Hubbard * - number of sectors: >= 1, <= size of partition 576696f22f0SJohn Baldwin * - number of FAT sectors: >= 1 577952a6212SJordan K. Hubbard */ 578952a6212SJordan K. Hubbard if ( (SecPerClust == 0) 579952a6212SJordan K. Hubbard || (SecPerClust & (SecPerClust - 1)) 58001f6cfbaSYoshihiro Takahashi || (pmp->pm_BytesPerSec < DEV_BSIZE) 581952a6212SJordan K. Hubbard || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 582952a6212SJordan K. Hubbard || (pmp->pm_HugeSectors == 0) 583696f22f0SJohn Baldwin || (pmp->pm_FATsecs == 0) 584952a6212SJordan K. Hubbard ) { 585952a6212SJordan K. Hubbard error = EINVAL; 586952a6212SJordan K. Hubbard goto error_exit; 587952a6212SJordan K. Hubbard } 58801f6cfbaSYoshihiro Takahashi 58901f6cfbaSYoshihiro Takahashi pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 59001f6cfbaSYoshihiro Takahashi pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 59101f6cfbaSYoshihiro Takahashi pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 59201f6cfbaSYoshihiro Takahashi SecPerClust *= pmp->pm_BlkPerSec; 59301f6cfbaSYoshihiro Takahashi 59401f6cfbaSYoshihiro Takahashi pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 59501f6cfbaSYoshihiro Takahashi 596952a6212SJordan K. Hubbard if (FAT32(pmp)) { 597952a6212SJordan K. Hubbard pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 598952a6212SJordan K. Hubbard pmp->pm_firstcluster = pmp->pm_fatblk 599952a6212SJordan K. Hubbard + (pmp->pm_FATs * pmp->pm_FATsecs); 60001f6cfbaSYoshihiro Takahashi pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 601952a6212SJordan K. Hubbard } else { 60227a0bc89SDoug Rabson pmp->pm_rootdirblk = pmp->pm_fatblk + 60327a0bc89SDoug Rabson (pmp->pm_FATs * pmp->pm_FATsecs); 604952a6212SJordan K. Hubbard pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 60501f6cfbaSYoshihiro Takahashi + DEV_BSIZE - 1) 60601f6cfbaSYoshihiro Takahashi / DEV_BSIZE; /* in blocks */ 60727a0bc89SDoug Rabson pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 608952a6212SJordan K. Hubbard } 609952a6212SJordan K. Hubbard 610499d3ffaSBoris Popov pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 611499d3ffaSBoris Popov SecPerClust + 1; 61201f6cfbaSYoshihiro Takahashi pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 613952a6212SJordan K. Hubbard 614952a6212SJordan K. Hubbard if (pmp->pm_fatmask == 0) { 615952a6212SJordan K. Hubbard if (pmp->pm_maxcluster 616952a6212SJordan K. Hubbard <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 61727a0bc89SDoug Rabson /* 618952a6212SJordan K. Hubbard * This will usually be a floppy disk. This size makes 619952a6212SJordan K. Hubbard * sure that one fat entry will not be split across 620952a6212SJordan K. Hubbard * multiple blocks. 62127a0bc89SDoug Rabson */ 622952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT12_MASK; 623952a6212SJordan K. Hubbard pmp->pm_fatmult = 3; 624952a6212SJordan K. Hubbard pmp->pm_fatdiv = 2; 625952a6212SJordan K. Hubbard } else { 626952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT16_MASK; 627952a6212SJordan K. Hubbard pmp->pm_fatmult = 2; 628952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 629952a6212SJordan K. Hubbard } 630952a6212SJordan K. Hubbard } 631499d3ffaSBoris Popov 632499d3ffaSBoris Popov clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 633499d3ffaSBoris Popov if (pmp->pm_maxcluster >= clusters) { 634499d3ffaSBoris Popov printf("Warning: number of clusters (%ld) exceeds FAT " 635499d3ffaSBoris Popov "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 636499d3ffaSBoris Popov pmp->pm_maxcluster = clusters - 1; 637499d3ffaSBoris Popov } 638499d3ffaSBoris Popov 639952a6212SJordan K. Hubbard if (FAT12(pmp)) 64093fe42b6SBruce Evans pmp->pm_fatblocksize = 3 * 512; 64127a0bc89SDoug Rabson else 64293fe42b6SBruce Evans pmp->pm_fatblocksize = PAGE_SIZE; 64393fe42b6SBruce Evans pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize, 64493fe42b6SBruce Evans pmp->pm_BytesPerSec); 64501f6cfbaSYoshihiro Takahashi pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 64601f6cfbaSYoshihiro Takahashi pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 64727a0bc89SDoug Rabson 64827a0bc89SDoug Rabson /* 64927a0bc89SDoug Rabson * Compute mask and shift value for isolating cluster relative byte 65027a0bc89SDoug Rabson * offsets and cluster numbers from a file offset. 65127a0bc89SDoug Rabson */ 65201f6cfbaSYoshihiro Takahashi pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 653952a6212SJordan K. Hubbard pmp->pm_crbomask = pmp->pm_bpcluster - 1; 654952a6212SJordan K. Hubbard pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 65527a0bc89SDoug Rabson 656952a6212SJordan K. Hubbard /* 657952a6212SJordan K. Hubbard * Check for valid cluster size 658952a6212SJordan K. Hubbard * must be a power of 2 659952a6212SJordan K. Hubbard */ 660952a6212SJordan K. Hubbard if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 661952a6212SJordan K. Hubbard error = EINVAL; 662952a6212SJordan K. Hubbard goto error_exit; 663ad63a118SSatoshi Asami } 66427a0bc89SDoug Rabson 66527a0bc89SDoug Rabson /* 66627a0bc89SDoug Rabson * Release the bootsector buffer. 66727a0bc89SDoug Rabson */ 668952a6212SJordan K. Hubbard brelse(bp); 669952a6212SJordan K. Hubbard bp = NULL; 670952a6212SJordan K. Hubbard 671952a6212SJordan K. Hubbard /* 6728d61a735SBruce Evans * Check the fsinfo sector if we have one. Silently fix up our 6738d61a735SBruce Evans * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff) 6748d61a735SBruce Evans * or too large. Ignore fp->fsinfree for now, since we need to 6758d61a735SBruce Evans * read the entire FAT anyway to fill the inuse map. 676952a6212SJordan K. Hubbard */ 677952a6212SJordan K. Hubbard if (pmp->pm_fsinfo) { 678952a6212SJordan K. Hubbard struct fsinfo *fp; 679952a6212SJordan K. Hubbard 68037269429SBruce Evans if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 68101f6cfbaSYoshihiro Takahashi NOCRED, &bp)) != 0) 682952a6212SJordan K. Hubbard goto error_exit; 683952a6212SJordan K. Hubbard fp = (struct fsinfo *)bp->b_data; 684952a6212SJordan K. Hubbard if (!bcmp(fp->fsisig1, "RRaA", 4) 685952a6212SJordan K. Hubbard && !bcmp(fp->fsisig2, "rrAa", 4) 686fd7c4230SBruce Evans && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) { 687952a6212SJordan K. Hubbard pmp->pm_nxtfree = getulong(fp->fsinxtfree); 6888d61a735SBruce Evans if (pmp->pm_nxtfree > pmp->pm_maxcluster) 6898bb386f2STim J. Robbins pmp->pm_nxtfree = CLUST_FIRST; 6908bb386f2STim J. Robbins } else 691952a6212SJordan K. Hubbard pmp->pm_fsinfo = 0; 692952a6212SJordan K. Hubbard brelse(bp); 693952a6212SJordan K. Hubbard bp = NULL; 694952a6212SJordan K. Hubbard } 695952a6212SJordan K. Hubbard 696952a6212SJordan K. Hubbard /* 6978d61a735SBruce Evans * Finish initializing pmp->pm_nxtfree (just in case the first few 6988d61a735SBruce Evans * sectors aren't properly reserved in the FAT). This completes 6998d61a735SBruce Evans * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 7008d61a735SBruce Evans * value if there is no fsinfo. We will use pmp->pm_nxtfree 7018d61a735SBruce Evans * internally even if there is no fsinfo. 702952a6212SJordan K. Hubbard */ 7038d61a735SBruce Evans if (pmp->pm_nxtfree < CLUST_FIRST) 7048d61a735SBruce Evans pmp->pm_nxtfree = CLUST_FIRST; 70527a0bc89SDoug Rabson 70627a0bc89SDoug Rabson /* 70727a0bc89SDoug Rabson * Allocate memory for the bitmap of allocated clusters, and then 70827a0bc89SDoug Rabson * fill it in. 70927a0bc89SDoug Rabson */ 7100ef0dd6fSBruce Evans pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS) 71127a0bc89SDoug Rabson * sizeof(*pmp->pm_inusemap), 712a163d034SWarner Losh M_MSDOSFSFAT, M_WAITOK); 71327a0bc89SDoug Rabson 71427a0bc89SDoug Rabson /* 71527a0bc89SDoug Rabson * fillinusemap() needs pm_devvp. 71627a0bc89SDoug Rabson */ 71727a0bc89SDoug Rabson pmp->pm_devvp = devvp; 718c72ae142SJohn Baldwin pmp->pm_dev = dev; 71927a0bc89SDoug Rabson 72027a0bc89SDoug Rabson /* 72127a0bc89SDoug Rabson * Have the inuse map filled in. 72227a0bc89SDoug Rabson */ 7236be1a4ccSKonstantin Belousov MSDOSFS_LOCK_MP(pmp); 7246be1a4ccSKonstantin Belousov error = fillinusemap(pmp); 7256be1a4ccSKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp); 7266be1a4ccSKonstantin Belousov if (error != 0) 72727a0bc89SDoug Rabson goto error_exit; 72827a0bc89SDoug Rabson 72927a0bc89SDoug Rabson /* 73027a0bc89SDoug Rabson * If they want fat updates to be synchronous then let them suffer 73127a0bc89SDoug Rabson * the performance degradation in exchange for the on disk copy of 73227a0bc89SDoug Rabson * the fat being correct just about all the time. I suppose this 73327a0bc89SDoug Rabson * would be a good thing to turn on if the kernel is still flakey. 73427a0bc89SDoug Rabson */ 735952a6212SJordan K. Hubbard if (mp->mnt_flag & MNT_SYNCHRONOUS) 736952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 73727a0bc89SDoug Rabson 73827a0bc89SDoug Rabson /* 73927a0bc89SDoug Rabson * Finish up. 74027a0bc89SDoug Rabson */ 741952a6212SJordan K. Hubbard if (ronly) 742952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_RONLY; 743cede1f56STom Rhodes else { 7443247c9ddSXin LI if ((error = markvoldirty(pmp, 1)) != 0) { 7453247c9ddSXin LI (void)markvoldirty(pmp, 0); 746cede1f56STom Rhodes goto error_exit; 7473247c9ddSXin LI } 74827a0bc89SDoug Rabson pmp->pm_fmod = 1; 749cede1f56STom Rhodes } 75077465d93SAlfred Perlstein mp->mnt_data = pmp; 751939cb752SBruce Evans mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 752af3f60d5SBruce Evans mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 7535da56ddbSTor Egge MNT_ILOCK(mp); 754cc9d8990SPeter Wemm mp->mnt_flag |= MNT_LOCAL; 7555da56ddbSTor Egge MNT_IUNLOCK(mp); 75627a0bc89SDoug Rabson 757f458f2a5SCraig Rodrigues if (pmp->pm_flags & MSDOSFS_LARGEFS) 7583bc482ecSTim J. Robbins msdosfs_fileno_init(mp); 7593bc482ecSTim J. Robbins 76027a0bc89SDoug Rabson return 0; 76127a0bc89SDoug Rabson 762952a6212SJordan K. Hubbard error_exit: 763952a6212SJordan K. Hubbard if (bp) 764952a6212SJordan K. Hubbard brelse(bp); 7659a135592SPoul-Henning Kamp if (cp != NULL) { 7669a135592SPoul-Henning Kamp DROP_GIANT(); 7679a135592SPoul-Henning Kamp g_topology_lock(); 7680d7935fdSAttilio Rao g_vfs_close(cp); 7699a135592SPoul-Henning Kamp g_topology_unlock(); 7709a135592SPoul-Henning Kamp PICKUP_GIANT(); 7719a135592SPoul-Henning Kamp } 77223b6c230SKonstantin Belousov lockdestroy(&pmp->pm_fatlock); 77327a0bc89SDoug Rabson if (pmp) { 77427a0bc89SDoug Rabson if (pmp->pm_inusemap) 775952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 776952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 77777465d93SAlfred Perlstein mp->mnt_data = NULL; 77827a0bc89SDoug Rabson } 779c72ae142SJohn Baldwin dev_rel(dev); 780952a6212SJordan K. Hubbard return (error); 78127a0bc89SDoug Rabson } 78227a0bc89SDoug Rabson 78327a0bc89SDoug Rabson /* 78427a0bc89SDoug Rabson * Unmount the filesystem described by mp. 78527a0bc89SDoug Rabson */ 7867fefffeeSPoul-Henning Kamp static int 787dfd233edSAttilio Rao msdosfs_unmount(struct mount *mp, int mntflags) 78827a0bc89SDoug Rabson { 789952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 790952a6212SJordan K. Hubbard int error, flags; 79127a0bc89SDoug Rabson 792952a6212SJordan K. Hubbard flags = 0; 793952a6212SJordan K. Hubbard if (mntflags & MNT_FORCE) 79427a0bc89SDoug Rabson flags |= FORCECLOSE; 795dfd233edSAttilio Rao error = vflush(mp, 0, flags, curthread); 7964f560d75SEdward Tomasz Napierala if (error && error != ENXIO) 79727a0bc89SDoug Rabson return error; 798952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 7993247c9ddSXin LI if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) { 8003247c9ddSXin LI error = markvoldirty(pmp, 0); 8014f560d75SEdward Tomasz Napierala if (error && error != ENXIO) { 8023247c9ddSXin LI (void)markvoldirty(pmp, 1); 8033247c9ddSXin LI return (error); 8043247c9ddSXin LI } 8053247c9ddSXin LI } 806c4f02a89SMax Khon if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 807c4f02a89SMax Khon if (pmp->pm_w2u) 808c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_w2u); 809c4f02a89SMax Khon if (pmp->pm_u2w) 810c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2w); 811c4f02a89SMax Khon if (pmp->pm_d2u) 812c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_d2u); 813c4f02a89SMax Khon if (pmp->pm_u2d) 814c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2d); 815c4f02a89SMax Khon } 816cede1f56STom Rhodes 817952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG 818952a6212SJordan K. Hubbard { 819952a6212SJordan K. Hubbard struct vnode *vp = pmp->pm_devvp; 820698b1a66SJeff Roberson struct bufobj *bo; 821952a6212SJordan K. Hubbard 822698b1a66SJeff Roberson bo = &vp->v_bufobj; 823698b1a66SJeff Roberson BO_LOCK(bo); 8244d93c0beSJeff Roberson VI_LOCK(vp); 825f69d42a1SPoul-Henning Kamp vn_printf(vp, 826f69d42a1SPoul-Henning Kamp "msdosfs_umount(): just before calling VOP_CLOSE()\n"); 827952a6212SJordan K. Hubbard printf("freef %p, freeb %p, mount %p\n", 828fc2ffbe6SPoul-Henning Kamp TAILQ_NEXT(vp, v_freelist), vp->v_freelist.tqe_prev, 829952a6212SJordan K. Hubbard vp->v_mount); 830952a6212SJordan K. Hubbard printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", 831156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd), 832156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd), 833156cb265SPoul-Henning Kamp vp->v_bufobj.bo_numoutput, vp->v_type); 8344d93c0beSJeff Roberson VI_UNLOCK(vp); 835698b1a66SJeff Roberson BO_UNLOCK(bo); 836952a6212SJordan K. Hubbard } 837952a6212SJordan K. Hubbard #endif 8389a135592SPoul-Henning Kamp DROP_GIANT(); 8399a135592SPoul-Henning Kamp g_topology_lock(); 8400d7935fdSAttilio Rao g_vfs_close(pmp->pm_cp); 8419a135592SPoul-Henning Kamp g_topology_unlock(); 8429a135592SPoul-Henning Kamp PICKUP_GIANT(); 84327a0bc89SDoug Rabson vrele(pmp->pm_devvp); 844c72ae142SJohn Baldwin dev_rel(pmp->pm_dev); 845952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 8463247c9ddSXin LI if (pmp->pm_flags & MSDOSFS_LARGEFS) 8473bc482ecSTim J. Robbins msdosfs_fileno_free(mp); 84823b6c230SKonstantin Belousov lockdestroy(&pmp->pm_fatlock); 849952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 85077465d93SAlfred Perlstein mp->mnt_data = NULL; 8515da56ddbSTor Egge MNT_ILOCK(mp); 852cc9d8990SPeter Wemm mp->mnt_flag &= ~MNT_LOCAL; 8535da56ddbSTor Egge MNT_IUNLOCK(mp); 8544f560d75SEdward Tomasz Napierala return (error); 85527a0bc89SDoug Rabson } 85627a0bc89SDoug Rabson 8577fefffeeSPoul-Henning Kamp static int 858dfd233edSAttilio Rao msdosfs_root(struct mount *mp, int flags, struct vnode **vpp) 85927a0bc89SDoug Rabson { 860952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 86127a0bc89SDoug Rabson struct denode *ndep; 86227a0bc89SDoug Rabson int error; 86327a0bc89SDoug Rabson 86427a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 865952a6212SJordan K. Hubbard printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 86627a0bc89SDoug Rabson #endif 867952a6212SJordan K. Hubbard error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 868952a6212SJordan K. Hubbard if (error) 869952a6212SJordan K. Hubbard return (error); 87027a0bc89SDoug Rabson *vpp = DETOV(ndep); 871952a6212SJordan K. Hubbard return (0); 87227a0bc89SDoug Rabson } 87327a0bc89SDoug Rabson 8747fefffeeSPoul-Henning Kamp static int 875dfd233edSAttilio Rao msdosfs_statfs(struct mount *mp, struct statfs *sbp) 87627a0bc89SDoug Rabson { 877952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 87827a0bc89SDoug Rabson 879952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 88027a0bc89SDoug Rabson sbp->f_bsize = pmp->pm_bpcluster; 88127a0bc89SDoug Rabson sbp->f_iosize = pmp->pm_bpcluster; 882499d3ffaSBoris Popov sbp->f_blocks = pmp->pm_maxcluster + 1; 88327a0bc89SDoug Rabson sbp->f_bfree = pmp->pm_freeclustercount; 88427a0bc89SDoug Rabson sbp->f_bavail = pmp->pm_freeclustercount; 88527a0bc89SDoug Rabson sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 88627a0bc89SDoug Rabson sbp->f_ffree = 0; /* what to put in here? */ 887952a6212SJordan K. Hubbard return (0); 88827a0bc89SDoug Rabson } 88927a0bc89SDoug Rabson 8907fefffeeSPoul-Henning Kamp static int 891dfd233edSAttilio Rao msdosfs_sync(struct mount *mp, int waitfor) 89227a0bc89SDoug Rabson { 893952a6212SJordan K. Hubbard struct vnode *vp, *nvp; 894dfd233edSAttilio Rao struct thread *td; 89527a0bc89SDoug Rabson struct denode *dep; 896952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 897952a6212SJordan K. Hubbard int error, allerror = 0; 89827a0bc89SDoug Rabson 899dfd233edSAttilio Rao td = curthread; 900dfd233edSAttilio Rao 90127a0bc89SDoug Rabson /* 90227a0bc89SDoug Rabson * If we ever switch to not updating all of the fats all the time, 90327a0bc89SDoug Rabson * this would be the place to update them from the first one. 90427a0bc89SDoug Rabson */ 905dfd5dee1SPeter Wemm if (pmp->pm_fmod != 0) { 906952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_RONLY) 90727a0bc89SDoug Rabson panic("msdosfs_sync: rofs mod"); 90827a0bc89SDoug Rabson else { 90927a0bc89SDoug Rabson /* update fats here */ 91027a0bc89SDoug Rabson } 911dfd5dee1SPeter Wemm } 91227a0bc89SDoug Rabson /* 913952a6212SJordan K. Hubbard * Write back each (modified) denode. 91427a0bc89SDoug Rabson */ 915ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 91627a0bc89SDoug Rabson loop: 917e3c5a7a4SPoul-Henning Kamp MNT_VNODE_FOREACH(vp, mp, nvp) { 9184d93c0beSJeff Roberson VI_LOCK(vp); 9198da00465SJeff Roberson if (vp->v_type == VNON || (vp->v_iflag & VI_DOOMED)) { 9204ab2c8bdSJeff Roberson VI_UNLOCK(vp); 9214ab2c8bdSJeff Roberson continue; 9224ab2c8bdSJeff Roberson } 923ca430f2eSAlexander Kabaev MNT_IUNLOCK(mp); 92427a0bc89SDoug Rabson dep = VTODE(vp); 925f00f5d71SPoul-Henning Kamp if ((dep->de_flag & 926c681be37SDmitrij Tejblum (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 927156cb265SPoul-Henning Kamp (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 928f00f5d71SPoul-Henning Kamp waitfor == MNT_LAZY)) { 9294d93c0beSJeff Roberson VI_UNLOCK(vp); 930ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 93127a0bc89SDoug Rabson continue; 932af3f60d5SBruce Evans } 933b40ce416SJulian Elischer error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 934af3f60d5SBruce Evans if (error) { 935ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 936af3f60d5SBruce Evans if (error == ENOENT) 93727a0bc89SDoug Rabson goto loop; 938af3f60d5SBruce Evans continue; 939af3f60d5SBruce Evans } 9408df6bac4SPoul-Henning Kamp error = VOP_FSYNC(vp, waitfor, td); 941c3c6d51eSPoul-Henning Kamp if (error) 94227a0bc89SDoug Rabson allerror = error; 94322db15c0SAttilio Rao VOP_UNLOCK(vp, 0); 944cb9ddc80SAlexander Kabaev vrele(vp); 945ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 94627a0bc89SDoug Rabson } 947ca430f2eSAlexander Kabaev MNT_IUNLOCK(mp); 94827a0bc89SDoug Rabson 94927a0bc89SDoug Rabson /* 95027a0bc89SDoug Rabson * Flush filesystem control info. 95127a0bc89SDoug Rabson */ 952c681be37SDmitrij Tejblum if (waitfor != MNT_LAZY) { 953cb05b60aSAttilio Rao vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 9548df6bac4SPoul-Henning Kamp error = VOP_FSYNC(pmp->pm_devvp, waitfor, td); 955c3c6d51eSPoul-Henning Kamp if (error) 95627a0bc89SDoug Rabson allerror = error; 95722db15c0SAttilio Rao VOP_UNLOCK(pmp->pm_devvp, 0); 958c681be37SDmitrij Tejblum } 959952a6212SJordan K. Hubbard return (allerror); 96027a0bc89SDoug Rabson } 96127a0bc89SDoug Rabson 9621be5bc74STom Rhodes static int 9631be5bc74STom Rhodes msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 9641be5bc74STom Rhodes { 9651be5bc74STom Rhodes struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 9661be5bc74STom Rhodes struct defid *defhp = (struct defid *) fhp; 9671be5bc74STom Rhodes struct denode *dep; 9681be5bc74STom Rhodes int error; 9691be5bc74STom Rhodes 9701be5bc74STom Rhodes error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); 9711be5bc74STom Rhodes if (error) { 9721be5bc74STom Rhodes *vpp = NULLVP; 9731be5bc74STom Rhodes return (error); 9741be5bc74STom Rhodes } 9751be5bc74STom Rhodes *vpp = DETOV(dep); 9761be5bc74STom Rhodes vnode_create_vobject(*vpp, dep->de_FileSize, curthread); 9771be5bc74STom Rhodes return (0); 9781be5bc74STom Rhodes } 9791be5bc74STom Rhodes 98030ffadf3SPoul-Henning Kamp static struct vfsops msdosfs_vfsops = { 9811be5bc74STom Rhodes .vfs_fhtovp = msdosfs_fhtovp, 9826a4b48f4SPoul-Henning Kamp .vfs_mount = msdosfs_mount, 9836a4b48f4SPoul-Henning Kamp .vfs_cmount = msdosfs_cmount, 9847652131bSPoul-Henning Kamp .vfs_root = msdosfs_root, 9857652131bSPoul-Henning Kamp .vfs_statfs = msdosfs_statfs, 9867652131bSPoul-Henning Kamp .vfs_sync = msdosfs_sync, 9877652131bSPoul-Henning Kamp .vfs_unmount = msdosfs_unmount, 98827a0bc89SDoug Rabson }; 989c901836cSGarrett Wollman 9904ccd7546SRuslan Ermilov VFS_SET(msdosfs_vfsops, msdosfs, 0); 991c4f02a89SMax Khon MODULE_VERSION(msdosfs, 1); 992