/*
 * 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.
 */

#define __NO_VERSION__

#include <xfs/xfs_locl.h>
#include <xfs/xfs_dev.h>
#include <xfs/xfs_cache.h>
#include <xfs/xfs_dirent.h>
#include <xfs/xfs_syscalls.h>
#include <linux/binfmts.h>
#include <linux/file.h>
#include <linux/mm.h>

RCSID("$Id: xfs_inodeops.c,v 1.78 1999/02/06 00:42:42 map Exp $");

static int
#ifndef LINUX2_1
xfs_lookup (struct inode * dir, const char * name, int len,
	    struct inode ** result);
#else
xfs_lookup (struct inode *inode, struct dentry *dentry);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_fsync(struct inode *, struct file *);
#else
xfs_fsync(struct file *file, struct dentry *dentry);
#endif /* LINUX2_1 */

static int
xfs_open(struct inode *i, struct file *f);

static int
xfs_permission(struct inode *inode, int mode);

#ifndef LINUX2_1
static int
xfs_write_file(struct inode *inode, struct file *file, const char *buf, int count);
#else
static ssize_t
xfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_create (struct inode * dir, const char * name, int len, int mode, struct inode ** result);
#else
xfs_create (struct inode * dir, struct dentry *dentry, int mode);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_mkdir(struct inode * dir, const char * name, int len, int mode);
#else
xfs_mkdir(struct inode * dir, struct dentry *dentry, int mode);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_rmdir(struct inode * dir, const char * name, int len);
#else
xfs_rmdir(struct inode * dir, struct dentry *dentry);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_readdir(struct inode * inode, struct file * file,
	    void * dirent, filldir_t filldir);
#else
xfs_readdir(struct file * file, void * dirent, filldir_t filldir);
#endif /* LINUX2_1 */

#ifndef LINUX2_1
int
xfs_rename (struct inode * old_dir, const char * old_name, int old_len,
	     struct inode * new_dir, const char * new_name, int new_len,
	     int must_be_dir);

#else
static int
xfs_rename (struct inode * old_dir, struct dentry *old_dentry,
	     struct inode * new_dir, struct dentry *new_dentry);
#endif /* LINUX2_1 */

static int
#ifndef LINUX2_1
xfs_unlink (struct inode * dir, const char * name, int len);
#else
xfs_unlink (struct inode * dir, struct dentry *dentry);
#endif /* LINUX2_1 */

#ifndef LINUX2_1
static int xfs_symlink(struct inode *dir, const char *name,
		       int len, const char *symname);
#else
static int xfs_symlink(struct inode *dir, struct dentry *dentry,
		       const char *symname);
#endif

#ifndef LINUX2_1
int xfs_link(struct inode *oldinode, struct inode *dir,
	      const char *name, int len);
#else
static int xfs_link(struct dentry *old_dentry,
		    struct inode *dir, struct dentry *dentry);
#endif

static int
xfs_readlink(struct DENTRY *dentry, char *buffer, int buflen);

#ifndef LINUX2_1
static int
xfs_follow_link(struct inode * dir, struct inode * inode,
		int flag, int mode, struct inode ** res_inode);
#elif defined(HAVE_FOLLOW_LINK_THREE_ARGS)
static struct dentry *
xfs_follow_link(struct dentry *, struct dentry *, unsigned int);
#else
static struct dentry *
xfs_follow_link(struct dentry *, struct dentry *);
#endif /* LINUX2_1 */

#ifndef LINUX2_1
static void
#else
static int
#endif /* LINUX2_1 */
xfs_release_file (struct inode *inode, struct file *file);

#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
static int
xfs_flush (struct file *file);
#endif

#ifdef LINUX2_1
static int xfs_mmap (struct file *file, struct vm_area_struct *vma);
#else
static int xfs_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma);
#endif

#ifdef LINUX2_1
static int xfs_readpage(struct file *file, struct page *page);
#else
static int xfs_readpage(struct inode *inode, struct page *page);
#endif

#ifdef LINUX2_1
static ssize_t
xfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos);
#else
static int
xfs_read_file(struct inode *inode, struct file *file, char *buf, int count);
#endif

#if 0
void xfs_truncate (struct inode * inode);
#endif

#ifdef LINUX2_1
static void
xfs_d_delete(struct dentry *dentry);

static int
xfs_d_validate(struct dentry *dentry);

static void
xfs_d_release(struct dentry *dentry);

static int
xfs_dead_lookup (struct inode *dir, struct dentry *dentry);
#endif

static struct file_operations xfs_file_operations = {
        NULL,                   /* lseek */
        xfs_read_file,          /* read */
        xfs_write_file,         /* write */
        NULL,                   /* readdir */
        NULL,                   /* select */
        NULL,                   /* ioctl */
        xfs_mmap,		/* mmap */
        xfs_open,               /* open */
#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
        xfs_flush,              /* flush */
#endif
        xfs_release_file,       /* release */
        xfs_fsync,              /* fsync */
	NULL,			/* fasync */
	NULL,			/* check_media_change */
	NULL			/* revalidate */
};

struct inode_operations xfs_file_inode_operations = {
        &xfs_file_operations,   /* default file operations */
        NULL,                   /* create */
        NULL,                   /* lookup */
        NULL,                   /* link */
        NULL,                   /* unlink */
        NULL,                   /* symlink */
        NULL,                   /* mkdir */
        NULL,                   /* rmdir */
        NULL,                   /* mknod */
        NULL,                   /* rename */
        NULL,                   /* readlink */
        NULL,                   /* follow_link */
        xfs_readpage,           /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        xfs_permission,         /* permission */
        NULL                    /* smap */
};

static struct file_operations xfs_dead_operations = {
        NULL,                   /* lseek */
        NULL,                   /* read */
        NULL,                   /* write */
        NULL,                   /* readdir */
        NULL,                   /* select */
        NULL,                   /* ioctl */
        NULL,                   /* mmap */
        NULL,                   /* open */
#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
        NULL,                   /* flush */
#endif
        NULL,                   /* release */
        NULL,                   /* fsync */
	NULL,			/* fasync */
	NULL,			/* check_media_change */
	NULL			/* revalidate */
};

struct inode_operations xfs_dead_inode_operations = {
        &xfs_dead_operations,   /* default file operations */
        NULL,                   /* create */
#ifdef LINUX2_1
        xfs_dead_lookup,        /* lookup */
#else
        NULL,                   /* lookup */
#endif
        NULL,                   /* link */
        NULL,                   /* unlink */
        NULL,                   /* symlink */
        NULL,                   /* mkdir */
        NULL,                   /* rmdir */
        NULL,                   /* mknod */
        NULL,                   /* rename */
        NULL,                   /* readlink */
        NULL,                   /* follow_link */
        NULL,                   /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        NULL,                   /* permission */
        NULL                    /* smap */
};

static struct file_operations xfs_dir_operations = {
        NULL,                   /* lseek */
        NULL,                   /* read */
        NULL,                   /* write */
        xfs_readdir,            /* readdir */
        NULL,                   /* select */
        NULL,                   /* ioctl */
        NULL,                   /* mmap */
        NULL,                   /* open */
#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
        xfs_flush,              /* flush */
#endif
        NULL,                   /* release */
        NULL,                   /* fsync */
	NULL,			/* fasync */
	NULL,			/* check_media_change */
	NULL			/* revalidate */
};

struct inode_operations xfs_dir_inode_operations = {
        &xfs_dir_operations,   /* default file operations */
        xfs_create,             /* create */
        xfs_lookup,             /* lookup */
        xfs_link,               /* link */
        xfs_unlink,             /* unlink */
        xfs_symlink,            /* symlink */
        xfs_mkdir,              /* mkdir */
        xfs_rmdir,              /* rmdir */
        NULL,                   /* mknod */
        xfs_rename,             /* rename */
        NULL,                   /* readlink */
        NULL,                   /* follow_link */
        NULL,                   /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        xfs_permission,         /* permission */
        NULL                    /* smap */
};

