/*
 * Copyright (c) 1995-2000 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_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.138.2.3 2000/10/18 07:24:17 lha Exp $");

static struct dentry *
xfs_lookup (struct inode *inode, struct dentry *dentry);

static int
xfs_fsync(struct file *file, struct dentry *dentry);

static int
do_fsync(struct xfs *xfsp, struct xfs_node *xn, u_int flag);

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

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

static int
xfs_create (struct inode * dir, struct dentry *dentry, int mode);

static int
xfs_mkdir(struct inode * dir, struct dentry *dentry, int mode);

static int
xfs_rmdir(struct inode * dir, struct dentry *dentry);

static int
xfs_readdir(struct file * file, void * dirent, filldir_t filldir);

static int
xfs_rename (struct inode * old_dir, struct dentry *old_dentry,
	     struct inode * new_dir, struct dentry *new_dentry);

static int
xfs_unlink (struct inode * dir, struct dentry *dentry);

static int xfs_symlink(struct inode *dir, struct dentry *dentry,
		       const char *symname);

static int xfs_link(struct dentry *old_dentry,
		    struct inode *dir, struct dentry *dentry);

static int
xfs_release_file (struct inode *inode, struct file *file);

static int
xfs_flush (struct file *file);

static int xfs_mmap (struct file *file,
		     struct vm_area_struct *vma);

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

static int
xfs_d_delete(struct dentry *dentry);

static int
xfs_d_revalidate(struct dentry *dentry, int flags);

static void
xfs_d_release(struct dentry *dentry);

static struct dentry *
xfs_dead_lookup (struct inode *dir, struct dentry *dentry);

static ssize_t
xfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos);

static ssize_t
xfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos);

#ifndef LINUX2_3

/*
 * These are operations specific to linux 2.2
 */

static int xfs_readpage(struct file *file, struct page *page);

static struct dentry *
xfs_follow_link(struct dentry *, struct dentry *, unsigned int);

#else /* LINUX2_3 */

/*
 * These are operations specific to linux 2.3+
 */

static int
xfs_getattr (struct dentry *inode, struct iattr *attr);

static int
xfs_setattr (struct dentry *inode, struct iattr *sb);

static int
xfs_follow_link (struct dentry *dentry, struct nameidata *nd);

#endif /* LINUX2_3 */

#ifdef LINUX2_3

/*
 * Address Space operations
 *
 * These are just placeholders to warn if we do the wrong thing
 * and use them.
 *
 * We pull the handle big time and oops, this shouldn't happen.
 */

static int
xfs_as_writepage(struct file *file, struct page *page)
{
    printk ("XFS PANIC: xfs_as_writepage\n");
    *(int *) NULL = 10;
    return -EIO;
}

static int
xfs_as_readpage(struct file *file, struct page *page)
{
    printk ("XFS PANIC: xfs_as_readpage\n");
    *(int *) NULL = 10;
    return -EIO;
}

static int
xfs_as_syncpage(struct page *page)
{
    printk ("XFS PANIC: xfs_as_syncpage\n");
    *(int *) NULL = 10;
    return -EIO;
}

static int
xfs_as_prepare_write(struct file * file, struct page *page,
		     unsigned foo, unsigned bar)
{
    printk ("XFS PANIC: xfs_as_prepare_write\n");
    *(int *) NULL = 10;
    return -EIO;
}

static int
xfs_as_commit_write(struct file *file, struct page *page,
		    unsigned foo, unsigned bar)
{
    printk ("XFS PANIC: xfs_as_commit_write\n");
    *(int *) NULL = 10;
    return -EIO;
}

static int
xfs_as_bmap(struct address_space *foo, long bar)
{
    printk ("XFS PANIC: xfs_as_bmap\n");
    *(int *) NULL = 10;
    return -EIO;
}

struct address_space_operations xfs_aops = {
    writepage: xfs_as_writepage,
    readpage: xfs_as_readpage,
    sync_page: xfs_as_syncpage,
    prepare_write: xfs_as_prepare_write,
    commit_write: xfs_as_commit_write,
    bmap: xfs_as_bmap,
};

/*
 * We need to fetch the close operation (and store the file
 * to the fileserver).
 */

struct vm_operations_struct xfs_file_shared_mmap;
struct vm_operations_struct xfs_file_private_mmap;

/*
 * When we close the mmap:ed memory, flush the data to the fileserver
 */

