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 /*- 5d63027b6SPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause 6d63027b6SPedro F. Giffuni * 7952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 8952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 927a0bc89SDoug Rabson * All rights reserved. 1027a0bc89SDoug Rabson * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 1127a0bc89SDoug Rabson * 1227a0bc89SDoug Rabson * Redistribution and use in source and binary forms, with or without 1327a0bc89SDoug Rabson * modification, are permitted provided that the following conditions 1427a0bc89SDoug Rabson * are met: 1527a0bc89SDoug Rabson * 1. Redistributions of source code must retain the above copyright 1627a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer. 1727a0bc89SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 1827a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer in the 1927a0bc89SDoug Rabson * documentation and/or other materials provided with the distribution. 2027a0bc89SDoug Rabson * 3. All advertising materials mentioning features or use of this software 2127a0bc89SDoug Rabson * must display the following acknowledgement: 2227a0bc89SDoug Rabson * This product includes software developed by TooLs GmbH. 2327a0bc89SDoug Rabson * 4. The name of TooLs GmbH may not be used to endorse or promote products 2427a0bc89SDoug Rabson * derived from this software without specific prior written permission. 2527a0bc89SDoug Rabson * 2627a0bc89SDoug Rabson * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2727a0bc89SDoug Rabson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2827a0bc89SDoug Rabson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2927a0bc89SDoug Rabson * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3027a0bc89SDoug Rabson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 3127a0bc89SDoug Rabson * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 3227a0bc89SDoug Rabson * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3327a0bc89SDoug Rabson * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3427a0bc89SDoug Rabson * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3527a0bc89SDoug Rabson * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3627a0bc89SDoug Rabson */ 37d167cf6fSWarner Losh /*- 3827a0bc89SDoug Rabson * Written by Paul Popelka (paulp@uts.amdahl.com) 3927a0bc89SDoug Rabson * 4027a0bc89SDoug Rabson * You can do anything you want with this software, just don't say you wrote 4127a0bc89SDoug Rabson * it, and don't remove this notice. 4227a0bc89SDoug Rabson * 4327a0bc89SDoug Rabson * This software is provided "as is". 4427a0bc89SDoug Rabson * 4527a0bc89SDoug Rabson * The author supplies this software to be publicly redistributed on the 4627a0bc89SDoug Rabson * understanding that the author is not responsible for the correct 4727a0bc89SDoug Rabson * functioning of this software in any circumstances and is not liable for 4827a0bc89SDoug Rabson * any damages caused by this software. 4927a0bc89SDoug Rabson * 5027a0bc89SDoug Rabson * October 1992 5127a0bc89SDoug Rabson */ 5227a0bc89SDoug Rabson 5327a0bc89SDoug Rabson #include <sys/param.h> 5427a0bc89SDoug Rabson #include <sys/systm.h> 556becd1c8SBruce Evans #include <sys/buf.h> 5665baf8f0SBruce Evans #include <sys/conf.h> 5757b4252eSKonstantin Belousov #include <sys/fcntl.h> 586becd1c8SBruce Evans #include <sys/iconv.h> 596becd1c8SBruce Evans #include <sys/kernel.h> 601103771dSBruce Evans #include <sys/lock.h> 616becd1c8SBruce Evans #include <sys/malloc.h> 626becd1c8SBruce Evans #include <sys/mount.h> 631103771dSBruce Evans #include <sys/mutex.h> 6427a0bc89SDoug Rabson #include <sys/namei.h> 65acd3428bSRobert Watson #include <sys/priv.h> 6627a0bc89SDoug Rabson #include <sys/proc.h> 676becd1c8SBruce Evans #include <sys/stat.h> 6827a0bc89SDoug Rabson #include <sys/vnode.h> 6927a0bc89SDoug Rabson 709a135592SPoul-Henning Kamp #include <geom/geom.h> 719a135592SPoul-Henning Kamp #include <geom/geom_vfs.h> 729a135592SPoul-Henning Kamp 736becd1c8SBruce Evans #include <fs/msdosfs/bootsect.h> 746becd1c8SBruce Evans #include <fs/msdosfs/bpb.h> 756becd1c8SBruce Evans #include <fs/msdosfs/direntry.h> 766becd1c8SBruce Evans #include <fs/msdosfs/denode.h> 776becd1c8SBruce Evans #include <fs/msdosfs/fat.h> 786becd1c8SBruce Evans #include <fs/msdosfs/msdosfsmount.h> 796becd1c8SBruce Evans 80027bebe8SEd Maste #ifdef MSDOSFS_DEBUG 81027bebe8SEd Maste #include <sys/rwlock.h> 82027bebe8SEd Maste #endif 83027bebe8SEd Maste 8423b6c230SKonstantin Belousov static const char msdosfs_lock_msg[] = "fatlk"; 8523b6c230SKonstantin Belousov 867c3fc9deSBruce Evans /* Mount options that we support. */ 876a4b48f4SPoul-Henning Kamp static const char *msdosfs_opts[] = { 88cb65c1eeSBruce Evans "async", "noatime", "noclusterr", "noclusterw", 897c3fc9deSBruce Evans "export", "force", "from", "sync", 907c3fc9deSBruce Evans "cs_dos", "cs_local", "cs_win", "dirmask", 9140373cf5SKonstantin Belousov "gid", "kiconv", "longname", 927c3fc9deSBruce Evans "longnames", "mask", "shortname", "shortnames", 937c3fc9deSBruce Evans "uid", "win95", "nowin95", 946a4b48f4SPoul-Henning Kamp NULL 956a4b48f4SPoul-Henning Kamp }; 966a4b48f4SPoul-Henning Kamp 9701f6cfbaSYoshihiro Takahashi #if 1 /*def PC98*/ 9801f6cfbaSYoshihiro Takahashi /* 9901f6cfbaSYoshihiro Takahashi * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 10001f6cfbaSYoshihiro Takahashi * garbage or a random value :-{ 10101f6cfbaSYoshihiro Takahashi * If you want to use that broken-signatured media, define the 10201f6cfbaSYoshihiro Takahashi * following symbol even though PC/AT. 10301f6cfbaSYoshihiro Takahashi * (ex. mount PC-98 DOS formatted FD on PC/AT) 10401f6cfbaSYoshihiro Takahashi */ 10501f6cfbaSYoshihiro Takahashi #define MSDOSFS_NOCHECKSIG 10601f6cfbaSYoshihiro Takahashi #endif 10701f6cfbaSYoshihiro Takahashi 1085bb84bc8SRobert Watson MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure"); 1095bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table"); 11055166637SPoul-Henning Kamp 1112d7c6b27SBruce Evans struct iconv_functions *msdosfs_iconv; 112c4f02a89SMax Khon 1136a4b48f4SPoul-Henning Kamp static int update_mp(struct mount *mp, struct thread *td); 1140d7935fdSAttilio Rao static int mountmsdosfs(struct vnode *devvp, struct mount *mp); 1151be5bc74STom Rhodes static vfs_fhtovp_t msdosfs_fhtovp; 1166a4b48f4SPoul-Henning Kamp static vfs_mount_t msdosfs_mount; 1179bf1a756SPoul-Henning Kamp static vfs_root_t msdosfs_root; 1189bf1a756SPoul-Henning Kamp static vfs_statfs_t msdosfs_statfs; 1199bf1a756SPoul-Henning Kamp static vfs_sync_t msdosfs_sync; 1209bf1a756SPoul-Henning Kamp static vfs_unmount_t msdosfs_unmount; 121af482601SBruce Evans 1221a9415afSTim J. Robbins /* Maximum length of a character set name (arbitrary). */ 1231a9415afSTim J. Robbins #define MAXCSLEN 64 1241a9415afSTim J. Robbins 125952a6212SJordan K. Hubbard static int 126dc9a617aSCraig Rodrigues update_mp(struct mount *mp, struct thread *td) 127952a6212SJordan K. Hubbard { 128952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1296a4b48f4SPoul-Henning Kamp void *dos, *win, *local; 1306a4b48f4SPoul-Henning Kamp int error, v; 131952a6212SJordan K. Hubbard 1326a4b48f4SPoul-Henning Kamp if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) { 1336a4b48f4SPoul-Henning Kamp if (msdosfs_iconv != NULL) { 1346a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1356a4b48f4SPoul-Henning Kamp "cs_win", &win, NULL); 1366a4b48f4SPoul-Henning Kamp if (!error) 1376a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1386a4b48f4SPoul-Henning Kamp "cs_local", &local, NULL); 1396a4b48f4SPoul-Henning Kamp if (!error) 1406a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1416a4b48f4SPoul-Henning Kamp "cs_dos", &dos, NULL); 1426a4b48f4SPoul-Henning Kamp if (!error) { 1431a9415afSTim J. Robbins msdosfs_iconv->open(win, local, &pmp->pm_u2w); 1441a9415afSTim J. Robbins msdosfs_iconv->open(local, win, &pmp->pm_w2u); 1451a9415afSTim J. Robbins msdosfs_iconv->open(dos, local, &pmp->pm_u2d); 1461a9415afSTim J. Robbins msdosfs_iconv->open(local, dos, &pmp->pm_d2u); 1476a4b48f4SPoul-Henning Kamp } 1481a9415afSTim J. Robbins if (error != 0) 1491a9415afSTim J. Robbins return (error); 150c4f02a89SMax Khon } else { 151c4f02a89SMax Khon pmp->pm_w2u = NULL; 152c4f02a89SMax Khon pmp->pm_u2w = NULL; 153c4f02a89SMax Khon pmp->pm_d2u = NULL; 154c4f02a89SMax Khon pmp->pm_u2d = NULL; 1557391f611SAndrey A. Chernov } 1561a9415afSTim J. Robbins } 1571a9415afSTim J. Robbins 158dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1) 1596a4b48f4SPoul-Henning Kamp pmp->pm_gid = v; 160dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1) 1616a4b48f4SPoul-Henning Kamp pmp->pm_uid = v; 162dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v) == 1) 1636a4b48f4SPoul-Henning Kamp pmp->pm_mask = v & ALLPERMS; 164dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v) == 1) 1656a4b48f4SPoul-Henning Kamp pmp->pm_dirmask = v & ALLPERMS; 1666a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "shortname", 1676a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1684ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "shortnames", 1694ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1706a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "longname", 1716a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1724ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "longnames", 1734ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1746a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "kiconv", 1756a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_KICONV); 1766a4b48f4SPoul-Henning Kamp 177d75b2048SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0) 1786a4b48f4SPoul-Henning Kamp pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 179d75b2048SCraig Rodrigues else 180d75b2048SCraig Rodrigues pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95; 181952a6212SJordan K. Hubbard 182952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 183952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 1840696afbeSAlan Somers else 185952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 186952a6212SJordan K. Hubbard return 0; 187952a6212SJordan K. Hubbard } 188952a6212SJordan K. Hubbard 1896a4b48f4SPoul-Henning Kamp static int 190cc672d35SKirk McKusick msdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 1916a4b48f4SPoul-Henning Kamp { 1926a4b48f4SPoul-Henning Kamp struct msdosfs_args args; 1936a4b48f4SPoul-Henning Kamp int error; 1946a4b48f4SPoul-Henning Kamp 1956a4b48f4SPoul-Henning Kamp if (data == NULL) 1966a4b48f4SPoul-Henning Kamp return (EINVAL); 1976a4b48f4SPoul-Henning Kamp error = copyin(data, &args, sizeof args); 1986a4b48f4SPoul-Henning Kamp if (error) 1996a4b48f4SPoul-Henning Kamp return (error); 2006a4b48f4SPoul-Henning Kamp 2016a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); 2021f7104d7SRick Macklem ma = mount_arg(ma, "export", &args.export, sizeof(args.export)); 2036a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "uid", "%d", args.uid); 2046a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "gid", "%d", args.gid); 2056a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "mask", "%d", args.mask); 2066a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "dirmask", "%d", args.dirmask); 2076a4b48f4SPoul-Henning Kamp 2086a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname"); 2096a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname"); 2106a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95"); 2116a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv"); 2126a4b48f4SPoul-Henning Kamp 2136a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN); 2146a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN); 2156a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN); 2166a4b48f4SPoul-Henning Kamp 2176a4b48f4SPoul-Henning Kamp error = kernel_mount(ma, flags); 2186a4b48f4SPoul-Henning Kamp 2196a4b48f4SPoul-Henning Kamp return (error); 2206a4b48f4SPoul-Henning Kamp } 2216a4b48f4SPoul-Henning Kamp 22227a0bc89SDoug Rabson /* 22327a0bc89SDoug Rabson * mp - path - addr in user space of mount point (ie /usr or whatever) 22427a0bc89SDoug Rabson * data - addr in user space of mount params including the name of the block 22527a0bc89SDoug Rabson * special file to treat as a filesystem. 22627a0bc89SDoug Rabson */ 2277fefffeeSPoul-Henning Kamp static int 228dfd233edSAttilio Rao msdosfs_mount(struct mount *mp) 22927a0bc89SDoug Rabson { 23027a0bc89SDoug Rabson struct vnode *devvp; /* vnode for blk device to mount */ 231dfd233edSAttilio Rao struct thread *td; 232952a6212SJordan K. Hubbard /* msdosfs specific mount control block */ 233952a6212SJordan K. Hubbard struct msdosfsmount *pmp = NULL; 2345e8c582aSPoul-Henning Kamp struct nameidata ndp; 235269c902fSPoul-Henning Kamp int error, flags; 23615bc6b2bSEdward Tomasz Napierala accmode_t accmode; 2376a4b48f4SPoul-Henning Kamp char *from; 23827a0bc89SDoug Rabson 239dfd233edSAttilio Rao td = curthread; 2406a4b48f4SPoul-Henning Kamp if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) 2416a4b48f4SPoul-Henning Kamp return (EINVAL); 2426a4b48f4SPoul-Henning Kamp 24327a0bc89SDoug Rabson /* 244952a6212SJordan K. Hubbard * If updating, check whether changing from read-only to 245952a6212SJordan K. Hubbard * read/write; if there is no device name, that's all we do. 24627a0bc89SDoug Rabson */ 24727a0bc89SDoug Rabson if (mp->mnt_flag & MNT_UPDATE) { 2482d7c6b27SBruce Evans pmp = VFSTOMSDOSFS(mp); 2499a135592SPoul-Henning Kamp if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 2506a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 251dfd233edSAttilio Rao error = VFS_SYNC(mp, MNT_WAIT); 252e83f1423SPoul-Henning Kamp if (error) 253e83f1423SPoul-Henning Kamp return (error); 25427a0bc89SDoug Rabson flags = WRITECLOSE; 25527a0bc89SDoug Rabson if (mp->mnt_flag & MNT_FORCE) 25627a0bc89SDoug Rabson flags |= FORCECLOSE; 257f257b7a5SAlfred Perlstein error = vflush(mp, 0, flags, td); 2586a4b48f4SPoul-Henning Kamp if (error) 2596a4b48f4SPoul-Henning Kamp return (error); 2603247c9ddSXin LI 2613247c9ddSXin LI /* 2623247c9ddSXin LI * Now the volume is clean. Mark it so while the 2633247c9ddSXin LI * device is still rw. 2643247c9ddSXin LI */ 2653247c9ddSXin LI error = markvoldirty(pmp, 0); 2663247c9ddSXin LI if (error) { 2673247c9ddSXin LI (void)markvoldirty(pmp, 1); 2683247c9ddSXin LI return (error); 2693247c9ddSXin LI } 2703247c9ddSXin LI 2713247c9ddSXin LI /* Downgrade the device from rw to ro. */ 2729a135592SPoul-Henning Kamp g_topology_lock(); 2734eb3abf0SBruce Evans error = g_access(pmp->pm_cp, 0, -1, 0); 2749a135592SPoul-Henning Kamp g_topology_unlock(); 2753247c9ddSXin LI if (error) { 2763247c9ddSXin LI (void)markvoldirty(pmp, 1); 2774eb3abf0SBruce Evans return (error); 2783247c9ddSXin LI } 2794eb3abf0SBruce Evans 2803247c9ddSXin LI /* 2813247c9ddSXin LI * Backing out after an error was painful in the 2823247c9ddSXin LI * above. Now we are committed to succeeding. 2833247c9ddSXin LI */ 2843247c9ddSXin LI pmp->pm_fmod = 0; 2853247c9ddSXin LI pmp->pm_flags |= MSDOSFSMNT_RONLY; 2863247c9ddSXin LI MNT_ILOCK(mp); 2873247c9ddSXin LI mp->mnt_flag |= MNT_RDONLY; 2883247c9ddSXin LI MNT_IUNLOCK(mp); 2896a4b48f4SPoul-Henning Kamp } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 2906a4b48f4SPoul-Henning Kamp !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 291952a6212SJordan K. Hubbard /* 292952a6212SJordan K. Hubbard * If upgrade to read-write by non-root, then verify 293952a6212SJordan K. Hubbard * that user has necessary permissions on the device. 294952a6212SJordan K. Hubbard */ 295952a6212SJordan K. Hubbard devvp = pmp->pm_devvp; 296cb05b60aSAttilio Rao vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 297952a6212SJordan K. Hubbard error = VOP_ACCESS(devvp, VREAD | VWRITE, 298a854ed98SJohn Baldwin td->td_ucred, td); 299acd3428bSRobert Watson if (error) 300acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 301952a6212SJordan K. Hubbard if (error) { 302b249ce48SMateusz Guzik VOP_UNLOCK(devvp); 303952a6212SJordan K. Hubbard return (error); 304952a6212SJordan K. Hubbard } 305b249ce48SMateusz Guzik VOP_UNLOCK(devvp); 3069a135592SPoul-Henning Kamp g_topology_lock(); 3079a135592SPoul-Henning Kamp error = g_access(pmp->pm_cp, 0, 1, 0); 3089a135592SPoul-Henning Kamp g_topology_unlock(); 3099a135592SPoul-Henning Kamp if (error) 3109a135592SPoul-Henning Kamp return (error); 311cede1f56STom Rhodes 312aaa38524SConrad Meyer /* Now that the volume is modifiable, mark it dirty. */ 313aaa38524SConrad Meyer error = markvoldirty_upgrade(pmp, true, true); 314aaa38524SConrad Meyer if (error) { 315aaa38524SConrad Meyer /* 316aaa38524SConrad Meyer * If dirtying the superblock failed, drop GEOM 317aaa38524SConrad Meyer * 'w' refs (we're still RO). 318aaa38524SConrad Meyer */ 319aaa38524SConrad Meyer g_topology_lock(); 320aaa38524SConrad Meyer (void)g_access(pmp->pm_cp, 0, -1, 0); 321aaa38524SConrad Meyer g_topology_unlock(); 322aaa38524SConrad Meyer 323aaa38524SConrad Meyer return (error); 324aaa38524SConrad Meyer } 325aaa38524SConrad Meyer 3263247c9ddSXin LI pmp->pm_fmod = 1; 3273247c9ddSXin LI pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 3283247c9ddSXin LI MNT_ILOCK(mp); 3293247c9ddSXin LI mp->mnt_flag &= ~MNT_RDONLY; 3303247c9ddSXin LI MNT_IUNLOCK(mp); 33182c59ec6SCraig Rodrigues } 332952a6212SJordan K. Hubbard } 33327a0bc89SDoug Rabson /* 334952a6212SJordan K. Hubbard * Not an update, or updating the name: look up the name 335e9827c6dSBruce Evans * and verify that it refers to a sensible disk device. 33627a0bc89SDoug Rabson */ 3376a4b48f4SPoul-Henning Kamp if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) 3386a4b48f4SPoul-Henning Kamp return (EINVAL); 33975d7ba93SSuleiman Souhlal NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); 3405e8c582aSPoul-Henning Kamp error = namei(&ndp); 341952a6212SJordan K. Hubbard if (error) 342952a6212SJordan K. Hubbard return (error); 3435e8c582aSPoul-Henning Kamp devvp = ndp.ni_vp; 3445e8c582aSPoul-Henning Kamp NDFREE(&ndp, NDF_ONLY_PNBUF); 345952a6212SJordan K. Hubbard 3467ad2a82dSMateusz Guzik if (!vn_isdisk_error(devvp, &error)) { 34775d7ba93SSuleiman Souhlal vput(devvp); 348ba4ad1fcSPoul-Henning Kamp return (error); 34927a0bc89SDoug Rabson } 35027a0bc89SDoug Rabson /* 351952a6212SJordan K. Hubbard * If mount by non-root, then verify that user has necessary 352952a6212SJordan K. Hubbard * permissions on the device. 35327a0bc89SDoug Rabson */ 35415bc6b2bSEdward Tomasz Napierala accmode = VREAD; 355952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_RDONLY) == 0) 35615bc6b2bSEdward Tomasz Napierala accmode |= VWRITE; 35715bc6b2bSEdward Tomasz Napierala error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 358acd3428bSRobert Watson if (error) 359acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 360952a6212SJordan K. Hubbard if (error) { 361952a6212SJordan K. Hubbard vput(devvp); 362952a6212SJordan K. Hubbard return (error); 363952a6212SJordan K. Hubbard } 364952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_UPDATE) == 0) { 3650d7935fdSAttilio Rao error = mountmsdosfs(devvp, mp); 366952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 367952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 368952a6212SJordan K. Hubbard #endif 369952a6212SJordan K. Hubbard } else { 37075d7ba93SSuleiman Souhlal vput(devvp); 3716c0358ccSKonstantin Belousov if (devvp != pmp->pm_devvp) 3726c0358ccSKonstantin Belousov return (EINVAL); /* XXX needs translation */ 37327a0bc89SDoug Rabson } 37427a0bc89SDoug Rabson if (error) { 37527a0bc89SDoug Rabson vrele(devvp); 376952a6212SJordan K. Hubbard return (error); 377952a6212SJordan K. Hubbard } 378952a6212SJordan K. Hubbard 3796a4b48f4SPoul-Henning Kamp error = update_mp(mp, td); 380952a6212SJordan K. Hubbard if (error) { 3811a9415afSTim J. Robbins if ((mp->mnt_flag & MNT_UPDATE) == 0) 382dfd233edSAttilio Rao msdosfs_unmount(mp, MNT_FORCE); 38327a0bc89SDoug Rabson return error; 38427a0bc89SDoug Rabson } 3856a4b48f4SPoul-Henning Kamp 3866a4b48f4SPoul-Henning Kamp vfs_mountedfrom(mp, from); 38727a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 3886a4b48f4SPoul-Henning Kamp printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 38927a0bc89SDoug Rabson #endif 390952a6212SJordan K. Hubbard return (0); 39127a0bc89SDoug Rabson } 39227a0bc89SDoug Rabson 3937fefffeeSPoul-Henning Kamp static int 3940d7935fdSAttilio Rao mountmsdosfs(struct vnode *devvp, struct mount *mp) 39527a0bc89SDoug Rabson { 396952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 397952a6212SJordan K. Hubbard struct buf *bp; 398c72ae142SJohn Baldwin struct cdev *dev; 39927a0bc89SDoug Rabson union bootsector *bsp; 40027a0bc89SDoug Rabson struct byte_bpb33 *b33; 40127a0bc89SDoug Rabson struct byte_bpb50 *b50; 402952a6212SJordan K. Hubbard struct byte_bpb710 *b710; 40323c53312SEd Maste uint8_t SecPerClust; 404499d3ffaSBoris Popov u_long clusters; 405952a6212SJordan K. Hubbard int ronly, error; 4069a135592SPoul-Henning Kamp struct g_consumer *cp; 4079a135592SPoul-Henning Kamp struct bufobj *bo; 40827a0bc89SDoug Rabson 409c72ae142SJohn Baldwin bp = NULL; /* This and pmp both used in error_exit. */ 410c72ae142SJohn Baldwin pmp = NULL; 4114eb3abf0SBruce Evans ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 412c72ae142SJohn Baldwin 413c72ae142SJohn Baldwin dev = devvp->v_rdev; 414bb8297e6SKonstantin Belousov if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0, 415bb8297e6SKonstantin Belousov (uintptr_t)mp) == 0) { 416b249ce48SMateusz Guzik VOP_UNLOCK(devvp); 417bb8297e6SKonstantin Belousov return (EBUSY); 418bb8297e6SKonstantin Belousov } 4199a135592SPoul-Henning Kamp g_topology_lock(); 4204eb3abf0SBruce Evans error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1); 4219a135592SPoul-Henning Kamp g_topology_unlock(); 422bb8297e6SKonstantin Belousov if (error != 0) { 423bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); 424b249ce48SMateusz Guzik VOP_UNLOCK(devvp); 425bb8297e6SKonstantin Belousov return (error); 426bb8297e6SKonstantin Belousov } 427bb8297e6SKonstantin Belousov dev_ref(dev); 4289a135592SPoul-Henning Kamp bo = &devvp->v_bufobj; 429b249ce48SMateusz Guzik VOP_UNLOCK(devvp); 4308ec22c4dSBruce Evans if (dev->si_iosize_max != 0) 4318ec22c4dSBruce Evans mp->mnt_iosize_max = dev->si_iosize_max; 4328ec22c4dSBruce Evans if (mp->mnt_iosize_max > MAXPHYS) 4338ec22c4dSBruce Evans mp->mnt_iosize_max = MAXPHYS; 434952a6212SJordan K. Hubbard 43527a0bc89SDoug Rabson /* 436952a6212SJordan K. Hubbard * Read the boot sector of the filesystem, and then check the 437952a6212SJordan K. Hubbard * boot signature. If not a dos boot sector then error out. 43801f6cfbaSYoshihiro Takahashi * 43993fe42b6SBruce Evans * NOTE: 8192 is a magic size that works for ffs. 44027a0bc89SDoug Rabson */ 44193fe42b6SBruce Evans error = bread(devvp, 0, 8192, NOCRED, &bp); 442c3c6d51eSPoul-Henning Kamp if (error) 44327a0bc89SDoug Rabson goto error_exit; 444952a6212SJordan K. Hubbard bp->b_flags |= B_AGE; 445952a6212SJordan K. Hubbard bsp = (union bootsector *)bp->b_data; 44627a0bc89SDoug Rabson b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 44727a0bc89SDoug Rabson b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 44801a4d019SJohn Baldwin b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 449952a6212SJordan K. Hubbard 45001f6cfbaSYoshihiro Takahashi #ifndef MSDOSFS_NOCHECKSIG 451952a6212SJordan K. Hubbard if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 452952a6212SJordan K. Hubbard || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 45327a0bc89SDoug Rabson error = EINVAL; 45427a0bc89SDoug Rabson goto error_exit; 45527a0bc89SDoug Rabson } 45601f6cfbaSYoshihiro Takahashi #endif 45727a0bc89SDoug Rabson 458a163d034SWarner Losh pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 45927a0bc89SDoug Rabson pmp->pm_mountp = mp; 4609a135592SPoul-Henning Kamp pmp->pm_cp = cp; 4619a135592SPoul-Henning Kamp pmp->pm_bo = bo; 46227a0bc89SDoug Rabson 46323b6c230SKonstantin Belousov lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0); 46423b6c230SKonstantin Belousov 46527a0bc89SDoug Rabson /* 4664eb3abf0SBruce Evans * Initialize ownerships and permissions, since nothing else will 46723c1e989SMaxim Konovalov * initialize them iff we are mounting root. 4684eb3abf0SBruce Evans */ 4694eb3abf0SBruce Evans pmp->pm_uid = UID_ROOT; 4704eb3abf0SBruce Evans pmp->pm_gid = GID_WHEEL; 4714eb3abf0SBruce Evans pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | 4724eb3abf0SBruce Evans S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 4734eb3abf0SBruce Evans 4744eb3abf0SBruce Evans /* 47527a0bc89SDoug Rabson * Compute several useful quantities from the bpb in the 47627a0bc89SDoug Rabson * bootsector. Copy in the dos 5 variant of the bpb then fix up 47727a0bc89SDoug Rabson * the fields that are different between dos 5 and dos 3.3. 47827a0bc89SDoug Rabson */ 479952a6212SJordan K. Hubbard SecPerClust = b50->bpbSecPerClust; 48027a0bc89SDoug Rabson pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 481696f22f0SJohn Baldwin if (pmp->pm_BytesPerSec < DEV_BSIZE) { 482696f22f0SJohn Baldwin error = EINVAL; 483696f22f0SJohn Baldwin goto error_exit; 484696f22f0SJohn Baldwin } 48527a0bc89SDoug Rabson pmp->pm_ResSectors = getushort(b50->bpbResSectors); 48627a0bc89SDoug Rabson pmp->pm_FATs = b50->bpbFATs; 48727a0bc89SDoug Rabson pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 48827a0bc89SDoug Rabson pmp->pm_Sectors = getushort(b50->bpbSectors); 48927a0bc89SDoug Rabson pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 49027a0bc89SDoug Rabson pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 49127a0bc89SDoug Rabson pmp->pm_Heads = getushort(b50->bpbHeads); 492952a6212SJordan K. Hubbard pmp->pm_Media = b50->bpbMedia; 49327a0bc89SDoug Rabson 49401f6cfbaSYoshihiro Takahashi /* calculate the ratio of sector size to DEV_BSIZE */ 49501f6cfbaSYoshihiro Takahashi pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 49601f6cfbaSYoshihiro Takahashi 497043ec583SMarcel Moolenaar /* 498043ec583SMarcel Moolenaar * We don't check pm_Heads nor pm_SecPerTrack, because 499043ec583SMarcel Moolenaar * these may not be set for EFI file systems. We don't 500043ec583SMarcel Moolenaar * use these anyway, so we're unaffected if they are 501043ec583SMarcel Moolenaar * invalid. 502043ec583SMarcel Moolenaar */ 503043ec583SMarcel Moolenaar if (!pmp->pm_BytesPerSec || !SecPerClust) { 50427a0bc89SDoug Rabson error = EINVAL; 50527a0bc89SDoug Rabson goto error_exit; 50627a0bc89SDoug Rabson } 50727a0bc89SDoug Rabson 50827a0bc89SDoug Rabson if (pmp->pm_Sectors == 0) { 50927a0bc89SDoug Rabson pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 51027a0bc89SDoug Rabson pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 51127a0bc89SDoug Rabson } else { 51227a0bc89SDoug Rabson pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 51327a0bc89SDoug Rabson pmp->pm_HugeSectors = pmp->pm_Sectors; 51427a0bc89SDoug Rabson } 515952a6212SJordan K. Hubbard 516952a6212SJordan K. Hubbard if (pmp->pm_RootDirEnts == 0) { 51748efa33bSKonstantin Belousov if (pmp->pm_FATsecs 518952a6212SJordan K. Hubbard || getushort(b710->bpbFSVers)) { 519952a6212SJordan K. Hubbard error = EINVAL; 52054cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG 521aaf0bb19SAndrey A. Chernov printf("mountmsdosfs(): bad FAT32 filesystem\n"); 52254cf9198SKonstantin Belousov #endif 523952a6212SJordan K. Hubbard goto error_exit; 524952a6212SJordan K. Hubbard } 525952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT32_MASK; 526952a6212SJordan K. Hubbard pmp->pm_fatmult = 4; 527952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 528952a6212SJordan K. Hubbard pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 529952a6212SJordan K. Hubbard if (getushort(b710->bpbExtFlags) & FATMIRROR) 530952a6212SJordan K. Hubbard pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 531952a6212SJordan K. Hubbard else 532952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 533952a6212SJordan K. Hubbard } else 534952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 535952a6212SJordan K. Hubbard 536952a6212SJordan K. Hubbard /* 537952a6212SJordan K. Hubbard * Check a few values (could do some more): 538952a6212SJordan K. Hubbard * - logical sector size: power of 2, >= block size 539952a6212SJordan K. Hubbard * - sectors per cluster: power of 2, >= 1 540952a6212SJordan K. Hubbard * - number of sectors: >= 1, <= size of partition 541696f22f0SJohn Baldwin * - number of FAT sectors: >= 1 542952a6212SJordan K. Hubbard */ 543952a6212SJordan K. Hubbard if ( (SecPerClust == 0) 544952a6212SJordan K. Hubbard || (SecPerClust & (SecPerClust - 1)) 54501f6cfbaSYoshihiro Takahashi || (pmp->pm_BytesPerSec < DEV_BSIZE) 546952a6212SJordan K. Hubbard || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 547952a6212SJordan K. Hubbard || (pmp->pm_HugeSectors == 0) 548696f22f0SJohn Baldwin || (pmp->pm_FATsecs == 0) 549423b0fb7SAndriy Gapon || (SecPerClust * pmp->pm_BlkPerSec > MAXBSIZE / DEV_BSIZE) 550952a6212SJordan K. Hubbard ) { 551952a6212SJordan K. Hubbard error = EINVAL; 552952a6212SJordan K. Hubbard goto error_exit; 553952a6212SJordan K. Hubbard } 55401f6cfbaSYoshihiro Takahashi 55501f6cfbaSYoshihiro Takahashi pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 55601f6cfbaSYoshihiro Takahashi pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 55701f6cfbaSYoshihiro Takahashi pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 55801f6cfbaSYoshihiro Takahashi SecPerClust *= pmp->pm_BlkPerSec; 55901f6cfbaSYoshihiro Takahashi 56001f6cfbaSYoshihiro Takahashi pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 56101f6cfbaSYoshihiro Takahashi 562952a6212SJordan K. Hubbard if (FAT32(pmp)) { 563952a6212SJordan K. Hubbard pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 564952a6212SJordan K. Hubbard pmp->pm_firstcluster = pmp->pm_fatblk 565952a6212SJordan K. Hubbard + (pmp->pm_FATs * pmp->pm_FATsecs); 56601f6cfbaSYoshihiro Takahashi pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 567952a6212SJordan K. Hubbard } else { 56827a0bc89SDoug Rabson pmp->pm_rootdirblk = pmp->pm_fatblk + 56927a0bc89SDoug Rabson (pmp->pm_FATs * pmp->pm_FATsecs); 57055e0987aSPedro F. Giffuni pmp->pm_rootdirsize = howmany(pmp->pm_RootDirEnts * 57155e0987aSPedro F. Giffuni sizeof(struct direntry), DEV_BSIZE); /* in blocks */ 57227a0bc89SDoug Rabson pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 573952a6212SJordan K. Hubbard } 574952a6212SJordan K. Hubbard 575499d3ffaSBoris Popov pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 576499d3ffaSBoris Popov SecPerClust + 1; 57701f6cfbaSYoshihiro Takahashi pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 578952a6212SJordan K. Hubbard 579952a6212SJordan K. Hubbard if (pmp->pm_fatmask == 0) { 580952a6212SJordan K. Hubbard if (pmp->pm_maxcluster 581952a6212SJordan K. Hubbard <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 58227a0bc89SDoug Rabson /* 583952a6212SJordan K. Hubbard * This will usually be a floppy disk. This size makes 5849287dbaaSEd Maste * sure that one FAT entry will not be split across 585952a6212SJordan K. Hubbard * multiple blocks. 58627a0bc89SDoug Rabson */ 587952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT12_MASK; 588952a6212SJordan K. Hubbard pmp->pm_fatmult = 3; 589952a6212SJordan K. Hubbard pmp->pm_fatdiv = 2; 590952a6212SJordan K. Hubbard } else { 591952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT16_MASK; 592952a6212SJordan K. Hubbard pmp->pm_fatmult = 2; 593952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 594952a6212SJordan K. Hubbard } 595952a6212SJordan K. Hubbard } 596499d3ffaSBoris Popov 597499d3ffaSBoris Popov clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 598499d3ffaSBoris Popov if (pmp->pm_maxcluster >= clusters) { 59954cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG 600499d3ffaSBoris Popov printf("Warning: number of clusters (%ld) exceeds FAT " 601499d3ffaSBoris Popov "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 60254cf9198SKonstantin Belousov #endif 603499d3ffaSBoris Popov pmp->pm_maxcluster = clusters - 1; 604499d3ffaSBoris Popov } 605499d3ffaSBoris Popov 606952a6212SJordan K. Hubbard if (FAT12(pmp)) 60793fe42b6SBruce Evans pmp->pm_fatblocksize = 3 * 512; 60827a0bc89SDoug Rabson else 60993fe42b6SBruce Evans pmp->pm_fatblocksize = PAGE_SIZE; 61093fe42b6SBruce Evans pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize, 61193fe42b6SBruce Evans pmp->pm_BytesPerSec); 61201f6cfbaSYoshihiro Takahashi pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 61301f6cfbaSYoshihiro Takahashi pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 61427a0bc89SDoug Rabson 61527a0bc89SDoug Rabson /* 61627a0bc89SDoug Rabson * Compute mask and shift value for isolating cluster relative byte 61727a0bc89SDoug Rabson * offsets and cluster numbers from a file offset. 61827a0bc89SDoug Rabson */ 61901f6cfbaSYoshihiro Takahashi pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 620952a6212SJordan K. Hubbard pmp->pm_crbomask = pmp->pm_bpcluster - 1; 621952a6212SJordan K. Hubbard pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 62227a0bc89SDoug Rabson 623952a6212SJordan K. Hubbard /* 624952a6212SJordan K. Hubbard * Check for valid cluster size 625952a6212SJordan K. Hubbard * must be a power of 2 626952a6212SJordan K. Hubbard */ 627952a6212SJordan K. Hubbard if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 628952a6212SJordan K. Hubbard error = EINVAL; 629952a6212SJordan K. Hubbard goto error_exit; 630ad63a118SSatoshi Asami } 63127a0bc89SDoug Rabson 63227a0bc89SDoug Rabson /* 63327a0bc89SDoug Rabson * Release the bootsector buffer. 63427a0bc89SDoug Rabson */ 635952a6212SJordan K. Hubbard brelse(bp); 636952a6212SJordan K. Hubbard bp = NULL; 637952a6212SJordan K. Hubbard 638952a6212SJordan K. Hubbard /* 6398d61a735SBruce Evans * Check the fsinfo sector if we have one. Silently fix up our 6408d61a735SBruce Evans * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff) 6418d61a735SBruce Evans * or too large. Ignore fp->fsinfree for now, since we need to 6428d61a735SBruce Evans * read the entire FAT anyway to fill the inuse map. 643952a6212SJordan K. Hubbard */ 644952a6212SJordan K. Hubbard if (pmp->pm_fsinfo) { 645952a6212SJordan K. Hubbard struct fsinfo *fp; 646952a6212SJordan K. Hubbard 64737269429SBruce Evans if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 64801f6cfbaSYoshihiro Takahashi NOCRED, &bp)) != 0) 649952a6212SJordan K. Hubbard goto error_exit; 650952a6212SJordan K. Hubbard fp = (struct fsinfo *)bp->b_data; 651952a6212SJordan K. Hubbard if (!bcmp(fp->fsisig1, "RRaA", 4) 652952a6212SJordan K. Hubbard && !bcmp(fp->fsisig2, "rrAa", 4) 653fd7c4230SBruce Evans && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) { 654952a6212SJordan K. Hubbard pmp->pm_nxtfree = getulong(fp->fsinxtfree); 6558d61a735SBruce Evans if (pmp->pm_nxtfree > pmp->pm_maxcluster) 6568bb386f2STim J. Robbins pmp->pm_nxtfree = CLUST_FIRST; 6578bb386f2STim J. Robbins } else 658952a6212SJordan K. Hubbard pmp->pm_fsinfo = 0; 659952a6212SJordan K. Hubbard brelse(bp); 660952a6212SJordan K. Hubbard bp = NULL; 661952a6212SJordan K. Hubbard } 662952a6212SJordan K. Hubbard 663952a6212SJordan K. Hubbard /* 6648d61a735SBruce Evans * Finish initializing pmp->pm_nxtfree (just in case the first few 6658d61a735SBruce Evans * sectors aren't properly reserved in the FAT). This completes 6668d61a735SBruce Evans * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 6678d61a735SBruce Evans * value if there is no fsinfo. We will use pmp->pm_nxtfree 6688d61a735SBruce Evans * internally even if there is no fsinfo. 669952a6212SJordan K. Hubbard */ 6708d61a735SBruce Evans if (pmp->pm_nxtfree < CLUST_FIRST) 6718d61a735SBruce Evans pmp->pm_nxtfree = CLUST_FIRST; 67227a0bc89SDoug Rabson 67327a0bc89SDoug Rabson /* 67427a0bc89SDoug Rabson * Allocate memory for the bitmap of allocated clusters, and then 67527a0bc89SDoug Rabson * fill it in. 67627a0bc89SDoug Rabson */ 6770ef0dd6fSBruce Evans pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS) 67827a0bc89SDoug Rabson * sizeof(*pmp->pm_inusemap), 679a163d034SWarner Losh M_MSDOSFSFAT, M_WAITOK); 68027a0bc89SDoug Rabson 68127a0bc89SDoug Rabson /* 68227a0bc89SDoug Rabson * fillinusemap() needs pm_devvp. 68327a0bc89SDoug Rabson */ 68427a0bc89SDoug Rabson pmp->pm_devvp = devvp; 685c72ae142SJohn Baldwin pmp->pm_dev = dev; 68627a0bc89SDoug Rabson 68727a0bc89SDoug Rabson /* 68827a0bc89SDoug Rabson * Have the inuse map filled in. 68927a0bc89SDoug Rabson */ 6906be1a4ccSKonstantin Belousov MSDOSFS_LOCK_MP(pmp); 6916be1a4ccSKonstantin Belousov error = fillinusemap(pmp); 6926be1a4ccSKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp); 6936be1a4ccSKonstantin Belousov if (error != 0) 69427a0bc89SDoug Rabson goto error_exit; 69527a0bc89SDoug Rabson 69627a0bc89SDoug Rabson /* 6979287dbaaSEd Maste * If they want FAT updates to be synchronous then let them suffer 69827a0bc89SDoug Rabson * the performance degradation in exchange for the on disk copy of 6999287dbaaSEd Maste * the FAT being correct just about all the time. I suppose this 70027a0bc89SDoug Rabson * would be a good thing to turn on if the kernel is still flakey. 70127a0bc89SDoug Rabson */ 702952a6212SJordan K. Hubbard if (mp->mnt_flag & MNT_SYNCHRONOUS) 703952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 70427a0bc89SDoug Rabson 70527a0bc89SDoug Rabson /* 70627a0bc89SDoug Rabson * Finish up. 70727a0bc89SDoug Rabson */ 708952a6212SJordan K. Hubbard if (ronly) 709952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_RONLY; 710cede1f56STom Rhodes else { 711aaa38524SConrad Meyer if ((error = markvoldirty(pmp, 1)) != 0) 712cede1f56STom Rhodes goto error_exit; 71327a0bc89SDoug Rabson pmp->pm_fmod = 1; 714cede1f56STom Rhodes } 71577465d93SAlfred Perlstein mp->mnt_data = pmp; 716939cb752SBruce Evans mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 717af3f60d5SBruce Evans mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 7185da56ddbSTor Egge MNT_ILOCK(mp); 719cc9d8990SPeter Wemm mp->mnt_flag |= MNT_LOCAL; 7202aa39445SKonstantin Belousov mp->mnt_kern_flag |= MNTK_USES_BCACHE | MNTK_NO_IOPF; 7215da56ddbSTor Egge MNT_IUNLOCK(mp); 72227a0bc89SDoug Rabson 72340373cf5SKonstantin Belousov return (0); 72427a0bc89SDoug Rabson 725952a6212SJordan K. Hubbard error_exit: 726952a6212SJordan K. Hubbard if (bp) 727952a6212SJordan K. Hubbard brelse(bp); 7289a135592SPoul-Henning Kamp if (cp != NULL) { 7299a135592SPoul-Henning Kamp g_topology_lock(); 7300d7935fdSAttilio Rao g_vfs_close(cp); 7319a135592SPoul-Henning Kamp g_topology_unlock(); 7329a135592SPoul-Henning Kamp } 73327a0bc89SDoug Rabson if (pmp) { 734a84ec05fSKonstantin Belousov lockdestroy(&pmp->pm_fatlock); 735952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 736952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 73777465d93SAlfred Perlstein mp->mnt_data = NULL; 73827a0bc89SDoug Rabson } 739bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0); 740c72ae142SJohn Baldwin dev_rel(dev); 741952a6212SJordan K. Hubbard return (error); 74227a0bc89SDoug Rabson } 74327a0bc89SDoug Rabson 74427a0bc89SDoug Rabson /* 74527a0bc89SDoug Rabson * Unmount the filesystem described by mp. 74627a0bc89SDoug Rabson */ 7477fefffeeSPoul-Henning Kamp static int 748dfd233edSAttilio Rao msdosfs_unmount(struct mount *mp, int mntflags) 74927a0bc89SDoug Rabson { 750952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 751952a6212SJordan K. Hubbard int error, flags; 75227a0bc89SDoug Rabson 7532359e2dcSKonstantin Belousov error = flags = 0; 7542359e2dcSKonstantin Belousov pmp = VFSTOMSDOSFS(mp); 7552359e2dcSKonstantin Belousov if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) 756b2344ab5SKonstantin Belousov error = msdosfs_sync(mp, MNT_WAIT); 7572359e2dcSKonstantin Belousov if ((mntflags & MNT_FORCE) != 0) 75827a0bc89SDoug Rabson flags |= FORCECLOSE; 7592359e2dcSKonstantin Belousov else if (error != 0) 760b2344ab5SKonstantin Belousov return (error); 761dfd233edSAttilio Rao error = vflush(mp, 0, flags, curthread); 762b2344ab5SKonstantin Belousov if (error != 0 && error != ENXIO) 763b2344ab5SKonstantin Belousov return (error); 7643247c9ddSXin LI if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) { 7653247c9ddSXin LI error = markvoldirty(pmp, 0); 7664f560d75SEdward Tomasz Napierala if (error && error != ENXIO) { 7673247c9ddSXin LI (void)markvoldirty(pmp, 1); 7683247c9ddSXin LI return (error); 7693247c9ddSXin LI } 7703247c9ddSXin LI } 771c4f02a89SMax Khon if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 772c4f02a89SMax Khon if (pmp->pm_w2u) 773c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_w2u); 774c4f02a89SMax Khon if (pmp->pm_u2w) 775c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2w); 776c4f02a89SMax Khon if (pmp->pm_d2u) 777c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_d2u); 778c4f02a89SMax Khon if (pmp->pm_u2d) 779c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2d); 780c4f02a89SMax Khon } 781cede1f56STom Rhodes 782952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG 783952a6212SJordan K. Hubbard { 784952a6212SJordan K. Hubbard struct vnode *vp = pmp->pm_devvp; 785698b1a66SJeff Roberson struct bufobj *bo; 786952a6212SJordan K. Hubbard 787698b1a66SJeff Roberson bo = &vp->v_bufobj; 788698b1a66SJeff Roberson BO_LOCK(bo); 7894d93c0beSJeff Roberson VI_LOCK(vp); 790f69d42a1SPoul-Henning Kamp vn_printf(vp, 791f69d42a1SPoul-Henning Kamp "msdosfs_umount(): just before calling VOP_CLOSE()\n"); 792952a6212SJordan K. Hubbard printf("freef %p, freeb %p, mount %p\n", 793cc3593fbSMateusz Guzik TAILQ_NEXT(vp, v_vnodelist), vp->v_vnodelist.tqe_prev, 794952a6212SJordan K. Hubbard vp->v_mount); 795c1c4d0e9SConrad Meyer printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n", 796156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd), 797156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd), 798156cb265SPoul-Henning Kamp vp->v_bufobj.bo_numoutput, vp->v_type); 7994d93c0beSJeff Roberson VI_UNLOCK(vp); 800698b1a66SJeff Roberson BO_UNLOCK(bo); 801952a6212SJordan K. Hubbard } 802952a6212SJordan K. Hubbard #endif 8039a135592SPoul-Henning Kamp g_topology_lock(); 8040d7935fdSAttilio Rao g_vfs_close(pmp->pm_cp); 8059a135592SPoul-Henning Kamp g_topology_unlock(); 806bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&pmp->pm_dev->si_mountpt, 0); 80727a0bc89SDoug Rabson vrele(pmp->pm_devvp); 808c72ae142SJohn Baldwin dev_rel(pmp->pm_dev); 809952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 81023b6c230SKonstantin Belousov lockdestroy(&pmp->pm_fatlock); 811952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 81277465d93SAlfred Perlstein mp->mnt_data = NULL; 8135da56ddbSTor Egge MNT_ILOCK(mp); 814cc9d8990SPeter Wemm mp->mnt_flag &= ~MNT_LOCAL; 8155da56ddbSTor Egge MNT_IUNLOCK(mp); 8164f560d75SEdward Tomasz Napierala return (error); 81727a0bc89SDoug Rabson } 81827a0bc89SDoug Rabson 8197fefffeeSPoul-Henning Kamp static int 820dfd233edSAttilio Rao msdosfs_root(struct mount *mp, int flags, struct vnode **vpp) 82127a0bc89SDoug Rabson { 822952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 82327a0bc89SDoug Rabson struct denode *ndep; 82427a0bc89SDoug Rabson int error; 82527a0bc89SDoug Rabson 82627a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 827952a6212SJordan K. Hubbard printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 82827a0bc89SDoug Rabson #endif 829952a6212SJordan K. Hubbard error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 830952a6212SJordan K. Hubbard if (error) 831952a6212SJordan K. Hubbard return (error); 83227a0bc89SDoug Rabson *vpp = DETOV(ndep); 833952a6212SJordan K. Hubbard return (0); 83427a0bc89SDoug Rabson } 83527a0bc89SDoug Rabson 8367fefffeeSPoul-Henning Kamp static int 837dfd233edSAttilio Rao msdosfs_statfs(struct mount *mp, struct statfs *sbp) 83827a0bc89SDoug Rabson { 839952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 84027a0bc89SDoug Rabson 841952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 84227a0bc89SDoug Rabson sbp->f_bsize = pmp->pm_bpcluster; 84327a0bc89SDoug Rabson sbp->f_iosize = pmp->pm_bpcluster; 844499d3ffaSBoris Popov sbp->f_blocks = pmp->pm_maxcluster + 1; 84527a0bc89SDoug Rabson sbp->f_bfree = pmp->pm_freeclustercount; 84627a0bc89SDoug Rabson sbp->f_bavail = pmp->pm_freeclustercount; 84727a0bc89SDoug Rabson sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 84827a0bc89SDoug Rabson sbp->f_ffree = 0; /* what to put in here? */ 849952a6212SJordan K. Hubbard return (0); 85027a0bc89SDoug Rabson } 85127a0bc89SDoug Rabson 852bb7ca822SKonstantin Belousov /* 853bb7ca822SKonstantin Belousov * If we have an FSInfo block, update it. 854bb7ca822SKonstantin Belousov */ 855bb7ca822SKonstantin Belousov static int 856bb7ca822SKonstantin Belousov msdosfs_fsiflush(struct msdosfsmount *pmp, int waitfor) 857bb7ca822SKonstantin Belousov { 858bb7ca822SKonstantin Belousov struct fsinfo *fp; 859bb7ca822SKonstantin Belousov struct buf *bp; 860bb7ca822SKonstantin Belousov int error; 861bb7ca822SKonstantin Belousov 862bb7ca822SKonstantin Belousov MSDOSFS_LOCK_MP(pmp); 863bb7ca822SKonstantin Belousov if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) { 864bb7ca822SKonstantin Belousov error = 0; 865bb7ca822SKonstantin Belousov goto unlock; 866bb7ca822SKonstantin Belousov } 867bb7ca822SKonstantin Belousov error = bread(pmp->pm_devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 868bb7ca822SKonstantin Belousov NOCRED, &bp); 869bb7ca822SKonstantin Belousov if (error != 0) { 870bb7ca822SKonstantin Belousov goto unlock; 871bb7ca822SKonstantin Belousov } 872bb7ca822SKonstantin Belousov fp = (struct fsinfo *)bp->b_data; 873bb7ca822SKonstantin Belousov putulong(fp->fsinfree, pmp->pm_freeclustercount); 874bb7ca822SKonstantin Belousov putulong(fp->fsinxtfree, pmp->pm_nxtfree); 875bb7ca822SKonstantin Belousov pmp->pm_flags &= ~MSDOSFS_FSIMOD; 876bb7ca822SKonstantin Belousov if (waitfor == MNT_WAIT) 877bb7ca822SKonstantin Belousov error = bwrite(bp); 878bb7ca822SKonstantin Belousov else 879bb7ca822SKonstantin Belousov bawrite(bp); 880bb7ca822SKonstantin Belousov unlock: 881bb7ca822SKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp); 882bb7ca822SKonstantin Belousov return (error); 883bb7ca822SKonstantin Belousov } 884bb7ca822SKonstantin Belousov 8857fefffeeSPoul-Henning Kamp static int 886dfd233edSAttilio Rao msdosfs_sync(struct mount *mp, int waitfor) 88727a0bc89SDoug Rabson { 888952a6212SJordan K. Hubbard struct vnode *vp, *nvp; 889dfd233edSAttilio Rao struct thread *td; 89027a0bc89SDoug Rabson struct denode *dep; 891952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 892952a6212SJordan K. Hubbard int error, allerror = 0; 89327a0bc89SDoug Rabson 894dfd233edSAttilio Rao td = curthread; 895dfd233edSAttilio Rao 89627a0bc89SDoug Rabson /* 8979287dbaaSEd Maste * If we ever switch to not updating all of the FATs all the time, 89827a0bc89SDoug Rabson * this would be the place to update them from the first one. 89927a0bc89SDoug Rabson */ 900dfd5dee1SPeter Wemm if (pmp->pm_fmod != 0) { 901952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_RONLY) 90227a0bc89SDoug Rabson panic("msdosfs_sync: rofs mod"); 90327a0bc89SDoug Rabson else { 9049287dbaaSEd Maste /* update FATs here */ 90527a0bc89SDoug Rabson } 906dfd5dee1SPeter Wemm } 90727a0bc89SDoug Rabson /* 908952a6212SJordan K. Hubbard * Write back each (modified) denode. 90927a0bc89SDoug Rabson */ 91027a0bc89SDoug Rabson loop: 91171469bb3SKirk McKusick MNT_VNODE_FOREACH_ALL(vp, mp, nvp) { 91271469bb3SKirk McKusick if (vp->v_type == VNON) { 9134ab2c8bdSJeff Roberson VI_UNLOCK(vp); 9144ab2c8bdSJeff Roberson continue; 9154ab2c8bdSJeff Roberson } 91627a0bc89SDoug Rabson dep = VTODE(vp); 917f00f5d71SPoul-Henning Kamp if ((dep->de_flag & 918c681be37SDmitrij Tejblum (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 919156cb265SPoul-Henning Kamp (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 920f00f5d71SPoul-Henning Kamp waitfor == MNT_LAZY)) { 9214d93c0beSJeff Roberson VI_UNLOCK(vp); 92227a0bc89SDoug Rabson continue; 923af3f60d5SBruce Evans } 924a92a971bSMateusz Guzik error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK); 925af3f60d5SBruce Evans if (error) { 926f342b91cSMateusz Guzik if (error == ENOENT) { 927f342b91cSMateusz Guzik MNT_VNODE_FOREACH_ALL_ABORT(mp, nvp); 92827a0bc89SDoug Rabson goto loop; 929f342b91cSMateusz Guzik } 930af3f60d5SBruce Evans continue; 931af3f60d5SBruce Evans } 9328df6bac4SPoul-Henning Kamp error = VOP_FSYNC(vp, waitfor, td); 933c3c6d51eSPoul-Henning Kamp if (error) 93427a0bc89SDoug Rabson allerror = error; 935b249ce48SMateusz Guzik VOP_UNLOCK(vp); 936cb9ddc80SAlexander Kabaev vrele(vp); 93727a0bc89SDoug Rabson } 93827a0bc89SDoug Rabson 93927a0bc89SDoug Rabson /* 94027a0bc89SDoug Rabson * Flush filesystem control info. 94127a0bc89SDoug Rabson */ 942c681be37SDmitrij Tejblum if (waitfor != MNT_LAZY) { 943cb05b60aSAttilio Rao vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 9448df6bac4SPoul-Henning Kamp error = VOP_FSYNC(pmp->pm_devvp, waitfor, td); 945c3c6d51eSPoul-Henning Kamp if (error) 94627a0bc89SDoug Rabson allerror = error; 947b249ce48SMateusz Guzik VOP_UNLOCK(pmp->pm_devvp); 948c681be37SDmitrij Tejblum } 949bb7ca822SKonstantin Belousov 950bb7ca822SKonstantin Belousov error = msdosfs_fsiflush(pmp, waitfor); 951bb7ca822SKonstantin Belousov if (error != 0) 952bb7ca822SKonstantin Belousov allerror = error; 953*1b3cb4dcSKonstantin Belousov 954*1b3cb4dcSKonstantin Belousov if (allerror == 0 && waitfor == MNT_SUSPEND) { 955*1b3cb4dcSKonstantin Belousov MNT_ILOCK(mp); 956*1b3cb4dcSKonstantin Belousov mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED; 957*1b3cb4dcSKonstantin Belousov MNT_IUNLOCK(mp); 958*1b3cb4dcSKonstantin Belousov } 959952a6212SJordan K. Hubbard return (allerror); 96027a0bc89SDoug Rabson } 96127a0bc89SDoug Rabson 9621be5bc74STom Rhodes static int 963694a586aSRick Macklem msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, 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