/*
 * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * 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 the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE OR CONTRIBUTORS 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.
 */

/*
 * XFS vfs operations.
 */

#include <xfs_common.h>
#include <xfs/xfs_node.h>
#include <xfs_message.h>
#include <xfs_dev.h>
#include <xfs_fs.h>
#include <nxfs.h>
#include <xdeb.h>

extern int nchrdev;
extern struct cdevsw cdevsw[];

static struct vnode *make_dead_vnode (struct vfs *vfsp);

struct xfs xfs[NXFS];

static int xfsfstype;

static int
xfs_mount(struct vfs *vfsp,
	  struct vnode *mvp,
	  struct mounta *uap,
	  struct cred *cred)
{
  struct vnode *devvp;
  dev_t dev;
  int errno;

  XFSDEB(XDEBVFOPS, ("xfs_mount vfsp = 0x%x path = %s args = '%s'\n",
		   (u_int) vfsp, uap->dir, uap->spec));

  /*
   * This is something that should be done before calling this
   * function, but it's not, so we do it here.
   */

  if (mvp->v_type != VDIR)
      return ENOTDIR;
  mutex_enter(&mvp->v_lock);
  if (mvp->v_count != 1 || (mvp->v_flag & VROOT)) {
      mutex_exit(&mvp->v_lock);
      return EBUSY;
  }
  mutex_exit(&mvp->v_lock);

  errno = lookupname(uap->spec, UIO_USERSPACE, FOLLOW, 0, &devvp);
  if (errno != 0)
    return errno;

  if (devvp->v_type != VCHR) {
      VN_RELE(devvp);
      return ENXIO;
  }
  dev = devvp->v_rdev;
  VN_RELE(devvp);

  /* Check that this device really is an xfs_dev */

  /* I'm not sure how to test this under solaris */
#if 0
  if (getmajor(dev) < 0 || nchrdev < getmajor(dev))
    return ENXIO;
#endif

  if (getminor(dev) < 0 || NXFS < getminor(dev))
    return ENXIO;
  if (devopsp[getmajor(dev)] == NULL ||
      devopsp[getmajor(dev)]->devo_cb_ops == NULL ||
      devopsp[getmajor(dev)]->devo_cb_ops->cb_open != xfs_devopen)
      return ENXIO;

  if (xfs[getminor(dev)].status & XFS_MOUNTED)
    return EBUSY;

  xfs[getminor(dev)].status = XFS_MOUNTED;
  xfs[getminor(dev)].vfsp = vfsp;
  xfs[getminor(dev)].root = 0;
  xfs[getminor(dev)].nnodes = 0;
  xfs[getminor(dev)].nodes = 0;
  xfs[getminor(dev)].fd = getminor(dev);

  VFS_TO_XFS(vfsp) = &xfs[getminor(dev)];
  vfsp->vfs_fstype = xfsfstype;
  vfsp->vfs_dev = getminor(dev);
  vfsp->vfs_fsid.val[0] = getminor(dev);
  vfsp->vfs_fsid.val[1] = getmajor(dev); /* What is this good for */

  return 0;
}

static int
xfs_unmount(struct vfs *vfsp, struct cred *cred)
{
  struct xfs *xfsp = VFS_TO_XFS(vfsp);

  XFSDEB(XDEBVFOPS, ("xfs_umount vfsp = 0x%x\n", (u_int) vfsp));

  free_all_xfs_nodes(xfsp);
  xfsp->status = 0;
  return 0;			/* Always allow umount to succed */
}

static int
xfs_root(struct vfs *vfsp,
	 struct vnode **vpp)     
{
  struct xfs *xfsp = VFS_TO_XFS(vfsp);
  struct xfs_message_getroot msg;
  int errno;
  
  XFSDEB(XDEBVFOPS, ("xfs_root vfsp = 0x%x\n", (u_int) vfsp));

  do {
    if (xfsp->root != 0) {
	*vpp = XNODE_TO_VNODE(xfsp->root);
	VN_HOLD(*vpp);
	return 0;
    }

    msg.header.opcode = XFS_MSG_GETROOT;
    msg.cred.uid = CRED()->cr_uid;
    msg.cred.pag = 0;		/* XXX */
    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (errno == 0)
      errno = ((struct xfs_message_wakeup *) &msg)->errno;
  } while (errno == 0);

  /*
   * Failed to get message through, need to pretend that all went well
   * and return a fake dead vnode to be able to unmount.
   */
  *vpp = make_dead_vnode(vfsp);
  VN_HOLD(*vpp);
  return 0;
}

static int
xfs_statvfs(struct vfs *vfsp,
	    struct statvfs *sbp)     
{
  XFSDEB(XDEBVFOPS, ("xfs_statvfs\n"));
  return EINVAL;
}

static int
xfs_sync(struct vfs *vfsp, short flag, struct cred *cred)
{
  return 0;
}

static int
xfs_vget(struct vfs *vfsp,
	 struct vnode **vpp,
	 struct fid *fidp)     
{
  XFSDEB(XDEBVFOPS, ("xfs_vget\n"));
  return EINVAL;
}