static void
xfs_vma_close (struct vm_area_struct *vma)
{
    int error;
    struct file *file = vma->vm_file;
    struct inode *inode = file->f_dentry->d_inode;
    struct xfs *xfsp = XFS_FROM_VNODE(inode);
    struct xfs_node *xn = VNODE_TO_XNODE(inode);

    XFSDEB(XDEBVNOPS, ("xfs_vma_close\n"));

    /*
     * I really want to hook the xfs_as_writepage, but then everything
     * will be cache:ed in the wrong struct address_space
     */

    if (file->f_mode & FMODE_WRITE && vma->vm_flags & VM_MAYWRITE) {
	error = do_fsync(xfsp, xn, XFS_WRITE);
	if (error) {
	    XFSDEB(XDEBVNOPS, ("xfs_vma_close: do_fsync returned %d\n",
			       error));
	}
    }
    iput(inode);
}

#endif /* LINUX2_3 */


/*
 * File operations
 *
 * Common to all linux versions
 */

#ifndef LINUX2_3

struct file_operations xfs_file_operations = {
    read:	xfs_read_file,
    write:	xfs_write_file,
    mmap:	xfs_mmap,
    open:	xfs_open,
    flush:	xfs_flush,
    release:	xfs_release_file,
    fsync:	xfs_fsync,
};

#else /* LINUX2_3 */

struct file_operations xfs_file_operations = {
    read:	xfs_read_file,
    write:	xfs_write_file,
    mmap:	xfs_mmap,
    open:	xfs_open,
    flush:	xfs_flush,
    release:	xfs_release_file,
    fsync:	xfs_fsync,
};

#endif /* LINUX2_3 */

struct file_operations xfs_dead_operations = {
};

struct file_operations xfs_dir_operations = {
    readdir:	xfs_readdir,
    flush:	xfs_flush,
};

struct file_operations xfs_link_operations = {
    flush: xfs_flush,
};

/*
 * Inode operations
 *
 * Split between 2.3 and 2.2 too avoid #ifdefs
 */

#ifndef LINUX2_3

struct inode_operations xfs_file_inode_operations = {
    default_file_ops:	&xfs_file_operations,
    readpage:		xfs_readpage,
    permission:		xfs_permission,
    
};

struct inode_operations xfs_dead_inode_operations = {
    default_file_ops:	&xfs_dead_operations,
    lookup:		xfs_dead_lookup,
};

struct inode_operations xfs_dir_inode_operations = {
    default_file_ops:	&xfs_dir_operations,
    create:		xfs_create,
    lookup:		xfs_lookup,
    link:		xfs_link,
    unlink:		xfs_unlink,
    symlink:		xfs_symlink,
    mkdir:		xfs_mkdir,
    rmdir:		xfs_rmdir,
    rename:		xfs_rename,
    permission:		xfs_permission,
};    

struct inode_operations xfs_link_inode_operations = {
    default_file_ops:	&xfs_link_operations,
    readlink:		xfs_readlink,
    follow_link:	xfs_follow_link,
};

#else /* LINUX2_3 */

struct inode_operations xfs_file_inode_operations = {
    permission: xfs_permission,
    setattr: xfs_setattr,
    getattr: xfs_getattr,
};

struct inode_operations xfs_dir_inode_operations = {
    create: xfs_create,
    lookup: xfs_lookup,
    link: xfs_link,
    unlink: xfs_unlink,
    symlink: xfs_symlink,
    mkdir: xfs_mkdir,
    rmdir: xfs_rmdir,
    rename: xfs_rename,
    permission: xfs_permission,
    setattr: xfs_setattr,
    getattr: xfs_getattr,
};

struct inode_operations xfs_dead_inode_operations = {
    lookup: xfs_dead_lookup
};

struct inode_operations xfs_link_inode_operations = {
    readlink: xfs_readlink,
    follow_link: xfs_follow_link,
};

#endif /* LINUX2_3 */

struct dentry_operations xfs_dentry_operations = {
    d_revalidate: xfs_d_revalidate,
    d_delete	: xfs_d_delete,
    d_release	: xfs_d_release,
};

/*
 *
 */

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

/*
 *
 */

void
xfs_print_lock(char *s, struct semaphore *sem)
{
    XFSDEB(XDEBLOCK, ("lock: %s sem: %p count: %d\n",
		      s, sem, (int)atomic_read(&sem->count)));
}

/*
 *
 */

