mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-16 10:20:30 +00:00
b1f4a44b03
Ever since I first say the way the mount flags were used I've hated the fact that modes, and events, internal and exported, and short-term and long term flags are all thrown together. Finally it's annoyed me enough.. This patch to the entire FreeBSD tree adds a second mount flag word to the mount struct. it is not exported to userspace. I have moved some of the non exported flags over to this word. this means that we now have 8 free bits in the mount flags. There are another two that might well move over, but which I'm not sure about. The only user visible change would have been in pstat -v, except that davidg has disabled it anyhow. I'd still like to move the state flags and the 'command' flags apart from each other.. e.g. MNT_FORCE really doesn't have the same semantics as MNT_RDONLY, but that's left for another day.
804 lines
22 KiB
C
804 lines
22 KiB
C
/* $Id: msdosfs_vfsops.c,v 1.22 1997/10/12 20:25:01 phk Exp $ */
|
|
/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */
|
|
|
|
/*-
|
|
* Copyright (C) 1994 Wolfgang Solfrank.
|
|
* Copyright (C) 1994 TooLs GmbH.
|
|
* All rights reserved.
|
|
* Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by TooLs GmbH.
|
|
* 4. The name of TooLs GmbH may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
/*
|
|
* Written by Paul Popelka (paulp@uts.amdahl.com)
|
|
*
|
|
* You can do anything you want with this software, just don't say you wrote
|
|
* it, and don't remove this notice.
|
|
*
|
|
* This software is provided "as is".
|
|
*
|
|
* The author supplies this software to be publicly redistributed on the
|
|
* understanding that the author is not responsible for the correct
|
|
* functioning of this software in any circumstances and is not liable for
|
|
* any damages caused by this software.
|
|
*
|
|
* October 1992
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/vnode.h>
|
|
#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
|
|
#include <sys/mount.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <msdosfs/bpb.h>
|
|
#include <msdosfs/bootsect.h>
|
|
#include <msdosfs/direntry.h>
|
|
#include <msdosfs/denode.h>
|
|
#include <msdosfs/msdosfsmount.h>
|
|
#include <msdosfs/fat.h>
|
|
|
|
MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure");
|
|
static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table");
|
|
|
|
static int mountmsdosfs __P((struct vnode *devvp, struct mount *mp,
|
|
struct proc *p));
|
|
static int msdosfs_fhtovp __P((struct mount *, struct fid *,
|
|
struct sockaddr *, struct vnode **, int *,
|
|
struct ucred **));
|
|
static int msdosfs_mount __P((struct mount *, char *, caddr_t,
|
|
struct nameidata *, struct proc *));
|
|
static int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t,
|
|
struct proc *));
|
|
static int msdosfs_root __P((struct mount *, struct vnode **));
|
|
static int msdosfs_start __P((struct mount *, int, struct proc *));
|
|
static int msdosfs_statfs __P((struct mount *, struct statfs *,
|
|
struct proc *));
|
|
static int msdosfs_sync __P((struct mount *, int, struct ucred *,
|
|
struct proc *));
|
|
static int msdosfs_unmount __P((struct mount *, int, struct proc *));
|
|
static int msdosfs_vget __P((struct mount *mp, ino_t ino,
|
|
struct vnode **vpp));
|
|
static int msdosfs_vptofh __P((struct vnode *, struct fid *));
|
|
|
|
/*
|
|
* mp - path - addr in user space of mount point (ie /usr or whatever)
|
|
* data - addr in user space of mount params including the name of the block
|
|
* special file to treat as a filesystem.
|
|
*/
|
|
static int
|
|
msdosfs_mount(mp, path, data, ndp, p)
|
|
struct mount *mp;
|
|
char *path;
|
|
caddr_t data;
|
|
struct nameidata *ndp;
|
|
struct proc *p;
|
|
{
|
|
struct vnode *devvp; /* vnode for blk device to mount */
|
|
struct msdosfs_args args; /* will hold data from mount request */
|
|
struct msdosfsmount *pmp; /* msdosfs specific mount control block */
|
|
int error, flags;
|
|
u_int size;
|
|
struct ucred *cred, *scred;
|
|
struct vattr va;
|
|
|
|
/*
|
|
* Copy in the args for the mount request.
|
|
*/
|
|
error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args));
|
|
if (error)
|
|
return error;
|
|
|
|
/*
|
|
* If they just want to update then be sure we can do what is
|
|
* asked. Can't change a filesystem from read/write to read only.
|
|
* Why? And if they've supplied a new device file name then we
|
|
* continue, otherwise return.
|
|
*/
|
|
if (mp->mnt_flag & MNT_UPDATE) {
|
|
pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
error = 0;
|
|
if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
|
|
flags = WRITECLOSE;
|
|
if (mp->mnt_flag & MNT_FORCE)
|
|
flags |= FORCECLOSE;
|
|
error = vflush(mp, NULLVP, flags);
|
|
}
|
|
if (!error && (mp->mnt_flag & MNT_RELOAD))
|
|
/* not yet implemented */
|
|
error = EINVAL;
|
|
if (error)
|
|
return error;
|
|
if (pmp->pm_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR))
|
|
pmp->pm_ronly = 0;
|
|
if (args.fspec == 0) {
|
|
/*
|
|
* Process export requests.
|
|
*/
|
|
return vfs_export(mp, &pmp->pm_export, &args.export);
|
|
}
|
|
} else
|
|
pmp = NULL;
|
|
|
|
/*
|
|
* check to see that the user in owns the target directory.
|
|
* Note the very XXX trick to make sure we're checking as the
|
|
* real user -- were mount() executable by anyone, this wouldn't
|
|
* be a problem.
|
|
*
|
|
* XXX there should be one consistent error out.
|
|
*/
|
|
cred = crdup(p->p_ucred); /* XXX */
|
|
cred->cr_uid = p->p_cred->p_ruid; /* XXX */
|
|
error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
|
|
if (error) {
|
|
crfree(cred); /* XXX */
|
|
return error;
|
|
}
|
|
if (cred->cr_uid != 0) {
|
|
if (va.va_uid != cred->cr_uid) {
|
|
error = EACCES;
|
|
crfree(cred); /* XXX */
|
|
return error;
|
|
}
|
|
|
|
/* a user mounted it; we'll verify permissions when unmounting */
|
|
mp->mnt_flag |= MNT_USER;
|
|
}
|
|
|
|
/*
|
|
* Now, lookup the name of the block device this mount or name
|
|
* update request is to apply to.
|
|
*/
|
|
NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
|
|
scred = p->p_ucred; /* XXX */
|
|
p->p_ucred = cred; /* XXX */
|
|
error = namei(ndp);
|
|
p->p_ucred = scred; /* XXX */
|
|
crfree(cred); /* XXX */
|
|
if (error != 0)
|
|
return error;
|
|
|
|
/*
|
|
* Be sure they've given us a block device to treat as a
|
|
* filesystem. And, that its major number is within the bdevsw
|
|
* table.
|
|
*/
|
|
devvp = ndp->ni_vp;
|
|
if (devvp->v_type != VBLK) {
|
|
vrele(devvp);
|
|
return ENOTBLK;
|
|
}
|
|
if (major(devvp->v_rdev) >= nblkdev) {
|
|
vrele(devvp);
|
|
return ENXIO;
|
|
}
|
|
|
|
/*
|
|
* If this is an update, then make sure the vnode for the block
|
|
* special device is the same as the one our filesystem is in.
|
|
*/
|
|
if (mp->mnt_flag & MNT_UPDATE) {
|
|
if (devvp != pmp->pm_devvp)
|
|
error = EINVAL;
|
|
else
|
|
vrele(devvp);
|
|
} else {
|
|
|
|
/*
|
|
* Well, it's not an update, it's a real mount request.
|
|
* Time to get dirty.
|
|
*/
|
|
error = mountmsdosfs(devvp, mp, p);
|
|
}
|
|
if (error) {
|
|
vrele(devvp);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Copy in the name of the directory the filesystem is to be
|
|
* mounted on. Then copy in the name of the block special file
|
|
* representing the filesystem being mounted. And we clear the
|
|
* remainder of the character strings to be tidy. Set up the
|
|
* user id/group id/mask as specified by the user. Then, we try to
|
|
* fill in the filesystem stats structure as best we can with
|
|
* whatever applies from a dos file system.
|
|
*/
|
|
pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname,
|
|
sizeof(mp->mnt_stat.f_mntonname) - 1, &size);
|
|
bzero(mp->mnt_stat.f_mntonname + size,
|
|
sizeof(mp->mnt_stat.f_mntonname) - size);
|
|
copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
|
|
bzero(mp->mnt_stat.f_mntfromname + size,
|
|
MNAMELEN - size);
|
|
pmp->pm_mounter = p->p_cred->p_ruid;
|
|
pmp->pm_gid = args.gid;
|
|
pmp->pm_uid = args.uid;
|
|
pmp->pm_mask = args.mask;
|
|
(void) msdosfs_statfs(mp, &mp->mnt_stat, p);
|
|
#ifdef MSDOSFS_DEBUG
|
|
printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mountmsdosfs(devvp, mp, p)
|
|
struct vnode *devvp;
|
|
struct mount *mp;
|
|
struct proc *p;
|
|
{
|
|
int i;
|
|
int bpc;
|
|
int bit;
|
|
int error;
|
|
int needclose;
|
|
int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
|
|
dev_t dev = devvp->v_rdev;
|
|
union bootsector *bsp;
|
|
struct msdosfsmount *pmp = NULL;
|
|
struct buf *bp0 = NULL;
|
|
struct byte_bpb33 *b33;
|
|
struct byte_bpb50 *b50;
|
|
#ifdef PC98
|
|
u_int pc98_wrk;
|
|
u_int Phy_Sector_Size;
|
|
#endif
|
|
|
|
/*
|
|
* Multiple mounts of the same block special file aren't allowed.
|
|
* Make sure no one else has the special file open. And flush any
|
|
* old buffers from this filesystem. Presumably this prevents us
|
|
* from running into buffers that are the wrong blocksize.
|
|
*/
|
|
error = vfs_mountedon(devvp);
|
|
if (error)
|
|
return error;
|
|
if (vcount(devvp) > 1)
|
|
return EBUSY;
|
|
error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0);
|
|
if (error)
|
|
return error;
|
|
|
|
/*
|
|
* Now open the block special file.
|
|
*/
|
|
error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p);
|
|
if (error)
|
|
return error;
|
|
needclose = 1;
|
|
#ifdef HDSUPPORT
|
|
/*
|
|
* Put this in when we support reading dos filesystems from
|
|
* partitioned harddisks.
|
|
*/
|
|
if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) {
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Read the boot sector of the filesystem, and then check the boot
|
|
* signature. If not a dos boot sector then error out. We could
|
|
* also add some checking on the bsOemName field. So far I've seen
|
|
* the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0"
|
|
*/
|
|
#ifdef PC98
|
|
devvp->v_flag &= 0xffff;
|
|
error = bread(devvp, 0, 1024, NOCRED, &bp0);
|
|
#else
|
|
error = bread(devvp, 0, 512, NOCRED, &bp0);
|
|
#endif
|
|
if (error)
|
|
goto error_exit;
|
|
bp0->b_flags |= B_AGE;
|
|
bsp = (union bootsector *) bp0->b_data;
|
|
b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB;
|
|
b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB;
|
|
#ifdef MSDOSFS_CHECKSIG
|
|
#ifdef PC98
|
|
if (bsp->bs50.bsBootSectSig != BOOTSIG &&
|
|
bsp->bs50.bsBootSectSig != 0 && /* PC98 DOS 3.3x */
|
|
bsp->bs50.bsBootSectSig != 15760 && /* PC98 DOS 5.0 */
|
|
bsp->bs50.bsBootSectSig != 64070) { /* PC98 DOS 3.3B */
|
|
#else
|
|
if (bsp->bs50.bsBootSectSig != BOOTSIG) {
|
|
#endif
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
#endif
|
|
if ( bsp->bs50.bsJump[0] != 0xe9 &&
|
|
(bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) {
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
|
|
pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
|
|
bzero((caddr_t)pmp, sizeof *pmp);
|
|
pmp->pm_mountp = mp;
|
|
|
|
/*
|
|
* Compute several useful quantities from the bpb in the
|
|
* bootsector. Copy in the dos 5 variant of the bpb then fix up
|
|
* the fields that are different between dos 5 and dos 3.3.
|
|
*/
|
|
pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
|
|
pmp->pm_SectPerClust = b50->bpbSecPerClust;
|
|
pmp->pm_ResSectors = getushort(b50->bpbResSectors);
|
|
pmp->pm_FATs = b50->bpbFATs;
|
|
pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
|
|
pmp->pm_Sectors = getushort(b50->bpbSectors);
|
|
pmp->pm_Media = b50->bpbMedia;
|
|
pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
|
|
pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
|
|
pmp->pm_Heads = getushort(b50->bpbHeads);
|
|
|
|
/* XXX - We should probably check more values here */
|
|
if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust ||
|
|
!pmp->pm_Heads || pmp->pm_Heads > 255 ||
|
|
#ifdef PC98
|
|
!pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) {
|
|
#else
|
|
!pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) {
|
|
#endif
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
|
|
if (pmp->pm_Sectors == 0) {
|
|
pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
|
|
pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
|
|
} else {
|
|
pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
|
|
pmp->pm_HugeSectors = pmp->pm_Sectors;
|
|
}
|
|
#ifdef PC98 /* for PC98 added Satoshi Yasuda */
|
|
Phy_Sector_Size = 512;
|
|
if ((devvp->v_rdev>>8) == 2) { /* floppy check */
|
|
if (((devvp->v_rdev&077) == 2) && (pmp->pm_HugeSectors == 1232)) {
|
|
Phy_Sector_Size = 1024; /* 2HD */
|
|
/*
|
|
* 1024byte/sector support
|
|
*/
|
|
devvp->v_flag |= 0x10000;
|
|
} else {
|
|
if ((((devvp->v_rdev&077) == 3) /* 2DD 8 or 9 sector */
|
|
&& (pmp->pm_HugeSectors == 1440)) /* 9 sector */
|
|
|| (((devvp->v_rdev&077) == 4)
|
|
&& (pmp->pm_HugeSectors == 1280)) /* 8 sector */
|
|
|| (((devvp->v_rdev&077) == 5)
|
|
&& (pmp->pm_HugeSectors == 2880))) { /* 1.44M */
|
|
Phy_Sector_Size = 512;
|
|
} else {
|
|
if (((devvp->v_rdev&077) != 1)
|
|
&& ((devvp->v_rdev&077) != 0)) { /* 2HC */
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size;
|
|
pmp->pm_BytesPerSec = Phy_Sector_Size;
|
|
pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk;
|
|
pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk;
|
|
pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk;
|
|
pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk;
|
|
pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk;
|
|
pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk;
|
|
#endif /* */
|
|
pmp->pm_fatblk = pmp->pm_ResSectors;
|
|
pmp->pm_rootdirblk = pmp->pm_fatblk +
|
|
(pmp->pm_FATs * pmp->pm_FATsecs);
|
|
pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry))
|
|
/
|
|
pmp->pm_BytesPerSec;/* in sectors */
|
|
pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
|
|
pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
|
|
pmp->pm_SectPerClust;
|
|
pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
|
|
pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
|
|
if (FAT12(pmp))
|
|
/*
|
|
* This will usually be a floppy disk. This size makes sure
|
|
* that one fat entry will not be split across multiple
|
|
* blocks.
|
|
*/
|
|
pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
|
|
else
|
|
/*
|
|
* This will usually be a hard disk. Reading or writing one
|
|
* block should be quite fast.
|
|
*/
|
|
pmp->pm_fatblocksize = MAXBSIZE;
|
|
pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
|
|
|
|
|
|
if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0)
|
|
printf("mountmsdosfs(): Warning: root directory is not a multiple of the clustersize in length\n");
|
|
|
|
/*
|
|
* Compute mask and shift value for isolating cluster relative byte
|
|
* offsets and cluster numbers from a file offset.
|
|
*/
|
|
bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec;
|
|
pmp->pm_bpcluster = bpc;
|
|
pmp->pm_depclust = bpc / sizeof(struct direntry);
|
|
pmp->pm_crbomask = bpc - 1;
|
|
if (bpc == 0) {
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
bit = 1;
|
|
for (i = 0; i < 32; i++) {
|
|
if (bit & bpc) {
|
|
if (bit ^ bpc) {
|
|
error = EINVAL;
|
|
goto error_exit;
|
|
}
|
|
pmp->pm_cnshift = i;
|
|
break;
|
|
}
|
|
bit <<= 1;
|
|
}
|
|
|
|
#ifdef PC98
|
|
if (Phy_Sector_Size == 512) {
|
|
pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
|
|
pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
|
|
} else {
|
|
pmp->pm_brbomask = 0x03ff;
|
|
pmp->pm_bnshift = 10;
|
|
}
|
|
#else
|
|
pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */
|
|
pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */
|
|
#endif
|
|
|
|
/*
|
|
* Release the bootsector buffer.
|
|
*/
|
|
brelse(bp0);
|
|
bp0 = NULL;
|
|
|
|
/*
|
|
* Allocate memory for the bitmap of allocated clusters, and then
|
|
* fill it in.
|
|
*/
|
|
pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1)
|
|
/ N_INUSEBITS)
|
|
* sizeof(*pmp->pm_inusemap),
|
|
M_MSDOSFSFAT, M_WAITOK);
|
|
|
|
/*
|
|
* fillinusemap() needs pm_devvp.
|
|
*/
|
|
pmp->pm_dev = dev;
|
|
pmp->pm_devvp = devvp;
|
|
|
|
/*
|
|
* Have the inuse map filled in.
|
|
*/
|
|
error = fillinusemap(pmp);
|
|
if (error)
|
|
goto error_exit;
|
|
|
|
/*
|
|
* If they want fat updates to be synchronous then let them suffer
|
|
* the performance degradation in exchange for the on disk copy of
|
|
* the fat being correct just about all the time. I suppose this
|
|
* would be a good thing to turn on if the kernel is still flakey.
|
|
*/
|
|
pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS;
|
|
|
|
/*
|
|
* Finish up.
|
|
*/
|
|
pmp->pm_ronly = ronly;
|
|
if (ronly == 0)
|
|
pmp->pm_fmod = 1;
|
|
mp->mnt_data = (qaddr_t) pmp;
|
|
mp->mnt_stat.f_fsid.val[0] = (long)dev;
|
|
mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
|
|
mp->mnt_flag |= MNT_LOCAL;
|
|
devvp->v_specflags |= SI_MOUNTEDON;
|
|
|
|
return 0;
|
|
|
|
error_exit:;
|
|
if (bp0)
|
|
brelse(bp0);
|
|
if (needclose)
|
|
(void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE,
|
|
NOCRED, p);
|
|
if (pmp) {
|
|
if (pmp->pm_inusemap)
|
|
free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
|
|
free((caddr_t) pmp, M_MSDOSFSMNT);
|
|
mp->mnt_data = (qaddr_t) 0;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
msdosfs_start(mp, flags, p)
|
|
struct mount *mp;
|
|
int flags;
|
|
struct proc *p;
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Unmount the filesystem described by mp.
|
|
*/
|
|
static int
|
|
msdosfs_unmount(mp, mntflags, p)
|
|
struct mount *mp;
|
|
int mntflags;
|
|
struct proc *p;
|
|
{
|
|
int flags = 0;
|
|
int error;
|
|
struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
|
|
/* only the mounter, or superuser can unmount */
|
|
if ((p->p_cred->p_ruid != pmp->pm_mounter) &&
|
|
(error = suser(p->p_ucred, &p->p_acflag)))
|
|
return error;
|
|
|
|
if (mntflags & MNT_FORCE) {
|
|
flags |= FORCECLOSE;
|
|
}
|
|
error = vflush(mp, NULLVP, flags);
|
|
if (error)
|
|
return error;
|
|
pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON;
|
|
error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE,
|
|
NOCRED, p);
|
|
vrele(pmp->pm_devvp);
|
|
free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT);
|
|
free((caddr_t) pmp, M_MSDOSFSMNT);
|
|
mp->mnt_data = (qaddr_t) 0;
|
|
mp->mnt_flag &= ~MNT_LOCAL;
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
msdosfs_root(mp, vpp)
|
|
struct mount *mp;
|
|
struct vnode **vpp;
|
|
{
|
|
struct denode *ndep;
|
|
struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data);
|
|
int error;
|
|
|
|
error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep);
|
|
#ifdef MSDOSFS_DEBUG
|
|
printf("msdosfs_root(); mp %p, pmp %p, ndep %p, vp %p\n",
|
|
mp, pmp, ndep, DETOV(ndep));
|
|
#endif
|
|
if (error == 0)
|
|
*vpp = DETOV(ndep);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
msdosfs_quotactl(mp, cmds, uid, arg, p)
|
|
struct mount *mp;
|
|
int cmds;
|
|
uid_t uid;
|
|
caddr_t arg;
|
|
struct proc *p;
|
|
{
|
|
return EOPNOTSUPP;
|
|
}
|
|
|
|
static int
|
|
msdosfs_statfs(mp, sbp, p)
|
|
struct mount *mp;
|
|
struct statfs *sbp;
|
|
struct proc *p;
|
|
{
|
|
struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
|
|
/*
|
|
* Fill in the stat block.
|
|
*/
|
|
sbp->f_bsize = pmp->pm_bpcluster;
|
|
sbp->f_iosize = pmp->pm_bpcluster;
|
|
sbp->f_blocks = pmp->pm_nmbrofclusters;
|
|
sbp->f_bfree = pmp->pm_freeclustercount;
|
|
sbp->f_bavail = pmp->pm_freeclustercount;
|
|
sbp->f_files = pmp->pm_RootDirEnts; /* XXX */
|
|
sbp->f_ffree = 0; /* what to put in here? */
|
|
|
|
/*
|
|
* Copy the mounted on and mounted from names into the passed in
|
|
* stat block, if it is not the one in the mount structure.
|
|
*/
|
|
if (sbp != &mp->mnt_stat) {
|
|
sbp->f_type = mp->mnt_vfc->vfc_typenum;
|
|
bcopy((caddr_t) mp->mnt_stat.f_mntonname,
|
|
(caddr_t) & sbp->f_mntonname[0], MNAMELEN);
|
|
bcopy((caddr_t) mp->mnt_stat.f_mntfromname,
|
|
(caddr_t) & sbp->f_mntfromname[0], MNAMELEN);
|
|
}
|
|
#if 0
|
|
strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN);
|
|
sbp->f_fstypename[MFSNAMELEN] = '\0';
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
msdosfs_sync(mp, waitfor, cred, p)
|
|
struct mount *mp;
|
|
int waitfor;
|
|
struct ucred *cred;
|
|
struct proc *p;
|
|
{
|
|
struct vnode *vp;
|
|
struct denode *dep;
|
|
struct msdosfsmount *pmp;
|
|
int error;
|
|
int allerror = 0;
|
|
|
|
pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
|
|
/*
|
|
* If we ever switch to not updating all of the fats all the time,
|
|
* this would be the place to update them from the first one.
|
|
*/
|
|
if (pmp->pm_fmod)
|
|
if (pmp->pm_ronly)
|
|
panic("msdosfs_sync: rofs mod");
|
|
else {
|
|
/* update fats here */
|
|
}
|
|
|
|
/*
|
|
* Go thru in memory denodes and write them out along with
|
|
* unwritten file blocks.
|
|
*/
|
|
simple_lock(&mntvnode_slock);
|
|
loop:
|
|
for (vp = mp->mnt_vnodelist.lh_first; vp;
|
|
vp = vp->v_mntvnodes.le_next) {
|
|
if (vp->v_mount != mp) /* not ours anymore */
|
|
goto loop;
|
|
simple_lock(&vp->v_interlock);
|
|
dep = VTODE(vp);
|
|
if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 &&
|
|
vp->v_dirtyblkhd.lh_first == NULL) {
|
|
simple_unlock(&vp->v_interlock);
|
|
continue;
|
|
}
|
|
simple_unlock(&mntvnode_slock);
|
|
error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
|
|
if (error) {
|
|
simple_lock(&mntvnode_slock);
|
|
if (error == ENOENT)
|
|
goto loop;
|
|
continue;
|
|
}
|
|
error = VOP_FSYNC(vp, cred, waitfor, p);
|
|
if (error)
|
|
allerror = error;
|
|
VOP_UNLOCK(vp, 0, p);
|
|
vrele(vp); /* done with this one */
|
|
simple_lock(&mntvnode_slock);
|
|
}
|
|
simple_unlock(&mntvnode_slock);
|
|
|
|
/*
|
|
* Flush filesystem control info.
|
|
*/
|
|
error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p);
|
|
if (error)
|
|
allerror = error;
|
|
return allerror;
|
|
}
|
|
|
|
static int
|
|
msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
|
|
struct mount *mp;
|
|
struct fid *fhp;
|
|
struct sockaddr *nam;
|
|
struct vnode **vpp;
|
|
int *exflagsp;
|
|
struct ucred **credanonp;
|
|
{
|
|
struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data;
|
|
struct defid *defhp = (struct defid *) fhp;
|
|
struct denode *dep;
|
|
struct netcred *np;
|
|
int error;
|
|
|
|
np = vfs_export_lookup(mp, &pmp->pm_export, nam);
|
|
if (np == NULL)
|
|
return EACCES;
|
|
error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs,
|
|
NULL, &dep);
|
|
if (error) {
|
|
*vpp = NULLVP;
|
|
return error;
|
|
}
|
|
*vpp = DETOV(dep);
|
|
*exflagsp = np->netc_exflags;
|
|
*credanonp = &np->netc_anon;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
msdosfs_vptofh(vp, fhp)
|
|
struct vnode *vp;
|
|
struct fid *fhp;
|
|
{
|
|
struct denode *dep = VTODE(vp);
|
|
struct defid *defhp = (struct defid *) fhp;
|
|
|
|
defhp->defid_len = sizeof(struct defid);
|
|
defhp->defid_dirclust = dep->de_dirclust;
|
|
defhp->defid_dirofs = dep->de_diroffset;
|
|
/* defhp->defid_gen = ip->i_gen; */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
msdosfs_vget(mp, ino, vpp)
|
|
struct mount *mp;
|
|
ino_t ino;
|
|
struct vnode **vpp;
|
|
{
|
|
return EOPNOTSUPP;
|
|
}
|
|
|
|
static struct vfsops msdosfs_vfsops = {
|
|
msdosfs_mount,
|
|
msdosfs_start,
|
|
msdosfs_unmount,
|
|
msdosfs_root,
|
|
msdosfs_quotactl,
|
|
msdosfs_statfs,
|
|
msdosfs_sync,
|
|
msdosfs_vget,
|
|
msdosfs_fhtovp,
|
|
msdosfs_vptofh,
|
|
msdosfs_init
|
|
};
|
|
|
|
VFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0);
|