1 /* $NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $ */
4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
35 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 * You can do anything you want with this software, just don't say you wrote
38 * it, and don't remove this notice.
40 * This software is provided "as is".
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.
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $");
53 #if defined(_KERNEL_OPT)
54 #include "opt_compat_netbsd.h"
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/sysctl.h>
60 #include <sys/namei.h>
62 #include <sys/kernel.h>
63 #include <sys/vnode.h>
64 #include <miscfs/genfs/genfs.h>
65 #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
66 #include <sys/mount.h>
69 #include <sys/device.h>
70 #include <sys/disklabel.h>
72 #include <sys/fstrans.h>
73 #include <sys/ioctl.h>
74 #include <sys/malloc.h>
75 #include <sys/dirent.h>
78 #include <sys/kauth.h>
79 #include <sys/module.h>
81 #include <fs/msdosfs/bpb.h>
82 #include <fs/msdosfs/bootsect.h>
83 #include <fs/msdosfs/direntry.h>
84 #include <fs/msdosfs/denode.h>
85 #include <fs/msdosfs/msdosfsmount.h>
86 #include <fs/msdosfs/fat.h>
88 MODULE(MODULE_CLASS_VFS
, msdos
, NULL
);
91 #define DPRINTF(fmt, ...) uprintf("%s(): " fmt "\n", __func__, ##__VA_ARGS__)
93 #define DPRINTF(fmt, ...)
96 #define GEMDOSFS_BSIZE 512
98 #define MSDOSFS_NAMEMAX(pmp) \
99 (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12
101 int msdosfs_mountfs(struct vnode
*, struct mount
*, struct lwp
*,
102 struct msdosfs_args
*);
104 static int update_mp(struct mount
*, struct msdosfs_args
*);
106 MALLOC_JUSTDEFINE(M_MSDOSFSMNT
, "MSDOSFS mount", "MSDOS FS mount structure");
107 MALLOC_JUSTDEFINE(M_MSDOSFSFAT
, "MSDOSFS FAT", "MSDOS FS FAT table");
108 MALLOC_JUSTDEFINE(M_MSDOSFSTMP
, "MSDOSFS temp", "MSDOS FS temp. structures");
110 static struct sysctllog
*msdosfs_sysctl_log
;
112 extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc
;
114 const struct vnodeopv_desc
* const msdosfs_vnodeopv_descs
[] = {
115 &msdosfs_vnodeop_opv_desc
,
119 struct vfsops msdosfs_vfsops
= {
120 .vfs_name
= MOUNT_MSDOS
,
121 .vfs_min_mount_data
= sizeof (struct msdosfs_args
),
122 .vfs_mount
= msdosfs_mount
,
123 .vfs_start
= msdosfs_start
,
124 .vfs_unmount
= msdosfs_unmount
,
125 .vfs_root
= msdosfs_root
,
126 .vfs_quotactl
= (void *)eopnotsupp
,
127 .vfs_statvfs
= msdosfs_statvfs
,
128 .vfs_sync
= msdosfs_sync
,
129 .vfs_vget
= msdosfs_vget
,
130 .vfs_loadvnode
= msdosfs_loadvnode
,
131 .vfs_fhtovp
= msdosfs_fhtovp
,
132 .vfs_vptofh
= msdosfs_vptofh
,
133 .vfs_init
= msdosfs_init
,
134 .vfs_reinit
= msdosfs_reinit
,
135 .vfs_done
= msdosfs_done
,
136 .vfs_mountroot
= msdosfs_mountroot
,
137 .vfs_snapshot
= (void *)eopnotsupp
,
138 .vfs_extattrctl
= vfs_stdextattrctl
,
139 .vfs_suspendctl
= msdosfs_suspendctl
,
140 .vfs_renamelock_enter
= genfs_renamelock_enter
,
141 .vfs_renamelock_exit
= genfs_renamelock_exit
,
142 .vfs_fsync
= (void *)eopnotsupp
,
143 .vfs_opv_descs
= msdosfs_vnodeopv_descs
147 msdos_modcmd(modcmd_t cmd
, void *arg
)
152 case MODULE_CMD_INIT
:
153 error
= vfs_attach(&msdosfs_vfsops
);
156 sysctl_createv(&msdosfs_sysctl_log
, 0, NULL
, NULL
,
158 CTLTYPE_NODE
, "msdosfs",
159 SYSCTL_DESCR("MS-DOS file system"),
161 CTL_VFS
, 4, CTL_EOL
);
163 * XXX the "4" above could be dynamic, thereby eliminating one
164 * more instance of the "number to vfs" mapping problem, but
165 * "4" is the order as taken from sys/mount.h
168 case MODULE_CMD_FINI
:
169 error
= vfs_detach(&msdosfs_vfsops
);
172 sysctl_teardown(&msdosfs_sysctl_log
);
183 update_mp(struct mount
*mp
, struct msdosfs_args
*argp
)
185 struct msdosfsmount
*pmp
= VFSTOMSDOSFS(mp
);
188 pmp
->pm_gid
= argp
->gid
;
189 pmp
->pm_uid
= argp
->uid
;
190 pmp
->pm_mask
= argp
->mask
& ALLPERMS
;
191 pmp
->pm_dirmask
= argp
->dirmask
& ALLPERMS
;
192 pmp
->pm_gmtoff
= argp
->gmtoff
;
193 pmp
->pm_flags
|= argp
->flags
& MSDOSFSMNT_MNTOPT
;
196 * GEMDOS knows nothing about win95 long filenames
198 if (pmp
->pm_flags
& MSDOSFSMNT_GEMDOSFS
)
199 pmp
->pm_flags
|= MSDOSFSMNT_NOWIN95
;
201 if (pmp
->pm_flags
& MSDOSFSMNT_NOWIN95
)
202 pmp
->pm_flags
|= MSDOSFSMNT_SHORTNAME
;
203 else if (!(pmp
->pm_flags
&
204 (MSDOSFSMNT_SHORTNAME
| MSDOSFSMNT_LONGNAME
))) {
208 * Try to divine whether to support Win'95 long filenames
211 pmp
->pm_flags
|= MSDOSFSMNT_LONGNAME
;
213 if ((error
= msdosfs_root(mp
, &rtvp
)) != 0)
215 pmp
->pm_flags
|= findwin95(VTODE(rtvp
))
216 ? MSDOSFSMNT_LONGNAME
217 : MSDOSFSMNT_SHORTNAME
;
222 mp
->mnt_stat
.f_namemax
= MSDOSFS_NAMEMAX(pmp
);
228 msdosfs_mountroot(void)
231 struct lwp
*l
= curlwp
; /* XXX */
233 struct msdosfs_args args
;
235 if (device_class(root_device
) != DV_DISK
)
238 if ((error
= vfs_rootmountalloc(MOUNT_MSDOS
, "root_device", &mp
))) {
243 args
.flags
= MSDOSFSMNT_VERSIONED
;
247 args
.version
= MSDOSFSMNT_VERSION
;
250 if ((error
= msdosfs_mountfs(rootvp
, mp
, l
, &args
)) != 0) {
251 vfs_unbusy(mp
, false, NULL
);
256 if ((error
= update_mp(mp
, &args
)) != 0) {
257 (void)msdosfs_unmount(mp
, 0);
258 vfs_unbusy(mp
, false, NULL
);
264 mountlist_append(mp
);
265 (void)msdosfs_statvfs(mp
, &mp
->mnt_stat
);
266 vfs_unbusy(mp
, false, NULL
);
271 * mp - path - addr in user space of mount point (ie /usr or whatever)
272 * data - addr in user space of mount params including the name of the block
273 * special file to treat as a filesystem.
276 msdosfs_mount(struct mount
*mp
, const char *path
, void *data
, size_t *data_len
)
278 struct lwp
*l
= curlwp
;
279 struct vnode
*devvp
; /* vnode for blk device to mount */
280 struct msdosfs_args
*args
= data
; /* holds data from mount request */
281 /* msdosfs specific mount control block */
282 struct msdosfsmount
*pmp
= NULL
;
288 if (*data_len
< sizeof *args
)
291 if (mp
->mnt_flag
& MNT_GETARGS
) {
292 pmp
= VFSTOMSDOSFS(mp
);
296 args
->uid
= pmp
->pm_uid
;
297 args
->gid
= pmp
->pm_gid
;
298 args
->mask
= pmp
->pm_mask
;
299 args
->flags
= pmp
->pm_flags
;
300 args
->version
= MSDOSFSMNT_VERSION
;
301 args
->dirmask
= pmp
->pm_dirmask
;
302 args
->gmtoff
= pmp
->pm_gmtoff
;
303 *data_len
= sizeof *args
;
308 * If not versioned (i.e. using old mount_msdos(8)), fill in
309 * the additional structure items with suitable defaults.
311 if ((args
->flags
& MSDOSFSMNT_VERSIONED
) == 0) {
313 args
->dirmask
= args
->mask
;
317 * Reset GMT offset for pre-v3 mount structure args.
319 if (args
->version
< 3)
323 * If updating, check whether changing from read-only to
324 * read/write; if there is no device name, that's all we do.
326 if (mp
->mnt_flag
& MNT_UPDATE
) {
327 pmp
= VFSTOMSDOSFS(mp
);
329 if (!(pmp
->pm_flags
& MSDOSFSMNT_RONLY
) &&
330 (mp
->mnt_flag
& MNT_RDONLY
)) {
332 if (mp
->mnt_flag
& MNT_FORCE
)
334 error
= vflush(mp
, NULLVP
, flags
);
336 if (!error
&& (mp
->mnt_flag
& MNT_RELOAD
))
337 /* not yet implemented */
340 DPRINTF("vflush %d", error
);
343 if ((pmp
->pm_flags
& MSDOSFSMNT_RONLY
) &&
344 (mp
->mnt_iflag
& IMNT_WANTRDWR
)) {
346 * If upgrade to read-write by non-root, then verify
347 * that user has necessary permissions on the device.
349 * Permission to update a mount is checked higher, so
350 * here we presume updating the mount is okay (for
351 * example, as far as securelevel goes) which leaves us
352 * with the normal check.
354 devvp
= pmp
->pm_devvp
;
355 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
356 error
= kauth_authorize_system(l
->l_cred
,
357 KAUTH_SYSTEM_MOUNT
, KAUTH_REQ_SYSTEM_MOUNT_DEVICE
,
358 mp
, devvp
, KAUTH_ARG(VREAD
| VWRITE
));
360 DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error
);
364 pmp
->pm_flags
&= ~MSDOSFSMNT_RONLY
;
366 if (args
->fspec
== NULL
) {
367 DPRINTF("missing fspec");
372 * Not an update, or updating the name: look up the name
373 * and verify that it refers to a sensible block device.
375 error
= namei_simple_user(args
->fspec
,
376 NSM_FOLLOW_NOEMULROOT
, &devvp
);
378 DPRINTF("namei %d", error
);
382 if (devvp
->v_type
!= VBLK
) {
383 DPRINTF("not block");
387 if (bdevsw_lookup(devvp
->v_rdev
) == NULL
) {
388 DPRINTF("no block switch");
393 * If mount by non-root, then verify that user has necessary
394 * permissions on the device.
397 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0)
398 accessmode
|= VWRITE
;
399 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
400 error
= kauth_authorize_system(l
->l_cred
, KAUTH_SYSTEM_MOUNT
,
401 KAUTH_REQ_SYSTEM_MOUNT_DEVICE
, mp
, devvp
, KAUTH_ARG(accessmode
));
404 DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error
);
408 if ((mp
->mnt_flag
& MNT_UPDATE
) == 0) {
411 if (mp
->mnt_flag
& MNT_RDONLY
)
414 xflags
= FREAD
|FWRITE
;
415 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
416 error
= VOP_OPEN(devvp
, xflags
, FSCRED
);
419 DPRINTF("VOP_OPEN %d", error
);
422 error
= msdosfs_mountfs(devvp
, mp
, l
, args
);
424 DPRINTF("msdosfs_mountfs %d", error
);
425 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
426 (void) VOP_CLOSE(devvp
, xflags
, NOCRED
);
430 #ifdef MSDOSFS_DEBUG /* only needed for the printf below */
431 pmp
= VFSTOMSDOSFS(mp
);
435 if (devvp
!= pmp
->pm_devvp
) {
436 DPRINTF("devvp %p pmp %p", devvp
, pmp
->pm_devvp
);
437 return (EINVAL
); /* needs translation */
440 if ((error
= update_mp(mp
, args
)) != 0) {
441 msdosfs_unmount(mp
, MNT_FORCE
);
442 DPRINTF("update_mp %d", error
);
447 printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp
, pmp
, pmp
->pm_inusemap
);
449 return set_statvfs_info(path
, UIO_USERSPACE
, args
->fspec
, UIO_USERSPACE
,
450 mp
->mnt_op
->vfs_name
, mp
, l
);
458 msdosfs_mountfs(struct vnode
*devvp
, struct mount
*mp
, struct lwp
*l
, struct msdosfs_args
*argp
)
460 struct msdosfsmount
*pmp
;
462 dev_t dev
= devvp
->v_rdev
;
463 union bootsector
*bsp
;
464 struct byte_bpb33
*b33
;
465 struct byte_bpb50
*b50
;
466 struct byte_bpb710
*b710
;
468 int ronly
, error
, BlkPerSec
;
472 /* Flush out any old buffers remaining from a previous use. */
473 if ((error
= vinvalbuf(devvp
, V_SAVE
, l
->l_cred
, l
, 0, 0)) != 0)
476 ronly
= (mp
->mnt_flag
& MNT_RDONLY
) != 0;
478 bp
= NULL
; /* both used in error_exit */
481 error
= fstrans_mount(mp
);
485 error
= getdisksize(devvp
, &psize
, &secsize
);
487 if (argp
->flags
& MSDOSFSMNT_GEMDOSFS
)
490 /* ok, so it failed. we most likely don't need the info */
495 if (secsize
< DEV_BSIZE
) {
496 DPRINTF("Invalid block secsize (%d < DEV_BSIZE)", secsize
);
501 if (argp
->flags
& MSDOSFSMNT_GEMDOSFS
) {
502 if (secsize
!= GEMDOSFS_BSIZE
) {
503 DPRINTF("Invalid block secsize %d for GEMDOS", secsize
);
510 * Read the boot sector of the filesystem, and then check the
511 * boot signature. If not a dos boot sector then error out.
513 if (secsize
< sizeof(*b50
)) {
514 DPRINTF("50 bootsec %u\n", secsize
);
518 if ((error
= bread(devvp
, 0, secsize
, 0, &bp
)) != 0)
520 bsp
= (union bootsector
*)bp
->b_data
;
521 b33
= (struct byte_bpb33
*)bsp
->bs33
.bsBPB
;
522 b50
= (struct byte_bpb50
*)bsp
->bs50
.bsBPB
;
523 b710
= (struct byte_bpb710
*)bsp
->bs710
.bsBPB
;
525 if (!(argp
->flags
& MSDOSFSMNT_GEMDOSFS
)) {
526 if (bsp
->bs50
.bsBootSectSig0
!= BOOTSIG0
527 || bsp
->bs50
.bsBootSectSig1
!= BOOTSIG1
) {
528 DPRINTF("bootsig0 %d bootsig1 %d",
529 bsp
->bs50
.bsBootSectSig0
,
530 bsp
->bs50
.bsBootSectSig1
);
536 pmp
= malloc(sizeof(*pmp
), M_MSDOSFSMNT
, M_WAITOK
|M_ZERO
);
540 * Compute several useful quantities from the bpb in the
541 * bootsector. Copy in the dos 5 variant of the bpb then fix up
542 * the fields that are different between dos 5 and dos 3.3.
544 SecPerClust
= b50
->bpbSecPerClust
;
545 pmp
->pm_BytesPerSec
= getushort(b50
->bpbBytesPerSec
);
546 pmp
->pm_ResSectors
= getushort(b50
->bpbResSectors
);
547 pmp
->pm_FATs
= b50
->bpbFATs
;
548 pmp
->pm_RootDirEnts
= getushort(b50
->bpbRootDirEnts
);
549 pmp
->pm_Sectors
= getushort(b50
->bpbSectors
);
550 pmp
->pm_FATsecs
= getushort(b50
->bpbFATsecs
);
551 pmp
->pm_SecPerTrack
= getushort(b50
->bpbSecPerTrack
);
552 pmp
->pm_Heads
= getushort(b50
->bpbHeads
);
553 pmp
->pm_Media
= b50
->bpbMedia
;
555 if (pmp
->pm_Sectors
== 0) {
556 pmp
->pm_HiddenSects
= getulong(b50
->bpbHiddenSecs
);
557 pmp
->pm_HugeSectors
= getulong(b50
->bpbHugeSectors
);
559 if (secsize
< sizeof(*b33
)) {
560 DPRINTF("33 bootsec %u\n", secsize
);
564 pmp
->pm_HiddenSects
= getushort(b33
->bpbHiddenSecs
);
565 pmp
->pm_HugeSectors
= pmp
->pm_Sectors
;
569 * Sanity checks, from the FAT specification:
570 * - sectors per cluster: >= 1, power of 2
571 * - logical sector size: >= 1, power of 2
572 * - cluster size: <= max FS block size
573 * - number of sectors: >= 1
575 if ((SecPerClust
== 0) || !powerof2(SecPerClust
) ||
576 (pmp
->pm_BytesPerSec
== 0) || !powerof2(pmp
->pm_BytesPerSec
) ||
577 (SecPerClust
* pmp
->pm_BytesPerSec
> MAXBSIZE
) ||
578 (pmp
->pm_HugeSectors
== 0)) {
579 DPRINTF("consistency checks");
584 if (!(argp
->flags
& MSDOSFSMNT_GEMDOSFS
) &&
585 (pmp
->pm_SecPerTrack
> 63)) {
586 DPRINTF("SecPerTrack %d", pmp
->pm_SecPerTrack
);
591 if (pmp
->pm_RootDirEnts
== 0) {
592 if (secsize
< sizeof(*b710
)) {
593 DPRINTF("710 bootsec %u\n", secsize
);
597 unsigned short FSVers
= getushort(b710
->bpbFSVers
);
598 unsigned short ExtFlags
= getushort(b710
->bpbExtFlags
);
600 * Some say that bsBootSectSig[23] must be zero, but
601 * Windows does not require this and some digital cameras
602 * do not set these to zero. Therefore, do not insist.
604 if (pmp
->pm_Sectors
|| pmp
->pm_FATsecs
|| FSVers
) {
605 DPRINTF("Sectors %d FATsecs %lu FSVers %d",
606 pmp
->pm_Sectors
, pmp
->pm_FATsecs
, FSVers
);
610 pmp
->pm_fatmask
= FAT32_MASK
;
613 pmp
->pm_FATsecs
= getulong(b710
->bpbBigFATsecs
);
615 /* Mirroring is enabled if the FATMIRROR bit is not set. */
616 if ((ExtFlags
& FATMIRROR
) == 0)
617 pmp
->pm_flags
|= MSDOSFS_FATMIRROR
;
619 pmp
->pm_curfat
= ExtFlags
& FATNUM
;
621 pmp
->pm_flags
|= MSDOSFS_FATMIRROR
;
623 if (argp
->flags
& MSDOSFSMNT_GEMDOSFS
) {
625 /* GEMDOS doesn't know FAT32. */
626 DPRINTF("FAT32 for GEMDOS");
632 * Check a few values (could do some more):
633 * - logical sector size: >= block size
634 * - number of sectors: <= size of partition
636 if ((pmp
->pm_BytesPerSec
< GEMDOSFS_BSIZE
) ||
637 (pmp
->pm_HugeSectors
*
638 (pmp
->pm_BytesPerSec
/ GEMDOSFS_BSIZE
) > psize
)) {
639 DPRINTF("consistency checks for GEMDOS");
644 * XXX - Many parts of the msdosfs driver seem to assume that
645 * the number of bytes per logical sector (BytesPerSec) will
646 * always be the same as the number of bytes per disk block
647 * Let's pretend it is.
649 BlkPerSec
= pmp
->pm_BytesPerSec
/ GEMDOSFS_BSIZE
;
650 pmp
->pm_BytesPerSec
= GEMDOSFS_BSIZE
;
651 pmp
->pm_HugeSectors
*= BlkPerSec
;
652 pmp
->pm_HiddenSects
*= BlkPerSec
;
653 pmp
->pm_ResSectors
*= BlkPerSec
;
654 pmp
->pm_Sectors
*= BlkPerSec
;
655 pmp
->pm_FATsecs
*= BlkPerSec
;
656 SecPerClust
*= BlkPerSec
;
659 /* Check that fs has nonzero FAT size */
660 if (pmp
->pm_FATsecs
== 0) {
661 DPRINTF("FATsecs is 0");
666 pmp
->pm_fatblk
= pmp
->pm_ResSectors
;
668 if (secsize
< sizeof(*b710
)) {
669 DPRINTF("710 bootsec %u\n", secsize
);
673 pmp
->pm_rootdirblk
= getulong(b710
->bpbRootClust
);
674 pmp
->pm_firstcluster
= pmp
->pm_fatblk
675 + (pmp
->pm_FATs
* pmp
->pm_FATsecs
);
676 pmp
->pm_fsinfo
= getushort(b710
->bpbFSInfo
);
678 pmp
->pm_rootdirblk
= pmp
->pm_fatblk
+
679 (pmp
->pm_FATs
* pmp
->pm_FATsecs
);
680 pmp
->pm_rootdirsize
= (pmp
->pm_RootDirEnts
* sizeof(struct direntry
)
681 + pmp
->pm_BytesPerSec
- 1)
682 / pmp
->pm_BytesPerSec
;/* in sectors */
683 pmp
->pm_firstcluster
= pmp
->pm_rootdirblk
+ pmp
->pm_rootdirsize
;
686 pmp
->pm_nmbrofclusters
= (pmp
->pm_HugeSectors
- pmp
->pm_firstcluster
) /
688 pmp
->pm_maxcluster
= pmp
->pm_nmbrofclusters
+ 1;
689 pmp
->pm_fatsize
= pmp
->pm_FATsecs
* pmp
->pm_BytesPerSec
;
691 if (argp
->flags
& MSDOSFSMNT_GEMDOSFS
) {
692 if (pmp
->pm_nmbrofclusters
<= (0xff0 - 2)) {
693 pmp
->pm_fatmask
= FAT12_MASK
;
697 pmp
->pm_fatmask
= FAT16_MASK
;
701 } else if (pmp
->pm_fatmask
== 0) {
702 if (pmp
->pm_maxcluster
703 <= ((CLUST_RSRVD
- CLUST_FIRST
) & FAT12_MASK
)) {
705 * This will usually be a floppy disk. This size makes
706 * sure that one FAT entry will not be split across
709 pmp
->pm_fatmask
= FAT12_MASK
;
713 pmp
->pm_fatmask
= FAT16_MASK
;
719 pmp
->pm_fatblocksize
= 3 * pmp
->pm_BytesPerSec
;
721 pmp
->pm_fatblocksize
= MAXBSIZE
;
723 pmp
->pm_fatblocksec
= pmp
->pm_fatblocksize
/ pmp
->pm_BytesPerSec
;
724 pmp
->pm_bnshift
= ffs(pmp
->pm_BytesPerSec
) - 1;
727 * Compute mask and shift value for isolating cluster relative byte
728 * offsets and cluster numbers from a file offset.
730 pmp
->pm_bpcluster
= SecPerClust
* pmp
->pm_BytesPerSec
;
731 pmp
->pm_crbomask
= pmp
->pm_bpcluster
- 1;
732 pmp
->pm_cnshift
= ffs(pmp
->pm_bpcluster
) - 1;
735 * Check for valid cluster size
736 * must be a power of 2
738 if (pmp
->pm_bpcluster
^ (1 << pmp
->pm_cnshift
)) {
739 DPRINTF("bpcluster %lu cnshift %lu", pmp
->pm_bpcluster
,
746 * Cluster size must be within limit of MAXBSIZE.
747 * Many FAT filesystems will not have clusters larger than
748 * 32KiB due to limits in Windows versions before Vista.
750 if (pmp
->pm_bpcluster
> MAXBSIZE
) {
751 DPRINTF("bpcluster %lu > MAXBSIZE %d",
752 pmp
->pm_bpcluster
, MAXBSIZE
);
758 * Release the bootsector buffer.
766 if (pmp
->pm_fsinfo
) {
768 const int rdsz
= roundup(sizeof(*fp
), pmp
->pm_BytesPerSec
);
771 * XXX If the fsinfo block is stored on media with
772 * 2KB or larger sectors, is the fsinfo structure
773 * padded at the end or in the middle?
775 if ((error
= bread(devvp
, de_bn2kb(pmp
, pmp
->pm_fsinfo
),
778 fp
= (struct fsinfo
*)bp
->b_data
;
779 if (!memcmp(fp
->fsisig1
, "RRaA", 4)
780 && !memcmp(fp
->fsisig2
, "rrAa", 4)
781 && !memcmp(fp
->fsisig3
, "\0\0\125\252", 4)
782 && !memcmp(fp
->fsisig4
, "\0\0\125\252", 4))
783 pmp
->pm_nxtfree
= getulong(fp
->fsinxtfree
);
791 * Check and validate (or perhaps invalidate?) the fsinfo structure?
794 if (pmp
->pm_fsinfo
) {
795 if ((pmp
->pm_nxtfree
== 0xffffffffUL
) ||
796 (pmp
->pm_nxtfree
> pmp
->pm_maxcluster
))
801 * Allocate memory for the bitmap of allocated clusters, and then
804 pmp
->pm_inusemap
= malloc(((pmp
->pm_maxcluster
+ N_INUSEBITS
)
806 * sizeof(*pmp
->pm_inusemap
),
807 M_MSDOSFSFAT
, M_WAITOK
);
810 * fillinusemap() needs pm_devvp.
813 pmp
->pm_devvp
= devvp
;
816 * Have the inuse map filled in.
818 if ((error
= fillinusemap(pmp
)) != 0) {
819 DPRINTF("fillinusemap %d", error
);
824 * If they want FAT updates to be synchronous then let them suffer
825 * the performance degradation in exchange for the on disk copy of
826 * the FAT being correct just about all the time. I suppose this
827 * would be a good thing to turn on if the kernel is still flakey.
829 if (mp
->mnt_flag
& MNT_SYNCHRONOUS
)
830 pmp
->pm_flags
|= MSDOSFSMNT_WAITONFAT
;
836 pmp
->pm_flags
|= MSDOSFSMNT_RONLY
;
840 mp
->mnt_stat
.f_fsidx
.__fsid_val
[0] = (long)dev
;
841 mp
->mnt_stat
.f_fsidx
.__fsid_val
[1] = makefstype(MOUNT_MSDOS
);
842 mp
->mnt_stat
.f_fsid
= mp
->mnt_stat
.f_fsidx
.__fsid_val
[0];
843 mp
->mnt_stat
.f_namemax
= MSDOSFS_NAMEMAX(pmp
);
844 mp
->mnt_flag
|= MNT_LOCAL
;
845 mp
->mnt_dev_bshift
= pmp
->pm_bnshift
;
846 mp
->mnt_fs_bshift
= pmp
->pm_cnshift
;
849 * If we ever do quotas for DOS filesystems this would be a place
850 * to fill in the info in the msdosfsmount structure. You dolt,
851 * quotas on dos filesystems make no sense because files have no
852 * owners on dos filesystems. of course there is some empty space
853 * in the directory entry where we could put uid's and gid's.
856 spec_node_setmountedfs(devvp
, mp
);
865 if (pmp
->pm_inusemap
)
866 free(pmp
->pm_inusemap
, M_MSDOSFSFAT
);
867 free(pmp
, M_MSDOSFSMNT
);
874 msdosfs_start(struct mount
*mp
, int flags
)
881 * Unmount the filesystem described by mp.
884 msdosfs_unmount(struct mount
*mp
, int mntflags
)
886 struct msdosfsmount
*pmp
;
890 if (mntflags
& MNT_FORCE
)
892 if ((error
= vflush(mp
, NULLVP
, flags
)) != 0)
894 pmp
= VFSTOMSDOSFS(mp
);
895 if (pmp
->pm_devvp
->v_type
!= VBAD
)
896 spec_node_setmountedfs(pmp
->pm_devvp
, NULL
);
899 struct vnode
*vp
= pmp
->pm_devvp
;
901 printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
902 printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
903 vp
->v_vflag
| vp
->v_iflag
| vp
->v_uflag
, vp
->v_usecount
,
904 vp
->v_writecount
, vp
->v_holdcnt
);
905 printf("mount %p, op %p\n",
906 vp
->v_mount
, vp
->v_op
);
907 printf("freef %p, freeb %p, mount %p\n",
908 vp
->v_freelist
.tqe_next
, vp
->v_freelist
.tqe_prev
,
910 printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
911 vp
->v_cleanblkhd
.lh_first
,
912 vp
->v_dirtyblkhd
.lh_first
,
913 vp
->v_numoutput
, vp
->v_type
);
914 printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
915 vp
->v_socket
, vp
->v_tag
,
916 ((u_int
*)vp
->v_data
)[0],
917 ((u_int
*)vp
->v_data
)[1]);
920 vn_lock(pmp
->pm_devvp
, LK_EXCLUSIVE
| LK_RETRY
);
921 (void) VOP_CLOSE(pmp
->pm_devvp
,
922 pmp
->pm_flags
& MSDOSFSMNT_RONLY
? FREAD
: FREAD
|FWRITE
, NOCRED
);
924 msdosfs_fh_destroy(pmp
);
925 free(pmp
->pm_inusemap
, M_MSDOSFSFAT
);
926 free(pmp
, M_MSDOSFSMNT
);
928 mp
->mnt_flag
&= ~MNT_LOCAL
;
934 msdosfs_root(struct mount
*mp
, struct vnode
**vpp
)
936 struct msdosfsmount
*pmp
= VFSTOMSDOSFS(mp
);
940 printf("msdosfs_root(); mp %p, pmp %p\n", mp
, pmp
);
942 if ((error
= deget(pmp
, MSDOSFSROOT
, MSDOSFSROOT_OFS
, vpp
)) != 0)
944 error
= vn_lock(*vpp
, LK_EXCLUSIVE
);
954 msdosfs_statvfs(struct mount
*mp
, struct statvfs
*sbp
)
956 struct msdosfsmount
*pmp
;
958 pmp
= VFSTOMSDOSFS(mp
);
959 sbp
->f_bsize
= pmp
->pm_bpcluster
;
960 sbp
->f_frsize
= sbp
->f_bsize
;
961 sbp
->f_iosize
= pmp
->pm_bpcluster
;
962 sbp
->f_blocks
= pmp
->pm_nmbrofclusters
;
963 sbp
->f_bfree
= pmp
->pm_freeclustercount
;
964 sbp
->f_bavail
= pmp
->pm_freeclustercount
;
966 sbp
->f_files
= pmp
->pm_RootDirEnts
; /* XXX */
967 sbp
->f_ffree
= 0; /* what to put in here? */
968 sbp
->f_favail
= 0; /* what to put in here? */
970 copy_statvfs_info(sbp
, mp
);
974 struct msdosfs_sync_ctx
{
979 msdosfs_sync_selector(void *cl
, struct vnode
*vp
)
981 struct msdosfs_sync_ctx
*c
= cl
;
985 if (c
->waitfor
== MNT_LAZY
|| vp
->v_type
== VNON
||
986 dep
== NULL
|| (((dep
->de_flag
&
987 (DE_ACCESS
| DE_CREATE
| DE_UPDATE
| DE_MODIFIED
)) == 0) &&
988 (LIST_EMPTY(&vp
->v_dirtyblkhd
) &&
989 UVM_OBJ_IS_CLEAN(&vp
->v_uobj
))))
995 msdosfs_sync(struct mount
*mp
, int waitfor
, kauth_cred_t cred
)
998 struct vnode_iterator
*marker
;
999 struct msdosfsmount
*pmp
= VFSTOMSDOSFS(mp
);
1000 int error
, allerror
= 0;
1001 struct msdosfs_sync_ctx ctx
;
1004 * If we ever switch to not updating all of the FATs all the time,
1005 * this would be the place to update them from the first one.
1007 if (pmp
->pm_fmod
!= 0) {
1008 if (pmp
->pm_flags
& MSDOSFSMNT_RONLY
)
1009 panic("msdosfs_sync: rofs mod");
1011 /* update FATs here */
1014 fstrans_start(mp
, FSTRANS_SHARED
);
1016 * Write back each (modified) denode.
1018 vfs_vnode_iterator_init(mp
, &marker
);
1019 ctx
.waitfor
= waitfor
;
1020 while ((vp
= vfs_vnode_iterator_next(marker
, msdosfs_sync_selector
,
1023 error
= vn_lock(vp
, LK_EXCLUSIVE
);
1028 if ((error
= VOP_FSYNC(vp
, cred
,
1029 waitfor
== MNT_WAIT
? FSYNC_WAIT
: 0, 0, 0)) != 0)
1033 vfs_vnode_iterator_destroy(marker
);
1036 * Force stale file system control information to be flushed.
1038 if ((error
= VOP_FSYNC(pmp
->pm_devvp
, cred
,
1039 waitfor
== MNT_WAIT
? FSYNC_WAIT
: 0, 0, 0)) != 0)
1046 msdosfs_fhtovp(struct mount
*mp
, struct fid
*fhp
, struct vnode
**vpp
)
1048 struct msdosfsmount
*pmp
= VFSTOMSDOSFS(mp
);
1053 if (fhp
->fid_len
!= sizeof(struct defid
)) {
1054 DPRINTF("fid_len %d %zd", fhp
->fid_len
, sizeof(struct defid
));
1057 memcpy(&defh
, fhp
, sizeof(defh
));
1058 error
= msdosfs_fh_lookup(pmp
, defh
.defid_dirclust
, defh
.defid_dirofs
,
1060 if (error
== 0 && gen
!= defh
.defid_gen
)
1066 error
= deget(pmp
, defh
.defid_dirclust
, defh
.defid_dirofs
, vpp
);
1068 DPRINTF("deget %d", error
);
1072 error
= vn_lock(*vpp
, LK_EXCLUSIVE
);
1082 msdosfs_vptofh(struct vnode
*vp
, struct fid
*fhp
, size_t *fh_size
)
1084 struct msdosfsmount
*pmp
= VFSTOMSDOSFS(vp
->v_mount
);
1089 if (*fh_size
< sizeof(struct defid
)) {
1090 *fh_size
= sizeof(struct defid
);
1093 *fh_size
= sizeof(struct defid
);
1095 memset(&defh
, 0, sizeof(defh
));
1096 defh
.defid_len
= sizeof(struct defid
);
1097 defh
.defid_dirclust
= dep
->de_dirclust
;
1098 defh
.defid_dirofs
= dep
->de_diroffset
;
1099 error
= msdosfs_fh_enter(pmp
, dep
->de_dirclust
, dep
->de_diroffset
,
1102 memcpy(fhp
, &defh
, sizeof(defh
));
1107 msdosfs_vget(struct mount
*mp
, ino_t ino
,
1111 return (EOPNOTSUPP
);
1115 msdosfs_suspendctl(struct mount
*mp
, int cmd
)
1118 struct lwp
*l
= curlwp
;
1121 case SUSPEND_SUSPEND
:
1122 if ((error
= fstrans_setstate(mp
, FSTRANS_SUSPENDING
)) != 0)
1124 error
= msdosfs_sync(mp
, MNT_WAIT
, l
->l_proc
->p_cred
);
1126 error
= fstrans_setstate(mp
, FSTRANS_SUSPENDED
);
1128 (void) fstrans_setstate(mp
, FSTRANS_NORMAL
);
1133 case SUSPEND_RESUME
:
1134 return fstrans_setstate(mp
, FSTRANS_NORMAL
);