1*0a6a1f1dSLionel Sambuc /* $NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $ */
284d9c625SLionel Sambuc
384d9c625SLionel Sambuc /*-
484d9c625SLionel Sambuc * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
584d9c625SLionel Sambuc * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
684d9c625SLionel Sambuc * All rights reserved.
784d9c625SLionel Sambuc * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
884d9c625SLionel Sambuc *
984d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without
1084d9c625SLionel Sambuc * modification, are permitted provided that the following conditions
1184d9c625SLionel Sambuc * are met:
1284d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
1384d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer.
1484d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
1584d9c625SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
1684d9c625SLionel Sambuc * documentation and/or other materials provided with the distribution.
1784d9c625SLionel Sambuc * 3. All advertising materials mentioning features or use of this software
1884d9c625SLionel Sambuc * must display the following acknowledgement:
1984d9c625SLionel Sambuc * This product includes software developed by TooLs GmbH.
2084d9c625SLionel Sambuc * 4. The name of TooLs GmbH may not be used to endorse or promote products
2184d9c625SLionel Sambuc * derived from this software without specific prior written permission.
2284d9c625SLionel Sambuc *
2384d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2484d9c625SLionel Sambuc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2584d9c625SLionel Sambuc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2684d9c625SLionel Sambuc * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2784d9c625SLionel Sambuc * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2884d9c625SLionel Sambuc * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2984d9c625SLionel Sambuc * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3084d9c625SLionel Sambuc * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3184d9c625SLionel Sambuc * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3284d9c625SLionel Sambuc * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3384d9c625SLionel Sambuc */
3484d9c625SLionel Sambuc /*
3584d9c625SLionel Sambuc * Written by Paul Popelka (paulp@uts.amdahl.com)
3684d9c625SLionel Sambuc *
3784d9c625SLionel Sambuc * You can do anything you want with this software, just don't say you wrote
3884d9c625SLionel Sambuc * it, and don't remove this notice.
3984d9c625SLionel Sambuc *
4084d9c625SLionel Sambuc * This software is provided "as is".
4184d9c625SLionel Sambuc *
4284d9c625SLionel Sambuc * The author supplies this software to be publicly redistributed on the
4384d9c625SLionel Sambuc * understanding that the author is not responsible for the correct
4484d9c625SLionel Sambuc * functioning of this software in any circumstances and is not liable for
4584d9c625SLionel Sambuc * any damages caused by this software.
4684d9c625SLionel Sambuc *
4784d9c625SLionel Sambuc * October 1992
4884d9c625SLionel Sambuc */
4984d9c625SLionel Sambuc
5084d9c625SLionel Sambuc #include <sys/cdefs.h>
51*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.118 2015/03/28 19:24:05 maxv Exp $");
5284d9c625SLionel Sambuc
5384d9c625SLionel Sambuc #if defined(_KERNEL_OPT)
5484d9c625SLionel Sambuc #include "opt_compat_netbsd.h"
5584d9c625SLionel Sambuc #endif
5684d9c625SLionel Sambuc
5784d9c625SLionel Sambuc #include <sys/param.h>
5884d9c625SLionel Sambuc #include <sys/systm.h>
5984d9c625SLionel Sambuc #include <sys/sysctl.h>
6084d9c625SLionel Sambuc #include <sys/namei.h>
6184d9c625SLionel Sambuc #include <sys/proc.h>
6284d9c625SLionel Sambuc #include <sys/kernel.h>
6384d9c625SLionel Sambuc #include <sys/vnode.h>
6484d9c625SLionel Sambuc #include <miscfs/genfs/genfs.h>
6584d9c625SLionel Sambuc #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
6684d9c625SLionel Sambuc #include <sys/mount.h>
6784d9c625SLionel Sambuc #include <sys/buf.h>
6884d9c625SLionel Sambuc #include <sys/file.h>
6984d9c625SLionel Sambuc #include <sys/device.h>
7084d9c625SLionel Sambuc #include <sys/disklabel.h>
7184d9c625SLionel Sambuc #include <sys/disk.h>
7284d9c625SLionel Sambuc #include <sys/fstrans.h>
7384d9c625SLionel Sambuc #include <sys/ioctl.h>
7484d9c625SLionel Sambuc #include <sys/malloc.h>
7584d9c625SLionel Sambuc #include <sys/dirent.h>
7684d9c625SLionel Sambuc #include <sys/stat.h>
7784d9c625SLionel Sambuc #include <sys/conf.h>
7884d9c625SLionel Sambuc #include <sys/kauth.h>
7984d9c625SLionel Sambuc #include <sys/module.h>
8084d9c625SLionel Sambuc
8184d9c625SLionel Sambuc #include <fs/msdosfs/bpb.h>
8284d9c625SLionel Sambuc #include <fs/msdosfs/bootsect.h>
8384d9c625SLionel Sambuc #include <fs/msdosfs/direntry.h>
8484d9c625SLionel Sambuc #include <fs/msdosfs/denode.h>
8584d9c625SLionel Sambuc #include <fs/msdosfs/msdosfsmount.h>
8684d9c625SLionel Sambuc #include <fs/msdosfs/fat.h>
8784d9c625SLionel Sambuc
8884d9c625SLionel Sambuc MODULE(MODULE_CLASS_VFS, msdos, NULL);
8984d9c625SLionel Sambuc
9084d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
91*0a6a1f1dSLionel Sambuc #define DPRINTF(fmt, ...) uprintf("%s(): " fmt "\n", __func__, ##__VA_ARGS__)
9284d9c625SLionel Sambuc #else
93*0a6a1f1dSLionel Sambuc #define DPRINTF(fmt, ...)
9484d9c625SLionel Sambuc #endif
9584d9c625SLionel Sambuc
96*0a6a1f1dSLionel Sambuc #define GEMDOSFS_BSIZE 512
97*0a6a1f1dSLionel Sambuc
9884d9c625SLionel Sambuc #define MSDOSFS_NAMEMAX(pmp) \
9984d9c625SLionel Sambuc (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12
10084d9c625SLionel Sambuc
10184d9c625SLionel Sambuc int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *,
10284d9c625SLionel Sambuc struct msdosfs_args *);
10384d9c625SLionel Sambuc
10484d9c625SLionel Sambuc static int update_mp(struct mount *, struct msdosfs_args *);
10584d9c625SLionel Sambuc
10684d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure");
10784d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOS FS FAT table");
10884d9c625SLionel Sambuc MALLOC_JUSTDEFINE(M_MSDOSFSTMP, "MSDOSFS temp", "MSDOS FS temp. structures");
10984d9c625SLionel Sambuc
11084d9c625SLionel Sambuc static struct sysctllog *msdosfs_sysctl_log;
11184d9c625SLionel Sambuc
11284d9c625SLionel Sambuc extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
11384d9c625SLionel Sambuc
11484d9c625SLionel Sambuc const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = {
11584d9c625SLionel Sambuc &msdosfs_vnodeop_opv_desc,
11684d9c625SLionel Sambuc NULL,
11784d9c625SLionel Sambuc };
11884d9c625SLionel Sambuc
11984d9c625SLionel Sambuc struct vfsops msdosfs_vfsops = {
120*0a6a1f1dSLionel Sambuc .vfs_name = MOUNT_MSDOS,
121*0a6a1f1dSLionel Sambuc .vfs_min_mount_data = sizeof (struct msdosfs_args),
122*0a6a1f1dSLionel Sambuc .vfs_mount = msdosfs_mount,
123*0a6a1f1dSLionel Sambuc .vfs_start = msdosfs_start,
124*0a6a1f1dSLionel Sambuc .vfs_unmount = msdosfs_unmount,
125*0a6a1f1dSLionel Sambuc .vfs_root = msdosfs_root,
126*0a6a1f1dSLionel Sambuc .vfs_quotactl = (void *)eopnotsupp,
127*0a6a1f1dSLionel Sambuc .vfs_statvfs = msdosfs_statvfs,
128*0a6a1f1dSLionel Sambuc .vfs_sync = msdosfs_sync,
129*0a6a1f1dSLionel Sambuc .vfs_vget = msdosfs_vget,
130*0a6a1f1dSLionel Sambuc .vfs_loadvnode = msdosfs_loadvnode,
131*0a6a1f1dSLionel Sambuc .vfs_fhtovp = msdosfs_fhtovp,
132*0a6a1f1dSLionel Sambuc .vfs_vptofh = msdosfs_vptofh,
133*0a6a1f1dSLionel Sambuc .vfs_init = msdosfs_init,
134*0a6a1f1dSLionel Sambuc .vfs_reinit = msdosfs_reinit,
135*0a6a1f1dSLionel Sambuc .vfs_done = msdosfs_done,
136*0a6a1f1dSLionel Sambuc .vfs_mountroot = msdosfs_mountroot,
137*0a6a1f1dSLionel Sambuc .vfs_snapshot = (void *)eopnotsupp,
138*0a6a1f1dSLionel Sambuc .vfs_extattrctl = vfs_stdextattrctl,
139*0a6a1f1dSLionel Sambuc .vfs_suspendctl = msdosfs_suspendctl,
140*0a6a1f1dSLionel Sambuc .vfs_renamelock_enter = genfs_renamelock_enter,
141*0a6a1f1dSLionel Sambuc .vfs_renamelock_exit = genfs_renamelock_exit,
142*0a6a1f1dSLionel Sambuc .vfs_fsync = (void *)eopnotsupp,
143*0a6a1f1dSLionel Sambuc .vfs_opv_descs = msdosfs_vnodeopv_descs
14484d9c625SLionel Sambuc };
14584d9c625SLionel Sambuc
14684d9c625SLionel Sambuc static int
msdos_modcmd(modcmd_t cmd,void * arg)14784d9c625SLionel Sambuc msdos_modcmd(modcmd_t cmd, void *arg)
14884d9c625SLionel Sambuc {
14984d9c625SLionel Sambuc int error;
15084d9c625SLionel Sambuc
15184d9c625SLionel Sambuc switch (cmd) {
15284d9c625SLionel Sambuc case MODULE_CMD_INIT:
15384d9c625SLionel Sambuc error = vfs_attach(&msdosfs_vfsops);
15484d9c625SLionel Sambuc if (error != 0)
15584d9c625SLionel Sambuc break;
15684d9c625SLionel Sambuc sysctl_createv(&msdosfs_sysctl_log, 0, NULL, NULL,
15784d9c625SLionel Sambuc CTLFLAG_PERMANENT,
15884d9c625SLionel Sambuc CTLTYPE_NODE, "msdosfs",
15984d9c625SLionel Sambuc SYSCTL_DESCR("MS-DOS file system"),
16084d9c625SLionel Sambuc NULL, 0, NULL, 0,
16184d9c625SLionel Sambuc CTL_VFS, 4, CTL_EOL);
16284d9c625SLionel Sambuc /*
16384d9c625SLionel Sambuc * XXX the "4" above could be dynamic, thereby eliminating one
16484d9c625SLionel Sambuc * more instance of the "number to vfs" mapping problem, but
16584d9c625SLionel Sambuc * "4" is the order as taken from sys/mount.h
16684d9c625SLionel Sambuc */
16784d9c625SLionel Sambuc break;
16884d9c625SLionel Sambuc case MODULE_CMD_FINI:
16984d9c625SLionel Sambuc error = vfs_detach(&msdosfs_vfsops);
17084d9c625SLionel Sambuc if (error != 0)
17184d9c625SLionel Sambuc break;
17284d9c625SLionel Sambuc sysctl_teardown(&msdosfs_sysctl_log);
17384d9c625SLionel Sambuc break;
17484d9c625SLionel Sambuc default:
17584d9c625SLionel Sambuc error = ENOTTY;
17684d9c625SLionel Sambuc break;
17784d9c625SLionel Sambuc }
17884d9c625SLionel Sambuc
17984d9c625SLionel Sambuc return (error);
18084d9c625SLionel Sambuc }
18184d9c625SLionel Sambuc
18284d9c625SLionel Sambuc static int
update_mp(struct mount * mp,struct msdosfs_args * argp)18384d9c625SLionel Sambuc update_mp(struct mount *mp, struct msdosfs_args *argp)
18484d9c625SLionel Sambuc {
18584d9c625SLionel Sambuc struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
18684d9c625SLionel Sambuc int error;
18784d9c625SLionel Sambuc
18884d9c625SLionel Sambuc pmp->pm_gid = argp->gid;
18984d9c625SLionel Sambuc pmp->pm_uid = argp->uid;
19084d9c625SLionel Sambuc pmp->pm_mask = argp->mask & ALLPERMS;
19184d9c625SLionel Sambuc pmp->pm_dirmask = argp->dirmask & ALLPERMS;
19284d9c625SLionel Sambuc pmp->pm_gmtoff = argp->gmtoff;
19384d9c625SLionel Sambuc pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
19484d9c625SLionel Sambuc
19584d9c625SLionel Sambuc /*
19684d9c625SLionel Sambuc * GEMDOS knows nothing about win95 long filenames
19784d9c625SLionel Sambuc */
19884d9c625SLionel Sambuc if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
19984d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
20084d9c625SLionel Sambuc
20184d9c625SLionel Sambuc if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
20284d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
20384d9c625SLionel Sambuc else if (!(pmp->pm_flags &
20484d9c625SLionel Sambuc (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
20584d9c625SLionel Sambuc struct vnode *rtvp;
20684d9c625SLionel Sambuc
20784d9c625SLionel Sambuc /*
20884d9c625SLionel Sambuc * Try to divine whether to support Win'95 long filenames
20984d9c625SLionel Sambuc */
21084d9c625SLionel Sambuc if (FAT32(pmp))
21184d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
21284d9c625SLionel Sambuc else {
21384d9c625SLionel Sambuc if ((error = msdosfs_root(mp, &rtvp)) != 0)
21484d9c625SLionel Sambuc return error;
21584d9c625SLionel Sambuc pmp->pm_flags |= findwin95(VTODE(rtvp))
21684d9c625SLionel Sambuc ? MSDOSFSMNT_LONGNAME
21784d9c625SLionel Sambuc : MSDOSFSMNT_SHORTNAME;
21884d9c625SLionel Sambuc vput(rtvp);
21984d9c625SLionel Sambuc }
22084d9c625SLionel Sambuc }
22184d9c625SLionel Sambuc
22284d9c625SLionel Sambuc mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
22384d9c625SLionel Sambuc
22484d9c625SLionel Sambuc return 0;
22584d9c625SLionel Sambuc }
22684d9c625SLionel Sambuc
22784d9c625SLionel Sambuc int
msdosfs_mountroot(void)22884d9c625SLionel Sambuc msdosfs_mountroot(void)
22984d9c625SLionel Sambuc {
23084d9c625SLionel Sambuc struct mount *mp;
23184d9c625SLionel Sambuc struct lwp *l = curlwp; /* XXX */
23284d9c625SLionel Sambuc int error;
23384d9c625SLionel Sambuc struct msdosfs_args args;
23484d9c625SLionel Sambuc
23584d9c625SLionel Sambuc if (device_class(root_device) != DV_DISK)
23684d9c625SLionel Sambuc return (ENODEV);
23784d9c625SLionel Sambuc
23884d9c625SLionel Sambuc if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) {
23984d9c625SLionel Sambuc vrele(rootvp);
24084d9c625SLionel Sambuc return (error);
24184d9c625SLionel Sambuc }
24284d9c625SLionel Sambuc
24384d9c625SLionel Sambuc args.flags = MSDOSFSMNT_VERSIONED;
24484d9c625SLionel Sambuc args.uid = 0;
24584d9c625SLionel Sambuc args.gid = 0;
24684d9c625SLionel Sambuc args.mask = 0777;
24784d9c625SLionel Sambuc args.version = MSDOSFSMNT_VERSION;
24884d9c625SLionel Sambuc args.dirmask = 0777;
24984d9c625SLionel Sambuc
25084d9c625SLionel Sambuc if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) {
25184d9c625SLionel Sambuc vfs_unbusy(mp, false, NULL);
25284d9c625SLionel Sambuc vfs_destroy(mp);
25384d9c625SLionel Sambuc return (error);
25484d9c625SLionel Sambuc }
25584d9c625SLionel Sambuc
25684d9c625SLionel Sambuc if ((error = update_mp(mp, &args)) != 0) {
25784d9c625SLionel Sambuc (void)msdosfs_unmount(mp, 0);
25884d9c625SLionel Sambuc vfs_unbusy(mp, false, NULL);
25984d9c625SLionel Sambuc vfs_destroy(mp);
26084d9c625SLionel Sambuc vrele(rootvp);
26184d9c625SLionel Sambuc return (error);
26284d9c625SLionel Sambuc }
26384d9c625SLionel Sambuc
26484d9c625SLionel Sambuc mountlist_append(mp);
26584d9c625SLionel Sambuc (void)msdosfs_statvfs(mp, &mp->mnt_stat);
26684d9c625SLionel Sambuc vfs_unbusy(mp, false, NULL);
26784d9c625SLionel Sambuc return (0);
26884d9c625SLionel Sambuc }
26984d9c625SLionel Sambuc
27084d9c625SLionel Sambuc /*
27184d9c625SLionel Sambuc * mp - path - addr in user space of mount point (ie /usr or whatever)
27284d9c625SLionel Sambuc * data - addr in user space of mount params including the name of the block
27384d9c625SLionel Sambuc * special file to treat as a filesystem.
27484d9c625SLionel Sambuc */
27584d9c625SLionel Sambuc int
msdosfs_mount(struct mount * mp,const char * path,void * data,size_t * data_len)27684d9c625SLionel Sambuc msdosfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
27784d9c625SLionel Sambuc {
27884d9c625SLionel Sambuc struct lwp *l = curlwp;
27984d9c625SLionel Sambuc struct vnode *devvp; /* vnode for blk device to mount */
28084d9c625SLionel Sambuc struct msdosfs_args *args = data; /* holds data from mount request */
28184d9c625SLionel Sambuc /* msdosfs specific mount control block */
28284d9c625SLionel Sambuc struct msdosfsmount *pmp = NULL;
28384d9c625SLionel Sambuc int error, flags;
28484d9c625SLionel Sambuc mode_t accessmode;
28584d9c625SLionel Sambuc
286*0a6a1f1dSLionel Sambuc if (args == NULL)
287*0a6a1f1dSLionel Sambuc return EINVAL;
28884d9c625SLionel Sambuc if (*data_len < sizeof *args)
28984d9c625SLionel Sambuc return EINVAL;
29084d9c625SLionel Sambuc
29184d9c625SLionel Sambuc if (mp->mnt_flag & MNT_GETARGS) {
29284d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(mp);
29384d9c625SLionel Sambuc if (pmp == NULL)
29484d9c625SLionel Sambuc return EIO;
29584d9c625SLionel Sambuc args->fspec = NULL;
29684d9c625SLionel Sambuc args->uid = pmp->pm_uid;
29784d9c625SLionel Sambuc args->gid = pmp->pm_gid;
29884d9c625SLionel Sambuc args->mask = pmp->pm_mask;
29984d9c625SLionel Sambuc args->flags = pmp->pm_flags;
30084d9c625SLionel Sambuc args->version = MSDOSFSMNT_VERSION;
30184d9c625SLionel Sambuc args->dirmask = pmp->pm_dirmask;
30284d9c625SLionel Sambuc args->gmtoff = pmp->pm_gmtoff;
30384d9c625SLionel Sambuc *data_len = sizeof *args;
30484d9c625SLionel Sambuc return 0;
30584d9c625SLionel Sambuc }
30684d9c625SLionel Sambuc
30784d9c625SLionel Sambuc /*
30884d9c625SLionel Sambuc * If not versioned (i.e. using old mount_msdos(8)), fill in
30984d9c625SLionel Sambuc * the additional structure items with suitable defaults.
31084d9c625SLionel Sambuc */
31184d9c625SLionel Sambuc if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) {
31284d9c625SLionel Sambuc args->version = 1;
31384d9c625SLionel Sambuc args->dirmask = args->mask;
31484d9c625SLionel Sambuc }
31584d9c625SLionel Sambuc
31684d9c625SLionel Sambuc /*
31784d9c625SLionel Sambuc * Reset GMT offset for pre-v3 mount structure args.
31884d9c625SLionel Sambuc */
31984d9c625SLionel Sambuc if (args->version < 3)
32084d9c625SLionel Sambuc args->gmtoff = 0;
32184d9c625SLionel Sambuc
32284d9c625SLionel Sambuc /*
32384d9c625SLionel Sambuc * If updating, check whether changing from read-only to
32484d9c625SLionel Sambuc * read/write; if there is no device name, that's all we do.
32584d9c625SLionel Sambuc */
32684d9c625SLionel Sambuc if (mp->mnt_flag & MNT_UPDATE) {
32784d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(mp);
32884d9c625SLionel Sambuc error = 0;
32984d9c625SLionel Sambuc if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
33084d9c625SLionel Sambuc (mp->mnt_flag & MNT_RDONLY)) {
33184d9c625SLionel Sambuc flags = WRITECLOSE;
33284d9c625SLionel Sambuc if (mp->mnt_flag & MNT_FORCE)
33384d9c625SLionel Sambuc flags |= FORCECLOSE;
33484d9c625SLionel Sambuc error = vflush(mp, NULLVP, flags);
33584d9c625SLionel Sambuc }
33684d9c625SLionel Sambuc if (!error && (mp->mnt_flag & MNT_RELOAD))
33784d9c625SLionel Sambuc /* not yet implemented */
33884d9c625SLionel Sambuc error = EOPNOTSUPP;
33984d9c625SLionel Sambuc if (error) {
340*0a6a1f1dSLionel Sambuc DPRINTF("vflush %d", error);
34184d9c625SLionel Sambuc return (error);
34284d9c625SLionel Sambuc }
34384d9c625SLionel Sambuc if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
34484d9c625SLionel Sambuc (mp->mnt_iflag & IMNT_WANTRDWR)) {
34584d9c625SLionel Sambuc /*
34684d9c625SLionel Sambuc * If upgrade to read-write by non-root, then verify
34784d9c625SLionel Sambuc * that user has necessary permissions on the device.
34884d9c625SLionel Sambuc *
34984d9c625SLionel Sambuc * Permission to update a mount is checked higher, so
35084d9c625SLionel Sambuc * here we presume updating the mount is okay (for
35184d9c625SLionel Sambuc * example, as far as securelevel goes) which leaves us
35284d9c625SLionel Sambuc * with the normal check.
35384d9c625SLionel Sambuc */
35484d9c625SLionel Sambuc devvp = pmp->pm_devvp;
35584d9c625SLionel Sambuc vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
35684d9c625SLionel Sambuc error = kauth_authorize_system(l->l_cred,
35784d9c625SLionel Sambuc KAUTH_SYSTEM_MOUNT, KAUTH_REQ_SYSTEM_MOUNT_DEVICE,
35884d9c625SLionel Sambuc mp, devvp, KAUTH_ARG(VREAD | VWRITE));
35984d9c625SLionel Sambuc VOP_UNLOCK(devvp);
360*0a6a1f1dSLionel Sambuc DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
36184d9c625SLionel Sambuc if (error)
36284d9c625SLionel Sambuc return (error);
36384d9c625SLionel Sambuc
36484d9c625SLionel Sambuc pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
36584d9c625SLionel Sambuc }
36684d9c625SLionel Sambuc if (args->fspec == NULL) {
367*0a6a1f1dSLionel Sambuc DPRINTF("missing fspec");
36884d9c625SLionel Sambuc return EINVAL;
36984d9c625SLionel Sambuc }
37084d9c625SLionel Sambuc }
37184d9c625SLionel Sambuc /*
37284d9c625SLionel Sambuc * Not an update, or updating the name: look up the name
37384d9c625SLionel Sambuc * and verify that it refers to a sensible block device.
37484d9c625SLionel Sambuc */
37584d9c625SLionel Sambuc error = namei_simple_user(args->fspec,
37684d9c625SLionel Sambuc NSM_FOLLOW_NOEMULROOT, &devvp);
37784d9c625SLionel Sambuc if (error != 0) {
378*0a6a1f1dSLionel Sambuc DPRINTF("namei %d", error);
37984d9c625SLionel Sambuc return (error);
38084d9c625SLionel Sambuc }
38184d9c625SLionel Sambuc
38284d9c625SLionel Sambuc if (devvp->v_type != VBLK) {
383*0a6a1f1dSLionel Sambuc DPRINTF("not block");
38484d9c625SLionel Sambuc vrele(devvp);
38584d9c625SLionel Sambuc return (ENOTBLK);
38684d9c625SLionel Sambuc }
38784d9c625SLionel Sambuc if (bdevsw_lookup(devvp->v_rdev) == NULL) {
388*0a6a1f1dSLionel Sambuc DPRINTF("no block switch");
38984d9c625SLionel Sambuc vrele(devvp);
39084d9c625SLionel Sambuc return (ENXIO);
39184d9c625SLionel Sambuc }
39284d9c625SLionel Sambuc /*
39384d9c625SLionel Sambuc * If mount by non-root, then verify that user has necessary
39484d9c625SLionel Sambuc * permissions on the device.
39584d9c625SLionel Sambuc */
39684d9c625SLionel Sambuc accessmode = VREAD;
39784d9c625SLionel Sambuc if ((mp->mnt_flag & MNT_RDONLY) == 0)
39884d9c625SLionel Sambuc accessmode |= VWRITE;
39984d9c625SLionel Sambuc vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
40084d9c625SLionel Sambuc error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
40184d9c625SLionel Sambuc KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
40284d9c625SLionel Sambuc VOP_UNLOCK(devvp);
40384d9c625SLionel Sambuc if (error) {
404*0a6a1f1dSLionel Sambuc DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
40584d9c625SLionel Sambuc vrele(devvp);
40684d9c625SLionel Sambuc return (error);
40784d9c625SLionel Sambuc }
40884d9c625SLionel Sambuc if ((mp->mnt_flag & MNT_UPDATE) == 0) {
40984d9c625SLionel Sambuc int xflags;
41084d9c625SLionel Sambuc
41184d9c625SLionel Sambuc if (mp->mnt_flag & MNT_RDONLY)
41284d9c625SLionel Sambuc xflags = FREAD;
41384d9c625SLionel Sambuc else
41484d9c625SLionel Sambuc xflags = FREAD|FWRITE;
41584d9c625SLionel Sambuc vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
41684d9c625SLionel Sambuc error = VOP_OPEN(devvp, xflags, FSCRED);
41784d9c625SLionel Sambuc VOP_UNLOCK(devvp);
41884d9c625SLionel Sambuc if (error) {
419*0a6a1f1dSLionel Sambuc DPRINTF("VOP_OPEN %d", error);
42084d9c625SLionel Sambuc goto fail;
42184d9c625SLionel Sambuc }
42284d9c625SLionel Sambuc error = msdosfs_mountfs(devvp, mp, l, args);
42384d9c625SLionel Sambuc if (error) {
424*0a6a1f1dSLionel Sambuc DPRINTF("msdosfs_mountfs %d", error);
42584d9c625SLionel Sambuc vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
42684d9c625SLionel Sambuc (void) VOP_CLOSE(devvp, xflags, NOCRED);
42784d9c625SLionel Sambuc VOP_UNLOCK(devvp);
42884d9c625SLionel Sambuc goto fail;
42984d9c625SLionel Sambuc }
43084d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG /* only needed for the printf below */
43184d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(mp);
43284d9c625SLionel Sambuc #endif
43384d9c625SLionel Sambuc } else {
43484d9c625SLionel Sambuc vrele(devvp);
43584d9c625SLionel Sambuc if (devvp != pmp->pm_devvp) {
436*0a6a1f1dSLionel Sambuc DPRINTF("devvp %p pmp %p", devvp, pmp->pm_devvp);
43784d9c625SLionel Sambuc return (EINVAL); /* needs translation */
43884d9c625SLionel Sambuc }
43984d9c625SLionel Sambuc }
44084d9c625SLionel Sambuc if ((error = update_mp(mp, args)) != 0) {
44184d9c625SLionel Sambuc msdosfs_unmount(mp, MNT_FORCE);
442*0a6a1f1dSLionel Sambuc DPRINTF("update_mp %d", error);
44384d9c625SLionel Sambuc return error;
44484d9c625SLionel Sambuc }
44584d9c625SLionel Sambuc
44684d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
44784d9c625SLionel Sambuc printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
44884d9c625SLionel Sambuc #endif
44984d9c625SLionel Sambuc return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
45084d9c625SLionel Sambuc mp->mnt_op->vfs_name, mp, l);
45184d9c625SLionel Sambuc
45284d9c625SLionel Sambuc fail:
45384d9c625SLionel Sambuc vrele(devvp);
45484d9c625SLionel Sambuc return (error);
45584d9c625SLionel Sambuc }
45684d9c625SLionel Sambuc
45784d9c625SLionel Sambuc int
msdosfs_mountfs(struct vnode * devvp,struct mount * mp,struct lwp * l,struct msdosfs_args * argp)45884d9c625SLionel Sambuc msdosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l, struct msdosfs_args *argp)
45984d9c625SLionel Sambuc {
46084d9c625SLionel Sambuc struct msdosfsmount *pmp;
46184d9c625SLionel Sambuc struct buf *bp;
46284d9c625SLionel Sambuc dev_t dev = devvp->v_rdev;
46384d9c625SLionel Sambuc union bootsector *bsp;
46484d9c625SLionel Sambuc struct byte_bpb33 *b33;
46584d9c625SLionel Sambuc struct byte_bpb50 *b50;
46684d9c625SLionel Sambuc struct byte_bpb710 *b710;
46784d9c625SLionel Sambuc uint8_t SecPerClust;
468*0a6a1f1dSLionel Sambuc int ronly, error, BlkPerSec;
46984d9c625SLionel Sambuc uint64_t psize;
47084d9c625SLionel Sambuc unsigned secsize;
47184d9c625SLionel Sambuc
47284d9c625SLionel Sambuc /* Flush out any old buffers remaining from a previous use. */
47384d9c625SLionel Sambuc if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0)
47484d9c625SLionel Sambuc return (error);
47584d9c625SLionel Sambuc
47684d9c625SLionel Sambuc ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
47784d9c625SLionel Sambuc
47884d9c625SLionel Sambuc bp = NULL; /* both used in error_exit */
47984d9c625SLionel Sambuc pmp = NULL;
48084d9c625SLionel Sambuc
48184d9c625SLionel Sambuc error = fstrans_mount(mp);
48284d9c625SLionel Sambuc if (error)
48384d9c625SLionel Sambuc goto error_exit;
48484d9c625SLionel Sambuc
48584d9c625SLionel Sambuc error = getdisksize(devvp, &psize, &secsize);
48684d9c625SLionel Sambuc if (error) {
48784d9c625SLionel Sambuc if (argp->flags & MSDOSFSMNT_GEMDOSFS)
48884d9c625SLionel Sambuc goto error_exit;
48984d9c625SLionel Sambuc
49084d9c625SLionel Sambuc /* ok, so it failed. we most likely don't need the info */
49184d9c625SLionel Sambuc secsize = DEV_BSIZE;
49284d9c625SLionel Sambuc psize = 0;
49384d9c625SLionel Sambuc error = 0;
49484d9c625SLionel Sambuc }
495*0a6a1f1dSLionel Sambuc if (secsize < DEV_BSIZE) {
496*0a6a1f1dSLionel Sambuc DPRINTF("Invalid block secsize (%d < DEV_BSIZE)", secsize);
49784d9c625SLionel Sambuc error = EINVAL;
49884d9c625SLionel Sambuc goto error_exit;
49984d9c625SLionel Sambuc }
500*0a6a1f1dSLionel Sambuc
501*0a6a1f1dSLionel Sambuc if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
502*0a6a1f1dSLionel Sambuc if (secsize != GEMDOSFS_BSIZE) {
503*0a6a1f1dSLionel Sambuc DPRINTF("Invalid block secsize %d for GEMDOS", secsize);
504*0a6a1f1dSLionel Sambuc error = EINVAL;
505*0a6a1f1dSLionel Sambuc goto error_exit;
506*0a6a1f1dSLionel Sambuc }
507*0a6a1f1dSLionel Sambuc }
50884d9c625SLionel Sambuc
50984d9c625SLionel Sambuc /*
51084d9c625SLionel Sambuc * Read the boot sector of the filesystem, and then check the
51184d9c625SLionel Sambuc * boot signature. If not a dos boot sector then error out.
51284d9c625SLionel Sambuc */
513*0a6a1f1dSLionel Sambuc if (secsize < sizeof(*b50)) {
514*0a6a1f1dSLionel Sambuc DPRINTF("50 bootsec %u\n", secsize);
515*0a6a1f1dSLionel Sambuc error = EINVAL;
516*0a6a1f1dSLionel Sambuc goto error_exit;
517*0a6a1f1dSLionel Sambuc }
518*0a6a1f1dSLionel Sambuc if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
51984d9c625SLionel Sambuc goto error_exit;
52084d9c625SLionel Sambuc bsp = (union bootsector *)bp->b_data;
52184d9c625SLionel Sambuc b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
52284d9c625SLionel Sambuc b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
52384d9c625SLionel Sambuc b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
52484d9c625SLionel Sambuc
52584d9c625SLionel Sambuc if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
52684d9c625SLionel Sambuc if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
52784d9c625SLionel Sambuc || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
528*0a6a1f1dSLionel Sambuc DPRINTF("bootsig0 %d bootsig1 %d",
52984d9c625SLionel Sambuc bsp->bs50.bsBootSectSig0,
530*0a6a1f1dSLionel Sambuc bsp->bs50.bsBootSectSig1);
53184d9c625SLionel Sambuc error = EINVAL;
53284d9c625SLionel Sambuc goto error_exit;
53384d9c625SLionel Sambuc }
53484d9c625SLionel Sambuc }
53584d9c625SLionel Sambuc
536*0a6a1f1dSLionel Sambuc pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK|M_ZERO);
53784d9c625SLionel Sambuc pmp->pm_mountp = mp;
53884d9c625SLionel Sambuc
53984d9c625SLionel Sambuc /*
54084d9c625SLionel Sambuc * Compute several useful quantities from the bpb in the
54184d9c625SLionel Sambuc * bootsector. Copy in the dos 5 variant of the bpb then fix up
54284d9c625SLionel Sambuc * the fields that are different between dos 5 and dos 3.3.
54384d9c625SLionel Sambuc */
54484d9c625SLionel Sambuc SecPerClust = b50->bpbSecPerClust;
54584d9c625SLionel Sambuc pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
54684d9c625SLionel Sambuc pmp->pm_ResSectors = getushort(b50->bpbResSectors);
54784d9c625SLionel Sambuc pmp->pm_FATs = b50->bpbFATs;
54884d9c625SLionel Sambuc pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
54984d9c625SLionel Sambuc pmp->pm_Sectors = getushort(b50->bpbSectors);
55084d9c625SLionel Sambuc pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
55184d9c625SLionel Sambuc pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
55284d9c625SLionel Sambuc pmp->pm_Heads = getushort(b50->bpbHeads);
55384d9c625SLionel Sambuc pmp->pm_Media = b50->bpbMedia;
55484d9c625SLionel Sambuc
55584d9c625SLionel Sambuc if (pmp->pm_Sectors == 0) {
55684d9c625SLionel Sambuc pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
55784d9c625SLionel Sambuc pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
55884d9c625SLionel Sambuc } else {
559*0a6a1f1dSLionel Sambuc if (secsize < sizeof(*b33)) {
560*0a6a1f1dSLionel Sambuc DPRINTF("33 bootsec %u\n", secsize);
561*0a6a1f1dSLionel Sambuc error = EINVAL;
562*0a6a1f1dSLionel Sambuc goto error_exit;
563*0a6a1f1dSLionel Sambuc }
56484d9c625SLionel Sambuc pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
56584d9c625SLionel Sambuc pmp->pm_HugeSectors = pmp->pm_Sectors;
56684d9c625SLionel Sambuc }
56784d9c625SLionel Sambuc
568*0a6a1f1dSLionel Sambuc /*
569*0a6a1f1dSLionel Sambuc * Sanity checks, from the FAT specification:
570*0a6a1f1dSLionel Sambuc * - sectors per cluster: >= 1, power of 2
571*0a6a1f1dSLionel Sambuc * - logical sector size: >= 1, power of 2
572*0a6a1f1dSLionel Sambuc * - cluster size: <= max FS block size
573*0a6a1f1dSLionel Sambuc * - number of sectors: >= 1
574*0a6a1f1dSLionel Sambuc */
575*0a6a1f1dSLionel Sambuc if ((SecPerClust == 0) || !powerof2(SecPerClust) ||
576*0a6a1f1dSLionel Sambuc (pmp->pm_BytesPerSec == 0) || !powerof2(pmp->pm_BytesPerSec) ||
577*0a6a1f1dSLionel Sambuc (SecPerClust * pmp->pm_BytesPerSec > MAXBSIZE) ||
578*0a6a1f1dSLionel Sambuc (pmp->pm_HugeSectors == 0)) {
579*0a6a1f1dSLionel Sambuc DPRINTF("consistency checks");
580*0a6a1f1dSLionel Sambuc error = EINVAL;
581*0a6a1f1dSLionel Sambuc goto error_exit;
582*0a6a1f1dSLionel Sambuc }
583*0a6a1f1dSLionel Sambuc
584*0a6a1f1dSLionel Sambuc if (!(argp->flags & MSDOSFSMNT_GEMDOSFS) &&
585*0a6a1f1dSLionel Sambuc (pmp->pm_SecPerTrack > 63)) {
586*0a6a1f1dSLionel Sambuc DPRINTF("SecPerTrack %d", pmp->pm_SecPerTrack);
587*0a6a1f1dSLionel Sambuc error = EINVAL;
588*0a6a1f1dSLionel Sambuc goto error_exit;
589*0a6a1f1dSLionel Sambuc }
590*0a6a1f1dSLionel Sambuc
59184d9c625SLionel Sambuc if (pmp->pm_RootDirEnts == 0) {
592*0a6a1f1dSLionel Sambuc if (secsize < sizeof(*b710)) {
593*0a6a1f1dSLionel Sambuc DPRINTF("710 bootsec %u\n", secsize);
594*0a6a1f1dSLionel Sambuc error = EINVAL;
595*0a6a1f1dSLionel Sambuc goto error_exit;
596*0a6a1f1dSLionel Sambuc }
597*0a6a1f1dSLionel Sambuc unsigned short FSVers = getushort(b710->bpbFSVers);
598*0a6a1f1dSLionel Sambuc unsigned short ExtFlags = getushort(b710->bpbExtFlags);
59984d9c625SLionel Sambuc /*
60084d9c625SLionel Sambuc * Some say that bsBootSectSig[23] must be zero, but
60184d9c625SLionel Sambuc * Windows does not require this and some digital cameras
60284d9c625SLionel Sambuc * do not set these to zero. Therefore, do not insist.
60384d9c625SLionel Sambuc */
604*0a6a1f1dSLionel Sambuc if (pmp->pm_Sectors || pmp->pm_FATsecs || FSVers) {
605*0a6a1f1dSLionel Sambuc DPRINTF("Sectors %d FATsecs %lu FSVers %d",
606*0a6a1f1dSLionel Sambuc pmp->pm_Sectors, pmp->pm_FATsecs, FSVers);
60784d9c625SLionel Sambuc error = EINVAL;
60884d9c625SLionel Sambuc goto error_exit;
60984d9c625SLionel Sambuc }
61084d9c625SLionel Sambuc pmp->pm_fatmask = FAT32_MASK;
61184d9c625SLionel Sambuc pmp->pm_fatmult = 4;
61284d9c625SLionel Sambuc pmp->pm_fatdiv = 1;
61384d9c625SLionel Sambuc pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
61484d9c625SLionel Sambuc
615*0a6a1f1dSLionel Sambuc /* Mirroring is enabled if the FATMIRROR bit is not set. */
616*0a6a1f1dSLionel Sambuc if ((ExtFlags & FATMIRROR) == 0)
61784d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFS_FATMIRROR;
61884d9c625SLionel Sambuc else
619*0a6a1f1dSLionel Sambuc pmp->pm_curfat = ExtFlags & FATNUM;
62084d9c625SLionel Sambuc } else
62184d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFS_FATMIRROR;
62284d9c625SLionel Sambuc
62384d9c625SLionel Sambuc if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
62484d9c625SLionel Sambuc if (FAT32(pmp)) {
625*0a6a1f1dSLionel Sambuc /* GEMDOS doesn't know FAT32. */
626*0a6a1f1dSLionel Sambuc DPRINTF("FAT32 for GEMDOS");
62784d9c625SLionel Sambuc error = EINVAL;
62884d9c625SLionel Sambuc goto error_exit;
62984d9c625SLionel Sambuc }
63084d9c625SLionel Sambuc
63184d9c625SLionel Sambuc /*
63284d9c625SLionel Sambuc * Check a few values (could do some more):
633*0a6a1f1dSLionel Sambuc * - logical sector size: >= block size
634*0a6a1f1dSLionel Sambuc * - number of sectors: <= size of partition
63584d9c625SLionel Sambuc */
636*0a6a1f1dSLionel Sambuc if ((pmp->pm_BytesPerSec < GEMDOSFS_BSIZE) ||
637*0a6a1f1dSLionel Sambuc (pmp->pm_HugeSectors *
638*0a6a1f1dSLionel Sambuc (pmp->pm_BytesPerSec / GEMDOSFS_BSIZE) > psize)) {
639*0a6a1f1dSLionel Sambuc DPRINTF("consistency checks for GEMDOS");
64084d9c625SLionel Sambuc error = EINVAL;
64184d9c625SLionel Sambuc goto error_exit;
64284d9c625SLionel Sambuc }
64384d9c625SLionel Sambuc /*
64484d9c625SLionel Sambuc * XXX - Many parts of the msdosfs driver seem to assume that
64584d9c625SLionel Sambuc * the number of bytes per logical sector (BytesPerSec) will
64684d9c625SLionel Sambuc * always be the same as the number of bytes per disk block
64784d9c625SLionel Sambuc * Let's pretend it is.
64884d9c625SLionel Sambuc */
649*0a6a1f1dSLionel Sambuc BlkPerSec = pmp->pm_BytesPerSec / GEMDOSFS_BSIZE;
650*0a6a1f1dSLionel Sambuc pmp->pm_BytesPerSec = GEMDOSFS_BSIZE;
651*0a6a1f1dSLionel Sambuc pmp->pm_HugeSectors *= BlkPerSec;
652*0a6a1f1dSLionel Sambuc pmp->pm_HiddenSects *= BlkPerSec;
653*0a6a1f1dSLionel Sambuc pmp->pm_ResSectors *= BlkPerSec;
654*0a6a1f1dSLionel Sambuc pmp->pm_Sectors *= BlkPerSec;
655*0a6a1f1dSLionel Sambuc pmp->pm_FATsecs *= BlkPerSec;
656*0a6a1f1dSLionel Sambuc SecPerClust *= BlkPerSec;
65784d9c625SLionel Sambuc }
65884d9c625SLionel Sambuc
65984d9c625SLionel Sambuc /* Check that fs has nonzero FAT size */
66084d9c625SLionel Sambuc if (pmp->pm_FATsecs == 0) {
661*0a6a1f1dSLionel Sambuc DPRINTF("FATsecs is 0");
66284d9c625SLionel Sambuc error = EINVAL;
66384d9c625SLionel Sambuc goto error_exit;
66484d9c625SLionel Sambuc }
66584d9c625SLionel Sambuc
66684d9c625SLionel Sambuc pmp->pm_fatblk = pmp->pm_ResSectors;
66784d9c625SLionel Sambuc if (FAT32(pmp)) {
668*0a6a1f1dSLionel Sambuc if (secsize < sizeof(*b710)) {
669*0a6a1f1dSLionel Sambuc DPRINTF("710 bootsec %u\n", secsize);
670*0a6a1f1dSLionel Sambuc error = EINVAL;
671*0a6a1f1dSLionel Sambuc goto error_exit;
672*0a6a1f1dSLionel Sambuc }
67384d9c625SLionel Sambuc pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
67484d9c625SLionel Sambuc pmp->pm_firstcluster = pmp->pm_fatblk
67584d9c625SLionel Sambuc + (pmp->pm_FATs * pmp->pm_FATsecs);
67684d9c625SLionel Sambuc pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
67784d9c625SLionel Sambuc } else {
67884d9c625SLionel Sambuc pmp->pm_rootdirblk = pmp->pm_fatblk +
67984d9c625SLionel Sambuc (pmp->pm_FATs * pmp->pm_FATsecs);
68084d9c625SLionel Sambuc pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
68184d9c625SLionel Sambuc + pmp->pm_BytesPerSec - 1)
68284d9c625SLionel Sambuc / pmp->pm_BytesPerSec;/* in sectors */
68384d9c625SLionel Sambuc pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
68484d9c625SLionel Sambuc }
68584d9c625SLionel Sambuc
68684d9c625SLionel Sambuc pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
68784d9c625SLionel Sambuc SecPerClust;
68884d9c625SLionel Sambuc pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
68984d9c625SLionel Sambuc pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
69084d9c625SLionel Sambuc
69184d9c625SLionel Sambuc if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
69284d9c625SLionel Sambuc if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
69384d9c625SLionel Sambuc pmp->pm_fatmask = FAT12_MASK;
69484d9c625SLionel Sambuc pmp->pm_fatmult = 3;
69584d9c625SLionel Sambuc pmp->pm_fatdiv = 2;
69684d9c625SLionel Sambuc } else {
69784d9c625SLionel Sambuc pmp->pm_fatmask = FAT16_MASK;
69884d9c625SLionel Sambuc pmp->pm_fatmult = 2;
69984d9c625SLionel Sambuc pmp->pm_fatdiv = 1;
70084d9c625SLionel Sambuc }
70184d9c625SLionel Sambuc } else if (pmp->pm_fatmask == 0) {
70284d9c625SLionel Sambuc if (pmp->pm_maxcluster
70384d9c625SLionel Sambuc <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
70484d9c625SLionel Sambuc /*
70584d9c625SLionel Sambuc * This will usually be a floppy disk. This size makes
70684d9c625SLionel Sambuc * sure that one FAT entry will not be split across
70784d9c625SLionel Sambuc * multiple blocks.
70884d9c625SLionel Sambuc */
70984d9c625SLionel Sambuc pmp->pm_fatmask = FAT12_MASK;
71084d9c625SLionel Sambuc pmp->pm_fatmult = 3;
71184d9c625SLionel Sambuc pmp->pm_fatdiv = 2;
71284d9c625SLionel Sambuc } else {
71384d9c625SLionel Sambuc pmp->pm_fatmask = FAT16_MASK;
71484d9c625SLionel Sambuc pmp->pm_fatmult = 2;
71584d9c625SLionel Sambuc pmp->pm_fatdiv = 1;
71684d9c625SLionel Sambuc }
71784d9c625SLionel Sambuc }
71884d9c625SLionel Sambuc if (FAT12(pmp))
71984d9c625SLionel Sambuc pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
72084d9c625SLionel Sambuc else
72184d9c625SLionel Sambuc pmp->pm_fatblocksize = MAXBSIZE;
72284d9c625SLionel Sambuc
72384d9c625SLionel Sambuc pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
72484d9c625SLionel Sambuc pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
72584d9c625SLionel Sambuc
72684d9c625SLionel Sambuc /*
72784d9c625SLionel Sambuc * Compute mask and shift value for isolating cluster relative byte
72884d9c625SLionel Sambuc * offsets and cluster numbers from a file offset.
72984d9c625SLionel Sambuc */
73084d9c625SLionel Sambuc pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
73184d9c625SLionel Sambuc pmp->pm_crbomask = pmp->pm_bpcluster - 1;
73284d9c625SLionel Sambuc pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
73384d9c625SLionel Sambuc
73484d9c625SLionel Sambuc /*
73584d9c625SLionel Sambuc * Check for valid cluster size
73684d9c625SLionel Sambuc * must be a power of 2
73784d9c625SLionel Sambuc */
73884d9c625SLionel Sambuc if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
739*0a6a1f1dSLionel Sambuc DPRINTF("bpcluster %lu cnshift %lu", pmp->pm_bpcluster,
740*0a6a1f1dSLionel Sambuc pmp->pm_cnshift);
74184d9c625SLionel Sambuc error = EINVAL;
74284d9c625SLionel Sambuc goto error_exit;
74384d9c625SLionel Sambuc }
74484d9c625SLionel Sambuc
74584d9c625SLionel Sambuc /*
74684d9c625SLionel Sambuc * Cluster size must be within limit of MAXBSIZE.
74784d9c625SLionel Sambuc * Many FAT filesystems will not have clusters larger than
74884d9c625SLionel Sambuc * 32KiB due to limits in Windows versions before Vista.
74984d9c625SLionel Sambuc */
75084d9c625SLionel Sambuc if (pmp->pm_bpcluster > MAXBSIZE) {
751*0a6a1f1dSLionel Sambuc DPRINTF("bpcluster %lu > MAXBSIZE %d",
752*0a6a1f1dSLionel Sambuc pmp->pm_bpcluster, MAXBSIZE);
75384d9c625SLionel Sambuc error = EINVAL;
75484d9c625SLionel Sambuc goto error_exit;
75584d9c625SLionel Sambuc }
75684d9c625SLionel Sambuc
75784d9c625SLionel Sambuc /*
75884d9c625SLionel Sambuc * Release the bootsector buffer.
75984d9c625SLionel Sambuc */
76084d9c625SLionel Sambuc brelse(bp, BC_AGE);
76184d9c625SLionel Sambuc bp = NULL;
76284d9c625SLionel Sambuc
76384d9c625SLionel Sambuc /*
76484d9c625SLionel Sambuc * Check FSInfo.
76584d9c625SLionel Sambuc */
76684d9c625SLionel Sambuc if (pmp->pm_fsinfo) {
76784d9c625SLionel Sambuc struct fsinfo *fp;
768*0a6a1f1dSLionel Sambuc const int rdsz = roundup(sizeof(*fp), pmp->pm_BytesPerSec);
76984d9c625SLionel Sambuc
77084d9c625SLionel Sambuc /*
77184d9c625SLionel Sambuc * XXX If the fsinfo block is stored on media with
77284d9c625SLionel Sambuc * 2KB or larger sectors, is the fsinfo structure
77384d9c625SLionel Sambuc * padded at the end or in the middle?
77484d9c625SLionel Sambuc */
77584d9c625SLionel Sambuc if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
776*0a6a1f1dSLionel Sambuc rdsz, 0, &bp)) != 0)
77784d9c625SLionel Sambuc goto error_exit;
77884d9c625SLionel Sambuc fp = (struct fsinfo *)bp->b_data;
77984d9c625SLionel Sambuc if (!memcmp(fp->fsisig1, "RRaA", 4)
78084d9c625SLionel Sambuc && !memcmp(fp->fsisig2, "rrAa", 4)
78184d9c625SLionel Sambuc && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
78284d9c625SLionel Sambuc && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
78384d9c625SLionel Sambuc pmp->pm_nxtfree = getulong(fp->fsinxtfree);
78484d9c625SLionel Sambuc else
78584d9c625SLionel Sambuc pmp->pm_fsinfo = 0;
78684d9c625SLionel Sambuc brelse(bp, 0);
78784d9c625SLionel Sambuc bp = NULL;
78884d9c625SLionel Sambuc }
78984d9c625SLionel Sambuc
79084d9c625SLionel Sambuc /*
79184d9c625SLionel Sambuc * Check and validate (or perhaps invalidate?) the fsinfo structure?
79284d9c625SLionel Sambuc * XXX
79384d9c625SLionel Sambuc */
79484d9c625SLionel Sambuc if (pmp->pm_fsinfo) {
79584d9c625SLionel Sambuc if ((pmp->pm_nxtfree == 0xffffffffUL) ||
79684d9c625SLionel Sambuc (pmp->pm_nxtfree > pmp->pm_maxcluster))
79784d9c625SLionel Sambuc pmp->pm_fsinfo = 0;
79884d9c625SLionel Sambuc }
79984d9c625SLionel Sambuc
80084d9c625SLionel Sambuc /*
80184d9c625SLionel Sambuc * Allocate memory for the bitmap of allocated clusters, and then
80284d9c625SLionel Sambuc * fill it in.
80384d9c625SLionel Sambuc */
80484d9c625SLionel Sambuc pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS)
80584d9c625SLionel Sambuc / N_INUSEBITS)
80684d9c625SLionel Sambuc * sizeof(*pmp->pm_inusemap),
80784d9c625SLionel Sambuc M_MSDOSFSFAT, M_WAITOK);
80884d9c625SLionel Sambuc
80984d9c625SLionel Sambuc /*
81084d9c625SLionel Sambuc * fillinusemap() needs pm_devvp.
81184d9c625SLionel Sambuc */
81284d9c625SLionel Sambuc pmp->pm_dev = dev;
81384d9c625SLionel Sambuc pmp->pm_devvp = devvp;
81484d9c625SLionel Sambuc
81584d9c625SLionel Sambuc /*
81684d9c625SLionel Sambuc * Have the inuse map filled in.
81784d9c625SLionel Sambuc */
81884d9c625SLionel Sambuc if ((error = fillinusemap(pmp)) != 0) {
819*0a6a1f1dSLionel Sambuc DPRINTF("fillinusemap %d", error);
82084d9c625SLionel Sambuc goto error_exit;
82184d9c625SLionel Sambuc }
82284d9c625SLionel Sambuc
82384d9c625SLionel Sambuc /*
82484d9c625SLionel Sambuc * If they want FAT updates to be synchronous then let them suffer
82584d9c625SLionel Sambuc * the performance degradation in exchange for the on disk copy of
82684d9c625SLionel Sambuc * the FAT being correct just about all the time. I suppose this
82784d9c625SLionel Sambuc * would be a good thing to turn on if the kernel is still flakey.
82884d9c625SLionel Sambuc */
82984d9c625SLionel Sambuc if (mp->mnt_flag & MNT_SYNCHRONOUS)
83084d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
83184d9c625SLionel Sambuc
83284d9c625SLionel Sambuc /*
83384d9c625SLionel Sambuc * Finish up.
83484d9c625SLionel Sambuc */
83584d9c625SLionel Sambuc if (ronly)
83684d9c625SLionel Sambuc pmp->pm_flags |= MSDOSFSMNT_RONLY;
83784d9c625SLionel Sambuc else
83884d9c625SLionel Sambuc pmp->pm_fmod = 1;
83984d9c625SLionel Sambuc mp->mnt_data = pmp;
84084d9c625SLionel Sambuc mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
84184d9c625SLionel Sambuc mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS);
84284d9c625SLionel Sambuc mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
84384d9c625SLionel Sambuc mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
84484d9c625SLionel Sambuc mp->mnt_flag |= MNT_LOCAL;
84584d9c625SLionel Sambuc mp->mnt_dev_bshift = pmp->pm_bnshift;
84684d9c625SLionel Sambuc mp->mnt_fs_bshift = pmp->pm_cnshift;
84784d9c625SLionel Sambuc
84884d9c625SLionel Sambuc /*
84984d9c625SLionel Sambuc * If we ever do quotas for DOS filesystems this would be a place
85084d9c625SLionel Sambuc * to fill in the info in the msdosfsmount structure. You dolt,
85184d9c625SLionel Sambuc * quotas on dos filesystems make no sense because files have no
85284d9c625SLionel Sambuc * owners on dos filesystems. of course there is some empty space
85384d9c625SLionel Sambuc * in the directory entry where we could put uid's and gid's.
85484d9c625SLionel Sambuc */
85584d9c625SLionel Sambuc
85684d9c625SLionel Sambuc spec_node_setmountedfs(devvp, mp);
85784d9c625SLionel Sambuc
85884d9c625SLionel Sambuc return (0);
85984d9c625SLionel Sambuc
86084d9c625SLionel Sambuc error_exit:
86184d9c625SLionel Sambuc fstrans_unmount(mp);
86284d9c625SLionel Sambuc if (bp)
86384d9c625SLionel Sambuc brelse(bp, BC_AGE);
86484d9c625SLionel Sambuc if (pmp) {
86584d9c625SLionel Sambuc if (pmp->pm_inusemap)
86684d9c625SLionel Sambuc free(pmp->pm_inusemap, M_MSDOSFSFAT);
86784d9c625SLionel Sambuc free(pmp, M_MSDOSFSMNT);
86884d9c625SLionel Sambuc mp->mnt_data = NULL;
86984d9c625SLionel Sambuc }
87084d9c625SLionel Sambuc return (error);
87184d9c625SLionel Sambuc }
87284d9c625SLionel Sambuc
87384d9c625SLionel Sambuc int
msdosfs_start(struct mount * mp,int flags)87484d9c625SLionel Sambuc msdosfs_start(struct mount *mp, int flags)
87584d9c625SLionel Sambuc {
87684d9c625SLionel Sambuc
87784d9c625SLionel Sambuc return (0);
87884d9c625SLionel Sambuc }
87984d9c625SLionel Sambuc
88084d9c625SLionel Sambuc /*
88184d9c625SLionel Sambuc * Unmount the filesystem described by mp.
88284d9c625SLionel Sambuc */
88384d9c625SLionel Sambuc int
msdosfs_unmount(struct mount * mp,int mntflags)88484d9c625SLionel Sambuc msdosfs_unmount(struct mount *mp, int mntflags)
88584d9c625SLionel Sambuc {
88684d9c625SLionel Sambuc struct msdosfsmount *pmp;
88784d9c625SLionel Sambuc int error, flags;
88884d9c625SLionel Sambuc
88984d9c625SLionel Sambuc flags = 0;
89084d9c625SLionel Sambuc if (mntflags & MNT_FORCE)
89184d9c625SLionel Sambuc flags |= FORCECLOSE;
89284d9c625SLionel Sambuc if ((error = vflush(mp, NULLVP, flags)) != 0)
89384d9c625SLionel Sambuc return (error);
89484d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(mp);
89584d9c625SLionel Sambuc if (pmp->pm_devvp->v_type != VBAD)
89684d9c625SLionel Sambuc spec_node_setmountedfs(pmp->pm_devvp, NULL);
89784d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
89884d9c625SLionel Sambuc {
89984d9c625SLionel Sambuc struct vnode *vp = pmp->pm_devvp;
90084d9c625SLionel Sambuc
90184d9c625SLionel Sambuc printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
90284d9c625SLionel Sambuc printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
90384d9c625SLionel Sambuc vp->v_vflag | vp->v_iflag | vp->v_uflag, vp->v_usecount,
90484d9c625SLionel Sambuc vp->v_writecount, vp->v_holdcnt);
90584d9c625SLionel Sambuc printf("mount %p, op %p\n",
90684d9c625SLionel Sambuc vp->v_mount, vp->v_op);
90784d9c625SLionel Sambuc printf("freef %p, freeb %p, mount %p\n",
90884d9c625SLionel Sambuc vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev,
90984d9c625SLionel Sambuc vp->v_mount);
91084d9c625SLionel Sambuc printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
91184d9c625SLionel Sambuc vp->v_cleanblkhd.lh_first,
91284d9c625SLionel Sambuc vp->v_dirtyblkhd.lh_first,
91384d9c625SLionel Sambuc vp->v_numoutput, vp->v_type);
91484d9c625SLionel Sambuc printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
91584d9c625SLionel Sambuc vp->v_socket, vp->v_tag,
91684d9c625SLionel Sambuc ((u_int *)vp->v_data)[0],
91784d9c625SLionel Sambuc ((u_int *)vp->v_data)[1]);
91884d9c625SLionel Sambuc }
91984d9c625SLionel Sambuc #endif
92084d9c625SLionel Sambuc vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
92184d9c625SLionel Sambuc (void) VOP_CLOSE(pmp->pm_devvp,
92284d9c625SLionel Sambuc pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED);
92384d9c625SLionel Sambuc vput(pmp->pm_devvp);
92484d9c625SLionel Sambuc msdosfs_fh_destroy(pmp);
92584d9c625SLionel Sambuc free(pmp->pm_inusemap, M_MSDOSFSFAT);
92684d9c625SLionel Sambuc free(pmp, M_MSDOSFSMNT);
92784d9c625SLionel Sambuc mp->mnt_data = NULL;
92884d9c625SLionel Sambuc mp->mnt_flag &= ~MNT_LOCAL;
92984d9c625SLionel Sambuc fstrans_unmount(mp);
93084d9c625SLionel Sambuc return (0);
93184d9c625SLionel Sambuc }
93284d9c625SLionel Sambuc
93384d9c625SLionel Sambuc int
msdosfs_root(struct mount * mp,struct vnode ** vpp)93484d9c625SLionel Sambuc msdosfs_root(struct mount *mp, struct vnode **vpp)
93584d9c625SLionel Sambuc {
93684d9c625SLionel Sambuc struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
93784d9c625SLionel Sambuc int error;
93884d9c625SLionel Sambuc
93984d9c625SLionel Sambuc #ifdef MSDOSFS_DEBUG
94084d9c625SLionel Sambuc printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
94184d9c625SLionel Sambuc #endif
942*0a6a1f1dSLionel Sambuc if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, vpp)) != 0)
943*0a6a1f1dSLionel Sambuc return error;
944*0a6a1f1dSLionel Sambuc error = vn_lock(*vpp, LK_EXCLUSIVE);
945*0a6a1f1dSLionel Sambuc if (error) {
946*0a6a1f1dSLionel Sambuc vrele(*vpp);
947*0a6a1f1dSLionel Sambuc *vpp = NULL;
948*0a6a1f1dSLionel Sambuc return error;
949*0a6a1f1dSLionel Sambuc }
950*0a6a1f1dSLionel Sambuc return 0;
95184d9c625SLionel Sambuc }
95284d9c625SLionel Sambuc
95384d9c625SLionel Sambuc int
msdosfs_statvfs(struct mount * mp,struct statvfs * sbp)95484d9c625SLionel Sambuc msdosfs_statvfs(struct mount *mp, struct statvfs *sbp)
95584d9c625SLionel Sambuc {
95684d9c625SLionel Sambuc struct msdosfsmount *pmp;
95784d9c625SLionel Sambuc
95884d9c625SLionel Sambuc pmp = VFSTOMSDOSFS(mp);
95984d9c625SLionel Sambuc sbp->f_bsize = pmp->pm_bpcluster;
96084d9c625SLionel Sambuc sbp->f_frsize = sbp->f_bsize;
96184d9c625SLionel Sambuc sbp->f_iosize = pmp->pm_bpcluster;
96284d9c625SLionel Sambuc sbp->f_blocks = pmp->pm_nmbrofclusters;
96384d9c625SLionel Sambuc sbp->f_bfree = pmp->pm_freeclustercount;
96484d9c625SLionel Sambuc sbp->f_bavail = pmp->pm_freeclustercount;
96584d9c625SLionel Sambuc sbp->f_bresvd = 0;
96684d9c625SLionel Sambuc sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
96784d9c625SLionel Sambuc sbp->f_ffree = 0; /* what to put in here? */
96884d9c625SLionel Sambuc sbp->f_favail = 0; /* what to put in here? */
96984d9c625SLionel Sambuc sbp->f_fresvd = 0;
97084d9c625SLionel Sambuc copy_statvfs_info(sbp, mp);
97184d9c625SLionel Sambuc return (0);
97284d9c625SLionel Sambuc }
97384d9c625SLionel Sambuc
974*0a6a1f1dSLionel Sambuc struct msdosfs_sync_ctx {
975*0a6a1f1dSLionel Sambuc int waitfor;
976*0a6a1f1dSLionel Sambuc };
977*0a6a1f1dSLionel Sambuc
978*0a6a1f1dSLionel Sambuc static bool
msdosfs_sync_selector(void * cl,struct vnode * vp)979*0a6a1f1dSLionel Sambuc msdosfs_sync_selector(void *cl, struct vnode *vp)
980*0a6a1f1dSLionel Sambuc {
981*0a6a1f1dSLionel Sambuc struct msdosfs_sync_ctx *c = cl;
982*0a6a1f1dSLionel Sambuc struct denode *dep;
983*0a6a1f1dSLionel Sambuc
984*0a6a1f1dSLionel Sambuc dep = VTODE(vp);
985*0a6a1f1dSLionel Sambuc if (c->waitfor == MNT_LAZY || vp->v_type == VNON ||
986*0a6a1f1dSLionel Sambuc dep == NULL || (((dep->de_flag &
987*0a6a1f1dSLionel Sambuc (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) &&
988*0a6a1f1dSLionel Sambuc (LIST_EMPTY(&vp->v_dirtyblkhd) &&
989*0a6a1f1dSLionel Sambuc UVM_OBJ_IS_CLEAN(&vp->v_uobj))))
990*0a6a1f1dSLionel Sambuc return false;
991*0a6a1f1dSLionel Sambuc return true;
992*0a6a1f1dSLionel Sambuc }
993*0a6a1f1dSLionel Sambuc
99484d9c625SLionel Sambuc int
msdosfs_sync(struct mount * mp,int waitfor,kauth_cred_t cred)99584d9c625SLionel Sambuc msdosfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
99684d9c625SLionel Sambuc {
997*0a6a1f1dSLionel Sambuc struct vnode *vp;
998*0a6a1f1dSLionel Sambuc struct vnode_iterator *marker;
99984d9c625SLionel Sambuc struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
100084d9c625SLionel Sambuc int error, allerror = 0;
1001*0a6a1f1dSLionel Sambuc struct msdosfs_sync_ctx ctx;
100284d9c625SLionel Sambuc
100384d9c625SLionel Sambuc /*
100484d9c625SLionel Sambuc * If we ever switch to not updating all of the FATs all the time,
100584d9c625SLionel Sambuc * this would be the place to update them from the first one.
100684d9c625SLionel Sambuc */
100784d9c625SLionel Sambuc if (pmp->pm_fmod != 0) {
100884d9c625SLionel Sambuc if (pmp->pm_flags & MSDOSFSMNT_RONLY)
100984d9c625SLionel Sambuc panic("msdosfs_sync: rofs mod");
101084d9c625SLionel Sambuc else {
101184d9c625SLionel Sambuc /* update FATs here */
101284d9c625SLionel Sambuc }
101384d9c625SLionel Sambuc }
101484d9c625SLionel Sambuc fstrans_start(mp, FSTRANS_SHARED);
101584d9c625SLionel Sambuc /*
101684d9c625SLionel Sambuc * Write back each (modified) denode.
101784d9c625SLionel Sambuc */
1018*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_init(mp, &marker);
1019*0a6a1f1dSLionel Sambuc ctx.waitfor = waitfor;
1020*0a6a1f1dSLionel Sambuc while ((vp = vfs_vnode_iterator_next(marker, msdosfs_sync_selector,
1021*0a6a1f1dSLionel Sambuc &ctx)))
1022*0a6a1f1dSLionel Sambuc {
1023*0a6a1f1dSLionel Sambuc error = vn_lock(vp, LK_EXCLUSIVE);
102484d9c625SLionel Sambuc if (error) {
1025*0a6a1f1dSLionel Sambuc vrele(vp);
102684d9c625SLionel Sambuc continue;
102784d9c625SLionel Sambuc }
102884d9c625SLionel Sambuc if ((error = VOP_FSYNC(vp, cred,
102984d9c625SLionel Sambuc waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
103084d9c625SLionel Sambuc allerror = error;
103184d9c625SLionel Sambuc vput(vp);
103284d9c625SLionel Sambuc }
1033*0a6a1f1dSLionel Sambuc vfs_vnode_iterator_destroy(marker);
103484d9c625SLionel Sambuc
103584d9c625SLionel Sambuc /*
103684d9c625SLionel Sambuc * Force stale file system control information to be flushed.
103784d9c625SLionel Sambuc */
103884d9c625SLionel Sambuc if ((error = VOP_FSYNC(pmp->pm_devvp, cred,
103984d9c625SLionel Sambuc waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
104084d9c625SLionel Sambuc allerror = error;
104184d9c625SLionel Sambuc fstrans_done(mp);
104284d9c625SLionel Sambuc return (allerror);
104384d9c625SLionel Sambuc }
104484d9c625SLionel Sambuc
104584d9c625SLionel Sambuc int
msdosfs_fhtovp(struct mount * mp,struct fid * fhp,struct vnode ** vpp)104684d9c625SLionel Sambuc msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
104784d9c625SLionel Sambuc {
104884d9c625SLionel Sambuc struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
104984d9c625SLionel Sambuc struct defid defh;
105084d9c625SLionel Sambuc uint32_t gen;
105184d9c625SLionel Sambuc int error;
105284d9c625SLionel Sambuc
105384d9c625SLionel Sambuc if (fhp->fid_len != sizeof(struct defid)) {
1054*0a6a1f1dSLionel Sambuc DPRINTF("fid_len %d %zd", fhp->fid_len, sizeof(struct defid));
105584d9c625SLionel Sambuc return EINVAL;
105684d9c625SLionel Sambuc }
105784d9c625SLionel Sambuc memcpy(&defh, fhp, sizeof(defh));
105884d9c625SLionel Sambuc error = msdosfs_fh_lookup(pmp, defh.defid_dirclust, defh.defid_dirofs,
105984d9c625SLionel Sambuc &gen);
106084d9c625SLionel Sambuc if (error == 0 && gen != defh.defid_gen)
106184d9c625SLionel Sambuc error = ESTALE;
106284d9c625SLionel Sambuc if (error) {
106384d9c625SLionel Sambuc *vpp = NULLVP;
106484d9c625SLionel Sambuc return error;
106584d9c625SLionel Sambuc }
1066*0a6a1f1dSLionel Sambuc error = deget(pmp, defh.defid_dirclust, defh.defid_dirofs, vpp);
106784d9c625SLionel Sambuc if (error) {
1068*0a6a1f1dSLionel Sambuc DPRINTF("deget %d", error);
106984d9c625SLionel Sambuc *vpp = NULLVP;
1070*0a6a1f1dSLionel Sambuc return error;
107184d9c625SLionel Sambuc }
1072*0a6a1f1dSLionel Sambuc error = vn_lock(*vpp, LK_EXCLUSIVE);
1073*0a6a1f1dSLionel Sambuc if (error) {
1074*0a6a1f1dSLionel Sambuc vrele(*vpp);
1075*0a6a1f1dSLionel Sambuc *vpp = NULLVP;
1076*0a6a1f1dSLionel Sambuc return error;
1077*0a6a1f1dSLionel Sambuc }
1078*0a6a1f1dSLionel Sambuc return 0;
107984d9c625SLionel Sambuc }
108084d9c625SLionel Sambuc
108184d9c625SLionel Sambuc int
msdosfs_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)108284d9c625SLionel Sambuc msdosfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
108384d9c625SLionel Sambuc {
108484d9c625SLionel Sambuc struct msdosfsmount *pmp = VFSTOMSDOSFS(vp->v_mount);
108584d9c625SLionel Sambuc struct denode *dep;
108684d9c625SLionel Sambuc struct defid defh;
108784d9c625SLionel Sambuc int error;
108884d9c625SLionel Sambuc
108984d9c625SLionel Sambuc if (*fh_size < sizeof(struct defid)) {
109084d9c625SLionel Sambuc *fh_size = sizeof(struct defid);
109184d9c625SLionel Sambuc return E2BIG;
109284d9c625SLionel Sambuc }
109384d9c625SLionel Sambuc *fh_size = sizeof(struct defid);
109484d9c625SLionel Sambuc dep = VTODE(vp);
109584d9c625SLionel Sambuc memset(&defh, 0, sizeof(defh));
109684d9c625SLionel Sambuc defh.defid_len = sizeof(struct defid);
109784d9c625SLionel Sambuc defh.defid_dirclust = dep->de_dirclust;
109884d9c625SLionel Sambuc defh.defid_dirofs = dep->de_diroffset;
109984d9c625SLionel Sambuc error = msdosfs_fh_enter(pmp, dep->de_dirclust, dep->de_diroffset,
110084d9c625SLionel Sambuc &defh.defid_gen);
110184d9c625SLionel Sambuc if (error == 0)
110284d9c625SLionel Sambuc memcpy(fhp, &defh, sizeof(defh));
110384d9c625SLionel Sambuc return error;
110484d9c625SLionel Sambuc }
110584d9c625SLionel Sambuc
110684d9c625SLionel Sambuc int
msdosfs_vget(struct mount * mp,ino_t ino,struct vnode ** vpp)110784d9c625SLionel Sambuc msdosfs_vget(struct mount *mp, ino_t ino,
110884d9c625SLionel Sambuc struct vnode **vpp)
110984d9c625SLionel Sambuc {
111084d9c625SLionel Sambuc
111184d9c625SLionel Sambuc return (EOPNOTSUPP);
111284d9c625SLionel Sambuc }
111384d9c625SLionel Sambuc
111484d9c625SLionel Sambuc int
msdosfs_suspendctl(struct mount * mp,int cmd)111584d9c625SLionel Sambuc msdosfs_suspendctl(struct mount *mp, int cmd)
111684d9c625SLionel Sambuc {
111784d9c625SLionel Sambuc int error;
111884d9c625SLionel Sambuc struct lwp *l = curlwp;
111984d9c625SLionel Sambuc
112084d9c625SLionel Sambuc switch (cmd) {
112184d9c625SLionel Sambuc case SUSPEND_SUSPEND:
112284d9c625SLionel Sambuc if ((error = fstrans_setstate(mp, FSTRANS_SUSPENDING)) != 0)
112384d9c625SLionel Sambuc return error;
112484d9c625SLionel Sambuc error = msdosfs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
112584d9c625SLionel Sambuc if (error == 0)
112684d9c625SLionel Sambuc error = fstrans_setstate(mp, FSTRANS_SUSPENDED);
112784d9c625SLionel Sambuc if (error != 0) {
112884d9c625SLionel Sambuc (void) fstrans_setstate(mp, FSTRANS_NORMAL);
112984d9c625SLionel Sambuc return error;
113084d9c625SLionel Sambuc }
113184d9c625SLionel Sambuc return 0;
113284d9c625SLionel Sambuc
113384d9c625SLionel Sambuc case SUSPEND_RESUME:
113484d9c625SLionel Sambuc return fstrans_setstate(mp, FSTRANS_NORMAL);
113584d9c625SLionel Sambuc
113684d9c625SLionel Sambuc default:
113784d9c625SLionel Sambuc return EINVAL;
113884d9c625SLionel Sambuc }
113984d9c625SLionel Sambuc }
1140