/*
 * Copyright (c) 2001 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 * 
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 * 
 * http://www.sgi.com 
 * 
 * For further information regarding this notice, see: 
 * 
 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
 */

#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

/*
 * Revalidate the inode. This is required for proper NFS attribute caching.
 * Blatently copied wholesale from fs/stat.c
 */
static __inline__ int
do_revalidate(struct dentry *dentry)
{
        struct inode * inode = dentry->d_inode;
        if (inode->i_op && inode->i_op->revalidate)
                return inode->i_op->revalidate(dentry);
        return 0;
}


asmlinkage long sys_acl_get(
			const char		*path,
			      int		fdes,
			      struct acl	*acl,
			      struct acl	*dacl)
{
	int		 error;
	struct nameidata nd;
	struct dentry	 *dentry = NULL;
	struct file *f;

	if (path == NULL && fdes == -1)
		return -EINVAL;

	if (path != NULL && fdes != -1)
		return -EINVAL;

	lock_kernel();

	error = -EINVAL;

	if (path) {
		error = user_path_walk_link(path, &nd);
		
		if (!error)
			dentry = nd.dentry;
	}
	else {
		f = fget (fdes);

		if (f)
			dentry = f->f_dentry;
	}

	if (dentry) {
		error = -EINVAL;

		if (dentry->d_inode->i_op &&
		    dentry->d_inode->i_op->acl_get &&
		    !(error = do_revalidate(dentry))) {

			UPDATE_ATIME(dentry->d_inode);

			error = dentry->d_inode->i_op->acl_get(dentry,
							       acl,
							       dacl);
		}

		if (path)
			path_release(&nd);
		else
			fput(f);
	}

	unlock_kernel();

	return error;
}


asmlinkage long sys_acl_set(
			const char	*path,
			int		fdes,
			struct acl	*acl,
			struct acl	*dacl)
{
	int		 error;
	struct nameidata nd;
	struct dentry	 *dentry = NULL;
	struct file *f;

	if (path == NULL && fdes == -1)
		return -EINVAL;

	if (path != NULL && fdes != -1)
		return -EINVAL;

	lock_kernel();

	error = -EINVAL;

	if (path) {
		error = user_path_walk_link(path, &nd);

		if (!error)
			dentry = nd.dentry;
	}
	else {
		f = fget (fdes);

		if (f)
			dentry = f->f_dentry;
	}

	if (dentry) {
		error = -EINVAL;

		if (dentry->d_inode->i_op &&
		    dentry->d_inode->i_op->acl_set &&
		    !(error = do_revalidate(dentry))) {

			UPDATE_ATIME(dentry->d_inode);

			error = dentry->d_inode->i_op->acl_set(dentry,
							       acl,
							       dacl);
			if (!error) {
			    	error = do_revalidate(dentry);
			}
		}

		if (path)
			path_release(&nd);
		else
			fput(f);
	}

	unlock_kernel();

	return error;
}
