/*
 * 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 <sys/types.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/errno.h>

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

#include <sys/conf.h>
extern int nchrdev;
extern struct cdevsw cdevsw[];

static char xfs_fsname[] = "xfs";
static struct vnode * make_dead_vnode _PARAMS((struct vfs *vfsp));

struct xfs xfs[NXFS];

#if defined(__STDC__)
static int xfs_mount(struct vfs *vfsp,
		     char *path,
		     caddr_t args)     
#else
static
int
xfs_mount(vfsp, path, args)
     struct vfs *vfsp;
     char *path;
     caddr_t args;
#endif
{
  struct vnode *devvp;
  dev_t dev;
  int errno;

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

  errno = lookupname(args, UIO_USERSPACE, FOLLOW_LINK, 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 */
  if (major(dev) < 0 || nchrdev < major(dev))
    return ENXIO;
  if (minor(dev) < 0 || NXFS < minor(dev))
    return ENXIO;
  if (cdevsw[major(dev)].d_open != (int (*)()) xfs_devopen)
    return ENXIO;

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

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

  VFS_TO_XFS(vfsp) = &xfs[minor(dev)];
  vfsp->vfs_fsid.val[0] = minor(dev);
  vfsp->vfs_fsid.val[1] = major(dev); /* What is this good for */

  return 0;
}

#if defined(__STDC__)
static int xfs_unmount(struct vfs *vfsp)     
#else
static
int
xfs_unmount(vfsp)
     struct vfs *vfsp;
#endif
{
  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 */
}

#if defined(__STDC__)
static int xfs_root(struct vfs *vfsp,
		    struct vnode **vpp)     
#else
static
int
xfs_root(vfsp, vpp)
     struct vfs *vfsp;
     struct vnode **vpp;
#endif
{
  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 = u.u_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;
}

#if defined(__STDC__)
static int xfs_statfs(struct vfs *vfsp,
		      struct statfs *sbp)     
#else
static
int
xfs_statfs(vfsp, sbp)
     struct vfs *vfsp;
     struct statfs *sbp;
#endif
{
  XFSDEB(XDEBVFOPS, ("xfs_statfs\n"));
  return EINVAL;
}

#if defined(__STDC__)
static int xfs_sync(struct vfs *vfsp)     
#else
static
int
xfs_sync(vfsp)
     struct vfs *vfsp;
#endif
{
  return 0;
}

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

#if defined(__STDC__)
static int xfs_mountroot(struct vfs *vfsp,
			 struct vnode **vpp,
			 char *name)     
#else
static
int
xfs_mountroot(vfsp, vpp, name)
     struct vfs *vfsp;
     struct vnode **vpp;
     char *name;
#endif
{
  XFSDEB(XDEBVFOPS, ("xfs_mountroot\n"));
  return EINVAL;
}

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

struct vfsops xfs_vfsops = {
        xfs_mount,
        xfs_unmount,
        xfs_root,
        xfs_statfs,
        xfs_sync,
        xfs_vget,
        xfs_mountroot,
        xfs_swapvp
};

/*
 * To be able to unmount when the XFS daemon is not
 * responding we need a root vnode, use a dead vnode!
 */
#if defined(__STDC__)
static int enodev(void)
#else
static int 
enodev()
#endif
{
  return ENODEV;
}

#if defined(__STDC__)
static int dead_vnode_inactive(struct vnode *vp, struct ucred *cred)
#else
static int
dead_vnode_inactive(vp, cred)
     register struct vnode *vp;
     struct ucred *cred;
#endif
{
  XFSDEB(XDEBVFOPS, ("dead_vnode_inactive\n"));
  xfs_free(vp, sizeof(*vp));
  return 0;
}

struct vnodeops dead_vnodeops = {
        enodev,			/* xfs_open */
        enodev,			/* xfs_close */
        enodev,			/* xfs_rdwr */
        enodev,			/* xfs_ioctl */
        enodev,			/* xfs_select */
        enodev,			/* xfs_getattr */
        enodev,			/* xfs_setattr */
        enodev,			/* xfs_access */
        enodev,			/* xfs_lookup */
        enodev,			/* xfs_create */
        enodev,			/* xfs_remove */
        enodev,			/* xfs_link */
        enodev,			/* xfs_rename */
        enodev,			/* xfs_mkdir */
        enodev,			/* xfs_rmdir */
        enodev,			/* xfs_readdir */
        enodev,			/* xfs_symlink */
        enodev,			/* xfs_readlink */
        enodev,			/* xfs_fsync */
        dead_vnode_inactive,	/* xfs_inactive */
        enodev,			/* xfs_lockctl */
        enodev,			/* xfs_fid */
        enodev,			/* xfs_getpage */
        enodev,			/* xfs_putpage */
        enodev,			/* xfs_map */
        enodev,			/* xfs_dump */
        enodev,			/* xfs_cmp */
        enodev,			/* xfs_realvp */
        enodev,			/* xfs_cntl */
};

#if defined(__STDC__)
static struct vnode * make_dead_vnode(struct vfs *vfsp)
#else
static struct vnode *
make_dead_vnode(vfsp)
     struct vfs *vfsp;
#endif
{
  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;
}

/*
 *
 */
#if defined(__STDC__)
static int xfs_uprintf_filsys(void)
#else
static
int
xfs_uprintf_filsys()
#endif
{
#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;
}

/*
 * Install and uninstall filesystem.
 */
extern struct vfssw vfssw[];
extern struct vfssw *vfsNVFS;

#if defined(__STDC__)
int xfs_install_filesys(void)
#else
int
xfs_install_filesys()
#endif
{
  struct vfssw *fs;
  
  for (fs = vfssw; fs < vfsNVFS; fs++)
    if (fs->vsw_name == 0 && fs->vsw_ops == 0) /* free slot? */
      {
	fs->vsw_name = xfs_fsname;
	fs->vsw_ops = &xfs_vfsops;
	break;			/* found a free slot */
      }
  if (fs == vfsNVFS)
    {
      uprintf("Failed to find free VFS slot for %s!\n", xfs_fsname);
      xfs_uprintf_filsys();
      return EINVAL;
    }
  return 0;
}

#if defined(__STDC__)
int xfs_uninstall_filesys(void)
#else
int
xfs_uninstall_filesys()
#endif
{
  struct vfssw *fs;
  int i;

  /* Check for open, mounted and active vnodes */
  for (i = 0; i < NXFS; i++)
    if (xfs[i].nodes)
      printf("Warning (error really): There are active vnodes!\n");
  for (fs = vfssw; fs < vfsNVFS; fs++)
    if (fs->vsw_name == xfs_fsname && fs->vsw_ops == &xfs_vfsops)
      {
	fs->vsw_name = 0;
	fs->vsw_ops = 0;
      }
  return 0;
}

#if defined(__STDC__)
int xfs_vdstat_filesys(void)
#else
int
xfs_vdstat_filesys()
#endif
{
  return xfs_uprintf_filsys();
}
