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