/*
 * Copyright (c) 2003-2005 Erez Zadok
 * Copyright (c) 2003-2005 Charles P. Wright
 * Copyright (c) 2005      Arun M. Krishnakumar
 * Copyright (c) 2005      David P. Quigley
 * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
 * Copyright (c) 2003-2003 Puja Gupta
 * Copyright (c) 2003-2003 Harikesavan Krishnan
 * Copyright (c) 2003-2005 Stony Brook University
 * Copyright (c) 2003-2005 The Research Foundation of State University of New York
 *
 * For specific licensing information, see the COPYING file distributed with
 * this package.
 *
 * This Copyright notice must be kept intact and distributed with all sources.
 */
/*
 *  $Id: xattr.c,v 1.25 2005/10/21 15:02:04 cwright Exp $
 */

#include "fist.h"
#include "unionfs.h"

#if defined(UNIONFS_XATTR)
/* This is lifted from fs/xattr.c */
void *xattr_alloc(size_t size, size_t limit)
{
	void *ptr;

	if (size > limit)
		return ERR_PTR(-E2BIG);

	if (!size)		/* size request, no buffer is needed */
		return NULL;
	else if (size <= PAGE_SIZE)
		ptr = KMALLOC((unsigned long)size, GFP_KERNEL);
	else
		ptr = vmalloc((unsigned long)size);
	if (!ptr)
		return ERR_PTR(-ENOMEM);
	return ptr;
}

void xattr_free(void *ptr, size_t size)
{
	if (!size)		/* size request, no buffer was needed */
		return;
	else if (size <= PAGE_SIZE)
		KFREE(ptr);
	else
		vfree(ptr);
}

/* BKL held by caller.
 * dentry->d_inode->i_sem down
 * ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
 */
ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value,
			 size_t size)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	/* Define these anyway so we don't need as much ifdef'ed code. */
	char *encoded_name = NULL;
	char *encoded_value = NULL;

	print_entry_location();

	lock_dentry(dentry);

	hidden_dentry = dtohd(dentry);

	PASSERT(hidden_dentry);
	PASSERT(hidden_dentry->d_inode);
	PASSERT(hidden_dentry->d_inode->i_op);

	fist_dprint(8, "getxattr: name=\"%s\", value %lu bytes\n", name,
		    (unsigned long)size);

	if (hidden_dentry->d_inode->i_op->getxattr) {
		encoded_name = (char *)name;

		encoded_value = (char *)value;

		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->getxattr(hidden_dentry,
							   encoded_name,
							   encoded_value, size);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);

	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}

/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
int
unionfs_setxattr(struct dentry *dentry, const char *name, const void *value,
		 size_t size, int flags)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;

	print_entry_location();

	lock_dentry(dentry);
	hidden_dentry = dtohd(dentry);

	PASSERT(hidden_dentry);
	PASSERT(hidden_dentry->d_inode);
	PASSERT(hidden_dentry->d_inode->i_op);

	fist_dprint(8, "setxattr: name=\"%s\", value %lu bytes, flags=%x\n",
		    name, (unsigned long)size, flags);

	if (hidden_dentry->d_inode->i_op->setxattr) {
		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err = hidden_dentry->d_inode->i_op->
		    setxattr(hidden_dentry, name, value, size, flags);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}

/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
int unionfs_removexattr(struct dentry *dentry, const char *name)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	char *encoded_name;
	print_entry_location();

	lock_dentry(dentry);
	hidden_dentry = dtohd(dentry);

	PASSERT(hidden_dentry);
	PASSERT(hidden_dentry->d_inode);
	PASSERT(hidden_dentry->d_inode->i_op);

	fist_dprint(8, "removexattr: name=\"%s\"\n", name);

	if (hidden_dentry->d_inode->i_op->removexattr) {
		encoded_name = (char *)name;

		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->removexattr(hidden_dentry,
							      encoded_name);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}

/* BKL held by caller.
 * dentry->d_inode->i_sem down
 */
ssize_t unionfs_listxattr(struct dentry * dentry, char *list, size_t size)
{
	struct dentry *hidden_dentry = NULL;
	int err = -EOPNOTSUPP;
	char *encoded_list = NULL;

	print_entry_location();
	lock_dentry(dentry);

	hidden_dentry = dtohd(dentry);

	PASSERT(hidden_dentry);
	PASSERT(hidden_dentry->d_inode);
	PASSERT(hidden_dentry->d_inode->i_op);

	if (hidden_dentry->d_inode->i_op->listxattr) {
		encoded_list = list;
		down(&hidden_dentry->d_inode->i_sem);
		/* lock_kernel() already done by caller. */
		err =
		    hidden_dentry->d_inode->i_op->listxattr(hidden_dentry,
							    encoded_list, size);
		/* unlock_kernel() will be done by caller. */
		up(&hidden_dentry->d_inode->i_sem);
	}

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
#endif				/* UNIONFS_XATTR */

/*
 *
 * vim:shiftwidth=8
 * vim:tabstop=8
 *
 * For Emacs:
 * Local variables:
 * c-basic-offset: 8
 * c-comment-only-line-offset: 0
 * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0)
 *              (substatement-open . 0) (label . 0) (statement-cont . +))
 * indent-tabs-mode: t
 * tab-width: 8
 * End:
 */