static struct file_operations xfs_link_operations = {
        NULL,                   /* lseek */
        NULL,                   /* read */
        NULL,                   /* write */
        NULL,                   /* readdir */
        NULL,                   /* select */
        NULL,                   /* ioctl */
        NULL,                   /* mmap */
        NULL,                   /* open */
#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
        xfs_flush,              /* flush */
#endif
        NULL,                   /* release */
        NULL,                   /* fsync */
	NULL,			/* fasync */
	NULL,			/* check_media_change */
	NULL			/* revalidate */
};

struct inode_operations xfs_link_inode_operations = {
        &xfs_link_operations,   /* default file operations */
        NULL,                   /* create */
        NULL,                   /* lookup */
        NULL,                   /* link */
        NULL,                   /* unlink */
        NULL,                   /* symlink */
        NULL,                   /* mkdir */
        NULL,                   /* rmdir */
        NULL,                   /* mknod */
        NULL,                   /* rename */
        xfs_readlink,           /* readlink */
        xfs_follow_link,        /* follow_link */
        NULL,                   /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        NULL,                   /* permission */
        NULL                    /* smap */
};

#ifdef LINUX2_1
struct dentry_operations xfs_dentry_operations = {
        xfs_d_validate,  	/* d_validate(struct dentry *) */
        NULL,                   /* d_hash(struct dentry *, struct qstr *) */
        NULL,                   /* d_compare(struct dentry *,
				   struct qstr *, struct qstr *) */
        xfs_d_delete,      	/* d_delete(struct dentry *) */
        xfs_d_release, 		/* d_release(struct dentry *) */
        NULL         		/* d_iput(struct dentry *, struct inode *) */
};
#endif

#ifdef LINUX2_1
static void
xfs_print_path(struct dentry *dentry)
{
    XFSDEB(XDEBVNOPS, ("path: %*s/%*s\n",
		       dentry->d_parent->d_name.len,
		       dentry->d_parent->d_name.name,
		       dentry->d_name.len,
		       dentry->d_name.name));
}
#endif

#ifndef LINUX2_1
static int
xfs_lookup (struct inode * dir, const char * name, int len,
	    struct inode ** result)
{
    struct xfs_message_getnode msg;
    struct xfs *xfsp;
    int error = 0;
    
    struct xfs_node *d;
    struct xfs_node *x;
    
    XFSDEB(XDEBVNOPS, ("xfs_lookup: name: %s(%d) ", name, len));
    
    if (dir == NULL) {
        XFSDEB(XDEBVNOPS, ("ENOENT\n"));
        return -ENOENT;
    }
    if (!S_ISDIR(dir->i_mode)) {
        iput(dir);
        XFSDEB(XDEBVNOPS, ("ENOTDIR\n"));
        return -ENOTDIR;
    }
    xfsp = XFS_FROM_VNODE(dir);
    d = VNODE_TO_XNODE(dir);
    if (d == NULL || xfsp == NULL) {
        iput(dir);
        XFSDEB(XDEBVNOPS, ("ENODEV\n"));
        return -ENODEV;
    }

    *result = NULL;
    do {
#ifdef notdef_but_correct_and_not_ported
        error = xfs_access(dvp, VEXEC, cred);
        if (error != 0)
            goto done;
#endif

        if ((x = xfs_cache_lookup(VNODE_TO_XNODE(dir), name, len)) == NULL) {
            msg.header.opcode = XFS_MSG_GETNODE;

            msg.cred.uid = current->uid;
            msg.cred.pag = xfs_get_pag();
            msg.parent_handle = d->handle;
            
            memcpy(msg.name, name, len);
            msg.name[len]='\0';
            
            error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
            
            if (error == 0)
                error = ((struct xfs_message_wakeup *) &msg)->error;
        } else {
	    *result=XNODE_TO_VNODE(x);
	    (*result)->i_count++;
            goto done;
        }
    } while (error == 0);

done:
    if (error)
        XFSDEB(XDEBVNOPS, ("error %d", error));
    XFSDEB(XDEBVNOPS, ("\n"));
    iput(dir);
    return error;
}
#else
void
xfs_print_lock(char *s, struct semaphore *sem)
{
    XFSDEB(XDEBLOCK, ("lock: %s sem: %p count: %d waking: %d wait: %p",
		      s, sem, atomic_read(&sem->count),
		      sem->waking, sem->wait));
    if (sem->wait) {
	XFSDEB(XDEBLOCK, (" task: %p next: %p",
			  sem->wait->task, sem->wait->next));
    }
    XFSDEB(XDEBLOCK, ("\n"));
}

void
xfs_d_init (struct dentry *dentry)
{
    struct xfs_dentry_data *dentry_data;

    XFSDEB(XDEBVNOPS, ("xfs_d_init: dentry: %p\n", dentry));
    dentry_data = xfs_alloc(sizeof(*dentry_data), XFS_MEM_DENTRY);
    memset(dentry_data, 0, sizeof(*dentry_data));
    dentry->d_op = &xfs_dentry_operations;
    dentry_data->valid = 0;
    dentry_data->requested = 0;
    sema_init(&dentry_data->sem, 1);
    xfs_print_lock("xfs_d_init",&dentry_data->sem);
    dentry->d_fsdata = dentry_data;
}

static void
xfs_d_release(struct dentry *dentry)
{
    XFSDEB(XDEBVNOPS, ("xfs_d_release: dentry: %p\n", dentry));
    xfs_free(dentry->d_fsdata, XFS_MEM_DENTRY);
    dentry->d_fsdata = NULL;
}

