| 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.25 2005/04/15 19:08:19 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/nlookup.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 | /* |
| 159 | * mp - path - addr in user space of mount point (ie /usr or whatever) |
| 160 | * data - addr in user space of mount params including the name of the block |
| 161 | * special file to treat as a filesystem. |
| 162 | */ |
| 163 | static int |
| 164 | msdosfs_mount(struct mount *mp, char *path, caddr_t data, struct thread *td) |
| 165 | { |
| 166 | struct vnode *devvp; /* vnode for blk device to mount */ |
| 167 | struct msdosfs_args args; /* will hold data from mount request */ |
| 168 | /* msdosfs specific mount control block */ |
| 169 | struct msdosfsmount *pmp = NULL; |
| 170 | size_t size; |
| 171 | int error, flags; |
| 172 | mode_t accessmode; |
| 173 | struct proc *p = td->td_proc; |
| 174 | struct nlookupdata nd; |
| 175 | |
| 176 | KKASSERT(p); |
| 177 | |
| 178 | error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); |
| 179 | if (error) |
| 180 | return (error); |
| 181 | if (args.magic != MSDOSFS_ARGSMAGIC) |
| 182 | args.flags = 0; |
| 183 | /* |
| 184 | * If updating, check whether changing from read-only to |
| 185 | * read/write; if there is no device name, that's all we do. |
| 186 | */ |
| 187 | if (mp->mnt_flag & MNT_UPDATE) { |
| 188 | pmp = VFSTOMSDOSFS(mp); |
| 189 | error = 0; |
| 190 | if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { |
| 191 | flags = WRITECLOSE; |
| 192 | if (mp->mnt_flag & MNT_FORCE) |
| 193 | flags |= FORCECLOSE; |
| 194 | error = vflush(mp, 0, flags); |
| 195 | } |
| 196 | if (!error && (mp->mnt_flag & MNT_RELOAD)) |
| 197 | /* not yet implemented */ |
| 198 | error = EOPNOTSUPP; |
| 199 | if (error) |
| 200 | return (error); |
| 201 | if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { |
| 202 | /* |
| 203 | * If upgrade to read-write by non-root, then verify |
| 204 | * that user has necessary permissions on the device. |
| 205 | */ |
| 206 | if (p->p_ucred->cr_uid != 0) { |
| 207 | devvp = pmp->pm_devvp; |
| 208 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 209 | error = VOP_ACCESS(devvp, VREAD | VWRITE, |
| 210 | p->p_ucred, td); |
| 211 | if (error) { |
| 212 | VOP_UNLOCK(devvp, 0, td); |
| 213 | return (error); |
| 214 | } |
| 215 | VOP_UNLOCK(devvp, 0, td); |
| 216 | } |
| 217 | pmp->pm_flags &= ~MSDOSFSMNT_RONLY; |
| 218 | } |
| 219 | if (args.fspec == 0) { |
| 220 | #ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ |
| 221 | if (args.flags & MSDOSFSMNT_MNTOPT) { |
| 222 | pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; |
| 223 | pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; |
| 224 | if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) |
| 225 | pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; |
| 226 | } |
| 227 | #endif |
| 228 | /* |
| 229 | * Process export requests. |
| 230 | */ |
| 231 | return (vfs_export(mp, &pmp->pm_export, &args.export)); |
| 232 | } |
| 233 | } |
| 234 | /* |
| 235 | * Not an update, or updating the name: look up the name |
| 236 | * and verify that it refers to a sensible block device. |
| 237 | */ |
| 238 | devvp = NULL; |
| 239 | error = nlookup_init(&nd, args.fspec, UIO_USERSPACE, NLC_FOLLOW); |
| 240 | if (error == 0) |
| 241 | error = nlookup(&nd); |
| 242 | if (error == 0) |
| 243 | error = cache_vref(nd.nl_ncp, nd.nl_cred, &devvp); |
| 244 | nlookup_done(&nd); |
| 245 | if (error) |
| 246 | return (error); |
| 247 | |
| 248 | if (!vn_isdisk(devvp, &error)) { |
| 249 | vrele(devvp); |
| 250 | return (error); |
| 251 | } |
| 252 | /* |
| 253 | * If mount by non-root, then verify that user has necessary |
| 254 | * permissions on the device. |
| 255 | */ |
| 256 | if (p->p_ucred->cr_uid != 0) { |
| 257 | accessmode = VREAD; |
| 258 | if ((mp->mnt_flag & MNT_RDONLY) == 0) |
| 259 | accessmode |= VWRITE; |
| 260 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 261 | error = VOP_ACCESS(devvp, accessmode, p->p_ucred, td); |
| 262 | if (error) { |
| 263 | vput(devvp); |
| 264 | return (error); |
| 265 | } |
| 266 | VOP_UNLOCK(devvp, 0, td); |
| 267 | } |
| 268 | if ((mp->mnt_flag & MNT_UPDATE) == 0) { |
| 269 | error = mountmsdosfs(devvp, mp, td, &args); |
| 270 | #ifdef MSDOSFS_DEBUG /* only needed for the printf below */ |
| 271 | pmp = VFSTOMSDOSFS(mp); |
| 272 | #endif |
| 273 | } else { |
| 274 | if (devvp != pmp->pm_devvp) |
| 275 | error = EINVAL; /* XXX needs translation */ |
| 276 | else |
| 277 | vrele(devvp); |
| 278 | } |
| 279 | if (error) { |
| 280 | vrele(devvp); |
| 281 | return (error); |
| 282 | } |
| 283 | |
| 284 | error = update_mp(mp, &args); |
| 285 | if (error) { |
| 286 | msdosfs_unmount(mp, MNT_FORCE, td); |
| 287 | return error; |
| 288 | } |
| 289 | |
| 290 | (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, |
| 291 | &size); |
| 292 | bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); |
| 293 | (void) msdosfs_statfs(mp, &mp->mnt_stat, td); |
| 294 | #ifdef MSDOSFS_DEBUG |
| 295 | printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); |
| 296 | #endif |
| 297 | return (0); |
| 298 | } |
| 299 | |
| 300 | static int |
| 301 | mountmsdosfs(struct vnode *devvp, struct mount *mp, struct thread *td, |
| 302 | struct msdosfs_args *argp) |
| 303 | { |
| 304 | struct msdosfsmount *pmp; |
| 305 | struct buf *bp; |
| 306 | dev_t dev; |
| 307 | #ifndef __DragonFly__ |
| 308 | struct partinfo dpart; |
| 309 | int bsize = 0, dtype = 0, tmp; |
| 310 | #endif |
| 311 | union bootsector *bsp; |
| 312 | struct byte_bpb33 *b33; |
| 313 | struct byte_bpb50 *b50; |
| 314 | struct byte_bpb710 *b710; |
| 315 | u_int8_t SecPerClust; |
| 316 | u_long clusters; |
| 317 | int ronly, error; |
| 318 | |
| 319 | /* |
| 320 | * Disallow multiple mounts of the same device. |
| 321 | * Disallow mounting of a device that is currently in use |
| 322 | * Flush out any old buffers remaining from a previous use. |
| 323 | */ |
| 324 | error = vfs_mountedon(devvp); |
| 325 | if (error) |
| 326 | return (error); |
| 327 | if (count_udev(devvp->v_udev) > 0) |
| 328 | return (EBUSY); |
| 329 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 330 | error = vinvalbuf(devvp, V_SAVE, td, 0, 0); |
| 331 | VOP_UNLOCK(devvp, 0, td); |
| 332 | if (error) |
| 333 | return (error); |
| 334 | |
| 335 | ronly = (mp->mnt_flag & MNT_RDONLY) != 0; |
| 336 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 337 | error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, NULL, td); |
| 338 | VOP_UNLOCK(devvp, 0, td); |
| 339 | if (error) |
| 340 | return (error); |
| 341 | dev = devvp->v_rdev; |
| 342 | bp = NULL; /* both used in error_exit */ |
| 343 | pmp = NULL; |
| 344 | |
| 345 | #ifndef __DragonFly__ |
| 346 | if (argp->flags & MSDOSFSMNT_GEMDOSFS) { |
| 347 | /* |
| 348 | * We need the disklabel to calculate the size of a FAT entry |
| 349 | * later on. Also make sure the partition contains a filesystem |
| 350 | * of type FS_MSDOS. This doesn't work for floppies, so we have |
| 351 | * to check for them too. |
| 352 | * |
| 353 | * At least some parts of the msdos fs driver seem to assume |
| 354 | * that the size of a disk block will always be 512 bytes. |
| 355 | * Let's check it... |
| 356 | */ |
| 357 | error = VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, td); |
| 358 | if (error) |
| 359 | goto error_exit; |
| 360 | tmp = dpart.part->p_fstype; |
| 361 | dtype = dpart.disklab->d_type; |
| 362 | bsize = dpart.disklab->d_secsize; |
| 363 | if (bsize != 512 || (dtype!=DTYPE_FLOPPY && tmp!=FS_MSDOS)) { |
| 364 | error = EINVAL; |
| 365 | goto error_exit; |
| 366 | } |
| 367 | } |
| 368 | #endif |
| 369 | |
| 370 | /* |
| 371 | * Read the boot sector of the filesystem, and then check the |
| 372 | * boot signature. If not a dos boot sector then error out. |
| 373 | * |
| 374 | * NOTE: 2048 is a maximum sector size in current... |
| 375 | */ |
| 376 | error = bread(devvp, 0, 2048, &bp); |
| 377 | if (error) |
| 378 | goto error_exit; |
| 379 | bp->b_flags |= B_AGE; |
| 380 | bsp = (union bootsector *)bp->b_data; |
| 381 | b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; |
| 382 | b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; |
| 383 | b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; |
| 384 | |
| 385 | #ifndef __DragonFly__ |
| 386 | if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { |
| 387 | #endif |
| 388 | #ifndef MSDOSFS_NOCHECKSIG |
| 389 | if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 |
| 390 | || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { |
| 391 | error = EINVAL; |
| 392 | goto error_exit; |
| 393 | } |
| 394 | #endif |
| 395 | #ifndef __DragonFly__ |
| 396 | } |
| 397 | #endif |
| 398 | |
| 399 | pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); |
| 400 | bzero((caddr_t)pmp, sizeof *pmp); |
| 401 | pmp->pm_mountp = mp; |
| 402 | |
| 403 | /* |
| 404 | * Compute several useful quantities from the bpb in the |
| 405 | * bootsector. Copy in the dos 5 variant of the bpb then fix up |
| 406 | * the fields that are different between dos 5 and dos 3.3. |
| 407 | */ |
| 408 | SecPerClust = b50->bpbSecPerClust; |
| 409 | pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); |
| 410 | pmp->pm_ResSectors = getushort(b50->bpbResSectors); |
| 411 | pmp->pm_FATs = b50->bpbFATs; |
| 412 | pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); |
| 413 | pmp->pm_Sectors = getushort(b50->bpbSectors); |
| 414 | pmp->pm_FATsecs = getushort(b50->bpbFATsecs); |
| 415 | pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); |
| 416 | pmp->pm_Heads = getushort(b50->bpbHeads); |
| 417 | pmp->pm_Media = b50->bpbMedia; |
| 418 | |
| 419 | /* calculate the ratio of sector size to DEV_BSIZE */ |
| 420 | pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; |
| 421 | |
| 422 | #ifndef __DragonFly__ |
| 423 | if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { |
| 424 | #endif |
| 425 | /* XXX - We should probably check more values here */ |
| 426 | if (!pmp->pm_BytesPerSec || !SecPerClust |
| 427 | || !pmp->pm_Heads |
| 428 | #ifdef PC98 |
| 429 | || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { |
| 430 | #else |
| 431 | || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { |
| 432 | #endif |
| 433 | error = EINVAL; |
| 434 | goto error_exit; |
| 435 | } |
| 436 | #ifndef __DragonFly__ |
| 437 | } |
| 438 | #endif |
| 439 | |
| 440 | if (pmp->pm_Sectors == 0) { |
| 441 | pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); |
| 442 | pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); |
| 443 | } else { |
| 444 | pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); |
| 445 | pmp->pm_HugeSectors = pmp->pm_Sectors; |
| 446 | } |
| 447 | if (pmp->pm_HugeSectors > 0xffffffff / |
| 448 | (pmp->pm_BytesPerSec / sizeof(struct direntry)) + 1) { |
| 449 | /* |
| 450 | * We cannot deal currently with this size of disk |
| 451 | * due to fileid limitations (see msdosfs_getattr and |
| 452 | * msdosfs_readdir) |
| 453 | */ |
| 454 | error = EINVAL; |
| 455 | printf("mountmsdosfs(): disk too big, sorry\n"); |
| 456 | goto error_exit; |
| 457 | } |
| 458 | |
| 459 | if (pmp->pm_RootDirEnts == 0) { |
| 460 | if (bsp->bs710.bsBootSectSig2 != BOOTSIG2 |
| 461 | || bsp->bs710.bsBootSectSig3 != BOOTSIG3 |
| 462 | || pmp->pm_Sectors |
| 463 | || pmp->pm_FATsecs |
| 464 | || getushort(b710->bpbFSVers)) { |
| 465 | error = EINVAL; |
| 466 | printf("mountmsdosfs(): bad FAT32 filesystem\n"); |
| 467 | goto error_exit; |
| 468 | } |
| 469 | pmp->pm_fatmask = FAT32_MASK; |
| 470 | pmp->pm_fatmult = 4; |
| 471 | pmp->pm_fatdiv = 1; |
| 472 | pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); |
| 473 | if (getushort(b710->bpbExtFlags) & FATMIRROR) |
| 474 | pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; |
| 475 | else |
| 476 | pmp->pm_flags |= MSDOSFS_FATMIRROR; |
| 477 | } else |
| 478 | pmp->pm_flags |= MSDOSFS_FATMIRROR; |
| 479 | |
| 480 | /* |
| 481 | * Check a few values (could do some more): |
| 482 | * - logical sector size: power of 2, >= block size |
| 483 | * - sectors per cluster: power of 2, >= 1 |
| 484 | * - number of sectors: >= 1, <= size of partition |
| 485 | */ |
| 486 | if ( (SecPerClust == 0) |
| 487 | || (SecPerClust & (SecPerClust - 1)) |
| 488 | || (pmp->pm_BytesPerSec < DEV_BSIZE) |
| 489 | || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) |
| 490 | || (pmp->pm_HugeSectors == 0) |
| 491 | ) { |
| 492 | error = EINVAL; |
| 493 | goto error_exit; |
| 494 | } |
| 495 | |
| 496 | pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; |
| 497 | pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ |
| 498 | pmp->pm_FATsecs *= pmp->pm_BlkPerSec; |
| 499 | SecPerClust *= pmp->pm_BlkPerSec; |
| 500 | |
| 501 | pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; |
| 502 | |
| 503 | if (FAT32(pmp)) { |
| 504 | pmp->pm_rootdirblk = getulong(b710->bpbRootClust); |
| 505 | pmp->pm_firstcluster = pmp->pm_fatblk |
| 506 | + (pmp->pm_FATs * pmp->pm_FATsecs); |
| 507 | pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; |
| 508 | } else { |
| 509 | pmp->pm_rootdirblk = pmp->pm_fatblk + |
| 510 | (pmp->pm_FATs * pmp->pm_FATsecs); |
| 511 | pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) |
| 512 | + DEV_BSIZE - 1) |
| 513 | / DEV_BSIZE; /* in blocks */ |
| 514 | pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; |
| 515 | } |
| 516 | |
| 517 | pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / |
| 518 | SecPerClust + 1; |
| 519 | pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ |
| 520 | |
| 521 | #ifndef __DragonFly__ |
| 522 | if (argp->flags & MSDOSFSMNT_GEMDOSFS) { |
| 523 | if ((pmp->pm_maxcluster <= (0xff0 - 2)) |
| 524 | && ((dtype == DTYPE_FLOPPY) || ((dtype == DTYPE_VNODE) |
| 525 | && ((pmp->pm_Heads == 1) || (pmp->pm_Heads == 2)))) |
| 526 | ) { |
| 527 | pmp->pm_fatmask = FAT12_MASK; |
| 528 | pmp->pm_fatmult = 3; |
| 529 | pmp->pm_fatdiv = 2; |
| 530 | } else { |
| 531 | pmp->pm_fatmask = FAT16_MASK; |
| 532 | pmp->pm_fatmult = 2; |
| 533 | pmp->pm_fatdiv = 1; |
| 534 | } |
| 535 | } else |
| 536 | #endif |
| 537 | if (pmp->pm_fatmask == 0) { |
| 538 | if (pmp->pm_maxcluster |
| 539 | <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { |
| 540 | /* |
| 541 | * This will usually be a floppy disk. This size makes |
| 542 | * sure that one fat entry will not be split across |
| 543 | * multiple blocks. |
| 544 | */ |
| 545 | pmp->pm_fatmask = FAT12_MASK; |
| 546 | pmp->pm_fatmult = 3; |
| 547 | pmp->pm_fatdiv = 2; |
| 548 | } else { |
| 549 | pmp->pm_fatmask = FAT16_MASK; |
| 550 | pmp->pm_fatmult = 2; |
| 551 | pmp->pm_fatdiv = 1; |
| 552 | } |
| 553 | } |
| 554 | |
| 555 | clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; |
| 556 | if (pmp->pm_maxcluster >= clusters) { |
| 557 | printf("Warning: number of clusters (%ld) exceeds FAT " |
| 558 | "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); |
| 559 | pmp->pm_maxcluster = clusters - 1; |
| 560 | } |
| 561 | |
| 562 | |
| 563 | if (FAT12(pmp)) |
| 564 | pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; |
| 565 | else |
| 566 | pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE; |
| 567 | |
| 568 | pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; |
| 569 | pmp->pm_bnshift = ffs(DEV_BSIZE) - 1; |
| 570 | |
| 571 | /* |
| 572 | * Compute mask and shift value for isolating cluster relative byte |
| 573 | * offsets and cluster numbers from a file offset. |
| 574 | */ |
| 575 | pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; |
| 576 | pmp->pm_crbomask = pmp->pm_bpcluster - 1; |
| 577 | pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; |
| 578 | |
| 579 | /* |
| 580 | * Check for valid cluster size |
| 581 | * must be a power of 2 |
| 582 | */ |
| 583 | if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { |
| 584 | error = EINVAL; |
| 585 | goto error_exit; |
| 586 | } |
| 587 | |
| 588 | /* |
| 589 | * Release the bootsector buffer. |
| 590 | */ |
| 591 | brelse(bp); |
| 592 | bp = NULL; |
| 593 | |
| 594 | /* |
| 595 | * Check FSInfo. |
| 596 | */ |
| 597 | if (pmp->pm_fsinfo) { |
| 598 | struct fsinfo *fp; |
| 599 | |
| 600 | if ((error = bread(devvp, pmp->pm_fsinfo, fsi_size(pmp), &bp)) != 0) |
| 601 | goto error_exit; |
| 602 | fp = (struct fsinfo *)bp->b_data; |
| 603 | if (!bcmp(fp->fsisig1, "RRaA", 4) |
| 604 | && !bcmp(fp->fsisig2, "rrAa", 4) |
| 605 | && !bcmp(fp->fsisig3, "\0\0\125\252", 4) |
| 606 | && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) { |
| 607 | pmp->pm_nxtfree = getulong(fp->fsinxtfree); |
| 608 | if (pmp->pm_nxtfree == 0xffffffff) |
| 609 | pmp->pm_nxtfree = CLUST_FIRST; |
| 610 | } else |
| 611 | pmp->pm_fsinfo = 0; |
| 612 | brelse(bp); |
| 613 | bp = NULL; |
| 614 | } |
| 615 | |
| 616 | /* |
| 617 | * Check and validate (or perhaps invalidate?) the fsinfo structure? |
| 618 | */ |
| 619 | if (pmp->pm_fsinfo && pmp->pm_nxtfree > pmp->pm_maxcluster) { |
| 620 | printf( |
| 621 | "Next free cluster in FSInfo (%lu) exceeds maxcluster (%lu)\n", |
| 622 | pmp->pm_nxtfree, pmp->pm_maxcluster); |
| 623 | error = EINVAL; |
| 624 | goto error_exit; |
| 625 | } |
| 626 | |
| 627 | /* |
| 628 | * Allocate memory for the bitmap of allocated clusters, and then |
| 629 | * fill it in. |
| 630 | */ |
| 631 | pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) |
| 632 | / N_INUSEBITS) |
| 633 | * sizeof(*pmp->pm_inusemap), |
| 634 | M_MSDOSFSFAT, M_WAITOK); |
| 635 | |
| 636 | /* |
| 637 | * fillinusemap() needs pm_devvp. |
| 638 | */ |
| 639 | pmp->pm_dev = dev; |
| 640 | pmp->pm_devvp = devvp; |
| 641 | |
| 642 | /* |
| 643 | * Have the inuse map filled in. |
| 644 | */ |
| 645 | if ((error = fillinusemap(pmp)) != 0) |
| 646 | goto error_exit; |
| 647 | |
| 648 | /* |
| 649 | * If they want fat updates to be synchronous then let them suffer |
| 650 | * the performance degradation in exchange for the on disk copy of |
| 651 | * the fat being correct just about all the time. I suppose this |
| 652 | * would be a good thing to turn on if the kernel is still flakey. |
| 653 | */ |
| 654 | if (mp->mnt_flag & MNT_SYNCHRONOUS) |
| 655 | pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; |
| 656 | |
| 657 | /* |
| 658 | * Finish up. |
| 659 | */ |
| 660 | if (ronly) |
| 661 | pmp->pm_flags |= MSDOSFSMNT_RONLY; |
| 662 | else |
| 663 | pmp->pm_fmod = 1; |
| 664 | mp->mnt_data = (qaddr_t) pmp; |
| 665 | mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); |
| 666 | mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; |
| 667 | mp->mnt_flag |= MNT_LOCAL; |
| 668 | vfs_add_vnodeops(mp, &mp->mnt_vn_norm_ops, msdosfs_vnodeop_entries); |
| 669 | dev->si_mountpoint = mp; |
| 670 | |
| 671 | return 0; |
| 672 | |
| 673 | error_exit: |
| 674 | if (bp) |
| 675 | brelse(bp); |
| 676 | (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, td); |
| 677 | if (pmp) { |
| 678 | if (pmp->pm_inusemap) |
| 679 | free(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 680 | free(pmp, M_MSDOSFSMNT); |
| 681 | mp->mnt_data = (qaddr_t)0; |
| 682 | } |
| 683 | return (error); |
| 684 | } |
| 685 | |
| 686 | /* |
| 687 | * Unmount the filesystem described by mp. |
| 688 | */ |
| 689 | static int |
| 690 | msdosfs_unmount(struct mount *mp, int mntflags, struct thread *td) |
| 691 | { |
| 692 | struct msdosfsmount *pmp; |
| 693 | int error, flags; |
| 694 | |
| 695 | flags = 0; |
| 696 | if (mntflags & MNT_FORCE) |
| 697 | flags |= FORCECLOSE; |
| 698 | error = vflush(mp, 0, flags); |
| 699 | if (error) |
| 700 | return error; |
| 701 | pmp = VFSTOMSDOSFS(mp); |
| 702 | pmp->pm_devvp->v_rdev->si_mountpoint = NULL; |
| 703 | #ifdef MSDOSFS_DEBUG |
| 704 | { |
| 705 | struct vnode *vp = pmp->pm_devvp; |
| 706 | |
| 707 | printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); |
| 708 | printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n", |
| 709 | vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); |
| 710 | printf("id %lu, mount %p, op %p\n", |
| 711 | vp->v_id, vp->v_mount, vp->v_op); |
| 712 | printf("freef %p, freeb %p, mount %p\n", |
| 713 | TAILQ_NEXT(vp, v_freelist), TAILQ_PREV(vp, v_freelist), |
| 714 | vp->v_mount); |
| 715 | printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", |
| 716 | RB_EMPTY(&vp->v_rbclean_tree), |
| 717 | RB_EMPTY(&vp->v_rbdirty_tree), |
| 718 | vp->v_numoutput, vp->v_type); |
| 719 | printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", |
| 720 | vp->v_socket, vp->v_tag, |
| 721 | ((u_int *)vp->v_data)[0], |
| 722 | ((u_int *)vp->v_data)[1]); |
| 723 | } |
| 724 | #endif |
| 725 | error = VOP_CLOSE(pmp->pm_devvp, |
| 726 | (pmp->pm_flags&MSDOSFSMNT_RONLY) ? FREAD : FREAD | FWRITE, |
| 727 | td); |
| 728 | vrele(pmp->pm_devvp); |
| 729 | free(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 730 | free(pmp, M_MSDOSFSMNT); |
| 731 | mp->mnt_data = (qaddr_t)0; |
| 732 | mp->mnt_flag &= ~MNT_LOCAL; |
| 733 | return (error); |
| 734 | } |
| 735 | |
| 736 | static int |
| 737 | msdosfs_root(struct mount *mp, struct vnode **vpp) |
| 738 | { |
| 739 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 740 | struct denode *ndep; |
| 741 | int error; |
| 742 | |
| 743 | #ifdef MSDOSFS_DEBUG |
| 744 | printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); |
| 745 | #endif |
| 746 | error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); |
| 747 | if (error) |
| 748 | return (error); |
| 749 | *vpp = DETOV(ndep); |
| 750 | return (0); |
| 751 | } |
| 752 | |
| 753 | static int |
| 754 | msdosfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) |
| 755 | { |
| 756 | struct msdosfsmount *pmp; |
| 757 | |
| 758 | pmp = VFSTOMSDOSFS(mp); |
| 759 | sbp->f_bsize = pmp->pm_bpcluster; |
| 760 | sbp->f_iosize = pmp->pm_bpcluster; |
| 761 | sbp->f_blocks = pmp->pm_maxcluster + 1; |
| 762 | sbp->f_bfree = pmp->pm_freeclustercount; |
| 763 | sbp->f_bavail = pmp->pm_freeclustercount; |
| 764 | sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ |
| 765 | sbp->f_ffree = 0; /* what to put in here? */ |
| 766 | if (sbp != &mp->mnt_stat) { |
| 767 | sbp->f_type = mp->mnt_vfc->vfc_typenum; |
| 768 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
| 769 | } |
| 770 | strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); |
| 771 | return (0); |
| 772 | } |
| 773 | |
| 774 | struct scaninfo { |
| 775 | int rescan; |
| 776 | int allerror; |
| 777 | int waitfor; |
| 778 | thread_t td; |
| 779 | }; |
| 780 | |
| 781 | static int msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data); |
| 782 | |
| 783 | static int |
| 784 | msdosfs_sync(struct mount *mp, int waitfor, struct thread *td) |
| 785 | { |
| 786 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 787 | struct scaninfo scaninfo; |
| 788 | int error; |
| 789 | |
| 790 | /* |
| 791 | * If we ever switch to not updating all of the fats all the time, |
| 792 | * this would be the place to update them from the first one. |
| 793 | */ |
| 794 | if (pmp->pm_fmod != 0) { |
| 795 | if (pmp->pm_flags & MSDOSFSMNT_RONLY) |
| 796 | panic("msdosfs_sync: rofs mod"); |
| 797 | else { |
| 798 | /* update fats here */ |
| 799 | } |
| 800 | } |
| 801 | /* |
| 802 | * Write back each (modified) denode. |
| 803 | */ |
| 804 | scaninfo.allerror = 0; |
| 805 | scaninfo.rescan = 1; |
| 806 | scaninfo.td = td; |
| 807 | while (scaninfo.rescan) { |
| 808 | scaninfo.rescan = 0; |
| 809 | vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT, NULL, msdosfs_sync_scan, &scaninfo); |
| 810 | } |
| 811 | |
| 812 | /* |
| 813 | * Flush filesystem control info. |
| 814 | */ |
| 815 | if (waitfor != MNT_LAZY) { |
| 816 | vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY, td); |
| 817 | if ((error = VOP_FSYNC(pmp->pm_devvp, waitfor, td)) != 0) |
| 818 | scaninfo.allerror = error; |
| 819 | VOP_UNLOCK(pmp->pm_devvp, 0, td); |
| 820 | } |
| 821 | return (scaninfo.allerror); |
| 822 | } |
| 823 | |
| 824 | static int |
| 825 | msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data) |
| 826 | { |
| 827 | struct scaninfo *info = data; |
| 828 | struct denode *dep; |
| 829 | int error; |
| 830 | |
| 831 | dep = VTODE(vp); |
| 832 | if (vp->v_type == VNON || |
| 833 | ((dep->de_flag & |
| 834 | (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && |
| 835 | (RB_EMPTY(&vp->v_rbdirty_tree) || info->waitfor == MNT_LAZY))) { |
| 836 | return(0); |
| 837 | } |
| 838 | if ((error = VOP_FSYNC(vp, info->waitfor, info->td)) != 0) |
| 839 | info->allerror = error; |
| 840 | return(0); |
| 841 | } |
| 842 | |
| 843 | static int |
| 844 | msdosfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) |
| 845 | { |
| 846 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 847 | struct defid *defhp = (struct defid *) fhp; |
| 848 | struct denode *dep; |
| 849 | int error; |
| 850 | |
| 851 | error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); |
| 852 | if (error) { |
| 853 | *vpp = NULLVP; |
| 854 | return (error); |
| 855 | } |
| 856 | *vpp = DETOV(dep); |
| 857 | return (0); |
| 858 | } |
| 859 | |
| 860 | static int |
| 861 | msdosfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp, |
| 862 | struct ucred **credanonp) |
| 863 | { |
| 864 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 865 | struct netcred *np; |
| 866 | |
| 867 | np = vfs_export_lookup(mp, &pmp->pm_export, nam); |
| 868 | if (np == NULL) |
| 869 | return (EACCES); |
| 870 | *exflagsp = np->netc_exflags; |
| 871 | *credanonp = &np->netc_anon; |
| 872 | return (0); |
| 873 | } |
| 874 | |
| 875 | static int |
| 876 | msdosfs_vptofh(struct vnode *vp, struct fid *fhp) |
| 877 | { |
| 878 | struct denode *dep; |
| 879 | struct defid *defhp; |
| 880 | |
| 881 | dep = VTODE(vp); |
| 882 | defhp = (struct defid *)fhp; |
| 883 | defhp->defid_len = sizeof(struct defid); |
| 884 | defhp->defid_dirclust = dep->de_dirclust; |
| 885 | defhp->defid_dirofs = dep->de_diroffset; |
| 886 | /* defhp->defid_gen = dep->de_gen; */ |
| 887 | return (0); |
| 888 | } |
| 889 | |
| 890 | static struct vfsops msdosfs_vfsops = { |
| 891 | msdosfs_mount, |
| 892 | vfs_stdstart, |
| 893 | msdosfs_unmount, |
| 894 | msdosfs_root, |
| 895 | vfs_stdquotactl, |
| 896 | msdosfs_statfs, |
| 897 | msdosfs_sync, |
| 898 | vfs_stdvget, |
| 899 | msdosfs_fhtovp, |
| 900 | msdosfs_checkexp, |
| 901 | msdosfs_vptofh, |
| 902 | msdosfs_init, |
| 903 | msdosfs_uninit, |
| 904 | vfs_stdextattrctl, |
| 905 | }; |
| 906 | |
| 907 | VFS_SET(msdosfs_vfsops, msdos, 0); |