| Commit | Line | Data |
|---|---|---|
| 50c068cf | 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 $ */ |
| 67863d04 | 2 | /* $DragonFly: src/sys/vfs/msdosfs/msdosfs_vfsops.c,v 1.52 2008/09/17 21:44:24 dillon Exp $ */ |
| 984263bc MD |
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> | |
| 984263bc | 55 | #include <sys/proc.h> |
| fad57d0e | 56 | #include <sys/nlookup.h> |
| 984263bc MD |
57 | #include <sys/kernel.h> |
| 58 | #include <sys/vnode.h> | |
| 3435d577 | 59 | #include <sys/iconv.h> |
| 984263bc MD |
60 | #include <sys/mount.h> |
| 61 | #include <sys/buf.h> | |
| 62 | #include <sys/fcntl.h> | |
| 63 | #include <sys/malloc.h> | |
| 64 | #include <sys/stat.h> /* defines ALLPERMS */ | |
| 65 | #include <vm/vm_zone.h> | |
| 66 | ||
| 1f2de5d4 MD |
67 | #include "bpb.h" |
| 68 | #include "bootsect.h" | |
| 69 | #include "direntry.h" | |
| 70 | #include "denode.h" | |
| 71 | #include "msdosfsmount.h" | |
| 72 | #include "fat.h" | |
| 984263bc | 73 | |
| 66a1ddf5 | 74 | extern struct vop_ops msdosfs_vnode_vops; |
| 3435d577 | 75 | struct iconv_functions *msdos_iconv; |
| 0961aa92 | 76 | |
| 984263bc | 77 | #define MSDOSFS_DFLTBSIZE 4096 |
| 3435d577 | 78 | #define ENCODING_UNICODE "UTF-16BE" |
| 984263bc MD |
79 | #if 1 /*def PC98*/ |
| 80 | /* | |
| 81 | * XXX - The boot signature formatted by NEC PC-98 DOS looks like a | |
| 82 | * garbage or a random value :-{ | |
| 83 | * If you want to use that broken-signatured media, define the | |
| 84 | * following symbol even though PC/AT. | |
| 85 | * (ex. mount PC-98 DOS formatted FD on PC/AT) | |
| 86 | */ | |
| 87 | #define MSDOSFS_NOCHECKSIG | |
| 88 | #endif | |
| 89 | ||
| 90 | MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); | |
| 91 | static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); | |
| 92 | ||
| a6ee311a RG |
93 | static int update_mp (struct mount *mp, struct msdosfs_args *argp); |
| 94 | static int mountmsdosfs (struct vnode *devvp, struct mount *mp, | |
| acde96db | 95 | struct msdosfs_args *argp); |
| 67863d04 MD |
96 | static int msdosfs_fhtovp (struct mount *, struct vnode *, |
| 97 | struct fid *, struct vnode **); | |
| a6ee311a RG |
98 | static int msdosfs_checkexp (struct mount *, struct sockaddr *, |
| 99 | int *, struct ucred **); | |
| 100 | static int msdosfs_mount (struct mount *, char *, caddr_t, | |
| acde96db | 101 | struct ucred *); |
| a6ee311a RG |
102 | static int msdosfs_root (struct mount *, struct vnode **); |
| 103 | static int msdosfs_statfs (struct mount *, struct statfs *, | |
| acde96db | 104 | struct ucred *); |
| 87de5057 | 105 | static int msdosfs_sync (struct mount *, int); |
| acde96db | 106 | static int msdosfs_unmount (struct mount *, int); |
| a6ee311a | 107 | static int msdosfs_vptofh (struct vnode *, struct fid *); |
| 984263bc MD |
108 | |
| 109 | static int | |
| 4625f023 | 110 | update_mp(struct mount *mp, struct msdosfs_args *argp) |
| 984263bc MD |
111 | { |
| 112 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); | |
| 113 | int error; | |
| 3435d577 AP |
114 | char cs_local[ICONV_CSNMAXLEN]; |
| 115 | char cs_dos[ICONV_CSNMAXLEN]; | |
| 984263bc MD |
116 | |
| 117 | pmp->pm_gid = argp->gid; | |
| 118 | pmp->pm_uid = argp->uid; | |
| 119 | pmp->pm_mask = argp->mask & ALLPERMS; | |
| 120 | pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; | |
| 3435d577 AP |
121 | if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { |
| 122 | bcopy(argp->cs_local, cs_local, sizeof(cs_local)); | |
| 123 | bcopy(argp->cs_dos, cs_dos, sizeof(cs_dos)); | |
| 124 | kprintf("local: %s dos: %s\n",argp->cs_local, argp->cs_dos); | |
| 125 | error = msdos_iconv->open(cs_local, ENCODING_UNICODE, &pmp->pm_w2u); | |
| 126 | if(error) | |
| 127 | return error; | |
| 128 | error = msdos_iconv->open(ENCODING_UNICODE, cs_local, &pmp->pm_u2w); | |
| 129 | if(error) | |
| 130 | return error; | |
| 131 | error = msdos_iconv->open(cs_dos, cs_local, &pmp->pm_u2d); | |
| 132 | if(error) | |
| 133 | return error; | |
| 134 | error = msdos_iconv->open(cs_local, cs_dos, &pmp->pm_d2u); | |
| 135 | if(error) | |
| 136 | return error; | |
| 984263bc MD |
137 | } |
| 138 | ||
| 984263bc MD |
139 | if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) |
| 140 | pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; | |
| 141 | else if (!(pmp->pm_flags & | |
| 142 | (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { | |
| 143 | struct vnode *rootvp; | |
| 144 | ||
| 145 | /* | |
| 146 | * Try to divine whether to support Win'95 long filenames | |
| 147 | */ | |
| 148 | if (FAT32(pmp)) | |
| 149 | pmp->pm_flags |= MSDOSFSMNT_LONGNAME; | |
| 150 | else { | |
| 151 | if ((error = msdosfs_root(mp, &rootvp)) != 0) | |
| 152 | return error; | |
| 153 | pmp->pm_flags |= findwin95(VTODE(rootvp)) | |
| 154 | ? MSDOSFSMNT_LONGNAME | |
| 155 | : MSDOSFSMNT_SHORTNAME; | |
| 156 | vput(rootvp); | |
| 157 | } | |
| 158 | } | |
| 159 | return 0; | |
| 160 | } | |
| 161 | ||
| 984263bc MD |
162 | /* |
| 163 | * mp - path - addr in user space of mount point (ie /usr or whatever) | |
| 164 | * data - addr in user space of mount params including the name of the block | |
| 165 | * special file to treat as a filesystem. | |
| 166 | */ | |
| 167 | static int | |
| acde96db | 168 | msdosfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) |
| 984263bc MD |
169 | { |
| 170 | struct vnode *devvp; /* vnode for blk device to mount */ | |
| 171 | struct msdosfs_args args; /* will hold data from mount request */ | |
| 172 | /* msdosfs specific mount control block */ | |
| 173 | struct msdosfsmount *pmp = NULL; | |
| 174 | size_t size; | |
| 175 | int error, flags; | |
| 176 | mode_t accessmode; | |
| fad57d0e | 177 | struct nlookupdata nd; |
| dadab5e9 | 178 | |
| 984263bc MD |
179 | error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); |
| 180 | if (error) | |
| 181 | return (error); | |
| 182 | if (args.magic != MSDOSFS_ARGSMAGIC) | |
| 183 | args.flags = 0; | |
| 184 | /* | |
| 185 | * If updating, check whether changing from read-only to | |
| 186 | * read/write; if there is no device name, that's all we do. | |
| 187 | */ | |
| 188 | if (mp->mnt_flag & MNT_UPDATE) { | |
| 189 | pmp = VFSTOMSDOSFS(mp); | |
| 190 | error = 0; | |
| 191 | if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { | |
| 192 | flags = WRITECLOSE; | |
| 193 | if (mp->mnt_flag & MNT_FORCE) | |
| 194 | flags |= FORCECLOSE; | |
| 195 | error = vflush(mp, 0, flags); | |
| b8bd7242 MD |
196 | if (error == 0) { |
| 197 | devvp = pmp->pm_devvp; | |
| 198 | VOP_OPEN(devvp, FREAD, FSCRED, NULL); | |
| 199 | VOP_CLOSE(devvp, FREAD|FWRITE); | |
| 200 | pmp->pm_flags |= MSDOSFSMNT_RONLY; | |
| 201 | } | |
| 984263bc MD |
202 | } |
| 203 | if (!error && (mp->mnt_flag & MNT_RELOAD)) | |
| 204 | /* not yet implemented */ | |
| 205 | error = EOPNOTSUPP; | |
| 206 | if (error) | |
| 207 | return (error); | |
| 208 | if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { | |
| 209 | /* | |
| 210 | * If upgrade to read-write by non-root, then verify | |
| 211 | * that user has necessary permissions on the device. | |
| 212 | */ | |
| b8bd7242 MD |
213 | devvp = pmp->pm_devvp; |
| 214 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); | |
| acde96db | 215 | if (cred->cr_uid != 0) { |
| cb66845a | 216 | error = VOP_EACCESS(devvp, VREAD | VWRITE, cred); |
| 984263bc | 217 | if (error) { |
| a11aaa81 | 218 | vn_unlock(devvp); |
| 984263bc MD |
219 | return (error); |
| 220 | } | |
| 984263bc | 221 | } |
| b8bd7242 MD |
222 | VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL); |
| 223 | VOP_CLOSE(devvp, FREAD); | |
| 224 | vn_unlock(devvp); | |
| 984263bc MD |
225 | pmp->pm_flags &= ~MSDOSFSMNT_RONLY; |
| 226 | } | |
| 227 | if (args.fspec == 0) { | |
| 228 | #ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ | |
| 229 | if (args.flags & MSDOSFSMNT_MNTOPT) { | |
| 230 | pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; | |
| 231 | pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; | |
| 232 | if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) | |
| 233 | pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; | |
| 234 | } | |
| 235 | #endif | |
| 236 | /* | |
| 237 | * Process export requests. | |
| 238 | */ | |
| 239 | return (vfs_export(mp, &pmp->pm_export, &args.export)); | |
| 240 | } | |
| 241 | } | |
| 242 | /* | |
| 243 | * Not an update, or updating the name: look up the name | |
| 244 | * and verify that it refers to a sensible block device. | |
| 245 | */ | |
| fad57d0e MD |
246 | devvp = NULL; |
| 247 | error = nlookup_init(&nd, args.fspec, UIO_USERSPACE, NLC_FOLLOW); | |
| 248 | if (error == 0) | |
| 249 | error = nlookup(&nd); | |
| 250 | if (error == 0) | |
| 28623bf9 | 251 | error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp); |
| fad57d0e | 252 | nlookup_done(&nd); |
| 984263bc MD |
253 | if (error) |
| 254 | return (error); | |
| 984263bc MD |
255 | |
| 256 | if (!vn_isdisk(devvp, &error)) { | |
| 257 | vrele(devvp); | |
| 258 | return (error); | |
| 259 | } | |
| 260 | /* | |
| 261 | * If mount by non-root, then verify that user has necessary | |
| 262 | * permissions on the device. | |
| 263 | */ | |
| acde96db | 264 | if (cred->cr_uid != 0) { |
| 984263bc MD |
265 | accessmode = VREAD; |
| 266 | if ((mp->mnt_flag & MNT_RDONLY) == 0) | |
| 267 | accessmode |= VWRITE; | |
| ca466bae | 268 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
| cb66845a | 269 | error = VOP_EACCESS(devvp, accessmode, cred); |
| 984263bc MD |
270 | if (error) { |
| 271 | vput(devvp); | |
| 272 | return (error); | |
| 273 | } | |
| a11aaa81 | 274 | vn_unlock(devvp); |
| 984263bc MD |
275 | } |
| 276 | if ((mp->mnt_flag & MNT_UPDATE) == 0) { | |
| acde96db | 277 | error = mountmsdosfs(devvp, mp, &args); |
| 086c1d7e | 278 | #ifdef MSDOSFS_DEBUG /* only needed for the kprintf below */ |
| 984263bc MD |
279 | pmp = VFSTOMSDOSFS(mp); |
| 280 | #endif | |
| 281 | } else { | |
| 282 | if (devvp != pmp->pm_devvp) | |
| 283 | error = EINVAL; /* XXX needs translation */ | |
| 284 | else | |
| 285 | vrele(devvp); | |
| 286 | } | |
| 287 | if (error) { | |
| 288 | vrele(devvp); | |
| 289 | return (error); | |
| 290 | } | |
| 291 | ||
| 292 | error = update_mp(mp, &args); | |
| 293 | if (error) { | |
| acde96db | 294 | msdosfs_unmount(mp, MNT_FORCE); |
| 984263bc MD |
295 | return error; |
| 296 | } | |
| 297 | ||
| b1ce5639 | 298 | copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); |
| 984263bc | 299 | bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); |
| acde96db | 300 | msdosfs_statfs(mp, &mp->mnt_stat, cred); |
| 984263bc | 301 | #ifdef MSDOSFS_DEBUG |
| 086c1d7e | 302 | kprintf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); |
| 984263bc MD |
303 | #endif |
| 304 | return (0); | |
| 305 | } | |
| 306 | ||
| 307 | static int | |
| acde96db | 308 | mountmsdosfs(struct vnode *devvp, struct mount *mp, struct msdosfs_args *argp) |
| 984263bc MD |
309 | { |
| 310 | struct msdosfsmount *pmp; | |
| 311 | struct buf *bp; | |
| b13267a5 | 312 | cdev_t dev; |
| 984263bc MD |
313 | union bootsector *bsp; |
| 314 | struct byte_bpb33 *b33; | |
| 315 | struct byte_bpb50 *b50; | |
| 316 | struct byte_bpb710 *b710; | |
| 317 | u_int8_t SecPerClust; | |
| 318 | u_long clusters; | |
| 319 | int ronly, error; | |
| 320 | ||
| 321 | /* | |
| 322 | * Disallow multiple mounts of the same device. | |
| 323 | * Disallow mounting of a device that is currently in use | |
| 984263bc MD |
324 | * Flush out any old buffers remaining from a previous use. |
| 325 | */ | |
| 326 | error = vfs_mountedon(devvp); | |
| 327 | if (error) | |
| 328 | return (error); | |
| 8be7edad | 329 | if (vcount(devvp) > 0) |
| 984263bc | 330 | return (EBUSY); |
| ca466bae | 331 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
| 87de5057 | 332 | error = vinvalbuf(devvp, V_SAVE, 0, 0); |
| a11aaa81 | 333 | vn_unlock(devvp); |
| 984263bc MD |
334 | if (error) |
| 335 | return (error); | |
| 336 | ||
| 337 | ronly = (mp->mnt_flag & MNT_RDONLY) != 0; | |
| ca466bae | 338 | vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); |
| 87de5057 | 339 | error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, NULL); |
| a11aaa81 | 340 | vn_unlock(devvp); |
| 984263bc MD |
341 | if (error) |
| 342 | return (error); | |
| e4c9c0c8 | 343 | dev = devvp->v_rdev; |
| 984263bc MD |
344 | bp = NULL; /* both used in error_exit */ |
| 345 | pmp = NULL; | |
| 346 | ||
| 984263bc MD |
347 | /* |
| 348 | * Read the boot sector of the filesystem, and then check the | |
| 349 | * boot signature. If not a dos boot sector then error out. | |
| 350 | * | |
| 351 | * NOTE: 2048 is a maximum sector size in current... | |
| 352 | */ | |
| 3b568787 | 353 | error = bread(devvp, 0, 2048, &bp); |
| 984263bc MD |
354 | if (error) |
| 355 | goto error_exit; | |
| 356 | bp->b_flags |= B_AGE; | |
| 357 | bsp = (union bootsector *)bp->b_data; | |
| 358 | b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; | |
| 359 | b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; | |
| 360 | b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; | |
| 361 | ||
| 984263bc | 362 | #ifndef MSDOSFS_NOCHECKSIG |
| ae2b058d HT |
363 | if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 |
| 364 | || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { | |
| 365 | error = EINVAL; | |
| 366 | goto error_exit; | |
| 984263bc MD |
367 | } |
| 368 | #endif | |
| 369 | ||
| e7b4468c | 370 | pmp = kmalloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK | M_ZERO); |
| 984263bc MD |
371 | pmp->pm_mountp = mp; |
| 372 | ||
| 373 | /* | |
| 374 | * Compute several useful quantities from the bpb in the | |
| 375 | * bootsector. Copy in the dos 5 variant of the bpb then fix up | |
| 376 | * the fields that are different between dos 5 and dos 3.3. | |
| 377 | */ | |
| 378 | SecPerClust = b50->bpbSecPerClust; | |
| 379 | pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); | |
| 380 | pmp->pm_ResSectors = getushort(b50->bpbResSectors); | |
| 381 | pmp->pm_FATs = b50->bpbFATs; | |
| 382 | pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); | |
| 383 | pmp->pm_Sectors = getushort(b50->bpbSectors); | |
| 384 | pmp->pm_FATsecs = getushort(b50->bpbFATsecs); | |
| 385 | pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); | |
| 386 | pmp->pm_Heads = getushort(b50->bpbHeads); | |
| 387 | pmp->pm_Media = b50->bpbMedia; | |
| 388 | ||
| 389 | /* calculate the ratio of sector size to DEV_BSIZE */ | |
| 390 | pmp->pm_BlkPerSec = pmp->pm_BytesPerSec / DEV_BSIZE; | |
| 391 | ||
| ae2b058d HT |
392 | /* |
| 393 | * We don't check pm_Heads nor pm_SecPerTrack, because | |
| 394 | * these may not be set for EFI file systems. We don't | |
| 395 | * use these anyway, so we're unaffected if they are | |
| 396 | * invalid. | |
| 397 | */ | |
| 398 | if (!pmp->pm_BytesPerSec || !SecPerClust) { | |
| 399 | error = EINVAL; | |
| 400 | goto error_exit; | |
| 984263bc | 401 | } |
| 984263bc MD |
402 | |
| 403 | if (pmp->pm_Sectors == 0) { | |
| 404 | pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); | |
| 405 | pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); | |
| 406 | } else { | |
| 407 | pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); | |
| 408 | pmp->pm_HugeSectors = pmp->pm_Sectors; | |
| 409 | } | |
| 984263bc MD |
410 | |
| 411 | if (pmp->pm_RootDirEnts == 0) { | |
| a70dba5d | 412 | if (pmp->pm_Sectors |
| 984263bc MD |
413 | || pmp->pm_FATsecs |
| 414 | || getushort(b710->bpbFSVers)) { | |
| 415 | error = EINVAL; | |
| 086c1d7e | 416 | kprintf("mountmsdosfs(): bad FAT32 filesystem\n"); |
| 984263bc MD |
417 | goto error_exit; |
| 418 | } | |
| 419 | pmp->pm_fatmask = FAT32_MASK; | |
| 420 | pmp->pm_fatmult = 4; | |
| 421 | pmp->pm_fatdiv = 1; | |
| 422 | pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); | |
| 423 | if (getushort(b710->bpbExtFlags) & FATMIRROR) | |
| 424 | pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; | |
| 425 | else | |
| 426 | pmp->pm_flags |= MSDOSFS_FATMIRROR; | |
| 427 | } else | |
| 428 | pmp->pm_flags |= MSDOSFS_FATMIRROR; | |
| 429 | ||
| 430 | /* | |
| 431 | * Check a few values (could do some more): | |
| 432 | * - logical sector size: power of 2, >= block size | |
| 433 | * - sectors per cluster: power of 2, >= 1 | |
| 434 | * - number of sectors: >= 1, <= size of partition | |
| 435 | */ | |
| 436 | if ( (SecPerClust == 0) | |
| 437 | || (SecPerClust & (SecPerClust - 1)) | |
| 438 | || (pmp->pm_BytesPerSec < DEV_BSIZE) | |
| 439 | || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) | |
| 440 | || (pmp->pm_HugeSectors == 0) | |
| 441 | ) { | |
| 442 | error = EINVAL; | |
| 443 | goto error_exit; | |
| 444 | } | |
| 445 | ||
| 446 | pmp->pm_HugeSectors *= pmp->pm_BlkPerSec; | |
| 447 | pmp->pm_HiddenSects *= pmp->pm_BlkPerSec; /* XXX not used? */ | |
| 448 | pmp->pm_FATsecs *= pmp->pm_BlkPerSec; | |
| 449 | SecPerClust *= pmp->pm_BlkPerSec; | |
| 450 | ||
| 451 | pmp->pm_fatblk = pmp->pm_ResSectors * pmp->pm_BlkPerSec; | |
| 452 | ||
| 453 | if (FAT32(pmp)) { | |
| 454 | pmp->pm_rootdirblk = getulong(b710->bpbRootClust); | |
| 455 | pmp->pm_firstcluster = pmp->pm_fatblk | |
| 456 | + (pmp->pm_FATs * pmp->pm_FATsecs); | |
| 457 | pmp->pm_fsinfo = getushort(b710->bpbFSInfo) * pmp->pm_BlkPerSec; | |
| 458 | } else { | |
| 459 | pmp->pm_rootdirblk = pmp->pm_fatblk + | |
| 460 | (pmp->pm_FATs * pmp->pm_FATsecs); | |
| 461 | pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) | |
| 462 | + DEV_BSIZE - 1) | |
| 463 | / DEV_BSIZE; /* in blocks */ | |
| 464 | pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; | |
| 465 | } | |
| 466 | ||
| 467 | pmp->pm_maxcluster = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / | |
| 468 | SecPerClust + 1; | |
| 469 | pmp->pm_fatsize = pmp->pm_FATsecs * DEV_BSIZE; /* XXX not used? */ | |
| 470 | ||
| 984263bc MD |
471 | if (pmp->pm_fatmask == 0) { |
| 472 | if (pmp->pm_maxcluster | |
| 473 | <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { | |
| 474 | /* | |
| 475 | * This will usually be a floppy disk. This size makes | |
| 476 | * sure that one fat entry will not be split across | |
| 477 | * multiple blocks. | |
| 478 | */ | |
| 479 | pmp->pm_fatmask = FAT12_MASK; | |
| 480 | pmp->pm_fatmult = 3; | |
| 481 | pmp->pm_fatdiv = 2; | |
| 482 | } else { | |
| 483 | pmp->pm_fatmask = FAT16_MASK; | |
| 484 | pmp->pm_fatmult = 2; | |
| 485 | pmp->pm_fatdiv = 1; | |
| 486 | } | |
| 487 | } | |
| 488 | ||
| 489 | clusters = (pmp->pm_fatsize / pmp->pm_fatmult) * pmp->pm_fatdiv; | |
| 490 | if (pmp->pm_maxcluster >= clusters) { | |
| 086c1d7e | 491 | kprintf("Warning: number of clusters (%ld) exceeds FAT " |
| 984263bc MD |
492 | "capacity (%ld)\n", pmp->pm_maxcluster + 1, clusters); |
| 493 | pmp->pm_maxcluster = clusters - 1; | |
| 494 | } | |
| 495 | ||
| 496 | ||
| 497 | if (FAT12(pmp)) | |
| 498 | pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; | |
| 499 | else | |
| 500 | pmp->pm_fatblocksize = MSDOSFS_DFLTBSIZE; | |
| 501 | ||
| 502 | pmp->pm_fatblocksec = pmp->pm_fatblocksize / DEV_BSIZE; | |
| 54078292 | 503 | pmp->pm_bnshift = DEV_BSHIFT; |
| 984263bc MD |
504 | |
| 505 | /* | |
| 506 | * Compute mask and shift value for isolating cluster relative byte | |
| 507 | * offsets and cluster numbers from a file offset. | |
| 508 | */ | |
| 509 | pmp->pm_bpcluster = SecPerClust * DEV_BSIZE; | |
| 510 | pmp->pm_crbomask = pmp->pm_bpcluster - 1; | |
| 511 | pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; | |
| 512 | ||
| 513 | /* | |
| 514 | * Check for valid cluster size | |
| 515 | * must be a power of 2 | |
| 516 | */ | |
| 517 | if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { | |
| 518 | error = EINVAL; | |
| 519 | goto error_exit; | |
| 520 | } | |
| 521 | ||
| 522 | /* | |
| 523 | * Release the bootsector buffer. | |
| 524 | */ | |
| 12d44297 | 525 | bp->b_flags |= B_RELBUF; |
| 984263bc MD |
526 | brelse(bp); |
| 527 | bp = NULL; | |
| 528 | ||
| 529 | /* | |
| 530 | * Check FSInfo. | |
| 531 | */ | |
| 532 | if (pmp->pm_fsinfo) { | |
| 533 | struct fsinfo *fp; | |
| 534 | ||
| 54078292 | 535 | if ((error = bread(devvp, de_bntodoff(pmp, pmp->pm_fsinfo), fsi_size(pmp), &bp)) != 0) |
| 984263bc MD |
536 | goto error_exit; |
| 537 | fp = (struct fsinfo *)bp->b_data; | |
| 538 | if (!bcmp(fp->fsisig1, "RRaA", 4) | |
| 539 | && !bcmp(fp->fsisig2, "rrAa", 4) | |
| 540 | && !bcmp(fp->fsisig3, "\0\0\125\252", 4) | |
| 50c068cf | 541 | && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) { |
| 984263bc | 542 | pmp->pm_nxtfree = getulong(fp->fsinxtfree); |
| 50c068cf CP |
543 | if (pmp->pm_nxtfree == 0xffffffff) |
| 544 | pmp->pm_nxtfree = CLUST_FIRST; | |
| 545 | } else | |
| 984263bc | 546 | pmp->pm_fsinfo = 0; |
| 12d44297 | 547 | bp->b_flags |= B_RELBUF; |
| 984263bc MD |
548 | brelse(bp); |
| 549 | bp = NULL; | |
| 550 | } | |
| 551 | ||
| 552 | /* | |
| 553 | * Check and validate (or perhaps invalidate?) the fsinfo structure? | |
| 554 | */ | |
| 555 | if (pmp->pm_fsinfo && pmp->pm_nxtfree > pmp->pm_maxcluster) { | |
| 086c1d7e | 556 | kprintf( |
| 984263bc MD |
557 | "Next free cluster in FSInfo (%lu) exceeds maxcluster (%lu)\n", |
| 558 | pmp->pm_nxtfree, pmp->pm_maxcluster); | |
| 559 | error = EINVAL; | |
| 560 | goto error_exit; | |
| 561 | } | |
| 562 | ||
| 563 | /* | |
| 564 | * Allocate memory for the bitmap of allocated clusters, and then | |
| 565 | * fill it in. | |
| 566 | */ | |
| 77652cad | 567 | pmp->pm_inusemap = kmalloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) |
| 984263bc MD |
568 | / N_INUSEBITS) |
| 569 | * sizeof(*pmp->pm_inusemap), | |
| 570 | M_MSDOSFSFAT, M_WAITOK); | |
| 571 | ||
| 572 | /* | |
| 573 | * fillinusemap() needs pm_devvp. | |
| 574 | */ | |
| 575 | pmp->pm_dev = dev; | |
| 576 | pmp->pm_devvp = devvp; | |
| 577 | ||
| 578 | /* | |
| 579 | * Have the inuse map filled in. | |
| 580 | */ | |
| 581 | if ((error = fillinusemap(pmp)) != 0) | |
| 582 | goto error_exit; | |
| 583 | ||
| 584 | /* | |
| 585 | * If they want fat updates to be synchronous then let them suffer | |
| 586 | * the performance degradation in exchange for the on disk copy of | |
| 587 | * the fat being correct just about all the time. I suppose this | |
| 588 | * would be a good thing to turn on if the kernel is still flakey. | |
| 589 | */ | |
| 590 | if (mp->mnt_flag & MNT_SYNCHRONOUS) | |
| 591 | pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; | |
| 592 | ||
| 593 | /* | |
| 594 | * Finish up. | |
| 595 | */ | |
| 596 | if (ronly) | |
| 597 | pmp->pm_flags |= MSDOSFSMNT_RONLY; | |
| 598 | else | |
| 599 | pmp->pm_fmod = 1; | |
| 600 | mp->mnt_data = (qaddr_t) pmp; | |
| 601 | mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); | |
| 602 | mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; | |
| 603 | mp->mnt_flag |= MNT_LOCAL; | |
| 66a1ddf5 | 604 | vfs_add_vnodeops(mp, &msdosfs_vnode_vops, &mp->mnt_vn_norm_ops); |
| e4c9c0c8 | 605 | dev->si_mountpoint = mp; |
| 984263bc MD |
606 | |
| 607 | return 0; | |
| 608 | ||
| 609 | error_exit: | |
| 12d44297 JT |
610 | if (bp) { |
| 611 | bp->b_flags |= B_RELBUF; | |
| 984263bc | 612 | brelse(bp); |
| 12d44297 | 613 | } |
| 87de5057 | 614 | VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE); |
| 984263bc MD |
615 | if (pmp) { |
| 616 | if (pmp->pm_inusemap) | |
| efda3bd0 MD |
617 | kfree(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 618 | kfree(pmp, M_MSDOSFSMNT); | |
| 984263bc MD |
619 | mp->mnt_data = (qaddr_t)0; |
| 620 | } | |
| 621 | return (error); | |
| 622 | } | |
| 623 | ||
| 624 | /* | |
| 625 | * Unmount the filesystem described by mp. | |
| 626 | */ | |
| 627 | static int | |
| acde96db | 628 | msdosfs_unmount(struct mount *mp, int mntflags) |
| 984263bc MD |
629 | { |
| 630 | struct msdosfsmount *pmp; | |
| 631 | int error, flags; | |
| 632 | ||
| 633 | flags = 0; | |
| 634 | if (mntflags & MNT_FORCE) | |
| 635 | flags |= FORCECLOSE; | |
| 636 | error = vflush(mp, 0, flags); | |
| 637 | if (error) | |
| 638 | return error; | |
| 639 | pmp = VFSTOMSDOSFS(mp); | |
| e4c9c0c8 | 640 | pmp->pm_devvp->v_rdev->si_mountpoint = NULL; |
| 3435d577 AP |
641 | if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { |
| 642 | if(pmp->pm_w2u) | |
| 643 | msdos_iconv->close(pmp->pm_w2u); | |
| 644 | if(pmp->pm_u2w) | |
| 645 | msdos_iconv->close(pmp->pm_u2w); | |
| 646 | if(pmp->pm_d2u) | |
| 647 | msdos_iconv->close(pmp->pm_d2u); | |
| 648 | if(pmp->pm_u2d) | |
| 649 | msdos_iconv->close(pmp->pm_u2d); | |
| 650 | } | |
| 984263bc MD |
651 | #ifdef MSDOSFS_DEBUG |
| 652 | { | |
| 653 | struct vnode *vp = pmp->pm_devvp; | |
| 654 | ||
| 086c1d7e | 655 | kprintf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); |
| 1ffae7f1 | 656 | kprintf("flag %08x, sysrefs %d, writecount %d, auxrefs %d\n", |
| 3c37c940 MD |
657 | vp->v_flag, vp->v_sysref.refcnt, |
| 658 | vp->v_writecount, vp->v_auxrefs); | |
| 1ffae7f1 | 659 | kprintf("mount %p, op %p\n", vp->v_mount, vp->v_ops); |
| 2ec4b00d | 660 | kprintf("mount %p\n", vp->v_mount); |
| 1ffae7f1 MD |
661 | kprintf("cleanblkhd %p, dirtyblkhd %p, numoutput %d, type %d\n", |
| 662 | RB_ROOT(&vp->v_rbclean_tree), | |
| 663 | RB_ROOT(&vp->v_rbdirty_tree), | |
| a9a20f98 MD |
664 | bio_track_active(&vp->v_track_write), |
| 665 | vp->v_type); | |
| 086c1d7e | 666 | kprintf("union %p, tag %d, data[0] %08x, data[1] %08x\n", |
| 984263bc MD |
667 | vp->v_socket, vp->v_tag, |
| 668 | ((u_int *)vp->v_data)[0], | |
| 669 | ((u_int *)vp->v_data)[1]); | |
| 670 | } | |
| 671 | #endif | |
| 672 | error = VOP_CLOSE(pmp->pm_devvp, | |
| 87de5057 | 673 | (pmp->pm_flags&MSDOSFSMNT_RONLY) ? FREAD : FREAD | FWRITE); |
| 984263bc | 674 | vrele(pmp->pm_devvp); |
| efda3bd0 MD |
675 | kfree(pmp->pm_inusemap, M_MSDOSFSFAT); |
| 676 | kfree(pmp, M_MSDOSFSMNT); | |
| 984263bc MD |
677 | mp->mnt_data = (qaddr_t)0; |
| 678 | mp->mnt_flag &= ~MNT_LOCAL; | |
| 679 | return (error); | |
| 680 | } | |
| 681 | ||
| 682 | static int | |
| 4625f023 | 683 | msdosfs_root(struct mount *mp, struct vnode **vpp) |
| 984263bc MD |
684 | { |
| 685 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); | |
| 686 | struct denode *ndep; | |
| 687 | int error; | |
| 688 | ||
| 689 | #ifdef MSDOSFS_DEBUG | |
| 086c1d7e | 690 | kprintf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); |
| 984263bc MD |
691 | #endif |
| 692 | error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); | |
| 693 | if (error) | |
| 694 | return (error); | |
| 695 | *vpp = DETOV(ndep); | |
| 696 | return (0); | |
| 697 | } | |
| 698 | ||
| 699 | static int | |
| acde96db | 700 | msdosfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) |
| 984263bc MD |
701 | { |
| 702 | struct msdosfsmount *pmp; | |
| 703 | ||
| 704 | pmp = VFSTOMSDOSFS(mp); | |
| 705 | sbp->f_bsize = pmp->pm_bpcluster; | |
| 706 | sbp->f_iosize = pmp->pm_bpcluster; | |
| 707 | sbp->f_blocks = pmp->pm_maxcluster + 1; | |
| 708 | sbp->f_bfree = pmp->pm_freeclustercount; | |
| 709 | sbp->f_bavail = pmp->pm_freeclustercount; | |
| 710 | sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ | |
| 711 | sbp->f_ffree = 0; /* what to put in here? */ | |
| 712 | if (sbp != &mp->mnt_stat) { | |
| 713 | sbp->f_type = mp->mnt_vfc->vfc_typenum; | |
| 984263bc MD |
714 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); |
| 715 | } | |
| 716 | strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); | |
| 717 | return (0); | |
| 718 | } | |
| 719 | ||
| 41a01a4d MD |
720 | struct scaninfo { |
| 721 | int rescan; | |
| 722 | int allerror; | |
| 723 | int waitfor; | |
| 41a01a4d MD |
724 | }; |
| 725 | ||
| 5fd012e0 | 726 | static int msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data); |
| 41a01a4d | 727 | |
| 984263bc | 728 | static int |
| 87de5057 | 729 | msdosfs_sync(struct mount *mp, int waitfor) |
| 984263bc | 730 | { |
| 984263bc | 731 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); |
| 41a01a4d MD |
732 | struct scaninfo scaninfo; |
| 733 | int error; | |
| 984263bc MD |
734 | |
| 735 | /* | |
| 736 | * If we ever switch to not updating all of the fats all the time, | |
| 737 | * this would be the place to update them from the first one. | |
| 738 | */ | |
| 739 | if (pmp->pm_fmod != 0) { | |
| 740 | if (pmp->pm_flags & MSDOSFSMNT_RONLY) | |
| 741 | panic("msdosfs_sync: rofs mod"); | |
| 742 | else { | |
| 743 | /* update fats here */ | |
| 744 | } | |
| 745 | } | |
| 746 | /* | |
| 747 | * Write back each (modified) denode. | |
| 748 | */ | |
| 41a01a4d MD |
749 | scaninfo.allerror = 0; |
| 750 | scaninfo.rescan = 1; | |
| 41a01a4d MD |
751 | while (scaninfo.rescan) { |
| 752 | scaninfo.rescan = 0; | |
| 5fd012e0 | 753 | vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT, NULL, msdosfs_sync_scan, &scaninfo); |
| 984263bc | 754 | } |
| 984263bc MD |
755 | |
| 756 | /* | |
| 757 | * Flush filesystem control info. | |
| 758 | */ | |
| 28271622 | 759 | if ((waitfor & MNT_LAZY) == 0) { |
| ca466bae | 760 | vn_lock(pmp->pm_devvp, LK_EXCLUSIVE | LK_RETRY); |
| 52174f71 | 761 | if ((error = VOP_FSYNC(pmp->pm_devvp, waitfor, 0)) != 0) |
| 41a01a4d | 762 | scaninfo.allerror = error; |
| a11aaa81 | 763 | vn_unlock(pmp->pm_devvp); |
| 41a01a4d MD |
764 | } |
| 765 | return (scaninfo.allerror); | |
| 766 | } | |
| 767 | ||
| 4625f023 | 768 | static int |
| 5fd012e0 | 769 | msdosfs_sync_scan(struct mount *mp, struct vnode *vp, void *data) |
| 41a01a4d MD |
770 | { |
| 771 | struct scaninfo *info = data; | |
| 772 | struct denode *dep; | |
| 773 | int error; | |
| 774 | ||
| 775 | dep = VTODE(vp); | |
| 986e7cda | 776 | if (vp->v_type == VNON || vp->v_type == VBAD || |
| 41a01a4d MD |
777 | ((dep->de_flag & |
| 778 | (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0 && | |
| 28271622 | 779 | (RB_EMPTY(&vp->v_rbdirty_tree) || (info->waitfor & MNT_LAZY)))) { |
| 41a01a4d | 780 | return(0); |
| 984263bc | 781 | } |
| 52174f71 | 782 | if ((error = VOP_FSYNC(vp, info->waitfor, 0)) != 0) |
| 41a01a4d | 783 | info->allerror = error; |
| 41a01a4d | 784 | return(0); |
| 984263bc MD |
785 | } |
| 786 | ||
| 787 | static int | |
| 67863d04 MD |
788 | msdosfs_fhtovp(struct mount *mp, struct vnode *rootvp, |
| 789 | struct fid *fhp, struct vnode **vpp) | |
| 984263bc MD |
790 | { |
| 791 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); | |
| 792 | struct defid *defhp = (struct defid *) fhp; | |
| 793 | struct denode *dep; | |
| 794 | int error; | |
| 795 | ||
| 796 | error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); | |
| 797 | if (error) { | |
| 798 | *vpp = NULLVP; | |
| 799 | return (error); | |
| 800 | } | |
| 801 | *vpp = DETOV(dep); | |
| 802 | return (0); | |
| 803 | } | |
| 804 | ||
| 805 | static int | |
| 4625f023 CP |
806 | msdosfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp, |
| 807 | struct ucred **credanonp) | |
| 984263bc MD |
808 | { |
| 809 | struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); | |
| 810 | struct netcred *np; | |
| 811 | ||
| 812 | np = vfs_export_lookup(mp, &pmp->pm_export, nam); | |
| 813 | if (np == NULL) | |
| 814 | return (EACCES); | |
| 815 | *exflagsp = np->netc_exflags; | |
| 816 | *credanonp = &np->netc_anon; | |
| 817 | return (0); | |
| 818 | } | |
| 819 | ||
| 820 | static int | |
| 4625f023 | 821 | msdosfs_vptofh(struct vnode *vp, struct fid *fhp) |
| 984263bc MD |
822 | { |
| 823 | struct denode *dep; | |
| 824 | struct defid *defhp; | |
| 825 | ||
| 826 | dep = VTODE(vp); | |
| 827 | defhp = (struct defid *)fhp; | |
| 828 | defhp->defid_len = sizeof(struct defid); | |
| 829 | defhp->defid_dirclust = dep->de_dirclust; | |
| 830 | defhp->defid_dirofs = dep->de_diroffset; | |
| 831 | /* defhp->defid_gen = dep->de_gen; */ | |
| 832 | return (0); | |
| 833 | } | |
| 834 | ||
| 835 | static struct vfsops msdosfs_vfsops = { | |
| 43c45e8f HP |
836 | .vfs_mount = msdosfs_mount, |
| 837 | .vfs_unmount = msdosfs_unmount, | |
| 838 | .vfs_root = msdosfs_root, | |
| 839 | .vfs_statfs = msdosfs_statfs, | |
| 840 | .vfs_sync = msdosfs_sync, | |
| 841 | .vfs_fhtovp = msdosfs_fhtovp, | |
| 842 | .vfs_checkexp = msdosfs_checkexp, | |
| 843 | .vfs_vptofh = msdosfs_vptofh, | |
| 844 | .vfs_init = msdosfs_init, | |
| 845 | .vfs_uninit = msdosfs_uninit | |
| 984263bc MD |
846 | }; |
| 847 | ||
| 848 | VFS_SET(msdosfs_vfsops, msdos, 0); | |
| 387d2c14 | 849 | MODULE_VERSION(msdos, 1); |