xref: /freebsd/sys/fs/msdosfs/msdosfs_vfsops.c (revision 2ad872c5794e4c26fdf6ed219ad3f09ca0d5304a)
1 /*	$Id: msdosfs_vfsops.c,v 1.38 1998/10/31 15:31:24 peter Exp $ */
2 /*	$NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $	*/
3 
4 /*-
5  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
6  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7  * All rights reserved.
8  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by TooLs GmbH.
21  * 4. The name of TooLs GmbH may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 /*
36  * Written by Paul Popelka (paulp@uts.amdahl.com)
37  *
38  * You can do anything you want with this software, just don't say you wrote
39  * it, and don't remove this notice.
40  *
41  * This software is provided "as is".
42  *
43  * The author supplies this software to be publicly redistributed on the
44  * understanding that the author is not responsible for the correct
45  * functioning of this software in any circumstances and is not liable for
46  * any damages caused by this software.
47  *
48  * October 1992
49  */
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/conf.h>
54 #include <sys/namei.h>
55 #include <sys/proc.h>
56 #include <sys/kernel.h>
57 #include <sys/vnode.h>
58 #include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
59 #include <sys/mount.h>
60 #include <sys/buf.h>
61 #include <sys/fcntl.h>
62 #include <sys/malloc.h>
63 #include <sys/stat.h> 				/* defines ALLPERMS */
64 
65 #include <msdosfs/bpb.h>
66 #include <msdosfs/bootsect.h>
67 #include <msdosfs/direntry.h>
68 #include <msdosfs/denode.h>
69 #include <msdosfs/msdosfsmount.h>
70 #include <msdosfs/fat.h>
71 
72 MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure");
73 static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table");
74 
75 static int	update_mp __P((struct mount *mp, struct msdosfs_args *argp));
76 static int	mountmsdosfs __P((struct vnode *devvp, struct mount *mp,
77 				  struct proc *p, struct msdosfs_args *argp));
78 static int	msdosfs_fhtovp __P((struct mount *, struct fid *,
79 				    struct sockaddr *, struct vnode **, int *,
80 				    struct ucred **));
81 static int	msdosfs_mount __P((struct mount *, char *, caddr_t,
82 				   struct nameidata *, struct proc *));
83 static int	msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t,
84 				      struct proc *));
85 static int	msdosfs_root __P((struct mount *, struct vnode **));
86 static int	msdosfs_start __P((struct mount *, int, struct proc *));
87 static int	msdosfs_statfs __P((struct mount *, struct statfs *,
88 				    struct proc *));
89 static int	msdosfs_sync __P((struct mount *, int, struct ucred *,
90 				  struct proc *));
91 static int	msdosfs_unmount __P((struct mount *, int, struct proc *));
92 static int	msdosfs_vget __P((struct mount *mp, ino_t ino,
93 				  struct vnode **vpp));
94 static int	msdosfs_vptofh __P((struct vnode *, struct fid *));
95 
96 static int
97 update_mp(mp, argp)
98 	struct mount *mp;
99 	struct msdosfs_args *argp;
100 {
101 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
102 	int error;
103 
104 	pmp->pm_gid = argp->gid;
105 	pmp->pm_uid = argp->uid;
106 	pmp->pm_mask = argp->mask & ALLPERMS;
107 	pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
108 	if (pmp->pm_flags & MSDOSFSMNT_U2WTABLE) {
109 		bcopy(argp->u2w, pmp->pm_u2w, sizeof(pmp->pm_u2w));
110 		bcopy(argp->d2u, pmp->pm_d2u, sizeof(pmp->pm_d2u));
111 		bcopy(argp->u2d, pmp->pm_u2d, sizeof(pmp->pm_u2d));
112 	}
113 	if (pmp->pm_flags & MSDOSFSMNT_ULTABLE) {
114 		bcopy(argp->ul, pmp->pm_ul, sizeof(pmp->pm_ul));
115 		bcopy(argp->lu, pmp->pm_lu, sizeof(pmp->pm_lu));
116 	}
117 
118 #ifndef __FreeBSD__
119 	/*
120 	 * GEMDOS knows nothing (yet) about win95
121 	 */
122 	if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
123 		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
124 #endif
125 
126 	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
127 		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
128 	else if (!(pmp->pm_flags &
129 	    (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
130 		struct vnode *rootvp;
131 
132 		/*
133 		 * Try to divine whether to support Win'95 long filenames
134 		 */
135 		if (FAT32(pmp))
136 			pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
137 		else {
138 			if ((error = msdosfs_root(mp, &rootvp)) != 0)
139 				return error;
140 			pmp->pm_flags |= findwin95(VTODE(rootvp))
141 				? MSDOSFSMNT_LONGNAME
142 					: MSDOSFSMNT_SHORTNAME;
143 			vput(rootvp);
144 		}
145 	}
146 	return 0;
147 }
148 
149 #ifndef __FreeBSD__
150 int
151 msdosfs_mountroot()
152 {
153 	register struct mount *mp;
154 	struct proc *p = curproc;	/* XXX */
155 	size_t size;
156 	int error;
157 	struct msdosfs_args args;
158 
159 	if (root_device->dv_class != DV_DISK)
160 		return (ENODEV);
161 
162 	/*
163 	 * Get vnodes for swapdev and rootdev.
164 	 */
165 	if (bdevvp(rootdev, &rootvp))
166 		panic("msdosfs_mountroot: can't setup rootvp");
167 
168 	mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
169 	bzero((char *)mp, (u_long)sizeof(struct mount));
170 	mp->mnt_op = &msdosfs_vfsops;
171 	mp->mnt_flag = 0;
172 	LIST_INIT(&mp->mnt_vnodelist);
173 
174 	args.flags = 0;
175 	args.uid = 0;
176 	args.gid = 0;
177 	args.mask = 0777;
178 
179 	if ((error = mountmsdosfs(rootvp, mp, p, &args)) != 0) {
180 		free(mp, M_MOUNT);
181 		return (error);
182 	}
183 
184 	if ((error = update_mp(mp, &args)) != 0) {
185 		(void)msdosfs_unmount(mp, 0, p);
186 		free(mp, M_MOUNT);
187 		return (error);
188 	}
189 
190 	if ((error = vfs_lock(mp)) != 0) {
191 		(void)msdosfs_unmount(mp, 0, p);
192 		free(mp, M_MOUNT);
193 		return (error);
194 	}
195 
196 	CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
197 	mp->mnt_vnodecovered = NULLVP;
198 	(void) copystr("/", mp->mnt_stat.f_mntonname, MNAMELEN - 1,
199 	    &size);
200 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
201 	(void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
202 	    &size);
203 	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
204 	(void)msdosfs_statfs(mp, &mp->mnt_stat, p);
205 	vfs_unlock(mp);
206 	return (0);
207 }
208 #endif
209 
210 /*
211  * mp - path - addr in user space of mount point (ie /usr or whatever)
212  * data - addr in user space of mount params including the name of the block
213  * special file to treat as a filesystem.
214  */
215 static int
216 msdosfs_mount(mp, path, data, ndp, p)
217 	struct mount *mp;
218 	char *path;
219 	caddr_t data;
220 	struct nameidata *ndp;
221 	struct proc *p;
222 {
223 	struct vnode *devvp;	  /* vnode for blk device to mount */
224 	struct msdosfs_args args; /* will hold data from mount request */
225 	/* msdosfs specific mount control block */
226 	struct msdosfsmount *pmp = NULL;
227 	size_t size;
228 	int error, flags;
229 	mode_t accessmode;
230 
231 	error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args));
232 	if (error)
233 		return (error);
234 	if (args.magic != MSDOSFS_ARGSMAGIC)
235 		args.flags = 0;
236 	/*
237 	 * If updating, check whether changing from read-only to
238 	 * read/write; if there is no device name, that's all we do.
239 	 */
240 	if (mp->mnt_flag & MNT_UPDATE) {
241 		pmp = VFSTOMSDOSFS(mp);
242 		error = 0;
243 		if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) {
244 			flags = WRITECLOSE;
245 			if (mp->mnt_flag & MNT_FORCE)
246 				flags |= FORCECLOSE;
247 			error = vflush(mp, NULLVP, flags);
248 		}
249 		if (!error && (mp->mnt_flag & MNT_RELOAD))
250 			/* not yet implemented */
251 			error = EOPNOTSUPP;
252 		if (error)
253 			return (error);
254 		if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
255 			/*
256 			 * If upgrade to read-write by non-root, then verify
257 			 * that user has necessary permissions on the device.
258 			 */
259 			if (p->p_ucred->cr_uid != 0) {
260 				devvp = pmp->pm_devvp;
261 				vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
262 				error = VOP_ACCESS(devvp, VREAD | VWRITE,
263 						   p->p_ucred, p);
264 				if (error) {
265 					VOP_UNLOCK(devvp, 0, p);
266 					return (error);
267 				}
268 				VOP_UNLOCK(devvp, 0, p);
269 			}
270 			pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
271 		}
272 		if (args.fspec == 0) {
273 #ifdef	__notyet__		/* doesn't work correctly with current mountd	XXX */
274 			if (args.flags & MSDOSFSMNT_MNTOPT) {
275 				pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT;
276 				pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT;
277 				if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
278 					pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
279 			}
280 #endif
281 			/*
282 			 * Process export requests.
283 			 */
284 			return (vfs_export(mp, &pmp->pm_export, &args.export));
285 		}
286 	}
287 	/*
288 	 * Not an update, or updating the name: look up the name
289 	 * and verify that it refers to a sensible block device.
290 	 */
291 	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
292 	error = namei(ndp);
293 	if (error)
294 		return (error);
295 	devvp = ndp->ni_vp;
296 
297 	if (devvp->v_type != VBLK) {
298 		vrele(devvp);
299 		return (ENOTBLK);
300 	}
301 	if (major(devvp->v_rdev) >= nblkdev ||
302 	    bdevsw[major(devvp->v_rdev)] == NULL) {
303 		vrele(devvp);
304 		return (ENXIO);
305 	}
306 	/*
307 	 * If mount by non-root, then verify that user has necessary
308 	 * permissions on the device.
309 	 */
310 	if (p->p_ucred->cr_uid != 0) {
311 		accessmode = VREAD;
312 		if ((mp->mnt_flag & MNT_RDONLY) == 0)
313 			accessmode |= VWRITE;
314 		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
315 		error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p);
316 		if (error) {
317 			vput(devvp);
318 			return (error);
319 		}
320 		VOP_UNLOCK(devvp, 0, p);
321 	}
322 	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
323 		error = mountmsdosfs(devvp, mp, p, &args);
324 #ifdef MSDOSFS_DEBUG		/* only needed for the printf below */
325 		pmp = VFSTOMSDOSFS(mp);
326 #endif
327 	} else {
328 		if (devvp != pmp->pm_devvp)
329 			error = EINVAL;	/* XXX needs translation */
330 		else
331 			vrele(devvp);
332 	}
333 	if (error) {
334 		vrele(devvp);
335 		return (error);
336 	}
337 
338 	error = update_mp(mp, &args);
339 	if (error) {
340 		msdosfs_unmount(mp, MNT_FORCE, p);
341 		return error;
342 	}
343 
344 	(void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
345 	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
346 	(void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
347 	    &size);
348 	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
349 	(void) msdosfs_statfs(mp, &mp->mnt_stat, p);
350 #ifdef MSDOSFS_DEBUG
351 	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
352 #endif
353 	return (0);
354 }
355 
356 static int
357 mountmsdosfs(devvp, mp, p, argp)
358 	struct vnode *devvp;
359 	struct mount *mp;
360 	struct proc *p;
361 	struct msdosfs_args *argp;
362 {
363 	struct msdosfsmount *pmp;
364 	struct buf *bp;
365 	dev_t dev = devvp->v_rdev;
366 #ifndef __FreeBSD__
367 	struct partinfo dpart;
368 	int bsize = 0, dtype = 0, tmp;
369 #endif
370 	union bootsector *bsp;
371 	struct byte_bpb33 *b33;
372 	struct byte_bpb50 *b50;
373 	struct byte_bpb710 *b710;
374 	u_int8_t SecPerClust;
375 	int	ronly, error;
376 
377 	/*
378 	 * Disallow multiple mounts of the same device.
379 	 * Disallow mounting of a device that is currently in use
380 	 * (except for root, which might share swap device for miniroot).
381 	 * Flush out any old buffers remaining from a previous use.
382 	 */
383 	error = vfs_mountedon(devvp);
384 	if (error)
385 		return (error);
386 	if (vcount(devvp) > 1 && devvp != rootvp)
387 		return (EBUSY);
388 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
389 	error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0);
390 	VOP_UNLOCK(devvp, 0, p);
391 	if (error)
392 		return (error);
393 
394 	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
395 	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
396 	if (error)
397 		return (error);
398 
399 	bp  = NULL; /* both used in error_exit */
400 	pmp = NULL;
401 
402 #ifndef __FreeBSD__
403 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
404 		/*
405 	 	 * We need the disklabel to calculate the size of a FAT entry
406 		 * later on. Also make sure the partition contains a filesystem
407 		 * of type FS_MSDOS. This doesn't work for floppies, so we have
408 		 * to check for them too.
409 	 	 *
410 	 	 * At least some parts of the msdos fs driver seem to assume
411 		 * that the size of a disk block will always be 512 bytes.
412 		 * Let's check it...
413 		 */
414 		error = VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart,
415 				  FREAD, NOCRED, p);
416 		if (error)
417 			goto error_exit;
418 		tmp   = dpart.part->p_fstype;
419 		dtype = dpart.disklab->d_type;
420 		bsize = dpart.disklab->d_secsize;
421 		if (bsize != 512 || (dtype!=DTYPE_FLOPPY && tmp!=FS_MSDOS)) {
422 			error = EINVAL;
423 			goto error_exit;
424 		}
425 	}
426 #endif
427 
428 	/*
429 	 * Read the boot sector of the filesystem, and then check the
430 	 * boot signature.  If not a dos boot sector then error out.
431 	 */
432 #ifdef	PC98
433 	error = bread(devvp, 0, 1024, NOCRED, &bp);
434 #else
435 	error = bread(devvp, 0, 512, NOCRED, &bp);
436 #endif
437 	if (error)
438 		goto error_exit;
439 	bp->b_flags |= B_AGE;
440 	bsp = (union bootsector *)bp->b_data;
441 	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
442 	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
443 	b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP;
444 
445 #ifndef __FreeBSD__
446 	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
447 #endif
448 #ifdef PC98
449 		if ((bsp->bs50.bsBootSectSig0 != BOOTSIG0
450 		    || bsp->bs50.bsBootSectSig1 != BOOTSIG1)
451 		    && (bsp->bs50.bsBootSectSig0 != 0       /* PC98 DOS 3.3x */
452 		    || bsp->bs50.bsBootSectSig1 != 0)
453 		    && (bsp->bs50.bsBootSectSig0 != 0x90    /* PC98 DOS 5.0  */
454 		    || bsp->bs50.bsBootSectSig1 != 0x3d)
455 		    && (bsp->bs50.bsBootSectSig0 != 0x46    /* PC98 DOS 3.3B */
456 		    || bsp->bs50.bsBootSectSig1 != 0xfa)) {
457 #else
458 		if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
459 		    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
460 #endif
461 			error = EINVAL;
462 			printf("mountmsdosfs(): bad signature\n");
463 			goto error_exit;
464 		}
465 #ifndef __FreeBSD__
466 	}
467 #endif
468 
469 	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
470 	bzero((caddr_t)pmp, sizeof *pmp);
471 	pmp->pm_mountp = mp;
472 
473 	/*
474 	 * Compute several useful quantities from the bpb in the
475 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
476 	 * the fields that are different between dos 5 and dos 3.3.
477 	 */
478 	SecPerClust = b50->bpbSecPerClust;
479 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
480 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
481 	pmp->pm_FATs = b50->bpbFATs;
482 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
483 	pmp->pm_Sectors = getushort(b50->bpbSectors);
484 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
485 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
486 	pmp->pm_Heads = getushort(b50->bpbHeads);
487 	pmp->pm_Media = b50->bpbMedia;
488 
489 #ifndef __FreeBSD__
490 	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
491 #endif
492 		/* XXX - We should probably check more values here */
493 		if (!pmp->pm_BytesPerSec || !SecPerClust
494 			|| !pmp->pm_Heads || pmp->pm_Heads > 255
495 #ifdef PC98
496 	    		|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
497 #else
498 			|| !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
499 #endif
500 			error = EINVAL;
501 			printf("mountmsdosfs(): bad bpb\n");
502 			goto error_exit;
503 		}
504 #ifndef __FreeBSD__
505 	}
506 #endif
507 
508 	if (pmp->pm_Sectors == 0) {
509 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
510 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
511 	} else {
512 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
513 		pmp->pm_HugeSectors = pmp->pm_Sectors;
514 	}
515 	if (pmp->pm_HugeSectors > 0xffffffff /
516 	    (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) {
517 		/*
518 		 * We cannot deal currently with this size of disk
519 		 * due to fileid limitations (see msdosfs_getattr and
520 		 * msdosfs_readdir)
521 		 */
522 		error = EINVAL;
523 		printf("mountmsdosfs(): disk too big, sorry\n");
524 		goto error_exit;
525 	}
526 
527 	if (pmp->pm_RootDirEnts == 0) {
528 		if (bsp->bs710.bsBootSectSig2 != BOOTSIG2
529 		    || bsp->bs710.bsBootSectSig3 != BOOTSIG3
530 		    || pmp->pm_Sectors
531 		    || pmp->pm_FATsecs
532 		    || getushort(b710->bpbFSVers)) {
533 			error = EINVAL;
534 			printf("mountmsdosfs(): bad FAT32 filesystem\n");
535 			goto error_exit;
536 		}
537 		pmp->pm_fatmask = FAT32_MASK;
538 		pmp->pm_fatmult = 4;
539 		pmp->pm_fatdiv = 1;
540 		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
541 		if (getushort(b710->bpbExtFlags) & FATMIRROR)
542 			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
543 		else
544 			pmp->pm_flags |= MSDOSFS_FATMIRROR;
545 	} else
546 		pmp->pm_flags |= MSDOSFS_FATMIRROR;
547 
548 #ifndef __FreeBSD__
549 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
550 		if (FAT32(pmp)) {
551 			/*
552 			 * GEMDOS doesn't know fat32.
553 			 */
554 			error = EINVAL;
555 			goto error_exit;
556 		}
557 
558 		/*
559 		 * Check a few values (could do some more):
560 		 * - logical sector size: power of 2, >= block size
561 		 * - sectors per cluster: power of 2, >= 1
562 		 * - number of sectors:   >= 1, <= size of partition
563 		 */
564 		if ( (SecPerClust == 0)
565 		  || (SecPerClust & (SecPerClust - 1))
566 		  || (pmp->pm_BytesPerSec < bsize)
567 		  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
568 		  || (pmp->pm_HugeSectors == 0)
569 		  || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
570 							> dpart.part->p_size)
571 		   ) {
572 			error = EINVAL;
573 			goto error_exit;
574 		}
575 		/*
576 		 * XXX - Many parts of the msdos fs driver seem to assume that
577 		 * the number of bytes per logical sector (BytesPerSec) will
578 		 * always be the same as the number of bytes per disk block
579 		 * Let's pretend it is.
580 		 */
581 		tmp = pmp->pm_BytesPerSec / bsize;
582 		pmp->pm_BytesPerSec  = bsize;
583 		pmp->pm_HugeSectors *= tmp;
584 		pmp->pm_HiddenSects *= tmp;
585 		pmp->pm_ResSectors  *= tmp;
586 		pmp->pm_Sectors     *= tmp;
587 		pmp->pm_FATsecs     *= tmp;
588 		SecPerClust         *= tmp;
589 	}
590 #endif
591 	pmp->pm_fatblk = pmp->pm_ResSectors;
592 	if (FAT32(pmp)) {
593 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
594 		pmp->pm_firstcluster = pmp->pm_fatblk
595 			+ (pmp->pm_FATs * pmp->pm_FATsecs);
596 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
597 	} else {
598 		pmp->pm_rootdirblk = pmp->pm_fatblk +
599 			(pmp->pm_FATs * pmp->pm_FATsecs);
600 		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
601 				       + pmp->pm_BytesPerSec - 1)
602 			/ pmp->pm_BytesPerSec;/* in sectors */
603 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
604 	}
605 
606 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
607 	    SecPerClust;
608 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
609 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
610 
611 #ifndef __FreeBSD__
612 	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
613 		if ((pmp->pm_nmbrofclusters <= (0xff0 - 2))
614 		      && ((dtype == DTYPE_FLOPPY) || ((dtype == DTYPE_VNODE)
615 		      && ((pmp->pm_Heads == 1) || (pmp->pm_Heads == 2))))
616 		    ) {
617 			pmp->pm_fatmask = FAT12_MASK;
618 			pmp->pm_fatmult = 3;
619 			pmp->pm_fatdiv = 2;
620 		} else {
621 			pmp->pm_fatmask = FAT16_MASK;
622 			pmp->pm_fatmult = 2;
623 			pmp->pm_fatdiv = 1;
624 		}
625 	} else
626 #endif
627 	if (pmp->pm_fatmask == 0) {
628 		if (pmp->pm_maxcluster
629 		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
630 			/*
631 			 * This will usually be a floppy disk. This size makes
632 			 * sure that one fat entry will not be split across
633 			 * multiple blocks.
634 			 */
635 			pmp->pm_fatmask = FAT12_MASK;
636 			pmp->pm_fatmult = 3;
637 			pmp->pm_fatdiv = 2;
638 		} else {
639 			pmp->pm_fatmask = FAT16_MASK;
640 			pmp->pm_fatmult = 2;
641 			pmp->pm_fatdiv = 1;
642 		}
643 	}
644 	if (FAT12(pmp))
645 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
646 	else
647 		pmp->pm_fatblocksize = DFLTBSIZE;
648 
649 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
650 	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
651 
652 	/*
653 	 * Compute mask and shift value for isolating cluster relative byte
654 	 * offsets and cluster numbers from a file offset.
655 	 */
656 	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
657 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
658 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
659 
660 	/*
661 	 * Check for valid cluster size
662 	 * must be a power of 2
663 	 */
664 	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
665 		error = EINVAL;
666 		goto error_exit;
667 	}
668 
669 	/*
670 	 * Release the bootsector buffer.
671 	 */
672 	brelse(bp);
673 	bp = NULL;
674 
675 	/*
676 	 * Check FSInfo.
677 	 */
678 	if (pmp->pm_fsinfo) {
679 		struct fsinfo *fp;
680 
681 		if ((error = bread(devvp, pmp->pm_fsinfo, 1024, NOCRED, &bp)) != 0)
682 			goto error_exit;
683 		fp = (struct fsinfo *)bp->b_data;
684 		if (!bcmp(fp->fsisig1, "RRaA", 4)
685 		    && !bcmp(fp->fsisig2, "rrAa", 4)
686 		    && !bcmp(fp->fsisig3, "\0\0\125\252", 4)
687 		    && !bcmp(fp->fsisig4, "\0\0\125\252", 4))
688 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
689 		else
690 			pmp->pm_fsinfo = 0;
691 		brelse(bp);
692 		bp = NULL;
693 	}
694 
695 	/*
696 	 * Check and validate (or perhaps invalidate?) the fsinfo structure?		XXX
697 	 */
698 
699 	/*
700 	 * Allocate memory for the bitmap of allocated clusters, and then
701 	 * fill it in.
702 	 */
703 	pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
704 				   / N_INUSEBITS)
705 				  * sizeof(*pmp->pm_inusemap),
706 				  M_MSDOSFSFAT, M_WAITOK);
707 
708 	/*
709 	 * fillinusemap() needs pm_devvp.
710 	 */
711 	pmp->pm_dev = dev;
712 	pmp->pm_devvp = devvp;
713 
714 	/*
715 	 * Have the inuse map filled in.
716 	 */
717 	if ((error = fillinusemap(pmp)) != 0)
718 		goto error_exit;
719 
720 	/*
721 	 * If they want fat updates to be synchronous then let them suffer
722 	 * the performance degradation in exchange for the on disk copy of
723 	 * the fat being correct just about all the time.  I suppose this
724 	 * would be a good thing to turn on if the kernel is still flakey.
725 	 */
726 	if (mp->mnt_flag & MNT_SYNCHRONOUS)
727 		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
728 
729 	/*
730 	 * Finish up.
731 	 */
732 	if (ronly)
733 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
734 	else
735 		pmp->pm_fmod = 1;
736 	mp->mnt_data = (qaddr_t) pmp;
737 	mp->mnt_stat.f_fsid.val[0] = (long)dev;
738 	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
739 	mp->mnt_flag |= MNT_LOCAL;
740 	devvp->v_specmountpoint = mp;
741 
742 	return 0;
743 
744 error_exit:
745 	if (bp)
746 		brelse(bp);
747 	(void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, NOCRED, p);
748 	if (pmp) {
749 		if (pmp->pm_inusemap)
750 			free(pmp->pm_inusemap, M_MSDOSFSFAT);
751 		free(pmp, M_MSDOSFSMNT);
752 		mp->mnt_data = (qaddr_t)0;
753 	}
754 	return (error);
755 }
756 
757 static int
758 msdosfs_start(mp, flags, p)
759 	struct mount *mp;
760 	int flags;
761 	struct proc *p;
762 {
763 
764 	return (0);
765 }
766 
767 /*
768  * Unmount the filesystem described by mp.
769  */
770 static int
771 msdosfs_unmount(mp, mntflags, p)
772 	struct mount *mp;
773 	int mntflags;
774 	struct proc *p;
775 {
776 	struct msdosfsmount *pmp;
777 	int error, flags;
778 
779 	flags = 0;
780 	if (mntflags & MNT_FORCE)
781 		flags |= FORCECLOSE;
782 	error = vflush(mp, NULLVP, flags);
783 	if (error)
784 		return error;
785 	pmp = VFSTOMSDOSFS(mp);
786 	pmp->pm_devvp->v_specmountpoint = NULL;
787 #ifdef MSDOSFS_DEBUG
788 	{
789 		struct vnode *vp = pmp->pm_devvp;
790 
791 		printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
792 		printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n",
793 		    vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
794 		printf("lastr %d, id %lu, mount %p, op %p\n",
795 		    vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op);
796 		printf("freef %p, freeb %p, mount %p\n",
797 		    vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev,
798 		    vp->v_mount);
799 		printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n",
800 		    TAILQ_FIRST(&vp->v_cleanblkhd),
801 		    TAILQ_FIRST(&vp->v_dirtyblkhd),
802 		    vp->v_numoutput, vp->v_type);
803 		printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
804 		    vp->v_socket, vp->v_tag,
805 		    ((u_int *)vp->v_data)[0],
806 		    ((u_int *)vp->v_data)[1]);
807 	}
808 #endif
809 	error = VOP_CLOSE(pmp->pm_devvp,
810 		    (pmp->pm_flags&MSDOSFSMNT_RONLY) ? FREAD : FREAD | FWRITE,
811 		    NOCRED, p);
812 	vrele(pmp->pm_devvp);
813 	free(pmp->pm_inusemap, M_MSDOSFSFAT);
814 	free(pmp, M_MSDOSFSMNT);
815 	mp->mnt_data = (qaddr_t)0;
816 	mp->mnt_flag &= ~MNT_LOCAL;
817 	return (error);
818 }
819 
820 static int
821 msdosfs_root(mp, vpp)
822 	struct mount *mp;
823 	struct vnode **vpp;
824 {
825 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
826 	struct denode *ndep;
827 	int error;
828 
829 #ifdef MSDOSFS_DEBUG
830 	printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
831 #endif
832 	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep);
833 	if (error)
834 		return (error);
835 	*vpp = DETOV(ndep);
836 	return (0);
837 }
838 
839 static int
840 msdosfs_quotactl(mp, cmds, uid, arg, p)
841 	struct mount *mp;
842 	int cmds;
843 	uid_t uid;
844 	caddr_t arg;
845 	struct proc *p;
846 {
847 	return EOPNOTSUPP;
848 }
849 
850 static int
851 msdosfs_statfs(mp, sbp, p)
852 	struct mount *mp;
853 	struct statfs *sbp;
854 	struct proc *p;
855 {
856 	struct msdosfsmount *pmp;
857 
858 	pmp = VFSTOMSDOSFS(mp);
859 	sbp->f_bsize = pmp->pm_bpcluster;
860 	sbp->f_iosize = pmp->pm_bpcluster;
861 	sbp->f_blocks = pmp->pm_nmbrofclusters;
862 	sbp->f_bfree = pmp->pm_freeclustercount;
863 	sbp->f_bavail = pmp->pm_freeclustercount;
864 	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
865 	sbp->f_ffree = 0;	/* what to put in here? */
866 	if (sbp != &mp->mnt_stat) {
867 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
868 		bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
869 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
870 	}
871 	strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN);
872 	return (0);
873 }
874 
875 static int
876 msdosfs_sync(mp, waitfor, cred, p)
877 	struct mount *mp;
878 	int waitfor;
879 	struct ucred *cred;
880 	struct proc *p;
881 {
882 	struct vnode *vp, *nvp;
883 	struct denode *dep;
884 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
885 	int error, allerror = 0;
886 
887 	/*
888 	 * If we ever switch to not updating all of the fats all the time,
889 	 * this would be the place to update them from the first one.
890 	 */
891 	if (pmp->pm_fmod != 0)
892 		if (pmp->pm_flags & MSDOSFSMNT_RONLY)
893 			panic("msdosfs_sync: rofs mod");
894 		else {
895 			/* update fats here */
896 		}
897 	/*
898 	 * Write back each (modified) denode.
899 	 */
900 	simple_lock(&mntvnode_slock);
901 loop:
902 	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
903 		/*
904 		 * If the vnode that we are about to sync is no longer
905 		 * associated with this mount point, start over.
906 		 */
907 		if (vp->v_mount != mp)
908 			goto loop;
909 
910 		simple_lock(&vp->v_interlock);
911 		nvp = vp->v_mntvnodes.le_next;
912 		dep = VTODE(vp);
913 		if (vp->v_type == VNON ||
914 		    (dep->de_flag &
915 		    (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 &&
916 		    (TAILQ_EMPTY(&vp->v_dirtyblkhd) || waitfor == MNT_LAZY)) {
917 			simple_unlock(&vp->v_interlock);
918 			continue;
919 		}
920 		simple_unlock(&mntvnode_slock);
921 		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
922 		if (error) {
923 			simple_lock(&mntvnode_slock);
924 			if (error == ENOENT)
925 				goto loop;
926 			continue;
927 		}
928 		error = VOP_FSYNC(vp, cred, waitfor, p);
929 		if (error)
930 			allerror = error;
931 		VOP_UNLOCK(vp, 0, p);
932 		vrele(vp);
933 		simple_lock(&mntvnode_slock);
934 	}
935 	simple_unlock(&mntvnode_slock);
936 
937 	/*
938 	 * Flush filesystem control info.
939 	 */
940 	if (waitfor != MNT_LAZY) {
941 		vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, p);
942 		error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p);
943 		if (error)
944 			allerror = error;
945 		VOP_UNLOCK(pmp->pm_devvp, 0, p);
946 	}
947 	return (allerror);
948 }
949 
950 static int
951 msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
952 	struct mount *mp;
953 	struct fid *fhp;
954 	struct sockaddr *nam;
955 	struct vnode **vpp;
956 	int *exflagsp;
957 	struct ucred **credanonp;
958 {
959 	struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
960 	struct defid *defhp = (struct defid *) fhp;
961 	struct denode *dep;
962 	struct netcred *np;
963 	int error;
964 
965 	np = vfs_export_lookup(mp, &pmp->pm_export, nam);
966 	if (np == NULL)
967 		return (EACCES);
968 	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep);
969 	if (error) {
970 		*vpp = NULLVP;
971 		return (error);
972 	}
973 	*vpp = DETOV(dep);
974 	*exflagsp = np->netc_exflags;
975 	*credanonp = &np->netc_anon;
976 	return (0);
977 }
978 
979 static int
980 msdosfs_vptofh(vp, fhp)
981 	struct vnode *vp;
982 	struct fid *fhp;
983 {
984 	struct denode *dep;
985 	struct defid *defhp;
986 
987 	dep = VTODE(vp);
988 	defhp = (struct defid *)fhp;
989 	defhp->defid_len = sizeof(struct defid);
990 	defhp->defid_dirclust = dep->de_dirclust;
991 	defhp->defid_dirofs = dep->de_diroffset;
992 	/* defhp->defid_gen = dep->de_gen; */
993 	return (0);
994 }
995 
996 static int
997 msdosfs_vget(mp, ino, vpp)
998 	struct mount *mp;
999 	ino_t ino;
1000 	struct vnode **vpp;
1001 {
1002 	return EOPNOTSUPP;
1003 }
1004 
1005 static struct vfsops msdosfs_vfsops = {
1006 	msdosfs_mount,
1007 	msdosfs_start,
1008 	msdosfs_unmount,
1009 	msdosfs_root,
1010 	msdosfs_quotactl,
1011 	msdosfs_statfs,
1012 	msdosfs_sync,
1013 	msdosfs_vget,
1014 	msdosfs_fhtovp,
1015 	msdosfs_vptofh,
1016 	msdosfs_init
1017 };
1018 
1019 VFS_SET(msdosfs_vfsops, msdos, 0);
1020