#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/signalvar.h>
#include <sys/signal2.h>
#include <sys/spinlock2.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/sysmsg.h>
#include <sys/mman.h>
#include <sys/linker.h>
#include <sys/fcntl.h>
#include <sys/nlookup.h>
#include <sys/devfs.h>
#include <vm/vm.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
#include <vm/vnode_pager.h>
#include <vm/vm_extern.h>
#include <sys/buf2.h>
#include <sys/thread2.h>
#include <vfs/ufs/quota.h>
#include <vfs/ufs/inode.h>
#include <vfs/ufs/ufsmount.h>
#include <vfs/ufs/ufs_extern.h>
#include <vfs/ufs/fs.h>
#include <vfs/ufs/ffs_extern.h>
#include "mfsnode.h"
#include "mfs_extern.h"
MALLOC_DEFINE(M_MFSNODE, "MFS node", "MFS vnode private part");
static int mfs_mount (struct mount *mp,
char *path, caddr_t data, struct ucred *td);
static int mfs_start (struct mount *mp, int flags);
static int mfs_statfs (struct mount *mp, struct statfs *sbp,
struct ucred *cred);
static int mfs_init (struct vfsconf *);
static void mfs_doio(struct bio *bio, struct mfsnode *mfsp);
d_open_t mfsopen;
d_close_t mfsclose;
d_strategy_t mfsstrategy;
static struct dev_ops mfs_ops = {
{ "MFS", -1, D_DISK | D_NOEMERGPGR },
.d_open = mfsopen,
.d_close = mfsclose,
.d_read = physread,
.d_write = physwrite,
.d_strategy = mfsstrategy,
};
static struct vfsops mfs_vfsops = {
.vfs_flags = 0,
.vfs_mount = mfs_mount,
.vfs_start = mfs_start,
.vfs_unmount = ffs_unmount,
.vfs_root = ufs_root,
.vfs_quotactl = ufs_quotactl,
.vfs_statfs = mfs_statfs,
.vfs_sync = ffs_sync,
.vfs_vget = ffs_vget,
.vfs_fhtovp = ffs_fhtovp,
.vfs_checkexp = ufs_check_export,
.vfs_vptofh = ffs_vptofh,
.vfs_init = mfs_init
};
VFS_SET(mfs_vfsops, mfs, 0);
MODULE_VERSION(mfs, 1);
int
mfsopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
#if 0
if (ap->a_oflags & FWRITE)
return(EROFS);
#endif
if (dev->si_drv1)
return(0);
return(ENXIO);
}
int
mfsclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct mfsnode *mfsp;
if ((mfsp = dev->si_drv1) == NULL)
return(0);
mfsp->mfs_active = 0;
wakeup((caddr_t)mfsp);
return(0);
}
int
mfsstrategy(struct dev_strategy_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bio *bio = ap->a_bio;
struct buf *bp = bio->bio_buf;
off_t boff = bio->bio_offset;
off_t eoff = boff + bp->b_bcount;
struct mfsnode *mfsp;
if ((mfsp = dev->si_drv1) == NULL) {
bp->b_error = ENXIO;
goto error;
}
if (boff < 0)
goto bad;
if (eoff > mfsp->mfs_size) {
if (boff > mfsp->mfs_size || (bp->b_flags & B_BNOCLIP))
goto bad;
if (boff == mfsp->mfs_size) {
bp->b_resid = bp->b_bcount;
bp->b_flags |= B_INVAL;
goto done;
}
bp->b_bcount = mfsp->mfs_size - boff;
}
if (mfsp->mfs_td == curthread) {
mfs_doio(bio, mfsp);
} else {
bioq_insert_tail(&mfsp->bio_queue, bio);
wakeup((caddr_t)mfsp);
}
return(0);
bad:
bp->b_error = EINVAL;
error:
bp->b_flags |= B_ERROR | B_INVAL;
done:
biodone(bio);
return(0);
}
static int
mfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
{
struct vnode *devvp;
struct mfs_args args;
struct ufsmount *ump;
struct fs *fs;
struct mfsnode *mfsp;
struct nlookupdata nd;
size_t size;
char devname[16];
int flags;
int minnum;
int error;
cdev_t dev;
if (path == NULL) {
panic("mfs_mount: mount MFS as root: not configured!");
}
mfsp = NULL;
error = copyin(data, (caddr_t)&args, sizeof (struct mfs_args));
if (error)
goto error_1;
if (mp->mnt_flag & MNT_UPDATE) {
ump = VFSTOUFS(mp);
fs = ump->um_fs;
if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
flags = WRITECLOSE;
if (mp->mnt_flag & MNT_FORCE)
flags |= FORCECLOSE;
error = ffs_flushfiles(mp, flags);
if (error)
goto error_1;
}
if (fs->fs_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
fs->fs_ronly = 0;
}
if (args.fspec == 0) {
error = vfs_export(mp, &ump->um_export, &args.export);
goto success;
}
goto success;
}
mfsp = kmalloc(sizeof *mfsp, M_MFSNODE, M_WAITOK | M_ZERO);
minnum = (int)curproc->p_pid;
dev = make_dev(&mfs_ops, minnum, UID_ROOT, GID_WHEEL, 0600,
"mfs%d", minnum);
dev->si_bsize_phys = DEV_BSIZE;
dev->si_iosize_max = MAXPHYS;
dev->si_drv1 = mfsp;
mfsp->mfs_baseoff = args.base;
mfsp->mfs_size = args.size;
mfsp->mfs_dev = dev;
mfsp->mfs_td = curthread;
mfsp->mfs_active = 1;
bioq_init(&mfsp->bio_queue);
devfs_config();
ksnprintf(devname, sizeof(devname), "/dev/mfs%d", minnum);
nlookup_init(&nd, devname, UIO_SYSSPACE, 0);
devvp = NULL;
error = nlookup(&nd);
if (error == 0) {
devvp = nd.nl_nch.ncp->nc_vp;
if (devvp == NULL)
error = ENOENT;
error = vget(devvp, LK_SHARED);
}
nlookup_done(&nd);
if (error)
goto error_1;
vn_unlock(devvp);
vnode_pager_setsize(devvp, args.size);
copyinstr(args.fspec,
mp->mnt_stat.f_mntfromname,
MNAMELEN - 1,
&size);
bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
error = ffs_mountfs(devvp, mp, M_MFSNODE);
if (error) {
mfsp->mfs_active = 0;
goto error_2;
}
VFS_STATFS(mp, &mp->mnt_stat, cred);
mp->mnt_kern_flag |= MNTK_ST_MPSAFE;
goto success;
error_2:
vrele(devvp);
error_1:
if (mfsp) {
if (mfsp->mfs_dev) {
destroy_dev(mfsp->mfs_dev);
mfsp->mfs_dev = NULL;
}
kfree(mfsp, M_MFSNODE);
}
success:
return(error);
}
static int
mfs_start(struct mount *mp, int flags)
{
struct vnode *vp = VFSTOUFS(mp)->um_devvp;
struct mfsnode *mfsp = vp->v_rdev->si_drv1;
struct bio *bio;
struct buf *bp;
int gotsig = 0, sig;
thread_t td = curthread;
KKASSERT(curproc);
PHOLD(curproc);
mfsp->mfs_td = td;
while (mfsp->mfs_active) {
crit_enter();
while ((bio = bioq_takefirst(&mfsp->bio_queue)) != NULL) {
crit_exit();
bp = bio->bio_buf;
mfs_doio(bio, mfsp);
wakeup(bp);
crit_enter();
}
crit_exit();
if (gotsig) {
gotsig = 0;
if (dounmount(mp, 0, 0) != 0) {
KKASSERT(td->td_proc);
lwkt_gettoken(&td->td_proc->p_token);
sig = CURSIG(td->td_lwp);
if (sig) {
spin_lock(&td->td_lwp->lwp_spin);
lwp_delsig(td->td_lwp, sig, 1);
spin_unlock(&td->td_lwp->lwp_spin);
}
lwkt_reltoken(&td->td_proc->p_token);
}
}
else if (tsleep((caddr_t)mfsp, PCATCH, "mfsidl", 0))
gotsig++;
}
PRELE(curproc);
if (mfsp->mfs_dev) {
destroy_dev(mfsp->mfs_dev);
mfsp->mfs_dev = NULL;
}
kfree(mfsp, M_MFSNODE);
return (EMOUNTEXIT);
}
static int
mfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
{
int error;
error = ffs_statfs(mp, sbp, cred);
sbp->f_type = mp->mnt_vfc->vfc_typenum;
return (error);
}
static int
mfs_init(struct vfsconf *vfsp)
{
return (0);
}
static void
mfs_doio(struct bio *bio, struct mfsnode *mfsp)
{
struct buf *bp = bio->bio_buf;
caddr_t base = mfsp->mfs_baseoff + bio->bio_offset;
int bytes;
switch(bp->b_cmd) {
case BUF_CMD_FREEBLKS:
bytes = bp->b_bcount;
if ((vm_offset_t)base & PAGE_MASK) {
int n = PAGE_SIZE - ((vm_offset_t)base & PAGE_MASK);
bytes -= n;
base += n;
}
if (bytes > 0) {
struct madvise_args uap;
bytes &= ~PAGE_MASK;
if (bytes != 0) {
struct sysmsg sysmsg;
bzero(&sysmsg, sizeof(sysmsg));
bzero(&uap, sizeof(uap));
uap.addr = base;
uap.len = bytes;
uap.behav = MADV_FREE;
sys_madvise(&sysmsg, &uap);
}
}
bp->b_error = 0;
break;
case BUF_CMD_READ:
bp->b_error = copyin(base, bp->b_data, bp->b_bcount);
break;
case BUF_CMD_WRITE:
bp->b_error = copyout(bp->b_data, base, bp->b_bcount);
break;
default:
panic("mfs: bad b_cmd %d", bp->b_cmd);
}
if (bp->b_error)
bp->b_flags |= B_ERROR;
biodone(bio);
}