/*
 * 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_message.h>
#include <xfs/xfs_msg_locl.h>
#include <xfs/xfs_deb.h>
#include <xfs/xfs_fs.h>
#include <xfs/xfs_dev.h>

RCSID("$Id: xfs_message.c,v 1.85.2.2 2000/09/14 11:31:31 lha Exp $");

static void
clear_all_children (struct inode *inode, int parent);

int
xfs_message_installroot(int fd,
			struct xfs_message_installroot *message,
			u_int size)
{
    int error = 0;
    
    XFSDEB(XDEBMSG, ("xfs_message_installroot\n"));
    
    if (xfs[fd].root != 0) {
	printk(KERN_EMERG "XFS Panic: xfs_message_installroot again\n");
	error = -EBUSY;
    } else {
	xfs[fd].root = new_xfs_node(&xfs[fd], &message->node); /* VN_HOLD's */
    }

    return error;
}

int
xfs_message_installnode(int fd,
			struct xfs_message_installnode *message,
			u_int size)
{
    int error = 0;
    struct xfs_node *n, *dp;
    
    XFSDEB(XDEBMSG, ("xfs_message_installnode\n"));
    
    dp = xfs_node_find(&xfs[fd], &message->parent_handle);
    if (dp) {
	struct dentry *dentry = NULL;
	struct inode *inode = XNODE_TO_VNODE(dp);
	struct dentry *parent = NULL;
	struct qstr sqstr;
	struct list_head *alias;
	
	XFSDEB(XDEBMSG, ("xfs_message_installnode: dp: %p aliases:", inode));
	print_aliases(inode);

	XFSDEB(XDEBMSG, ("xfs_message_installnode: fetching new node\n"));
	n = new_xfs_node(&xfs[fd], &message->node); /* VN_HOLD's */
	XFSDEB(XDEBMSG, ("xfs_message_installnode: inode: %p aliases: ",
			 XNODE_TO_VNODE(n)));
	print_aliases(XNODE_TO_VNODE(n));
	sqstr.name = message->name;
	sqstr.len  = strlen(message->name);
	sqstr.hash = full_name_hash(sqstr.name, sqstr.len);

	/*
	 * for all parent dentries
	 *   if node with name
	 *     if empty
	 *       d_instantiate
	 *     else if `check inode'
	 *       complain
	 *     else
	 *       already inserted
	 *   else
	 *     allocate node
	 *
	 */

	alias = inode->i_dentry.next;
	while (alias != &inode->i_dentry) {
	    parent = list_entry(alias, struct dentry, d_alias);
#ifdef HAVE_DGET_LOCKED
	    spin_lock(&dcache_lock);
	    parent = dget_locked(parent);
	    spin_unlock(&dcache_lock);
#else
	    parent = dget(parent);
#endif
	    dentry = d_lookup(parent, &sqstr);
	    XFSDEB(XDEBMSG, ("xfs_message_installnode: alias %p\n", parent));
	    XFSDEB(XDEBMSG, ("xfs_message_installnode: lookup %p\n", dentry));

	    if (dentry) {
		if (dentry->d_inode == NULL) {
		    xfs_iref(XNODE_TO_VNODE(n));
		    d_instantiate(dentry, XNODE_TO_VNODE(n));
		    DENTRY_TO_XDENTRY(dentry)->valid = 1;
		} else if (dentry->d_inode != XNODE_TO_VNODE(n)) {
		    printk(KERN_EMERG
			   "XFS SoftAssert: existing inode "
			   "(%x, fid %d.%d.%d.%d) != "
			   "installing %s(%x, fid %d.%d.%d.%d)\n",
			   (unsigned int)dentry->d_inode,
			   VNODE_TO_XNODE(dentry->d_inode)->handle.a,
			   VNODE_TO_XNODE(dentry->d_inode)->handle.b,
			   VNODE_TO_XNODE(dentry->d_inode)->handle.c,
			   VNODE_TO_XNODE(dentry->d_inode)->handle.d,
			   message->name,
			   (unsigned int)XNODE_TO_VNODE(n),
			   n->handle.a,
			   n->handle.b,
			   n->handle.c,
			   n->handle.d);
		} else {
		    DENTRY_TO_XDENTRY(dentry)->valid = 1;
		}
	    } else {
		/* unprovoked installnode, ie bulkstatus) */
		dentry = d_alloc(parent, &sqstr);
		XFSDEB(XDEBMSG, ("xfs_message_installnode: "
				 "allocated new entry: %p\n",
				 dentry));
		error = xfs_d_init(dentry);
		if (error == 0) {
		    DENTRY_TO_XDENTRY(dentry)->valid = 1;
		    xfs_iref(XNODE_TO_VNODE(n));
		    d_add(dentry, XNODE_TO_VNODE(n));
		}
	    }

	    dput(dentry);
	    dentry = NULL;
	    alias = alias->next;
	    dput(parent);
	}
	XFSDEB(XDEBMSG, ("xfs_message_installnode: done installing\n"));
	
	iput(XNODE_TO_VNODE(n));

    } else {
	printk(KERN_EMERG "XFS Panic: xfs_message_install "
	       "could not find parent\n");
	error = -ENOENT;
    }

    return error;
}