int
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);
    if (dentry_data == NULL)
        return -ENOMEM;
    memset(dentry_data, 0, sizeof(*dentry_data));
    dentry->d_op = &xfs_dentry_operations;
    dentry_data->valid = 0;
    sema_init(&dentry_data->sem, 1);
    xfs_print_lock("xfs_d_init",&dentry_data->sem);
    dentry->d_fsdata = dentry_data;
    return 0;
}

/*
 *
 */

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 struct dentry *
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 dir: %p\n",
		       dentry, (int)dentry->d_name.len, dentry->d_name.name,
		       dir));

    if (dentry->d_name.len >= XFS_MAX_NAME)
	return -ENAMETOOLONG;

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

    error = xfs_d_init(dentry);
    if (error)
	return ERR_PTR(error);

    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);

    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;
        
        strlcpy(msg.name, dentry->d_name.name, sizeof(msg.name));
        
	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;
        error = 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 ERR_PTR(error);
    }
    return NULL;
}

/*
 *
 */

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: tokens 0x%x\n", xn->tokens));
  
  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:
  XFSDEB(XDEBVFOPS, ("xfs_open_valid: exit tokens 0x%x\n", xn->tokens));
  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));
  print_aliases(i);

  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
xfs_fsync(struct file *file, struct dentry *dentry)
{
    struct inode *inode = DENTRY_TO_INODE(dentry);
    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));
    XFSDEB(XDEBVNOPS, ("xfs_fsync: name: %.*s aliases:",
		       (int)dentry->d_name.len, dentry->d_name.name));
    print_aliases(inode);

    if (xn == NULL)
	return 0;

    if (xn->flags & XFS_DATA_DIRTY)
	error = do_fsync(xfsp, xn, XFS_WRITE | XFS_FSYNC);
    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;

    xfs_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, xfs_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;
    xfs_pag_t pag = xfs_get_pag();
    
    XFSDEB(XDEBVNOPS, ("xfs_access (%p) mode = 0%o aliases:", inode, mode));
    print_aliases(inode);

    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;
}

/*
 * Called when the cached node has been changed, to update the relevant
 * part of the `overlaying' node.
 */

static void
update_from_cache_node (struct inode *inode)
{
    struct inode *t_inode = DENTRY_TO_INODE(DATA_FROM_VNODE(inode));

    inode->i_size   = t_inode->i_size;
    inode->i_blocks = inode->i_size >> I_BLOCKS_BITS; /* / I_BLOCKS_UNIT */
    inode->i_mtime  = t_inode->i_mtime;
    VNODE_TO_XNODE(inode)->flags |= XFS_DATA_DIRTY;
}

/*
 *
 */

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

#ifdef LINUX2_3
	/*
	 * The first time, if we are not init-ed copy the
	 * struct since its static in mm/filemap.c
	 *
	 * We need to know when the file is munmap, so
	 * hook the close.
	 */
	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
	    if (xfs_file_shared_mmap.close == NULL) {
		xfs_file_shared_mmap = *vma->vm_ops;
		xfs_file_shared_mmap.close = xfs_vma_close;
	    }
	    vma->vm_ops = &xfs_file_shared_mmap;
	} else {
	    if (xfs_file_private_mmap.close == NULL) {
		xfs_file_private_mmap = *vma->vm_ops;
		xfs_file_private_mmap.close = xfs_vma_close;
	    } 
	    vma->vm_ops = &xfs_file_private_mmap;
	}	    
	xfs_iref(inode);
#endif
    }
    return error;
}

#ifndef LINUX2_3

/*
 *
 */

static ssize_t
xfs_read_file(struct file *xfs_file, char *buf, size_t count, loff_t *ppos)
{
    int error = 0;
    struct inode *xfs_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_read_file file->f_pos: %d count: %u\n",
		       (int) xfs_file->f_pos,(unsigned)count));
    xfs_print_path(xfs_file->f_dentry);
    XFSDEB(XDEBVNOPS, ("xfs_read_file aliases:"));
    print_aliases(xfs_inode);
    
    error = xfs_data_valid(xfs_inode, XFS_DATA_R);

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

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

    if (error) {
	dput(cache_dentry);
	return error;
    }

    if (cache_file.f_op == NULL ||
	cache_file.f_op->read == NULL) {
	dput(cache_dentry);
	printk(KERN_EMERG "XFS Panic: read not defined in cache file system\n");
	return -EINVAL;
    }

    down(&DENTRY_TO_INODE(cache_dentry)->i_sem);
    error = cache_file.f_op->read(&cache_file, buf, count, ppos);
    xfs_file->f_pos = *ppos;
    up(&DENTRY_TO_INODE(cache_dentry)->i_sem);

    dput(cache_dentry);

    return error;
}

