msdosfs_vfsops.c revision 170188
150477Speter/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_vfsops.c 170188 2007-06-01 17:06:46Z trhodes $ */
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 */
35139776Simp/*-
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>
55164033Srwatson#include <sys/priv.h>
562893Sdfr#include <sys/proc.h>
572893Sdfr#include <sys/kernel.h>
582893Sdfr#include <sys/vnode.h>
592893Sdfr#include <sys/mount.h>
6060041Sphk#include <sys/bio.h>
612893Sdfr#include <sys/buf.h>
6224131Sbde#include <sys/fcntl.h>
632893Sdfr#include <sys/malloc.h>
6433548Sjkh#include <sys/stat.h> 				/* defines ALLPERMS */
65120492Sfjoe#include <sys/iconv.h>
66102391Sbde#include <sys/mutex.h>
672893Sdfr
6877162Sru#include <fs/msdosfs/bpb.h>
6977162Sru#include <fs/msdosfs/bootsect.h>
70120492Sfjoe#include <fs/msdosfs/msdosfsmount.h>
7177162Sru#include <fs/msdosfs/direntry.h>
7277162Sru#include <fs/msdosfs/denode.h>
7377162Sru#include <fs/msdosfs/fat.h>
742893Sdfr
75137036Sphk#include <geom/geom.h>
76137036Sphk#include <geom/geom_vfs.h>
77137036Sphk
78138471Sphk/* List of mount options we support */
79138471Sphkstatic const char *msdosfs_opts[] = {
80138471Sphk	"from",
81166559Srodrigc	"atime", "export", "force", "sync",
82138471Sphk	"uid", "gid", "mask", "dirmask",
83152610Srodrigc	"shortname", "shortnames", "longname", "longnames", "nowin95", "win95",
84166340Srodrigc	"kiconv", "cs_win", "cs_dos", "cs_local", "large",
85138471Sphk	NULL
86138471Sphk};
87138471Sphk
88102391Sbde#define MSDOSFS_DFLTBSIZE       4096
89102391Sbde
9056674Snyan#if 1 /*def PC98*/
9156674Snyan/*
9256674Snyan * XXX - The boot signature formatted by NEC PC-98 DOS looks like a
9356674Snyan *       garbage or a random value :-{
9456674Snyan *       If you want to use that broken-signatured media, define the
9556674Snyan *       following symbol even though PC/AT.
9656674Snyan *       (ex. mount PC-98 DOS formatted FD on PC/AT)
9756674Snyan */
9856674Snyan#define	MSDOSFS_NOCHECKSIG
9956674Snyan#endif
10056674Snyan
101151897SrwatsonMALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure");
102151897Srwatsonstatic MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table");
10330309Sphk
104120492Sfjoestruct iconv_functions *msdosfs_iconv = NULL;
105120492Sfjoe
106138471Sphkstatic int	update_mp(struct mount *mp, struct thread *td);
107138471Sphkstatic int	mountmsdosfs(struct vnode *devvp, struct mount *mp,
108132023Salfred		    struct thread *td);
109170188Strhodesstatic vfs_fhtovp_t	msdosfs_fhtovp;
110138471Sphkstatic vfs_mount_t	msdosfs_mount;
111101777Sphkstatic vfs_root_t	msdosfs_root;
112101777Sphkstatic vfs_statfs_t	msdosfs_statfs;
113101777Sphkstatic vfs_sync_t	msdosfs_sync;
114101777Sphkstatic vfs_unmount_t	msdosfs_unmount;
11512338Sbde
116134345Stjr/* Maximum length of a character set name (arbitrary). */
117134345Stjr#define	MAXCSLEN	64
118134345Stjr
11933548Sjkhstatic int
120166558Srodrigcupdate_mp(struct mount *mp, struct thread *td)
12133548Sjkh{
12233548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
123138471Sphk	void *dos, *win, *local;
124138471Sphk	int error, v;
12533548Sjkh
126138471Sphk	if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) {
127138471Sphk		if (msdosfs_iconv != NULL) {
128138471Sphk			error = vfs_getopt(mp->mnt_optnew,
129138471Sphk			    "cs_win", &win, NULL);
130138471Sphk			if (!error)
131138471Sphk				error = vfs_getopt(mp->mnt_optnew,
132138471Sphk				    "cs_local", &local, NULL);
133138471Sphk			if (!error)
134138471Sphk				error = vfs_getopt(mp->mnt_optnew,
135138471Sphk				    "cs_dos", &dos, NULL);
136138471Sphk			if (!error) {
137138471Sphk				msdosfs_iconv->open(win, local, &pmp->pm_u2w);
138138471Sphk				msdosfs_iconv->open(local, win, &pmp->pm_w2u);
139138471Sphk				msdosfs_iconv->open(dos, local, &pmp->pm_u2d);
140138471Sphk				msdosfs_iconv->open(local, dos, &pmp->pm_d2u);
141138471Sphk			}
142134345Stjr			if (error != 0)
143134345Stjr				return (error);
144134345Stjr		} else {
145134345Stjr			pmp->pm_w2u = NULL;
146134345Stjr			pmp->pm_u2w = NULL;
147134345Stjr			pmp->pm_d2u = NULL;
148134345Stjr			pmp->pm_u2d = NULL;
149134345Stjr		}
150134345Stjr	}
151134345Stjr
152138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v))
153138471Sphk		pmp->pm_gid = v;
154138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v))
155138471Sphk		pmp->pm_uid = v;
156138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v))
157138471Sphk		pmp->pm_mask = v & ALLPERMS;
158138471Sphk	if (1 == vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v))
159138471Sphk		pmp->pm_dirmask = v & ALLPERMS;
160138471Sphk	vfs_flagopt(mp->mnt_optnew, "shortname",
161138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
162152595Srodrigc	vfs_flagopt(mp->mnt_optnew, "shortnames",
163152595Srodrigc	    &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
164138471Sphk	vfs_flagopt(mp->mnt_optnew, "longname",
165138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
166152595Srodrigc	vfs_flagopt(mp->mnt_optnew, "longnames",
167152595Srodrigc	    &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
168138471Sphk	vfs_flagopt(mp->mnt_optnew, "kiconv",
169138471Sphk	    &pmp->pm_flags, MSDOSFSMNT_KICONV);
17033548Sjkh
171152610Srodrigc	if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0)
172152610Srodrigc		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
173152610Srodrigc	else
174138471Sphk		pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95;
175138471Sphk
17633548Sjkh	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
17733548Sjkh		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
17833548Sjkh	else if (!(pmp->pm_flags &
17933548Sjkh	    (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
18033548Sjkh		struct vnode *rootvp;
18133548Sjkh
18233548Sjkh		/*
18333548Sjkh		 * Try to divine whether to support Win'95 long filenames
18433548Sjkh		 */
18533548Sjkh		if (FAT32(pmp))
18633548Sjkh			pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
18733548Sjkh		else {
188144058Sjeff			if ((error =
189144058Sjeff			    msdosfs_root(mp, LK_EXCLUSIVE, &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 */
24233548Sjkh	/* msdosfs specific mount control block */
24333548Sjkh	struct msdosfsmount *pmp = NULL;
244132902Sphk	struct nameidata ndp;
245138689Sphk	int error, flags;
24633548Sjkh	mode_t accessmode;
247138471Sphk	char *from;
2482893Sdfr
249138471Sphk	if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts))
250138471Sphk		return (EINVAL);
251138471Sphk
2522893Sdfr	/*
25333548Sjkh	 * If updating, check whether changing from read-only to
25433548Sjkh	 * read/write; if there is no device name, that's all we do.
2552893Sdfr	 */
2562893Sdfr	if (mp->mnt_flag & MNT_UPDATE) {
257165836Srodrigc		int ro_to_rw = 0;
25833548Sjkh		pmp = VFSTOMSDOSFS(mp);
259138689Sphk
260158924Srodrigc		if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) {
261165022Srodrigc			/*
262165022Srodrigc			 * Forbid export requests if filesystem has
263165022Srodrigc			 * MSDOSFS_LARGEFS flag set.
264165022Srodrigc			 */
265165022Srodrigc			if ((pmp->pm_flags & MSDOSFS_LARGEFS) != 0) {
266165022Srodrigc				vfs_mount_error(mp,
267165022Srodrigc				    "MSDOSFS_LARGEFS flag set, cannot export");
268138689Sphk				return (EOPNOTSUPP);
269165022Srodrigc			}
270138689Sphk		}
271137036Sphk		if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
272138471Sphk		    vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
273140048Sphk			error = VFS_SYNC(mp, MNT_WAIT, 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();
287160939Sdelphij			/* Now the volume is clean. Mark it. */
288160939Sdelphij			error = markvoldirty(pmp, 0);
289160939Sdelphij			if (error && (flags & FORCECLOSE) == 0)
290160939Sdelphij				return (error);
291138471Sphk		} else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
292138471Sphk		    !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
29333548Sjkh			/*
29433548Sjkh			 * If upgrade to read-write by non-root, then verify
29533548Sjkh			 * that user has necessary permissions on the device.
29633548Sjkh			 */
297164033Srwatson			devvp = pmp->pm_devvp;
298164033Srwatson			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
299164033Srwatson			error = VOP_ACCESS(devvp, VREAD | VWRITE,
300164033Srwatson			   td->td_ucred, td);
301164033Srwatson			if (error)
302164033Srwatson				error = priv_check(td, PRIV_VFS_MOUNT_PERM);
303164033Srwatson			if (error) {
30483366Sjulian				VOP_UNLOCK(devvp, 0, td);
305164033Srwatson				return (error);
30633548Sjkh			}
307164033Srwatson			VOP_UNLOCK(devvp, 0, td);
308137036Sphk			DROP_GIANT();
309137036Sphk			g_topology_lock();
310137036Sphk			error = g_access(pmp->pm_cp, 0, 1, 0);
311137036Sphk			g_topology_unlock();
312137036Sphk			PICKUP_GIANT();
313137036Sphk			if (error)
314137036Sphk				return (error);
315123963Sbde
316165836Srodrigc			ro_to_rw = 1;
317165836Srodrigc		}
318165836Srodrigc		vfs_flagopt(mp->mnt_optnew, "ro",
319165836Srodrigc		    &pmp->pm_flags, MSDOSFSMNT_RONLY);
320165836Srodrigc		vfs_flagopt(mp->mnt_optnew, "ro",
321165836Srodrigc		    &mp->mnt_flag, MNT_RDONLY);
322165836Srodrigc
323165836Srodrigc		if (ro_to_rw) {
324123963Sbde			/* Now that the volume is modifiable, mark it dirty. */
325123873Strhodes			error = markvoldirty(pmp, 1);
326123873Strhodes			if (error)
327123963Sbde				return (error);
32833548Sjkh		}
3292893Sdfr	}
3302893Sdfr	/*
33133548Sjkh	 * Not an update, or updating the name: look up the name
332125796Sbde	 * and verify that it refers to a sensible disk device.
3332893Sdfr	 */
334138471Sphk	if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL))
335138471Sphk		return (EINVAL);
336149720Sssouhlal	NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from, td);
337132902Sphk	error = namei(&ndp);
33833548Sjkh	if (error)
33933548Sjkh		return (error);
340132902Sphk	devvp = ndp.ni_vp;
341132902Sphk	NDFREE(&ndp, NDF_ONLY_PNBUF);
3428876Srgrimes
34355756Sphk	if (!vn_isdisk(devvp, &error)) {
344149720Sssouhlal		vput(devvp);
34555756Sphk		return (error);
3462893Sdfr	}
3472893Sdfr	/*
34833548Sjkh	 * If mount by non-root, then verify that user has necessary
34933548Sjkh	 * permissions on the device.
3502893Sdfr	 */
351164033Srwatson	accessmode = VREAD;
352164033Srwatson	if ((mp->mnt_flag & MNT_RDONLY) == 0)
353164033Srwatson		accessmode |= VWRITE;
354164033Srwatson	error = VOP_ACCESS(devvp, accessmode, td->td_ucred, td);
355164033Srwatson	if (error)
356164033Srwatson		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
357164033Srwatson	if (error) {
358164033Srwatson		vput(devvp);
359164033Srwatson		return (error);
36033548Sjkh	}
36133548Sjkh	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
362138471Sphk		error = mountmsdosfs(devvp, mp, td);
36333548Sjkh#ifdef MSDOSFS_DEBUG		/* only needed for the printf below */
36433548Sjkh		pmp = VFSTOMSDOSFS(mp);
36533548Sjkh#endif
36633548Sjkh	} else {
3672893Sdfr		if (devvp != pmp->pm_devvp)
36833548Sjkh			error = EINVAL;	/* XXX needs translation */
3692893Sdfr		else
370149720Sssouhlal			vput(devvp);
3712893Sdfr	}
3722893Sdfr	if (error) {
3732893Sdfr		vrele(devvp);
37433548Sjkh		return (error);
37533548Sjkh	}
37633548Sjkh
377138471Sphk	error = update_mp(mp, td);
37833548Sjkh	if (error) {
379134345Stjr		if ((mp->mnt_flag & MNT_UPDATE) == 0)
380134345Stjr			msdosfs_unmount(mp, MNT_FORCE, td);
3812893Sdfr		return error;
3822893Sdfr	}
383138471Sphk
384138471Sphk	vfs_mountedfrom(mp, from);
3852893Sdfr#ifdef MSDOSFS_DEBUG
386138471Sphk	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
3872893Sdfr#endif
38833548Sjkh	return (0);
3892893Sdfr}
3902893Sdfr
39112144Sphkstatic int
392166558Srodrigcmountmsdosfs(struct vnode *devvp, struct mount *mp, struct thread *td)
3932893Sdfr{
39433548Sjkh	struct msdosfsmount *pmp;
39533548Sjkh	struct buf *bp;
396130585Sphk	struct cdev *dev = devvp->v_rdev;
3972893Sdfr	union bootsector *bsp;
3982893Sdfr	struct byte_bpb33 *b33;
3992893Sdfr	struct byte_bpb50 *b50;
40033548Sjkh	struct byte_bpb710 *b710;
40133548Sjkh	u_int8_t SecPerClust;
40255188Sbp	u_long clusters;
40333548Sjkh	int	ronly, error;
404137036Sphk	struct g_consumer *cp;
405137036Sphk	struct bufobj *bo;
4062893Sdfr
407138471Sphk	ronly = !vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL);
408137036Sphk	/* XXX: use VOP_ACCESS to check FS perms */
409137036Sphk	DROP_GIANT();
410137036Sphk	g_topology_lock();
411137036Sphk	error = g_vfs_open(devvp, &cp, "msdos", ronly ? 0 : 1);
412137036Sphk	g_topology_unlock();
413137036Sphk	PICKUP_GIANT();
41483366Sjulian	VOP_UNLOCK(devvp, 0, td);
4153152Sphk	if (error)
41633548Sjkh		return (error);
41733548Sjkh
418137036Sphk	bo = &devvp->v_bufobj;
41933548Sjkh	bp  = NULL; /* both used in error_exit */
42033548Sjkh	pmp = NULL;
42133548Sjkh
4222893Sdfr	/*
42333548Sjkh	 * Read the boot sector of the filesystem, and then check the
42433548Sjkh	 * boot signature.  If not a dos boot sector then error out.
42556674Snyan	 *
426102391Sbde	 * NOTE: 2048 is a maximum sector size in current...
4272893Sdfr	 */
428102391Sbde	error = bread(devvp, 0, 2048, NOCRED, &bp);
4293152Sphk	if (error)
4302893Sdfr		goto error_exit;
43133548Sjkh	bp->b_flags |= B_AGE;
43233548Sjkh	bsp = (union bootsector *)bp->b_data;
43333548Sjkh	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
43433548Sjkh	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
435105655Sjhb	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
43633548Sjkh
43756674Snyan#ifndef MSDOSFS_NOCHECKSIG
43887068Sjhb	if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
43987068Sjhb	    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
44087068Sjhb		error = EINVAL;
44187068Sjhb		goto error_exit;
44287068Sjhb	}
44356674Snyan#endif
4442893Sdfr
445111119Simp	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO);
4462893Sdfr	pmp->pm_mountp = mp;
447137036Sphk	pmp->pm_cp = cp;
448137036Sphk	pmp->pm_bo = bo;
4492893Sdfr
4502893Sdfr	/*
451166340Srodrigc	 * Experimental support for large MS-DOS filesystems.
452166340Srodrigc	 * WARNING: This uses at least 32 bytes of kernel memory (which is not
453166340Srodrigc	 * reclaimed until the FS is unmounted) for each file on disk to map
454166340Srodrigc	 * between the 32-bit inode numbers used by VFS and the 64-bit
455166340Srodrigc	 * pseudo-inode numbers used internally by msdosfs. This is only
456166340Srodrigc	 * safe to use in certain controlled situations (e.g. read-only FS
457166340Srodrigc	 * with less than 1 million files).
458166340Srodrigc	 * Since the mappings do not persist across unmounts (or reboots), these
459166340Srodrigc	 * filesystems are not suitable for exporting through NFS, or any other
460166340Srodrigc	 * application that requires fixed inode numbers.
461166340Srodrigc	 */
462166340Srodrigc	vfs_flagopt(mp->mnt_optnew, "large", &pmp->pm_flags,
463166340Srodrigc	  MSDOSFS_LARGEFS);
464166340Srodrigc
465166340Srodrigc	/*
4662893Sdfr	 * Compute several useful quantities from the bpb in the
4672893Sdfr	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
4682893Sdfr	 * the fields that are different between dos 5 and dos 3.3.
4692893Sdfr	 */
47033548Sjkh	SecPerClust = b50->bpbSecPerClust;
4712893Sdfr	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
472113979Sjhb	if (pmp->pm_BytesPerSec < DEV_BSIZE) {
473113979Sjhb		error = EINVAL;
474113979Sjhb		goto error_exit;
475113979Sjhb	}
4762893Sdfr	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
4772893Sdfr	pmp->pm_FATs = b50->bpbFATs;
4782893Sdfr	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
4792893Sdfr	pmp->pm_Sectors = getushort(b50->bpbSectors);
4802893Sdfr	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
4812893Sdfr	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
4822893Sdfr	pmp->pm_Heads = getushort(b50->bpbHeads);
48333548Sjkh	pmp->pm_Media = b50->bpbMedia;
4842893Sdfr
48556674Snyan	/* calculate the ratio of sector size to DEV_BSIZE */
48656674Snyan	pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE;
48756674Snyan
48887068Sjhb	/* XXX - We should probably check more values here */
48987068Sjhb	if (!pmp->pm_BytesPerSec || !SecPerClust
490126998Srwatson		|| !pmp->pm_Heads
49116363Sasami#ifdef PC98
49287068Sjhb    		|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
49316363Sasami#else
49487068Sjhb		|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
49516363Sasami#endif
49687068Sjhb		error = EINVAL;
49787068Sjhb		goto error_exit;
49887068Sjhb	}
4992893Sdfr
5002893Sdfr	if (pmp->pm_Sectors == 0) {
5012893Sdfr		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
5022893Sdfr		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
5032893Sdfr	} else {
5042893Sdfr		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
5052893Sdfr		pmp->pm_HugeSectors = pmp->pm_Sectors;
5062893Sdfr	}
507166340Srodrigc	if (!(pmp->pm_flags & MSDOSFS_LARGEFS)) {
508166340Srodrigc		if (pmp->pm_HugeSectors > 0xffffffff /
509166340Srodrigc		    (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) {
510166340Srodrigc			/*
511166340Srodrigc			 * We cannot deal currently with this size of disk
512166340Srodrigc			 * due to fileid limitations (see msdosfs_getattr and
513166340Srodrigc			 * msdosfs_readdir)
514166340Srodrigc			 */
515166340Srodrigc			error = EINVAL;
516166340Srodrigc			vfs_mount_error(mp,
517166340Srodrigc			    "Disk too big, try '-o large' mount option");
518166340Srodrigc			goto error_exit;
519166340Srodrigc		}
52033548Sjkh	}
52133548Sjkh
52233548Sjkh	if (pmp->pm_RootDirEnts == 0) {
523150711Speadar		if (pmp->pm_Sectors
52433548Sjkh		    || pmp->pm_FATsecs
52533548Sjkh		    || getushort(b710->bpbFSVers)) {
52633548Sjkh			error = EINVAL;
52735046Sache			printf("mountmsdosfs(): bad FAT32 filesystem\n");
52833548Sjkh			goto error_exit;
52933548Sjkh		}
53033548Sjkh		pmp->pm_fatmask = FAT32_MASK;
53133548Sjkh		pmp->pm_fatmult = 4;
53233548Sjkh		pmp->pm_fatdiv = 1;
53333548Sjkh		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
53433548Sjkh		if (getushort(b710->bpbExtFlags) & FATMIRROR)
53533548Sjkh			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
53633548Sjkh		else
53733548Sjkh			pmp->pm_flags |= MSDOSFS_FATMIRROR;
53833548Sjkh	} else
53933548Sjkh		pmp->pm_flags |= MSDOSFS_FATMIRROR;
54033548Sjkh
54156674Snyan	/*
54256674Snyan	 * Check a few values (could do some more):
54356674Snyan	 * - logical sector size: power of 2, >= block size
54456674Snyan	 * - sectors per cluster: power of 2, >= 1
54556674Snyan	 * - number of sectors:   >= 1, <= size of partition
546113979Sjhb	 * - number of FAT sectors: >= 1
54756674Snyan	 */
54856674Snyan	if ( (SecPerClust == 0)
54956674Snyan	  || (SecPerClust & (SecPerClust - 1))
55056674Snyan	  || (pmp->pm_BytesPerSec < DEV_BSIZE)
55156674Snyan	  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
55256674Snyan	  || (pmp->pm_HugeSectors == 0)
553113979Sjhb	  || (pmp->pm_FATsecs == 0)
55456674Snyan	) {
55556674Snyan		error = EINVAL;
55656674Snyan		goto error_exit;
55756674Snyan	}
55833548Sjkh
55956674Snyan	pmp->pm_HugeSectors *= pmp->pm_BlkPerSec;
56056674Snyan	pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */
56156674Snyan	pmp->pm_FATsecs     *= pmp->pm_BlkPerSec;
56256674Snyan	SecPerClust         *= pmp->pm_BlkPerSec;
56356674Snyan
56456674Snyan	pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec;
56556674Snyan
56633548Sjkh	if (FAT32(pmp)) {
56733548Sjkh		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
56833548Sjkh		pmp->pm_firstcluster = pmp->pm_fatblk
56933548Sjkh			+ (pmp->pm_FATs * pmp->pm_FATsecs);
57056674Snyan		pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec;
57133548Sjkh	} else {
57233548Sjkh		pmp->pm_rootdirblk = pmp->pm_fatblk +
57333548Sjkh			(pmp->pm_FATs * pmp->pm_FATsecs);
57433548Sjkh		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
57556674Snyan				       + DEV_BSIZE - 1)
57656674Snyan			/ DEV_BSIZE; /* in blocks */
57733548Sjkh		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
57833548Sjkh	}
57933548Sjkh
58055188Sbp	pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
58155188Sbp	    SecPerClust + 1;
58256674Snyan	pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */
58333548Sjkh
58433548Sjkh	if (pmp->pm_fatmask == 0) {
58533548Sjkh		if (pmp->pm_maxcluster
58633548Sjkh		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
58733548Sjkh			/*
58833548Sjkh			 * This will usually be a floppy disk. This size makes
58933548Sjkh			 * sure that one fat entry will not be split across
59033548Sjkh			 * multiple blocks.
59133548Sjkh			 */
59233548Sjkh			pmp->pm_fatmask = FAT12_MASK;
59333548Sjkh			pmp->pm_fatmult = 3;
59433548Sjkh			pmp->pm_fatdiv = 2;
59533548Sjkh		} else {
59633548Sjkh			pmp->pm_fatmask = FAT16_MASK;
59733548Sjkh			pmp->pm_fatmult = 2;
59833548Sjkh			pmp->pm_fatdiv = 1;
59933548Sjkh		}
60033548Sjkh	}
60155188Sbp
60255188Sbp	clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv;
60355188Sbp	if (pmp->pm_maxcluster >= clusters) {
60455188Sbp		printf("Warning: number of clusters (%ld) exceeds FAT "
60555188Sbp		    "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters);
60655188Sbp		pmp->pm_maxcluster = clusters - 1;
60755188Sbp	}
60855188Sbp
60955188Sbp
6102893Sdfr	if (FAT12(pmp))
6112893Sdfr		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
6122893Sdfr	else
613102391Sbde		pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE;
61433548Sjkh
61556674Snyan	pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE;
61656674Snyan	pmp->pm_bnshift = ffs(DEV_BSIZE) - 1;
6172893Sdfr
6182893Sdfr	/*
6192893Sdfr	 * Compute mask and shift value for isolating cluster relative byte
6202893Sdfr	 * offsets and cluster numbers from a file offset.
6212893Sdfr	 */
62256674Snyan	pmp->pm_bpcluster = SecPerClust * DEV_BSIZE;
62333548Sjkh	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
62433548Sjkh	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
62533548Sjkh
62633548Sjkh	/*
62733548Sjkh	 * Check for valid cluster size
62833548Sjkh	 * must be a power of 2
62933548Sjkh	 */
63033548Sjkh	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
6312893Sdfr		error = EINVAL;
6322893Sdfr		goto error_exit;
6332893Sdfr	}
6342893Sdfr
63533548Sjkh	/*
63633548Sjkh	 * Release the bootsector buffer.
63733548Sjkh	 */
63833548Sjkh	brelse(bp);
63933548Sjkh	bp = NULL;
64033548Sjkh
64133548Sjkh	/*
64233548Sjkh	 * Check FSInfo.
64333548Sjkh	 */
64433548Sjkh	if (pmp->pm_fsinfo) {
64533548Sjkh		struct fsinfo *fp;
64633548Sjkh
64756674Snyan		if ((error = bread(devvp, pmp->pm_fsinfo, fsi_size(pmp),
64856674Snyan		    NOCRED, &bp)) != 0)
64933548Sjkh			goto error_exit;
65033548Sjkh		fp = (struct fsinfo *)bp->b_data;
65133548Sjkh		if (!bcmp(fp->fsisig1, "RRaA", 4)
65233548Sjkh		    && !bcmp(fp->fsisig2, "rrAa", 4)
65333548Sjkh		    && !bcmp(fp->fsisig3, "\0\0\125\252", 4)
654125934Stjr		    && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) {
65533548Sjkh			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
656125934Stjr			if (pmp->pm_nxtfree == 0xffffffff)
657125934Stjr				pmp->pm_nxtfree = CLUST_FIRST;
658125934Stjr		} else
65933548Sjkh			pmp->pm_fsinfo = 0;
66033548Sjkh		brelse(bp);
66133548Sjkh		bp = NULL;
66216363Sasami	}
6632893Sdfr
6642893Sdfr	/*
665102295Strhodes	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
6662893Sdfr	 */
667102295Strhodes	if (pmp->pm_fsinfo && pmp->pm_nxtfree > pmp->pm_maxcluster) {
668102392Sbde		printf(
669102392Sbde		"Next free cluster in FSInfo (%lu) exceeds maxcluster (%lu)\n",
670102392Sbde		    pmp->pm_nxtfree, pmp->pm_maxcluster);
671102295Strhodes		error = EINVAL;
672102295Strhodes		goto error_exit;
673102295Strhodes	}
6742893Sdfr
6752893Sdfr	/*
6762893Sdfr	 * Allocate memory for the bitmap of allocated clusters, and then
6772893Sdfr	 * fill it in.
6782893Sdfr	 */
679126086Sbde	pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1, N_INUSEBITS)
6802893Sdfr				  * sizeof(*pmp->pm_inusemap),
681111119Simp				  M_MSDOSFSFAT, M_WAITOK);
6822893Sdfr
6832893Sdfr	/*
6842893Sdfr	 * fillinusemap() needs pm_devvp.
6852893Sdfr	 */
6862893Sdfr	pmp->pm_devvp = devvp;
6872893Sdfr
6882893Sdfr	/*
6892893Sdfr	 * Have the inuse map filled in.
6902893Sdfr	 */
69133548Sjkh	if ((error = fillinusemap(pmp)) != 0)
6922893Sdfr		goto error_exit;
6932893Sdfr
6942893Sdfr	/*
6952893Sdfr	 * If they want fat updates to be synchronous then let them suffer
6962893Sdfr	 * the performance degradation in exchange for the on disk copy of
6972893Sdfr	 * the fat being correct just about all the time.  I suppose this
6982893Sdfr	 * would be a good thing to turn on if the kernel is still flakey.
6992893Sdfr	 */
70033548Sjkh	if (mp->mnt_flag & MNT_SYNCHRONOUS)
70133548Sjkh		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
7022893Sdfr
7032893Sdfr	/*
7042893Sdfr	 * Finish up.
7052893Sdfr	 */
70633548Sjkh	if (ronly)
70733548Sjkh		pmp->pm_flags |= MSDOSFSMNT_RONLY;
708123873Strhodes	else {
709123963Sbde		/* Mark the volume dirty while it is mounted read/write. */
710123963Sbde		if ((error = markvoldirty(pmp, 1)) != 0)
711123963Sbde			goto error_exit;
7122893Sdfr		pmp->pm_fmod = 1;
713123873Strhodes	}
7142893Sdfr	mp->mnt_data = (qaddr_t) pmp;
71550256Sbde	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
71623134Sbde	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
717162647Stegge	MNT_ILOCK(mp);
71823997Speter	mp->mnt_flag |= MNT_LOCAL;
719162647Stegge	MNT_IUNLOCK(mp);
7202893Sdfr
721166340Srodrigc	if (pmp->pm_flags & MSDOSFS_LARGEFS)
722166340Srodrigc		msdosfs_fileno_init(mp);
723131523Stjr
7242893Sdfr	return 0;
7252893Sdfr
72633548Sjkherror_exit:
72733548Sjkh	if (bp)
72833548Sjkh		brelse(bp);
729137036Sphk	if (cp != NULL) {
730137036Sphk		DROP_GIANT();
731137036Sphk		g_topology_lock();
732140822Sphk		g_vfs_close(cp, td);
733137036Sphk		g_topology_unlock();
734137036Sphk		PICKUP_GIANT();
735137036Sphk	}
7362893Sdfr	if (pmp) {
7372893Sdfr		if (pmp->pm_inusemap)
73833548Sjkh			free(pmp->pm_inusemap, M_MSDOSFSFAT);
73933548Sjkh		free(pmp, M_MSDOSFSMNT);
74033548Sjkh		mp->mnt_data = (qaddr_t)0;
7412893Sdfr	}
74233548Sjkh	return (error);
7432893Sdfr}
7442893Sdfr
7452893Sdfr/*
7462893Sdfr * Unmount the filesystem described by mp.
7472893Sdfr */
74812144Sphkstatic int
749166558Srodrigcmsdosfs_unmount(struct mount *mp, int mntflags, struct thread *td)
7502893Sdfr{
75133548Sjkh	struct msdosfsmount *pmp;
75233548Sjkh	int error, flags;
7532893Sdfr
75433548Sjkh	flags = 0;
75533548Sjkh	if (mntflags & MNT_FORCE)
7562893Sdfr		flags |= FORCECLOSE;
757132023Salfred	error = vflush(mp, 0, flags, td);
7583152Sphk	if (error)
7592893Sdfr		return error;
76033548Sjkh	pmp = VFSTOMSDOSFS(mp);
761120492Sfjoe	if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) {
762120492Sfjoe		if (pmp->pm_w2u)
763120492Sfjoe			msdosfs_iconv->close(pmp->pm_w2u);
764120492Sfjoe		if (pmp->pm_u2w)
765120492Sfjoe			msdosfs_iconv->close(pmp->pm_u2w);
766120492Sfjoe		if (pmp->pm_d2u)
767120492Sfjoe			msdosfs_iconv->close(pmp->pm_d2u);
768120492Sfjoe		if (pmp->pm_u2d)
769120492Sfjoe			msdosfs_iconv->close(pmp->pm_u2d);
770120492Sfjoe	}
771123873Strhodes
772123963Sbde	/* If the volume was mounted read/write, mark it clean now. */
773123963Sbde	if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) {
774123963Sbde		error = markvoldirty(pmp, 0);
775123963Sbde		if (error && (flags & FORCECLOSE) == 0)
776123963Sbde			return (error);
777123963Sbde	}
77833548Sjkh#ifdef MSDOSFS_DEBUG
77933548Sjkh	{
78033548Sjkh		struct vnode *vp = pmp->pm_devvp;
78133548Sjkh
782103936Sjeff		VI_LOCK(vp);
783142235Sphk		vn_printf(vp,
784142235Sphk		    "msdosfs_umount(): just before calling VOP_CLOSE()\n");
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);
792103936Sjeff		VI_UNLOCK(vp);
79333548Sjkh	}
79433548Sjkh#endif
795137036Sphk	DROP_GIANT();
796137036Sphk	g_topology_lock();
797140822Sphk	g_vfs_close(pmp->pm_cp, td);
798137036Sphk	g_topology_unlock();
799137036Sphk	PICKUP_GIANT();
8002893Sdfr	vrele(pmp->pm_devvp);
80133548Sjkh	free(pmp->pm_inusemap, M_MSDOSFSFAT);
802166340Srodrigc	if (pmp->pm_flags & MSDOSFS_LARGEFS) {
803166340Srodrigc		msdosfs_fileno_free(mp);
804166340Srodrigc	}
80533548Sjkh	free(pmp, M_MSDOSFSMNT);
80633548Sjkh	mp->mnt_data = (qaddr_t)0;
807162647Stegge	MNT_ILOCK(mp);
80823997Speter	mp->mnt_flag &= ~MNT_LOCAL;
809162647Stegge	MNT_IUNLOCK(mp);
81033548Sjkh	return (error);
8112893Sdfr}
8122893Sdfr
81312144Sphkstatic int
814166558Srodrigcmsdosfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
8152893Sdfr{
81633548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
8172893Sdfr	struct denode *ndep;
8182893Sdfr	int error;
8192893Sdfr
8202893Sdfr#ifdef MSDOSFS_DEBUG
82133548Sjkh	printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
8222893Sdfr#endif
82333548Sjkh	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep);
82433548Sjkh	if (error)
82533548Sjkh		return (error);
82633548Sjkh	*vpp = DETOV(ndep);
82733548Sjkh	return (0);
8282893Sdfr}
8292893Sdfr
83012144Sphkstatic int
831166558Srodrigcmsdosfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
8322893Sdfr{
83333548Sjkh	struct msdosfsmount *pmp;
8342893Sdfr
83533548Sjkh	pmp = VFSTOMSDOSFS(mp);
8362893Sdfr	sbp->f_bsize = pmp->pm_bpcluster;
8372893Sdfr	sbp->f_iosize = pmp->pm_bpcluster;
83855188Sbp	sbp->f_blocks = pmp->pm_maxcluster + 1;
8392893Sdfr	sbp->f_bfree = pmp->pm_freeclustercount;
8402893Sdfr	sbp->f_bavail = pmp->pm_freeclustercount;
8412893Sdfr	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
8422893Sdfr	sbp->f_ffree = 0;	/* what to put in here? */
84333548Sjkh	return (0);
8442893Sdfr}
8452893Sdfr
84612144Sphkstatic int
847166558Srodrigcmsdosfs_sync(struct mount *mp, int waitfor, struct thread *td)
8482893Sdfr{
84933548Sjkh	struct vnode *vp, *nvp;
8502893Sdfr	struct denode *dep;
85133548Sjkh	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
85233548Sjkh	int error, allerror = 0;
8532893Sdfr
8542893Sdfr	/*
8552893Sdfr	 * If we ever switch to not updating all of the fats all the time,
8562893Sdfr	 * this would be the place to update them from the first one.
8572893Sdfr	 */
85846568Speter	if (pmp->pm_fmod != 0) {
85933548Sjkh		if (pmp->pm_flags & MSDOSFSMNT_RONLY)
8602893Sdfr			panic("msdosfs_sync: rofs mod");
8612893Sdfr		else {
8622893Sdfr			/* update fats here */
8632893Sdfr		}
86446568Speter	}
8652893Sdfr	/*
86633548Sjkh	 * Write back each (modified) denode.
8672893Sdfr	 */
868122091Skan	MNT_ILOCK(mp);
8692893Sdfrloop:
870131551Sphk	MNT_VNODE_FOREACH(vp, mp, nvp) {
871120733Sjeff		VI_LOCK(vp);
872143513Sjeff		if (vp->v_type == VNON || (vp->v_iflag & VI_DOOMED)) {
873120785Sjeff			VI_UNLOCK(vp);
874120785Sjeff			continue;
875120785Sjeff		}
876122091Skan		MNT_IUNLOCK(mp);
8772893Sdfr		dep = VTODE(vp);
878137008Sphk		if ((dep->de_flag &
87935511Sdt		    (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 &&
880136943Sphk		    (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
881137008Sphk		    waitfor == MNT_LAZY)) {
882103936Sjeff			VI_UNLOCK(vp);
883122091Skan			MNT_ILOCK(mp);
8842893Sdfr			continue;
88523134Sbde		}
88683366Sjulian		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td);
88723134Sbde		if (error) {
888122091Skan			MNT_ILOCK(mp);
88923134Sbde			if (error == ENOENT)
89023134Sbde				goto loop;
89123134Sbde			continue;
89223134Sbde		}
893140048Sphk		error = VOP_FSYNC(vp, waitfor, td);
8943152Sphk		if (error)
8952893Sdfr			allerror = error;
896121874Skan		VOP_UNLOCK(vp, 0, td);
897121874Skan		vrele(vp);
898122091Skan		MNT_ILOCK(mp);
8992893Sdfr	}
900122091Skan	MNT_IUNLOCK(mp);
9012893Sdfr
9022893Sdfr	/*
9032893Sdfr	 * Flush filesystem control info.
9042893Sdfr	 */
90535511Sdt	if (waitfor != MNT_LAZY) {
90683366Sjulian		vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, td);
907140048Sphk		error = VOP_FSYNC(pmp->pm_devvp, waitfor, td);
90835511Sdt		if (error)
90935511Sdt			allerror = error;
91083366Sjulian		VOP_UNLOCK(pmp->pm_devvp, 0, td);
91135511Sdt	}
91233548Sjkh	return (allerror);
9132893Sdfr}
9142893Sdfr
915170188Strhodesstatic int
916170188Strhodesmsdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
917170188Strhodes{
918170188Strhodes	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
919170188Strhodes	struct defid *defhp = (struct defid *) fhp;
920170188Strhodes	struct denode *dep;
921170188Strhodes	int error;
922170188Strhodes
923170188Strhodes	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep);
924170188Strhodes	if (error) {
925170188Strhodes		*vpp = NULLVP;
926170188Strhodes		return (error);
927170188Strhodes	}
928170188Strhodes	*vpp = DETOV(dep);
929170188Strhodes	vnode_create_vobject(*vpp, dep->de_FileSize, curthread);
930170188Strhodes	return (0);
931170188Strhodes}
932170188Strhodes
93312145Sphkstatic struct vfsops msdosfs_vfsops = {
934170188Strhodes	.vfs_fhtovp =		msdosfs_fhtovp,
935138471Sphk	.vfs_mount =		msdosfs_mount,
936138471Sphk	.vfs_cmount =		msdosfs_cmount,
937116271Sphk	.vfs_root =		msdosfs_root,
938116271Sphk	.vfs_statfs =		msdosfs_statfs,
939116271Sphk	.vfs_sync =		msdosfs_sync,
940116271Sphk	.vfs_unmount =		msdosfs_unmount,
9412893Sdfr};
9422946Swollman
94377577SruVFS_SET(msdosfs_vfsops, msdosfs, 0);
944120492SfjoeMODULE_VERSION(msdosfs, 1);
945