int
xfs_message_installattr(int fd, struct xfs_message_installattr *message,
			u_int size)
{
    int error = 0;
    struct xfs_node *t;
    int i;
    
    XFSDEB(XDEBMSG, ("xfs_message_installattr (%d.%d.%d.%d)\n",
		     message->node.handle.a,
		     message->node.handle.b,
		     message->node.handle.c,
		     message->node.handle.d));

    t = xfs_node_find(&xfs[fd], &message->node.handle);
    if (t != 0) {
	struct inode *inode = XNODE_TO_VNODE(t);
	struct dentry *dentry = list_entry(inode->i_dentry.next,
					   struct dentry, d_alias);
	
	XFSDEB(XDEBMSG, ("xfs_message_installattr name:%s\n",
			 dentry->d_name.name));

	/*
	 * Paranoid checks
	 */

	t->tokens = message->node.tokens;
	if (XFS_TOKEN_GOT(t, XFS_DATA_R) && DATA_FROM_XNODE(t) == NULL) {
	    printk(KERN_EMERG 
		   "XFS Panic: xfs_message_installattr: "
		   "got token w/o data!\n");
	    XFS_TOKEN_CLEAR (t, XFS_DATA_R|XFS_DATA_W , XFS_DATA_MASK);
	}
	
	if (!XFS_TOKEN_GOT(t, XFS_DATA_R) && DATA_FROM_XNODE(t))
	    printk(KERN_EMERG 
		   "XFS SoftAssert: xfs_message_installattr: "
		   "got data w/o token!\n");

	t->attr = message->node.attr;
	xfs_attr2inode (&t->attr, inode);
	memmove(t->id, message->node.id, sizeof(t->id));
	memmove(t->rights, message->node.rights, sizeof(t->rights));
	for (i = 0; i < MAXRIGHTS; i++) {
	    XFSDEB(XDEBMSG, ("rights %d:", t->id[i]));
	    XFSDEB(XDEBMSG, (t->rights[i]&XFS_RIGHT_R?"r":"-"));
	    XFSDEB(XDEBMSG, (t->rights[i]&XFS_RIGHT_W?"w":"-"));
	    XFSDEB(XDEBMSG, (t->rights[i]&XFS_RIGHT_X?"x":"-"));
	    XFSDEB(XDEBMSG, ("\n"));
	}
	t->anonrights = message->node.anonrights;
    } else {
	struct xfs_message_inactivenode msg;
	
	XFSDEB(XDEBMSG, ("xfs_message_installattr: no such node\n"));

	/*
	 * Eich, we got a installattr on a node we didn't have.  It
	 * might have been gc in a large message.  Make sure the
	 * user-land daemon knows we doesn't have it.
	 */
	
	msg.header.opcode = XFS_MSG_INACTIVENODE;
	msg.handle = message->node.handle;
	msg.flag   = XFS_NOREFS | XFS_DELETE;
	xfs_message_send(fd, &msg.header, sizeof(msg));
    }

    return error;
}