/*
 *
 */

static int
xfs_readpage(struct file *xfs_file, struct page *page)
{
    int error = 0;
    struct inode *xfs_inode;
    struct dentry *cache_dentry;
    struct file cache_file;

#if LINUX_VERSION_CODE >= 131869
    lock_kernel();
#endif

    if (xfs_file->f_dentry == NULL || xfs_file->f_dentry->d_inode == NULL) {
	error = -EINVAL;
	goto out;
    }
    xfs_inode = xfs_file->f_dentry->d_inode;

    XFSDEB(XDEBVNOPS, ("xfs_readpage inode: %p\n", xfs_inode));
    XFSDEB(XDEBVNOPS, ("xfs_readpage aliases:"));
    print_aliases(xfs_inode);
    
    error = xfs_data_valid(xfs_inode, XFS_DATA_R);
    
    if (error)
	return error;

    cache_dentry = DATA_FROM_VNODE(xfs_inode);
    if (cache_dentry == NULL) {
	printk(KERN_EMERG "XFS Panic: readpage on NULL cache file\n");
	error = -EINVAL;
	goto out;
    }

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

    if (error) {
	dput(cache_dentry);	/* ? */
	goto out;
    }

    error = cache_dentry->d_inode->i_op->readpage(&cache_file, page);
    dput(cache_dentry);

out:

#if LINUX_VERSION_CODE >= 131869
    unlock_kernel ();
#endif
    return error;
}

/*
 *
 */

static ssize_t
xfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
    int error = 0;
    struct inode *inode = file->f_dentry->d_inode;
    
    XFSDEB(XDEBVNOPS, ("xfs_write_file file->f_pos: %d count: %u inode: %p ",
		       (int) file->f_pos, (unsigned) count, inode));
    xfs_print_path(file->f_dentry);

    XFSDEB(XDEBVNOPS, ("xfs_write_file ppos: %d\n", (int) *ppos));
    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 (t == NULL) {
	    printk(KERN_EMERG "XFS Panic: write_file on NULL cache file\n");
	    return -EINVAL;
	}

	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;
        }
        
	XFSDEB(XDEBVNOPS, ("xfs_write_file: before down cachefile"));
        down(&DENTRY_TO_INODE(t)->i_sem);
	XFSDEB(XDEBVNOPS, ("xfs_write_file: after down cachefile"));
        error = cache_file.f_op->write(&cache_file,
                                       buf,
                                       count,
                                       ppos);
	XFSDEB(XDEBVNOPS, ("xfs_write_file: return ->write"));
        file->f_pos = *ppos;
	XFSDEB(XDEBVNOPS, ("xfs_write_file: before up cachefile"));
	up(&DENTRY_TO_INODE(t)->i_sem);
	XFSDEB(XDEBVNOPS, ("xfs_write_file: after up cachefile"));

	update_from_cache_node(inode);

    }
    XFSDEB(XDEBVNOPS, ("xfs_write_file: error = %d\n", error));
    return error;
}
#else /* LINUX2_3 */

/*
 *
 */

static ssize_t
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;
    struct xfs_node *xn = VNODE_TO_XNODE(inode);
    
    XFSDEB(XDEBVNOPS, ("xfs_read_file: tokens: 0x%x\n", xn->tokens));

    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error) {
	XFSDEB(XDEBVNOPS, ("xfs_read_file: data not valid %d\n", error));
	return error;
    }

    error = generic_file_read (file, buf, count, ppos);

    XFSDEB(XDEBVNOPS, ("xfs_read_file: error = %d\n", error));
    return error;
}

/*
 *
 */

static ssize_t
xfs_write_file(struct file *file, const char *buf,size_t count, loff_t *ppos)
{
    int error = 0;
    struct inode *inode = file->f_dentry->d_inode;
    struct xfs_node *xn = VNODE_TO_XNODE(inode);
    
    XFSDEB(XDEBVNOPS, ("xfs_write_file: tokens: 0x%x\n", xn->tokens));

    error = xfs_data_valid(inode, XFS_DATA_W);
    if (error) {
	XFSDEB(XDEBVNOPS, ("xfs_write_file: data not valid %d\n", error));
	return error;
    }

    error = generic_file_write (file, buf, count, ppos);

    if (error == count)
	update_from_cache_node(inode);

    XFSDEB(XDEBVNOPS, ("xfs_write_file: error = %d\n", error));
    return error;
}

