xref: /freebsd/sys/fs/msdosfs/msdosfs_vfsops.c (revision 952d112864d8008aa87278a30a539d888a8493cd)
1 /*	$Id: msdosfs_vfsops.c,v 1.18 1997/03/18 19:50:10 peter Exp $ */
2 /*	$NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $	*/
3 
4 /*-
5  * Copyright (C) 1994 Wolfgang Solfrank.
6  * Copyright (C) 1994 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/namei.h>
54 #include <sys/proc.h>
55 #include <sys/kernel.h>
56 #include <sys/vnode.h>
57 #include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
58 #include <sys/mount.h>
59 #include <sys/buf.h>
60 #include <sys/fcntl.h>
61 #include <sys/malloc.h>
62 
63 #include <msdosfs/bpb.h>
64 #include <msdosfs/bootsect.h>
65 #include <msdosfs/direntry.h>
66 #include <msdosfs/denode.h>
67 #include <msdosfs/msdosfsmount.h>
68 #include <msdosfs/fat.h>
69 
70 static int	mountmsdosfs __P((struct vnode *devvp, struct mount *mp,
71 				  struct proc *p));
72 static int	msdosfs_fhtovp __P((struct mount *, struct fid *,
73 				    struct mbuf *, struct vnode **, int *,
74 				    struct ucred **));
75 static int	msdosfs_mount __P((struct mount *, char *, caddr_t,
76 				   struct nameidata *, struct proc *));
77 static int	msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t,
78 				      struct proc *));
79 static int	msdosfs_root __P((struct mount *, struct vnode **));
80 static int	msdosfs_start __P((struct mount *, int, struct proc *));
81 static int	msdosfs_statfs __P((struct mount *, struct statfs *,
82 				    struct proc *));
83 static int	msdosfs_sync __P((struct mount *, int, struct ucred *,
84 				  struct proc *));
85 static int	msdosfs_unmount __P((struct mount *, int, struct proc *));
86 static int	msdosfs_vget __P((struct mount *mp, ino_t ino,
87 				  struct vnode **vpp));
88 static int	msdosfs_vptofh __P((struct vnode *, struct fid *));
89 
90 /*
91  * mp - path - addr in user space of mount point (ie /usr or whatever)
92  * data - addr in user space of mount params including the name of the block
93  * special file to treat as a filesystem.
94  */
95 static int
96 msdosfs_mount(mp, path, data, ndp, p)
97 	struct mount *mp;
98 	char *path;
99 	caddr_t data;
100 	struct nameidata *ndp;
101 	struct proc *p;
102 {
103 	struct vnode *devvp;	  /* vnode for blk device to mount */
104 	struct msdosfs_args args; /* will hold data from mount request */
105 	struct msdosfsmount *pmp; /* msdosfs specific mount control block */
106 	int error, flags;
107 	u_int size;
108 	struct ucred *cred, *scred;
109 	struct vattr va;
110 
111 	/*
112 	 * Copy in the args for the mount request.
113 	 */
114 	error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args));
115 	if (error)
116 		return error;
117 
118 	/*
119 	 * If they just want to update then be sure we can do what is
120 	 * asked.  Can't change a filesystem from read/write to read only.
121 	 * Why? And if they've supplied a new device file name then we
122 	 * continue, otherwise return.
123 	 */
124 	if (mp->mnt_flag & MNT_UPDATE) {
125 		pmp = (struct msdosfsmount *) mp->mnt_data;
126 		error = 0;
127 		if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
128 			flags = WRITECLOSE;
129 			if (mp->mnt_flag & MNT_FORCE)
130 				flags |= FORCECLOSE;
131 			error = vflush(mp, NULLVP, flags);
132 		}
133 		if (!error && (mp->mnt_flag & MNT_RELOAD))
134 			/* not yet implemented */
135 			error = EINVAL;
136 		if (error)
137 			return error;
138 		if (pmp->pm_ronly && (mp->mnt_flag & MNT_WANTRDWR))
139 			pmp->pm_ronly = 0;
140 		if (args.fspec == 0) {
141 			/*
142 			 * Process export requests.
143 			 */
144 			return vfs_export(mp, &pmp->pm_export, &args.export);
145 		}
146 	} else
147 		pmp = NULL;
148 
149 	/*
150 	 * check to see that the user in owns the target directory.
151 	 * Note the very XXX trick to make sure we're checking as the
152 	 * real user -- were mount() executable by anyone, this wouldn't
153 	 * be a problem.
154 	 *
155 	 * XXX there should be one consistent error out.
156 	 */
157 	cred = crdup(p->p_ucred);			/* XXX */
158 	cred->cr_uid = p->p_cred->p_ruid;		/* XXX */
159 	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
160 	if (error) {
161 		crfree(cred);				/* XXX */
162 		return error;
163 	}
164 	if (cred->cr_uid != 0) {
165 		if (va.va_uid != cred->cr_uid) {
166 			error = EACCES;
167 			crfree(cred);			/* XXX */
168 			return error;
169 		}
170 
171 		/* a user mounted it; we'll verify permissions when unmounting */
172 		mp->mnt_flag |= MNT_USER;
173 	}
174 
175 	/*
176 	 * Now, lookup the name of the block device this mount or name
177 	 * update request is to apply to.
178 	 */
179 	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
180 	scred = p->p_ucred;				/* XXX */
181 	p->p_ucred = cred;				/* XXX */
182 	error = namei(ndp);
183 	p->p_ucred = scred;				/* XXX */
184 	crfree(cred);					/* XXX */
185 	if (error != 0)
186 		return error;
187 
188 	/*
189 	 * Be sure they've given us a block device to treat as a
190 	 * filesystem.  And, that its major number is within the bdevsw
191 	 * table.
192 	 */
193 	devvp = ndp->ni_vp;
194 	if (devvp->v_type != VBLK) {
195 		vrele(devvp);
196 		return ENOTBLK;
197 	}
198 	if (major(devvp->v_rdev) >= nblkdev) {
199 		vrele(devvp);
200 		return ENXIO;
201 	}
202 
203 	/*
204 	 * If this is an update, then make sure the vnode for the block
205 	 * special device is the same as the one our filesystem is in.
206 	 */
207 	if (mp->mnt_flag & MNT_UPDATE) {
208 		if (devvp != pmp->pm_devvp)
209 			error = EINVAL;
210 		else
211 			vrele(devvp);
212 	} else {
213 
214 		/*
215 		 * Well, it's not an update, it's a real mount request.
216 		 * Time to get dirty.
217 		 */
218 		error = mountmsdosfs(devvp, mp, p);
219 	}
220 	if (error) {
221 		vrele(devvp);
222 		return error;
223 	}
224 
225 	/*
226 	 * Copy in the name of the directory the filesystem is to be
227 	 * mounted on. Then copy in the name of the block special file
228 	 * representing the filesystem being mounted. And we clear the
229 	 * remainder of the character strings to be tidy. Set up the
230 	 * user id/group id/mask as specified by the user. Then, we try to
231 	 * fill in the filesystem stats structure as best we can with
232 	 * whatever applies from a dos file system.
233 	 */
234 	pmp = (struct msdosfsmount *) mp->mnt_data;
235 	copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname,
236 	    sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
237 	bzero(mp->mnt_stat.f_mntonname + size,
238 	    sizeof(mp->mnt_stat.f_mntonname) - size);
239 	copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
240 	bzero(mp->mnt_stat.f_mntfromname + size,
241 	    MNAMELEN - size);
242 	pmp->pm_mounter = p->p_cred->p_ruid;
243 	pmp->pm_gid = args.gid;
244 	pmp->pm_uid = args.uid;
245 	pmp->pm_mask = args.mask;
246 	(void) msdosfs_statfs(mp, &mp->mnt_stat, p);
247 #ifdef MSDOSFS_DEBUG
248 	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
249 #endif
250 	return 0;
251 }
252 
253 static int
254 mountmsdosfs(devvp, mp, p)
255 	struct vnode *devvp;
256 	struct mount *mp;
257 	struct proc *p;
258 {
259 	int i;
260 	int bpc;
261 	int bit;
262 	int error;
263 	int needclose;
264 	int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
265 	dev_t dev = devvp->v_rdev;
266 	union bootsector *bsp;
267 	struct msdosfsmount *pmp = NULL;
268 	struct buf *bp0 = NULL;
269 	struct byte_bpb33 *b33;
270 	struct byte_bpb50 *b50;
271 #ifdef	PC98
272 	u_int	pc98_wrk;
273 	u_int	Phy_Sector_Size;
274 #endif
275 
276 	/*
277 	 * Multiple mounts of the same block special file aren't allowed.
278 	 * Make sure no one else has the special file open.  And flush any
279 	 * old buffers from this filesystem.  Presumably this prevents us
280 	 * from running into buffers that are the wrong blocksize.
281 	 */
282 	error = vfs_mountedon(devvp);
283 	if (error)
284 		return error;
285 	if (vcount(devvp) > 1)
286 		return EBUSY;
287 	error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0);
288 	if (error)
289 		return error;
290 
291 	/*
292 	 * Now open the block special file.
293 	 */
294 	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p);
295 	if (error)
296 		return error;
297 	needclose = 1;
298 #ifdef HDSUPPORT
299 	/*
300 	 * Put this in when we support reading dos filesystems from
301 	 * partitioned harddisks.
302 	 */
303 	if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) {
304 	}
305 #endif
306 
307 	/*
308 	 * Read the boot sector of the filesystem, and then check the boot
309 	 * signature.  If not a dos boot sector then error out.  We could
310 	 * also add some checking on the bsOemName field.  So far I've seen
311 	 * the following values: "IBM  3.3" "MSDOS3.3" "MSDOS5.0"
312 	 */
313 #ifdef	PC98
314 	devvp->v_flag &= 0xffff;
315 	error = bread(devvp, 0, 1024, NOCRED, &bp0);
316 #else
317 	error = bread(devvp, 0, 512, NOCRED, &bp0);
318 #endif
319 	if (error)
320 		goto error_exit;
321 	bp0->b_flags |= B_AGE;
322 	bsp = (union bootsector *) bp0->b_data;
323 	b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB;
324 	b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB;
325 #ifdef MSDOSFS_CHECKSIG
326 #ifdef	PC98
327 	if (bsp->bs50.bsBootSectSig != BOOTSIG &&
328 	    bsp->bs50.bsBootSectSig != 0 &&		/* PC98 DOS 3.3x */
329 	    bsp->bs50.bsBootSectSig != 15760 &&		/* PC98 DOS 5.0	 */
330 	    bsp->bs50.bsBootSectSig != 64070) {		/* PC98 DOS 3.3B */
331 #else
332 	if (bsp->bs50.bsBootSectSig != BOOTSIG) {
333 #endif
334 		error = EINVAL;
335 		goto error_exit;
336 	}
337 #endif
338 	if ( bsp->bs50.bsJump[0] != 0xe9 &&
339 	    (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) {
340 		error = EINVAL;
341 		goto error_exit;
342 	}
343 
344 	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
345 	bzero((caddr_t)pmp, sizeof *pmp);
346 	pmp->pm_mountp = mp;
347 
348 	/*
349 	 * Compute several useful quantities from the bpb in the
350 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
351 	 * the fields that are different between dos 5 and dos 3.3.
352 	 */
353 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
354 	pmp->pm_SectPerClust = b50->bpbSecPerClust;
355 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
356 	pmp->pm_FATs = b50->bpbFATs;
357 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
358 	pmp->pm_Sectors = getushort(b50->bpbSectors);
359 	pmp->pm_Media = b50->bpbMedia;
360 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
361 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
362 	pmp->pm_Heads = getushort(b50->bpbHeads);
363 
364 	/* XXX - We should probably check more values here */
365     	if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust ||
366 	    !pmp->pm_Heads || pmp->pm_Heads > 255 ||
367 #ifdef PC98
368 	    !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
369 #else
370 	    !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
371 #endif
372 		error = EINVAL;
373 		goto error_exit;
374 	}
375 
376 	if (pmp->pm_Sectors == 0) {
377 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
378 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
379 	} else {
380 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
381 		pmp->pm_HugeSectors = pmp->pm_Sectors;
382 	}
383 #ifdef	PC98	/* for PC98		added Satoshi Yasuda	*/
384 	Phy_Sector_Size = 512;
385 	if ((devvp->v_rdev>>8) == 2) {	/* floppy check */
386 		if (((devvp->v_rdev&077) == 2) && (pmp->pm_HugeSectors == 1232)) {
387 				Phy_Sector_Size = 1024;	/* 2HD */
388 				/*
389 				 * 1024byte/sector support
390 				 */
391 				devvp->v_flag |= 0x10000;
392 		} else {
393 			if ((((devvp->v_rdev&077) == 3)	/* 2DD 8 or 9 sector */
394 				&& (pmp->pm_HugeSectors == 1440)) /* 9 sector */
395 				|| (((devvp->v_rdev&077) == 4)
396 				&& (pmp->pm_HugeSectors == 1280)) /* 8 sector */
397 				|| (((devvp->v_rdev&077) == 5)
398 				&& (pmp->pm_HugeSectors == 2880))) { /* 1.44M */
399 					Phy_Sector_Size = 512;
400 			} else {
401 				if (((devvp->v_rdev&077) != 1)
402 				    && ((devvp->v_rdev&077) != 0)) { /* 2HC */
403 					error = EINVAL;
404 					goto error_exit;
405 				}
406 			}
407 		}
408 	}
409 	pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size;
410 	pmp->pm_BytesPerSec = Phy_Sector_Size;
411 	pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk;
412 	pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk;
413 	pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk;
414 	pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk;
415 	pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk;
416 	pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk;
417 #endif			/*						*/
418 	pmp->pm_fatblk = pmp->pm_ResSectors;
419 	pmp->pm_rootdirblk = pmp->pm_fatblk +
420 	    (pmp->pm_FATs * pmp->pm_FATsecs);
421 	pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
422 	    /
423 	    pmp->pm_BytesPerSec;/* in sectors */
424 	pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
425 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
426 	    pmp->pm_SectPerClust;
427 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
428 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
429 	if (FAT12(pmp))
430 		/*
431 		 * This will usually be a floppy disk. This size makes sure
432 		 * that one fat entry will not be split across multiple
433 		 * blocks.
434 		 */
435 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
436 	else
437 		/*
438 		 * This will usually be a hard disk. Reading or writing one
439 		 * block should be quite fast.
440 		 */
441 		pmp->pm_fatblocksize = MAXBSIZE;
442 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
443 
444 
445 	if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
446 		printf("mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length\n");
447 
448 	/*
449 	 * Compute mask and shift value for isolating cluster relative byte
450 	 * offsets and cluster numbers from a file offset.
451 	 */
452 	bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
453 	pmp->pm_bpcluster = bpc;
454 	pmp->pm_depclust = bpc / sizeof(struct direntry);
455 	pmp->pm_crbomask = bpc - 1;
456 	if (bpc == 0) {
457 		error = EINVAL;
458 		goto error_exit;
459 	}
460 	bit = 1;
461 	for (i = 0; i < 32; i++) {
462 		if (bit & bpc) {
463 			if (bit ^ bpc) {
464 				error = EINVAL;
465 				goto error_exit;
466 			}
467 			pmp->pm_cnshift = i;
468 			break;
469 		}
470 		bit <<= 1;
471 	}
472 
473 #ifdef	PC98
474 	if (Phy_Sector_Size == 512) {
475 		pmp->pm_brbomask = 0x01ff;	/* 512 byte blocks only (so far) */
476 		pmp->pm_bnshift = 9;	/* shift right 9 bits to get bn */
477 	} else {
478 		pmp->pm_brbomask = 0x03ff;
479 		pmp->pm_bnshift = 10;
480 	}
481 #else
482 	pmp->pm_brbomask = 0x01ff;	/* 512 byte blocks only (so far) */
483 	pmp->pm_bnshift = 9;	/* shift right 9 bits to get bn */
484 #endif
485 
486 	/*
487 	 * Release the bootsector buffer.
488 	 */
489 	brelse(bp0);
490 	bp0 = NULL;
491 
492 	/*
493 	 * Allocate memory for the bitmap of allocated clusters, and then
494 	 * fill it in.
495 	 */
496 	pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
497 				   / N_INUSEBITS)
498 				  * sizeof(*pmp->pm_inusemap),
499 				  M_MSDOSFSFAT, M_WAITOK);
500 
501 	/*
502 	 * fillinusemap() needs pm_devvp.
503 	 */
504 	pmp->pm_dev = dev;
505 	pmp->pm_devvp = devvp;
506 
507 	/*
508 	 * Have the inuse map filled in.
509 	 */
510 	error = fillinusemap(pmp);
511 	if (error)
512 		goto error_exit;
513 
514 	/*
515 	 * If they want fat updates to be synchronous then let them suffer
516 	 * the performance degradation in exchange for the on disk copy of
517 	 * the fat being correct just about all the time.  I suppose this
518 	 * would be a good thing to turn on if the kernel is still flakey.
519 	 */
520 	pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
521 
522 	/*
523 	 * Finish up.
524 	 */
525 	pmp->pm_ronly = ronly;
526 	if (ronly == 0)
527 		pmp->pm_fmod = 1;
528 	mp->mnt_data = (qaddr_t) pmp;
529 	mp->mnt_stat.f_fsid.val[0] = (long)dev;
530 	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
531 	mp->mnt_flag |= MNT_LOCAL;
532 	devvp->v_specflags |= SI_MOUNTEDON;
533 
534 	return 0;
535 
536 error_exit:;
537 	if (bp0)
538 		brelse(bp0);
539 	if (needclose)
540 		(void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE,
541 		    NOCRED, p);
542 	if (pmp) {
543 		if (pmp->pm_inusemap)
544 			free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
545 		free((caddr_t) pmp, M_MSDOSFSMNT);
546 		mp->mnt_data = (qaddr_t) 0;
547 	}
548 	return error;
549 }
550 
551 static int
552 msdosfs_start(mp, flags, p)
553 	struct mount *mp;
554 	int flags;
555 	struct proc *p;
556 {
557 	return 0;
558 }
559 
560 /*
561  * Unmount the filesystem described by mp.
562  */
563 static int
564 msdosfs_unmount(mp, mntflags, p)
565 	struct mount *mp;
566 	int mntflags;
567 	struct proc *p;
568 {
569 	int flags = 0;
570 	int error;
571 	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
572 
573 	/* only the mounter, or superuser can unmount */
574 	if ((p->p_cred->p_ruid != pmp->pm_mounter) &&
575 	    (error = suser(p->p_ucred, &p->p_acflag)))
576 		return error;
577 
578 	if (mntflags & MNT_FORCE) {
579 		flags |= FORCECLOSE;
580 	}
581 	error = vflush(mp, NULLVP, flags);
582 	if (error)
583 		return error;
584 	pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
585 	error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE,
586 	    NOCRED, p);
587 	vrele(pmp->pm_devvp);
588 	free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
589 	free((caddr_t) pmp, M_MSDOSFSMNT);
590 	mp->mnt_data = (qaddr_t) 0;
591 	mp->mnt_flag &= ~MNT_LOCAL;
592 	return error;
593 }
594 
595 static int
596 msdosfs_root(mp, vpp)
597 	struct mount *mp;
598 	struct vnode **vpp;
599 {
600 	struct denode *ndep;
601 	struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data);
602 	int error;
603 
604 	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep);
605 #ifdef MSDOSFS_DEBUG
606 	printf("msdosfs_root(); mp %p, pmp %p, ndep %p, vp %p\n",
607 	    mp, pmp, ndep, DETOV(ndep));
608 #endif
609 	if (error == 0)
610 		*vpp = DETOV(ndep);
611 	return error;
612 }
613 
614 static int
615 msdosfs_quotactl(mp, cmds, uid, arg, p)
616 	struct mount *mp;
617 	int cmds;
618 	uid_t uid;
619 	caddr_t arg;
620 	struct proc *p;
621 {
622 	return EOPNOTSUPP;
623 }
624 
625 static int
626 msdosfs_statfs(mp, sbp, p)
627 	struct mount *mp;
628 	struct statfs *sbp;
629 	struct proc *p;
630 {
631 	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
632 
633 	/*
634 	 * Fill in the stat block.
635 	 */
636 	sbp->f_bsize = pmp->pm_bpcluster;
637 	sbp->f_iosize = pmp->pm_bpcluster;
638 	sbp->f_blocks = pmp->pm_nmbrofclusters;
639 	sbp->f_bfree = pmp->pm_freeclustercount;
640 	sbp->f_bavail = pmp->pm_freeclustercount;
641 	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
642 	sbp->f_ffree = 0;	/* what to put in here? */
643 
644 	/*
645 	 * Copy the mounted on and mounted from names into the passed in
646 	 * stat block, if it is not the one in the mount structure.
647 	 */
648 	if (sbp != &mp->mnt_stat) {
649 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
650 		bcopy((caddr_t) mp->mnt_stat.f_mntonname,
651 		    (caddr_t) & sbp->f_mntonname[0], MNAMELEN);
652 		bcopy((caddr_t) mp->mnt_stat.f_mntfromname,
653 		    (caddr_t) & sbp->f_mntfromname[0], MNAMELEN);
654 	}
655 #if 0
656 	strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN);
657 	sbp->f_fstypename[MFSNAMELEN] = '\0';
658 #endif
659 	return 0;
660 }
661 
662 static int
663 msdosfs_sync(mp, waitfor, cred, p)
664 	struct mount *mp;
665 	int waitfor;
666 	struct ucred *cred;
667 	struct proc *p;
668 {
669 	struct vnode *vp;
670 	struct denode *dep;
671 	struct msdosfsmount *pmp;
672 	int error;
673 	int allerror = 0;
674 
675 	pmp = (struct msdosfsmount *) mp->mnt_data;
676 
677 	/*
678 	 * If we ever switch to not updating all of the fats all the time,
679 	 * this would be the place to update them from the first one.
680 	 */
681 	if (pmp->pm_fmod)
682 		if (pmp->pm_ronly)
683 			panic("msdosfs_sync: rofs mod");
684 		else {
685 			/* update fats here */
686 		}
687 
688 	/*
689 	 * Go thru in memory denodes and write them out along with
690 	 * unwritten file blocks.
691 	 */
692 	simple_lock(&mntvnode_slock);
693 loop:
694 	for (vp = mp->mnt_vnodelist.lh_first; vp;
695 	    vp = vp->v_mntvnodes.le_next) {
696 		if (vp->v_mount != mp)	/* not ours anymore	 */
697 			goto loop;
698 		simple_lock(&vp->v_interlock);
699 		dep = VTODE(vp);
700 		if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 &&
701 		    vp->v_dirtyblkhd.lh_first == NULL) {
702 			simple_unlock(&vp->v_interlock);
703 			continue;
704 		}
705 		simple_unlock(&mntvnode_slock);
706 		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
707 		if (error) {
708 			simple_lock(&mntvnode_slock);
709 			if (error == ENOENT)
710 				goto loop;
711 			continue;
712 		}
713 		error = VOP_FSYNC(vp, cred, waitfor, p);
714 		if (error)
715 			allerror = error;
716 		VOP_UNLOCK(vp, 0, p);
717 		vrele(vp);	/* done with this one	 */
718 		simple_lock(&mntvnode_slock);
719 	}
720 	simple_unlock(&mntvnode_slock);
721 
722 	/*
723 	 * Flush filesystem control info.
724 	 */
725 	error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p);
726 	if (error)
727 		allerror = error;
728 	return allerror;
729 }
730 
731 static int
732 msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
733 	struct mount *mp;
734 	struct fid *fhp;
735 	struct mbuf *nam;
736 	struct vnode **vpp;
737 	int *exflagsp;
738 	struct ucred **credanonp;
739 {
740 	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
741 	struct defid *defhp = (struct defid *) fhp;
742 	struct denode *dep;
743 	struct netcred *np;
744 	int error;
745 
746 	np = vfs_export_lookup(mp, &pmp->pm_export, nam);
747 	if (np == NULL)
748 		return EACCES;
749 	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
750 	    NULL, &dep);
751 	if (error) {
752 		*vpp = NULLVP;
753 		return error;
754 	}
755 	*vpp = DETOV(dep);
756 	*exflagsp = np->netc_exflags;
757 	*credanonp = &np->netc_anon;
758 	return 0;
759 }
760 
761 
762 static int
763 msdosfs_vptofh(vp, fhp)
764 	struct vnode *vp;
765 	struct fid *fhp;
766 {
767 	struct denode *dep = VTODE(vp);
768 	struct defid *defhp = (struct defid *) fhp;
769 
770 	defhp->defid_len = sizeof(struct defid);
771 	defhp->defid_dirclust = dep->de_dirclust;
772 	defhp->defid_dirofs = dep->de_diroffset;
773 	/* defhp->defid_gen = ip->i_gen; */
774 	return 0;
775 }
776 
777 static int
778 msdosfs_vget(mp, ino, vpp)
779 	struct mount *mp;
780 	ino_t ino;
781 	struct vnode **vpp;
782 {
783 	return EOPNOTSUPP;
784 }
785 
786 static struct vfsops msdosfs_vfsops = {
787 	msdosfs_mount,
788 	msdosfs_start,
789 	msdosfs_unmount,
790 	msdosfs_root,
791 	msdosfs_quotactl,
792 	msdosfs_statfs,
793 	msdosfs_sync,
794 	msdosfs_vget,
795 	msdosfs_fhtovp,
796 	msdosfs_vptofh,
797 	msdosfs_init
798 };
799 
800 VFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0);
801