#define MODULE_ID	5050

/***
Copyright (c) 1991, 1992 by Frank J. Edwards
See the file COPYRIGHT in this distribution for copyright details.
***/

#ident "$Id: vfsops.c,v 1.25 1992/12/13 15:59:20 root Exp root $";

#include "sys/types.h"
#include "sys/buf.h"
#include "sys/cred.h"
#include "sys/errno.h"
#include "sys/file.h"
#include "sys/inline.h"
#include "sys/mount.h"
#include "sys/open.h"
#include "sys/param.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/uio.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/kmem.h"
#include "sys/statvfs.h"
#include "sys/stat.h"
#include "fs/fs_subr.h"

#include "sys/proc.h"		/* For "proc_t" in <disp.h> */
#include "sys/disp.h"		/* For "extern curproc" */
#include "sys/fbuf.h"		/* Added for ADOS_fs */
#include "vm/seg.h"		/* Added for ADOS_fs */
#include "missing.h"

#include "ados.h"

int ados_init(struct vfssw *vswp, int fstype);
static int ados_mount(struct vfs *vfsp, vnode_t *mvp, struct mounta *uap,
	struct cred *cr);
static int ados_unmount(struct vfs *vfsp, struct cred *cr);
static int ados_root(struct vfs *vfsp, vnode_t **vpp);
static int ados_sync(struct vfs *vfsp, short flag, struct cred *cr);
static int ados_vget(struct vfs *vfsp, vnode_t **vpp, struct fid *fidp);
static int ados_statvfs(struct vfs *vfsp, struct statvfs *sp);

int ados_type;

/*NOSTRICT*/
struct vfsops ados_vfsops = {
#ifdef lint
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#else
    ados_mount,
    ados_unmount,
    ados_root,
    ados_statvfs,
    ados_sync,			/* ados_sync, */
    ados_vget,			/* */
    fs_nosys,			/* mountroot */
    fs_nosys,			/* swapvp */
    fs_nosys,			/* filler */
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
#endif
};

int ados_init(struct vfssw * vswp, int fstype)
{
    vswp->vsw_vfsops = &ados_vfsops;
    ados_type = fstype;
    return 0;
}

static int ados_mount(struct vfs * vfsp, vnode_t * mvp,
		          struct mounta * uap, struct cred * cr)
{
    struct ados *afsp;
    struct fbuf *mapp;
    anode_t *root;
    vnode_t *rvp;
    struct a_rdir *disk;
    struct mntparam { int len, rootblk, dos; } mntparam;
    int error;
    dev_t dev;
    off_t nbytes;

IN_FUNC(ados_mount);
    if ((uap->flags & MS_RDONLY) == 0)
	return EROFS;

    if (copyin((caddr_t) uap->dataptr, (caddr_t) &mntparam, sizeof(mntparam)))
	return EFAULT;

    /*
     * Resolve path name of special file being mounted.
     */
    if (error = lookupname(uap->spec, UIO_USERSPACE, FOLLOW, NULLVPP, &rvp))
	return error;

    if (rvp->v_type != VBLK) {
	error = ENOTBLK;
	goto err_rele;
    }
    dev = rvp->v_rdev;
    if (vfs_devsearch(dev) != NULL) {	/* Walks thru vfs list... */
	error = EBUSY;
	goto err_rele;
    }
    /* Allocate filesystem-specific data space using kmem_zalloc() */
    afsp = (struct ados *) kmem_zalloc(sizeof(*afsp), KM_SLEEP);
    if (afsp == (struct ados *) 0) {
	error = ENOMEM;
	goto err_rele;
    }
    afsp->vfs_vfsp = vfsp;
    afsp->vfs_dos = mntparam.dos;	/* See "ados_vfs.h" for definitions */
    afsp->vfs_bufbits = 9;
    afsp->vfs_nels = 0;
    afsp->vfs_nelmax = 100;
    afsp->vfs_fstr = NULL;		/* Null them out so error cleanup */
    afsp->vfs_bmap = NULL;		/*   knows which ones to free().  */
    afsp->vfs_nbmap = 0;

