1*dad6cdf0Shannken /* $NetBSD: msdosfs_vfsops.c,v 1.138 2022/04/16 07:58:21 hannken Exp $ */
298d58548Sjdolecek
398d58548Sjdolecek /*-
498d58548Sjdolecek * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
598d58548Sjdolecek * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
698d58548Sjdolecek * All rights reserved.
798d58548Sjdolecek * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
898d58548Sjdolecek *
998d58548Sjdolecek * Redistribution and use in source and binary forms, with or without
1098d58548Sjdolecek * modification, are permitted provided that the following conditions
1198d58548Sjdolecek * are met:
1298d58548Sjdolecek * 1. Redistributions of source code must retain the above copyright
1398d58548Sjdolecek * notice, this list of conditions and the following disclaimer.
1498d58548Sjdolecek * 2. Redistributions in binary form must reproduce the above copyright
1598d58548Sjdolecek * notice, this list of conditions and the following disclaimer in the
1698d58548Sjdolecek * documentation and/or other materials provided with the distribution.
1798d58548Sjdolecek * 3. All advertising materials mentioning features or use of this software
1898d58548Sjdolecek * must display the following acknowledgement:
1998d58548Sjdolecek * This product includes software developed by TooLs GmbH.
2098d58548Sjdolecek * 4. The name of TooLs GmbH may not be used to endorse or promote products
2198d58548Sjdolecek * derived from this software without specific prior written permission.
2298d58548Sjdolecek *
2398d58548Sjdolecek * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2498d58548Sjdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2598d58548Sjdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2698d58548Sjdolecek * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2798d58548Sjdolecek * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2898d58548Sjdolecek * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2998d58548Sjdolecek * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3098d58548Sjdolecek * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3198d58548Sjdolecek * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3298d58548Sjdolecek * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3398d58548Sjdolecek */
3498d58548Sjdolecek /*
3598d58548Sjdolecek * Written by Paul Popelka (paulp@uts.amdahl.com)
3698d58548Sjdolecek *
3798d58548Sjdolecek * You can do anything you want with this software, just don't say you wrote
3898d58548Sjdolecek * it, and don't remove this notice.
3998d58548Sjdolecek *
4098d58548Sjdolecek * This software is provided "as is".
4198d58548Sjdolecek *
4298d58548Sjdolecek * The author supplies this software to be publicly redistributed on the
4398d58548Sjdolecek * understanding that the author is not responsible for the correct
4498d58548Sjdolecek * functioning of this software in any circumstances and is not liable for
4598d58548Sjdolecek * any damages caused by this software.
4698d58548Sjdolecek *
4798d58548Sjdolecek * October 1992
4898d58548Sjdolecek */
4998d58548Sjdolecek
5098d58548Sjdolecek #include <sys/cdefs.h>
51*dad6cdf0Shannken __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.138 2022/04/16 07:58:21 hannken Exp $");
5298d58548Sjdolecek
5398d58548Sjdolecek #if defined(_KERNEL_OPT)
5498d58548Sjdolecek #include "opt_compat_netbsd.h"
5598d58548Sjdolecek #endif
5698d58548Sjdolecek
5798d58548Sjdolecek #include <sys/param.h>
5898d58548Sjdolecek #include <sys/systm.h>
5913f8d2ceSatatat #include <sys/sysctl.h>
6098d58548Sjdolecek #include <sys/namei.h>
6198d58548Sjdolecek #include <sys/proc.h>
6298d58548Sjdolecek #include <sys/kernel.h>
6398d58548Sjdolecek #include <sys/vnode.h>
64717e1785Sdholland #include <miscfs/genfs/genfs.h>
6598d58548Sjdolecek #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
6698d58548Sjdolecek #include <sys/mount.h>
6798d58548Sjdolecek #include <sys/buf.h>
6898d58548Sjdolecek #include <sys/file.h>
6998d58548Sjdolecek #include <sys/device.h>
7098d58548Sjdolecek #include <sys/disklabel.h>
716b98a174Schristos #include <sys/disk.h>
7298d58548Sjdolecek #include <sys/ioctl.h>
7398d58548Sjdolecek #include <sys/malloc.h>
7498d58548Sjdolecek #include <sys/dirent.h>
7598d58548Sjdolecek #include <sys/stat.h>
7698d58548Sjdolecek #include <sys/conf.h>
77fc9422c9Selad #include <sys/kauth.h>
78a1221b6dSrumble #include <sys/module.h>
7998d58548Sjdolecek
8098d58548Sjdolecek #include <fs/msdosfs/bpb.h>
8198d58548Sjdolecek #include <fs/msdosfs/bootsect.h>
8298d58548Sjdolecek #include <fs/msdosfs/direntry.h>
8398d58548Sjdolecek #include <fs/msdosfs/denode.h>
8498d58548Sjdolecek #include <fs/msdosfs/msdosfsmount.h>
8598d58548Sjdolecek #include <fs/msdosfs/fat.h>
8698d58548Sjdolecek
87024c6fe9Spooka MODULE(MODULE_CLASS_VFS, msdos, NULL);
88a1221b6dSrumble
896b98a174Schristos #ifdef MSDOSFS_DEBUG
90549e2319Smaxv #define DPRINTF(fmt, ...) uprintf("%s(): " fmt "\n", __func__, ##__VA_ARGS__)
916b98a174Schristos #else
92549e2319Smaxv #define DPRINTF(fmt, ...)
936b98a174Schristos #endif
946b98a174Schristos
951273c3cbSmaxv #define GEMDOSFS_BSIZE 512
961273c3cbSmaxv
97caffd8efSjdolecek #define MSDOSFS_NAMEMAX(pmp) \
98caffd8efSjdolecek (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12
99caffd8efSjdolecek
10095e1ffb1Schristos int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *,
10147216f84Sxtraeme struct msdosfs_args *);
10298d58548Sjdolecek
10347216f84Sxtraeme static int update_mp(struct mount *, struct msdosfs_args *);
10498d58548Sjdolecek
105835b0326Spooka MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure");
10648958e52Sjakllsch MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOS FS FAT table");
1070ae0a486Srumble MALLOC_JUSTDEFINE(M_MSDOSFSTMP, "MSDOSFS temp", "MSDOS FS temp. structures");
108b1934809Sthorpej
10998d58548Sjdolecek extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc;
11098d58548Sjdolecek
11198d58548Sjdolecek const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = {
11298d58548Sjdolecek &msdosfs_vnodeop_opv_desc,
11398d58548Sjdolecek NULL,
11498d58548Sjdolecek };
11598d58548Sjdolecek
11698d58548Sjdolecek struct vfsops msdosfs_vfsops = {
1176d285189Shannken .vfs_name = MOUNT_MSDOS,
1186d285189Shannken .vfs_min_mount_data = sizeof (struct msdosfs_args),
1196d285189Shannken .vfs_mount = msdosfs_mount,
1206d285189Shannken .vfs_start = msdosfs_start,
1216d285189Shannken .vfs_unmount = msdosfs_unmount,
1226d285189Shannken .vfs_root = msdosfs_root,
1236d285189Shannken .vfs_quotactl = (void *)eopnotsupp,
1246d285189Shannken .vfs_statvfs = msdosfs_statvfs,
1256d285189Shannken .vfs_sync = msdosfs_sync,
1266d285189Shannken .vfs_vget = msdosfs_vget,
127798256c9Shannken .vfs_loadvnode = msdosfs_loadvnode,
1286d285189Shannken .vfs_fhtovp = msdosfs_fhtovp,
1296d285189Shannken .vfs_vptofh = msdosfs_vptofh,
1306d285189Shannken .vfs_init = msdosfs_init,
1316d285189Shannken .vfs_reinit = msdosfs_reinit,
1326d285189Shannken .vfs_done = msdosfs_done,
1336d285189Shannken .vfs_mountroot = msdosfs_mountroot,
1346d285189Shannken .vfs_snapshot = (void *)eopnotsupp,
1356d285189Shannken .vfs_extattrctl = vfs_stdextattrctl,
136326db3aaShannken .vfs_suspendctl = genfs_suspendctl,
1376d285189Shannken .vfs_renamelock_enter = genfs_renamelock_enter,
1386d285189Shannken .vfs_renamelock_exit = genfs_renamelock_exit,
1396d285189Shannken .vfs_fsync = (void *)eopnotsupp,
1406d285189Shannken .vfs_opv_descs = msdosfs_vnodeopv_descs
14198d58548Sjdolecek };
142a1221b6dSrumble
1439120d451Spgoyette SYSCTL_SETUP(msdosfs_sysctl_setup, "msdosfs sysctl")
144a1221b6dSrumble {
1459120d451Spgoyette sysctl_createv(clog, 0, NULL, NULL,
14628f5ebd8Srumble CTLFLAG_PERMANENT,
14728f5ebd8Srumble CTLTYPE_NODE, "msdosfs",
14828f5ebd8Srumble SYSCTL_DESCR("MS-DOS file system"),
14928f5ebd8Srumble NULL, 0, NULL, 0,
15028f5ebd8Srumble CTL_VFS, 4, CTL_EOL);
15128f5ebd8Srumble /*
15228f5ebd8Srumble * XXX the "4" above could be dynamic, thereby eliminating one
15328f5ebd8Srumble * more instance of the "number to vfs" mapping problem, but
15428f5ebd8Srumble * "4" is the order as taken from sys/mount.h
15528f5ebd8Srumble */
1569120d451Spgoyette }
1579120d451Spgoyette
1589120d451Spgoyette static int
msdos_modcmd(modcmd_t cmd,void * arg)1599120d451Spgoyette msdos_modcmd(modcmd_t cmd, void *arg)
1609120d451Spgoyette {
1619120d451Spgoyette int error;
1629120d451Spgoyette
1639120d451Spgoyette switch (cmd) {
1649120d451Spgoyette case MODULE_CMD_INIT:
1659120d451Spgoyette error = vfs_attach(&msdosfs_vfsops);
1669120d451Spgoyette if (error != 0)
1679120d451Spgoyette break;
16828f5ebd8Srumble break;
169a1221b6dSrumble case MODULE_CMD_FINI:
17028f5ebd8Srumble error = vfs_detach(&msdosfs_vfsops);
17128f5ebd8Srumble if (error != 0)
17228f5ebd8Srumble break;
17328f5ebd8Srumble break;
174a1221b6dSrumble default:
17528f5ebd8Srumble error = ENOTTY;
17628f5ebd8Srumble break;
177a1221b6dSrumble }
17828f5ebd8Srumble
17928f5ebd8Srumble return (error);
180a1221b6dSrumble }
18198d58548Sjdolecek
18298d58548Sjdolecek static int
update_mp(struct mount * mp,struct msdosfs_args * argp)183454af1c0Sdsl update_mp(struct mount *mp, struct msdosfs_args *argp)
18498d58548Sjdolecek {
18598d58548Sjdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
18698d58548Sjdolecek int error;
18798d58548Sjdolecek
18898d58548Sjdolecek pmp->pm_gid = argp->gid;
18998d58548Sjdolecek pmp->pm_uid = argp->uid;
19098d58548Sjdolecek pmp->pm_mask = argp->mask & ALLPERMS;
19162e0ed44Sjdolecek pmp->pm_dirmask = argp->dirmask & ALLPERMS;
192ce112dfcSitojun pmp->pm_gmtoff = argp->gmtoff;
19398d58548Sjdolecek pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT;
19498d58548Sjdolecek
19598d58548Sjdolecek /*
196d82fca09Sabs * GEMDOS knows nothing about win95 long filenames
19798d58548Sjdolecek */
19898d58548Sjdolecek if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
19998d58548Sjdolecek pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
20098d58548Sjdolecek
20198d58548Sjdolecek if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
20298d58548Sjdolecek pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
20398d58548Sjdolecek else if (!(pmp->pm_flags &
20498d58548Sjdolecek (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) {
205a29d4b25Schristos struct vnode *rtvp;
20698d58548Sjdolecek
20798d58548Sjdolecek /*
20898d58548Sjdolecek * Try to divine whether to support Win'95 long filenames
20998d58548Sjdolecek */
21098d58548Sjdolecek if (FAT32(pmp))
21198d58548Sjdolecek pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
21298d58548Sjdolecek else {
213c2e9cb94Sad error = msdosfs_root(mp, LK_EXCLUSIVE, &rtvp);
214c2e9cb94Sad if (error != 0)
21598d58548Sjdolecek return error;
2168086f46eSthorpej pmp->pm_flags |= msdosfs_findwin95(VTODE(rtvp))
21798d58548Sjdolecek ? MSDOSFSMNT_LONGNAME
21898d58548Sjdolecek : MSDOSFSMNT_SHORTNAME;
219a29d4b25Schristos vput(rtvp);
22098d58548Sjdolecek }
22198d58548Sjdolecek }
222caffd8efSjdolecek
223caffd8efSjdolecek mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
224caffd8efSjdolecek
22598d58548Sjdolecek return 0;
22698d58548Sjdolecek }
22798d58548Sjdolecek
22898d58548Sjdolecek int
msdosfs_mountroot(void)229b8817e4aScegger msdosfs_mountroot(void)
23098d58548Sjdolecek {
23198d58548Sjdolecek struct mount *mp;
23295e1ffb1Schristos struct lwp *l = curlwp; /* XXX */
23398d58548Sjdolecek int error;
23498d58548Sjdolecek struct msdosfs_args args;
23598d58548Sjdolecek
23658853410Sthorpej if (device_class(root_device) != DV_DISK)
23798d58548Sjdolecek return (ENODEV);
23898d58548Sjdolecek
23998d58548Sjdolecek if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) {
24098d58548Sjdolecek vrele(rootvp);
24198d58548Sjdolecek return (error);
24298d58548Sjdolecek }
24398d58548Sjdolecek
24462e0ed44Sjdolecek args.flags = MSDOSFSMNT_VERSIONED;
24598d58548Sjdolecek args.uid = 0;
24698d58548Sjdolecek args.gid = 0;
24798d58548Sjdolecek args.mask = 0777;
24862e0ed44Sjdolecek args.version = MSDOSFSMNT_VERSION;
24962e0ed44Sjdolecek args.dirmask = 0777;
25098d58548Sjdolecek
25195e1ffb1Schristos if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) {
25220bb034fShannken vfs_unbusy(mp);
253ebb8f73bShannken vfs_rele(mp);
25498d58548Sjdolecek return (error);
25598d58548Sjdolecek }
25698d58548Sjdolecek
257a06b275eSthorpej if ((error = update_mp(mp, &args)) != 0) {
25861e8303eSpooka (void)msdosfs_unmount(mp, 0);
25920bb034fShannken vfs_unbusy(mp);
260ebb8f73bShannken vfs_rele(mp);
26198d58548Sjdolecek vrele(rootvp);
26298d58548Sjdolecek return (error);
26398d58548Sjdolecek }
26498d58548Sjdolecek
2650b725b63Schristos mountlist_append(mp);
26661e8303eSpooka (void)msdosfs_statvfs(mp, &mp->mnt_stat);
26720bb034fShannken vfs_unbusy(mp);
26898d58548Sjdolecek return (0);
26998d58548Sjdolecek }
27098d58548Sjdolecek
27198d58548Sjdolecek /*
27298d58548Sjdolecek * mp - path - addr in user space of mount point (ie /usr or whatever)
27398d58548Sjdolecek * data - addr in user space of mount params including the name of the block
27498d58548Sjdolecek * special file to treat as a filesystem.
27598d58548Sjdolecek */
27698d58548Sjdolecek int
msdosfs_mount(struct mount * mp,const char * path,void * data,size_t * data_len)277454af1c0Sdsl msdosfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
27898d58548Sjdolecek {
27961e8303eSpooka struct lwp *l = curlwp;
28098d58548Sjdolecek struct vnode *devvp; /* vnode for blk device to mount */
2812721ab6cSdsl struct msdosfs_args *args = data; /* holds data from mount request */
28298d58548Sjdolecek /* msdosfs specific mount control block */
28398d58548Sjdolecek struct msdosfsmount *pmp = NULL;
28498d58548Sjdolecek int error, flags;
28598d58548Sjdolecek mode_t accessmode;
28698d58548Sjdolecek
28723f76b6dSmaxv if (args == NULL)
28823f76b6dSmaxv return EINVAL;
2892721ab6cSdsl if (*data_len < sizeof *args)
2902721ab6cSdsl return EINVAL;
2912721ab6cSdsl
29298d58548Sjdolecek if (mp->mnt_flag & MNT_GETARGS) {
29398d58548Sjdolecek pmp = VFSTOMSDOSFS(mp);
29498d58548Sjdolecek if (pmp == NULL)
29598d58548Sjdolecek return EIO;
2962721ab6cSdsl args->fspec = NULL;
2972721ab6cSdsl args->uid = pmp->pm_uid;
2982721ab6cSdsl args->gid = pmp->pm_gid;
2992721ab6cSdsl args->mask = pmp->pm_mask;
3002721ab6cSdsl args->flags = pmp->pm_flags;
3012721ab6cSdsl args->version = MSDOSFSMNT_VERSION;
3022721ab6cSdsl args->dirmask = pmp->pm_dirmask;
3032721ab6cSdsl args->gmtoff = pmp->pm_gmtoff;
3042721ab6cSdsl *data_len = sizeof *args;
3052721ab6cSdsl return 0;
3066b98a174Schristos }
30762e0ed44Sjdolecek
30862e0ed44Sjdolecek /*
30962e0ed44Sjdolecek * If not versioned (i.e. using old mount_msdos(8)), fill in
31062e0ed44Sjdolecek * the additional structure items with suitable defaults.
31162e0ed44Sjdolecek */
3122721ab6cSdsl if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) {
3132721ab6cSdsl args->version = 1;
3142721ab6cSdsl args->dirmask = args->mask;
31562e0ed44Sjdolecek }
31662e0ed44Sjdolecek
31798d58548Sjdolecek /*
318d1e0d393Sjdolecek * Reset GMT offset for pre-v3 mount structure args.
319d1e0d393Sjdolecek */
3202721ab6cSdsl if (args->version < 3)
3212721ab6cSdsl args->gmtoff = 0;
322d1e0d393Sjdolecek
323d1e0d393Sjdolecek /*
32498d58548Sjdolecek * If updating, check whether changing from read-only to
32598d58548Sjdolecek * read/write; if there is no device name, that's all we do.
32698d58548Sjdolecek */
32798d58548Sjdolecek if (mp->mnt_flag & MNT_UPDATE) {
32898d58548Sjdolecek pmp = VFSTOMSDOSFS(mp);
32998d58548Sjdolecek error = 0;
330f9642d24Selad if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
331f9642d24Selad (mp->mnt_flag & MNT_RDONLY)) {
33298d58548Sjdolecek flags = WRITECLOSE;
33398d58548Sjdolecek if (mp->mnt_flag & MNT_FORCE)
33498d58548Sjdolecek flags |= FORCECLOSE;
33598d58548Sjdolecek error = vflush(mp, NULLVP, flags);
33698d58548Sjdolecek }
33798d58548Sjdolecek if (!error && (mp->mnt_flag & MNT_RELOAD))
33898d58548Sjdolecek /* not yet implemented */
33998d58548Sjdolecek error = EOPNOTSUPP;
3406b98a174Schristos if (error) {
341549e2319Smaxv DPRINTF("vflush %d", error);
34298d58548Sjdolecek return (error);
3436b98a174Schristos }
344f9642d24Selad if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
345f9642d24Selad (mp->mnt_iflag & IMNT_WANTRDWR)) {
34698d58548Sjdolecek /*
34798d58548Sjdolecek * If upgrade to read-write by non-root, then verify
34898d58548Sjdolecek * that user has necessary permissions on the device.
3499670d2e4Selad *
350f9642d24Selad * Permission to update a mount is checked higher, so
351f9642d24Selad * here we presume updating the mount is okay (for
352f9642d24Selad * example, as far as securelevel goes) which leaves us
353f9642d24Selad * with the normal check.
35498d58548Sjdolecek */
35598d58548Sjdolecek devvp = pmp->pm_devvp;
35698d58548Sjdolecek vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
3570c9d8d15Selad error = kauth_authorize_system(l->l_cred,
3580c9d8d15Selad KAUTH_SYSTEM_MOUNT, KAUTH_REQ_SYSTEM_MOUNT_DEVICE,
3590c9d8d15Selad mp, devvp, KAUTH_ARG(VREAD | VWRITE));
3601423e65bShannken VOP_UNLOCK(devvp);
361549e2319Smaxv DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
36298d58548Sjdolecek if (error)
36398d58548Sjdolecek return (error);
3649670d2e4Selad
36598d58548Sjdolecek pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
36698d58548Sjdolecek }
3672721ab6cSdsl if (args->fspec == NULL) {
368549e2319Smaxv DPRINTF("missing fspec");
3692a3e5eebSjmmv return EINVAL;
37098d58548Sjdolecek }
3716b98a174Schristos }
37298d58548Sjdolecek /*
37398d58548Sjdolecek * Not an update, or updating the name: look up the name
37498d58548Sjdolecek * and verify that it refers to a sensible block device.
37598d58548Sjdolecek */
376effcf1afSdholland error = namei_simple_user(args->fspec,
377effcf1afSdholland NSM_FOLLOW_NOEMULROOT, &devvp);
378effcf1afSdholland if (error != 0) {
379549e2319Smaxv DPRINTF("namei %d", error);
38098d58548Sjdolecek return (error);
3816b98a174Schristos }
38298d58548Sjdolecek
38398d58548Sjdolecek if (devvp->v_type != VBLK) {
384549e2319Smaxv DPRINTF("not block");
38598d58548Sjdolecek vrele(devvp);
38698d58548Sjdolecek return (ENOTBLK);
38798d58548Sjdolecek }
38898d58548Sjdolecek if (bdevsw_lookup(devvp->v_rdev) == NULL) {
389549e2319Smaxv DPRINTF("no block switch");
39098d58548Sjdolecek vrele(devvp);
39198d58548Sjdolecek return (ENXIO);
39298d58548Sjdolecek }
39398d58548Sjdolecek /*
39498d58548Sjdolecek * If mount by non-root, then verify that user has necessary
39598d58548Sjdolecek * permissions on the device.
39698d58548Sjdolecek */
39798d58548Sjdolecek accessmode = VREAD;
39898d58548Sjdolecek if ((mp->mnt_flag & MNT_RDONLY) == 0)
39998d58548Sjdolecek accessmode |= VWRITE;
40098d58548Sjdolecek vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
4010c9d8d15Selad error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
4020c9d8d15Selad KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
4031423e65bShannken VOP_UNLOCK(devvp);
40498d58548Sjdolecek if (error) {
405549e2319Smaxv DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error);
40698d58548Sjdolecek vrele(devvp);
40798d58548Sjdolecek return (error);
40898d58548Sjdolecek }
40998d58548Sjdolecek if ((mp->mnt_flag & MNT_UPDATE) == 0) {
410a29d4b25Schristos int xflags;
4110461b30aSmycroft
4120461b30aSmycroft if (mp->mnt_flag & MNT_RDONLY)
413a29d4b25Schristos xflags = FREAD;
4140461b30aSmycroft else
415a29d4b25Schristos xflags = FREAD|FWRITE;
416d84a65ddShannken vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
41761e8303eSpooka error = VOP_OPEN(devvp, xflags, FSCRED);
418d84a65ddShannken VOP_UNLOCK(devvp);
4196b98a174Schristos if (error) {
420549e2319Smaxv DPRINTF("VOP_OPEN %d", error);
4210461b30aSmycroft goto fail;
4226b98a174Schristos }
4232721ab6cSdsl error = msdosfs_mountfs(devvp, mp, l, args);
4240461b30aSmycroft if (error) {
425549e2319Smaxv DPRINTF("msdosfs_mountfs %d", error);
4260461b30aSmycroft vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
42761e8303eSpooka (void) VOP_CLOSE(devvp, xflags, NOCRED);
4281423e65bShannken VOP_UNLOCK(devvp);
4290461b30aSmycroft goto fail;
4300461b30aSmycroft }
43198d58548Sjdolecek #ifdef MSDOSFS_DEBUG /* only needed for the printf below */
43298d58548Sjdolecek pmp = VFSTOMSDOSFS(mp);
43398d58548Sjdolecek #endif
43498d58548Sjdolecek } else {
4350461b30aSmycroft vrele(devvp);
4366b98a174Schristos if (devvp != pmp->pm_devvp) {
437549e2319Smaxv DPRINTF("devvp %p pmp %p", devvp, pmp->pm_devvp);
4380461b30aSmycroft return (EINVAL); /* needs translation */
43998d58548Sjdolecek }
4406b98a174Schristos }
4412721ab6cSdsl if ((error = update_mp(mp, args)) != 0) {
44261e8303eSpooka msdosfs_unmount(mp, MNT_FORCE);
443549e2319Smaxv DPRINTF("update_mp %d", error);
44498d58548Sjdolecek return error;
44598d58548Sjdolecek }
44698d58548Sjdolecek
44798d58548Sjdolecek #ifdef MSDOSFS_DEBUG
44898d58548Sjdolecek printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
44998d58548Sjdolecek #endif
4502721ab6cSdsl return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
451e24b0872Spooka mp->mnt_op->vfs_name, mp, l);
4520461b30aSmycroft
4530461b30aSmycroft fail:
4540461b30aSmycroft vrele(devvp);
4550461b30aSmycroft return (error);
45698d58548Sjdolecek }
45798d58548Sjdolecek
45898d58548Sjdolecek int
msdosfs_mountfs(struct vnode * devvp,struct mount * mp,struct lwp * l,struct msdosfs_args * argp)459454af1c0Sdsl msdosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l, struct msdosfs_args *argp)
46098d58548Sjdolecek {
46198d58548Sjdolecek struct msdosfsmount *pmp;
46298d58548Sjdolecek struct buf *bp;
46398d58548Sjdolecek dev_t dev = devvp->v_rdev;
46498d58548Sjdolecek union bootsector *bsp;
46598d58548Sjdolecek struct byte_bpb33 *b33;
46698d58548Sjdolecek struct byte_bpb50 *b50;
46798d58548Sjdolecek struct byte_bpb710 *b710;
468ba0d3275Smlelstv uint8_t SecPerClust;
4691273c3cbSmaxv int ronly, error, BlkPerSec;
470ba0d3275Smlelstv uint64_t psize;
471ba0d3275Smlelstv unsigned secsize;
4725815ca8fSmlelstv u_long fatbytes, fatblocksecs;
47398d58548Sjdolecek
4740461b30aSmycroft /* Flush out any old buffers remaining from a previous use. */
475*dad6cdf0Shannken vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
476*dad6cdf0Shannken error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0);
477*dad6cdf0Shannken VOP_UNLOCK(devvp);
478*dad6cdf0Shannken if (error)
47998d58548Sjdolecek return (error);
48098d58548Sjdolecek
48198d58548Sjdolecek ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
48298d58548Sjdolecek
48398d58548Sjdolecek bp = NULL; /* both used in error_exit */
48498d58548Sjdolecek pmp = NULL;
48598d58548Sjdolecek
486ba0d3275Smlelstv error = getdisksize(devvp, &psize, &secsize);
48728d320dcStsutsui if (error) {
488b0e7c8b4Spooka if (argp->flags & MSDOSFSMNT_GEMDOSFS)
4892794f988Skochi goto error_exit;
4904fe640bdSpooka
4914fe640bdSpooka /* ok, so it failed. we most likely don't need the info */
492b0e7c8b4Spooka secsize = DEV_BSIZE;
493b0e7c8b4Spooka psize = 0;
4944fe640bdSpooka error = 0;
495b0e7c8b4Spooka }
4968ee3c88dSmaxv if (secsize < DEV_BSIZE) {
497549e2319Smaxv DPRINTF("Invalid block secsize (%d < DEV_BSIZE)", secsize);
4988ee3c88dSmaxv error = EINVAL;
4998ee3c88dSmaxv goto error_exit;
5008ee3c88dSmaxv }
501ba0d3275Smlelstv
5026b98a174Schristos if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
5031273c3cbSmaxv if (secsize != GEMDOSFS_BSIZE) {
504549e2319Smaxv DPRINTF("Invalid block secsize %d for GEMDOS", secsize);
50598d58548Sjdolecek error = EINVAL;
50698d58548Sjdolecek goto error_exit;
50798d58548Sjdolecek }
5081273c3cbSmaxv }
50998d58548Sjdolecek
51098d58548Sjdolecek /*
51198d58548Sjdolecek * Read the boot sector of the filesystem, and then check the
51298d58548Sjdolecek * boot signature. If not a dos boot sector then error out.
51398d58548Sjdolecek */
5142d356080Schristos if (secsize < sizeof(*b50)) {
515135c211dSchristos DPRINTF("50 bootsec %u\n", secsize);
5162d356080Schristos error = EINVAL;
5172d356080Schristos goto error_exit;
5182d356080Schristos }
5196e392401Smaxv if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
52098d58548Sjdolecek goto error_exit;
52198d58548Sjdolecek bsp = (union bootsector *)bp->b_data;
52298d58548Sjdolecek b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
52398d58548Sjdolecek b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
5241ab040b9Slukem b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
52598d58548Sjdolecek
52628390de1Sryoon #if 0
52728390de1Sryoon /*
52828390de1Sryoon * Some FAT partition, for example Raspberry Pi Pico's
52928390de1Sryoon * USB mass storage, does not have exptected BOOTSIGs.
53028390de1Sryoon * According to FreeBSD's comment, some PC-9800/9821
53128390de1Sryoon * FAT floppy disks have similar problems.
53228390de1Sryoon */
53398d58548Sjdolecek if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
53498d58548Sjdolecek if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
53598d58548Sjdolecek || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
536549e2319Smaxv DPRINTF("bootsig0 %d bootsig1 %d",
5376b98a174Schristos bsp->bs50.bsBootSectSig0,
538549e2319Smaxv bsp->bs50.bsBootSectSig1);
53998d58548Sjdolecek error = EINVAL;
54098d58548Sjdolecek goto error_exit;
54198d58548Sjdolecek }
54298d58548Sjdolecek }
54328390de1Sryoon #endif
54498d58548Sjdolecek
54556ce0b03Smaxv pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK|M_ZERO);
54698d58548Sjdolecek pmp->pm_mountp = mp;
54798d58548Sjdolecek
54898d58548Sjdolecek /*
54998d58548Sjdolecek * Compute several useful quantities from the bpb in the
55098d58548Sjdolecek * bootsector. Copy in the dos 5 variant of the bpb then fix up
55198d58548Sjdolecek * the fields that are different between dos 5 and dos 3.3.
55298d58548Sjdolecek */
55398d58548Sjdolecek SecPerClust = b50->bpbSecPerClust;
55498d58548Sjdolecek pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
55598d58548Sjdolecek pmp->pm_ResSectors = getushort(b50->bpbResSectors);
55698d58548Sjdolecek pmp->pm_FATs = b50->bpbFATs;
55798d58548Sjdolecek pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
55898d58548Sjdolecek pmp->pm_Sectors = getushort(b50->bpbSectors);
55998d58548Sjdolecek pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
56098d58548Sjdolecek pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
56198d58548Sjdolecek pmp->pm_Heads = getushort(b50->bpbHeads);
56298d58548Sjdolecek pmp->pm_Media = b50->bpbMedia;
56398d58548Sjdolecek
56498d58548Sjdolecek if (pmp->pm_Sectors == 0) {
56598d58548Sjdolecek pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
56698d58548Sjdolecek pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
56798d58548Sjdolecek } else {
5682d356080Schristos if (secsize < sizeof(*b33)) {
569135c211dSchristos DPRINTF("33 bootsec %u\n", secsize);
5702d356080Schristos error = EINVAL;
5712d356080Schristos goto error_exit;
5722d356080Schristos }
57398d58548Sjdolecek pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
57498d58548Sjdolecek pmp->pm_HugeSectors = pmp->pm_Sectors;
57598d58548Sjdolecek }
57698d58548Sjdolecek
5771273c3cbSmaxv /*
5781273c3cbSmaxv * Sanity checks, from the FAT specification:
5791273c3cbSmaxv * - sectors per cluster: >= 1, power of 2
5801273c3cbSmaxv * - logical sector size: >= 1, power of 2
5811273c3cbSmaxv * - cluster size: <= max FS block size
5821273c3cbSmaxv * - number of sectors: >= 1
5831273c3cbSmaxv */
5841273c3cbSmaxv if ((SecPerClust == 0) || !powerof2(SecPerClust) ||
5851273c3cbSmaxv (pmp->pm_BytesPerSec == 0) || !powerof2(pmp->pm_BytesPerSec) ||
5861273c3cbSmaxv (SecPerClust * pmp->pm_BytesPerSec > MAXBSIZE) ||
5871273c3cbSmaxv (pmp->pm_HugeSectors == 0)) {
588549e2319Smaxv DPRINTF("consistency checks");
5891273c3cbSmaxv error = EINVAL;
5901273c3cbSmaxv goto error_exit;
5911273c3cbSmaxv }
5921273c3cbSmaxv
5931273c3cbSmaxv if (!(argp->flags & MSDOSFSMNT_GEMDOSFS) &&
5941273c3cbSmaxv (pmp->pm_SecPerTrack > 63)) {
595549e2319Smaxv DPRINTF("SecPerTrack %d", pmp->pm_SecPerTrack);
5961273c3cbSmaxv error = EINVAL;
5971273c3cbSmaxv goto error_exit;
5981273c3cbSmaxv }
5991273c3cbSmaxv
60098d58548Sjdolecek if (pmp->pm_RootDirEnts == 0) {
6012d356080Schristos if (secsize < sizeof(*b710)) {
602135c211dSchristos DPRINTF("710 bootsec %u\n", secsize);
6032d356080Schristos error = EINVAL;
6042d356080Schristos goto error_exit;
6052d356080Schristos }
60656ce0b03Smaxv unsigned short FSVers = getushort(b710->bpbFSVers);
60756ce0b03Smaxv unsigned short ExtFlags = getushort(b710->bpbExtFlags);
6087db024f8Sgdt /*
6097db024f8Sgdt * Some say that bsBootSectSig[23] must be zero, but
6107db024f8Sgdt * Windows does not require this and some digital cameras
6117db024f8Sgdt * do not set these to zero. Therefore, do not insist.
6127db024f8Sgdt */
61356ce0b03Smaxv if (pmp->pm_Sectors || pmp->pm_FATsecs || FSVers) {
614549e2319Smaxv DPRINTF("Sectors %d FATsecs %lu FSVers %d",
615549e2319Smaxv pmp->pm_Sectors, pmp->pm_FATsecs, FSVers);
61698d58548Sjdolecek error = EINVAL;
61798d58548Sjdolecek goto error_exit;
61898d58548Sjdolecek }
61998d58548Sjdolecek pmp->pm_fatmask = FAT32_MASK;
62098d58548Sjdolecek pmp->pm_fatmult = 4;
62198d58548Sjdolecek pmp->pm_fatdiv = 1;
62298d58548Sjdolecek pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
62398d58548Sjdolecek
62456ce0b03Smaxv /* Mirroring is enabled if the FATMIRROR bit is not set. */
62556ce0b03Smaxv if ((ExtFlags & FATMIRROR) == 0)
62698d58548Sjdolecek pmp->pm_flags |= MSDOSFS_FATMIRROR;
62798d58548Sjdolecek else
62856ce0b03Smaxv pmp->pm_curfat = ExtFlags & FATNUM;
62998d58548Sjdolecek } else
63098d58548Sjdolecek pmp->pm_flags |= MSDOSFS_FATMIRROR;
63198d58548Sjdolecek
63298d58548Sjdolecek if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
63398d58548Sjdolecek if (FAT32(pmp)) {
63456ce0b03Smaxv /* GEMDOS doesn't know FAT32. */
635549e2319Smaxv DPRINTF("FAT32 for GEMDOS");
63698d58548Sjdolecek error = EINVAL;
63798d58548Sjdolecek goto error_exit;
63898d58548Sjdolecek }
63998d58548Sjdolecek
64098d58548Sjdolecek /*
64198d58548Sjdolecek * Check a few values (could do some more):
6421273c3cbSmaxv * - logical sector size: >= block size
6431273c3cbSmaxv * - number of sectors: <= size of partition
64498d58548Sjdolecek */
6451273c3cbSmaxv if ((pmp->pm_BytesPerSec < GEMDOSFS_BSIZE) ||
6461273c3cbSmaxv (pmp->pm_HugeSectors *
6471273c3cbSmaxv (pmp->pm_BytesPerSec / GEMDOSFS_BSIZE) > psize)) {
648549e2319Smaxv DPRINTF("consistency checks for GEMDOS");
64998d58548Sjdolecek error = EINVAL;
65098d58548Sjdolecek goto error_exit;
65198d58548Sjdolecek }
65298d58548Sjdolecek /*
65398d58548Sjdolecek * XXX - Many parts of the msdosfs driver seem to assume that
65498d58548Sjdolecek * the number of bytes per logical sector (BytesPerSec) will
65598d58548Sjdolecek * always be the same as the number of bytes per disk block
65698d58548Sjdolecek * Let's pretend it is.
65798d58548Sjdolecek */
6581273c3cbSmaxv BlkPerSec = pmp->pm_BytesPerSec / GEMDOSFS_BSIZE;
6591273c3cbSmaxv pmp->pm_BytesPerSec = GEMDOSFS_BSIZE;
6601273c3cbSmaxv pmp->pm_HugeSectors *= BlkPerSec;
6611273c3cbSmaxv pmp->pm_HiddenSects *= BlkPerSec;
6621273c3cbSmaxv pmp->pm_ResSectors *= BlkPerSec;
6631273c3cbSmaxv pmp->pm_Sectors *= BlkPerSec;
6641273c3cbSmaxv pmp->pm_FATsecs *= BlkPerSec;
6651273c3cbSmaxv SecPerClust *= BlkPerSec;
66698d58548Sjdolecek }
667933a1a1aSpooka
668933a1a1aSpooka /* Check that fs has nonzero FAT size */
669933a1a1aSpooka if (pmp->pm_FATsecs == 0) {
670549e2319Smaxv DPRINTF("FATsecs is 0");
671933a1a1aSpooka error = EINVAL;
672933a1a1aSpooka goto error_exit;
673933a1a1aSpooka }
674933a1a1aSpooka
67598d58548Sjdolecek pmp->pm_fatblk = pmp->pm_ResSectors;
67698d58548Sjdolecek if (FAT32(pmp)) {
6772d356080Schristos if (secsize < sizeof(*b710)) {
678135c211dSchristos DPRINTF("710 bootsec %u\n", secsize);
6792d356080Schristos error = EINVAL;
6802d356080Schristos goto error_exit;
6812d356080Schristos }
68298d58548Sjdolecek pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
68398d58548Sjdolecek pmp->pm_firstcluster = pmp->pm_fatblk
68498d58548Sjdolecek + (pmp->pm_FATs * pmp->pm_FATsecs);
68598d58548Sjdolecek pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
68698d58548Sjdolecek } else {
68798d58548Sjdolecek pmp->pm_rootdirblk = pmp->pm_fatblk +
68898d58548Sjdolecek (pmp->pm_FATs * pmp->pm_FATsecs);
68998d58548Sjdolecek pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
69098d58548Sjdolecek + pmp->pm_BytesPerSec - 1)
69198d58548Sjdolecek / pmp->pm_BytesPerSec;/* in sectors */
69298d58548Sjdolecek pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
69398d58548Sjdolecek }
69498d58548Sjdolecek
69598d58548Sjdolecek pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
69698d58548Sjdolecek SecPerClust;
69798d58548Sjdolecek pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
69898d58548Sjdolecek pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
69998d58548Sjdolecek
70098d58548Sjdolecek if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
70161ec757eSmlelstv if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
70298d58548Sjdolecek pmp->pm_fatmask = FAT12_MASK;
70398d58548Sjdolecek pmp->pm_fatmult = 3;
70498d58548Sjdolecek pmp->pm_fatdiv = 2;
70598d58548Sjdolecek } else {
70698d58548Sjdolecek pmp->pm_fatmask = FAT16_MASK;
70798d58548Sjdolecek pmp->pm_fatmult = 2;
70898d58548Sjdolecek pmp->pm_fatdiv = 1;
70998d58548Sjdolecek }
71098d58548Sjdolecek } else if (pmp->pm_fatmask == 0) {
71198d58548Sjdolecek if (pmp->pm_maxcluster
71298d58548Sjdolecek <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
71398d58548Sjdolecek /*
71498d58548Sjdolecek * This will usually be a floppy disk. This size makes
71548958e52Sjakllsch * sure that one FAT entry will not be split across
71698d58548Sjdolecek * multiple blocks.
71798d58548Sjdolecek */
71898d58548Sjdolecek pmp->pm_fatmask = FAT12_MASK;
71998d58548Sjdolecek pmp->pm_fatmult = 3;
72098d58548Sjdolecek pmp->pm_fatdiv = 2;
72198d58548Sjdolecek } else {
72298d58548Sjdolecek pmp->pm_fatmask = FAT16_MASK;
72398d58548Sjdolecek pmp->pm_fatmult = 2;
72498d58548Sjdolecek pmp->pm_fatdiv = 1;
72598d58548Sjdolecek }
72698d58548Sjdolecek }
72798d58548Sjdolecek
7285815ca8fSmlelstv /* validate cluster count against FAT */
7295815ca8fSmlelstv if ((pmp->pm_maxcluster & pmp->pm_fatmask) != pmp->pm_maxcluster) {
7305815ca8fSmlelstv DPRINTF("maxcluster %lu outside of mask %#lx\n",
7315815ca8fSmlelstv pmp->pm_maxcluster, pmp->pm_fatmask);
7325815ca8fSmlelstv error = EINVAL;
7335815ca8fSmlelstv goto error_exit;
7345815ca8fSmlelstv }
7355815ca8fSmlelstv
7365815ca8fSmlelstv /* validate FAT size */
7375815ca8fSmlelstv fatbytes = (pmp->pm_maxcluster+1) * pmp->pm_fatmult / pmp->pm_fatdiv;
7385815ca8fSmlelstv fatblocksecs = howmany(fatbytes, pmp->pm_BytesPerSec);
7395815ca8fSmlelstv
740d8f0582eSmlelstv if (pmp->pm_FATsecs < fatblocksecs) {
741d8f0582eSmlelstv DPRINTF("FATsecs %lu < real %lu\n", pmp->pm_FATsecs,
7425815ca8fSmlelstv fatblocksecs);
7435815ca8fSmlelstv error = EINVAL;
7445815ca8fSmlelstv goto error_exit;
7455815ca8fSmlelstv }
7465815ca8fSmlelstv
7475815ca8fSmlelstv if (FAT12(pmp)) {
7485815ca8fSmlelstv /*
7495815ca8fSmlelstv * limit block size to what is needed to read a FAT block
7505815ca8fSmlelstv * to not exceed MAXBSIZE
7515815ca8fSmlelstv */
752d1579b2dSriastradh pmp->pm_fatblocksec = uimin(3, fatblocksecs);
7535815ca8fSmlelstv pmp->pm_fatblocksize = pmp->pm_fatblocksec
7545815ca8fSmlelstv * pmp->pm_BytesPerSec;
7555815ca8fSmlelstv } else {
7565815ca8fSmlelstv pmp->pm_fatblocksize = MAXBSIZE;
7575815ca8fSmlelstv pmp->pm_fatblocksec = pmp->pm_fatblocksize
7585815ca8fSmlelstv / pmp->pm_BytesPerSec;
7595815ca8fSmlelstv }
7605815ca8fSmlelstv
76198d58548Sjdolecek pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
76298d58548Sjdolecek
76398d58548Sjdolecek /*
76498d58548Sjdolecek * Compute mask and shift value for isolating cluster relative byte
76598d58548Sjdolecek * offsets and cluster numbers from a file offset.
76698d58548Sjdolecek */
76798d58548Sjdolecek pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
76898d58548Sjdolecek pmp->pm_crbomask = pmp->pm_bpcluster - 1;
76998d58548Sjdolecek pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
77098d58548Sjdolecek
77198d58548Sjdolecek /*
77298d58548Sjdolecek * Check for valid cluster size
77398d58548Sjdolecek * must be a power of 2
77498d58548Sjdolecek */
77598d58548Sjdolecek if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
776549e2319Smaxv DPRINTF("bpcluster %lu cnshift %lu", pmp->pm_bpcluster,
777549e2319Smaxv pmp->pm_cnshift);
77898d58548Sjdolecek error = EINVAL;
77998d58548Sjdolecek goto error_exit;
78098d58548Sjdolecek }
78198d58548Sjdolecek
78298d58548Sjdolecek /*
783e7f2d5bdSjakllsch * Cluster size must be within limit of MAXBSIZE.
784e7f2d5bdSjakllsch * Many FAT filesystems will not have clusters larger than
785e7f2d5bdSjakllsch * 32KiB due to limits in Windows versions before Vista.
786e7f2d5bdSjakllsch */
787e7f2d5bdSjakllsch if (pmp->pm_bpcluster > MAXBSIZE) {
788549e2319Smaxv DPRINTF("bpcluster %lu > MAXBSIZE %d",
789549e2319Smaxv pmp->pm_bpcluster, MAXBSIZE);
790e7f2d5bdSjakllsch error = EINVAL;
791e7f2d5bdSjakllsch goto error_exit;
792e7f2d5bdSjakllsch }
793e7f2d5bdSjakllsch
794e7f2d5bdSjakllsch /*
79598d58548Sjdolecek * Release the bootsector buffer.
79698d58548Sjdolecek */
7979f56dfa5Sad brelse(bp, BC_AGE);
79898d58548Sjdolecek bp = NULL;
79998d58548Sjdolecek
80098d58548Sjdolecek /*
80198d58548Sjdolecek * Check FSInfo.
80298d58548Sjdolecek */
80398d58548Sjdolecek if (pmp->pm_fsinfo) {
80498d58548Sjdolecek struct fsinfo *fp;
805c0371c86Schristos const int rdsz = roundup(sizeof(*fp), pmp->pm_BytesPerSec);
80698d58548Sjdolecek
8074f0ca272Sscw /*
8084f0ca272Sscw * XXX If the fsinfo block is stored on media with
8094f0ca272Sscw * 2KB or larger sectors, is the fsinfo structure
8104f0ca272Sscw * padded at the end or in the middle?
8114f0ca272Sscw */
8124f0ca272Sscw if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
8136e392401Smaxv rdsz, 0, &bp)) != 0)
81498d58548Sjdolecek goto error_exit;
81598d58548Sjdolecek fp = (struct fsinfo *)bp->b_data;
81698d58548Sjdolecek if (!memcmp(fp->fsisig1, "RRaA", 4)
81798d58548Sjdolecek && !memcmp(fp->fsisig2, "rrAa", 4)
81898d58548Sjdolecek && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
81998d58548Sjdolecek && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
82098d58548Sjdolecek pmp->pm_nxtfree = getulong(fp->fsinxtfree);
82198d58548Sjdolecek else
82298d58548Sjdolecek pmp->pm_fsinfo = 0;
8239f56dfa5Sad brelse(bp, 0);
82498d58548Sjdolecek bp = NULL;
82598d58548Sjdolecek }
82698d58548Sjdolecek
82798d58548Sjdolecek /*
82898d58548Sjdolecek * Check and validate (or perhaps invalidate?) the fsinfo structure?
82998d58548Sjdolecek * XXX
83098d58548Sjdolecek */
83198d58548Sjdolecek if (pmp->pm_fsinfo) {
832a8a52604Sjakllsch if ((pmp->pm_nxtfree == 0xffffffffUL) ||
833a8a52604Sjakllsch (pmp->pm_nxtfree > pmp->pm_maxcluster))
83498d58548Sjdolecek pmp->pm_fsinfo = 0;
83598d58548Sjdolecek }
83698d58548Sjdolecek
83798d58548Sjdolecek /*
83898d58548Sjdolecek * Allocate memory for the bitmap of allocated clusters, and then
83998d58548Sjdolecek * fill it in.
84098d58548Sjdolecek */
8418b020b7eSjakllsch pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS)
84298d58548Sjdolecek / N_INUSEBITS)
84398d58548Sjdolecek * sizeof(*pmp->pm_inusemap),
84498d58548Sjdolecek M_MSDOSFSFAT, M_WAITOK);
84598d58548Sjdolecek
84698d58548Sjdolecek /*
84798d58548Sjdolecek * fillinusemap() needs pm_devvp.
84898d58548Sjdolecek */
84998d58548Sjdolecek pmp->pm_dev = dev;
85098d58548Sjdolecek pmp->pm_devvp = devvp;
85198d58548Sjdolecek
85298d58548Sjdolecek /*
85398d58548Sjdolecek * Have the inuse map filled in.
85498d58548Sjdolecek */
8558086f46eSthorpej if ((error = msdosfs_fillinusemap(pmp)) != 0) {
856549e2319Smaxv DPRINTF("fillinusemap %d", error);
85798d58548Sjdolecek goto error_exit;
8586b98a174Schristos }
85998d58548Sjdolecek
86098d58548Sjdolecek /*
86148958e52Sjakllsch * If they want FAT updates to be synchronous then let them suffer
86298d58548Sjdolecek * the performance degradation in exchange for the on disk copy of
86348958e52Sjakllsch * the FAT being correct just about all the time. I suppose this
86498d58548Sjdolecek * would be a good thing to turn on if the kernel is still flakey.
86598d58548Sjdolecek */
86698d58548Sjdolecek if (mp->mnt_flag & MNT_SYNCHRONOUS)
86798d58548Sjdolecek pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
86898d58548Sjdolecek
86998d58548Sjdolecek /*
87098d58548Sjdolecek * Finish up.
87198d58548Sjdolecek */
87298d58548Sjdolecek if (ronly)
87398d58548Sjdolecek pmp->pm_flags |= MSDOSFSMNT_RONLY;
87498d58548Sjdolecek else
87598d58548Sjdolecek pmp->pm_fmod = 1;
87698d58548Sjdolecek mp->mnt_data = pmp;
8776bd1d6d4Schristos mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
8786bd1d6d4Schristos mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS);
8796bd1d6d4Schristos mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
880caffd8efSjdolecek mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
88198d58548Sjdolecek mp->mnt_flag |= MNT_LOCAL;
882c90f9c8cSad mp->mnt_iflag |= IMNT_SHRLOOKUP;
88398d58548Sjdolecek mp->mnt_dev_bshift = pmp->pm_bnshift;
88498d58548Sjdolecek mp->mnt_fs_bshift = pmp->pm_cnshift;
88598d58548Sjdolecek
88698d58548Sjdolecek /*
88798d58548Sjdolecek * If we ever do quotas for DOS filesystems this would be a place
88898d58548Sjdolecek * to fill in the info in the msdosfsmount structure. You dolt,
88998d58548Sjdolecek * quotas on dos filesystems make no sense because files have no
89098d58548Sjdolecek * owners on dos filesystems. of course there is some empty space
89198d58548Sjdolecek * in the directory entry where we could put uid's and gid's.
89298d58548Sjdolecek */
89381723cc9Spooka
8943881f4f3Shannken spec_node_setmountedfs(devvp, mp);
89598d58548Sjdolecek
89698d58548Sjdolecek return (0);
89798d58548Sjdolecek
898c8dcca66Spooka error_exit:
89998d58548Sjdolecek if (bp)
9009f56dfa5Sad brelse(bp, BC_AGE);
90198d58548Sjdolecek if (pmp) {
90298d58548Sjdolecek if (pmp->pm_inusemap)
90398d58548Sjdolecek free(pmp->pm_inusemap, M_MSDOSFSFAT);
90498d58548Sjdolecek free(pmp, M_MSDOSFSMNT);
90598d58548Sjdolecek mp->mnt_data = NULL;
90698d58548Sjdolecek }
90798d58548Sjdolecek return (error);
90898d58548Sjdolecek }
90998d58548Sjdolecek
91098d58548Sjdolecek int
msdosfs_start(struct mount * mp,int flags)91161e8303eSpooka msdosfs_start(struct mount *mp, int flags)
91298d58548Sjdolecek {
91398d58548Sjdolecek
91498d58548Sjdolecek return (0);
91598d58548Sjdolecek }
91698d58548Sjdolecek
91798d58548Sjdolecek /*
91898d58548Sjdolecek * Unmount the filesystem described by mp.
91998d58548Sjdolecek */
92098d58548Sjdolecek int
msdosfs_unmount(struct mount * mp,int mntflags)921454af1c0Sdsl msdosfs_unmount(struct mount *mp, int mntflags)
92298d58548Sjdolecek {
92398d58548Sjdolecek struct msdosfsmount *pmp;
92498d58548Sjdolecek int error, flags;
92598d58548Sjdolecek
92698d58548Sjdolecek flags = 0;
92798d58548Sjdolecek if (mntflags & MNT_FORCE)
92898d58548Sjdolecek flags |= FORCECLOSE;
92998d58548Sjdolecek if ((error = vflush(mp, NULLVP, flags)) != 0)
93098d58548Sjdolecek return (error);
93198d58548Sjdolecek pmp = VFSTOMSDOSFS(mp);
93298d58548Sjdolecek if (pmp->pm_devvp->v_type != VBAD)
9333881f4f3Shannken spec_node_setmountedfs(pmp->pm_devvp, NULL);
93498d58548Sjdolecek #ifdef MSDOSFS_DEBUG
93598d58548Sjdolecek {
93698d58548Sjdolecek struct vnode *vp = pmp->pm_devvp;
93798d58548Sjdolecek
93898d58548Sjdolecek printf("msdosfs_umount(): just before calling VOP_CLOSE()\n");
9391498ad22Sad printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n",
94023bf8800Sad vp->v_vflag | vp->v_iflag | vp->v_uflag, vrefcnt(vp),
9417dad9f73Sad vp->v_writecount, vp->v_holdcnt);
942e062fbf9Sjmmv printf("mount %p, op %p\n",
943e062fbf9Sjmmv vp->v_mount, vp->v_op);
94498d58548Sjdolecek printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
94598d58548Sjdolecek vp->v_cleanblkhd.lh_first,
94698d58548Sjdolecek vp->v_dirtyblkhd.lh_first,
94798d58548Sjdolecek vp->v_numoutput, vp->v_type);
94898d58548Sjdolecek printf("union %p, tag %d, data[0] %08x, data[1] %08x\n",
94998d58548Sjdolecek vp->v_socket, vp->v_tag,
95098d58548Sjdolecek ((u_int *)vp->v_data)[0],
95198d58548Sjdolecek ((u_int *)vp->v_data)[1]);
95298d58548Sjdolecek }
95398d58548Sjdolecek #endif
95498d58548Sjdolecek vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
955c8dcca66Spooka (void) VOP_CLOSE(pmp->pm_devvp,
95661e8303eSpooka pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED);
95798d58548Sjdolecek vput(pmp->pm_devvp);
958fff1c84cShannken msdosfs_fh_destroy(pmp);
95998d58548Sjdolecek free(pmp->pm_inusemap, M_MSDOSFSFAT);
96098d58548Sjdolecek free(pmp, M_MSDOSFSMNT);
96198d58548Sjdolecek mp->mnt_data = NULL;
96298d58548Sjdolecek mp->mnt_flag &= ~MNT_LOCAL;
963c8dcca66Spooka return (0);
96498d58548Sjdolecek }
96598d58548Sjdolecek
96698d58548Sjdolecek int
msdosfs_root(struct mount * mp,int lktype,struct vnode ** vpp)967c2e9cb94Sad msdosfs_root(struct mount *mp, int lktype, struct vnode **vpp)
96898d58548Sjdolecek {
96998d58548Sjdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
97098d58548Sjdolecek int error;
97198d58548Sjdolecek
97298d58548Sjdolecek #ifdef MSDOSFS_DEBUG
97398d58548Sjdolecek printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
97498d58548Sjdolecek #endif
9758086f46eSthorpej if ((error = msdosfs_deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS,
9768086f46eSthorpej vpp)) != 0)
977798256c9Shannken return error;
978c2e9cb94Sad error = vn_lock(*vpp, lktype);
979798256c9Shannken if (error) {
980798256c9Shannken vrele(*vpp);
981798256c9Shannken *vpp = NULL;
982798256c9Shannken return error;
983798256c9Shannken }
984798256c9Shannken return 0;
98598d58548Sjdolecek }
98698d58548Sjdolecek
98798d58548Sjdolecek int
msdosfs_statvfs(struct mount * mp,struct statvfs * sbp)98861e8303eSpooka msdosfs_statvfs(struct mount *mp, struct statvfs *sbp)
98998d58548Sjdolecek {
99098d58548Sjdolecek struct msdosfsmount *pmp;
99198d58548Sjdolecek
99298d58548Sjdolecek pmp = VFSTOMSDOSFS(mp);
99398d58548Sjdolecek sbp->f_bsize = pmp->pm_bpcluster;
9946bd1d6d4Schristos sbp->f_frsize = sbp->f_bsize;
99598d58548Sjdolecek sbp->f_iosize = pmp->pm_bpcluster;
99698d58548Sjdolecek sbp->f_blocks = pmp->pm_nmbrofclusters;
99798d58548Sjdolecek sbp->f_bfree = pmp->pm_freeclustercount;
99898d58548Sjdolecek sbp->f_bavail = pmp->pm_freeclustercount;
9996bd1d6d4Schristos sbp->f_bresvd = 0;
100098d58548Sjdolecek sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
100198d58548Sjdolecek sbp->f_ffree = 0; /* what to put in here? */
10026bd1d6d4Schristos sbp->f_favail = 0; /* what to put in here? */
10036bd1d6d4Schristos sbp->f_fresvd = 0;
10046bd1d6d4Schristos copy_statvfs_info(sbp, mp);
100598d58548Sjdolecek return (0);
100698d58548Sjdolecek }
100798d58548Sjdolecek
100802cb0c6eSchristos struct msdosfs_sync_ctx {
100902cb0c6eSchristos int waitfor;
101002cb0c6eSchristos };
101102cb0c6eSchristos
101202cb0c6eSchristos static bool
msdosfs_sync_selector(void * cl,struct vnode * vp)101302cb0c6eSchristos msdosfs_sync_selector(void *cl, struct vnode *vp)
101402cb0c6eSchristos {
101502cb0c6eSchristos struct msdosfs_sync_ctx *c = cl;
101602cb0c6eSchristos struct denode *dep;
101702cb0c6eSchristos
101830509f80Sriastradh KASSERT(mutex_owned(vp->v_interlock));
101930509f80Sriastradh
102002cb0c6eSchristos dep = VTODE(vp);
102102cb0c6eSchristos if (c->waitfor == MNT_LAZY || vp->v_type == VNON ||
102202cb0c6eSchristos dep == NULL || (((dep->de_flag &
102302cb0c6eSchristos (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) &&
102402cb0c6eSchristos (LIST_EMPTY(&vp->v_dirtyblkhd) &&
1025bf797310Sad (vp->v_iflag & VI_ONWORKLST) == 0)))
102602cb0c6eSchristos return false;
102702cb0c6eSchristos return true;
102802cb0c6eSchristos }
102902cb0c6eSchristos
103098d58548Sjdolecek int
msdosfs_sync(struct mount * mp,int waitfor,kauth_cred_t cred)1031454af1c0Sdsl msdosfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
103298d58548Sjdolecek {
1033b1e067f2Shannken struct vnode *vp;
1034b1e067f2Shannken struct vnode_iterator *marker;
103598d58548Sjdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
103653b57e33Shannken int error, allerror = 0;
103702cb0c6eSchristos struct msdosfs_sync_ctx ctx;
103898d58548Sjdolecek
103998d58548Sjdolecek /*
104048958e52Sjakllsch * If we ever switch to not updating all of the FATs all the time,
104198d58548Sjdolecek * this would be the place to update them from the first one.
104298d58548Sjdolecek */
104398d58548Sjdolecek if (pmp->pm_fmod != 0) {
104498d58548Sjdolecek if (pmp->pm_flags & MSDOSFSMNT_RONLY)
104598d58548Sjdolecek panic("msdosfs_sync: rofs mod");
104698d58548Sjdolecek else {
104748958e52Sjakllsch /* update FATs here */
104898d58548Sjdolecek }
104998d58548Sjdolecek }
105098d58548Sjdolecek /*
105198d58548Sjdolecek * Write back each (modified) denode.
105298d58548Sjdolecek */
1053b1e067f2Shannken vfs_vnode_iterator_init(mp, &marker);
105402cb0c6eSchristos ctx.waitfor = waitfor;
105502cb0c6eSchristos while ((vp = vfs_vnode_iterator_next(marker, msdosfs_sync_selector,
105602cb0c6eSchristos &ctx)))
105702cb0c6eSchristos {
1058b1e067f2Shannken error = vn_lock(vp, LK_EXCLUSIVE);
1059b1e067f2Shannken if (error) {
1060b1e067f2Shannken vrele(vp);
10614a780c9aSad continue;
1062b1e067f2Shannken }
106398d58548Sjdolecek if ((error = VOP_FSYNC(vp, cred,
106461e8303eSpooka waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
106598d58548Sjdolecek allerror = error;
106698d58548Sjdolecek vput(vp);
106798d58548Sjdolecek }
1068b1e067f2Shannken vfs_vnode_iterator_destroy(marker);
10694a780c9aSad
107098d58548Sjdolecek /*
107198d58548Sjdolecek * Force stale file system control information to be flushed.
107298d58548Sjdolecek */
10734b595f6eShannken vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
107498d58548Sjdolecek if ((error = VOP_FSYNC(pmp->pm_devvp, cred,
107561e8303eSpooka waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0)
107698d58548Sjdolecek allerror = error;
10774b595f6eShannken VOP_UNLOCK(pmp->pm_devvp);
107898d58548Sjdolecek return (allerror);
107998d58548Sjdolecek }
108098d58548Sjdolecek
108198d58548Sjdolecek int
msdosfs_fhtovp(struct mount * mp,struct fid * fhp,int lktype,struct vnode ** vpp)1082c2e9cb94Sad msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp)
108398d58548Sjdolecek {
108498d58548Sjdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1085a3b5baedSmartin struct defid defh;
1086fff1c84cShannken uint32_t gen;
108798d58548Sjdolecek int error;
108898d58548Sjdolecek
10896b98a174Schristos if (fhp->fid_len != sizeof(struct defid)) {
1090549e2319Smaxv DPRINTF("fid_len %d %zd", fhp->fid_len, sizeof(struct defid));
1091a3b5baedSmartin return EINVAL;
10926b98a174Schristos }
1093a3b5baedSmartin memcpy(&defh, fhp, sizeof(defh));
1094fff1c84cShannken error = msdosfs_fh_lookup(pmp, defh.defid_dirclust, defh.defid_dirofs,
1095fff1c84cShannken &gen);
1096fff1c84cShannken if (error == 0 && gen != defh.defid_gen)
1097fff1c84cShannken error = ESTALE;
1098fff1c84cShannken if (error) {
1099fff1c84cShannken *vpp = NULLVP;
1100fff1c84cShannken return error;
1101fff1c84cShannken }
11028086f46eSthorpej error = msdosfs_deget(pmp, defh.defid_dirclust, defh.defid_dirofs, vpp);
110398d58548Sjdolecek if (error) {
1104549e2319Smaxv DPRINTF("deget %d", error);
110598d58548Sjdolecek *vpp = NULLVP;
1106798256c9Shannken return error;
110798d58548Sjdolecek }
1108c2e9cb94Sad error = vn_lock(*vpp, lktype);
1109798256c9Shannken if (error) {
1110798256c9Shannken vrele(*vpp);
1111798256c9Shannken *vpp = NULLVP;
1112798256c9Shannken return error;
1113798256c9Shannken }
1114798256c9Shannken return 0;
111598d58548Sjdolecek }
111698d58548Sjdolecek
111798d58548Sjdolecek int
msdosfs_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)1118454af1c0Sdsl msdosfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
111998d58548Sjdolecek {
1120fff1c84cShannken struct msdosfsmount *pmp = VFSTOMSDOSFS(vp->v_mount);
112198d58548Sjdolecek struct denode *dep;
1122a3b5baedSmartin struct defid defh;
1123fff1c84cShannken int error;
112498d58548Sjdolecek
1125a3b5baedSmartin if (*fh_size < sizeof(struct defid)) {
1126a3b5baedSmartin *fh_size = sizeof(struct defid);
1127a3b5baedSmartin return E2BIG;
1128a3b5baedSmartin }
1129a3b5baedSmartin *fh_size = sizeof(struct defid);
113098d58548Sjdolecek dep = VTODE(vp);
1131a3b5baedSmartin memset(&defh, 0, sizeof(defh));
1132a3b5baedSmartin defh.defid_len = sizeof(struct defid);
1133a3b5baedSmartin defh.defid_dirclust = dep->de_dirclust;
1134a3b5baedSmartin defh.defid_dirofs = dep->de_diroffset;
1135fff1c84cShannken error = msdosfs_fh_enter(pmp, dep->de_dirclust, dep->de_diroffset,
1136fff1c84cShannken &defh.defid_gen);
1137fff1c84cShannken if (error == 0)
1138a3b5baedSmartin memcpy(fhp, &defh, sizeof(defh));
1139fff1c84cShannken return error;
114098d58548Sjdolecek }
114198d58548Sjdolecek
114298d58548Sjdolecek int
msdosfs_vget(struct mount * mp,ino_t ino,int lktype,struct vnode ** vpp)1143c2e9cb94Sad msdosfs_vget(struct mount *mp, ino_t ino, int lktype,
1144168cd830Schristos struct vnode **vpp)
114598d58548Sjdolecek {
114698d58548Sjdolecek
114798d58548Sjdolecek return (EOPNOTSUPP);
114898d58548Sjdolecek }
1149