1952a6212SJordan K. Hubbard /* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */
227a0bc89SDoug Rabson
327a0bc89SDoug Rabson /*-
4d63027b6SPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
5d63027b6SPedro F. Giffuni *
6952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
7952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
827a0bc89SDoug Rabson * All rights reserved.
927a0bc89SDoug Rabson * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
1027a0bc89SDoug Rabson *
1127a0bc89SDoug Rabson * Redistribution and use in source and binary forms, with or without
1227a0bc89SDoug Rabson * modification, are permitted provided that the following conditions
1327a0bc89SDoug Rabson * are met:
1427a0bc89SDoug Rabson * 1. Redistributions of source code must retain the above copyright
1527a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer.
1627a0bc89SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright
1727a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer in the
1827a0bc89SDoug Rabson * documentation and/or other materials provided with the distribution.
1927a0bc89SDoug Rabson * 3. All advertising materials mentioning features or use of this software
2027a0bc89SDoug Rabson * must display the following acknowledgement:
2127a0bc89SDoug Rabson * This product includes software developed by TooLs GmbH.
2227a0bc89SDoug Rabson * 4. The name of TooLs GmbH may not be used to endorse or promote products
2327a0bc89SDoug Rabson * derived from this software without specific prior written permission.
2427a0bc89SDoug Rabson *
2527a0bc89SDoug Rabson * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
2627a0bc89SDoug Rabson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2727a0bc89SDoug Rabson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2827a0bc89SDoug Rabson * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2927a0bc89SDoug Rabson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3027a0bc89SDoug Rabson * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3127a0bc89SDoug Rabson * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3227a0bc89SDoug Rabson * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3327a0bc89SDoug Rabson * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3427a0bc89SDoug Rabson * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3527a0bc89SDoug Rabson */
36d167cf6fSWarner Losh /*-
3727a0bc89SDoug Rabson * Written by Paul Popelka (paulp@uts.amdahl.com)
3827a0bc89SDoug Rabson *
3927a0bc89SDoug Rabson * You can do anything you want with this software, just don't say you wrote
4027a0bc89SDoug Rabson * it, and don't remove this notice.
4127a0bc89SDoug Rabson *
4227a0bc89SDoug Rabson * This software is provided "as is".
4327a0bc89SDoug Rabson *
4427a0bc89SDoug Rabson * The author supplies this software to be publicly redistributed on the
4527a0bc89SDoug Rabson * understanding that the author is not responsible for the correct
4627a0bc89SDoug Rabson * functioning of this software in any circumstances and is not liable for
4727a0bc89SDoug Rabson * any damages caused by this software.
4827a0bc89SDoug Rabson *
4927a0bc89SDoug Rabson * October 1992
5027a0bc89SDoug Rabson */
5127a0bc89SDoug Rabson
5227a0bc89SDoug Rabson #include <sys/param.h>
5327a0bc89SDoug Rabson #include <sys/systm.h>
546becd1c8SBruce Evans #include <sys/buf.h>
55aaaa4fb5SKonstantin Belousov #include <sys/bufobj.h>
5665baf8f0SBruce Evans #include <sys/conf.h>
5757b4252eSKonstantin Belousov #include <sys/fcntl.h>
586becd1c8SBruce Evans #include <sys/iconv.h>
596becd1c8SBruce Evans #include <sys/kernel.h>
601103771dSBruce Evans #include <sys/lock.h>
616becd1c8SBruce Evans #include <sys/malloc.h>
626becd1c8SBruce Evans #include <sys/mount.h>
631103771dSBruce Evans #include <sys/mutex.h>
6427a0bc89SDoug Rabson #include <sys/namei.h>
65acd3428bSRobert Watson #include <sys/priv.h>
6627a0bc89SDoug Rabson #include <sys/proc.h>
67aaaa4fb5SKonstantin Belousov #include <sys/rwlock.h>
686becd1c8SBruce Evans #include <sys/stat.h>
69b2e4b635SKonstantin Belousov #include <sys/taskqueue.h>
7027a0bc89SDoug Rabson #include <sys/vnode.h>
7127a0bc89SDoug Rabson
729a135592SPoul-Henning Kamp #include <geom/geom.h>
739a135592SPoul-Henning Kamp #include <geom/geom_vfs.h>
749a135592SPoul-Henning Kamp
756becd1c8SBruce Evans #include <fs/msdosfs/bootsect.h>
766becd1c8SBruce Evans #include <fs/msdosfs/bpb.h>
776becd1c8SBruce Evans #include <fs/msdosfs/direntry.h>
786becd1c8SBruce Evans #include <fs/msdosfs/denode.h>
796becd1c8SBruce Evans #include <fs/msdosfs/fat.h>
806becd1c8SBruce Evans #include <fs/msdosfs/msdosfsmount.h>
816becd1c8SBruce Evans
82027bebe8SEd Maste #ifdef MSDOSFS_DEBUG
83027bebe8SEd Maste #include <sys/rwlock.h>
84027bebe8SEd Maste #endif
85027bebe8SEd Maste
8623b6c230SKonstantin Belousov static const char msdosfs_lock_msg[] = "fatlk";
8723b6c230SKonstantin Belousov
887c3fc9deSBruce Evans /* Mount options that we support. */
896a4b48f4SPoul-Henning Kamp static const char *msdosfs_opts[] = {
90cb65c1eeSBruce Evans "async", "noatime", "noclusterr", "noclusterw",
917c3fc9deSBruce Evans "export", "force", "from", "sync",
927c3fc9deSBruce Evans "cs_dos", "cs_local", "cs_win", "dirmask",
9340373cf5SKonstantin Belousov "gid", "kiconv", "longname",
947c3fc9deSBruce Evans "longnames", "mask", "shortname", "shortnames",
957c3fc9deSBruce Evans "uid", "win95", "nowin95",
966a4b48f4SPoul-Henning Kamp NULL
976a4b48f4SPoul-Henning Kamp };
986a4b48f4SPoul-Henning Kamp
9901f6cfbaSYoshihiro Takahashi #if 1 /*def PC98*/
10001f6cfbaSYoshihiro Takahashi /*
10101f6cfbaSYoshihiro Takahashi * XXX - The boot signature formatted by NEC PC-98 DOS looks like a
10201f6cfbaSYoshihiro Takahashi * garbage or a random value :-{
10301f6cfbaSYoshihiro Takahashi * If you want to use that broken-signatured media, define the
10401f6cfbaSYoshihiro Takahashi * following symbol even though PC/AT.
10501f6cfbaSYoshihiro Takahashi * (ex. mount PC-98 DOS formatted FD on PC/AT)
10601f6cfbaSYoshihiro Takahashi */
10701f6cfbaSYoshihiro Takahashi #define MSDOSFS_NOCHECKSIG
10801f6cfbaSYoshihiro Takahashi #endif
10901f6cfbaSYoshihiro Takahashi
1105bb84bc8SRobert Watson MALLOC_DEFINE(M_MSDOSFSMNT, "msdosfs_mount", "MSDOSFS mount structure");
1115bb84bc8SRobert Watson static MALLOC_DEFINE(M_MSDOSFSFAT, "msdosfs_fat", "MSDOSFS file allocation table");
11255166637SPoul-Henning Kamp
1132d7c6b27SBruce Evans struct iconv_functions *msdosfs_iconv;
114c4f02a89SMax Khon
1156a4b48f4SPoul-Henning Kamp static int update_mp(struct mount *mp, struct thread *td);
1160d7935fdSAttilio Rao static int mountmsdosfs(struct vnode *devvp, struct mount *mp);
117b2e4b635SKonstantin Belousov static void msdosfs_remount_ro(void *arg, int pending);
1181be5bc74STom Rhodes static vfs_fhtovp_t msdosfs_fhtovp;
1196a4b48f4SPoul-Henning Kamp static vfs_mount_t msdosfs_mount;
1209bf1a756SPoul-Henning Kamp static vfs_root_t msdosfs_root;
1219bf1a756SPoul-Henning Kamp static vfs_statfs_t msdosfs_statfs;
1229bf1a756SPoul-Henning Kamp static vfs_sync_t msdosfs_sync;
1239bf1a756SPoul-Henning Kamp static vfs_unmount_t msdosfs_unmount;
124af482601SBruce Evans
1251a9415afSTim J. Robbins /* Maximum length of a character set name (arbitrary). */
1261a9415afSTim J. Robbins #define MAXCSLEN 64
1271a9415afSTim J. Robbins
128952a6212SJordan K. Hubbard static int
update_mp(struct mount * mp,struct thread * td)129dc9a617aSCraig Rodrigues update_mp(struct mount *mp, struct thread *td)
130952a6212SJordan K. Hubbard {
131952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1326a4b48f4SPoul-Henning Kamp void *dos, *win, *local;
1336a4b48f4SPoul-Henning Kamp int error, v;
134952a6212SJordan K. Hubbard
1356a4b48f4SPoul-Henning Kamp if (!vfs_getopt(mp->mnt_optnew, "kiconv", NULL, NULL)) {
1366a4b48f4SPoul-Henning Kamp if (msdosfs_iconv != NULL) {
1376a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew,
1386a4b48f4SPoul-Henning Kamp "cs_win", &win, NULL);
1396a4b48f4SPoul-Henning Kamp if (!error)
1406a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew,
1416a4b48f4SPoul-Henning Kamp "cs_local", &local, NULL);
1426a4b48f4SPoul-Henning Kamp if (!error)
1436a4b48f4SPoul-Henning Kamp error = vfs_getopt(mp->mnt_optnew,
1446a4b48f4SPoul-Henning Kamp "cs_dos", &dos, NULL);
1456a4b48f4SPoul-Henning Kamp if (!error) {
1461a9415afSTim J. Robbins msdosfs_iconv->open(win, local, &pmp->pm_u2w);
1471a9415afSTim J. Robbins msdosfs_iconv->open(local, win, &pmp->pm_w2u);
1481a9415afSTim J. Robbins msdosfs_iconv->open(dos, local, &pmp->pm_u2d);
1491a9415afSTim J. Robbins msdosfs_iconv->open(local, dos, &pmp->pm_d2u);
1506a4b48f4SPoul-Henning Kamp }
1511a9415afSTim J. Robbins if (error != 0)
1521a9415afSTim J. Robbins return (error);
153c4f02a89SMax Khon } else {
154c4f02a89SMax Khon pmp->pm_w2u = NULL;
155c4f02a89SMax Khon pmp->pm_u2w = NULL;
156c4f02a89SMax Khon pmp->pm_d2u = NULL;
157c4f02a89SMax Khon pmp->pm_u2d = NULL;
1587391f611SAndrey A. Chernov }
1591a9415afSTim J. Robbins }
1601a9415afSTim J. Robbins
161dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v) == 1)
1626a4b48f4SPoul-Henning Kamp pmp->pm_gid = v;
163dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v) == 1)
1646a4b48f4SPoul-Henning Kamp pmp->pm_uid = v;
165dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "mask", "%d", &v) == 1)
1666a4b48f4SPoul-Henning Kamp pmp->pm_mask = v & ALLPERMS;
167dd104b33SKevin Lo if (vfs_scanopt(mp->mnt_optnew, "dirmask", "%d", &v) == 1)
1686a4b48f4SPoul-Henning Kamp pmp->pm_dirmask = v & ALLPERMS;
1696a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "shortname",
1706a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
1714ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "shortnames",
1724ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_SHORTNAME);
1736a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "longname",
1746a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
1754ab12573SCraig Rodrigues vfs_flagopt(mp->mnt_optnew, "longnames",
1764ab12573SCraig Rodrigues &pmp->pm_flags, MSDOSFSMNT_LONGNAME);
1776a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "kiconv",
1786a4b48f4SPoul-Henning Kamp &pmp->pm_flags, MSDOSFSMNT_KICONV);
1796a4b48f4SPoul-Henning Kamp
180d75b2048SCraig Rodrigues if (vfs_getopt(mp->mnt_optnew, "nowin95", NULL, NULL) == 0)
1816a4b48f4SPoul-Henning Kamp pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
182d75b2048SCraig Rodrigues else
183d75b2048SCraig Rodrigues pmp->pm_flags &= ~MSDOSFSMNT_NOWIN95;
184952a6212SJordan K. Hubbard
185952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
186952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
1870696afbeSAlan Somers else
188952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_LONGNAME;
189952a6212SJordan K. Hubbard return 0;
190952a6212SJordan K. Hubbard }
191952a6212SJordan K. Hubbard
1926a4b48f4SPoul-Henning Kamp static int
msdosfs_cmount(struct mntarg * ma,void * data,uint64_t flags)193cc672d35SKirk McKusick msdosfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
1946a4b48f4SPoul-Henning Kamp {
1956a4b48f4SPoul-Henning Kamp struct msdosfs_args args;
1966a4b48f4SPoul-Henning Kamp int error;
1976a4b48f4SPoul-Henning Kamp
1986a4b48f4SPoul-Henning Kamp if (data == NULL)
1996a4b48f4SPoul-Henning Kamp return (EINVAL);
2006a4b48f4SPoul-Henning Kamp error = copyin(data, &args, sizeof args);
2016a4b48f4SPoul-Henning Kamp if (error)
2026a4b48f4SPoul-Henning Kamp return (error);
2036a4b48f4SPoul-Henning Kamp
2046a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "from", args.fspec, MAXPATHLEN);
2051f7104d7SRick Macklem ma = mount_arg(ma, "export", &args.export, sizeof(args.export));
2066a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "uid", "%d", args.uid);
2076a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "gid", "%d", args.gid);
2086a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "mask", "%d", args.mask);
2096a4b48f4SPoul-Henning Kamp ma = mount_argf(ma, "dirmask", "%d", args.dirmask);
2106a4b48f4SPoul-Henning Kamp
2116a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_SHORTNAME, "noshortname");
2126a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_LONGNAME, "nolongname");
2136a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, !(args.flags & MSDOSFSMNT_NOWIN95), "nowin95");
2146a4b48f4SPoul-Henning Kamp ma = mount_argb(ma, args.flags & MSDOSFSMNT_KICONV, "nokiconv");
2156a4b48f4SPoul-Henning Kamp
2166a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_win", args.cs_win, MAXCSLEN);
2176a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_dos", args.cs_dos, MAXCSLEN);
2186a4b48f4SPoul-Henning Kamp ma = mount_argsu(ma, "cs_local", args.cs_local, MAXCSLEN);
2196a4b48f4SPoul-Henning Kamp
2206a4b48f4SPoul-Henning Kamp error = kernel_mount(ma, flags);
2216a4b48f4SPoul-Henning Kamp
2226a4b48f4SPoul-Henning Kamp return (error);
2236a4b48f4SPoul-Henning Kamp }
2246a4b48f4SPoul-Henning Kamp
22527a0bc89SDoug Rabson /*
22627a0bc89SDoug Rabson * mp - path - addr in user space of mount point (ie /usr or whatever)
22727a0bc89SDoug Rabson * data - addr in user space of mount params including the name of the block
22827a0bc89SDoug Rabson * special file to treat as a filesystem.
22927a0bc89SDoug Rabson */
2307fefffeeSPoul-Henning Kamp static int
msdosfs_mount(struct mount * mp)231dfd233edSAttilio Rao msdosfs_mount(struct mount *mp)
23227a0bc89SDoug Rabson {
233aaaa4fb5SKonstantin Belousov struct vnode *devvp, *odevvp; /* vnode for blk device to mount */
234dfd233edSAttilio Rao struct thread *td;
235952a6212SJordan K. Hubbard /* msdosfs specific mount control block */
236952a6212SJordan K. Hubbard struct msdosfsmount *pmp = NULL;
2375e8c582aSPoul-Henning Kamp struct nameidata ndp;
238269c902fSPoul-Henning Kamp int error, flags;
23915bc6b2bSEdward Tomasz Napierala accmode_t accmode;
2406a4b48f4SPoul-Henning Kamp char *from;
24127a0bc89SDoug Rabson
242dfd233edSAttilio Rao td = curthread;
2436a4b48f4SPoul-Henning Kamp if (vfs_filteropt(mp->mnt_optnew, msdosfs_opts))
2446a4b48f4SPoul-Henning Kamp return (EINVAL);
2456a4b48f4SPoul-Henning Kamp
24627a0bc89SDoug Rabson /*
247952a6212SJordan K. Hubbard * If updating, check whether changing from read-only to
248952a6212SJordan K. Hubbard * read/write; if there is no device name, that's all we do.
24927a0bc89SDoug Rabson */
25027a0bc89SDoug Rabson if (mp->mnt_flag & MNT_UPDATE) {
2512d7c6b27SBruce Evans pmp = VFSTOMSDOSFS(mp);
2529a135592SPoul-Henning Kamp if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) &&
2536a4b48f4SPoul-Henning Kamp vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
25469367793SKonstantin Belousov if ((error = vn_start_write(NULL, &mp, V_WAIT)) != 0)
255e83f1423SPoul-Henning Kamp return (error);
25669367793SKonstantin Belousov error = vfs_write_suspend_umnt(mp);
25769367793SKonstantin Belousov if (error != 0)
25869367793SKonstantin Belousov return (error);
25969367793SKonstantin Belousov
26027a0bc89SDoug Rabson flags = WRITECLOSE;
26127a0bc89SDoug Rabson if (mp->mnt_flag & MNT_FORCE)
26227a0bc89SDoug Rabson flags |= FORCECLOSE;
263f257b7a5SAlfred Perlstein error = vflush(mp, 0, flags, td);
26469367793SKonstantin Belousov if (error != 0) {
26569367793SKonstantin Belousov vfs_write_resume(mp, 0);
2666a4b48f4SPoul-Henning Kamp return (error);
26769367793SKonstantin Belousov }
2683247c9ddSXin LI
2693247c9ddSXin LI /*
2703247c9ddSXin LI * Now the volume is clean. Mark it so while the
2713247c9ddSXin LI * device is still rw.
2723247c9ddSXin LI */
2733247c9ddSXin LI error = markvoldirty(pmp, 0);
27469367793SKonstantin Belousov if (error != 0) {
27569367793SKonstantin Belousov vfs_write_resume(mp, 0);
2763247c9ddSXin LI (void)markvoldirty(pmp, 1);
2773247c9ddSXin LI return (error);
2783247c9ddSXin LI }
2793247c9ddSXin LI
2803247c9ddSXin LI /* Downgrade the device from rw to ro. */
2819a135592SPoul-Henning Kamp g_topology_lock();
2824eb3abf0SBruce Evans error = g_access(pmp->pm_cp, 0, -1, 0);
2839a135592SPoul-Henning Kamp g_topology_unlock();
2843247c9ddSXin LI if (error) {
28569367793SKonstantin Belousov vfs_write_resume(mp, 0);
2863247c9ddSXin LI (void)markvoldirty(pmp, 1);
2874eb3abf0SBruce Evans return (error);
2883247c9ddSXin LI }
2894eb3abf0SBruce Evans
2903247c9ddSXin LI /*
2913247c9ddSXin LI * Backing out after an error was painful in the
2923247c9ddSXin LI * above. Now we are committed to succeeding.
2933247c9ddSXin LI */
2943247c9ddSXin LI pmp->pm_fmod = 0;
2953247c9ddSXin LI pmp->pm_flags |= MSDOSFSMNT_RONLY;
2963247c9ddSXin LI MNT_ILOCK(mp);
2973247c9ddSXin LI mp->mnt_flag |= MNT_RDONLY;
2983247c9ddSXin LI MNT_IUNLOCK(mp);
29969367793SKonstantin Belousov vfs_write_resume(mp, 0);
3006a4b48f4SPoul-Henning Kamp } else if ((pmp->pm_flags & MSDOSFSMNT_RONLY) &&
3016a4b48f4SPoul-Henning Kamp !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
302952a6212SJordan K. Hubbard /*
303952a6212SJordan K. Hubbard * If upgrade to read-write by non-root, then verify
304952a6212SJordan K. Hubbard * that user has necessary permissions on the device.
305952a6212SJordan K. Hubbard */
306aaaa4fb5SKonstantin Belousov odevvp = pmp->pm_odevvp;
307aaaa4fb5SKonstantin Belousov vn_lock(odevvp, LK_EXCLUSIVE | LK_RETRY);
308aaaa4fb5SKonstantin Belousov error = VOP_ACCESS(odevvp, VREAD | VWRITE,
309a854ed98SJohn Baldwin td->td_ucred, td);
310acd3428bSRobert Watson if (error)
311acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM);
312952a6212SJordan K. Hubbard if (error) {
313aaaa4fb5SKonstantin Belousov VOP_UNLOCK(odevvp);
314952a6212SJordan K. Hubbard return (error);
315952a6212SJordan K. Hubbard }
316aaaa4fb5SKonstantin Belousov VOP_UNLOCK(odevvp);
3179a135592SPoul-Henning Kamp g_topology_lock();
3189a135592SPoul-Henning Kamp error = g_access(pmp->pm_cp, 0, 1, 0);
3199a135592SPoul-Henning Kamp g_topology_unlock();
3209a135592SPoul-Henning Kamp if (error)
3219a135592SPoul-Henning Kamp return (error);
322cede1f56STom Rhodes
323aaa38524SConrad Meyer /* Now that the volume is modifiable, mark it dirty. */
324aaa38524SConrad Meyer error = markvoldirty_upgrade(pmp, true, true);
325aaa38524SConrad Meyer if (error) {
326aaa38524SConrad Meyer /*
327aaa38524SConrad Meyer * If dirtying the superblock failed, drop GEOM
328aaa38524SConrad Meyer * 'w' refs (we're still RO).
329aaa38524SConrad Meyer */
330aaa38524SConrad Meyer g_topology_lock();
331aaa38524SConrad Meyer (void)g_access(pmp->pm_cp, 0, -1, 0);
332aaa38524SConrad Meyer g_topology_unlock();
333aaa38524SConrad Meyer
334aaa38524SConrad Meyer return (error);
335aaa38524SConrad Meyer }
336aaa38524SConrad Meyer
3373247c9ddSXin LI pmp->pm_fmod = 1;
3383247c9ddSXin LI pmp->pm_flags &= ~MSDOSFSMNT_RONLY;
3393247c9ddSXin LI MNT_ILOCK(mp);
3403247c9ddSXin LI mp->mnt_flag &= ~MNT_RDONLY;
3413247c9ddSXin LI MNT_IUNLOCK(mp);
34282c59ec6SCraig Rodrigues }
343b2e4b635SKonstantin Belousov
344b2e4b635SKonstantin Belousov /*
345b2e4b635SKonstantin Belousov * Avoid namei() below. The "from" option is not set.
346b2e4b635SKonstantin Belousov * Update of the devvp is pointless for this case.
347b2e4b635SKonstantin Belousov */
348b2e4b635SKonstantin Belousov if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
349b2e4b635SKonstantin Belousov return (0);
350952a6212SJordan K. Hubbard }
35127a0bc89SDoug Rabson /*
352952a6212SJordan K. Hubbard * Not an update, or updating the name: look up the name
353e9827c6dSBruce Evans * and verify that it refers to a sensible disk device.
35427a0bc89SDoug Rabson */
3556a4b48f4SPoul-Henning Kamp if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL))
3566a4b48f4SPoul-Henning Kamp return (EINVAL);
3577e1d3eefSMateusz Guzik NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
3585e8c582aSPoul-Henning Kamp error = namei(&ndp);
359952a6212SJordan K. Hubbard if (error)
360952a6212SJordan K. Hubbard return (error);
3615e8c582aSPoul-Henning Kamp devvp = ndp.ni_vp;
362bb92cd7bSMateusz Guzik NDFREE_PNBUF(&ndp);
363952a6212SJordan K. Hubbard
3647ad2a82dSMateusz Guzik if (!vn_isdisk_error(devvp, &error)) {
36575d7ba93SSuleiman Souhlal vput(devvp);
366ba4ad1fcSPoul-Henning Kamp return (error);
36727a0bc89SDoug Rabson }
36827a0bc89SDoug Rabson /*
369952a6212SJordan K. Hubbard * If mount by non-root, then verify that user has necessary
370952a6212SJordan K. Hubbard * permissions on the device.
37127a0bc89SDoug Rabson */
37215bc6b2bSEdward Tomasz Napierala accmode = VREAD;
373952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_RDONLY) == 0)
37415bc6b2bSEdward Tomasz Napierala accmode |= VWRITE;
37515bc6b2bSEdward Tomasz Napierala error = VOP_ACCESS(devvp, accmode, td->td_ucred, td);
376acd3428bSRobert Watson if (error)
377acd3428bSRobert Watson error = priv_check(td, PRIV_VFS_MOUNT_PERM);
378952a6212SJordan K. Hubbard if (error) {
379952a6212SJordan K. Hubbard vput(devvp);
380952a6212SJordan K. Hubbard return (error);
381952a6212SJordan K. Hubbard }
382952a6212SJordan K. Hubbard if ((mp->mnt_flag & MNT_UPDATE) == 0) {
3830d7935fdSAttilio Rao error = mountmsdosfs(devvp, mp);
384952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG /* only needed for the printf below */
385952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp);
386952a6212SJordan K. Hubbard #endif
387952a6212SJordan K. Hubbard } else {
38875d7ba93SSuleiman Souhlal vput(devvp);
389aaaa4fb5SKonstantin Belousov if (devvp != pmp->pm_odevvp)
3906c0358ccSKonstantin Belousov return (EINVAL); /* XXX needs translation */
39127a0bc89SDoug Rabson }
39227a0bc89SDoug Rabson if (error) {
39327a0bc89SDoug Rabson vrele(devvp);
394952a6212SJordan K. Hubbard return (error);
395952a6212SJordan K. Hubbard }
396952a6212SJordan K. Hubbard
3976a4b48f4SPoul-Henning Kamp error = update_mp(mp, td);
398952a6212SJordan K. Hubbard if (error) {
3991a9415afSTim J. Robbins if ((mp->mnt_flag & MNT_UPDATE) == 0)
400dfd233edSAttilio Rao msdosfs_unmount(mp, MNT_FORCE);
40127a0bc89SDoug Rabson return error;
40227a0bc89SDoug Rabson }
4036a4b48f4SPoul-Henning Kamp
4046a4b48f4SPoul-Henning Kamp vfs_mountedfrom(mp, from);
40527a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
4066a4b48f4SPoul-Henning Kamp printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
40727a0bc89SDoug Rabson #endif
408952a6212SJordan K. Hubbard return (0);
40927a0bc89SDoug Rabson }
41027a0bc89SDoug Rabson
411c33db74bSStefan Eßer /*
412c33db74bSStefan Eßer * The FAT12 and FAT16 file systems use a limited size root directory that
413c33db74bSStefan Eßer * can be created with 1 to 65535 entries for files, directories, or a disk
414c33db74bSStefan Eßer * label (but DOS or Windows creates at most 512 root directory entries).
415c33db74bSStefan Eßer * This function calculates the number of free root directory entries by
416c33db74bSStefan Eßer * counting the non-deleted entries (not starting with 0xE5) and by adding
417c33db74bSStefan Eßer * the amount of never used entries (with the position indicated by an
418c33db74bSStefan Eßer * entry that starts with 0x00).
419c33db74bSStefan Eßer */
420c33db74bSStefan Eßer static int
rootdir_free(struct msdosfsmount * pmp)421c33db74bSStefan Eßer rootdir_free(struct msdosfsmount* pmp)
422c33db74bSStefan Eßer {
423c33db74bSStefan Eßer struct buf *bp;
424c33db74bSStefan Eßer struct direntry *dep;
425c33db74bSStefan Eßer u_long readsize;
426c33db74bSStefan Eßer int dirclu;
427c33db74bSStefan Eßer int diridx;
428c33db74bSStefan Eßer int dirmax;
429c33db74bSStefan Eßer int dirleft;
430c33db74bSStefan Eßer int ffree;
431c33db74bSStefan Eßer
432c33db74bSStefan Eßer dirclu = pmp->pm_rootdirblk;
433c33db74bSStefan Eßer
434c33db74bSStefan Eßer /*
435c33db74bSStefan Eßer * The msdosfs code ignores pm_RootDirEnts and uses pm_rootdirsize
436c33db74bSStefan Eßer * (measured in DEV_BSIZE) to prevent excess root dir allocations.
437c33db74bSStefan Eßer */
438c33db74bSStefan Eßer dirleft = howmany(pmp->pm_rootdirsize * DEV_BSIZE,
439c33db74bSStefan Eßer sizeof(struct direntry));
440c33db74bSStefan Eßer
441c33db74bSStefan Eßer /* Read in chunks of default maximum root directory size */
442c33db74bSStefan Eßer readsize = 512 * sizeof(struct direntry);
443c33db74bSStefan Eßer
444c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
445c33db74bSStefan Eßer printf("rootdir_free: blkpersec=%lu fatblksize=%lu dirsize=%lu "
446c33db74bSStefan Eßer "firstclu=%lu dirclu=%d entries=%d rootdirsize=%lu "
447c33db74bSStefan Eßer "bytespersector=%hu bytepercluster=%lu\n",
448c33db74bSStefan Eßer pmp->pm_BlkPerSec, pmp->pm_fatblocksize, readsize,
449c33db74bSStefan Eßer pmp->pm_firstcluster, dirclu, dirleft, pmp->pm_rootdirsize,
450c33db74bSStefan Eßer pmp->pm_BytesPerSec, pmp->pm_bpcluster);
451c33db74bSStefan Eßer #endif
452c33db74bSStefan Eßer ffree = dirleft;
453c33db74bSStefan Eßer while (dirleft > 0 && ffree > 0) {
454c33db74bSStefan Eßer if (readsize > dirleft * sizeof(struct direntry))
455c33db74bSStefan Eßer readsize = dirleft * sizeof(struct direntry);
456c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
457c33db74bSStefan Eßer printf("rootdir_free: dirclu=%d dirleft=%d readsize=%lu\n",
458c33db74bSStefan Eßer dirclu, dirleft, readsize);
459c33db74bSStefan Eßer #endif
460c33db74bSStefan Eßer if (bread(pmp->pm_devvp, dirclu, readsize, NOCRED, &bp) != 0) {
461c33db74bSStefan Eßer printf("rootdir_free: read error\n");
462c33db74bSStefan Eßer if (bp != NULL)
463c33db74bSStefan Eßer brelse(bp);
464c33db74bSStefan Eßer return (-1);
465c33db74bSStefan Eßer }
466c33db74bSStefan Eßer dirmax = readsize / sizeof(struct direntry);
467c33db74bSStefan Eßer for (diridx = 0; diridx < dirmax && dirleft > 0;
468c33db74bSStefan Eßer diridx++, dirleft--) {
469c33db74bSStefan Eßer dep = (struct direntry*)bp->b_data + diridx;
470c33db74bSStefan Eßer #ifdef MSDOSFS_DEBUG
471c33db74bSStefan Eßer if (dep->deName[0] == SLOT_DELETED)
472c33db74bSStefan Eßer printf("rootdir_free: idx=%d <deleted>\n",
473c33db74bSStefan Eßer diridx);
474c33db74bSStefan Eßer else if (dep->deName[0] == SLOT_EMPTY)
475c33db74bSStefan Eßer printf("rootdir_free: idx=%d <end marker>\n",
476c33db74bSStefan Eßer diridx);
477c33db74bSStefan Eßer else if (dep->deAttributes == ATTR_WIN95)
478c33db74bSStefan Eßer printf("rootdir_free: idx=%d <LFN part %d>\n",
479c33db74bSStefan Eßer diridx, (dep->deName[0] & 0x1f) + 1);
480c33db74bSStefan Eßer else if (dep->deAttributes & ATTR_VOLUME)
481c33db74bSStefan Eßer printf("rootdir_free: idx=%d label='%11.11s'\n",
482c33db74bSStefan Eßer diridx, dep->deName);
483c33db74bSStefan Eßer else if (dep->deAttributes & ATTR_DIRECTORY)
484c33db74bSStefan Eßer printf("rootdir_free: idx=%d dir='%11.11s'\n",
485c33db74bSStefan Eßer diridx, dep->deName);
486c33db74bSStefan Eßer else
487c33db74bSStefan Eßer printf("rootdir_free: idx=%d file='%11.11s'\n",
488c33db74bSStefan Eßer diridx, dep->deName);
489c33db74bSStefan Eßer #endif
490c33db74bSStefan Eßer if (dep->deName[0] == SLOT_EMPTY)
491c33db74bSStefan Eßer dirleft = 0;
492c33db74bSStefan Eßer else if (dep->deName[0] != SLOT_DELETED)
493c33db74bSStefan Eßer ffree--;
494c33db74bSStefan Eßer }
495c33db74bSStefan Eßer brelse(bp);
496c33db74bSStefan Eßer bp = NULL;
497c33db74bSStefan Eßer dirclu += readsize / DEV_BSIZE;
498c33db74bSStefan Eßer }
499c33db74bSStefan Eßer return (ffree);
500c33db74bSStefan Eßer }
501c33db74bSStefan Eßer
5027fefffeeSPoul-Henning Kamp static int
mountmsdosfs(struct vnode * odevvp,struct mount * mp)503aaaa4fb5SKonstantin Belousov mountmsdosfs(struct vnode *odevvp, struct mount *mp)
50427a0bc89SDoug Rabson {
505952a6212SJordan K. Hubbard struct msdosfsmount *pmp;
506952a6212SJordan K. Hubbard struct buf *bp;
507c72ae142SJohn Baldwin struct cdev *dev;
508aaaa4fb5SKonstantin Belousov struct vnode *devvp;
50927a0bc89SDoug Rabson union bootsector *bsp;
51027a0bc89SDoug Rabson struct byte_bpb33 *b33;
51127a0bc89SDoug Rabson struct byte_bpb50 *b50;
512952a6212SJordan K. Hubbard struct byte_bpb710 *b710;
51323c53312SEd Maste uint8_t SecPerClust;
514499d3ffaSBoris Popov u_long clusters;
515952a6212SJordan K. Hubbard int ronly, error;
5169a135592SPoul-Henning Kamp struct g_consumer *cp;
5179a135592SPoul-Henning Kamp struct bufobj *bo;
51827a0bc89SDoug Rabson
519c72ae142SJohn Baldwin bp = NULL; /* This and pmp both used in error_exit. */
520c72ae142SJohn Baldwin pmp = NULL;
5214eb3abf0SBruce Evans ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
522c72ae142SJohn Baldwin
523aaaa4fb5SKonstantin Belousov devvp = mntfs_allocvp(mp, odevvp);
524c72ae142SJohn Baldwin dev = devvp->v_rdev;
525bb8297e6SKonstantin Belousov if (atomic_cmpset_acq_ptr((uintptr_t *)&dev->si_mountpt, 0,
526bb8297e6SKonstantin Belousov (uintptr_t)mp) == 0) {
527aaaa4fb5SKonstantin Belousov mntfs_freevp(devvp);
528bb8297e6SKonstantin Belousov return (EBUSY);
529bb8297e6SKonstantin Belousov }
5309a135592SPoul-Henning Kamp g_topology_lock();
5314eb3abf0SBruce Evans error = g_vfs_open(devvp, &cp, "msdosfs", ronly ? 0 : 1);
5329a135592SPoul-Henning Kamp g_topology_unlock();
533bb8297e6SKonstantin Belousov if (error != 0) {
534bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
535aaaa4fb5SKonstantin Belousov mntfs_freevp(devvp);
536bb8297e6SKonstantin Belousov return (error);
537bb8297e6SKonstantin Belousov }
538bb8297e6SKonstantin Belousov dev_ref(dev);
5399a135592SPoul-Henning Kamp bo = &devvp->v_bufobj;
540aaaa4fb5SKonstantin Belousov BO_LOCK(&odevvp->v_bufobj);
541aaaa4fb5SKonstantin Belousov odevvp->v_bufobj.bo_flag |= BO_NOBUFS;
542aaaa4fb5SKonstantin Belousov BO_UNLOCK(&odevvp->v_bufobj);
543b249ce48SMateusz Guzik VOP_UNLOCK(devvp);
5448ec22c4dSBruce Evans if (dev->si_iosize_max != 0)
5458ec22c4dSBruce Evans mp->mnt_iosize_max = dev->si_iosize_max;
546cd853791SKonstantin Belousov if (mp->mnt_iosize_max > maxphys)
547cd853791SKonstantin Belousov mp->mnt_iosize_max = maxphys;
548952a6212SJordan K. Hubbard
54927a0bc89SDoug Rabson /*
550952a6212SJordan K. Hubbard * Read the boot sector of the filesystem, and then check the
551952a6212SJordan K. Hubbard * boot signature. If not a dos boot sector then error out.
55201f6cfbaSYoshihiro Takahashi *
55393fe42b6SBruce Evans * NOTE: 8192 is a magic size that works for ffs.
55427a0bc89SDoug Rabson */
55593fe42b6SBruce Evans error = bread(devvp, 0, 8192, NOCRED, &bp);
556c3c6d51eSPoul-Henning Kamp if (error)
55727a0bc89SDoug Rabson goto error_exit;
558952a6212SJordan K. Hubbard bp->b_flags |= B_AGE;
559952a6212SJordan K. Hubbard bsp = (union bootsector *)bp->b_data;
56027a0bc89SDoug Rabson b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
56127a0bc89SDoug Rabson b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
56201a4d019SJohn Baldwin b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
563952a6212SJordan K. Hubbard
56401f6cfbaSYoshihiro Takahashi #ifndef MSDOSFS_NOCHECKSIG
56504fd468dSKonstantin Belousov if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 ||
56604fd468dSKonstantin Belousov bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
56727a0bc89SDoug Rabson error = EINVAL;
56827a0bc89SDoug Rabson goto error_exit;
56927a0bc89SDoug Rabson }
57001f6cfbaSYoshihiro Takahashi #endif
57127a0bc89SDoug Rabson
57204fd468dSKonstantin Belousov pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK | M_ZERO);
57327a0bc89SDoug Rabson pmp->pm_mountp = mp;
5749a135592SPoul-Henning Kamp pmp->pm_cp = cp;
5759a135592SPoul-Henning Kamp pmp->pm_bo = bo;
57627a0bc89SDoug Rabson
57723b6c230SKonstantin Belousov lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0);
5786ae13c0fSKonstantin Belousov lockinit(&pmp->pm_checkpath_lock, 0, "msdoscp", 0, 0);
57923b6c230SKonstantin Belousov
580b2e4b635SKonstantin Belousov TASK_INIT(&pmp->pm_rw2ro_task, 0, msdosfs_remount_ro, pmp);
581b2e4b635SKonstantin Belousov
58227a0bc89SDoug Rabson /*
5834eb3abf0SBruce Evans * Initialize ownerships and permissions, since nothing else will
58423c1e989SMaxim Konovalov * initialize them iff we are mounting root.
5854eb3abf0SBruce Evans */
5864eb3abf0SBruce Evans pmp->pm_uid = UID_ROOT;
5874eb3abf0SBruce Evans pmp->pm_gid = GID_WHEEL;
5884eb3abf0SBruce Evans pmp->pm_mask = pmp->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH |
5894eb3abf0SBruce Evans S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
5904eb3abf0SBruce Evans
5914eb3abf0SBruce Evans /*
59227a0bc89SDoug Rabson * Compute several useful quantities from the bpb in the
59327a0bc89SDoug Rabson * bootsector. Copy in the dos 5 variant of the bpb then fix up
59427a0bc89SDoug Rabson * the fields that are different between dos 5 and dos 3.3.
59527a0bc89SDoug Rabson */
596952a6212SJordan K. Hubbard SecPerClust = b50->bpbSecPerClust;
59727a0bc89SDoug Rabson pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
598696f22f0SJohn Baldwin if (pmp->pm_BytesPerSec < DEV_BSIZE) {
599696f22f0SJohn Baldwin error = EINVAL;
600696f22f0SJohn Baldwin goto error_exit;
601696f22f0SJohn Baldwin }
60227a0bc89SDoug Rabson pmp->pm_ResSectors = getushort(b50->bpbResSectors);
60327a0bc89SDoug Rabson pmp->pm_FATs = b50->bpbFATs;
60427a0bc89SDoug Rabson pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
60527a0bc89SDoug Rabson pmp->pm_Sectors = getushort(b50->bpbSectors);
60627a0bc89SDoug Rabson pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
60727a0bc89SDoug Rabson pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
60827a0bc89SDoug Rabson pmp->pm_Heads = getushort(b50->bpbHeads);
609952a6212SJordan K. Hubbard pmp->pm_Media = b50->bpbMedia;
61027a0bc89SDoug Rabson
61101f6cfbaSYoshihiro Takahashi /* calculate the ratio of sector size to DEV_BSIZE */
61201f6cfbaSYoshihiro Takahashi pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE;
61301f6cfbaSYoshihiro Takahashi
614043ec583SMarcel Moolenaar /*
615043ec583SMarcel Moolenaar * We don't check pm_Heads nor pm_SecPerTrack, because
616043ec583SMarcel Moolenaar * these may not be set for EFI file systems. We don't
617043ec583SMarcel Moolenaar * use these anyway, so we're unaffected if they are
618043ec583SMarcel Moolenaar * invalid.
619043ec583SMarcel Moolenaar */
62004fd468dSKonstantin Belousov if (pmp->pm_BytesPerSec == 0 || SecPerClust == 0) {
62127a0bc89SDoug Rabson error = EINVAL;
62227a0bc89SDoug Rabson goto error_exit;
62327a0bc89SDoug Rabson }
62427a0bc89SDoug Rabson
62527a0bc89SDoug Rabson if (pmp->pm_Sectors == 0) {
62627a0bc89SDoug Rabson pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
62727a0bc89SDoug Rabson pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
62827a0bc89SDoug Rabson } else {
62927a0bc89SDoug Rabson pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
63027a0bc89SDoug Rabson pmp->pm_HugeSectors = pmp->pm_Sectors;
63127a0bc89SDoug Rabson }
632952a6212SJordan K. Hubbard
633952a6212SJordan K. Hubbard if (pmp->pm_RootDirEnts == 0) {
63404fd468dSKonstantin Belousov if (pmp->pm_FATsecs != 0 || getushort(b710->bpbFSVers) != 0) {
635952a6212SJordan K. Hubbard error = EINVAL;
63654cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
637aaf0bb19SAndrey A. Chernov printf("mountmsdosfs(): bad FAT32 filesystem\n");
63854cf9198SKonstantin Belousov #endif
639952a6212SJordan K. Hubbard goto error_exit;
640952a6212SJordan K. Hubbard }
641952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT32_MASK;
642952a6212SJordan K. Hubbard pmp->pm_fatmult = 4;
643952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1;
644952a6212SJordan K. Hubbard pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
64504fd468dSKonstantin Belousov if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0)
646952a6212SJordan K. Hubbard pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
647952a6212SJordan K. Hubbard else
648952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR;
649952a6212SJordan K. Hubbard } else
650952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFS_FATMIRROR;
651952a6212SJordan K. Hubbard
652952a6212SJordan K. Hubbard /*
653952a6212SJordan K. Hubbard * Check a few values (could do some more):
654952a6212SJordan K. Hubbard * - logical sector size: power of 2, >= block size
655952a6212SJordan K. Hubbard * - sectors per cluster: power of 2, >= 1
656952a6212SJordan K. Hubbard * - number of sectors: >= 1, <= size of partition
657696f22f0SJohn Baldwin * - number of FAT sectors: >= 1
658952a6212SJordan K. Hubbard */
65904fd468dSKonstantin Belousov if (SecPerClust == 0 || (SecPerClust & (SecPerClust - 1)) != 0 ||
66004fd468dSKonstantin Belousov pmp->pm_BytesPerSec < DEV_BSIZE ||
66104fd468dSKonstantin Belousov (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) != 0 ||
66204fd468dSKonstantin Belousov pmp->pm_HugeSectors == 0 || pmp->pm_FATsecs == 0 ||
66304fd468dSKonstantin Belousov SecPerClust * pmp->pm_BlkPerSec > MAXBSIZE / DEV_BSIZE) {
664952a6212SJordan K. Hubbard error = EINVAL;
665952a6212SJordan K. Hubbard goto error_exit;
666952a6212SJordan K. Hubbard }
66701f6cfbaSYoshihiro Takahashi
668ba2c9838SKonstantin Belousov if ((off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec <
669ba2c9838SKonstantin Belousov pmp->pm_HugeSectors /* overflow */ ||
670ba2c9838SKonstantin Belousov (off_t)pmp->pm_HugeSectors * pmp->pm_BytesPerSec >
671ba2c9838SKonstantin Belousov cp->provider->mediasize /* past end of vol */) {
672ba2c9838SKonstantin Belousov error = EINVAL;
673ba2c9838SKonstantin Belousov goto error_exit;
674ba2c9838SKonstantin Belousov }
675ba2c9838SKonstantin Belousov
676c7cd607aSMark Johnston pmp->pm_HugeSectors *= pmp->pm_BlkPerSec;
67701f6cfbaSYoshihiro Takahashi pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */
67801f6cfbaSYoshihiro Takahashi pmp->pm_FATsecs *= pmp->pm_BlkPerSec;
67901f6cfbaSYoshihiro Takahashi SecPerClust *= pmp->pm_BlkPerSec;
68001f6cfbaSYoshihiro Takahashi
68101f6cfbaSYoshihiro Takahashi pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec;
68201f6cfbaSYoshihiro Takahashi
683952a6212SJordan K. Hubbard if (FAT32(pmp)) {
684952a6212SJordan K. Hubbard pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
68504fd468dSKonstantin Belousov pmp->pm_firstcluster = pmp->pm_fatblk +
68604fd468dSKonstantin Belousov pmp->pm_FATs * pmp->pm_FATsecs;
68701f6cfbaSYoshihiro Takahashi pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec;
688952a6212SJordan K. Hubbard } else {
68927a0bc89SDoug Rabson pmp->pm_rootdirblk = pmp->pm_fatblk +
69004fd468dSKonstantin Belousov pmp->pm_FATs * pmp->pm_FATsecs;
69155e0987aSPedro F. Giffuni pmp->pm_rootdirsize = howmany(pmp->pm_RootDirEnts *
69255e0987aSPedro F. Giffuni sizeof(struct direntry), DEV_BSIZE); /* in blocks */
69327a0bc89SDoug Rabson pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
694952a6212SJordan K. Hubbard }
695952a6212SJordan K. Hubbard
696ba2c9838SKonstantin Belousov if (pmp->pm_HugeSectors <= pmp->pm_firstcluster) {
697ba2c9838SKonstantin Belousov error = EINVAL;
698ba2c9838SKonstantin Belousov goto error_exit;
699ba2c9838SKonstantin Belousov }
700499d3ffaSBoris Popov pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
701499d3ffaSBoris Popov SecPerClust + 1;
7020728695cSStefan Eßer pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE;
703952a6212SJordan K. Hubbard
704952a6212SJordan K. Hubbard if (pmp->pm_fatmask == 0) {
7050728695cSStefan Eßer /*
7060728695cSStefan Eßer * The last 10 (or 16?) clusters are reserved and must not
7070728695cSStefan Eßer * be allocated for data.
7080728695cSStefan Eßer */
7090728695cSStefan Eßer if (pmp->pm_maxcluster < (CLUST_RSRVD & FAT12_MASK)) {
71027a0bc89SDoug Rabson /*
711952a6212SJordan K. Hubbard * This will usually be a floppy disk. This size makes
7129287dbaaSEd Maste * sure that one FAT entry will not be split across
713952a6212SJordan K. Hubbard * multiple blocks.
71427a0bc89SDoug Rabson */
715952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT12_MASK;
716952a6212SJordan K. Hubbard pmp->pm_fatmult = 3;
717952a6212SJordan K. Hubbard pmp->pm_fatdiv = 2;
718952a6212SJordan K. Hubbard } else {
719952a6212SJordan K. Hubbard pmp->pm_fatmask = FAT16_MASK;
720952a6212SJordan K. Hubbard pmp->pm_fatmult = 2;
721952a6212SJordan K. Hubbard pmp->pm_fatdiv = 1;
722952a6212SJordan K. Hubbard }
723952a6212SJordan K. Hubbard }
724499d3ffaSBoris Popov
725499d3ffaSBoris Popov clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv;
726*45d4e82bSStefan Eßer if (clusters >= (CLUST_RSRVD & pmp->pm_fatmask))
727*45d4e82bSStefan Eßer clusters = CLUST_RSRVD & pmp->pm_fatmask;
728499d3ffaSBoris Popov if (pmp->pm_maxcluster >= clusters) {
72954cf9198SKonstantin Belousov #ifdef MSDOSFS_DEBUG
730499d3ffaSBoris Popov printf("Warning: number of clusters (%ld) exceeds FAT "
7310728695cSStefan Eßer "capacity (%ld)\n", pmp->pm_maxcluster - 1, clusters);
73254cf9198SKonstantin Belousov #endif
733499d3ffaSBoris Popov pmp->pm_maxcluster = clusters - 1;
734499d3ffaSBoris Popov }
735499d3ffaSBoris Popov
736952a6212SJordan K. Hubbard if (FAT12(pmp))
73793fe42b6SBruce Evans pmp->pm_fatblocksize = 3 * 512;
73827a0bc89SDoug Rabson else
73993fe42b6SBruce Evans pmp->pm_fatblocksize = PAGE_SIZE;
74093fe42b6SBruce Evans pmp->pm_fatblocksize = roundup(pmp->pm_fatblocksize,
74193fe42b6SBruce Evans pmp->pm_BytesPerSec);
74201f6cfbaSYoshihiro Takahashi pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE;
74301f6cfbaSYoshihiro Takahashi pmp->pm_bnshift = ffs(DEV_BSIZE) - 1;
74427a0bc89SDoug Rabson
74527a0bc89SDoug Rabson /*
74627a0bc89SDoug Rabson * Compute mask and shift value for isolating cluster relative byte
74727a0bc89SDoug Rabson * offsets and cluster numbers from a file offset.
74827a0bc89SDoug Rabson */
74901f6cfbaSYoshihiro Takahashi pmp->pm_bpcluster = SecPerClust * DEV_BSIZE;
750952a6212SJordan K. Hubbard pmp->pm_crbomask = pmp->pm_bpcluster - 1;
751952a6212SJordan K. Hubbard pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
75227a0bc89SDoug Rabson
753952a6212SJordan K. Hubbard /*
754952a6212SJordan K. Hubbard * Check for valid cluster size
755952a6212SJordan K. Hubbard * must be a power of 2
756952a6212SJordan K. Hubbard */
75704fd468dSKonstantin Belousov if ((pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) != 0) {
758952a6212SJordan K. Hubbard error = EINVAL;
759952a6212SJordan K. Hubbard goto error_exit;
760ad63a118SSatoshi Asami }
76127a0bc89SDoug Rabson
76227a0bc89SDoug Rabson /*
76327a0bc89SDoug Rabson * Release the bootsector buffer.
76427a0bc89SDoug Rabson */
765952a6212SJordan K. Hubbard brelse(bp);
766952a6212SJordan K. Hubbard bp = NULL;
767952a6212SJordan K. Hubbard
768952a6212SJordan K. Hubbard /*
7698d61a735SBruce Evans * Check the fsinfo sector if we have one. Silently fix up our
7708d61a735SBruce Evans * in-core copy of fp->fsinxtfree if it is unknown (0xffffffff)
7718d61a735SBruce Evans * or too large. Ignore fp->fsinfree for now, since we need to
7728d61a735SBruce Evans * read the entire FAT anyway to fill the inuse map.
773952a6212SJordan K. Hubbard */
774952a6212SJordan K. Hubbard if (pmp->pm_fsinfo) {
775952a6212SJordan K. Hubbard struct fsinfo *fp;
776952a6212SJordan K. Hubbard
77737269429SBruce Evans if ((error = bread(devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec,
77801f6cfbaSYoshihiro Takahashi NOCRED, &bp)) != 0)
779952a6212SJordan K. Hubbard goto error_exit;
780952a6212SJordan K. Hubbard fp = (struct fsinfo *)bp->b_data;
78104fd468dSKonstantin Belousov if (!bcmp(fp->fsisig1, "RRaA", 4) &&
78204fd468dSKonstantin Belousov !bcmp(fp->fsisig2, "rrAa", 4) &&
78304fd468dSKonstantin Belousov !bcmp(fp->fsisig3, "\0\0\125\252", 4)) {
784952a6212SJordan K. Hubbard pmp->pm_nxtfree = getulong(fp->fsinxtfree);
7858d61a735SBruce Evans if (pmp->pm_nxtfree > pmp->pm_maxcluster)
7868bb386f2STim J. Robbins pmp->pm_nxtfree = CLUST_FIRST;
7878bb386f2STim J. Robbins } else
788952a6212SJordan K. Hubbard pmp->pm_fsinfo = 0;
789952a6212SJordan K. Hubbard brelse(bp);
790952a6212SJordan K. Hubbard bp = NULL;
791952a6212SJordan K. Hubbard }
792952a6212SJordan K. Hubbard
793952a6212SJordan K. Hubbard /*
7948d61a735SBruce Evans * Finish initializing pmp->pm_nxtfree (just in case the first few
7958d61a735SBruce Evans * sectors aren't properly reserved in the FAT). This completes
7968d61a735SBruce Evans * the fixup for fp->fsinxtfree, and fixes up the zero-initialized
7978d61a735SBruce Evans * value if there is no fsinfo. We will use pmp->pm_nxtfree
7988d61a735SBruce Evans * internally even if there is no fsinfo.
799952a6212SJordan K. Hubbard */
8008d61a735SBruce Evans if (pmp->pm_nxtfree < CLUST_FIRST)
8018d61a735SBruce Evans pmp->pm_nxtfree = CLUST_FIRST;
80227a0bc89SDoug Rabson
80327a0bc89SDoug Rabson /*
80427a0bc89SDoug Rabson * Allocate memory for the bitmap of allocated clusters, and then
80527a0bc89SDoug Rabson * fill it in.
80627a0bc89SDoug Rabson */
80704fd468dSKonstantin Belousov pmp->pm_inusemap = malloc(howmany(pmp->pm_maxcluster + 1,
80804fd468dSKonstantin Belousov N_INUSEBITS) * sizeof(*pmp->pm_inusemap), M_MSDOSFSFAT, M_WAITOK);
80927a0bc89SDoug Rabson
81027a0bc89SDoug Rabson /*
81127a0bc89SDoug Rabson * fillinusemap() needs pm_devvp.
81227a0bc89SDoug Rabson */
81327a0bc89SDoug Rabson pmp->pm_devvp = devvp;
814aaaa4fb5SKonstantin Belousov pmp->pm_odevvp = odevvp;
815c72ae142SJohn Baldwin pmp->pm_dev = dev;
81627a0bc89SDoug Rabson
81727a0bc89SDoug Rabson /*
81827a0bc89SDoug Rabson * Have the inuse map filled in.
81927a0bc89SDoug Rabson */
8206be1a4ccSKonstantin Belousov MSDOSFS_LOCK_MP(pmp);
8216be1a4ccSKonstantin Belousov error = fillinusemap(pmp);
8226be1a4ccSKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp);
8236be1a4ccSKonstantin Belousov if (error != 0)
82427a0bc89SDoug Rabson goto error_exit;
82527a0bc89SDoug Rabson
82627a0bc89SDoug Rabson /*
8279287dbaaSEd Maste * If they want FAT updates to be synchronous then let them suffer
82827a0bc89SDoug Rabson * the performance degradation in exchange for the on disk copy of
8299287dbaaSEd Maste * the FAT being correct just about all the time. I suppose this
83027a0bc89SDoug Rabson * would be a good thing to turn on if the kernel is still flakey.
83127a0bc89SDoug Rabson */
832952a6212SJordan K. Hubbard if (mp->mnt_flag & MNT_SYNCHRONOUS)
833952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;
83427a0bc89SDoug Rabson
83527a0bc89SDoug Rabson /*
83627a0bc89SDoug Rabson * Finish up.
83727a0bc89SDoug Rabson */
838952a6212SJordan K. Hubbard if (ronly)
839952a6212SJordan K. Hubbard pmp->pm_flags |= MSDOSFSMNT_RONLY;
840cede1f56STom Rhodes else {
841aaa38524SConrad Meyer if ((error = markvoldirty(pmp, 1)) != 0)
842cede1f56STom Rhodes goto error_exit;
84327a0bc89SDoug Rabson pmp->pm_fmod = 1;
844cede1f56STom Rhodes }
845c33db74bSStefan Eßer
846c33db74bSStefan Eßer if (FAT32(pmp)) {
847c33db74bSStefan Eßer pmp->pm_rootdirfree = 0;
848c33db74bSStefan Eßer } else {
849c33db74bSStefan Eßer pmp->pm_rootdirfree = rootdir_free(pmp);
850c33db74bSStefan Eßer if (pmp->pm_rootdirfree < 0)
851c33db74bSStefan Eßer goto error_exit;
852c33db74bSStefan Eßer }
853c33db74bSStefan Eßer
85477465d93SAlfred Perlstein mp->mnt_data = pmp;
855939cb752SBruce Evans mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
856af3f60d5SBruce Evans mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
8575da56ddbSTor Egge MNT_ILOCK(mp);
858cc9d8990SPeter Wemm mp->mnt_flag |= MNT_LOCAL;
8592aa39445SKonstantin Belousov mp->mnt_kern_flag |= MNTK_USES_BCACHE | MNTK_NO_IOPF;
8605da56ddbSTor Egge MNT_IUNLOCK(mp);
86127a0bc89SDoug Rabson
86240373cf5SKonstantin Belousov return (0);
86327a0bc89SDoug Rabson
864952a6212SJordan K. Hubbard error_exit:
86504fd468dSKonstantin Belousov if (bp != NULL)
866952a6212SJordan K. Hubbard brelse(bp);
8679a135592SPoul-Henning Kamp if (cp != NULL) {
8689a135592SPoul-Henning Kamp g_topology_lock();
8690d7935fdSAttilio Rao g_vfs_close(cp);
8709a135592SPoul-Henning Kamp g_topology_unlock();
8719a135592SPoul-Henning Kamp }
87204fd468dSKonstantin Belousov if (pmp != NULL) {
873a84ec05fSKonstantin Belousov lockdestroy(&pmp->pm_fatlock);
8746ae13c0fSKonstantin Belousov lockdestroy(&pmp->pm_checkpath_lock);
875952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT);
876952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT);
87777465d93SAlfred Perlstein mp->mnt_data = NULL;
87827a0bc89SDoug Rabson }
879aaaa4fb5SKonstantin Belousov BO_LOCK(&odevvp->v_bufobj);
880aaaa4fb5SKonstantin Belousov odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
881aaaa4fb5SKonstantin Belousov BO_UNLOCK(&odevvp->v_bufobj);
882bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&dev->si_mountpt, 0);
883aaaa4fb5SKonstantin Belousov vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
884aaaa4fb5SKonstantin Belousov mntfs_freevp(devvp);
885c72ae142SJohn Baldwin dev_rel(dev);
886952a6212SJordan K. Hubbard return (error);
88727a0bc89SDoug Rabson }
88827a0bc89SDoug Rabson
88927a0bc89SDoug Rabson /*
89027a0bc89SDoug Rabson * Unmount the filesystem described by mp.
89127a0bc89SDoug Rabson */
8927fefffeeSPoul-Henning Kamp static int
msdosfs_unmount(struct mount * mp,int mntflags)893dfd233edSAttilio Rao msdosfs_unmount(struct mount *mp, int mntflags)
89427a0bc89SDoug Rabson {
895952a6212SJordan K. Hubbard struct msdosfsmount *pmp;
896952a6212SJordan K. Hubbard int error, flags;
89769367793SKonstantin Belousov bool susp;
89827a0bc89SDoug Rabson
8992359e2dcSKonstantin Belousov error = flags = 0;
9002359e2dcSKonstantin Belousov pmp = VFSTOMSDOSFS(mp);
90169367793SKonstantin Belousov susp = (pmp->pm_flags & MSDOSFSMNT_RONLY) == 0;
90269367793SKonstantin Belousov
90369367793SKonstantin Belousov if (susp) {
90469367793SKonstantin Belousov error = vfs_write_suspend_umnt(mp);
90569367793SKonstantin Belousov if (error != 0)
90669367793SKonstantin Belousov return (error);
90769367793SKonstantin Belousov }
90869367793SKonstantin Belousov
9092359e2dcSKonstantin Belousov if ((mntflags & MNT_FORCE) != 0)
91027a0bc89SDoug Rabson flags |= FORCECLOSE;
911dfd233edSAttilio Rao error = vflush(mp, 0, flags, curthread);
91269367793SKonstantin Belousov if (error != 0 && error != ENXIO) {
91369367793SKonstantin Belousov if (susp)
91469367793SKonstantin Belousov vfs_write_resume(mp, VR_START_WRITE);
915b2344ab5SKonstantin Belousov return (error);
91669367793SKonstantin Belousov }
91769367793SKonstantin Belousov if (susp) {
9183247c9ddSXin LI error = markvoldirty(pmp, 0);
91969367793SKonstantin Belousov if (error != 0 && error != ENXIO) {
92069367793SKonstantin Belousov if (susp)
92169367793SKonstantin Belousov vfs_write_resume(mp, VR_START_WRITE);
9223247c9ddSXin LI (void)markvoldirty(pmp, 1);
9233247c9ddSXin LI return (error);
9243247c9ddSXin LI }
9253247c9ddSXin LI }
926c4f02a89SMax Khon if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) {
927c4f02a89SMax Khon if (pmp->pm_w2u)
928c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_w2u);
929c4f02a89SMax Khon if (pmp->pm_u2w)
930c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2w);
931c4f02a89SMax Khon if (pmp->pm_d2u)
932c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_d2u);
933c4f02a89SMax Khon if (pmp->pm_u2d)
934c4f02a89SMax Khon msdosfs_iconv->close(pmp->pm_u2d);
935c4f02a89SMax Khon }
936cede1f56STom Rhodes
937952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG
938952a6212SJordan K. Hubbard {
939952a6212SJordan K. Hubbard struct vnode *vp = pmp->pm_devvp;
940698b1a66SJeff Roberson struct bufobj *bo;
941952a6212SJordan K. Hubbard
942698b1a66SJeff Roberson bo = &vp->v_bufobj;
943698b1a66SJeff Roberson BO_LOCK(bo);
9444d93c0beSJeff Roberson VI_LOCK(vp);
945f69d42a1SPoul-Henning Kamp vn_printf(vp,
946f69d42a1SPoul-Henning Kamp "msdosfs_umount(): just before calling VOP_CLOSE()\n");
947952a6212SJordan K. Hubbard printf("freef %p, freeb %p, mount %p\n",
948cc3593fbSMateusz Guzik TAILQ_NEXT(vp, v_vnodelist), vp->v_vnodelist.tqe_prev,
949952a6212SJordan K. Hubbard vp->v_mount);
950c1c4d0e9SConrad Meyer printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n",
951156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_clean.bv_hd),
952156cb265SPoul-Henning Kamp TAILQ_FIRST(&vp->v_bufobj.bo_dirty.bv_hd),
953156cb265SPoul-Henning Kamp vp->v_bufobj.bo_numoutput, vp->v_type);
9544d93c0beSJeff Roberson VI_UNLOCK(vp);
955698b1a66SJeff Roberson BO_UNLOCK(bo);
956952a6212SJordan K. Hubbard }
957952a6212SJordan K. Hubbard #endif
95869367793SKonstantin Belousov if (susp)
95969367793SKonstantin Belousov vfs_write_resume(mp, VR_START_WRITE);
96069367793SKonstantin Belousov
961aaaa4fb5SKonstantin Belousov vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
9629a135592SPoul-Henning Kamp g_topology_lock();
9630d7935fdSAttilio Rao g_vfs_close(pmp->pm_cp);
9649a135592SPoul-Henning Kamp g_topology_unlock();
965aaaa4fb5SKonstantin Belousov BO_LOCK(&pmp->pm_odevvp->v_bufobj);
966aaaa4fb5SKonstantin Belousov pmp->pm_odevvp->v_bufobj.bo_flag &= ~BO_NOBUFS;
967aaaa4fb5SKonstantin Belousov BO_UNLOCK(&pmp->pm_odevvp->v_bufobj);
968bb8297e6SKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&pmp->pm_dev->si_mountpt, 0);
969aaaa4fb5SKonstantin Belousov mntfs_freevp(pmp->pm_devvp);
970aaaa4fb5SKonstantin Belousov vrele(pmp->pm_odevvp);
971c72ae142SJohn Baldwin dev_rel(pmp->pm_dev);
972952a6212SJordan K. Hubbard free(pmp->pm_inusemap, M_MSDOSFSFAT);
97323b6c230SKonstantin Belousov lockdestroy(&pmp->pm_fatlock);
9746ae13c0fSKonstantin Belousov lockdestroy(&pmp->pm_checkpath_lock);
975952a6212SJordan K. Hubbard free(pmp, M_MSDOSFSMNT);
97677465d93SAlfred Perlstein mp->mnt_data = NULL;
9774f560d75SEdward Tomasz Napierala return (error);
97827a0bc89SDoug Rabson }
97927a0bc89SDoug Rabson
980b2e4b635SKonstantin Belousov static void
msdosfs_remount_ro(void * arg,int pending)981b2e4b635SKonstantin Belousov msdosfs_remount_ro(void *arg, int pending)
982b2e4b635SKonstantin Belousov {
983b2e4b635SKonstantin Belousov struct msdosfsmount *pmp;
984b2e4b635SKonstantin Belousov int error;
985b2e4b635SKonstantin Belousov
986b2e4b635SKonstantin Belousov pmp = arg;
987b2e4b635SKonstantin Belousov
988b2e4b635SKonstantin Belousov MSDOSFS_LOCK_MP(pmp);
989b2e4b635SKonstantin Belousov if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0) {
990b2e4b635SKonstantin Belousov while ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
991b2e4b635SKonstantin Belousov msleep(&pmp->pm_flags, &pmp->pm_fatlock, PVFS,
992b2e4b635SKonstantin Belousov "msdoserrro", hz);
993b2e4b635SKonstantin Belousov } else if ((pmp->pm_mountp->mnt_flag & MNT_RDONLY) == 0) {
994b2e4b635SKonstantin Belousov pmp->pm_flags |= MSDOSFS_ERR_RO;
995b2e4b635SKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp);
996b2e4b635SKonstantin Belousov printf("%s: remounting read-only due to corruption\n",
997b2e4b635SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntfromname);
998b2e4b635SKonstantin Belousov error = vfs_remount_ro(pmp->pm_mountp);
999b2e4b635SKonstantin Belousov if (error != 0)
1000b2e4b635SKonstantin Belousov printf("%s: remounting read-only failed: error %d\n",
1001b2e4b635SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntfromname, error);
1002b2e4b635SKonstantin Belousov else
1003b2e4b635SKonstantin Belousov printf("remounted %s read-only\n",
1004b2e4b635SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntfromname);
1005b2e4b635SKonstantin Belousov MSDOSFS_LOCK_MP(pmp);
1006b2e4b635SKonstantin Belousov pmp->pm_flags &= ~MSDOSFS_ERR_RO;
1007b2e4b635SKonstantin Belousov wakeup(&pmp->pm_flags);
1008b2e4b635SKonstantin Belousov }
1009b2e4b635SKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp);
1010b2e4b635SKonstantin Belousov
10114b3ffc59SKonstantin Belousov while (--pending >= 0)
1012b2e4b635SKonstantin Belousov vfs_unbusy(pmp->pm_mountp);
1013b2e4b635SKonstantin Belousov }
1014b2e4b635SKonstantin Belousov
1015b2e4b635SKonstantin Belousov void
msdosfs_integrity_error(struct msdosfsmount * pmp)1016b2e4b635SKonstantin Belousov msdosfs_integrity_error(struct msdosfsmount *pmp)
1017b2e4b635SKonstantin Belousov {
1018b2e4b635SKonstantin Belousov int error;
1019b2e4b635SKonstantin Belousov
1020b2e4b635SKonstantin Belousov error = vfs_busy(pmp->pm_mountp, MBF_NOWAIT);
102113ccb045SKonstantin Belousov if (error == 0) {
102213ccb045SKonstantin Belousov error = taskqueue_enqueue(taskqueue_thread,
102313ccb045SKonstantin Belousov &pmp->pm_rw2ro_task);
102413ccb045SKonstantin Belousov if (error != 0) {
102513ccb045SKonstantin Belousov printf("%s: integrity error scheduling failed, "
102613ccb045SKonstantin Belousov "error %d\n",
102713ccb045SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntfromname, error);
102813ccb045SKonstantin Belousov vfs_unbusy(pmp->pm_mountp);
102913ccb045SKonstantin Belousov }
103013ccb045SKonstantin Belousov } else {
1031b2e4b635SKonstantin Belousov printf("%s: integrity error busying failed, error %d\n",
1032b2e4b635SKonstantin Belousov pmp->pm_mountp->mnt_stat.f_mntfromname, error);
1033b2e4b635SKonstantin Belousov }
103413ccb045SKonstantin Belousov }
1035b2e4b635SKonstantin Belousov
10367fefffeeSPoul-Henning Kamp static int
msdosfs_root(struct mount * mp,int flags,struct vnode ** vpp)1037dfd233edSAttilio Rao msdosfs_root(struct mount *mp, int flags, struct vnode **vpp)
103827a0bc89SDoug Rabson {
1039952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
104027a0bc89SDoug Rabson struct denode *ndep;
104127a0bc89SDoug Rabson int error;
104227a0bc89SDoug Rabson
104327a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG
1044952a6212SJordan K. Hubbard printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp);
104527a0bc89SDoug Rabson #endif
1046ae7e8a02SKonstantin Belousov error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, LK_EXCLUSIVE, &ndep);
1047952a6212SJordan K. Hubbard if (error)
1048952a6212SJordan K. Hubbard return (error);
104927a0bc89SDoug Rabson *vpp = DETOV(ndep);
1050952a6212SJordan K. Hubbard return (0);
105127a0bc89SDoug Rabson }
105227a0bc89SDoug Rabson
10537fefffeeSPoul-Henning Kamp static int
msdosfs_statfs(struct mount * mp,struct statfs * sbp)1054dfd233edSAttilio Rao msdosfs_statfs(struct mount *mp, struct statfs *sbp)
105527a0bc89SDoug Rabson {
1056952a6212SJordan K. Hubbard struct msdosfsmount *pmp;
105727a0bc89SDoug Rabson
1058952a6212SJordan K. Hubbard pmp = VFSTOMSDOSFS(mp);
105927a0bc89SDoug Rabson sbp->f_bsize = pmp->pm_bpcluster;
106027a0bc89SDoug Rabson sbp->f_iosize = pmp->pm_bpcluster;
10610728695cSStefan Eßer sbp->f_blocks = pmp->pm_maxcluster - CLUST_FIRST + 1;
106227a0bc89SDoug Rabson sbp->f_bfree = pmp->pm_freeclustercount;
106327a0bc89SDoug Rabson sbp->f_bavail = pmp->pm_freeclustercount;
1064c33db74bSStefan Eßer sbp->f_files = howmany(pmp->pm_rootdirsize * DEV_BSIZE,
1065c33db74bSStefan Eßer sizeof(struct direntry));
1066c33db74bSStefan Eßer sbp->f_ffree = pmp->pm_rootdirfree;
1067952a6212SJordan K. Hubbard return (0);
106827a0bc89SDoug Rabson }
106927a0bc89SDoug Rabson
1070bb7ca822SKonstantin Belousov /*
1071bb7ca822SKonstantin Belousov * If we have an FSInfo block, update it.
1072bb7ca822SKonstantin Belousov */
1073bb7ca822SKonstantin Belousov static int
msdosfs_fsiflush(struct msdosfsmount * pmp,int waitfor)1074bb7ca822SKonstantin Belousov msdosfs_fsiflush(struct msdosfsmount *pmp, int waitfor)
1075bb7ca822SKonstantin Belousov {
1076bb7ca822SKonstantin Belousov struct fsinfo *fp;
1077bb7ca822SKonstantin Belousov struct buf *bp;
1078bb7ca822SKonstantin Belousov int error;
1079bb7ca822SKonstantin Belousov
1080bb7ca822SKonstantin Belousov MSDOSFS_LOCK_MP(pmp);
1081bb7ca822SKonstantin Belousov if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) {
1082bb7ca822SKonstantin Belousov error = 0;
1083bb7ca822SKonstantin Belousov goto unlock;
1084bb7ca822SKonstantin Belousov }
1085bb7ca822SKonstantin Belousov error = bread(pmp->pm_devvp, pmp->pm_fsinfo, pmp->pm_BytesPerSec,
1086bb7ca822SKonstantin Belousov NOCRED, &bp);
1087bb7ca822SKonstantin Belousov if (error != 0) {
1088bb7ca822SKonstantin Belousov goto unlock;
1089bb7ca822SKonstantin Belousov }
1090bb7ca822SKonstantin Belousov fp = (struct fsinfo *)bp->b_data;
1091bb7ca822SKonstantin Belousov putulong(fp->fsinfree, pmp->pm_freeclustercount);
1092bb7ca822SKonstantin Belousov putulong(fp->fsinxtfree, pmp->pm_nxtfree);
1093bb7ca822SKonstantin Belousov pmp->pm_flags &= ~MSDOSFS_FSIMOD;
1094bb7ca822SKonstantin Belousov if (waitfor == MNT_WAIT)
1095bb7ca822SKonstantin Belousov error = bwrite(bp);
1096bb7ca822SKonstantin Belousov else
1097bb7ca822SKonstantin Belousov bawrite(bp);
1098bb7ca822SKonstantin Belousov unlock:
1099bb7ca822SKonstantin Belousov MSDOSFS_UNLOCK_MP(pmp);
1100bb7ca822SKonstantin Belousov return (error);
1101bb7ca822SKonstantin Belousov }
1102bb7ca822SKonstantin Belousov
11037fefffeeSPoul-Henning Kamp static int
msdosfs_sync(struct mount * mp,int waitfor)1104dfd233edSAttilio Rao msdosfs_sync(struct mount *mp, int waitfor)
110527a0bc89SDoug Rabson {
1106952a6212SJordan K. Hubbard struct vnode *vp, *nvp;
1107dfd233edSAttilio Rao struct thread *td;
110827a0bc89SDoug Rabson struct denode *dep;
1109952a6212SJordan K. Hubbard struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
1110952a6212SJordan K. Hubbard int error, allerror = 0;
111127a0bc89SDoug Rabson
1112dfd233edSAttilio Rao td = curthread;
1113dfd233edSAttilio Rao
111427a0bc89SDoug Rabson /*
11159287dbaaSEd Maste * If we ever switch to not updating all of the FATs all the time,
111627a0bc89SDoug Rabson * this would be the place to update them from the first one.
111727a0bc89SDoug Rabson */
1118dfd5dee1SPeter Wemm if (pmp->pm_fmod != 0) {
1119952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_RONLY)
112027a0bc89SDoug Rabson panic("msdosfs_sync: rofs mod");
112127a0bc89SDoug Rabson else {
11229287dbaaSEd Maste /* update FATs here */
112327a0bc89SDoug Rabson }
1124dfd5dee1SPeter Wemm }
112527a0bc89SDoug Rabson /*
1126952a6212SJordan K. Hubbard * Write back each (modified) denode.
112727a0bc89SDoug Rabson */
112827a0bc89SDoug Rabson loop:
112971469bb3SKirk McKusick MNT_VNODE_FOREACH_ALL(vp, mp, nvp) {
113071469bb3SKirk McKusick if (vp->v_type == VNON) {
11314ab2c8bdSJeff Roberson VI_UNLOCK(vp);
11324ab2c8bdSJeff Roberson continue;
11334ab2c8bdSJeff Roberson }
113427a0bc89SDoug Rabson dep = VTODE(vp);
1135f00f5d71SPoul-Henning Kamp if ((dep->de_flag &
1136c681be37SDmitrij Tejblum (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 &&
1137156cb265SPoul-Henning Kamp (vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
1138f00f5d71SPoul-Henning Kamp waitfor == MNT_LAZY)) {
11394d93c0beSJeff Roberson VI_UNLOCK(vp);
114027a0bc89SDoug Rabson continue;
1141af3f60d5SBruce Evans }
1142a92a971bSMateusz Guzik error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
1143af3f60d5SBruce Evans if (error) {
1144f342b91cSMateusz Guzik if (error == ENOENT) {
1145f342b91cSMateusz Guzik MNT_VNODE_FOREACH_ALL_ABORT(mp, nvp);
114627a0bc89SDoug Rabson goto loop;
1147f342b91cSMateusz Guzik }
1148af3f60d5SBruce Evans continue;
1149af3f60d5SBruce Evans }
11508df6bac4SPoul-Henning Kamp error = VOP_FSYNC(vp, waitfor, td);
1151c3c6d51eSPoul-Henning Kamp if (error)
115227a0bc89SDoug Rabson allerror = error;
1153b935e867SMateusz Guzik vput(vp);
115427a0bc89SDoug Rabson }
115527a0bc89SDoug Rabson
115627a0bc89SDoug Rabson /*
115727a0bc89SDoug Rabson * Flush filesystem control info.
115827a0bc89SDoug Rabson */
1159c681be37SDmitrij Tejblum if (waitfor != MNT_LAZY) {
1160cb05b60aSAttilio Rao vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY);
11618df6bac4SPoul-Henning Kamp error = VOP_FSYNC(pmp->pm_devvp, waitfor, td);
1162c3c6d51eSPoul-Henning Kamp if (error)
116327a0bc89SDoug Rabson allerror = error;
1164b249ce48SMateusz Guzik VOP_UNLOCK(pmp->pm_devvp);
1165c681be37SDmitrij Tejblum }
1166bb7ca822SKonstantin Belousov
1167bb7ca822SKonstantin Belousov error = msdosfs_fsiflush(pmp, waitfor);
1168bb7ca822SKonstantin Belousov if (error != 0)
1169bb7ca822SKonstantin Belousov allerror = error;
11701b3cb4dcSKonstantin Belousov
11711b3cb4dcSKonstantin Belousov if (allerror == 0 && waitfor == MNT_SUSPEND) {
11721b3cb4dcSKonstantin Belousov MNT_ILOCK(mp);
11731b3cb4dcSKonstantin Belousov mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED;
11741b3cb4dcSKonstantin Belousov MNT_IUNLOCK(mp);
11751b3cb4dcSKonstantin Belousov }
1176952a6212SJordan K. Hubbard return (allerror);
117727a0bc89SDoug Rabson }
117827a0bc89SDoug Rabson
11791be5bc74STom Rhodes static int
msdosfs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)1180694a586aSRick Macklem msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
11811be5bc74STom Rhodes {
11821be5bc74STom Rhodes struct msdosfsmount *pmp = VFSTOMSDOSFS(mp);
11831be5bc74STom Rhodes struct defid *defhp = (struct defid *) fhp;
11841be5bc74STom Rhodes struct denode *dep;
11851be5bc74STom Rhodes int error;
11861be5bc74STom Rhodes
1187ae7e8a02SKonstantin Belousov error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
1188ae7e8a02SKonstantin Belousov LK_EXCLUSIVE, &dep);
11891be5bc74STom Rhodes if (error) {
11901be5bc74STom Rhodes *vpp = NULLVP;
11911be5bc74STom Rhodes return (error);
11921be5bc74STom Rhodes }
11931be5bc74STom Rhodes *vpp = DETOV(dep);
11941be5bc74STom Rhodes vnode_create_vobject(*vpp, dep->de_FileSize, curthread);
11951be5bc74STom Rhodes return (0);
11961be5bc74STom Rhodes }
11971be5bc74STom Rhodes
119830ffadf3SPoul-Henning Kamp static struct vfsops msdosfs_vfsops = {
11991be5bc74STom Rhodes .vfs_fhtovp = msdosfs_fhtovp,
12006a4b48f4SPoul-Henning Kamp .vfs_mount = msdosfs_mount,
12016a4b48f4SPoul-Henning Kamp .vfs_cmount = msdosfs_cmount,
12027652131bSPoul-Henning Kamp .vfs_root = msdosfs_root,
12037652131bSPoul-Henning Kamp .vfs_statfs = msdosfs_statfs,
12047652131bSPoul-Henning Kamp .vfs_sync = msdosfs_sync,
12057652131bSPoul-Henning Kamp .vfs_unmount = msdosfs_unmount,
120627a0bc89SDoug Rabson };
1207c901836cSGarrett Wollman
12084ccd7546SRuslan Ermilov VFS_SET(msdosfs_vfsops, msdosfs, 0);
1209c4f02a89SMax Khon MODULE_VERSION(msdosfs, 1);
1210