    vfsp->vfs_bcount = 0;
    vfsp->vfs_bsize = 1 << afsp->vfs_bufbits;
    vfsp->vfs_data = (caddr_t) afsp;
    vfsp->vfs_fstype = ados_type;
    vfsp->vfs_flag |= VFS_RDONLY | VFS_NOSUID;
    vfsp->vfs_dev = dev;
    vfsp->vfs_fsid.val[0] = dev;
    vfsp->vfs_fsid.val[1] = ados_type;

    DBG("attempting open of special device", 0, 0, 0);
    if (error = VOP_OPEN(&rvp, FREAD, cr))
	goto err_free;
    afsp->vfs_devvp = rvp;
    error = fbread(rvp, (off_t) mntparam.rootblk << afsp->vfs_bufbits,
	vfsp->vfs_bsize, S_WRITE, &mapp);
    if (error)
	goto err_clos;
    disk = AROOT(mapp->fb_addr);

    DBG("reading root directory (%x)", afsp->vfs_dos, 0, 0);
    afsp->vfs_fstr = kmem_zalloc(error = disk->rd_name[0] + 1, KM_SLEEP);
    bcopy((caddr_t) disk->rd_name + 1, (caddr_t) afsp->vfs_fstr, error - 1);

    /* Initialize (struct anode)  */
    root = &afsp->vfs_root;
    if (error = ados_read_anode(root, mntparam.rootblk, vfsp))
	goto err_frel;
    root->a_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;	/* But EROFS */
    root->a_mtime = *date2unix(&disk->rd_dirstmp);

    /* Grab the bitmaps once and hold them the entire time... */
    nbytes = vfsp->vfs_bsize - 1;
    nbytes = ((((off_t) mntparam.rootblk+3) >> 2) + nbytes) & ~nbytes;
    DBG("bitmap would be %d bytes", nbytes, 0, 0);
    /*
     *  `nbytes' is the number of bytes req'd to represent the bitmap
     *  (rounded up to the nearest block size); divide by BSIZE to get
     *  number of blocks.
     */
    if (mntparam.dos & CACHE_BMAPS) {
#if 1
	DBG("alloc'd bitmap (%x)", mntparam.dos, 0, 0);
#else
	struct fbuf *mapp2;
	/*
	 *  I allocate them contiguous because it's easy, but also
	 *  because I want the low-level routines to be able to flush
	 *  them to disk with a single DMA operation so that it's not
	 *  overly expensive to do a flush.
	 */
	afsp->vfs_nbmap = nbytes;
	afsp->vfs_bmap = kmem_zalloc(nbytes, KM_SLEEP);

	/* Printed here so that I'll have the `nbytes' value for debugging. */
	DBG("bitmap buffer = @ %x", afsp->vfs_bmap, 0, 0);

	error = fbread(rvp, (off_t) (mntparam.rootblk+1) << afsp->vfs_bufbits,
	    nbytes, S_WRITE, &mapp2);
	if (error)
	    goto err_frel;
	bcopy((caddr_t) mapp2->fb_addr, (caddr_t) afsp->vfs_bmap, nbytes);
	fbrelse(mapp2, S_OTHER);
#endif
    }
    DBG("vfsp for ADOS is @ 0x%x", vfsp, 0, 0);
    DBG("afsp for ADOS is @ 0x%x", afsp, 0, 0);

    fbrelse(mapp, S_OTHER);
    /*
     * Create an initial list for caching anodes.  Running out causes no
     * errors since it will be dynamically extended.  However, it is not
     * currently free'd at any point except unmounting!!
     */
    root->a_forw = root;	/* List of used, in-core, anodes */
    root->a_back = root;
    afsp->vfs_forw = (void *) &afsp->vfs_forw;  /* List of free anodes */
    afsp->vfs_back = (void *) &afsp->vfs_forw;
    extend_chain(afsp, 4);

    root->a_flag = 0;
    root->a_nilocks = 0;
    root->a_owner = 0;