static int
xfs_lookup (struct inode *dir, struct dentry *dentry)
{
    struct xfs_message_getnode msg;
    struct xfs *xfsp;
    int error = 0;
    
    struct xfs_node *d;
    
    XFSDEB(XDEBVNOPS, ("xfs_lookup: %p name: %*s\n", dentry,
		       dentry->d_name.len, dentry->d_name.name));
    
    if (dir == NULL) {
        XFSDEB(XDEBVNOPS, ("ENOENT\n"));
        return -ENOENT;
    }

    XFSDEB(XDEBVNOPS, ("xfs_lookup: dir: %p\n", dir));

    xfsp = XFS_FROM_VNODE(dir);
    d = VNODE_TO_XNODE(dir);
    if (d == NULL || xfsp == NULL) {
        XFSDEB(XDEBVNOPS, ("ENODEV\n"));
        return -ENODEV;
    }


    xfs_d_init(dentry);
    xfs_print_lock("xfs_lookup before down", &DENTRY_TO_XDENTRY(dentry)->sem);
    down(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_lookup after down", &DENTRY_TO_XDENTRY(dentry)->sem);
    DENTRY_TO_XDENTRY(dentry)->requested = 1;
    d_add(dentry, NULL);
    do {
#ifdef notdef_but_correct_and_not_ported
        error = xfs_access(dvp, VEXEC, cred);
        if (error != 0)
            goto done;
#endif
	if (DENTRY_TO_XDENTRY(dentry)->valid)
	    goto done;

        msg.header.opcode = XFS_MSG_GETNODE;
        
        msg.cred.uid = current->uid;
        msg.cred.pag = xfs_get_pag();
        msg.parent_handle = d->handle;
        
        memcpy(msg.name, dentry->d_name.name, dentry->d_name.len);
        msg.name[dentry->d_name.len]='\0';
        
	XFSDEB(XDEBVNOPS, ("xfs_lookup: sending getnode rpc, dentry: %p\n",
			   dentry));
        error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	XFSDEB(XDEBVNOPS, ("xfs_lookup: getnode rpc done, dentry: %p\n",
			   dentry));
        
        if (error == 0)
            error = ((struct xfs_message_wakeup *) &msg)->error;
    } while (error == 0);

done:
    if (error == -ENOENT) {
        XFSDEB(XDEBVNOPS, ("xfs_lookup: leaving negative cache\n"));
	DENTRY_TO_XDENTRY(dentry)->valid = 1;
	DENTRY_TO_XDENTRY(dentry)->requested = 0;
        error = 0;
    }
    if (DENTRY_TO_XDENTRY(dentry)->requested && error == 0)
	printk(KERN_EMERG "XFS Panic: xfs_lookup: requested != 0\n");
    DENTRY_TO_XDENTRY(dentry)->requested = 0;
    
    xfs_print_lock("xfs_lookup before up", &DENTRY_TO_XDENTRY(dentry)->sem);
    up(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_lookup after up", &DENTRY_TO_XDENTRY(dentry)->sem);
    if (error)
        XFSDEB(XDEBVNOPS, ("error %d", error));
    return error;
}
#endif /* LINUX2_1 */


static int
xfs_open_valid(struct inode *vp, u_int tok)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;

  XFSDEB(XDEBVFOPS, ("xfs_open_valid\n"));
  
  do {
    if (!XFS_TOKEN_GOT(xn, tok))
      {
	struct xfs_message_open msg;
	msg.header.opcode = XFS_MSG_OPEN;
	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
	msg.handle = xn->handle;
	msg.tokens = tok;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	goto done;
      }
  } while (error == 0);

done:
  return error;
}

static int
xfs_open(struct inode *i, struct file *f)
{
  XFSDEB(XDEBVNOPS, ("xfs_open inode: %p f->f_mode: %d aliases:",
		     i, f->f_mode));
#ifdef LINUX2_1
  print_aliases(i);
#endif

  if (f->f_mode & FMODE_WRITE)
    return xfs_open_valid(i, XFS_OPEN_NW);
  else
    return xfs_open_valid(i, XFS_OPEN_NR);
}

static int
do_fsync(struct xfs *xfsp, struct xfs_node *xn, u_int flag)
{
    int error;
    struct xfs_message_putdata msg;

    msg.header.opcode = XFS_MSG_PUTDATA;
    msg.cred.uid = current->uid;
    msg.cred.pag = xfs_get_pag();
    msg.handle = xn->handle;
    msg.flag = flag;
    XA_CLEAR(&msg.attr);
    XA_SET_MTIME(&msg.attr, XNODE_TO_VNODE(xn)->i_mtime);

    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

    if (error == 0)
	xn->flags &= ~XFS_DATA_DIRTY;

    XFSDEB(XDEBVNOPS, ("do_fsync error:%d\n", error));

    return error;
}

static int
#ifndef LINUX2_1
xfs_fsync(struct inode *inode, struct file *file)
#else
xfs_fsync(struct file *file, struct dentry *dentry)
#endif /* LINUX2_1 */
{
#ifdef LINUX2_1
    struct inode *inode = DENTRY_TO_INODE(dentry);
#endif /* LINUX2_1 */
    struct xfs *xfsp = XFS_FROM_VNODE(inode);
    struct xfs_node *xn = VNODE_TO_XNODE(inode);
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_fsync: 0x%p\n", inode));
#ifdef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_fsync: name: %*s aliases:",
		       dentry->d_name.len, dentry->d_name.name));
    print_aliases(inode);
#endif

    if (xn == NULL)
	return 0;

    if (xn->flags & XFS_DATA_DIRTY)
	error = do_fsync(xfsp, xn, XFS_WRITE);
    return error;
}

static int
xfs_attr_valid(struct inode * vp, u_int tok)
{
    struct xfs *xfsp = XFS_FROM_VNODE(vp);
    struct xfs_node *xn = VNODE_TO_XNODE(vp);
    int error = 0;

    pag_t pag = xfs_get_pag();

    do {
        if (!XFS_TOKEN_GOT(xn, tok)) {
            struct xfs_message_getattr msg;

            msg.header.opcode = XFS_MSG_GETATTR;
            msg.cred.uid = current->uid;
            msg.cred.pag = pag;
            msg.handle = xn->handle;
            error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
            if (error == 0)
                error = ((struct xfs_message_wakeup *) & msg)->error;
        } else {
            goto done;
        }
    } while (error == 0);

done:
    return error;
}

static int
xfs_rights_valid(struct inode * vp, pag_t pag)
{
    struct xfs *xfsp = XFS_FROM_VNODE(vp);
    struct xfs_node *xn = VNODE_TO_XNODE(vp);
    int error = 0;
    XFSDEB(XDEBVNOPS, ("pag: %d\n", pag));

    do {
        if (!xfs_has_pag(xn, pag))
        {
            struct xfs_message_getattr msg;

            msg.header.opcode = XFS_MSG_GETATTR;
            msg.cred.uid = current->uid;
            msg.cred.pag = pag;
            msg.handle = xn->handle;
            error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
            if (error == 0)
                error = ((struct xfs_message_wakeup *) & msg)->error;
        }
        else {
            goto done;
        }
    } while (error == 0);

done:
    return error;
}


static int
check_rights (u_char rights, int mode)
{
    int error = 0;

    if (mode & MAY_READ)
	if ((rights & XFS_RIGHT_R) == 0)
	    error = -EACCES;
    if (mode & MAY_WRITE)
	if ((rights & XFS_RIGHT_W) == 0)
	    error = -EACCES;
    if (mode & MAY_EXEC)
	if ((rights & XFS_RIGHT_X) == 0)
	    error = -EACCES;
    return error;
}


static int
xfs_permission(struct inode *inode, int mode)
{
    int error = 0;
    pag_t pag = xfs_get_pag();
    
    XFSDEB(XDEBVNOPS, ("xfs_access (%p) mode = 0%o aliases:", inode, mode));
#ifdef LINUX2_1
    print_aliases(inode);
#endif

    if (VNODE_TO_XNODE(inode) == NULL) {
       printk(KERN_EMERG "XFS Panic: xnode == NULL in xfs_permission\n");
       printk(KERN_EMERG "   inode = %p i_ino: 0x%lx\n", inode, inode->i_ino);
       return -EACCES;
    }

    error = xfs_attr_valid(inode, XFS_ATTR_R);
    if (error == 0) {
	struct xfs_node *xn = VNODE_TO_XNODE(inode);
	int i;

	error = check_rights (xn->anonrights, mode);
	
	if (error == 0)
	    goto done;

	XFSDEB(XDEBVNOPS, ("xfs_access anonaccess failed\n"));

	xfs_rights_valid(inode, pag); /* ignore error */
	
	error = -EACCES;
	
	for (i = 0; i < MAXRIGHTS; i++)
	    if (xn->id[i] == pag) {
		error = check_rights (xn->rights[i], mode);
		break;
	    }
    }

done:
    XFSDEB(XDEBVNOPS, ("xfs_access(0%o) = %d\n", mode, error));
    return error;
}

static int
xfs_data_valid(struct inode *vp, u_int tok)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  do {
    if (!XFS_TOKEN_GOT(xn, tok))
      {
	struct xfs_message_getdata msg;
	msg.header.opcode = XFS_MSG_GETDATA;
	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
	msg.handle = xn->handle;
	msg.tokens = tok;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	goto done;
      }
  } while (error == 0);

 done:
  return error;
}


#ifndef LINUX2_1
static int
xfs_read_file(struct inode *inode, struct file *file, char *buf, int count)
{
    int error = 0;
    
    XFSDEB(XDEBVNOPS, ("xfs_read_file file->f_pos: %d count: %d\n",(int) file->f_pos,count));
    error = xfs_data_valid(inode, XFS_DATA_R);

    if (error == 0) {
	return generic_file_read(inode, file, buf, count);
    }
    return error;
}
#else
static int
xfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
{
    int error = 0;
    struct inode *inode = file->f_dentry->d_inode;
    
    XFSDEB(XDEBVNOPS, ("xfs_read_file file->f_pos: %d count: %d\n",(int) file->f_pos,count));
    xfs_print_path(file->f_dentry);
    XFSDEB(XDEBVNOPS, ("xfs_read_file aliases:"));
    print_aliases(inode);
    
    error = xfs_data_valid(inode, XFS_DATA_R);

    if (error == 0) {
	return generic_file_read(file, buf, count, ppos);
    }
    return error;
}
#endif