static int
xfs_mountroot(struct vfs *vfsp,
	      enum whymountroot reason)
{
  XFSDEB(XDEBVFOPS, ("xfs_mountroot\n"));
  return EINVAL;
}

static int
xfs_swapvp(struct vfs *vfsp,
	   struct vnode **vpp,
	   char *path)     
{
  XFSDEB(XDEBVFOPS, ("xfs_swapvp\n"));
  return EINVAL;
}

/*
 * To be able to unmount when the XFS daemon is not
 * responding we need a root vnode, use a dead vnode!
 */

static int
dead_vnode_inactive(struct vnode *vp, struct cred *cred)
{
  XFSDEB(XDEBVFOPS, ("dead_vnode_inactive\n"));
  xfs_free(vp, sizeof(*vp));
  return 0;
}

struct vnodeops dead_vnodeops = {
        nodev,			/* xfs_open */
        nodev,			/* xfs_close */
        nodev,			/* xfs_read */
        nodev,			/* xfs_write */
        nodev,			/* xfs_ioctl */
	nodev,			/* xfs_setfl */
        nodev,			/* xfs_getattr */
        nodev,			/* xfs_setattr */
        nodev,			/* xfs_access */
        nodev,			/* xfs_lookup */
        nodev,			/* xfs_create */
        nodev,			/* xfs_remove */
        nodev,			/* xfs_link */
        nodev,			/* xfs_rename */
        nodev,			/* xfs_mkdir */
        nodev,			/* xfs_rmdir */
        nodev,			/* xfs_readdir */
        nodev,			/* xfs_symlink */
        nodev,			/* xfs_readlink */
        nodev,			/* xfs_fsync */
        dead_vnode_inactive,	/* xfs_inactive */
        nodev,			/* xfs_fid */
        nodev,			/* xfs_rwlock */
        nodev,			/* xfs_rwunlock */
        nodev,			/* xfs_seek */
        nodev,			/* xfs_cmp */
        nodev,			/* xfs_frlock */
        nodev,			/* xfs_space */
        nodev,			/* xfs_realvp */
        nodev,			/* xfs_getpage */
        nodev,			/* xfs_putpage */
        nodev,			/* xfs_map */
        nodev,			/* xfs_addmap */
        nodev,			/* xfs_delmap */
        nodev,			/* xfs_poll */
        nodev,			/* xfs_dump */
        nodev,			/* xfs_pathconf */
        nodev,			/* xfs_pageio */
        nodev,			/* xfs_dumpctl */
        nodev,			/* xfs_dispose */
        nodev,			/* xfs_setsecattr */
        nodev			/* xfs_getsecattr */
};

static struct vnode *
make_dead_vnode(struct vfs *vfsp)
{
  struct vnode *dead;

  XFSDEB(XDEBNODE, ("make_dead_vnode vfsp = 0x%x\n", (u_int) vfsp));

  dead = xfs_alloc(sizeof(*dead));
  bzero(dead, sizeof(*dead));
  VN_INIT(dead, vfsp, VDIR, 0);
  dead->v_flag = VROOT;
  dead->v_op = &dead_vnodeops;
  dead->v_count = 0;
  return dead;
}

static int
xfs_uprintf_filsys(void)
{
#if 0
  {
    struct vfssw *fs;
    uprintf("Currently loaded filesystems are:\n");
    for (fs = vfssw; fs < vfsNVFS; fs++)
      uprintf("vfssw[%d] == { \"%s\", 0x%x}\n",
	      fs - vfssw, fs->vsw_name, fs->vsw_ops);
  }
#endif
#if 1
  {
    int i;
    struct xfs_node *t;
    for (i = 0; i < NXFS; i++)
      if (xfs[i].nodes)
	{
	  uprintf("Current xfs_nodes on xfs[%d] are:\n", i);
	  for (t = xfs[i].nodes; t; t = t->next)
	    uprintf("%d.%d.%d.%d(%d) ",
		    t->handle.a,
		    t->handle.b,
		    t->handle.c,
		    t->handle.d,
		    t->vn.v_count);
	  uprintf(" !\n");
	}
  }
#endif
  return 0;
}

/*
 * file system
 */

static int
xfs_vfs_init (struct vfssw *vfssw, int offset)
{
    printf ("xfs_vfs_init: offset = %d\n", offset);
    xfsfstype = offset;
    return 0;
}

static struct vfsops xfs_vfsops = {
    xfs_mount,			/* mount */
    xfs_unmount,		/* unmount */
    xfs_root,			/* root */
    xfs_statvfs,		/* statvfs */
    xfs_sync,			/* sync */
    xfs_vget,			/* vget */
    xfs_mountroot,		/* mountroot */
    xfs_swapvp			/* swapvp */
};

static struct vfssw xfs_vfssw = {
    "xfs",			/* name */
    xfs_vfs_init,		/* init */
    &xfs_vfsops,		/* vfsops */
    0				/* flags */
};

struct modlfs xfs_modlfs = {
    &mod_fsops,
    "xfs filesystem",
    &xfs_vfssw
};

