| 1 | /* $FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/sys/msdosfs/Attic/msdosfs_vfsops.c,v 1.60.2.8 2004/03/02 09:43:04 tjr Exp $ */ |
| 2 | /* $DragonFly: src/sys/vfs/msdosfs/msdosfs_vfsops.c,v 1.20 2004/10/12 19:21:00 dillon Exp $ */ |
| 3 | /* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ |
| 4 | |
| 5 | /*- |
| 6 | * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. |
| 7 | * Copyright (C) 1994, 1995, 1997 TooLs GmbH. |
| 8 | * All rights reserved. |
| 9 | * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). |
| 10 | * |
| 11 | * Redistribution and use in source and binary forms, with or without |
| 12 | * modification, are permitted provided that the following conditions |
| 13 | * are met: |
| 14 | * 1. Redistributions of source code must retain the above copyright |
| 15 | * notice, this list of conditions and the following disclaimer. |
| 16 | * 2. Redistributions in binary form must reproduce the above copyright |
| 17 | * notice, this list of conditions and the following disclaimer in the |
| 18 | * documentation and/or other materials provided with the distribution. |
| 19 | * 3. All advertising materials mentioning features or use of this software |
| 20 | * must display the following acknowledgement: |
| 21 | * This product includes software developed by TooLs GmbH. |
| 22 | * 4. The name of TooLs GmbH may not be used to endorse or promote products |
| 23 | * derived from this software without specific prior written permission. |
| 24 | * |
| 25 | * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR |
| 26 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 27 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 28 | * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 29 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 30 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 31 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 33 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 34 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 35 | */ |
| 36 | /* |
| 37 | * Written by Paul Popelka (paulp@uts.amdahl.com) |
| 38 | * |
| 39 | * You can do anything you want with this software, just don't say you wrote |
| 40 | * it, and don't remove this notice. |
| 41 | * |
| 42 | * This software is provided "as is". |
| 43 | * |
| 44 | * The author supplies this software to be publicly redistributed on the |
| 45 | * understanding that the author is not responsible for the correct |
| 46 | * functioning of this software in any circumstances and is not liable for |
| 47 | * any damages caused by this software. |
| 48 | * |
| 49 | * October 1992 |
| 50 | */ |
| 51 | |
| 52 | #include <sys/param.h> |
| 53 | #include <sys/systm.h> |
| 54 | #include <sys/conf.h> |
| 55 | #include <sys/proc.h> |
| 56 | #include <sys/namei.h> |
| 57 | #include <sys/kernel.h> |
| 58 | #include <sys/vnode.h> |
| 59 | #include <sys/mount.h> |
| 60 | #include <sys/buf.h> |
| 61 | #include <sys/fcntl.h> |
| 62 | #include <sys/malloc.h> |
| 63 | #include <sys/stat.h> /* defines ALLPERMS */ |
| 64 | #include <vm/vm_zone.h> |
| 65 | |
| 66 | #include "bpb.h" |
| 67 | #include "bootsect.h" |
| 68 | #include "direntry.h" |
| 69 | #include "denode.h" |
| 70 | #include "msdosfsmount.h" |
| 71 | #include "fat.h" |
| 72 | |
| 73 | extern struct vnodeopv_entry_desc msdosfs_vnodeop_entries[]; |
| 74 | |
| 75 | #define MSDOSFS_DFLTBSIZE 4096 |
| 76 | |
| 77 | #if 1 /*def PC98*/ |
| 78 | /* |
| 79 | * XXX - The boot signature formatted by NEC PC-98 DOS looks like a |
| 80 | * garbage or a random value :-{ |
| 81 | * If you want to use that broken-signatured media, define the |
| 82 | * following symbol even though PC/AT. |
| 83 | * (ex. mount PC-98 DOS formatted FD on PC/AT) |
| 84 | */ |
| 85 | #define MSDOSFS_NOCHECKSIG |
| 86 | #endif |
| 87 | |
| 88 | MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); |
| 89 | static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); |
| 90 | |
| 91 | static int update_mp (struct mount *mp, struct msdosfs_args *argp); |
| 92 | static int mountmsdosfs (struct vnode *devvp, struct mount *mp, |
| 93 | struct thread *td, struct msdosfs_args *argp); |
| 94 | static int msdosfs_fhtovp (struct mount *, struct fid *, |
| 95 | struct vnode **); |
| 96 | static int msdosfs_checkexp (struct mount *, struct sockaddr *, |
| 97 | int *, struct ucred **); |
| 98 | static int msdosfs_mount (struct mount *, char *, caddr_t, |
| 99 | struct thread *); |
| 100 | static int msdosfs_root (struct mount *, struct vnode **); |
| 101 | static int msdosfs_statfs (struct mount *, struct statfs *, |
| 102 | struct thread *); |
| 103 | static int msdosfs_sync (struct mount *, int, struct thread *); |
| 104 | static int msdosfs_unmount (struct mount *, int, struct thread *); |
| 105 | static int msdosfs_vptofh (struct vnode *, struct fid *); |
| 106 | |
| 107 | static int |
| 108 | update_mp(struct mount *mp, struct msdosfs_args *argp) |
| 109 | { |
| 110 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 111 | int error; |
| 112 | |
| 113 | pmp->pm_gid = argp->gid; |
| 114 | pmp->pm_uid = argp->uid; |
| 115 | pmp->pm_mask = argp->mask & ALLPERMS; |
| 116 | pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; |
| 117 | if (pmp->pm_flags & MSDOSFSMNT_U2WTABLE) { |
| 118 | bcopy(argp->u2w, pmp->pm_u2w, sizeof(pmp->pm_u2w)); |
| 119 | bcopy(argp->d2u, pmp->pm_d2u, sizeof(pmp->pm_d2u)); |
| 120 | bcopy(argp->u2d, pmp->pm_u2d, sizeof(pmp->pm_u2d)); |
| 121 | } |
| 122 | if (pmp->pm_flags & MSDOSFSMNT_ULTABLE) { |
| 123 | bcopy(argp->ul, pmp->pm_ul, sizeof(pmp->pm_ul)); |
| 124 | bcopy(argp->lu, pmp->pm_lu, sizeof(pmp->pm_lu)); |
| 125 | } |
| 126 | |
| 127 | #ifndef __DragonFly__ |
| 128 | /* |
| 129 | * GEMDOS knows nothing (yet) about win95 |
| 130 | */ |
| 131 | if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) |
| 132 | pmp->pm_flags |= MSDOSFSMNT_NOWIN95; |
| 133 | #endif |
| 134 | |
| 135 | if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) |
| 136 | pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; |
| 137 | else if (!(pmp->pm_flags & |
| 138 | (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { |
| 139 | struct vnode *rootvp; |
| 140 | |
| 141 | /* |
| 142 | * Try to divine whether to support Win'95 long filenames |
| 143 | */ |
| 144 | if (FAT32(pmp)) |
| 145 | pmp->pm_flags |= MSDOSFSMNT_LONGNAME; |
| 146 | else { |
| 147 | if ((error = msdosfs_root(mp, &rootvp)) != 0) |
| 148 | return error; |
| 149 | pmp->pm_flags |= findwin95(VTODE(rootvp)) |
| 150 | ? MSDOSFSMNT_LONGNAME |
| 151 | : MSDOSFSMNT_SHORTNAME; |
| 152 | vput(rootvp); |
| 153 | } |
| 154 | } |
| 155 | return 0; |
| 156 | } |
| 157 | |
| 158 | #ifndef __DragonFly__ |
| 159 | int |
| 160 | msdosfs_mountroot(void) |
| 161 | { |
| 162 | struct mount *mp; |
| 163 | struct thread *td = curthread; /* XXX */ |
| 164 | size_t size; |
| 165 | int error; |
| 166 | struct msdosfs_args args; |
| 167 | struct vnode *rootvp; |
| 168 | |
| 169 | if (root_device->dv_class != DV_DISK) |
| 170 | return (ENODEV); |
| 171 | |
| 172 | /* |
| 173 | * Get vnodes for swapdev and rootdev. |
| 174 | */ |
| 175 | if (bdevvp(rootdev, &rootvp)) |
| 176 | panic("msdosfs_mountroot: can't setup rootvp"); |
| 177 | |
| 178 | mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); |
| 179 | bzero((char *)mp, (u_long)sizeof(struct mount)); |
| 180 | mp->mnt_op = &msdosfs_vfsops; |
| 181 | mp->mnt_flag = 0; |
| 182 | TAILQ_INIT(&mp->mnt_nvnodelist); |
| 183 | TAILQ_INIT(&mp->mnt_reservedvnlist); |
| 184 | |
| 185 | args.flags = 0; |
| 186 | args.uid = 0; |
| 187 | args.gid = 0; |
| 188 | args.mask = 0777; |
| 189 | |
| 190 | if ((error = mountmsdosfs(rootvp, mp, p, &args)) != 0) { |
| 191 | free(mp, M_MOUNT); |
| 192 | return (error); |
| 193 | } |
| 194 | |
| 195 | if ((error = update_mp(mp, &args)) != 0) { |
| 196 | (void)msdosfs_unmount(mp, 0, p); |
| 197 | free(mp, M_MOUNT); |
| 198 | return (error); |
| 199 | } |
| 200 | |
| 201 | if ((error = vfs_lock(mp)) != 0) { |
| 202 | (void)msdosfs_unmount(mp, 0, p); |
| 203 | free(mp, M_MOUNT); |
| 204 | return (error); |
| 205 | } |
| 206 | |
| 207 | TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); |
| 208 | mp->mnt_vnodecovered = NULLVP; |
| 209 | (void) copystr("/", mp->mnt_stat.f_mntonname, MNAMELEN - 1, |
| 210 | &size); |
| 211 | bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); |
| 212 | (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, |
| 213 | &size); |
| 214 | bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); |
| 215 | |
| 216 | (void)msdosfs_statfs(mp, &mp->mnt_stat, p); |
| 217 | vfs_unlock(mp); |
| 218 | return (0); |
| 219 | } |
| 220 | #endif |
| 221 | |
| 222 | /* |
| 223 | * mp - path - addr in user space of mount point (ie /usr or whatever) |
| 224 | * data - addr in user space of mount params including the name of the block |
| 225 | * special file to treat as a filesystem. |
| 226 | */ |
| 227 | static int |
| 228 | msdosfs_mount(struct mount *mp, char *path, caddr_t data, struct thread *td) |
| 229 | { |
| 230 | struct vnode *devvp; /* vnode for blk device to mount */ |
| 231 | struct msdosfs_args args; /* will hold data from mount request */ |
| 232 | /* msdosfs specific mount control block */ |
| 233 | struct msdosfsmount *pmp = NULL; |
| 234 | size_t size; |
| 235 | int error, flags; |
| 236 | mode_t accessmode; |
| 237 | struct proc *p = td->td_proc; |
| 238 | struct nameidata nd; |
| 239 | |
| 240 | KKASSERT(p); |
| 241 | |
| 242 | error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); |
| 243 | if (error) |
| 244 | return (error); |
| 245 | if (args.magic != MSDOSFS_ARGSMAGIC) |
| 246 | args.flags = 0; |
| 247 | /* |
| 248 | * If updating, check whether changing from read-only to |
| 249 | * read/write; if there is no device name, that's all we do. |
| 250 | */ |
| 251 | if (mp->mnt_flag & MNT_UPDATE) { |
| 252 | pmp = VFSTOMSDOSFS(mp); |
| 253 | error = 0; |
| 254 | if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { |
| 255 | flags = WRITECLOSE; |
| 256 | if (mp->mnt_flag & MNT_FORCE) |
| 257 | flags |= FORCECLOSE; |
| 258 | error = vflush(mp, 0, flags); |
| 259 | } |
| 260 | if (!error && (mp->mnt_flag & MNT_RELOAD)) |
| 261 | /* not yet implemented */ |
| 262 | error = EOPNOTSUPP; |
| 263 | if (error) |
| 264 | return (error); |
| 265 | if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { |
| 266 | /* |
| 267 | * If upgrade to read-write by non-root, then verify |
| 268 | * that user has necessary permissions on the device. |
| 269 | */ |
| 270 | if (p->p_ucred->cr_uid != 0) { |
| 271 | devvp = pmp->pm_devvp; |
| 272 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 273 | error = VOP_ACCESS(devvp, VREAD | VWRITE, |
| 274 | p->p_ucred, td); |
| 275 | if (error) { |
| 276 | VOP_UNLOCK(devvp, 0, td); |
| 277 | return (error); |
| 278 | } |
| 279 | VOP_UNLOCK(devvp, 0, td); |
| 280 | } |
| 281 | pmp->pm_flags &= ~MSDOSFSMNT_RONLY; |
| 282 | } |
| 283 | if (args.fspec == 0) { |
| 284 | #ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ |
| 285 | if (args.flags & MSDOSFSMNT_MNTOPT) { |
| 286 | pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; |
| 287 | pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; |
| 288 | if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) |
| 289 | pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; |
| 290 | } |
| 291 | #endif |
| 292 | /* |
| 293 | * Process export requests. |
| 294 | */ |
| 295 | return (vfs_export(mp, &pmp->pm_export, &args.export)); |
| 296 | } |
| 297 | } |
| 298 | /* |
| 299 | * Not an update, or updating the name: look up the name |
| 300 | * and verify that it refers to a sensible block device. |
| 301 | */ |
| 302 | NDINIT(&nd, NAMEI_LOOKUP, CNP_FOLLOW, UIO_USERSPACE, args.fspec, td); |
| 303 | error = namei(&nd); |
| 304 | if (error) |
| 305 | return (error); |
| 306 | devvp = nd.ni_vp; |
| 307 | NDFREE(&nd, NDF_ONLY_PNBUF); |
| 308 | |
| 309 | if (!vn_isdisk(devvp, &error)) { |
| 310 | vrele(devvp); |
| 311 | return (error); |
| 312 | } |
| 313 | /* |
| 314 | * If mount by non-root, then verify that user has necessary |
| 315 | * permissions on the device. |
| 316 | */ |
| 317 | if (p->p_ucred->cr_uid != 0) { |
| 318 | accessmode = VREAD; |
| 319 | if ((mp->mnt_flag & MNT_RDONLY) == 0) |
| 320 | accessmode |= VWRITE; |
| 321 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 322 | error = VOP_ACCESS(devvp, accessmode, p->p_ucred, td); |
| 323 | if (error) { |
| 324 | vput(devvp); |
| 325 | return (error); |
| 326 | } |
| 327 | VOP_UNLOCK(devvp, 0, td); |
| 328 | } |
| 329 | if ((mp->mnt_flag & MNT_UPDATE) == 0) { |
| 330 | error = mountmsdosfs(devvp, mp, td, &args); |
| 331 | #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ |
| 332 | pmp = VFSTOMSDOSFS(mp); |
| 333 | #endif |
| 334 | } else { |
| 335 | if (devvp != pmp->pm_devvp) |
| 336 | error = EINVAL; /* XXX needs translation */ |
| 337 | else |
| 338 | vrele(devvp); |
| 339 | } |
| 340 | if (error) { |
| 341 | vrele(devvp); |
| 342 | return (error); |
| 343 | } |
| 344 | |
| 345 | error = update_mp(mp, &args); |
| 346 | if (error) { |
| 347 | msdosfs_unmount(mp, MNT_FORCE, td); |
| 348 | return error; |
| 349 | } |
| 350 | |
| 351 | (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); |
| 352 | bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); |
| 353 | (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, |
| 354 | &size); |
| 355 | bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); |
| 356 | (void) msdosfs_statfs(mp, &mp->mnt_stat, td); |
| 357 | #ifdef MSDOSFS_DEBUG |
| 358 | printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); |
| 359 | #endif |
| 360 | return (0); |
| 361 | } |
| 362 | |
| 363 | static int |
| 364 | mountmsdosfs(struct vnode *devvp, struct mount *mp, struct thread *td, |
| 365 | struct msdosfs_args *argp) |
| 366 | { |
| 367 | struct msdosfsmount *pmp; |
| 368 | struct buf *bp; |
| 369 | dev_t dev; |
| 370 | #ifndef __DragonFly__ |
| 371 | struct partinfo dpart; |
| 372 | int bsize = 0, dtype = 0, tmp; |
| 373 | #endif |
| 374 | union bootsector *bsp; |
| 375 | struct byte_bpb33 *b33; |
| 376 | struct byte_bpb50 *b50; |
| 377 | struct byte_bpb710 *b710; |
| 378 | u_int8_t SecPerClust; |
| 379 | u_long clusters; |
| 380 | int ronly, error; |
| 381 | |
| 382 | /* |
| 383 | * Disallow multiple mounts of the same device. |
| 384 | * Disallow mounting of a device that is currently in use |
| 385 | * Flush out any old buffers remaining from a previous use. |
| 386 | */ |
| 387 | error = vfs_mountedon(devvp); |
| 388 | if (error) |
| 389 | return (error); |
| 390 | if (count_udev(devvp->v_udev) > 0) |
| 391 | return (EBUSY); |
| 392 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 393 | error = vinvalbuf(devvp, V_SAVE, td, 0, 0); |
| 394 | VOP_UNLOCK(devvp, 0, td); |
| 395 | if (error) |
| 396 | return (error); |
| 397 | |
| 398 | ronly = (mp->mnt_flag & MNT_RDONLY) != 0; |
| 399 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 400 | error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, td); |
| 401 | VOP_UNLOCK(devvp, 0, td); |
| 402 | if (error) |
| 403 | return (error); |
| 404 | dev = devvp->v_rdev; |
| 405 | bp = NULL; /* both used in error_exit */ |
| 406 | pmp = NULL; |
| 407 | |
| 408 | #ifndef __DragonFly__ |
| 409 | if (argp->flags & MSDOSFSMNT_GEMDOSFS) { |
| 410 | /* |
| 411 | * We need the disklabel to calculate the size of a FAT entry |
| 412 | * later on. Also make sure the partition contains a filesystem |
| 413 | * of type FS_MSDOS. This doesn't work for floppies, so we have |
| 414 | * to check for them too. |
| 415 | * |
| 416 | * At least some parts of the msdos fs driver seem to assume |
| 417 | * that the size of a disk block will always be 512 bytes. |
| 418 | * Let's check it... |
| 419 | */ |
| 420 | error = VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, td); |
| 421 | if (error) |
| 422 | goto error_exit; |
| 423 | tmp = dpart.part->p_fstype; |
| 424 | dtype = dpart.disklab->d_type; |
| 425 | bsize = dpart.disklab->d_secsize; |
| 426 | if (bsize != 512 || (dtype!=DTYPE_FLOPPY && tmp!=FS_MSDOS)) { |
| 427 | error = EINVAL; |
| 428 | goto error_exit; |
| 429 | } |
| 430 | } |
| 431 | #endif |
| 432 | |
| 433 | /* |
| 434 | * Read the boot sector of the filesystem, and then check the |
| 435 | * boot signature. If not a dos boot sector then error out. |
| 436 | * |
| 437 | * NOTE: 2048 is a maximum sector size in current... |
| 438 | */ |
| 439 | error = bread(devvp, 0, 2048, &bp); |
| 440 | if (error) |
| 441 | goto error_exit; |
| 442 | bp->b_flags |= B_AGE; |
| 443 | bsp = (union bootsector *)bp->b_data; |
| 444 | b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; |
| 445 | b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; |
| 446 | b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; |
| 447 | |
| 448 | #ifndef __DragonFly__ |
| 449 | if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { |
| 450 | #endif |
| 451 | #ifndef MSDOSFS_NOCHECKSIG |
| 452 | if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 |
| 453 | || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { |
| 454 | error = EINVAL; |
| 455 | goto error_exit; |
| 456 | } |
| 457 | #endif |
| 458 | #ifndef __DragonFly__ |
| 459 | } |
| 460 | #endif |
| 461 | |
| 462 | pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); |
| 463 | bzero((caddr_t)pmp, sizeof *pmp); |
| 464 | pmp->pm_mountp = mp; |
| 465 | |
| 466 | /* |
| 467 | * Compute several useful quantities from the bpb in the |
| 468 | * bootsector. Copy in the dos 5 variant of the bpb then fix up |
| 469 | * the fields that are different between dos 5 and dos 3.3. |
| 470 | */ |
| 471 | SecPerClust = b50->bpbSecPerClust; |
| 472 | pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); |
| 473 | pmp->pm_ResSectors = getushort(b50->bpbResSectors); |
| 474 | pmp->pm_FATs = b50->bpbFATs; |
| 475 | pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); |
| 476 | pmp->pm_Sectors = getushort(b50->bpbSectors); |
| 477 | pmp->pm_FATsecs = getushort(b50->bpbFATsecs); |
| 478 | pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); |
| 479 | pmp->pm_Heads = getushort(b50->bpbHeads); |
| 480 | pmp->pm_Media = b50->bpbMedia; |
| 481 | |
| 482 | /* calculate the ratio of sector size to DEV_BSIZE */ |
| 483 | pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; |
| 484 | |
| 485 | #ifndef __DragonFly__ |
| 486 | if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { |
| 487 | #endif |
| 488 | /* XXX - We should probably check more values here */ |
| 489 | if (!pmp->pm_BytesPerSec || !SecPerClust |
| 490 | || !pmp->pm_Heads |
| 491 | #ifdef PC98 |
| 492 | || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { |
| 493 | #else |
| 494 | || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { |
| 495 | #endif |
| 496 | error = EINVAL; |
| 497 | goto error_exit; |
| 498 | } |
| 499 | #ifndef __DragonFly__ |
| 500 | } |
| 501 | #endif |
| 502 | |
| 503 | if (pmp->pm_Sectors == 0) { |
| 504 | pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); |
| 505 | pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); |
| 506 | } else { |
| 507 | pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); |
| 508 | pmp->pm_HugeSectors = pmp->pm_Sectors; |
| 509 | } |
| 510 | if (pmp->pm_HugeSectors > 0xffffffff / |
| 511 | (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { |
| 512 | /* |
| 513 | * We cannot deal currently with this size of disk |
| 514 | * due to fileid limitations (see msdosfs_getattr and |
| 515 | * msdosfs_readdir) |
| 516 | */ |
| 517 | error = EINVAL; |
| 518 | printf("mountmsdosfs(): disk too big, sorry\n"); |
| 519 | goto error_exit; |
| 520 | } |
| 521 | |
| 522 | if (pmp->pm_RootDirEnts == 0) { |
| 523 | if (bsp->bs710.bsBootSectSig2 != BOOTSIG2 |
| 524 | || bsp->bs710.bsBootSectSig3 != BOOTSIG3 |
| 525 | || pmp->pm_Sectors |
| 526 | || pmp->pm_FATsecs |
| 527 | || getushort(b710->bpbFSVers)) { |
| 528 | error = EINVAL; |
| 529 | printf("mountmsdosfs(): bad FAT32 filesystem\n"); |
| 530 | goto error_exit; |
| 531 | } |
| 532 | pmp->pm_fatmask = FAT32_MASK; |
| 533 | pmp->pm_fatmult = 4; |
| 534 | pmp->pm_fatdiv = 1; |
| 535 | pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); |
| 536 | if (getushort(b710->bpbExtFlags) & FATMIRROR) |
| 537 | pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; |
| 538 | else |
| 539 | pmp->pm_flags |= MSDOSFS_FATMIRROR; |
| 540 | } else |
| 541 | pmp->pm_flags |= MSDOSFS_FATMIRROR; |
| 542 | |
| 543 | /* |
| 544 | * Check a few values (could do some more): |
| 545 | * - logical sector size: power of 2, >= block size |
| 546 | * - sectors per cluster: power of 2, >= 1 |
| 547 | * - number of sectors: >= 1, <= size of partition |
| 548 | */ |
| 549 | if ( (SecPerClust == 0) |
| 550 | || (SecPerClust & (SecPerClust - 1)) |
| 551 | || (pmp->pm_BytesPerSec < DEV_BSIZE) |
| 552 | || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) |
| 553 | || (pmp->pm_HugeSectors == 0) |
| 554 | ) { |
| 555 | error = EINVAL; |
| 556 | goto error_exit; |
| 557 | } |
| 558 | |
| 559 | pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; |
| 560 | pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ |
| 561 | pmp->pm_FATsecs *= pmp->pm_BlkPerSec; |
| 562 | SecPerClust *= pmp->pm_BlkPerSec; |
| 563 | |
| 564 | pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; |
| 565 | |
| 566 | if (FAT32(pmp)) { |
| 567 | pmp->pm_rootdirblk = getulong(b710->bpbRootClust); |
| 568 | pmp->pm_firstcluster = pmp->pm_fatblk |
| 569 | + (pmp->pm_FATs * pmp->pm_FATsecs); |
| 570 | pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; |
| 571 | } else { |
| 572 | pmp->pm_rootdirblk = pmp->pm_fatblk + |
| 573 | (pmp->pm_FATs * pmp->pm_FATsecs); |
| 574 | pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) |
| 575 | + DEV_BSIZE - 1) |
| 576 | / DEV_BSIZE; /* in blocks */ |
| 577 | pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; |
| 578 | } |
| 579 | |
| 580 | pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / |
| 581 | SecPerClust + 1; |
| 582 | pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ |
| 583 | |
| 584 | #ifndef __DragonFly__ |
| 585 | if (argp->flags & MSDOSFSMNT_GEMDOSFS) { |
| 586 | if ((pmp->pm_maxcluster <= (0xff0 - 2)) |
| 587 | && ((dtype == DTYPE_FLOPPY) || ((dtype == DTYPE_VNODE) |
| 588 | && ((pmp->pm_Heads == 1) || (pmp->pm_Heads == 2)))) |
| 589 | ) { |
| 590 | pmp->pm_fatmask = FAT12_MASK; |
| 591 | pmp->pm_fatmult = 3; |
| 592 | pmp->pm_fatdiv = 2; |
| 593 | } else { |
| 594 | pmp->pm_fatmask = FAT16_MASK; |
| 595 | pmp->pm_fatmult = 2; |
| 596 | pmp->pm_fatdiv = 1; |
| 597 | } |
| 598 | } else |
| 599 | #endif |
| 600 | if (pmp->pm_fatmask == 0) { |
| 601 | if (pmp->pm_maxcluster |
| 602 | <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { |
| 603 | /* |
| 604 | * This will usually be a floppy disk. This size makes |
| 605 | * sure that one fat entry will not be split across |
| 606 | * multiple blocks. |
| 607 | */ |
| 608 | pmp->pm_fatmask = FAT12_MASK; |
| 609 | pmp->pm_fatmult = 3; |
| 610 | pmp->pm_fatdiv = 2; |
| 611 | } else { |
| 612 | pmp->pm_fatmask = FAT16_MASK; |
| 613 | pmp->pm_fatmult = 2; |
| 614 | pmp->pm_fatdiv = 1; |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; |
| 619 | if (pmp->pm_maxcluster >= clusters) { |
| 620 | printf("Warning: number of clusters (%ld) exceeds FAT " |
| 621 | "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); |
| 622 | pmp->pm_maxcluster = clusters - 1; |
| 623 | } |
| 624 | |
| 625 | |
| 626 | if (FAT12(pmp)) |
| 627 | pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; |
| 628 | else |
| 629 | pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE; |
| 630 | |
| 631 | pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; |
| 632 | pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; |
| 633 | |
| 634 | /* |
| 635 | * Compute mask and shift value for isolating cluster relative byte |
| 636 | * offsets and cluster numbers from a file offset. |
| 637 | */ |
| 638 | pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; |
| 639 | pmp->pm_crbomask = pmp->pm_bpcluster - 1; |
| 640 | pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; |
| 641 | |
| 642 | /* |
| 643 | * Check for valid cluster size |
| 644 | * must be a power of 2 |
| 645 | */ |
| 646 | if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { |
| 647 | error = EINVAL; |
| 648 | goto error_exit; |
| 649 | } |
| 650 | |
| 651 | /* |
| 652 | * Release the bootsector buffer. |
| 653 | */ |
| 654 | brelse(bp); |
| 655 | bp = NULL; |
| 656 | |
| 657 | /* |
| 658 | * Check FSInfo. |
| 659 | */ |
| 660 | if (pmp->pm_fsinfo) { |
| 661 | struct fsinfo *fp; |
| 662 | |
| 663 | if ((error = bread(devvp, pmp->pm_fsinfo, fsi_size(pmp), &bp)) != 0) |
| 664 | goto error_exit; |
| 665 | fp = (struct fsinfo *)bp->b_data; |
| 666 | if (!bcmp(fp->fsisig1, "RRaA", 4) |
| 667 | && !bcmp(fp->fsisig2, "rrAa", 4) |
| 668 | && !bcmp(fp->fsisig3, "\0\0\125\252", 4) |
| 669 | && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) { |
| 670 | pmp->pm_nxtfree = getulong(fp->fsinxtfree); |
| 671 | if (pmp->pm_nxtfree == 0xffffffff) |
| 672 | pmp->pm_nxtfree = CLUST_FIRST; |
| 673 | } else |
| 674 | pmp->pm_fsinfo = 0; |
| 675 | brelse(bp); |
| 676 | bp = NULL; |
| 677 | } |
| 678 | |
| 679 | /* |
| 680 | * Check and validate (or perhaps invalidate?) the fsinfo structure? |
| 681 | */ |
| 682 | if (pmp->pm_fsinfo && pmp->pm_nxtfree > pmp->pm_maxcluster) { |
| 683 | printf( |
| 684 | "Next free cluster in FSInfo (%lu) exceeds maxcluster (%lu)\n", |
| 685 | pmp->pm_nxtfree, pmp->pm_maxcluster); |
| 686 | error = EINVAL; |
| 687 | goto error_exit; |
| 688 | } |
| 689 | |
| 690 | /* |
| 691 | * Allocate memory for the bitmap of allocated clusters, and then |
| 692 | * fill it in. |
| 693 | */ |
| 694 | pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) |
| 695 | / N_INUSEBITS) |
| 696 | * sizeof(*pmp->pm_inusemap), |
| 697 | M_MSDOSFSFAT, M_WAITOK); |
| 698 | |
| 699 | /* |
| 700 | * fillinusemap() needs pm_devvp. |
| 701 | */ |
| 702 | pmp->pm_dev = dev; |
| 703 | pmp->pm_devvp = devvp; |
| 704 | |
| 705 | /* |
| 706 | * Have the inuse map filled in. |
| 707 | */ |
| 708 | if ((error = fillinusemap(pmp)) != 0) |
| 709 | goto error_exit; |
| 710 | |
| 711 | /* |
| 712 | * If they want fat updates to be synchronous then let them suffer |
| 713 | * the performance degradation in exchange for the on disk copy of |
| 714 | * the fat being correct just about all the time. I suppose this |
| 715 | * would be a good thing to turn on if the kernel is still flakey. |
| 716 | */ |
| 717 | if (mp->mnt_flag & MNT_SYNCHRONOUS) |
| 718 | pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; |
| 719 | |
| 720 | /* |
| 721 | * Finish up. |
| 722 | */ |
| 723 | if (ronly) |
| 724 | pmp->pm_flags |= MSDOSFSMNT_RONLY; |
| 725 | else |
| 726 | pmp->pm_fmod = 1; |
| 727 | mp->mnt_data = (qaddr_t) pmp; |
| 728 | mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); |
| 729 | mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; |
| 730 | mp->mnt_flag |= MNT_LOCAL; |
| 731 | vfs_add_vnodeops(&mp->mnt_vn_ops, msdosfs_vnodeop_entries); |
| 732 | dev->si_mountpoint = mp; |
| 733 | |
| 734 | return 0; |
| 735 | |
| 736 | error_exit: |
| 737 | if (bp) |
| 738 | brelse(bp); |
| 739 | (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, td); |
| 740 | if (pmp) { |
| 741 | if (pmp->pm_inusemap) |
| 742 | free(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 743 | free(pmp, M_MSDOSFSMNT); |
| 744 | mp->mnt_data = (qaddr_t)0; |
| 745 | } |
| 746 | return (error); |
| 747 | } |
| 748 | |
| 749 | /* |
| 750 | * Unmount the filesystem described by mp. |
| 751 | */ |
| 752 | static int |
| 753 | msdosfs_unmount(struct mount *mp, int mntflags, struct thread *td) |
| 754 | { |
| 755 | struct msdosfsmount *pmp; |
| 756 | int error, flags; |
| 757 | |
| 758 | flags = 0; |
| 759 | if (mntflags & MNT_FORCE) |
| 760 | flags |= FORCECLOSE; |
| 761 | error = vflush(mp, 0, flags); |
| 762 | if (error) |
| 763 | return error; |
| 764 | pmp = VFSTOMSDOSFS(mp); |
| 765 | pmp->pm_devvp->v_rdev->si_mountpoint = NULL; |
| 766 | #ifdef MSDOSFS_DEBUG |
| 767 | { |
| 768 | struct vnode *vp = pmp->pm_devvp; |
| 769 | |
| 770 | printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); |
| 771 | printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n", |
| 772 | vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); |
| 773 | printf("id %lu, mount %p, op %p\n", |
| 774 | vp->v_id, vp->v_mount, vp->v_op); |
| 775 | printf("freef %p, freeb %p, mount %p\n", |
| 776 | TAILQ_NEXT(vp, v_freelist), TAILQ_PREV(vp, v_freelist), |
| 777 | vp->v_mount); |
| 778 | printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", |
| 779 | TAILQ_FIRST(&vp->v_cleanblkhd), |
| 780 | TAILQ_FIRST(&vp->v_dirtyblkhd), |
| 781 | vp->v_numoutput, vp->v_type); |
| 782 | printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", |
| 783 | vp->v_socket, vp->v_tag, |
| 784 | ((u_int *)vp->v_data)[0], |
| 785 | ((u_int *)vp->v_data)[1]); |
| 786 | } |
| 787 | #endif |
| 788 | error = VOP_CLOSE(pmp->pm_devvp, |
| 789 | (pmp->pm_flags&MSDOSFSMNT_RONLY) ? FREAD : FREAD | FWRITE, |
| 790 | td); |
| 791 | vrele(pmp->pm_devvp); |
| 792 | free(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 793 | free(pmp, M_MSDOSFSMNT); |
| 794 | mp->mnt_data = (qaddr_t)0; |
| 795 | mp->mnt_flag &= ~MNT_LOCAL; |
| 796 | return (error); |
| 797 | } |
| 798 | |
| 799 | static int |
| 800 | msdosfs_root(struct mount *mp, struct vnode **vpp) |
| 801 | { |
| 802 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 803 | struct denode *ndep; |
| 804 | int error; |
| 805 | |
| 806 | #ifdef MSDOSFS_DEBUG |
| 807 | printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); |
| 808 | #endif |
| 809 | error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); |
| 810 | if (error) |
| 811 | return (error); |
| 812 | *vpp = DETOV(ndep); |
| 813 | return (0); |
| 814 | } |
| 815 | |
| 816 | static int |
| 817 | msdosfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) |
| 818 | { |
| 819 | struct msdosfsmount *pmp; |
| 820 | |
| 821 | pmp = VFSTOMSDOSFS(mp); |
| 822 | sbp->f_bsize = pmp->pm_bpcluster; |
| 823 | sbp->f_iosize = pmp->pm_bpcluster; |
| 824 | sbp->f_blocks = pmp->pm_maxcluster + 1; |
| 825 | sbp->f_bfree = pmp->pm_freeclustercount; |
| 826 | sbp->f_bavail = pmp->pm_freeclustercount; |
| 827 | sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ |
| 828 | sbp->f_ffree = 0; /* what to put in here? */ |
| 829 | if (sbp != &mp->mnt_stat) { |
| 830 | sbp->f_type = mp->mnt_vfc->vfc_typenum; |
| 831 | bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); |
| 832 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
| 833 | } |
| 834 | strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); |
| 835 | return (0); |
| 836 | } |
| 837 | |
| 838 | struct scaninfo { |
| 839 | int rescan; |
| 840 | int allerror; |
| 841 | int waitfor; |
| 842 | thread_t td; |
| 843 | }; |
| 844 | |
| 845 | static int msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data); |
| 846 | |
| 847 | static int |
| 848 | msdosfs_sync(struct mount *mp, int waitfor, struct thread *td) |
| 849 | { |
| 850 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 851 | struct scaninfo scaninfo; |
| 852 | int error; |
| 853 | |
| 854 | /* |
| 855 | * If we ever switch to not updating all of the fats all the time, |
| 856 | * this would be the place to update them from the first one. |
| 857 | */ |
| 858 | if (pmp->pm_fmod != 0) { |
| 859 | if (pmp->pm_flags & MSDOSFSMNT_RONLY) |
| 860 | panic("msdosfs_sync: rofs mod"); |
| 861 | else { |
| 862 | /* update fats here */ |
| 863 | } |
| 864 | } |
| 865 | /* |
| 866 | * Write back each (modified) denode. |
| 867 | */ |
| 868 | scaninfo.allerror = 0; |
| 869 | scaninfo.rescan = 1; |
| 870 | scaninfo.td = td; |
| 871 | while (scaninfo.rescan) { |
| 872 | scaninfo.rescan = 0; |
| 873 | vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT, NULL, msdosfs_sync_scan, &scaninfo); |
| 874 | } |
| 875 | |
| 876 | /* |
| 877 | * Flush filesystem control info. |
| 878 | */ |
| 879 | if (waitfor != MNT_LAZY) { |
| 880 | vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 881 | if ((error = VOP_FSYNC(pmp->pm_devvp, waitfor, td)) != 0) |
| 882 | scaninfo.allerror = error; |
| 883 | VOP_UNLOCK(pmp->pm_devvp, 0, td); |
| 884 | } |
| 885 | return (scaninfo.allerror); |
| 886 | } |
| 887 | |
| 888 | static int |
| 889 | msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data) |
| 890 | { |
| 891 | struct scaninfo *info = data; |
| 892 | struct denode *dep; |
| 893 | int error; |
| 894 | |
| 895 | dep = VTODE(vp); |
| 896 | if (vp->v_type == VNON || |
| 897 | ((dep->de_flag & |
| 898 | (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && |
| 899 | (TAILQ_EMPTY(&vp->v_dirtyblkhd) || info->waitfor == MNT_LAZY))) { |
| 900 | return(0); |
| 901 | } |
| 902 | if ((error = VOP_FSYNC(vp, info->waitfor, info->td)) != 0) |
| 903 | info->allerror = error; |
| 904 | return(0); |
| 905 | } |
| 906 | |
| 907 | static int |
| 908 | msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) |
| 909 | { |
| 910 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 911 | struct defid *defhp = (struct defid *) fhp; |
| 912 | struct denode *dep; |
| 913 | int error; |
| 914 | |
| 915 | error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); |
| 916 | if (error) { |
| 917 | *vpp = NULLVP; |
| 918 | return (error); |
| 919 | } |
| 920 | *vpp = DETOV(dep); |
| 921 | return (0); |
| 922 | } |
| 923 | |
| 924 | static int |
| 925 | msdosfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp, |
| 926 | struct ucred **credanonp) |
| 927 | { |
| 928 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 929 | struct netcred *np; |
| 930 | |
| 931 | np = vfs_export_lookup(mp, &pmp->pm_export, nam); |
| 932 | if (np == NULL) |
| 933 | return (EACCES); |
| 934 | *exflagsp = np->netc_exflags; |
| 935 | *credanonp = &np->netc_anon; |
| 936 | return (0); |
| 937 | } |
| 938 | |
| 939 | static int |
| 940 | msdosfs_vptofh(struct vnode *vp, struct fid *fhp) |
| 941 | { |
| 942 | struct denode *dep; |
| 943 | struct defid *defhp; |
| 944 | |
| 945 | dep = VTODE(vp); |
| 946 | defhp = (struct defid *)fhp; |
| 947 | defhp->defid_len = sizeof(struct defid); |
| 948 | defhp->defid_dirclust = dep->de_dirclust; |
| 949 | defhp->defid_dirofs = dep->de_diroffset; |
| 950 | /* defhp->defid_gen = dep->de_gen; */ |
| 951 | return (0); |
| 952 | } |
| 953 | |
| 954 | static struct vfsops msdosfs_vfsops = { |
| 955 | msdosfs_mount, |
| 956 | vfs_stdstart, |
| 957 | msdosfs_unmount, |
| 958 | msdosfs_root, |
| 959 | vfs_stdquotactl, |
| 960 | msdosfs_statfs, |
| 961 | msdosfs_sync, |
| 962 | vfs_stdvget, |
| 963 | msdosfs_fhtovp, |
| 964 | msdosfs_checkexp, |
| 965 | msdosfs_vptofh, |
| 966 | msdosfs_init, |
| 967 | msdosfs_uninit, |
| 968 | vfs_stdextattrctl, |
| 969 | }; |
| 970 | |
| 971 | VFS_SET(msdosfs_vfsops, msdos, 0); |