int
xfs_message_installdata(int fd,
			struct xfs_message_installdata *message,
			u_int size)
{
    struct xfs_node *t;
    struct dentry *dentry;
    int error = 0;
    
    XFSDEB(XDEBMSG, ("xfs_message_installdata\n"));
    
    t = xfs_node_find(&xfs[fd], &message->node.handle);
    if (t != 0) {
	struct inode *inode = XNODE_TO_VNODE(t);

	XFSDEB(XDEBMSG, ("cache_name '%s'\n", message->cache_name));
	dentry = xfs_fh_to_dentry(&message->cache_handle);
	XFSDEB(XDEBMSG, ("xfs_message_installdata dentry: %p\n",
			 dentry));
	if (IS_ERR(dentry)) {
	    printk(KERN_EMERG "XFS Panic: "
		   "xfs_message_installdata failed "
		   "fh_to_dentry = %s, errno: %ld\n",
		   message->cache_name, PTR_ERR(dentry));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,99)
	    dentry = open_namei(message->cache_name, 3, 0);
	    if (IS_ERR(dentry))
		error = PTR_ERR(dentry);
#else
	    {
		struct nameidata nd;

		if (path_init (message->cache_name, LOOKUP_POSITIVE, &nd))
		    error = path_walk (message->cache_name, &nd);
		if (error == 0)
		    dentry = nd.dentry;
	    }
#endif
	    if (error) {
		printk(KERN_EMERG "XFS Panic: "
		       "xfs_message_installdata failed open_namei, "
		       "errno: %d\n", error);
		return error;
	    }
	}
	if (!IS_ERR(dentry)) {
#ifdef LINUX2_3
	    inode->i_mapping = &dentry->d_inode->i_data;
#endif
	    if (DATA_FROM_XNODE(t)) {
		dput(DATA_FROM_XNODE(t));
	    }
	    DATA_FROM_XNODE(t) = dentry;
	    if (message->flag & XFS_ID_INVALID_DNLC)
		clear_all_children (inode, 0);
	    t->tokens = message->node.tokens;
	    t->attr = message->node.attr;
	    XFSDEB(XDEBMSG, ("xfs_message_installdata size before: %ld\n",
			     (long) inode->i_size));
	    xfs_attr2inode (&t->attr, inode);
	    XFSDEB(XDEBMSG, ("xfs_message_installdata size after: %ld\n",
			     (long) inode->i_size));
	    memmove(t->id, message->node.id, sizeof(t->id));
	    memmove(t->rights, message->node.rights, sizeof(t->rights));
	    t->anonrights = message->node.anonrights;
	    error = 0;
	}
    } else {
	printk(KERN_EMERG "XFS Panic: "
	       "xfs_message_installdata didn't find node!\n");
	error = -ENOENT;
    }

    return error;
}

static void
clear_all_children (struct inode *inode, int parent)
{
    struct list_head *alias;

    alias = inode->i_dentry.next;
    while (alias != &inode->i_dentry) {
	struct dentry *dentry;
	struct list_head *subdirs;
	struct xfs_dentry_data *xd;

	dentry = list_entry(alias, struct dentry, d_alias);
	if (dentry == NULL) {
	    printk(KERN_EMERG "XFS Panic: dentry in alias list is null\n");
	    break;
	}
	xd = DENTRY_TO_XDENTRY(dentry);
	if (parent)
	    xd->valid = 0;
	XFSDEB(XDEBMSG, ("clear_all_children parent: %.*s\n",
			 (int)dentry->d_name.len, dentry->d_name.name));
#if 0
	shrink_dcache_parent (dentry);
#endif
#if 1
	subdirs = dentry->d_subdirs.next;
	while (subdirs != &dentry->d_subdirs) {
	    struct list_head *tmp = subdirs;
	    struct dentry *child = list_entry(tmp, struct dentry, d_child);
	    subdirs = tmp->next;
	    XFSDEB(XDEBMSG, ("clear_all_children child: %.*s\n",
			     (int)child->d_name.len, child->d_name.name));
	    if (child->d_inode &&
		child->d_inode->i_sb &&
		child->d_inode->i_sb->s_type &&
		child->d_inode->i_sb->s_type->name &&
		(strcmp(child->d_inode->i_sb->s_type->name, "xfs") == 0)) {
		XFSDEB(XDEBMSG, ("clear_all_children: "
				 "invalidating %p aliases:\n",
				 child->d_inode));
		print_aliases(child->d_inode);
		DENTRY_TO_XDENTRY(child)->valid = 0;
	    }
#ifndef LINUX2_3
	    XFSDEB(XDEBMSG, ("clean_all_children: "
			     "child->d_count: %d child->d_inode: %p\n",
			     child->d_count, child->d_inode));
	    if (child->d_count == 0)  /* Throw immediately */
		d_drop(child);
#else
	    XFSDEB(XDEBMSG, ("clean_all_children: "
			     "child->d_count: %d child->d_inode: %p\n",
			     atomic_read(&child->d_count), child->d_inode));
	    if (atomic_read(&child->d_count) == 0)  /* Throw immediately */
		d_drop(child);
#endif
	}
#endif
	alias = alias->next;
    }
}

