msdosfs_vfsops.c revision 1.48 1 /* $NetBSD: msdosfs_vfsops.c,v 1.48 2007/07/20 16:46:43 pooka 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.48 2007/07/20 16:46:43 pooka 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/sysctl.h>
61 #include <sys/namei.h>
62 #include <sys/proc.h>
63 #include <sys/kernel.h>
64 #include <sys/vnode.h>
65 #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
66 #include <sys/mount.h>
67 #include <sys/buf.h>
68 #include <sys/file.h>
69 #include <sys/device.h>
70 #include <sys/disklabel.h>
71 #include <sys/disk.h>
72 #include <sys/ioctl.h>
73 #include <sys/malloc.h>
74 #include <sys/dirent.h>
75 #include <sys/stat.h>
76 #include <sys/conf.h>
77 #include <sys/kauth.h>
78
79 #include <fs/msdosfs/bpb.h>
80 #include <fs/msdosfs/bootsect.h>
81 #include <fs/msdosfs/direntry.h>
82 #include <fs/msdosfs/denode.h>
83 #include <fs/msdosfs/msdosfsmount.h>
84 #include <fs/msdosfs/fat.h>
85
86 #ifdef MSDOSFS_DEBUG
87 #define DPRINTF(a) uprintf a
88 #else
89 #define DPRINTF(a)
90 #endif
91
92 #define MSDOSFS_NAMEMAX(pmp) \
93 (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12
94
95 int msdosfs_mountroot(void);
96 int msdosfs_mount(struct mount *, const char *, void *, size_t *,
97 struct nameidata *, struct lwp *);
98 int msdosfs_start(struct mount *, int, struct lwp *);
99 int msdosfs_unmount(struct mount *, int, struct lwp *);
100 int msdosfs_root(struct mount *, struct vnode **);
101 int msdosfs_quotactl(struct mount *, int, uid_t, void *, struct lwp *);
102 int msdosfs_statvfs(struct mount *, struct statvfs *, struct lwp *);
103 int msdosfs_sync(struct mount *, int, kauth_cred_t, struct lwp *);
104 int msdosfs_vget(struct mount *, ino_t, struct vnode **);
105 int msdosfs_fhtovp(struct mount *, struct fid *, struct vnode **);
106 int msdosfs_vptofh(struct vnode *, struct fid *, size_t *fh_size);
107
108 int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *,
109 struct msdosfs_args *);
110
111 static int update_mp(struct mount *, struct msdosfs_args *);
112
113 MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure");
114 MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS fat", "MSDOS FS fat table");
115
116 #define ROOTNAME "root_device"
117
118 extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
119
120 const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = {
121 &msdosfs_vnodeop_opv_desc,
122 NULL,
123 };
124
125 struct vfsops msdosfs_vfsops = {
126 MOUNT_MSDOS,
127 sizeof (struct msdosfs_args),
128 msdosfs_mount,
129 msdosfs_start,
130 msdosfs_unmount,
131 msdosfs_root,
132 msdosfs_quotactl,
133 msdosfs_statvfs,
134 msdosfs_sync,
135 msdosfs_vget,
136 msdosfs_fhtovp,
137 msdosfs_vptofh,
138 msdosfs_init,
139 msdosfs_reinit,
140 msdosfs_done,
141 msdosfs_mountroot,
142 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp,
143 vfs_stdextattrctl,
144 vfs_stdsuspendctl,
145 msdosfs_vnodeopv_descs,
146 0,
147 { NULL, NULL },
148 };
149 VFS_ATTACH(msdosfs_vfsops);
150
151 static int
152 update_mp(mp, argp)
153 struct mount *mp;
154 struct msdosfs_args *argp;
155 {
156 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
157 int error;
158
159 pmp->pm_gid = argp->gid;
160 pmp->pm_uid = argp->uid;
161 pmp->pm_mask = argp->mask & ALLPERMS;
162 pmp->pm_dirmask = argp->dirmask & ALLPERMS;
163 pmp->pm_gmtoff = argp->gmtoff;
164 pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
165
166 /*
167 * GEMDOS knows nothing (yet) about win95
168 */
169 if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
170 pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
171
172 if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
173 pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
174 else if (!(pmp->pm_flags &
175 (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
176 struct vnode *rtvp;
177
178 /*
179 * Try to divine whether to support Win'95 long filenames
180 */
181 if (FAT32(pmp))
182 pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
183 else {
184 if ((error = msdosfs_root(mp, &rtvp)) != 0)
185 return error;
186 pmp->pm_flags |= findwin95(VTODE(rtvp))
187 ? MSDOSFSMNT_LONGNAME
188 : MSDOSFSMNT_SHORTNAME;
189 vput(rtvp);
190 }
191 }
192
193 mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
194
195 return 0;
196 }
197
198 int
199 msdosfs_mountroot()
200 {
201 struct mount *mp;
202 struct lwp *l = curlwp; /* XXX */
203 int error;
204 struct msdosfs_args args;
205
206 if (device_class(root_device) != DV_DISK)
207 return (ENODEV);
208
209 if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) {
210 vrele(rootvp);
211 return (error);
212 }
213
214 args.flags = MSDOSFSMNT_VERSIONED;
215 args.uid = 0;
216 args.gid = 0;
217 args.mask = 0777;
218 args.version = MSDOSFSMNT_VERSION;
219 args.dirmask = 0777;
220
221 if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) {
222 mp->mnt_op->vfs_refcount--;
223 vfs_unbusy(mp);
224 free(mp, M_MOUNT);
225 return (error);
226 }
227
228 if ((error = update_mp(mp, &args)) != 0) {
229 (void)msdosfs_unmount(mp, 0, l);
230 vfs_unbusy(mp);
231 free(mp, M_MOUNT);
232 vrele(rootvp);
233 return (error);
234 }
235
236 simple_lock(&mountlist_slock);
237 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
238 simple_unlock(&mountlist_slock);
239 (void)msdosfs_statvfs(mp, &mp->mnt_stat, l);
240 vfs_unbusy(mp);
241 return (0);
242 }
243
244 /*
245 * mp - path - addr in user space of mount point (ie /usr or whatever)
246 * data - addr in user space of mount params including the name of the block
247 * special file to treat as a filesystem.
248 */
249 int
250 msdosfs_mount(mp, path, data, data_len, ndp, l)
251 struct mount *mp;
252 const char *path;
253 void *data;
254 size_t *data_len;
255 struct nameidata *ndp;
256 struct lwp *l;
257 {
258 struct vnode *devvp; /* vnode for blk device to mount */
259 struct msdosfs_args *args = data; /* holds data from mount request */
260 /* msdosfs specific mount control block */
261 struct msdosfsmount *pmp = NULL;
262 int error, flags;
263 mode_t accessmode;
264
265 if (*data_len < sizeof *args)
266 return EINVAL;
267
268 if (mp->mnt_flag & MNT_GETARGS) {
269 pmp = VFSTOMSDOSFS(mp);
270 if (pmp == NULL)
271 return EIO;
272 args->fspec = NULL;
273 args->uid = pmp->pm_uid;
274 args->gid = pmp->pm_gid;
275 args->mask = pmp->pm_mask;
276 args->flags = pmp->pm_flags;
277 args->version = MSDOSFSMNT_VERSION;
278 args->dirmask = pmp->pm_dirmask;
279 args->gmtoff = pmp->pm_gmtoff;
280 *data_len = sizeof *args;
281 return 0;
282 }
283
284 /*
285 * If not versioned (i.e. using old mount_msdos(8)), fill in
286 * the additional structure items with suitable defaults.
287 */
288 if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) {
289 args->version = 1;
290 args->dirmask = args->mask;
291 }
292
293 /*
294 * Reset GMT offset for pre-v3 mount structure args.
295 */
296 if (args->version < 3)
297 args->gmtoff = 0;
298
299 /*
300 * If updating, check whether changing from read-only to
301 * read/write; if there is no device name, that's all we do.
302 */
303 if (mp->mnt_flag & MNT_UPDATE) {
304 pmp = VFSTOMSDOSFS(mp);
305 error = 0;
306 if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) {
307 flags = WRITECLOSE;
308 if (mp->mnt_flag & MNT_FORCE)
309 flags |= FORCECLOSE;
310 error = vflush(mp, NULLVP, flags);
311 }
312 if (!error && (mp->mnt_flag & MNT_RELOAD))
313 /* not yet implemented */
314 error = EOPNOTSUPP;
315 if (error) {
316 DPRINTF(("vflush %d\n", error));
317 return (error);
318 }
319 if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_iflag & IMNT_WANTRDWR)) {
320 /*
321 * If upgrade to read-write by non-root, then verify
322 * that user has necessary permissions on the device.
323 */
324 if (kauth_authorize_generic(l->l_cred,
325 KAUTH_GENERIC_ISSUSER, NULL) != 0) {
326 devvp = pmp->pm_devvp;
327 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
328 error = VOP_ACCESS(devvp, VREAD | VWRITE,
329 l->l_cred, l);
330 VOP_UNLOCK(devvp, 0);
331 DPRINTF(("VOP_ACCESS %d\n", error));
332 if (error)
333 return (error);
334 }
335 pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
336 }
337 if (args->fspec == NULL) {
338 DPRINTF(("missing fspec\n"));
339 return EINVAL;
340 }
341 }
342 /*
343 * Not an update, or updating the name: look up the name
344 * and verify that it refers to a sensible block device.
345 */
346 NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args->fspec, l);
347 if ((error = namei(ndp)) != 0) {
348 DPRINTF(("namei %d\n", error));
349 return (error);
350 }
351 devvp = ndp->ni_vp;
352
353 if (devvp->v_type != VBLK) {
354 DPRINTF(("not block\n"));
355 vrele(devvp);
356 return (ENOTBLK);
357 }
358 if (bdevsw_lookup(devvp->v_rdev) == NULL) {
359 DPRINTF(("no block switch\n"));
360 vrele(devvp);
361 return (ENXIO);
362 }
363 /*
364 * If mount by non-root, then verify that user has necessary
365 * permissions on the device.
366 */
367 if (kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL) != 0) {
368 accessmode = VREAD;
369 if ((mp->mnt_flag & MNT_RDONLY) == 0)
370 accessmode |= VWRITE;
371 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
372 error = VOP_ACCESS(devvp, accessmode, l->l_cred, l);
373 VOP_UNLOCK(devvp, 0);
374 if (error) {
375 DPRINTF(("VOP_ACCESS2 %d\n", error));
376 vrele(devvp);
377 return (error);
378 }
379 }
380 if ((mp->mnt_flag & MNT_UPDATE) == 0) {
381 int xflags;
382
383 /*
384 * Disallow multiple mounts of the same device.
385 * Disallow mounting of a device that is currently in use
386 * (except for root, which might share swap device for
387 * miniroot).
388 */
389 error = vfs_mountedon(devvp);
390 if (error) {
391 DPRINTF(("vfs_mountedon %d\n", error));
392 goto fail;
393 }
394 if (vcount(devvp) > 1 && devvp != rootvp) {
395 DPRINTF(("vcount %d\n", error));
396 error = EBUSY;
397 goto fail;
398 }
399 if (mp->mnt_flag & MNT_RDONLY)
400 xflags = FREAD;
401 else
402 xflags = FREAD|FWRITE;
403 error = VOP_OPEN(devvp, xflags, FSCRED, l);
404 if (error) {
405 DPRINTF(("VOP_OPEN %d\n", error));
406 goto fail;
407 }
408 error = msdosfs_mountfs(devvp, mp, l, args);
409 if (error) {
410 DPRINTF(("msdosfs_mountfs %d\n", error));
411 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
412 (void) VOP_CLOSE(devvp, xflags, NOCRED, l);
413 VOP_UNLOCK(devvp, 0);
414 goto fail;
415 }
416 #ifdef MSDOSFS_DEBUG /* only needed for the printf below */
417 pmp = VFSTOMSDOSFS(mp);
418 #endif
419 } else {
420 vrele(devvp);
421 if (devvp != pmp->pm_devvp) {
422 DPRINTF(("devvp %p pmp %p\n",
423 devvp, pmp->pm_devvp));
424 return (EINVAL); /* needs translation */
425 }
426 }
427 if ((error = update_mp(mp, args)) != 0) {
428 msdosfs_unmount(mp, MNT_FORCE, l);
429 DPRINTF(("update_mp %d\n", error));
430 return error;
431 }
432
433 #ifdef MSDOSFS_DEBUG
434 printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
435 #endif
436 return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
437 mp->mnt_op->vfs_name, mp, l);
438
439 fail:
440 vrele(devvp);
441 return (error);
442 }
443
444 int
445 msdosfs_mountfs(devvp, mp, l, argp)
446 struct vnode *devvp;
447 struct mount *mp;
448 struct lwp *l;
449 struct msdosfs_args *argp;
450 {
451 struct msdosfsmount *pmp;
452 struct buf *bp;
453 dev_t dev = devvp->v_rdev;
454 struct partinfo dpart;
455 union bootsector *bsp;
456 struct byte_bpb33 *b33;
457 struct byte_bpb50 *b50;
458 struct byte_bpb710 *b710;
459 u_int8_t SecPerClust;
460 int ronly, error, tmp;
461 int bsize, dtype, fstype, secsize;
462 u_int64_t psize;
463
464 /* Flush out any old buffers remaining from a previous use. */
465 if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0)
466 return (error);
467
468 ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
469
470 bp = NULL; /* both used in error_exit */
471 pmp = NULL;
472
473 /*
474 * We need the disklabel to calculate the size of a FAT entry
475 * later on. Also make sure the partition contains a filesystem
476 * of type FS_MSDOS. This doesn't work for floppies, so we have
477 * to check for them too.
478 *
479 * There might still be parts of the msdos fs driver which assume
480 * that the size of a disk block will always be 512 bytes.
481 * Let's root them out...
482 */
483 error = VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, NOCRED, l);
484 if (error == 0) {
485 secsize = dpart.disklab->d_secsize;
486 dtype = dpart.disklab->d_type;
487 fstype = dpart.part->p_fstype;
488 psize = dpart.part->p_size;
489 } else {
490 struct dkwedge_info dkw;
491 error = VOP_IOCTL(devvp, DIOCGWEDGEINFO, &dkw, FREAD,
492 NOCRED, l);
493 secsize = 512; /* XXX */
494 dtype = DTYPE_FLOPPY; /* XXX */
495 fstype = FS_MSDOS;
496 psize = -1;
497 if (error) {
498 if (error != ENOTTY) {
499 DPRINTF(("Error getting partition info %d\n",
500 error));
501 goto error_exit;
502 }
503 } else {
504 fstype = strcmp(dkw.dkw_ptype, DKW_PTYPE_FAT) == 0 ?
505 FS_MSDOS : -1;
506 psize = dkw.dkw_size;
507 }
508 }
509 if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
510 bsize = secsize;
511 if (bsize != 512 ||
512 (dtype != DTYPE_FLOPPY && fstype != FS_MSDOS)) {
513 DPRINTF(("bsize %d dtype %d fstype %d\n", bsize, dtype,
514 fstype));
515 error = EINVAL;
516 goto error_exit;
517 }
518 } else
519 bsize = 0;
520
521 /*
522 * Read the boot sector of the filesystem, and then check the
523 * boot signature. If not a dos boot sector then error out.
524 */
525 if ((error = bread(devvp, 0, secsize, NOCRED, &bp)) != 0)
526 goto error_exit;
527 bp->b_flags |= B_AGE;
528 bsp = (union bootsector *)bp->b_data;
529 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
530 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
531 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
532
533 if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
534 if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
535 || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
536 DPRINTF(("bootsig0 %d bootsig1 %d\n",
537 bsp->bs50.bsBootSectSig0,
538 bsp->bs50.bsBootSectSig1));
539 error = EINVAL;
540 goto error_exit;
541 }
542 }
543
544 pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
545 memset(pmp, 0, sizeof *pmp);
546 pmp->pm_mountp = mp;
547
548 /*
549 * Compute several useful quantities from the bpb in the
550 * bootsector. Copy in the dos 5 variant of the bpb then fix up
551 * the fields that are different between dos 5 and dos 3.3.
552 */
553 SecPerClust = b50->bpbSecPerClust;
554 pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
555 pmp->pm_ResSectors = getushort(b50->bpbResSectors);
556 pmp->pm_FATs = b50->bpbFATs;
557 pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
558 pmp->pm_Sectors = getushort(b50->bpbSectors);
559 pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
560 pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
561 pmp->pm_Heads = getushort(b50->bpbHeads);
562 pmp->pm_Media = b50->bpbMedia;
563
564 if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
565 /* XXX - We should probably check more values here */
566 if (!pmp->pm_BytesPerSec || !SecPerClust
567 || pmp->pm_Heads > 255 || pmp->pm_SecPerTrack > 63) {
568 DPRINTF(("bytespersec %d secperclust %d "
569 "heads %d secpertrack %d\n",
570 pmp->pm_BytesPerSec, SecPerClust,
571 pmp->pm_Heads, pmp->pm_SecPerTrack));
572 error = EINVAL;
573 goto error_exit;
574 }
575 }
576
577 if (pmp->pm_Sectors == 0) {
578 pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
579 pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
580 } else {
581 pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
582 pmp->pm_HugeSectors = pmp->pm_Sectors;
583 }
584
585 if (pmp->pm_RootDirEnts == 0) {
586 unsigned short vers = getushort(b710->bpbFSVers);
587 /*
588 * Some say that bsBootSectSig[23] must be zero, but
589 * Windows does not require this and some digital cameras
590 * do not set these to zero. Therefore, do not insist.
591 */
592 if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
593 DPRINTF(("sectors %d fatsecs %lu vers %d\n",
594 pmp->pm_Sectors, pmp->pm_FATsecs, vers));
595 error = EINVAL;
596 goto error_exit;
597 }
598 pmp->pm_fatmask = FAT32_MASK;
599 pmp->pm_fatmult = 4;
600 pmp->pm_fatdiv = 1;
601 pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
602
603 /* mirrorring is enabled if the FATMIRROR bit is not set */
604 if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
605 pmp->pm_flags |= MSDOSFS_FATMIRROR;
606 else
607 pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
608 } else
609 pmp->pm_flags |= MSDOSFS_FATMIRROR;
610
611 if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
612 if (FAT32(pmp)) {
613 DPRINTF(("fat32 for gemdos\n"));
614 /*
615 * GEMDOS doesn't know fat32.
616 */
617 error = EINVAL;
618 goto error_exit;
619 }
620
621 /*
622 * Check a few values (could do some more):
623 * - logical sector size: power of 2, >= block size
624 * - sectors per cluster: power of 2, >= 1
625 * - number of sectors: >= 1, <= size of partition
626 */
627 if ( (SecPerClust == 0)
628 || (SecPerClust & (SecPerClust - 1))
629 || (pmp->pm_BytesPerSec < bsize)
630 || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
631 || (pmp->pm_HugeSectors == 0)
632 || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
633 > psize)) {
634 DPRINTF(("consistency checks for gemdos\n"));
635 error = EINVAL;
636 goto error_exit;
637 }
638 /*
639 * XXX - Many parts of the msdos fs driver seem to assume that
640 * the number of bytes per logical sector (BytesPerSec) will
641 * always be the same as the number of bytes per disk block
642 * Let's pretend it is.
643 */
644 tmp = pmp->pm_BytesPerSec / bsize;
645 pmp->pm_BytesPerSec = bsize;
646 pmp->pm_HugeSectors *= tmp;
647 pmp->pm_HiddenSects *= tmp;
648 pmp->pm_ResSectors *= tmp;
649 pmp->pm_Sectors *= tmp;
650 pmp->pm_FATsecs *= tmp;
651 SecPerClust *= tmp;
652 }
653 pmp->pm_fatblk = pmp->pm_ResSectors;
654 if (FAT32(pmp)) {
655 pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
656 pmp->pm_firstcluster = pmp->pm_fatblk
657 + (pmp->pm_FATs * pmp->pm_FATsecs);
658 pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
659 } else {
660 pmp->pm_rootdirblk = pmp->pm_fatblk +
661 (pmp->pm_FATs * pmp->pm_FATsecs);
662 pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
663 + pmp->pm_BytesPerSec - 1)
664 / pmp->pm_BytesPerSec;/* in sectors */
665 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
666 }
667
668 pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
669 SecPerClust;
670 pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
671 pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
672
673 if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
674 if (pmp->pm_nmbrofclusters <= (0xff0 - 2)
675 && (dtype == DTYPE_FLOPPY
676 || (dtype == DTYPE_VND
677 && (pmp->pm_Heads == 1 || pmp->pm_Heads == 2)))
678 ) {
679 pmp->pm_fatmask = FAT12_MASK;
680 pmp->pm_fatmult = 3;
681 pmp->pm_fatdiv = 2;
682 } else {
683 pmp->pm_fatmask = FAT16_MASK;
684 pmp->pm_fatmult = 2;
685 pmp->pm_fatdiv = 1;
686 }
687 } else if (pmp->pm_fatmask == 0) {
688 if (pmp->pm_maxcluster
689 <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
690 /*
691 * This will usually be a floppy disk. This size makes
692 * sure that one fat entry will not be split across
693 * multiple blocks.
694 */
695 pmp->pm_fatmask = FAT12_MASK;
696 pmp->pm_fatmult = 3;
697 pmp->pm_fatdiv = 2;
698 } else {
699 pmp->pm_fatmask = FAT16_MASK;
700 pmp->pm_fatmult = 2;
701 pmp->pm_fatdiv = 1;
702 }
703 }
704 if (FAT12(pmp))
705 pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
706 else
707 pmp->pm_fatblocksize = MAXBSIZE;
708
709 pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
710 pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
711
712 /*
713 * Compute mask and shift value for isolating cluster relative byte
714 * offsets and cluster numbers from a file offset.
715 */
716 pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
717 pmp->pm_crbomask = pmp->pm_bpcluster - 1;
718 pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
719
720 /*
721 * Check for valid cluster size
722 * must be a power of 2
723 */
724 if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
725 DPRINTF(("bpcluster %lu cnshift %lu\n",
726 pmp->pm_bpcluster, pmp->pm_cnshift));
727 error = EINVAL;
728 goto error_exit;
729 }
730
731 /*
732 * Release the bootsector buffer.
733 */
734 brelse(bp);
735 bp = NULL;
736
737 /*
738 * Check FSInfo.
739 */
740 if (pmp->pm_fsinfo) {
741 struct fsinfo *fp;
742
743 /*
744 * XXX If the fsinfo block is stored on media with
745 * 2KB or larger sectors, is the fsinfo structure
746 * padded at the end or in the middle?
747 */
748 if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
749 pmp->pm_BytesPerSec, NOCRED, &bp)) != 0)
750 goto error_exit;
751 fp = (struct fsinfo *)bp->b_data;
752 if (!memcmp(fp->fsisig1, "RRaA", 4)
753 && !memcmp(fp->fsisig2, "rrAa", 4)
754 && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
755 && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
756 pmp->pm_nxtfree = getulong(fp->fsinxtfree);
757 else
758 pmp->pm_fsinfo = 0;
759 brelse(bp);
760 bp = NULL;
761 }
762
763 /*
764 * Check and validate (or perhaps invalidate?) the fsinfo structure?
765 * XXX
766 */
767 if (pmp->pm_fsinfo) {
768 if (pmp->pm_nxtfree == (u_long)-1)
769 pmp->pm_fsinfo = 0;
770 }
771
772 /*
773 * Allocate memory for the bitmap of allocated clusters, and then
774 * fill it in.
775 */
776 pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
777 / N_INUSEBITS)
778 * sizeof(*pmp->pm_inusemap),
779 M_MSDOSFSFAT, M_WAITOK);
780
781 /*
782 * fillinusemap() needs pm_devvp.
783 */
784 pmp->pm_dev = dev;
785 pmp->pm_devvp = devvp;
786
787 /*
788 * Have the inuse map filled in.
789 */
790 if ((error = fillinusemap(pmp)) != 0) {
791 DPRINTF(("fillinusemap %d\n", error));
792 goto error_exit;
793 }
794
795 /*
796 * If they want fat updates to be synchronous then let them suffer
797 * the performance degradation in exchange for the on disk copy of
798 * the fat being correct just about all the time. I suppose this
799 * would be a good thing to turn on if the kernel is still flakey.
800 */
801 if (mp->mnt_flag & MNT_SYNCHRONOUS)
802 pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
803
804 /*
805 * Finish up.
806 */
807 if (ronly)
808 pmp->pm_flags |= MSDOSFSMNT_RONLY;
809 else
810 pmp->pm_fmod = 1;
811 mp->mnt_data = pmp;
812 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
813 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS);
814 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
815 mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
816 mp->mnt_flag |= MNT_LOCAL;
817 mp->mnt_dev_bshift = pmp->pm_bnshift;
818 mp->mnt_fs_bshift = pmp->pm_cnshift;
819
820 #ifdef QUOTA
821 /*
822 * If we ever do quotas for DOS filesystems this would be a place
823 * to fill in the info in the msdosfsmount structure. You dolt,
824 * quotas on dos filesystems make no sense because files have no
825 * owners on dos filesystems. of course there is some empty space
826 * in the directory entry where we could put uid's and gid's.
827 */
828 #endif
829 devvp->v_specmountpoint = mp;
830
831 return (0);
832
833 error_exit:;
834 if (bp)
835 brelse(bp);
836 if (pmp) {
837 if (pmp->pm_inusemap)
838 free(pmp->pm_inusemap, M_MSDOSFSFAT);
839 free(pmp, M_MSDOSFSMNT);
840 mp->mnt_data = NULL;
841 }
842 return (error);
843 }
844
845 int
846 msdosfs_start(struct mount *mp, int flags,
847 struct lwp *l)
848 {
849
850 return (0);
851 }
852
853 /*
854 * Unmount the filesystem described by mp.
855 */
856 int
857 msdosfs_unmount(mp, mntflags, l)
858 struct mount *mp;
859 int mntflags;
860 struct lwp *l;
861 {
862 struct msdosfsmount *pmp;
863 int error, flags;
864
865 flags = 0;
866 if (mntflags & MNT_FORCE)
867 flags |= FORCECLOSE;
868 #ifdef QUOTA
869 #endif
870 if ((error = vflush(mp, NULLVP, flags)) != 0)
871 return (error);
872 pmp = VFSTOMSDOSFS(mp);
873 if (pmp->pm_devvp->v_type != VBAD)
874 pmp->pm_devvp->v_specmountpoint = NULL;
875 #ifdef MSDOSFS_DEBUG
876 {
877 struct vnode *vp = pmp->pm_devvp;
878
879 printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
880 printf("flag %08x, usecount %d, writecount %ld, holdcnt %ld\n",
881 vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
882 printf("mount %p, op %p\n",
883 vp->v_mount, vp->v_op);
884 printf("freef %p, freeb %p, mount %p\n",
885 vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev,
886 vp->v_mount);
887 printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
888 vp->v_cleanblkhd.lh_first,
889 vp->v_dirtyblkhd.lh_first,
890 vp->v_numoutput, vp->v_type);
891 printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
892 vp->v_socket, vp->v_tag,
893 ((u_int *)vp->v_data)[0],
894 ((u_int *)vp->v_data)[1]);
895 }
896 #endif
897 vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
898 error = VOP_CLOSE(pmp->pm_devvp,
899 pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED, l);
900 vput(pmp->pm_devvp);
901 free(pmp->pm_inusemap, M_MSDOSFSFAT);
902 free(pmp, M_MSDOSFSMNT);
903 mp->mnt_data = NULL;
904 mp->mnt_flag &= ~MNT_LOCAL;
905 return (error);
906 }
907
908 int
909 msdosfs_root(mp, vpp)
910 struct mount *mp;
911 struct vnode **vpp;
912 {
913 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
914 struct denode *ndep;
915 int error;
916
917 #ifdef MSDOSFS_DEBUG
918 printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
919 #endif
920 if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0)
921 return (error);
922 *vpp = DETOV(ndep);
923 return (0);
924 }
925
926 int
927 msdosfs_quotactl(struct mount *mp, int cmds,
928 uid_t uid, void *arg, struct lwp *l)
929 {
930
931 return (EOPNOTSUPP);
932 }
933
934 int
935 msdosfs_statvfs(struct mount *mp, struct statvfs *sbp, struct lwp *l)
936 {
937 struct msdosfsmount *pmp;
938
939 pmp = VFSTOMSDOSFS(mp);
940 sbp->f_bsize = pmp->pm_bpcluster;
941 sbp->f_frsize = sbp->f_bsize;
942 sbp->f_iosize = pmp->pm_bpcluster;
943 sbp->f_blocks = pmp->pm_nmbrofclusters;
944 sbp->f_bfree = pmp->pm_freeclustercount;
945 sbp->f_bavail = pmp->pm_freeclustercount;
946 sbp->f_bresvd = 0;
947 sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
948 sbp->f_ffree = 0; /* what to put in here? */
949 sbp->f_favail = 0; /* what to put in here? */
950 sbp->f_fresvd = 0;
951 copy_statvfs_info(sbp, mp);
952 return (0);
953 }
954
955 int
956 msdosfs_sync(mp, waitfor, cred, l)
957 struct mount *mp;
958 int waitfor;
959 kauth_cred_t cred;
960 struct lwp *l;
961 {
962 struct vnode *vp, *nvp;
963 struct denode *dep;
964 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
965 int error, allerror = 0;
966
967 /*
968 * If we ever switch to not updating all of the fats all the time,
969 * this would be the place to update them from the first one.
970 */
971 if (pmp->pm_fmod != 0) {
972 if (pmp->pm_flags & MSDOSFSMNT_RONLY)
973 panic("msdosfs_sync: rofs mod");
974 else {
975 /* update fats here */
976 }
977 }
978 /*
979 * Write back each (modified) denode.
980 */
981 simple_lock(&mntvnode_slock);
982 loop:
983 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) {
984 /*
985 * If the vnode that we are about to sync is no longer
986 * associated with this mount point, start over.
987 */
988 if (vp->v_mount != mp)
989 goto loop;
990 simple_lock(&vp->v_interlock);
991 nvp = TAILQ_NEXT(vp, v_mntvnodes);
992 dep = VTODE(vp);
993 if (waitfor == MNT_LAZY || vp->v_type == VNON ||
994 (((dep->de_flag &
995 (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) &&
996 (LIST_EMPTY(&vp->v_dirtyblkhd) &&
997 UVM_OBJ_IS_CLEAN(&vp->v_uobj)))) {
998 simple_unlock(&vp->v_interlock);
999 continue;
1000 }
1001 simple_unlock(&mntvnode_slock);
1002 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
1003 if (error) {
1004 simple_lock(&mntvnode_slock);
1005 if (error == ENOENT)
1006 goto loop;
1007 continue;
1008 }
1009 if ((error = VOP_FSYNC(vp, cred,
1010 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0, l)) != 0)
1011 allerror = error;
1012 vput(vp);
1013 simple_lock(&mntvnode_slock);
1014 }
1015 simple_unlock(&mntvnode_slock);
1016 /*
1017 * Force stale file system control information to be flushed.
1018 */
1019 if ((error = VOP_FSYNC(pmp->pm_devvp, cred,
1020 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0, l)) != 0)
1021 allerror = error;
1022 #ifdef QUOTA
1023 /* qsync(mp); */
1024 #endif
1025 return (allerror);
1026 }
1027
1028 int
1029 msdosfs_fhtovp(mp, fhp, vpp)
1030 struct mount *mp;
1031 struct fid *fhp;
1032 struct vnode **vpp;
1033 {
1034 struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1035 struct defid defh;
1036 struct denode *dep;
1037 int error;
1038
1039 if (fhp->fid_len != sizeof(struct defid)) {
1040 DPRINTF(("fid_len %d %zd\n", fhp->fid_len,
1041 sizeof(struct defid)));
1042 return EINVAL;
1043 }
1044
1045 memcpy(&defh, fhp, sizeof(defh));
1046 error = deget(pmp, defh.defid_dirclust, defh.defid_dirofs, &dep);
1047 if (error) {
1048 DPRINTF(("deget %d\n", error));
1049 *vpp = NULLVP;
1050 return (error);
1051 }
1052 *vpp = DETOV(dep);
1053 return (0);
1054 }
1055
1056 int
1057 msdosfs_vptofh(vp, fhp, fh_size)
1058 struct vnode *vp;
1059 struct fid *fhp;
1060 size_t *fh_size;
1061 {
1062 struct denode *dep;
1063 struct defid defh;
1064
1065 if (*fh_size < sizeof(struct defid)) {
1066 *fh_size = sizeof(struct defid);
1067 return E2BIG;
1068 }
1069 *fh_size = sizeof(struct defid);
1070 dep = VTODE(vp);
1071 memset(&defh, 0, sizeof(defh));
1072 defh.defid_len = sizeof(struct defid);
1073 defh.defid_dirclust = dep->de_dirclust;
1074 defh.defid_dirofs = dep->de_diroffset;
1075 /* defh.defid_gen = dep->de_gen; */
1076 memcpy(fhp, &defh, sizeof(defh));
1077 return (0);
1078 }
1079
1080 int
1081 msdosfs_vget(struct mount *mp, ino_t ino,
1082 struct vnode **vpp)
1083 {
1084
1085 return (EOPNOTSUPP);
1086 }
1087
1088 SYSCTL_SETUP(sysctl_vfs_msdosfs_setup, "sysctl vfs.msdosfs subtree setup")
1089 {
1090
1091 sysctl_createv(clog, 0, NULL, NULL,
1092 CTLFLAG_PERMANENT,
1093 CTLTYPE_NODE, "vfs", NULL,
1094 NULL, 0, NULL, 0,
1095 CTL_VFS, CTL_EOL);
1096 sysctl_createv(clog, 0, NULL, NULL,
1097 CTLFLAG_PERMANENT,
1098 CTLTYPE_NODE, "msdosfs",
1099 SYSCTL_DESCR("MS-DOS file system"),
1100 NULL, 0, NULL, 0,
1101 CTL_VFS, 4, CTL_EOL);
1102 /*
1103 * XXX the "4" above could be dynamic, thereby eliminating one
1104 * more instance of the "number to vfs" mapping problem, but
1105 * "4" is the order as taken from sys/mount.h
1106 */
1107 }
1108