#endif /* LINUX2_3 */

static int
xfs_create (struct inode * dir, struct dentry *dentry, int mode)
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    int error = 0;
    struct xfs_message_create msg;

    if (!dir)
	return -ENOENT;

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

    XFSDEB(XDEBVNOPS, ("xfs_create: (%s, %d) dir(%p):",
		       dentry->d_name.name, dentry->d_name.len,
		       dir));
    print_aliases(dir);

    msg.header.opcode = XFS_MSG_CREATE;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, dentry->d_name.name, sizeof(msg.name)) >= XFS_MAX_NAME)
	return -ENAMETOOLONG;

    XA_CLEAR(&msg.attr);
    XA_SET_MODE(&msg.attr, mode);
    XA_SET_TYPE(&msg.attr, XFS_FILE_REG);
    XA_SET_GID(&msg.attr, current->fsgid);
    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;

    /* XXX should this really be here with new style dcache insert */
    if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	printk(KERN_EMERG "XFS Panic: dentry not valid\n");
    }

    return error;
    
}

static int
xfs_unlink (struct inode * dir, struct dentry *dentry)
{
    struct xfs_message_remove msg;
    struct xfs *xfsp  = XFS_FROM_VNODE(dir);
    struct xfs_node *xn = VNODE_TO_XNODE(dir);
    int error;
    
    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);
    
    msg.header.opcode = XFS_MSG_REMOVE;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, dentry->d_name.name, sizeof(msg.name)) >= XFS_MAX_NAME)
	return -ENAMETOOLONG;

    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) {
	XFSDEB(XDEBVNOPS, ("xfs_remove: aliases:"));
	print_aliases(dentry->d_inode);
	d_delete(dentry);	
    }

    return error;
}

int
xfs_rename (struct inode * old_dir, struct dentry *old_dentry,
	     struct inode * new_dir, struct dentry *new_dentry)
{
    struct xfs *xfsp = XFS_FROM_VNODE(old_dir);
    struct xfs_message_rename msg;
    int error;

    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);

    msg.header.opcode = XFS_MSG_RENAME;
    msg.old_parent_handle = VNODE_TO_XNODE(old_dir)->handle;
    if (strlcpy(msg.old_name, old_dentry->d_name.name, sizeof(msg.old_name)) >= XFS_MAX_NAME)
	return -ENAMETOOLONG;

    msg.new_parent_handle = VNODE_TO_XNODE(new_dir)->handle;
    if (strlcpy(msg.new_name, new_dentry->d_name.name, sizeof(msg.new_name)) >= XFS_MAX_NAME)
	return -ENAMETOOLONG;
	
    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 LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7)
    if (error == 0)
	d_move(old_dentry, new_dentry);
#endif

    return error;
}

static int
xfs_mkdir(struct inode * dir, struct dentry *dentry, int mode)
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    int error = 0;

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

    if (!dir)
	return -ENOENT;
    if (dentry->d_name.len >= XFS_MAX_NAME)
	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;
	if (strlcpy(msg.name, dentry->d_name.name, sizeof(msg.name)) >= XFS_MAX_NAME)
	    return -ENAMETOOLONG;

	XA_CLEAR(&msg.attr);
	XA_SET_MODE(&msg.attr, mode);
	XA_SET_TYPE(&msg.attr, XFS_FILE_DIR);
	XA_SET_GID(&msg.attr, current->fsgid);

	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;

	/* XXX should this really be here */
	if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	    printk(KERN_EMERG "XFS Panic: dentry not valid\n");
	}
    }

    return error;
}

static int
xfs_rmdir(struct inode * dir, struct dentry *dentry)
{
    struct xfs *xfsp;
    struct xfs_node *xn;
    struct xfs_message_rmdir msg;
    int error;

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

    if (dentry->d_name.len >= XFS_MAX_NAME)
	return -ENAMETOOLONG;

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

    msg.header.opcode = XFS_MSG_RMDIR;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, dentry->d_name.name, sizeof(msg.name)) >= XFS_MAX_NAME)
	    return -ENAMETOOLONG;
    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)
	d_delete(dentry);

    return error;
}

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

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

    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;
	if (strlcpy(msg.name, name, sizeof(msg.name)) >= XFS_MAX_NAME)
	    return -ENAMETOOLONG;

	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;
    }

    return error;
}

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

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

    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;
	if (strlcpy(msg.name, name, sizeof(msg.name)) >= XFS_MAX_NAME)
	    return -ENAMETOOLONG;
	if (strlcpy(msg.contents, symname, sizeof(msg.contents)) >= XFS_MAX_SYMLINK_CONTENT)
	    return -ENAMETOOLONG;
	XA_CLEAR(&msg.attr);
	XA_SET_MODE(&msg.attr, 0777);
	XA_SET_TYPE(&msg.attr, XFS_FILE_LNK);
	XA_SET_GID(&msg.attr, current->fsgid);

	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;
	/* XXX should this really be here */
	if (!DENTRY_TO_XDENTRY(dentry)->valid) {
	    printk(KERN_EMERG "XFS Panic: dentry not valid\n");
	}
    }

    return error;
}

