#define MODULE_ID	5060

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

#ident "$Id: vnops.c,v 1.23 1992/11/26 03:51:42 root Exp root $";

#include "sys/types.h"
#include "sys/buf.h"
#include "sys/cred.h"
#include "sys/dirent.h"
#include "sys/errno.h"
#include "sys/fbuf.h"
#include "sys/fcntl.h"
#include "sys/file.h"
#include "sys/inline.h"
#include "sys/kmem.h"
#include "sys/mman.h"
#include "sys/open.h"
#include "sys/param.h"
#include "sys/pathname.h"
#include "sys/proc.h"		/* For "proc_t" in <disp.h> */
#include "sys/disp.h"		/* For "extern curproc" (after proc.h) */
#include "sys/stat.h"
#include "sys/sysinfo.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/time.h"
#include "sys/uio.h"
#include "sys/user.h"
#include "sys/vfs.h"
#include "sys/vnode.h"
#include "sys/dnlc.h"
#include "vm/page.h"
#include "vm/pvn.h"
#include "vm/seg.h"
#include "fs/fs_subr.h"

#include "missing.h"

#include "ados.h"
#include "ados_dir.h"
#include "ados_udir.h"

#ifndef NOCRED
# define NOCRED	((struct cred *)0)
#endif

static int ados_open(vnode_t **vpp, int flag, struct cred *cr);
static int ados_close(vnode_t *vp, int flag, int count, off_t offset,
	struct cred *cr);
static int ados_ioctl(vnode_t *vp, int cmd, int arg, int flag, struct cred *cr,
	int *rvalp);
static int ados_fsync(vnode_t *vp, struct cred *cr);
static void ados_inactive(vnode_t *vp, struct cred *cr);
static int ados_fid(vnode_t *vp, struct fid **fidpp);
static int ados_getattr(vnode_t *vp, struct vattr *vap, int flags,
	struct cred *cr);
static int ados_setattr(vnode_t *vp, struct vattr *vap, int flags,
	struct cred *cr);
static int ados_access(vnode_t *vp, int mode, int flags, struct cred *cr);
static int ados_seek(vnode_t *vp, off_t ooff, off_t *noffp);
static int ados_cmp(vnode_t *vp1, vnode_t *vp2);
static int ados_readdir(vnode_t *vp, register struct uio *uiop, struct cred *cr,
	int *eofp);
static void ados_rwlock(vnode_t *vp);
static void ados_rwunlock(vnode_t *vp);
static int ados_read(vnode_t *vp, struct uio *uiop, int ioflag,
	struct cred *cr);
static int ados_write(vnode_t *vp, struct uio *uiop, int ioflag,
	struct cred *cr);
static int ados_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct cred *cr,
	struct pathname *pnp, vnode_t *rdir, int flags);
static int ados_remove(vnode_t *vp, char *nm, struct cred *cr);
static int ados_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
	struct cred *cr);
static int ados_link(vnode_t *tdvp, vnode_t *svp, char *tnm, struct cred *cr);
static int ados_create(vnode_t *dvp, char *fname, struct vattr *vap,
	enum vcexcl excl, int mode, vnode_t **vpp, struct cred *cr);
static int ados_freespace(vnode_t *vp, int cmd, struct flock *bfp, int flag,
	off_t offset, struct cred *cr);
static int ados_dump(vnode_t *vp, caddr_t addr, int bn, size_t c);