#ifdef LINUX2_1
static int xfs_mmap (struct file *file, struct vm_area_struct *vma)
#else
static int xfs_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
#endif
{
    int error = 0;
#ifdef LINUX2_1
    struct inode *inode = file->f_dentry->d_inode;
#endif
    
    XFSDEB(XDEBVNOPS, ("xfs_mmap inode: %p\n", inode));
#ifdef LINUX2_1
    xfs_print_path(file->f_dentry);
    XFSDEB(XDEBVNOPS, ("xfs_mmap aliases:"));
    print_aliases(inode);
#endif
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error == 0) {
#ifdef LINUX2_1
	return generic_file_mmap(file, vma);
#else
	return generic_file_mmap(inode, file, vma);
#endif
    }
    return error;
}

#ifndef LINUX2_1
static int
xfs_readpage(struct inode *inode, struct page *page)
{
    int error;

    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error == 0) {
	struct inode *t = DATA_FROM_VNODE(inode);
	t->i_count++;
	if (t->i_op &&
	    t->i_op->readpage)
	    error = t->i_op->readpage(t, page);
	else {
	    printk(KERN_EMERG "XFS Panic: readpage not defined in cache file system\n");
	    error = -EINVAL;
	}
	iput(t);
    }
    return error;
}
#else
static int xfs_readpage(struct file *xfs_file, struct page *page)
{
    int error = 0;
    struct inode *xfs_inode;
    struct inode *cache_inode;
    struct dentry *cache_dentry;
    struct file cache_file;

    if (xfs_file->f_dentry == NULL || xfs_file->f_dentry->d_inode == NULL)
	return -EINVAL;

    xfs_inode = xfs_file->f_dentry->d_inode;
    
    XFSDEB(XDEBVNOPS, ("xfs_readpage file: %p inode: %p\n",
		       xfs_file, xfs_inode));
    XFSDEB(XDEBVNOPS, ("xfs_readpage aliases:"));
    print_aliases(xfs_inode);
    
    error = xfs_data_valid(xfs_inode, XFS_DATA_R);
    
    if (error)
	return error;

    if (DATA_FROM_VNODE(xfs_inode) == NULL) {
	printk(KERN_EMERG "XFS Panic: readpage on NULL cache file\n");
	return -EINVAL;
    }
    cache_inode = DENTRY_TO_INODE(DATA_FROM_VNODE(xfs_inode));

    cache_dentry = d_alloc(NULL, &(const struct qstr) { "xfs cache file", 14, 0});
    cache_dentry->d_sb = xfs_inode->i_sb;
    cache_dentry->d_parent = cache_dentry;
    
    cache_inode->i_count++;
    d_instantiate(cache_dentry, cache_inode);

    error = init_private_file(&cache_file, cache_dentry, FMODE_READ);

    if (error)
	return error;

    error = cache_inode->i_op->readpage(&cache_file, page);

    dput(cache_dentry);

    return error;
}
#endif

#ifdef LINUX2_1
static ssize_t
xfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
#else
static int
xfs_write_file(struct inode *inode, struct file *file, const char *buf, int count)
#endif
{
    int error = 0;
#ifdef LINUX2_1
    struct inode *inode = file->f_dentry->d_inode;
#endif
    unsigned long page;
    
    XFSDEB(XDEBVNOPS, ("xfs_write_file file->f_pos: %d count: %d inode: %p ",(int) file->f_pos,count,inode));
#ifdef LINUX2_1
    xfs_print_path(file->f_dentry);
#else
    XFSDEB(XDEBVNOPS, ("\n"));
#endif

#ifdef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_write_file ppos: %d\n", (int) *ppos));
#endif
    XFSDEB(XDEBVNOPS, ("xfs_write_file buf: %p (%x %x)\n", buf, (int) buf[0], (int) buf[1]));

    error = xfs_data_valid(inode, XFS_DATA_W);
    
    if (error == 0) {
        struct DENTRY *t = DATA_FROM_VNODE(inode);
        struct file cache_file;
        
	if (DENTRY_TO_INODE(t)->i_op == NULL ||
	    DENTRY_TO_INODE(t)->i_op->default_file_ops == NULL) {
            printk(KERN_EMERG "XFS Panic: cache file system does not have file operations\n");
            return -EINVAL;
	}

	if (init_private_file(&cache_file, t, FMODE_WRITE))
	    return -EINVAL;

	cache_file.f_mode = file->f_mode;
	cache_file.f_flags = file->f_flags;

        if (cache_file.f_op == NULL ||
            cache_file.f_op->write == NULL) {
            printk(KERN_EMERG "XFS Panic: write not defined in cache file system\n");
            return -EINVAL;
        }
        
        down(&DENTRY_TO_INODE(t)->i_sem);
#ifdef LINUX2_1
        error = cache_file.f_op->write(&cache_file,
                                       buf,
                                       count,
                                       ppos);
        file->f_pos = *ppos;
#else
	cache_file.f_pos = file->f_pos;
	error = cache_file.f_op->write(t,
				       &cache_file,
				       buf,
				       count);
	file->f_pos = cache_file.f_pos;
#endif
	up(&DENTRY_TO_INODE(t)->i_sem);

        inode->i_size = DENTRY_TO_INODE(t)->i_size;
	inode->i_blocks = inode->i_size / I_BLOCKS_UNIT;
        inode->i_mtime = DENTRY_TO_INODE(t)->i_mtime;
        VNODE_TO_XNODE(inode)->flags |= XFS_DATA_DIRTY;

#if 0			/* XXX - should this be done in */
			/* a) 2.0.x? */
			/* b) 2.1.x? */
			/* c) both? */
			/* d) neither? */
			/* e) none of the above? */
	page = get_free_page(GFP_KERNEL);
	if (!page)
	    invalidate_inode_pages(inode);
	else {
	    int pos = file->f_pos;
	    int cnt = count;
	    int blk;
	    char *data=(char *)page;

	    while (cnt > 0) {
		blk = cnt;
		if (blk > PAGE_SIZE)
		    blk = PAGE_SIZE;
		copy_from_user(data, buf, blk);
		update_vm_cache(inode, pos, data, blk);
		pos += blk;
		cnt -= blk;
	    }
	    free_page(page);
	}
#endif
    }
    return error;
}


static int
#ifndef LINUX2_1
xfs_create (struct inode * dir, const char * name, int len, int mode, struct inode ** result)
#else
xfs_create (struct inode * dir, struct dentry *dentry, int mode)
#endif /* LINUX2_1 */
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    int error = 0;
    struct xfs_message_create msg;

#ifndef LINUX2_1
    *result = NULL;
#endif /* LINUX2_1 */
    if (!dir)
	return -ENOENT;

    xfsp = XFS_FROM_VNODE(dir);
    xn = VNODE_TO_XNODE(dir);

#ifndef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_create: (%s, %d)\n",
		       name, len));
#else
    XFSDEB(XDEBVNOPS, ("xfs_create: (%s, %d) dir(%p):",
		       dentry->d_name.name, dentry->d_name.len,
		       dir));
    print_aliases(dir);
#endif /* LINUX2_1 */

    msg.header.opcode = XFS_MSG_CREATE;
    msg.parent_handle = xn->handle;
#ifndef LINUX2_1
    strncpy(msg.name, name, 256);
#else
    strncpy(msg.name, dentry->d_name.name, 256);
#endif /* LINUX2_1 */

#ifdef LINUX2_1
    DENTRY_TO_XDENTRY(dentry)->requested = 1;
#endif
    XA_CLEAR(&msg.attr);
    XA_SET_MODE(&msg.attr, mode);
    XA_SET_TYPE(&msg.attr, XFS_FILE_REG);
    msg.mode = 0;		/* XXX */
    msg.cred.uid = current->uid;
    msg.cred.pag = xfs_get_pag();
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

#ifndef LINUX2_1
    if (error == 0)
	error = xfs_lookup(dir, name, len, result);
#else
    if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	printk(KERN_EMERG "XFS Panic: dentry not valid\n");
    }
    DENTRY_TO_XDENTRY(dentry)->requested = 0;