static int
xfs_readdir(struct file * file, void * dirent, filldir_t filldir)
{
    int error = 0;
    int filldir_error;
    off_t offset, begin_offset;
    struct inode *inode = file->f_dentry->d_inode;
    char *buf;
    struct dentry *t;
#ifdef LINUX2_3
    struct page *page;
    off_t inpage;
    off_t page_num;
    struct address_space *mapping;
#endif

    XFSDEB(XDEBREADDIR, ("xfs_readdir\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error) {
	printk(KERN_EMERG "xfs_readdir: data not valid: %d\n", error);
	return error;
    }

    t = DATA_FROM_VNODE(inode);
    if (t == NULL) {
	printk(KERN_EMERG "XFS Panic: readdir on NULL cache file\n");
	return -EINVAL;
    }
    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));
	begin_offset = file->f_pos &~ (XFS_DIRENT_BLOCKSIZE - 1);
	offset = file->f_pos & (XFS_DIRENT_BLOCKSIZE - 1);
	file->f_pos = begin_offset;
	XFSDEB(XDEBREADDIR, ("xfs_readdir begin_offset: %d offset: %d\n",
			     (int)begin_offset, (int)offset));
#ifndef LINUX2_3 

	buf = xfs_alloc(XFS_DIRENT_BLOCKSIZE, XFS_MEM_READDIR);
	if (buf == NULL) {
	    printk(KERN_EMERG "XFS Panic: readdir: cannot allocate memory\n");
	    return -ENOMEM;
	}
	
	error = read_exec(t,
			  file->f_pos,
			  buf,
			  XFS_DIRENT_BLOCKSIZE,
			  1);
	if (error < 0) {
	    printk(KERN_EMERG "XFS Panic: cannot read directory: %d\n",
		   error);
	    xfs_free(buf, XFS_MEM_READDIR);
	    return error;
	}
	if (error == 0) {
	    xfs_free(buf, XFS_MEM_READDIR);
	    return error;
	}
#else /* LINUX2_3 */
	mapping = t->d_inode->i_mapping;
	inpage = file->f_pos & (PAGE_CACHE_SIZE-1);
	page_num = file->f_pos >> PAGE_CACHE_SHIFT;

	XFSDEB(XDEBREADDIR,
	       ("xfs_readdir inpage: %d page_num: %d\n",
		(int) inpage,
		(int) page_num));

	page = read_cache_page (mapping, page_num,
				(filler_t *)mapping->a_ops->readpage,
				t);
	if (IS_ERR(page)) {
	    printk(KERN_EMERG "xfs_readdir: read_cache_page failed: %ld\n",
		   PTR_ERR(page));
	    return PTR_ERR(page);
	}
	wait_on_page(page);
	if (!Page_Uptodate(page)) {
	    printk(KERN_EMERG "xfs_readdir: page not uptodate\n");
	    page_cache_release (page);
	    return -EIO;
	}
	buf = (char *)kmap (page);
	buf += inpage;
	error = XFS_DIRENT_BLOCKSIZE;