    return 0;

err_frel: fbrelse(mapp, S_OTHER);
    if (afsp->vfs_bmap)
	kmem_free((caddr_t) afsp->vfs_bmap, afsp->vfs_nbmap << vfsp->vfs_bsize);
    if (afsp->vfs_fstr)
	kmem_free((caddr_t) afsp->vfs_fstr, strlen(afsp->vfs_fstr) + 1);
err_clos: VOP_CLOSE(rvp, FREAD, 1, (off_t) 0, cr);
err_free: kmem_free((caddr_t) afsp, sizeof(*afsp));
err_rele: VN_RELE(rvp);
    return error;
}


/*
 * Unmount an ADOS filesystem.
 */
static int ados_unmount(struct vfs * vfsp, struct cred * cr)
{
    struct ados *afsp = ADOS_VFS(vfsp);
    dev_t dev;
    register vnode_t *tmp, *rvp;
    int error;

    if (!suser(cr))
	return EPERM;
    rvp = &afsp->vfs_root.a_vnode;

IN_FUNC(ados_unmount);
    if (rvp->v_count > 1)
	return EBUSY;

    if (aflush(vfsp, 0) < 0)
	return EBUSY;

    ILOCK(&afsp->vfs_root);
    DBG("vfsp->vfs_flag = 0x%x", vfsp->vfs_flag, 0, 0);
    /*
     * At this point there should be no active files on the file system, and
     * the super block should not be locked.  Break the connections.
     */
    tmp = afsp->vfs_devvp;

    if ((vfsp->vfs_flag & VFS_RDONLY) == 0) {
	DBG("Shouldn't be here.  rootblk = %d", afsp->vfs_root.a_keyblk, 0, 0);
#if 0
	bflush(vfsp->vfs_dev);
	afsp->vfs_rdir.rd_dskstmp = *TIME2A(time((long *) 0));
	if (error = fbread(tmp, (off_t) afsp->vfs_root.a_keyblk << afsp->vfs_bufbits, vfsp->vfs_bsize, S_READ, &mapp)) {
	    IUNLOCK(&afsp->vfs_root);
	    return error;
	}
	bcopy((caddr_t) & afsp->vfs_rdir, mapp->fb_addr, vfsp->vfs_bsize);
	(void) fbwritei(mapp);
#endif
    }
    DBG("calling VOP_PUTPAGE()", 0, 0, 0);
    (void) VOP_PUTPAGE(tmp, 0, 0, B_INVAL, cr);
    {
	register anode_t *aused, *afree, *this, *next;

	DBG("deallocating used anodes", 0, 0, 0);
	aused = &afsp->vfs_root;
#ifdef TYPICAL
	dump_chain("Used", aused);
#endif
	for (this = aused->a_forw; this && this != aused; this = next) {
	    next = this->a_forw;
	    ASSERT(next->a_back == this);
	    if (this->a_ll.ll_forw)
		freechn(this);
	    DBG("free'ing anode @ %x", this, 0, 0);
	    kmem_free((caddr_t) this, sizeof(*this));
	}
	DBG("done with used loop", 0, 0, 0);
	aused->a_forw = aused;	/* In-use list */
	aused->a_back = aused;

	DBG("deallocating free anodes", 0, 0, 0);
	afree = (anode_t *) &afsp->vfs_forw;
#ifdef TYPICAL
	dump_chain("Free", afree);
#endif
	for (this = afree->a_forw; this != afree; this = next) {
	    next = this->a_forw;
	    ASSERT(next->a_back == this);
	    if (this->a_ll.ll_forw)
		freechn(this);
	    DBG("free'ing anode @ %x", this, 0, 0);
	    kmem_free((caddr_t) this, sizeof(*this));
	}
	DBG("done with free loop", 0, 0, 0);
	afree->a_forw = afree;	/* Free list */
	afree->a_back = afree;
    }
    DBG("calling VOP_CLOSE()", 0, 0, 0);
    if (error = VOP_CLOSE(tmp, (vfsp->vfs_flag & VFS_RDONLY) ?
			  FREAD : FREAD | FWRITE, 1, (off_t) 0, cr)) {
	IUNLOCK(&afsp->vfs_root);
	return error;		/* okay 'cuz a_forw/a_back are stable */
    }
    DBG("calling VN_RELE()", 0, 0, 0);
    VN_RELE(tmp);
    DBG("calling binval()", 0, 0, 0);
    binval(dev);
    DBG("calling kmem_free()", 0, 0, 0);
    kmem_free((caddr_t) afsp->vfs_fstr, strlen(afsp->vfs_fstr) + 1);
    kmem_free((caddr_t) afsp->vfs_bmap, afsp->vfs_nbmap << vfsp->vfs_bsize);
    kmem_free((caddr_t) afsp, sizeof(*afsp));
IN_FUNC(ados_unmount);
    return 0;
}

