/*
 * Copyright (c) 1995, 1996, 1997, 1998, 1999 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_cache.h>
#include <xfs/xfs_message.h>
#include <xfs/xfs_msg_locl.h>
#include <xfs/xfs_deb.h>
#include <xfs/xfs_fs.h>

RCSID("$Id: xfs_message.c,v 1.49 1999/02/21 05:07:14 map Exp $");

#ifdef LINUX2_1
static void
clear_all_childs (struct inode *inode);
#endif

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);
#ifndef LINUX2_1
    if (dp) {
	n = new_xfs_node(&xfs[fd], &message->node); /* VN_HOLD's */
	xfs_cache_add(dp, message->name, strlen(message->name), n);
    } else {
	printk(KERN_EMERG "XFS Panic: xfs_message_install could not find parent\n");
	error = ENOENT;
    }
#else
    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\n", 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 = xfs_full_name_hash(sqstr.name, sqstr.len);
	alias = inode->i_dentry.next;
	/* If a requested dentry can be found, that is preferred */
	while (alias != &inode->i_dentry) {
	    parent = list_entry(alias, struct dentry, d_alias);
	    dentry = d_lookup(parent, &sqstr);
	    if (dentry) {
		if (DENTRY_TO_XDENTRY(dentry)->requested)
		    break;
		dput(dentry);
		dentry = NULL;
	    }
	    alias = alias->next;
	}
	/* If a requested dentry could not be found, try the first one */
	if (dentry == NULL) {
	    alias = inode->i_dentry.next;
	    while (alias != &inode->i_dentry) {
		parent = list_entry(alias, struct dentry, d_alias);
		dentry = d_lookup(parent, &sqstr);
		if (dentry)
		    break;
		alias = alias->next;
	    }
	}
	XFSDEB(XDEBMSG, ("xfs_message_installnode: dentry found: %p\n",
			 dentry));
	if (dentry) {
	    if (dentry->d_inode)
		list_del(&dentry->d_alias);
	    d_instantiate(dentry, XNODE_TO_VNODE(n));
	    DENTRY_TO_XDENTRY(dentry)->valid = 1;
	    DENTRY_TO_XDENTRY(dentry)->requested = 0;
	    dput(dentry);
	    dentry = NULL;
	} else {
	    dentry = d_alloc(parent, &sqstr);
	    XFSDEB(XDEBMSG, ("xfs_message_installnode: allocated new entry: %p\n",
			 dentry));
	    xfs_d_init(dentry);
	    DENTRY_TO_XDENTRY(dentry)->valid = 1;
	    d_add(dentry, XNODE_TO_VNODE(n));
	}
	XFSDEB(XDEBMSG, ("xfs_message_installnode: done d_add\n"));
    } else {
	printk(KERN_EMERG "XFS Panic: xfs_message_install could not find parent\n");
	error = ENOENT;
    }
#endif /* LINUX2_1 */

  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\n"));

    t = xfs_node_find(&xfs[fd], &message->node.handle);
    if (t != 0) {
	struct inode *inode = XNODE_TO_VNODE(t);
#ifdef LINUX2_1
	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));
#endif /* LINUX2_1 */

	t->tokens = message->node.tokens;
	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 {
	printk(KERN_EMERG "XFS Panic: xfs_message_installattr didn't find node!\n");
	error = ENOENT;
    }

    return error;
}

int xfs_message_installdata(int fd,
			    struct xfs_message_installdata *message,
			    u_int size)
{
    struct xfs_node *t;
#ifndef LINUX2_1
    struct inode *vp;
#else
    struct dentry *dentry;
#endif /* LINUX2_1 */
    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));
#ifndef LINUX2_1
	current->fs->pwd->i_count++;
	error = open_namei(message->cache_name, 3, 0, &vp, current->fs->pwd);
#else
	dentry = xfs_fh_to_dentry(&message->cache_handle);
	XFSDEB(XDEBMSG, ("xfs_message_installdata dentry: %p\n",
			 dentry));
#endif /* LINUX2_1 */
#ifndef LINUX2_1
	if (error == 0) {
	    if (DATA_FROM_XNODE(t)) {
		iput(DATA_FROM_XNODE(t));
	    }
	    DATA_FROM_XNODE(t) = vp; /* VOP_LOOKUP does an implicit VN_HOLD? */
	    if (message->flag & XFS_INVALID_DNLC)
		xfs_cache_delete_dir (t);
	    t->tokens = message->node.tokens;
	    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));
	    t->anonrights = message->node.anonrights;
	} else
	    printk(KERN_EMERG "XFS Panic: xfs_message_installdata failed to lookup cache file = %s, error = %d\n", message->cache_name, error);
#else
	if (!IS_ERR(dentry)) {
	    if (DATA_FROM_XNODE(t)) {
		dput(DATA_FROM_XNODE(t));
	    }
	    DATA_FROM_XNODE(t) = dentry;
	    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;
	} else {
	    printk(KERN_EMERG "XFS Panic: xfs_message_installdata failed to lookup cache file = %s, errno: %ld\n", message->cache_name, PTR_ERR(dentry));
	    error = ENOTTY; /* XXXXX */
	}
#endif /* LINUX2_1 */
    } else {
	printk(KERN_EMERG "XFS Panic: xfs_message_installdata didn't find node!\n");
	error = ENOENT;
    }

    return error;
}

#ifdef LINUX2_1
static void
clear_all_childs (struct inode *inode)
{
    struct list_head *alias;

    alias = inode->i_dentry.next;
    while (alias != &inode->i_dentry) {
	struct dentry *dentry;
	struct list_head *subdirs;
	dentry = list_entry(alias, struct dentry, d_alias);
	if (dentry == NULL) {
	    printk(KERN_EMERG "XFS Panic: dentry in alias list is null\n");
	    break;
	}
	XFSDEB(XDEBMSG, ("clear_all_childs parent: %*s\n",
			 dentry->d_name.len, dentry->d_name.name));
	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_childs child: %*s\n",
			     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_childs: invalidating %p aliases:\n",
				 child->d_inode));
		print_aliases(child->d_inode);
		DENTRY_TO_XDENTRY(child)->valid = 0;
	    }
	    XFSDEB(XDEBMSG, ("clean_all_childs: 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);
	}
	alias = alias->next;
    }
}
#endif /* LINUX2_1 */

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. */
    if (DATA_FROM_XNODE(xnode)) {
#ifndef LINUX2_1
	iput(DATA_FROM_XNODE(xnode));
	DATA_FROM_XNODE(xnode) = (struct inode *) 0;
#else
	dput(DATA_FROM_XNODE(xnode));
	DATA_FROM_XNODE(xnode) = NULL;
#endif /* LINUX2_1 */
	invalidate_inode_pages(XNODE_TO_VNODE(xnode));
    }
    XFS_TOKEN_CLEAR(xnode, ~0,
		    XFS_OPEN_MASK | XFS_ATTR_MASK |
		    XFS_DATA_MASK | XFS_LOCK_MASK);
#ifndef LINUX2_1
    xfs_cache_delete(xnode);
#else
    clear_all_childs(XNODE_TO_VNODE(xnode));
#endif /* LINUX2_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 {
	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);
	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;
}
