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 787c3fc9deSBruce Evans /* Mount options that we support. */ 796a4b48f4SPoul-Henning Kamp static const char *msdosfs_opts[] = { 80cb65c1eeSBruce Evans "async", "noatime", "noclusterr", "noclusterw", 817c3fc9deSBruce Evans "export", "force", "from", "sync", 827c3fc9deSBruce Evans "cs_dos", "cs_local", "cs_win", "dirmask", 837c3fc9deSBruce Evans "gid", "kiconv", "large", "longname", 847c3fc9deSBruce Evans "longnames", "mask", "shortname", "shortnames", 857c3fc9deSBruce Evans "uid", "win95", "nowin95", 866a4b48f4SPoul-Henning Kamp NULL 876a4b48f4SPoul-Henning Kamp }; 886a4b48f4SPoul-Henning Kamp 8901f6cfbaSYoshihiro Takahashi #if 1 /*def PC98*/ 9001f6cfbaSYoshihiro Takahashi /* 9101f6cfbaSYoshihiro Takahashi * XXX - The boot signature formatted by NEC PC-98 DOS looks like a 9201f6cfbaSYoshihiro Takahashi * garbage or a random value :-{ 9301f6cfbaSYoshihiro Takahashi * If you want to use that broken-signatured media, define the 9401f6cfbaSYoshihiro Takahashi * following symbol even though PC/AT. 9501f6cfbaSYoshihiro Takahashi * (ex. mount PC-98 DOS formatted FD on PC/AT) 9601f6cfbaSYoshihiro Takahashi */ 9701f6cfbaSYoshihiro Takahashi #define MSDOSFS_NOCHECKSIG 9801f6cfbaSYoshihiro Takahashi #endif 9901f6cfbaSYoshihiro Takahashi 1005bb84bc8SRobert Watson MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure"); 1015bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table"); 10255166637SPoul-Henning Kamp 1032d7c6b27SBruce Evans struct iconv_functions *msdosfs_iconv; 104c4f02a89SMax Khon 1056a4b48f4SPoul-Henning Kamp static int update_mp(struct mount *mp, struct thread *td); 1060d7935fdSAttilio Rao static int mountmsdosfs(struct vnode *devvp, struct mount *mp); 1071be5bc74STom Rhodes static vfs_fhtovp_t msdosfs_fhtovp; 1086a4b48f4SPoul-Henning Kamp static vfs_mount_t msdosfs_mount; 1099bf1a756SPoul-Henning Kamp static vfs_root_t msdosfs_root; 1109bf1a756SPoul-Henning Kamp static vfs_statfs_t msdosfs_statfs; 1119bf1a756SPoul-Henning Kamp static vfs_sync_t msdosfs_sync; 1129bf1a756SPoul-Henning Kamp static vfs_unmount_t msdosfs_unmount; 113af482601SBruce Evans 1141a9415afSTim J. Robbins /* Maximum length of a character set name (arbitrary). */ 1151a9415afSTim J. Robbins #define MAXCSLEN 64 1161a9415afSTim J. Robbins 117952a6212SJordan K. Hubbard static int 118dc9a617aSCraig Rodrigues update_mp(struct mount *mp, struct thread *td) 119952a6212SJordan K. Hubbard { 120952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1216a4b48f4SPoul-Henning Kamp void *dos, *win, *local; 1226a4b48f4SPoul-Henning Kamp int error, v; 123952a6212SJordan K. Hubbard 1246a4b48f4SPoul-Henning Kamp if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) { 1256a4b48f4SPoul-Henning Kamp if (msdosfs_iconv != NULL) { 1266a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1276a4b48f4SPoul-Henning Kamp "cs_win", &win, NULL); 1286a4b48f4SPoul-Henning Kamp if (!error) 1296a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1306a4b48f4SPoul-Henning Kamp "cs_local", &local, NULL); 1316a4b48f4SPoul-Henning Kamp if (!error) 1326a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew, 1336a4b48f4SPoul-Henning Kamp "cs_dos", &dos, NULL); 1346a4b48f4SPoul-Henning Kamp if (!error) { 1351a9415afSTim J. Robbins msdosfs_iconv->open(win, local, &pmp->pm_u2w); 1361a9415afSTim J. Robbins msdosfs_iconv->open(local, win, &pmp->pm_w2u); 1371a9415afSTim J. Robbins msdosfs_iconv->open(dos, local, &pmp->pm_u2d); 1381a9415afSTim J. Robbins msdosfs_iconv->open(local, dos, &pmp->pm_d2u); 1396a4b48f4SPoul-Henning Kamp } 1401a9415afSTim J. Robbins if (error != 0) 1411a9415afSTim J. Robbins return (error); 142c4f02a89SMax Khon } else { 143c4f02a89SMax Khon pmp->pm_w2u = NULL; 144c4f02a89SMax Khon pmp->pm_u2w = NULL; 145c4f02a89SMax Khon pmp->pm_d2u = NULL; 146c4f02a89SMax Khon pmp->pm_u2d = NULL; 1477391f611SAndrey A. Chernov } 1481a9415afSTim J. Robbins } 1491a9415afSTim J. Robbins 1506a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v)) 1516a4b48f4SPoul-Henning Kamp pmp->pm_gid = v; 1526a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v)) 1536a4b48f4SPoul-Henning Kamp pmp->pm_uid = v; 1546a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v)) 1556a4b48f4SPoul-Henning Kamp pmp->pm_mask = v & ALLPERMS; 1566a4b48f4SPoul-Henning Kamp if (1 == vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v)) 1576a4b48f4SPoul-Henning Kamp pmp->pm_dirmask = v & ALLPERMS; 1586a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "shortname", 1596a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1604ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "shortnames", 1614ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_SHORTNAME); 1626a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "longname", 1636a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1644ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "longnames", 1654ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_LONGNAME); 1666a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "kiconv", 1676a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_KICONV); 1686a4b48f4SPoul-Henning Kamp 169d75b2048SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0) 1706a4b48f4SPoul-Henning Kamp pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 171d75b2048SCraig Rodrigues else 172d75b2048SCraig Rodrigues pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95; 173952a6212SJordan K. Hubbard 174952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 175952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 176952a6212SJordan K. Hubbard else if (!(pmp->pm_flags & 177952a6212SJordan K. Hubbard (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 178952a6212SJordan K. Hubbard struct vnode *rootvp; 179952a6212SJordan K. Hubbard 180952a6212SJordan K. Hubbard /* 181952a6212SJordan K. Hubbard * Try to divine whether to support Win'95 long filenames 182952a6212SJordan K. Hubbard */ 183952a6212SJordan K. Hubbard if (FAT32(pmp)) 184952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 185952a6212SJordan K. Hubbard else { 186d9b2d9f7SJeff Roberson if ((error = 187d9b2d9f7SJeff Roberson msdosfs_root(mp, LK_EXCLUSIVE, &rootvp, td)) != 0) 188952a6212SJordan K. Hubbard return error; 1892d7c6b27SBruce Evans pmp->pm_flags |= findwin95(VTODE(rootvp)) ? 1902d7c6b27SBruce Evans MSDOSFSMNT_LONGNAME : MSDOSFSMNT_SHORTNAME; 191952a6212SJordan K. Hubbard vput(rootvp); 192952a6212SJordan K. Hubbard } 193952a6212SJordan K. Hubbard } 194952a6212SJordan K. Hubbard return 0; 195952a6212SJordan K. Hubbard } 196952a6212SJordan K. Hubbard 1976a4b48f4SPoul-Henning Kamp static int 1986a4b48f4SPoul-Henning Kamp msdosfs_cmount(struct mntarg *ma, void *data, int flags, struct thread *td) 1996a4b48f4SPoul-Henning Kamp { 2006a4b48f4SPoul-Henning Kamp struct msdosfs_args args; 2016a4b48f4SPoul-Henning Kamp int error; 2026a4b48f4SPoul-Henning Kamp 2036a4b48f4SPoul-Henning Kamp if (data == NULL) 2046a4b48f4SPoul-Henning Kamp return (EINVAL); 2056a4b48f4SPoul-Henning Kamp error = copyin(data, &args, sizeof args); 2066a4b48f4SPoul-Henning Kamp if (error) 2076a4b48f4SPoul-Henning Kamp return (error); 2086a4b48f4SPoul-Henning Kamp 2096a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN); 2106a4b48f4SPoul-Henning Kamp ma = mount_arg(ma, "export", &args.export, sizeof args.export); 2116a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "uid", "%d", args.uid); 2126a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "gid", "%d", args.gid); 2136a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "mask", "%d", args.mask); 2146a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "dirmask", "%d", args.dirmask); 2156a4b48f4SPoul-Henning Kamp 2166a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname"); 2176a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname"); 2186a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95"); 2196a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv"); 2206a4b48f4SPoul-Henning Kamp 2216a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN); 2226a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN); 2236a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN); 2246a4b48f4SPoul-Henning Kamp 2256a4b48f4SPoul-Henning Kamp error = kernel_mount(ma, flags); 2266a4b48f4SPoul-Henning Kamp 2276a4b48f4SPoul-Henning Kamp return (error); 2286a4b48f4SPoul-Henning Kamp } 2296a4b48f4SPoul-Henning Kamp 23027a0bc89SDoug Rabson /* 23127a0bc89SDoug Rabson * mp - path - addr in user space of mount point (ie /usr or whatever) 23227a0bc89SDoug Rabson * data - addr in user space of mount params including the name of the block 23327a0bc89SDoug Rabson * special file to treat as a filesystem. 23427a0bc89SDoug Rabson */ 2357fefffeeSPoul-Henning Kamp static int 2366a4b48f4SPoul-Henning Kamp msdosfs_mount(struct mount *mp, struct thread *td) 23727a0bc89SDoug Rabson { 23827a0bc89SDoug Rabson struct vnode *devvp; /* vnode for blk device to mount */ 239952a6212SJordan K. Hubbard /* msdosfs specific mount control block */ 240952a6212SJordan K. Hubbard struct msdosfsmount *pmp = NULL; 2415e8c582aSPoul-Henning Kamp struct nameidata ndp; 242269c902fSPoul-Henning Kamp int error, flags; 24315bc6b2bSEdward Tomasz Napierala accmode_t accmode; 2446a4b48f4SPoul-Henning Kamp char *from; 24527a0bc89SDoug Rabson 2466a4b48f4SPoul-Henning Kamp if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts)) 2476a4b48f4SPoul-Henning Kamp return (EINVAL); 2486a4b48f4SPoul-Henning Kamp 24927a0bc89SDoug Rabson /* 250952a6212SJordan K. Hubbard * If updating, check whether changing from read-only to 251952a6212SJordan K. Hubbard * read/write; if there is no device name, that's all we do. 25227a0bc89SDoug Rabson */ 25327a0bc89SDoug Rabson if (mp->mnt_flag & MNT_UPDATE) { 2542d7c6b27SBruce Evans pmp = VFSTOMSDOSFS(mp); 2555eb304a9SCraig Rodrigues if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { 256e9022ef8SCraig Rodrigues /* 257e9022ef8SCraig Rodrigues * Forbid export requests if filesystem has 258e9022ef8SCraig Rodrigues * MSDOSFS_LARGEFS flag set. 259e9022ef8SCraig Rodrigues */ 260e9022ef8SCraig Rodrigues if ((pmp->pm_flags & MSDOSFS_LARGEFS) != 0) { 261e9022ef8SCraig Rodrigues vfs_mount_error(mp, 262e9022ef8SCraig Rodrigues "MSDOSFS_LARGEFS flag set, cannot export"); 263269c902fSPoul-Henning Kamp return (EOPNOTSUPP); 264e9022ef8SCraig Rodrigues } 265269c902fSPoul-Henning Kamp } 2669a135592SPoul-Henning Kamp if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 2676a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 2688df6bac4SPoul-Henning Kamp error = VFS_SYNC(mp, MNT_WAIT, td); 269e83f1423SPoul-Henning Kamp if (error) 270e83f1423SPoul-Henning Kamp return (error); 27127a0bc89SDoug Rabson flags = WRITECLOSE; 27227a0bc89SDoug Rabson if (mp->mnt_flag & MNT_FORCE) 27327a0bc89SDoug Rabson flags |= FORCECLOSE; 274f257b7a5SAlfred Perlstein error = vflush(mp, 0, flags, td); 2756a4b48f4SPoul-Henning Kamp if (error) 2766a4b48f4SPoul-Henning Kamp return (error); 2773247c9ddSXin LI 2783247c9ddSXin LI /* 2793247c9ddSXin LI * Now the volume is clean. Mark it so while the 2803247c9ddSXin LI * device is still rw. 2813247c9ddSXin LI */ 2823247c9ddSXin LI error = markvoldirty(pmp, 0); 2833247c9ddSXin LI if (error) { 2843247c9ddSXin LI (void)markvoldirty(pmp, 1); 2853247c9ddSXin LI return (error); 2863247c9ddSXin LI } 2873247c9ddSXin LI 2883247c9ddSXin LI /* Downgrade the device from rw to ro. */ 2899a135592SPoul-Henning Kamp DROP_GIANT(); 2909a135592SPoul-Henning Kamp g_topology_lock(); 2914eb3abf0SBruce Evans error = g_access(pmp->pm_cp, 0, -1, 0); 2929a135592SPoul-Henning Kamp g_topology_unlock(); 2939a135592SPoul-Henning Kamp PICKUP_GIANT(); 2943247c9ddSXin LI if (error) { 2953247c9ddSXin LI (void)markvoldirty(pmp, 1); 2964eb3abf0SBruce Evans return (error); 2973247c9ddSXin LI } 2984eb3abf0SBruce Evans 2993247c9ddSXin LI /* 3003247c9ddSXin LI * Backing out after an error was painful in the 3013247c9ddSXin LI * above. Now we are committed to succeeding. 3023247c9ddSXin LI */ 3033247c9ddSXin LI pmp->pm_fmod = 0; 3043247c9ddSXin LI pmp->pm_flags |= MSDOSFSMNT_RONLY; 3053247c9ddSXin LI MNT_ILOCK(mp); 3063247c9ddSXin LI mp->mnt_flag |= MNT_RDONLY; 3073247c9ddSXin LI MNT_IUNLOCK(mp); 3086a4b48f4SPoul-Henning Kamp } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 3096a4b48f4SPoul-Henning Kamp !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) { 310952a6212SJordan K. Hubbard /* 311952a6212SJordan K. Hubbard * If upgrade to read-write by non-root, then verify 312952a6212SJordan K. Hubbard * that user has necessary permissions on the device. 313952a6212SJordan K. Hubbard */ 314952a6212SJordan K. Hubbard devvp = pmp->pm_devvp; 315cb05b60aSAttilio Rao vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 316952a6212SJordan K. Hubbard error = VOP_ACCESS(devvp, VREAD | VWRITE, 317a854ed98SJohn Baldwin td->td_ucred, td); 318acd3428bSRobert Watson if (error) 319acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 320952a6212SJordan K. Hubbard if (error) { 32122db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 322952a6212SJordan K. Hubbard return (error); 323952a6212SJordan K. Hubbard } 32422db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 3259a135592SPoul-Henning Kamp DROP_GIANT(); 3269a135592SPoul-Henning Kamp g_topology_lock(); 3279a135592SPoul-Henning Kamp error = g_access(pmp->pm_cp, 0, 1, 0); 3289a135592SPoul-Henning Kamp g_topology_unlock(); 3299a135592SPoul-Henning Kamp PICKUP_GIANT(); 3309a135592SPoul-Henning Kamp if (error) 3319a135592SPoul-Henning Kamp return (error); 332cede1f56STom Rhodes 3333247c9ddSXin LI pmp->pm_fmod = 1; 3343247c9ddSXin LI pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 3353247c9ddSXin LI MNT_ILOCK(mp); 3363247c9ddSXin LI mp->mnt_flag &= ~MNT_RDONLY; 3373247c9ddSXin LI MNT_IUNLOCK(mp); 33882c59ec6SCraig Rodrigues 33982c59ec6SCraig Rodrigues /* Now that the volume is modifiable, mark it dirty. */ 34082c59ec6SCraig Rodrigues error = markvoldirty(pmp, 1); 34182c59ec6SCraig Rodrigues if (error) 34282c59ec6SCraig Rodrigues return (error); 34382c59ec6SCraig Rodrigues } 344952a6212SJordan K. Hubbard } 34527a0bc89SDoug Rabson /* 346952a6212SJordan K. Hubbard * Not an update, or updating the name: look up the name 347e9827c6dSBruce Evans * and verify that it refers to a sensible disk device. 34827a0bc89SDoug Rabson */ 3496a4b48f4SPoul-Henning Kamp if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) 3506a4b48f4SPoul-Henning Kamp return (EINVAL); 35175d7ba93SSuleiman Souhlal NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td); 3525e8c582aSPoul-Henning Kamp error = namei(&ndp); 353952a6212SJordan K. Hubbard if (error) 354952a6212SJordan K. Hubbard return (error); 3555e8c582aSPoul-Henning Kamp devvp = ndp.ni_vp; 3565e8c582aSPoul-Henning Kamp NDFREE(&ndp, NDF_ONLY_PNBUF); 357952a6212SJordan K. Hubbard 358ba4ad1fcSPoul-Henning Kamp if (!vn_isdisk(devvp, &error)) { 35975d7ba93SSuleiman Souhlal vput(devvp); 360ba4ad1fcSPoul-Henning Kamp return (error); 36127a0bc89SDoug Rabson } 36227a0bc89SDoug Rabson /* 363952a6212SJordan K. Hubbard * If mount by non-root, then verify that user has necessary 364952a6212SJordan K. Hubbard * permissions on the device. 36527a0bc89SDoug Rabson */ 36615bc6b2bSEdward Tomasz Napierala accmode = VREAD; 367952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_RDONLY) == 0) 36815bc6b2bSEdward Tomasz Napierala accmode |= VWRITE; 36915bc6b2bSEdward Tomasz Napierala error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); 370acd3428bSRobert Watson if (error) 371acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM); 372952a6212SJordan K. Hubbard if (error) { 373952a6212SJordan K. Hubbard vput(devvp); 374952a6212SJordan K. Hubbard return (error); 375952a6212SJordan K. Hubbard } 376952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_UPDATE) == 0) { 3770d7935fdSAttilio Rao error = mountmsdosfs(devvp, mp); 378952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 379952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 380952a6212SJordan K. Hubbard #endif 381952a6212SJordan K. Hubbard } else { 38227a0bc89SDoug Rabson if (devvp != pmp->pm_devvp) 383952a6212SJordan K. Hubbard error = EINVAL; /* XXX needs translation */ 38427a0bc89SDoug Rabson else 38575d7ba93SSuleiman Souhlal vput(devvp); 38627a0bc89SDoug Rabson } 38727a0bc89SDoug Rabson if (error) { 38827a0bc89SDoug Rabson vrele(devvp); 389952a6212SJordan K. Hubbard return (error); 390952a6212SJordan K. Hubbard } 391952a6212SJordan K. Hubbard 3926a4b48f4SPoul-Henning Kamp error = update_mp(mp, td); 393952a6212SJordan K. Hubbard if (error) { 3941a9415afSTim J. Robbins if ((mp->mnt_flag & MNT_UPDATE) == 0) 395b40ce416SJulian Elischer msdosfs_unmount(mp, MNT_FORCE, td); 39627a0bc89SDoug Rabson return error; 39727a0bc89SDoug Rabson } 3986a4b48f4SPoul-Henning Kamp 3996a4b48f4SPoul-Henning Kamp vfs_mountedfrom(mp, from); 40027a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 4016a4b48f4SPoul-Henning Kamp printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 40227a0bc89SDoug Rabson #endif 403952a6212SJordan K. Hubbard return (0); 40427a0bc89SDoug Rabson } 40527a0bc89SDoug Rabson 4067fefffeeSPoul-Henning Kamp static int 4070d7935fdSAttilio Rao mountmsdosfs(struct vnode *devvp, struct mount *mp) 40827a0bc89SDoug Rabson { 409952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 410952a6212SJordan K. Hubbard struct buf *bp; 41189c9c53dSPoul-Henning Kamp struct cdev *dev = devvp->v_rdev; 41227a0bc89SDoug Rabson union bootsector *bsp; 41327a0bc89SDoug Rabson struct byte_bpb33 *b33; 41427a0bc89SDoug Rabson struct byte_bpb50 *b50; 415952a6212SJordan K. Hubbard struct byte_bpb710 *b710; 416952a6212SJordan K. Hubbard u_int8_t SecPerClust; 417499d3ffaSBoris Popov u_long clusters; 418952a6212SJordan K. Hubbard int ronly, error; 4199a135592SPoul-Henning Kamp struct g_consumer *cp; 4209a135592SPoul-Henning Kamp struct bufobj *bo; 42127a0bc89SDoug Rabson 4224eb3abf0SBruce Evans ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 4239a135592SPoul-Henning Kamp /* XXX: use VOP_ACCESS to check FS perms */ 4249a135592SPoul-Henning Kamp DROP_GIANT(); 4259a135592SPoul-Henning Kamp g_topology_lock(); 4264eb3abf0SBruce Evans error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1); 4279a135592SPoul-Henning Kamp g_topology_unlock(); 4289a135592SPoul-Henning Kamp PICKUP_GIANT(); 42922db15c0SAttilio Rao VOP_UNLOCK(devvp, 0); 430c3c6d51eSPoul-Henning Kamp if (error) 431952a6212SJordan K. Hubbard return (error); 432952a6212SJordan K. Hubbard 4339a135592SPoul-Henning Kamp bo = &devvp->v_bufobj; 4342d7c6b27SBruce Evans bp = NULL; /* This and pmp both used in error_exit. */ 435952a6212SJordan K. Hubbard pmp = NULL; 436952a6212SJordan K. Hubbard 43727a0bc89SDoug Rabson /* 438952a6212SJordan K. Hubbard * Read the boot sector of the filesystem, and then check the 439952a6212SJordan K. Hubbard * boot signature. If not a dos boot sector then error out. 44001f6cfbaSYoshihiro Takahashi * 44193fe42b6SBruce Evans * NOTE: 8192 is a magic size that works for ffs. 44227a0bc89SDoug Rabson */ 44393fe42b6SBruce Evans error = bread(devvp, 0, 8192, NOCRED, &bp); 444c3c6d51eSPoul-Henning Kamp if (error) 44527a0bc89SDoug Rabson goto error_exit; 446952a6212SJordan K. Hubbard bp->b_flags |= B_AGE; 447952a6212SJordan K. Hubbard bsp = (union bootsector *)bp->b_data; 44827a0bc89SDoug Rabson b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 44927a0bc89SDoug Rabson b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 45001a4d019SJohn Baldwin b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 451952a6212SJordan K. Hubbard 45201f6cfbaSYoshihiro Takahashi #ifndef MSDOSFS_NOCHECKSIG 453952a6212SJordan K. Hubbard if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 454952a6212SJordan K. Hubbard || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 45527a0bc89SDoug Rabson error = EINVAL; 45627a0bc89SDoug Rabson goto error_exit; 45727a0bc89SDoug Rabson } 45801f6cfbaSYoshihiro Takahashi #endif 45927a0bc89SDoug Rabson 460a163d034SWarner Losh pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); 46127a0bc89SDoug Rabson pmp->pm_mountp = mp; 4629a135592SPoul-Henning Kamp pmp->pm_cp = cp; 4639a135592SPoul-Henning Kamp pmp->pm_bo = bo; 46427a0bc89SDoug Rabson 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 /* 475f458f2a5SCraig Rodrigues * Experimental support for large MS-DOS filesystems. 476f458f2a5SCraig Rodrigues * WARNING: This uses at least 32 bytes of kernel memory (which is not 477f458f2a5SCraig Rodrigues * reclaimed until the FS is unmounted) for each file on disk to map 478f458f2a5SCraig Rodrigues * between the 32-bit inode numbers used by VFS and the 64-bit 479f458f2a5SCraig Rodrigues * pseudo-inode numbers used internally by msdosfs. This is only 480f458f2a5SCraig Rodrigues * safe to use in certain controlled situations (e.g. read-only FS 481f458f2a5SCraig Rodrigues * with less than 1 million files). 482f458f2a5SCraig Rodrigues * Since the mappings do not persist across unmounts (or reboots), these 483f458f2a5SCraig Rodrigues * filesystems are not suitable for exporting through NFS, or any other 484f458f2a5SCraig Rodrigues * application that requires fixed inode numbers. 485f458f2a5SCraig Rodrigues */ 4862d7c6b27SBruce Evans vfs_flagopt(mp->mnt_optnew, "large", &pmp->pm_flags, MSDOSFS_LARGEFS); 487f458f2a5SCraig Rodrigues 488f458f2a5SCraig Rodrigues /* 48927a0bc89SDoug Rabson * Compute several useful quantities from the bpb in the 49027a0bc89SDoug Rabson * bootsector. Copy in the dos 5 variant of the bpb then fix up 49127a0bc89SDoug Rabson * the fields that are different between dos 5 and dos 3.3. 49227a0bc89SDoug Rabson */ 493952a6212SJordan K. Hubbard SecPerClust = b50->bpbSecPerClust; 49427a0bc89SDoug Rabson pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 495696f22f0SJohn Baldwin if (pmp->pm_BytesPerSec < DEV_BSIZE) { 496696f22f0SJohn Baldwin error = EINVAL; 497696f22f0SJohn Baldwin goto error_exit; 498696f22f0SJohn Baldwin } 49927a0bc89SDoug Rabson pmp->pm_ResSectors = getushort(b50->bpbResSectors); 50027a0bc89SDoug Rabson pmp->pm_FATs = b50->bpbFATs; 50127a0bc89SDoug Rabson pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 50227a0bc89SDoug Rabson pmp->pm_Sectors = getushort(b50->bpbSectors); 50327a0bc89SDoug Rabson pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 50427a0bc89SDoug Rabson pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 50527a0bc89SDoug Rabson pmp->pm_Heads = getushort(b50->bpbHeads); 506952a6212SJordan K. Hubbard pmp->pm_Media = b50->bpbMedia; 50727a0bc89SDoug Rabson 50801f6cfbaSYoshihiro Takahashi /* calculate the ratio of sector size to DEV_BSIZE */ 50901f6cfbaSYoshihiro Takahashi pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; 51001f6cfbaSYoshihiro Takahashi 511043ec583SMarcel Moolenaar /* 512043ec583SMarcel Moolenaar * We don't check pm_Heads nor pm_SecPerTrack, because 513043ec583SMarcel Moolenaar * these may not be set for EFI file systems. We don't 514043ec583SMarcel Moolenaar * use these anyway, so we're unaffected if they are 515043ec583SMarcel Moolenaar * invalid. 516043ec583SMarcel Moolenaar */ 517043ec583SMarcel Moolenaar if (!pmp->pm_BytesPerSec || !SecPerClust) { 51827a0bc89SDoug Rabson error = EINVAL; 51927a0bc89SDoug Rabson goto error_exit; 52027a0bc89SDoug Rabson } 52127a0bc89SDoug Rabson 52227a0bc89SDoug Rabson if (pmp->pm_Sectors == 0) { 52327a0bc89SDoug Rabson pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 52427a0bc89SDoug Rabson pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 52527a0bc89SDoug Rabson } else { 52627a0bc89SDoug Rabson pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 52727a0bc89SDoug Rabson pmp->pm_HugeSectors = pmp->pm_Sectors; 52827a0bc89SDoug Rabson } 529f458f2a5SCraig Rodrigues if (!(pmp->pm_flags & MSDOSFS_LARGEFS)) { 530c681be37SDmitrij Tejblum if (pmp->pm_HugeSectors > 0xffffffff / 531c681be37SDmitrij Tejblum (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { 532952a6212SJordan K. Hubbard /* 533952a6212SJordan K. Hubbard * We cannot deal currently with this size of disk 534952a6212SJordan K. Hubbard * due to fileid limitations (see msdosfs_getattr and 535952a6212SJordan K. Hubbard * msdosfs_readdir) 536952a6212SJordan K. Hubbard */ 537952a6212SJordan K. Hubbard error = EINVAL; 538f458f2a5SCraig Rodrigues vfs_mount_error(mp, 539f458f2a5SCraig Rodrigues "Disk too big, try '-o large' mount option"); 540952a6212SJordan K. Hubbard goto error_exit; 541952a6212SJordan K. Hubbard } 542f458f2a5SCraig Rodrigues } 543952a6212SJordan K. Hubbard 544952a6212SJordan K. Hubbard if (pmp->pm_RootDirEnts == 0) { 54520c5ba36SPeter Edwards if (pmp->pm_Sectors 546952a6212SJordan K. Hubbard || pmp->pm_FATsecs 547952a6212SJordan K. Hubbard || getushort(b710->bpbFSVers)) { 548952a6212SJordan K. Hubbard error = EINVAL; 549aaf0bb19SAndrey A. Chernov printf("mountmsdosfs(): bad FAT32 filesystem\n"); 550952a6212SJordan K. Hubbard goto error_exit; 551952a6212SJordan K. Hubbard } 552952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT32_MASK; 553952a6212SJordan K. Hubbard pmp->pm_fatmult = 4; 554952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 555952a6212SJordan K. Hubbard pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 556952a6212SJordan K. Hubbard if (getushort(b710->bpbExtFlags) & FATMIRROR) 557952a6212SJordan K. Hubbard pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 558952a6212SJordan K. Hubbard else 559952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 560952a6212SJordan K. Hubbard } else 561952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR; 562952a6212SJordan K. Hubbard 563952a6212SJordan K. Hubbard /* 564952a6212SJordan K. Hubbard * Check a few values (could do some more): 565952a6212SJordan K. Hubbard * - logical sector size: power of 2, >= block size 566952a6212SJordan K. Hubbard * - sectors per cluster: power of 2, >= 1 567952a6212SJordan K. Hubbard * - number of sectors: >= 1, <= size of partition 568696f22f0SJohn Baldwin * - number of FAT sectors: >= 1 569952a6212SJordan K. Hubbard */ 570952a6212SJordan K. Hubbard if ( (SecPerClust == 0) 571952a6212SJordan K. Hubbard || (SecPerClust & (SecPerClust - 1)) 57201f6cfbaSYoshihiro Takahashi || (pmp->pm_BytesPerSec < DEV_BSIZE) 573952a6212SJordan K. Hubbard || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) 574952a6212SJordan K. Hubbard || (pmp->pm_HugeSectors == 0) 575696f22f0SJohn Baldwin || (pmp->pm_FATsecs == 0) 576952a6212SJordan K. Hubbard ) { 577952a6212SJordan K. Hubbard error = EINVAL; 578952a6212SJordan K. Hubbard goto error_exit; 579952a6212SJordan K. Hubbard } 58001f6cfbaSYoshihiro Takahashi 58101f6cfbaSYoshihiro Takahashi pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; 58201f6cfbaSYoshihiro Takahashi pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ 58301f6cfbaSYoshihiro Takahashi pmp->pm_FATsecs *= pmp->pm_BlkPerSec; 58401f6cfbaSYoshihiro Takahashi SecPerClust *= pmp->pm_BlkPerSec; 58501f6cfbaSYoshihiro Takahashi 58601f6cfbaSYoshihiro Takahashi pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; 58701f6cfbaSYoshihiro Takahashi 588952a6212SJordan K. Hubbard if (FAT32(pmp)) { 589952a6212SJordan K. Hubbard pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 590952a6212SJordan K. Hubbard pmp->pm_firstcluster = pmp->pm_fatblk 591952a6212SJordan K. Hubbard + (pmp->pm_FATs * pmp->pm_FATsecs); 59201f6cfbaSYoshihiro Takahashi pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; 593952a6212SJordan K. Hubbard } else { 59427a0bc89SDoug Rabson pmp->pm_rootdirblk = pmp->pm_fatblk + 59527a0bc89SDoug Rabson (pmp->pm_FATs * pmp->pm_FATsecs); 596952a6212SJordan K. Hubbard pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 59701f6cfbaSYoshihiro Takahashi + DEV_BSIZE - 1) 59801f6cfbaSYoshihiro Takahashi / DEV_BSIZE; /* in blocks */ 59927a0bc89SDoug Rabson pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 600952a6212SJordan K. Hubbard } 601952a6212SJordan K. Hubbard 602499d3ffaSBoris Popov pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 603499d3ffaSBoris Popov SecPerClust + 1; 60401f6cfbaSYoshihiro Takahashi pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ 605952a6212SJordan K. Hubbard 606952a6212SJordan K. Hubbard if (pmp->pm_fatmask == 0) { 607952a6212SJordan K. Hubbard if (pmp->pm_maxcluster 608952a6212SJordan K. Hubbard <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 60927a0bc89SDoug Rabson /* 610952a6212SJordan K. Hubbard * This will usually be a floppy disk. This size makes 611952a6212SJordan K. Hubbard * sure that one fat entry will not be split across 612952a6212SJordan K. Hubbard * multiple blocks. 61327a0bc89SDoug Rabson */ 614952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT12_MASK; 615952a6212SJordan K. Hubbard pmp->pm_fatmult = 3; 616952a6212SJordan K. Hubbard pmp->pm_fatdiv = 2; 617952a6212SJordan K. Hubbard } else { 618952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT16_MASK; 619952a6212SJordan K. Hubbard pmp->pm_fatmult = 2; 620952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1; 621952a6212SJordan K. Hubbard } 622952a6212SJordan K. Hubbard } 623499d3ffaSBoris Popov 624499d3ffaSBoris Popov clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; 625499d3ffaSBoris Popov if (pmp->pm_maxcluster >= clusters) { 626499d3ffaSBoris Popov printf("Warning: number of clusters (%ld) exceeds FAT " 627499d3ffaSBoris Popov "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); 628499d3ffaSBoris Popov pmp->pm_maxcluster = clusters - 1; 629499d3ffaSBoris Popov } 630499d3ffaSBoris Popov 631952a6212SJordan K. Hubbard if (FAT12(pmp)) 63293fe42b6SBruce Evans pmp->pm_fatblocksize = 3 * 512; 63327a0bc89SDoug Rabson else 63493fe42b6SBruce Evans pmp->pm_fatblocksize = PAGE_SIZE; 63593fe42b6SBruce Evans pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize, 63693fe42b6SBruce Evans pmp->pm_BytesPerSec); 63701f6cfbaSYoshihiro Takahashi pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; 63801f6cfbaSYoshihiro Takahashi pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; 63927a0bc89SDoug Rabson 64027a0bc89SDoug Rabson /* 64127a0bc89SDoug Rabson * Compute mask and shift value for isolating cluster relative byte 64227a0bc89SDoug Rabson * offsets and cluster numbers from a file offset. 64327a0bc89SDoug Rabson */ 64401f6cfbaSYoshihiro Takahashi pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; 645952a6212SJordan K. Hubbard pmp->pm_crbomask = pmp->pm_bpcluster - 1; 646952a6212SJordan K. Hubbard pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 64727a0bc89SDoug Rabson 648952a6212SJordan K. Hubbard /* 649952a6212SJordan K. Hubbard * Check for valid cluster size 650952a6212SJordan K. Hubbard * must be a power of 2 651952a6212SJordan K. Hubbard */ 652952a6212SJordan K. Hubbard if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 653952a6212SJordan K. Hubbard error = EINVAL; 654952a6212SJordan K. Hubbard goto error_exit; 655ad63a118SSatoshi Asami } 65627a0bc89SDoug Rabson 65727a0bc89SDoug Rabson /* 65827a0bc89SDoug Rabson * Release the bootsector buffer. 65927a0bc89SDoug Rabson */ 660952a6212SJordan K. Hubbard brelse(bp); 661952a6212SJordan K. Hubbard bp = NULL; 662952a6212SJordan K. Hubbard 663952a6212SJordan K. Hubbard /* 6648d61a735SBruce Evans * Check the fsinfo sector if we have one. Silently fix up our 6658d61a735SBruce Evans * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff) 6668d61a735SBruce Evans * or too large. Ignore fp->fsinfree for now, since we need to 6678d61a735SBruce Evans * read the entire FAT anyway to fill the inuse map. 668952a6212SJordan K. Hubbard */ 669952a6212SJordan K. Hubbard if (pmp->pm_fsinfo) { 670952a6212SJordan K. Hubbard struct fsinfo *fp; 671952a6212SJordan K. Hubbard 67237269429SBruce Evans if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec, 67301f6cfbaSYoshihiro Takahashi NOCRED, &bp)) != 0) 674952a6212SJordan K. Hubbard goto error_exit; 675952a6212SJordan K. Hubbard fp = (struct fsinfo *)bp->b_data; 676952a6212SJordan K. Hubbard if (!bcmp(fp->fsisig1, "RRaA", 4) 677952a6212SJordan K. Hubbard && !bcmp(fp->fsisig2, "rrAa", 4) 678fd7c4230SBruce Evans && !bcmp(fp->fsisig3, "\0\0\125\252", 4)) { 679952a6212SJordan K. Hubbard pmp->pm_nxtfree = getulong(fp->fsinxtfree); 6808d61a735SBruce Evans if (pmp->pm_nxtfree > pmp->pm_maxcluster) 6818bb386f2STim J. Robbins pmp->pm_nxtfree = CLUST_FIRST; 6828bb386f2STim J. Robbins } else 683952a6212SJordan K. Hubbard pmp->pm_fsinfo = 0; 684952a6212SJordan K. Hubbard brelse(bp); 685952a6212SJordan K. Hubbard bp = NULL; 686952a6212SJordan K. Hubbard } 687952a6212SJordan K. Hubbard 688952a6212SJordan K. Hubbard /* 6898d61a735SBruce Evans * Finish initializing pmp->pm_nxtfree (just in case the first few 6908d61a735SBruce Evans * sectors aren't properly reserved in the FAT). This completes 6918d61a735SBruce Evans * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 6928d61a735SBruce Evans * value if there is no fsinfo. We will use pmp->pm_nxtfree 6938d61a735SBruce Evans * internally even if there is no fsinfo. 694952a6212SJordan K. Hubbard */ 6958d61a735SBruce Evans if (pmp->pm_nxtfree < CLUST_FIRST) 6968d61a735SBruce Evans pmp->pm_nxtfree = CLUST_FIRST; 69727a0bc89SDoug Rabson 69827a0bc89SDoug Rabson /* 69927a0bc89SDoug Rabson * Allocate memory for the bitmap of allocated clusters, and then 70027a0bc89SDoug Rabson * fill it in. 70127a0bc89SDoug Rabson */ 7020ef0dd6fSBruce Evans pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS) 70327a0bc89SDoug Rabson * sizeof(*pmp->pm_inusemap), 704a163d034SWarner Losh M_MSDOSFSFAT, M_WAITOK); 70527a0bc89SDoug Rabson 70627a0bc89SDoug Rabson /* 70727a0bc89SDoug Rabson * fillinusemap() needs pm_devvp. 70827a0bc89SDoug Rabson */ 70927a0bc89SDoug Rabson pmp->pm_devvp = devvp; 71027a0bc89SDoug Rabson 71127a0bc89SDoug Rabson /* 71227a0bc89SDoug Rabson * Have the inuse map filled in. 71327a0bc89SDoug Rabson */ 714952a6212SJordan K. Hubbard if ((error = fillinusemap(pmp)) != 0) 71527a0bc89SDoug Rabson goto error_exit; 71627a0bc89SDoug Rabson 71727a0bc89SDoug Rabson /* 71827a0bc89SDoug Rabson * If they want fat updates to be synchronous then let them suffer 71927a0bc89SDoug Rabson * the performance degradation in exchange for the on disk copy of 72027a0bc89SDoug Rabson * the fat being correct just about all the time. I suppose this 72127a0bc89SDoug Rabson * would be a good thing to turn on if the kernel is still flakey. 72227a0bc89SDoug Rabson */ 723952a6212SJordan K. Hubbard if (mp->mnt_flag & MNT_SYNCHRONOUS) 724952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 72527a0bc89SDoug Rabson 72627a0bc89SDoug Rabson /* 72727a0bc89SDoug Rabson * Finish up. 72827a0bc89SDoug Rabson */ 729952a6212SJordan K. Hubbard if (ronly) 730952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_RONLY; 731cede1f56STom Rhodes else { 7323247c9ddSXin LI if ((error = markvoldirty(pmp, 1)) != 0) { 7333247c9ddSXin LI (void)markvoldirty(pmp, 0); 734cede1f56STom Rhodes goto error_exit; 7353247c9ddSXin LI } 73627a0bc89SDoug Rabson pmp->pm_fmod = 1; 737cede1f56STom Rhodes } 73877465d93SAlfred Perlstein mp->mnt_data = pmp; 739939cb752SBruce Evans mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); 740af3f60d5SBruce Evans mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; 7415da56ddbSTor Egge MNT_ILOCK(mp); 742cc9d8990SPeter Wemm mp->mnt_flag |= MNT_LOCAL; 7435da56ddbSTor Egge MNT_IUNLOCK(mp); 74427a0bc89SDoug Rabson 745f458f2a5SCraig Rodrigues if (pmp->pm_flags & MSDOSFS_LARGEFS) 7463bc482ecSTim J. Robbins msdosfs_fileno_init(mp); 7473bc482ecSTim J. Robbins 74827a0bc89SDoug Rabson return 0; 74927a0bc89SDoug Rabson 750952a6212SJordan K. Hubbard error_exit: 751952a6212SJordan K. Hubbard if (bp) 752952a6212SJordan K. Hubbard brelse(bp); 7539a135592SPoul-Henning Kamp if (cp != NULL) { 7549a135592SPoul-Henning Kamp DROP_GIANT(); 7559a135592SPoul-Henning Kamp g_topology_lock(); 7560d7935fdSAttilio Rao g_vfs_close(cp); 7579a135592SPoul-Henning Kamp g_topology_unlock(); 7589a135592SPoul-Henning Kamp PICKUP_GIANT(); 7599a135592SPoul-Henning Kamp } 76027a0bc89SDoug Rabson if (pmp) { 76127a0bc89SDoug Rabson if (pmp->pm_inusemap) 762952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 763952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 76477465d93SAlfred Perlstein mp->mnt_data = NULL; 76527a0bc89SDoug Rabson } 766952a6212SJordan K. Hubbard return (error); 76727a0bc89SDoug Rabson } 76827a0bc89SDoug Rabson 76927a0bc89SDoug Rabson /* 77027a0bc89SDoug Rabson * Unmount the filesystem described by mp. 77127a0bc89SDoug Rabson */ 7727fefffeeSPoul-Henning Kamp static int 773dc9a617aSCraig Rodrigues msdosfs_unmount(struct mount *mp, int mntflags, struct thread *td) 77427a0bc89SDoug Rabson { 775952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 776952a6212SJordan K. Hubbard int error, flags; 77727a0bc89SDoug Rabson 778952a6212SJordan K. Hubbard flags = 0; 779952a6212SJordan K. Hubbard if (mntflags & MNT_FORCE) 78027a0bc89SDoug Rabson flags |= FORCECLOSE; 781f257b7a5SAlfred Perlstein error = vflush(mp, 0, flags, td); 782c3c6d51eSPoul-Henning Kamp if (error) 78327a0bc89SDoug Rabson return error; 784952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 7853247c9ddSXin LI if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) { 7863247c9ddSXin LI error = markvoldirty(pmp, 0); 7873247c9ddSXin LI if (error) { 7883247c9ddSXin LI (void)markvoldirty(pmp, 1); 7893247c9ddSXin LI return (error); 7903247c9ddSXin LI } 7913247c9ddSXin LI } 792c4f02a89SMax Khon if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 793c4f02a89SMax Khon if (pmp->pm_w2u) 794c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_w2u); 795c4f02a89SMax Khon if (pmp->pm_u2w) 796c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2w); 797c4f02a89SMax Khon if (pmp->pm_d2u) 798c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_d2u); 799c4f02a89SMax Khon if (pmp->pm_u2d) 800c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2d); 801c4f02a89SMax Khon } 802cede1f56STom Rhodes 803952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG 804952a6212SJordan K. Hubbard { 805952a6212SJordan K. Hubbard struct vnode *vp = pmp->pm_devvp; 806698b1a66SJeff Roberson struct bufobj *bo; 807952a6212SJordan K. Hubbard 808698b1a66SJeff Roberson bo = &vp->v_bufobj; 809698b1a66SJeff Roberson BO_LOCK(bo); 8104d93c0beSJeff Roberson VI_LOCK(vp); 811f69d42a1SPoul-Henning Kamp vn_printf(vp, 812f69d42a1SPoul-Henning Kamp "msdosfs_umount(): just before calling VOP_CLOSE()\n"); 813952a6212SJordan K. Hubbard printf("freef %p, freeb %p, mount %p\n", 814fc2ffbe6SPoul-Henning Kamp TAILQ_NEXT(vp, v_freelist), vp->v_freelist.tqe_prev, 815952a6212SJordan K. Hubbard vp->v_mount); 816952a6212SJordan K. Hubbard printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", 817156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd), 818156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd), 819156cb265SPoul-Henning Kamp vp->v_bufobj.bo_numoutput, vp->v_type); 8204d93c0beSJeff Roberson VI_UNLOCK(vp); 821698b1a66SJeff Roberson BO_UNLOCK(bo); 822952a6212SJordan K. Hubbard } 823952a6212SJordan K. Hubbard #endif 8249a135592SPoul-Henning Kamp DROP_GIANT(); 8259a135592SPoul-Henning Kamp g_topology_lock(); 8260d7935fdSAttilio Rao g_vfs_close(pmp->pm_cp); 8279a135592SPoul-Henning Kamp g_topology_unlock(); 8289a135592SPoul-Henning Kamp PICKUP_GIANT(); 82927a0bc89SDoug Rabson vrele(pmp->pm_devvp); 830952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT); 8313247c9ddSXin LI if (pmp->pm_flags & MSDOSFS_LARGEFS) 8323bc482ecSTim J. Robbins msdosfs_fileno_free(mp); 833952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT); 83477465d93SAlfred Perlstein mp->mnt_data = NULL; 8355da56ddbSTor Egge MNT_ILOCK(mp); 836cc9d8990SPeter Wemm mp->mnt_flag &= ~MNT_LOCAL; 8375da56ddbSTor Egge MNT_IUNLOCK(mp); 8383247c9ddSXin LI return (0); 83927a0bc89SDoug Rabson } 84027a0bc89SDoug Rabson 8417fefffeeSPoul-Henning Kamp static int 842dc9a617aSCraig Rodrigues msdosfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) 84327a0bc89SDoug Rabson { 844952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 84527a0bc89SDoug Rabson struct denode *ndep; 84627a0bc89SDoug Rabson int error; 84727a0bc89SDoug Rabson 84827a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 849952a6212SJordan K. Hubbard printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 85027a0bc89SDoug Rabson #endif 851952a6212SJordan K. Hubbard error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); 852952a6212SJordan K. Hubbard if (error) 853952a6212SJordan K. Hubbard return (error); 85427a0bc89SDoug Rabson *vpp = DETOV(ndep); 855952a6212SJordan K. Hubbard return (0); 85627a0bc89SDoug Rabson } 85727a0bc89SDoug Rabson 8587fefffeeSPoul-Henning Kamp static int 859dc9a617aSCraig Rodrigues msdosfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) 86027a0bc89SDoug Rabson { 861952a6212SJordan K. Hubbard struct msdosfsmount *pmp; 86227a0bc89SDoug Rabson 863952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp); 86427a0bc89SDoug Rabson sbp->f_bsize = pmp->pm_bpcluster; 86527a0bc89SDoug Rabson sbp->f_iosize = pmp->pm_bpcluster; 866499d3ffaSBoris Popov sbp->f_blocks = pmp->pm_maxcluster + 1; 86727a0bc89SDoug Rabson sbp->f_bfree = pmp->pm_freeclustercount; 86827a0bc89SDoug Rabson sbp->f_bavail = pmp->pm_freeclustercount; 86927a0bc89SDoug Rabson sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 87027a0bc89SDoug Rabson sbp->f_ffree = 0; /* what to put in here? */ 871952a6212SJordan K. Hubbard return (0); 87227a0bc89SDoug Rabson } 87327a0bc89SDoug Rabson 8747fefffeeSPoul-Henning Kamp static int 875dc9a617aSCraig Rodrigues msdosfs_sync(struct mount *mp, int waitfor, struct thread *td) 87627a0bc89SDoug Rabson { 877952a6212SJordan K. Hubbard struct vnode *vp, *nvp; 87827a0bc89SDoug Rabson struct denode *dep; 879952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 880952a6212SJordan K. Hubbard int error, allerror = 0; 88127a0bc89SDoug Rabson 88227a0bc89SDoug Rabson /* 88327a0bc89SDoug Rabson * If we ever switch to not updating all of the fats all the time, 88427a0bc89SDoug Rabson * this would be the place to update them from the first one. 88527a0bc89SDoug Rabson */ 886dfd5dee1SPeter Wemm if (pmp->pm_fmod != 0) { 887952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_RONLY) 88827a0bc89SDoug Rabson panic("msdosfs_sync: rofs mod"); 88927a0bc89SDoug Rabson else { 89027a0bc89SDoug Rabson /* update fats here */ 89127a0bc89SDoug Rabson } 892dfd5dee1SPeter Wemm } 89327a0bc89SDoug Rabson /* 894952a6212SJordan K. Hubbard * Write back each (modified) denode. 89527a0bc89SDoug Rabson */ 896ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 89727a0bc89SDoug Rabson loop: 898e3c5a7a4SPoul-Henning Kamp MNT_VNODE_FOREACH(vp, mp, nvp) { 8994d93c0beSJeff Roberson VI_LOCK(vp); 9008da00465SJeff Roberson if (vp->v_type == VNON || (vp->v_iflag & VI_DOOMED)) { 9014ab2c8bdSJeff Roberson VI_UNLOCK(vp); 9024ab2c8bdSJeff Roberson continue; 9034ab2c8bdSJeff Roberson } 904ca430f2eSAlexander Kabaev MNT_IUNLOCK(mp); 90527a0bc89SDoug Rabson dep = VTODE(vp); 906f00f5d71SPoul-Henning Kamp if ((dep->de_flag & 907c681be37SDmitrij Tejblum (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && 908156cb265SPoul-Henning Kamp (vp->v_bufobj.bo_dirty.bv_cnt == 0 || 909f00f5d71SPoul-Henning Kamp waitfor == MNT_LAZY)) { 9104d93c0beSJeff Roberson VI_UNLOCK(vp); 911ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 91227a0bc89SDoug Rabson continue; 913af3f60d5SBruce Evans } 914b40ce416SJulian Elischer error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); 915af3f60d5SBruce Evans if (error) { 916ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 917af3f60d5SBruce Evans if (error == ENOENT) 91827a0bc89SDoug Rabson goto loop; 919af3f60d5SBruce Evans continue; 920af3f60d5SBruce Evans } 9218df6bac4SPoul-Henning Kamp error = VOP_FSYNC(vp, waitfor, td); 922c3c6d51eSPoul-Henning Kamp if (error) 92327a0bc89SDoug Rabson allerror = error; 92422db15c0SAttilio Rao VOP_UNLOCK(vp, 0); 925cb9ddc80SAlexander Kabaev vrele(vp); 926ca430f2eSAlexander Kabaev MNT_ILOCK(mp); 92727a0bc89SDoug Rabson } 928ca430f2eSAlexander Kabaev MNT_IUNLOCK(mp); 92927a0bc89SDoug Rabson 93027a0bc89SDoug Rabson /* 93127a0bc89SDoug Rabson * Flush filesystem control info. 93227a0bc89SDoug Rabson */ 933c681be37SDmitrij Tejblum if (waitfor != MNT_LAZY) { 934cb05b60aSAttilio Rao vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 9358df6bac4SPoul-Henning Kamp error = VOP_FSYNC(pmp->pm_devvp, waitfor, td); 936c3c6d51eSPoul-Henning Kamp if (error) 93727a0bc89SDoug Rabson allerror = error; 93822db15c0SAttilio Rao VOP_UNLOCK(pmp->pm_devvp, 0); 939c681be37SDmitrij Tejblum } 940952a6212SJordan K. Hubbard return (allerror); 94127a0bc89SDoug Rabson } 94227a0bc89SDoug Rabson 9431be5bc74STom Rhodes static int 9441be5bc74STom Rhodes msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 9451be5bc74STom Rhodes { 9461be5bc74STom Rhodes struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 9471be5bc74STom Rhodes struct defid *defhp = (struct defid *) fhp; 9481be5bc74STom Rhodes struct denode *dep; 9491be5bc74STom Rhodes int error; 9501be5bc74STom Rhodes 9511be5bc74STom Rhodes error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); 9521be5bc74STom Rhodes if (error) { 9531be5bc74STom Rhodes *vpp = NULLVP; 9541be5bc74STom Rhodes return (error); 9551be5bc74STom Rhodes } 9561be5bc74STom Rhodes *vpp = DETOV(dep); 9571be5bc74STom Rhodes vnode_create_vobject(*vpp, dep->de_FileSize, curthread); 9581be5bc74STom Rhodes return (0); 9591be5bc74STom Rhodes } 9601be5bc74STom Rhodes 96130ffadf3SPoul-Henning Kamp static struct vfsops msdosfs_vfsops = { 9621be5bc74STom Rhodes .vfs_fhtovp = msdosfs_fhtovp, 9636a4b48f4SPoul-Henning Kamp .vfs_mount = msdosfs_mount, 9646a4b48f4SPoul-Henning Kamp .vfs_cmount = msdosfs_cmount, 9657652131bSPoul-Henning Kamp .vfs_root = msdosfs_root, 9667652131bSPoul-Henning Kamp .vfs_statfs = msdosfs_statfs, 9677652131bSPoul-Henning Kamp .vfs_sync = msdosfs_sync, 9687652131bSPoul-Henning Kamp .vfs_unmount = msdosfs_unmount, 96927a0bc89SDoug Rabson }; 970c901836cSGarrett Wollman 9714ccd7546SRuslan Ermilov VFS_SET(msdosfs_vfsops, msdosfs, 0); 972c4f02a89SMax Khon MODULE_VERSION(msdosfs, 1); 973