msdosfs_vfsops.c revision 30309
1219820Sjeff/*	$Id: msdosfs_vfsops.c,v 1.20 1997/08/16 19:15:24 wollman Exp $ */
2272027Shselasky/*	$NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $	*/
3219820Sjeff
4219820Sjeff/*-
5219820Sjeff * Copyright (C) 1994 Wolfgang Solfrank.
6219820Sjeff * Copyright (C) 1994 TooLs GmbH.
7219820Sjeff * All rights reserved.
8219820Sjeff * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9219820Sjeff *
10219820Sjeff * Redistribution and use in source and binary forms, with or without
11219820Sjeff * modification, are permitted provided that the following conditions
12219820Sjeff * are met:
13219820Sjeff * 1. Redistributions of source code must retain the above copyright
14219820Sjeff *    notice, this list of conditions and the following disclaimer.
15219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
16219820Sjeff *    notice, this list of conditions and the following disclaimer in the
17219820Sjeff *    documentation and/or other materials provided with the distribution.
18219820Sjeff * 3. All advertising materials mentioning features or use of this software
19219820Sjeff *    must display the following acknowledgement:
20219820Sjeff *	This product includes software developed by TooLs GmbH.
21219820Sjeff * 4. The name of TooLs GmbH may not be used to endorse or promote products
22219820Sjeff *    derived from this software without specific prior written permission.
23219820Sjeff *
24219820Sjeff * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27219820Sjeff * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28219820Sjeff * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29219820Sjeff * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30219820Sjeff * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31219820Sjeff * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32219820Sjeff * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33300676Shselasky * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34300676Shselasky */
35219820Sjeff/*
36219820Sjeff * Written by Paul Popelka (paulp@uts.amdahl.com)
37272027Shselasky *
38272027Shselasky * You can do anything you want with this software, just don't say you wrote
39219820Sjeff * it, and don't remove this notice.
40306486Shselasky *
41255932Salfred * This software is provided "as is".
42219820Sjeff *
43306486Shselasky * The author supplies this software to be publicly redistributed on the
44219820Sjeff * understanding that the author is not responsible for the correct
45272027Shselasky * functioning of this software in any circumstances and is not liable for
46255932Salfred * any damages caused by this software.
47255932Salfred *
48255932Salfred * October 1992
49255932Salfred */
50219820Sjeff
51255932Salfred#include <sys/param.h>
52219820Sjeff#include <sys/systm.h>
53219820Sjeff#include <sys/namei.h>
54219820Sjeff#include <sys/proc.h>
55219820Sjeff#include <sys/kernel.h>
56219820Sjeff#include <sys/vnode.h>
57219820Sjeff#include <miscfs/specfs/specdev.h> /* XXX */	/* defines v_rdev */
58219820Sjeff#include <sys/mount.h>
59219820Sjeff#include <sys/buf.h>
60219820Sjeff#include <sys/fcntl.h>
61219820Sjeff#include <sys/malloc.h>
62219820Sjeff
63219820Sjeff#include <msdosfs/bpb.h>
64219820Sjeff#include <msdosfs/bootsect.h>
65219820Sjeff#include <msdosfs/direntry.h>
66219820Sjeff#include <msdosfs/denode.h>
67219820Sjeff#include <msdosfs/msdosfsmount.h>
68219820Sjeff#include <msdosfs/fat.h>
69219820Sjeff
70219820SjeffMALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table");
71219820Sjeff
72219820Sjeffstatic int	mountmsdosfs __P((struct vnode *devvp, struct mount *mp,
73219820Sjeff				  struct proc *p));
74219820Sjeffstatic int	msdosfs_fhtovp __P((struct mount *, struct fid *,
75219820Sjeff				    struct sockaddr *, struct vnode **, int *,
76219820Sjeff				    struct ucred **));
77219820Sjeffstatic int	msdosfs_mount __P((struct mount *, char *, caddr_t,
78219820Sjeff				   struct nameidata *, struct proc *));
79255932Salfredstatic int	msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t,
80219820Sjeff				      struct proc *));
81219820Sjeffstatic int	msdosfs_root __P((struct mount *, struct vnode **));
82219820Sjeffstatic int	msdosfs_start __P((struct mount *, int, struct proc *));
83255932Salfredstatic int	msdosfs_statfs __P((struct mount *, struct statfs *,
84255932Salfred				    struct proc *));
85255932Salfredstatic int	msdosfs_sync __P((struct mount *, int, struct ucred *,
86255932Salfred				  struct proc *));
87255932Salfredstatic int	msdosfs_unmount __P((struct mount *, int, struct proc *));
88272027Shselaskystatic int	msdosfs_vget __P((struct mount *mp, ino_t ino,
89255932Salfred				  struct vnode **vpp));
90255932Salfredstatic int	msdosfs_vptofh __P((struct vnode *, struct fid *));
91255932Salfred
92255932Salfred/*
93255932Salfred * mp - path - addr in user space of mount point (ie /usr or whatever)
94255932Salfred * data - addr in user space of mount params including the name of the block
95255932Salfred * special file to treat as a filesystem.
96255932Salfred */
97255932Salfredstatic int
98255932Salfredmsdosfs_mount(mp, path, data, ndp, p)
99255932Salfred	struct mount *mp;
100255932Salfred	char *path;
101255932Salfred	caddr_t data;
102255932Salfred	struct nameidata *ndp;
103255932Salfred	struct proc *p;
104255932Salfred{
105255932Salfred	struct vnode *devvp;	  /* vnode for blk device to mount */
106255932Salfred	struct msdosfs_args args; /* will hold data from mount request */
107255932Salfred	struct msdosfsmount *pmp; /* msdosfs specific mount control block */
108255932Salfred	int error, flags;
109219820Sjeff	u_int size;
110219820Sjeff	struct ucred *cred, *scred;
111219820Sjeff	struct vattr va;
112219820Sjeff
113219820Sjeff	/*
114219820Sjeff	 * Copy in the args for the mount request.
115219820Sjeff	 */
116219820Sjeff	error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args));
117219820Sjeff	if (error)
118219820Sjeff		return error;
119219820Sjeff
120219820Sjeff	/*
121219820Sjeff	 * If they just want to update then be sure we can do what is
122219820Sjeff	 * asked.  Can't change a filesystem from read/write to read only.
123255932Salfred	 * Why? And if they've supplied a new device file name then we
124219820Sjeff	 * continue, otherwise return.
125255932Salfred	 */
126219820Sjeff	if (mp->mnt_flag & MNT_UPDATE) {
127219820Sjeff		pmp = (struct msdosfsmount *) mp->mnt_data;
128219820Sjeff		error = 0;
129219820Sjeff		if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
130219820Sjeff			flags = WRITECLOSE;
131255932Salfred			if (mp->mnt_flag & MNT_FORCE)
132219820Sjeff				flags |= FORCECLOSE;
133255932Salfred			error = vflush(mp, NULLVP, flags);
134255932Salfred		}
135219820Sjeff		if (!error && (mp->mnt_flag & MNT_RELOAD))
136219820Sjeff			/* not yet implemented */
137219820Sjeff			error = EINVAL;
138255932Salfred		if (error)
139255932Salfred			return error;
140255932Salfred		if (pmp->pm_ronly && (mp->mnt_flag & MNT_WANTRDWR))
141219820Sjeff			pmp->pm_ronly = 0;
142255932Salfred		if (args.fspec == 0) {
143272027Shselasky			/*
144219820Sjeff			 * Process export requests.
145219820Sjeff			 */
146219820Sjeff			return vfs_export(mp, &pmp->pm_export, &args.export);
147219820Sjeff		}
148272027Shselasky	} else
149272027Shselasky		pmp = NULL;
150255932Salfred
151272027Shselasky	/*
152219820Sjeff	 * check to see that the user in owns the target directory.
153219820Sjeff	 * Note the very XXX trick to make sure we're checking as the
154219820Sjeff	 * real user -- were mount() executable by anyone, this wouldn't
155219820Sjeff	 * be a problem.
156219820Sjeff	 *
157219820Sjeff	 * XXX there should be one consistent error out.
158219820Sjeff	 */
159219820Sjeff	cred = crdup(p->p_ucred);			/* XXX */
160219820Sjeff	cred->cr_uid = p->p_cred->p_ruid;		/* XXX */
161219820Sjeff	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
162219820Sjeff	if (error) {
163219820Sjeff		crfree(cred);				/* XXX */
164219820Sjeff		return error;
165219820Sjeff	}
166219820Sjeff	if (cred->cr_uid != 0) {
167219820Sjeff		if (va.va_uid != cred->cr_uid) {
168219820Sjeff			error = EACCES;
169219820Sjeff			crfree(cred);			/* XXX */
170255932Salfred			return error;
171255932Salfred		}
172219820Sjeff
173219820Sjeff		/* a user mounted it; we'll verify permissions when unmounting */
174219820Sjeff		mp->mnt_flag |= MNT_USER;
175255932Salfred	}
176219820Sjeff
177255932Salfred	/*
178219820Sjeff	 * Now, lookup the name of the block device this mount or name
179219820Sjeff	 * update request is to apply to.
180219820Sjeff	 */
181219820Sjeff	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
182219820Sjeff	scred = p->p_ucred;				/* XXX */
183255932Salfred	p->p_ucred = cred;				/* XXX */
184255932Salfred	error = namei(ndp);
185255932Salfred	p->p_ucred = scred;				/* XXX */
186255932Salfred	crfree(cred);					/* XXX */
187255932Salfred	if (error != 0)
188272027Shselasky		return error;
189255932Salfred
190255932Salfred	/*
191272027Shselasky	 * Be sure they've given us a block device to treat as a
192272027Shselasky	 * filesystem.  And, that its major number is within the bdevsw
193272027Shselasky	 * table.
194272027Shselasky	 */
195272027Shselasky	devvp = ndp->ni_vp;
196272027Shselasky	if (devvp->v_type != VBLK) {
197272027Shselasky		vrele(devvp);
198272027Shselasky		return ENOTBLK;
199272027Shselasky	}
200272027Shselasky	if (major(devvp->v_rdev) >= nblkdev) {
201272027Shselasky		vrele(devvp);
202272027Shselasky		return ENXIO;
203272027Shselasky	}
204272027Shselasky
205272027Shselasky	/*
206255932Salfred	 * If this is an update, then make sure the vnode for the block
207255932Salfred	 * special device is the same as the one our filesystem is in.
208255932Salfred	 */
209255932Salfred	if (mp->mnt_flag & MNT_UPDATE) {
210255932Salfred		if (devvp != pmp->pm_devvp)
211255932Salfred			error = EINVAL;
212255932Salfred		else
213219820Sjeff			vrele(devvp);
214219820Sjeff	} else {
215255932Salfred
216219820Sjeff		/*
217255932Salfred		 * Well, it's not an update, it's a real mount request.
218255932Salfred		 * Time to get dirty.
219255932Salfred		 */
220255932Salfred		error = mountmsdosfs(devvp, mp, p);
221219820Sjeff	}
222255932Salfred	if (error) {
223255932Salfred		vrele(devvp);
224255932Salfred		return error;
225255932Salfred	}
226255932Salfred
227255932Salfred	/*
228255932Salfred	 * Copy in the name of the directory the filesystem is to be
229255932Salfred	 * mounted on. Then copy in the name of the block special file
230255932Salfred	 * representing the filesystem being mounted. And we clear the
231255932Salfred	 * remainder of the character strings to be tidy. Set up the
232255932Salfred	 * user id/group id/mask as specified by the user. Then, we try to
233255932Salfred	 * fill in the filesystem stats structure as best we can with
234219820Sjeff	 * whatever applies from a dos file system.
235255932Salfred	 */
236255932Salfred	pmp = (struct msdosfsmount *) mp->mnt_data;
237255932Salfred	copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname,
238255932Salfred	    sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
239219820Sjeff	bzero(mp->mnt_stat.f_mntonname + size,
240255932Salfred	    sizeof(mp->mnt_stat.f_mntonname) - size);
241219820Sjeff	copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
242255932Salfred	bzero(mp->mnt_stat.f_mntfromname + size,
243255932Salfred	    MNAMELEN - size);
244219820Sjeff	pmp->pm_mounter = p->p_cred->p_ruid;
245219820Sjeff	pmp->pm_gid = args.gid;
246255932Salfred	pmp->pm_uid = args.uid;
247219820Sjeff	pmp->pm_mask = args.mask;
248219820Sjeff	(void) msdosfs_statfs(mp, &mp->mnt_stat, p);
249219820Sjeff#ifdef MSDOSFS_DEBUG
250219820Sjeff	printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
251219820Sjeff#endif
252219820Sjeff	return 0;
253255932Salfred}
254255932Salfred
255255932Salfredstatic int
256255932Salfredmountmsdosfs(devvp, mp, p)
257255932Salfred	struct vnode *devvp;
258255932Salfred	struct mount *mp;
259255932Salfred	struct proc *p;
260272027Shselasky{
261272027Shselasky	int i;
262272027Shselasky	int bpc;
263272027Shselasky	int bit;
264272027Shselasky	int error;
265272027Shselasky	int needclose;
266272027Shselasky	int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
267272027Shselasky	dev_t dev = devvp->v_rdev;
268272027Shselasky	union bootsector *bsp;
269272027Shselasky	struct msdosfsmount *pmp = NULL;
270272027Shselasky	struct buf *bp0 = NULL;
271272027Shselasky	struct byte_bpb33 *b33;
272255932Salfred	struct byte_bpb50 *b50;
273255932Salfred#ifdef	PC98
274255932Salfred	u_int	pc98_wrk;
275255932Salfred	u_int	Phy_Sector_Size;
276255932Salfred#endif
277219820Sjeff
278219820Sjeff	/*
279255932Salfred	 * Multiple mounts of the same block special file aren't allowed.
280255932Salfred	 * Make sure no one else has the special file open.  And flush any
281255932Salfred	 * old buffers from this filesystem.  Presumably this prevents us
282255932Salfred	 * from running into buffers that are the wrong blocksize.
283255932Salfred	 */
284255932Salfred	error = vfs_mountedon(devvp);
285255932Salfred	if (error)
286255932Salfred		return error;
287255932Salfred	if (vcount(devvp) > 1)
288255932Salfred		return EBUSY;
289255932Salfred	error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0);
290255932Salfred	if (error)
291255932Salfred		return error;
292255932Salfred
293255932Salfred	/*
294255932Salfred	 * Now open the block special file.
295255932Salfred	 */
296255932Salfred	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p);
297255932Salfred	if (error)
298255932Salfred		return error;
299255932Salfred	needclose = 1;
300255932Salfred#ifdef HDSUPPORT
301255932Salfred	/*
302255932Salfred	 * Put this in when we support reading dos filesystems from
303255932Salfred	 * partitioned harddisks.
304255932Salfred	 */
305255932Salfred	if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) {
306255932Salfred	}
307219820Sjeff#endif
308219820Sjeff
309219820Sjeff	/*
310219820Sjeff	 * Read the boot sector of the filesystem, and then check the boot
311219820Sjeff	 * signature.  If not a dos boot sector then error out.  We could
312219820Sjeff	 * also add some checking on the bsOemName field.  So far I've seen
313219820Sjeff	 * the following values: "IBM  3.3" "MSDOS3.3" "MSDOS5.0"
314219820Sjeff	 */
315219820Sjeff#ifdef	PC98
316219820Sjeff	devvp->v_flag &= 0xffff;
317219820Sjeff	error = bread(devvp, 0, 1024, NOCRED, &bp0);
318219820Sjeff#else
319219820Sjeff	error = bread(devvp, 0, 512, NOCRED, &bp0);
320219820Sjeff#endif
321255932Salfred	if (error)
322219820Sjeff		goto error_exit;
323219820Sjeff	bp0->b_flags |= B_AGE;
324219820Sjeff	bsp = (union bootsector *) bp0->b_data;
325219820Sjeff	b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB;
326219820Sjeff	b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB;
327219820Sjeff#ifdef MSDOSFS_CHECKSIG
328219820Sjeff#ifdef	PC98
329219820Sjeff	if (bsp->bs50.bsBootSectSig != BOOTSIG &&
330219820Sjeff	    bsp->bs50.bsBootSectSig != 0 &&		/* PC98 DOS 3.3x */
331219820Sjeff	    bsp->bs50.bsBootSectSig != 15760 &&		/* PC98 DOS 5.0	 */
332219820Sjeff	    bsp->bs50.bsBootSectSig != 64070) {		/* PC98 DOS 3.3B */
333219820Sjeff#else
334219820Sjeff	if (bsp->bs50.bsBootSectSig != BOOTSIG) {
335219820Sjeff#endif
336219820Sjeff		error = EINVAL;
337255932Salfred		goto error_exit;
338219820Sjeff	}
339219820Sjeff#endif
340219820Sjeff	if ( bsp->bs50.bsJump[0] != 0xe9 &&
341219820Sjeff	    (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) {
342219820Sjeff		error = EINVAL;
343219820Sjeff		goto error_exit;
344219820Sjeff	}
345219820Sjeff
346219820Sjeff	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
347255932Salfred	bzero((caddr_t)pmp, sizeof *pmp);
348255932Salfred	pmp->pm_mountp = mp;
349219820Sjeff
350219820Sjeff	/*
351219820Sjeff	 * Compute several useful quantities from the bpb in the
352219820Sjeff	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
353219820Sjeff	 * the fields that are different between dos 5 and dos 3.3.
354219820Sjeff	 */
355255932Salfred	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
356255932Salfred	pmp->pm_SectPerClust = b50->bpbSecPerClust;
357255932Salfred	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
358255932Salfred	pmp->pm_FATs = b50->bpbFATs;
359255932Salfred	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
360255932Salfred	pmp->pm_Sectors = getushort(b50->bpbSectors);
361255932Salfred	pmp->pm_Media = b50->bpbMedia;
362219820Sjeff	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
363219820Sjeff	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
364219820Sjeff	pmp->pm_Heads = getushort(b50->bpbHeads);
365219820Sjeff
366219820Sjeff	/* XXX - We should probably check more values here */
367219820Sjeff    	if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust ||
368219820Sjeff	    !pmp->pm_Heads || pmp->pm_Heads > 255 ||
369219820Sjeff#ifdef PC98
370219820Sjeff	    !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
371255932Salfred#else
372219820Sjeff	    !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
373219820Sjeff#endif
374219820Sjeff		error = EINVAL;
375219820Sjeff		goto error_exit;
376219820Sjeff	}
377219820Sjeff
378219820Sjeff	if (pmp->pm_Sectors == 0) {
379219820Sjeff		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
380219820Sjeff		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
381219820Sjeff	} else {
382219820Sjeff		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
383255932Salfred		pmp->pm_HugeSectors = pmp->pm_Sectors;
384219820Sjeff	}
385219820Sjeff#ifdef	PC98	/* for PC98		added Satoshi Yasuda	*/
386219820Sjeff	Phy_Sector_Size = 512;
387219820Sjeff	if ((devvp->v_rdev>>8) == 2) {	/* floppy check */
388219820Sjeff		if (((devvp->v_rdev&077) == 2) && (pmp->pm_HugeSectors == 1232)) {
389219820Sjeff				Phy_Sector_Size = 1024;	/* 2HD */
390219820Sjeff				/*
391219820Sjeff				 * 1024byte/sector support
392219820Sjeff				 */
393219820Sjeff				devvp->v_flag |= 0x10000;
394219820Sjeff		} else {
395219820Sjeff			if ((((devvp->v_rdev&077) == 3)	/* 2DD 8 or 9 sector */
396219820Sjeff				&& (pmp->pm_HugeSectors == 1440)) /* 9 sector */
397219820Sjeff				|| (((devvp->v_rdev&077) == 4)
398219820Sjeff				&& (pmp->pm_HugeSectors == 1280)) /* 8 sector */
399219820Sjeff				|| (((devvp->v_rdev&077) == 5)
400219820Sjeff				&& (pmp->pm_HugeSectors == 2880))) { /* 1.44M */
401255932Salfred					Phy_Sector_Size = 512;
402255932Salfred			} else {
403255932Salfred				if (((devvp->v_rdev&077) != 1)
404255932Salfred				    && ((devvp->v_rdev&077) != 0)) { /* 2HC */
405255932Salfred					error = EINVAL;
406255932Salfred					goto error_exit;
407255932Salfred				}
408255932Salfred			}
409255932Salfred		}
410255932Salfred	}
411255932Salfred	pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size;
412255932Salfred	pmp->pm_BytesPerSec = Phy_Sector_Size;
413255932Salfred	pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk;
414255932Salfred	pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk;
415255932Salfred	pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk;
416255932Salfred	pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk;
417255932Salfred	pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk;
418255932Salfred	pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk;
419255932Salfred#endif			/*						*/
420255932Salfred	pmp->pm_fatblk = pmp->pm_ResSectors;
421255932Salfred	pmp->pm_rootdirblk = pmp->pm_fatblk +
422219820Sjeff	    (pmp->pm_FATs * pmp->pm_FATsecs);
423219820Sjeff	pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
424255932Salfred	    /
425219820Sjeff	    pmp->pm_BytesPerSec;/* in sectors */
426219820Sjeff	pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
427255932Salfred	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
428219820Sjeff	    pmp->pm_SectPerClust;
429255932Salfred	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
430255932Salfred	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
431255932Salfred	if (FAT12(pmp))
432255932Salfred		/*
433255932Salfred		 * This will usually be a floppy disk. This size makes sure
434255932Salfred		 * that one fat entry will not be split across multiple
435219820Sjeff		 * blocks.
436219820Sjeff		 */
437255932Salfred		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
438219820Sjeff	else
439219820Sjeff		/*
440219820Sjeff		 * This will usually be a hard disk. Reading or writing one
441255932Salfred		 * block should be quite fast.
442255932Salfred		 */
443255932Salfred		pmp->pm_fatblocksize = MAXBSIZE;
444219820Sjeff	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
445219820Sjeff
446219820Sjeff
447219820Sjeff	if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
448219820Sjeff		printf("mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length\n");
449219820Sjeff
450219820Sjeff	/*
451219820Sjeff	 * Compute mask and shift value for isolating cluster relative byte
452255932Salfred	 * offsets and cluster numbers from a file offset.
453255932Salfred	 */
454255932Salfred	bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
455255932Salfred	pmp->pm_bpcluster = bpc;
456255932Salfred	pmp->pm_depclust = bpc / sizeof(struct direntry);
457255932Salfred	pmp->pm_crbomask = bpc - 1;
458255932Salfred	if (bpc == 0) {
459255932Salfred		error = EINVAL;
460255932Salfred		goto error_exit;
461255932Salfred	}
462255932Salfred	bit = 1;
463255932Salfred	for (i = 0; i < 32; i++) {
464255932Salfred		if (bit & bpc) {
465255932Salfred			if (bit ^ bpc) {
466255932Salfred				error = EINVAL;
467219820Sjeff				goto error_exit;
468219820Sjeff			}
469219820Sjeff			pmp->pm_cnshift = i;
470219820Sjeff			break;
471219820Sjeff		}
472219820Sjeff		bit <<= 1;
473219820Sjeff	}
474219820Sjeff
475219820Sjeff#ifdef	PC98
476219820Sjeff	if (Phy_Sector_Size == 512) {
477219820Sjeff		pmp->pm_brbomask = 0x01ff;	/* 512 byte blocks only (so far) */
478219820Sjeff		pmp->pm_bnshift = 9;	/* shift right 9 bits to get bn */
479219820Sjeff	} else {
480219820Sjeff		pmp->pm_brbomask = 0x03ff;
481219820Sjeff		pmp->pm_bnshift = 10;
482219820Sjeff	}
483219820Sjeff#else
484219820Sjeff	pmp->pm_brbomask = 0x01ff;	/* 512 byte blocks only (so far) */
485219820Sjeff	pmp->pm_bnshift = 9;	/* shift right 9 bits to get bn */
486219820Sjeff#endif
487219820Sjeff
488219820Sjeff	/*
489219820Sjeff	 * Release the bootsector buffer.
490219820Sjeff	 */
491219820Sjeff	brelse(bp0);
492219820Sjeff	bp0 = NULL;
493219820Sjeff
494219820Sjeff	/*
495219820Sjeff	 * Allocate memory for the bitmap of allocated clusters, and then
496219820Sjeff	 * fill it in.
497255932Salfred	 */
498255932Salfred	pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
499219820Sjeff				   / N_INUSEBITS)
500219820Sjeff				  * sizeof(*pmp->pm_inusemap),
501219820Sjeff				  M_MSDOSFSFAT, M_WAITOK);
502219820Sjeff
503219820Sjeff	/*
504219820Sjeff	 * fillinusemap() needs pm_devvp.
505255932Salfred	 */
506219820Sjeff	pmp->pm_dev = dev;
507255932Salfred	pmp->pm_devvp = devvp;
508219820Sjeff
509255932Salfred	/*
510255932Salfred	 * Have the inuse map filled in.
511255932Salfred	 */
512255932Salfred	error = fillinusemap(pmp);
513255932Salfred	if (error)
514255932Salfred		goto error_exit;
515255932Salfred
516255932Salfred	/*
517255932Salfred	 * If they want fat updates to be synchronous then let them suffer
518255932Salfred	 * the performance degradation in exchange for the on disk copy of
519255932Salfred	 * the fat being correct just about all the time.  I suppose this
520255932Salfred	 * would be a good thing to turn on if the kernel is still flakey.
521255932Salfred	 */
522255932Salfred	pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
523255932Salfred
524255932Salfred	/*
525255932Salfred	 * Finish up.
526255932Salfred	 */
527255932Salfred	pmp->pm_ronly = ronly;
528255932Salfred	if (ronly == 0)
529255932Salfred		pmp->pm_fmod = 1;
530255932Salfred	mp->mnt_data = (qaddr_t) pmp;
531255932Salfred	mp->mnt_stat.f_fsid.val[0] = (long)dev;
532255932Salfred	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
533255932Salfred	mp->mnt_flag |= MNT_LOCAL;
534255932Salfred	devvp->v_specflags |= SI_MOUNTEDON;
535255932Salfred
536255932Salfred	return 0;
537255932Salfred
538255932Salfrederror_exit:;
539255932Salfred	if (bp0)
540255932Salfred		brelse(bp0);
541255932Salfred	if (needclose)
542255932Salfred		(void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE,
543255932Salfred		    NOCRED, p);
544255932Salfred	if (pmp) {
545255932Salfred		if (pmp->pm_inusemap)
546255932Salfred			free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
547255932Salfred		free((caddr_t) pmp, M_MSDOSFSMNT);
548255932Salfred		mp->mnt_data = (qaddr_t) 0;
549255932Salfred	}
550219820Sjeff	return error;
551255932Salfred}
552255932Salfred
553255932Salfredstatic int
554255932Salfredmsdosfs_start(mp, flags, p)
555255932Salfred	struct mount *mp;
556219820Sjeff	int flags;
557255932Salfred	struct proc *p;
558272027Shselasky{
559255932Salfred	return 0;
560255932Salfred}
561255932Salfred
562272027Shselasky/*
563272027Shselasky * Unmount the filesystem described by mp.
564272027Shselasky */
565255932Salfredstatic int
566255932Salfredmsdosfs_unmount(mp, mntflags, p)
567255932Salfred	struct mount *mp;
568255932Salfred	int mntflags;
569255932Salfred	struct proc *p;
570272027Shselasky{
571272027Shselasky	int flags = 0;
572272027Shselasky	int error;
573255932Salfred	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
574255932Salfred
575255932Salfred	/* only the mounter, or superuser can unmount */
576255932Salfred	if ((p->p_cred->p_ruid != pmp->pm_mounter) &&
577255932Salfred	    (error = suser(p->p_ucred, &p->p_acflag)))
578255932Salfred		return error;
579255932Salfred
580255932Salfred	if (mntflags & MNT_FORCE) {
581255932Salfred		flags |= FORCECLOSE;
582255932Salfred	}
583255932Salfred	error = vflush(mp, NULLVP, flags);
584255932Salfred	if (error)
585255932Salfred		return error;
586255932Salfred	pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
587255932Salfred	error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE,
588255932Salfred	    NOCRED, p);
589255932Salfred	vrele(pmp->pm_devvp);
590255932Salfred	free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
591255932Salfred	free((caddr_t) pmp, M_MSDOSFSMNT);
592255932Salfred	mp->mnt_data = (qaddr_t) 0;
593255932Salfred	mp->mnt_flag &= ~MNT_LOCAL;
594255932Salfred	return error;
595255932Salfred}
596255932Salfred
597272027Shselaskystatic int
598272027Shselaskymsdosfs_root(mp, vpp)
599255932Salfred	struct mount *mp;
600255932Salfred	struct vnode **vpp;
601255932Salfred{
602255932Salfred	struct denode *ndep;
603255932Salfred	struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data);
604255932Salfred	int error;
605255932Salfred
606255932Salfred	error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep);
607255932Salfred#ifdef MSDOSFS_DEBUG
608255932Salfred	printf("msdosfs_root(); mp %p, pmp %p, ndep %p, vp %p\n",
609255932Salfred	    mp, pmp, ndep, DETOV(ndep));
610255932Salfred#endif
611255932Salfred	if (error == 0)
612255932Salfred		*vpp = DETOV(ndep);
613255932Salfred	return error;
614255932Salfred}
615255932Salfred
616255932Salfredstatic int
617255932Salfredmsdosfs_quotactl(mp, cmds, uid, arg, p)
618255932Salfred	struct mount *mp;
619255932Salfred	int cmds;
620255932Salfred	uid_t uid;
621255932Salfred	caddr_t arg;
622255932Salfred	struct proc *p;
623255932Salfred{
624255932Salfred	return EOPNOTSUPP;
625255932Salfred}
626255932Salfred
627255932Salfredstatic int
628255932Salfredmsdosfs_statfs(mp, sbp, p)
629255932Salfred	struct mount *mp;
630255932Salfred	struct statfs *sbp;
631255932Salfred	struct proc *p;
632255932Salfred{
633255932Salfred	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
634255932Salfred
635255932Salfred	/*
636255932Salfred	 * Fill in the stat block.
637255932Salfred	 */
638255932Salfred	sbp->f_bsize = pmp->pm_bpcluster;
639255932Salfred	sbp->f_iosize = pmp->pm_bpcluster;
640255932Salfred	sbp->f_blocks = pmp->pm_nmbrofclusters;
641255932Salfred	sbp->f_bfree = pmp->pm_freeclustercount;
642255932Salfred	sbp->f_bavail = pmp->pm_freeclustercount;
643255932Salfred	sbp->f_files = pmp->pm_RootDirEnts;			/* XXX */
644255932Salfred	sbp->f_ffree = 0;	/* what to put in here? */
645255932Salfred
646255932Salfred	/*
647255932Salfred	 * Copy the mounted on and mounted from names into the passed in
648255932Salfred	 * stat block, if it is not the one in the mount structure.
649255932Salfred	 */
650255932Salfred	if (sbp != &mp->mnt_stat) {
651255932Salfred		sbp->f_type = mp->mnt_vfc->vfc_typenum;
652255932Salfred		bcopy((caddr_t) mp->mnt_stat.f_mntonname,
653255932Salfred		    (caddr_t) & sbp->f_mntonname[0], MNAMELEN);
654255932Salfred		bcopy((caddr_t) mp->mnt_stat.f_mntfromname,
655255932Salfred		    (caddr_t) & sbp->f_mntfromname[0], MNAMELEN);
656255932Salfred	}
657255932Salfred#if 0
658255932Salfred	strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN);
659255932Salfred	sbp->f_fstypename[MFSNAMELEN] = '\0';
660255932Salfred#endif
661255932Salfred	return 0;
662255932Salfred}
663255932Salfred
664255932Salfredstatic int
665255932Salfredmsdosfs_sync(mp, waitfor, cred, p)
666255932Salfred	struct mount *mp;
667255932Salfred	int waitfor;
668255932Salfred	struct ucred *cred;
669255932Salfred	struct proc *p;
670255932Salfred{
671255932Salfred	struct vnode *vp;
672255932Salfred	struct denode *dep;
673255932Salfred	struct msdosfsmount *pmp;
674255932Salfred	int error;
675255932Salfred	int allerror = 0;
676255932Salfred
677255932Salfred	pmp = (struct msdosfsmount *) mp->mnt_data;
678272027Shselasky
679255932Salfred	/*
680255932Salfred	 * If we ever switch to not updating all of the fats all the time,
681255932Salfred	 * this would be the place to update them from the first one.
682255932Salfred	 */
683255932Salfred	if (pmp->pm_fmod)
684255932Salfred		if (pmp->pm_ronly)
685255932Salfred			panic("msdosfs_sync: rofs mod");
686255932Salfred		else {
687255932Salfred			/* update fats here */
688255932Salfred		}
689255932Salfred
690255932Salfred	/*
691255932Salfred	 * Go thru in memory denodes and write them out along with
692255932Salfred	 * unwritten file blocks.
693255932Salfred	 */
694255932Salfred	simple_lock(&mntvnode_slock);
695255932Salfredloop:
696255932Salfred	for (vp = mp->mnt_vnodelist.lh_first; vp;
697255932Salfred	    vp = vp->v_mntvnodes.le_next) {
698255932Salfred		if (vp->v_mount != mp)	/* not ours anymore	 */
699255932Salfred			goto loop;
700255932Salfred		simple_lock(&vp->v_interlock);
701255932Salfred		dep = VTODE(vp);
702255932Salfred		if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 &&
703255932Salfred		    vp->v_dirtyblkhd.lh_first == NULL) {
704255932Salfred			simple_unlock(&vp->v_interlock);
705255932Salfred			continue;
706255932Salfred		}
707255932Salfred		simple_unlock(&mntvnode_slock);
708255932Salfred		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
709255932Salfred		if (error) {
710255932Salfred			simple_lock(&mntvnode_slock);
711255932Salfred			if (error == ENOENT)
712255932Salfred				goto loop;
713255932Salfred			continue;
714255932Salfred		}
715255932Salfred		error = VOP_FSYNC(vp, cred, waitfor, p);
716255932Salfred		if (error)
717255932Salfred			allerror = error;
718255932Salfred		VOP_UNLOCK(vp, 0, p);
719255932Salfred		vrele(vp);	/* done with this one	 */
720255932Salfred		simple_lock(&mntvnode_slock);
721255932Salfred	}
722255932Salfred	simple_unlock(&mntvnode_slock);
723255932Salfred
724255932Salfred	/*
725255932Salfred	 * Flush filesystem control info.
726255932Salfred	 */
727255932Salfred	error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p);
728255932Salfred	if (error)
729255932Salfred		allerror = error;
730255932Salfred	return allerror;
731255932Salfred}
732255932Salfred
733255932Salfredstatic int
734255932Salfredmsdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
735255932Salfred	struct mount *mp;
736255932Salfred	struct fid *fhp;
737255932Salfred	struct sockaddr *nam;
738255932Salfred	struct vnode **vpp;
739255932Salfred	int *exflagsp;
740255932Salfred	struct ucred **credanonp;
741255932Salfred{
742255932Salfred	struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
743255932Salfred	struct defid *defhp = (struct defid *) fhp;
744255932Salfred	struct denode *dep;
745255932Salfred	struct netcred *np;
746255932Salfred	int error;
747255932Salfred
748255932Salfred	np = vfs_export_lookup(mp, &pmp->pm_export, nam);
749255932Salfred	if (np == NULL)
750255932Salfred		return EACCES;
751255932Salfred	error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
752255932Salfred	    NULL, &dep);
753255932Salfred	if (error) {
754255932Salfred		*vpp = NULLVP;
755255932Salfred		return error;
756255932Salfred	}
757255932Salfred	*vpp = DETOV(dep);
758272027Shselasky	*exflagsp = np->netc_exflags;
759255932Salfred	*credanonp = &np->netc_anon;
760255932Salfred	return 0;
761255932Salfred}
762219820Sjeff
763219820Sjeff
764219820Sjeffstatic int
765219820Sjeffmsdosfs_vptofh(vp, fhp)
766219820Sjeff	struct vnode *vp;
767219820Sjeff	struct fid *fhp;
768219820Sjeff{
769219820Sjeff	struct denode *dep = VTODE(vp);
770255932Salfred	struct defid *defhp = (struct defid *) fhp;
771255932Salfred
772255932Salfred	defhp->defid_len = sizeof(struct defid);
773255932Salfred	defhp->defid_dirclust = dep->de_dirclust;
774255932Salfred	defhp->defid_dirofs = dep->de_diroffset;
775255932Salfred	/* defhp->defid_gen = ip->i_gen; */
776255932Salfred	return 0;
777219820Sjeff}
778255932Salfred
779255932Salfredstatic int
780255932Salfredmsdosfs_vget(mp, ino, vpp)
781255932Salfred	struct mount *mp;
782219820Sjeff	ino_t ino;
783255932Salfred	struct vnode **vpp;
784272027Shselasky{
785272027Shselasky	return EOPNOTSUPP;
786255932Salfred}
787255932Salfred
788255932Salfredstatic struct vfsops msdosfs_vfsops = {
789255932Salfred	msdosfs_mount,
790255932Salfred	msdosfs_start,
791255932Salfred	msdosfs_unmount,
792255932Salfred	msdosfs_root,
793255932Salfred	msdosfs_quotactl,
794255932Salfred	msdosfs_statfs,
795255932Salfred	msdosfs_sync,
796255932Salfred	msdosfs_vget,
797255932Salfred	msdosfs_fhtovp,
798255932Salfred	msdosfs_vptofh,
799219820Sjeff	msdosfs_init
800219820Sjeff};
801219820Sjeff
802255932SalfredVFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0);
803255932Salfred