1 1.138 hannken /* $NetBSD: msdosfs_vfsops.c,v 1.138 2022/04/16 07:58:21 hannken Exp $ */ 2 1.1 jdolecek 3 1.1 jdolecek /*- 4 1.1 jdolecek * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 1.1 jdolecek * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 1.1 jdolecek * All rights reserved. 7 1.1 jdolecek * Original code by Paul Popelka (paulp (at) uts.amdahl.com) (see below). 8 1.1 jdolecek * 9 1.1 jdolecek * Redistribution and use in source and binary forms, with or without 10 1.1 jdolecek * modification, are permitted provided that the following conditions 11 1.1 jdolecek * are met: 12 1.1 jdolecek * 1. Redistributions of source code must retain the above copyright 13 1.1 jdolecek * notice, this list of conditions and the following disclaimer. 14 1.1 jdolecek * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 jdolecek * notice, this list of conditions and the following disclaimer in the 16 1.1 jdolecek * documentation and/or other materials provided with the distribution. 17 1.1 jdolecek * 3. All advertising materials mentioning features or use of this software 18 1.1 jdolecek * must display the following acknowledgement: 19 1.1 jdolecek * This product includes software developed by TooLs GmbH. 20 1.1 jdolecek * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 1.1 jdolecek * derived from this software without specific prior written permission. 22 1.1 jdolecek * 23 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 1.1 jdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 1.1 jdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 1.1 jdolecek * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 1.1 jdolecek * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 1.1 jdolecek * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 1.1 jdolecek * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 1.1 jdolecek * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 1.1 jdolecek * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 1.1 jdolecek * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 1.1 jdolecek */ 34 1.1 jdolecek /* 35 1.1 jdolecek * Written by Paul Popelka (paulp (at) uts.amdahl.com) 36 1.1 jdolecek * 37 1.1 jdolecek * You can do anything you want with this software, just don't say you wrote 38 1.1 jdolecek * it, and don't remove this notice. 39 1.1 jdolecek * 40 1.1 jdolecek * This software is provided "as is". 41 1.1 jdolecek * 42 1.1 jdolecek * The author supplies this software to be publicly redistributed on the 43 1.1 jdolecek * understanding that the author is not responsible for the correct 44 1.1 jdolecek * functioning of this software in any circumstances and is not liable for 45 1.1 jdolecek * any damages caused by this software. 46 1.1 jdolecek * 47 1.1 jdolecek * October 1992 48 1.1 jdolecek */ 49 1.1 jdolecek 50 1.1 jdolecek #include <sys/cdefs.h> 51 1.138 hannken __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.138 2022/04/16 07:58:21 hannken Exp $"); 52 1.1 jdolecek 53 1.1 jdolecek #if defined(_KERNEL_OPT) 54 1.1 jdolecek #include "opt_compat_netbsd.h" 55 1.1 jdolecek #endif 56 1.1 jdolecek 57 1.1 jdolecek #include <sys/param.h> 58 1.1 jdolecek #include <sys/systm.h> 59 1.12 atatat #include <sys/sysctl.h> 60 1.1 jdolecek #include <sys/namei.h> 61 1.1 jdolecek #include <sys/proc.h> 62 1.1 jdolecek #include <sys/kernel.h> 63 1.1 jdolecek #include <sys/vnode.h> 64 1.60 dholland #include <miscfs/genfs/genfs.h> 65 1.1 jdolecek #include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ 66 1.1 jdolecek #include <sys/mount.h> 67 1.1 jdolecek #include <sys/buf.h> 68 1.1 jdolecek #include <sys/file.h> 69 1.1 jdolecek #include <sys/device.h> 70 1.1 jdolecek #include <sys/disklabel.h> 71 1.42 christos #include <sys/disk.h> 72 1.1 jdolecek #include <sys/ioctl.h> 73 1.1 jdolecek #include <sys/malloc.h> 74 1.1 jdolecek #include <sys/dirent.h> 75 1.1 jdolecek #include <sys/stat.h> 76 1.1 jdolecek #include <sys/conf.h> 77 1.31 elad #include <sys/kauth.h> 78 1.66 rumble #include <sys/module.h> 79 1.1 jdolecek 80 1.1 jdolecek #include <fs/msdosfs/bpb.h> 81 1.1 jdolecek #include <fs/msdosfs/bootsect.h> 82 1.1 jdolecek #include <fs/msdosfs/direntry.h> 83 1.1 jdolecek #include <fs/msdosfs/denode.h> 84 1.1 jdolecek #include <fs/msdosfs/msdosfsmount.h> 85 1.1 jdolecek #include <fs/msdosfs/fat.h> 86 1.1 jdolecek 87 1.82 pooka MODULE(MODULE_CLASS_VFS, msdos, NULL); 88 1.66 rumble 89 1.42 christos #ifdef MSDOSFS_DEBUG 90 1.115 maxv #define DPRINTF(fmt, ...) uprintf("%s(): " fmt "\n", __func__, ##__VA_ARGS__) 91 1.42 christos #else 92 1.115 maxv #define DPRINTF(fmt, ...) 93 1.42 christos #endif 94 1.42 christos 95 1.110 maxv #define GEMDOSFS_BSIZE 512 96 1.110 maxv 97 1.20 jdolecek #define MSDOSFS_NAMEMAX(pmp) \ 98 1.20 jdolecek (pmp)->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12 99 1.20 jdolecek 100 1.29 christos int msdosfs_mountfs(struct vnode *, struct mount *, struct lwp *, 101 1.26 xtraeme struct msdosfs_args *); 102 1.1 jdolecek 103 1.26 xtraeme static int update_mp(struct mount *, struct msdosfs_args *); 104 1.2 thorpej 105 1.45 pooka MALLOC_JUSTDEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOS FS mount structure"); 106 1.100 jakllsch MALLOC_JUSTDEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOS FS FAT table"); 107 1.51 rumble MALLOC_JUSTDEFINE(M_MSDOSFSTMP, "MSDOSFS temp", "MSDOS FS temp. structures"); 108 1.1 jdolecek 109 1.1 jdolecek extern const struct vnodeopv_desc msdosfs_vnodeop_opv_desc; 110 1.1 jdolecek 111 1.1 jdolecek const struct vnodeopv_desc * const msdosfs_vnodeopv_descs[] = { 112 1.1 jdolecek &msdosfs_vnodeop_opv_desc, 113 1.1 jdolecek NULL, 114 1.1 jdolecek }; 115 1.1 jdolecek 116 1.1 jdolecek struct vfsops msdosfs_vfsops = { 117 1.106 hannken .vfs_name = MOUNT_MSDOS, 118 1.106 hannken .vfs_min_mount_data = sizeof (struct msdosfs_args), 119 1.106 hannken .vfs_mount = msdosfs_mount, 120 1.106 hannken .vfs_start = msdosfs_start, 121 1.106 hannken .vfs_unmount = msdosfs_unmount, 122 1.106 hannken .vfs_root = msdosfs_root, 123 1.106 hannken .vfs_quotactl = (void *)eopnotsupp, 124 1.106 hannken .vfs_statvfs = msdosfs_statvfs, 125 1.106 hannken .vfs_sync = msdosfs_sync, 126 1.106 hannken .vfs_vget = msdosfs_vget, 127 1.109 hannken .vfs_loadvnode = msdosfs_loadvnode, 128 1.106 hannken .vfs_fhtovp = msdosfs_fhtovp, 129 1.106 hannken .vfs_vptofh = msdosfs_vptofh, 130 1.106 hannken .vfs_init = msdosfs_init, 131 1.106 hannken .vfs_reinit = msdosfs_reinit, 132 1.106 hannken .vfs_done = msdosfs_done, 133 1.106 hannken .vfs_mountroot = msdosfs_mountroot, 134 1.106 hannken .vfs_snapshot = (void *)eopnotsupp, 135 1.106 hannken .vfs_extattrctl = vfs_stdextattrctl, 136 1.122 hannken .vfs_suspendctl = genfs_suspendctl, 137 1.106 hannken .vfs_renamelock_enter = genfs_renamelock_enter, 138 1.106 hannken .vfs_renamelock_exit = genfs_renamelock_exit, 139 1.106 hannken .vfs_fsync = (void *)eopnotsupp, 140 1.106 hannken .vfs_opv_descs = msdosfs_vnodeopv_descs 141 1.1 jdolecek }; 142 1.66 rumble 143 1.133 pgoyette SYSCTL_SETUP(msdosfs_sysctl_setup, "msdosfs sysctl") 144 1.133 pgoyette { 145 1.133 pgoyette sysctl_createv(clog, 0, NULL, NULL, 146 1.133 pgoyette CTLFLAG_PERMANENT, 147 1.133 pgoyette CTLTYPE_NODE, "msdosfs", 148 1.133 pgoyette SYSCTL_DESCR("MS-DOS file system"), 149 1.133 pgoyette NULL, 0, NULL, 0, 150 1.133 pgoyette CTL_VFS, 4, CTL_EOL); 151 1.133 pgoyette /* 152 1.133 pgoyette * XXX the "4" above could be dynamic, thereby eliminating one 153 1.133 pgoyette * more instance of the "number to vfs" mapping problem, but 154 1.133 pgoyette * "4" is the order as taken from sys/mount.h 155 1.133 pgoyette */ 156 1.133 pgoyette } 157 1.133 pgoyette 158 1.66 rumble static int 159 1.83 mlelstv msdos_modcmd(modcmd_t cmd, void *arg) 160 1.66 rumble { 161 1.68 rumble int error; 162 1.66 rumble 163 1.66 rumble switch (cmd) { 164 1.66 rumble case MODULE_CMD_INIT: 165 1.68 rumble error = vfs_attach(&msdosfs_vfsops); 166 1.68 rumble if (error != 0) 167 1.68 rumble break; 168 1.68 rumble break; 169 1.66 rumble case MODULE_CMD_FINI: 170 1.68 rumble error = vfs_detach(&msdosfs_vfsops); 171 1.68 rumble if (error != 0) 172 1.68 rumble break; 173 1.68 rumble break; 174 1.66 rumble default: 175 1.68 rumble error = ENOTTY; 176 1.68 rumble break; 177 1.66 rumble } 178 1.68 rumble 179 1.68 rumble return (error); 180 1.66 rumble } 181 1.1 jdolecek 182 1.1 jdolecek static int 183 1.72 dsl update_mp(struct mount *mp, struct msdosfs_args *argp) 184 1.1 jdolecek { 185 1.1 jdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 186 1.1 jdolecek int error; 187 1.1 jdolecek 188 1.1 jdolecek pmp->pm_gid = argp->gid; 189 1.1 jdolecek pmp->pm_uid = argp->uid; 190 1.1 jdolecek pmp->pm_mask = argp->mask & ALLPERMS; 191 1.8 jdolecek pmp->pm_dirmask = argp->dirmask & ALLPERMS; 192 1.9 itojun pmp->pm_gmtoff = argp->gmtoff; 193 1.1 jdolecek pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; 194 1.1 jdolecek 195 1.1 jdolecek /* 196 1.71 abs * GEMDOS knows nothing about win95 long filenames 197 1.1 jdolecek */ 198 1.1 jdolecek if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) 199 1.1 jdolecek pmp->pm_flags |= MSDOSFSMNT_NOWIN95; 200 1.1 jdolecek 201 1.1 jdolecek if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) 202 1.1 jdolecek pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; 203 1.1 jdolecek else if (!(pmp->pm_flags & 204 1.1 jdolecek (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { 205 1.24 christos struct vnode *rtvp; 206 1.1 jdolecek 207 1.1 jdolecek /* 208 1.1 jdolecek * Try to divine whether to support Win'95 long filenames 209 1.1 jdolecek */ 210 1.1 jdolecek if (FAT32(pmp)) 211 1.1 jdolecek pmp->pm_flags |= MSDOSFSMNT_LONGNAME; 212 1.1 jdolecek else { 213 1.131 ad error = msdosfs_root(mp, LK_EXCLUSIVE, &rtvp); 214 1.131 ad if (error != 0) 215 1.1 jdolecek return error; 216 1.137 thorpej pmp->pm_flags |= msdosfs_findwin95(VTODE(rtvp)) 217 1.1 jdolecek ? MSDOSFSMNT_LONGNAME 218 1.1 jdolecek : MSDOSFSMNT_SHORTNAME; 219 1.24 christos vput(rtvp); 220 1.1 jdolecek } 221 1.1 jdolecek } 222 1.20 jdolecek 223 1.20 jdolecek mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp); 224 1.20 jdolecek 225 1.1 jdolecek return 0; 226 1.1 jdolecek } 227 1.1 jdolecek 228 1.1 jdolecek int 229 1.73 cegger msdosfs_mountroot(void) 230 1.1 jdolecek { 231 1.1 jdolecek struct mount *mp; 232 1.29 christos struct lwp *l = curlwp; /* XXX */ 233 1.1 jdolecek int error; 234 1.1 jdolecek struct msdosfs_args args; 235 1.1 jdolecek 236 1.30 thorpej if (device_class(root_device) != DV_DISK) 237 1.1 jdolecek return (ENODEV); 238 1.1 jdolecek 239 1.1 jdolecek if ((error = vfs_rootmountalloc(MOUNT_MSDOS, "root_device", &mp))) { 240 1.1 jdolecek vrele(rootvp); 241 1.1 jdolecek return (error); 242 1.1 jdolecek } 243 1.1 jdolecek 244 1.8 jdolecek args.flags = MSDOSFSMNT_VERSIONED; 245 1.1 jdolecek args.uid = 0; 246 1.1 jdolecek args.gid = 0; 247 1.1 jdolecek args.mask = 0777; 248 1.8 jdolecek args.version = MSDOSFSMNT_VERSION; 249 1.8 jdolecek args.dirmask = 0777; 250 1.1 jdolecek 251 1.29 christos if ((error = msdosfs_mountfs(rootvp, mp, l, &args)) != 0) { 252 1.127 hannken vfs_unbusy(mp); 253 1.126 hannken vfs_rele(mp); 254 1.1 jdolecek return (error); 255 1.1 jdolecek } 256 1.1 jdolecek 257 1.6 thorpej if ((error = update_mp(mp, &args)) != 0) { 258 1.54 pooka (void)msdosfs_unmount(mp, 0); 259 1.127 hannken vfs_unbusy(mp); 260 1.126 hannken vfs_rele(mp); 261 1.1 jdolecek vrele(rootvp); 262 1.1 jdolecek return (error); 263 1.1 jdolecek } 264 1.1 jdolecek 265 1.103 christos mountlist_append(mp); 266 1.54 pooka (void)msdosfs_statvfs(mp, &mp->mnt_stat); 267 1.127 hannken vfs_unbusy(mp); 268 1.1 jdolecek return (0); 269 1.1 jdolecek } 270 1.1 jdolecek 271 1.1 jdolecek /* 272 1.1 jdolecek * mp - path - addr in user space of mount point (ie /usr or whatever) 273 1.1 jdolecek * data - addr in user space of mount params including the name of the block 274 1.1 jdolecek * special file to treat as a filesystem. 275 1.1 jdolecek */ 276 1.1 jdolecek int 277 1.72 dsl msdosfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 278 1.1 jdolecek { 279 1.54 pooka struct lwp *l = curlwp; 280 1.1 jdolecek struct vnode *devvp; /* vnode for blk device to mount */ 281 1.46 dsl struct msdosfs_args *args = data; /* holds data from mount request */ 282 1.1 jdolecek /* msdosfs specific mount control block */ 283 1.1 jdolecek struct msdosfsmount *pmp = NULL; 284 1.1 jdolecek int error, flags; 285 1.1 jdolecek mode_t accessmode; 286 1.1 jdolecek 287 1.107 maxv if (args == NULL) 288 1.107 maxv return EINVAL; 289 1.46 dsl if (*data_len < sizeof *args) 290 1.46 dsl return EINVAL; 291 1.46 dsl 292 1.1 jdolecek if (mp->mnt_flag & MNT_GETARGS) { 293 1.1 jdolecek pmp = VFSTOMSDOSFS(mp); 294 1.1 jdolecek if (pmp == NULL) 295 1.1 jdolecek return EIO; 296 1.46 dsl args->fspec = NULL; 297 1.46 dsl args->uid = pmp->pm_uid; 298 1.46 dsl args->gid = pmp->pm_gid; 299 1.46 dsl args->mask = pmp->pm_mask; 300 1.46 dsl args->flags = pmp->pm_flags; 301 1.46 dsl args->version = MSDOSFSMNT_VERSION; 302 1.46 dsl args->dirmask = pmp->pm_dirmask; 303 1.46 dsl args->gmtoff = pmp->pm_gmtoff; 304 1.46 dsl *data_len = sizeof *args; 305 1.46 dsl return 0; 306 1.42 christos } 307 1.8 jdolecek 308 1.8 jdolecek /* 309 1.8 jdolecek * If not versioned (i.e. using old mount_msdos(8)), fill in 310 1.8 jdolecek * the additional structure items with suitable defaults. 311 1.8 jdolecek */ 312 1.46 dsl if ((args->flags & MSDOSFSMNT_VERSIONED) == 0) { 313 1.46 dsl args->version = 1; 314 1.46 dsl args->dirmask = args->mask; 315 1.8 jdolecek } 316 1.8 jdolecek 317 1.1 jdolecek /* 318 1.15 jdolecek * Reset GMT offset for pre-v3 mount structure args. 319 1.15 jdolecek */ 320 1.46 dsl if (args->version < 3) 321 1.46 dsl args->gmtoff = 0; 322 1.15 jdolecek 323 1.15 jdolecek /* 324 1.1 jdolecek * If updating, check whether changing from read-only to 325 1.1 jdolecek * read/write; if there is no device name, that's all we do. 326 1.1 jdolecek */ 327 1.1 jdolecek if (mp->mnt_flag & MNT_UPDATE) { 328 1.1 jdolecek pmp = VFSTOMSDOSFS(mp); 329 1.1 jdolecek error = 0; 330 1.75 elad if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && 331 1.75 elad (mp->mnt_flag & MNT_RDONLY)) { 332 1.1 jdolecek flags = WRITECLOSE; 333 1.1 jdolecek if (mp->mnt_flag & MNT_FORCE) 334 1.1 jdolecek flags |= FORCECLOSE; 335 1.1 jdolecek error = vflush(mp, NULLVP, flags); 336 1.1 jdolecek } 337 1.1 jdolecek if (!error && (mp->mnt_flag & MNT_RELOAD)) 338 1.1 jdolecek /* not yet implemented */ 339 1.1 jdolecek error = EOPNOTSUPP; 340 1.42 christos if (error) { 341 1.115 maxv DPRINTF("vflush %d", error); 342 1.1 jdolecek return (error); 343 1.42 christos } 344 1.75 elad if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && 345 1.75 elad (mp->mnt_iflag & IMNT_WANTRDWR)) { 346 1.1 jdolecek /* 347 1.1 jdolecek * If upgrade to read-write by non-root, then verify 348 1.1 jdolecek * that user has necessary permissions on the device. 349 1.74 elad * 350 1.75 elad * Permission to update a mount is checked higher, so 351 1.75 elad * here we presume updating the mount is okay (for 352 1.75 elad * example, as far as securelevel goes) which leaves us 353 1.75 elad * with the normal check. 354 1.1 jdolecek */ 355 1.74 elad devvp = pmp->pm_devvp; 356 1.74 elad vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 357 1.94 elad error = kauth_authorize_system(l->l_cred, 358 1.94 elad KAUTH_SYSTEM_MOUNT, KAUTH_REQ_SYSTEM_MOUNT_DEVICE, 359 1.94 elad mp, devvp, KAUTH_ARG(VREAD | VWRITE)); 360 1.86 hannken VOP_UNLOCK(devvp); 361 1.115 maxv DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error); 362 1.74 elad if (error) 363 1.74 elad return (error); 364 1.74 elad 365 1.1 jdolecek pmp->pm_flags &= ~MSDOSFSMNT_RONLY; 366 1.1 jdolecek } 367 1.46 dsl if (args->fspec == NULL) { 368 1.115 maxv DPRINTF("missing fspec"); 369 1.28 jmmv return EINVAL; 370 1.42 christos } 371 1.1 jdolecek } 372 1.1 jdolecek /* 373 1.1 jdolecek * Not an update, or updating the name: look up the name 374 1.1 jdolecek * and verify that it refers to a sensible block device. 375 1.1 jdolecek */ 376 1.76 dholland error = namei_simple_user(args->fspec, 377 1.76 dholland NSM_FOLLOW_NOEMULROOT, &devvp); 378 1.76 dholland if (error != 0) { 379 1.115 maxv DPRINTF("namei %d", error); 380 1.1 jdolecek return (error); 381 1.42 christos } 382 1.1 jdolecek 383 1.1 jdolecek if (devvp->v_type != VBLK) { 384 1.115 maxv DPRINTF("not block"); 385 1.1 jdolecek vrele(devvp); 386 1.1 jdolecek return (ENOTBLK); 387 1.1 jdolecek } 388 1.1 jdolecek if (bdevsw_lookup(devvp->v_rdev) == NULL) { 389 1.115 maxv DPRINTF("no block switch"); 390 1.1 jdolecek vrele(devvp); 391 1.1 jdolecek return (ENXIO); 392 1.1 jdolecek } 393 1.1 jdolecek /* 394 1.1 jdolecek * If mount by non-root, then verify that user has necessary 395 1.1 jdolecek * permissions on the device. 396 1.1 jdolecek */ 397 1.74 elad accessmode = VREAD; 398 1.74 elad if ((mp->mnt_flag & MNT_RDONLY) == 0) 399 1.74 elad accessmode |= VWRITE; 400 1.74 elad vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 401 1.94 elad error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, 402 1.94 elad KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode)); 403 1.86 hannken VOP_UNLOCK(devvp); 404 1.74 elad if (error) { 405 1.115 maxv DPRINTF("KAUTH_REQ_SYSTEM_MOUNT_DEVICE %d", error); 406 1.74 elad vrele(devvp); 407 1.74 elad return (error); 408 1.1 jdolecek } 409 1.1 jdolecek if ((mp->mnt_flag & MNT_UPDATE) == 0) { 410 1.24 christos int xflags; 411 1.22 mycroft 412 1.22 mycroft if (mp->mnt_flag & MNT_RDONLY) 413 1.24 christos xflags = FREAD; 414 1.22 mycroft else 415 1.24 christos xflags = FREAD|FWRITE; 416 1.93 hannken vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 417 1.54 pooka error = VOP_OPEN(devvp, xflags, FSCRED); 418 1.93 hannken VOP_UNLOCK(devvp); 419 1.42 christos if (error) { 420 1.115 maxv DPRINTF("VOP_OPEN %d", error); 421 1.22 mycroft goto fail; 422 1.42 christos } 423 1.46 dsl error = msdosfs_mountfs(devvp, mp, l, args); 424 1.22 mycroft if (error) { 425 1.115 maxv DPRINTF("msdosfs_mountfs %d", error); 426 1.22 mycroft vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 427 1.54 pooka (void) VOP_CLOSE(devvp, xflags, NOCRED); 428 1.86 hannken VOP_UNLOCK(devvp); 429 1.22 mycroft goto fail; 430 1.22 mycroft } 431 1.1 jdolecek #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ 432 1.1 jdolecek pmp = VFSTOMSDOSFS(mp); 433 1.1 jdolecek #endif 434 1.1 jdolecek } else { 435 1.22 mycroft vrele(devvp); 436 1.42 christos if (devvp != pmp->pm_devvp) { 437 1.115 maxv DPRINTF("devvp %p pmp %p", devvp, pmp->pm_devvp); 438 1.22 mycroft return (EINVAL); /* needs translation */ 439 1.42 christos } 440 1.1 jdolecek } 441 1.46 dsl if ((error = update_mp(mp, args)) != 0) { 442 1.54 pooka msdosfs_unmount(mp, MNT_FORCE); 443 1.115 maxv DPRINTF("update_mp %d", error); 444 1.1 jdolecek return error; 445 1.1 jdolecek } 446 1.1 jdolecek 447 1.1 jdolecek #ifdef MSDOSFS_DEBUG 448 1.1 jdolecek printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); 449 1.1 jdolecek #endif 450 1.46 dsl return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, 451 1.47 pooka mp->mnt_op->vfs_name, mp, l); 452 1.22 mycroft 453 1.22 mycroft fail: 454 1.22 mycroft vrele(devvp); 455 1.22 mycroft return (error); 456 1.1 jdolecek } 457 1.1 jdolecek 458 1.1 jdolecek int 459 1.72 dsl msdosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l, struct msdosfs_args *argp) 460 1.1 jdolecek { 461 1.1 jdolecek struct msdosfsmount *pmp; 462 1.1 jdolecek struct buf *bp; 463 1.1 jdolecek dev_t dev = devvp->v_rdev; 464 1.1 jdolecek union bootsector *bsp; 465 1.1 jdolecek struct byte_bpb33 *b33; 466 1.1 jdolecek struct byte_bpb50 *b50; 467 1.1 jdolecek struct byte_bpb710 *b710; 468 1.79 mlelstv uint8_t SecPerClust; 469 1.110 maxv int ronly, error, BlkPerSec; 470 1.79 mlelstv uint64_t psize; 471 1.79 mlelstv unsigned secsize; 472 1.128 mlelstv u_long fatbytes, fatblocksecs; 473 1.1 jdolecek 474 1.22 mycroft /* Flush out any old buffers remaining from a previous use. */ 475 1.138 hannken vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 476 1.138 hannken error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0); 477 1.138 hannken VOP_UNLOCK(devvp); 478 1.138 hannken if (error) 479 1.1 jdolecek return (error); 480 1.1 jdolecek 481 1.1 jdolecek ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 482 1.1 jdolecek 483 1.1 jdolecek bp = NULL; /* both used in error_exit */ 484 1.1 jdolecek pmp = NULL; 485 1.1 jdolecek 486 1.79 mlelstv error = getdisksize(devvp, &psize, &secsize); 487 1.96 tsutsui if (error) { 488 1.84 pooka if (argp->flags & MSDOSFSMNT_GEMDOSFS) 489 1.84 pooka goto error_exit; 490 1.85 pooka 491 1.85 pooka /* ok, so it failed. we most likely don't need the info */ 492 1.84 pooka secsize = DEV_BSIZE; 493 1.84 pooka psize = 0; 494 1.85 pooka error = 0; 495 1.84 pooka } 496 1.114 maxv if (secsize < DEV_BSIZE) { 497 1.115 maxv DPRINTF("Invalid block secsize (%d < DEV_BSIZE)", secsize); 498 1.114 maxv error = EINVAL; 499 1.114 maxv goto error_exit; 500 1.114 maxv } 501 1.79 mlelstv 502 1.1 jdolecek if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 503 1.110 maxv if (secsize != GEMDOSFS_BSIZE) { 504 1.115 maxv DPRINTF("Invalid block secsize %d for GEMDOS", secsize); 505 1.1 jdolecek error = EINVAL; 506 1.1 jdolecek goto error_exit; 507 1.1 jdolecek } 508 1.110 maxv } 509 1.1 jdolecek 510 1.1 jdolecek /* 511 1.1 jdolecek * Read the boot sector of the filesystem, and then check the 512 1.1 jdolecek * boot signature. If not a dos boot sector then error out. 513 1.1 jdolecek */ 514 1.116 christos if (secsize < sizeof(*b50)) { 515 1.117 christos DPRINTF("50 bootsec %u\n", secsize); 516 1.116 christos error = EINVAL; 517 1.116 christos goto error_exit; 518 1.116 christos } 519 1.118 maxv if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0) 520 1.1 jdolecek goto error_exit; 521 1.1 jdolecek bsp = (union bootsector *)bp->b_data; 522 1.1 jdolecek b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; 523 1.1 jdolecek b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; 524 1.10 lukem b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; 525 1.1 jdolecek 526 1.136 ryoon #if 0 527 1.136 ryoon /* 528 1.136 ryoon * Some FAT partition, for example Raspberry Pi Pico's 529 1.136 ryoon * USB mass storage, does not have exptected BOOTSIGs. 530 1.136 ryoon * According to FreeBSD's comment, some PC-9800/9821 531 1.136 ryoon * FAT floppy disks have similar problems. 532 1.136 ryoon */ 533 1.1 jdolecek if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { 534 1.1 jdolecek if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 535 1.1 jdolecek || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { 536 1.115 maxv DPRINTF("bootsig0 %d bootsig1 %d", 537 1.42 christos bsp->bs50.bsBootSectSig0, 538 1.115 maxv bsp->bs50.bsBootSectSig1); 539 1.1 jdolecek error = EINVAL; 540 1.1 jdolecek goto error_exit; 541 1.1 jdolecek } 542 1.1 jdolecek } 543 1.136 ryoon #endif 544 1.1 jdolecek 545 1.112 maxv pmp = malloc(sizeof(*pmp), M_MSDOSFSMNT, M_WAITOK|M_ZERO); 546 1.1 jdolecek pmp->pm_mountp = mp; 547 1.1 jdolecek 548 1.1 jdolecek /* 549 1.1 jdolecek * Compute several useful quantities from the bpb in the 550 1.1 jdolecek * bootsector. Copy in the dos 5 variant of the bpb then fix up 551 1.1 jdolecek * the fields that are different between dos 5 and dos 3.3. 552 1.1 jdolecek */ 553 1.1 jdolecek SecPerClust = b50->bpbSecPerClust; 554 1.1 jdolecek pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); 555 1.1 jdolecek pmp->pm_ResSectors = getushort(b50->bpbResSectors); 556 1.1 jdolecek pmp->pm_FATs = b50->bpbFATs; 557 1.1 jdolecek pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); 558 1.1 jdolecek pmp->pm_Sectors = getushort(b50->bpbSectors); 559 1.1 jdolecek pmp->pm_FATsecs = getushort(b50->bpbFATsecs); 560 1.1 jdolecek pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); 561 1.1 jdolecek pmp->pm_Heads = getushort(b50->bpbHeads); 562 1.1 jdolecek pmp->pm_Media = b50->bpbMedia; 563 1.1 jdolecek 564 1.1 jdolecek if (pmp->pm_Sectors == 0) { 565 1.1 jdolecek pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 566 1.1 jdolecek pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); 567 1.1 jdolecek } else { 568 1.116 christos if (secsize < sizeof(*b33)) { 569 1.117 christos DPRINTF("33 bootsec %u\n", secsize); 570 1.116 christos error = EINVAL; 571 1.116 christos goto error_exit; 572 1.116 christos } 573 1.1 jdolecek pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 574 1.1 jdolecek pmp->pm_HugeSectors = pmp->pm_Sectors; 575 1.1 jdolecek } 576 1.1 jdolecek 577 1.110 maxv /* 578 1.110 maxv * Sanity checks, from the FAT specification: 579 1.110 maxv * - sectors per cluster: >= 1, power of 2 580 1.110 maxv * - logical sector size: >= 1, power of 2 581 1.110 maxv * - cluster size: <= max FS block size 582 1.110 maxv * - number of sectors: >= 1 583 1.110 maxv */ 584 1.110 maxv if ((SecPerClust == 0) || !powerof2(SecPerClust) || 585 1.110 maxv (pmp->pm_BytesPerSec == 0) || !powerof2(pmp->pm_BytesPerSec) || 586 1.110 maxv (SecPerClust * pmp->pm_BytesPerSec > MAXBSIZE) || 587 1.110 maxv (pmp->pm_HugeSectors == 0)) { 588 1.115 maxv DPRINTF("consistency checks"); 589 1.110 maxv error = EINVAL; 590 1.110 maxv goto error_exit; 591 1.110 maxv } 592 1.110 maxv 593 1.110 maxv if (!(argp->flags & MSDOSFSMNT_GEMDOSFS) && 594 1.110 maxv (pmp->pm_SecPerTrack > 63)) { 595 1.115 maxv DPRINTF("SecPerTrack %d", pmp->pm_SecPerTrack); 596 1.110 maxv error = EINVAL; 597 1.110 maxv goto error_exit; 598 1.110 maxv } 599 1.110 maxv 600 1.1 jdolecek if (pmp->pm_RootDirEnts == 0) { 601 1.116 christos if (secsize < sizeof(*b710)) { 602 1.117 christos DPRINTF("710 bootsec %u\n", secsize); 603 1.116 christos error = EINVAL; 604 1.116 christos goto error_exit; 605 1.116 christos } 606 1.112 maxv unsigned short FSVers = getushort(b710->bpbFSVers); 607 1.112 maxv unsigned short ExtFlags = getushort(b710->bpbExtFlags); 608 1.34 gdt /* 609 1.34 gdt * Some say that bsBootSectSig[23] must be zero, but 610 1.34 gdt * Windows does not require this and some digital cameras 611 1.34 gdt * do not set these to zero. Therefore, do not insist. 612 1.34 gdt */ 613 1.112 maxv if (pmp->pm_Sectors || pmp->pm_FATsecs || FSVers) { 614 1.115 maxv DPRINTF("Sectors %d FATsecs %lu FSVers %d", 615 1.115 maxv pmp->pm_Sectors, pmp->pm_FATsecs, FSVers); 616 1.1 jdolecek error = EINVAL; 617 1.1 jdolecek goto error_exit; 618 1.1 jdolecek } 619 1.1 jdolecek pmp->pm_fatmask = FAT32_MASK; 620 1.1 jdolecek pmp->pm_fatmult = 4; 621 1.1 jdolecek pmp->pm_fatdiv = 1; 622 1.1 jdolecek pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); 623 1.1 jdolecek 624 1.112 maxv /* Mirroring is enabled if the FATMIRROR bit is not set. */ 625 1.112 maxv if ((ExtFlags & FATMIRROR) == 0) 626 1.1 jdolecek pmp->pm_flags |= MSDOSFS_FATMIRROR; 627 1.1 jdolecek else 628 1.112 maxv pmp->pm_curfat = ExtFlags & FATNUM; 629 1.1 jdolecek } else 630 1.1 jdolecek pmp->pm_flags |= MSDOSFS_FATMIRROR; 631 1.1 jdolecek 632 1.1 jdolecek if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 633 1.1 jdolecek if (FAT32(pmp)) { 634 1.112 maxv /* GEMDOS doesn't know FAT32. */ 635 1.115 maxv DPRINTF("FAT32 for GEMDOS"); 636 1.1 jdolecek error = EINVAL; 637 1.1 jdolecek goto error_exit; 638 1.1 jdolecek } 639 1.1 jdolecek 640 1.1 jdolecek /* 641 1.1 jdolecek * Check a few values (could do some more): 642 1.110 maxv * - logical sector size: >= block size 643 1.110 maxv * - number of sectors: <= size of partition 644 1.1 jdolecek */ 645 1.110 maxv if ((pmp->pm_BytesPerSec < GEMDOSFS_BSIZE) || 646 1.110 maxv (pmp->pm_HugeSectors * 647 1.110 maxv (pmp->pm_BytesPerSec / GEMDOSFS_BSIZE) > psize)) { 648 1.115 maxv DPRINTF("consistency checks for GEMDOS"); 649 1.1 jdolecek error = EINVAL; 650 1.1 jdolecek goto error_exit; 651 1.1 jdolecek } 652 1.1 jdolecek /* 653 1.100 jakllsch * XXX - Many parts of the msdosfs driver seem to assume that 654 1.1 jdolecek * the number of bytes per logical sector (BytesPerSec) will 655 1.1 jdolecek * always be the same as the number of bytes per disk block 656 1.1 jdolecek * Let's pretend it is. 657 1.1 jdolecek */ 658 1.110 maxv BlkPerSec = pmp->pm_BytesPerSec / GEMDOSFS_BSIZE; 659 1.110 maxv pmp->pm_BytesPerSec = GEMDOSFS_BSIZE; 660 1.110 maxv pmp->pm_HugeSectors *= BlkPerSec; 661 1.110 maxv pmp->pm_HiddenSects *= BlkPerSec; 662 1.110 maxv pmp->pm_ResSectors *= BlkPerSec; 663 1.110 maxv pmp->pm_Sectors *= BlkPerSec; 664 1.110 maxv pmp->pm_FATsecs *= BlkPerSec; 665 1.110 maxv SecPerClust *= BlkPerSec; 666 1.1 jdolecek } 667 1.58 pooka 668 1.58 pooka /* Check that fs has nonzero FAT size */ 669 1.58 pooka if (pmp->pm_FATsecs == 0) { 670 1.115 maxv DPRINTF("FATsecs is 0"); 671 1.58 pooka error = EINVAL; 672 1.58 pooka goto error_exit; 673 1.58 pooka } 674 1.58 pooka 675 1.1 jdolecek pmp->pm_fatblk = pmp->pm_ResSectors; 676 1.1 jdolecek if (FAT32(pmp)) { 677 1.116 christos if (secsize < sizeof(*b710)) { 678 1.117 christos DPRINTF("710 bootsec %u\n", secsize); 679 1.116 christos error = EINVAL; 680 1.116 christos goto error_exit; 681 1.116 christos } 682 1.1 jdolecek pmp->pm_rootdirblk = getulong(b710->bpbRootClust); 683 1.1 jdolecek pmp->pm_firstcluster = pmp->pm_fatblk 684 1.1 jdolecek + (pmp->pm_FATs * pmp->pm_FATsecs); 685 1.1 jdolecek pmp->pm_fsinfo = getushort(b710->bpbFSInfo); 686 1.1 jdolecek } else { 687 1.1 jdolecek pmp->pm_rootdirblk = pmp->pm_fatblk + 688 1.1 jdolecek (pmp->pm_FATs * pmp->pm_FATsecs); 689 1.1 jdolecek pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) 690 1.1 jdolecek + pmp->pm_BytesPerSec - 1) 691 1.1 jdolecek / pmp->pm_BytesPerSec;/* in sectors */ 692 1.1 jdolecek pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; 693 1.1 jdolecek } 694 1.1 jdolecek 695 1.1 jdolecek pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / 696 1.1 jdolecek SecPerClust; 697 1.1 jdolecek pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; 698 1.1 jdolecek pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; 699 1.1 jdolecek 700 1.1 jdolecek if (argp->flags & MSDOSFSMNT_GEMDOSFS) { 701 1.78 mlelstv if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) { 702 1.1 jdolecek pmp->pm_fatmask = FAT12_MASK; 703 1.1 jdolecek pmp->pm_fatmult = 3; 704 1.1 jdolecek pmp->pm_fatdiv = 2; 705 1.1 jdolecek } else { 706 1.1 jdolecek pmp->pm_fatmask = FAT16_MASK; 707 1.1 jdolecek pmp->pm_fatmult = 2; 708 1.1 jdolecek pmp->pm_fatdiv = 1; 709 1.1 jdolecek } 710 1.1 jdolecek } else if (pmp->pm_fatmask == 0) { 711 1.1 jdolecek if (pmp->pm_maxcluster 712 1.1 jdolecek <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 713 1.1 jdolecek /* 714 1.1 jdolecek * This will usually be a floppy disk. This size makes 715 1.100 jakllsch * sure that one FAT entry will not be split across 716 1.1 jdolecek * multiple blocks. 717 1.1 jdolecek */ 718 1.1 jdolecek pmp->pm_fatmask = FAT12_MASK; 719 1.1 jdolecek pmp->pm_fatmult = 3; 720 1.1 jdolecek pmp->pm_fatdiv = 2; 721 1.1 jdolecek } else { 722 1.1 jdolecek pmp->pm_fatmask = FAT16_MASK; 723 1.1 jdolecek pmp->pm_fatmult = 2; 724 1.1 jdolecek pmp->pm_fatdiv = 1; 725 1.1 jdolecek } 726 1.1 jdolecek } 727 1.128 mlelstv 728 1.128 mlelstv /* validate cluster count against FAT */ 729 1.128 mlelstv if ((pmp->pm_maxcluster & pmp->pm_fatmask) != pmp->pm_maxcluster) { 730 1.128 mlelstv DPRINTF("maxcluster %lu outside of mask %#lx\n", 731 1.128 mlelstv pmp->pm_maxcluster, pmp->pm_fatmask); 732 1.128 mlelstv error = EINVAL; 733 1.128 mlelstv goto error_exit; 734 1.128 mlelstv } 735 1.128 mlelstv 736 1.128 mlelstv /* validate FAT size */ 737 1.128 mlelstv fatbytes = (pmp->pm_maxcluster+1) * pmp->pm_fatmult / pmp->pm_fatdiv; 738 1.128 mlelstv fatblocksecs = howmany(fatbytes, pmp->pm_BytesPerSec); 739 1.128 mlelstv 740 1.129 mlelstv if (pmp->pm_FATsecs < fatblocksecs) { 741 1.129 mlelstv DPRINTF("FATsecs %lu < real %lu\n", pmp->pm_FATsecs, 742 1.128 mlelstv fatblocksecs); 743 1.128 mlelstv error = EINVAL; 744 1.128 mlelstv goto error_exit; 745 1.128 mlelstv } 746 1.128 mlelstv 747 1.128 mlelstv if (FAT12(pmp)) { 748 1.128 mlelstv /* 749 1.128 mlelstv * limit block size to what is needed to read a FAT block 750 1.128 mlelstv * to not exceed MAXBSIZE 751 1.128 mlelstv */ 752 1.130 riastrad pmp->pm_fatblocksec = uimin(3, fatblocksecs); 753 1.128 mlelstv pmp->pm_fatblocksize = pmp->pm_fatblocksec 754 1.128 mlelstv * pmp->pm_BytesPerSec; 755 1.128 mlelstv } else { 756 1.1 jdolecek pmp->pm_fatblocksize = MAXBSIZE; 757 1.128 mlelstv pmp->pm_fatblocksec = pmp->pm_fatblocksize 758 1.128 mlelstv / pmp->pm_BytesPerSec; 759 1.128 mlelstv } 760 1.1 jdolecek 761 1.1 jdolecek pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; 762 1.1 jdolecek 763 1.1 jdolecek /* 764 1.1 jdolecek * Compute mask and shift value for isolating cluster relative byte 765 1.1 jdolecek * offsets and cluster numbers from a file offset. 766 1.1 jdolecek */ 767 1.1 jdolecek pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; 768 1.1 jdolecek pmp->pm_crbomask = pmp->pm_bpcluster - 1; 769 1.1 jdolecek pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; 770 1.1 jdolecek 771 1.1 jdolecek /* 772 1.1 jdolecek * Check for valid cluster size 773 1.1 jdolecek * must be a power of 2 774 1.1 jdolecek */ 775 1.1 jdolecek if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 776 1.115 maxv DPRINTF("bpcluster %lu cnshift %lu", pmp->pm_bpcluster, 777 1.115 maxv pmp->pm_cnshift); 778 1.1 jdolecek error = EINVAL; 779 1.1 jdolecek goto error_exit; 780 1.1 jdolecek } 781 1.1 jdolecek 782 1.1 jdolecek /* 783 1.101 jakllsch * Cluster size must be within limit of MAXBSIZE. 784 1.101 jakllsch * Many FAT filesystems will not have clusters larger than 785 1.101 jakllsch * 32KiB due to limits in Windows versions before Vista. 786 1.101 jakllsch */ 787 1.101 jakllsch if (pmp->pm_bpcluster > MAXBSIZE) { 788 1.115 maxv DPRINTF("bpcluster %lu > MAXBSIZE %d", 789 1.115 maxv pmp->pm_bpcluster, MAXBSIZE); 790 1.101 jakllsch error = EINVAL; 791 1.101 jakllsch goto error_exit; 792 1.101 jakllsch } 793 1.101 jakllsch 794 1.101 jakllsch /* 795 1.1 jdolecek * Release the bootsector buffer. 796 1.1 jdolecek */ 797 1.52 ad brelse(bp, BC_AGE); 798 1.1 jdolecek bp = NULL; 799 1.1 jdolecek 800 1.1 jdolecek /* 801 1.1 jdolecek * Check FSInfo. 802 1.1 jdolecek */ 803 1.1 jdolecek if (pmp->pm_fsinfo) { 804 1.1 jdolecek struct fsinfo *fp; 805 1.113 christos const int rdsz = roundup(sizeof(*fp), pmp->pm_BytesPerSec); 806 1.1 jdolecek 807 1.40 scw /* 808 1.40 scw * XXX If the fsinfo block is stored on media with 809 1.40 scw * 2KB or larger sectors, is the fsinfo structure 810 1.40 scw * padded at the end or in the middle? 811 1.40 scw */ 812 1.40 scw if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo), 813 1.118 maxv rdsz, 0, &bp)) != 0) 814 1.1 jdolecek goto error_exit; 815 1.1 jdolecek fp = (struct fsinfo *)bp->b_data; 816 1.1 jdolecek if (!memcmp(fp->fsisig1, "RRaA", 4) 817 1.1 jdolecek && !memcmp(fp->fsisig2, "rrAa", 4) 818 1.1 jdolecek && !memcmp(fp->fsisig3, "\0\0\125\252", 4) 819 1.1 jdolecek && !memcmp(fp->fsisig4, "\0\0\125\252", 4)) 820 1.1 jdolecek pmp->pm_nxtfree = getulong(fp->fsinxtfree); 821 1.1 jdolecek else 822 1.1 jdolecek pmp->pm_fsinfo = 0; 823 1.52 ad brelse(bp, 0); 824 1.1 jdolecek bp = NULL; 825 1.1 jdolecek } 826 1.1 jdolecek 827 1.1 jdolecek /* 828 1.1 jdolecek * Check and validate (or perhaps invalidate?) the fsinfo structure? 829 1.1 jdolecek * XXX 830 1.1 jdolecek */ 831 1.1 jdolecek if (pmp->pm_fsinfo) { 832 1.99 jakllsch if ((pmp->pm_nxtfree == 0xffffffffUL) || 833 1.99 jakllsch (pmp->pm_nxtfree > pmp->pm_maxcluster)) 834 1.1 jdolecek pmp->pm_fsinfo = 0; 835 1.1 jdolecek } 836 1.1 jdolecek 837 1.1 jdolecek /* 838 1.1 jdolecek * Allocate memory for the bitmap of allocated clusters, and then 839 1.1 jdolecek * fill it in. 840 1.1 jdolecek */ 841 1.97 jakllsch pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS) 842 1.1 jdolecek / N_INUSEBITS) 843 1.1 jdolecek * sizeof(*pmp->pm_inusemap), 844 1.1 jdolecek M_MSDOSFSFAT, M_WAITOK); 845 1.1 jdolecek 846 1.1 jdolecek /* 847 1.1 jdolecek * fillinusemap() needs pm_devvp. 848 1.1 jdolecek */ 849 1.1 jdolecek pmp->pm_dev = dev; 850 1.1 jdolecek pmp->pm_devvp = devvp; 851 1.1 jdolecek 852 1.1 jdolecek /* 853 1.1 jdolecek * Have the inuse map filled in. 854 1.1 jdolecek */ 855 1.137 thorpej if ((error = msdosfs_fillinusemap(pmp)) != 0) { 856 1.115 maxv DPRINTF("fillinusemap %d", error); 857 1.1 jdolecek goto error_exit; 858 1.42 christos } 859 1.1 jdolecek 860 1.1 jdolecek /* 861 1.100 jakllsch * If they want FAT updates to be synchronous then let them suffer 862 1.1 jdolecek * the performance degradation in exchange for the on disk copy of 863 1.100 jakllsch * the FAT being correct just about all the time. I suppose this 864 1.1 jdolecek * would be a good thing to turn on if the kernel is still flakey. 865 1.1 jdolecek */ 866 1.1 jdolecek if (mp->mnt_flag & MNT_SYNCHRONOUS) 867 1.1 jdolecek pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; 868 1.1 jdolecek 869 1.1 jdolecek /* 870 1.1 jdolecek * Finish up. 871 1.1 jdolecek */ 872 1.1 jdolecek if (ronly) 873 1.1 jdolecek pmp->pm_flags |= MSDOSFSMNT_RONLY; 874 1.1 jdolecek else 875 1.1 jdolecek pmp->pm_fmod = 1; 876 1.1 jdolecek mp->mnt_data = pmp; 877 1.20 jdolecek mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev; 878 1.20 jdolecek mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS); 879 1.20 jdolecek mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 880 1.20 jdolecek mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp); 881 1.1 jdolecek mp->mnt_flag |= MNT_LOCAL; 882 1.134 ad mp->mnt_iflag |= IMNT_SHRLOOKUP; 883 1.1 jdolecek mp->mnt_dev_bshift = pmp->pm_bnshift; 884 1.1 jdolecek mp->mnt_fs_bshift = pmp->pm_cnshift; 885 1.1 jdolecek 886 1.1 jdolecek /* 887 1.1 jdolecek * If we ever do quotas for DOS filesystems this would be a place 888 1.1 jdolecek * to fill in the info in the msdosfsmount structure. You dolt, 889 1.1 jdolecek * quotas on dos filesystems make no sense because files have no 890 1.1 jdolecek * owners on dos filesystems. of course there is some empty space 891 1.1 jdolecek * in the directory entry where we could put uid's and gid's. 892 1.1 jdolecek */ 893 1.69 pooka 894 1.102 hannken spec_node_setmountedfs(devvp, mp); 895 1.1 jdolecek 896 1.1 jdolecek return (0); 897 1.1 jdolecek 898 1.80 pooka error_exit: 899 1.1 jdolecek if (bp) 900 1.52 ad brelse(bp, BC_AGE); 901 1.1 jdolecek if (pmp) { 902 1.1 jdolecek if (pmp->pm_inusemap) 903 1.1 jdolecek free(pmp->pm_inusemap, M_MSDOSFSFAT); 904 1.1 jdolecek free(pmp, M_MSDOSFSMNT); 905 1.1 jdolecek mp->mnt_data = NULL; 906 1.1 jdolecek } 907 1.1 jdolecek return (error); 908 1.1 jdolecek } 909 1.1 jdolecek 910 1.1 jdolecek int 911 1.54 pooka msdosfs_start(struct mount *mp, int flags) 912 1.1 jdolecek { 913 1.1 jdolecek 914 1.1 jdolecek return (0); 915 1.1 jdolecek } 916 1.1 jdolecek 917 1.1 jdolecek /* 918 1.1 jdolecek * Unmount the filesystem described by mp. 919 1.1 jdolecek */ 920 1.1 jdolecek int 921 1.72 dsl msdosfs_unmount(struct mount *mp, int mntflags) 922 1.1 jdolecek { 923 1.1 jdolecek struct msdosfsmount *pmp; 924 1.1 jdolecek int error, flags; 925 1.1 jdolecek 926 1.1 jdolecek flags = 0; 927 1.1 jdolecek if (mntflags & MNT_FORCE) 928 1.1 jdolecek flags |= FORCECLOSE; 929 1.1 jdolecek if ((error = vflush(mp, NULLVP, flags)) != 0) 930 1.1 jdolecek return (error); 931 1.1 jdolecek pmp = VFSTOMSDOSFS(mp); 932 1.1 jdolecek if (pmp->pm_devvp->v_type != VBAD) 933 1.102 hannken spec_node_setmountedfs(pmp->pm_devvp, NULL); 934 1.1 jdolecek #ifdef MSDOSFS_DEBUG 935 1.1 jdolecek { 936 1.1 jdolecek struct vnode *vp = pmp->pm_devvp; 937 1.1 jdolecek 938 1.1 jdolecek printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); 939 1.64 ad printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n", 940 1.135 ad vp->v_vflag | vp->v_iflag | vp->v_uflag, vrefcnt(vp), 941 1.53 ad vp->v_writecount, vp->v_holdcnt); 942 1.25 jmmv printf("mount %p, op %p\n", 943 1.25 jmmv vp->v_mount, vp->v_op); 944 1.1 jdolecek printf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n", 945 1.1 jdolecek vp->v_cleanblkhd.lh_first, 946 1.1 jdolecek vp->v_dirtyblkhd.lh_first, 947 1.1 jdolecek vp->v_numoutput, vp->v_type); 948 1.1 jdolecek printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", 949 1.1 jdolecek vp->v_socket, vp->v_tag, 950 1.1 jdolecek ((u_int *)vp->v_data)[0], 951 1.1 jdolecek ((u_int *)vp->v_data)[1]); 952 1.1 jdolecek } 953 1.1 jdolecek #endif 954 1.1 jdolecek vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 955 1.80 pooka (void) VOP_CLOSE(pmp->pm_devvp, 956 1.54 pooka pmp->pm_flags & MSDOSFSMNT_RONLY ? FREAD : FREAD|FWRITE, NOCRED); 957 1.1 jdolecek vput(pmp->pm_devvp); 958 1.90 hannken msdosfs_fh_destroy(pmp); 959 1.1 jdolecek free(pmp->pm_inusemap, M_MSDOSFSFAT); 960 1.1 jdolecek free(pmp, M_MSDOSFSMNT); 961 1.1 jdolecek mp->mnt_data = NULL; 962 1.1 jdolecek mp->mnt_flag &= ~MNT_LOCAL; 963 1.80 pooka return (0); 964 1.1 jdolecek } 965 1.1 jdolecek 966 1.1 jdolecek int 967 1.131 ad msdosfs_root(struct mount *mp, int lktype, struct vnode **vpp) 968 1.1 jdolecek { 969 1.1 jdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 970 1.1 jdolecek int error; 971 1.1 jdolecek 972 1.1 jdolecek #ifdef MSDOSFS_DEBUG 973 1.1 jdolecek printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); 974 1.1 jdolecek #endif 975 1.137 thorpej if ((error = msdosfs_deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, 976 1.137 thorpej vpp)) != 0) 977 1.109 hannken return error; 978 1.131 ad error = vn_lock(*vpp, lktype); 979 1.109 hannken if (error) { 980 1.109 hannken vrele(*vpp); 981 1.109 hannken *vpp = NULL; 982 1.109 hannken return error; 983 1.109 hannken } 984 1.109 hannken return 0; 985 1.1 jdolecek } 986 1.1 jdolecek 987 1.1 jdolecek int 988 1.54 pooka msdosfs_statvfs(struct mount *mp, struct statvfs *sbp) 989 1.1 jdolecek { 990 1.1 jdolecek struct msdosfsmount *pmp; 991 1.1 jdolecek 992 1.1 jdolecek pmp = VFSTOMSDOSFS(mp); 993 1.1 jdolecek sbp->f_bsize = pmp->pm_bpcluster; 994 1.14 christos sbp->f_frsize = sbp->f_bsize; 995 1.1 jdolecek sbp->f_iosize = pmp->pm_bpcluster; 996 1.1 jdolecek sbp->f_blocks = pmp->pm_nmbrofclusters; 997 1.1 jdolecek sbp->f_bfree = pmp->pm_freeclustercount; 998 1.1 jdolecek sbp->f_bavail = pmp->pm_freeclustercount; 999 1.14 christos sbp->f_bresvd = 0; 1000 1.1 jdolecek sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ 1001 1.1 jdolecek sbp->f_ffree = 0; /* what to put in here? */ 1002 1.14 christos sbp->f_favail = 0; /* what to put in here? */ 1003 1.14 christos sbp->f_fresvd = 0; 1004 1.14 christos copy_statvfs_info(sbp, mp); 1005 1.1 jdolecek return (0); 1006 1.1 jdolecek } 1007 1.1 jdolecek 1008 1.108 christos struct msdosfs_sync_ctx { 1009 1.108 christos int waitfor; 1010 1.108 christos }; 1011 1.108 christos 1012 1.108 christos static bool 1013 1.108 christos msdosfs_sync_selector(void *cl, struct vnode *vp) 1014 1.108 christos { 1015 1.108 christos struct msdosfs_sync_ctx *c = cl; 1016 1.108 christos struct denode *dep; 1017 1.108 christos 1018 1.125 riastrad KASSERT(mutex_owned(vp->v_interlock)); 1019 1.125 riastrad 1020 1.108 christos dep = VTODE(vp); 1021 1.108 christos if (c->waitfor == MNT_LAZY || vp->v_type == VNON || 1022 1.108 christos dep == NULL || (((dep->de_flag & 1023 1.108 christos (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) && 1024 1.108 christos (LIST_EMPTY(&vp->v_dirtyblkhd) && 1025 1.132 ad (vp->v_iflag & VI_ONWORKLST) == 0))) 1026 1.108 christos return false; 1027 1.108 christos return true; 1028 1.108 christos } 1029 1.108 christos 1030 1.1 jdolecek int 1031 1.72 dsl msdosfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 1032 1.1 jdolecek { 1033 1.105 hannken struct vnode *vp; 1034 1.105 hannken struct vnode_iterator *marker; 1035 1.1 jdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1036 1.89 hannken int error, allerror = 0; 1037 1.108 christos struct msdosfs_sync_ctx ctx; 1038 1.1 jdolecek 1039 1.1 jdolecek /* 1040 1.100 jakllsch * If we ever switch to not updating all of the FATs all the time, 1041 1.1 jdolecek * this would be the place to update them from the first one. 1042 1.1 jdolecek */ 1043 1.1 jdolecek if (pmp->pm_fmod != 0) { 1044 1.1 jdolecek if (pmp->pm_flags & MSDOSFSMNT_RONLY) 1045 1.1 jdolecek panic("msdosfs_sync: rofs mod"); 1046 1.1 jdolecek else { 1047 1.100 jakllsch /* update FATs here */ 1048 1.1 jdolecek } 1049 1.1 jdolecek } 1050 1.1 jdolecek /* 1051 1.1 jdolecek * Write back each (modified) denode. 1052 1.1 jdolecek */ 1053 1.105 hannken vfs_vnode_iterator_init(mp, &marker); 1054 1.108 christos ctx.waitfor = waitfor; 1055 1.108 christos while ((vp = vfs_vnode_iterator_next(marker, msdosfs_sync_selector, 1056 1.108 christos &ctx))) 1057 1.108 christos { 1058 1.105 hannken error = vn_lock(vp, LK_EXCLUSIVE); 1059 1.105 hannken if (error) { 1060 1.105 hannken vrele(vp); 1061 1.56 ad continue; 1062 1.105 hannken } 1063 1.1 jdolecek if ((error = VOP_FSYNC(vp, cred, 1064 1.54 pooka waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0) 1065 1.1 jdolecek allerror = error; 1066 1.89 hannken vput(vp); 1067 1.1 jdolecek } 1068 1.105 hannken vfs_vnode_iterator_destroy(marker); 1069 1.56 ad 1070 1.1 jdolecek /* 1071 1.1 jdolecek * Force stale file system control information to be flushed. 1072 1.1 jdolecek */ 1073 1.120 hannken vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); 1074 1.1 jdolecek if ((error = VOP_FSYNC(pmp->pm_devvp, cred, 1075 1.54 pooka waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0) 1076 1.1 jdolecek allerror = error; 1077 1.120 hannken VOP_UNLOCK(pmp->pm_devvp); 1078 1.1 jdolecek return (allerror); 1079 1.1 jdolecek } 1080 1.1 jdolecek 1081 1.1 jdolecek int 1082 1.131 ad msdosfs_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp) 1083 1.1 jdolecek { 1084 1.1 jdolecek struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 1085 1.32 martin struct defid defh; 1086 1.90 hannken uint32_t gen; 1087 1.1 jdolecek int error; 1088 1.1 jdolecek 1089 1.42 christos if (fhp->fid_len != sizeof(struct defid)) { 1090 1.115 maxv DPRINTF("fid_len %d %zd", fhp->fid_len, sizeof(struct defid)); 1091 1.32 martin return EINVAL; 1092 1.42 christos } 1093 1.32 martin memcpy(&defh, fhp, sizeof(defh)); 1094 1.90 hannken error = msdosfs_fh_lookup(pmp, defh.defid_dirclust, defh.defid_dirofs, 1095 1.90 hannken &gen); 1096 1.90 hannken if (error == 0 && gen != defh.defid_gen) 1097 1.90 hannken error = ESTALE; 1098 1.90 hannken if (error) { 1099 1.90 hannken *vpp = NULLVP; 1100 1.90 hannken return error; 1101 1.90 hannken } 1102 1.137 thorpej error = msdosfs_deget(pmp, defh.defid_dirclust, defh.defid_dirofs, vpp); 1103 1.1 jdolecek if (error) { 1104 1.115 maxv DPRINTF("deget %d", error); 1105 1.1 jdolecek *vpp = NULLVP; 1106 1.109 hannken return error; 1107 1.109 hannken } 1108 1.131 ad error = vn_lock(*vpp, lktype); 1109 1.109 hannken if (error) { 1110 1.109 hannken vrele(*vpp); 1111 1.109 hannken *vpp = NULLVP; 1112 1.109 hannken return error; 1113 1.1 jdolecek } 1114 1.109 hannken return 0; 1115 1.1 jdolecek } 1116 1.1 jdolecek 1117 1.1 jdolecek int 1118 1.72 dsl msdosfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 1119 1.1 jdolecek { 1120 1.90 hannken struct msdosfsmount *pmp = VFSTOMSDOSFS(vp->v_mount); 1121 1.1 jdolecek struct denode *dep; 1122 1.32 martin struct defid defh; 1123 1.90 hannken int error; 1124 1.1 jdolecek 1125 1.32 martin if (*fh_size < sizeof(struct defid)) { 1126 1.32 martin *fh_size = sizeof(struct defid); 1127 1.32 martin return E2BIG; 1128 1.32 martin } 1129 1.32 martin *fh_size = sizeof(struct defid); 1130 1.1 jdolecek dep = VTODE(vp); 1131 1.32 martin memset(&defh, 0, sizeof(defh)); 1132 1.32 martin defh.defid_len = sizeof(struct defid); 1133 1.32 martin defh.defid_dirclust = dep->de_dirclust; 1134 1.32 martin defh.defid_dirofs = dep->de_diroffset; 1135 1.90 hannken error = msdosfs_fh_enter(pmp, dep->de_dirclust, dep->de_diroffset, 1136 1.90 hannken &defh.defid_gen); 1137 1.90 hannken if (error == 0) 1138 1.90 hannken memcpy(fhp, &defh, sizeof(defh)); 1139 1.90 hannken return error; 1140 1.1 jdolecek } 1141 1.1 jdolecek 1142 1.1 jdolecek int 1143 1.131 ad msdosfs_vget(struct mount *mp, ino_t ino, int lktype, 1144 1.39 christos struct vnode **vpp) 1145 1.1 jdolecek { 1146 1.1 jdolecek 1147 1.1 jdolecek return (EOPNOTSUPP); 1148 1.1 jdolecek } 1149