#endif /* LINUX2_1 */

    return error;
    
}

int
#ifndef LINUX2_1
xfs_unlink (struct inode * dir, const char * name, int len)
#else
xfs_unlink (struct inode * dir, struct dentry *dentry)
#endif /* LINUX2_1 */
{
    struct xfs_message_remove msg;
    struct xfs *xfsp  = XFS_FROM_VNODE(dir);
    struct xfs_node *xn = VNODE_TO_XNODE(dir);
#ifndef LINUX2_1
    struct xfs_node *xn_file;
#endif /* LINUX2_1 */
    int error;
    
#ifndef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_remove: (%s, %d)\n", name, len));
#else
    xfs_print_path(dentry);
    XFSDEB(XDEBVNOPS, ("xfs_remove: dentry: %p aliases:", dentry));
    print_aliases(dentry->d_inode);
    XFSDEB(XDEBVNOPS, ("xfs_remove: dir: %p aliases:", dir));
    print_aliases(dir);
#endif /* LINUX2_1 */
    
    msg.header.opcode = XFS_MSG_REMOVE;
    msg.parent_handle = xn->handle;
#ifndef LINUX2_1
    strncpy(msg.name, name, 256);
#else
    strncpy(msg.name, dentry->d_name.name, 256);
#endif /* LINUX2_1 */
    msg.cred.uid = current->uid;
    msg.cred.pag = xfs_get_pag();
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
        error = ((struct xfs_message_wakeup *) &msg)->error;

#ifndef LINUX2_1
    xn_file = xfs_cache_lookup(xn, name, len);
    if (xn_file != NULL)
	xfs_cache_delete(xn_file);
    iput(dir);
#else
    if (error == 0) {
	XFSDEB(XDEBVNOPS, ("xfs_remove: aliases:"));
	print_aliases(dentry->d_inode);
	d_delete(dentry);	
    }
#endif /* LINUX2_1 */

    return error;
}

int
#ifndef LINUX2_1
xfs_rename (struct inode * old_dir, const char * old_name, int old_len,
	     struct inode * new_dir, const char * new_name, int new_len,
	     int must_be_dir)
#else
xfs_rename (struct inode * old_dir, struct dentry *old_dentry,
	     struct inode * new_dir, struct dentry *new_dentry)
#endif /* LINUX2_1 */
{
    struct xfs *xfsp = XFS_FROM_VNODE(old_dir);
    struct xfs_message_rename msg;
#ifndef LINUX2_1
    struct xfs_node *xn_file;
#endif /* LINUX2_1 */
    int error;

#ifdef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_rename old"));
    xfs_print_path(old_dentry);
    XFSDEB(XDEBVNOPS, ("xfs_rename: dentry: %p aliases:", old_dentry));
    if (old_dentry->d_inode)
	print_aliases(old_dentry->d_inode);
    else
	XFSDEB(XDEBVNOPS, ("\n"));
    XFSDEB(XDEBVNOPS, ("xfs_rename: dir: %p aliases:", old_dir));
    print_aliases(old_dir);
    XFSDEB(XDEBVNOPS, ("xfs_rename new"));
    xfs_print_path(new_dentry);
    XFSDEB(XDEBVNOPS, ("xfs_rename: dentry: %p aliases:", new_dentry));
    if (new_dentry->d_inode)
	print_aliases(new_dentry->d_inode);
    else
	XFSDEB(XDEBVNOPS, ("\n"));
    XFSDEB(XDEBVNOPS, ("xfs_rename: dir: %p aliases:", new_dir));
    print_aliases(new_dir);
#endif

    if (old_dir->i_sb != new_dir->i_sb)
	return -EXDEV;
    
    msg.header.opcode = XFS_MSG_RENAME;
    msg.old_parent_handle = VNODE_TO_XNODE(old_dir)->handle;
#ifndef LINUX2_1
    strncpy(msg.old_name, old_name, 256);
#else
    strncpy(msg.old_name, old_dentry->d_name.name, 256);
#endif /* LINUX2_1 */
    msg.new_parent_handle = VNODE_TO_XNODE(new_dir)->handle;
#ifndef LINUX2_1
    strncpy(msg.new_name, new_name, 256);
#else
    strncpy(msg.new_name, new_dentry->d_name.name, 256);
#endif /* LINUX2_1 */
    msg.cred.uid = current->uid;
    msg.cred.pag = xfs_get_pag();
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;

#ifndef LINUX2_1
    xn_file = xfs_cache_lookup(VNODE_TO_XNODE(old_dir), old_name, old_len);
    xfs_cache_delete(xn_file);
    iput(old_dir);
    iput(new_dir);
#else
    if (error == 0)
	d_move(old_dentry, new_dentry);
#endif /* LINUX2_1 */

    return error;
}

static int
#ifndef LINUX2_1
xfs_mkdir(struct inode * dir, const char * name, int len, int mode)
#else
xfs_mkdir(struct inode * dir, struct dentry *dentry, int mode)
#endif /* LINUX2_1 */
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    int error = 0;

#ifndef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_mkdir name:%s\n",name));
#else
    XFSDEB(XDEBVNOPS, ("xfs_mkdir name:%s\n", dentry->d_name.name));
#endif /* LINUX2_1 */

    if (!dir)
	return -ENOENT;
#ifndef LINUX2_1
    if (len > 255) {
	iput (dir);
#else
    if (dentry->d_name.len > 255) {
#endif /* LINUX2_1 */
	return -ENAMETOOLONG;
    }

    xfsp = XFS_FROM_VNODE(dir);
    xn = VNODE_TO_XNODE(dir);

    {
	struct xfs_message_mkdir msg;

	msg.header.opcode = XFS_MSG_MKDIR;
	msg.parent_handle = xn->handle;
#ifndef LINUX2_1
	strncpy(msg.name, name, 256);
#else
	strncpy(msg.name, dentry->d_name.name, 256);
#endif /* LINUX2_1 */

#ifdef LINUX2_1
	DENTRY_TO_XDENTRY(dentry)->requested = 1;
#endif
	XA_CLEAR(&msg.attr);
	XA_SET_MODE(&msg.attr, mode);
	XA_SET_TYPE(&msg.attr, XFS_FILE_DIR);

	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
#ifdef LINUX2_1
	if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	    printk(KERN_EMERG "XFS Panic: dentry not valid\n");
	}
	DENTRY_TO_XDENTRY(dentry)->requested = 0;
#endif /* LINUX2_1 */
    }

#ifndef LINUX2_1
    iput(dir);
#endif /* LINUX2_1 */

    return error;
}