#endif /* LINUX2_3 */

	file->f_pos += error;

	if (error != XFS_DIRENT_BLOCKSIZE) {
	    printk(KERN_EMERG
		   "XFS Panic: expected %d bytes, got %d in xfs_readdir\n",
		   XFS_DIRENT_BLOCKSIZE, error);
	    kill_proc (current->pid, SIGSEGV, 0);
#ifndef LINUX2_3
	    xfs_free(buf, XFS_MEM_READDIR);
#else
	    kunmap (page);
	    page_cache_release (page);
#endif
	    return -EINVAL;
	}
	XFSDEB(XDEBREADDIR, ("xfs_readdir error: %d\n",error));
	while (offset < XFS_DIRENT_BLOCKSIZE) {
	    struct xfs_dirent *xdirent = (struct xfs_dirent *) (buf + offset);
	    XFSDEB(XDEBREADDIR,
		   ("xfs_readdir offset: %d namlen: %d offset2: %d\n",
		    (int) offset,
		    (int) xdirent->d_namlen,
		    (int) (offset+begin_offset)));
	    if (xdirent->d_fileno != 0
		&& (filldir_error = filldir (dirent,
					     xdirent->d_name,
					     xdirent->d_namlen,
					     offset+begin_offset,
					     xdirent->d_fileno
#ifdef HAVE_FILLDIR_T_DT_TYPE
, DT_UNKNOWN
#endif
		    )) < 0) {
		XFSDEB(XDEBREADDIR,
		       ("xfs_readdir filldir: %d\n", filldir_error));
		file->f_pos = offset + begin_offset;
#ifndef LINUX2_3
		xfs_free(buf, XFS_MEM_READDIR);
#else
		kunmap (page);
		page_cache_release (page);
#endif
		return 0;
	    }
	    offset += xdirent->d_reclen;
	}
#ifndef LINUX2_3
	xfs_free(buf, XFS_MEM_READDIR);
#else
	kunmap (page);
	page_cache_release (page);
#endif
    }
    
    return error;
}
   
#ifndef LINUX2_3

/*
 *
 */

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);
    XFSDEB(XDEBVNOPS, ("xfs_readlink: datavalid: %d\n", error));
    if (error == 0)
    {
	off_t size;
	
	t = DATA_FROM_VNODE(inode);
	if (t == NULL) {
	    printk(KERN_EMERG "XFS Panic: readlink on NULL cache file\n");
	    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);
}

/*
 *
 */

static struct dentry *
xfs_follow_link(struct dentry *dentry,
		struct dentry *base,
		unsigned int follow)
{
    char *buffer;
    int error;

    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;
}

#else /* LINUX2_3 */

/*
 *
 */

static int
xfs_readlink (struct dentry *dentry, char *buffer, int buflen)
{
    int error = 0;
    struct inode *inode = DENTRY_TO_INODE(dentry);

    XFSDEB(XDEBVNOPS, ("xfs_readlink\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error == 0)
	error = page_readlink(dentry, buffer, buflen);

    return error;
}

/*
 *
 */

static int
xfs_follow_link (struct dentry *dentry,
		 struct nameidata *nd)
{
    int error = 0;
    struct inode *inode = DENTRY_TO_INODE(dentry);

    XFSDEB(XDEBVNOPS, ("xfs_follow_link\n"));
    
    error = xfs_data_valid(inode, XFS_DATA_R);
    if (error)
	return error;

    return page_follow_link(dentry, nd);
}

/*
 * fetch the attributes of `dentry' and store them in `attr'.
 * as far as I can tell this is not used in 2.3.48 but we
 * implement it here sa part of the 5 year plan
 */

static int
xfs_getattr (struct dentry *dentry, struct iattr *attr)
{
    struct inode *inode = DENTRY_TO_INODE(dentry);
    int error;

    XFSDEB(XDEBVNOPS, ("xfs_getattr\n"));

    error = xfs_attr_valid(inode, XFS_ATTR_R);
    if (error == 0) {
	attr->ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID
	    | ATTR_SIZE | ATTR_ATIME | ATTR_MTIME
	    | ATTR_CTIME;
	attr->ia_mode  = inode->i_mode;
	attr->ia_uid   = inode->i_uid;
	attr->ia_gid   = inode->i_gid;
	attr->ia_size  = inode->i_size;
	attr->ia_atime = inode->i_atime;
	attr->ia_mtime = inode->i_mtime;
	attr->ia_ctime = inode->i_ctime;
    }

    return error;
}

/*
 *
 */

static int
xfs_setattr (struct dentry *dentry, struct iattr *attr)
{
    struct inode *inode = DENTRY_TO_INODE(dentry);
    struct xfs_node *xn = VNODE_TO_XNODE(inode);
    struct xfs *xfsp    = XFS_FROM_VNODE(inode);
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_setattr\n"));

    if (XFS_TOKEN_GOT(xn, XFS_ATTR_W)) {
        /* Update attributes and mark them dirty. */
        VNODE_TO_XNODE(inode)->flags |= XFS_ATTR_DIRTY;
	return -EINVAL;                /* XXX not yet implemented */
    } else {
        struct xfs_message_putattr msg;

        msg.header.opcode = XFS_MSG_PUTATTR;
	msg.cred.uid = current->uid;
	msg.cred.pag = xfs_get_pag();
        msg.handle = xn->handle;
        vattr2xfs_attr(attr, &msg.attr);
	inode_setattr(inode, attr);
	if (XFS_TOKEN_GOT(xn, XFS_DATA_R)) {
	    if (S_ISREG(inode->i_mode))
		XA_SET_SIZE(&msg.attr,  inode->i_size);
	    XA_SET_MTIME(&msg.attr, inode->i_mtime);
	}
        error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
        if (error == 0) {
            error = ((struct xfs_message_wakeup *) & msg)->error;
	    
	}
        XFS_TOKEN_CLEAR(xn, XFS_ATTR_VALID, XFS_ATTR_MASK);
    }
    
    return error;
}

#endif /* LINUX2_3 */


/*
 *
 */

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;
}

/*
 *
 */

static int
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_release_file\n"));
    XFSDEB(XDEBVNOPS,
	   ("xfs_release_file inode->i_count: %d inode: %p aliases:",
	    xfs_iread(inode), inode));
    print_aliases(inode);
    
    if (file->f_mode & FMODE_WRITE
	&& (xn->flags & XFS_DATA_DIRTY
	    || (DATA_FROM_VNODE(inode) != NULL
		&& DATA_FROM_VNODE(inode)->d_inode != NULL
		&& DATA_FROM_VNODE(inode)->d_inode->i_state & I_DIRTY)))
	error = do_fsync(xfsp, xn, XFS_WRITE);
    
#ifndef LINUX2_3
    invalidate_inode_pages(inode);
#endif
    
    if (error)
	XFSDEB(XDEBVNOPS, ("xfs_release_file error: %d\n",error));

    return error;
}

