xref: /minix3/sys/fs/msdosfs/msdosfs_vfsops.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $	*/
284d9c625SLionel Sambuc 
384d9c625SLionel Sambuc /*-
484d9c625SLionel Sambuc  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
584d9c625SLionel Sambuc  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
684d9c625SLionel Sambuc  * All rights reserved.
784d9c625SLionel Sambuc  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
884d9c625SLionel Sambuc  *
984d9c625SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
1084d9c625SLionel Sambuc  * modification, are permitted provided that the following conditions
1184d9c625SLionel Sambuc  * are met:
1284d9c625SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
1384d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
1484d9c625SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
1584d9c625SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
1684d9c625SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
1784d9c625SLionel Sambuc  * 3. All advertising materials mentioning features or use of this software
1884d9c625SLionel Sambuc  *    must display the following acknowledgement:
1984d9c625SLionel Sambuc  *	This product includes software developed by TooLs GmbH.
2084d9c625SLionel Sambuc  * 4. The name of TooLs GmbH may not be used to endorse or promote products
2184d9c625SLionel Sambuc  *    derived from this software without specific prior written permission.
2284d9c625SLionel Sambuc  *
2384d9c625SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2484d9c625SLionel Sambuc  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2584d9c625SLionel Sambuc  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2684d9c625SLionel Sambuc  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2784d9c625SLionel Sambuc  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2884d9c625SLionel Sambuc  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2984d9c625SLionel Sambuc  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3084d9c625SLionel Sambuc  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3184d9c625SLionel Sambuc  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3284d9c625SLionel Sambuc  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3384d9c625SLionel Sambuc  */
3484d9c625SLionel Sambuc /*
3584d9c625SLionel Sambuc  * Written by Paul Popelka (paulp@uts.amdahl.com)
3684d9c625SLionel Sambuc  *
3784d9c625SLionel Sambuc  * You can do anything you want with this software, just don't say you wrote
3884d9c625SLionel Sambuc  * it, and don't remove this notice.
3984d9c625SLionel Sambuc  *
4084d9c625SLionel Sambuc  * This software is provided "as is".
4184d9c625SLionel Sambuc  *
4284d9c625SLionel Sambuc  * The author supplies this software to be publicly redistributed on the
4384d9c625SLionel Sambuc  * understanding that the author is not responsible for the correct
4484d9c625SLionel Sambuc  * functioning of this software in any circumstances and is not liable for
4584d9c625SLionel Sambuc  * any damages caused by this software.
4684d9c625SLionel Sambuc  *
4784d9c625SLionel Sambuc  * October 1992
4884d9c625SLionel Sambuc  */
4984d9c625SLionel Sambuc 
5084d9c625SLionel Sambuc #include <sys/cdefs.h>
51*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $");
5284d9c625SLionel Sambuc 
5384d9c625SLionel Sambuc #if defined(_KERNEL_OPT)
5484d9c625SLionel Sambuc #include "opt_compat_netbsd.h"
5584d9c625SLionel Sambuc #endif
5684d9c625SLionel Sambuc 
5784d9c625SLionel Sambuc #include <sys/param.h>
5884d9c625SLionel Sambuc #include <sys/systm.h>
5984d9c625SLionel Sambuc #include <sys/sysctl.h>
6084d9c625SLionel Sambuc #include <sys/namei.h>
6184d9c625SLionel Sambuc #include <sys/proc.h>
6284d9c625SLionel Sambuc #include <sys/kernel.h>
6384d9c625SLionel Sambuc #include <sys/vnode.h>
6484d9c625SLionel Sambuc #include <miscfs/genfs/genfs.h>
6584d9c625SLionel Sambuc #include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
6684d9c625SLionel Sambuc #include <sys/mount.h>
6784d9c625SLionel Sambuc #include <sys/buf.h>
6884d9c625SLionel Sambuc #include <sys/file.h>
6984d9c625SLionel Sambuc #include <sys/device.h>
7084d9c625SLionel Sambuc #include <sys/disklabel.h>
7184d9c625SLionel Sambuc #include <sys/disk.h>
7284d9c625SLionel Sambuc #include <sys/fstrans.h>
7384d9c625SLionel Sambuc #include <sys/ioctl.h>
7484d9c625SLionel Sambuc #include <sys/malloc.h>
7584d9c625SLionel Sambuc #include <sys/dirent.h>
7684d9c625SLionel Sambuc #include <sys/stat.h>
7784d9c625SLionel Sambuc #include <sys/conf.h>
7884d9c625SLionel Sambuc #include <sys/kauth.h>
7984d9c625SLionel Sambuc #include <sys/module.h>
8084d9c625SLionel Sambuc 
8184d9c625SLionel Sambuc #include <fs/msdosfs/bpb.h>
8284d9c625SLionel Sambuc #include <fs/msdosfs/bootsect.h>
8384d9c625SLionel Sambuc #include <fs/msdosfs/direntry.h>
8484d9c625SLionel Sambuc #include <fs/msdosfs/denode.h>
8584d9c625SLionel Sambuc #include <fs/msdosfs/msdosfsmount.h>
8684d9c625SLionel Sambuc #include <fs/msdosfs/fat.h>
8784d9c625SLionel Sambuc 
8884d9c625SLionel Sambuc MODULE(MODULE_CLASS_VFS, msdos, NULL);
8984d9c625SLionel Sambuc 
9084d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
91*0a6a1f1dSLionel Sambuc #define DPRINTF(fmt, ...) uprintf("%s(): " fmt "\n", __func__, ##__VA_ARGS__)
9284d9c625SLionel Sambuc #else
93*0a6a1f1dSLionel Sambuc #define DPRINTF(fmt, ...)
9484d9c625SLionel Sambuc #endif
9584d9c625SLionel Sambuc 
96*0a6a1f1dSLionel Sambuc #define GEMDOSFS_BSIZE	512
97*0a6a1f1dSLionel Sambuc 
9884d9c625SLionel Sambuc #define MSDOSFS_NAMEMAX(pmp) \
9984d9c625SLionel Sambuc 	(pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12
10084d9c625SLionel Sambuc 
10184d9c625SLionel Sambuc int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *,
10284d9c625SLionel Sambuc     struct msdosfs_args *);
10384d9c625SLionel Sambuc 
10484d9c625SLionel Sambuc static int update_mp(struct mount *, struct msdosfs_args *);
10584d9c625SLionel Sambuc 
10684d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure");
10784d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOS FS FAT table");
10884d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSTMP, "MSDOSFS temp", "MSDOS FS temp. structures");
10984d9c625SLionel Sambuc 
11084d9c625SLionel Sambuc static struct sysctllog *msdosfs_sysctl_log;
11184d9c625SLionel Sambuc 
11284d9c625SLionel Sambuc extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
11384d9c625SLionel Sambuc 
11484d9c625SLionel Sambuc const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = {
11584d9c625SLionel Sambuc 	&msdosfs_vnodeop_opv_desc,
11684d9c625SLionel Sambuc 	NULL,
11784d9c625SLionel Sambuc };
11884d9c625SLionel Sambuc 
11984d9c625SLionel Sambuc struct vfsops msdosfs_vfsops = {
120*0a6a1f1dSLionel Sambuc 	.vfs_name = MOUNT_MSDOS,
121*0a6a1f1dSLionel Sambuc 	.vfs_min_mount_data = sizeof (struct msdosfs_args),
122*0a6a1f1dSLionel Sambuc 	.vfs_mount = msdosfs_mount,
123*0a6a1f1dSLionel Sambuc 	.vfs_start = msdosfs_start,
124*0a6a1f1dSLionel Sambuc 	.vfs_unmount = msdosfs_unmount,
125*0a6a1f1dSLionel Sambuc 	.vfs_root = msdosfs_root,
126*0a6a1f1dSLionel Sambuc 	.vfs_quotactl = (void *)eopnotsupp,
127*0a6a1f1dSLionel Sambuc 	.vfs_statvfs = msdosfs_statvfs,
128*0a6a1f1dSLionel Sambuc 	.vfs_sync = msdosfs_sync,
129*0a6a1f1dSLionel Sambuc 	.vfs_vget = msdosfs_vget,
130*0a6a1f1dSLionel Sambuc 	.vfs_loadvnode = msdosfs_loadvnode,
131*0a6a1f1dSLionel Sambuc 	.vfs_fhtovp = msdosfs_fhtovp,
132*0a6a1f1dSLionel Sambuc 	.vfs_vptofh = msdosfs_vptofh,
133*0a6a1f1dSLionel Sambuc 	.vfs_init = msdosfs_init,
134*0a6a1f1dSLionel Sambuc 	.vfs_reinit = msdosfs_reinit,
135*0a6a1f1dSLionel Sambuc 	.vfs_done = msdosfs_done,
136*0a6a1f1dSLionel Sambuc 	.vfs_mountroot = msdosfs_mountroot,
137*0a6a1f1dSLionel Sambuc 	.vfs_snapshot = (void *)eopnotsupp,
138*0a6a1f1dSLionel Sambuc 	.vfs_extattrctl = vfs_stdextattrctl,
139*0a6a1f1dSLionel Sambuc 	.vfs_suspendctl = msdosfs_suspendctl,
140*0a6a1f1dSLionel Sambuc 	.vfs_renamelock_enter = genfs_renamelock_enter,
141*0a6a1f1dSLionel Sambuc 	.vfs_renamelock_exit = genfs_renamelock_exit,
142*0a6a1f1dSLionel Sambuc 	.vfs_fsync = (void *)eopnotsupp,
143*0a6a1f1dSLionel Sambuc 	.vfs_opv_descs = msdosfs_vnodeopv_descs
14484d9c625SLionel Sambuc };
14584d9c625SLionel Sambuc 
14684d9c625SLionel Sambuc static int
msdos_modcmd(modcmd_t cmd,void * arg)14784d9c625SLionel Sambuc msdos_modcmd(modcmd_t cmd, void *arg)
14884d9c625SLionel Sambuc {
14984d9c625SLionel Sambuc 	int error;
15084d9c625SLionel Sambuc 
15184d9c625SLionel Sambuc 	switch (cmd) {
15284d9c625SLionel Sambuc 	case MODULE_CMD_INIT:
15384d9c625SLionel Sambuc 		error = vfs_attach(&msdosfs_vfsops);
15484d9c625SLionel Sambuc 		if (error != 0)
15584d9c625SLionel Sambuc 			break;
15684d9c625SLionel Sambuc 		sysctl_createv(&msdosfs_sysctl_log, 0, NULL, NULL,
15784d9c625SLionel Sambuc 			       CTLFLAG_PERMANENT,
15884d9c625SLionel Sambuc 			       CTLTYPE_NODE, "msdosfs",
15984d9c625SLionel Sambuc 			       SYSCTL_DESCR("MS-DOS file system"),
16084d9c625SLionel Sambuc 			       NULL, 0, NULL, 0,
16184d9c625SLionel Sambuc 			       CTL_VFS, 4, CTL_EOL);
16284d9c625SLionel Sambuc 		/*
16384d9c625SLionel Sambuc 		 * XXX the "4" above could be dynamic, thereby eliminating one
16484d9c625SLionel Sambuc 		 * more instance of the "number to vfs" mapping problem, but
16584d9c625SLionel Sambuc 		 * "4" is the order as taken from sys/mount.h
16684d9c625SLionel Sambuc 		 */
16784d9c625SLionel Sambuc 		break;
16884d9c625SLionel Sambuc 	case MODULE_CMD_FINI:
16984d9c625SLionel Sambuc 		error = vfs_detach(&msdosfs_vfsops);
17084d9c625SLionel Sambuc 		if (error != 0)
17184d9c625SLionel Sambuc 			break;
17284d9c625SLionel Sambuc 		sysctl_teardown(&msdosfs_sysctl_log);
17384d9c625SLionel Sambuc 		break;
17484d9c625SLionel Sambuc 	default:
17584d9c625SLionel Sambuc 		error = ENOTTY;
17684d9c625SLionel Sambuc 		break;
17784d9c625SLionel Sambuc 	}
17884d9c625SLionel Sambuc 
17984d9c625SLionel Sambuc 	return (error);
18084d9c625SLionel Sambuc }
18184d9c625SLionel Sambuc 
18284d9c625SLionel Sambuc static int
update_mp(struct mount * mp,struct msdosfs_args * argp)18384d9c625SLionel Sambuc update_mp(struct mount *mp, struct msdosfs_args *argp)
18484d9c625SLionel Sambuc {
18584d9c625SLionel Sambuc 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
18684d9c625SLionel Sambuc 	int error;
18784d9c625SLionel Sambuc 
18884d9c625SLionel Sambuc 	pmp->pm_gid = argp->gid;
18984d9c625SLionel Sambuc 	pmp->pm_uid = argp->uid;
19084d9c625SLionel Sambuc 	pmp->pm_mask = argp->mask & ALLPERMS;
19184d9c625SLionel Sambuc 	pmp->pm_dirmask = argp->dirmask & ALLPERMS;
19284d9c625SLionel Sambuc 	pmp->pm_gmtoff = argp->gmtoff;
19384d9c625SLionel Sambuc 	pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
19484d9c625SLionel Sambuc 
19584d9c625SLionel Sambuc 	/*
19684d9c625SLionel Sambuc 	 * GEMDOS knows nothing about win95 long filenames
19784d9c625SLionel Sambuc 	 */
19884d9c625SLionel Sambuc 	if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
19984d9c625SLionel Sambuc 		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
20084d9c625SLionel Sambuc 
20184d9c625SLionel Sambuc 	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
20284d9c625SLionel Sambuc 		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
20384d9c625SLionel Sambuc 	else if (!(pmp->pm_flags &
20484d9c625SLionel Sambuc 	    (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
20584d9c625SLionel Sambuc 		struct vnode *rtvp;
20684d9c625SLionel Sambuc 
20784d9c625SLionel Sambuc 		/*
20884d9c625SLionel Sambuc 		 * Try to divine whether to support Win'95 long filenames
20984d9c625SLionel Sambuc 		 */
21084d9c625SLionel Sambuc 		if (FAT32(pmp))
21184d9c625SLionel Sambuc 			pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
21284d9c625SLionel Sambuc 		else {
21384d9c625SLionel Sambuc 			if ((error = msdosfs_root(mp, &rtvp)) != 0)
21484d9c625SLionel Sambuc 				return error;
21584d9c625SLionel Sambuc 			pmp->pm_flags |= findwin95(VTODE(rtvp))
21684d9c625SLionel Sambuc 				? MSDOSFSMNT_LONGNAME
21784d9c625SLionel Sambuc 					: MSDOSFSMNT_SHORTNAME;
21884d9c625SLionel Sambuc 			vput(rtvp);
21984d9c625SLionel Sambuc 		}
22084d9c625SLionel Sambuc 	}
22184d9c625SLionel Sambuc 
22284d9c625SLionel Sambuc 	mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
22384d9c625SLionel Sambuc 
22484d9c625SLionel Sambuc 	return 0;
22584d9c625SLionel Sambuc }
22684d9c625SLionel Sambuc 
22784d9c625SLionel Sambuc int
msdosfs_mountroot(void)22884d9c625SLionel Sambuc msdosfs_mountroot(void)
22984d9c625SLionel Sambuc {
23084d9c625SLionel Sambuc 	struct mount *mp;
23184d9c625SLionel Sambuc 	struct lwp *l = curlwp;	/* XXX */
23284d9c625SLionel Sambuc 	int error;
23384d9c625SLionel Sambuc 	struct msdosfs_args args;
23484d9c625SLionel Sambuc 
23584d9c625SLionel Sambuc 	if (device_class(root_device) != DV_DISK)
23684d9c625SLionel Sambuc 		return (ENODEV);
23784d9c625SLionel Sambuc 
23884d9c625SLionel Sambuc 	if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) {
23984d9c625SLionel Sambuc 		vrele(rootvp);
24084d9c625SLionel Sambuc 		return (error);
24184d9c625SLionel Sambuc 	}
24284d9c625SLionel Sambuc 
24384d9c625SLionel Sambuc 	args.flags = MSDOSFSMNT_VERSIONED;
24484d9c625SLionel Sambuc 	args.uid = 0;
24584d9c625SLionel Sambuc 	args.gid = 0;
24684d9c625SLionel Sambuc 	args.mask = 0777;
24784d9c625SLionel Sambuc 	args.version = MSDOSFSMNT_VERSION;
24884d9c625SLionel Sambuc 	args.dirmask = 0777;
24984d9c625SLionel Sambuc 
25084d9c625SLionel Sambuc 	if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) {
25184d9c625SLionel Sambuc 		vfs_unbusy(mp, false, NULL);
25284d9c625SLionel Sambuc 		vfs_destroy(mp);
25384d9c625SLionel Sambuc 		return (error);
25484d9c625SLionel Sambuc 	}
25584d9c625SLionel Sambuc 
25684d9c625SLionel Sambuc 	if ((error = update_mp(mp, &args)) != 0) {
25784d9c625SLionel Sambuc 		(void)msdosfs_unmount(mp, 0);
25884d9c625SLionel Sambuc 		vfs_unbusy(mp, false, NULL);
25984d9c625SLionel Sambuc 		vfs_destroy(mp);
26084d9c625SLionel Sambuc 		vrele(rootvp);
26184d9c625SLionel Sambuc 		return (error);
26284d9c625SLionel Sambuc 	}
26384d9c625SLionel Sambuc 
26484d9c625SLionel Sambuc 	mountlist_append(mp);
26584d9c625SLionel Sambuc 	(void)msdosfs_statvfs(mp, &mp->mnt_stat);
26684d9c625SLionel Sambuc 	vfs_unbusy(mp, false, NULL);
26784d9c625SLionel Sambuc 	return (0);
26884d9c625SLionel Sambuc }
26984d9c625SLionel Sambuc 
27084d9c625SLionel Sambuc /*
27184d9c625SLionel Sambuc  * mp - path - addr in user space of mount point (ie /usr or whatever)
27284d9c625SLionel Sambuc  * data - addr in user space of mount params including the name of the block
27384d9c625SLionel Sambuc  * special file to treat as a filesystem.
27484d9c625SLionel Sambuc  */
27584d9c625SLionel Sambuc int
msdosfs_mount(struct mount * mp,const char * path,void * data,size_t * data_len)27684d9c625SLionel Sambuc msdosfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
27784d9c625SLionel Sambuc {
27884d9c625SLionel Sambuc 	struct lwp *l = curlwp;
27984d9c625SLionel Sambuc 	struct vnode *devvp;	  /* vnode for blk device to mount */
28084d9c625SLionel Sambuc 	struct msdosfs_args *args = data; /* holds data from mount request */
28184d9c625SLionel Sambuc 	/* msdosfs specific mount control block */
28284d9c625SLionel Sambuc 	struct msdosfsmount *pmp = NULL;
28384d9c625SLionel Sambuc 	int error, flags;
28484d9c625SLionel Sambuc 	mode_t accessmode;
28584d9c625SLionel Sambuc 
286*0a6a1f1dSLionel Sambuc 	if (args == NULL)
287*0a6a1f1dSLionel Sambuc 		return EINVAL;
28884d9c625SLionel Sambuc 	if (*data_len < sizeof *args)
28984d9c625SLionel Sambuc 		return EINVAL;
29084d9c625SLionel Sambuc 
29184d9c625SLionel Sambuc 	if (mp->mnt_flag & MNT_GETARGS) {
29284d9c625SLionel Sambuc 		pmp = VFSTOMSDOSFS(mp);
29384d9c625SLionel Sambuc 		if (pmp == NULL)
29484d9c625SLionel Sambuc 			return EIO;
29584d9c625SLionel Sambuc 		args->fspec = NULL;
29684d9c625SLionel Sambuc 		args->uid = pmp->pm_uid;
29784d9c625SLionel Sambuc 		args->gid = pmp->pm_gid;
29884d9c625SLionel Sambuc 		args->mask = pmp->pm_mask;
29984d9c625SLionel Sambuc 		args->flags = pmp->pm_flags;
30084d9c625SLionel Sambuc 		args->version = MSDOSFSMNT_VERSION;
30184d9c625SLionel Sambuc 		args->dirmask = pmp->pm_dirmask;
30284d9c625SLionel Sambuc 		args->gmtoff = pmp->pm_gmtoff;
30384d9c625SLionel Sambuc 		*data_len = sizeof *args;
30484d9c625SLionel Sambuc 		return 0;
30584d9c625SLionel Sambuc 	}
30684d9c625SLionel Sambuc 
30784d9c625SLionel Sambuc 	/*
30884d9c625SLionel Sambuc 	 * If not versioned (i.e. using old mount_msdos(8)), fill in
30984d9c625SLionel Sambuc 	 * the additional structure items with suitable defaults.
31084d9c625SLionel Sambuc 	 */
31184d9c625SLionel Sambuc 	if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) {
31284d9c625SLionel Sambuc 		args->version = 1;
31384d9c625SLionel Sambuc 		args->dirmask = args->mask;
31484d9c625SLionel Sambuc 	}
31584d9c625SLionel Sambuc 
31684d9c625SLionel Sambuc 	/*
31784d9c625SLionel Sambuc 	 * Reset GMT offset for pre-v3 mount structure args.
31884d9c625SLionel Sambuc 	 */
31984d9c625SLionel Sambuc 	if (args->version < 3)
32084d9c625SLionel Sambuc 		args->gmtoff = 0;
32184d9c625SLionel Sambuc 
32284d9c625SLionel Sambuc 	/*
32384d9c625SLionel Sambuc 	 * If updating, check whether changing from read-only to
32484d9c625SLionel Sambuc 	 * read/write; if there is no device name, that's all we do.
32584d9c625SLionel Sambuc 	 */
32684d9c625SLionel Sambuc 	if (mp->mnt_flag & MNT_UPDATE) {
32784d9c625SLionel Sambuc 		pmp = VFSTOMSDOSFS(mp);
32884d9c625SLionel Sambuc 		error = 0;
32984d9c625SLionel Sambuc 		if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
33084d9c625SLionel Sambuc 		    (mp->mnt_flag & MNT_RDONLY)) {
33184d9c625SLionel Sambuc 			flags = WRITECLOSE;
33284d9c625SLionel Sambuc 			if (mp->mnt_flag & MNT_FORCE)
33384d9c625SLionel Sambuc 				flags |= FORCECLOSE;
33484d9c625SLionel Sambuc 			error = vflush(mp, NULLVP, flags);
33584d9c625SLionel Sambuc 		}
33684d9c625SLionel Sambuc 		if (!error && (mp->mnt_flag & MNT_RELOAD))
33784d9c625SLionel Sambuc 			/* not yet implemented */
33884d9c625SLionel Sambuc 			error = EOPNOTSUPP;
33984d9c625SLionel Sambuc 		if (error) {
340*0a6a1f1dSLionel Sambuc 			DPRINTF("vflush %d", error);
34184d9c625SLionel Sambuc 			return (error);
34284d9c625SLionel Sambuc 		}
34384d9c625SLionel Sambuc 		if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
34484d9c625SLionel Sambuc 		    (mp->mnt_iflag & IMNT_WANTRDWR)) {
34584d9c625SLionel Sambuc 			/*
34684d9c625SLionel Sambuc 			 * If upgrade to read-write by non-root, then verify
34784d9c625SLionel Sambuc 			 * that user has necessary permissions on the device.
34884d9c625SLionel Sambuc 			 *
34984d9c625SLionel Sambuc 			 * Permission to update a mount is checked higher, so
35084d9c625SLionel Sambuc 			 * here we presume updating the mount is okay (for
35184d9c625SLionel Sambuc 			 * example, as far as securelevel goes) which leaves us
35284d9c625SLionel Sambuc 			 * with the normal check.
35384d9c625SLionel Sambuc 			 */
35484d9c625SLionel Sambuc 			devvp = pmp->pm_devvp;
35584d9c625SLionel Sambuc 			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
35684d9c625SLionel Sambuc 			error = kauth_authorize_system(l->l_cred,
35784d9c625SLionel Sambuc 			    KAUTH_SYSTEM_MOUNT, KAUTH_REQ_SYSTEM_MOUNT_DEVICE,
35884d9c625SLionel Sambuc 			    mp, devvp, KAUTH_ARG(VREAD | VWRITE));
35984d9c625SLionel Sambuc 			VOP_UNLOCK(devvp);
360*0a6a1f1dSLionel Sambuc 			DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
36184d9c625SLionel Sambuc 			if (error)
36284d9c625SLionel Sambuc 				return (error);
36384d9c625SLionel Sambuc 
36484d9c625SLionel Sambuc 			pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
36584d9c625SLionel Sambuc 		}
36684d9c625SLionel Sambuc 		if (args->fspec == NULL) {
367*0a6a1f1dSLionel Sambuc 			DPRINTF("missing fspec");
36884d9c625SLionel Sambuc 			return EINVAL;
36984d9c625SLionel Sambuc 		}
37084d9c625SLionel Sambuc 	}
37184d9c625SLionel Sambuc 	/*
37284d9c625SLionel Sambuc 	 * Not an update, or updating the name: look up the name
37384d9c625SLionel Sambuc 	 * and verify that it refers to a sensible block device.
37484d9c625SLionel Sambuc 	 */
37584d9c625SLionel Sambuc 	error = namei_simple_user(args->fspec,
37684d9c625SLionel Sambuc 				NSM_FOLLOW_NOEMULROOT, &devvp);
37784d9c625SLionel Sambuc 	if (error != 0) {
378*0a6a1f1dSLionel Sambuc 		DPRINTF("namei %d", error);
37984d9c625SLionel Sambuc 		return (error);
38084d9c625SLionel Sambuc 	}
38184d9c625SLionel Sambuc 
38284d9c625SLionel Sambuc 	if (devvp->v_type != VBLK) {
383*0a6a1f1dSLionel Sambuc 		DPRINTF("not block");
38484d9c625SLionel Sambuc 		vrele(devvp);
38584d9c625SLionel Sambuc 		return (ENOTBLK);
38684d9c625SLionel Sambuc 	}
38784d9c625SLionel Sambuc 	if (bdevsw_lookup(devvp->v_rdev) == NULL) {
388*0a6a1f1dSLionel Sambuc 		DPRINTF("no block switch");
38984d9c625SLionel Sambuc 		vrele(devvp);
39084d9c625SLionel Sambuc 		return (ENXIO);
39184d9c625SLionel Sambuc 	}
39284d9c625SLionel Sambuc 	/*
39384d9c625SLionel Sambuc 	 * If mount by non-root, then verify that user has necessary
39484d9c625SLionel Sambuc 	 * permissions on the device.
39584d9c625SLionel Sambuc 	 */
39684d9c625SLionel Sambuc 	accessmode = VREAD;
39784d9c625SLionel Sambuc 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
39884d9c625SLionel Sambuc 		accessmode |= VWRITE;
39984d9c625SLionel Sambuc 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
40084d9c625SLionel Sambuc 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
40184d9c625SLionel Sambuc 	    KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
40284d9c625SLionel Sambuc 	VOP_UNLOCK(devvp);
40384d9c625SLionel Sambuc 	if (error) {
404*0a6a1f1dSLionel Sambuc 		DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
40584d9c625SLionel Sambuc 		vrele(devvp);
40684d9c625SLionel Sambuc 		return (error);
40784d9c625SLionel Sambuc 	}
40884d9c625SLionel Sambuc 	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
40984d9c625SLionel Sambuc 		int xflags;
41084d9c625SLionel Sambuc 
41184d9c625SLionel Sambuc 		if (mp->mnt_flag & MNT_RDONLY)
41284d9c625SLionel Sambuc 			xflags = FREAD;
41384d9c625SLionel Sambuc 		else
41484d9c625SLionel Sambuc 			xflags = FREAD|FWRITE;
41584d9c625SLionel Sambuc 		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
41684d9c625SLionel Sambuc 		error = VOP_OPEN(devvp, xflags, FSCRED);
41784d9c625SLionel Sambuc 		VOP_UNLOCK(devvp);
41884d9c625SLionel Sambuc 		if (error) {
419*0a6a1f1dSLionel Sambuc 			DPRINTF("VOP_OPEN %d", error);
42084d9c625SLionel Sambuc 			goto fail;
42184d9c625SLionel Sambuc 		}
42284d9c625SLionel Sambuc 		error = msdosfs_mountfs(devvp, mp, l, args);
42384d9c625SLionel Sambuc 		if (error) {
424*0a6a1f1dSLionel Sambuc 			DPRINTF("msdosfs_mountfs %d", error);
42584d9c625SLionel Sambuc 			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
42684d9c625SLionel Sambuc 			(void) VOP_CLOSE(devvp, xflags, NOCRED);
42784d9c625SLionel Sambuc 			VOP_UNLOCK(devvp);
42884d9c625SLionel Sambuc 			goto fail;
42984d9c625SLionel Sambuc 		}
43084d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG		/* only needed for the printf below */
43184d9c625SLionel Sambuc 		pmp = VFSTOMSDOSFS(mp);
43284d9c625SLionel Sambuc #endif
43384d9c625SLionel Sambuc 	} else {
43484d9c625SLionel Sambuc 		vrele(devvp);
43584d9c625SLionel Sambuc 		if (devvp != pmp->pm_devvp) {
436*0a6a1f1dSLionel Sambuc 			DPRINTF("devvp %p pmp %p", devvp, pmp->pm_devvp);
43784d9c625SLionel Sambuc 			return (EINVAL);	/* needs translation */
43884d9c625SLionel Sambuc 		}
43984d9c625SLionel Sambuc 	}
44084d9c625SLionel Sambuc 	if ((error = update_mp(mp, args)) != 0) {
44184d9c625SLionel Sambuc 		msdosfs_unmount(mp, MNT_FORCE);
442*0a6a1f1dSLionel Sambuc 		DPRINTF("update_mp %d", error);
44384d9c625SLionel Sambuc 		return error;
44484d9c625SLionel Sambuc 	}
44584d9c625SLionel Sambuc 
44684d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
44784d9c625SLionel Sambuc 	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
44884d9c625SLionel Sambuc #endif
44984d9c625SLionel Sambuc 	return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
45084d9c625SLionel Sambuc 	    mp->mnt_op->vfs_name, mp, l);
45184d9c625SLionel Sambuc 
45284d9c625SLionel Sambuc fail:
45384d9c625SLionel Sambuc 	vrele(devvp);
45484d9c625SLionel Sambuc 	return (error);
45584d9c625SLionel Sambuc }
45684d9c625SLionel Sambuc 
45784d9c625SLionel Sambuc int
msdosfs_mountfs(struct vnode * devvp,struct mount * mp,struct lwp * l,struct msdosfs_args * argp)45884d9c625SLionel Sambuc msdosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l, struct msdosfs_args *argp)
45984d9c625SLionel Sambuc {
46084d9c625SLionel Sambuc 	struct msdosfsmount *pmp;
46184d9c625SLionel Sambuc 	struct buf *bp;
46284d9c625SLionel Sambuc 	dev_t dev = devvp->v_rdev;
46384d9c625SLionel Sambuc 	union bootsector *bsp;
46484d9c625SLionel Sambuc 	struct byte_bpb33 *b33;
46584d9c625SLionel Sambuc 	struct byte_bpb50 *b50;
46684d9c625SLionel Sambuc 	struct byte_bpb710 *b710;
46784d9c625SLionel Sambuc 	uint8_t SecPerClust;
468*0a6a1f1dSLionel Sambuc 	int	ronly, error, BlkPerSec;
46984d9c625SLionel Sambuc 	uint64_t psize;
47084d9c625SLionel Sambuc 	unsigned secsize;
47184d9c625SLionel Sambuc 
47284d9c625SLionel Sambuc 	/* Flush out any old buffers remaining from a previous use. */
47384d9c625SLionel Sambuc 	if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0)
47484d9c625SLionel Sambuc 		return (error);
47584d9c625SLionel Sambuc 
47684d9c625SLionel Sambuc 	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
47784d9c625SLionel Sambuc 
47884d9c625SLionel Sambuc 	bp  = NULL; /* both used in error_exit */
47984d9c625SLionel Sambuc 	pmp = NULL;
48084d9c625SLionel Sambuc 
48184d9c625SLionel Sambuc 	error = fstrans_mount(mp);
48284d9c625SLionel Sambuc 	if (error)
48384d9c625SLionel Sambuc 		goto error_exit;
48484d9c625SLionel Sambuc 
48584d9c625SLionel Sambuc 	error = getdisksize(devvp, &psize, &secsize);
48684d9c625SLionel Sambuc 	if (error) {
48784d9c625SLionel Sambuc 		if (argp->flags & MSDOSFSMNT_GEMDOSFS)
48884d9c625SLionel Sambuc 			goto error_exit;
48984d9c625SLionel Sambuc 
49084d9c625SLionel Sambuc 		/* ok, so it failed.  we most likely don't need the info */
49184d9c625SLionel Sambuc 		secsize = DEV_BSIZE;
49284d9c625SLionel Sambuc 		psize = 0;
49384d9c625SLionel Sambuc 		error = 0;
49484d9c625SLionel Sambuc 	}
495*0a6a1f1dSLionel Sambuc 	if (secsize < DEV_BSIZE) {
496*0a6a1f1dSLionel Sambuc 		DPRINTF("Invalid block secsize (%d < DEV_BSIZE)", secsize);
49784d9c625SLionel Sambuc 		error = EINVAL;
49884d9c625SLionel Sambuc 		goto error_exit;
49984d9c625SLionel Sambuc 	}
500*0a6a1f1dSLionel Sambuc 
501*0a6a1f1dSLionel Sambuc 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
502*0a6a1f1dSLionel Sambuc 		if (secsize != GEMDOSFS_BSIZE) {
503*0a6a1f1dSLionel Sambuc 			DPRINTF("Invalid block secsize %d for GEMDOS", secsize);
504*0a6a1f1dSLionel Sambuc 			error = EINVAL;
505*0a6a1f1dSLionel Sambuc 			goto error_exit;
506*0a6a1f1dSLionel Sambuc 		}
507*0a6a1f1dSLionel Sambuc 	}
50884d9c625SLionel Sambuc 
50984d9c625SLionel Sambuc 	/*
51084d9c625SLionel Sambuc 	 * Read the boot sector of the filesystem, and then check the
51184d9c625SLionel Sambuc 	 * boot signature.  If not a dos boot sector then error out.
51284d9c625SLionel Sambuc 	 */
513*0a6a1f1dSLionel Sambuc 	if (secsize < sizeof(*b50)) {
514*0a6a1f1dSLionel Sambuc 		DPRINTF("50 bootsec %u\n", secsize);
515*0a6a1f1dSLionel Sambuc 		error = EINVAL;
516*0a6a1f1dSLionel Sambuc 		goto error_exit;
517*0a6a1f1dSLionel Sambuc 	}
518*0a6a1f1dSLionel Sambuc 	if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
51984d9c625SLionel Sambuc 		goto error_exit;
52084d9c625SLionel Sambuc 	bsp = (union bootsector *)bp->b_data;
52184d9c625SLionel Sambuc 	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
52284d9c625SLionel Sambuc 	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
52384d9c625SLionel Sambuc 	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
52484d9c625SLionel Sambuc 
52584d9c625SLionel Sambuc 	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
52684d9c625SLionel Sambuc 		if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
52784d9c625SLionel Sambuc 		    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
528*0a6a1f1dSLionel Sambuc 			DPRINTF("bootsig0 %d bootsig1 %d",
52984d9c625SLionel Sambuc 			    bsp->bs50.bsBootSectSig0,
530*0a6a1f1dSLionel Sambuc 			    bsp->bs50.bsBootSectSig1);
53184d9c625SLionel Sambuc 			error = EINVAL;
53284d9c625SLionel Sambuc 			goto error_exit;
53384d9c625SLionel Sambuc 		}
53484d9c625SLionel Sambuc 	}
53584d9c625SLionel Sambuc 
536*0a6a1f1dSLionel Sambuc 	pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK|M_ZERO);
53784d9c625SLionel Sambuc 	pmp->pm_mountp = mp;
53884d9c625SLionel Sambuc 
53984d9c625SLionel Sambuc 	/*
54084d9c625SLionel Sambuc 	 * Compute several useful quantities from the bpb in the
54184d9c625SLionel Sambuc 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
54284d9c625SLionel Sambuc 	 * the fields that are different between dos 5 and dos 3.3.
54384d9c625SLionel Sambuc 	 */
54484d9c625SLionel Sambuc 	SecPerClust = b50->bpbSecPerClust;
54584d9c625SLionel Sambuc 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
54684d9c625SLionel Sambuc 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
54784d9c625SLionel Sambuc 	pmp->pm_FATs = b50->bpbFATs;
54884d9c625SLionel Sambuc 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
54984d9c625SLionel Sambuc 	pmp->pm_Sectors = getushort(b50->bpbSectors);
55084d9c625SLionel Sambuc 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
55184d9c625SLionel Sambuc 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
55284d9c625SLionel Sambuc 	pmp->pm_Heads = getushort(b50->bpbHeads);
55384d9c625SLionel Sambuc 	pmp->pm_Media = b50->bpbMedia;
55484d9c625SLionel Sambuc 
55584d9c625SLionel Sambuc 	if (pmp->pm_Sectors == 0) {
55684d9c625SLionel Sambuc 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
55784d9c625SLionel Sambuc 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
55884d9c625SLionel Sambuc 	} else {
559*0a6a1f1dSLionel Sambuc 		if (secsize < sizeof(*b33)) {
560*0a6a1f1dSLionel Sambuc 			DPRINTF("33 bootsec %u\n", secsize);
561*0a6a1f1dSLionel Sambuc 			error = EINVAL;
562*0a6a1f1dSLionel Sambuc 			goto error_exit;
563*0a6a1f1dSLionel Sambuc 		}
56484d9c625SLionel Sambuc 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
56584d9c625SLionel Sambuc 		pmp->pm_HugeSectors = pmp->pm_Sectors;
56684d9c625SLionel Sambuc 	}
56784d9c625SLionel Sambuc 
568*0a6a1f1dSLionel Sambuc 	/*
569*0a6a1f1dSLionel Sambuc 	 * Sanity checks, from the FAT specification:
570*0a6a1f1dSLionel Sambuc 	 * - sectors per cluster: >= 1, power of 2
571*0a6a1f1dSLionel Sambuc 	 * - logical sector size: >= 1, power of 2
572*0a6a1f1dSLionel Sambuc 	 * - cluster size:        <= max FS block size
573*0a6a1f1dSLionel Sambuc 	 * - number of sectors:   >= 1
574*0a6a1f1dSLionel Sambuc 	 */
575*0a6a1f1dSLionel Sambuc 	if ((SecPerClust == 0) || !powerof2(SecPerClust) ||
576*0a6a1f1dSLionel Sambuc 	    (pmp->pm_BytesPerSec == 0) || !powerof2(pmp->pm_BytesPerSec) ||
577*0a6a1f1dSLionel Sambuc 	    (SecPerClust * pmp->pm_BytesPerSec > MAXBSIZE) ||
578*0a6a1f1dSLionel Sambuc 	    (pmp->pm_HugeSectors == 0)) {
579*0a6a1f1dSLionel Sambuc 		DPRINTF("consistency checks");
580*0a6a1f1dSLionel Sambuc 		error = EINVAL;
581*0a6a1f1dSLionel Sambuc 		goto error_exit;
582*0a6a1f1dSLionel Sambuc 	}
583*0a6a1f1dSLionel Sambuc 
584*0a6a1f1dSLionel Sambuc 	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS) &&
585*0a6a1f1dSLionel Sambuc 	    (pmp->pm_SecPerTrack > 63)) {
586*0a6a1f1dSLionel Sambuc 		DPRINTF("SecPerTrack %d", pmp->pm_SecPerTrack);
587*0a6a1f1dSLionel Sambuc 		error = EINVAL;
588*0a6a1f1dSLionel Sambuc 		goto error_exit;
589*0a6a1f1dSLionel Sambuc 	}
590*0a6a1f1dSLionel Sambuc 
59184d9c625SLionel Sambuc 	if (pmp->pm_RootDirEnts == 0) {
592*0a6a1f1dSLionel Sambuc 		if (secsize < sizeof(*b710)) {
593*0a6a1f1dSLionel Sambuc 			DPRINTF("710 bootsec %u\n", secsize);
594*0a6a1f1dSLionel Sambuc 			error = EINVAL;
595*0a6a1f1dSLionel Sambuc 			goto error_exit;
596*0a6a1f1dSLionel Sambuc 		}
597*0a6a1f1dSLionel Sambuc 		unsigned short FSVers = getushort(b710->bpbFSVers);
598*0a6a1f1dSLionel Sambuc 		unsigned short ExtFlags = getushort(b710->bpbExtFlags);
59984d9c625SLionel Sambuc 		/*
60084d9c625SLionel Sambuc 		 * Some say that bsBootSectSig[23] must be zero, but
60184d9c625SLionel Sambuc 		 * Windows does not require this and some digital cameras
60284d9c625SLionel Sambuc 		 * do not set these to zero.  Therefore, do not insist.
60384d9c625SLionel Sambuc 		 */
604*0a6a1f1dSLionel Sambuc 		if (pmp->pm_Sectors || pmp->pm_FATsecs || FSVers) {
605*0a6a1f1dSLionel Sambuc 			DPRINTF("Sectors %d FATsecs %lu FSVers %d",
606*0a6a1f1dSLionel Sambuc 			    pmp->pm_Sectors, pmp->pm_FATsecs, FSVers);
60784d9c625SLionel Sambuc 			error = EINVAL;
60884d9c625SLionel Sambuc 			goto error_exit;
60984d9c625SLionel Sambuc 		}
61084d9c625SLionel Sambuc 		pmp->pm_fatmask = FAT32_MASK;
61184d9c625SLionel Sambuc 		pmp->pm_fatmult = 4;
61284d9c625SLionel Sambuc 		pmp->pm_fatdiv = 1;
61384d9c625SLionel Sambuc 		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
61484d9c625SLionel Sambuc 
615*0a6a1f1dSLionel Sambuc 		/* Mirroring is enabled if the FATMIRROR bit is not set. */
616*0a6a1f1dSLionel Sambuc 		if ((ExtFlags & FATMIRROR) == 0)
61784d9c625SLionel Sambuc 			pmp->pm_flags |= MSDOSFS_FATMIRROR;
61884d9c625SLionel Sambuc 		else
619*0a6a1f1dSLionel Sambuc 			pmp->pm_curfat = ExtFlags & FATNUM;
62084d9c625SLionel Sambuc 	} else
62184d9c625SLionel Sambuc 		pmp->pm_flags |= MSDOSFS_FATMIRROR;
62284d9c625SLionel Sambuc 
62384d9c625SLionel Sambuc 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
62484d9c625SLionel Sambuc 		if (FAT32(pmp)) {
625*0a6a1f1dSLionel Sambuc 			/* GEMDOS doesn't know FAT32. */
626*0a6a1f1dSLionel Sambuc 			DPRINTF("FAT32 for GEMDOS");
62784d9c625SLionel Sambuc 			error = EINVAL;
62884d9c625SLionel Sambuc 			goto error_exit;
62984d9c625SLionel Sambuc 		}
63084d9c625SLionel Sambuc 
63184d9c625SLionel Sambuc 		/*
63284d9c625SLionel Sambuc 		 * Check a few values (could do some more):
633*0a6a1f1dSLionel Sambuc 		 * - logical sector size: >= block size
634*0a6a1f1dSLionel Sambuc 		 * - number of sectors:   <= size of partition
63584d9c625SLionel Sambuc 		 */
636*0a6a1f1dSLionel Sambuc 		if ((pmp->pm_BytesPerSec < GEMDOSFS_BSIZE) ||
637*0a6a1f1dSLionel Sambuc 		    (pmp->pm_HugeSectors *
638*0a6a1f1dSLionel Sambuc 		     (pmp->pm_BytesPerSec / GEMDOSFS_BSIZE) > psize)) {
639*0a6a1f1dSLionel Sambuc 			DPRINTF("consistency checks for GEMDOS");
64084d9c625SLionel Sambuc 			error = EINVAL;
64184d9c625SLionel Sambuc 			goto error_exit;
64284d9c625SLionel Sambuc 		}
64384d9c625SLionel Sambuc 		/*
64484d9c625SLionel Sambuc 		 * XXX - Many parts of the msdosfs driver seem to assume that
64584d9c625SLionel Sambuc 		 * the number of bytes per logical sector (BytesPerSec) will
64684d9c625SLionel Sambuc 		 * always be the same as the number of bytes per disk block
64784d9c625SLionel Sambuc 		 * Let's pretend it is.
64884d9c625SLionel Sambuc 		 */
649*0a6a1f1dSLionel Sambuc 		BlkPerSec = pmp->pm_BytesPerSec / GEMDOSFS_BSIZE;
650*0a6a1f1dSLionel Sambuc 		pmp->pm_BytesPerSec  = GEMDOSFS_BSIZE;
651*0a6a1f1dSLionel Sambuc 		pmp->pm_HugeSectors *= BlkPerSec;
652*0a6a1f1dSLionel Sambuc 		pmp->pm_HiddenSects *= BlkPerSec;
653*0a6a1f1dSLionel Sambuc 		pmp->pm_ResSectors  *= BlkPerSec;
654*0a6a1f1dSLionel Sambuc 		pmp->pm_Sectors     *= BlkPerSec;
655*0a6a1f1dSLionel Sambuc 		pmp->pm_FATsecs     *= BlkPerSec;
656*0a6a1f1dSLionel Sambuc 		SecPerClust         *= BlkPerSec;
65784d9c625SLionel Sambuc 	}
65884d9c625SLionel Sambuc 
65984d9c625SLionel Sambuc 	/* Check that fs has nonzero FAT size */
66084d9c625SLionel Sambuc 	if (pmp->pm_FATsecs == 0) {
661*0a6a1f1dSLionel Sambuc 		DPRINTF("FATsecs is 0");
66284d9c625SLionel Sambuc 		error = EINVAL;
66384d9c625SLionel Sambuc 		goto error_exit;
66484d9c625SLionel Sambuc 	}
66584d9c625SLionel Sambuc 
66684d9c625SLionel Sambuc 	pmp->pm_fatblk = pmp->pm_ResSectors;
66784d9c625SLionel Sambuc 	if (FAT32(pmp)) {
668*0a6a1f1dSLionel Sambuc 		if (secsize < sizeof(*b710)) {
669*0a6a1f1dSLionel Sambuc 			DPRINTF("710 bootsec %u\n", secsize);
670*0a6a1f1dSLionel Sambuc 			error = EINVAL;
671*0a6a1f1dSLionel Sambuc 			goto error_exit;
672*0a6a1f1dSLionel Sambuc 		}
67384d9c625SLionel Sambuc 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
67484d9c625SLionel Sambuc 		pmp->pm_firstcluster = pmp->pm_fatblk
67584d9c625SLionel Sambuc 			+ (pmp->pm_FATs * pmp->pm_FATsecs);
67684d9c625SLionel Sambuc 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
67784d9c625SLionel Sambuc 	} else {
67884d9c625SLionel Sambuc 		pmp->pm_rootdirblk = pmp->pm_fatblk +
67984d9c625SLionel Sambuc 			(pmp->pm_FATs * pmp->pm_FATsecs);
68084d9c625SLionel Sambuc 		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
68184d9c625SLionel Sambuc 				       + pmp->pm_BytesPerSec - 1)
68284d9c625SLionel Sambuc 			/ pmp->pm_BytesPerSec;/* in sectors */
68384d9c625SLionel Sambuc 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
68484d9c625SLionel Sambuc 	}
68584d9c625SLionel Sambuc 
68684d9c625SLionel Sambuc 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
68784d9c625SLionel Sambuc 	    SecPerClust;
68884d9c625SLionel Sambuc 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
68984d9c625SLionel Sambuc 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
69084d9c625SLionel Sambuc 
69184d9c625SLionel Sambuc 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
69284d9c625SLionel Sambuc 		if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
69384d9c625SLionel Sambuc 			pmp->pm_fatmask = FAT12_MASK;
69484d9c625SLionel Sambuc 			pmp->pm_fatmult = 3;
69584d9c625SLionel Sambuc 			pmp->pm_fatdiv = 2;
69684d9c625SLionel Sambuc 		} else {
69784d9c625SLionel Sambuc 			pmp->pm_fatmask = FAT16_MASK;
69884d9c625SLionel Sambuc 			pmp->pm_fatmult = 2;
69984d9c625SLionel Sambuc 			pmp->pm_fatdiv = 1;
70084d9c625SLionel Sambuc 		}
70184d9c625SLionel Sambuc 	} else if (pmp->pm_fatmask == 0) {
70284d9c625SLionel Sambuc 		if (pmp->pm_maxcluster
70384d9c625SLionel Sambuc 		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
70484d9c625SLionel Sambuc 			/*
70584d9c625SLionel Sambuc 			 * This will usually be a floppy disk. This size makes
70684d9c625SLionel Sambuc 			 * sure that one FAT entry will not be split across
70784d9c625SLionel Sambuc 			 * multiple blocks.
70884d9c625SLionel Sambuc 			 */
70984d9c625SLionel Sambuc 			pmp->pm_fatmask = FAT12_MASK;
71084d9c625SLionel Sambuc 			pmp->pm_fatmult = 3;
71184d9c625SLionel Sambuc 			pmp->pm_fatdiv = 2;
71284d9c625SLionel Sambuc 		} else {
71384d9c625SLionel Sambuc 			pmp->pm_fatmask = FAT16_MASK;
71484d9c625SLionel Sambuc 			pmp->pm_fatmult = 2;
71584d9c625SLionel Sambuc 			pmp->pm_fatdiv = 1;
71684d9c625SLionel Sambuc 		}
71784d9c625SLionel Sambuc 	}
71884d9c625SLionel Sambuc 	if (FAT12(pmp))
71984d9c625SLionel Sambuc 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
72084d9c625SLionel Sambuc 	else
72184d9c625SLionel Sambuc 		pmp->pm_fatblocksize = MAXBSIZE;
72284d9c625SLionel Sambuc 
72384d9c625SLionel Sambuc 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
72484d9c625SLionel Sambuc 	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
72584d9c625SLionel Sambuc 
72684d9c625SLionel Sambuc 	/*
72784d9c625SLionel Sambuc 	 * Compute mask and shift value for isolating cluster relative byte
72884d9c625SLionel Sambuc 	 * offsets and cluster numbers from a file offset.
72984d9c625SLionel Sambuc 	 */
73084d9c625SLionel Sambuc 	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
73184d9c625SLionel Sambuc 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
73284d9c625SLionel Sambuc 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
73384d9c625SLionel Sambuc 
73484d9c625SLionel Sambuc 	/*
73584d9c625SLionel Sambuc 	 * Check for valid cluster size
73684d9c625SLionel Sambuc 	 * must be a power of 2
73784d9c625SLionel Sambuc 	 */
73884d9c625SLionel Sambuc 	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
739*0a6a1f1dSLionel Sambuc 		DPRINTF("bpcluster %lu cnshift %lu", pmp->pm_bpcluster,
740*0a6a1f1dSLionel Sambuc 		    pmp->pm_cnshift);
74184d9c625SLionel Sambuc 		error = EINVAL;
74284d9c625SLionel Sambuc 		goto error_exit;
74384d9c625SLionel Sambuc 	}
74484d9c625SLionel Sambuc 
74584d9c625SLionel Sambuc 	/*
74684d9c625SLionel Sambuc 	 * Cluster size must be within limit of MAXBSIZE.
74784d9c625SLionel Sambuc 	 * Many FAT filesystems will not have clusters larger than
74884d9c625SLionel Sambuc 	 * 32KiB due to limits in Windows versions before Vista.
74984d9c625SLionel Sambuc 	 */
75084d9c625SLionel Sambuc 	if (pmp->pm_bpcluster > MAXBSIZE) {
751*0a6a1f1dSLionel Sambuc 		DPRINTF("bpcluster %lu > MAXBSIZE %d",
752*0a6a1f1dSLionel Sambuc 		    pmp->pm_bpcluster, MAXBSIZE);
75384d9c625SLionel Sambuc 		error = EINVAL;
75484d9c625SLionel Sambuc 		goto error_exit;
75584d9c625SLionel Sambuc 	}
75684d9c625SLionel Sambuc 
75784d9c625SLionel Sambuc 	/*
75884d9c625SLionel Sambuc 	 * Release the bootsector buffer.
75984d9c625SLionel Sambuc 	 */
76084d9c625SLionel Sambuc 	brelse(bp, BC_AGE);
76184d9c625SLionel Sambuc 	bp = NULL;
76284d9c625SLionel Sambuc 
76384d9c625SLionel Sambuc 	/*
76484d9c625SLionel Sambuc 	 * Check FSInfo.
76584d9c625SLionel Sambuc 	 */
76684d9c625SLionel Sambuc 	if (pmp->pm_fsinfo) {
76784d9c625SLionel Sambuc 		struct fsinfo *fp;
768*0a6a1f1dSLionel Sambuc 		const int rdsz = roundup(sizeof(*fp), pmp->pm_BytesPerSec);
76984d9c625SLionel Sambuc 
77084d9c625SLionel Sambuc 		/*
77184d9c625SLionel Sambuc 		 * XXX	If the fsinfo block is stored on media with
77284d9c625SLionel Sambuc 		 *	2KB or larger sectors, is the fsinfo structure
77384d9c625SLionel Sambuc 		 *	padded at the end or in the middle?
77484d9c625SLionel Sambuc 		 */
77584d9c625SLionel Sambuc 		if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
776*0a6a1f1dSLionel Sambuc 		    rdsz, 0, &bp)) != 0)
77784d9c625SLionel Sambuc 			goto error_exit;
77884d9c625SLionel Sambuc 		fp = (struct fsinfo *)bp->b_data;
77984d9c625SLionel Sambuc 		if (!memcmp(fp->fsisig1, "RRaA", 4)
78084d9c625SLionel Sambuc 		    && !memcmp(fp->fsisig2, "rrAa", 4)
78184d9c625SLionel Sambuc 		    && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
78284d9c625SLionel Sambuc 		    && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
78384d9c625SLionel Sambuc 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
78484d9c625SLionel Sambuc 		else
78584d9c625SLionel Sambuc 			pmp->pm_fsinfo = 0;
78684d9c625SLionel Sambuc 		brelse(bp, 0);
78784d9c625SLionel Sambuc 		bp = NULL;
78884d9c625SLionel Sambuc 	}
78984d9c625SLionel Sambuc 
79084d9c625SLionel Sambuc 	/*
79184d9c625SLionel Sambuc 	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
79284d9c625SLionel Sambuc 	 * XXX
79384d9c625SLionel Sambuc 	 */
79484d9c625SLionel Sambuc 	if (pmp->pm_fsinfo) {
79584d9c625SLionel Sambuc 		if ((pmp->pm_nxtfree == 0xffffffffUL) ||
79684d9c625SLionel Sambuc 		    (pmp->pm_nxtfree > pmp->pm_maxcluster))
79784d9c625SLionel Sambuc 			pmp->pm_fsinfo = 0;
79884d9c625SLionel Sambuc 	}
79984d9c625SLionel Sambuc 
80084d9c625SLionel Sambuc 	/*
80184d9c625SLionel Sambuc 	 * Allocate memory for the bitmap of allocated clusters, and then
80284d9c625SLionel Sambuc 	 * fill it in.
80384d9c625SLionel Sambuc 	 */
80484d9c625SLionel Sambuc 	pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS)
80584d9c625SLionel Sambuc 				   / N_INUSEBITS)
80684d9c625SLionel Sambuc 				  * sizeof(*pmp->pm_inusemap),
80784d9c625SLionel Sambuc 				  M_MSDOSFSFAT, M_WAITOK);
80884d9c625SLionel Sambuc 
80984d9c625SLionel Sambuc 	/*
81084d9c625SLionel Sambuc 	 * fillinusemap() needs pm_devvp.
81184d9c625SLionel Sambuc 	 */
81284d9c625SLionel Sambuc 	pmp->pm_dev = dev;
81384d9c625SLionel Sambuc 	pmp->pm_devvp = devvp;
81484d9c625SLionel Sambuc 
81584d9c625SLionel Sambuc 	/*
81684d9c625SLionel Sambuc 	 * Have the inuse map filled in.
81784d9c625SLionel Sambuc 	 */
81884d9c625SLionel Sambuc 	if ((error = fillinusemap(pmp)) != 0) {
819*0a6a1f1dSLionel Sambuc 		DPRINTF("fillinusemap %d", error);
82084d9c625SLionel Sambuc 		goto error_exit;
82184d9c625SLionel Sambuc 	}
82284d9c625SLionel Sambuc 
82384d9c625SLionel Sambuc 	/*
82484d9c625SLionel Sambuc 	 * If they want FAT updates to be synchronous then let them suffer
82584d9c625SLionel Sambuc 	 * the performance degradation in exchange for the on disk copy of
82684d9c625SLionel Sambuc 	 * the FAT being correct just about all the time.  I suppose this
82784d9c625SLionel Sambuc 	 * would be a good thing to turn on if the kernel is still flakey.
82884d9c625SLionel Sambuc 	 */
82984d9c625SLionel Sambuc 	if (mp->mnt_flag & MNT_SYNCHRONOUS)
83084d9c625SLionel Sambuc 		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
83184d9c625SLionel Sambuc 
83284d9c625SLionel Sambuc 	/*
83384d9c625SLionel Sambuc 	 * Finish up.
83484d9c625SLionel Sambuc 	 */
83584d9c625SLionel Sambuc 	if (ronly)
83684d9c625SLionel Sambuc 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
83784d9c625SLionel Sambuc 	else
83884d9c625SLionel Sambuc 		pmp->pm_fmod = 1;
83984d9c625SLionel Sambuc 	mp->mnt_data = pmp;
84084d9c625SLionel Sambuc 	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
84184d9c625SLionel Sambuc 	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS);
84284d9c625SLionel Sambuc 	mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
84384d9c625SLionel Sambuc 	mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
84484d9c625SLionel Sambuc 	mp->mnt_flag |= MNT_LOCAL;
84584d9c625SLionel Sambuc 	mp->mnt_dev_bshift = pmp->pm_bnshift;
84684d9c625SLionel Sambuc 	mp->mnt_fs_bshift = pmp->pm_cnshift;
84784d9c625SLionel Sambuc 
84884d9c625SLionel Sambuc 	/*
84984d9c625SLionel Sambuc 	 * If we ever do quotas for DOS filesystems this would be a place
85084d9c625SLionel Sambuc 	 * to fill in the info in the msdosfsmount structure. You dolt,
85184d9c625SLionel Sambuc 	 * quotas on dos filesystems make no sense because files have no
85284d9c625SLionel Sambuc 	 * owners on dos filesystems. of course there is some empty space
85384d9c625SLionel Sambuc 	 * in the directory entry where we could put uid's and gid's.
85484d9c625SLionel Sambuc 	 */
85584d9c625SLionel Sambuc 
85684d9c625SLionel Sambuc 	spec_node_setmountedfs(devvp, mp);
85784d9c625SLionel Sambuc 
85884d9c625SLionel Sambuc 	return (0);
85984d9c625SLionel Sambuc 
86084d9c625SLionel Sambuc error_exit:
86184d9c625SLionel Sambuc 	fstrans_unmount(mp);
86284d9c625SLionel Sambuc 	if (bp)
86384d9c625SLionel Sambuc 		brelse(bp, BC_AGE);
86484d9c625SLionel Sambuc 	if (pmp) {
86584d9c625SLionel Sambuc 		if (pmp->pm_inusemap)
86684d9c625SLionel Sambuc 			free(pmp->pm_inusemap, M_MSDOSFSFAT);
86784d9c625SLionel Sambuc 		free(pmp, M_MSDOSFSMNT);
86884d9c625SLionel Sambuc 		mp->mnt_data = NULL;
86984d9c625SLionel Sambuc 	}
87084d9c625SLionel Sambuc 	return (error);
87184d9c625SLionel Sambuc }
87284d9c625SLionel Sambuc 
87384d9c625SLionel Sambuc int
msdosfs_start(struct mount * mp,int flags)87484d9c625SLionel Sambuc msdosfs_start(struct mount *mp, int flags)
87584d9c625SLionel Sambuc {
87684d9c625SLionel Sambuc 
87784d9c625SLionel Sambuc 	return (0);
87884d9c625SLionel Sambuc }
87984d9c625SLionel Sambuc 
88084d9c625SLionel Sambuc /*
88184d9c625SLionel Sambuc  * Unmount the filesystem described by mp.
88284d9c625SLionel Sambuc  */
88384d9c625SLionel Sambuc int
msdosfs_unmount(struct mount * mp,int mntflags)88484d9c625SLionel Sambuc msdosfs_unmount(struct mount *mp, int mntflags)
88584d9c625SLionel Sambuc {
88684d9c625SLionel Sambuc 	struct msdosfsmount *pmp;
88784d9c625SLionel Sambuc 	int error, flags;
88884d9c625SLionel Sambuc 
88984d9c625SLionel Sambuc 	flags = 0;
89084d9c625SLionel Sambuc 	if (mntflags & MNT_FORCE)
89184d9c625SLionel Sambuc 		flags |= FORCECLOSE;
89284d9c625SLionel Sambuc 	if ((error = vflush(mp, NULLVP, flags)) != 0)
89384d9c625SLionel Sambuc 		return (error);
89484d9c625SLionel Sambuc 	pmp = VFSTOMSDOSFS(mp);
89584d9c625SLionel Sambuc 	if (pmp->pm_devvp->v_type != VBAD)
89684d9c625SLionel Sambuc 		spec_node_setmountedfs(pmp->pm_devvp, NULL);
89784d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
89884d9c625SLionel Sambuc 	{
89984d9c625SLionel Sambuc 		struct vnode *vp = pmp->pm_devvp;
90084d9c625SLionel Sambuc 
90184d9c625SLionel Sambuc 		printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
90284d9c625SLionel Sambuc 		printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
90384d9c625SLionel Sambuc 		    vp->v_vflag | vp->v_iflag | vp->v_uflag, vp->v_usecount,
90484d9c625SLionel Sambuc 		    vp->v_writecount, vp->v_holdcnt);
90584d9c625SLionel Sambuc 		printf("mount %p, op %p\n",
90684d9c625SLionel Sambuc 		    vp->v_mount, vp->v_op);
90784d9c625SLionel Sambuc 		printf("freef %p, freeb %p, mount %p\n",
90884d9c625SLionel Sambuc 		    vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev,
90984d9c625SLionel Sambuc 		    vp->v_mount);
91084d9c625SLionel Sambuc 		printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
91184d9c625SLionel Sambuc 		    vp->v_cleanblkhd.lh_first,
91284d9c625SLionel Sambuc 		    vp->v_dirtyblkhd.lh_first,
91384d9c625SLionel Sambuc 		    vp->v_numoutput, vp->v_type);
91484d9c625SLionel Sambuc 		printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
91584d9c625SLionel Sambuc 		    vp->v_socket, vp->v_tag,
91684d9c625SLionel Sambuc 		    ((u_int *)vp->v_data)[0],
91784d9c625SLionel Sambuc 		    ((u_int *)vp->v_data)[1]);
91884d9c625SLionel Sambuc 	}
91984d9c625SLionel Sambuc #endif
92084d9c625SLionel Sambuc 	vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
92184d9c625SLionel Sambuc 	(void) VOP_CLOSE(pmp->pm_devvp,
92284d9c625SLionel Sambuc 	    pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED);
92384d9c625SLionel Sambuc 	vput(pmp->pm_devvp);
92484d9c625SLionel Sambuc 	msdosfs_fh_destroy(pmp);
92584d9c625SLionel Sambuc 	free(pmp->pm_inusemap, M_MSDOSFSFAT);
92684d9c625SLionel Sambuc 	free(pmp, M_MSDOSFSMNT);
92784d9c625SLionel Sambuc 	mp->mnt_data = NULL;
92884d9c625SLionel Sambuc 	mp->mnt_flag &= ~MNT_LOCAL;
92984d9c625SLionel Sambuc 	fstrans_unmount(mp);
93084d9c625SLionel Sambuc 	return (0);
93184d9c625SLionel Sambuc }
93284d9c625SLionel Sambuc 
93384d9c625SLionel Sambuc int
msdosfs_root(struct mount * mp,struct vnode ** vpp)93484d9c625SLionel Sambuc msdosfs_root(struct mount *mp, struct vnode **vpp)
93584d9c625SLionel Sambuc {
93684d9c625SLionel Sambuc 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
93784d9c625SLionel Sambuc 	int error;
93884d9c625SLionel Sambuc 
93984d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
94084d9c625SLionel Sambuc 	printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
94184d9c625SLionel Sambuc #endif
942*0a6a1f1dSLionel Sambuc 	if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, vpp)) != 0)
943*0a6a1f1dSLionel Sambuc 		return error;
944*0a6a1f1dSLionel Sambuc 	error = vn_lock(*vpp, LK_EXCLUSIVE);
945*0a6a1f1dSLionel Sambuc 	if (error) {
946*0a6a1f1dSLionel Sambuc 		vrele(*vpp);
947*0a6a1f1dSLionel Sambuc 		*vpp = NULL;
948*0a6a1f1dSLionel Sambuc 		return error;
949*0a6a1f1dSLionel Sambuc 	}
950*0a6a1f1dSLionel Sambuc 	return 0;
95184d9c625SLionel Sambuc }
95284d9c625SLionel Sambuc 
95384d9c625SLionel Sambuc int
msdosfs_statvfs(struct mount * mp,struct statvfs * sbp)95484d9c625SLionel Sambuc msdosfs_statvfs(struct mount *mp, struct statvfs *sbp)
95584d9c625SLionel Sambuc {
95684d9c625SLionel Sambuc 	struct msdosfsmount *pmp;
95784d9c625SLionel Sambuc 
95884d9c625SLionel Sambuc 	pmp = VFSTOMSDOSFS(mp);
95984d9c625SLionel Sambuc 	sbp->f_bsize = pmp->pm_bpcluster;
96084d9c625SLionel Sambuc 	sbp->f_frsize = sbp->f_bsize;
96184d9c625SLionel Sambuc 	sbp->f_iosize = pmp->pm_bpcluster;
96284d9c625SLionel Sambuc 	sbp->f_blocks = pmp->pm_nmbrofclusters;
96384d9c625SLionel Sambuc 	sbp->f_bfree = pmp->pm_freeclustercount;
96484d9c625SLionel Sambuc 	sbp->f_bavail = pmp->pm_freeclustercount;
96584d9c625SLionel Sambuc 	sbp->f_bresvd = 0;
96684d9c625SLionel Sambuc 	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
96784d9c625SLionel Sambuc 	sbp->f_ffree = 0;	/* what to put in here? */
96884d9c625SLionel Sambuc 	sbp->f_favail = 0;	/* what to put in here? */
96984d9c625SLionel Sambuc 	sbp->f_fresvd = 0;
97084d9c625SLionel Sambuc 	copy_statvfs_info(sbp, mp);
97184d9c625SLionel Sambuc 	return (0);
97284d9c625SLionel Sambuc }
97384d9c625SLionel Sambuc 
974*0a6a1f1dSLionel Sambuc struct msdosfs_sync_ctx {
975*0a6a1f1dSLionel Sambuc 	int waitfor;
976*0a6a1f1dSLionel Sambuc };
977*0a6a1f1dSLionel Sambuc 
978*0a6a1f1dSLionel Sambuc static bool
msdosfs_sync_selector(void * cl,struct vnode * vp)979*0a6a1f1dSLionel Sambuc msdosfs_sync_selector(void *cl, struct vnode *vp)
980*0a6a1f1dSLionel Sambuc {
981*0a6a1f1dSLionel Sambuc 	struct msdosfs_sync_ctx *c = cl;
982*0a6a1f1dSLionel Sambuc 	struct denode *dep;
983*0a6a1f1dSLionel Sambuc 
984*0a6a1f1dSLionel Sambuc 	dep = VTODE(vp);
985*0a6a1f1dSLionel Sambuc 	if (c->waitfor == MNT_LAZY || vp->v_type == VNON ||
986*0a6a1f1dSLionel Sambuc 	    dep == NULL || (((dep->de_flag &
987*0a6a1f1dSLionel Sambuc 	    (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) &&
988*0a6a1f1dSLionel Sambuc 	     (LIST_EMPTY(&vp->v_dirtyblkhd) &&
989*0a6a1f1dSLionel Sambuc 	      UVM_OBJ_IS_CLEAN(&vp->v_uobj))))
990*0a6a1f1dSLionel Sambuc 		return false;
991*0a6a1f1dSLionel Sambuc 	return true;
992*0a6a1f1dSLionel Sambuc }
993*0a6a1f1dSLionel Sambuc 
99484d9c625SLionel Sambuc int
msdosfs_sync(struct mount * mp,int waitfor,kauth_cred_t cred)99584d9c625SLionel Sambuc msdosfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
99684d9c625SLionel Sambuc {
997*0a6a1f1dSLionel Sambuc 	struct vnode *vp;
998*0a6a1f1dSLionel Sambuc 	struct vnode_iterator *marker;
99984d9c625SLionel Sambuc 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
100084d9c625SLionel Sambuc 	int error, allerror = 0;
1001*0a6a1f1dSLionel Sambuc 	struct msdosfs_sync_ctx ctx;
100284d9c625SLionel Sambuc 
100384d9c625SLionel Sambuc 	/*
100484d9c625SLionel Sambuc 	 * If we ever switch to not updating all of the FATs all the time,
100584d9c625SLionel Sambuc 	 * this would be the place to update them from the first one.
100684d9c625SLionel Sambuc 	 */
100784d9c625SLionel Sambuc 	if (pmp->pm_fmod != 0) {
100884d9c625SLionel Sambuc 		if (pmp->pm_flags & MSDOSFSMNT_RONLY)
100984d9c625SLionel Sambuc 			panic("msdosfs_sync: rofs mod");
101084d9c625SLionel Sambuc 		else {
101184d9c625SLionel Sambuc 			/* update FATs here */
101284d9c625SLionel Sambuc 		}
101384d9c625SLionel Sambuc 	}
101484d9c625SLionel Sambuc 	fstrans_start(mp, FSTRANS_SHARED);
101584d9c625SLionel Sambuc 	/*
101684d9c625SLionel Sambuc 	 * Write back each (modified) denode.
101784d9c625SLionel Sambuc 	 */
1018*0a6a1f1dSLionel Sambuc 	vfs_vnode_iterator_init(mp, &marker);
1019*0a6a1f1dSLionel Sambuc 	ctx.waitfor = waitfor;
1020*0a6a1f1dSLionel Sambuc 	while ((vp = vfs_vnode_iterator_next(marker, msdosfs_sync_selector,
1021*0a6a1f1dSLionel Sambuc 	    &ctx)))
1022*0a6a1f1dSLionel Sambuc 	{
1023*0a6a1f1dSLionel Sambuc 		error = vn_lock(vp, LK_EXCLUSIVE);
102484d9c625SLionel Sambuc 		if (error) {
1025*0a6a1f1dSLionel Sambuc 			vrele(vp);
102684d9c625SLionel Sambuc 			continue;
102784d9c625SLionel Sambuc 		}
102884d9c625SLionel Sambuc 		if ((error = VOP_FSYNC(vp, cred,
102984d9c625SLionel Sambuc 		    waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
103084d9c625SLionel Sambuc 			allerror = error;
103184d9c625SLionel Sambuc 		vput(vp);
103284d9c625SLionel Sambuc 	}
1033*0a6a1f1dSLionel Sambuc 	vfs_vnode_iterator_destroy(marker);
103484d9c625SLionel Sambuc 
103584d9c625SLionel Sambuc 	/*
103684d9c625SLionel Sambuc 	 * Force stale file system control information to be flushed.
103784d9c625SLionel Sambuc 	 */
103884d9c625SLionel Sambuc 	if ((error = VOP_FSYNC(pmp->pm_devvp, cred,
103984d9c625SLionel Sambuc 	    waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
104084d9c625SLionel Sambuc 		allerror = error;
104184d9c625SLionel Sambuc 	fstrans_done(mp);
104284d9c625SLionel Sambuc 	return (allerror);
104384d9c625SLionel Sambuc }
104484d9c625SLionel Sambuc 
104584d9c625SLionel Sambuc int
msdosfs_fhtovp(struct mount * mp,struct fid * fhp,struct vnode ** vpp)104684d9c625SLionel Sambuc msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
104784d9c625SLionel Sambuc {
104884d9c625SLionel Sambuc 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
104984d9c625SLionel Sambuc 	struct defid defh;
105084d9c625SLionel Sambuc 	uint32_t gen;
105184d9c625SLionel Sambuc 	int error;
105284d9c625SLionel Sambuc 
105384d9c625SLionel Sambuc 	if (fhp->fid_len != sizeof(struct defid)) {
1054*0a6a1f1dSLionel Sambuc 		DPRINTF("fid_len %d %zd", fhp->fid_len, sizeof(struct defid));
105584d9c625SLionel Sambuc 		return EINVAL;
105684d9c625SLionel Sambuc 	}
105784d9c625SLionel Sambuc 	memcpy(&defh, fhp, sizeof(defh));
105884d9c625SLionel Sambuc 	error = msdosfs_fh_lookup(pmp, defh.defid_dirclust, defh.defid_dirofs,
105984d9c625SLionel Sambuc 	    &gen);
106084d9c625SLionel Sambuc 	if (error == 0 && gen != defh.defid_gen)
106184d9c625SLionel Sambuc 		error = ESTALE;
106284d9c625SLionel Sambuc 	if (error) {
106384d9c625SLionel Sambuc 		*vpp = NULLVP;
106484d9c625SLionel Sambuc 		return error;
106584d9c625SLionel Sambuc 	}
1066*0a6a1f1dSLionel Sambuc 	error = deget(pmp, defh.defid_dirclust, defh.defid_dirofs, vpp);
106784d9c625SLionel Sambuc 	if (error) {
1068*0a6a1f1dSLionel Sambuc 		DPRINTF("deget %d", error);
106984d9c625SLionel Sambuc 		*vpp = NULLVP;
1070*0a6a1f1dSLionel Sambuc 		return error;
107184d9c625SLionel Sambuc 	}
1072*0a6a1f1dSLionel Sambuc 	error = vn_lock(*vpp, LK_EXCLUSIVE);
1073*0a6a1f1dSLionel Sambuc 	if (error) {
1074*0a6a1f1dSLionel Sambuc 		vrele(*vpp);
1075*0a6a1f1dSLionel Sambuc 		*vpp = NULLVP;
1076*0a6a1f1dSLionel Sambuc 		return error;
1077*0a6a1f1dSLionel Sambuc 	}
1078*0a6a1f1dSLionel Sambuc 	return 0;
107984d9c625SLionel Sambuc }
108084d9c625SLionel Sambuc 
108184d9c625SLionel Sambuc int
msdosfs_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)108284d9c625SLionel Sambuc msdosfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
108384d9c625SLionel Sambuc {
108484d9c625SLionel Sambuc 	struct msdosfsmount *pmp = VFSTOMSDOSFS(vp->v_mount);
108584d9c625SLionel Sambuc 	struct denode *dep;
108684d9c625SLionel Sambuc 	struct defid defh;
108784d9c625SLionel Sambuc 	int error;
108884d9c625SLionel Sambuc 
108984d9c625SLionel Sambuc 	if (*fh_size < sizeof(struct defid)) {
109084d9c625SLionel Sambuc 		*fh_size = sizeof(struct defid);
109184d9c625SLionel Sambuc 		return E2BIG;
109284d9c625SLionel Sambuc 	}
109384d9c625SLionel Sambuc 	*fh_size = sizeof(struct defid);
109484d9c625SLionel Sambuc 	dep = VTODE(vp);
109584d9c625SLionel Sambuc 	memset(&defh, 0, sizeof(defh));
109684d9c625SLionel Sambuc 	defh.defid_len = sizeof(struct defid);
109784d9c625SLionel Sambuc 	defh.defid_dirclust = dep->de_dirclust;
109884d9c625SLionel Sambuc 	defh.defid_dirofs = dep->de_diroffset;
109984d9c625SLionel Sambuc 	error = msdosfs_fh_enter(pmp, dep->de_dirclust, dep->de_diroffset,
110084d9c625SLionel Sambuc 	     &defh.defid_gen);
110184d9c625SLionel Sambuc 	if (error == 0)
110284d9c625SLionel Sambuc 		memcpy(fhp, &defh, sizeof(defh));
110384d9c625SLionel Sambuc 	return error;
110484d9c625SLionel Sambuc }
110584d9c625SLionel Sambuc 
110684d9c625SLionel Sambuc int
msdosfs_vget(struct mount * mp,ino_t ino,struct vnode ** vpp)110784d9c625SLionel Sambuc msdosfs_vget(struct mount *mp, ino_t ino,
110884d9c625SLionel Sambuc     struct vnode **vpp)
110984d9c625SLionel Sambuc {
111084d9c625SLionel Sambuc 
111184d9c625SLionel Sambuc 	return (EOPNOTSUPP);
111284d9c625SLionel Sambuc }
111384d9c625SLionel Sambuc 
111484d9c625SLionel Sambuc int
msdosfs_suspendctl(struct mount * mp,int cmd)111584d9c625SLionel Sambuc msdosfs_suspendctl(struct mount *mp, int cmd)
111684d9c625SLionel Sambuc {
111784d9c625SLionel Sambuc 	int error;
111884d9c625SLionel Sambuc 	struct lwp *l = curlwp;
111984d9c625SLionel Sambuc 
112084d9c625SLionel Sambuc 	switch (cmd) {
112184d9c625SLionel Sambuc 	case SUSPEND_SUSPEND:
112284d9c625SLionel Sambuc 		if ((error = fstrans_setstate(mp, FSTRANS_SUSPENDING)) != 0)
112384d9c625SLionel Sambuc 			return error;
112484d9c625SLionel Sambuc 		error = msdosfs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
112584d9c625SLionel Sambuc 		if (error == 0)
112684d9c625SLionel Sambuc 			error = fstrans_setstate(mp, FSTRANS_SUSPENDED);
112784d9c625SLionel Sambuc 		if (error != 0) {
112884d9c625SLionel Sambuc 			(void) fstrans_setstate(mp, FSTRANS_NORMAL);
112984d9c625SLionel Sambuc 			return error;
113084d9c625SLionel Sambuc 		}
113184d9c625SLionel Sambuc 		return 0;
113284d9c625SLionel Sambuc 
113384d9c625SLionel Sambuc 	case SUSPEND_RESUME:
113484d9c625SLionel Sambuc 		return fstrans_setstate(mp, FSTRANS_NORMAL);
113584d9c625SLionel Sambuc 
113684d9c625SLionel Sambuc 	default:
113784d9c625SLionel Sambuc 		return EINVAL;
113884d9c625SLionel Sambuc 	}
113984d9c625SLionel Sambuc }
1140