static int
#ifndef LINUX2_1
xfs_rmdir(struct inode * dir, const char * name, int len)
#else
xfs_rmdir(struct inode * dir, struct dentry *dentry)
#endif /* LINUX2_1 */
{
    struct xfs *xfsp;
    struct xfs_node *xn;
#ifndef LINUX2_1
    struct xfs_node *xn_file;
#endif /* LINUX2_1 */
    struct xfs_message_rmdir msg;
    int error;

#ifndef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_rmdir: (%*s)\n", len, name));
#else
    XFSDEB(XDEBVNOPS, ("xfs_rmdir: (%*s)\n",
		       dentry->d_name.len,
		       dentry->d_name.name));
#endif /* LINUX2_1 */

    if (!dir)
	return -ENOENT;
#ifndef LINUX2_1
    if (len > 255) {
	iput (dir);
#else
    if (dentry->d_name.len > 255) {
#endif /* LINUX2_1 */
	return -ENAMETOOLONG;
    }

    xfsp = XFS_FROM_VNODE(dir);
    xn = VNODE_TO_XNODE(dir);

    msg.header.opcode = XFS_MSG_RMDIR;
    msg.parent_handle = xn->handle;
#ifndef LINUX2_1
    strncpy(msg.name, name, 256);
#else
    strncpy(msg.name, dentry->d_name.name, 256);
#endif /* LINUX2_1 */
    msg.cred.uid = current->uid;
    msg.cred.pag = xfs_get_pag();
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;

    if (error == 0) {
#ifndef LINUX2_1
	xn_file = xfs_cache_lookup(xn, name, len);
	if (xn_file != NULL)
	    xfs_cache_delete(xn_file);
#else
	d_delete(dentry);
#endif /* LINUX2_1 */
    }

#ifndef LINUX2_1
    iput(dir);
#endif /* LINUX2_1 */

    return error;
}

#ifndef LINUX2_1
int xfs_link(struct inode *oldinode, struct inode *dir,
	      const char *name, int len)
#else
static int xfs_link(struct dentry *old_dentry,
		    struct inode *dir, struct dentry *dentry)
#endif
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    struct xfs_node *from_xn;
    int error = 0;
#ifdef LINUX2_1
    const char *name = dentry->d_name.name;
    int len = dentry->d_name.len;
    struct inode *oldinode = DENTRY_TO_INODE(old_dentry);
#endif

    XFSDEB(XDEBVNOPS, ("xfs_link name:%*s\n", len, name));

    if (!dir)
	return -ENOENT;

    xfsp = XFS_FROM_VNODE(dir);
    xn = VNODE_TO_XNODE(dir);
    from_xn = VNODE_TO_XNODE(oldinode);

    {
	struct xfs_message_link msg;

	msg.header.opcode = XFS_MSG_LINK;
	msg.parent_handle = xn->handle;
	msg.from_handle = from_xn->handle;
	strncpy(msg.name, name, 256);
#ifdef LINUX2_1
	DENTRY_TO_XDENTRY(dentry)->requested = 1;
#endif

	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
#ifdef LINUX2_1
	DENTRY_TO_XDENTRY(dentry)->requested = 0;
#endif /* LINUX2_1 */
    }

#ifndef LINUX2_1
    iput(dir);
#endif /* LINUX2_1 */

    return error;
}

#ifndef LINUX2_1
static int xfs_symlink(struct inode *dir, const char *name,
		       int len, const char *symname)
#else
static int xfs_symlink(struct inode *dir, struct dentry *dentry,
		       const char *symname)
#endif
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    int error = 0;
#ifdef LINUX2_1
    const char *name = dentry->d_name.name;
    int len = dentry->d_name.len;
#endif

    XFSDEB(XDEBVNOPS, ("xfs_symlink name:%*s\n", len, name));

    if (!dir)
	return -ENOENT;

    xfsp = XFS_FROM_VNODE(dir);
    xn = VNODE_TO_XNODE(dir);

    {
	struct xfs_message_symlink msg;

	msg.header.opcode = XFS_MSG_SYMLINK;
	msg.parent_handle = xn->handle;
	strncpy(msg.name, name, 256);
	strncpy(msg.contents, symname, 2048);

#ifdef LINUX2_1
	DENTRY_TO_XDENTRY(dentry)->requested = 1;
#endif
	XA_CLEAR(&msg.attr);
	XA_SET_MODE(&msg.attr, 0777);
	XA_SET_TYPE(&msg.attr, XFS_FILE_LNK);

	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
#ifdef LINUX2_1
	if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	    printk(KERN_EMERG "XFS Panic: dentry not valid\n");
	}
	DENTRY_TO_XDENTRY(dentry)->requested = 0;
#endif /* LINUX2_1 */
    }

#ifndef LINUX2_1
    iput(dir);
#endif /* LINUX2_1 */

    return error;
}

#ifndef LINUX2_1
static int xfs_readdir(struct inode * inode, struct file * file,
		       void * dirent, filldir_t filldir)
