xref: /freebsd/sys/fs/msdosfs/msdosfs_vfsops.c (revision 45d4e82bf61f91792142a2b9e2af657dab8500fd)
1952a6212SJordan K. Hubbard /*	$NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $	*/
227a0bc89SDoug Rabson 
327a0bc89SDoug Rabson /*-
4d63027b6SPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
5d63027b6SPedro F. Giffuni  *
6952a6212SJordan K. Hubbard  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7952a6212SJordan K. Hubbard  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
827a0bc89SDoug Rabson  * All rights reserved.
927a0bc89SDoug Rabson  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
1027a0bc89SDoug Rabson  *
1127a0bc89SDoug Rabson  * Redistribution and use in source and binary forms, with or without
1227a0bc89SDoug Rabson  * modification, are permitted provided that the following conditions
1327a0bc89SDoug Rabson  * are met:
1427a0bc89SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
1527a0bc89SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
1627a0bc89SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
1727a0bc89SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
1827a0bc89SDoug Rabson  *    documentation and/or other materials provided with the distribution.
1927a0bc89SDoug Rabson  * 3. All advertising materials mentioning features or use of this software
2027a0bc89SDoug Rabson  *    must display the following acknowledgement:
2127a0bc89SDoug Rabson  *	This product includes software developed by TooLs GmbH.
2227a0bc89SDoug Rabson  * 4. The name of TooLs GmbH may not be used to endorse or promote products
2327a0bc89SDoug Rabson  *    derived from this software without specific prior written permission.
2427a0bc89SDoug Rabson  *
2527a0bc89SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2627a0bc89SDoug Rabson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2727a0bc89SDoug Rabson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2827a0bc89SDoug Rabson  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2927a0bc89SDoug Rabson  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3027a0bc89SDoug Rabson  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3127a0bc89SDoug Rabson  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3227a0bc89SDoug Rabson  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3327a0bc89SDoug Rabson  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3427a0bc89SDoug Rabson  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3527a0bc89SDoug Rabson  */
36d167cf6fSWarner Losh /*-
3727a0bc89SDoug Rabson  * Written by Paul Popelka (paulp@uts.amdahl.com)
3827a0bc89SDoug Rabson  *
3927a0bc89SDoug Rabson  * You can do anything you want with this software, just don't say you wrote
4027a0bc89SDoug Rabson  * it, and don't remove this notice.
4127a0bc89SDoug Rabson  *
4227a0bc89SDoug Rabson  * This software is provided "as is".
4327a0bc89SDoug Rabson  *
4427a0bc89SDoug Rabson  * The author supplies this software to be publicly redistributed on the
4527a0bc89SDoug Rabson  * understanding that the author is not responsible for the correct
4627a0bc89SDoug Rabson  * functioning of this software in any circumstances and is not liable for
4727a0bc89SDoug Rabson  * any damages caused by this software.
4827a0bc89SDoug Rabson  *
4927a0bc89SDoug Rabson  * October 1992
5027a0bc89SDoug Rabson  */
5127a0bc89SDoug Rabson 
5227a0bc89SDoug Rabson #include <sys/param.h>
5327a0bc89SDoug Rabson #include <sys/systm.h>
546becd1c8SBruce Evans #include <sys/buf.h>
55aaaa4fb5SKonstantin Belousov #include <sys/bufobj.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>
67aaaa4fb5SKonstantin Belousov #include <sys/rwlock.h>
686becd1c8SBruce Evans #include <sys/stat.h>
69b2e4b635SKonstantin Belousov #include <sys/taskqueue.h>
7027a0bc89SDoug Rabson #include <sys/vnode.h>
7127a0bc89SDoug Rabson 
729a135592SPoul-Henning Kamp #include <geom/geom.h>
739a135592SPoul-Henning Kamp #include <geom/geom_vfs.h>
749a135592SPoul-Henning Kamp 
756becd1c8SBruce Evans #include <fs/msdosfs/bootsect.h>
766becd1c8SBruce Evans #include <fs/msdosfs/bpb.h>
776becd1c8SBruce Evans #include <fs/msdosfs/direntry.h>
786becd1c8SBruce Evans #include <fs/msdosfs/denode.h>
796becd1c8SBruce Evans #include <fs/msdosfs/fat.h>
806becd1c8SBruce Evans #include <fs/msdosfs/msdosfsmount.h>
816becd1c8SBruce Evans 
82027bebe8SEd Maste #ifdef MSDOSFS_DEBUG
83027bebe8SEd Maste #include <sys/rwlock.h>
84027bebe8SEd Maste #endif
85027bebe8SEd Maste 
8623b6c230SKonstantin Belousov static const char msdosfs_lock_msg[] = "fatlk";
8723b6c230SKonstantin Belousov 
887c3fc9deSBruce Evans /* Mount options that we support. */
896a4b48f4SPoul-Henning Kamp static const char *msdosfs_opts[] = {
90cb65c1eeSBruce Evans 	"async", "noatime", "noclusterr", "noclusterw",
917c3fc9deSBruce Evans 	"export", "force", "from", "sync",
927c3fc9deSBruce Evans 	"cs_dos", "cs_local", "cs_win", "dirmask",
9340373cf5SKonstantin Belousov 	"gid", "kiconv", "longname",
947c3fc9deSBruce Evans 	"longnames", "mask", "shortname", "shortnames",
957c3fc9deSBruce Evans 	"uid", "win95", "nowin95",
966a4b48f4SPoul-Henning Kamp 	NULL
976a4b48f4SPoul-Henning Kamp };
986a4b48f4SPoul-Henning Kamp 
9901f6cfbaSYoshihiro Takahashi #if 1 /*def PC98*/
10001f6cfbaSYoshihiro Takahashi /*
10101f6cfbaSYoshihiro Takahashi  * XXX - The boot signature formatted by NEC PC-98 DOS looks like a
10201f6cfbaSYoshihiro Takahashi  *       garbage or a random value :-{
10301f6cfbaSYoshihiro Takahashi  *       If you want to use that broken-signatured media, define the
10401f6cfbaSYoshihiro Takahashi  *       following symbol even though PC/AT.
10501f6cfbaSYoshihiro Takahashi  *       (ex. mount PC-98 DOS formatted FD on PC/AT)
10601f6cfbaSYoshihiro Takahashi  */
10701f6cfbaSYoshihiro Takahashi #define	MSDOSFS_NOCHECKSIG
10801f6cfbaSYoshihiro Takahashi #endif
10901f6cfbaSYoshihiro Takahashi 
1105bb84bc8SRobert Watson MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure");
1115bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table");
11255166637SPoul-Henning Kamp 
1132d7c6b27SBruce Evans struct iconv_functions *msdosfs_iconv;
114c4f02a89SMax Khon 
1156a4b48f4SPoul-Henning Kamp static int	update_mp(struct mount *mp, struct thread *td);
1160d7935fdSAttilio Rao static int	mountmsdosfs(struct vnode *devvp, struct mount *mp);
117b2e4b635SKonstantin Belousov static void	msdosfs_remount_ro(void *arg, int pending);
1181be5bc74STom Rhodes static vfs_fhtovp_t	msdosfs_fhtovp;
1196a4b48f4SPoul-Henning Kamp static vfs_mount_t	msdosfs_mount;
1209bf1a756SPoul-Henning Kamp static vfs_root_t	msdosfs_root;
1219bf1a756SPoul-Henning Kamp static vfs_statfs_t	msdosfs_statfs;
1229bf1a756SPoul-Henning Kamp static vfs_sync_t	msdosfs_sync;
1239bf1a756SPoul-Henning Kamp static vfs_unmount_t	msdosfs_unmount;
124af482601SBruce Evans 
1251a9415afSTim J. Robbins /* Maximum length of a character set name (arbitrary). */
1261a9415afSTim J. Robbins #define	MAXCSLEN	64
1271a9415afSTim J. Robbins 
128952a6212SJordan K. Hubbard static int
update_mp(struct mount * mp,struct thread * td)129dc9a617aSCraig Rodrigues update_mp(struct mount *mp, struct thread *td)
130952a6212SJordan K. Hubbard {
131952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1326a4b48f4SPoul-Henning Kamp 	void *dos, *win, *local;
1336a4b48f4SPoul-Henning Kamp 	int error, v;
134952a6212SJordan K. Hubbard 
1356a4b48f4SPoul-Henning Kamp 	if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) {
1366a4b48f4SPoul-Henning Kamp 		if (msdosfs_iconv != NULL) {
1376a4b48f4SPoul-Henning Kamp 			error = vfs_getopt(mp->mnt_optnew,
1386a4b48f4SPoul-Henning Kamp 			    "cs_win", &win, NULL);
1396a4b48f4SPoul-Henning Kamp 			if (!error)
1406a4b48f4SPoul-Henning Kamp 				error = vfs_getopt(mp->mnt_optnew,
1416a4b48f4SPoul-Henning Kamp 				    "cs_local", &local, NULL);
1426a4b48f4SPoul-Henning Kamp 			if (!error)
1436a4b48f4SPoul-Henning Kamp 				error = vfs_getopt(mp->mnt_optnew,
1446a4b48f4SPoul-Henning Kamp 				    "cs_dos", &dos, NULL);
1456a4b48f4SPoul-Henning Kamp 			if (!error) {
1461a9415afSTim J. Robbins 				msdosfs_iconv->open(win, local, &pmp->pm_u2w);
1471a9415afSTim J. Robbins 				msdosfs_iconv->open(local, win, &pmp->pm_w2u);
1481a9415afSTim J. Robbins 				msdosfs_iconv->open(dos, local, &pmp->pm_u2d);
1491a9415afSTim J. Robbins 				msdosfs_iconv->open(local, dos, &pmp->pm_d2u);
1506a4b48f4SPoul-Henning Kamp 			}
1511a9415afSTim J. Robbins 			if (error != 0)
1521a9415afSTim J. Robbins 				return (error);
153c4f02a89SMax Khon 		} else {
154c4f02a89SMax Khon 			pmp->pm_w2u = NULL;
155c4f02a89SMax Khon 			pmp->pm_u2w = NULL;
156c4f02a89SMax Khon 			pmp->pm_d2u = NULL;
157c4f02a89SMax Khon 			pmp->pm_u2d = NULL;
1587391f611SAndrey A. Chernov 		}
1591a9415afSTim J. Robbins 	}
1601a9415afSTim J. Robbins 
161dd104b33SKevin Lo 	if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1)
1626a4b48f4SPoul-Henning Kamp 		pmp->pm_gid = v;
163dd104b33SKevin Lo 	if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1)
1646a4b48f4SPoul-Henning Kamp 		pmp->pm_uid = v;
165dd104b33SKevin Lo 	if (vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v) == 1)
1666a4b48f4SPoul-Henning Kamp 		pmp->pm_mask = v & ALLPERMS;
167dd104b33SKevin Lo 	if (vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v) == 1)
1686a4b48f4SPoul-Henning Kamp 		pmp->pm_dirmask = v & ALLPERMS;
1696a4b48f4SPoul-Henning Kamp 	vfs_flagopt(mp->mnt_optnew, "shortname",
1706a4b48f4SPoul-Henning Kamp 	    &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
1714ab12573SCraig Rodrigues 	vfs_flagopt(mp->mnt_optnew, "shortnames",
1724ab12573SCraig Rodrigues 	    &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
1736a4b48f4SPoul-Henning Kamp 	vfs_flagopt(mp->mnt_optnew, "longname",
1746a4b48f4SPoul-Henning Kamp 	    &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
1754ab12573SCraig Rodrigues 	vfs_flagopt(mp->mnt_optnew, "longnames",
1764ab12573SCraig Rodrigues 	    &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
1776a4b48f4SPoul-Henning Kamp 	vfs_flagopt(mp->mnt_optnew, "kiconv",
1786a4b48f4SPoul-Henning Kamp 	    &pmp->pm_flags, MSDOSFSMNT_KICONV);
1796a4b48f4SPoul-Henning Kamp 
180d75b2048SCraig Rodrigues 	if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0)
1816a4b48f4SPoul-Henning Kamp 		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
182d75b2048SCraig Rodrigues 	else
183d75b2048SCraig Rodrigues 		pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95;
184952a6212SJordan K. Hubbard 
185952a6212SJordan K. Hubbard 	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
186952a6212SJordan K. Hubbard 		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
1870696afbeSAlan Somers 	else
188952a6212SJordan K. Hubbard 		pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
189952a6212SJordan K. Hubbard 	return 0;
190952a6212SJordan K. Hubbard }
191952a6212SJordan K. Hubbard 
1926a4b48f4SPoul-Henning Kamp static int
msdosfs_cmount(struct mntarg * ma,void * data,uint64_t flags)193cc672d35SKirk McKusick msdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1946a4b48f4SPoul-Henning Kamp {
1956a4b48f4SPoul-Henning Kamp 	struct msdosfs_args args;
1966a4b48f4SPoul-Henning Kamp 	int error;
1976a4b48f4SPoul-Henning Kamp 
1986a4b48f4SPoul-Henning Kamp 	if (data == NULL)
1996a4b48f4SPoul-Henning Kamp 		return (EINVAL);
2006a4b48f4SPoul-Henning Kamp 	error = copyin(data, &args, sizeof args);
2016a4b48f4SPoul-Henning Kamp 	if (error)
2026a4b48f4SPoul-Henning Kamp 		return (error);
2036a4b48f4SPoul-Henning Kamp 
2046a4b48f4SPoul-Henning Kamp 	ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN);
2051f7104d7SRick Macklem 	ma = mount_arg(ma, "export", &args.export, sizeof(args.export));
2066a4b48f4SPoul-Henning Kamp 	ma = mount_argf(ma, "uid", "%d", args.uid);
2076a4b48f4SPoul-Henning Kamp 	ma = mount_argf(ma, "gid", "%d", args.gid);
2086a4b48f4SPoul-Henning Kamp 	ma = mount_argf(ma, "mask", "%d", args.mask);
2096a4b48f4SPoul-Henning Kamp 	ma = mount_argf(ma, "dirmask", "%d", args.dirmask);
2106a4b48f4SPoul-Henning Kamp 
2116a4b48f4SPoul-Henning Kamp 	ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname");
2126a4b48f4SPoul-Henning Kamp 	ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname");
2136a4b48f4SPoul-Henning Kamp 	ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95");
2146a4b48f4SPoul-Henning Kamp 	ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv");
2156a4b48f4SPoul-Henning Kamp 
2166a4b48f4SPoul-Henning Kamp 	ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN);
2176a4b48f4SPoul-Henning Kamp 	ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN);
2186a4b48f4SPoul-Henning Kamp 	ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN);
2196a4b48f4SPoul-Henning Kamp 
2206a4b48f4SPoul-Henning Kamp 	error = kernel_mount(ma, flags);
2216a4b48f4SPoul-Henning Kamp 
2226a4b48f4SPoul-Henning Kamp 	return (error);
2236a4b48f4SPoul-Henning Kamp }
2246a4b48f4SPoul-Henning Kamp 
22527a0bc89SDoug Rabson /*
22627a0bc89SDoug Rabson  * mp - path - addr in user space of mount point (ie /usr or whatever)
22727a0bc89SDoug Rabson  * data - addr in user space of mount params including the name of the block
22827a0bc89SDoug Rabson  * special file to treat as a filesystem.
22927a0bc89SDoug Rabson  */
2307fefffeeSPoul-Henning Kamp static int
msdosfs_mount(struct mount * mp)231dfd233edSAttilio Rao msdosfs_mount(struct mount *mp)
23227a0bc89SDoug Rabson {
233aaaa4fb5SKonstantin Belousov 	struct vnode *devvp, *odevvp;	  /* vnode for blk device to mount */
234dfd233edSAttilio Rao 	struct thread *td;
235952a6212SJordan K. Hubbard 	/* msdosfs specific mount control block */
236952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = NULL;
2375e8c582aSPoul-Henning Kamp 	struct nameidata ndp;
238269c902fSPoul-Henning Kamp 	int error, flags;
23915bc6b2bSEdward Tomasz Napierala 	accmode_t accmode;
2406a4b48f4SPoul-Henning Kamp 	char *from;
24127a0bc89SDoug Rabson 
242dfd233edSAttilio Rao 	td = curthread;
2436a4b48f4SPoul-Henning Kamp 	if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts))
2446a4b48f4SPoul-Henning Kamp 		return (EINVAL);
2456a4b48f4SPoul-Henning Kamp 
24627a0bc89SDoug Rabson 	/*
247952a6212SJordan K. Hubbard 	 * If updating, check whether changing from read-only to
248952a6212SJordan K. Hubbard 	 * read/write; if there is no device name, that's all we do.
24927a0bc89SDoug Rabson 	 */
25027a0bc89SDoug Rabson 	if (mp->mnt_flag & MNT_UPDATE) {
2512d7c6b27SBruce Evans 		pmp = VFSTOMSDOSFS(mp);
2529a135592SPoul-Henning Kamp 		if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
2536a4b48f4SPoul-Henning Kamp 		    vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
25469367793SKonstantin Belousov 			if ((error = vn_start_write(NULL, &mp, V_WAIT)) != 0)
255e83f1423SPoul-Henning Kamp 				return (error);
25669367793SKonstantin Belousov 			error = vfs_write_suspend_umnt(mp);
25769367793SKonstantin Belousov 			if (error != 0)
25869367793SKonstantin Belousov 				return (error);
25969367793SKonstantin Belousov 
26027a0bc89SDoug Rabson 			flags = WRITECLOSE;
26127a0bc89SDoug Rabson 			if (mp->mnt_flag & MNT_FORCE)
26227a0bc89SDoug Rabson 				flags |= FORCECLOSE;
263f257b7a5SAlfred Perlstein 			error = vflush(mp, 0, flags, td);
26469367793SKonstantin Belousov 			if (error != 0) {
26569367793SKonstantin Belousov 				vfs_write_resume(mp, 0);
2666a4b48f4SPoul-Henning Kamp 				return (error);
26769367793SKonstantin Belousov 			}
2683247c9ddSXin LI 
2693247c9ddSXin LI 			/*
2703247c9ddSXin LI 			 * Now the volume is clean.  Mark it so while the
2713247c9ddSXin LI 			 * device is still rw.
2723247c9ddSXin LI 			 */
2733247c9ddSXin LI 			error = markvoldirty(pmp, 0);
27469367793SKonstantin Belousov 			if (error != 0) {
27569367793SKonstantin Belousov 				vfs_write_resume(mp, 0);
2763247c9ddSXin LI 				(void)markvoldirty(pmp, 1);
2773247c9ddSXin LI 				return (error);
2783247c9ddSXin LI 			}
2793247c9ddSXin LI 
2803247c9ddSXin LI 			/* Downgrade the device from rw to ro. */
2819a135592SPoul-Henning Kamp 			g_topology_lock();
2824eb3abf0SBruce Evans 			error = g_access(pmp->pm_cp, 0, -1, 0);
2839a135592SPoul-Henning Kamp 			g_topology_unlock();
2843247c9ddSXin LI 			if (error) {
28569367793SKonstantin Belousov 				vfs_write_resume(mp, 0);
2863247c9ddSXin LI 				(void)markvoldirty(pmp, 1);
2874eb3abf0SBruce Evans 				return (error);
2883247c9ddSXin LI 			}
2894eb3abf0SBruce Evans 
2903247c9ddSXin LI 			/*
2913247c9ddSXin LI 			 * Backing out after an error was painful in the
2923247c9ddSXin LI 			 * above.  Now we are committed to succeeding.
2933247c9ddSXin LI 			 */
2943247c9ddSXin LI 			pmp->pm_fmod = 0;
2953247c9ddSXin LI 			pmp->pm_flags |= MSDOSFSMNT_RONLY;
2963247c9ddSXin LI 			MNT_ILOCK(mp);
2973247c9ddSXin LI 			mp->mnt_flag |= MNT_RDONLY;
2983247c9ddSXin LI 			MNT_IUNLOCK(mp);
29969367793SKonstantin Belousov 			vfs_write_resume(mp, 0);
3006a4b48f4SPoul-Henning Kamp 		} else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
3016a4b48f4SPoul-Henning Kamp 		    !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
302952a6212SJordan K. Hubbard 			/*
303952a6212SJordan K. Hubbard 			 * If upgrade to read-write by non-root, then verify
304952a6212SJordan K. Hubbard 			 * that user has necessary permissions on the device.
305952a6212SJordan K. Hubbard 			 */
306aaaa4fb5SKonstantin Belousov 			odevvp = pmp->pm_odevvp;
307aaaa4fb5SKonstantin Belousov 			vn_lock(odevvp, LK_EXCLUSIVE | LK_RETRY);
308aaaa4fb5SKonstantin Belousov 			error = VOP_ACCESS(odevvp, VREAD | VWRITE,
309a854ed98SJohn Baldwin 			    td->td_ucred, td);
310acd3428bSRobert Watson 			if (error)
311acd3428bSRobert Watson 				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
312952a6212SJordan K. Hubbard 			if (error) {
313aaaa4fb5SKonstantin Belousov 				VOP_UNLOCK(odevvp);
314952a6212SJordan K. Hubbard 				return (error);
315952a6212SJordan K. Hubbard 			}
316aaaa4fb5SKonstantin Belousov 			VOP_UNLOCK(odevvp);
3179a135592SPoul-Henning Kamp 			g_topology_lock();
3189a135592SPoul-Henning Kamp 			error = g_access(pmp->pm_cp, 0, 1, 0);
3199a135592SPoul-Henning Kamp 			g_topology_unlock();
3209a135592SPoul-Henning Kamp 			if (error)
3219a135592SPoul-Henning Kamp 				return (error);
322cede1f56STom Rhodes 
323aaa38524SConrad Meyer 			/* Now that the volume is modifiable, mark it dirty. */
324aaa38524SConrad Meyer 			error = markvoldirty_upgrade(pmp, true, true);
325aaa38524SConrad Meyer 			if (error) {
326aaa38524SConrad Meyer 				/*
327aaa38524SConrad Meyer 				 * If dirtying the superblock failed, drop GEOM
328aaa38524SConrad Meyer 				 * 'w' refs (we're still RO).
329aaa38524SConrad Meyer 				 */
330aaa38524SConrad Meyer 				g_topology_lock();
331aaa38524SConrad Meyer 				(void)g_access(pmp->pm_cp, 0, -1, 0);
332aaa38524SConrad Meyer 				g_topology_unlock();
333aaa38524SConrad Meyer 
334aaa38524SConrad Meyer 				return (error);
335aaa38524SConrad Meyer 			}
336aaa38524SConrad Meyer 
3373247c9ddSXin LI 			pmp->pm_fmod = 1;
3383247c9ddSXin LI 			pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
3393247c9ddSXin LI 			MNT_ILOCK(mp);
3403247c9ddSXin LI 			mp->mnt_flag &= ~MNT_RDONLY;
3413247c9ddSXin LI 			MNT_IUNLOCK(mp);
34282c59ec6SCraig Rodrigues 		}
343b2e4b635SKonstantin Belousov 
344b2e4b635SKonstantin Belousov 		/*
345b2e4b635SKonstantin Belousov 		 * Avoid namei() below.  The "from" option is not set.
346b2e4b635SKonstantin Belousov 		 * Update of the devvp is pointless for this case.
347b2e4b635SKonstantin Belousov 		 */
348b2e4b635SKonstantin Belousov 		if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
349b2e4b635SKonstantin Belousov 			return (0);
350952a6212SJordan K. Hubbard 	}
35127a0bc89SDoug Rabson 	/*
352952a6212SJordan K. Hubbard 	 * Not an update, or updating the name: look up the name
353e9827c6dSBruce Evans 	 * and verify that it refers to a sensible disk device.
35427a0bc89SDoug Rabson 	 */
3556a4b48f4SPoul-Henning Kamp 	if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL))
3566a4b48f4SPoul-Henning Kamp 		return (EINVAL);
3577e1d3eefSMateusz Guzik 	NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
3585e8c582aSPoul-Henning Kamp 	error = namei(&ndp);
359952a6212SJordan K. Hubbard 	if (error)
360952a6212SJordan K. Hubbard 		return (error);
3615e8c582aSPoul-Henning Kamp 	devvp = ndp.ni_vp;
362bb92cd7bSMateusz Guzik 	NDFREE_PNBUF(&ndp);
363952a6212SJordan K. Hubbard 
3647ad2a82dSMateusz Guzik 	if (!vn_isdisk_error(devvp, &error)) {
36575d7ba93SSuleiman Souhlal 		vput(devvp);
366ba4ad1fcSPoul-Henning Kamp 		return (error);
36727a0bc89SDoug Rabson 	}
36827a0bc89SDoug Rabson 	/*
369952a6212SJordan K. Hubbard 	 * If mount by non-root, then verify that user has necessary
370952a6212SJordan K. Hubbard 	 * permissions on the device.
37127a0bc89SDoug Rabson 	 */
37215bc6b2bSEdward Tomasz Napierala 	accmode = VREAD;
373952a6212SJordan K. Hubbard 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
37415bc6b2bSEdward Tomasz Napierala 		accmode |= VWRITE;
37515bc6b2bSEdward Tomasz Napierala 	error = VOP_ACCESS(devvp, accmode, td->td_ucred, td);
376acd3428bSRobert Watson 	if (error)
377acd3428bSRobert Watson 		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
378952a6212SJordan K. Hubbard 	if (error) {
379952a6212SJordan K. Hubbard 		vput(devvp);
380952a6212SJordan K. Hubbard 		return (error);
381952a6212SJordan K. Hubbard 	}
382952a6212SJordan K. Hubbard 	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
3830d7935fdSAttilio Rao 		error = mountmsdosfs(devvp, mp);
384952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG		/* only needed for the printf below */
385952a6212SJordan K. Hubbard 		pmp = VFSTOMSDOSFS(mp);
386952a6212SJordan K. Hubbard #endif
387952a6212SJordan K. Hubbard 	} else {
38875d7ba93SSuleiman Souhlal 		vput(devvp);
389aaaa4fb5SKonstantin Belousov 		if (devvp != pmp->pm_odevvp)
3906c0358ccSKonstantin Belousov 			return (EINVAL);	/* XXX needs translation */
39127a0bc89SDoug Rabson 	}
39227a0bc89SDoug Rabson 	if (error) {
39327a0bc89SDoug Rabson 		vrele(devvp);
394952a6212SJordan K. Hubbard 		return (error);
395952a6212SJordan K. Hubbard 	}
396952a6212SJordan K. Hubbard 
3976a4b48f4SPoul-Henning Kamp 	error = update_mp(mp, td);
398952a6212SJordan K. Hubbard 	if (error) {
3991a9415afSTim J. Robbins 		if ((mp->mnt_flag & MNT_UPDATE) == 0)
400dfd233edSAttilio Rao 			msdosfs_unmount(mp, MNT_FORCE);
40127a0bc89SDoug Rabson 		return error;
40227a0bc89SDoug Rabson 	}
4036a4b48f4SPoul-Henning Kamp 
4046a4b48f4SPoul-Henning Kamp 	vfs_mountedfrom(mp, from);
40527a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
4066a4b48f4SPoul-Henning Kamp 	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
40727a0bc89SDoug Rabson #endif
408952a6212SJordan K. Hubbard 	return (0);
40927a0bc89SDoug Rabson }
41027a0bc89SDoug Rabson 
411c33db74bSStefan Eßer /*
412c33db74bSStefan Eßer  * The FAT12 and FAT16 file systems use a limited size root directory that
413c33db74bSStefan Eßer  * can be created with 1 to 65535 entries for files, directories, or a disk
414c33db74bSStefan Eßer  * label (but DOS or Windows creates at most 512 root directory entries).
415c33db74bSStefan Eßer  * This function calculates the number of free root directory entries by
416c33db74bSStefan Eßer  * counting the non-deleted entries (not starting with 0xE5) and by adding
417c33db74bSStefan Eßer  * the amount of never used entries (with the position indicated by an
418c33db74bSStefan Eßer  * entry that starts with 0x00).
419c33db74bSStefan Eßer  */
420c33db74bSStefan Eßer static int
rootdir_free(struct msdosfsmount * pmp)421c33db74bSStefan Eßer rootdir_free(struct msdosfsmount* pmp)
422c33db74bSStefan Eßer {
423c33db74bSStefan Eßer 	struct buf *bp;
424c33db74bSStefan Eßer 	struct direntry *dep;
425c33db74bSStefan Eßer 	u_long readsize;
426c33db74bSStefan Eßer 	int dirclu;
427c33db74bSStefan Eßer 	int diridx;
428c33db74bSStefan Eßer 	int dirmax;
429c33db74bSStefan Eßer 	int dirleft;
430c33db74bSStefan Eßer 	int ffree;
431c33db74bSStefan Eßer 
432c33db74bSStefan Eßer 	dirclu = pmp->pm_rootdirblk;
433c33db74bSStefan Eßer 
434c33db74bSStefan Eßer 	/*
435c33db74bSStefan Eßer 	 * The msdosfs code ignores pm_RootDirEnts and uses pm_rootdirsize
436c33db74bSStefan Eßer 	 * (measured in DEV_BSIZE) to prevent excess root dir allocations.
437c33db74bSStefan Eßer 	 */
438c33db74bSStefan Eßer 	dirleft = howmany(pmp->pm_rootdirsize * DEV_BSIZE,
439c33db74bSStefan Eßer 			  sizeof(struct direntry));
440c33db74bSStefan Eßer 
441c33db74bSStefan Eßer 	/* Read in chunks of default maximum root directory size */
442c33db74bSStefan Eßer 	readsize = 512 * sizeof(struct direntry);
443c33db74bSStefan Eßer 
444c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
445c33db74bSStefan Eßer 	printf("rootdir_free: blkpersec=%lu fatblksize=%lu dirsize=%lu "
446c33db74bSStefan Eßer 	    "firstclu=%lu dirclu=%d entries=%d rootdirsize=%lu "
447c33db74bSStefan Eßer 	    "bytespersector=%hu bytepercluster=%lu\n",
448c33db74bSStefan Eßer 	    pmp->pm_BlkPerSec, pmp->pm_fatblocksize, readsize,
449c33db74bSStefan Eßer 	    pmp->pm_firstcluster, dirclu, dirleft, pmp->pm_rootdirsize,
450c33db74bSStefan Eßer 	    pmp->pm_BytesPerSec, pmp->pm_bpcluster);
451c33db74bSStefan Eßer #endif
452c33db74bSStefan Eßer 	ffree = dirleft;
453c33db74bSStefan Eßer 	while (dirleft > 0 && ffree > 0) {
454c33db74bSStefan Eßer 		if (readsize > dirleft * sizeof(struct direntry))
455c33db74bSStefan Eßer 			readsize = dirleft * sizeof(struct direntry);
456c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
457c33db74bSStefan Eßer 		printf("rootdir_free: dirclu=%d dirleft=%d readsize=%lu\n",
458c33db74bSStefan Eßer 		       dirclu, dirleft, readsize);
459c33db74bSStefan Eßer #endif
460c33db74bSStefan Eßer 		if (bread(pmp->pm_devvp, dirclu, readsize, NOCRED, &bp) != 0) {
461c33db74bSStefan Eßer 			printf("rootdir_free: read error\n");
462c33db74bSStefan Eßer 			if (bp != NULL)
463c33db74bSStefan Eßer 				brelse(bp);
464c33db74bSStefan Eßer 			return (-1);
465c33db74bSStefan Eßer 		}
466c33db74bSStefan Eßer 		dirmax = readsize / sizeof(struct direntry);
467c33db74bSStefan Eßer 		for (diridx = 0; diridx < dirmax && dirleft > 0;
468c33db74bSStefan Eßer 		     diridx++, dirleft--) {
469c33db74bSStefan Eßer 			dep = (struct direntry*)bp->b_data + diridx;
470c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
471c33db74bSStefan Eßer 			if (dep->deName[0] == SLOT_DELETED)
472c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d <deleted>\n",
473c33db74bSStefan Eßer 				    diridx);
474c33db74bSStefan Eßer 			else if (dep->deName[0] == SLOT_EMPTY)
475c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d <end marker>\n",
476c33db74bSStefan Eßer 				    diridx);
477c33db74bSStefan Eßer 			else if (dep->deAttributes == ATTR_WIN95)
478c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d <LFN part %d>\n",
479c33db74bSStefan Eßer 				    diridx, (dep->deName[0] & 0x1f) + 1);
480c33db74bSStefan Eßer 			else if (dep->deAttributes & ATTR_VOLUME)
481c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d label='%11.11s'\n",
482c33db74bSStefan Eßer 				    diridx, dep->deName);
483c33db74bSStefan Eßer 			else if (dep->deAttributes & ATTR_DIRECTORY)
484c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d dir='%11.11s'\n",
485c33db74bSStefan Eßer 				    diridx, dep->deName);
486c33db74bSStefan Eßer 			else
487c33db74bSStefan Eßer 				printf("rootdir_free: idx=%d file='%11.11s'\n",
488c33db74bSStefan Eßer 				    diridx, dep->deName);
489c33db74bSStefan Eßer #endif
490c33db74bSStefan Eßer 			if (dep->deName[0] == SLOT_EMPTY)
491c33db74bSStefan Eßer 				dirleft = 0;
492c33db74bSStefan Eßer 			else if (dep->deName[0] != SLOT_DELETED)
493c33db74bSStefan Eßer 				ffree--;
494c33db74bSStefan Eßer 		}
495c33db74bSStefan Eßer 		brelse(bp);
496c33db74bSStefan Eßer 		bp = NULL;
497c33db74bSStefan Eßer 		dirclu += readsize / DEV_BSIZE;
498c33db74bSStefan Eßer 	}
499c33db74bSStefan Eßer 	return (ffree);
500c33db74bSStefan Eßer }
501c33db74bSStefan Eßer 
5027fefffeeSPoul-Henning Kamp static int
mountmsdosfs(struct vnode * odevvp,struct mount * mp)503aaaa4fb5SKonstantin Belousov mountmsdosfs(struct vnode *odevvp, struct mount *mp)
50427a0bc89SDoug Rabson {
505952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp;
506952a6212SJordan K. Hubbard 	struct buf *bp;
507c72ae142SJohn Baldwin 	struct cdev *dev;
508aaaa4fb5SKonstantin Belousov 	struct vnode *devvp;
50927a0bc89SDoug Rabson 	union bootsector *bsp;
51027a0bc89SDoug Rabson 	struct byte_bpb33 *b33;
51127a0bc89SDoug Rabson 	struct byte_bpb50 *b50;
512952a6212SJordan K. Hubbard 	struct byte_bpb710 *b710;
51323c53312SEd Maste 	uint8_t SecPerClust;
514499d3ffaSBoris Popov 	u_long clusters;
515952a6212SJordan K. Hubbard 	int ronly, error;
5169a135592SPoul-Henning Kamp 	struct g_consumer *cp;
5179a135592SPoul-Henning Kamp 	struct bufobj *bo;
51827a0bc89SDoug Rabson 
519c72ae142SJohn Baldwin 	bp = NULL;		/* This and pmp both used in error_exit. */
520c72ae142SJohn Baldwin 	pmp = NULL;
5214eb3abf0SBruce Evans 	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
522c72ae142SJohn Baldwin 
523aaaa4fb5SKonstantin Belousov 	devvp = mntfs_allocvp(mp, odevvp);
524c72ae142SJohn Baldwin 	dev = devvp->v_rdev;
525bb8297e6SKonstantin Belousov 	if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0,
526bb8297e6SKonstantin Belousov 	    (uintptr_t)mp) == 0) {
527aaaa4fb5SKonstantin Belousov 		mntfs_freevp(devvp);
528bb8297e6SKonstantin Belousov 		return (EBUSY);
529bb8297e6SKonstantin Belousov 	}
5309a135592SPoul-Henning Kamp 	g_topology_lock();
5314eb3abf0SBruce Evans 	error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1);
5329a135592SPoul-Henning Kamp 	g_topology_unlock();
533bb8297e6SKonstantin Belousov 	if (error != 0) {
534bb8297e6SKonstantin Belousov 		atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
535aaaa4fb5SKonstantin Belousov 		mntfs_freevp(devvp);
536bb8297e6SKonstantin Belousov 		return (error);
537bb8297e6SKonstantin Belousov 	}
538bb8297e6SKonstantin Belousov 	dev_ref(dev);
5399a135592SPoul-Henning Kamp 	bo = &devvp->v_bufobj;
540aaaa4fb5SKonstantin Belousov 	BO_LOCK(&odevvp->v_bufobj);
541aaaa4fb5SKonstantin Belousov 	odevvp->v_bufobj.bo_flag |= BO_NOBUFS;
542aaaa4fb5SKonstantin Belousov 	BO_UNLOCK(&odevvp->v_bufobj);
543b249ce48SMateusz Guzik 	VOP_UNLOCK(devvp);
5448ec22c4dSBruce Evans 	if (dev->si_iosize_max != 0)
5458ec22c4dSBruce Evans 		mp->mnt_iosize_max = dev->si_iosize_max;
546cd853791SKonstantin Belousov 	if (mp->mnt_iosize_max > maxphys)
547cd853791SKonstantin Belousov 		mp->mnt_iosize_max = maxphys;
548952a6212SJordan K. Hubbard 
54927a0bc89SDoug Rabson 	/*
550952a6212SJordan K. Hubbard 	 * Read the boot sector of the filesystem, and then check the
551952a6212SJordan K. Hubbard 	 * boot signature.  If not a dos boot sector then error out.
55201f6cfbaSYoshihiro Takahashi 	 *
55393fe42b6SBruce Evans 	 * NOTE: 8192 is a magic size that works for ffs.
55427a0bc89SDoug Rabson 	 */
55593fe42b6SBruce Evans 	error = bread(devvp, 0, 8192, NOCRED, &bp);
556c3c6d51eSPoul-Henning Kamp 	if (error)
55727a0bc89SDoug Rabson 		goto error_exit;
558952a6212SJordan K. Hubbard 	bp->b_flags |= B_AGE;
559952a6212SJordan K. Hubbard 	bsp = (union bootsector *)bp->b_data;
56027a0bc89SDoug Rabson 	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
56127a0bc89SDoug Rabson 	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
56201a4d019SJohn Baldwin 	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
563952a6212SJordan K. Hubbard 
56401f6cfbaSYoshihiro Takahashi #ifndef MSDOSFS_NOCHECKSIG
56504fd468dSKonstantin Belousov 	if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 ||
56604fd468dSKonstantin Belousov 	    bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
56727a0bc89SDoug Rabson 		error = EINVAL;
56827a0bc89SDoug Rabson 		goto error_exit;
56927a0bc89SDoug Rabson 	}
57001f6cfbaSYoshihiro Takahashi #endif
57127a0bc89SDoug Rabson 
57204fd468dSKonstantin Belousov 	pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK | M_ZERO);
57327a0bc89SDoug Rabson 	pmp->pm_mountp = mp;
5749a135592SPoul-Henning Kamp 	pmp->pm_cp = cp;
5759a135592SPoul-Henning Kamp 	pmp->pm_bo = bo;
57627a0bc89SDoug Rabson 
57723b6c230SKonstantin Belousov 	lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0);
5786ae13c0fSKonstantin Belousov 	lockinit(&pmp->pm_checkpath_lock, 0, "msdoscp", 0, 0);
57923b6c230SKonstantin Belousov 
580b2e4b635SKonstantin Belousov 	TASK_INIT(&pmp->pm_rw2ro_task, 0, msdosfs_remount_ro, pmp);
581b2e4b635SKonstantin Belousov 
58227a0bc89SDoug Rabson 	/*
5834eb3abf0SBruce Evans 	 * Initialize ownerships and permissions, since nothing else will
58423c1e989SMaxim Konovalov 	 * initialize them iff we are mounting root.
5854eb3abf0SBruce Evans 	 */
5864eb3abf0SBruce Evans 	pmp->pm_uid = UID_ROOT;
5874eb3abf0SBruce Evans 	pmp->pm_gid = GID_WHEEL;
5884eb3abf0SBruce Evans 	pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH |
5894eb3abf0SBruce Evans 	    S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
5904eb3abf0SBruce Evans 
5914eb3abf0SBruce Evans 	/*
59227a0bc89SDoug Rabson 	 * Compute several useful quantities from the bpb in the
59327a0bc89SDoug Rabson 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
59427a0bc89SDoug Rabson 	 * the fields that are different between dos 5 and dos 3.3.
59527a0bc89SDoug Rabson 	 */
596952a6212SJordan K. Hubbard 	SecPerClust = b50->bpbSecPerClust;
59727a0bc89SDoug Rabson 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
598696f22f0SJohn Baldwin 	if (pmp->pm_BytesPerSec < DEV_BSIZE) {
599696f22f0SJohn Baldwin 		error = EINVAL;
600696f22f0SJohn Baldwin 		goto error_exit;
601696f22f0SJohn Baldwin 	}
60227a0bc89SDoug Rabson 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
60327a0bc89SDoug Rabson 	pmp->pm_FATs = b50->bpbFATs;
60427a0bc89SDoug Rabson 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
60527a0bc89SDoug Rabson 	pmp->pm_Sectors = getushort(b50->bpbSectors);
60627a0bc89SDoug Rabson 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
60727a0bc89SDoug Rabson 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
60827a0bc89SDoug Rabson 	pmp->pm_Heads = getushort(b50->bpbHeads);
609952a6212SJordan K. Hubbard 	pmp->pm_Media = b50->bpbMedia;
61027a0bc89SDoug Rabson 
61101f6cfbaSYoshihiro Takahashi 	/* calculate the ratio of sector size to DEV_BSIZE */
61201f6cfbaSYoshihiro Takahashi 	pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE;
61301f6cfbaSYoshihiro Takahashi 
614043ec583SMarcel Moolenaar 	/*
615043ec583SMarcel Moolenaar 	 * We don't check pm_Heads nor pm_SecPerTrack, because
616043ec583SMarcel Moolenaar 	 * these may not be set for EFI file systems. We don't
617043ec583SMarcel Moolenaar 	 * use these anyway, so we're unaffected if they are
618043ec583SMarcel Moolenaar 	 * invalid.
619043ec583SMarcel Moolenaar 	 */
62004fd468dSKonstantin Belousov 	if (pmp->pm_BytesPerSec == 0 || SecPerClust == 0) {
62127a0bc89SDoug Rabson 		error = EINVAL;
62227a0bc89SDoug Rabson 		goto error_exit;
62327a0bc89SDoug Rabson 	}
62427a0bc89SDoug Rabson 
62527a0bc89SDoug Rabson 	if (pmp->pm_Sectors == 0) {
62627a0bc89SDoug Rabson 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
62727a0bc89SDoug Rabson 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
62827a0bc89SDoug Rabson 	} else {
62927a0bc89SDoug Rabson 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
63027a0bc89SDoug Rabson 		pmp->pm_HugeSectors = pmp->pm_Sectors;
63127a0bc89SDoug Rabson 	}
632952a6212SJordan K. Hubbard 
633952a6212SJordan K. Hubbard 	if (pmp->pm_RootDirEnts == 0) {
63404fd468dSKonstantin Belousov 		if (pmp->pm_FATsecs != 0 || getushort(b710->bpbFSVers) != 0) {
635952a6212SJordan K. Hubbard 			error = EINVAL;
63654cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
637aaf0bb19SAndrey A. Chernov 			printf("mountmsdosfs(): bad FAT32 filesystem\n");
63854cf9198SKonstantin Belousov #endif
639952a6212SJordan K. Hubbard 			goto error_exit;
640952a6212SJordan K. Hubbard 		}
641952a6212SJordan K. Hubbard 		pmp->pm_fatmask = FAT32_MASK;
642952a6212SJordan K. Hubbard 		pmp->pm_fatmult = 4;
643952a6212SJordan K. Hubbard 		pmp->pm_fatdiv = 1;
644952a6212SJordan K. Hubbard 		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
64504fd468dSKonstantin Belousov 		if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0)
646952a6212SJordan K. Hubbard 			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
647952a6212SJordan K. Hubbard 		else
648952a6212SJordan K. Hubbard 			pmp->pm_flags |= MSDOSFS_FATMIRROR;
649952a6212SJordan K. Hubbard 	} else
650952a6212SJordan K. Hubbard 		pmp->pm_flags |= MSDOSFS_FATMIRROR;
651952a6212SJordan K. Hubbard 
652952a6212SJordan K. Hubbard 	/*
653952a6212SJordan K. Hubbard 	 * Check a few values (could do some more):
654952a6212SJordan K. Hubbard 	 * - logical sector size: power of 2, >= block size
655952a6212SJordan K. Hubbard 	 * - sectors per cluster: power of 2, >= 1
656952a6212SJordan K. Hubbard 	 * - number of sectors:   >= 1, <= size of partition
657696f22f0SJohn Baldwin 	 * - number of FAT sectors: >= 1
658952a6212SJordan K. Hubbard 	 */
65904fd468dSKonstantin Belousov 	if (SecPerClust == 0 || (SecPerClust & (SecPerClust - 1)) != 0 ||
66004fd468dSKonstantin Belousov 	    pmp->pm_BytesPerSec < DEV_BSIZE ||
66104fd468dSKonstantin Belousov 	    (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) != 0 ||
66204fd468dSKonstantin Belousov 	    pmp->pm_HugeSectors == 0 || pmp->pm_FATsecs == 0 ||
66304fd468dSKonstantin Belousov 	    SecPerClust * pmp->pm_BlkPerSec > MAXBSIZE / DEV_BSIZE) {
664952a6212SJordan K. Hubbard 		error = EINVAL;
665952a6212SJordan K. Hubbard 		goto error_exit;
666952a6212SJordan K. Hubbard 	}
66701f6cfbaSYoshihiro Takahashi 
668ba2c9838SKonstantin Belousov 	if ((off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec <
669ba2c9838SKonstantin Belousov 	    pmp->pm_HugeSectors /* overflow */ ||
670ba2c9838SKonstantin Belousov 	    (off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec >
671ba2c9838SKonstantin Belousov 	    cp->provider->mediasize /* past end of vol */) {
672ba2c9838SKonstantin Belousov 		error = EINVAL;
673ba2c9838SKonstantin Belousov 		goto error_exit;
674ba2c9838SKonstantin Belousov 	}
675ba2c9838SKonstantin Belousov 
676c7cd607aSMark Johnston 	pmp->pm_HugeSectors *= pmp->pm_BlkPerSec;
67701f6cfbaSYoshihiro Takahashi 	pmp->pm_HiddenSects *= pmp->pm_BlkPerSec;	/* XXX not used? */
67801f6cfbaSYoshihiro Takahashi 	pmp->pm_FATsecs     *= pmp->pm_BlkPerSec;
67901f6cfbaSYoshihiro Takahashi 	SecPerClust         *= pmp->pm_BlkPerSec;
68001f6cfbaSYoshihiro Takahashi 
68101f6cfbaSYoshihiro Takahashi 	pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec;
68201f6cfbaSYoshihiro Takahashi 
683952a6212SJordan K. Hubbard 	if (FAT32(pmp)) {
684952a6212SJordan K. Hubbard 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
68504fd468dSKonstantin Belousov 		pmp->pm_firstcluster = pmp->pm_fatblk +
68604fd468dSKonstantin Belousov 		    pmp->pm_FATs * pmp->pm_FATsecs;
68701f6cfbaSYoshihiro Takahashi 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec;
688952a6212SJordan K. Hubbard 	} else {
68927a0bc89SDoug Rabson 		pmp->pm_rootdirblk = pmp->pm_fatblk +
69004fd468dSKonstantin Belousov 		    pmp->pm_FATs * pmp->pm_FATsecs;
69155e0987aSPedro F. Giffuni 		pmp->pm_rootdirsize = howmany(pmp->pm_RootDirEnts *
69255e0987aSPedro F. Giffuni 		    sizeof(struct direntry), DEV_BSIZE); /* in blocks */
69327a0bc89SDoug Rabson 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
694952a6212SJordan K. Hubbard 	}
695952a6212SJordan K. Hubbard 
696ba2c9838SKonstantin Belousov 	if (pmp->pm_HugeSectors <= pmp->pm_firstcluster) {
697ba2c9838SKonstantin Belousov 		error = EINVAL;
698ba2c9838SKonstantin Belousov 		goto error_exit;
699ba2c9838SKonstantin Belousov 	}
700499d3ffaSBoris Popov 	pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
701499d3ffaSBoris Popov 	    SecPerClust + 1;
7020728695cSStefan Eßer 	pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE;
703952a6212SJordan K. Hubbard 
704952a6212SJordan K. Hubbard 	if (pmp->pm_fatmask == 0) {
7050728695cSStefan Eßer 		/*
7060728695cSStefan Eßer 		 * The last 10 (or 16?) clusters are reserved and must not
7070728695cSStefan Eßer 		 * be allocated for data.
7080728695cSStefan Eßer 		 */
7090728695cSStefan Eßer 		if (pmp->pm_maxcluster < (CLUST_RSRVD & FAT12_MASK)) {
71027a0bc89SDoug Rabson 			/*
711952a6212SJordan K. Hubbard 			 * This will usually be a floppy disk. This size makes
7129287dbaaSEd Maste 			 * sure that one FAT entry will not be split across
713952a6212SJordan K. Hubbard 			 * multiple blocks.
71427a0bc89SDoug Rabson 			 */
715952a6212SJordan K. Hubbard 			pmp->pm_fatmask = FAT12_MASK;
716952a6212SJordan K. Hubbard 			pmp->pm_fatmult = 3;
717952a6212SJordan K. Hubbard 			pmp->pm_fatdiv = 2;
718952a6212SJordan K. Hubbard 		} else {
719952a6212SJordan K. Hubbard 			pmp->pm_fatmask = FAT16_MASK;
720952a6212SJordan K. Hubbard 			pmp->pm_fatmult = 2;
721952a6212SJordan K. Hubbard 			pmp->pm_fatdiv = 1;
722952a6212SJordan K. Hubbard 		}
723952a6212SJordan K. Hubbard 	}
724499d3ffaSBoris Popov 
725499d3ffaSBoris Popov 	clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv;
726*45d4e82bSStefan Eßer 	if (clusters >= (CLUST_RSRVD & pmp->pm_fatmask))
727*45d4e82bSStefan Eßer 		clusters = CLUST_RSRVD & pmp->pm_fatmask;
728499d3ffaSBoris Popov 	if (pmp->pm_maxcluster >= clusters) {
72954cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
730499d3ffaSBoris Popov 		printf("Warning: number of clusters (%ld) exceeds FAT "
7310728695cSStefan Eßer 		    "capacity (%ld)\n", pmp->pm_maxcluster - 1, clusters);
73254cf9198SKonstantin Belousov #endif
733499d3ffaSBoris Popov 		pmp->pm_maxcluster = clusters - 1;
734499d3ffaSBoris Popov 	}
735499d3ffaSBoris Popov 
736952a6212SJordan K. Hubbard 	if (FAT12(pmp))
73793fe42b6SBruce Evans 		pmp->pm_fatblocksize = 3 * 512;
73827a0bc89SDoug Rabson 	else
73993fe42b6SBruce Evans 		pmp->pm_fatblocksize = PAGE_SIZE;
74093fe42b6SBruce Evans 	pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize,
74193fe42b6SBruce Evans 	    pmp->pm_BytesPerSec);
74201f6cfbaSYoshihiro Takahashi 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE;
74301f6cfbaSYoshihiro Takahashi 	pmp->pm_bnshift = ffs(DEV_BSIZE) - 1;
74427a0bc89SDoug Rabson 
74527a0bc89SDoug Rabson 	/*
74627a0bc89SDoug Rabson 	 * Compute mask and shift value for isolating cluster relative byte
74727a0bc89SDoug Rabson 	 * offsets and cluster numbers from a file offset.
74827a0bc89SDoug Rabson 	 */
74901f6cfbaSYoshihiro Takahashi 	pmp->pm_bpcluster = SecPerClust * DEV_BSIZE;
750952a6212SJordan K. Hubbard 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
751952a6212SJordan K. Hubbard 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
75227a0bc89SDoug Rabson 
753952a6212SJordan K. Hubbard 	/*
754952a6212SJordan K. Hubbard 	 * Check for valid cluster size
755952a6212SJordan K. Hubbard 	 * must be a power of 2
756952a6212SJordan K. Hubbard 	 */
75704fd468dSKonstantin Belousov 	if ((pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) != 0) {
758952a6212SJordan K. Hubbard 		error = EINVAL;
759952a6212SJordan K. Hubbard 		goto error_exit;
760ad63a118SSatoshi Asami 	}
76127a0bc89SDoug Rabson 
76227a0bc89SDoug Rabson 	/*
76327a0bc89SDoug Rabson 	 * Release the bootsector buffer.
76427a0bc89SDoug Rabson 	 */
765952a6212SJordan K. Hubbard 	brelse(bp);
766952a6212SJordan K. Hubbard 	bp = NULL;
767952a6212SJordan K. Hubbard 
768952a6212SJordan K. Hubbard 	/*
7698d61a735SBruce Evans 	 * Check the fsinfo sector if we have one.  Silently fix up our
7708d61a735SBruce Evans 	 * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff)
7718d61a735SBruce Evans 	 * or too large.  Ignore fp->fsinfree for now, since we need to
7728d61a735SBruce Evans 	 * read the entire FAT anyway to fill the inuse map.
773952a6212SJordan K. Hubbard 	 */
774952a6212SJordan K. Hubbard 	if (pmp->pm_fsinfo) {
775952a6212SJordan K. Hubbard 		struct fsinfo *fp;
776952a6212SJordan K. Hubbard 
77737269429SBruce Evans 		if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec,
77801f6cfbaSYoshihiro Takahashi 		    NOCRED, &bp)) != 0)
779952a6212SJordan K. Hubbard 			goto error_exit;
780952a6212SJordan K. Hubbard 		fp = (struct fsinfo *)bp->b_data;
78104fd468dSKonstantin Belousov 		if (!bcmp(fp->fsisig1, "RRaA", 4) &&
78204fd468dSKonstantin Belousov 		    !bcmp(fp->fsisig2, "rrAa", 4) &&
78304fd468dSKonstantin Belousov 		    !bcmp(fp->fsisig3, "\0\0\125\252", 4)) {
784952a6212SJordan K. Hubbard 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
7858d61a735SBruce Evans 			if (pmp->pm_nxtfree > pmp->pm_maxcluster)
7868bb386f2STim J. Robbins 				pmp->pm_nxtfree = CLUST_FIRST;
7878bb386f2STim J. Robbins 		} else
788952a6212SJordan K. Hubbard 			pmp->pm_fsinfo = 0;
789952a6212SJordan K. Hubbard 		brelse(bp);
790952a6212SJordan K. Hubbard 		bp = NULL;
791952a6212SJordan K. Hubbard 	}
792952a6212SJordan K. Hubbard 
793952a6212SJordan K. Hubbard 	/*
7948d61a735SBruce Evans 	 * Finish initializing pmp->pm_nxtfree (just in case the first few
7958d61a735SBruce Evans 	 * sectors aren't properly reserved in the FAT).  This completes
7968d61a735SBruce Evans 	 * the fixup for fp->fsinxtfree, and fixes up the zero-initialized
7978d61a735SBruce Evans 	 * value if there is no fsinfo.  We will use pmp->pm_nxtfree
7988d61a735SBruce Evans 	 * internally even if there is no fsinfo.
799952a6212SJordan K. Hubbard 	 */
8008d61a735SBruce Evans 	if (pmp->pm_nxtfree < CLUST_FIRST)
8018d61a735SBruce Evans 		pmp->pm_nxtfree = CLUST_FIRST;
80227a0bc89SDoug Rabson 
80327a0bc89SDoug Rabson 	/*
80427a0bc89SDoug Rabson 	 * Allocate memory for the bitmap of allocated clusters, and then
80527a0bc89SDoug Rabson 	 * fill it in.
80627a0bc89SDoug Rabson 	 */
80704fd468dSKonstantin Belousov 	pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1,
80804fd468dSKonstantin Belousov 	    N_INUSEBITS) * sizeof(*pmp->pm_inusemap), M_MSDOSFSFAT, M_WAITOK);
80927a0bc89SDoug Rabson 
81027a0bc89SDoug Rabson 	/*
81127a0bc89SDoug Rabson 	 * fillinusemap() needs pm_devvp.
81227a0bc89SDoug Rabson 	 */
81327a0bc89SDoug Rabson 	pmp->pm_devvp = devvp;
814aaaa4fb5SKonstantin Belousov 	pmp->pm_odevvp = odevvp;
815c72ae142SJohn Baldwin 	pmp->pm_dev = dev;
81627a0bc89SDoug Rabson 
81727a0bc89SDoug Rabson 	/*
81827a0bc89SDoug Rabson 	 * Have the inuse map filled in.
81927a0bc89SDoug Rabson 	 */
8206be1a4ccSKonstantin Belousov 	MSDOSFS_LOCK_MP(pmp);
8216be1a4ccSKonstantin Belousov 	error = fillinusemap(pmp);
8226be1a4ccSKonstantin Belousov 	MSDOSFS_UNLOCK_MP(pmp);
8236be1a4ccSKonstantin Belousov 	if (error != 0)
82427a0bc89SDoug Rabson 		goto error_exit;
82527a0bc89SDoug Rabson 
82627a0bc89SDoug Rabson 	/*
8279287dbaaSEd Maste 	 * If they want FAT updates to be synchronous then let them suffer
82827a0bc89SDoug Rabson 	 * the performance degradation in exchange for the on disk copy of
8299287dbaaSEd Maste 	 * the FAT being correct just about all the time.  I suppose this
83027a0bc89SDoug Rabson 	 * would be a good thing to turn on if the kernel is still flakey.
83127a0bc89SDoug Rabson 	 */
832952a6212SJordan K. Hubbard 	if (mp->mnt_flag & MNT_SYNCHRONOUS)
833952a6212SJordan K. Hubbard 		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
83427a0bc89SDoug Rabson 
83527a0bc89SDoug Rabson 	/*
83627a0bc89SDoug Rabson 	 * Finish up.
83727a0bc89SDoug Rabson 	 */
838952a6212SJordan K. Hubbard 	if (ronly)
839952a6212SJordan K. Hubbard 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
840cede1f56STom Rhodes 	else {
841aaa38524SConrad Meyer 		if ((error = markvoldirty(pmp, 1)) != 0)
842cede1f56STom Rhodes 			goto error_exit;
84327a0bc89SDoug Rabson 		pmp->pm_fmod = 1;
844cede1f56STom Rhodes 	}
845c33db74bSStefan Eßer 
846c33db74bSStefan Eßer 	if (FAT32(pmp)) {
847c33db74bSStefan Eßer 		pmp->pm_rootdirfree = 0;
848c33db74bSStefan Eßer 	} else {
849c33db74bSStefan Eßer 		pmp->pm_rootdirfree = rootdir_free(pmp);
850c33db74bSStefan Eßer 		if (pmp->pm_rootdirfree < 0)
851c33db74bSStefan Eßer 			goto error_exit;
852c33db74bSStefan Eßer 	}
853c33db74bSStefan Eßer 
85477465d93SAlfred Perlstein 	mp->mnt_data =  pmp;
855939cb752SBruce Evans 	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
856af3f60d5SBruce Evans 	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
8575da56ddbSTor Egge 	MNT_ILOCK(mp);
858cc9d8990SPeter Wemm 	mp->mnt_flag |= MNT_LOCAL;
8592aa39445SKonstantin Belousov 	mp->mnt_kern_flag |= MNTK_USES_BCACHE | MNTK_NO_IOPF;
8605da56ddbSTor Egge 	MNT_IUNLOCK(mp);
86127a0bc89SDoug Rabson 
86240373cf5SKonstantin Belousov 	return (0);
86327a0bc89SDoug Rabson 
864952a6212SJordan K. Hubbard error_exit:
86504fd468dSKonstantin Belousov 	if (bp != NULL)
866952a6212SJordan K. Hubbard 		brelse(bp);
8679a135592SPoul-Henning Kamp 	if (cp != NULL) {
8689a135592SPoul-Henning Kamp 		g_topology_lock();
8690d7935fdSAttilio Rao 		g_vfs_close(cp);
8709a135592SPoul-Henning Kamp 		g_topology_unlock();
8719a135592SPoul-Henning Kamp 	}
87204fd468dSKonstantin Belousov 	if (pmp != NULL) {
873a84ec05fSKonstantin Belousov 		lockdestroy(&pmp->pm_fatlock);
8746ae13c0fSKonstantin Belousov 		lockdestroy(&pmp->pm_checkpath_lock);
875952a6212SJordan K. Hubbard 		free(pmp->pm_inusemap, M_MSDOSFSFAT);
876952a6212SJordan K. Hubbard 		free(pmp, M_MSDOSFSMNT);
87777465d93SAlfred Perlstein 		mp->mnt_data = NULL;
87827a0bc89SDoug Rabson 	}
879aaaa4fb5SKonstantin Belousov 	BO_LOCK(&odevvp->v_bufobj);
880aaaa4fb5SKonstantin Belousov 	odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
881aaaa4fb5SKonstantin Belousov 	BO_UNLOCK(&odevvp->v_bufobj);
882bb8297e6SKonstantin Belousov 	atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
883aaaa4fb5SKonstantin Belousov 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
884aaaa4fb5SKonstantin Belousov 	mntfs_freevp(devvp);
885c72ae142SJohn Baldwin 	dev_rel(dev);
886952a6212SJordan K. Hubbard 	return (error);
88727a0bc89SDoug Rabson }
88827a0bc89SDoug Rabson 
88927a0bc89SDoug Rabson /*
89027a0bc89SDoug Rabson  * Unmount the filesystem described by mp.
89127a0bc89SDoug Rabson  */
8927fefffeeSPoul-Henning Kamp static int
msdosfs_unmount(struct mount * mp,int mntflags)893dfd233edSAttilio Rao msdosfs_unmount(struct mount *mp, int mntflags)
89427a0bc89SDoug Rabson {
895952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp;
896952a6212SJordan K. Hubbard 	int error, flags;
89769367793SKonstantin Belousov 	bool susp;
89827a0bc89SDoug Rabson 
8992359e2dcSKonstantin Belousov 	error = flags = 0;
9002359e2dcSKonstantin Belousov 	pmp = VFSTOMSDOSFS(mp);
90169367793SKonstantin Belousov 	susp = (pmp->pm_flags & MSDOSFSMNT_RONLY) == 0;
90269367793SKonstantin Belousov 
90369367793SKonstantin Belousov 	if (susp) {
90469367793SKonstantin Belousov 		error = vfs_write_suspend_umnt(mp);
90569367793SKonstantin Belousov 		if (error != 0)
90669367793SKonstantin Belousov 			return (error);
90769367793SKonstantin Belousov 	}
90869367793SKonstantin Belousov 
9092359e2dcSKonstantin Belousov 	if ((mntflags & MNT_FORCE) != 0)
91027a0bc89SDoug Rabson 		flags |= FORCECLOSE;
911dfd233edSAttilio Rao 	error = vflush(mp, 0, flags, curthread);
91269367793SKonstantin Belousov 	if (error != 0 && error != ENXIO) {
91369367793SKonstantin Belousov 		if (susp)
91469367793SKonstantin Belousov 			vfs_write_resume(mp, VR_START_WRITE);
915b2344ab5SKonstantin Belousov 		return (error);
91669367793SKonstantin Belousov 	}
91769367793SKonstantin Belousov 	if (susp) {
9183247c9ddSXin LI 		error = markvoldirty(pmp, 0);
91969367793SKonstantin Belousov 		if (error != 0 && error != ENXIO) {
92069367793SKonstantin Belousov 			if (susp)
92169367793SKonstantin Belousov 				vfs_write_resume(mp, VR_START_WRITE);
9223247c9ddSXin LI 			(void)markvoldirty(pmp, 1);
9233247c9ddSXin LI 			return (error);
9243247c9ddSXin LI 		}
9253247c9ddSXin LI 	}
926c4f02a89SMax Khon 	if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) {
927c4f02a89SMax Khon 		if (pmp->pm_w2u)
928c4f02a89SMax Khon 			msdosfs_iconv->close(pmp->pm_w2u);
929c4f02a89SMax Khon 		if (pmp->pm_u2w)
930c4f02a89SMax Khon 			msdosfs_iconv->close(pmp->pm_u2w);
931c4f02a89SMax Khon 		if (pmp->pm_d2u)
932c4f02a89SMax Khon 			msdosfs_iconv->close(pmp->pm_d2u);
933c4f02a89SMax Khon 		if (pmp->pm_u2d)
934c4f02a89SMax Khon 			msdosfs_iconv->close(pmp->pm_u2d);
935c4f02a89SMax Khon 	}
936cede1f56STom Rhodes 
937952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG
938952a6212SJordan K. Hubbard 	{
939952a6212SJordan K. Hubbard 		struct vnode *vp = pmp->pm_devvp;
940698b1a66SJeff Roberson 		struct bufobj *bo;
941952a6212SJordan K. Hubbard 
942698b1a66SJeff Roberson 		bo = &vp->v_bufobj;
943698b1a66SJeff Roberson 		BO_LOCK(bo);
9444d93c0beSJeff Roberson 		VI_LOCK(vp);
945f69d42a1SPoul-Henning Kamp 		vn_printf(vp,
946f69d42a1SPoul-Henning Kamp 		    "msdosfs_umount(): just before calling VOP_CLOSE()\n");
947952a6212SJordan K. Hubbard 		printf("freef %p, freeb %p, mount %p\n",
948cc3593fbSMateusz Guzik 		    TAILQ_NEXT(vp, v_vnodelist), vp->v_vnodelist.tqe_prev,
949952a6212SJordan K. Hubbard 		    vp->v_mount);
950c1c4d0e9SConrad Meyer 		printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
951156cb265SPoul-Henning Kamp 		    TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd),
952156cb265SPoul-Henning Kamp 		    TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd),
953156cb265SPoul-Henning Kamp 		    vp->v_bufobj.bo_numoutput, vp->v_type);
9544d93c0beSJeff Roberson 		VI_UNLOCK(vp);
955698b1a66SJeff Roberson 		BO_UNLOCK(bo);
956952a6212SJordan K. Hubbard 	}
957952a6212SJordan K. Hubbard #endif
95869367793SKonstantin Belousov 	if (susp)
95969367793SKonstantin Belousov 		vfs_write_resume(mp, VR_START_WRITE);
96069367793SKonstantin Belousov 
961aaaa4fb5SKonstantin Belousov 	vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
9629a135592SPoul-Henning Kamp 	g_topology_lock();
9630d7935fdSAttilio Rao 	g_vfs_close(pmp->pm_cp);
9649a135592SPoul-Henning Kamp 	g_topology_unlock();
965aaaa4fb5SKonstantin Belousov 	BO_LOCK(&pmp->pm_odevvp->v_bufobj);
966aaaa4fb5SKonstantin Belousov 	pmp->pm_odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
967aaaa4fb5SKonstantin Belousov 	BO_UNLOCK(&pmp->pm_odevvp->v_bufobj);
968bb8297e6SKonstantin Belousov 	atomic_store_rel_ptr((uintptr_t *)&pmp->pm_dev->si_mountpt, 0);
969aaaa4fb5SKonstantin Belousov 	mntfs_freevp(pmp->pm_devvp);
970aaaa4fb5SKonstantin Belousov 	vrele(pmp->pm_odevvp);
971c72ae142SJohn Baldwin 	dev_rel(pmp->pm_dev);
972952a6212SJordan K. Hubbard 	free(pmp->pm_inusemap, M_MSDOSFSFAT);
97323b6c230SKonstantin Belousov 	lockdestroy(&pmp->pm_fatlock);
9746ae13c0fSKonstantin Belousov 	lockdestroy(&pmp->pm_checkpath_lock);
975952a6212SJordan K. Hubbard 	free(pmp, M_MSDOSFSMNT);
97677465d93SAlfred Perlstein 	mp->mnt_data = NULL;
9774f560d75SEdward Tomasz Napierala 	return (error);
97827a0bc89SDoug Rabson }
97927a0bc89SDoug Rabson 
980b2e4b635SKonstantin Belousov static void
msdosfs_remount_ro(void * arg,int pending)981b2e4b635SKonstantin Belousov msdosfs_remount_ro(void *arg, int pending)
982b2e4b635SKonstantin Belousov {
983b2e4b635SKonstantin Belousov 	struct msdosfsmount *pmp;
984b2e4b635SKonstantin Belousov 	int error;
985b2e4b635SKonstantin Belousov 
986b2e4b635SKonstantin Belousov 	pmp = arg;
987b2e4b635SKonstantin Belousov 
988b2e4b635SKonstantin Belousov 	MSDOSFS_LOCK_MP(pmp);
989b2e4b635SKonstantin Belousov 	if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0) {
990b2e4b635SKonstantin Belousov 		while ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
991b2e4b635SKonstantin Belousov 			msleep(&pmp->pm_flags, &pmp->pm_fatlock, PVFS,
992b2e4b635SKonstantin Belousov 			    "msdoserrro", hz);
993b2e4b635SKonstantin Belousov 	} else if ((pmp->pm_mountp->mnt_flag & MNT_RDONLY) == 0) {
994b2e4b635SKonstantin Belousov 		pmp->pm_flags |= MSDOSFS_ERR_RO;
995b2e4b635SKonstantin Belousov 		MSDOSFS_UNLOCK_MP(pmp);
996b2e4b635SKonstantin Belousov 		printf("%s: remounting read-only due to corruption\n",
997b2e4b635SKonstantin Belousov 		    pmp->pm_mountp->mnt_stat.f_mntfromname);
998b2e4b635SKonstantin Belousov 		error = vfs_remount_ro(pmp->pm_mountp);
999b2e4b635SKonstantin Belousov 		if (error != 0)
1000b2e4b635SKonstantin Belousov 			printf("%s: remounting read-only failed: error %d\n",
1001b2e4b635SKonstantin Belousov 			    pmp->pm_mountp->mnt_stat.f_mntfromname, error);
1002b2e4b635SKonstantin Belousov 		else
1003b2e4b635SKonstantin Belousov 			printf("remounted %s read-only\n",
1004b2e4b635SKonstantin Belousov 			    pmp->pm_mountp->mnt_stat.f_mntfromname);
1005b2e4b635SKonstantin Belousov 		MSDOSFS_LOCK_MP(pmp);
1006b2e4b635SKonstantin Belousov 		pmp->pm_flags &= ~MSDOSFS_ERR_RO;
1007b2e4b635SKonstantin Belousov 		wakeup(&pmp->pm_flags);
1008b2e4b635SKonstantin Belousov 	}
1009b2e4b635SKonstantin Belousov 	MSDOSFS_UNLOCK_MP(pmp);
1010b2e4b635SKonstantin Belousov 
10114b3ffc59SKonstantin Belousov 	while (--pending >= 0)
1012b2e4b635SKonstantin Belousov 		vfs_unbusy(pmp->pm_mountp);
1013b2e4b635SKonstantin Belousov }
1014b2e4b635SKonstantin Belousov 
1015b2e4b635SKonstantin Belousov void
msdosfs_integrity_error(struct msdosfsmount * pmp)1016b2e4b635SKonstantin Belousov msdosfs_integrity_error(struct msdosfsmount *pmp)
1017b2e4b635SKonstantin Belousov {
1018b2e4b635SKonstantin Belousov 	int error;
1019b2e4b635SKonstantin Belousov 
1020b2e4b635SKonstantin Belousov 	error = vfs_busy(pmp->pm_mountp, MBF_NOWAIT);
102113ccb045SKonstantin Belousov 	if (error == 0) {
102213ccb045SKonstantin Belousov 		error = taskqueue_enqueue(taskqueue_thread,
102313ccb045SKonstantin Belousov 		    &pmp->pm_rw2ro_task);
102413ccb045SKonstantin Belousov 		if (error != 0) {
102513ccb045SKonstantin Belousov 			printf("%s: integrity error scheduling failed, "
102613ccb045SKonstantin Belousov 			    "error %d\n",
102713ccb045SKonstantin Belousov 			    pmp->pm_mountp->mnt_stat.f_mntfromname, error);
102813ccb045SKonstantin Belousov 			vfs_unbusy(pmp->pm_mountp);
102913ccb045SKonstantin Belousov 		}
103013ccb045SKonstantin Belousov 	} else {
1031b2e4b635SKonstantin Belousov 		printf("%s: integrity error busying failed, error %d\n",
1032b2e4b635SKonstantin Belousov 		    pmp->pm_mountp->mnt_stat.f_mntfromname, error);
1033b2e4b635SKonstantin Belousov 	}
103413ccb045SKonstantin Belousov }
1035b2e4b635SKonstantin Belousov 
10367fefffeeSPoul-Henning Kamp static int
msdosfs_root(struct mount * mp,int flags,struct vnode ** vpp)1037dfd233edSAttilio Rao msdosfs_root(struct mount *mp, int flags, struct vnode **vpp)
103827a0bc89SDoug Rabson {
1039952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
104027a0bc89SDoug Rabson 	struct denode *ndep;
104127a0bc89SDoug Rabson 	int error;
104227a0bc89SDoug Rabson 
104327a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
1044952a6212SJordan K. Hubbard 	printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
104527a0bc89SDoug Rabson #endif
1046ae7e8a02SKonstantin Belousov 	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, LK_EXCLUSIVE, &ndep);
1047952a6212SJordan K. Hubbard 	if (error)
1048952a6212SJordan K. Hubbard 		return (error);
104927a0bc89SDoug Rabson 	*vpp = DETOV(ndep);
1050952a6212SJordan K. Hubbard 	return (0);
105127a0bc89SDoug Rabson }
105227a0bc89SDoug Rabson 
10537fefffeeSPoul-Henning Kamp static int
msdosfs_statfs(struct mount * mp,struct statfs * sbp)1054dfd233edSAttilio Rao msdosfs_statfs(struct mount *mp, struct statfs *sbp)
105527a0bc89SDoug Rabson {
1056952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp;
105727a0bc89SDoug Rabson 
1058952a6212SJordan K. Hubbard 	pmp = VFSTOMSDOSFS(mp);
105927a0bc89SDoug Rabson 	sbp->f_bsize = pmp->pm_bpcluster;
106027a0bc89SDoug Rabson 	sbp->f_iosize = pmp->pm_bpcluster;
10610728695cSStefan Eßer 	sbp->f_blocks = pmp->pm_maxcluster - CLUST_FIRST + 1;
106227a0bc89SDoug Rabson 	sbp->f_bfree = pmp->pm_freeclustercount;
106327a0bc89SDoug Rabson 	sbp->f_bavail = pmp->pm_freeclustercount;
1064c33db74bSStefan Eßer 	sbp->f_files =	howmany(pmp->pm_rootdirsize * DEV_BSIZE,
1065c33db74bSStefan Eßer 				sizeof(struct direntry));
1066c33db74bSStefan Eßer 	sbp->f_ffree = pmp->pm_rootdirfree;
1067952a6212SJordan K. Hubbard 	return (0);
106827a0bc89SDoug Rabson }
106927a0bc89SDoug Rabson 
1070bb7ca822SKonstantin Belousov /*
1071bb7ca822SKonstantin Belousov  * If we have an FSInfo block, update it.
1072bb7ca822SKonstantin Belousov  */
1073bb7ca822SKonstantin Belousov static int
msdosfs_fsiflush(struct msdosfsmount * pmp,int waitfor)1074bb7ca822SKonstantin Belousov msdosfs_fsiflush(struct msdosfsmount *pmp, int waitfor)
1075bb7ca822SKonstantin Belousov {
1076bb7ca822SKonstantin Belousov 	struct fsinfo *fp;
1077bb7ca822SKonstantin Belousov 	struct buf *bp;
1078bb7ca822SKonstantin Belousov 	int error;
1079bb7ca822SKonstantin Belousov 
1080bb7ca822SKonstantin Belousov 	MSDOSFS_LOCK_MP(pmp);
1081bb7ca822SKonstantin Belousov 	if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) {
1082bb7ca822SKonstantin Belousov 		error = 0;
1083bb7ca822SKonstantin Belousov 		goto unlock;
1084bb7ca822SKonstantin Belousov 	}
1085bb7ca822SKonstantin Belousov 	error = bread(pmp->pm_devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec,
1086bb7ca822SKonstantin Belousov 	    NOCRED, &bp);
1087bb7ca822SKonstantin Belousov 	if (error != 0) {
1088bb7ca822SKonstantin Belousov 		goto unlock;
1089bb7ca822SKonstantin Belousov 	}
1090bb7ca822SKonstantin Belousov 	fp = (struct fsinfo *)bp->b_data;
1091bb7ca822SKonstantin Belousov 	putulong(fp->fsinfree, pmp->pm_freeclustercount);
1092bb7ca822SKonstantin Belousov 	putulong(fp->fsinxtfree, pmp->pm_nxtfree);
1093bb7ca822SKonstantin Belousov 	pmp->pm_flags &= ~MSDOSFS_FSIMOD;
1094bb7ca822SKonstantin Belousov 	if (waitfor == MNT_WAIT)
1095bb7ca822SKonstantin Belousov 		error = bwrite(bp);
1096bb7ca822SKonstantin Belousov 	else
1097bb7ca822SKonstantin Belousov 		bawrite(bp);
1098bb7ca822SKonstantin Belousov unlock:
1099bb7ca822SKonstantin Belousov 	MSDOSFS_UNLOCK_MP(pmp);
1100bb7ca822SKonstantin Belousov 	return (error);
1101bb7ca822SKonstantin Belousov }
1102bb7ca822SKonstantin Belousov 
11037fefffeeSPoul-Henning Kamp static int
msdosfs_sync(struct mount * mp,int waitfor)1104dfd233edSAttilio Rao msdosfs_sync(struct mount *mp, int waitfor)
110527a0bc89SDoug Rabson {
1106952a6212SJordan K. Hubbard 	struct vnode *vp, *nvp;
1107dfd233edSAttilio Rao 	struct thread *td;
110827a0bc89SDoug Rabson 	struct denode *dep;
1109952a6212SJordan K. Hubbard 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1110952a6212SJordan K. Hubbard 	int error, allerror = 0;
111127a0bc89SDoug Rabson 
1112dfd233edSAttilio Rao 	td = curthread;
1113dfd233edSAttilio Rao 
111427a0bc89SDoug Rabson 	/*
11159287dbaaSEd Maste 	 * If we ever switch to not updating all of the FATs all the time,
111627a0bc89SDoug Rabson 	 * this would be the place to update them from the first one.
111727a0bc89SDoug Rabson 	 */
1118dfd5dee1SPeter Wemm 	if (pmp->pm_fmod != 0) {
1119952a6212SJordan K. Hubbard 		if (pmp->pm_flags & MSDOSFSMNT_RONLY)
112027a0bc89SDoug Rabson 			panic("msdosfs_sync: rofs mod");
112127a0bc89SDoug Rabson 		else {
11229287dbaaSEd Maste 			/* update FATs here */
112327a0bc89SDoug Rabson 		}
1124dfd5dee1SPeter Wemm 	}
112527a0bc89SDoug Rabson 	/*
1126952a6212SJordan K. Hubbard 	 * Write back each (modified) denode.
112727a0bc89SDoug Rabson 	 */
112827a0bc89SDoug Rabson loop:
112971469bb3SKirk McKusick 	MNT_VNODE_FOREACH_ALL(vp, mp, nvp) {
113071469bb3SKirk McKusick 		if (vp->v_type == VNON) {
11314ab2c8bdSJeff Roberson 			VI_UNLOCK(vp);
11324ab2c8bdSJeff Roberson 			continue;
11334ab2c8bdSJeff Roberson 		}
113427a0bc89SDoug Rabson 		dep = VTODE(vp);
1135f00f5d71SPoul-Henning Kamp 		if ((dep->de_flag &
1136c681be37SDmitrij Tejblum 		    (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 &&
1137156cb265SPoul-Henning Kamp 		    (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1138f00f5d71SPoul-Henning Kamp 		    waitfor == MNT_LAZY)) {
11394d93c0beSJeff Roberson 			VI_UNLOCK(vp);
114027a0bc89SDoug Rabson 			continue;
1141af3f60d5SBruce Evans 		}
1142a92a971bSMateusz Guzik 		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
1143af3f60d5SBruce Evans 		if (error) {
1144f342b91cSMateusz Guzik 			if (error == ENOENT) {
1145f342b91cSMateusz Guzik 				MNT_VNODE_FOREACH_ALL_ABORT(mp, nvp);
114627a0bc89SDoug Rabson 				goto loop;
1147f342b91cSMateusz Guzik 			}
1148af3f60d5SBruce Evans 			continue;
1149af3f60d5SBruce Evans 		}
11508df6bac4SPoul-Henning Kamp 		error = VOP_FSYNC(vp, waitfor, td);
1151c3c6d51eSPoul-Henning Kamp 		if (error)
115227a0bc89SDoug Rabson 			allerror = error;
1153b935e867SMateusz Guzik 		vput(vp);
115427a0bc89SDoug Rabson 	}
115527a0bc89SDoug Rabson 
115627a0bc89SDoug Rabson 	/*
115727a0bc89SDoug Rabson 	 * Flush filesystem control info.
115827a0bc89SDoug Rabson 	 */
1159c681be37SDmitrij Tejblum 	if (waitfor != MNT_LAZY) {
1160cb05b60aSAttilio Rao 		vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
11618df6bac4SPoul-Henning Kamp 		error = VOP_FSYNC(pmp->pm_devvp, waitfor, td);
1162c3c6d51eSPoul-Henning Kamp 		if (error)
116327a0bc89SDoug Rabson 			allerror = error;
1164b249ce48SMateusz Guzik 		VOP_UNLOCK(pmp->pm_devvp);
1165c681be37SDmitrij Tejblum 	}
1166bb7ca822SKonstantin Belousov 
1167bb7ca822SKonstantin Belousov 	error = msdosfs_fsiflush(pmp, waitfor);
1168bb7ca822SKonstantin Belousov 	if (error != 0)
1169bb7ca822SKonstantin Belousov 		allerror = error;
11701b3cb4dcSKonstantin Belousov 
11711b3cb4dcSKonstantin Belousov 	if (allerror == 0 && waitfor == MNT_SUSPEND) {
11721b3cb4dcSKonstantin Belousov 		MNT_ILOCK(mp);
11731b3cb4dcSKonstantin Belousov 		mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED;
11741b3cb4dcSKonstantin Belousov 		MNT_IUNLOCK(mp);
11751b3cb4dcSKonstantin Belousov 	}
1176952a6212SJordan K. Hubbard 	return (allerror);
117727a0bc89SDoug Rabson }
117827a0bc89SDoug Rabson 
11791be5bc74STom Rhodes static int
msdosfs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)1180694a586aSRick Macklem msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
11811be5bc74STom Rhodes {
11821be5bc74STom Rhodes 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
11831be5bc74STom Rhodes 	struct defid *defhp = (struct defid *) fhp;
11841be5bc74STom Rhodes 	struct denode *dep;
11851be5bc74STom Rhodes 	int error;
11861be5bc74STom Rhodes 
1187ae7e8a02SKonstantin Belousov 	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
1188ae7e8a02SKonstantin Belousov 	    LK_EXCLUSIVE, &dep);
11891be5bc74STom Rhodes 	if (error) {
11901be5bc74STom Rhodes 		*vpp = NULLVP;
11911be5bc74STom Rhodes 		return (error);
11921be5bc74STom Rhodes 	}
11931be5bc74STom Rhodes 	*vpp = DETOV(dep);
11941be5bc74STom Rhodes 	vnode_create_vobject(*vpp, dep->de_FileSize, curthread);
11951be5bc74STom Rhodes 	return (0);
11961be5bc74STom Rhodes }
11971be5bc74STom Rhodes 
119830ffadf3SPoul-Henning Kamp static struct vfsops msdosfs_vfsops = {
11991be5bc74STom Rhodes 	.vfs_fhtovp =		msdosfs_fhtovp,
12006a4b48f4SPoul-Henning Kamp 	.vfs_mount =		msdosfs_mount,
12016a4b48f4SPoul-Henning Kamp 	.vfs_cmount =		msdosfs_cmount,
12027652131bSPoul-Henning Kamp 	.vfs_root =		msdosfs_root,
12037652131bSPoul-Henning Kamp 	.vfs_statfs =		msdosfs_statfs,
12047652131bSPoul-Henning Kamp 	.vfs_sync =		msdosfs_sync,
12057652131bSPoul-Henning Kamp 	.vfs_unmount =		msdosfs_unmount,
120627a0bc89SDoug Rabson };
1207c901836cSGarrett Wollman 
12084ccd7546SRuslan Ermilov VFS_SET(msdosfs_vfsops, msdosfs, 0);
1209c4f02a89SMax Khon MODULE_VERSION(msdosfs, 1);
1210