void
xfs_invalid_xnode(struct xfs_node *xnode)
{
    XFSDEB(XDEBNODE, ("xfs_invalid_xnode: %p\n", xnode));
    /* 
     * XXX Really need to put back dirty data first.
     * XXXRACE set DATA_FROM_XNODE(xnode) before dput() ?
     */
    XFS_TOKEN_CLEAR(xnode, ~0,
		    XFS_OPEN_MASK | XFS_ATTR_MASK |
		    XFS_DATA_MASK | XFS_LOCK_MASK);
    if (DATA_FROM_XNODE(xnode)) {
	XFS_RESET_I_MAPPING(XNODE_TO_VNODE(xnode));
	dput(DATA_FROM_XNODE(xnode));
	DATA_FROM_XNODE(xnode) = NULL;
	invalidate_inode_pages(XNODE_TO_VNODE(xnode));
    }
    clear_all_children(XNODE_TO_VNODE(xnode), 1);
}

int
xfs_message_invalidnode(int fd, struct xfs_message_invalidnode *message,
			u_int size)
{
    int error = 0;
    struct xfs_node *t;
    
    XFSDEB(XDEBMSG, ("xfs_message_invalidnode\n"));
    t = xfs_node_find(&xfs[fd], &message->handle);
    if (t != 0) {
	xfs_invalid_xnode(t);
    } else {
#if 0
	printk(KERN_EMERG "XFS Panic: "
	       "xfs_message_invalidnode didn't find node!"
	       " (%d.%d.%d.%d)\n",
	       message->handle.a,
	       message->handle.b,
	       message->handle.c,
	       message->handle.d);
#endif
	error = -ENOENT;
    }

    return error;
}

int
xfs_message_updatefid(int fd, struct xfs_message_updatefid * message,
		      u_int size)
{
    int error = 0;
    struct xfs_node *t;

    XFSDEB(XDEBMSG, ("xfs_message_updatefid\n"));
    t = xfs_node_find (&xfs[fd], &message->old_handle);
    if (t != NULL) {
	t->handle = message->new_handle;
    } else {
	printk (KERN_EMERG "XFS Panic: xfs_message_updatefid: no node!\n");
	error = -ENOENT;
    }
    return error;
}

/*
 * there's already a function that does this in 2.3.99-pre9
 */

#ifndef LINUX2_3
static void
d_prune_aliases(struct inode *inode)
{
    struct list_head *alias;
    struct dentry *dentry;

again:
    alias = inode->i_dentry.next;
    while (alias != &inode->i_dentry) {
	dentry = list_entry(alias, struct dentry, d_alias);
	XFSDEB(XDEBMSG,(" %.*s(%p) count: %d",
			  (int)dentry->d_name.len,
			  dentry->d_name.name,
			  dentry,
			  dentry->d_count));
#ifndef LINUX2_3
	if (dentry->d_count == 0) 
#else
	if (atomic_read(&dentry->d_count) == 0) 
#endif	    
	{
	    dget(dentry);
	    d_drop(dentry);
	    dput(dentry);
	    goto again;
	}
	alias = alias->next;
    }
}
#endif /* !LINUX2_3 */

void
gc_vnode(struct inode *inode)
{
    XFSDEB(XDEBMSG,("xfs_message_gc: inode: %p count: %d",
		    inode, xfs_iread(inode)));
    xfs_iref(inode);
    d_prune_aliases(inode);
    XFSDEB(XDEBMSG, ("\nxfs_message_gc: i_count after gc: %d\n",
		     xfs_iread(inode)));
    iput(inode);
}

int
xfs_message_gc_nodes(int fd,
		     struct xfs_message_gc_nodes *message,
		     u_int size)
{
    XFSDEB(XDEBMSG, ("xfs_message_gc\n"));

    if (message->len == 0) {
	struct xfs_node *xnode, *next;

	for (xnode = xfs[fd].nodes; xnode; xnode = next) {
	    next = xnode->next;
	    gc_vnode(XNODE_TO_VNODE(xnode));
	}
	
    } else {
	struct xfs_node *t;
	int i;

	for (i = 0; i < message->len; i++) {
	    t = xfs_node_find (&xfs[fd], &message->handle[i]);
	    if (t == NULL)
		continue;

	    gc_vnode(XNODE_TO_VNODE(t));
	}
    }

    return 0;
}

/*
 * Probe what version of the interface this xfs supports
 */

int
xfs_message_version(int fd,
		    struct xfs_message_version *message,
		    u_int size)
{
    struct xfs_message_wakeup msg;
    int ret;

    ret = XFS_VERSION;

    msg.header.opcode = XFS_MSG_WAKEUP;
    msg.sleepers_sequence_num = message->header.sequence_num;
    msg.error = ret;

    return xfs_message_send(fd, 
			    (struct xfs_message_header *) &msg, 
			    sizeof(msg));
}