#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

/*
 * Return 1 if `dentry' is still valid, otherwise 0.
 */

static int
xfs_d_revalidate(struct dentry *dentry, int flags)
{
    struct inode *inode = DENTRY_TO_INODE(dentry);
    XFSDEB(XDEBVNOPS, ("xfs_d_revalidate %p \"%.*s\" (inode %p)\n",
		       dentry,
		       (int)dentry->d_name.len,
		       dentry->d_name.name,
		       inode));
    xfs_print_lock("xfs_d_revalidate before down",
		   &DENTRY_TO_XDENTRY(dentry)->sem);
    down(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_revalidate after down",
		   &DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_revalidate before up",
		   &DENTRY_TO_XDENTRY(dentry)->sem);
    up(&DENTRY_TO_XDENTRY(dentry)->sem);
    xfs_print_lock("xfs_d_revalidate after up",
		   &DENTRY_TO_XDENTRY(dentry)->sem);
    if (!DENTRY_TO_XDENTRY(dentry)->valid) {
#ifndef LINUX2_3
	if (dentry->d_count == 1) /* We are the only one */
	    d_drop(dentry);
#else
	if (atomic_read(&dentry->d_count) == 1) /* We are the only one */
	    d_drop(dentry);
#endif
	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 xfs_d_revalidate has not "
	   "been taken care of\n");
    return 0;
}

/*
 *
 */

static int
xfs_d_delete(struct dentry *dentry)
{
    XFSDEB(XDEBVNOPS, ("xfs_d_delete: dentry %p(%.*s): "
		       "all references dropped\n",
		       dentry,
		       (int)dentry->d_name.len,
		       dentry->d_name.name));
    return 0;
}

/*
 * when a `dead' operation is attempted on `inode', try replacing it
 * with the real file system.
 * returning 0 if this was succesful.
 */

static int
try_replacing (struct inode *inode)
{
    struct xfs *xfsp = XFS_FROM_VNODE(inode);

    if (inode->i_op == &xfs_dead_inode_operations
	&& !(xfsp->status & XFS_ROOTINSTALLED)
	&& xfs_replace_root (XFS_TO_VFS(xfsp)) == 0)
	return 0;
    else
	return 1;
}

/*
 *
 */

static struct dentry *
xfs_dead_lookup (struct inode *dir, struct dentry *dentry)
{
    XFSDEB(XDEBVNOPS, ("xfs_dead_lookup: name: %.*s\n", 
		       (int)dentry->d_name.len, dentry->d_name.name));

    if (try_replacing (dir) == 0)
	return dir->i_op->lookup(dir, dentry);
    else
	return ERR_PTR(-ENOTDIR);
}