#else
static int
xfs_readdir(struct file * file, void * dirent, filldir_t filldir)
#endif /* LINUX2_1 */
{
    int error = 0;
    int filldir_error;
    off_t offset, begin_offset;
#ifndef LINUX2_1
    struct xfs_node *xn_file;
#else
    struct inode *inode = file->f_dentry->d_inode;
#endif /* LINUX2_1 */
    int fileno;
    char *buf;
#ifndef LINUX2_1
    long oldfs;
#endif /* LINUX2_1 */

    XFSDEB(XDEBREADDIR, ("xfs_readdir\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error == 0) {
#ifndef LINUX2_1
	struct inode *t = DATA_FROM_VNODE(inode);
	t->i_count++;
	
	while (file->f_pos < t->i_size && error >= 0) {
	  XFSDEB(XDEBREADDIR, ("xfs_readdir file->f_pos: %d t->i_size: %d\n",(int) file->f_pos,(int) t->i_size));
	    if (t->i_op &&
		t->i_op->default_file_ops &&
		t->i_op->default_file_ops->read) {
		buf = xfs_alloc(XFS_DIRENT_BLOCKSIZE, XFS_MEM_READDIR);
		begin_offset = file->f_pos &~ (XFS_DIRENT_BLOCKSIZE - 1);
		offset = file->f_pos & (XFS_DIRENT_BLOCKSIZE - 1);
		
		XFSDEB(XDEBREADDIR, ("xfs_readdir begin_offset: %d offset: %d\n",(int)begin_offset,(int)offset));
		oldfs = get_fs();
		set_fs(KERNEL_DS);
		file->f_pos = begin_offset;
		file->f_reada = 0;
		error = t->i_op->default_file_ops->read(t, file, buf, XFS_DIRENT_BLOCKSIZE);
		set_fs(oldfs);
		if (error == XFS_DIRENT_BLOCKSIZE) {
		  XFSDEB(XDEBREADDIR, ("xfs_readdir error: %d\n",error));
		    while (offset < XFS_DIRENT_BLOCKSIZE) {
			XFSDEB(XDEBREADDIR,
			       ("xfs_readdir offset: %d namlen: %d offset2: %d\n",
				(int) offset,
				(int) ((struct xfs_dirent *) (buf + offset))->d_namlen,
				(int) (offset+begin_offset)));
			xn_file = xfs_cache_lookup (VNODE_TO_XNODE(inode),
						    ((struct xfs_dirent *) (buf + offset))->d_name,
						    ((struct xfs_dirent *) (buf + offset))->d_namlen);
			fileno = xn_file == NULL ?
			    ((struct xfs_dirent *) (buf + offset))->d_fileno :
			    XNODE_TO_VNODE(xn_file)->i_ino;
			if ((filldir_error = filldir (dirent,
						      ((struct xfs_dirent *) (buf + offset))->d_name,
						      ((struct xfs_dirent *) (buf + offset))->d_namlen,
						      offset+begin_offset,
						      fileno)) < 0) {
			    XFSDEB(XDEBREADDIR, ("xfs_readdir filldir: %d\n", filldir_error));
			    file->f_pos = offset + begin_offset;
			    file->f_reada = 0;
			    iput(t);
			    xfs_free(buf, XFS_MEM_READDIR);
			    return 0;
			}
			offset += ((struct xfs_dirent *) (buf + offset))->d_reclen;
		    }
		    xfs_free(buf, XFS_MEM_READDIR);
		} else if (error > 0) {
		    printk(KERN_EMERG "XFS Panic: expected %d bytes, got %d in xfs_readdir\n", XFS_DIRENT_BLOCKSIZE, error);
		    error = -EINVAL;
		}
	    } else {
		error = -EINVAL;
		printk(KERN_EMERG "XFS Panic: read not defined in cache file system\n");
	    }
#else
	struct DENTRY *t = DATA_FROM_VNODE(inode);
	struct qstr sqstr;
	ino_t ino;

	XFSDEB(XDEBREADDIR, ("xfs_readdir: inode: %p data:%p\n",inode, t));

	while (file->f_pos < DENTRY_TO_INODE(t)->i_size && error >= 0) {
	    XFSDEB(XDEBREADDIR, ("xfs_readdir file->f_pos: %d t->i_size: %d\n",(int) file->f_pos,(int) DENTRY_TO_INODE(t)->i_size));
	    buf = xfs_alloc(XFS_DIRENT_BLOCKSIZE, XFS_MEM_READDIR);
	    begin_offset = file->f_pos &~ (XFS_DIRENT_BLOCKSIZE - 1);
	    offset = file->f_pos & (XFS_DIRENT_BLOCKSIZE - 1);
	    
	    XFSDEB(XDEBREADDIR, ("xfs_readdir begin_offset: %d offset: %d\n",(int)begin_offset,(int)offset));
	    file->f_pos = begin_offset;
	    error = read_exec(t,
			      file->f_pos,
			      buf,
			      XFS_DIRENT_BLOCKSIZE,
			      1);
	    if (error < 0) {
		printk(KERN_EMERG "XFS Panic: cannot read directory\n");
		xfs_free(buf, XFS_MEM_READDIR);
		return error;
	    }
	    if (error == 0) {
		xfs_free(buf, XFS_MEM_READDIR);
		return error;
	    }

	    file->f_pos += error;
	    
	    if (error == XFS_DIRENT_BLOCKSIZE) {
		XFSDEB(XDEBREADDIR, ("xfs_readdir error: %d\n",error));
		while (offset < XFS_DIRENT_BLOCKSIZE) {
		    XFSDEB(XDEBREADDIR,
			   ("xfs_readdir offset: %d namlen: %d offset2: %d\n",
			    (int) offset,
			    (int) ((struct xfs_dirent *) (buf + offset))->d_namlen,
			    (int) (offset+begin_offset)));
		    sqstr.name = ((struct xfs_dirent *) (buf + offset))->d_name;
		    sqstr.len = ((struct xfs_dirent *) (buf + offset))->d_namlen;
		    sqstr.hash = xfs_full_name_hash(sqstr.name, sqstr.len);
		    
		    ino = find_inode_number(file->f_dentry->d_parent,
					    &sqstr);

		    fileno = ino ? ino
			: ((struct xfs_dirent *) (buf + offset))->d_fileno;

		    if ((filldir_error = filldir (dirent,
						  sqstr.name,
						  sqstr.len,
						  offset+begin_offset,
						  fileno)) < 0) {
			XFSDEB(XDEBREADDIR, ("xfs_readdir filldir: %d\n",
					     filldir_error));
			file->f_pos = offset + begin_offset;
			xfs_free(buf, XFS_MEM_READDIR);
			return 0;
		    }
		    offset += ((struct xfs_dirent *) (buf + offset))->d_reclen;
		}
	    } else if (error > 0) {
		printk(KERN_EMERG "XFS Panic: expected %d bytes, got %d in xfs_readdir\n",
		       XFS_DIRENT_BLOCKSIZE, error);
		error = -EINVAL;
	    }
	    xfs_free(buf, XFS_MEM_READDIR);
#endif /* LINUX2_1 */
	}
#ifndef LINUX2_1
	iput(t);
#endif /* LINUX2_1 */
    }
    
    return error;
}
   
static int
xfs_do_readlink(struct DENTRY *dentry, char *buffer, int buflen, int to_kmem)
{
    int error = 0;
    struct inode *inode = DENTRY_TO_INODE(dentry);

    struct DENTRY *t;
    
    XFSDEB(XDEBVNOPS, ("xfs_readlink\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error == 0)
    {
	off_t size;
	
	t = DATA_FROM_VNODE(inode);
	if (t == NULL)
	    return -EINVAL;
	size = (buflen > inode->i_size) ?
	    (inode->i_size) :
	    (buflen);
	error = read_exec(t, 0, buffer, size, to_kmem);
    }
    
    return error;
}

static int
xfs_readlink(struct DENTRY *dentry, char *buffer, int buflen)
{
    return xfs_do_readlink(dentry, buffer, buflen, 0);
}

#ifndef LINUX2_1
static int xfs_follow_link(struct inode * dir, struct inode * inode,
			   int flag, int mode, struct inode ** res_inode)
{
    int error;
    char *buffer;
    *res_inode = NULL;
    if (dir == NULL) {
	dir = current->fs->root;
	dir->i_count++;
    }
    if (!inode) {
	iput(dir);
	return -ENOENT;
    }
    if (!S_ISLNK(inode->i_mode)) {
	iput(dir);
	*res_inode = inode;
	return 0;
    }
    if (current->link_count > 5) {
	iput(dir);
	iput(inode);
	return -ELOOP;
    }
    buffer = xfs_alloc(MAXPATHLEN + 1, XFS_MEM_FOLLOWLINK);
    if (buffer == NULL) {
	iput(dir);
	iput(inode);
	return -ENOMEM;
    }
    error = xfs_do_readlink(inode, buffer, MAXPATHLEN, 1);
    if (error < 0) {
	iput(dir);
	iput(inode);
	xfs_free(buffer, XFS_MEM_FOLLOWLINK);
	return error;
    }
    if (error <= MAXPATHLEN)
	buffer[error]='\0';
    current->link_count++;
    error = open_namei (buffer, 0, 0, res_inode, dir);
    current->link_count--;
    iput(inode);
    xfs_free(buffer, XFS_MEM_FOLLOWLINK);
    return error;
}
#else
#if HAVE_FOLLOW_LINK_THREE_ARGS
static struct dentry *
xfs_follow_link(struct dentry *dentry,
		struct dentry *base,
		unsigned int follow)
#else
static struct dentry *
xfs_follow_link(struct dentry *dentry, struct dentry *base)
#endif
{
    char *buffer;
    int error;
#ifndef HAVE_FOLLOW_LINK_THREE_ARGS
    int follow = 1;
#endif

    XFSDEB(XDEBVNOPS, ("xfs_follow_link "));
    xfs_print_path(dentry);
    XFSDEB(XDEBVNOPS, ("base: "));
    xfs_print_path(base);

    buffer = xfs_alloc(MAXPATHLEN + 1, XFS_MEM_FOLLOWLINK);
    if (buffer == NULL) {
	dput(base);
	base = NULL;
	return ERR_PTR(-ENOMEM);
    }
    error = xfs_do_readlink(dentry, buffer, MAXPATHLEN, 1);
    if (error < 0) {
	dput(base);
	base = NULL;
	xfs_free(buffer, XFS_MEM_FOLLOWLINK);
	return ERR_PTR(error);
    }

    if (error <= MAXPATHLEN)
	buffer[error]='\0';
    XFSDEB(XDEBVNOPS, ("xfs_follow_link linkname: %s\n", buffer));
    base = lookup_dentry(buffer, base, follow);
    xfs_free(buffer, XFS_MEM_FOLLOWLINK);
    return base;
}
#endif /* LINUX2_1 */


#ifdef HAVE_STRUCT_FILE_OPERATIONS_FLUSH
static int
xfs_flush (struct file *file)
{
    XFSDEB(XDEBVNOPS, ("xfs_flush\n"));

    if (file && file->f_dentry && file->f_dentry->d_inode)
	return xfs_release_file(file->f_dentry->d_inode, file);
    else
	return 0;
}
#endif

#ifndef LINUX2_1
static void
#else
static int
#endif /* LINUX2_1 */
xfs_release_file (struct inode *inode, struct file *file)
{
    struct xfs *xfsp = XFS_FROM_VNODE(inode);
    struct xfs_node *xn = VNODE_TO_XNODE(inode);
    int error = 0;
    
    XFSDEB(XDEBVNOPS, ("xfs_close\n"));
#ifdef LINUX2_1
    XFSDEB(XDEBVNOPS, ("xfs_close inode->i_count: %d inode: %p aliases:",
		       inode->i_count, inode));
    print_aliases(inode);
#endif
    
    if (file->f_mode & FMODE_WRITE && xn->flags & XFS_DATA_DIRTY)
	error = do_fsync(xfsp, xn, XFS_WRITE);
    
    invalidate_inode_pages(inode);
    
#ifndef LINUX2_1
    if (error)
	XFSDEB(XDEBVNOPS, ("xfs_release_file error: %d\n",error));

    return;
#else
    return error;
#endif /* LINUX2_1 */
}

#if 0
void xfs_truncate (struct inode *inode)
{
    int error = 0;
    
    XFSDEB(XDEBVNOPS, ("xfs_truncate\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_W);
    
    if (error == 0) {
        struct DENTRY *t = DATA_FROM_VNODE(inode);
        
	if (DENTRY_TO_INODE(t)->i_op == NULL ||
	    DENTRY_TO_INODE(t)->i_op->truncate == NULL) {
	    printk(KERN_EMERG "XFS Panic: truncate not defined in cache file system\n");
            return;
	}
	
        down(&DENTRY_TO_INODE(t)->i_sem);
	DENTRY_TO_INODE(t)->i_size = inode->i_size;
        DENTRY_TO_INODE(t)->i_op->truncate(DENTRY_TO_INODE(t));
        up(&DENTRY_TO_INODE(t)->i_sem);

	/* XXXX: This should be reported to arla */

        VNODE_TO_XNODE(inode)->flags |= XFS_DATA_DIRTY;
    }

    if (error) {
	printk(KERN_EMERG "XFS Panic: truncate failed: %d\n", error);
    }
}
#endif

#ifdef LINUX2_1
static int
xfs_d_validate(struct dentry *dentry)
{
    struct inode *inode = DENTRY_TO_INODE(dentry);
    XFSDEB(XDEBVNOPS, ("validate %p \"%*s\" (inode %p)\n",
		       dentry,
		       dentry->d_name.len,
		       dentry->d_name.name,
		       inode));
    xfs_print_lock("xfs_d_validate before down",&DENTRY_TO_XDENTRY(dentry)->sem);
    down(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_validate after down",&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_validate before up",&DENTRY_TO_XDENTRY(dentry)->sem);
    up(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_validate after up",&DENTRY_TO_XDENTRY(dentry)->sem);
    if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	if (dentry->d_count == 1) /* We are the only one */
	    d_drop(dentry);
	return 0;
    }

    if (inode) {
	struct xfs_node *xnode = VNODE_TO_XNODE(inode);

	print_aliases(inode);
	if (IS_ROOT(dentry))
	    return 1;
	if (xnode) {
	    if (XFS_TOKEN_GOT(xnode, XFS_ATTR_VALID)) {
		XFSDEB(XDEBVNOPS, ("valid\n"));
		return 1;
	    } else {
		XFSDEB(XDEBVNOPS, ("invalid\n"));
		    return 0;
	    }
	} else {
	    printk(KERN_EMERG "XFS Panic: inode does not have xnode\n");
	    return 0;
	}
    } else {
	/*
	 * Negative entries are always valid,
	 * they are cleared in xfs_message_invalidnode
	 */
	return 1;
    }
    printk(KERN_EMERG "XFS Panic: a case in d_validate has not been taken care of\n");
    return 0;
}

static void
xfs_d_delete(struct dentry *dentry)
{
    struct list_head *alias;
    struct list_head *this_alias;
    struct dentry *cur_dentry;
    int d_count = 0;
    struct xfs_message_inactivenode msg;
    struct xfs *xfsp;
    struct xfs_node *xn;

    XFSDEB(XDEBVNOPS, ("xfs_d_delete: dentry %p(%*s): all references dropped\n",
		       dentry,
		       dentry->d_name.len,
		       dentry->d_name.name));
    
    this_alias = &dentry->d_alias;
    alias = this_alias->next;
    while (alias != this_alias) {
	if (dentry->d_inode && (&dentry->d_inode->i_dentry == alias)) {
	    alias = alias->next;
	    continue;
	}
	cur_dentry = list_entry(alias, struct dentry, d_alias);
	if (cur_dentry)
	    XFSDEB(XDEBVNOPS,("xfs_d_delete alias: %*s(%p) ref %d\n",
                              cur_dentry->d_name.len,
                              cur_dentry->d_name.name,
                              cur_dentry,
			      cur_dentry->d_count));
	d_count += cur_dentry->d_count;
	alias = alias->next;
    }

    XFSDEB(XDEBVNOPS,("xfs_d_delete: total d_count: %d\n", d_count));
    if (d_count == 0 &&
	dentry->d_inode &&
	VNODE_TO_XNODE(dentry->d_inode)) {
	XFSDEB(XDEBVNOPS,("xfs_d_delete: no references, tell arlad\n"));
	xfsp = XFS_FROM_VNODE(dentry->d_inode);
	xn = VNODE_TO_XNODE(dentry->d_inode);
	XFS_TOKEN_CLEAR(xn, ~0,
			XFS_OPEN_MASK |
			XFS_DATA_MASK |
			XFS_LOCK_MASK);
	msg.header.opcode = XFS_MSG_INACTIVENODE;
	msg.handle = xn->handle;
	msg.flag   = XFS_NOREFS;
	xfs_message_send(xfsp->fd, &msg.header, sizeof(msg));
    }
}
#endif

#ifdef LINUX2_1
static int
get_new_root(struct inode *dir, struct dentry **new_root)
{
	struct xfs *xfsp = VFS_TO_XFS(dir->i_sb);
	struct super_block *sb = XFS_TO_VFS(xfsp);
	struct dentry *root;
	struct dentry *covered;
	
	if (!(xfsp->status & XFS_DEVOPEN)) {
	    XFSDEB(XDEBVNOPS, ("get_new_root: device is still closed\n"));
	    return -ENODEV;
	}
	
	if (sb == NULL) {
	    printk(KERN_EMERG "XFS Panic: get_new_root: file system is mounted, but sb is NULL\n");
	    return -ENODEV;
	}
	root = sb->s_root;
	if (root == NULL) {
	    printk(KERN_EMERG "XFS Panic: get_new_root: file system is mounted, but root is NULL\n");
	    return -ENODEV;
	}
	covered = root->d_covers;
	if (covered == NULL) {
	    printk(KERN_EMERG "XFS Panic: get_new_root: file system is mounted, but root covers NULL\n");
	    return -ENODEV;
	}
	if (DENTRY_TO_INODE(root)->i_op != &xfs_dead_inode_operations) {
	    printk(KERN_EMERG "XFS Panic: get_new_root: device just opened, but root inode was not dead\n");
	    return -ENODEV;
	}
	dput(root);
	root = NULL;
	if (xfs_root(sb,&sb->s_root) || !sb->s_root) {
	    printk(KERN_EMERG "XFS Panic: get_new_root: get root inode failed\n");
	    return -ENODEV;
	}
	root = sb->s_root;
	covered->d_mounts = root;
	root->d_covers = covered;
	*new_root = root;
	return 0;
}

static int
xfs_dead_lookup (struct inode *dir, struct dentry *dentry)
{
    int error = 0;
    struct xfs *xfsp = VFS_TO_XFS(dir->i_sb);

    XFSDEB(XDEBVNOPS, ("xfs_dead_lookup: name: %*s\n", dentry->d_name.len, dentry->d_name.name));

    if ((dir->i_op == &xfs_dead_inode_operations) && !(xfsp->status & XFS_ROOTINSTALLED)) {
	struct dentry *new_root;
	struct dentry *old_root = dentry->d_parent;
	XFSDEB(XDEBVNOPS, ("old_root: %p old_root->d_inode: %p"
			   " old_root->d_subdirs:",
			   old_root, old_root->d_inode));
	print_childs(old_root);
	XFSDEB(XDEBVNOPS, ("dentry: %p dentry->d_parent: %p",
			   dentry, dentry->d_parent));

	error = get_new_root(dir, &new_root);
	if (error)
	    return error;
	list_del(&dentry->d_child);
	list_add(&dentry->d_child, &new_root->d_subdirs);
	dput(old_root);
	dentry->d_parent = dget(new_root);

	XFSDEB(XDEBVNOPS, ("new_root: %p new_root->d_inode: %p "
			   "new_root->d_subdirs:",
			   new_root, new_root->d_inode));
	print_childs(new_root);
	XFSDEB(XDEBVNOPS, ("dentry: %p dentry->d_parent: %p",
			   dentry, dentry->d_parent));

	return DENTRY_TO_INODE(new_root)->i_op->lookup(new_root->d_inode, dentry);
    } else {
	return -ENOTDIR;
    }
}

#endif