struct vnodeops ados_vnops = {
#ifdef lint
    0,
#else
    ados_open,
    ados_close,
    ados_read,
    fs_nosys,			/* ados_write, */
    fs_nosys,			/* ados_ioctl, */
    fs_setfl,			/* FJE:  File flags... */
    ados_getattr,
    ados_setattr,
    ados_access,
    ados_lookup,
    fs_nosys,			/* ados_create, */
    fs_nosys,			/* ados_remove, */
    fs_nosys,			/* ados_link, */
    fs_nosys,			/* ados_rename, */
    fs_nosys,			/* ados_mkdir, */
    fs_nosys,			/* ados_rmdir, */
    ados_readdir,
    fs_nosys,			/* ados_symlink, */
    fs_nosys,			/* ados_readlink, */
    ados_fsync,
    ados_inactive,		/* No more references to the given vnode... */
    ados_fid,			/* vnode ==> fid translation */
    ados_rwlock,		/* vnode/inode locking */
    ados_rwunlock,
    ados_seek,
    ados_cmp,
    fs_frlock,			/* file record locking */
    ados_freespace,
    fs_nosys,			/* realvp */
    fs_nosys,			/* getpage */
    fs_nosys,			/* putpage */
    fs_nosys,			/* mmap */
    fs_nosys,			/* addmap */
    fs_nosys,			/* delmap */
    fs_poll,
    ados_dump,			/* dump [FJE -- I added printf() stmts] */
    fs_pathconf,
    fs_nosys,			/* filler */
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
    fs_nosys,
#endif
};


static int ados_open(vnode_t ** vpp, int flag, struct cred * cr)
{
IN_FUNC(ados_open);
    return 0;
}

static int ados_close(vnode_t * vp, int flag, int count, off_t offset,
		          struct cred * cr)
{
    register anode_t *ap = VTOA(vp);

IN_FUNC(ados_close);
    ILOCK(ap);
    cleanlocks(vp, u.u_procp->p_epid, u.u_procp->p_sysid);
    IUNLOCK(ap);
    return 0;
}

#if 0
static int ados_ioctl(vnode_t * vp, int cmd, int arg,
		          int flag, struct cred * cr, int *rvalp)
{
IN_FUNC(ados_ioctl);
    return ENOTTY;
}
#endif

static int ados_fsync(vnode_t * vp, struct cred * cr)
{
    register anode_t *ap = VTOA(vp);
    register int    error;

IN_FUNC(ados_fsync);
    IRWLOCK(ap);
    ILOCK(ap);
    error = syncap(ap, 0);	/* Do synchronous writes */
IN_FUNC(ados_fsync);

    IUNLOCK(ap);
    IRWUNLOCK(ap);

IN_FUNC(ados_fsync);
    return error;
}

/*
 * This vnode has no more references.
 */
static void ados_inactive(vnode_t * vp, struct cred * cr)
{
    anode_t *ap = VTOA(vp);

IN_FUNC(ados_inactive);
    ASSERT(vp->v_count == 0);
    PREEMPT();
    ILOCK(ap);
#if 0
    if (ip->i_nlink <= 0) {
	    ip->i_gen++;
	    ados_itrunc(ip);
	    ip->i_flag |= IUPD|ICHG;
	    ados_ifree(ip);
    } ...
#endif
    (void) syncap(ap, B_FREE | B_ASYNC);
    /*
     *  If it's the root anode, don't free the anode pointer.
     */
    if (&ADOS_VFS(vp->v_vfsp)->vfs_root != ap) {
	apfree(ap);	/* Remove from "in use" list; put on end of free list */
    }
    IUNLOCK(ap);
    PREEMPT();
IN_FUNC(ados_inactive);
}

/*
 * Convert a vnode to a fid.
 */
static int ados_fid(vnode_t * vp, struct fid ** fidpp)
{
    anode_t *np = VTOA(vp);
    struct afid *fidp;

IN_FUNC(ados_fid);
    fidp = (struct afid *) kmem_zalloc(sizeof(*fidp), KM_SLEEP);
    fidp->afid_len = sizeof(*fidp) - sizeof(fidp->afid_len);
    fidp->afid_key = np->a_keyblk;

    *fidpp = (struct fid *) fidp;
    return 0;
}

static int ados_getattr(vnode_t * vp, struct vattr * vap, int flags,
			    struct cred * cr)
{
    struct ados *afsp = ADOS_VFS(vp->v_vfsp);
    anode_t *ap = VTOA(vp);

IN_FUNC(ados_getattr);
    ILOCK(ap);
    vap->va_type = vp->v_type;
    vap->va_mode = ap->a_mode;
    vap->va_uid = ap->a_uid;
    vap->va_gid = ap->a_gid;
    vap->va_nlink = ap->a_nlink;

