/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* 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 Christian E. Hopps.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*/
if ((mp->mnt_flag & MNT_RDONLY) == 0)
return (EROFS);
if ((mp->mnt_flag & MNT_UPDATE) && args->fspec == NULL)
return EOPNOTSUPP;
/*
* Not an update, or updating the name: look up the name
* and verify that it refers to a sensible block device.
*/
error = namei_simple_user(args->fspec,
NSM_FOLLOW_NOEMULROOT, &devvp);
if (error != 0)
return (error);
if (devvp->v_type != VBLK) {
vrele(devvp);
return (ENOTBLK);
}
if (bdevsw_lookup(devvp->v_rdev) == NULL) {
vrele(devvp);
return (ENXIO);
}
/*
* If mount by non-root, then verify that user has necessary
* permissions on the device.
*/
accessmode = VREAD;
if ((mp->mnt_flag & MNT_RDONLY) == 0)
accessmode |= VWRITE;
vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode));
VOP_UNLOCK(devvp);
if (error) {
vrele(devvp);
return (error);
}
/* MNT_UPDATE? */
if ((error = adosfs_mountfs(devvp, mp, l)) != 0) {
vrele(devvp);
return (error);
}
amp = VFSTOADOSFS(mp);
amp->uid = args->uid;
amp->gid = args->gid;
amp->mask = args->mask;
return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
mp->mnt_op->vfs_name, mp, l);
}
/*
* compute filesystem parameters from disklabel
* on arch/amiga the disklabel is computed from the native
* partition tables
* - p_fsize is the filesystem block size
* - p_frag is the number of sectors per filesystem block
* - p_cpg is the number of reserved blocks (boot blocks)
* - p_psize is reduced by the number of preallocated blocks
* at the end of a partition
*
* XXX
* - bsize and secsperblk could be computed from the first sector
* of the root block
* - resvblks (the number of boot blocks) can only be guessed
* by scanning for the root block as its position moves
* with resvblks
*/
error = VOP_IOCTL(devvp, DIOCGDINFO, &dl, FREAD, NOCRED);
VOP_UNLOCK(devvp);
if (error)
goto fail;
parp = &dl.d_partitions[DISKPART(devvp->v_rdev)];
if (dl.d_type == DKTYPE_FLOPPY) {
amp->bsize = secsize;
secsperblk = 1;
resvblks = 2;
} else if (parp->p_fsize > 0 && parp->p_frag > 0) {
amp->bsize = parp->p_fsize * parp->p_frag;
secsperblk = parp->p_frag;
resvblks = parp->p_cpg;
} else {
error = EINVAL;
goto fail;
}
blksperdisk = numsecs / secsperblk;
/* The filesystem variant ('dostype') is stored in the boot block */
bp = NULL;
if ((error = bread(devvp, (daddr_t)BBOFF,
amp->bsize, 0, &bp)) != 0) {
goto fail;
}
amp->dostype = adoswordn(bp, 0);
brelse(bp, 0);
/*
* get the root anode, if not a valid fs this will fail.
*/
if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp)) != 0)
goto fail;
/* allocate and load bitmap, set free space */
bitmap_sz = ((amp->numblks + 31) / 32) * sizeof(*amp->bitmap);
amp->bitmap = kmem_alloc(bitmap_sz, KM_SLEEP);
adosfs_loadbitmap(amp);
if (mp->mnt_flag & MNT_RDONLY) {
/*
* Don't need the bitmap any more if it's read-only.
*/
kmem_free(amp->bitmap, bitmap_sz);
amp->bitmap = NULL;
}
vput(rvp);
/*
* lookup an anode, if not found, create
* return locked and referenced
*/
int
adosfs_vget(struct mount *mp, ino_t an, int lktype, struct vnode **vpp)
{
u_long block;
int error;
if (ap->type == AROOT) {
ap->adprot = 15;
ap->uid = amp->uid;
ap->gid = amp->gid;
} else {
ap->adprot = adoswordn(bp, ap->nwords - 48) ^ 15;
/*
* ADOS directories do not have a `x' protection bit as
* it is known in VFS; this functionality is fulfilled
* by the ADOS `r' bit.
*
* To retain the ADOS behaviour, fake execute permissions
* in that case.
*/
if ((ap->type == ADIR || ap->type == ALDIR) &&
(ap->adprot & 0x00000008) == 0)
ap->adprot &= ~0x00000002;
/*
* Get uid/gid from extensions in file header
* (really need to know if this is a muFS partition)
*/
ap->uid = (adoswordn(bp, ap->nwords - 49) >> 16) & 0xffff;
ap->gid = adoswordn(bp, ap->nwords - 49) & 0xffff;
if (ap->uid || ap->gid) {
if (ap->uid == 0xffff)
ap->uid = 0;
if (ap->gid == 0xffff)
ap->gid = 0;
ap->adprot |= 0x40000000; /* Kludge */
}
else {
/*
* uid & gid extension don't exist,
* so use the mount-point uid/gid
*/
ap->uid = amp->uid;
ap->gid = amp->gid;
}
}
ap->mtime.days = adoswordn(bp, ap->nwords - 23);
ap->mtime.mins = adoswordn(bp, ap->nwords - 22);
ap->mtime.ticks = adoswordn(bp, ap->nwords - 21);
bad:
if (bp)
brelse(bp, 0);
pool_put(&adosfs_node_pool, ap);
return error;
}
/*
* Load the bitmap into memory, and count the number of available
* blocks.
* The bitmap will be released if the filesystem is read-only; it's
* only needed to find the free space.
*/
int
adosfs_loadbitmap(struct adosfsmount *amp)
{
struct buf *bp, *mapbp;
u_long bn;
int blkix, endix, mapix;
int bmsize;
int error;
if (adoswordn(bp, blkix) == 0)
break;
if (mapbp != NULL)
brelse(mapbp, 0);
if ((error = bread(amp->devvp,
adoswordn(bp, blkix) * amp->bsize / DEV_BSIZE, amp->bsize,
0, &mapbp)) != 0)
break;
if (adoscksum(mapbp, amp->nwords)) {
#ifdef DIAGNOSTIC
printf("adosfs: loadbitmap - cksum of blk %d failed\n",
adoswordn(bp, blkix));
#endif
/* XXX Force read-only? Set free space 0? */
break;
}
n = 1;
while (n < amp->nwords && mapix < bmsize) {
amp->bitmap[mapix++] = bits = adoswordn(mapbp, n);
++n;
if (mapix == bmsize && amp->numblks & 31)
bits &= ~(0xffffffff << (amp->numblks & 31));
while (bits) {
if (bits & 1)
++amp->freeblks;
bits >>= 1;
}
}
++blkix;
if (mapix < bmsize && blkix == endix) {
bn = adoswordn(bp, blkix);
brelse(bp, 0);
if ((error = bread(amp->devvp, bn * amp->bsize / DEV_BSIZE,
amp->bsize, 0, &bp)) != 0)
break;
/*
* Why is there no checksum on these blocks?
*/
blkix = 0;
endix = amp->nwords - 1;
}
}
if (bp)
brelse(bp, 0);
if (mapbp)
brelse(mapbp, 0);
return (error);
}
/*
* File handle to vnode
*
* Have to be really careful about stale file handles:
* - check that the inode number is in range
* - call iget() to get the locked inode
* - check for an unallocated inode (i_mode == 0)
* - check that the generation number matches
*/
struct ifid {
ushort ifid_len;
ushort ifid_pad;
int ifid_ino;
long ifid_start;
};
int
adosfs_fhtovp(struct mount *mp, struct fid *fhp, int lktype,
struct vnode **vpp)
{
struct ifid ifh;
#if 0
struct anode *ap;
#endif
struct vnode *nvp;
int error;
if (fhp->fid_len != sizeof(struct ifid))
return EINVAL;
static int
adosfs_modcmd(modcmd_t cmd, void *arg)
{
int error;
switch (cmd) {
case MODULE_CMD_INIT:
error = vfs_attach(&adosfs_vfsops);
if (error != 0)
break;
/*
* XXX the "16" above could be dynamic, thereby eliminating
* one more instance of the "number to vfs" mapping problem,
* but "16" is the order as taken from sys/mount.h
*/
break;
case MODULE_CMD_FINI:
error = vfs_detach(&adosfs_vfsops);
if (error != 0)
break;
break;
default:
error = ENOTTY;
break;
}