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