    vap->va_mtime = ap->a_mtime;
    vap->va_atime = ap->a_mtime;
    vap->va_ctime = ap->a_mtime;

    vap->va_blksize = 1 << afsp->vfs_bufbits;
    vap->va_vcode = 0;		/* not used -- version code (for the file?) */
    vap->va_size = ap->a_size;
    vap->va_nblocks = ap->a_blocks;
    vap->va_nodeid = ap->a_keyblk;
    vap->va_fsid = afsp->vfs_devvp->v_rdev;
    vap->va_rdev = 0;
    IUNLOCK(ap);

    return 0;
}

static int ados_setattr(vnode_t * vp, struct vattr * vap, int flags,
			    struct cred * cr)
{
IN_FUNC(ados_setattr);
    return EROFS;
}

static int ados_access(vnode_t * vp, int mode, int flags,
		           struct cred * cr)
{
    register anode_t *ap = VTOA(vp);
    register int error;

IN_FUNC(ados_access);
    ILOCK(ap);
    error = ados_naccess(ap, mode, cr);
    IUNLOCK(ap);
    return error;
}

static int ados_seek(vnode_t * vp, off_t ooff, off_t * noffp)
{
IN_FUNC(ados_seek);
    return *noffp < 0 ? EINVAL : 0;
}

static int ados_cmp(vnode_t * vp1, vnode_t * vp2)
{
IN_FUNC(ados_cmp);
    return vp1 == vp2;
}

static int ados_readdir(vnode_t * vp, register struct uio * uiop,
			    struct cred * cr, int *eofp)
{
    register int doff = uiop->uio_offset;
    struct ados *afsp = ADOS_VFS(vp->v_vfsp);
    anode_t *ap = VTOA(vp);
    struct a_udir udir;		/* Format of a user directory */
    struct uio uio_dir;
    struct iovec iovector;
    struct {
	struct dirent dirent;
	char pad[ADOS_DIRSIZ];
    }   one;
    int blk, error = 0, blocksz = 1 << afsp->vfs_bufbits;

IN_FUNC(ados_readdir);
    /* Check for a valid offset. */
    if (doff < 0 || uiop->uio_resid < sizeof(one) || (doff % sizeof(one)))
	return EINVAL;
    uio_dir.uio_segflg = UIO_SYSSPACE;
    uio_dir.uio_fmode = FREAD;
    uio_dir.uio_limit = -1;
    uio_dir.uio_iov = &iovector;
    uio_dir.uio_iovcnt = 1;
    blk = (doff / sizeof(one)) * blocksz;

    DBG("uio_resid %d, blk %d, a_size %d", uiop->uio_resid, blk, ap->a_size);
    while (!error && uiop->uio_resid >= sizeof(one) && blk < ap->a_size) {
	uio_dir.uio_resid = blocksz;
	uio_dir.uio_offset = blk;
	iovector.iov_base = (caddr_t) & udir;
	iovector.iov_len = blocksz;

	if ((error = reada(ap, &uio_dir, 0)) || uio_dir.uio_resid == blocksz)
	    break;
	if (uio_dir.uio_resid == 0) {
	    one.dirent.d_ino = udir.ud_hkey;
	    one.dirent.d_off = doff;
	    one.dirent.d_reclen = sizeof(one);
	    udir.ud_name[ (int)udir.ud_name[0]+1 ] = '\0';
	    strcpy(one.dirent.d_name, (char *)udir.ud_name+1);
	    DBG("just read %s", one.dirent.d_name, 0, 0);
	    error = uiomove(&one, sizeof(one), UIO_READ, uiop);
	    if (!error)
		doff += sizeof(one), blk += blocksz;
	} else if (uio_dir.uio_resid != blocksz)
	    error = EIO;
    }
    if (!error) {
	uiop->uio_offset = doff;
	if (eofp)
	    *eofp = (blk >= ap->a_size);
    }
    return error;
}

static void ados_rwlock(vnode_t * vp)
{
IN_FUNC(ados_rwlock);
    IRWLOCK(VTOA(vp));
}

