msdosfs_vfsops.c revision 138689
150477Speter/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_vfsops.c 138689 2004-12-11 20:37:48Z phk $ */
233548Sjkh/*	$NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $	*/
32893Sdfr
42893Sdfr/*-
533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
72893Sdfr * All rights reserved.
82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
92893Sdfr *
102893Sdfr * Redistribution and use in source and binary forms, with or without
112893Sdfr * modification, are permitted provided that the following conditions
122893Sdfr * are met:
132893Sdfr * 1. Redistributions of source code must retain the above copyright
142893Sdfr *    notice, this list of conditions and the following disclaimer.
152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright
162893Sdfr *    notice, this list of conditions and the following disclaimer in the
172893Sdfr *    documentation and/or other materials provided with the distribution.
182893Sdfr * 3. All advertising materials mentioning features or use of this software
192893Sdfr *    must display the following acknowledgement:
202893Sdfr *	This product includes software developed by TooLs GmbH.
212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products
222893Sdfr *    derived from this software without specific prior written permission.
232893Sdfr *
242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
342893Sdfr */
352893Sdfr/*
362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com)
378876Srgrimes *
382893Sdfr * You can do anything you want with this software, just don't say you wrote
392893Sdfr * it, and don't remove this notice.
408876Srgrimes *
412893Sdfr * This software is provided "as is".
428876Srgrimes *
432893Sdfr * The author supplies this software to be publicly redistributed on the
442893Sdfr * understanding that the author is not responsible for the correct
452893Sdfr * functioning of this software in any circumstances and is not liable for
462893Sdfr * any damages caused by this software.
478876Srgrimes *
482893Sdfr * October 1992
492893Sdfr */
502893Sdfr
512893Sdfr#include <sys/param.h>
522893Sdfr#include <sys/systm.h>
5340651Sbde#include <sys/conf.h>
542893Sdfr#include <sys/namei.h>
552893Sdfr#include <sys/proc.h>
562893Sdfr#include <sys/kernel.h>
572893Sdfr#include <sys/vnode.h>
582893Sdfr#include <sys/mount.h>
5960041Sphk#include <sys/bio.h>
602893Sdfr#include <sys/buf.h>
6124131Sbde#include <sys/fcntl.h>
622893Sdfr#include <sys/malloc.h>
6333548Sjkh#include <sys/stat.h> 				/* defines ALLPERMS */
64120492Sfjoe#include <sys/iconv.h>
65102391Sbde#include <sys/mutex.h>
662893Sdfr
6777162Sru#include <fs/msdosfs/bpb.h>
6877162Sru#include <fs/msdosfs/bootsect.h>
69120492Sfjoe#include <fs/msdosfs/msdosfsmount.h>
7077162Sru#include <fs/msdosfs/direntry.h>
7177162Sru#include <fs/msdosfs/denode.h>
7277162Sru#include <fs/msdosfs/fat.h>
732893Sdfr
74137036Sphk#include <geom/geom.h>
75137036Sphk#include <geom/geom_vfs.h>
76137036Sphk
77131523Stjr#include "opt_msdosfs.h"
78131523Stjr
79138471Sphk/* List of mount options we support */
80138471Sphkstatic const char *msdosfs_opts[] = {
81138471Sphk	"from",
82138471Sphk	"export",
83138471Sphk	"uid", "gid", "mask", "dirmask",
84138471Sphk	"shortname", "longname", "win95",
85138471Sphk	"kiconv", "cs_win", "cs_dos", "cs_local",
86138471Sphk	NULL
87138471Sphk};
88138471Sphk
89102391Sbde#define MSDOSFS_DFLTBSIZE       4096
90102391Sbde
9156674Snyan#if 1 /*def PC98*/
9256674Snyan/*
9356674Snyan * XXX - The boot signature formatted by NEC PC-98 DOS looks like a
9456674Snyan *       garbage or a random value :-{
9556674Snyan *       If you want to use that broken-signatured media, define the
9656674Snyan *       following symbol even though PC/AT.
9756674Snyan *       (ex. mount PC-98 DOS formatted FD on PC/AT)
9856674Snyan */
9956674Snyan#define	MSDOSFS_NOCHECKSIG
10056674Snyan#endif
10156674Snyan
10230354SphkMALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure");
10330354Sphkstatic MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table");
10430309Sphk
105120492Sfjoestruct iconv_functions *msdosfs_iconv = NULL;
106120492Sfjoe
107138471Sphkstatic int	update_mp(struct mount *mp, struct thread *td);
108138471Sphkstatic int	mountmsdosfs(struct vnode *devvp, struct mount *mp,
109132023Salfred		    struct thread *td);
110101777Sphkstatic vfs_fhtovp_t	msdosfs_fhtovp;
111138471Sphkstatic vfs_mount_t	msdosfs_mount;
112101777Sphkstatic vfs_root_t	msdosfs_root;
113101777Sphkstatic vfs_statfs_t	msdosfs_statfs;
114101777Sphkstatic vfs_sync_t	msdosfs_sync;
115101777Sphkstatic vfs_unmount_t	msdosfs_unmount;
116101777Sphkstatic vfs_vptofh_t	msdosfs_vptofh;
11712338Sbde
118134345Stjr/* Maximum length of a character set name (arbitrary). */
119134345Stjr#define	MAXCSLEN	64
120134345Stjr
12133548Sjkhstatic int
122138471Sphkupdate_mp(mp, td)
12333548Sjkh	struct mount *mp;
124132023Salfred	struct thread *td;
12533548Sjkh{
12633548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
127138471Sphk	void *dos, *win, *local;
128138471Sphk	int error, v;
12933548Sjkh
130138471Sphk	if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) {
131138471Sphk		if (msdosfs_iconv != NULL) {
132138471Sphk			error = vfs_getopt(mp->mnt_optnew,
133138471Sphk			    "cs_win", &win, NULL);
134138471Sphk			if (!error)
135138471Sphk				error = vfs_getopt(mp->mnt_optnew,
136138471Sphk				    "cs_local", &local, NULL);
137138471Sphk			if (!error)
138138471Sphk				error = vfs_getopt(mp->mnt_optnew,
139138471Sphk				    "cs_dos", &dos, NULL);
140138471Sphk			if (!error) {
141138471Sphk				msdosfs_iconv->open(win, local, &pmp->pm_u2w);
142138471Sphk				msdosfs_iconv->open(local, win, &pmp->pm_w2u);
143138471Sphk				msdosfs_iconv->open(dos, local, &pmp->pm_u2d);
144138471Sphk				msdosfs_iconv->open(local, dos, &pmp->pm_d2u);
145138471Sphk			}
146134345Stjr			if (error != 0)
147134345Stjr				return (error);
148134345Stjr		} else {
149134345Stjr			pmp->pm_w2u = NULL;
150134345Stjr			pmp->pm_u2w = NULL;
151134345Stjr			pmp->pm_d2u = NULL;
152134345Stjr			pmp->pm_u2d = NULL;
153134345Stjr		}
154134345Stjr	}
155134345Stjr
156138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v))
157138471Sphk		pmp->pm_gid = v;
158138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v))
159138471Sphk		pmp->pm_uid = v;
160138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v))
161138471Sphk		pmp->pm_mask = v & ALLPERMS;
162138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v))
163138471Sphk		pmp->pm_dirmask = v & ALLPERMS;
164138471Sphk	vfs_flagopt(mp->mnt_optnew, "shortname",
165138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
166138471Sphk	vfs_flagopt(mp->mnt_optnew, "longname",
167138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
168138471Sphk	vfs_flagopt(mp->mnt_optnew, "kiconv",
169138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_KICONV);
17033548Sjkh
171138471Sphk	/* XXX: Can't use flagopt due to negative option */
172138471Sphk	if (!vfs_getopt(mp->mnt_optnew, "win95", NULL, NULL))
173138471Sphk		pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95;
174138471Sphk	else
175138471Sphk		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
176138471Sphk
17733548Sjkh	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
17833548Sjkh		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
17933548Sjkh	else if (!(pmp->pm_flags &
18033548Sjkh	    (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
18133548Sjkh		struct vnode *rootvp;
18233548Sjkh
18333548Sjkh		/*
18433548Sjkh		 * Try to divine whether to support Win'95 long filenames
18533548Sjkh		 */
18633548Sjkh		if (FAT32(pmp))
18733548Sjkh			pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
18833548Sjkh		else {
189132023Salfred			if ((error = msdosfs_root(mp, &rootvp, td)) != 0)
19033548Sjkh				return error;
19133548Sjkh			pmp->pm_flags |= findwin95(VTODE(rootvp))
19233548Sjkh				? MSDOSFSMNT_LONGNAME
19333548Sjkh					: MSDOSFSMNT_SHORTNAME;
19433548Sjkh			vput(rootvp);
19533548Sjkh		}
19633548Sjkh	}
19733548Sjkh	return 0;
19833548Sjkh}
19933548Sjkh
200138471Sphkstatic int
201138471Sphkmsdosfs_cmount(struct mntarg *ma, void *data, int flags, struct thread *td)
202138471Sphk{
203138471Sphk	struct msdosfs_args args;
204138471Sphk	int error;
205138471Sphk
206138471Sphk	if (data == NULL)
207138471Sphk		return (EINVAL);
208138471Sphk	error = copyin(data, &args, sizeof args);
209138471Sphk	if (error)
210138471Sphk		return (error);
211138471Sphk
212138471Sphk	ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN);
213138471Sphk	ma = mount_arg(ma, "export", &args.export, sizeof args.export);
214138471Sphk	ma = mount_argf(ma, "uid", "%d", args.uid);
215138471Sphk	ma = mount_argf(ma, "gid", "%d", args.gid);
216138471Sphk	ma = mount_argf(ma, "mask", "%d", args.mask);
217138471Sphk	ma = mount_argf(ma, "dirmask", "%d", args.dirmask);
218138471Sphk
219138471Sphk        ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname");
220138471Sphk        ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname");
221138471Sphk        ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95");
222138471Sphk        ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv");
223138471Sphk
224138471Sphk        ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN);
225138471Sphk        ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN);
226138471Sphk        ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN);
227138471Sphk
228138471Sphk	error = kernel_mount(ma, flags);
229138471Sphk
230138471Sphk	return (error);
231138471Sphk}
232138471Sphk
2332893Sdfr/*
2348876Srgrimes * mp - path - addr in user space of mount point (ie /usr or whatever)
2352893Sdfr * data - addr in user space of mount params including the name of the block
2368876Srgrimes * special file to treat as a filesystem.
2372893Sdfr */
23812144Sphkstatic int
239138471Sphkmsdosfs_mount(struct mount *mp, struct thread *td)
2402893Sdfr{
2412893Sdfr	struct vnode *devvp;	  /* vnode for blk device to mount */
242138689Sphk	struct export_args export;
24333548Sjkh	/* msdosfs specific mount control block */
24433548Sjkh	struct msdosfsmount *pmp = NULL;
245132902Sphk	struct nameidata ndp;
246138689Sphk	int error, flags;
24733548Sjkh	mode_t accessmode;
248138471Sphk	char *from;
2492893Sdfr
250138471Sphk	if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts))
251138471Sphk		return (EINVAL);
252138471Sphk
2532893Sdfr	/*
25433548Sjkh	 * If updating, check whether changing from read-only to
25533548Sjkh	 * read/write; if there is no device name, that's all we do.
2562893Sdfr	 */
2572893Sdfr	if (mp->mnt_flag & MNT_UPDATE) {
25833548Sjkh		pmp = VFSTOMSDOSFS(mp);
259138689Sphk
260138689Sphk		error = vfs_copyopt(mp->mnt_optnew, "export",
261138689Sphk		    &export, sizeof export);
262138689Sphk		if (error == 0 && export.ex_flags != 0) {
263138689Sphk			/*
264138689Sphk			 * Process export requests.
265138689Sphk			 */
266138689Sphk			if ((export.ex_flags & MNT_EXPORTED) != 0 &&
267138689Sphk			    (pmp->pm_flags & MSDOSFS_LARGEFS) != 0)
268138689Sphk				return (EOPNOTSUPP);
269138689Sphk			return (vfs_export(mp, &export));
270138689Sphk		}
271137036Sphk		if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
272138471Sphk		    vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
273133287Sphk			error = VFS_SYNC(mp, MNT_WAIT, td->td_ucred, td);
274133287Sphk			if (error)
275133287Sphk				return (error);
2762893Sdfr			flags = WRITECLOSE;
2772893Sdfr			if (mp->mnt_flag & MNT_FORCE)
2782893Sdfr				flags |= FORCECLOSE;
279132023Salfred			error = vflush(mp, 0, flags, td);
280138471Sphk			if (error)
281138471Sphk				return (error);
282137036Sphk			DROP_GIANT();
283137036Sphk			g_topology_lock();
284137036Sphk			g_access(pmp->pm_cp, 0, -1, 0);
285137036Sphk			g_topology_unlock();
286137036Sphk			PICKUP_GIANT();
287138471Sphk		} else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
288138471Sphk		    !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
28933548Sjkh			/*
29033548Sjkh			 * If upgrade to read-write by non-root, then verify
29133548Sjkh			 * that user has necessary permissions on the device.
29233548Sjkh			 */
29393593Sjhb			if (suser(td)) {
29433548Sjkh				devvp = pmp->pm_devvp;
29583366Sjulian				vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
29633548Sjkh				error = VOP_ACCESS(devvp, VREAD | VWRITE,
29791406Sjhb						   td->td_ucred, td);
29833548Sjkh				if (error) {
29983366Sjulian					VOP_UNLOCK(devvp, 0, td);
30033548Sjkh					return (error);
30133548Sjkh				}
30283366Sjulian				VOP_UNLOCK(devvp, 0, td);
30333548Sjkh			}
304137036Sphk			DROP_GIANT();
305137036Sphk			g_topology_lock();
306137036Sphk			error = g_access(pmp->pm_cp, 0, 1, 0);
307137036Sphk			g_topology_unlock();
308137036Sphk			PICKUP_GIANT();
309137036Sphk			if (error)
310137036Sphk				return (error);
311123963Sbde
312123963Sbde			/* Now that the volume is modifiable, mark it dirty. */
313123873Strhodes			error = markvoldirty(pmp, 1);
314123873Strhodes			if (error)
315123963Sbde				return (error);
31633548Sjkh		}
317138471Sphk		vfs_flagopt(mp->mnt_optnew, "ro",
318138471Sphk		    &pmp->pm_flags, MSDOSFSMNT_RONLY);
319138471Sphk		vfs_flagopt(mp->mnt_optnew, "ro",
320138471Sphk		    &mp->mnt_flag, MNT_RDONLY);
321138471Sphk		if (vfs_getopt(mp->mnt_optnew, "from", NULL, NULL)) {
32287068Sjhb#ifdef	__notyet__	/* doesn't work correctly with current mountd	XXX */
32333548Sjkh			if (args.flags & MSDOSFSMNT_MNTOPT) {
32433548Sjkh				pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT;
32533548Sjkh				pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT;
32633548Sjkh				if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
32733548Sjkh					pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
32833548Sjkh			}
32933548Sjkh#endif
3302893Sdfr		}
3312893Sdfr	}
3322893Sdfr	/*
33333548Sjkh	 * Not an update, or updating the name: look up the name
334125796Sbde	 * and verify that it refers to a sensible disk device.
3352893Sdfr	 */
336138471Sphk	if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL))
337138471Sphk		return (EINVAL);
338138471Sphk	NDINIT(&ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, from, td);
339132902Sphk	error = namei(&ndp);
34033548Sjkh	if (error)
34133548Sjkh		return (error);
342132902Sphk	devvp = ndp.ni_vp;
343132902Sphk	NDFREE(&ndp, NDF_ONLY_PNBUF);
3448876Srgrimes
34555756Sphk	if (!vn_isdisk(devvp, &error)) {
3462893Sdfr		vrele(devvp);
34755756Sphk		return (error);
3482893Sdfr	}
3492893Sdfr	/*
35033548Sjkh	 * If mount by non-root, then verify that user has necessary
35133548Sjkh	 * permissions on the device.
3522893Sdfr	 */
35393593Sjhb	if (suser(td)) {
35433548Sjkh		accessmode = VREAD;
35533548Sjkh		if ((mp->mnt_flag & MNT_RDONLY) == 0)
35633548Sjkh			accessmode |= VWRITE;
35783366Sjulian		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
35891406Sjhb		error = VOP_ACCESS(devvp, accessmode, td->td_ucred, td);
35933548Sjkh		if (error) {
36033548Sjkh			vput(devvp);
36133548Sjkh			return (error);
36233548Sjkh		}
36383366Sjulian		VOP_UNLOCK(devvp, 0, td);
36433548Sjkh	}
36533548Sjkh	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
366138471Sphk		error = mountmsdosfs(devvp, mp, td);
36733548Sjkh#ifdef MSDOSFS_DEBUG		/* only needed for the printf below */
36833548Sjkh		pmp = VFSTOMSDOSFS(mp);
36933548Sjkh#endif
37033548Sjkh	} else {
3712893Sdfr		if (devvp != pmp->pm_devvp)
37233548Sjkh			error = EINVAL;	/* XXX needs translation */
3732893Sdfr		else
3742893Sdfr			vrele(devvp);
3752893Sdfr	}
3762893Sdfr	if (error) {
3772893Sdfr		vrele(devvp);
37833548Sjkh		return (error);
37933548Sjkh	}
38033548Sjkh
381138471Sphk	error = update_mp(mp, td);
38233548Sjkh	if (error) {
383134345Stjr		if ((mp->mnt_flag & MNT_UPDATE) == 0)
384134345Stjr			msdosfs_unmount(mp, MNT_FORCE, td);
3852893Sdfr		return error;
3862893Sdfr	}
387138471Sphk
388138471Sphk	vfs_mountedfrom(mp, from);
3892893Sdfr#ifdef MSDOSFS_DEBUG
390138471Sphk	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
3912893Sdfr#endif
39233548Sjkh	return (0);
3932893Sdfr}
3942893Sdfr
39512144Sphkstatic int
396138471Sphkmountmsdosfs(devvp, mp, td)
3972893Sdfr	struct vnode *devvp;
3982893Sdfr	struct mount *mp;
39983366Sjulian	struct thread *td;
4002893Sdfr{
40133548Sjkh	struct msdosfsmount *pmp;
40233548Sjkh	struct buf *bp;
403130585Sphk	struct cdev *dev = devvp->v_rdev;
4042893Sdfr	union bootsector *bsp;
4052893Sdfr	struct byte_bpb33 *b33;
4062893Sdfr	struct byte_bpb50 *b50;
40733548Sjkh	struct byte_bpb710 *b710;
40833548Sjkh	u_int8_t SecPerClust;
40955188Sbp	u_long clusters;
41033548Sjkh	int	ronly, error;
411137036Sphk	struct g_consumer *cp;
412137036Sphk	struct bufobj *bo;
4132893Sdfr
414138471Sphk	ronly = !vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL);
415137036Sphk	/* XXX: use VOP_ACCESS to check FS perms */
416137036Sphk	DROP_GIANT();
417137036Sphk	g_topology_lock();
418137036Sphk	error = g_vfs_open(devvp, &cp, "msdos", ronly ? 0 : 1);
419137036Sphk	g_topology_unlock();
420137036Sphk	PICKUP_GIANT();
42183366Sjulian	VOP_UNLOCK(devvp, 0, td);
4223152Sphk	if (error)
42333548Sjkh		return (error);
42433548Sjkh
425137036Sphk	bo = &devvp->v_bufobj;
42633548Sjkh	bp  = NULL; /* both used in error_exit */
42733548Sjkh	pmp = NULL;
42833548Sjkh
4292893Sdfr	/*
43033548Sjkh	 * Read the boot sector of the filesystem, and then check the
43133548Sjkh	 * boot signature.  If not a dos boot sector then error out.
43256674Snyan	 *
433102391Sbde	 * NOTE: 2048 is a maximum sector size in current...
4342893Sdfr	 */
435102391Sbde	error = bread(devvp, 0, 2048, NOCRED, &bp);
4363152Sphk	if (error)
4372893Sdfr		goto error_exit;
43833548Sjkh	bp->b_flags |= B_AGE;
43933548Sjkh	bsp = (union bootsector *)bp->b_data;
44033548Sjkh	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
44133548Sjkh	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
442105655Sjhb	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
44333548Sjkh
44456674Snyan#ifndef MSDOSFS_NOCHECKSIG
44587068Sjhb	if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
44687068Sjhb	    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
44787068Sjhb		error = EINVAL;
44887068Sjhb		goto error_exit;
44987068Sjhb	}
45056674Snyan#endif
4512893Sdfr
452111119Simp	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO);
4532893Sdfr	pmp->pm_mountp = mp;
454137036Sphk	pmp->pm_cp = cp;
455137036Sphk	pmp->pm_bo = bo;
4562893Sdfr
4572893Sdfr	/*
4582893Sdfr	 * Compute several useful quantities from the bpb in the
4592893Sdfr	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
4602893Sdfr	 * the fields that are different between dos 5 and dos 3.3.
4612893Sdfr	 */
46233548Sjkh	SecPerClust = b50->bpbSecPerClust;
4632893Sdfr	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
464113979Sjhb	if (pmp->pm_BytesPerSec < DEV_BSIZE) {
465113979Sjhb		error = EINVAL;
466113979Sjhb		goto error_exit;
467113979Sjhb	}
4682893Sdfr	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
4692893Sdfr	pmp->pm_FATs = b50->bpbFATs;
4702893Sdfr	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
4712893Sdfr	pmp->pm_Sectors = getushort(b50->bpbSectors);
4722893Sdfr	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
4732893Sdfr	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
4742893Sdfr	pmp->pm_Heads = getushort(b50->bpbHeads);
47533548Sjkh	pmp->pm_Media = b50->bpbMedia;
4762893Sdfr
47756674Snyan	/* calculate the ratio of sector size to DEV_BSIZE */
47856674Snyan	pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE;
47956674Snyan
48087068Sjhb	/* XXX - We should probably check more values here */
48187068Sjhb	if (!pmp->pm_BytesPerSec || !SecPerClust
482126998Srwatson		|| !pmp->pm_Heads
48316363Sasami#ifdef PC98
48487068Sjhb    		|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
48516363Sasami#else
48687068Sjhb		|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
48716363Sasami#endif
48887068Sjhb		error = EINVAL;
48987068Sjhb		goto error_exit;
49087068Sjhb	}
4912893Sdfr
4922893Sdfr	if (pmp->pm_Sectors == 0) {
4932893Sdfr		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
4942893Sdfr		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
4952893Sdfr	} else {
4962893Sdfr		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
4972893Sdfr		pmp->pm_HugeSectors = pmp->pm_Sectors;
4982893Sdfr	}
499131523Stjr#ifndef MSDOSFS_LARGE
50035511Sdt	if (pmp->pm_HugeSectors > 0xffffffff /
50135511Sdt	    (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) {
50233548Sjkh		/*
50333548Sjkh		 * We cannot deal currently with this size of disk
50433548Sjkh		 * due to fileid limitations (see msdosfs_getattr and
50533548Sjkh		 * msdosfs_readdir)
50633548Sjkh		 */
50733548Sjkh		error = EINVAL;
50835046Sache		printf("mountmsdosfs(): disk too big, sorry\n");
50933548Sjkh		goto error_exit;
51033548Sjkh	}
511131523Stjr#endif	/* !MSDOSFS_LARGE */
51233548Sjkh
51333548Sjkh	if (pmp->pm_RootDirEnts == 0) {
51433548Sjkh		if (bsp->bs710.bsBootSectSig2 != BOOTSIG2
51533548Sjkh		    || bsp->bs710.bsBootSectSig3 != BOOTSIG3
51633548Sjkh		    || pmp->pm_Sectors
51733548Sjkh		    || pmp->pm_FATsecs
51833548Sjkh		    || getushort(b710->bpbFSVers)) {
51933548Sjkh			error = EINVAL;
52035046Sache			printf("mountmsdosfs(): bad FAT32 filesystem\n");
52133548Sjkh			goto error_exit;
52233548Sjkh		}
52333548Sjkh		pmp->pm_fatmask = FAT32_MASK;
52433548Sjkh		pmp->pm_fatmult = 4;
52533548Sjkh		pmp->pm_fatdiv = 1;
52633548Sjkh		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
52733548Sjkh		if (getushort(b710->bpbExtFlags) & FATMIRROR)
52833548Sjkh			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
52933548Sjkh		else
53033548Sjkh			pmp->pm_flags |= MSDOSFS_FATMIRROR;
53133548Sjkh	} else
53233548Sjkh		pmp->pm_flags |= MSDOSFS_FATMIRROR;
53333548Sjkh
53456674Snyan	/*
53556674Snyan	 * Check a few values (could do some more):
53656674Snyan	 * - logical sector size: power of 2, >= block size
53756674Snyan	 * - sectors per cluster: power of 2, >= 1
53856674Snyan	 * - number of sectors:   >= 1, <= size of partition
539113979Sjhb	 * - number of FAT sectors: >= 1
54056674Snyan	 */
54156674Snyan	if ( (SecPerClust == 0)
54256674Snyan	  || (SecPerClust & (SecPerClust - 1))
54356674Snyan	  || (pmp->pm_BytesPerSec < DEV_BSIZE)
54456674Snyan	  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
54556674Snyan	  || (pmp->pm_HugeSectors == 0)
546113979Sjhb	  || (pmp->pm_FATsecs == 0)
54756674Snyan	) {
54856674Snyan		error = EINVAL;
54956674Snyan		goto error_exit;
55056674Snyan	}
55133548Sjkh
55256674Snyan	pmp->pm_HugeSectors *= pmp->pm_BlkPerSec;
55356674Snyan	pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */
55456674Snyan	pmp->pm_FATsecs     *= pmp->pm_BlkPerSec;
55556674Snyan	SecPerClust         *= pmp->pm_BlkPerSec;
55656674Snyan
55756674Snyan	pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec;
55856674Snyan
55933548Sjkh	if (FAT32(pmp)) {
56033548Sjkh		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
56133548Sjkh		pmp->pm_firstcluster = pmp->pm_fatblk
56233548Sjkh			+ (pmp->pm_FATs * pmp->pm_FATsecs);
56356674Snyan		pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec;
56433548Sjkh	} else {
56533548Sjkh		pmp->pm_rootdirblk = pmp->pm_fatblk +
56633548Sjkh			(pmp->pm_FATs * pmp->pm_FATsecs);
56733548Sjkh		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
56856674Snyan				       + DEV_BSIZE - 1)
56956674Snyan			/ DEV_BSIZE; /* in blocks */
57033548Sjkh		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
57133548Sjkh	}
57233548Sjkh
57355188Sbp	pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
57455188Sbp	    SecPerClust + 1;
57556674Snyan	pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */
57633548Sjkh
57733548Sjkh	if (pmp->pm_fatmask == 0) {
57833548Sjkh		if (pmp->pm_maxcluster
57933548Sjkh		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
58033548Sjkh			/*
58133548Sjkh			 * This will usually be a floppy disk. This size makes
58233548Sjkh			 * sure that one fat entry will not be split across
58333548Sjkh			 * multiple blocks.
58433548Sjkh			 */
58533548Sjkh			pmp->pm_fatmask = FAT12_MASK;
58633548Sjkh			pmp->pm_fatmult = 3;
58733548Sjkh			pmp->pm_fatdiv = 2;
58833548Sjkh		} else {
58933548Sjkh			pmp->pm_fatmask = FAT16_MASK;
59033548Sjkh			pmp->pm_fatmult = 2;
59133548Sjkh			pmp->pm_fatdiv = 1;
59233548Sjkh		}
59333548Sjkh	}
59455188Sbp
59555188Sbp	clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv;
59655188Sbp	if (pmp->pm_maxcluster >= clusters) {
59755188Sbp		printf("Warning: number of clusters (%ld) exceeds FAT "
59855188Sbp		    "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters);
59955188Sbp		pmp->pm_maxcluster = clusters - 1;
60055188Sbp	}
60155188Sbp
60255188Sbp
6032893Sdfr	if (FAT12(pmp))
6042893Sdfr		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
6052893Sdfr	else
606102391Sbde		pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE;
60733548Sjkh
60856674Snyan	pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE;
60956674Snyan	pmp->pm_bnshift = ffs(DEV_BSIZE) - 1;
6102893Sdfr
6112893Sdfr	/*
6122893Sdfr	 * Compute mask and shift value for isolating cluster relative byte
6132893Sdfr	 * offsets and cluster numbers from a file offset.
6142893Sdfr	 */
61556674Snyan	pmp->pm_bpcluster = SecPerClust * DEV_BSIZE;
61633548Sjkh	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
61733548Sjkh	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
61833548Sjkh
61933548Sjkh	/*
62033548Sjkh	 * Check for valid cluster size
62133548Sjkh	 * must be a power of 2
62233548Sjkh	 */
62333548Sjkh	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
6242893Sdfr		error = EINVAL;
6252893Sdfr		goto error_exit;
6262893Sdfr	}
6272893Sdfr
62833548Sjkh	/*
62933548Sjkh	 * Release the bootsector buffer.
63033548Sjkh	 */
63133548Sjkh	brelse(bp);
63233548Sjkh	bp = NULL;
63333548Sjkh
63433548Sjkh	/*
63533548Sjkh	 * Check FSInfo.
63633548Sjkh	 */
63733548Sjkh	if (pmp->pm_fsinfo) {
63833548Sjkh		struct fsinfo *fp;
63933548Sjkh
64056674Snyan		if ((error = bread(devvp, pmp->pm_fsinfo, fsi_size(pmp),
64156674Snyan		    NOCRED, &bp)) != 0)
64233548Sjkh			goto error_exit;
64333548Sjkh		fp = (struct fsinfo *)bp->b_data;
64433548Sjkh		if (!bcmp(fp->fsisig1, "RRaA", 4)
64533548Sjkh		    && !bcmp(fp->fsisig2, "rrAa", 4)
64633548Sjkh		    && !bcmp(fp->fsisig3, "\0\0\125\252", 4)
647125934Stjr		    && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) {
64833548Sjkh			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
649125934Stjr			if (pmp->pm_nxtfree == 0xffffffff)
650125934Stjr				pmp->pm_nxtfree = CLUST_FIRST;
651125934Stjr		} else
65233548Sjkh			pmp->pm_fsinfo = 0;
65333548Sjkh		brelse(bp);
65433548Sjkh		bp = NULL;
65516363Sasami	}
6562893Sdfr
6572893Sdfr	/*
658102295Strhodes	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
6592893Sdfr	 */
660102295Strhodes	if (pmp->pm_fsinfo && pmp->pm_nxtfree > pmp->pm_maxcluster) {
661102392Sbde		printf(
662102392Sbde		"Next free cluster in FSInfo (%lu) exceeds maxcluster (%lu)\n",
663102392Sbde		    pmp->pm_nxtfree, pmp->pm_maxcluster);
664102295Strhodes		error = EINVAL;
665102295Strhodes		goto error_exit;
666102295Strhodes	}
6672893Sdfr
6682893Sdfr	/*
6692893Sdfr	 * Allocate memory for the bitmap of allocated clusters, and then
6702893Sdfr	 * fill it in.
6712893Sdfr	 */
672126086Sbde	pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS)
6732893Sdfr				  * sizeof(*pmp->pm_inusemap),
674111119Simp				  M_MSDOSFSFAT, M_WAITOK);
6752893Sdfr
6762893Sdfr	/*
6772893Sdfr	 * fillinusemap() needs pm_devvp.
6782893Sdfr	 */
6792893Sdfr	pmp->pm_dev = dev;
6802893Sdfr	pmp->pm_devvp = devvp;
6812893Sdfr
6822893Sdfr	/*
6832893Sdfr	 * Have the inuse map filled in.
6842893Sdfr	 */
68533548Sjkh	if ((error = fillinusemap(pmp)) != 0)
6862893Sdfr		goto error_exit;
6872893Sdfr
6882893Sdfr	/*
6892893Sdfr	 * If they want fat updates to be synchronous then let them suffer
6902893Sdfr	 * the performance degradation in exchange for the on disk copy of
6912893Sdfr	 * the fat being correct just about all the time.  I suppose this
6922893Sdfr	 * would be a good thing to turn on if the kernel is still flakey.
6932893Sdfr	 */
69433548Sjkh	if (mp->mnt_flag & MNT_SYNCHRONOUS)
69533548Sjkh		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
6962893Sdfr
6972893Sdfr	/*
6982893Sdfr	 * Finish up.
6992893Sdfr	 */
70033548Sjkh	if (ronly)
70133548Sjkh		pmp->pm_flags |= MSDOSFSMNT_RONLY;
702123873Strhodes	else {
703123963Sbde		/* Mark the volume dirty while it is mounted read/write. */
704123963Sbde		if ((error = markvoldirty(pmp, 1)) != 0)
705123963Sbde			goto error_exit;
7062893Sdfr		pmp->pm_fmod = 1;
707123873Strhodes	}
7082893Sdfr	mp->mnt_data = (qaddr_t) pmp;
70950256Sbde	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
71023134Sbde	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
71123997Speter	mp->mnt_flag |= MNT_LOCAL;
7122893Sdfr
713131523Stjr#ifdef MSDOSFS_LARGE
714131523Stjr	msdosfs_fileno_init(mp);
715131523Stjr#endif
716131523Stjr
7172893Sdfr	return 0;
7182893Sdfr
71933548Sjkherror_exit:
72033548Sjkh	if (bp)
72133548Sjkh		brelse(bp);
722137036Sphk	if (cp != NULL) {
723137036Sphk		DROP_GIANT();
724137036Sphk		g_topology_lock();
725137036Sphk		g_wither_geom_close(cp->geom, ENXIO);
726137036Sphk		g_topology_unlock();
727137036Sphk		PICKUP_GIANT();
728137036Sphk	}
7292893Sdfr	if (pmp) {
7302893Sdfr		if (pmp->pm_inusemap)
73133548Sjkh			free(pmp->pm_inusemap, M_MSDOSFSFAT);
73233548Sjkh		free(pmp, M_MSDOSFSMNT);
73333548Sjkh		mp->mnt_data = (qaddr_t)0;
7342893Sdfr	}
73533548Sjkh	return (error);
7362893Sdfr}
7372893Sdfr
7382893Sdfr/*
7392893Sdfr * Unmount the filesystem described by mp.
7402893Sdfr */
74112144Sphkstatic int
74283366Sjulianmsdosfs_unmount(mp, mntflags, td)
7432893Sdfr	struct mount *mp;
7442893Sdfr	int mntflags;
74583366Sjulian	struct thread *td;
7462893Sdfr{
74733548Sjkh	struct msdosfsmount *pmp;
74833548Sjkh	int error, flags;
7492893Sdfr
75033548Sjkh	flags = 0;
75133548Sjkh	if (mntflags & MNT_FORCE)
7522893Sdfr		flags |= FORCECLOSE;
753132023Salfred	error = vflush(mp, 0, flags, td);
7543152Sphk	if (error)
7552893Sdfr		return error;
75633548Sjkh	pmp = VFSTOMSDOSFS(mp);
757120492Sfjoe	if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) {
758120492Sfjoe		if (pmp->pm_w2u)
759120492Sfjoe			msdosfs_iconv->close(pmp->pm_w2u);
760120492Sfjoe		if (pmp->pm_u2w)
761120492Sfjoe			msdosfs_iconv->close(pmp->pm_u2w);
762120492Sfjoe		if (pmp->pm_d2u)
763120492Sfjoe			msdosfs_iconv->close(pmp->pm_d2u);
764120492Sfjoe		if (pmp->pm_u2d)
765120492Sfjoe			msdosfs_iconv->close(pmp->pm_u2d);
766120492Sfjoe	}
767123873Strhodes
768123963Sbde	/* If the volume was mounted read/write, mark it clean now. */
769123963Sbde	if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) {
770123963Sbde		error = markvoldirty(pmp, 0);
771123963Sbde		if (error && (flags & FORCECLOSE) == 0)
772123963Sbde			return (error);
773123963Sbde	}
77433548Sjkh#ifdef MSDOSFS_DEBUG
77533548Sjkh	{
77633548Sjkh		struct vnode *vp = pmp->pm_devvp;
77733548Sjkh
778103936Sjeff		VI_LOCK(vp);
77933548Sjkh		printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
780101308Sjeff		printf("iflag %08lx, usecount %d, writecount %d, holdcnt %ld\n",
781101308Sjeff		    vp->vi_flag, vp->v_usecount, vp->v_writecount,
782101308Sjeff		    vp->v_holdcnt);
78351486Sdillon		printf("id %lu, mount %p, op %p\n",
78451486Sdillon		    vp->v_id, vp->v_mount, vp->v_op);
78533548Sjkh		printf("freef %p, freeb %p, mount %p\n",
78671999Sphk		    TAILQ_NEXT(vp, v_freelist), vp->v_freelist.tqe_prev,
78733548Sjkh		    vp->v_mount);
78833548Sjkh		printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n",
789136943Sphk		    TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd),
790136943Sphk		    TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd),
791136943Sphk		    vp->v_bufobj.bo_numoutput, vp->v_type);
792103314Snjl		printf("union %p, tag %s, data[0] %08x, data[1] %08x\n",
79333548Sjkh		    vp->v_socket, vp->v_tag,
79433548Sjkh		    ((u_int *)vp->v_data)[0],
79533548Sjkh		    ((u_int *)vp->v_data)[1]);
796103936Sjeff		VI_UNLOCK(vp);
79733548Sjkh	}
79833548Sjkh#endif
799137036Sphk	DROP_GIANT();
800137036Sphk	g_topology_lock();
801137036Sphk	g_wither_geom_close(pmp->pm_cp->geom, ENXIO);
802137036Sphk	g_topology_unlock();
803137036Sphk	PICKUP_GIANT();
8042893Sdfr	vrele(pmp->pm_devvp);
80533548Sjkh	free(pmp->pm_inusemap, M_MSDOSFSFAT);
806131523Stjr#ifdef MSDOSFS_LARGE
807131523Stjr	msdosfs_fileno_free(mp);
808131523Stjr#endif
80933548Sjkh	free(pmp, M_MSDOSFSMNT);
81033548Sjkh	mp->mnt_data = (qaddr_t)0;
81123997Speter	mp->mnt_flag &= ~MNT_LOCAL;
81233548Sjkh	return (error);
8132893Sdfr}
8142893Sdfr
81512144Sphkstatic int
816132023Salfredmsdosfs_root(mp, vpp, td)
8172893Sdfr	struct mount *mp;
8182893Sdfr	struct vnode **vpp;
819132023Salfred	struct thread *td;
8202893Sdfr{
82133548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
8222893Sdfr	struct denode *ndep;
8232893Sdfr	int error;
8242893Sdfr
8252893Sdfr#ifdef MSDOSFS_DEBUG
82633548Sjkh	printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
8272893Sdfr#endif
82833548Sjkh	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep);
82933548Sjkh	if (error)
83033548Sjkh		return (error);
83133548Sjkh	*vpp = DETOV(ndep);
83233548Sjkh	return (0);
8332893Sdfr}
8342893Sdfr
83512144Sphkstatic int
83683366Sjulianmsdosfs_statfs(mp, sbp, td)
8372893Sdfr	struct mount *mp;
8382893Sdfr	struct statfs *sbp;
83983366Sjulian	struct thread *td;
8402893Sdfr{
84133548Sjkh	struct msdosfsmount *pmp;
8422893Sdfr
84333548Sjkh	pmp = VFSTOMSDOSFS(mp);
8442893Sdfr	sbp->f_bsize = pmp->pm_bpcluster;
8452893Sdfr	sbp->f_iosize = pmp->pm_bpcluster;
84655188Sbp	sbp->f_blocks = pmp->pm_maxcluster + 1;
8472893Sdfr	sbp->f_bfree = pmp->pm_freeclustercount;
8482893Sdfr	sbp->f_bavail = pmp->pm_freeclustercount;
8492893Sdfr	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
8502893Sdfr	sbp->f_ffree = 0;	/* what to put in here? */
85133548Sjkh	return (0);
8522893Sdfr}
8532893Sdfr
85412144Sphkstatic int
85583366Sjulianmsdosfs_sync(mp, waitfor, cred, td)
8562893Sdfr	struct mount *mp;
8572893Sdfr	int waitfor;
8582893Sdfr	struct ucred *cred;
85983366Sjulian	struct thread *td;
8602893Sdfr{
86133548Sjkh	struct vnode *vp, *nvp;
8622893Sdfr	struct denode *dep;
86333548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
86433548Sjkh	int error, allerror = 0;
8652893Sdfr
8662893Sdfr	/*
8672893Sdfr	 * If we ever switch to not updating all of the fats all the time,
8682893Sdfr	 * this would be the place to update them from the first one.
8692893Sdfr	 */
87046568Speter	if (pmp->pm_fmod != 0) {
87133548Sjkh		if (pmp->pm_flags & MSDOSFSMNT_RONLY)
8722893Sdfr			panic("msdosfs_sync: rofs mod");
8732893Sdfr		else {
8742893Sdfr			/* update fats here */
8752893Sdfr		}
87646568Speter	}
8772893Sdfr	/*
87833548Sjkh	 * Write back each (modified) denode.
8792893Sdfr	 */
880122091Skan	MNT_ILOCK(mp);
8812893Sdfrloop:
882131551Sphk	MNT_VNODE_FOREACH(vp, mp, nvp) {
883120733Sjeff		VI_LOCK(vp);
884137008Sphk		if (vp->v_type == VNON || (vp->v_iflag & VI_XLOCK)) {
885120785Sjeff			VI_UNLOCK(vp);
886120785Sjeff			continue;
887120785Sjeff		}
888122091Skan		MNT_IUNLOCK(mp);
8892893Sdfr		dep = VTODE(vp);
890137008Sphk		if ((dep->de_flag &
89135511Sdt		    (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 &&
892136943Sphk		    (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
893137008Sphk		    waitfor == MNT_LAZY)) {
894103936Sjeff			VI_UNLOCK(vp);
895122091Skan			MNT_ILOCK(mp);
8962893Sdfr			continue;
89723134Sbde		}
89883366Sjulian		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
89923134Sbde		if (error) {
900122091Skan			MNT_ILOCK(mp);
90123134Sbde			if (error == ENOENT)
90223134Sbde				goto loop;
90323134Sbde			continue;
90423134Sbde		}
90583366Sjulian		error = VOP_FSYNC(vp, cred, waitfor, td);
9063152Sphk		if (error)
9072893Sdfr			allerror = error;
908121874Skan		VOP_UNLOCK(vp, 0, td);
909121874Skan		vrele(vp);
910122091Skan		MNT_ILOCK(mp);
9112893Sdfr	}
912122091Skan	MNT_IUNLOCK(mp);
9132893Sdfr
9142893Sdfr	/*
9152893Sdfr	 * Flush filesystem control info.
9162893Sdfr	 */
91735511Sdt	if (waitfor != MNT_LAZY) {
91883366Sjulian		vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, td);
91983366Sjulian		error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, td);
92035511Sdt		if (error)
92135511Sdt			allerror = error;
92283366Sjulian		VOP_UNLOCK(pmp->pm_devvp, 0, td);
92335511Sdt	}
92433548Sjkh	return (allerror);
9252893Sdfr}
9262893Sdfr
92712144Sphkstatic int
92851138Salfredmsdosfs_fhtovp(mp, fhp, vpp)
9292893Sdfr	struct mount *mp;
9302893Sdfr	struct fid *fhp;
9312893Sdfr	struct vnode **vpp;
9322893Sdfr{
93333548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
9342893Sdfr	struct defid *defhp = (struct defid *) fhp;
9352893Sdfr	struct denode *dep;
9362893Sdfr	int error;
9372893Sdfr
93833548Sjkh	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep);
9392893Sdfr	if (error) {
9402893Sdfr		*vpp = NULLVP;
94133548Sjkh		return (error);
9422893Sdfr	}
9432893Sdfr	*vpp = DETOV(dep);
94451138Salfred	return (0);
94551138Salfred}
94651138Salfred
94751138Salfredstatic int
9482893Sdfrmsdosfs_vptofh(vp, fhp)
9492893Sdfr	struct vnode *vp;
9502893Sdfr	struct fid *fhp;
9512893Sdfr{
95233548Sjkh	struct denode *dep;
95333548Sjkh	struct defid *defhp;
9542893Sdfr
95533548Sjkh	dep = VTODE(vp);
95633548Sjkh	defhp = (struct defid *)fhp;
9572893Sdfr	defhp->defid_len = sizeof(struct defid);
9582893Sdfr	defhp->defid_dirclust = dep->de_dirclust;
9592893Sdfr	defhp->defid_dirofs = dep->de_diroffset;
96033548Sjkh	/* defhp->defid_gen = dep->de_gen; */
96133548Sjkh	return (0);
9622893Sdfr}
9632893Sdfr
96412145Sphkstatic struct vfsops msdosfs_vfsops = {
965116271Sphk	.vfs_fhtovp =		msdosfs_fhtovp,
966116271Sphk	.vfs_init =		msdosfs_init,
967138471Sphk	.vfs_mount =		msdosfs_mount,
968138471Sphk	.vfs_cmount =		msdosfs_cmount,
969116271Sphk	.vfs_root =		msdosfs_root,
970116271Sphk	.vfs_statfs =		msdosfs_statfs,
971116271Sphk	.vfs_sync =		msdosfs_sync,
972116271Sphk	.vfs_uninit =		msdosfs_uninit,
973116271Sphk	.vfs_unmount =		msdosfs_unmount,
974116271Sphk	.vfs_vptofh =		msdosfs_vptofh,
9752893Sdfr};
9762946Swollman
97777577SruVFS_SET(msdosfs_vfsops, msdosfs, 0);
978120492SfjoeMODULE_VERSION(msdosfs, 1);
979