/*
 * Return a pointer to the root vnode.
 */
static int ados_root(struct vfs * vfsp, vnode_t ** vpp)
{
    struct ados *afsp = ADOS_VFS(vfsp);
    vnode_t *vp = ATOV(&afsp->vfs_root);

IN_FUNC(ados_root);
    VN_HOLD(vp);
    *vpp = vp;
    return 0;
}

/*
 * Sync the filesystem.
 */
static int ados_sync(struct vfs *vfsp, short flag, struct cred *cr)
{
static int ados_updlock, ados_updwant;

    if (vfsp) {
IN_FUNC(ados_sync);
	while (ados_updlock) {
	    ados_updwant = 1;
	    sleep((caddr_t)&ados_updlock, PINOD);
	}
	ados_updlock = 1;
	if (flag & SYNC_ATTR)
	    ados_flusha(vfsp, SYNC_ATTR);
	else
	    ados_update();	/* Update all ADOS mounts */
	ados_updlock = 0;
	if (ados_updwant) {
	    ados_updwant = 0;
	    wakeprocs((caddr_t)&ados_updlock, PRMPT);
	}
    }
    return 0;
}

/*
 * Given a fid, find the vnode.  [FJE?  What is a "fid"?]
 */
static int ados_vget(struct vfs * vfsp, vnode_t ** vpp, struct fid * fidp)
{
    anode_t *ap;
    struct afid *fp = (struct afid *) fidp;

IN_FUNC(ados_vget);
    if (fp->afid_len != sizeof(*fp) - sizeof(fp->afid_len))
	return EINVAL;
    if (aget(vfsp, fp->afid_key, &ap)) {
	*vpp = NULLVP;
	return (0);
    }
    IUNLOCK(ap);
    *vpp = ATOV(ap);

    return 0;
}


/*
 * Return "filesystem independent" information about this VFS.
 */
static int ados_statvfs(struct vfs * vfsp, struct statvfs * sp)
{
    struct ados *afsp = ADOS_VFS(vfsp);
    int i;

    bzero((caddr_t) sp, sizeof *sp);
IN_FUNC(ados_statvfs);
    sp->f_bsize = 1 << afsp->vfs_bufbits;
    sp->f_frsize = 1 << afsp->vfs_bufbits;
    sp->f_blocks = afsp->vfs_root.a_keyblk << 1;

    sp->f_bfree = 0;		/* Get this info from block bitmap */
    sp->f_bavail = 0;		/* Get this info from block bitmap */
    sp->f_fsid = vfsp->vfs_dev;
    sp->f_flag = vf_to_stf(vfsp->vfs_flag);
    sp->f_namemax = ADOS_DIRSIZ;

    strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
    i = min(strlen(afsp->vfs_fstr), sizeof(sp->f_fstr) - 1);
    strncpy((char *) sp->f_fstr, afsp->vfs_fstr, i);
    sp->f_fstr[i] = '\0';

#if 0
    /*
     * This is information which ADOS doesn't keep... it'd be brute-force to
     * try to determine it.
     */
    sp->f_files = 1;		/* Brute force is the only way... */
    sp->f_ffree = 0;		/* Same as number of free blocks... */
    sp->f_favail = 0;		/* Ditto */
#else
    sp->f_files = afsp->vfs_nels;
    sp->f_ffree = afsp->vfs_nelmax - afsp->vfs_nels;
    sp->f_favail = afsp->vfs_nelmax - afsp->vfs_nels;
#endif

    return 0;
}