static void ados_rwunlock(vnode_t * vp)
{
IN_FUNC(ados_rwunlock);
    IRWUNLOCK(VTOA(vp));
}

static int ados_read(vnode_t * vp, struct uio * uiop, int ioflag,
		         struct cred * cr)
{
    int error;

IN_FUNC(ados_read);
    ASSERT(vp->v_type != VREG && vp->v_type != VDIR);
    error = reada(VTOA(vp), uiop, ioflag);

    DBG("error from reada() is %d", error, 0, 0);
    return (error);
}

#if 0
static int ados_write(vnode_t * vp, struct uio * uiop, int ioflag,
		          struct cred * cr)
{
IN_FUNC(ados_write);
    return EROFS;
}
#endif

static int ados_lookup(vnode_t * dvp, char *nm, vnode_t ** vpp,
		           struct cred * cr,
		      struct pathname * pnp, vnode_t * rdir, int flags)
{
    anode_t *dnp = VTOA(dvp);
    anode_t *np;
    int error;

IN_FUNC(ados_lookup);
    if (dvp->v_type != VDIR)
	return ENOTDIR;

    if (error = ados_naccess(dnp, VEXEC, cr))
	return error;

    /*
     * The null name means the current directory.
     */
    if (*nm == '\0' || (*nm == '.' && nm[1] == '\0')) {
	DBG("Inside ados_lookup() -- found '.'", 0, 0, 0);
	VN_HOLD(*vpp = dvp);
	return 0;
    }
    /*
     * Handle '..' even though it doesn't exist. (Should never be called --
     * FJE)
     */
    if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
	DBG("Inside ados_lookup() -- found '..'", 0, 0, 0);
	VN_HOLD(*vpp = dvp);
	return 0;
    }
    /*
     * Check the directory name lookup cache.
     */
    if (*vpp = dnlc_lookup(dvp, nm, NOCRED)) {
	DBG("%x = dnlc_lookup(%s)", VTOA(*vpp), nm, 0);
	VN_HOLD(*vpp);
	return 0;
    }
    /*
     * Search through the directory for the file.  Returns (locked) anode
     * pointer.
     */
    ILOCK(dnp);
    np = ados_searchdir(dvp, nm, cr);
    IUNLOCK(dnp);
IN_FUNC(ados_lookup);

    if (np != NULL) {
	IUNLOCK(np);
	*vpp = ATOV(np);
	DBG("dnlc_enter(%s, %x)", nm, np, 0);
	dnlc_enter(dvp, nm, *vpp, NOCRED); /* directory vnode & new vnode */
	return 0;
    }
    return ENOENT;
}

#if 0
static int ados_remove(vnode_t * vp, char *nm, struct cred * cr)
{
IN_FUNC(ados_remove);
    return EROFS;
}

static int ados_rename(vnode_t * sdvp, char *snm,	/* old vnode & name */
		           vnode_t * tdvp, char *tnm,	/* new vnode & name */
		           struct cred * cr)
{
IN_FUNC(ados_rename);
    return EROFS;
}

static int ados_link(vnode_t * tdvp, vnode_t * svp, char *tnm,
		         struct cred * cr)
{
IN_FUNC(ados_link);
    return EROFS;
}

/*
 * Either creat() or open() with O_CREAT.
 */
static int ados_create(vnode_t * dvp, char *fname, struct vattr * vap,
		           enum vcexcl excl, int mode, vnode_t ** vpp,
		           struct cred * cr)
{
IN_FUNC(ados_create);
    return EROFS;
}
#endif

static int ados_freespace(vnode_t * vp, int cmd, struct flock * bfp,
			      int flag, off_t offset, struct cred * cr)
{
IN_FUNC(ados_freespace);
    if (cmd != F_FREESP)	/* FJE?  What other "cmd"'s are possible? */
	return EINVAL;

    return EROFS;
}

static int ados_dump(vnode_t * vp, caddr_t addr, int bn, size_t c)
{
    DBG("ados_dump:  (0x%x,", vp, 0, 0);
    DBG("ados_dump:     0x%x, %d, %d)", addr, bn, c);
    return (0);
}
