diff -Naur linux-2.6.12-rc2-mm3/fs/Kconfig linux-2.6.12-rc2-mm3-unionfs/fs/Kconfig
--- linux-2.6.12-rc2-mm3/fs/Kconfig	2005-04-14 02:47:17.540776592 -0700
+++ linux-2.6.12-rc2-mm3-unionfs/fs/Kconfig	2005-04-25 11:56:14.484664080 -0700
@@ -4,6 +4,8 @@
 
 menu "File systems"
 
+source "fs/unionfs/Kconfig"
+
 config EXT2_FS
 	tristate "Second extended fs support"
 	help
diff -Naur linux-2.6.12-rc2-mm3/fs/Makefile linux-2.6.12-rc2-mm3-unionfs/fs/Makefile
--- linux-2.6.12-rc2-mm3/fs/Makefile	2005-04-14 02:47:17.570772032 -0700
+++ linux-2.6.12-rc2-mm3-unionfs/fs/Makefile	2005-04-25 11:57:01.926451832 -0700
@@ -13,6 +13,7 @@
 		ioprio.o
 
 obj-$(CONFIG_INOTIFY)		+= inotify.o
+obj-$(CONFIG_UNIONFS)           += unionfs/
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
 obj-$(CONFIG_COMPAT)		+= compat.o
 
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/Kconfig linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/Kconfig
--- linux-2.6.12-rc2-mm3/fs/unionfs/Kconfig	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/Kconfig	2005-04-25 11:56:14.494662560 -0700
@@ -0,0 +1,16 @@
+config UNIONFS
+	tristate "UnionFS filesystem"
+	depends on MODULES && m
+	default n
+	help
+          UnionFS is a stackable unification file system, which can appear to
+          merge the contents of several directories (branches), while keeping
+          their physical content separate.  UnionFS is useful for unified source
+          tree management, merged contents of split CD-ROM, merged separate
+          software package directories, data grids, and more.  UnionFS allows
+          any mix of read-only and read-write branches, as well as insertion and
+          deletion of branches anywhere in the fan-out.  To maintain Unix
+          semantics, Unionfs handles elimination of duplicates, partial-error
+          conditions, and more.
+
+	  If unsure, say n
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/Makefile linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/Makefile
--- linux-2.6.12-rc2-mm3/fs/unionfs/Makefile	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/Makefile	2005-04-25 11:56:14.510660128 -0700
@@ -0,0 +1,15 @@
+#
+# Makefile for the Linux 2.6 unionfs
+#
+
+
+##EXTRA_CFLAGS := -DUNIONFS_XATTR -DFIST_SETXATTR_CONSTVOID
+
+CFLAGS += -D__KERNEL__ -DMODULE -I. ${TOPINC} -Wall -Werror -fno-strict-aliasing ${EXTRACFLAGS} ${UNIONFS_DEBUG_CFLAG} ${UNIONFS_OPT_CFLAG}
+UCFLAGS = -I. ${UNIONFS_DEBUG_CFLAG} ${UNIONFS_OPT_CFLAG} -Wall -Werror ${EXTRAUCFLAGS}
+
+obj-$(CONFIG_UNIONFS) := unionfs.o
+unionfs-objs := subr.o dentry.o file.o inode.o main.o super.o print.o \
+	stale_inode.o branchman.o xattr.o rdstate.o copyup.o  \
+	dirhelper.o
+
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/branchman.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/branchman.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/branchman.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/branchman.c	2005-04-25 11:56:14.511659976 -0700
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: branchman.c,v 1.23 2005/02/08 15:17:38 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+#include <linux/dcache.h>
+
+
+int unionfs_ioctl_branchcount(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
+{
+	struct unionfs_sb_info *spd;
+	int err = 0;
+	int bstart, bend;
+	int i;
+
+	print_entry_location();
+
+
+	spd = stopd(inode->i_sb);
+
+	bstart = spd->b_start;
+	bend = spd->b_end;
+
+	err = bend + 1;
+	if (!arg) {
+		goto out;
+	}
+
+
+	fist_dprint(1, "b_start = %d, b_end = %d\n", bstart, bend);
+	for (i = bstart; i <= bend; i++) {
+		if (put_user(atomic_read(&spd->usi_sbcount[i]), ((int *)arg) + i)) {
+			err = -EFAULT;
+			goto out;
+		}
+	}
+
+out:
+	print_exit_status(err);
+	return err;
+}
+
+int unionfs_ioctl_incgen(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+
+	print_entry_location();
+
+	lock_super(inode->i_sb);
+
+	atomic_inc(&stopd(inode->i_sb)->usi_generation);
+	err = atomic_read(&stopd(inode->i_sb)->usi_generation);
+
+	atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, err);
+	atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, err);
+
+	unlock_super(inode->i_sb);
+
+	print_exit_status(err);
+
+	return err;
+}
+
+int unionfs_ioctl_addbranch(inode_t *inode, file_t *unused_file, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	struct unionfs_sb_info *spd;
+	struct unionfs_dentry_info *dpd;
+	struct unionfs_inode_info *ipd;
+	struct unionfs_addbranch_args *addargs = NULL;
+	struct nameidata nd;
+	struct vfsmount **new_hidden_mnt = NULL;
+	struct inode **new_uii_inode = NULL;
+	struct dentry **new_udi_dentry = NULL;
+	struct super_block **new_usi_sb = NULL;
+	int *new_branchperms = NULL;
+	atomic_t *new_counts = NULL;
+	char *path = NULL;
+	int gen;
+	int i;
+	int count;
+
+	print_entry_location();
+
+	/* If we ever use this we have problems! */
+	unused_file = EXPLOSIVE;
+
+	lock_super(inode->i_sb);
+
+	spd = stopd(inode->i_sb);
+	dpd = dtopd(inode->i_sb->s_root);
+	ipd = itopd(inode->i_sb->s_root->d_inode);
+
+	addargs = KMALLOC(sizeof(struct unionfs_addbranch_args), GFP_UNIONFS);
+	if (!addargs) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (copy_from_user(addargs, (void *)arg, sizeof(struct unionfs_addbranch_args))) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	path = getname(addargs->ab_path);
+	if (!path) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Add a branch. */
+	if (addargs->ab_branch < 0 || (addargs->ab_branch > (spd->b_end + 1))) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if ((spd->b_end + 1) > FD_SETSIZE) {
+		err = -E2BIG;
+		goto out;
+	}
+
+	if (addargs->ab_perms & ~(MAY_READ|MAY_WRITE)) {
+		err = -EINVAL;
+		goto out;
+	}
+	if (!(addargs->ab_perms & MAY_READ)) {
+		err = -EINVAL;
+		goto out;
+	}
+//DQ: 2.6 has a different way of doing this
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	/* Look it up */
+	if (path_init(path, LOOKUP_FOLLOW, &nd)) {
+		err = path_walk(path, &nd);
+	}
+#else
+	err = path_lookup(path, LOOKUP_FOLLOW, &nd);
+#endif
+	if (err) {
+		goto out;
+	}
+	if (!nd.dentry->d_inode) {
+		path_release(&nd);
+		err = -ENOENT;
+		goto out;
+	}
+	if (!S_ISDIR(nd.dentry->d_inode->i_mode)) {
+		path_release(&nd);
+		err = -ENOTDIR;
+		goto out;
+	}
+
+	spd->b_end++;
+	dtopd(inode->i_sb->s_root)->udi_bcount++;
+	set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) + 1);
+	ipd->b_end++;
+
+	atomic_inc(&spd->usi_generation);
+	gen = atomic_read(&spd->usi_generation);
+
+	/* Reallocate the dynamic structures. */
+	new_hidden_mnt = KMALLOC(sizeof(struct hidden_mnt *) * (spd->b_end + 1), GFP_UNIONFS);
+	new_udi_dentry = KMALLOC(sizeof(struct dentry *) * (spd->b_end + 1), GFP_UNIONFS);
+	new_uii_inode = KMALLOC(sizeof(struct inode *) * (spd->b_end + 1), GFP_UNIONFS);
+	new_usi_sb = KMALLOC(sizeof(struct super_block *) * (spd->b_end + 1), GFP_UNIONFS);
+	new_counts = KMALLOC(sizeof(atomic_t) * (spd->b_end + 1), GFP_UNIONFS);
+	new_branchperms = KMALLOC(sizeof(int) * (spd->b_end + 1), GFP_UNIONFS);
+	if (!new_hidden_mnt || !new_udi_dentry || !new_uii_inode || !new_counts || !new_usi_sb || !new_branchperms) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Copy the values to our new structure, but shift some to the right. */
+	for (i = 0; i < addargs->ab_branch; i++) {
+		count = atomic_read(&(spd->usi_sbcount[i]));
+		atomic_set(&(new_counts[i]), count);
+		new_branchperms[i] = spd->usi_branchperms[i];
+
+		new_hidden_mnt[i] = spd->usi_hidden_mnt[i];
+
+		new_usi_sb[i] = spd->usi_sb[i];
+		new_udi_dentry[i] = dpd->udi_dentry[i];
+		new_uii_inode[i] = ipd->uii_inode[i];
+	}
+
+	for (i = addargs->ab_branch; i < spd->b_end; i++) {
+		count = atomic_read(&(spd->usi_sbcount[i]));
+		atomic_set(&(new_counts[i + 1]), count);
+
+		new_branchperms[i + 1] = spd->usi_branchperms[i];
+
+		new_hidden_mnt[i + 1] = spd->usi_hidden_mnt[i];
+		new_usi_sb[i + 1] = spd->usi_sb[i];
+		new_udi_dentry[i + 1] = dpd->udi_dentry[i];
+		new_uii_inode[i + 1] = ipd->uii_inode[i];
+	}
+
+	/* Put the new dentry information into it's slot. */
+	new_udi_dentry[addargs->ab_branch] = nd.dentry;
+	new_uii_inode[addargs->ab_branch] = igrab(nd.dentry->d_inode);
+	new_hidden_mnt[addargs->ab_branch] = nd.mnt;
+	new_usi_sb[addargs->ab_branch] = nd.dentry->d_sb;
+	new_branchperms[addargs->ab_branch] = addargs->ab_perms;
+	atomic_set(&new_counts[addargs->ab_branch], 0);
+
+	/* Free the pointers. */
+	KFREE(dpd->udi_dentry);
+	KFREE(ipd->uii_inode);
+	KFREE(spd->usi_hidden_mnt);
+	KFREE(spd->usi_sb);
+	KFREE(spd->usi_branchperms);
+
+	/* Update the real pointers. */
+	dpd->udi_dentry = new_udi_dentry;
+	ipd->uii_inode = new_uii_inode;
+	spd->usi_hidden_mnt = new_hidden_mnt;
+	spd->usi_sb = new_usi_sb;
+	spd->usi_sbcount = new_counts;
+	spd->usi_branchperms = new_branchperms;
+
+	/* Re-NULL the new ones so we don't try to free them. */
+	new_hidden_mnt = NULL;
+	new_udi_dentry = NULL;
+	new_usi_sb = NULL;
+	new_uii_inode = NULL;
+	new_counts = NULL;
+	new_branchperms = NULL;
+
+	atomic_set(&dpd->udi_generation, gen);
+	atomic_set(&ipd->uii_generation, gen);
+
+out:
+	unlock_super(inode->i_sb);
+
+	if (new_hidden_mnt) {
+		KFREE(new_hidden_mnt);
+	}
+	if (new_udi_dentry) {
+		KFREE(new_udi_dentry);
+	}
+	if (new_uii_inode) {
+		KFREE(new_uii_inode);
+	}
+	if (new_usi_sb) {
+		KFREE(new_usi_sb);
+	}
+	if (new_counts) {
+		KFREE(new_counts);
+	}
+	if (new_branchperms) {
+		KFREE(new_branchperms);
+	}
+	if (addargs) {
+	    KFREE(addargs);
+	}
+
+	if (path) {
+	    putname(path);
+	}
+
+	print_exit_status(err);
+
+	return err;
+}
+
+int unionfs_ioctl_delbranch(inode_t *inode, file_t *unused_file, unsigned int cmd, unsigned long arg)
+{
+	struct dentry *hidden_dentry;
+	struct inode *hidden_inode;
+	struct super_block *hidden_sb;
+	vfs_mount_t *hidden_mnt;
+	struct unionfs_sb_info *spd;
+	struct unionfs_dentry_info *dpd;
+	struct unionfs_inode_info *ipd;
+	int err = 0;
+	int i;
+	int gen;
+	int count;
+
+	print_entry("branch = %lu ", arg); /* Delete a branch. */
+
+	/* If we ever use this we have problems! */
+	unused_file = EXPLOSIVE;
+
+	lock_super(inode->i_sb);
+
+	spd = stopd(inode->i_sb);
+	dpd = dtopd(inode->i_sb->s_root);
+	ipd = itopd(inode->i_sb->s_root->d_inode);
+
+	if (sbmax(inode->i_sb) == 1) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/* Delete a branch. */
+	if (arg < 0 || arg > spd->b_end) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (atomic_read(&(spd->usi_sbcount[arg]))) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	atomic_inc(&spd->usi_generation);
+	gen = atomic_read(&spd->usi_generation);
+
+	hidden_dentry = dpd->udi_dentry[arg];
+	hidden_mnt = spd->usi_hidden_mnt[arg];
+	hidden_inode = ipd->uii_inode[arg];
+	hidden_sb = spd->usi_sb[arg];
+
+	dput(hidden_dentry);
+	iput(hidden_inode);
+	mntput(hidden_mnt);
+	//XXX: Leak! put_super(hidden_sb);
+
+	for (i = arg; i <= (spd->b_end - 1); i++) {
+		count = atomic_read(&(spd->usi_sbcount[i]));
+		atomic_set(&(spd->usi_sbcount[i]), count);
+
+		spd->usi_hidden_mnt[i] = spd->usi_hidden_mnt[i + 1];
+		spd->usi_sb[i] = spd->usi_sb[i + 1];
+		spd->usi_branchperms[i] = spd->usi_branchperms[i + 1];
+		dpd->udi_dentry[i] = dpd->udi_dentry[i + 1];
+		ipd->uii_inode[i] = ipd->uii_inode[i + 1];
+	}
+
+	dpd->udi_dentry[spd->b_end] = EXPLOSIVE;
+	ipd->uii_inode[spd->b_end] = EXPLOSIVE;
+        spd->usi_hidden_mnt[spd->b_end] = EXPLOSIVE;
+
+	spd->b_end--;
+	set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) - 1);
+	dtopd(inode->i_sb->s_root)->udi_bcount--;
+	ipd->b_end--;
+
+	atomic_set(&dpd->udi_generation, gen);
+	atomic_set(&ipd->uii_generation, gen);
+
+out:
+	unlock_super(inode->i_sb);
+
+	print_exit_status(err);
+
+	return err;
+}
+
+int unionfs_ioctl_rdwrbranch(inode_t *inode, file_t *unused_file, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	struct unionfs_sb_info *spd;
+	struct unionfs_dentry_info *dpd;
+	struct unionfs_inode_info *ipd;
+	struct unionfs_rdwrbranch_args *rdwrargs = NULL;
+	int gen;
+
+	print_entry_location();
+
+	/* If we ever use this we have problems! */
+	unused_file = EXPLOSIVE;
+
+	lock_super(inode->i_sb);
+
+	spd = stopd(inode->i_sb);
+	dpd = dtopd(inode->i_sb->s_root);
+	ipd = itopd(inode->i_sb->s_root->d_inode);
+
+	rdwrargs = KMALLOC(sizeof(struct unionfs_rdwrbranch_args), GFP_UNIONFS);
+	if (!rdwrargs) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (copy_from_user(rdwrargs, (void *)arg, sizeof(struct unionfs_rdwrbranch_args))) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	if (rdwrargs->rwb_branch < 0 || (rdwrargs->rwb_branch > (spd->b_end + 1))) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (rdwrargs->rwb_perms & ~(MAY_READ|MAY_WRITE)) {
+		err = -EINVAL;
+		goto out;
+	}
+	if (!(rdwrargs->rwb_perms & MAY_READ)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	spd->usi_branchperms[rdwrargs->rwb_branch] = rdwrargs->rwb_perms;
+
+	atomic_inc(&spd->usi_generation);
+	gen = atomic_read(&spd->usi_generation);
+
+	atomic_set(&dpd->udi_generation, gen);
+	atomic_set(&ipd->uii_generation, gen);
+
+out:
+	unlock_super(inode->i_sb);
+	if (rdwrargs) {
+	    KFREE(rdwrargs);
+	}
+
+	print_exit_status(err);
+
+	return err;
+}
+
+int unionfs_ioctl_superduper(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg) {
+	int err = 0;
+#ifdef SPLIT_VIEW_CACHES
+	struct super_block *ret_sb;
+#endif
+
+	print_entry_location();
+
+#ifdef SPLIT_VIEW_CACHES
+	lock_super(inode->i_sb);
+
+	ret_sb = unionfs_duplicate_super(inode->i_sb);
+	if (IS_ERR(ret_sb)) {
+	    err = PTR_ERR(ret_sb);
+	}
+
+	unlock_super(inode->i_sb);
+#else
+	err = -ENOSYS;
+#endif
+
+	print_exit_status(err);
+	return err;
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/copyup.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/copyup.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/copyup.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/copyup.c	2005-04-25 11:56:14.512659824 -0700
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: copyup.c,v 1.17 2005/03/02 15:10:40 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
+/*Not Working Yet*/
+static int unionfs_copyup_xattrs(struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) {
+	int err = 0;
+	ssize_t list_size = -1;
+	char *name_list = NULL;
+	char *attr_value = NULL;
+
+	print_entry_location();
+
+	PASSERT(old_hidden_dentry);
+	PASSERT(old_hidden_dentry->d_inode);
+	PASSERT(old_hidden_dentry->d_inode->i_op);
+	PASSERT(new_hidden_dentry);
+	PASSERT(new_hidden_dentry->d_inode);
+	PASSERT(new_hidden_dentry->d_inode->i_op);
+
+	if(!old_hidden_dentry->d_inode->i_op->getxattr ||
+			!old_hidden_dentry->d_inode->i_op->listxattr ||
+			!new_hidden_dentry->d_inode->i_op->setxattr) {
+		err = -ENOTSUPP;
+		goto out;
+	}
+
+	list_size = old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry,NULL,0);
+	if (list_size <= 0) {
+		err = list_size;
+		goto out;
+	}
+
+	name_list = xattr_alloc(list_size + 1, XATTR_LIST_MAX);
+	if (!name_list || IS_ERR(name_list)) {
+		err = PTR_ERR(name_list);
+		goto out;
+	}
+	list_size = old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry,name_list,list_size);
+
+	attr_value = xattr_alloc(XATTR_SIZE_MAX,XATTR_SIZE_MAX);
+	if (!attr_value || IS_ERR(attr_value)) {
+		err = PTR_ERR(name_list);
+		goto out;
+	}
+
+	while(*name_list) {
+		size_t size;
+		down(&old_hidden_dentry->d_inode->i_sem);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+		err = security_inode_getxattr(old_hidden_dentry, name_list);
+		if (!err)
+#endif
+		size = old_hidden_dentry->d_inode->i_op->getxattr(old_hidden_dentry,name_list,attr_value, XATTR_SIZE_MAX);
+		up(&old_hidden_dentry->d_inode->i_sem);
+		if(size < 0) {
+			err = size;
+			goto out;
+		}
+
+		if(size > XATTR_SIZE_MAX) {
+			err = -E2BIG;
+			goto out;
+		}
+
+		down(&new_hidden_dentry->d_inode->i_sem);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+		err = security_inode_setxattr(old_hidden_dentry, name_list, attr_value, size, XATTR_CREATE);
+		if (!err) {
+#endif
+			err = new_hidden_dentry->d_inode->i_op->setxattr(new_hidden_dentry,name_list,attr_value, size, XATTR_CREATE);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+			if (!err)
+				security_inode_post_setxattr(old_hidden_dentry, name_list, attr_value, size, XATTR_CREATE);
+		}
+#endif
+		up(&new_hidden_dentry->d_inode->i_sem);
+		if(err < 0)
+			goto out;
+
+		name_list += strlen(name_list) + 1;
+	}
+
+out:
+	if(name_list)
+		xattr_free(name_list, XATTR_LIST_MAX);
+	if(attr_value)
+		xattr_free(attr_value, XATTR_SIZE_MAX);
+	/* It is no big deal if this fails, we just roll with the punches. */
+	if (err == -ENOTSUPP) {
+		err = 0;
+	}
+	return err;
+}
+#endif
+
+/* Determine the mode based on the copyup flags, and the existing dentry. */
+static int unionfs_copyup_permissions(struct super_block *sb, struct dentry *old_hidden_dentry, struct dentry *new_hidden_dentry) {
+	struct iattr newattrs;
+	int err;
+
+	print_entry_location();
+
+	newattrs.ia_valid = ATTR_CTIME;
+	if (IS_SET(sb, COPYUP_FS_MOUNTER)) {
+		/* f/s mounter */
+		newattrs.ia_mode = stopd(sb)->copyupmode;
+		newattrs.ia_valid |= ATTR_FORCE | ATTR_MODE;
+		if (stopd(sb)->copyupuid != -1) {
+			newattrs.ia_uid = stopd(sb)->copyupuid;
+			newattrs.ia_valid |= ATTR_UID;
+		}
+		if (stopd(sb)->copyupgid != -1) {
+			newattrs.ia_gid = stopd(sb)->copyupgid;
+			newattrs.ia_valid |= ATTR_GID;
+		}
+	} else if (IS_SET(sb, COPYUP_CURRENT_USER)) {
+		/* current file permission */
+		newattrs.ia_mode = ~current->fs->umask & S_IRWXUGO;
+		newattrs.ia_valid |= ATTR_FORCE | ATTR_MODE;
+	} else {
+		/* original mode of old file */
+		newattrs.ia_mode = old_hidden_dentry->d_inode->i_mode;
+		newattrs.ia_gid = old_hidden_dentry->d_inode->i_gid;
+		newattrs.ia_uid = old_hidden_dentry->d_inode->i_uid;
+		newattrs.ia_valid |= ATTR_FORCE | ATTR_GID | ATTR_UID | ATTR_MODE;
+	}
+	if (newattrs.ia_valid & ATTR_MODE) {
+		newattrs.ia_mode = (newattrs.ia_mode & S_IALLUGO) | (old_hidden_dentry->d_inode->i_mode & ~S_IALLUGO);
+	}
+
+	err = notify_change(new_hidden_dentry, &newattrs);
+
+	print_exit_status(err);
+	return err;
+}
+
+STATIC int
+unionfs_copyup_dentry_len(inode_t *dir, dentry_t *dentry, int bstart, int new_bindex, file_t **copyup_file, int len)
+{
+    dentry_t *new_hidden_dentry;
+    dentry_t *old_hidden_dentry = NULL;
+    super_block_t *sb;
+    file_t *input_file = NULL;
+    file_t *output_file = NULL;
+    ssize_t read_bytes, write_bytes;
+    mm_segment_t old_fs;
+    int err = 0;
+    char *buf;
+    int old_bindex;
+    int got_branch_input = -1;
+    int got_branch_output = -1;
+    int old_bstart;
+    int old_bend;
+    int size = len;
+    struct dentry *new_hidden_parent_dentry;
+    mm_segment_t oldfs;
+    char *symbuf = NULL;
+    uid_t saved_uid = current->fsuid;
+    gid_t saved_gid = current->fsgid;
+
+    print_entry_location();
+
+    fist_print_dentry("IN: unionfs_copyup_dentry_len: ", dentry);
+
+    dget(dentry);
+    old_bindex = bstart;
+    old_bstart = dbstart(dentry);
+    old_bend = dbend(dentry);
+
+    ASSERT(new_bindex >= 0);
+    ASSERT(new_bindex < old_bindex);
+    PASSERT(dir);
+    PASSERT(dentry);
+
+    sb = dir->i_sb;
+
+	if((err = is_robranch_super(sb, new_bindex)))
+		goto out;
+
+	/* Create the directory structure above this dentry. */
+	new_hidden_dentry = unionfs_create_dirs(dir, dentry, new_bindex);
+	PASSERT(new_hidden_dentry);
+	if (IS_ERR(new_hidden_dentry)) {
+		err = PTR_ERR(new_hidden_dentry);
+		goto out;
+	}
+
+    /* Now we actually create the object. */
+    old_hidden_dentry = dtohd_index(dentry, old_bindex);
+    PASSERT(old_hidden_dentry);
+    PASSERT(old_hidden_dentry->d_inode);
+    dget(old_hidden_dentry);
+
+    /* For symlinks only, we have to read the link before we lock the directory. */
+	if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {
+		PASSERT(old_hidden_dentry->d_inode->i_op);
+		PASSERT(old_hidden_dentry->d_inode->i_op->readlink);
+
+		symbuf = KMALLOC(PATH_MAX, GFP_UNIONFS);
+		if (!symbuf) {
+			err = -ENOMEM;
+			goto copyup_readlink_err;
+		}
+
+		oldfs = get_fs();
+		set_fs(KERNEL_DS);
+		err = old_hidden_dentry->d_inode->i_op->readlink(old_hidden_dentry, symbuf, PATH_MAX);
+		set_fs(oldfs);
+		if (err < 0) {
+			goto copyup_readlink_err;
+		}
+		symbuf[err] = '\0';
+	}
+
+    /* Now we lock the parent, and create the object in the new branch. */
+    new_hidden_parent_dentry = lock_parent(new_hidden_dentry);
+    current->fsuid = new_hidden_parent_dentry->d_inode->i_uid;
+    current->fsgid = new_hidden_parent_dentry->d_inode->i_gid;
+	if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) {
+		err = vfs_mkdir(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU);
+	} else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+		err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry, symbuf);
+#else
+		err = vfs_symlink(new_hidden_parent_dentry->d_inode, new_hidden_dentry,symbuf,S_IRWXU);
+#endif
+	} else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) || S_ISCHR(old_hidden_dentry->d_inode->i_mode) || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+		err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode, kdev_t_to_nr(old_hidden_dentry->d_inode->i_rdev));
+#else
+		err = vfs_mknod(new_hidden_parent_dentry->d_inode, new_hidden_dentry, old_hidden_dentry->d_inode->i_mode,old_hidden_dentry->d_inode->i_rdev);
+#endif
+	} else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) {
+		//DQ: number of params changes in 2.6
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+		err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU);
+#else
+		err = vfs_create(new_hidden_parent_dentry->d_inode, new_hidden_dentry, S_IRWXU, NULL);
+#endif
+	} else {
+		char diemsg[100];
+		snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", old_hidden_dentry->d_inode->i_mode);
+		FISTBUG(diemsg);
+	}
+    current->fsuid = saved_uid;
+    current->fsgid = saved_gid;
+    unlock_dir(new_hidden_parent_dentry);
+copyup_readlink_err:
+	if (symbuf) {
+		KFREE(symbuf);
+	}
+	if (err) {
+		/* get rid of the hidden dentry and all its traces */
+		dput(new_hidden_dentry);
+		set_dtohd_index(dentry, new_bindex, NULL);
+		set_dbstart(dentry, old_bstart);
+		set_dbend(dentry, old_bend);
+		goto out;
+	}
+
+    /* We actually copyup the file here. */
+	if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) {
+		mntget(stohiddenmnt_index(sb, old_bindex));
+		branchget(sb, old_bindex);
+		got_branch_input = old_bindex;
+		input_file = dentry_open(old_hidden_dentry, stohiddenmnt_index(sb, old_bindex), O_RDONLY);
+		if (IS_ERR(input_file)) {
+			err = PTR_ERR(input_file);
+			goto out;
+		}
+		if (!input_file->f_op || !input_file->f_op->read) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		/* copy the new file */
+		dget(new_hidden_dentry);
+		mntget(stohiddenmnt_index(sb, new_bindex));
+		branchget(sb, new_bindex);
+		got_branch_output = new_bindex;
+		output_file = dentry_open(new_hidden_dentry, stohiddenmnt_index(sb, new_bindex), O_WRONLY);
+		if (IS_ERR(output_file)) {
+			err = PTR_ERR(output_file);
+			goto out;
+		}
+		if (!output_file->f_op || !output_file->f_op->write) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		/* allocating a buffer */
+		buf = (char *) KMALLOC(PAGE_SIZE, GFP_UNIONFS);
+		if (!buf) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		/* now read PAGE_SIZE bytes from offset 0 in a loop*/
+		old_fs = get_fs();
+
+		input_file->f_pos = 0;
+		output_file->f_pos = 0;
+
+		set_fs(KERNEL_DS);
+		do {
+			if (len >= PAGE_SIZE) {
+				size = PAGE_SIZE;
+			} else if ((len < PAGE_SIZE) && (len > 0)) {
+				size = len;
+			}
+
+			len -= PAGE_SIZE;
+
+			read_bytes = input_file->f_op->read(input_file, buf, size, &input_file->f_pos);
+			if (read_bytes <= 0) {
+				err = read_bytes;
+				break;
+			}
+
+			write_bytes = output_file->f_op->write(output_file, buf, read_bytes, &output_file->f_pos);
+			if (write_bytes < 0 || (write_bytes < read_bytes)) {
+				err = -EIO;
+				break;
+			}
+		} while ((read_bytes > 0) && (len > 0));
+		set_fs(old_fs);
+		KFREE(buf);
+	}
+
+    /* Set permissions. */
+	if ((err = unionfs_copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) {
+		goto out;
+	}
+	/* Selinux uses extended attributes for permissions. */
+#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
+	if ((err = unionfs_copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) {
+		goto out;
+	}
+#endif
+
+    unionfs_reinterpose(dentry);
+
+ out:
+    if (input_file && !IS_ERR(input_file)) {
+    	fput(input_file);
+    } else {
+        /* since input file was not opened, we need to explicitly
+         * dput the old_hidden_dentry
+         */
+        if (old_hidden_dentry) {
+	   dput(old_hidden_dentry);
+	}
+    }
+
+    /* in any case, we have to branchput */
+    if (got_branch_input >= 0) {
+        branchput(sb, got_branch_input);
+    }
+
+    if (output_file) {
+    	if (copyup_file && !err) {
+    	    *copyup_file = output_file;
+    	} else {
+            fput(output_file);
+            branchput(sb, got_branch_output);
+	}
+    }
+
+    dput(dentry);
+    fist_print_dentry("OUT: unionfs_copyup_dentry_len: ", dentry);
+    fist_print_inode("OUT: unionfs_copyup_dentry_len: ", dentry->d_inode);
+
+    print_exit_status(err);
+    return err;
+}
+
+/* This function creates a copy of a file represented by 'file' which currently
+ * resides in branch 'bstart' to branch 'new_bindex.
+ */
+STATIC int
+unionfs_copyup_file(inode_t *dir, file_t *file, int bstart, int new_bindex, int len)
+{
+    int err = 0;
+    file_t *output_file = NULL;
+
+    print_entry_location();
+
+    err = unionfs_copyup_dentry_len(dir, file->f_dentry, bstart, new_bindex, &output_file, len);
+    if (!err) {
+        fbstart(file) = new_bindex;
+        ftohf_index(file, new_bindex) = output_file;
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+
+/* This function replicates the directory structure upto given dentry
+ * in the bindex branch. Can create directory structure recursively to the right
+ * also.
+ */
+struct dentry *unionfs_create_dirs(inode_t *dir, dentry_t *dentry, int bindex)
+{
+	int err;
+	dentry_t *child_dentry;
+	dentry_t *parent_dentry;
+	dentry_t *hidden_parent_dentry = NULL;
+	dentry_t *hidden_dentry = NULL;
+
+	const char *name;
+	unsigned int namelen;
+
+	dentry_t *path[MAX_DIR_CREATE];
+	int count;
+
+	int old_bstart;
+	int old_bend;
+
+	print_entry_location();
+
+	if ((err = is_robranch_super(dir->i_sb, bindex))) {
+		hidden_dentry = ERR_PTR(err);
+		goto out;
+	}
+
+	fist_print_dentry("IN: unionfs_create_dirs: ", dentry);
+
+	old_bstart = dbstart(dentry);
+	old_bend = dbend(dentry);
+
+	memset(path, 0, sizeof(dentry_t *) * MAX_DIR_CREATE);
+
+	/* assume the negative dentry of unionfs as the parent dentry */
+	parent_dentry = dentry;
+
+	count = 0;
+	/* find out in the current branch the parent directory from
+	 * where we need to start building a new directory structure
+	 */
+	do {
+		child_dentry = parent_dentry;
+
+		/* find the parent directory dentry in unionfs */
+		parent_dentry = child_dentry->d_parent;
+
+		/* find out the hidden_parent_dentry in the given branch */
+		hidden_parent_dentry = dtohd_index(parent_dentry, bindex);
+
+		/* store the child dentry */
+		path[count++] = child_dentry;
+
+	} while(!hidden_parent_dentry);
+	count--;
+
+	while (1) {
+		PASSERT(child_dentry);
+		PASSERT(parent_dentry);
+		PASSERT(parent_dentry->d_inode);
+
+		// get hidden parent dir in the current branch
+		hidden_parent_dentry = dtohd_index(parent_dentry, bindex);
+		PASSERT(hidden_parent_dentry);
+		PASSERT(hidden_parent_dentry->d_inode);
+
+		// init the values to lookup
+		name = child_dentry->d_name.name;
+		namelen = child_dentry->d_name.len;
+
+		// lookup child in the underlying file system
+		hidden_dentry = lookup_one_len(name, hidden_parent_dentry, namelen);
+		if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+			printk("ERR from hidden_dentry!!!\n");
+			goto out;
+		}
+
+		if (child_dentry == dentry) {
+			int loop_start;
+			int loop_end;
+			int new_bstart = -1;
+			int new_bend = -1;
+			int i;
+
+			/* initialize the new dentry */
+			set_dtohd_index(dentry, bindex, hidden_dentry);
+
+			loop_start = (old_bstart < bindex) ? old_bstart : bindex;
+			loop_end = (old_bend > bindex) ? old_bend : bindex;
+
+			/* This loop sets the bstart and bend for the new
+			 * dentry by traversing from left to right.
+			 * It also dputs all negative dentries except
+			 * bindex (the newly looked dentry
+			 */
+			for ( i = loop_start; i <= loop_end; i++) {
+
+				if (!dtohd_index(dentry, i)) {
+					continue;
+				}
+
+				if ( i == bindex) {
+					new_bend = i;
+					if (new_bstart < 0) {
+						new_bstart = i;
+					}
+					continue;
+				}
+
+				if (!dtohd_index(dentry, i)->d_inode) {
+					dput(dtohd_index(dentry, i));
+					set_dtohd_index(dentry, i, NULL);
+				} else {
+					if (new_bstart < 0) {
+						new_bstart = i;
+					}
+					new_bend = i;
+				}
+			}
+
+			if (new_bstart < 0) {
+				new_bstart = bindex;
+			}
+			if (new_bend < 0) {
+				new_bend = bindex;
+			}
+			set_dbstart(dentry, new_bstart);
+			set_dbend(dentry, new_bend);
+			break;
+		}
+
+		if (hidden_dentry->d_inode) {
+			/* since this already exists we dput to avoid
+			 * multiple references on the same dentry
+			 */
+			dput(hidden_dentry);
+		}
+		else {
+			uid_t saved_uid = current->fsuid;
+			gid_t saved_gid = current->fsgid;
+
+			/* its a negative dentry, create a new dir */
+			hidden_parent_dentry = lock_parent(hidden_dentry);
+			current->fsuid = hidden_parent_dentry->d_inode->i_uid;
+			current->fsgid = hidden_parent_dentry->d_inode->i_gid;
+			err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, S_IRWXUGO);
+			current->fsuid = saved_uid;
+			current->fsgid = saved_gid;
+			unlock_dir(hidden_parent_dentry);
+			if (err || !hidden_dentry->d_inode) {
+				dput(hidden_dentry);
+				hidden_dentry = ERR_PTR(err);
+				goto out;
+			}
+			err = unionfs_copyup_permissions(dir->i_sb,child_dentry,hidden_dentry);
+			itohi_index(child_dentry->d_inode, bindex) = igrab(hidden_dentry->d_inode);
+			if (ibstart(child_dentry->d_inode) > bindex) {
+				ibstart(child_dentry->d_inode) = bindex;
+			}
+			if (ibend(child_dentry->d_inode) < bindex) {
+				ibend(child_dentry->d_inode) = bindex;
+			}
+
+			set_dtohd_index(child_dentry, bindex,  hidden_dentry);
+			if (dbstart(child_dentry) > bindex) {
+				set_dbstart(child_dentry, bindex);
+			}
+			if (dbend(child_dentry) < bindex) {
+				set_dbend(child_dentry,  bindex);
+			}
+		}
+
+		parent_dentry = child_dentry;
+		child_dentry = path[--count];
+	}
+out:
+	fist_print_dentry("OUT: unionfs_create_dirs: ", dentry);
+	print_exit_pointer(hidden_dentry);
+	return hidden_dentry;
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/dentry.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/dentry.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/dentry.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/dentry.c	2005-04-25 11:56:14.513659672 -0700
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: dentry.c,v 1.31 2005/03/03 13:28:41 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+
+/*
+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static int unionfs_d_revalidate(dentry_t *dentry, struct nameidata *nd)
+#else
+static int unionfs_d_revalidate(dentry_t *dentry, int flags)
+#endif
+{
+    int err = 1; // default is valid (1); invalid is 0.
+    int invalid = 0;
+    dentry_t *hidden_dentry;
+    int bindex, bstart, bend;
+    int sbgen, dgen;
+    int positive = 0;
+    int locked = 0;
+    int restart = 0;
+
+    print_util_entry_location();
+
+restart:
+    /* Before anything else, we should check if the generation number is valid. */
+    PASSERT(dentry);
+    PASSERT(dentry->d_sb);
+    ASSERT(dbstart(dentry) != -1);
+    if (dentry->d_inode) {
+	positive = 1;
+    }
+    dgen = atomic_read(&dtopd(dentry)->udi_generation);
+    sbgen = atomic_read(&stopd(dentry->d_sb)->usi_generation);
+    if (sbgen != dgen) {
+	struct dentry *result;
+	int pdgen;
+
+	lock_super(dentry->d_sb);
+	lock_dpd(dentry);
+	locked = 1;
+
+	/* The root entry should always be valid */
+	ASSERT(!IS_ROOT(dentry));
+	PASSERT(dentry->d_parent);
+
+	/* We can't work correctly if our parent isn't valid. */
+	pdgen = atomic_read(&dtopd(dentry->d_parent)->udi_generation);
+	if (!restart && (pdgen != sbgen)) {
+		PASSERT(dentry->d_parent->d_op);
+		PASSERT(dentry->d_parent->d_op->d_revalidate);
+		unlock_super(dentry->d_sb);
+		unlock_dpd(dentry);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+		if (!(dentry->d_parent->d_op->d_revalidate(dentry->d_parent, nd)))
+#else
+		if (!(dentry->d_parent->d_op->d_revalidate(dentry->d_parent, flags)))
+#endif
+		{
+			invalid = 1;
+			goto out;
+		}
+		restart = 1;
+		goto restart;
+	}
+	ASSERT(pdgen == sbgen);
+
+	PASSERT(dentry->d_parent);
+	PASSERT(dentry->d_parent->d_inode);
+
+	/* Free the pointers for our inodes and this dentry. */
+	PASSERT(dtohd_ptr(dentry));
+	bstart = dbstart(dentry);
+	bend = dbend(dentry);
+	if (bstart >= 0) {
+		struct dentry *hidden_dentry;
+		for (bindex = bstart; bindex <= bend; bindex++) {
+			hidden_dentry = dtohd_index(dentry, bindex);
+			if (!hidden_dentry) continue;
+			dput(hidden_dentry);
+		}
+	}
+	set_dbstart(dentry, -1);
+	set_dbend(dentry, -1);
+
+	KFREE(dtohd_ptr(dentry));
+	dtohd_ptr(dentry) = NULL;
+	dtopd(dentry)->udi_bcount = 0;
+
+	if (positive) {
+		PASSERT(dentry->d_inode);
+		PASSERT(itopd(dentry->d_inode));
+		PASSERT(itohi_ptr(dentry->d_inode));
+		bstart = ibstart(dentry->d_inode);
+		bend = ibend(dentry->d_inode);
+		if (bstart >= 0) {
+			struct inode *hidden_inode;
+			for (bindex = bstart; bindex <= bend; bindex++) {
+				hidden_inode = itohi_index(dentry->d_inode, bindex);
+				if (!hidden_inode) continue;
+				iput(hidden_inode);
+			}
+		}
+		KFREE(itohi_ptr(dentry->d_inode));
+		itohi_ptr(dentry->d_inode) = NULL;
+		ibstart(dentry->d_inode) = -1;
+		ibend(dentry->d_inode) = -1;
+	}
+
+	result = unionfs_lookup_backend(dentry->d_parent->d_inode, dentry, positive ? INTERPOSE_REVAL : INTERPOSE_REVAL_NEG);
+	if (result && IS_ERR(result)) {
+		err = 0;
+		goto out;
+	}
+
+	if (positive && itopd(dentry->d_inode)->uii_stale) {
+		make_stale_inode(dentry->d_inode);
+		d_drop(dentry);
+  	        err = 0;
+		goto out;
+	}
+	goto out;
+    }
+
+    /*
+     * The revalidation must occur for all the common files across branches
+     */
+    bstart = dbstart(dentry);
+    bend = dbend(dentry);
+    ASSERT(bstart != -1);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+      hidden_dentry  = dtohd_index(dentry, bindex);
+
+      /* Lower level dentries could be NULL for certain indices
+       * Check for that.
+       */
+      if (!hidden_dentry || !hidden_dentry->d_op || !hidden_dentry->d_op->d_revalidate)
+        continue;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+      err = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd);
+#else
+      err = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags);
+#endif
+      if (!err) {
+        invalid = 1;
+      }
+
+    }
+
+    if (!invalid && dtohd(dentry) && dtohd(dentry)->d_inode) {
+	fist_copy_attr_all(dentry->d_inode, dtohd(dentry)->d_inode);
+    }
+
+ out:
+    if (locked) {
+	unlock_super(dentry->d_sb);
+	unlock_dpd(dentry);
+    }
+    if (invalid) {
+      err = 0;
+    }
+    fist_print_dentry("revalidate out", dentry);
+    print_util_exit_status(err);
+    return err;
+}
+
+void
+unionfs_d_release(dentry_t *dentry)
+{
+    dentry_t *hidden_dentry;
+    int bindex, bstart, bend;
+
+    print_entry_location();
+    fist_print_dentry("unionfs_d_release IN dentry", dentry);
+
+    /* this could be a negative dentry, so check first */
+    if (!dtopd(dentry)) {
+	fist_dprint(6, "dentry without private data: %*s", dentry->d_name.len, dentry->d_name.name);
+	goto out;
+    }
+
+    /* Release all the hidden dentries */
+    bstart = dbstart(dentry);
+    bend = dbend(dentry);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+      hidden_dentry = dtohd_index(dentry, bindex);
+      if (hidden_dentry) {
+        /* decrement hidden dentry's counter and free its inode */
+        dput(hidden_dentry);
+      }
+      set_dtohd_index(dentry, bindex, NULL);
+    }
+
+    /* free private data (unionfs_dentry_info) here */
+    KFREE(dtohd_ptr(dentry));
+    dtohd_ptr(dentry) = NULL;
+    KFREE(dtopd(dentry));
+    dtopd_lhs(dentry) = NULL;	/* just to be safe */
+ out:
+    print_exit_location();
+}
+
+/*
+ * we don't really need unionfs_d_iput, because dentry_iput will call iput() if
+ * unionfs_d_iput is not defined. We left this implemented for ease of
+ * tracing/debugging.
+ */
+void
+unionfs_d_iput(dentry_t *dentry, inode_t *inode)
+{
+    print_entry_location();
+    iput(inode);
+    print_exit_location();
+}
+
+
+struct dentry_operations unionfs_dops = {
+    d_revalidate:	unionfs_d_revalidate,
+    d_release:		unionfs_d_release,
+    d_iput:		unionfs_d_iput,
+};
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/dirhelper.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/dirhelper.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/dirhelper.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/dirhelper.c	2005-04-25 11:56:14.531656936 -0700
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: dirhelper.c,v 1.6 2005/03/02 21:39:38 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+/* Delete all of the whiteouts in a given directory for rmdir. */
+int delete_whiteouts(struct dentry *dentry, int bindex, struct unionfs_dir_state *namelist)
+{
+	int err = 0;
+	struct dentry *hidden_dir_dentry = NULL;
+	struct dentry *hidden_dentry;
+	super_block_t *sb;
+	char *name = NULL;
+
+	int i;
+	struct list_head *pos;
+	struct filldir_node *cursor;
+
+	print_entry_location();
+
+	sb = dentry->d_sb;
+
+	ASSERT(S_ISDIR(dentry->d_inode->i_mode));
+
+	/* First we need to make sure all of the hidden_dentries are filled in. */
+	err = unionfs_partial_lookup(dentry);
+	if (err) {
+		goto out;
+	}
+
+	ASSERT(bindex >= dbstart(dentry));
+	ASSERT(bindex <= dbend(dentry));
+
+	/* Find out hidden parent dentry */
+	hidden_dir_dentry = dtohd_index(dentry, bindex);
+	PASSERT(hidden_dir_dentry);
+	PASSERT(hidden_dir_dentry->d_inode);
+	ASSERT(S_ISDIR(hidden_dir_dentry->d_inode->i_mode));
+
+	name = (char *)__get_free_page(GFP_UNIONFS);
+	if (!name) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < namelist->uds_size; i++) {
+		list_for_each(pos, &namelist->uds_list[i]) {
+			cursor = list_entry(pos, struct filldir_node, file_list);
+			/* Only operate on whiteouts in this branch. */
+			if (cursor->bindex != bindex) continue;
+			if (!cursor->whiteout) continue;
+
+			strcpy(name, ".wh.");
+			strncpy(name + 4, cursor->name, PAGE_SIZE - 4);
+
+			hidden_dentry = lookup_one_len(name, hidden_dir_dentry, cursor->namelen + 4);
+			if (IS_ERR(hidden_dentry)) {
+				err = PTR_ERR(hidden_dentry);
+				goto out;
+			}
+			if (!hidden_dentry->d_inode) {
+				dput(hidden_dentry);
+				continue;
+			}
+
+			down(&hidden_dir_dentry->d_inode->i_sem);
+			err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
+			up(&hidden_dir_dentry->d_inode->i_sem);
+			dput(hidden_dentry);
+
+			if (!err) {
+				d_delete(hidden_dentry);
+			} else if (!IS_COPYUP_ERR(err)) {
+				goto out;
+			}
+		}
+	}
+
+out:
+	/* After all of the removals, we should copy the attributes once. */
+	fist_copy_attr_times(dentry->d_inode, hidden_dir_dentry->d_inode);
+	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+
+	if (name) {
+		free_page((unsigned long) name);
+	}
+	print_exit_status(err);
+	return err;
+}
+
+
+#define RD_NONE 0
+#define RD_CHECK_EMPTY 1
+/* The callback structure for check_empty. */
+struct unionfs_rdutil_callback {
+	int err;
+	int filldir_called;
+	struct unionfs_dir_state *rdstate;
+	int mode;
+};
+
+/* This is a filldir function that makes sure only whiteouts exist within a directory. */
+static int readdir_util_callback(void *dirent, const char *name, int namelen, loff_t offset, ino_t ino, unsigned int d_type)
+{
+	int err = 0;
+	struct unionfs_rdutil_callback *buf = (struct unionfs_rdutil_callback *) dirent;
+	int whiteout = 0;
+	struct filldir_node *found;
+
+	print_entry_location();
+
+	buf->filldir_called = 1;
+
+	if (name[0] == '.' && (namelen == 1 || (name[1] == '.' && namelen == 2))) {
+		goto out;
+	}
+
+	if (!strncmp(name, ".wh.", 4)) {
+		namelen -= 4;
+		name += 4;
+		whiteout = 1;
+	}
+
+	found = find_filldir_node(buf->rdstate, name, namelen);
+	/* If it was found in the table there was a previous whiteout. */
+	if (found) {
+		goto out;
+	}
+
+	/* If it wasn't found, and it isn't a whiteout the directory is not actually empty. */
+	if ((buf->mode == RD_CHECK_EMPTY) && !whiteout) {
+		err = -ENOTEMPTY;
+		goto out;
+	}
+
+	err = add_filldir_node(buf->rdstate, name, namelen, buf->rdstate->uds_bindex, whiteout);
+
+out:
+	buf->err = err;
+	print_exit_status(err);
+	return err;
+}
+
+/* Is a directory logically empty? */
+int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist)
+{
+	int err = 0;
+	struct dentry *hidden_dentry = NULL;
+	super_block_t *sb;
+	file_t *hidden_file;
+	struct unionfs_rdutil_callback *buf = NULL;
+	int bindex, bstart, bend;
+
+	print_entry_location();
+
+	sb = dentry->d_sb;
+
+	ASSERT(S_ISDIR(dentry->d_inode->i_mode));
+
+	/* First we need to make sure all of the hidden_dentries are filled in. */
+	err = unionfs_partial_lookup(dentry);
+	if (err) {
+		goto out;
+	}
+
+	bstart = dbstart(dentry);
+	bend = dbend(dentry);
+
+	buf = KMALLOC(sizeof(struct unionfs_rdutil_callback), GFP_UNIONFS);
+	if (!buf) {
+		err = -ENOMEM;
+		goto out;
+	}
+	buf->err = 0;
+	buf->mode = RD_CHECK_EMPTY;
+	buf->rdstate = alloc_rdstate(dentry->d_inode, bstart);
+	if (!buf->rdstate) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Open all of the hidden_dentries and use the rdutil_callback function as a filldir. */
+	for (bindex = bstart; bindex <= bend; bindex++) {
+		/* Find out hidden parent dentry */
+		hidden_dentry = dtohd_index(dentry, bindex);
+
+		/* continue if the hidden_dentry doesn't exist */
+		if (!hidden_dentry) {
+			continue;
+		}
+
+		/* If it is a positive dentry. */
+		if (hidden_dentry->d_inode && S_ISDIR(hidden_dentry->d_inode->i_mode)) {
+			dget(hidden_dentry);
+			mntget(stohiddenmnt_index(sb, bindex));
+			branchget(sb, bindex);
+			hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bindex), O_RDONLY);
+			if (IS_ERR(hidden_file)) {
+				err = PTR_ERR(hidden_file);
+				dput(hidden_dentry);
+				branchput(sb, bindex);
+				goto out;
+			}
+
+			do {
+				buf->filldir_called = 0;
+				buf->rdstate->uds_bindex = bindex;
+				err = vfs_readdir(hidden_file, readdir_util_callback, buf);
+				if (buf->err) {
+					err = buf->err;
+				}
+			} while ((err >= 0) && buf->filldir_called);
+
+			/* fput calls dput for hidden_dentry */
+			fput(hidden_file);
+			branchput(sb, bindex);
+
+			if (err < 0)
+				goto out;
+		}
+	}
+
+out:
+	if (buf) {
+		if (namelist && !err) {
+			*namelist = buf->rdstate;
+		} else if (buf->rdstate) {
+			free_rdstate(buf->rdstate);
+		}
+		KFREE(buf);
+	}
+
+	print_exit_status(err);
+	return err;
+}
+
+/*
+ * Creates a whiteout for the files existing in a directory
+ *  dentry is the unionfs dentry
+ *  cur_index is the directory where we will create the whiteouts
+ */
+int create_dir_whs(struct dentry *dentry, int cur_index)
+{
+	int err = 0;
+	int bindex = 0;
+	struct dentry *hidden_dentry = NULL;
+	struct dentry *hidden_parent_dentry;
+	super_block_t *sb;
+	file_t *hidden_file;
+	struct unionfs_rdutil_callback buf;
+	struct dentry *hidden_dir_dentry;
+	char *name = NULL;
+	int i;
+
+	print_entry_location();
+	fist_print_dentry("IN create_dir_whs: ", dentry);
+
+	hidden_dir_dentry = dtohd(dentry);
+	PASSERT(hidden_dir_dentry);
+
+	sb = dentry->d_sb;
+
+	buf.rdstate = alloc_rdstate(dentry->d_inode, cur_index);
+	if (!buf.rdstate) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* open all dentries to the right of branch 'cur_index' and do vfs_readdir on them.
+	   In the create_dir_whs_filldir() function, for each entry create a whiteout in branch 'cur_index'  */
+	for (bindex = cur_index + 1; bindex <= sbend(sb); bindex++) {
+		/* Find out hidden parent dentry */
+		hidden_parent_dentry = dtohd_index(dentry->d_parent, bindex);
+
+		/* continue if hidden parent doesn't exist */
+		if (!hidden_parent_dentry) {
+			continue;
+		}
+
+		/* lookup underlying file system */
+		hidden_dentry = lookup_one_len(dentry->d_name.name, hidden_parent_dentry, dentry->d_name.len);
+		if (IS_ERR(hidden_dentry)) {
+			err = PTR_ERR(hidden_dentry);
+			goto out;
+		}
+
+		if (hidden_dentry->d_inode && S_ISDIR(hidden_dentry->d_inode->i_mode)) {
+			mntget(stohiddenmnt_index(sb, bindex));
+			branchget(sb, bindex);
+			hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bindex), O_RDONLY);
+			if (IS_ERR(hidden_file)) {
+				err = PTR_ERR(hidden_file);
+				branchput(sb, bindex);
+				goto out;
+			}
+			/* prepare callback buffer */
+			do {
+				buf.filldir_called = 0;
+				buf.err = 0;
+				buf.rdstate->uds_bindex = bindex;
+				err = vfs_readdir(hidden_file, readdir_util_callback, &buf);
+				if (buf.err) {
+					err = buf.err;
+				}
+			} while ((err >= 0) && buf.filldir_called);
+
+			/* fput calls dput for internally */
+			fput(hidden_file);
+			branchput(sb, bindex);
+		} else {
+			/* dput the unnecessary negative dentry */
+			dput(hidden_dentry);
+		}
+	}
+
+	/* Now we have assembled a list, so we can go through it. */
+	name = (char *)__get_free_page(GFP_UNIONFS);
+	if (!name) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < buf.rdstate->uds_size; i++) {
+		struct list_head *pos;
+		struct filldir_node *cursor;
+
+		list_for_each(pos, &buf.rdstate->uds_list[i]) {
+			cursor = list_entry(pos, struct filldir_node, file_list);
+			if (cursor->whiteout) continue;
+
+			strcpy(name, ".wh.");
+			strncpy(name + 4, cursor->name, PAGE_SIZE - 4);
+
+			hidden_dentry = lookup_one_len(name, hidden_dir_dentry, cursor->namelen + 4);
+			if (IS_ERR(hidden_dentry)) {
+				err = PTR_ERR(hidden_dentry);
+				goto out;
+			}
+
+			down(&hidden_dir_dentry->d_inode->i_sem);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+			err = vfs_create(hidden_dir_dentry->d_inode, hidden_dentry, 0600);
+#else
+			err = vfs_create(hidden_dir_dentry->d_inode, hidden_dentry, 0600, NULL);
+#endif
+			up(&hidden_dir_dentry->d_inode->i_sem);
+			dput(hidden_dentry);
+
+			if (!err) {
+				d_delete(hidden_dentry);
+			} else if (!IS_COPYUP_ERR(err)) {
+				goto out;
+			}
+		}
+	}
+	/* After all of the creations, we should copy the attributes once. */
+	fist_copy_attr_times(dentry->d_inode, hidden_dir_dentry->d_inode);
+
+out:
+	if (name) {
+		free_page((unsigned long) name);
+	}
+	if (buf.rdstate) {
+		free_rdstate(buf.rdstate);
+	}
+	fist_print_dentry("OUT create_dir_whs :", dentry);
+	print_exit_status(err);
+	return err;
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/file.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/file.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/file.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/file.c	2005-04-25 11:56:14.534656480 -0700
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: file.c,v 1.93 2005/03/02 21:39:38 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+#include <linux/dcache.h>
+
+/*******************
+ * File Operations *
+ *******************/
+
+static int unionfs_file_revalidate(struct file *file) {
+    struct super_block *sb;
+    struct dentry *dentry;
+    int sbgen, fgen, dgen;
+    int bindex, bstart, bend;
+    struct file *hidden_file;
+    struct dentry *hidden_dentry;
+
+    int err = 0;
+
+    print_entry(" file = %p", file);
+
+    PASSERT(file);
+    PASSERT(ftopd(file));
+
+    dentry = file->f_dentry;
+    PASSERT(dentry);
+    PASSERT(dentry->d_op);
+    PASSERT(dentry->d_op->d_revalidate);
+    if (!(dentry->d_op->d_revalidate(dentry, 0))) {
+	err = -ESTALE;
+	goto out;
+    }
+
+    sb = dentry->d_sb;
+    PASSERT(sb);
+    sbgen = atomic_read(&stopd(sb)->usi_generation);
+    dgen = atomic_read(&dtopd(dentry)->udi_generation);
+    fgen = atomic_read(&ftopd(file)->ufi_generation);
+    if (sbgen > dgen) {
+	FISTBUG("The dentry is not up to date!\n");
+    }
+    if (sbgen > fgen) {
+	/* First we throw out the existing files. */
+	bstart = fbstart(file);
+	bend = fbend(file);
+	for (bindex = bstart; bindex <= bend; bindex++) {
+	    if (ftohf_index(file, bindex)) {
+		branchput(dentry->d_sb, bindex);
+		fput(ftohf_index(file, bindex));
+	    }
+	}
+	if (ftohf_ptr(file)) {
+	    KFREE(ftohf_ptr(file));
+	}
+
+	/* Now we reopen the file(s) as in unionfs_open. */
+	bstart = fbstart(file) = dbstart(dentry);
+	bend = fbend(file) = dbend(dentry);
+
+	ftohf_ptr(file) = KMALLOC(sizeof(file_t *) * sbmax(sb), GFP_UNIONFS);
+	if (!ftohf_ptr(file)) {
+	    err = -ENOMEM;
+	    goto out;
+	}
+
+	init_ftohf_ptr(file, sbmax(sb));
+
+
+	if (S_ISDIR(dentry->d_inode->i_mode)) {
+	    /* We need to open all the files. */
+	    for (bindex = bstart; bindex <= bend; bindex++) {
+		hidden_dentry = dtohd_index(dentry, bindex);
+		if (!hidden_dentry) {
+		    continue;
+		}
+
+		dget(hidden_dentry);
+		mntget(stohiddenmnt_index(sb, bindex));
+		branchget(sb, bindex);
+
+		hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bindex), file->f_flags);
+		if (IS_ERR(hidden_file)) {
+		    err = PTR_ERR(hidden_file);
+		    goto out;
+		} else {
+		    ftohf_index(file, bindex) = hidden_file;
+		}
+	    }
+	} else {
+	    /* We only open the highest priority branch. */
+	    hidden_dentry = dtohd(dentry);
+	    PASSERT(hidden_dentry);
+	    PASSERT(hidden_dentry->d_inode);
+	    if (IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) {
+		for (bindex = bstart - 1; bindex >= 0; bindex--) {
+		    err = unionfs_copyup_file(dentry->d_parent->d_inode, file, bstart, bindex, file->f_dentry->d_inode->i_size);
+		    if (!err) {
+			break;
+		    }
+		}
+		goto out;
+	    }
+
+	    dget(hidden_dentry);
+	    mntget(stohiddenmnt_index(sb, bstart));
+	    branchget(sb, bstart);
+	    hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bstart), file->f_flags);
+	    if (IS_ERR(hidden_file)) {
+		err = PTR_ERR(hidden_file);
+		goto out;
+	    }
+	    ftohf(file) = hidden_file;
+	    /* Fix up the position. */
+	    hidden_file->f_pos = file->f_pos;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	    /* update readahead information, all the time, because the old file
+	     * could have read-ahead information that doesn't match.
+	     */
+	    if(file->f_reada)
+	    {
+		hidden_file->f_reada = file->f_reada;
+		hidden_file->f_ramax = file->f_ramax;
+		hidden_file->f_raend = file->f_raend;
+		hidden_file->f_ralen = file->f_ralen;
+		hidden_file->f_rawin = file->f_rawin;
+	    }
+#else
+	    memcpy(&(hidden_file->f_ra), &(file->f_ra), sizeof(struct file_ra_state));
+#endif
+	}
+        atomic_set(&ftopd(file)->ufi_generation, atomic_read(&itopd(dentry->d_inode)->uii_generation));
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+STATIC loff_t
+unionfs_llseek(file_t *file, loff_t offset, int origin)
+{
+    loff_t err;
+    file_t *hidden_file = NULL;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    if (ftopd(file) != NULL) {
+	hidden_file = ftohf(file);
+    }
+
+    fist_dprint(6, "unionfs_llseek: file=%p, offset=0x%llx, origin=%d\n",
+		file, offset, origin);
+
+    /* always set hidden position to this one */
+    hidden_file->f_pos = file->f_pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    /* update readahead information, all the time, because the old file
+     * could have read-ahead information that doesn't match.
+     */
+    if(file->f_reada)
+    {
+	hidden_file->f_reada = file->f_reada;
+	hidden_file->f_ramax = file->f_ramax;
+	hidden_file->f_raend = file->f_raend;
+	hidden_file->f_ralen = file->f_ralen;
+	hidden_file->f_rawin = file->f_rawin;
+    }
+#else
+    memcpy(&(hidden_file->f_ra), &(file->f_ra), sizeof(struct file_ra_state));
+#endif
+
+    if (hidden_file->f_op && hidden_file->f_op->llseek)
+	err = hidden_file->f_op->llseek(hidden_file, offset, origin);
+    else
+	err = generic_file_llseek(hidden_file, offset, origin);
+
+    if (err < 0)
+	goto out;
+    if (err != file->f_pos) {
+	file->f_pos = err;
+	// ION maybe this?
+	// 	file->f_pos = hidden_file->f_pos;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+        file->f_reada = 0;
+#endif
+	file->f_version++;
+    }
+out:
+    print_exit_status((int) err);
+    return err;
+}
+
+
+STATIC ssize_t
+unionfs_read(file_t *file, char *buf, size_t count, loff_t *ppos)
+{
+    int err = -EINVAL;
+    file_t *hidden_file = NULL;
+    loff_t pos = *ppos;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    fist_print_file("entering read()", file);
+
+    if (ftopd(file) != NULL) {
+	hidden_file = ftohf(file);
+    }
+
+    if (!hidden_file->f_op || !hidden_file->f_op->read)
+	goto out;
+
+    err = hidden_file->f_op->read(hidden_file, buf, count, &pos);
+    *ppos = pos;
+    if (err >= 0) {
+	/* atime should also be updated for reads of size zero or more */
+	fist_copy_attr_atime(file->f_dentry->d_inode,
+			     hidden_file->f_dentry->d_inode);
+    }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    /*
+     * because pread() does not have any way to tell us that it is
+     * our caller, then we don't know for sure if we have to update
+     * the file positions.  This hack relies on read() having passed us
+     * the "real" pointer of its struct file's f_pos field.
+     */
+    if (ppos == &file->f_pos)
+	hidden_file->f_pos = *ppos = pos;
+    if (hidden_file->f_reada) { /* update readahead information if needed */
+	file->f_reada = hidden_file->f_reada;
+	file->f_ramax = hidden_file->f_ramax;
+	file->f_raend = hidden_file->f_raend;
+	file->f_ralen = hidden_file->f_ralen;
+	file->f_rawin = hidden_file->f_rawin;
+    }
+#else
+    memcpy(&(file->f_ra),&(hidden_file->f_ra),sizeof(struct file_ra_state));
+#endif
+
+out:
+    fist_print_file("leaving read()", file);
+    print_exit_status(err);
+    return err;
+}
+
+
+/* this unionfs_write() does not modify data pages! */
+STATIC ssize_t
+unionfs_write(file_t *file, const char *buf, size_t count, loff_t *ppos)
+{
+    int err = -EINVAL;
+    file_t *hidden_file = NULL;
+    inode_t *inode;
+    inode_t *hidden_inode;
+    loff_t pos = *ppos;
+    int bstart, bend;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    inode = file->f_dentry->d_inode;
+
+    bstart = fbstart(file);
+    bend = fbend(file);
+
+    ASSERT(bstart != -1);
+    PASSERT(ftopd(file));
+    PASSERT(ftohf(file));
+
+    hidden_file = ftohf(file);
+    hidden_inode = hidden_file->f_dentry->d_inode;
+
+    if (!hidden_file->f_op || !hidden_file->f_op->write) {
+	goto out;
+    }
+
+    /* adjust for append -- seek to the end of the file */
+    if (file->f_flags & O_APPEND)
+	pos = inode->i_size;
+
+    err = hidden_file->f_op->write(hidden_file, buf, count, &pos);
+
+    /*
+     * copy ctime and mtime from lower layer attributes
+     * atime is unchanged for both layers
+     */
+    if (err >= 0)
+	fist_copy_attr_times(inode, hidden_inode);
+
+    /*
+     * XXX: MAJOR HACK
+     *
+     * because pwrite() does not have any way to tell us that it is
+     * our caller, then we don't know for sure if we have to update
+     * the file positions.  This hack relies on write() having passed us
+     * the "real" pointer of its struct file's f_pos field.
+     */
+	*ppos = pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	if (ppos == &file->f_pos)
+	hidden_file->f_pos = *ppos = pos;
+#endif
+
+    /* update this inode's size */
+    if (pos > inode->i_size)
+	inode->i_size = pos;
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+struct unionfs_getdents_callback {
+    struct unionfs_dir_state *rdstate;
+    void *dirent;
+    int entries_written;
+    int filldir_called;
+    int filldir_error;
+    filldir_t filldir;
+};
+
+/* copied from generic filldir in fs/readir.c */
+static int unionfs_filldir(void *dirent, const char *name, int namelen, loff_t offset, ino_t ino, unsigned int d_type)
+{
+    struct unionfs_getdents_callback *buf = (struct unionfs_getdents_callback *) dirent;
+    struct filldir_node *found = NULL;
+    int err = 0;
+    int is_wh_entry = 0;
+
+    fist_dprint(9, "unionfs_filldir name=%*s\n", namelen, name);
+
+    PASSERT(buf);
+    buf->filldir_called++;
+
+    if (!strncmp(name, ".wh.", 4)) {
+	name += 4;
+	namelen -= 4;
+	is_wh_entry = 1;
+    }
+    found = find_filldir_node(buf->rdstate, name, namelen);
+
+    if (!found) {
+	/* if 'name' isn't a whiteout filldir it. */
+	if (!is_wh_entry) {
+		err = buf->filldir(buf->dirent, name, namelen, buf->rdstate->uds_offset++, ino, d_type);
+		ASSERT(buf->rdstate->uds_offset < DIREOF);
+	}
+	/* If we did fill it, stuff it in our hash, otherwise return an error */
+	if (err) {
+		buf->filldir_error = err;
+		goto out;
+	} else {
+		buf->entries_written++;
+		if ((err = add_filldir_node(buf->rdstate, name, namelen, buf->rdstate->uds_bindex, is_wh_entry))) {
+			buf->filldir_error = err;
+		}
+	}
+    }
+
+out:
+    return err;
+}
+
+STATIC int
+unionfs_readdir(file_t *file, void *dirent, filldir_t filldir)
+{
+	int err = 0;
+	file_t *hidden_file = NULL;
+	inode_t *inode = NULL;
+	struct unionfs_getdents_callback buf;
+	struct unionfs_dir_state *uds;
+	int bend;
+
+	print_entry("file = %p, pos = %llx", file, file->f_pos);
+
+	fist_print_file("In unionfs_readdir()", file);
+
+	if ((err = unionfs_file_revalidate(file))) {
+		goto out;
+	}
+
+	inode = file->f_dentry->d_inode;
+	fist_checkinode(inode, "unionfs_readdir");
+
+	uds = ftopd(file)->rdstate;
+	if(!uds) {
+		if (file->f_pos == DIREOF) {
+			goto out;
+		} else if (file->f_pos > 0) {
+			struct list_head *pos;
+
+			fist_dprint(8, ">Detected second NFS access to a file: file->f_pos = %llu\n", file->f_pos);
+			spin_lock(&itopd(inode)->uii_rdlock);
+			list_for_each(pos, &itopd(inode)->uii_readdircache) {
+				struct unionfs_dir_state *rdstate = list_entry(pos, struct unionfs_dir_state, uds_cache);
+				if (rdstate->uds_key == file->f_pos) {
+					itopd(inode)->uii_rdcount--;
+					list_del(&rdstate->uds_cache);
+					uds = ftopd(file)->rdstate = rdstate;
+					fist_dprint(8, "Found an rdstate that matches: %d.%d\n", uds->uds_bindex, uds->uds_dirpos);
+					break;
+				}
+			}
+			spin_unlock(&itopd(inode)->uii_rdlock);
+
+			if (!uds) {
+				err = -ESTALE;
+				goto out;
+			}
+		} else {
+			init_rdstate(file);
+			uds = ftopd(file)->rdstate;
+		}
+	}
+	bend = fbend(file);
+
+	while (uds->uds_bindex <= bend) {
+		hidden_file = ftohf_index(file, uds->uds_bindex);
+		if (!hidden_file) {
+			fist_dprint(7, "Incremented bindex to %d of %d because hidden file is NULL.\n", uds->uds_bindex, bend);
+			uds->uds_bindex++;
+			uds->uds_dirpos = 0;
+			continue;
+		}
+
+		/* prepare callback buffer */
+		buf.filldir_called = 0;
+		buf.filldir_error = 0;
+		buf.entries_written = 0;
+		buf.dirent = dirent;
+		buf.filldir = filldir;
+		buf.rdstate = uds;
+
+		/* Read starting from where we last left off. */
+		hidden_file->f_pos = uds->uds_dirpos;
+		fist_dprint(7, "calling readdir for %d.%lld\n", uds->uds_bindex, hidden_file->f_pos);
+		err = vfs_readdir(hidden_file, unionfs_filldir, (void *) &buf);
+		fist_dprint(7, "readdir on %d.%d = %d (entries written %d, filldir called %d)\n", uds->uds_bindex, uds->uds_dirpos, err, buf.entries_written, buf.filldir_called);
+		/* Save the position for when we continue. */
+		uds->uds_dirpos = hidden_file->f_pos;
+		/* Copy the atime. */
+		fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode);
+
+		if (err < 0) {
+			goto out;
+		}
+
+		if (buf.filldir_error) {
+			break;
+		}
+
+		if (!buf.entries_written) {
+			uds->uds_bindex++;
+			uds->uds_dirpos = 0;
+		}
+	}
+
+	if (!buf.filldir_error && uds->uds_bindex >= bend) {
+		fist_dprint(8, "Discarding rdstate because readdir is over.\n");
+		/* Save the number of hash entries for next time. */
+		itopd(inode)->uii_hashsize = uds->uds_hashentries;
+		ftopd(file)->ufi_enddiroff = uds->uds_offset;
+		free_rdstate(uds);
+		ftopd(file)->rdstate = NULL;
+		file->f_pos = DIREOF;
+	} else {
+		spin_lock(&itopd(inode)->uii_rdlock);
+		uds->uds_key = file->f_pos = itopd(inode)->uii_rdversion++;
+		spin_unlock(&itopd(inode)->uii_rdlock);
+		fist_dprint(8, "Rdstate now has a key of %lu (err = %d)\n", uds->uds_key, err);
+	}
+
+out:
+	fist_checkinode(inode, "post unionfs_readdir");
+	print_exit_status(err);
+	return err;
+}
+
+STATIC loff_t
+unionfs_dir_llseek(file_t *file, loff_t offset, int origin)
+{
+    struct unionfs_dir_state *rdstate;
+    loff_t err;
+
+    print_entry(" file=%p, offset=0x%llx, origin = %d", file, offset, origin);
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    rdstate = ftopd(file)->rdstate;
+
+    /* We let users seek to their current position, but not anywhere else. */
+    if (offset) {
+	switch(origin) {
+	    case SEEK_SET:
+		if (rdstate && (origin == rdstate->uds_offset)) {
+			err = rdstate->uds_offset;
+		} else if (file->f_pos == DIREOF) {
+			err = ftopd(file)->ufi_enddiroff;
+		} else {
+			err = -EINVAL;
+		}
+		break;
+	    case SEEK_CUR:
+	    case SEEK_END:
+		/* Unsupported, because we would break everything.  */
+		err = -EINVAL;
+		break;
+	}
+    } else {
+	switch(origin) {
+	    /* We let you start over. */
+	    case SEEK_SET:
+		file->f_pos = 0;
+		if (rdstate) {
+		    free_rdstate(rdstate);
+		    ftopd(file)->rdstate = NULL;
+		}
+		break;
+	    /* We let you figure out where you are. */
+	    case SEEK_CUR:
+		/* XXX: Dave this is a provisional fix for bug 187, which
+		 * was caused by not checking rdstate's NULLness. Please
+		 * check it out. */
+		if (rdstate) {
+			err = rdstate->uds_offset;
+		} else {
+			ASSERT(file->f_pos == 0);
+			err = 0;
+		}
+		break;
+	    case SEEK_END:
+		/* Unsupported, because we would break everything.  */
+		err = -EINVAL;
+		break;
+	}
+    }
+
+out:
+    print_exit_status((int) err);
+    return err;
+}
+
+STATIC int
+unionfs_file_readdir(file_t *file, void *dirent, filldir_t filldir)
+{
+    int err = -ENOTDIR;
+    print_entry_location();
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC unsigned int
+unionfs_poll(file_t *file, poll_table *wait)
+{
+    unsigned int mask = DEFAULT_POLLMASK;
+    file_t *hidden_file = NULL;
+
+    print_entry_location();
+
+    if (unionfs_file_revalidate(file)) {
+	/* We should pretend an error happend. */
+	mask = POLLERR|POLLIN|POLLOUT;
+	goto out;
+    }
+
+    if (ftopd(file) != NULL)
+	hidden_file = ftohf(file);
+
+    if (!hidden_file->f_op || !hidden_file->f_op->poll)
+	goto out;
+
+    mask = hidden_file->f_op->poll(hidden_file, wait);
+
+out:
+    print_exit_status(mask);
+    return mask;
+}
+
+STATIC int
+unionfs_ioctl(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg)
+{
+    int err = 0;		/* don't fail by default */
+    file_t *hidden_file = NULL;
+    vfs_t *this_vfs;
+    vnode_t *this_vnode;
+    int val;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    this_vfs = inode->i_sb;
+    this_vnode = inode;
+
+    /* check if asked for local commands */
+    switch (cmd) {
+    case FIST_IOCTL_GET_DEBUG_VALUE:
+        if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+	val = fist_get_debug_value();
+	err = put_user(val, (int *) arg);
+	break;
+
+    case FIST_IOCTL_SET_DEBUG_VALUE:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = get_user(val, (int *) arg);
+	if (err)
+	    break;
+	fist_dprint(6, "IOCTL SET: got arg %d\n", val);
+	if (val < 0 || val > 20) {
+	    err = -EINVAL;
+	    break;
+	}
+	fist_set_debug_value(val);
+	break;
+
+    /* add non-debugging fist ioctl's here */
+
+    case UNIONFS_IOCTL_BRANCH_COUNT:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_branchcount(inode, file, cmd, arg);
+	break;
+
+    case UNIONFS_IOCTL_INCGEN:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_incgen(inode, file, cmd, arg);
+	break;
+
+    case UNIONFS_IOCTL_ADDBRANCH:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_addbranch(inode, file, cmd, arg);
+	break;
+
+    case UNIONFS_IOCTL_DELBRANCH:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_delbranch(inode, file, cmd, arg);
+	break;
+
+    case UNIONFS_IOCTL_RDWRBRANCH:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_rdwrbranch(inode, file, cmd, arg);
+	break;
+
+    case UNIONFS_IOCTL_SUPERDUPER:
+	if (!capable(CAP_SYS_ADMIN)) {
+            err = -EACCES;
+            goto out;
+        }
+        err = unionfs_ioctl_superduper(inode, file, cmd, arg);
+	break;
+
+    default:
+        if (ftopd(file) != NULL) {
+	    hidden_file = ftohf(file);
+	}
+	/* pass operation to hidden filesystem, and return status */
+	if (hidden_file && hidden_file->f_op && hidden_file->f_op->ioctl)
+	    err = hidden_file->f_op->ioctl(itohi(inode), hidden_file, cmd, arg);
+	else
+	    err = -ENOTTY;	/* this is an unknown ioctl */
+    } /* end of outer switch statement */
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+/* FIST-LITE special version of mmap */
+STATIC int
+unionfs_mmap(file_t *file, vm_area_t *vma)
+{
+    int err = 0;
+    file_t *hidden_file = NULL;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    if (ftopd(file) != NULL)
+	hidden_file = ftohf(file);
+
+    if (!file->f_op || !file->f_op->mmap) {
+	err = -ENODEV;
+	goto out;
+    }
+
+    PASSERT(hidden_file);
+    PASSERT(hidden_file->f_op);
+    PASSERT(hidden_file->f_op->mmap);
+
+    vma->vm_file = hidden_file;
+    err = hidden_file->f_op->mmap(hidden_file, vma);
+    get_file(hidden_file); /* make sure it doesn't get freed on us */
+    fput(file);			/* no need to keep extra ref on ours */
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_open(inode_t *inode, file_t *file)
+{
+    int err = 0;
+    int hidden_flags;
+    file_t *hidden_file = NULL;
+    dentry_t *hidden_dentry = NULL;
+    dentry_t *dentry = NULL;
+    super_block_t *sb;
+    int bindex = 0, bstart = 0, bend = 0;
+
+    print_entry_location();
+    ftopd_lhs(file) = KMALLOC(sizeof(struct unionfs_file_info), GFP_UNIONFS);
+    if (!ftopd(file)) {
+	err = -ENOMEM;
+	goto out;
+    }
+    init_ftopd(file);
+    atomic_set(&ftopd(file)->ufi_generation, atomic_read(&itopd(inode)->uii_generation));
+    ftohf_ptr(file) = KMALLOC((sizeof(file_t *) * sbmax(inode->i_sb)), GFP_UNIONFS);
+    if (!ftohf_ptr(file)) {
+        err = -ENOMEM;
+        goto out;
+    }
+    init_ftohf_ptr(file, sbmax(inode->i_sb));
+
+    hidden_flags = file->f_flags;
+
+    dentry = file->f_dentry;
+    PASSERT(dentry);
+
+    bstart = fbstart(file) = dbstart(dentry);
+    bend = fbend(file) = dbend(dentry);
+
+    sb = inode->i_sb;
+
+    /* open all directories and make the unionfs file struct point to these hidden file structs */
+    if (S_ISDIR(inode->i_mode)) {
+
+	for (bindex = bstart; bindex <= bend; bindex++) {
+
+            hidden_dentry = dtohd_index(dentry, bindex);
+	    if (!hidden_dentry) {
+		continue;
+	    }
+
+	    dget(hidden_dentry);
+	    /*
+	     * dentry_open will decrement mnt refcnt if err.
+	     * otherwise fput() will do an mntput() for us upon file close.
+	     */
+	    mntget(stohiddenmnt_index(sb, bindex));
+	    branchget(sb, bindex);
+	    hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bindex), hidden_flags);
+	    if (IS_ERR(hidden_file)) {
+		err =  PTR_ERR(hidden_file);
+		goto out;
+	    }
+
+	    ftohf_index(file, bindex) = hidden_file;	/* link two files */
+	}
+    } else {
+	/* open a file */
+	hidden_dentry = dtohd(dentry);
+	PASSERT(hidden_dentry);
+
+        /* check for the permission for hidden file.  If the error is COPYUP_ERR,
+         * copyup the file.
+         */
+        if (hidden_dentry->d_inode) {
+            /* if this is file is on RO branch, copyup the file */
+	    if (IS_WRITE_FLAG(hidden_flags) && is_robranch(dentry)) {
+		int size = file->f_dentry->d_inode->i_size;
+		if (hidden_flags & O_TRUNC) {
+			size = 0;
+		}
+                /* copyup the file */
+                for (bindex = bstart - 1; bindex >= 0; bindex--) {
+		    err = unionfs_copyup_file(dentry->d_parent->d_inode, file, bstart, bindex, size);
+		    if (!err) {
+                        break;
+                    }
+                }
+                goto out;
+            }
+        }
+
+        dget(hidden_dentry);
+	/* dentry_open will decrement mnt refcnt if err.
+         * otherwise fput() will do an mntput() for us upon file close.
+	 */
+	mntget(stohiddenmnt_index(sb, bstart));
+	branchget(sb, bstart);
+	hidden_file = dentry_open(hidden_dentry, stohiddenmnt_index(sb, bstart), hidden_flags);
+	if (IS_ERR(hidden_file)) {
+	    err =  PTR_ERR(hidden_file);
+	    goto out;
+	} else {
+	    ftohf(file) = hidden_file;	/* link two files */
+	}
+    }
+
+out:
+    /* freeing the allocated resources, and fput the opened files */
+    if (err < 0 && ftopd(file)) {
+	for (bindex = bstart; bindex <= bend; bindex++) {
+	    hidden_file  = ftohf_index(file, bindex);
+	    if (hidden_file) {
+		branchput(file->f_dentry->d_sb, bindex);
+		/* fput calls dput for hidden_dentry */
+		fput(hidden_file);
+	    }
+	}
+	if (ftohf_ptr(file)) {
+	    KFREE(ftohf_ptr(file));
+	}
+	KFREE(ftopd(file));
+    }
+
+    fist_print_file("OUT: unionfs_open", file);
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_flush(file_t *file)
+{
+    int err = 0;		/* assume ok (see open.c:close_fp) */
+    file_t *hidden_file = NULL;
+    int bindex, bstart, bend;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    bstart = fbstart(file);
+    bend = fbend(file);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+	hidden_file  = ftohf_index(file, bindex);
+	if (hidden_file && hidden_file->f_op && hidden_file->f_op->flush) {
+	    err = hidden_file->f_op->flush(hidden_file);
+	    if (err) {
+		goto out;
+	    }
+	}
+    }
+
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_release(inode_t *inode, file_t *file)
+{
+    int err = 0;
+    file_t *hidden_file = NULL;
+    int bindex, bstart, bend;
+
+    print_entry_location();
+
+    fist_checkinode(inode, "unionfs_release");
+
+    /* fput all the hidden files */
+    bstart = fbstart(file);
+    bend = fbend(file);
+
+    for (bindex = bstart; bindex <= bend; bindex++) {
+      hidden_file = ftohf_index(file, bindex);
+
+      if (hidden_file) {
+        /*
+         * will decrement file refcount, and if 0, destroy the file,
+         * which will call the lower file system's file release function.
+         */
+        fput(hidden_file);
+      }
+
+      branchput(inode->i_sb, bindex);
+    }
+    KFREE(ftohf_ptr(file));
+
+    if (ftopd(file)->rdstate) {
+	fist_dprint(8, "Saving rdstate with key %lu [%d.%d]\n", ftopd(file)->rdstate->uds_key, ftopd(file)->rdstate->uds_bindex, ftopd(file)->rdstate->uds_dirpos);
+	spin_lock(&itopd(inode)->uii_rdlock);
+	itopd(inode)->uii_rdcount++;
+	list_add_tail(&ftopd(file)->rdstate->uds_cache, &itopd(inode)->uii_readdircache);
+	mark_inode_dirty(inode);
+	spin_unlock(&itopd(inode)->uii_rdlock);
+	ftopd(file)->rdstate = NULL;
+    }
+    KFREE(ftopd(file));
+
+    fist_checkinode(inode, "post unionfs_release");
+
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_fsync(file_t *file, dentry_t *dentry, int datasync)
+{
+    int err = -EINVAL;
+    file_t *hidden_file = NULL;
+    dentry_t *hidden_dentry;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    if (ftopd(file) != NULL)
+	hidden_file = ftohf(file);
+    hidden_dentry = dtohd(dentry);
+
+    if (hidden_file->f_op && hidden_file->f_op->fsync) {
+	down(&hidden_dentry->d_inode->i_sem);
+	err = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync);
+	up(&hidden_dentry->d_inode->i_sem);
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_fasync(int fd, file_t *file, int flag)
+{
+    int err = 0;
+    file_t *hidden_file = NULL;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    if (ftopd(file) != NULL)
+	hidden_file = ftohf(file);
+
+    if (hidden_file->f_op && hidden_file->f_op->fasync)
+	err = hidden_file->f_op->fasync(fd, hidden_file, flag);
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_lock(file_t *file, int cmd, struct file_lock *fl)
+{
+    int err = 0;
+    file_t *hidden_file = NULL;
+
+    print_entry_location();
+
+    if ((err = unionfs_file_revalidate(file))) {
+	goto out;
+    }
+
+    if (ftopd(file) != NULL)
+	hidden_file = ftohf(file);
+
+    if (hidden_file->f_op->lock) {
+	err = hidden_file->f_op->lock(hidden_file, F_GETLK, fl);
+    } else {
+	posix_test_lock(hidden_file, fl);
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+/* Trimmed directory options, we shouldn't pass everything down since
+ * we don't want to operate on partial directories.
+ */
+struct file_operations unionfs_dir_fops =
+{
+    llseek:	unionfs_dir_llseek,
+    read:	generic_read_dir,
+    readdir:	unionfs_readdir,
+    ioctl:	unionfs_ioctl,
+    open:	unionfs_open,
+    release:	unionfs_release,
+    flush:	unionfs_flush,
+};
+
+struct file_operations unionfs_main_fops =
+{
+    llseek:	unionfs_llseek,
+    read:	unionfs_read,
+    write:	unionfs_write,
+    readdir:	unionfs_file_readdir,
+    poll:	unionfs_poll,
+    ioctl:	unionfs_ioctl,
+    mmap:	unionfs_mmap,
+    open:	unionfs_open,
+    flush:	unionfs_flush,
+    release:	unionfs_release,
+    fsync:	unionfs_fsync,
+    fasync:	unionfs_fasync,
+    lock:	unionfs_lock,
+    /* not needed: readv */
+    /* not needed: writev */
+    /* not implemented: sendpage */
+    /* not implemented: get_unmapped_area */
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ * vim:shiftwidth=4
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/fist.h linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/fist.h
--- linux-2.6.12-rc2-mm3/fs/unionfs/fist.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/fist.h	2005-04-25 11:56:14.535656328 -0700
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: fist.h,v 1.39 2005/02/08 15:17:38 cwright Exp $
+ */
+
+#ifndef __FIST_H_
+#define __FIST_H_
+
+/*
+ * KERNEL ONLY CODE:
+ */
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#ifdef CONFIG_MODVERSIONS
+# define MODVERSIONS
+# include <linux/modversions.h>
+#endif /* CONFIG_MODVERSIONS */
+#endif /* KERNEL_VERSION < 2.6.0 */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/limits.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <linux/locks.h>
+#else
+#include <linux/buffer_head.h>
+#include <linux/pagemap.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/page-flags.h>
+#include <linux/writeback.h>
+#include <linux/page-flags.h>
+#include <linux/statfs.h>
+#include "missing_vfs_funcs.h"
+#endif
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/poll.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
+#include <linux/xattr.h>
+#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+#include <linux/security.h>
+#endif
+
+#include <linux/swap.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/mman.h>
+#include <linux/seq_file.h>
+
+/*
+ * MACROS:
+ */
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif /* not SEEK_SET */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif /* not SEEK_CUR */
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif /* not SEEK_END */
+
+
+#ifndef DEFAULT_POLLMASK
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+#endif
+
+/*
+ * TYPEDEFS:
+ */
+typedef struct dentry dentry_t;
+typedef struct file file_t;
+typedef struct inode inode_t;
+typedef inode_t vnode_t;
+typedef struct page page_t;
+typedef struct qstr qstr_t;
+typedef struct super_block super_block_t;
+typedef super_block_t vfs_t;
+typedef struct vfsmount vfs_mount_t;
+typedef struct vm_area_struct vm_area_t;
+
+/*
+ * EXTERNALS:
+ */
+
+#ifdef FIST_MALLOC_DEBUG
+extern void *unionfs_kmalloc(size_t len, int flag, int line, const char *file);
+extern void unionfs_kfree(void *ptr, int line, const char *file);
+#define KMALLOC(size,flag) unionfs_kmalloc((size),(flag),__LINE__,__FILE__)
+#define KFREE(ptr) unionfs_kfree((ptr),__LINE__,__FILE__)
+#else /* not FIST_MALLOC_DEBUG */
+# define KMALLOC(a,b)	kmalloc((a),(b))
+# define KFREE(a)	kfree((a))
+#endif /* not FIST_MALLOC_DEBUG */
+
+extern int fist_get_debug_value(void);
+extern int fist_set_debug_value(int val);
+extern void fist_dprint_internal(const char *file, const char *function, int line, int level, char *str,...) __attribute__ ((format (__printf__, 5, 6)));
+
+extern void fist_print_dentry(char *str, const dentry_t *dentry);
+extern void fist_print_inode(char *str, const inode_t *inode);
+extern void fist_print_file(char *str, const file_t *file);
+extern void fist_checkinode(inode_t *inode, char *msg);
+extern void fist_print_sb(char *str, const super_block_t *sb);
+
+extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer);
+extern void fist_print_page_flags(char *str, page_t *page);
+extern void fist_print_pte_flags(char *str, const page_t *page);
+extern char *add_indent(void);
+extern char *del_indent(void);
+
+/* The poison pointer.  This needs to be changed on an ia64. */
+#define POISON    ((void *)0x5a5a5a5a)
+/* Used where we want poisoning, but distinct from 5a5a5a5a. */
+#define EXPLOSIVE ((void *)0xc4c4c4c4)
+
+
+#define STATIC
+
+#define WHEREAMI() \
+do { \
+	printk("HERE: %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); \
+} while (0)
+
+#ifndef NODEBUG
+
+/* Call if you encounter a bug. */
+#define FISTBUG(msg) \
+do { \
+	printk("<0>FISTBUG at %s:%s:%d %s", __FILE__, __FUNCTION__, __LINE__, msg); \
+	(*((char *)0))=0;	\
+} while (0);
+
+/* The if (0 ...) is so that we can make sure that you don't pass this
+ * define a non-pointer.  gcc should optimize it away. */
+#define PASSERT(EX)	\
+do {	\
+    void *p = (void *)(EX);\
+    if (0 && ((EX) == (void *)4)) { /* do nothing */ }; \
+    if (!(p) || (p == POISON)) {	\
+	printk(KERN_CRIT "ASSERTION FAILED: %s %s at %s:%d (%s)\n", #EX,	\
+	        (p == POISON) ? "(poisoned)" : "(null)", \
+                __FILE__, __LINE__, __FUNCTION__);	\
+	(*((char *)0))=0;	\
+    }	\
+} while (0)
+
+/* The if (0 ...) is so that we can make sure that you don't pass this
+ * define a non-pointer.  gcc should optimize it away. */
+/* same PASSERT, but tell me who was the caller of the function */
+#define PASSERT2(EX)	\
+do {	\
+    void *p = (void *)(EX);\
+    if (0 && ((EX) ==  (void *)4)) { /* do nothing */ }; \
+    if (!(p) || (p == POISON)) {	\
+	printk(KERN_CRIT "ASSERTION FAILED %s %s at %s:%d (%s) called by %s:%d (%s)\n", #EX, (p == POISON) ? "(poisoned)" : "(null)",	\
+	       __FILE__, __LINE__, __FUNCTION__, file, line, function);\
+	(*((char *)0))=0;	\
+    }	\
+} while (0)
+
+/* The if (0 ...) is so that we can make sure that you don't pass this
+ * define a pointer.  gcc should optimize it away. */
+#define ASSERT(EX)	\
+do {	\
+    if (0 && ((EX) == 1)) { /* do nothing */ }; \
+    if ((EX) == 0) {	\
+	printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX,	\
+	       __FILE__, __LINE__, __FUNCTION__);	\
+	(*((char *)0))=0;	\
+    }	\
+} while (0)
+
+/* same ASSERT, but tell me who was the caller of the function */
+#define ASSERT2(EX)	\
+do {	\
+    if (0 && ((EX) == 1)) { /* do nothing */ }; \
+    if ((EX) == 0) {	\
+	printk(KERN_CRIT "ASSERTION FAILED %s at %s:%d (%s) called by %s:%d (%s)\n", #EX,	\
+	       __FILE__, __LINE__, __FUNCTION__, file, line, function);\
+	(*((char *)0))=0;	\
+    }	\
+} while (0)
+
+#define fist_dprint(level, str, args...) fist_dprint_internal(__FILE__, __FUNCTION__, __LINE__, level, KERN_DEBUG str, ## args)
+#define print_entry(format, args...) fist_dprint(4, "%sIN:  %s %s:%d " format "\n", add_indent(), __FUNCTION__, __FILE__, __LINE__, ##args)
+#define print_entry_location() fist_dprint(4, "%sIN:  %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__)
+#define print_exit_location() fist_dprint(5, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__)
+#define print_exit_status(status) fist_dprint(5, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status)
+#define print_exit_pointer(status) \
+do { \
+  if (IS_ERR(status)) \
+    fist_dprint(5, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \
+  else \
+    fist_dprint(5, "%s OUT: %s %s:%d, RESULT: 0x%p\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status); \
+} while (0)
+
+#define print_util_entry(format, args...) fist_dprint(6, "%sIN:  %s %s:%d" format "\n", add_indent(), __FUNCTION__, __FILE__, __LINE__, ##args)
+#define print_util_entry_location() fist_dprint(6, "%sIN:  %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__)
+#define print_util_exit_location() fist_dprint(7, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__)
+#define print_util_exit_status(status) fist_dprint(7, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status)
+#define print_util_exit_pointer(status) \
+do { \
+  if (IS_ERR(status)) \
+    fist_dprint(7, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \
+  else \
+    fist_dprint(5, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \
+} while (0)
+
+#else
+#define ASSERT(ex)
+#define ASSERT2(ex)
+#define PASSERT(ex)
+#define PASSERT2(ex)
+#define FISTBUG(args...)
+#define fist_dprint(args...)
+#define print_entry(args...)
+#define print_entry_location()
+#define print_exit_location()
+#define print_exit_status(status)
+#define print_exit_pointer(status)
+#define print_util_entry(args...)
+#define print_util_entry_location()
+#define print_util_exit_location()
+#define print_util_exit_status(status)
+#define print_util_exit_pointer(status)
+#endif
+
+
+#ifndef list_for_each_entry
+/* This is stolen from 2.4.21 linux/list.h:223 */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+#endif
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * DEFINITIONS FOR USER AND KERNEL CODE:
+ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest
+ *  are auto-generated automatically based on the user's .fist file.)
+ */
+# define FIST_IOCTL_GET_DEBUG_VALUE	_IOR(0x15, 1, int)
+# define FIST_IOCTL_SET_DEBUG_VALUE	_IOW(0x15, 2, int)
+# define UNIONFS_IOCTL_BRANCH_COUNT	_IOR(0x15, 10, int)
+# define UNIONFS_IOCTL_INCGEN		_IOR(0x15, 11, int)
+# define UNIONFS_IOCTL_ADDBRANCH	_IOW(0x15, 12, int)
+# define UNIONFS_IOCTL_DELBRANCH	_IOW(0x15, 13, int)
+# define UNIONFS_IOCTL_RDWRBRANCH	_IOW(0x15, 14, int)
+# define UNIONFS_IOCTL_SUPERDUPER	_IOW(0x15, 15, int)
+
+# define UNIONFS_EAFUNC_CLASS		"unionfs."
+# define UNIONFS_EAFUNC_DELBRANCH	"delbranch"
+
+struct unionfs_addbranch_args {
+	unsigned int ab_branch;
+	char *ab_path;
+	unsigned int ab_perms;
+};
+
+struct unionfs_rdwrbranch_args {
+	unsigned int rwb_branch;
+	unsigned int rwb_perms;
+};
+
+#endif /* not __FIST_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/inode.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/inode.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/inode.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/inode.c	2005-04-25 11:56:14.540655568 -0700
@@ -0,0 +1,2599 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: inode.c,v 1.189 2005/03/02 13:30:13 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+
+STATIC int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+unionfs_create(inode_t *parent, dentry_t *dentry, int mode)
+#else
+unionfs_create(inode_t *parent, dentry_t *dentry, int mode, struct nameidata *nd)
+#endif
+{
+    int err = 0;
+    dentry_t *hidden_dentry = NULL;
+    dentry_t *whiteout_dentry = NULL;
+    dentry_t *new_hidden_dentry;
+    dentry_t *hidden_parent_dentry = NULL;
+    int bindex = 0, bstart;
+    char *name = NULL;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_create :", dentry);
+
+    /* We start out in the leftmost branch. */
+    bstart = dbstart(dentry);
+    hidden_dentry = dtohd(dentry);
+
+    /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[4 + dentry->d_name.len] = '\0';
+
+    whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent, dentry->d_name.len + 4);
+    if (IS_ERR(whiteout_dentry)) {
+	err = PTR_ERR(whiteout_dentry);
+	goto out;
+    }
+    PASSERT(whiteout_dentry);
+
+    if (whiteout_dentry->d_inode) {
+	/* .wh.foo has been found. */
+	/* First truncate it and then rename it to foo (hence having the same
+	 * overall effect as a normal create.
+	 *
+	 * XXX: This is not strictly correct.  If we have unlinked the file and
+	 * XXX: it still has a reference count, then we should actually unlink
+	 * XXX: the whiteout so that user's data isn't hosed over.
+	 *
+	 */
+	dentry_t *hidden_dir_dentry;
+	struct iattr newattrs;
+
+	down(&whiteout_dentry->d_inode->i_sem);
+	newattrs.ia_valid = ATTR_CTIME;
+        if (whiteout_dentry->d_inode->i_size != 0) {
+	    newattrs.ia_valid |= ATTR_SIZE;
+	    newattrs.ia_size = 0;
+        }
+	err = notify_change(whiteout_dentry, &newattrs);
+	up(&whiteout_dentry->d_inode->i_sem);
+
+        new_hidden_dentry = dtohd(dentry);
+	dget(new_hidden_dentry);
+
+	hidden_dir_dentry = get_parent(whiteout_dentry);
+	/* They are in the same directory, but we need to get it twice. */
+	ASSERT(get_parent(whiteout_dentry) == hidden_dir_dentry);
+	double_lock(hidden_dir_dentry, hidden_dir_dentry);
+
+        if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+            err = vfs_rename(hidden_dir_dentry->d_inode, whiteout_dentry,
+                             hidden_dir_dentry->d_inode, new_hidden_dentry);
+	}
+	if (!err) {
+        	fist_copy_attr_timesizes(parent, new_hidden_dentry->d_parent->d_inode);
+        	parent->i_nlink = get_nlinks(parent);
+	}
+
+	/* This will dput our double gotten parent. */
+	double_unlock(hidden_dir_dentry, hidden_dir_dentry);
+
+	dput(new_hidden_dentry);
+
+	if (err) {
+            if (IS_SET(parent->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* exit if the error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                goto out;
+            }
+            /* We were not able to create the file in this branch,
+             * so, we try to create it in one branch to left
+             */
+	    bstart--;
+	} else {
+	    /* reset the unionfs dentry to point to the .wh.foo entry. */
+
+	    /* Discard any old reference. */
+            PASSERT(dtohd(dentry));
+            dput(dtohd(dentry));
+
+	    /* Trade one reference to another. */
+	    set_dtohd_index(dentry, bstart, whiteout_dentry);
+	    whiteout_dentry = NULL;
+
+            err = unionfs_interpose(dentry, parent->i_sb, 0);
+            goto out;
+	}
+    }
+
+    for (bindex = bstart; bindex >= 0; bindex--) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (!hidden_dentry) {
+	    /* if hidden_dentry is NULL, create the entire
+	     * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when
+	     * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a
+	     * lone negative underlying dentry */
+	    hidden_dentry = unionfs_create_dirs(parent, dentry, bindex);
+	    if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+		if (IS_ERR(hidden_dentry)) {
+		    err = PTR_ERR(hidden_dentry);
+		}
+		continue;
+	    }
+	}
+
+        PASSERT(hidden_dentry);
+	fist_checkinode(parent, "unionfs_create");
+
+	hidden_parent_dentry = lock_parent(hidden_dentry);
+	if (IS_ERR(hidden_parent_dentry)) {
+	    err = PTR_ERR(hidden_parent_dentry);
+	    goto out;
+	}
+	/* We shouldn't create things in a read-only branch. */
+	if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+	    //DQ: vfs_create has a different prototype in 2.6
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	    err = vfs_create(hidden_parent_dentry->d_inode, hidden_dentry, mode);
+#else
+	    err = vfs_create(hidden_parent_dentry->d_inode, hidden_dentry, mode, nd);
+#endif
+
+	}
+	/* XXX this could potentially return a negative hidden_dentry! */
+        if (err || !hidden_dentry->d_inode) {
+            unlock_dir(hidden_parent_dentry);
+
+            if (IS_SET(parent->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        } else {
+            err = unionfs_interpose(dentry, parent->i_sb, 0);
+            if (!err) {
+                fist_copy_attr_timesizes(parent, hidden_parent_dentry->d_inode);
+                /* update number of links on parent directory */
+                parent->i_nlink = get_nlinks(parent);
+            }
+            unlock_dir(hidden_parent_dentry);
+            break;
+        }
+    } // end for
+
+ out:
+    if (whiteout_dentry) {
+	fist_dprint(8, "whiteout_dentry: %p\n", whiteout_dentry);
+	dput(whiteout_dentry);
+    }
+    if (name) {
+	fist_dprint(8, "name: %p\n", name);
+	KFREE(name);
+    }
+
+    fist_print_dentry("OUT unionfs_create :", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+dentry_t * unionfs_lookup_backend(inode_t *parent, dentry_t *dentry, int interpose_flag)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = NULL;
+    dentry_t *wh_hidden_dentry = NULL;
+    dentry_t *hidden_dir_dentry = NULL;
+    dentry_t *parent_dentry;
+    unsigned int namelen;
+    int bindex, bstart, bend;
+    int dentry_count;
+    int is_first_negative = 0;
+    int first_dentry_offset = -1;
+    dentry_t *first_hidden_dentry;
+    char *name = NULL;
+
+    print_entry_location();
+    PASSERT(dentry);
+    /* No dentries should get created for possible whiteout names. */
+    if (!strncmp(dentry->d_name.name, ".wh.", 4)) {
+	err = -EPERM;
+	goto out;
+    }
+
+    parent_dentry = dentry->d_parent;
+    namelen = dentry->d_name.len;
+    PASSERT(parent_dentry);
+    bstart = dbstart(parent_dentry);
+    bend = dbend(parent_dentry);
+    ASSERT(bstart >= 0);
+
+    fist_print_dentry("IN unionfs_lookup:", dentry);
+    fist_print_inode("IN unionfs_lookup:", parent);
+
+    /* must initialize dentry operations */
+    dentry->d_op = &unionfs_dops;
+
+    /* allocate space for hidden dentries */
+    if (!dtopd_lhs(dentry)) {
+    	dtopd_lhs(dentry) =  (struct unionfs_dentry_info *) KMALLOC(sizeof(struct unionfs_dentry_info), GFP_UNIONFS);
+        if (!dtopd_lhs(dentry)) {
+	   err = -ENOMEM;
+	   goto out;
+        }
+    	init_dtopd(dentry, parent->i_sb);
+    } else {
+	reinit_dtopd(dentry, parent->i_sb);
+    }
+    dtohd_ptr(dentry) = KMALLOC(sizeof(dentry_t *) * sbmax(parent->i_sb), GFP_UNIONFS);
+    if (!dtohd_ptr(dentry)) {
+	err = -ENOMEM;
+	goto out_free_dtopd;
+    }
+    init_dtohd_ptr(dentry, sbmax(parent->i_sb));
+
+    /* these fields are useful when all dentries are negative */
+    dentry_count = 0;      /* stores the number of positive dentries found */
+    is_first_negative = 0;   /* boolean value, to indicate if first dentry was negative */
+    first_hidden_dentry = NULL;  /* pointer to first dentry, either positive or negative */
+
+    for (bindex = bstart; bindex <= bend; bindex++) {
+	hidden_dir_dentry = dtohd_index(parent_dentry, bindex);
+
+	/* if the parent hidden dentry doesn't exists skip this */
+	if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode)) {
+	    continue;
+	}
+
+	PASSERT(hidden_dir_dentry->d_inode);
+	/* also skip it if it isn't a directory. */
+	if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)) {
+	    continue;
+	}
+
+	/* we reuse the whiteout name for the entire function because its value doesn't change. */
+	if (!name) {
+	    name = KMALLOC(namelen + 5, GFP_UNIONFS);
+	    if (!name) {
+		err = -ENOMEM;
+		goto out_free;
+	    }
+	    strcpy(name, ".wh.");
+	    strncat(name, dentry->d_name.name, dentry->d_name.len);
+	    name[4 + dentry->d_name.len] = '\0';
+	}
+
+	/* check if whiteout exists in this branch; lookup .wh.foo first */
+	wh_hidden_dentry = lookup_one_len(name, hidden_dir_dentry, namelen + 4);
+	if (IS_ERR(wh_hidden_dentry)) {
+	    err = PTR_ERR(wh_hidden_dentry);
+	    goto out_free;
+	}
+
+	if (!wh_hidden_dentry->d_inode) {
+	    dput(wh_hidden_dentry);
+	    wh_hidden_dentry = NULL;
+	} else {
+	    /* check to see if whiteout dentry is a directory */
+	    if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) {
+		/* We found a whiteout so lets give up. */
+		dput(wh_hidden_dentry);
+		break;
+	    } else if (S_ISLNK(wh_hidden_dentry->d_inode->i_mode)) {
+		/* This buffer should only be for an integer
+		 * which means it can only be 10 characters. */
+		char buf[12];
+		int grayout;
+		int size;
+
+		if (!wh_hidden_dentry->d_inode->i_op || !wh_hidden_dentry->d_inode->i_op->readlink) {
+			err = -ENOSYS;
+			dput(wh_hidden_dentry);
+			goto out_free;
+		}
+
+    		size = wh_hidden_dentry->d_inode->i_op->readlink(wh_hidden_dentry, buf, sizeof(buf));
+		dput(wh_hidden_dentry);
+		if (size < 0) {
+			err = size;
+			goto out_free;
+		}
+
+		if (size <= 10) {
+			char *end;
+			buf[err] = '\0';
+
+			grayout = simple_strtoul(buf, &end, 0);
+
+			if (*end) {
+				printk("<0>Improperly formatted grayout.\n");
+				err = -EIO;
+			}
+		} else {
+			printk("<0>Improperly formatted grayout.\n");
+			err = -EIO;
+			goto out_free;
+		}
+
+		bindex += grayout;
+		continue;
+	    } else {
+		err = -EIO;
+		printk("<0>EIO: A whiteout entry is an invalid unknown type (%d)!\n", wh_hidden_dentry->d_inode->i_mode);
+		dput(wh_hidden_dentry);
+		goto out_free;
+	    }
+	}
+
+	/* Now do regular lookup; lookup foo */
+	hidden_dentry = lookup_one_len(dentry->d_name.name, hidden_dir_dentry, dentry->d_name.len);
+	if (IS_ERR(hidden_dentry)) {
+	    err = PTR_ERR(hidden_dentry);
+	    goto out_free;
+	}
+
+	if (hidden_dentry->d_inode) {
+	    /* number of positive dentries */
+	    dentry_count++;
+
+	    /* store underlying dentry */
+	    set_dtohd_index(dentry, bindex,  hidden_dentry);
+	    if (dbstart(dentry) == -1) {
+		set_dbstart(dentry, bindex);
+	    }
+	    set_dbend(dentry, bindex);
+
+	    /* update parent directory's atime with the bindex */
+	    fist_copy_attr_atime(parent, hidden_dir_dentry->d_inode);
+
+	    /* if the first hidden dentry is a file, stop the lookup now */
+	    if (!S_ISDIR(hidden_dentry->d_inode->i_mode) && (dentry_count == 1)) {
+		goto out_interpose;
+	    }
+	} else {
+	    /* Store the first negative dentry specially, because if they are all negative
+	     * we need this for future creates. */
+	    if (!is_first_negative && (dbstart(dentry) == -1)) {
+		/* entering only if its _first_ ever negative dentry */
+		first_hidden_dentry = hidden_dentry;
+		first_dentry_offset = bindex;
+		is_first_negative = 1;
+	    } else {
+		dput(hidden_dentry);
+	    }
+
+	    /* If we've only got negative dentries, then lets use the leftmost one. */
+	    if (is_first_negative && (bindex == bend) && (dentry_count == 0)) {
+		set_dtohd_index(dentry, first_dentry_offset, first_hidden_dentry);
+		set_dbstart(dentry, first_dentry_offset);
+		set_dbend(dentry, first_dentry_offset);
+		if (interpose_flag == INTERPOSE_REVAL) {
+			if (dentry->d_inode) {
+				PASSERT(itopd(dentry->d_inode));
+	    			itopd(dentry->d_inode)->uii_stale = 1;
+			}
+		} else if (interpose_flag == INTERPOSE_REVAL_NEG) {
+			/* We don't need to do anything. */
+			ASSERT(dentry->d_inode == NULL);
+		} else {
+			d_add(dentry, NULL);
+		}
+		goto out;
+	    }
+	} // else negative dentry
+    } // end for
+
+out_interpose:
+    /* If we're holding onto the first negative dentry we can throw it out now. */
+    if (is_first_negative) {
+	dput(first_hidden_dentry);
+    }
+
+    /* If we have found a positive dentry. */
+    if (dentry_count) {
+	err = unionfs_interpose(dentry, parent->i_sb, interpose_flag);
+	if (err)
+	    goto out_drop;
+
+	fist_checkinode(dentry->d_inode, "unionfs_lookup OUT: dentry->d_inode:");
+	fist_checkinode(parent, "unionfs_lookup OUT: dir");
+    } else {
+	if (interpose_flag == INTERPOSE_REVAL) {
+	    PASSERT(dentry);
+	    PASSERT(dentry->d_inode);
+	    PASSERT(itopd(dentry->d_inode));
+	    itopd(dentry->d_inode)->uii_stale = 1;
+	} else {
+	    /* If no positive dentries were found, and we didn't go to out because of the
+	     * negative entry path, then we must have found a whiteout */
+	    hidden_dentry = lookup_one_len(dentry->d_name.name, hidden_dir_dentry, dentry->d_name.len);
+	    if (IS_ERR(hidden_dentry)) {
+		err = PTR_ERR(hidden_dentry);
+		goto out;
+	    }
+
+	    set_dtohd_index(dentry, bindex, hidden_dentry);
+	    set_dbstart(dentry, bindex);
+	    set_dbend(dentry, bindex);
+	    /* We don't want to hash our dentry if it is already hashed. */
+	    if (interpose_flag != INTERPOSE_REVAL_NEG) {
+	    	d_add(dentry, NULL);
+	    }
+	}
+    }
+    ASSERT(dbstart(dentry) >= 0);
+    goto out;
+
+out_drop:
+    d_drop(dentry);		/* so that our bad dentry will get destroyed */
+
+out_free:
+    /* should dput all the underlying dentries on error condition */
+    bstart = dbstart(dentry);
+    if (bstart >= 0) {
+    	bend = dbend(dentry);
+    	for (bindex = bstart; bindex <= bend; bindex++) {
+		hidden_dentry = dtohd_index(dentry, bindex);
+		if (hidden_dentry) {
+	    		dput(hidden_dentry);
+		}
+    	}
+    }
+    KFREE(dtohd_ptr(dentry));
+    dtohd_ptr(dentry) = NULL;
+
+out_free_dtopd:
+    KFREE(dtopd(dentry));
+    dtopd_lhs(dentry) = NULL;	/* be safe */
+
+out:
+    if (dtopd(dentry)) {
+	ASSERT(dbend(dentry) <= dtopd(dentry)->udi_bcount);
+	ASSERT(dbend(dentry) <= sbmax(dentry->d_sb));
+	ASSERT(dbstart(dentry) >= 0);
+    }
+    if (name) {
+	KFREE(name);
+    }
+    fist_print_dentry("OUT unionfs_lookup:", dentry);
+    fist_print_inode("OUT unionfs_lookup:", parent);
+    print_exit_status(err);
+    return ERR_PTR(err);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+dentry_t * unionfs_lookup(inode_t *parent, dentry_t *dentry)
+#else
+dentry_t * unionfs_lookup(inode_t *parent, dentry_t *dentry, struct nameidata* nd)
+#endif
+{
+	return unionfs_lookup_backend(parent, dentry, INTERPOSE_LOOKUP);
+}
+
+
+STATIC int
+unionfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+    int err = 0;
+    struct dentry *hidden_old_dentry = NULL;
+    struct dentry *hidden_new_dentry = NULL;
+    struct dentry *hidden_dir_dentry = NULL;
+    struct dentry *whiteout_dentry;
+    char *name = NULL;
+
+    print_entry_location();
+
+    hidden_new_dentry = dtohd(new_dentry);
+
+    /* check if whiteout exists in the branch of new dentry, i.e. lookup .wh.foo first. If present, delete it */
+    name = KMALLOC(sizeof(char) * (new_dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, new_dentry->d_name.name, new_dentry->d_name.len);
+    name[4 + new_dentry->d_name.len] = '\0';
+
+    whiteout_dentry = lookup_one_len(name, hidden_new_dentry->d_parent, new_dentry->d_name.len + 4);
+    if (IS_ERR(whiteout_dentry)) {
+	err = PTR_ERR(whiteout_dentry);
+	goto out;
+    }
+
+    PASSERT(whiteout_dentry);
+
+    if (!whiteout_dentry->d_inode) {
+	dput(whiteout_dentry);
+	whiteout_dentry = NULL;
+    } else {
+	/* found a .wh.foo entry, unlink it and then call vfs_link() */
+	hidden_dir_dentry = lock_parent(whiteout_dentry);
+	if (!(err = is_robranch_super(new_dentry->d_sb,dbstart(new_dentry)))) {
+	    err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry);
+	}
+	dput(whiteout_dentry);
+
+	if (!err) {
+	    d_delete(whiteout_dentry);
+	} else {
+	    fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	    unlock_dir(hidden_dir_dentry);
+	    goto out;
+	}
+
+	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	/* propagate number of hard-links */
+	dir->i_nlink = get_nlinks(dir);
+	unlock_dir(hidden_dir_dentry);
+    }
+
+    if (dbstart(old_dentry) != dbstart(new_dentry)) {
+	if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP)) {
+	    err = -EXDEV;
+	    goto out;
+	}
+
+	/* if GLOBAL_ERR_PASSUP is set then create destination path in source's branch */
+	hidden_new_dentry = unionfs_create_dirs(dir, new_dentry, dbstart(old_dentry));
+	err = PTR_ERR(hidden_new_dentry);
+	if (IS_COPYUP_ERR(err)) {
+	    goto docopyup;
+	}
+	if (!hidden_new_dentry || IS_ERR(hidden_new_dentry)) {
+	    goto out;
+	}
+    }
+    hidden_new_dentry = dtohd(new_dentry);
+    hidden_old_dentry = dtohd(old_dentry);
+
+    ASSERT (dbstart(old_dentry) == dbstart(new_dentry));
+    dget(hidden_old_dentry);
+    hidden_dir_dentry = lock_parent(hidden_new_dentry);
+
+    if (!(err = is_robranch(old_dentry))) {
+	err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry);
+    }
+docopyup:
+    if (IS_COPYUP_ERR(err)) {
+	int old_bstart = dbstart(old_dentry);
+	int bindex;
+
+    	if (hidden_dir_dentry) {
+		unlock_dir(hidden_dir_dentry);
+		hidden_dir_dentry = NULL;
+	}
+    	if (hidden_old_dentry) {
+		dput(hidden_old_dentry);
+		hidden_old_dentry = NULL;
+	}
+
+	for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
+	    err = unionfs_copyup_dentry_len(old_dentry->d_parent->d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size);
+	    if (!err) {
+		hidden_new_dentry = unionfs_create_dirs(dir, new_dentry, bindex);
+		hidden_old_dentry = dtohd(old_dentry);
+		dget(hidden_old_dentry);
+		hidden_dir_dentry = lock_parent(hidden_new_dentry);
+		/* do vfs_link */
+		err = vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, hidden_new_dentry);
+		goto check_link;
+	    }
+	}
+	goto out_lock;
+    }
+check_link:
+    if (err || !hidden_new_dentry->d_inode) {
+	goto out_lock;
+    }
+
+    /* Its a hard link, so use the same inode */
+    new_dentry->d_inode=old_dentry->d_inode;
+    atomic_inc(&new_dentry->d_inode->i_count);
+    /* FIXME: Update c_time & co ? */
+
+    err = unionfs_interpose(new_dentry, dir->i_sb, INTERPOSE_LINK);
+
+    /*  if (err)
+	goto out_lock;*/
+    fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode);
+    /* propagate number of hard-links */
+    old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode);
+
+out_lock:
+    if (hidden_dir_dentry) unlock_dir(hidden_dir_dentry);
+    if (hidden_old_dentry) dput(hidden_old_dentry);
+
+out:
+    if (!new_dentry->d_inode)
+	d_drop(new_dentry);
+
+    if (name)
+	KFREE(name);
+
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+unionfs_unlink_first(inode_t *dir, dentry_t *dentry)
+{
+	int err;
+	dentry_t *hidden_dentry;
+	dentry_t *hidden_dir_dentry = NULL;
+
+	print_entry_location();
+
+	hidden_dentry = dtohd(dentry);
+	PASSERT(hidden_dentry);
+
+	hidden_dir_dentry = lock_parent(hidden_dentry);
+
+        /* avoid destroying the hidden inode if the file is in use */
+        dget(hidden_dentry);
+	if (!(err = is_robranch(dentry))) {
+        	err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
+	}
+        dput(hidden_dentry);
+
+        if (!err) {	/* vfs_unlink does that */
+	    d_delete(hidden_dentry);
+	}
+
+	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	/* propagate number of hard-links */
+	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+
+        if (hidden_dir_dentry) {
+		unlock_dir(hidden_dir_dentry);
+	}
+	print_exit_status(err);
+	return err;
+}
+
+STATIC int
+unionfs_unlink_all(inode_t *dir, dentry_t *dentry)
+{
+	struct dentry *hidden_dentry;
+	struct dentry *hidden_dir_dentry;
+ 	int bstart, bend, bindex;
+	int err = 0;
+	int global_err = 0;
+
+	print_entry_location();
+
+	err = unionfs_partial_lookup(dentry);
+	if (err) {
+	    fist_dprint(8, "Error in partial lookup\n");
+	    goto out;
+	}
+
+	bstart = dbstart(dentry);
+	bend = dbend(dentry);
+
+	for (bindex = bend; bindex >= bstart; bindex--) {
+	    hidden_dentry = dtohd_index(dentry, bindex);
+	    if (!hidden_dentry)
+		continue;
+
+	    hidden_dir_dentry = lock_parent(hidden_dentry);
+
+	    /* avoid destroying the hidden inode if the file is in use */
+	    dget(hidden_dentry);
+	    if(!(err = is_robranch_super(dentry->d_sb, bindex))) {
+	    	err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
+	    }
+	    dput(hidden_dentry);
+	    fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	    if (err) {
+	    	if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP) || !IS_COPYUP_ERR(err)) {
+			/* passup the last error we got */
+	                unlock_dir(hidden_dir_dentry);
+                	goto out;
+		}
+		global_err = err;
+	    } else {		/* since it was OK, we kill the hidden dentry. */
+		d_delete(hidden_dentry);
+		if (bindex != bstart) {
+			dput(hidden_dentry);
+			set_dtohd_index(dentry, bindex, NULL);
+		}
+	    }
+
+	    unlock_dir(hidden_dir_dentry);
+        }
+
+	/* check if encountered error in the above loop */
+	if (global_err) {
+		/* If we failed in the leftmost branch, then err will be set and we should
+		 * move one over to create the whiteout.  Otherwise, we should try in the
+		 * leftmost branch.
+		 */
+		if (err) {
+		    if (dbstart(dentry) == 0) {
+			goto out;
+		    }
+		    err = create_whiteout(dentry, dbstart(dentry) - 1);
+		} else {
+		    err = create_whiteout(dentry, dbstart(dentry));
+		}
+	}
+
+out:
+	/* propagate number of hard-links */
+	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+
+	print_exit_status(err);
+	return err;
+}
+
+STATIC int
+unionfs_unlink_whiteout(inode_t *dir, dentry_t *dentry)
+{
+    int err = 0;
+    struct dentry *hidden_old_dentry;
+    struct dentry *hidden_wh_dentry = NULL;
+    struct dentry *hidden_old_dir_dentry, *hidden_new_dir_dentry;
+    char *name = NULL;
+//    struct iattr newattrs;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_unlink_whiteout: ", dentry);
+
+    /* create whiteout, get the leftmost underlying dentry and rename it */
+    hidden_old_dentry = dtohd(dentry);
+
+    /* lookup .wh.foo first, MUST NOT EXIST */
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+        err = -ENOMEM;
+        goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[4 + dentry->d_name.len] = '\0';
+
+    hidden_wh_dentry = lookup_one_len(name, hidden_old_dentry->d_parent, dentry->d_name.len + 4);
+    if (IS_ERR(hidden_wh_dentry)) {
+        err = PTR_ERR(hidden_wh_dentry);
+        goto out;
+    }
+    ASSERT(hidden_wh_dentry->d_inode == NULL);
+
+    dget(hidden_old_dentry);
+
+    hidden_old_dir_dentry = get_parent(hidden_old_dentry);
+    hidden_new_dir_dentry = get_parent(hidden_wh_dentry);
+    double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+    if(!(err = is_robranch(dentry))) {
+    	err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
+			 hidden_new_dir_dentry->d_inode, hidden_wh_dentry);
+    }
+
+    double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+    dput(hidden_old_dentry);
+    dput(hidden_wh_dentry);
+
+    if (err) {
+	if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP) || (dbstart(dentry) == 0)) {
+	    goto out;
+	}
+        /* exit if the error returned was NOT -EROFS */
+        if (!IS_COPYUP_ERR(err)) {
+            goto out;
+        }
+	err = create_whiteout(dentry, dbstart(dentry) - 1);
+    } else {
+        fist_copy_attr_all(dir, hidden_new_dir_dentry->d_inode);
+    }
+
+out:
+    if (name) {
+        KFREE(name);
+    }
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+unionfs_unlink(inode_t *dir, dentry_t *dentry)
+{
+    int err = 0;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_unlink: ", dentry);
+
+    dget(dentry);
+
+    if (IS_SET(dir->i_sb, DELETE_WHITEOUT)) {
+        /* create whiteout */
+        err = unionfs_unlink_whiteout(dir, dentry);
+    } else if (IS_SET(dir->i_sb, DELETE_FIRST)) {
+        /* delete only first file */
+	err = unionfs_unlink_first(dir, dentry);
+	/* The VFS will kill this dentry now, and it will end up being recreated on lookup. */
+    } else {
+	/* delete all. */
+	err = unionfs_unlink_all(dir, dentry);
+    }
+
+    /* call d_drop so the system "forgets" about us */
+    if (!err) {
+	d_drop(dentry);
+    }
+    dput(dentry);
+
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, struct unionfs_dir_state *namelist)
+{
+	int err;
+	dentry_t *hidden_dentry;
+	dentry_t *hidden_dir_dentry = NULL;
+
+	print_entry_location();
+	fist_print_dentry("IN unionfs_rmdir_first: ", dentry);
+
+	/* Here we need to remove whiteout entries. */
+	err = delete_whiteouts(dentry, dbstart(dentry), namelist);
+	if (err) {
+	    goto out;
+	}
+
+	hidden_dentry = dtohd(dentry);
+	PASSERT(hidden_dentry);
+
+	hidden_dir_dentry = lock_parent(hidden_dentry);
+
+        /* avoid destroying the hidden inode if the file is in use */
+        dget(hidden_dentry);
+	if (!(err = is_robranch(dentry))) {
+        	err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry);
+	}
+        dput(hidden_dentry);
+        if (!err) {	/* vfs_rmdir does that */
+	    d_delete(hidden_dentry);
+	}
+
+	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	/* propagate number of hard-links */
+	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+
+out:
+        if (hidden_dir_dentry) {
+		unlock_dir(hidden_dir_dentry);
+	}
+	fist_print_dentry("OUT unionfs_rmdir_first: ", dentry);
+	print_exit_status(err);
+	return err;
+}
+
+STATIC int
+unionfs_rmdir_all(inode_t *dir, dentry_t *dentry, struct unionfs_dir_state *namelist)
+{
+	struct dentry *hidden_dentry;
+	struct dentry *hidden_dir_dentry;
+ 	int bstart, bend, bindex;
+	int err = 0;
+	int global_err = 0;
+        int try_flag = 0;
+
+	print_entry_location();
+	fist_print_dentry("IN unionfs_rmdir_all: ", dentry);
+
+	err = unionfs_partial_lookup(dentry);
+	if (err) {
+	    fist_dprint(8, "Error in partial lookup\n");
+	    goto out;
+	}
+
+	bstart = dbstart(dentry);
+	bend = dbend(dentry);
+
+	for (bindex = bend; bindex >= bstart; bindex--) {
+
+	    hidden_dentry = dtohd_index(dentry, bindex);
+	    if (!hidden_dentry)
+		continue;
+
+try_rmdir_again:
+	    hidden_dir_dentry = lock_parent(hidden_dentry);
+	    /* avoid destroying the hidden inode if the file is in use */
+	    dget(hidden_dentry);
+	    if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+	    	err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry);
+	    }
+	    dput(hidden_dentry);
+	    fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	    unlock_dir(hidden_dir_dentry);
+	    if (err) {
+		/* passup the last error we got if GLOBAL_ERR_PASSUP is set
+                 or exit if error is (NOT -EROFS and NOT -ENOTEMPTY) */
+		if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP) || (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY)) {
+                    goto out;
+		}
+
+                global_err = err;
+
+                /* If we got ENOTEMPTY in the zeroth branch, we try to remove
+                 * whiteouts, and rmdir again.
+                 */
+                if ((bindex == 0) && (err == -ENOTEMPTY) && !try_flag) {
+                    try_flag = 1;
+                    err = delete_whiteouts(dentry, 0, namelist);
+                    if (!err) {
+                        goto try_rmdir_again;
+                    }
+                }
+	    }
+
+	    if (!err) {		/* since it was OK, we kill the hidden dentry. */
+		d_delete(hidden_dentry);
+	    }
+        }
+
+	/* check if encountered error in the above loop */
+	if (global_err) {
+		/* If we failed in the leftmost branch, then err will be set and we should
+		 * move one over to create the whiteout.  Otherwise, we should try in the
+		 * leftmost branch.
+		 */
+		if (err) {
+		    if (dbstart(dentry) == 0) {
+			goto out;
+		    }
+		    err = create_whiteout(dentry, dbstart(dentry) - 1);
+		} else {
+		    err = create_whiteout(dentry, dbstart(dentry));
+		}
+	}
+
+out:
+	/* propagate number of hard-links */
+	dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
+
+	fist_print_dentry("OUT unionfs_rmdir_all: ", dentry);
+	print_exit_status(err);
+	return err;
+}
+
+STATIC int
+unionfs_rmdir(inode_t *dir, dentry_t *dentry)
+{
+    int err = 0;
+    struct unionfs_dir_state *namelist = NULL;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_rmdir: ", dentry);
+
+    dget(dentry);
+
+    /* check if this unionfs directory is empty or not */
+    err = check_empty(dentry, &namelist);
+    if (err) {
+	goto out;
+    }
+
+    if (IS_SET(dir->i_sb, DELETE_WHITEOUT)) {
+	/* delete just like if we were doing DELETE_FIRST. */
+	err = unionfs_rmdir_first(dir, dentry, namelist);
+        /* create whiteout */
+	if (!err) {
+	    err = create_whiteout(dentry, dbstart(dentry));
+	} else {
+	    int new_err;
+
+	    if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP) || (dbstart(dentry) == 0)) {
+		goto out;
+	    }
+            /* exit if the error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                goto out;
+            }
+	    new_err = create_whiteout(dentry, dbstart(dentry) - 1);
+	    if (new_err != -EEXIST) {
+		err = new_err;
+	    }
+	}
+    } else if (IS_SET(dir->i_sb, DELETE_FIRST)) {
+        /* delete only first directory */
+	err = unionfs_rmdir_first(dir, dentry, namelist);
+	/* The VFS will kill this dentry now, and it will end up being recreated on lookup. */
+    } else {
+	/* delete all. */
+	err = unionfs_rmdir_all(dir, dentry, namelist);
+    }
+
+ out:
+    /* call d_drop so the system "forgets" about us */
+    if (!err) {
+	d_drop(dentry);
+    }
+    dput(dentry);
+
+
+    if (namelist) {
+	free_rdstate(namelist);
+    }
+
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+unionfs_symlink(inode_t *dir, dentry_t *dentry, const char *symname)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = NULL;
+    dentry_t *whiteout_dentry = NULL;
+    dentry_t *hidden_dir_dentry = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+	umode_t	mode;
+#endif
+    int bindex = 0, bstart;
+    char *name = NULL;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_symlink :", dentry);
+
+    /* We start out in the leftmost branch. */
+    bstart = dbstart(dentry);
+
+    hidden_dentry = dtohd(dentry);
+
+    /* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[4 + dentry->d_name.len] = '\0';
+
+    whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent, dentry->d_name.len + 4);
+    if (IS_ERR(whiteout_dentry)) {
+	err = PTR_ERR(whiteout_dentry);
+	goto out;
+    }
+
+    PASSERT(whiteout_dentry);
+    if (!whiteout_dentry->d_inode) {
+        dput(whiteout_dentry);
+        whiteout_dentry = NULL;
+    } else {
+	/* found a .wh.foo entry, unlink it and then call vfs_symlink() */
+	hidden_dir_dentry = lock_parent(whiteout_dentry);
+
+        if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+            err = vfs_unlink(hidden_dir_dentry->d_inode, whiteout_dentry);
+	}
+	dput(whiteout_dentry);
+
+	if (!err)			// vfs_unlink does that
+	   d_delete(whiteout_dentry);
+
+	fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+	/* propagate number of hard-links */
+	dir->i_nlink = get_nlinks(dir);
+
+	unlock_dir(hidden_dir_dentry);
+
+	if (err) {
+	    if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP)) {
+		goto out;
+            }
+	    /* exit if the error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                goto out;
+            }
+            /* should now try to create symlink in the another branch */
+	    bstart--;
+	}
+    }
+
+    /* deleted whiteout if it was present, now do a normal vfs_symlink() with
+       possible recursive directory creation */
+    for (bindex = bstart; bindex >= 0; bindex--) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (!hidden_dentry) {
+	    /* if hidden_dentry is NULL, create the entire
+	     * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when
+	     * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a
+	     * lone negative underlying dentry */
+	    hidden_dentry = unionfs_create_dirs(dir, dentry, bindex);
+	    if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+		if (IS_ERR(hidden_dentry)) {
+		    err = PTR_ERR(hidden_dentry);
+		}
+		fist_dprint(8, "hidden dentry NULL (or error) for bindex = %d\n", bindex);
+		continue;
+	    }
+	}
+
+        PASSERT(hidden_dentry);
+
+	hidden_dir_dentry = lock_parent(hidden_dentry);
+
+	if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+		err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, symname);
+#else
+		mode = S_IALLUGO;
+		err = vfs_symlink(hidden_dir_dentry->d_inode, hidden_dentry, symname, mode);
+#endif
+	}
+        unlock_dir(hidden_dir_dentry);
+
+	if (err || !hidden_dentry->d_inode) {
+            if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP)) {
+		goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        } else {
+            err = unionfs_interpose(dentry, dir->i_sb, 0);
+            if (!err) {
+                fist_copy_attr_timesizes(dir, hidden_dir_dentry->d_inode);
+                /* update number of links on parent directory */
+                dir->i_nlink = get_nlinks(dir);
+            }
+            break;
+        }
+    }
+
+out:
+    if (!dentry->d_inode)
+	d_drop(dentry);
+
+    if (name) {
+	KFREE(name);
+    }
+    fist_print_dentry("OUT unionfs_symlink :", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+
+STATIC int
+unionfs_mkdir(inode_t *parent, dentry_t *dentry, int mode)
+{
+    int err = 0;
+    dentry_t *hidden_dentry = NULL, *whiteout_dentry = NULL;
+    dentry_t *hidden_parent_dentry = NULL;
+    int bindex = 0, bstart;
+    char *name = NULL;
+    int whiteout_unlinked = 0;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_mkdir :", dentry);
+    bstart = dbstart(dentry);
+
+    hidden_dentry = dtohd(dentry);
+
+    // check if whiteout exists in this branch, i.e. lookup .wh.foo first
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[4 + dentry->d_name.len] = '\0';
+
+    whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent, dentry->d_name.len + 4);
+    if (IS_ERR(whiteout_dentry)) {
+	err = PTR_ERR(whiteout_dentry);
+	goto out;
+    }
+    PASSERT(whiteout_dentry);
+
+    if (!whiteout_dentry->d_inode) {
+        dput(whiteout_dentry);
+        whiteout_dentry = NULL;
+    } else {
+        /* found .wh.foo, unlink it */
+	hidden_parent_dentry = lock_parent(whiteout_dentry);
+
+	//found a.wh.foo entry, remove it then do vfs_mkdir
+    	if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+	    err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry);
+	}
+	dput(whiteout_dentry);
+
+        unlock_dir(hidden_parent_dentry);
+
+        if (err) {
+            /* error in unlink of whiteout */
+            if (IS_SET(parent->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* exit if the error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                goto out;
+            }
+	    bstart--;
+        } else {
+            whiteout_unlinked = 1;
+        }
+    }
+
+    for (bindex = bstart; bindex >= 0; bindex--) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (!hidden_dentry) {
+	    hidden_dentry = unionfs_create_dirs(parent, dentry, bindex);
+	    if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+		fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex);
+		continue;
+	    }
+        }
+
+	PASSERT(hidden_dentry);
+
+	hidden_parent_dentry = lock_parent(hidden_dentry);
+	if (IS_ERR(hidden_parent_dentry)) {
+	    err = PTR_ERR(hidden_parent_dentry);
+	    goto out;
+	}
+	if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+	    err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, mode);
+	}
+	/* XXX this could potentially return a negative hidden_dentry! */
+        if (err || !hidden_dentry->d_inode) {
+            unlock_dir(hidden_parent_dentry);
+
+            if (IS_SET(parent->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        } else {
+            err = unionfs_interpose(dentry, parent->i_sb, 0);
+            if (!err) {
+                fist_copy_attr_timesizes(parent, hidden_parent_dentry->d_inode);
+                /* update number of links on parent directory */
+                parent->i_nlink = get_nlinks(parent);
+            }
+            unlock_dir(hidden_parent_dentry);
+
+            if (whiteout_unlinked == 1) {
+                /* create whiteout entries for directories named foo present to the right */
+	        err = create_dir_whs(dentry, bstart);
+	        if (err) {
+                    fist_dprint(8, "Error creating whiteouts after mkdir success\n");
+                    goto out;
+                }
+            }
+            break;
+        }
+    } // end for
+
+out:
+    if (!dentry->d_inode)
+	d_drop(dentry);
+
+    if (name) {
+	KFREE(name);
+    }
+
+    fist_print_dentry("OUT unionfs_mkdir :", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+//DQ: Change of prototype in 2.6
+STATIC int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+unionfs_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev)
+#else
+unionfs_mknod(inode_t *dir, dentry_t *dentry, int mode, dev_t dev)
+#endif
+{
+    int err = 0;
+    dentry_t *hidden_dentry = NULL, *whiteout_dentry = NULL;
+    dentry_t *hidden_parent_dentry = NULL;
+    int bindex = 0, bstart;
+    char *name = NULL;
+    int whiteout_unlinked = 0;
+
+    print_entry_location();
+    fist_print_dentry("IN unionfs_mknod :", dentry);
+    bstart = dbstart(dentry);
+
+    hidden_dentry = dtohd(dentry);
+
+    // check if whiteout exists in this branch, i.e. lookup .wh.foo first
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[4 + dentry->d_name.len] = '\0';
+
+    whiteout_dentry = lookup_one_len(name, hidden_dentry->d_parent, dentry->d_name.len + 4);
+    if (IS_ERR(whiteout_dentry)) {
+	err = PTR_ERR(whiteout_dentry);
+	goto out;
+    }
+
+    if (!whiteout_dentry->d_inode) {
+        dput(whiteout_dentry);
+        whiteout_dentry = NULL;
+    } else {
+        /* found .wh.foo, unlink it */
+	hidden_parent_dentry = lock_parent(whiteout_dentry);
+
+	//found a.wh.foo entry, remove it then do vfs_mkdir
+        if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+	   err = vfs_unlink(hidden_parent_dentry->d_inode, whiteout_dentry);
+	}
+	dput(whiteout_dentry);
+
+        unlock_dir(hidden_parent_dentry);
+
+        if (err) {
+            /* error in unlink of whiteout */
+            if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* exit if the error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                goto out;
+            }
+
+            bstart--;
+        } else {
+            whiteout_unlinked = 1;
+        }
+    }
+
+    for (bindex = bstart; bindex >= 0; bindex--) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (!hidden_dentry) {
+	    hidden_dentry = unionfs_create_dirs(dir, dentry, bindex);
+	    if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+		fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex);
+		continue;
+	    }
+        }
+
+	PASSERT(hidden_dentry);
+
+	hidden_parent_dentry = lock_parent(hidden_dentry);
+	if (IS_ERR(hidden_parent_dentry)) {
+	    err = PTR_ERR(hidden_parent_dentry);
+	    goto out;
+	}
+	if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+	     err = vfs_mknod(hidden_parent_dentry->d_inode, hidden_dentry, mode, dev);
+	}
+	/* XXX this could potentially return a negative hidden_dentry! */
+        if (err || !hidden_dentry->d_inode) {
+            unlock_dir(hidden_parent_dentry);
+
+            if (IS_SET(dir->i_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        } else {
+            err = unionfs_interpose(dentry, dir->i_sb, 0);
+            if (!err) {
+                fist_copy_attr_timesizes(dir, hidden_parent_dentry->d_inode);
+                /* update number of links on parent directory */
+                dir->i_nlink = get_nlinks(dir);
+            }
+            unlock_dir(hidden_parent_dentry);
+
+	    break;
+        }
+    } // end for
+
+out:
+    if (!dentry->d_inode)
+	d_drop(dentry);
+
+    if (name) {
+	KFREE(name);
+    }
+
+    fist_print_dentry("OUT unionfs_mknod :", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+do_rename(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry, int bindex)
+{
+    int err = 0;
+    dentry_t *hidden_old_dentry;
+    dentry_t *hidden_new_dentry;
+    dentry_t *hidden_old_dir_dentry;
+    dentry_t *hidden_new_dir_dentry;
+    dentry_t *hidden_wh_dentry;
+    dentry_t *hidden_wh_dir_dentry;
+    char *wh_name = NULL;
+
+    print_entry(" bindex=%d", bindex);
+
+    fist_print_dentry("IN: do_rename, old_dentry:", old_dentry);
+    fist_print_dentry("IN: do_rename, new_dentry:", new_dentry);
+    fist_dprint(7, "do_rename for bindex = %d\n", bindex);
+
+    hidden_new_dentry = dtohd_index(new_dentry, bindex);
+    hidden_old_dentry = dtohd_index(old_dentry, bindex);
+    PASSERT(hidden_old_dentry);
+
+    if(!hidden_new_dentry) {
+	hidden_new_dentry = unionfs_create_dirs(new_dentry->d_parent->d_inode, new_dentry, bindex);
+        if (IS_ERR(hidden_new_dentry)) {
+            fist_dprint(7, "error creating directory tree for rename\n");
+	    err = PTR_ERR(hidden_new_dentry);
+            goto out;
+        }
+    }
+
+    wh_name = KMALLOC(new_dentry->d_name.len + 5, GFP_UNIONFS);
+    if (!wh_name) {
+	err = -ENOMEM;
+	goto out;
+    }
+    strcpy(wh_name, ".wh.");
+    strncat(wh_name, new_dentry->d_name.name, new_dentry->d_name.len);
+    wh_name[4 + new_dentry->d_name.len] = '\0';
+
+    hidden_wh_dentry = lookup_one_len(wh_name, hidden_new_dentry->d_parent, new_dentry->d_name.len + 4);
+    if (IS_ERR(hidden_wh_dentry)) {
+	err = PTR_ERR(hidden_wh_dentry);
+        goto out;
+    }
+
+    if (hidden_wh_dentry->d_inode) {
+        /* get rid of the whiteout that is existing */
+        if (hidden_new_dentry->d_inode) {
+	    printk(KERN_WARNING "Both a whiteout and a dentry exist when doing a rename!\n");
+	    err = -EIO;
+
+	    dput(hidden_wh_dentry);
+	    goto out;
+	}
+
+        fist_print_dentry("hidden_wh_dentry->d_parent:", hidden_wh_dentry->d_parent);
+        hidden_wh_dir_dentry = lock_parent(hidden_wh_dentry);
+    	if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) {
+            err = vfs_unlink(hidden_wh_dir_dentry->d_inode, hidden_wh_dentry);
+	}
+	dput(hidden_wh_dentry);
+        unlock_dir(hidden_wh_dir_dentry);
+        if (err) {
+            goto out;
+        }
+        d_delete(hidden_wh_dentry);
+    } else {
+        dput(hidden_wh_dentry);
+    }
+
+    dget(hidden_old_dentry);
+    hidden_old_dir_dentry = get_parent(hidden_old_dentry);
+    hidden_new_dir_dentry = get_parent(hidden_new_dentry);
+
+    double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+    if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) {
+	PASSERT(hidden_old_dir_dentry->d_inode);
+	PASSERT(hidden_old_dentry);
+	PASSERT(hidden_old_dentry->d_inode);
+	PASSERT(hidden_new_dir_dentry->d_inode);
+	PASSERT(hidden_new_dentry);
+    	err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
+            hidden_new_dir_dentry->d_inode, hidden_new_dentry);
+    }
+
+    double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+    dput(hidden_old_dentry);
+
+out:
+    if (!err) {
+        /* Fixup the newdentry. */
+    	if (bindex < dbstart(new_dentry)) {
+	   set_dbstart(new_dentry, bindex);
+    	} else if (bindex > dbend(new_dentry)) {
+	    set_dbend(new_dentry, bindex);
+        }
+    }
+
+    if (wh_name) {
+	KFREE(wh_name);
+    }
+
+    fist_print_dentry("OUT: do_rename, old_dentry:", old_dentry);
+    fist_print_dentry("OUT: do_rename, new_dentry:", new_dentry);
+
+    print_exit_status(err);
+    return err;
+}
+
+STATIC int
+unionfs_rename_whiteout(inode_t *old_dir, dentry_t *old_dentry,
+                        inode_t *new_dir, dentry_t *new_dentry)
+{
+    int err = 0;
+    int bindex;
+    int old_bstart, old_bend;
+    int new_bstart, new_bend;
+    int do_copyup = -1;
+    dentry_t *parent_dentry = NULL;
+    int local_err = 0;
+    int eio = 0;
+    int revert = 0;
+
+    print_entry_location();
+
+    old_bstart = dbstart(old_dentry);
+    old_bend = dbend(old_dentry);
+    parent_dentry = old_dentry->d_parent;
+
+    new_bstart = dbstart(new_dentry);
+    new_bend = dbend(new_dentry);
+
+    /* Rename source to destination. */
+    err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart);
+    if (err) {
+	if (!IS_COPYUP_ERR(err)) {
+	    goto out;
+	}
+	do_copyup = old_bstart -1;
+    } else {
+	revert = 1;
+    }
+
+    /* Unlink all instances of destination that exist to the left of
+     * bstart of source. On error, revert back, goto out.
+     */
+    for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {
+        struct dentry *unlink_dentry;
+        struct dentry *unlink_dir_dentry;
+
+        unlink_dentry = dtohd_index(new_dentry, bindex);
+        if (!unlink_dentry) {
+            continue;
+        }
+
+        unlink_dir_dentry = lock_parent(unlink_dentry);
+        dget(unlink_dentry);
+    	if (!(err = is_robranch_super(old_dir->i_sb, bindex))) {
+            err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry);
+	}
+        dput(unlink_dentry);
+
+        fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode);
+        /* propagate number of hard-links */
+        new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode);
+
+        unlock_dir(unlink_dir_dentry);
+        if (!err) {
+	    d_delete(unlink_dentry);
+	    if (bindex != new_bstart) {
+		dput(unlink_dentry);
+		set_dtohd_index(new_dentry, bindex, NULL);
+	    }
+	} else if (IS_COPYUP_ERR(err)) {
+		do_copyup = bindex - 1;
+	} else if (revert) {
+	    goto revert;
+	}
+    }
+
+    if (do_copyup != -1) {
+	for (bindex = do_copyup; bindex >= 0; bindex--) {
+	    /* copyup the file into some left directory, so that you can rename it */
+	    err = unionfs_copyup_dentry_len(old_dentry->d_parent->d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size);
+	    if (!err) {
+		parent_dentry = old_dentry->d_parent;
+		err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex);
+	    }
+	}
+    }
+
+    /* Create whiteout for source, only if:
+     * (1) There is more than one underlying instance of source.
+     * (2) We did a copy_up
+     */
+    if ((old_bstart != old_bend) || (do_copyup != -1)) {
+	int start = (do_copyup == -1) ? old_bstart : do_copyup;
+        /* we want to create a whiteout for name in  this parent dentry */
+	local_err = create_whiteout_parent(parent_dentry, old_dentry->d_name.name, start);
+        if (local_err) {
+            /* We can't fix anything now, so we cop-out and use -EIO. */
+	    printk("<0>We can't create a whiteout for the source in rename!\n");
+            err = -EIO;
+            goto out;
+        }
+    }
+
+out:
+
+    print_exit_status(err);
+    return err;
+
+revert:
+    /* Do revert here. */
+    local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart);
+    if (local_err) {
+	printk(KERN_WARNING "Revert failed in rename: the new refresh failed.\n");
+	eio = -EIO;
+    }
+
+    local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart);
+    if (local_err) {
+	printk(KERN_WARNING "Revert failed in rename: the old refresh failed.\n");
+	eio = -EIO;
+	goto revert_out;
+    }
+
+    if (!dtohd_index(new_dentry, bindex) || !dtohd_index(new_dentry, bindex)->d_inode) {
+	printk(KERN_WARNING "Revert failed in rename: the object disappeared from under us!\n");
+	eio = -EIO;
+	goto revert_out;
+    }
+
+    if (dtohd_index(old_dentry, bindex) && dtohd_index(old_dentry, bindex)->d_inode) {
+	printk(KERN_WARNING "Revert failed in rename: the object was created underneath us!\n");
+	eio = -EIO;
+	goto revert_out;
+    }
+
+    local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart);
+
+    /* If we can't fix it, then we cop-out with -EIO. */
+    if (local_err) {
+	printk(KERN_WARNING "Revert failed in rename!\n");
+	eio = -EIO;
+    }
+
+    local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
+    if (local_err) {
+	eio = -EIO;
+    }
+    local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
+    if (local_err) {
+	eio = -EIO;
+    }
+
+revert_out:
+    if (eio) {
+	err = eio;
+    }
+    print_exit_status(err);
+    return err;
+}
+
+/*
+ * The function is nasty, nasty, nasty, but so is rename. :(
+ *
+ * This psuedo-code describes what the function should do.  Essentially we move from
+ * right-to-left, renaming each item.  We skip the leftmost destination (this is so
+ * we can always undo the rename if the reverts work), and then the very last thing
+ * we do is fix up the leftmost destination (either through renaming it or unlinking it).
+ *
+ * for i = S_R downto S_L
+ *	if (i != L_D && exists(S[i]))
+ *		err = rename(S[i], D[i])
+ *		if (err == COPYUP)  {
+ *			do_whiteout = i - 1;
+ *			if (i == S_R) {
+ *				do_copyup = i - 1
+ *			}
+ *		}
+ *		else if (err)
+ *			goto revert;
+ *		else
+ *			ok = i;
+ *
+ *
+ * If we get to the leftmost source (S_L) and it is EROFS, we should do copyup
+ *
+ * if (err == COPYUP) {
+ * 	do_copyup = i - 1;
+ * }
+ *
+ * while (i > new_bstart) {
+ * 	err = unlink(D[i]);
+ *	if (err == COPYUP) {
+ *		do_copyup = i - 1;
+ *	} else if (err) {
+ *		goto revert;
+ *	}
+ * }
+ *
+ * if (exists(S[L_D])) {
+ *	err = rename(S[L_D], D[L_D]);
+ *	if (err = COPYUP) {
+ *		if (ok > L_D) {
+ *			do_copyup = min(do_copyup, L_D - 1);
+ *		}
+ *		do_whiteout = min(do_whiteout, L_D - 1);
+ *	} else {
+ *		goto revert;
+ *	}
+ * } else {
+ *	err = unlink(D[L_D]);
+ *	if (err = COPYUP) {
+ *		do_copyup = min(do_copyup, L_D - 1);
+ *	} else {
+ *		goto revert;
+ *	}
+ * }
+ *
+ * out:
+ *	if (do_whiteout != -1) {
+ *		create_whiteout(do_whiteout);
+ *	}
+ *	if (do_copyup != -1) {
+ *		copyup(source to do_copyup)
+ *		rename source to destination
+ *	}
+ *	return err;
+ * out_revert:
+ *	do the reverting;
+ *	return err;
+ * }
+ */
+STATIC int
+unionfs_rename_all(inode_t *old_dir, dentry_t *old_dentry,
+                   inode_t *new_dir, dentry_t *new_dentry)
+{
+    int old_bstart, old_bend;
+    int new_bstart, new_bend;
+    dentry_t *parent_dentry = NULL;
+    int bindex;
+    int err = 0;
+    int eio = 0; /* Used for revert. */
+
+    /* These variables control error handling. */
+    int rename_ok = FD_SETSIZE; /* The last rename that is ok. */
+    int do_copyup = -1; /* Where we should start copyup. */
+    int do_whiteout = -1; /* Where we should start whiteouts of the source. */
+    int clobber;	/* Are we clobbering the destination. */
+    fd_set success_mask;
+
+    print_entry_location();
+
+    old_bstart = dbstart(old_dentry);
+    old_bend = dbend(old_dentry);
+    parent_dentry = old_dentry->d_parent;
+    new_bstart = dbstart(new_dentry);
+    new_bend = dbend(new_dentry);
+    ASSERT(new_bstart >= 0);
+    ASSERT(old_bstart >= 0);
+
+    /* The failure mask only can deal with FD_SETSIZE entries. */
+    ASSERT(old_bend <= FD_SETSIZE);
+    ASSERT(new_bend <= FD_SETSIZE);
+    FD_ZERO(&success_mask);
+
+    /* Life is simpler if the dentry doesn't exist. */
+    clobber = (dtohd_index(new_dentry, new_bstart)->d_inode) ? 1 : 0;
+
+    /* Loop through all the branches from right to left and rename all
+     * instances of old dentry to new dentry, except if they are
+     */
+    for (bindex = old_bend; bindex >= old_bstart; bindex--) {
+	/* We don't rename if there is no source. */
+        if (dtohd_index(old_dentry, bindex) == NULL) {
+            continue;
+        }
+
+        /* we rename the bstart of destination only at the last of
+         * all operations, so that we don't lose it on error
+         */
+	if (clobber && (bindex == new_bstart)) {
+	    continue;
+	}
+
+	/* We shouldn't have a handle on this if there is no inode. */
+	PASSERT(dtohd_index(old_dentry, bindex)->d_inode);
+
+        err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex);
+	if (!err) {
+	    /* For reverting. */
+	    FD_SET(bindex, &success_mask);
+	    /* So we know not to copyup on failures the right */
+	    rename_ok = bindex;
+        } else if (IS_COPYUP_ERR(err)) {
+	    do_whiteout = bindex - 1;
+	    if (bindex == old_bstart) {
+		do_copyup = bindex - 1;
+            }
+	} else {
+	    goto revert;
+        }
+    }
+
+    while (bindex > new_bstart) {
+        struct dentry *unlink_dentry;
+        struct dentry *unlink_dir_dentry;
+
+        unlink_dentry = dtohd_index(new_dentry, bindex);
+        if (!unlink_dentry) {
+            bindex--;
+            continue;
+        }
+
+        unlink_dir_dentry = lock_parent(unlink_dentry);
+        dget(unlink_dentry);
+    	if (!(err = is_robranch_super(old_dir->i_sb, bindex))) {
+            err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry);
+	}
+        dput(unlink_dentry);
+
+        fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode);
+        new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode);
+
+        unlock_dir(unlink_dir_dentry);
+
+	if (!err) {
+            d_delete(unlink_dentry);
+	    if (bindex != new_bstart) {
+		dput(unlink_dentry);
+		set_dtohd_index(new_dentry, bindex, NULL);
+	    }
+	}
+
+        if (IS_COPYUP_ERR(err)) {
+	    do_copyup = bindex - 1;
+	} else if (err) {
+            goto revert;
+	}
+
+        bindex--;
+    } // while bindex
+
+    /* Now we need to handle the leftmost of the destination. */
+    if (clobber && dtohd_index(old_dentry, new_bstart)) {
+        err = do_rename(old_dir, old_dentry, new_dir, new_dentry, new_bstart);
+        if (IS_COPYUP_ERR(err)) {
+		if (rename_ok > new_bstart) {
+			if ((do_copyup == -1) || (new_bstart - 1 < do_copyup)) {
+				do_copyup = new_bstart - 1;
+			}
+		}
+		if ((do_whiteout == -1) || (new_bstart - 1 < do_whiteout)) {
+			do_whiteout = new_bstart - 1;
+		}
+	} else if (err) {
+		goto revert;
+	}
+    } else if (clobber && (new_bstart < old_bstart)) {
+        struct dentry *unlink_dentry;
+        struct dentry *unlink_dir_dentry;
+
+        unlink_dentry = dtohd_index(new_dentry, new_bstart);
+	PASSERT(unlink_dentry);
+	PASSERT(unlink_dentry->d_inode);
+
+        unlink_dir_dentry = lock_parent(unlink_dentry);
+        dget(unlink_dentry);
+    	if (!(err = is_robranch_super(old_dir->i_sb, new_bstart))) {
+            err = vfs_unlink(unlink_dir_dentry->d_inode, unlink_dentry);
+	}
+        dput(unlink_dentry);
+
+        fist_copy_attr_times(new_dentry->d_parent->d_inode, unlink_dir_dentry->d_inode);
+        new_dentry->d_parent->d_inode->i_nlink = get_nlinks(new_dentry->d_parent->d_inode);
+
+        unlock_dir(unlink_dir_dentry);
+        if (!err) {
+             d_delete(unlink_dentry);
+        }
+
+        if (IS_COPYUP_ERR(err)) {
+	    if ((do_copyup == -1) || (new_bstart - 1 < do_copyup)) {
+		do_copyup = new_bstart - 1;
+	    }
+	} else if (err) {
+            goto revert;
+	}
+    }
+
+    /* Create a whiteout for the source. */
+    if (do_whiteout != -1) {
+	ASSERT(do_whiteout >= 0);
+	err = create_whiteout_parent(parent_dentry, old_dentry->d_name.name, do_whiteout);
+        if (err) {
+            /* We can't fix anything now, so we cop-out and use -EIO. */
+	    printk("<0>We can't create a whiteout for the source in rename!\n");
+            err = -EIO;
+            goto out;
+        }
+    }
+
+    if (do_copyup != -1) {
+	for (bindex = do_copyup; bindex >= 0; bindex--) {
+	   err = unionfs_copyup_dentry_len(old_dentry->d_parent->d_inode, old_dentry, old_bstart, bindex, NULL, old_dentry->d_inode->i_size);
+	   if (!err) {
+		err = do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex);
+	   }
+	}
+    }
+
+
+    /* We are at the point where reverting doesn't happen. */
+    goto out;
+
+revert:
+    for (bindex = old_bstart; bindex <= old_bend; bindex++) {
+	int local_err;
+
+	if (FD_ISSET(bindex, &success_mask)) {
+	    local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
+	    if (local_err) {
+		printk(KERN_WARNING "Revert failed in rename: the new refresh failed.\n");
+		eio = -EIO;
+	    }
+
+	    local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
+	    if (local_err) {
+		printk(KERN_WARNING "Revert failed in rename: the old refresh failed.\n");
+		eio = -EIO;
+		continue;
+	    }
+
+    	    if (!dtohd_index(new_dentry, bindex) || !dtohd_index(new_dentry, bindex)->d_inode) {
+		printk(KERN_WARNING "Revert failed in rename: the object disappeared from under us!\n");
+		eio = -EIO;
+		continue;
+    	    }
+
+    	    if (dtohd_index(old_dentry, bindex) && dtohd_index(old_dentry, bindex)->d_inode) {
+		printk(KERN_WARNING "Revert failed in rename: the object was created underneath us!\n");
+		eio = -EIO;
+		continue;
+    	    }
+
+	    local_err = do_rename(new_dir, new_dentry, old_dir, old_dentry, bindex);
+
+	    /* If we can't fix it, then we cop-out with -EIO. */
+	    if (local_err) {
+		printk(KERN_WARNING "Revert failed in rename!\n");
+		eio = -EIO;
+	    }
+
+
+	    local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex);
+	    if (local_err) {
+		eio = -EIO;
+            }
+	    local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex);
+	    if (local_err) {
+		eio = -EIO;
+            }
+	}
+    }
+    if (eio) {
+	err = eio;
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_rename_first(inode_t *old_dir, dentry_t *old_dentry,
+                     inode_t *new_dir, dentry_t *new_dentry)
+{
+    int err;
+    dentry_t *hidden_old_dentry;
+    dentry_t *hidden_new_dentry;
+    dentry_t *hidden_old_dir_dentry;
+    dentry_t *hidden_new_dir_dentry;
+
+    print_entry_location();
+
+    hidden_old_dentry = dtohd(old_dentry);
+    hidden_new_dentry = dtohd(new_dentry);
+
+    fist_checkinode(old_dir, "unionfs_rename-old_dir");
+    fist_checkinode(new_dir, "unionfs_rename-new_dir");
+
+    dget(hidden_old_dentry);
+    dget(hidden_new_dentry);
+    hidden_old_dir_dentry = get_parent(hidden_old_dentry);
+    hidden_new_dir_dentry = get_parent(hidden_new_dentry);
+    double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+    if (!(err = is_robranch(old_dentry))) {
+        err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
+                         hidden_new_dir_dentry->d_inode, hidden_new_dentry);
+    }
+    if (err)
+	goto out_lock;
+
+    fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode);
+    if (new_dir != old_dir)
+	fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode);
+
+out_lock:
+    // double_unlock will dput the new/old parent dentries whose refcnts
+    // were incremented via get_parent above.
+    double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
+    dput(hidden_new_dentry);
+    dput(hidden_old_dentry);
+
+    fist_checkinode(new_dir, "post unionfs_rename-new_dir");
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_rename(inode_t *old_dir, dentry_t *old_dentry,
+	       inode_t *new_dir, dentry_t *new_dentry)
+{
+    int err = 0;
+    dentry_t *hidden_old_dentry;
+    dentry_t *hidden_new_dentry;
+
+    print_entry_location();
+
+    fist_checkinode(old_dir, "unionfs_rename-old_dir");
+    fist_checkinode(new_dir, "unionfs_rename-new_dir");
+    fist_print_dentry("IN: unionfs_rename, old_dentry", old_dentry);
+    fist_print_dentry("IN: unionfs_rename, new_dentry", new_dentry);
+
+    err = unionfs_partial_lookup(old_dentry);
+    if (err) {
+        fist_dprint(8, "Error in partial lookup\n");
+        goto out;
+    }
+    err = unionfs_partial_lookup(new_dentry);
+    if (err) {
+        fist_dprint(8, "Error in partial lookup\n");
+        goto out;
+    }
+
+    hidden_new_dentry = dtohd(new_dentry);
+    hidden_old_dentry = dtohd(old_dentry);
+
+    if (new_dentry->d_inode) {
+        if(S_ISDIR(old_dentry->d_inode->i_mode) != S_ISDIR(new_dentry->d_inode->i_mode)) {
+            err = S_ISDIR(old_dentry->d_inode->i_mode) ? -ENOTDIR : -EISDIR;
+            goto out;
+        }
+
+    	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+    	    /* check if this unionfs directory is empty or not */
+    	    err = check_empty(new_dentry, NULL);
+    	    if (err) {
+		goto out;
+    	    }
+	    /* Handle the case where we are overwriting directories that are not
+	     * really empty because of whiteout or non-whiteout entries.
+	     */
+        }
+    }
+
+    if (IS_SET(old_dir->i_sb, DELETE_WHITEOUT)) {
+        /* create whiteout */
+        err = unionfs_rename_whiteout(old_dir, old_dentry, new_dir, new_dentry);
+    } else if (IS_SET(old_dir->i_sb, DELETE_FIRST)) {
+        /* rename only first source entry */
+        err = unionfs_rename_first(old_dir, old_dentry, new_dir, new_dentry);
+    } else {
+	/* delete all. */
+	err = unionfs_rename_all(old_dir, old_dentry, new_dir, new_dentry);
+    }
+
+out:
+    fist_checkinode(new_dir, "post unionfs_rename-new_dir");
+    fist_print_dentry("OUT: unionfs_rename, old_dentry", old_dentry);
+    fist_print_dentry("OUT: unionfs_rename, new_dentry", new_dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+
+
+STATIC int
+unionfs_readlink(dentry_t *dentry, char *buf, int bufsiz)
+{
+    int err;
+    dentry_t *hidden_dentry;
+
+    print_entry_location();
+    hidden_dentry = dtohd(dentry);
+//    fist_print_dentry("unionfs_readlink IN", dentry);
+
+    if (!hidden_dentry->d_inode->i_op ||
+	!hidden_dentry->d_inode->i_op->readlink) {
+	err = -EINVAL;
+	goto out;
+    }
+
+    err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry,
+						 buf,
+						 bufsiz);
+    if (err > 0)
+	fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode);
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+
+STATIC int
+unionfs_follow_link(dentry_t *dentry, struct nameidata *nd)
+{
+    char *buf;
+    int len = PAGE_SIZE, err;
+    mm_segment_t old_fs;
+
+    print_entry_location();
+//    fist_print_dentry("unionfs_follow_link dentry IN", dentry);
+
+    buf = KMALLOC(len, GFP_UNIONFS);
+    if (!buf) {
+	err = -ENOMEM;
+	goto out;
+    }
+
+    /* read the symlink, and then we will follow it */
+    old_fs = get_fs();
+    set_fs(KERNEL_DS);
+    err = dentry->d_inode->i_op->readlink(dentry, buf, len);
+    set_fs(old_fs);
+    if (err < 0)
+	goto out_free;
+
+    buf[err] = 0;	// terminate the buffer -- XXX still needed?
+
+    // XXX: FIXME w/ unionfs_encode_filename()
+    /*
+     * vfs_follow_link will increase the nd's mnt refcnt
+     * we assume that some other VFS code decrements it.
+     */
+    err = vfs_follow_link(nd, buf);
+
+ out_free:
+    KFREE(buf);
+ out:
+#if 0
+    if (err < 0) {
+	dput(nd->dentry);
+	printk("EZK follow_link() mnt_cnt %d\n", atomic_read(&nd->mnt->mnt_count));
+	mntput(nd->mnt);
+    }
+#endif
+
+    print_exit_status(err);
+    return err;
+}
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+static int unionfs_permission(inode_t *inode, int mask, struct nameidata *nd)
+#else
+static int unionfs_permission(inode_t *inode, int mask)
+#endif
+{
+    inode_t *hidden_inode=NULL;
+    int err = 0;
+    int bindex, bstart, bend;
+    int is_file = 0;
+
+    print_entry_location();
+
+    bstart = ibstart(inode);
+    bend = ibend(inode);
+
+    fist_print_inode("IN unionfs_permission: unionfs inode=", inode);
+
+    /* set if check is for file */
+    if (!S_ISDIR(inode->i_mode)) {
+        is_file = 1;
+    }
+
+    for (bindex = bstart; bindex <= bend; bindex++) {
+
+        hidden_inode = itohi_index(inode, bindex);
+	if (!hidden_inode) {
+	    continue;
+	}
+
+        /* check the condition for D-F-D underlying files/directories,
+         * we dont have to check for files, if we are checking for
+         * directories.
+         */
+        if (!S_ISDIR(hidden_inode->i_mode) && (!is_file)) {
+            continue;
+        }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	err = permission(hidden_inode, mask);
+#else
+	err = permission(hidden_inode, mask, nd);
+#endif
+
+	/* Our permission needs to return OK, even if it is a copy-up. */
+	if ((mask & MAY_WRITE) && (!(stopd(inode->i_sb)->usi_branchperms[bindex] & MAY_WRITE))) {
+		fist_dprint(8, "Checking permission on read-only branch: need to force copy-up later.\n");
+	}
+
+        /* any error in read/execute should be returned back immediately
+         * since read/exec is an intersection.
+         */
+	if ((mask & MAY_READ) || (mask & MAY_EXEC)) {
+	    if (!IS_COPYUP_ERR(err)) {
+		goto out;
+            }
+        }
+
+        /* Any error, except for COPYUP_ERR should be passed up */
+	if (mask & MAY_WRITE) {
+	    if (err) {
+                /* If leftmost is RO, return error */
+                if (IS_COPYUP_ERR(err) && bindex) {
+                    /* This success will finally trigger copyup */
+                    err = 0;
+                    continue;
+		}
+	    	goto out;
+            }
+	}
+
+        /* check for only leftmost file */
+        if (is_file) {
+            break;
+        }
+    } // end for
+
+ out:
+    print_exit_status(err);
+    return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+STATIC int unionfs_inode_revalidate(dentry_t *dentry)
+{
+    int err = 0;
+    dentry_t *hidden_dentry;
+    inode_t *hidden_inode;
+    inode_t *inode;
+    int bindex, bstart, bend;
+    int sbgen, igen;
+    mode_t savemode;
+    int locked = 0;
+
+    print_entry_location();
+    PASSERT(dentry);
+    inode = dentry->d_inode;
+    PASSERT(inode);
+
+    /* Before anything else, we should check if the generation number is valid. */
+restart:
+    PASSERT(inode->i_sb);
+    PASSERT(itopd(inode));
+    sbgen = atomic_read(&stopd(inode->i_sb)->usi_generation);
+    igen = atomic_read(&itopd(inode)->uii_generation);
+    if (sbgen != igen) {
+	struct dentry *result;
+
+	lock_super(inode->i_sb);
+	lock_dpd(dentry);
+	locked = 1;
+
+        savemode = inode->i_mode;
+
+	/* The root entry should always be valid */
+	fist_dprint(8, "revalidate: sbgen = %d, igen = %d\n", sbgen, igen);
+
+	ASSERT(!IS_ROOT(dentry));
+	PASSERT(dentry->d_parent);
+	PASSERT(dentry->d_parent->d_inode);
+
+	/* We can't work correctly if our parent isn't valid. */
+	if(atomic_read(&stopd(inode->i_sb)->usi_generation) != atomic_read(&dtopd(dentry->d_parent)->udi_generation)) {
+		PASSERT(dentry->d_parent->d_inode);
+		PASSERT(dentry->d_parent->d_inode->i_op);
+		PASSERT(dentry->d_parent->d_inode->i_op->revalidate);
+		unlock_super(inode->i_sb);
+		unlock_dpd(dentry);
+
+		err = dentry->d_parent->d_inode->i_op->revalidate(dentry->d_parent);
+		if (err) {
+			goto out;
+		}
+
+		goto restart;
+	}
+
+	/* Free the pointers for our inodes and this dentry. */
+	bstart = dbstart(dentry);
+	bend = dbend(dentry);
+	if (bstart >= 0) {
+		struct dentry *hidden_dentry;
+		for (bindex = bstart; bindex <= bend; bindex++) {
+			hidden_dentry = dtohd_index(dentry, bindex);
+			if (!hidden_dentry) continue;
+			dput(hidden_dentry);
+		}
+	}
+	KFREE(dtohd_ptr(dentry));
+	dtohd_ptr(dentry) = NULL;
+	set_dbstart(dentry, -1);
+	set_dbend(dentry, -1);
+
+	bstart = ibstart(inode);
+	bend = ibend(inode);
+	if (bstart >= 0) {
+		struct inode *hidden_inode;
+		for (bindex = bstart; bindex <= bend; bindex++) {
+			hidden_inode = itohi_index(inode, bindex);
+			if (!hidden_inode) continue;
+			iput(hidden_inode);
+		}
+	}
+	KFREE(itohi_ptr(dentry->d_inode));
+	itohi_ptr(dentry->d_inode) = NULL;
+	ibstart(dentry->d_inode) = -1;
+	ibend(dentry->d_inode) = -1;
+
+
+
+	result = unionfs_lookup_backend(dentry->d_parent->d_inode, dentry, INTERPOSE_REVAL);
+	if (result && IS_ERR(result)) {
+		err = PTR_ERR(result);
+		goto out;
+	}
+
+	if (itopd(dentry->d_inode)->uii_stale) {
+		make_stale_inode(dentry->d_inode);
+		d_drop(dentry);
+  	        err = -ESTALE;
+		goto out;
+	}
+    }
+
+    bstart = dbstart(dentry);
+    bend = dbend(dentry);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (!hidden_dentry) {
+	    continue;
+	}
+
+	hidden_inode = hidden_dentry->d_inode;
+	PASSERT(hidden_inode);
+
+	/* Now revalidate the lower level. */
+	if (hidden_inode->i_op && hidden_inode->i_op->revalidate) {
+	    err = hidden_inode->i_op->revalidate(hidden_dentry);
+	}
+    }
+
+    hidden_inode = NULL;
+    for (bindex = bstart; bindex <= bend && !hidden_inode; bindex++) {
+	hidden_inode = itohi_index(inode, bindex);
+    }
+
+    if (hidden_inode && !err) {
+	fist_copy_attr_all(inode, hidden_inode);
+    }
+
+out:
+    if (locked) {
+        unlock_dpd(dentry);
+	unlock_super(inode->i_sb);
+    }
+    print_exit_status(err);
+    return err;
+}
+#endif /* End of revalidate */
+
+STATIC int
+unionfs_setattr(dentry_t *dentry, struct iattr *ia)
+{
+    int err = 0;
+    dentry_t *hidden_dentry;
+    inode_t *inode = NULL;
+    inode_t *hidden_inode = NULL;
+    int bstart, bend, bindex;
+    int i;
+    int copyup = 0;
+    int size = 0;
+    int locked = 0;
+
+    print_entry_location();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    if (!(ia->ia_valid & ATTR_SIZE)) {
+		locked = 1;
+		PASSERT(dentry->d_inode);
+		down(&dentry->d_inode->i_sem);
+    }
+#endif
+	if (IS_SET(dentry->d_sb, SETATTR_ALL)) {
+		err = unionfs_partial_lookup(dentry);
+		if (err) {
+			fist_dprint(8, "Error in partial lookup\n");
+			goto out;
+		}
+    }
+    bstart = dbstart(dentry);
+    bend = dbend(dentry);
+    inode = dentry->d_inode;
+
+	for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) {
+		hidden_dentry = dtohd_index(dentry, bindex);
+		if (!hidden_dentry) {
+			continue;
+		}
+		ASSERT(hidden_dentry->d_inode != NULL);
+
+		/* If the file is on a read only branch */
+		if (is_robranch_super(dentry->d_sb, bindex) || IS_RDONLY(hidden_dentry->d_inode)) {
+            if (copyup || (bindex != bstart)) {
+                continue;
+            }
+
+            /* Only if its the leftmost file, copyup the file */
+	    for (i = bstart - 1; i >= 0; i--) {
+		    if (ia->ia_valid & ATTR_SIZE) {
+			    size = ia->ia_size;
+			    err = unionfs_copyup_dentry_len(dentry->d_parent->d_inode, dentry, bstart, i, NULL, size);
+		    } else {
+			    err = unionfs_copyup_dentry_len(dentry->d_parent->d_inode, dentry, bstart, i, NULL, dentry->d_inode->i_size);
+		    }
+		    if (err) {
+			    /* if error is in the leftmost f/s, stop and passup the error */
+			    if (i == 0) {
+				    goto out;
+			    }
+		    } else {
+			    copyup = 1;
+			    hidden_dentry = dtohd(dentry);
+			    break;
+		    }
+	    }
+
+	}
+	err = notify_change(hidden_dentry, ia);
+	if (err) {
+	    goto out;
+	}
+
+	if (!IS_SET(dentry->d_sb, SETATTR_ALL)) {
+            break;
+	}
+    }
+
+    /* get the size from the first hidden inode */
+    hidden_inode = itohi(dentry->d_inode);
+    fist_checkinode(inode, "unionfs_setattr");
+    fist_copy_attr_all(inode, hidden_inode);
+
+ out:
+    if (locked) {
+	up(&dentry->d_inode->i_sem);
+    }
+    fist_checkinode(inode, "post unionfs_setattr");
+    print_exit_status(err);
+    return err;
+}
+#if NOT_USED_YET
+STATIC int
+unionfs_getattr(dentry_t *dentry, struct iattr *ia)
+{
+    return -ENOSYS;
+}
+#endif /* NOT_USED_YET */
+
+
+struct inode_operations unionfs_symlink_iops =
+{
+    readlink:	unionfs_readlink,
+    follow_link: unionfs_follow_link,
+    permission:	unionfs_permission,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    revalidate:	unionfs_inode_revalidate,
+#endif
+    setattr:	unionfs_setattr,
+};
+
+
+struct inode_operations unionfs_dir_iops =
+{
+    create:	unionfs_create,
+    lookup:	unionfs_lookup,
+    link:	unionfs_link,
+    unlink:	unionfs_unlink,
+    symlink:	unionfs_symlink,
+    mkdir:	unionfs_mkdir,
+    rmdir:	unionfs_rmdir,
+    mknod:	unionfs_mknod,
+    rename:	unionfs_rename,
+    /* no readlink/follow_link for non-symlinks */
+    // off because we have setattr
+    //    truncate:	unionfs_truncate,
+    permission:	unionfs_permission,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    revalidate:	unionfs_inode_revalidate,
+#endif
+    setattr:	unionfs_setattr,
+// If you have problems with these lines, try defining FIST_SETXATTR_CONSTVOID
+# if defined(UNIONFS_XATTR) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
+    setxattr:   unionfs_setxattr,
+    getxattr:   unionfs_getxattr,
+    removexattr:unionfs_removexattr,
+    listxattr:  unionfs_listxattr,
+# endif
+};
+
+struct inode_operations unionfs_main_iops =
+{
+    permission:	unionfs_permission,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    revalidate:	unionfs_inode_revalidate,
+#endif
+	setattr:	unionfs_setattr,
+// If you have problems with these lines, try defining FIST_SETXATTR_CONSTVOID
+# if defined(UNIONFS_XATTR) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
+    setxattr:   unionfs_setxattr,
+    getxattr:   unionfs_getxattr,
+    removexattr:unionfs_removexattr,
+    listxattr:  unionfs_listxattr,
+# endif
+};
+
+/*
+ * vim:shiftwidth=4
+ * vim:tabstop=8
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/main.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/main.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/main.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/main.c	2005-04-25 11:56:14.545654808 -0700
@@ -0,0 +1,1121 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: main.c,v 1.84 2005/02/22 18:42:58 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+#include <linux/module.h>
+
+/* sb we pass is unionfs's super_block */
+int unionfs_interpose(dentry_t *dentry, super_block_t *sb, int flag)
+{
+        inode_t *hidden_inode;
+        dentry_t *hidden_dentry;
+        int err = 0;
+        inode_t *inode;
+        int is_negative_dentry = 1;
+        int bindex, bstart, bend;
+
+        print_entry_location();
+
+        fist_print_dentry("In unionfs_interpose: ", dentry);
+
+        bstart = dbstart(dentry);
+        bend = dbend(dentry);
+
+        /* Make sure that we didn't get a negative dentry. */
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                if (dtohd_index(dentry, bindex) && dtohd_index(dentry, bindex)->d_inode) {
+                        is_negative_dentry = 0;
+                        break;
+                }
+        }
+        ASSERT(!is_negative_dentry);
+
+        /* We allocate our new inode below, by calling iget.
+         * iget will call our read_inode which will initialize some
+         * of the new inode's fields */
+
+        /* On revalidate we've already got our own inode and just need to fix it up. */
+        if (flag == INTERPOSE_REVAL) {
+                inode = dentry->d_inode;
+                PASSERT(inode);
+                itopd(inode)->b_start = -1;
+                itopd(inode)->b_end = -1;
+                atomic_set(&itopd(inode)->uii_generation, atomic_read(&stopd(sb)->usi_generation));
+
+                itohi_ptr(inode) = KMALLOC(sizeof(inode_t *) * sbmax(sb), GFP_UNIONFS);
+                if (!itohi_ptr(inode)) {
+                        err = -ENOMEM;
+                        goto out;
+                }
+                init_itohi_ptr(inode, sbmax(inode->i_sb));
+	} else if (flag == INTERPOSE_LINK) {
+		inode = dentry->d_inode;
+        } else {
+                /* get unique inode number for unionfs */
+                inode = iget(sb, iunique(sb, UNIONFS_ROOT_INO));
+                if (!inode) {
+                        err = -EACCES;		/* should be impossible??? */
+                        goto out;
+                }
+        }
+
+        /*
+         * interpose the inode if not already interposed
+         *   this is possible if the inode is being reused
+         * XXX: what happens if we get_empty_inode() but there's another already?
+         * for now, ASSERT() that this can't happen; fix later.
+         */
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                hidden_dentry = dtohd_index(dentry, bindex);
+                if (!hidden_dentry) {
+                        itohi_index(inode, bindex) = NULL;
+                        continue;
+                }
+
+                /* initialize the hidden inode to the new hidden inode */
+                if (hidden_dentry->d_inode != NULL) {
+                        itohi_index(inode, bindex) = igrab(hidden_dentry->d_inode);
+                }
+        }
+
+        ibstart(inode) = dbstart(dentry);
+        ibend(inode) = dbend(dentry);
+
+        /* Use attributes from the first branch. */
+        hidden_inode = itohi(inode);
+
+        /* Use different set of inode ops for symlinks & directories*/
+        if (S_ISLNK(hidden_inode->i_mode))
+                inode->i_op = &unionfs_symlink_iops;
+        else if (S_ISDIR(hidden_inode->i_mode))
+                inode->i_op = &unionfs_dir_iops;
+
+        /* Use different set of file ops for directories */
+        if (S_ISDIR(hidden_inode->i_mode))
+                inode->i_fop = &unionfs_dir_fops;
+
+        /* properly initialize special inodes */
+        if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) ||
+            S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode)) {
+                init_special_inode(inode, hidden_inode->i_mode, hidden_inode->i_rdev);
+        }
+
+        /* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */
+        if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops) {
+                fist_dprint(7, "fixing inode 0x%p a_ops (0x%p -> 0x%p)\n",
+                            inode, inode->i_mapping->a_ops,
+                            hidden_inode->i_mapping->a_ops);
+                inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops;
+        }
+
+        /* only (our) lookup wants to do a d_add */
+        switch(flag) {
+        case INTERPOSE_DEFAULT:
+        case INTERPOSE_LINK:
+                d_instantiate(dentry, inode);
+                break;
+        case INTERPOSE_LOOKUP:
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+                err = PTR_ERR(d_splice_alias(inode, dentry));
+#else
+                d_add(dentry, inode);
+#endif
+                break;
+        case INTERPOSE_REVAL:
+                /* Do nothing. */
+                break;
+        default:
+                FISTBUG("Invalid interpose flag passed!");
+        }
+
+        PASSERT(dtopd(dentry));
+
+        /* all well, copy inode attributes */
+        fist_copy_attr_all(inode, hidden_inode);
+
+        fist_print_dentry("Leaving unionfs_interpose: ", dentry);
+        fist_print_inode("Leaving unionfs_interpose: ", inode);
+
+out:
+        print_exit_status(err);
+        return err;
+}
+
+void
+unionfs_reinterpose(dentry_t *dentry)
+{
+        dentry_t *hidden_dentry;
+        inode_t *inode;
+        int bindex, bstart, bend;
+
+        print_entry_location();
+
+        fist_print_dentry("IN: unionfs_reinterpose: ", dentry);
+
+        /* This is pre-allocated inode */
+        inode = dentry->d_inode;
+        PASSERT(inode);
+
+        bstart = dbstart(dentry);
+        bend = dbend(dentry);
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                hidden_dentry = dtohd_index(dentry, bindex);
+
+                if (!hidden_dentry)
+                        continue;
+
+                /* initialize the hidden inode to the new hidden inode */
+                if (hidden_dentry->d_inode) {
+                        if (itohi_index(inode, bindex) == NULL) {
+                                itohi_index(inode, bindex) = igrab(hidden_dentry->d_inode);
+                        }
+                }
+        }
+        ibstart(inode) = dbstart(dentry);
+        ibend(inode) = dbend(dentry);
+
+        /* TODO : do we need to call, d_add/d_instantiate? */
+
+        fist_print_dentry("OUT: unionfs_reinterpose: ", dentry);
+        fist_print_inode("OUT: unionfs_reinterpose: ", inode);
+
+        print_exit_location();
+}
+
+/*
+ * Parse mount options:
+ * Current mount command example =
+ * mount -t unionfs -o dirs=b1=rw:b2=ro:b3=rw:b4=ro,delete=all,copyup=mounter,copyupuid=2,copyupgid=3,copyupmode=0777,
+ * setattr=left,err=passup,,debug=N null /mnt/unionfs
+ * any leading and/or trailing colons are ignored
+ *
+ * Returns the dentry object of the lower-level (hidden) directory;
+ * We want to mount our stackable file system on top of that hidden directory.
+ *
+ * Sets default debugging level to N, if any.
+ */
+struct unionfs_dentry_info *
+unionfs_parse_options(super_block_t *sb, char *options)
+{
+        struct unionfs_dentry_info *hidden_root_info;
+        struct nameidata nd;
+        char *name, *tmp, *tmp2, *end, *end2, *firstchar;
+        int err = 0;
+        int bindex = 0;
+        int i;
+	int branches = -1;
+
+        print_entry_location();
+
+        /* allocate private data area */
+        hidden_root_info = KMALLOC(sizeof(struct unionfs_dentry_info), GFP_UNIONFS);
+        if (!hidden_root_info) {
+                err = -ENOMEM;
+                goto out_error;
+        }
+        init_dentry_info(hidden_root_info);
+
+        firstchar = options;
+
+        /* We don't want to go off the end of our arguments later on. */
+        for (end = options; *end; end++);
+
+        while (options < end) {
+                tmp = options;
+
+                while (*tmp && *tmp != ',') {
+                        tmp++;
+                }
+                *tmp = '\0';
+
+                if (!strncmp("dirs=", options, (sizeof("dirs=") - 1))) {
+                        options = options + (sizeof("dirs=") - 1);
+
+                        /* colon at the beginning */
+                        if (*options == ':') {
+                                options++;
+                        }
+
+                        /* We don't want to go off the end of our dir arguments later on. */
+                        for (end2 = options; *end2; end2++) ;
+
+                        /* colon at the end */
+                        if (*(end2-1) == ':') {
+                                end2--;
+                                *end2 = '\0';
+                        }
+
+			if (options == end2) {
+				err = -EINVAL;
+                		printk(KERN_WARNING "unionfs: non-blank dirs option required\n");
+				goto out_error;
+			}
+
+			/* We have to have at least one branch, and each ':' means we have one more. */
+			branches = 1;
+                        for(tmp2 = options; *tmp2; tmp2++) {
+                                if(*tmp2 == ':') {
+				    branches++;
+                                }
+                        }
+
+                        /* allocate space for  underlying pointers to hidden dentry */
+                        hidden_root_info->udi_dentry = KMALLOC(sizeof(dentry_t *) * branches, GFP_UNIONFS);
+                        if (IS_ERR(hidden_root_info->udi_dentry)) {
+                                err = -ENOMEM;
+                                goto out_error;
+                        }
+                        memset(hidden_root_info->udi_dentry, 0, sizeof(dentry_t *) * branches);
+
+                        /* allocate space for underlying pointers to super block */
+                        stohs_ptr(sb) = KMALLOC(sizeof(super_block_t *) * branches, GFP_UNIONFS);
+                        if (IS_ERR(stohs_ptr(sb))) {
+                                err = -ENOMEM;
+                                goto out_error;
+                        }
+                        init_stohs_ptr(sb, branches);
+
+                        /* Set all reference counts to zero. */
+                        stopd(sb)->usi_sbcount = KMALLOC(sizeof(atomic_t) * branches, GFP_UNIONFS);
+                        if (IS_ERR(stopd(sb)->usi_sbcount)) {
+                                err = -ENOMEM;
+                                goto out_error;
+                        }
+                        for (i = 0; i < branches; i++) {
+                                atomic_set(&stopd(sb)->usi_sbcount[i], 0);
+                        }
+
+                        /* Set the permissions to none (we'll fix them up later). */
+                        stopd(sb)->usi_branchperms = KMALLOC(sizeof(int) * branches, GFP_UNIONFS);
+                        if (IS_ERR(stopd(sb)->usi_branchperms)) {
+                                err = -ENOMEM;
+                                goto out_error;
+                        }
+
+                        /* allocate space for array of pointers for underlying mount points */
+                        stohiddenmnt_ptr(sb) = KMALLOC(sizeof(struct vfsmount *) * branches, GFP_UNIONFS);
+                        if (IS_ERR(stohiddenmnt_ptr(sb))) {
+                                err = -ENOMEM;
+                                goto out_error;
+                        }
+                        init_stohiddenmnt_ptr(sb, branches);
+
+                        /* now parsing the string b1:b2=rw:b3=ro:b4 */
+                        while (options < end2) {
+                                int perms;
+
+                                int l;
+
+                                tmp2 = options;
+
+                                while (*tmp2 && *tmp2 != ':') {
+                                        tmp2++;
+                                }
+                                *tmp2 = '\0';
+
+                                /* name contains individual dir */
+                                name = options;
+
+                                /* strip off =rw or =ro if it is specified. */
+                                l = strlen(name);
+                                if(!strcmp(name + l - 3, "=ro")) {
+                                        perms = MAY_READ;
+                                        name[l - 3] = '\0';
+                                } else if(!strcmp(name + l - 3, "=rw")) {
+                                        perms = MAY_READ | MAY_WRITE;
+                                        name[l - 3] = '\0';
+                                } else {
+                                        perms = MAY_READ | MAY_WRITE;
+                                }
+
+                                fist_dprint(4, "unionfs: using directory: %s (%c%c)\n", name, perms & MAY_READ ? 'r' : '-', perms & MAY_WRITE ? 'w' : '-');
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+				if (path_init(name, LOOKUP_FOLLOW, &nd))
+                                        err = path_walk(name, &nd);
+#else
+				err = path_lookup(name, LOOKUP_FOLLOW, &nd);
+#endif
+                                if (err) {
+                                        printk("unionfs: error accessing hidden directory '%s' (error %d)\n", name, err);
+                                        goto out_error;
+                                }
+
+                                hidden_root_info->udi_dentry[bindex] = nd.dentry;
+                                stopd(sb)->usi_hidden_mnt[bindex] = nd.mnt;
+                                stopd(sb)->usi_branchperms[bindex] = perms;
+
+                                //set the branch start index
+                                if (hidden_root_info->udi_bstart < 0) {
+                                        hidden_root_info->udi_bstart = bindex;
+                                }
+                                // increment end branch as we add more branches
+                                hidden_root_info->udi_bend = bindex;
+
+                                // increment the branch index for the next branch to be added
+                                bindex++;
+
+                                /* increment the string pointer */
+                                options = tmp2 + 1;
+                        }
+                } else if (!strncmp("debug=", options, (sizeof("debug=") - 1))) {
+                        char *endptr;
+			int debug;
+
+                        options = options + (sizeof("debug=") - 1);
+                        debug = simple_strtoul(options, &endptr, 0);
+
+                        if (*endptr) {
+                                printk(KERN_WARNING "unionfs: invalid debug option '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                        fist_set_debug_value(debug);
+                } else if (!strncmp("err=", options, (sizeof("err=") - 1))) {
+                        options = options + (sizeof("err=") - 1);
+
+                        if (!strcmp("tryleft", options)) {
+                                /* default */
+                        } else if (!strcmp("passup", options)) {
+                                MOUNT_FLAG(sb) |=  GLOBAL_ERR_PASSUP;
+                        } else {
+                                printk(KERN_WARNING "unionfs: could not parse err option value '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("delete=", options, (sizeof("delete=") - 1))) {
+                        options = options + (sizeof("delete=") - 1);
+
+                        if (!strcmp("all", options)) {
+                                /* default */
+                        } else if (!strcmp("first", options)) {
+                                MOUNT_FLAG(sb) |= DELETE_FIRST;
+                        } else if (!strcmp("whiteout", options)) {
+                                MOUNT_FLAG(sb) |= DELETE_WHITEOUT;
+                        } else {
+                                printk(KERN_WARNING "unionfs: could not parse delete option value '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("setattr=", options, (sizeof("setattr=") - 1))) {
+                        options = options + (sizeof("setattr=") - 1);
+
+                        if (!strcmp("left", options)) {
+                                /* default */
+                        } else if (!strcmp("all", options)) {
+                                MOUNT_FLAG(sb) |= SETATTR_ALL;
+                        } else {
+                                printk(KERN_WARNING "unionfs: could not parse setattr option value '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("copyup=", options, (sizeof("copyup=") - 1))) {
+                        char *tmp;
+                        char found = 0;
+
+                        options = options + (sizeof("copyup=") - 1);
+
+                        if (!strcmp("preserve", options)) {
+                                /* default */
+                        } else if (!strcmp("currentuser", options)) {
+                                MOUNT_FLAG(sb) |= COPYUP_CURRENT_USER;
+                        } else if (!strcmp("mounter", options)) {
+                                /* if mounter is specified then copyupuid, copyupgid and
+                                   copyupmode should be specified also. */
+                                for (tmp = firstchar; tmp < end; tmp++) {
+                                        if (!strncmp("copyupuid", tmp, (sizeof("copyupuid") - 1))) {
+                                                found = 1;
+                                                break;
+                                        }
+                                }
+                                if (!found) {
+                                        printk(KERN_WARNING "unionfs: copyupuid option is not specified\n");
+                                        err = -EINVAL;
+                                        goto out_error;
+                                }
+                                found = 0;
+                                for (tmp = firstchar; tmp < end; tmp++) {
+                                        if (!strncmp("copyupgid", tmp, (sizeof("copyupgid") - 1))) {
+                                                found = 1;
+                                                break;
+                                        }
+                                }
+                                if (!found) {
+                                        printk(KERN_WARNING "unionfs: copyupgid option is not specified\n");
+                                        err = -EINVAL;
+                                        goto out_error;
+                                }
+                                found = 0;
+                                for (tmp = firstchar; tmp < end; tmp++) {
+                                        if (!strncmp("copyupmode", tmp, (sizeof("copyupmode") - 1))) {
+                                                found = 1;
+                                                break;
+                                        }
+                                }
+                                if (!found) {
+                                        printk(KERN_WARNING "unionfs: copyupmode option is not specified\n");
+                                        err = -EINVAL;
+                                        goto out_error;
+                                }
+
+                                MOUNT_FLAG(sb) |= COPYUP_FS_MOUNTER;
+
+                        } else {
+                                printk(KERN_WARNING "unionfs: could not parse copyup option value '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("copyupuid=", options, (sizeof("copyupuid=") - 1))) {
+                        char *tmp, *endptr;
+                        char found = 0;
+
+                        options = options + (sizeof("copyupuid=") - 1);
+                        /* check for the presence of the string 'copyup=mounter' in the options string
+                           firstchar points to the first character in the options string,
+                           end points to the last */
+                        for (tmp = firstchar; tmp < end; tmp++) {
+                                if (!strncmp("copyup=mounter", tmp, (sizeof("copyup=mounter") - 1))) {
+                                        found = 1;
+                                        break;
+                                }
+                        }
+                        if (!found) {
+                                printk(KERN_WARNING "unionfs: copyup option is not set to mounter\n");
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+
+                        stopd(sb)->copyupuid = simple_strtoul(options, &endptr, 0);
+
+                        if (*endptr) {
+                                printk(KERN_WARNING "unionfs: invalid copyupuid option '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("copyupgid=", options, (sizeof("copyupgid=") - 1))) {
+                        char *tmp, *endptr;
+                        char found = 0;
+
+                        options = options + (sizeof("copyupgid=") - 1);
+                        /* check for the presence of the string 'copyup=mounter' in the options string
+                           firstchar points to the first character in the options string,
+                           end points to the last */
+                        for (tmp = firstchar; tmp < end; tmp++) {
+                                if (!strncmp("copyup=mounter", tmp, (sizeof("copyup=mounter") - 1))) {
+                                        found = 1;
+                                        break;
+                                }
+                        }
+                        if (!found) {
+                                printk(KERN_WARNING "unionfs: copyup option is not set to mounter\n");
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+
+                        stopd(sb)->copyupgid = simple_strtoul(options, &endptr, 0);
+
+                        if (*endptr) {
+                                printk(KERN_WARNING "unionfs: invalid copyupgid option '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else if (!strncmp("copyupmode=", options, (sizeof("copyupmode=") - 1))) {
+                        char *tmp, *endptr;
+                        char found = 0;
+
+                        options = options + (sizeof("copyupmode=") - 1);
+                        /* check for the presence of the string 'copyup=mounter' in the options string
+                           firstchar points to the first character in the options string,
+                           end points to the last */
+                        for (tmp = firstchar; tmp < end; tmp++) {
+                                if (!strncmp("copyup=mounter", tmp, (sizeof("copyup=mounter") - 1))) {
+                                        found = 1;
+                                        break;
+                                }
+                        }
+                        if (!found) {
+                                printk(KERN_WARNING "unionfs: copyup option is not set to mounter\n");
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+
+                        stopd(sb)->copyupmode = simple_strtoul(options, &endptr, 0);
+
+                        if (*endptr) {
+                                printk(KERN_WARNING "unionfs: invalid copyupmode option '%s'\n", options);
+                                err = -EINVAL;
+                                goto out_error;
+                        }
+                } else {
+                        printk(KERN_WARNING "unionfs: unrecognized option '%s'\n", options);
+                        err = -EINVAL;
+                        goto out_error;
+                }
+                options = tmp + 1;
+        }
+	if (branches == -1) {
+                printk(KERN_WARNING "unionfs: dirs option required\n");
+		err = -EINVAL;
+		goto out_error;
+	}
+	ASSERT(branches == (hidden_root_info->udi_bend + 1));
+
+        goto out;
+
+out_error:
+        if (hidden_root_info->udi_dentry ) {
+                for (bindex = hidden_root_info->udi_bstart; bindex >= 0 && bindex <= hidden_root_info->udi_bend; bindex++) {
+                        if (hidden_root_info->udi_dentry[bindex]) {
+                                dput(hidden_root_info->udi_dentry[bindex]);
+                        }
+                        if (stopd(sb) && stohiddenmnt_index(sb, bindex)) {
+                                mntput(stohiddenmnt_index(sb, bindex));
+                        }
+                }
+                KFREE(hidden_root_info->udi_dentry);
+        }
+        if (hidden_root_info) {
+                KFREE(hidden_root_info);
+        }
+        if (stohiddenmnt_ptr(sb)) {
+                KFREE(stohiddenmnt_ptr(sb));
+		stohiddenmnt_ptr(sb) = NULL;
+        }
+        if (stopd(sb)->usi_sbcount) {
+                KFREE(stopd(sb)->usi_sbcount);
+		stopd(sb)->usi_sbcount = NULL;
+        }
+        if (stopd(sb)->usi_branchperms) {
+                KFREE(stopd(sb)->usi_branchperms);
+		stopd(sb)->usi_branchperms = NULL;
+        }
+        if (stohs_ptr(sb)) {
+                KFREE(stohs_ptr(sb));
+		stohs_ptr(sb) = NULL;
+        }
+
+        hidden_root_info = ERR_PTR(err);
+out:
+        print_exit_location();
+        return hidden_root_info;
+}
+
+#ifdef FIST_MALLOC_DEBUG
+/* for malloc debugging */
+atomic_t unionfs_malloc_counter;
+atomic_t unionfs_mallocs_outstanding;
+
+atomic_t unionfs_dget_counter;
+atomic_t unionfs_dgets_outstanding;
+
+void *
+unionfs_kmalloc(size_t len, int flag, int line, const char *file)
+{
+        void *ptr = (void *) kmalloc(len, flag);
+        if (ptr) {
+                atomic_inc(&unionfs_malloc_counter);
+                atomic_inc(&unionfs_mallocs_outstanding);
+                printk("KM:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_malloc_counter),atomic_read(&unionfs_mallocs_outstanding), ptr, line, file);
+        }
+        return ptr;
+}
+
+void
+unionfs_kfree(void *ptr, int line, const char *file)
+{
+        atomic_inc(&unionfs_malloc_counter);
+        atomic_dec(&unionfs_mallocs_outstanding);
+        printk("KF:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_malloc_counter), atomic_read(&unionfs_mallocs_outstanding), ptr, line, file);
+        kfree(ptr);
+}
+
+#endif /* FIST_MALLOC_DEBUG */
+
+
+/* for attach mode, we use a different ->read_super() in attach.c */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+super_block_t *
+#else
+static int
+#endif
+unionfs_read_super(super_block_t *sb, void *raw_data, int silent)
+{
+	int err = 0;
+
+        struct unionfs_dentry_info *hidden_root_info = NULL;
+        int bindex, bstart, bend;
+        unsigned long long maxbytes;
+
+        print_entry_location();
+
+#ifdef FIST_MALLOC_DEBUG
+        atomic_set(&unionfs_malloc_counter, 0); /* for malloc debugging */
+#endif /* FIST_MALLOC_DEBUG */
+
+        if (!raw_data){
+                printk(KERN_WARNING "unionfs_read_super: missing data argument\n");
+		err = -EINVAL;
+                goto out;
+        }
+
+        /*
+         * Allocate superblock private data
+         */
+        stopd_lhs(sb) = KMALLOC(sizeof(struct unionfs_sb_info), GFP_UNIONFS);
+        if (!stopd(sb)) {
+                printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
+		err = -ENOMEM;
+                goto out;
+        }
+        init_stopd(sb);
+
+        hidden_root_info = unionfs_parse_options(sb, raw_data);
+
+        if (IS_ERR(hidden_root_info)) {
+                printk(KERN_WARNING "unionfs_read_super: error while parsing options (err = %ld)\n", PTR_ERR(hidden_root_info));
+		err = PTR_ERR(hidden_root_info);
+                hidden_root_info = NULL;
+                goto out_free;
+        }
+        if(hidden_root_info->udi_bstart == -1) {
+		err = -ENOENT;
+                goto out_free;
+        }
+
+        /*
+         * Traverse the entire returned hidden_root_info structure
+         * to find if all inode exists
+         */
+        bstart = hidden_root_info->udi_bstart;
+        bend = hidden_root_info->udi_bend;
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                if (!hidden_root_info->udi_dentry[bindex]->d_inode) {
+		    printk(KERN_WARNING "unionfs_read_super: no directory to interpose on for branch %d\n", bindex);
+		    err = -ENOENT;
+		    goto out_dput;
+                }
+        }
+
+        /*
+         * set the hidden superblock field of upper superblock
+         */
+        sbstart(sb) = bstart;
+        sbend(sb) = bend;
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                stohs_index(sb, bindex) = hidden_root_info->udi_dentry[bindex]->d_sb;
+        }
+
+        /*
+         * Linux 2.4.2-ac3 and beyond has code in
+         * mm/filemap.c:generic_file_write() that requires sb->s_maxbytes
+         * to be populated.  If not set, all write()s under that sb will
+         * return 0.
+         *
+         * Linux 2.4.4+ automatically sets s_maxbytes to MAX_NON_LFS;
+         * the filesystem should override it only if it supports LFS.
+         */
+        /* non-SCA code is good to go with LFS */
+
+        /* UFS2: Max Bytes is the maximum bytes from among all the branches */
+        maxbytes = -1;
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                if ( maxbytes < stohs_index(sb, bindex)->s_maxbytes) {
+                        maxbytes = stohs_index(sb, bindex)->s_maxbytes;
+                }
+        }
+        sb->s_maxbytes = maxbytes;
+
+        sb->s_op = &unionfs_sops;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+	sb->s_export_op = &unionfs_export_ops;
+#endif
+
+        /*
+         * we can't use d_alloc_root if we want to use
+         * our own interpose function unchanged,
+         * so we simply replicate *most* of the code in d_alloc_root here
+         */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+        sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
+#else
+	sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len: 1});
+#endif
+        if (IS_ERR(sb->s_root)) {
+                printk(KERN_WARNING "unionfs_read_super: d_alloc failed\n");
+		err = PTR_ERR(sb->s_root);
+                goto out_dput;
+        }
+
+        sb->s_root->d_op = &unionfs_dops;
+        sb->s_root->d_sb = sb;
+        sb->s_root->d_parent = sb->s_root;
+
+        /* link the upper and lower dentries */
+        dtopd_lhs(sb->s_root) =  KMALLOC(sizeof(struct unionfs_dentry_info), GFP_UNIONFS);
+        if (!dtopd_lhs(sb->s_root)) {
+		err = -ENOMEM;
+                goto out_dput2;
+        }
+	init_dtopd(sb->s_root, sb);
+
+        /* allocate array of dentry pointers for the root dentry */
+        dtohd_ptr(sb->s_root) = KMALLOC(sizeof(dentry_t *) * sbmax(sb), GFP_UNIONFS);
+        if (!dtohd_ptr(sb->s_root)) {
+		err = -ENOMEM;
+                goto out_dput3;
+        }
+        init_dtohd_ptr(sb->s_root, sbmax(sb));
+
+        /* Set the hidden dentries for s_root */
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                set_dtohd_index(sb->s_root, bindex, hidden_root_info->udi_dentry[bindex]);
+        }
+        set_dbstart(sb->s_root, bstart);
+        set_dbend(sb->s_root, bend);
+
+        /* Set the generation number to one, since this is for the mount. */
+        atomic_set(&dtopd(sb->s_root)->udi_generation, 1);
+
+        /* call interpose to create the upper level inode */
+	if ((err = unionfs_interpose(sb->s_root, sb, 0)) )
+	{
+                goto out_dput4;
+        }
+        //    fist_print_dentry("unionfs_read_super OUT hidden_dentry", hidden_root);
+        //    fist_print_inode("unionfs_read_super OUT hidden_inode", hidden_root->d_inode);
+        // next line causes null ptr deref at mount(2) time
+        // fist_print_dentry("%s OUT sb->s_root", __FUNCTION__, sb->s_root);
+        goto out;
+
+out_dput4:
+        if (dtohd_ptr(sb->s_root)) {
+                KFREE(dtohd_ptr(sb->s_root));
+        }
+out_dput3:
+        if (dtopd(sb->s_root)) {
+                KFREE(dtopd(sb->s_root));
+        }
+out_dput2:
+        dput(sb->s_root);
+out_dput:
+        if (hidden_root_info && !IS_ERR(hidden_root_info) && hidden_root_info->udi_dentry) {
+                for (bindex = hidden_root_info->udi_bstart; bindex <= hidden_root_info->udi_bend; bindex++) {
+                        if (hidden_root_info->udi_dentry[bindex]) {
+                                dput(hidden_root_info->udi_dentry[bindex]);
+                        }
+                        if (stopd(sb) && stohiddenmnt_index(sb, bindex)) {
+                                mntput(stohiddenmnt_index(sb, bindex));
+                        }
+                }
+                KFREE(hidden_root_info->udi_dentry);
+                KFREE(hidden_root_info);
+		hidden_root_info = NULL;
+        }
+out_free:
+        if (stohiddenmnt_ptr(sb)) {
+                KFREE(stohiddenmnt_ptr(sb));
+        }
+        if (stopd(sb)->usi_sbcount) {
+                KFREE(stopd(sb)->usi_sbcount);
+        }
+        if (stopd(sb)->usi_branchperms) {
+                KFREE(stopd(sb)->usi_branchperms);
+        }
+        if (stohs_ptr(sb)) {
+                KFREE(stohs_ptr(sb));
+        }
+        if (stopd(sb)) {
+                KFREE(stopd(sb));
+                stopd_lhs(sb) = NULL;
+        }
+out:
+        if (hidden_root_info && !IS_ERR(hidden_root_info)) {
+                if (hidden_root_info->udi_dentry) {
+                        KFREE(hidden_root_info->udi_dentry);
+                }
+                KFREE(hidden_root_info);
+        }
+
+        print_exit_location();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	if (err) {
+		return ERR_PTR(err);
+	} else {
+		return sb;
+	}
+#else
+	return err;
+#endif
+}
+
+
+#ifdef SPLIT_VIEW_CACHES
+/* Given this super-block, lets make a brand new one. */
+super_block_t *
+unionfs_duplicate_super(super_block_t *sb)
+{
+        int err = 0;
+        super_block_t *ret_sb = NULL;
+        int i;
+
+        print_entry_location();
+
+        /* This is a bit of code from the bowels of mount that lets us get a handle on a brand new shiny super-block to use for unionfs. */
+        ret_sb = get_anon_super(sb->s_type, NULL, NULL);
+        if (!ret_sb) {
+                err = -ENOMEM;
+                goto out;
+        }
+
+        ret_sb->s_flags = sb->s_flags;
+
+        /* This duplicates a lot of read_super, but we basically need to make the super block from scratch. */
+
+        /*
+         * Allocate superblock private data
+         */
+        stopd(ret_sb) = KMALLOC(sizeof(struct unionfs_sb_info), GFP_UNIONFS);
+        if (!stopd(ret_sb)) {
+                err = -ENOMEM;
+                goto out;
+        }
+        init_stopd(ret_sb);
+        stopd(ret_sb)->usi_primary = sb;
+        list_add_tail(&stopd(ret_sb)->usi_altsupers, &stopd(sb)->usi_altsupers);
+
+        atomic_set(&stopd(ret_sb)->usi_generation, atomic_read(&stopd(sb)->usi_generation) + 1);
+
+        sbmax(ret_sb) = sbmax(sb);
+
+        /* allocate space for underlying pointers to super block */
+        stohs_ptr(ret_sb) = KMALLOC(sizeof(super_block_t *) * sbmax(ret_sb), GFP_UNIONFS);
+        if (!stohs_ptr(ret_sb)) {
+                err = -ENOMEM;
+                goto out;
+        }
+        /* No need to increase reference counts, because the vfsmounts should do that*/
+        sbstart(ret_sb) = sbstart(sb);
+        sbend(ret_sb) = sbend(sb);
+        for (i = sbstart(ret_sb); i <= sbend(ret_sb); i++) {
+                stohs_index(ret_sb, i) = stohs_index(sb, i);
+        }
+
+        /* Set all reference counts to zero. */
+        stopd(ret_sb)->usi_sbcount = KMALLOC(sizeof(atomic_t) * sbmax(ret_sb), GFP_UNIONFS);
+        if (!stopd(ret_sb)->usi_sbcount) {
+                err = -ENOMEM;
+                goto out;
+        }
+        for (i = 0; i < sbmax(ret_sb); i++) {
+                atomic_set(&stopd(ret_sb)->usi_sbcount[i], 0);
+        }
+
+        /* Set the permissions to none (we'll fix them up later). */
+        stopd(ret_sb)->usi_branchperms = KMALLOC(sizeof(int) * sbmax(ret_sb), GFP_UNIONFS);
+        if (!stopd(ret_sb)->usi_branchperms) {
+                err = -ENOMEM;
+                goto out;
+        }
+        for (i = 0; i < sbmax(sb); i++) {
+                stopd(ret_sb)->usi_branchperms[i] = stopd(sb)->usi_branchperms[i];
+        }
+
+        /* allocate space for array of pointers for underlying mount points */
+        stohiddenmnt_ptr(ret_sb) = KMALLOC(sizeof(struct vfsmount *) * sbmax(ret_sb), GFP_UNIONFS);
+        if (!stohiddenmnt_ptr(ret_sb)) {
+                err = -ENOMEM;
+                goto out;
+        }
+        init_stohiddenmnt_ptr(ret_sb, sbmax(ret_sb));
+        for (i = 0; i < sbmax(sb); i++) {
+                stohiddenmnt_index(ret_sb, i) = mntget(stohiddenmnt_index(sb, i));
+        }
+
+        ret_sb->s_maxbytes = sb->s_maxbytes;
+        ret_sb->s_op = &unionfs_sops;
+
+        /*
+         * we can't use d_alloc_root if we want to use
+         * our own interpose function unchanged,
+         * so we simply replicate *most* of the code in d_alloc_root here
+         */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+        sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
+#else
+	sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len: 1});
+#endif
+
+        if (IS_ERR(ret_sb->s_root)) {
+                printk(KERN_WARNING "unionfs_read_super: d_alloc failed\n");
+                goto out;
+        }
+
+        ret_sb->s_root->d_op = &unionfs_dops;
+        ret_sb->s_root->d_sb = ret_sb;
+        ret_sb->s_root->d_parent = ret_sb->s_root;
+
+        /* link the upper and lower dentries */
+        dtopd(ret_sb->s_root) = KMALLOC(sizeof(struct unionfs_dentry_info), GFP_UNIONFS);
+        if (!dtopd(ret_sb->s_root)) {
+                goto out;
+        }
+
+        /* allocate array of dentry pointers for the root dentry */
+        dtohd_ptr(ret_sb->s_root) = KMALLOC(sizeof(dentry_t *) * sbmax(ret_sb), GFP_UNIONFS);
+        if (!dtohd_ptr(ret_sb->s_root)) {
+                goto out;
+        }
+        init_dtohd_ptr(ret_sb->s_root, sbmax(ret_sb));
+
+
+        /* Set the hidden dentries for s_root */
+        for (i = sbstart(ret_sb); i <= sbend(ret_sb); i++) {
+                dtohd_index(ret_sb->s_root, i) = dget(dtohd_index(sb->s_root, i));
+        }
+        dbstart(ret_sb->s_root) = sbstart(ret_sb);
+        dbend(ret_sb->s_root) = sbend(ret_sb);
+
+        /* Set the generation number to one, since this is for the mount. */
+        atomic_set(&dtopd(ret_sb->s_root)->udi_generation, atomic_read(&stopd(ret_sb)->usi_generation));
+
+        /* call interpose to create the upper level inode */
+        if ((err = unionfs_interpose(ret_sb->s_root, ret_sb, 0))) {
+                goto out;
+        }
+
+        /* this is after we do the read super equivalent. */
+        ret_sb->s_flags |= MS_ACTIVE;
+
+        /* this is the relavant portion of do_kern_mount */
+        up_write(&ret_sb->s_umount);
+
+out:
+        if (err) {
+                if (ret_sb && !IS_ERR(ret_sb)) {
+                        if (stopd(ret_sb)) {
+                                if (stopd(ret_sb)->usi_sbcount) {
+                                        KFREE(stopd(ret_sb)->usi_sbcount);
+                                }
+                                if (stopd(ret_sb)->usi_branchperms) {
+                                        KFREE(stopd(ret_sb)->usi_branchperms);
+                                }
+                                if (stohs_ptr(ret_sb)) {
+                                        KFREE(stohs_ptr(ret_sb));
+                                }
+                                if (stohiddenmnt_ptr(ret_sb)) {
+                                        for (i = 0; i < sbmax(ret_sb); i++) {
+                                                if (stohiddenmnt_index(ret_sb, i)) {
+                                                        mntput(stohiddenmnt_index(ret_sb, i));
+                                                }
+                                        }
+                                }
+                                KFREE(stopd(ret_sb));
+                                stopd(sb) = NULL;
+                        }
+
+                        if (ret_sb->s_root) {
+                                dput(ret_sb->s_root);
+                        }
+
+                        deactivate_super(ret_sb);
+                        remove_super(ret_sb);
+                }
+                ret_sb = ERR_PTR(err);
+        }
+
+        print_exit_location();
+        return ret_sb;
+}
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+/*----*/
+// this structure *must* be static!!! (or the memory for 'name' gets
+// corrupted in 2.3...)
+static DECLARE_FSTYPE(unionfs_fs_type, "unionfs", unionfs_read_super, 0);
+#else
+static struct super_block *unionfs_get_sb(struct file_system_type *fs_type,
+                                         int flags, const char *dev_name,
+                                         void *raw_data) {
+	return get_sb_nodev( fs_type, flags, raw_data, unionfs_read_super);
+}
+
+void unionfs_kill_block_super(struct super_block *sb)
+{
+	generic_shutdown_super(sb);
+/*
+ *	XXX: BUG: Halcrow: Things get unstable sometime after this point:
+ *
+ *	lib/rwsem-spinlock.c:127: spin_is_locked on uninitialized
+ *	spinlock a1c953d8.
+ *
+ *	fs/fs-writeback.c:402: spin_lock(fs/super.c:a0381828) already
+ *	locked by fs/fs-writeback.c/402
+ *
+ *	Apparently, someone's not releasing a lock on sb_lock...
+*/
+}
+
+static struct file_system_type unionfs_fs_type = {
+	.owner          = THIS_MODULE,
+	.name           = "unionfs",
+	.get_sb         = unionfs_get_sb,
+	.kill_sb        = unionfs_kill_block_super,
+	.fs_flags       = 0,
+};
+
+#endif
+static int __init init_unionfs_fs(void)
+{
+	int err;
+        printk("Registering unionfs version $Id: main.c,v 1.84 2005/02/22 18:42:58 cwright Exp $\n");
+	if ((err = init_filldir_cache())) {
+		return err;
+	}
+        return register_filesystem(&unionfs_fs_type);
+}
+static void __exit exit_unionfs_fs(void)
+{
+        printk("Unregistering unionfs version $Id: main.c,v 1.84 2005/02/22 18:42:58 cwright Exp $\n");
+	destroy_filldir_cache();
+        unregister_filesystem(&unionfs_fs_type);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+EXPORT_NO_SYMBOLS;
+#endif
+MODULE_AUTHOR("Erez Zadok <ezk@cs.columbia.edu>");
+MODULE_DESCRIPTION("FiST-generated unionfs filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_unionfs_fs)
+module_exit(exit_unionfs_fs)
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ * vim:shiftwidth=4
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/missing_vfs_funcs.h linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/missing_vfs_funcs.h
--- linux-2.6.12-rc2-mm3/fs/unionfs/missing_vfs_funcs.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/missing_vfs_funcs.h	2005-04-25 11:56:14.545654808 -0700
@@ -0,0 +1,127 @@
+#ifndef __MISSING_VFS_FUNCS_H_
+#define __MISSING_VFS_FUNCS_H_
+/*
+ * DQ: These functions used to be in the actual vfs but now they are missing so we redefine them here if we are using 2.6 this code is used in the 2.6 templates
+ * Common dentry functions for inclusion in the VFS
+ * or in other stackable file systems.  Some of these
+ * functions were in linux/fs/ C (VFS) files.
+ *
+ */
+
+/*
+ * Locking the parent is needed to:
+ *  - serialize directory operations
+ *  - make sure the parent doesn't change from
+ *    under us in the middle of an operation.
+ *
+ * NOTE! Right now we'd rather use a "struct inode"
+ * for this, but as I expect things to move toward
+ * using dentries instead for most things it is
+ * probably better to start with the conceptually
+ * better interface of relying on a path of dentries.
+ */
+static inline struct dentry *lock_parent(struct dentry *dentry) {
+        struct dentry *dir = dget(dentry->d_parent);
+
+        down(&dir->d_inode->i_sem);
+        return dir;
+}
+
+static inline struct dentry *get_parent(struct dentry *dentry) {
+        return dget(dentry->d_parent);
+}
+
+static inline void unlock_dir(struct dentry *dir)
+{
+        up(&dir->d_inode->i_sem);
+        dput(dir);
+}
+
+/*
+ * Whee.. Deadlock country. Happily there are only two VFS
+ * operations that does this..
+ */
+static inline void double_down(struct semaphore *s1, struct semaphore *s2)
+{
+        if (s1 != s2) {
+                if ((unsigned long) s1 < (unsigned long) s2) {
+                        struct semaphore *tmp = s2;
+                        s2 = s1; s1 = tmp;
+                }
+                down(s1);
+        }
+        down(s2);
+}
+
+/*
+ * Ewwwwwwww... _triple_ lock. We are guaranteed that the 3rd argument is
+ * not equal to 1st and not equal to 2nd - the first case (target is parent of
+ * source) would be already caught, the second is plain impossible (target is
+ * its own parent and that case would be caught even earlier). Very messy.
+ * I _think_ that it works, but no warranties - please, look it through.
+ * Pox on bloody lusers who mandated overwriting rename() for directories...
+ */
+
+static inline void triple_down(struct semaphore *s1,
+                               struct semaphore *s2,
+                               struct semaphore *s3)
+{
+        if (s1 != s2) {
+                if ((unsigned long) s1 < (unsigned long) s2) {
+                        if ((unsigned long) s1 < (unsigned long) s3) {
+                                struct semaphore *tmp = s3;
+                                s3 = s1; s1 = tmp;
+                        }
+                        if ((unsigned long) s1 < (unsigned long) s2) {
+                                struct semaphore *tmp = s2;
+                                s2 = s1; s1 = tmp;
+                        }
+                } else {
+                        if ((unsigned long) s1 < (unsigned long) s3) {
+                                struct semaphore *tmp = s3;
+                                s3 = s1; s1 = tmp;
+                        }
+                        if ((unsigned long) s2 < (unsigned long) s3) {
+                                struct semaphore *tmp = s3;
+                                s3 = s2; s2 = tmp;
+                        }
+                }
+                down(s1);
+        } else if ((unsigned long) s2 < (unsigned long) s3) {
+                struct semaphore *tmp = s3;
+                s3 = s2; s2 = tmp;
+        }
+        down(s2);
+        down(s3);
+}
+
+static inline void double_up(struct semaphore *s1, struct semaphore *s2)
+{
+        up(s1);
+        if (s1 != s2)
+                up(s2);
+}
+
+static inline void triple_up(struct semaphore *s1,
+                             struct semaphore *s2,
+                             struct semaphore *s3)
+{
+        up(s1);
+        if (s1 != s2)
+                up(s2);
+        up(s3);
+}
+
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+        double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem);
+}
+
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+        double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem);
+        dput(d1);
+        dput(d2);
+}
+
+#endif //__MISSING_VFS_FUNCS_H_
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/print.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/print.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/print.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/print.c	2005-04-25 11:56:14.547654504 -0700
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: print.c,v 1.46 2005/01/10 13:53:37 cwright Exp $
+ */
+
+/* Print debugging functions */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+static int fist_debug_var = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define PageUptodate(page) Page_Uptodate(page)
+#endif
+
+
+/* get value of debugging variable */
+int
+fist_get_debug_value(void)
+{
+  return fist_debug_var;
+}
+
+/* set debug level variable and return the previous value */
+int
+fist_set_debug_value(int val)
+{
+  int prev = fist_debug_var;
+
+  fist_debug_var = val;
+  fist_dprint(1, "unionfs: setting debug level to %d\n", val);
+  return prev;
+}
+
+/*
+ * Utilities used by both client and server
+ * Standard levels:
+ * 0) no debugging
+ * 1) hard failures
+ * 2) soft failures
+ * 3) current test software
+ * 4) main procedure entry points
+ * 5) main procedure exit points
+ * 6) utility procedure entry points
+ * 7) utility procedure exit points
+ * 8) obscure procedure entry points
+ * 9) obscure procedure exit points
+ * 10) random stuff
+ * 11) all <= 1
+ * 12) all <= 2
+ * 13) all <= 3
+ * ...
+ */
+
+static char buf[4096];
+
+void
+fist_dprint_internal(const char *file, const char *function, int line, int level, char *str,...)
+{
+  va_list ap;
+  int var = fist_get_debug_value();
+
+  if (level >= 10 || level < 0) {
+      printk("<0>fist_dprint_internal: Invalid level passed from %s:%s:%d\n", file, function, line);
+  }
+
+  if (var == level || (var > 10 && (var - 10) >= level)) {
+    va_start(ap, str);
+    vsnprintf(buf, 4096, str, ap);
+    printk("%s", buf);
+    va_end(ap);
+  }
+  return;
+}
+
+
+static int num_indents = 0;
+char indent_buf[80] = "                                                                               ";
+char *
+add_indent(void)
+{
+    indent_buf[num_indents] = ' ';
+    num_indents++;
+    if (num_indents > 79)
+	num_indents = 79;
+    indent_buf[num_indents] = '\0';
+    return indent_buf;
+}
+
+char *
+del_indent(void)
+{
+    if (num_indents <= 0)
+	return "<IBUG>";
+    indent_buf[num_indents] = ' ';
+    num_indents--;
+    indent_buf[num_indents] = '\0';
+    return indent_buf;
+}
+
+void
+fist_print_buffer_flags(char *str, struct buffer_head *buffer)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+    /*
+     * XXX: 2.4.10 removed buffer_protected state flag.
+     */
+# define buffer_protected(buffer) (-1)
+#endif /* kernel 2.4.10 and newer */
+
+    if (!buffer) {
+	printk("PBF %s 0x%p\n", str, buffer);
+	return;
+    }
+
+    fist_dprint(8, "PBF %s 0x%p: Uptodate:%d Dirty:%d Locked:%d Req:%d Protected:%d\n",
+		str,
+		buffer,
+		buffer_uptodate(buffer),
+		buffer_dirty(buffer),
+		buffer_locked(buffer),
+		buffer_req(buffer),
+		buffer_protected(buffer)
+		);
+}
+
+
+void
+fist_print_page_flags(char *str, page_t *page)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+    /*
+     * XXX: 2.4.10 forgot to export the ksym to swapper_space,
+     * which is used in PageSwapCache().
+     */
+# define fist_PageSwapCache(page) (-1)
+#else
+# define fist_PageSwapCache(page) (PageSwapCache(page) ? 1 : 0)
+#endif /* kernel 2.4.10 and newer */
+
+#ifdef PageDecrAfter
+#define fist_PageDecrAfter(page)	(PageDecrAfter(page) ? 1 : 0)
+#else  /* not PageDecrAfter */
+#define fist_PageDecrAfter(page)	(-1)
+#endif /* not PageDecrAfter */
+
+    fist_dprint(8, "PPF %s 0x%p/0x%lx: Locked:%d Error:%d Referenced:%d Uptodate:%d DecrAfter:%d Slab:%d SwapCache:%d Reserved:%d\n",
+		str,
+		page,
+		page->index,
+		(PageLocked(page) ? 1 : 0),
+		(PageError(page) ? 1 : 0),
+		(PageReferenced(page) ? 1 : 0),
+		(PageUptodate(page) ? 1 : 0),
+		fist_PageDecrAfter(page),
+		(PageSlab(page) ? 1 : 0),
+		fist_PageSwapCache(page),
+		(PageReserved(page) ? 1 : 0)
+		);
+}
+
+
+void
+fist_print_inode(char *str, const inode_t *inode)
+{
+    inode_t *hidden_inode;
+
+    if (!inode) {
+	printk("PI:%s: NULL INODE PASSED!\n", str);
+	return;
+    }
+    fist_dprint(8, "\n");
+    fist_dprint(8, "=====Inode Data=====\n");
+    fist_dprint(8, "PI:%s: %s=%lu\n", str, "i_ino", inode->i_ino);
+    fist_dprint(8, "PI:%s: %s=%u\n", str, "i_count", atomic_read(&inode->i_count));
+    fist_dprint(8, "PI:%s: %s=%u\n", str, "i_nlink", inode->i_nlink);
+    fist_dprint(8, "PI:%s: %s=%o\n", str, "i_mode", inode->i_mode);
+    fist_dprint(8, "PI:%s: %s=%llu\n", str, "i_size", inode->i_size);
+    fist_dprint(8, "PI:%s: %s=%p\n", str, "i_op", inode->i_op);
+    fist_dprint(8, "PI:%s: %s=%p (%s)\n", str, "i_sb",
+		inode->i_sb,
+		(inode->i_sb ? sbt(inode->i_sb) : "NullTypeSB")
+		);
+
+    if(itopd(inode)) {
+	PASSERT(itopd(inode));
+	fist_dprint(8, "ibstart=%d, ibend=%d\n", ibstart(inode), ibend(inode));
+    }
+    fist_dprint(8, "\n");   /* printing underlying inodes, if any */
+
+    if(itopd(inode)) {
+	int bindex;
+
+	fist_dprint(8, "Hidden Inodes\n");
+	for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+
+	    hidden_inode = itohi_index(inode, bindex);
+	    fist_dprint(8, "---------------------\n");
+
+            if(!hidden_inode) {
+		fist_dprint(8, "Hidden Inode # %d = NULL", bindex);
+		continue;
+	    }
+
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%lu\n", bindex, str, "i_ino", hidden_inode->i_ino);
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%u\n", bindex, str, "i_count", atomic_read(&hidden_inode->i_count));
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%u\n", bindex, str, "i_nlink", hidden_inode->i_nlink);
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%o\n", bindex, str, "i_mode", hidden_inode->i_mode);
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%llu\n", bindex, str, "i_size", hidden_inode->i_size);
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%p\n", bindex, str, "i_op", hidden_inode->i_op);
+	    fist_dprint(8, "Hidden Inode # %d: %s: %s=%p (%s)\n", bindex, str, "i_sb",
+			hidden_inode->i_sb,
+			(hidden_inode->i_sb ? sbt(hidden_inode->i_sb) : "NullTypeSB")
+			);
+	}
+    }
+}
+
+
+void
+fist_print_pte_flags(char *str, const page_t *page)
+{
+    unsigned long address;
+
+    PASSERT(page);
+    address = page->index;
+    fist_dprint(8, "PTE-FL:%s index=0x%lx\n", str, address);
+}
+
+
+void
+fist_print_vma(char *str, const vm_area_t *vma)
+{
+    return;
+}
+
+
+void
+fist_print_file(char *str, const file_t *file)
+{
+    file_t *hidden_file;
+
+    if (!file) {
+	fist_dprint(8, "PF:%s: NULL FILE PASSED!\n", str);
+	return;
+    }
+    fist_dprint(8, "PF:%s: %s=0x%p\n", str, "f_dentry", file->f_dentry);
+    fist_dprint(8, "PF:%s: file's dentry name=%s\n", str, file->f_dentry->d_name.name);
+    if (file->f_dentry->d_inode) {
+	PASSERT(file->f_dentry->d_inode);
+	fist_dprint(8, "PF:%s: %s=%lu\n", str,"f_dentry->d_inode->i_ino", file->f_dentry->d_inode->i_ino);
+	fist_dprint(8, "PF:%s: %s=%o\n", str, "f_dentry->d_inode->i_mode", file->f_dentry->d_inode->i_mode);
+    }
+    fist_dprint(8, "PF:%s: %s=0x%p\n", str, "f_op", file->f_op);
+    fist_dprint(8, "PF:%s: %s=0x%x\n", str, "f_mode", file->f_mode);
+    fist_dprint(8, "PF:%s: %s=0x%llu\n", str, "f_pos", file->f_pos);
+    fist_dprint(8, "PF:%s: %s=%u\n", str, "f_count", atomic_read(&file->f_count));
+    fist_dprint(8, "PF:%s: %s=0x%x\n", str, "f_flags", file->f_flags);
+
+    fist_dprint(8, "PF:%s: %s=%lu\n", str, "f_version", file->f_version);
+    if(ftopd(file)) {
+	fist_dprint(8, "PF:%s: fbstart=%d, fbend=%d\n", str, fbstart(file), fbend(file));
+    }
+    if(ftopd(file)) {
+	int bindex;
+	for (bindex = fbstart(file); bindex <= fbend(file); bindex++) {
+
+	    hidden_file = ftohf_index(file, bindex);
+	    if(!hidden_file) {
+		fist_dprint(8, "PF:%s: HF#%d is NULL\n", str, bindex);
+		continue;
+	    }
+
+	    fist_dprint(8, "PF:%s: HF#%d %s=0x%p\n", str, bindex, "f_dentry", hidden_file->f_dentry);
+	    if (hidden_file->f_dentry->d_inode) {
+		PASSERT(hidden_file->f_dentry->d_inode);
+	        fist_dprint(8, "PF:%s: HF#%d: %s=%lu\n", str, bindex, "f_dentry->d_inode->i_ino", hidden_file->f_dentry->d_inode->i_ino);
+	        fist_dprint(8, "PF:%s: HF#%d: %s=%o\n", str, bindex, "f_dentry->d_inode->i_mode", hidden_file->f_dentry->d_inode->i_mode);
+	    }
+	    fist_dprint(8, "PF:%s: HF#%d: %s=0x%p\n", str, bindex, "f_op", hidden_file->f_op);
+	    fist_dprint(8, "PF:%s: HF#%d: %s=0x%x\n", str, bindex, "f_mode", hidden_file->f_mode);
+	    fist_dprint(8, "PF:%s: HF#%d: %s=%llu\n", str, bindex, "f_pos", hidden_file->f_pos);
+	    fist_dprint(8, "PF:%s: HF#%d: %s=%u\n", str, bindex, "f_count", atomic_read(&hidden_file->f_count));
+	    fist_dprint(8, "PF:%s: HF#%d: %s=0x%x\n", str, bindex,"f_flags", hidden_file->f_flags);
+	    fist_dprint(8, "PF:%s: HF#%d: %s=%lu\n", str, bindex, "f_version", hidden_file->f_version);
+	}
+    }
+}
+
+
+void
+fist_print_dentry(char *str, const dentry_t *dentry)
+{
+    dentry_t *hidden_dentry = NULL;
+
+    if (!dentry) {
+	fist_dprint(8, "PD:%s: NULL DENTRY PASSED!\n", str);
+	return;
+    }
+    if (IS_ERR(dentry)) {
+	fist_dprint(8, "PD:%s: ERROR DENTRY (%ld)!\n", str, PTR_ERR(dentry));
+	return;
+    }
+    PASSERT(dentry);
+
+    fist_dprint(8, "=====Dentry Data (%p)=====\n", dentry);
+
+    fist_dprint(8, "PD:%s: %s=%d\n", str, "d_count",
+		atomic_read(&dentry->d_count));
+    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_flags", (int) dentry->d_flags);
+    fist_dprint(8, "PD:%s: %s=%p (%s)\n", str, "d_inode",
+		dentry->d_inode,
+		(dentry->d_inode ? sbt(dentry->d_inode->i_sb) : "nil") );
+    fist_dprint(8, "PD:%s: %s=%p (%s)\n", str, "d_parent",
+		dentry->d_parent,
+		(dentry->d_parent ? sbt(dentry->d_parent->d_sb) : "nil") );
+    fist_dprint(8, "PD:%s: %s=\"%s\"\n", str, "d_parent->d_name.name", dentry->d_parent->d_name.name);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_mounts", (int) dentry->d_mounts);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_covers", (int) dentry->d_covers);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_hash", (int) & dentry->d_hash);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_lru", (int) & dentry->d_lru);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_child", (int) & dentry->d_child);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_subdirs", (int) & dentry->d_subdirs);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_alias", (int) & dentry->d_alias);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_name", (int) & dentry->d_name);
+    //
+    fist_dprint(8, "PD:%s: %s=\"%s\"\n", str, "d_name.name", dentry->d_name.name);
+    if (dentry->d_inode) {
+	char t;
+	if (S_ISDIR(dentry->d_inode->i_mode))
+		t = 'd';
+	else if (S_ISLNK(dentry->d_inode->i_mode))
+		t = 'l';
+	else if (S_ISCHR(dentry->d_inode->i_mode))
+		t = 'c';
+	else if (S_ISBLK(dentry->d_inode->i_mode))
+		t = 'b';
+	else if (S_ISREG(dentry->d_inode->i_mode))
+		t = 'f';
+	else
+		t = '?';
+	fist_dprint(8, "PD:%s: dentry->d_inode->i_mode: %c%o\n", str, t, dentry->d_inode->i_mode);
+    }
+    fist_dprint(8, "PD:%s: %s=%d\n", str, "d_name.len", (int) dentry->d_name.len);
+    //    fist_dprint(8, "PD:%s: %s=%x\n", str, "d_name.hash", (int) dentry->d_name.hash);
+    //    fist_dprint(8, "PD:%s: %s=%lu\n", str, "d_time", dentry->d_time);
+    fist_dprint(8, "PD:%s: %s=%p\n", str, "d_op", dentry->d_op);
+    fist_dprint(8, "PD:%s: %s=%p (%s)\n", str, "d_sb",
+		dentry->d_sb, sbt(dentry->d_sb));
+    //    fist_dprint(8, "PD:%s: %s=%lu\n", str, "d_reftime", dentry->d_reftime);
+    fist_dprint(8, "PD:%s: %s=%p\n", str, "d_fsdata", dentry->d_fsdata);
+    // don't do this, it's not zero-terminated!!!
+    //    fist_dprint(8, "PD:%s: %s=\"%s\"\n", str, "d_iname", dentry->d_iname);
+    //    fist_dprint(8, "\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    fist_dprint(8, "PD:%s:%s=%d\n", str, "list_empty(d_hash)", list_empty(&((dentry_t *)dentry)->d_hash));
+#else
+    fist_dprint(8, "PD:%s: %s=%d\n", str, "hlist_unhashed(d_hash)", hlist_unhashed(&((dentry_t *)dentry)->d_hash));
+#endif
+
+    if(dtopd(dentry)) {
+        fist_dprint(8, "PD:%s: dbstart=%d, dbend=%d\n", str, dbstart(dentry), dbend(dentry));
+    }
+    fist_dprint(8, "\n");
+
+    if(dtopd(dentry) && (dbstart(dentry) != -1)) {
+	int bindex;
+
+	PASSERT(dtopd(dentry));
+
+	fist_dprint(8, "PD:%s: Hidden Dentries:\n", str);
+
+	for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {
+
+	    hidden_dentry = dtohd_index(dentry, bindex);
+	    fist_dprint(8, "---------------------\n");
+	    if(!hidden_dentry) {
+		fist_dprint(8, "%s: HD#%d = NULL\n", str, bindex);
+		continue;
+	    }
+
+	    fist_dprint(8, "%s: HD#%d: %s=%d\n", str, bindex, "d_count",
+		    atomic_read(&hidden_dentry->d_count));
+	    fist_dprint(8, "%s: HD#%d: %s=%x\n", str, bindex, "d_flags", (int) hidden_dentry->d_flags);
+	    fist_dprint(8, "%s: HD#%d: %s=%p (%s)\n", str, bindex, "d_inode",
+		    hidden_dentry->d_inode,
+		    (hidden_dentry->d_inode ? sbt(hidden_dentry->d_inode->i_sb) : "nil") );
+	    fist_dprint(8, "%s: HD#%d: %s=%p (%s)\n", str, bindex, "d_parent",
+		    hidden_dentry->d_parent,
+		    (hidden_dentry->d_parent ? sbt(hidden_dentry->d_parent->d_sb) : "nil") );
+	    fist_dprint(8, "%s: HD#%d: %s=\"%s\"\n", str, bindex, "d_name.name", hidden_dentry->d_name.name);
+	    fist_dprint(8, "%s: HD#%d: %s=\"%s\"\n", str, bindex, "d_parent->d_name.name", hidden_dentry->d_parent->d_name.name);
+	    fist_dprint(8, "%s: HD#%d: %s=%d\n", str, bindex, "d_name.len", (int) hidden_dentry->d_name.len);
+	    if (hidden_dentry->d_inode) {
+		char t;
+		if (S_ISDIR(hidden_dentry->d_inode->i_mode))
+		    t = 'd';
+		else if (S_ISLNK(hidden_dentry->d_inode->i_mode))
+		    t = 'l';
+		else if (S_ISCHR(hidden_dentry->d_inode->i_mode))
+		    t = 'c';
+		else if (S_ISBLK(hidden_dentry->d_inode->i_mode))
+		    t = 'b';
+		else if (S_ISREG(hidden_dentry->d_inode->i_mode))
+		    t = '-';
+		else
+		    t = '?';
+		fist_dprint(8, "%s: HD#%d: dentry->d_inode->i_mode: %c%o\n", str, bindex, t, hidden_dentry->d_inode->i_mode);
+	    }
+	    fist_dprint(8, "%s: HD#%d: %s=%p\n", str, bindex, "d_op", hidden_dentry->d_op);
+	    fist_dprint(8, "%s: HD#%d: %s=%p (%s)\n", str, bindex, "d_sb",
+		    hidden_dentry->d_sb, sbt(hidden_dentry->d_sb));
+	    fist_dprint(8, "%s: HD#%d: %s=%p\n", str, bindex, "d_fsdata", hidden_dentry->d_fsdata);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	    fist_dprint(8, "%s: HD#%d: %s=%d\n", str, bindex, "list_empty(d_hash)", list_empty(&((dentry_t *)hidden_dentry)->d_hash));
+#else
+	    fist_dprint(8, "%s: HD#%d: %s=%d\n", str, bindex, "hlist_unhashed(d_hash)", hlist_unhashed(&((dentry_t *)dentry)->d_hash));
+#endif
+
+	}
+    }
+}
+
+void
+fist_checkinode(inode_t *inode, char *msg)
+{
+    if (!inode) {
+	printk(KERN_WARNING "fist_checkinode - inode is NULL! (%s)\n", msg);
+	return;
+    }
+    if (!itopd(inode)) {
+	fist_dprint(8, "fist_checkinode(%ld) - no private data (%s)\n", inode->i_ino, msg);
+	return;
+    }
+    if ((itopd(inode)->b_start < 0) || !itohi(inode)) {
+	fist_dprint(8, "fist_checkinode(%ld) - underlying is NULL! (%s)\n", inode->i_ino, msg);
+	return;
+    }
+    if (!inode->i_sb) {
+	fist_dprint(8, "fist_checkinode(%ld) - inode->i_sb is NULL! (%s)\n", inode->i_ino, msg);
+	return;
+    }
+    fist_dprint(8, "inode->i_sb->s_type %p\n", inode->i_sb->s_type);
+    if (!inode->i_sb->s_type) {
+	fist_dprint(8, "fist_checkinode(%ld) - inode->i_sb->s_type is NULL! (%s)\n", inode->i_ino, msg);
+	return;
+    }
+    fist_dprint(6, "CI: %s: inode->i_count = %d, hidden_inode->i_count = %d, inode = %lu, sb = %s, hidden_sb = %s\n",
+		msg, atomic_read(&inode->i_count),
+		itopd(inode)->b_start >= 0 ? atomic_read(&itohi(inode)->i_count): -1, inode->i_ino,
+		inode->i_sb->s_type->name, itopd(inode)->b_start >= 0 ? itohi(inode)->i_sb->s_type->name: "(none)");
+}
+
+void
+fist_print_sb(char *str, const super_block_t *sb)
+{
+    super_block_t *hidden_superblock;
+
+    if (!sb) {
+        fist_dprint(8, "PSB:%s: NULL SB PASSED!\n", str);
+        return;
+    }
+
+    fist_dprint(8, "PSB:%s: %s=%u\n", str, "s_blocksize", (int) sb->s_blocksize);
+    fist_dprint(8, "PSB:%s: %s=%u\n", str, "s_blocksize_bits", (int) sb->s_blocksize_bits);
+    fist_dprint(8, "PSB:%s: %s=0x%x\n", str, "s_flags", (int) sb->s_flags);
+    fist_dprint(8, "PSB:%s: %s=0x%x\n", str, "s_magic", (int) sb->s_magic);
+    fist_dprint(8, "PSB:%s: %s=%llu\n", str, "s_maxbytes", sb->s_maxbytes);
+    fist_dprint(8, "PSB:%s: %s=%d\n", str, "s_count", (int) sb->s_count);
+    fist_dprint(8, "PSB:%s: %s=%d\n", str, "s_active", (int) atomic_read(&sb->s_active));
+    if(stopd(sb))
+	fist_dprint(8, "sbstart=%d, sbend=%d\n", sbstart(sb), sbend(sb));
+    fist_dprint(8, "\n");
+
+    if(stopd(sb)) {
+	int bindex;
+	for (bindex = sbstart(sb); bindex <= sbend(sb); bindex++) {
+	    hidden_superblock = stohs_index(sb, bindex);
+	    if(!hidden_superblock) {
+		fist_dprint(8, "PSB:%s: HS#%d is NULL", str, bindex);
+		continue;
+	    }
+
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=%u\n", str, bindex, "s_blocksize", (int) hidden_superblock->s_blocksize);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=%u\n", str, bindex, "s_blocksize_bits", (int) hidden_superblock->s_blocksize_bits);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=0x%x\n", str, bindex, "s_flags", (int) hidden_superblock->s_flags);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=0x%x\n", str, bindex, "s_magic", (int) hidden_superblock->s_magic);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=%llu\n", str, bindex, "s_maxbytes", hidden_superblock->s_maxbytes);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=%d\n", str, bindex, "s_count", (int) hidden_superblock->s_count);
+	    fist_dprint(8, "PSB:%s: HS#%d: %s=%d\n", str, bindex, "s_active", (int) atomic_read(&hidden_superblock->s_active));
+	}
+    }
+}
+
+
+
+
+/*
+ * vim:shiftwidth=4
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/rdstate.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/rdstate.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/rdstate.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/rdstate.c	2005-04-25 11:56:14.548654352 -0700
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: rdstate.c,v 1.11 2005/02/22 18:42:58 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+/* This file contains the routines for maintaining readdir state. */
+/* There are two structures here, rdstate which is a hash table
+ * of the second structure which is a filldir_node. */
+
+
+/* This is a kmem_cache_t for filldir nodes, because we allocate a lot of them
+ * and they shouldn't waste memory.  If the node has a small name (as defined
+ * by the dentry structure), then we use an inline name to preserve kmalloc
+ * space. */
+static kmem_cache_t *unionfs_filldir_cachep;
+int init_filldir_cache() {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+	unionfs_filldir_cachep = kmem_cache_create("unionfs_filldir", sizeof(struct filldir_node), 0, SLAB_RECLAIM_ACCOUNT, NULL, NULL);
+#else
+	unionfs_filldir_cachep = kmem_cache_create("unionfs_filldir", sizeof(struct filldir_node), 0, 0, NULL, NULL);
+#endif
+	if (!unionfs_filldir_cachep) {
+		return -ENOMEM;
+	}
+	return 0;
+}
+void destroy_filldir_cache() {
+	if (kmem_cache_destroy(unionfs_filldir_cachep)) {
+		printk(KERN_INFO "unionfs_filldir_cache: not all structures were freed\n");
+	}
+	return;
+}
+
+
+/* This is a tuning parameter that tells us roughly how big to make the
+ * hash table in directory entries per page.  This isn't perfect, but
+ * at least we get a hash table size that shouldn't be too overloaded.
+ * The following averages are based on my home directory.
+ * 14.44693	Overall
+ * 12.29 	Single Page Directories
+ * 117.93	Multi-page directories
+ */
+#define DENTPAGE 4096
+#define DENTPERONEPAGE 12
+#define DENTPERPAGE 118
+#define MINHASHSIZE 1
+int guesstimate_hash_size(struct inode *inode) {
+	struct inode *hidden_inode;
+	int bindex;
+	int hashsize = MINHASHSIZE;
+
+	if (itopd(inode)->uii_hashsize > 0) {
+		return itopd(inode)->uii_hashsize;
+	}
+
+	for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+		if ((hidden_inode = itohi_index(inode, bindex))) {
+			if (hidden_inode->i_size == DENTPAGE) {
+				hashsize += DENTPERONEPAGE;
+			} else {
+				hashsize += (hidden_inode->i_size / DENTPAGE) * DENTPERPAGE;
+			}
+		}
+	}
+
+	return hashsize;
+}
+
+int init_rdstate(struct file *file) {
+	PASSERT(file);
+	ASSERT(ftopd(file)->rdstate == NULL);
+	PASSERT(file->f_dentry);
+	PASSERT(file->f_dentry->d_inode);
+
+	ftopd(file)->rdstate = alloc_rdstate(file->f_dentry->d_inode, fbstart(file));
+	if (!ftopd(file)->rdstate) {
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex) {
+	int i = 0;
+	int hashsize;
+	int mallocsize = sizeof(struct unionfs_dir_state);
+	struct unionfs_dir_state *rdstate;
+
+	hashsize = guesstimate_hash_size(inode);
+	mallocsize += hashsize * sizeof(struct list_head);
+	/* Round it up to the next highest power of two. */
+	mallocsize--;
+	mallocsize |= mallocsize >> 1;
+	mallocsize |= mallocsize >> 2;
+	mallocsize |= mallocsize >> 4;
+	mallocsize |= mallocsize >> 8;
+	mallocsize |= mallocsize >> 16;
+	mallocsize++;
+	if (mallocsize > PAGE_SIZE) { /* This should give us thousands of entries anyway. */
+		mallocsize = PAGE_SIZE;
+	}
+	hashsize = (mallocsize - sizeof(struct unionfs_dir_state)) / sizeof(struct list_head);
+
+	rdstate = KMALLOC(mallocsize, GFP_UNIONFS);
+	if (!rdstate) {
+		return NULL;
+	}
+
+	rdstate->uds_key = 0;
+	rdstate->uds_offset = 1;
+	rdstate->uds_access = jiffies;
+	rdstate->uds_bindex = bindex;
+	rdstate->uds_dirpos = 0;
+	rdstate->uds_hashentries = 0;
+	rdstate->uds_size = hashsize;
+	for (i = 0; i < rdstate->uds_size; i++)
+		INIT_LIST_HEAD(&rdstate->uds_list[i]);
+
+	return rdstate;
+}
+
+void free_filldir_node(struct filldir_node *node) {
+	PASSERT(node);
+	if (node->namelen >= DNAME_INLINE_LEN_MIN) {
+		KFREE(node->name);
+	}
+	kmem_cache_free(unionfs_filldir_cachep, node);
+}
+
+void free_rdstate(struct unionfs_dir_state *state) {
+    struct filldir_node *tmp;
+    int i;
+
+    PASSERT(state);
+
+    for (i = 0; i < state->uds_size; i++) {
+	struct list_head *head = &(state->uds_list[i]);
+	struct list_head *pos, *n;
+
+	/* traverse the list and deallocate space */
+	list_for_each_safe(pos, n, head) {
+	    tmp = list_entry(pos, struct filldir_node, file_list);
+	    list_del(&tmp->file_list);
+	    free_filldir_node(tmp);
+	}
+    }
+
+    KFREE(state);
+}
+
+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen) {
+	int index;
+	unsigned int hash;
+	struct list_head *head;
+	struct list_head *pos;
+	struct filldir_node *cursor = NULL;
+	int found = 0;
+
+	/* If we print entry, we end up with spurious data. */
+	/* print_entry("name = %*s", namelen, name); */
+	print_entry_location();
+
+	PASSERT(rdstate);
+	PASSERT(name);
+	ASSERT(namelen != 0);
+
+	hash = full_name_hash(name, namelen);
+	index = hash % rdstate->uds_size;
+
+	head = &(rdstate->uds_list[index]);
+	list_for_each(pos, head) {
+		cursor = list_entry(pos, struct filldir_node, file_list);
+
+		if (cursor->namelen == namelen && cursor->hash == hash && !strncmp(cursor->name, name, namelen)) {
+			/* a duplicate exists, and hence no need to create entry to the list */
+			found = 1;
+			/* if the duplicate is in this branch, then the file system is corrupted. */
+			if (cursor->bindex == rdstate->uds_bindex) {
+				//buf->error = err = -EIO;
+				fist_dprint(8, "Possible I/O error unionfs_filldir: a file is duplicated in the same branch %d: %s\n", rdstate->uds_bindex, cursor->name);
+			}
+			break;
+		}
+	}
+
+	if (!found ) {
+		cursor = NULL;
+	}
+	print_exit_pointer(cursor);
+	return cursor;
+}
+
+inline struct filldir_node *alloc_filldir_node(const char* name, int namelen, unsigned int hash, int bindex) {
+	struct filldir_node *newnode;
+
+	newnode = (struct filldir_node *)kmem_cache_alloc(unionfs_filldir_cachep, SLAB_KERNEL);
+	if (!newnode) {
+		goto out;
+	}
+
+out:
+	return newnode;
+}
+
+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen, int bindex, int whiteout) {
+	struct filldir_node *new;
+	unsigned int hash;
+	int index;
+	int err = 0;
+	struct list_head *head;
+
+	/* We can't print this because we end up Oopsing. */
+	/* print_entry("name = %*s", namelen, name); */
+	print_entry_location();
+
+	PASSERT(rdstate);
+	PASSERT(name);
+	ASSERT(namelen != 0);
+
+	hash = full_name_hash(name, namelen);
+	index = hash % rdstate->uds_size;
+	head = &(rdstate->uds_list[index]);
+
+	new = alloc_filldir_node(name, namelen, hash, bindex);
+	if (!new) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	INIT_LIST_HEAD(&new->file_list);
+	new->namelen = namelen;
+	new->hash = hash;
+	new->bindex = bindex;
+	new->whiteout = whiteout;
+
+	if (namelen < DNAME_INLINE_LEN_MIN) {
+	    new->name = new->iname;
+	} else {
+	    new->name = (char *) KMALLOC(namelen + 1, GFP_UNIONFS);
+	    if (!new->name) {
+		kmem_cache_free(unionfs_filldir_cachep, new);
+		new = NULL;
+		goto out;
+	    }
+	}
+
+	memcpy(new->name, name, namelen);
+	new->name[namelen] = '\0';
+
+	rdstate->uds_hashentries++;
+
+	list_add(&(new->file_list), head);
+out:
+	print_exit_status(err);
+	return err;
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/stale_inode.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/stale_inode.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/stale_inode.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/stale_inode.c	2005-04-25 11:56:14.548654352 -0700
@@ -0,0 +1,126 @@
+/*
+ *  Adpated from linux/fs/bad_inode.c
+ *
+ *  Copyright (C) 1997, Stephen Tweedie
+ *
+ *  Provide stub functions for "stale" inodes, a bit friendlier than the
+ *  -EIO that bad_inode.c does.
+ */
+/*
+ *  $Id: stale_inode.c,v 1.6 2005/01/09 21:43:25 ezk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#ifdef CONFIG_MODVERSIONS
+# define MODVERSIONS
+# include <linux/modversions.h>
+#endif /* CONFIG_MODVERSIONS */
+#endif /* KERNEL_VERSION < 2.6.0 */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+static struct address_space_operations unionfs_stale_aops;
+
+/*
+ * The follow_link operation is special: it must behave as a no-op
+ * so that a stale root inode can at least be unmounted. To do this
+ * we must dput() the base and return the dentry with a dget().
+ */
+static int stale_follow_link(struct dentry *dent, struct nameidata *nd)
+{
+	return vfs_follow_link(nd, ERR_PTR(-ESTALE));
+}
+
+static int return_ESTALE(void)
+{
+	return -ESTALE;
+}
+
+#define ESTALE_ERROR ((void *) (return_ESTALE))
+
+static struct file_operations stale_file_ops =
+{
+	llseek:		ESTALE_ERROR,
+	read:		ESTALE_ERROR,
+	write:		ESTALE_ERROR,
+	readdir:	ESTALE_ERROR,
+	poll:		ESTALE_ERROR,
+	ioctl:		ESTALE_ERROR,
+	mmap:		ESTALE_ERROR,
+	open:		ESTALE_ERROR,
+	flush:		ESTALE_ERROR,
+	release:	ESTALE_ERROR,
+	fsync:		ESTALE_ERROR,
+	fasync:		ESTALE_ERROR,
+	lock:		ESTALE_ERROR,
+};
+
+struct inode_operations stale_inode_ops =
+{
+	create:		ESTALE_ERROR,
+	lookup:		ESTALE_ERROR,
+	link:		ESTALE_ERROR,
+	unlink:		ESTALE_ERROR,
+	symlink:	ESTALE_ERROR,
+	mkdir:		ESTALE_ERROR,
+	rmdir:		ESTALE_ERROR,
+	mknod:		ESTALE_ERROR,
+	rename:		ESTALE_ERROR,
+	readlink:	ESTALE_ERROR,
+	follow_link:	stale_follow_link,
+	truncate:	ESTALE_ERROR,
+	permission:	ESTALE_ERROR,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	revalidate:	ESTALE_ERROR,
+#endif
+};
+
+
+/*
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_stale_inode() to return a
+ * set of stubs which will return ESTALE errors as required.
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+
+/**
+ *	make_stale_inode - mark an inode stale due to an I/O error
+ *	@inode: Inode to mark stale
+ *
+ *	When an inode cannot be read due to a media or remote network
+ *	failure this function makes the inode "stale" and causes I/O operations
+ *	on it to fail from this point on.
+ */
+
+void make_stale_inode(struct inode * inode)
+{
+	inode->i_mode = S_IFREG;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_op = &stale_inode_ops;
+	inode->i_fop = &stale_file_ops;
+	inode->i_mapping->a_ops = &unionfs_stale_aops;
+}
+
+/*
+ * This tests whether an inode has been flagged as stale. The test uses
+ * &stale_inode_ops to cover the case of invalidated inodes as well as
+ * those created by make_stale_inode() above.
+ */
+
+/**
+ *	is_stale_inode - is an inode errored
+ *	@inode: inode to test
+ *
+ *	Returns true if the inode in question has been marked as stale.
+ */
+
+int is_stale_inode(struct inode * inode)
+{
+	return (inode->i_op == &stale_inode_ops);
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/subr.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/subr.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/subr.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/subr.c	2005-04-25 11:56:14.549654200 -0700
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: subr.c,v 1.98 2005/02/08 15:17:38 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+/* lookup files/directories from index 'bstart' onwards, does not looksup bstart */
+STATIC int
+unionfs_partial_lookup(dentry_t *dentry)
+{
+    int err = 0;
+    int dentry_count = 0;
+    int bstart, bindex;
+    const char *name;
+    unsigned int namelen;
+    dentry_t *hidden_dentry;
+    dentry_t *hidden_dir_dentry;
+    int positive;
+
+    print_entry_location();
+
+    fist_print_dentry("IN: unionfs_partial_lookup: ", dentry);
+    lock_dpd(dentry);
+    bstart = dbstart(dentry);
+
+    name = dentry->d_name.name;
+    namelen = dentry->d_name.len;
+
+    positive = dentry->d_inode ? 1 :  0;
+
+    for (bindex = bstart + 1; bindex < sbmax(dentry->d_sb); bindex++) {
+	/* Check if there is already something there. */
+	hidden_dentry = dtohd_index(dentry, bindex);
+	if (hidden_dentry) {
+	    continue;
+	}
+
+	/* Find out hidden parent dentry */
+	hidden_dir_dentry = dtohd_index(dentry->d_parent, bindex);
+
+	/* continue if hidden parent doesn't exist */
+	if (!hidden_dir_dentry) {
+	    continue;
+	}
+
+	/* lookup underlying file system */
+	hidden_dentry = lookup_one_len(name, hidden_dir_dentry, namelen);
+	if (IS_ERR(hidden_dentry)) {
+	    printk("ERR from hidden_dentry!!!\n");
+	    err = PTR_ERR(hidden_dentry);
+	    goto out;
+	}
+
+	if (hidden_dentry->d_inode) {
+	    /* this is not a negative dentry, so init dtohd with
+	     * this new dentry.  Also, change bend.
+	     */
+	    set_dtohd_index(dentry, bindex, hidden_dentry);
+	    set_dbend(dentry, bindex);
+	    dentry_count++;
+	} else {
+	    /* dput the unnecessary negative dentry */
+	    dput(hidden_dentry);
+	}
+    } // end for
+
+    if (dentry_count) {
+	if (positive) {
+    		/* need to connect the new inodes to the pre-existing
+     		 * unionfs inode, unionfs_reinterpose
+      		 */
+		unionfs_reinterpose(dentry);
+	} else {
+		int bstart;
+		int bend;
+
+		/* Somehow the file was created for us, so we need to interpose it with a new inode. */
+
+		bstart = dbstart(dentry);
+		bend = dbend(dentry);
+
+        	for (bindex = bstart; bindex <= bend; bindex++) {
+               		if (dtohd_index(dentry, bindex)) {
+				if (!dtohd_index(dentry, bindex)->d_inode) {
+					dput(dtohd_index(dentry, bindex));
+					set_dtohd_index(dentry, bindex, NULL);
+				} else {
+					set_dbstart(dentry, bindex);
+                        		break;
+				}
+			}
+		}
+
+		unionfs_interpose(dentry, dentry->d_sb, 0);
+	}
+    }
+
+ out:
+    unlock_dpd(dentry);
+    fist_print_dentry("OUT: unionfs_partial_lookup: ", dentry);
+    print_exit_status(err);
+    return err;
+}
+
+
+/* Pass an unionfs dentry and an index.  It will try to create a whiteout for the filename
+ * in dentry, and will try in branch 'index'.  On error, it will proceed to a branch
+ * to the left
+ */
+int create_whiteout(dentry_t *dentry, int start)
+{
+    int bstart, bend, bindex;
+    dentry_t *hidden_dir_dentry;
+    dentry_t *hidden_dentry;
+    dentry_t *hidden_wh_dentry;
+    char *name = NULL;
+    int err = -EUNIONFS_NO_WHITEOUT;
+
+    print_entry_location();
+
+    PASSERT(dentry);
+    PASSERT(dentry->d_parent);
+
+    fist_print_dentry("IN create_whiteout", dentry);
+    bstart = dbstart(dentry);
+    bend = dbend(dentry);
+
+    /* create dentry's whiteout equivalent */
+    name = KMALLOC(sizeof(char) * (dentry->d_name.len + 5), GFP_UNIONFS);
+    if (!name) {
+        err = -ENOMEM;
+        goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, dentry->d_name.name, dentry->d_name.len);
+    name[dentry->d_name.len + 4] = '\0';
+
+    /* start from 'start', create foo then rename it to .wh.foo, on error go to
+       the next left branch, if hidden_dentry is NULL do recursive directory creation */
+    for (bindex = start; bindex >= 0; bindex--) {
+	hidden_dentry = dtohd_index(dentry, bindex);
+
+	if (!hidden_dentry) {
+	    /* if hidden dentry is not present, create the entire
+	     * hidden dentry directory structure and go ahead.
+	     * Since we want to just create whiteout, we only want the parent
+	     * dentry, and hence get rid of this dentry.
+	     */
+	    hidden_dentry = unionfs_create_dirs(dentry->d_inode, dentry, bindex);
+	    if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+		fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex);
+		continue;
+	    }
+	}
+	hidden_wh_dentry = lookup_one_len(name, hidden_dentry->d_parent, dentry->d_name.len + 4);
+
+	if (!hidden_wh_dentry || IS_ERR(hidden_wh_dentry)) {
+	    continue;
+	}
+	ASSERT(!hidden_wh_dentry->d_inode);
+
+	hidden_dir_dentry = lock_parent(hidden_wh_dentry);
+    	if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+	    err = vfs_create(hidden_dir_dentry->d_inode, hidden_wh_dentry, ~current->fs->umask & S_IRWXUGO);
+#else
+	    err = vfs_create(hidden_dir_dentry->d_inode, hidden_wh_dentry, ~current->fs->umask & S_IRWXUGO,NULL);
+
+#endif
+	}
+	unlock_dir(hidden_dir_dentry);
+	dput(hidden_wh_dentry);
+
+
+	if (!err) {
+	    break;
+        } else {
+            if (IS_SET(dentry->d_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        }
+    } // end for
+
+    fist_print_dentry("OUT create_whiteout", dentry);
+out:
+    if (name) {
+       KFREE(name);
+    }
+    print_exit_status(err);
+    return err;
+}
+
+/* Pass an unionfs dentry, name of file, and an index.  It will try to create a whiteout
+ * for the filename passed in the parent directory passed.  It will start trying
+ * in branch start.  On error, it will proceed to a branch to the left
+ */
+int create_whiteout_parent(dentry_t *parent_dentry, const char *filename, int start)
+{
+    int bindex;
+    int old_bstart, old_bend;
+    dentry_t *hidden_dir_dentry;
+    dentry_t *hidden_grand_parent_dentry;
+    dentry_t *hidden_parent_dentry;
+    dentry_t *hidden_wh_dentry;
+    char *name = NULL;
+    int err = -EUNIONFS_NO_WHITEOUT;
+
+    print_entry_location();
+
+    PASSERT(parent_dentry);
+    old_bstart = dbstart(parent_dentry);
+    old_bend = dbend(parent_dentry);
+
+    fist_print_dentry("IN create_whiteout_parent", parent_dentry);
+
+    /* create dentry's whiteout equivalent */
+    name = KMALLOC(sizeof(char) * (strlen(filename) + 5), GFP_UNIONFS);
+    if (!name) {
+        err = -ENOMEM;
+        goto out;
+    }
+    strcpy(name, ".wh.");
+    strncat(name, filename, strlen(filename));
+    name[strlen(filename) + 4] = '\0';
+
+    for (bindex = start; bindex >= 0; bindex--) {
+
+        hidden_parent_dentry = dtohd_index(parent_dentry, bindex);
+
+	if (!hidden_parent_dentry) {
+            /* create the recursive directory structure and return the
+             * negative dentry for the parent where we want to create whiteout
+             */
+            ASSERT(parent_dentry->d_inode != NULL);
+	    hidden_parent_dentry = unionfs_create_dirs(parent_dentry->d_inode, parent_dentry, bindex);
+	    if (!hidden_parent_dentry || IS_ERR(hidden_parent_dentry)) {
+		fist_dprint(8, "hidden dentry NULL for bindex = %d\n", bindex);
+		continue;
+	    } else {
+	        /* create directory of the hidden parent, if it is negative.
+                 * This is where whiteout is created
+                 */
+                hidden_grand_parent_dentry = lock_parent(hidden_parent_dentry);
+                PASSERT(hidden_grand_parent_dentry->d_inode);
+
+                /* We shouldn't create things in a read-only branch. */
+                if (!(err = is_robranch_super(parent_dentry->d_sb, bindex))) {
+                    err = vfs_mkdir(hidden_grand_parent_dentry->d_inode, hidden_parent_dentry, S_IRWXU);
+                }
+
+                unlock_dir(hidden_grand_parent_dentry);
+                if (err || !hidden_parent_dentry->d_inode) {
+		    dput(hidden_parent_dentry);
+                    /* if error, we need to revert back the old bstart and bend for this
+                     * parent and return back
+                     */
+                    set_dbstart(parent_dentry, old_bstart);
+                    set_dbend(parent_dentry, old_bend);
+                    set_dtohd_index(parent_dentry, bindex, NULL);
+
+                    if (IS_SET(parent_dentry->d_sb, GLOBAL_ERR_PASSUP)) {
+                        goto out;
+                    }
+                    /* break out of for loop if error returned was NOT -EROFS */
+                    if (!IS_COPYUP_ERR(err)) {
+                        break;
+                    }
+
+                    continue;
+                }
+                itohi_index(parent_dentry->d_inode, bindex) = igrab(hidden_parent_dentry->d_inode);
+            }
+        }
+
+        /* lookup for the whiteout dentry that we want to create */
+	hidden_wh_dentry = lookup_one_len(name, hidden_parent_dentry, strlen(filename) + 4);
+	if (!hidden_wh_dentry || IS_ERR(hidden_wh_dentry)) {
+	    continue;
+	}
+	ASSERT(!hidden_wh_dentry->d_inode);
+
+        /* hidden_dir_dentry and hidden_parent_dentry are going to be same only */
+	hidden_dir_dentry = lock_parent(hidden_wh_dentry);
+
+        /* We shouldn't create things in a read-only branch. */
+        if (!(err = is_robranch_super(parent_dentry->d_sb, bindex))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+            err = vfs_create(hidden_dir_dentry->d_inode, hidden_wh_dentry, ~current->fs->umask & S_IRWXUGO);
+#else
+			err = vfs_create(hidden_dir_dentry->d_inode, hidden_wh_dentry, ~current->fs->umask & S_IRWXUGO,NULL);
+#endif
+	}
+
+        unlock_dir(hidden_dir_dentry);
+	dput(hidden_wh_dentry);
+
+	if (!err) {
+	    break;
+        } else {
+            if (IS_SET(parent_dentry->d_sb, GLOBAL_ERR_PASSUP)) {
+                goto out;
+            }
+            /* break out of for loop if error returned was NOT -EROFS */
+            if (!IS_COPYUP_ERR(err)) {
+                break;
+            }
+        }
+    } // end for
+
+out:
+    fist_print_dentry("OUT create_whiteout", parent_dentry);
+    if (name) {
+       KFREE(name);
+    }
+    print_exit_status(err);
+    return err;
+}
+
+/* This is a helper function for rename, which ends up with hosed over dentries
+ * when it needs to revert. */
+int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex) {
+    struct dentry *hidden_dentry;
+    struct dentry *hidden_parent;
+    int err = 0;
+
+    print_entry(" bindex = %d", bindex);
+
+    hidden_parent = dtohd_index(dentry->d_parent, bindex);
+
+    PASSERT(hidden_parent);
+    PASSERT(hidden_parent->d_inode);
+    ASSERT(S_ISDIR(hidden_parent->d_inode->i_mode));
+
+    hidden_dentry = lookup_one_len(dentry->d_name.name, hidden_parent, dentry->d_name.len);
+    if (IS_ERR(hidden_dentry)) {
+	err = PTR_ERR(hidden_dentry);
+	goto out;
+    }
+
+    if (dtohd_index(dentry, bindex)) {
+    	dput(dtohd_index(dentry, bindex));
+    }
+    if(itohi_index(dentry->d_inode, bindex)) {
+    	iput(itohi_index(dentry->d_inode, bindex));
+	itohi_index(dentry->d_inode, bindex) =  NULL;
+    }
+    if (!hidden_dentry->d_inode) {
+	dput(hidden_dentry);
+    	set_dtohd_index(dentry, bindex, NULL);
+    } else {
+    	set_dtohd_index(dentry, bindex, hidden_dentry);
+    	itohi_index(dentry->d_inode, bindex) = igrab(hidden_dentry->d_inode);
+    }
+
+out:
+    print_exit_status(err);
+    return err;
+}
+
+/*
+ * vim:shiftwidth=4
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/super.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/super.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/super.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/super.c	2005-04-25 11:56:14.551653896 -0700
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: super.c,v 1.42 2005/02/19 00:51:48 ezk Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+STATIC void
+unionfs_read_inode(inode_t *inode)
+{
+    static struct address_space_operations unionfs_empty_aops;
+
+    print_entry_location();
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    ASSERT(sizeof(struct unionfs_inode_info) < sizeof(inode->u) - sizeof(void *));
+    itopd_lhs(inode) = (&(inode->u.generic_ip) + 1);
+#else
+    itopd_lhs(inode) = KMALLOC(sizeof(struct unionfs_inode_info), GFP_UNIONFS);
+#endif
+    if (!itopd(inode)) {
+	FISTBUG("No kernel memory when allocating inode private data!\n");
+    }
+    init_itopd(inode);
+    itohi_ptr(inode) = KMALLOC(sizeof(inode_t *) * sbmax(inode->i_sb), GFP_UNIONFS);
+    if (!itohi_ptr(inode)) {
+	FISTBUG("No kernel memory when allocating lower-pointer array!\n");
+    }
+    init_itohi_ptr(inode, sbmax(inode->i_sb));
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    inode->i_version = ++event;	/* increment inode version */
+#else
+    inode->i_version++;
+#endif
+    inode->i_op = &unionfs_main_iops;
+    inode->i_fop = &unionfs_main_fops;
+    /* I don't think ->a_ops is ever allowed to be NULL */
+    inode->i_mapping->a_ops = &unionfs_empty_aops;
+    fist_dprint(7, "setting inode 0x%p a_ops to empty (0x%p)\n",
+		inode, inode->i_mapping->a_ops);
+
+    print_exit_location();
+}
+
+
+STATIC void
+unionfs_put_inode(inode_t *inode)
+{
+    print_entry_location();
+    fist_dprint(8, "%s i_count = %d, i_nlink = %d\n", __FUNCTION__,
+		atomic_read(&inode->i_count), inode->i_nlink);
+    /*
+     * This is really funky stuff:
+     * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed.
+     * It is currently holding a reference to the hidden inode.
+     * Therefore, it needs to release that reference by calling iput on the hidden inode.
+     * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0.
+     * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files.
+     * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode.
+     */
+    if (atomic_read(&inode->i_count) == 1)
+	inode->i_nlink = 0;
+    print_exit_location();
+}
+
+
+/*
+ * we now define delete_inode, because there are two VFS paths that may
+ * destroy an inode: one of them calls clear inode before doing everything
+ * else that's needed, and the other is fine.  This way we truncate the inode
+ * size (and its pages) and then clear our own inode, which will do an iput
+ * on our and the lower inode.
+ */
+STATIC void
+unionfs_delete_inode(inode_t *inode)
+{
+    print_entry_location();
+
+    fist_checkinode(inode, "unionfs_delete_inode IN");
+    inode->i_size = 0;		/* every f/s seems to do that */
+    clear_inode(inode);
+
+    print_exit_location();
+}
+
+
+/* final actions when unmounting a file system */
+STATIC void
+unionfs_put_super(super_block_t *sb)
+{
+    int bindex, bstart, bend;
+    print_entry_location();
+
+    if (stopd(sb)) {
+#ifdef SPLIT_VIEW_CACHES
+	if ((stopd(sb)->usi_primary == sb) && !list_empty(&stopd(sb)->usi_altsupers)) {
+	    struct unionfs_sb_info *sbinfo;
+	    struct unionfs_sb_info *t;
+	    list_for_each_entry_safe(sbinfo, t, &stopd(sb)->usi_altsupers, usi_altsupers) {
+		kill_super(sbinfo->usi_thissb);
+	    }
+	}
+#endif
+        bstart = sbstart(sb);
+        bend = sbend(sb);
+        for (bindex = bstart; bindex <= bend; bindex++) {
+	    mntput(stohiddenmnt_index(sb, bindex));
+        }
+        KFREE(stohs_ptr(sb));
+        KFREE(stohiddenmnt_ptr(sb));
+        KFREE(stopd(sb)->usi_sbcount);
+        KFREE(stopd(sb)->usi_branchperms);
+	KFREE(stopd(sb));
+	stopd_lhs(sb) = NULL;
+    }
+    fist_dprint(6, "unionfs: released super\n");
+
+    print_exit_location();
+}
+
+
+#ifdef NOT_NEEDED
+/*
+ * This is called in do_umount before put_super.
+ * The superblock lock is not held yet.
+ * We probably do not need to define this or call write_super
+ * on the hidden_sb, because sync_supers() will get to hidden_sb
+ * sooner or later.  But it is also called from file_fsync()...
+ */
+STATIC void
+unionfs_write_super(super_block_t *sb)
+{
+    return;
+}
+#endif /* NOT_NEEDED */
+
+
+STATIC int
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+unionfs_statfs(super_block_t *sb, struct statfs *buf)
+#else
+unionfs_statfs(super_block_t *sb, struct kstatfs *buf)
+#endif
+{
+    int err = 0;
+    super_block_t *hidden_sb;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    struct statfs rsb;
+#else
+	struct kstatfs rsb;
+#endif
+    int bindex, bindex1, bstart, bend;
+    unsigned short *hidden_s_dev;
+
+    print_entry_location();
+    hidden_s_dev = (unsigned short *) KMALLOC (sizeof(unsigned short) * sbmax(sb), GFP_UNIONFS);
+    if (!hidden_s_dev) {
+        err = -ENOMEM;
+        goto out;
+    }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    memset(buf, 0,  sizeof(struct statfs));
+#else
+	memset(buf, 0, sizeof(struct kstatfs));
+#endif
+    buf->f_type = UNIONFS_SUPER_MAGIC;
+
+    bstart = sbstart(sb);
+    bend = sbend(sb);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+
+        hidden_sb = stohs_index(sb, bindex);
+
+        /* store the current s_dev for checking any
+         * duplicate additions for super block data
+         */
+        hidden_s_dev[bindex] = hidden_sb->s_dev;
+
+        for (bindex1 = bstart; bindex1 < bindex; bindex1++) {
+            /* check the hidden super blocks with any previously
+             * added super block data.  If match, continue
+             */
+            if (hidden_s_dev[bindex1] == hidden_sb->s_dev) {
+                continue;
+            }
+        }
+
+        err = vfs_statfs(hidden_sb, &rsb);
+
+        buf->f_bsize += rsb.f_bsize;
+	buf->f_blocks += rsb.f_blocks;
+        buf->f_bfree += rsb.f_bfree;
+        buf->f_bavail += rsb.f_bavail;
+	buf->f_files += rsb.f_files;
+	buf->f_ffree += rsb.f_ffree;
+    }
+
+    KFREE(hidden_s_dev);
+out:
+    print_exit_status(err);
+    return err;
+}
+
+
+/*
+ * XXX: not implemented.  This is not allowed yet.
+ * Should we call this on the hidden_sb?  Probably not.
+ */
+STATIC int
+unionfs_remount_fs(super_block_t *sb, int *flags, char *data)
+{
+    return -ENOSYS;
+}
+
+
+/*
+ * Called by iput() when the inode reference count reached zero
+ * and the inode is not hashed anywhere.  Used to clear anything
+ * that needs to be, before the inode is completely destroyed and put
+ * on the inode free list.
+ */
+STATIC void
+unionfs_clear_inode(inode_t *inode)
+{
+    int bindex, bstart,bend;
+    inode_t *hidden_inode;
+    struct list_head *pos, *n;
+    struct unionfs_dir_state *rdstate;
+
+    print_entry_location();
+
+    fist_checkinode(inode, "unionfs_clear_inode IN");
+
+    list_for_each_safe(pos, n, &itopd(inode)->uii_readdircache) {
+	rdstate = list_entry(pos, struct unionfs_dir_state, uds_cache);
+	list_del(&rdstate->uds_cache);
+	free_rdstate(rdstate);
+    }
+
+    /* Decrement a reference to a hidden_inode, which was incremented
+     * by our read_inode when it was created initially.  */
+    bstart = ibstart(inode);
+    bend = ibend(inode);
+    if (bstart >= 0) {
+        for (bindex = bstart; bindex <= bend; bindex++) {
+
+            hidden_inode = itohi_index(inode, bindex);
+
+            if (hidden_inode == NULL) continue;
+
+            iput(hidden_inode);
+        }
+    }
+    // XXX: why this assertion fails?
+    // because it doesn't like us
+    // ASSERT((inode->i_state & I_DIRTY) == 0);
+    KFREE(itohi_ptr(inode));
+    itohi_ptr(inode) = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+    KFREE(itopd(inode));
+#endif
+    itopd_lhs(inode) = NULL;
+
+    print_exit_location();
+}
+
+/* Called when we have a dirty inode, right here we only throw out
+ * parts of our readdir list that are too old.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)
+static int unionfs_write_inode(struct inode *inode, int sync) {
+#else
+static void unionfs_write_inode(struct inode *inode, int sync) {
+#endif
+    struct list_head *pos, *n;
+    struct unionfs_dir_state *rdstate;
+
+    print_entry_location();
+
+    PASSERT(inode);
+    PASSERT(itopd(inode));
+    spin_lock(&itopd(inode)->uii_rdlock);
+    list_for_each_safe(pos, n, &itopd(inode)->uii_readdircache) {
+	rdstate = list_entry(pos, struct unionfs_dir_state, uds_cache);
+	/* We keep this list in LRU order. */
+	if ((rdstate->uds_access + RDCACHE_JIFFIES) > jiffies)
+	    break;
+	itopd(inode)->uii_rdcount--;
+	list_del(&rdstate->uds_cache);
+	free_rdstate(rdstate);
+    }
+    spin_unlock(&itopd(inode)->uii_rdlock);
+
+    print_exit_location();
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+    return 0;
+#endif
+}
+
+/*
+ * Called in do_umount() if the MNT_FORCE flag was used and this
+ * function is defined.  See comment in linux/fs/super.c:do_umount().
+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent
+ * code can actually succeed and won't leave tasks that need handling.
+ *
+ * PS. I wonder if this is somehow useful to undo damage that was
+ * left in the kernel after a user level file server (such as amd)
+ * dies.
+ */
+STATIC void
+unionfs_umount_begin(super_block_t *sb)
+{
+    super_block_t *hidden_sb;
+    int bindex, bstart, bend;
+
+    print_entry_location();
+
+    bstart = sbstart(sb);
+    bend = sbend(sb);
+    for (bindex = bstart; bindex <= bend; bindex++) {
+
+        hidden_sb = stohs_index(sb, bindex);
+
+        if (hidden_sb && hidden_sb->s_op && hidden_sb->s_op->umount_begin) {
+	    hidden_sb->s_op->umount_begin(hidden_sb);
+        }
+    }
+    print_exit_location();
+}
+
+
+STATIC int
+unionfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ 	struct super_block *sb = mnt->mnt_sb;
+	int ret = 0;
+	unsigned long tmp = 0;
+	char *hidden_path;
+        int bindex, bstart, bend;
+        int perms;
+
+	tmp = __get_free_page(GFP_UNIONFS);
+	if (!tmp) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+        bindex = bstart = sbstart(sb);
+        bend = sbend(sb);
+
+        seq_printf(m, ",dirs=");
+        for (bindex = bstart; bindex <= bend; bindex++) {
+                hidden_path = d_path(dtohd_index(sb->s_root, bindex), stohiddenmnt_index(sb, bindex), (char *)tmp, PAGE_SIZE);
+                perms = stopd(sb)->usi_branchperms[bindex];
+                seq_printf(m, "%s=%s", hidden_path, perms & MAY_WRITE ? "rw" : "ro");
+                if (bindex != bend) {
+                        seq_printf(m, ":");
+                }
+        }
+
+        seq_printf(m, ",debug=%d", fist_get_debug_value());
+
+        if (IS_SET(sb, GLOBAL_ERR_PASSUP)) {
+                seq_printf(m, ",err=passup");
+        } else {
+                seq_printf(m, ",err=tryleft");
+        }
+
+        if (IS_SET(sb, DELETE_FIRST)) {
+                seq_printf(m, ",delete=first");
+        } else if (IS_SET(sb, DELETE_WHITEOUT)) {
+                seq_printf(m, ",delete=whiteout");
+        } else {
+                seq_printf(m, ",delete=all");
+        }
+
+        if (IS_SET(sb, COPYUP_CURRENT_USER)) {
+                seq_printf(m, ",copyup=currentuser");
+        } else if (IS_SET(sb, COPYUP_FS_MOUNTER)) {
+                seq_printf(m, ",copyup=mounter");
+        } else {
+                seq_printf(m, ",copyup=preserve");
+        }
+
+        if (IS_SET(sb, SETATTR_ALL)) {
+                seq_printf(m, ",setattr=all");
+        } else {
+                seq_printf(m, ",setattr=left");
+        }
+out:
+	if (tmp) {
+		free_page(tmp);
+	}
+	return ret;
+}
+
+#ifdef SPLIT_VIEW_CACHES
+static struct dentry *unionfs_select_super(super_block_t *sb, struct vfsmount *mnt) {
+    struct dentry *root = NULL;
+
+    print_entry_location();
+
+    if (current->fsuid == 0 || list_empty(&stopd(sb)->usi_altsupers)) {
+	root = sb->s_root;
+    } else {
+	struct unionfs_sb_info *sbinfo;
+	lock_super(sb);
+	list_for_each_entry(sbinfo, &stopd(sb)->usi_altsupers, usi_altsupers) {
+	    if (sbinfo->usi_thissb != sb) {
+		root = sbinfo->usi_thissb->s_root;
+		break;
+	    }
+	}
+	if (!root) {
+		FISTBUG("How can an non-empty list have only the same super!\n");
+		root = sb->s_root;
+	}
+	unlock_super(sb);
+    }
+
+out:
+    print_exit_pointer(root);
+    return root;
+}
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+/**
+ * The entry given by dentry here is always a directory.
+ * We can't just do dentry->d_parent, because it may
+ * not be there, since this dentry could have been
+ * created by calling d_alloc_anon.
+ */
+static struct dentry *unionfs_get_parent(struct dentry *dentry) {
+
+	struct dentry *ret;
+	inode_t *childinode;
+	childinode = dentry->d_inode;
+	ret = d_find_alias(childinode);
+        return ret;
+}
+
+struct export_operations unionfs_export_ops = {
+	.get_parent	=	unionfs_get_parent
+};
+#endif
+
+struct super_operations unionfs_sops =
+{
+    read_inode:		unionfs_read_inode,
+    put_inode:		unionfs_put_inode,
+    delete_inode:	unionfs_delete_inode,
+    put_super:		unionfs_put_super,
+    statfs:		unionfs_statfs,
+    remount_fs:		unionfs_remount_fs,
+    clear_inode:	unionfs_clear_inode,
+    umount_begin:	unionfs_umount_begin,
+    show_options:       unionfs_show_options,
+    write_inode:	unionfs_write_inode,
+#ifdef SPLIT_VIEW_CACHES
+    select_super:	unionfs_select_super,
+#endif
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ * vim:shiftwidth=4
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/unionctl.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/unionctl.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/unionctl.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/unionctl.c	2005-04-25 11:56:14.552653744 -0700
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: unionctl.c,v 1.15 2005/02/22 21:36:46 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define MAY_READ 4
+#define MAY_WRITE 2
+
+char **branches;
+int *branchperms;
+static const char *progname;
+
+#define usage() __usage(__LINE__)
+
+void __attribute__((__noreturn__)) __usage(int line)
+{
+#ifdef DEBUG
+	fprintf(stderr, "Line: %d\n", line);
+#endif
+	fprintf(stderr, "unionctl version: $Id: unionctl.c,v 1.15 2005/02/22 21:36:46 cwright Exp $\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "unionctl UNION ACTION [arguments]\n");
+	fprintf(stderr, "ACTION can be --add, --remove, --mode, or --list.\nFurther arguments depend on ACTION.\n");
+	fprintf(stderr, "\tunionctl UNION --add [ --before BRANCH | --after BRANCH ] [ --mode (rw|ro) ] DIRECTORY\n");
+	fprintf(stderr, "\tunionctl UNION --remove BRANCH\n");
+	fprintf(stderr, "\tunionctl UNION --mode BRANCH (rw|ro)\n");
+	fprintf(stderr, "\tunionctl UNION --list\n");
+	fprintf(stderr, "The unionctl man page has a more detailed description of its usage.\n");
+	exit(1);
+}
+
+int find_union(const char *path, char **options) {
+	FILE *f = NULL;
+	char *s = NULL;
+	char *s2 = NULL;
+	char *p;
+	char *q;
+	int candidate = 0;
+	int mallocsize = 1024; /* Just a reasonable starting value. */
+
+retry:
+	if (*options) { 
+		free(*options);
+		*options = NULL;
+	}
+
+	if (f) {
+		fclose(f);
+		f = NULL;
+	}
+	s2 = realloc(s, mallocsize);
+	if (!s2) {
+		fprintf(stderr, "realloc(%d): %s\n", mallocsize, strerror(errno));
+		if (s) free(s);
+		goto out;
+	}
+	s = s2;
+
+	f = fopen("/proc/mounts", "r");
+	if (!f) {
+		fprintf(stderr, "fopen(/proc/mounts): %s\n", strerror(errno));
+		goto out;
+	}
+	while (fgets(s, mallocsize, f)) {
+		int testcan;
+
+		/* If we don't have enough information, we should remalloc it. */	
+		if (strlen(s) == (mallocsize - 1)) {
+			mallocsize *= 2;
+			goto retry;
+		}
+
+		p = strchr(s, ' ');
+		if (!p) continue;
+		p++;
+
+		q = strchr(p, ' ');
+		if (!q) continue;
+		*q++ = '\0';
+
+		testcan = strlen(p);
+		if (testcan <= candidate) {
+			continue;
+		}
+
+		if (!strncmp(path, p, testcan)) {
+			p = strchr(q, ' ');
+			if (!p) continue;
+			*p++ = '\0';
+
+			if (strcmp(q, "unionfs")) {
+				candidate = 0;
+				continue;
+			}
+
+			candidate = testcan;
+
+			q = strrchr(p, ' ');
+			if (!q) continue;
+			*q = '\0';
+			q = strrchr(p, ' ');
+			if (!q) continue;
+			*q = '\0';
+
+			if (*options) {
+				free(*options);
+			}
+			*options = strdup(p);
+			if (!*options) {
+				fprintf(stderr, "strdup: %s\n", strerror(errno));
+				goto out;
+			}
+		}
+	}
+
+out:
+	if (s) free(s);
+	if (f) fclose(f);
+
+	if (*options) {
+		return 0;
+	}
+
+	errno = -ENOENT;
+	return -1;
+}
+
+char **parse_options(char *options) {
+	char **ret = NULL;
+	int n = 0;
+
+	char *p;
+	char *q;
+	char *r;
+	char *s;
+	int l;
+
+	p = options;
+	do
+	{
+		q = strchr(p, ',');
+		if (q) {
+			*q++ = '\0';
+		}
+		if (!strncmp(p, "dirs=", strlen("dirs="))) {
+			r = p + strlen("dirs=");
+			do
+			{
+				s = strchr(r, ':');
+				if (s) {
+					*s++ = '\0';
+				}
+
+				n++;
+				ret = realloc(ret, sizeof(char *) * (n + 1));
+				if (!ret) {
+					perror("realloc()");
+					exit(1);
+				}
+				branchperms = realloc(branchperms, sizeof(int) * n);
+				if (!branchperms) {
+					perror("realloc()");
+					exit(1);
+				}
+
+				l = strlen(r);
+				if (((r[l - 1] == 'o') || (r[l - 1] == 'w')) && (r[l - 2] == 'r') && (r[l - 3] == '=')) {
+					r[l - 3] = '\0';
+					branchperms[n - 1] = MAY_READ;
+					if (r[l - 1] == 'w') {
+						branchperms[n - 1] |= MAY_WRITE;
+					}
+				}
+				ret[n - 1] = strdup(r);
+				ret[n] = NULL;
+
+				r = s;
+			}
+			while(r);
+		}
+		p = q;
+	}
+	while(p);
+
+	branches = ret;
+	return ret;
+}
+
+int get_branch(char *path) {
+	char *end;
+	int ret;
+
+	ret = strtol(path, &end, 0);
+	if (!*end) {
+		return ret;
+	}
+
+	ret = strlen(path);
+	if(path[ret - 1] == '/') {
+		path[ret - 1] = '\0';
+	}
+
+	ret = 0;
+	while(branches[ret]) {
+		if (!strcmp(branches[ret], path)) {
+			return ret;
+		}
+		ret++;
+	}
+
+	return -1;
+}
+
+void dump_branches(const char *prefix) {
+	int n = 0;
+
+	while(branches[n]) {
+		char r, w;
+		r = (branchperms[n] & MAY_READ) ? 'r' : '-';
+		w = (branchperms[n] & MAY_WRITE) ? 'w' : '-';
+		printf("%s%s (%c%c)\n", prefix, branches[n], r, w);
+		n++;
+	}
+}
+
+#define ADD 1
+#define REMOVE 2
+#define MODE 3
+#define LIST 4
+
+int
+main(int argc, char *argv[])
+{
+        struct unionfs_addbranch_args addargs;
+        struct unionfs_rdwrbranch_args rdwrargs;
+	struct stat st;
+
+	int fd, ret, i;
+
+	char *path;
+	char *options = NULL;
+	int action;
+
+	char *branchpath;
+	int branchnum;
+
+        progname = argv[0];
+
+        /* check that minimum number of args were specified */
+        if (argc < 3)
+                usage();
+
+	path = argv[1];
+	if (strcmp(path, "/") && (path[strlen(path) - 1] == '/')) {
+		path[strlen(path) - 1] = '\0';
+	}
+
+	if (!strcmp(argv[2], "--add")) {
+		action = ADD;
+	} else if (!strcmp(argv[2], "--remove")) {
+		action = REMOVE;
+	} else if (!strcmp(argv[2], "--mode")) {
+		action = MODE;
+	} else if (!strcmp(argv[2], "--list")) {
+		action = LIST;
+	} else {
+		usage();
+	}
+
+	if (stat(path, &st) == -1) {
+		fprintf(stderr, "stat(%s): %s\n", path, strerror(errno));
+		exit(1);
+	}
+
+	if (find_union(path, &options) < 0) {
+		fprintf(stderr, "%s is not a valid union.\n", path);
+		exit(1);
+	}
+	branches = parse_options(options);
+	if (!branches) {
+		fprintf(stderr, "Could not parse options from /proc/mounts!\n");
+		exit(1);
+	}
+
+
+        /* open file on which ioctl will operate (that is actually any file in the union) */
+        fd = open(path, O_RDONLY);
+        if (fd < 0) {
+                fprintf(stderr, "open(%s): %s\n", path, strerror(errno));
+                exit(1);
+        }
+
+
+	/* Parse the action's options into something usable, and do it. */
+	switch(action) {
+	case ADD:
+		if (argc < 4) {
+			usage();
+		}
+
+		/* Default values if the user leaves them unspecified. */
+		branchnum = 0;
+                addargs.ab_perms = MAY_READ | MAY_WRITE;
+		branchpath = NULL;
+		for (i = 3; i < argc; i++) {
+			if (argv[i][0] == '-' && argv[i][1] == '-') {
+				if (!strcmp(argv[i], "--before")) {
+					i++;
+					if (i == argc) {
+						fprintf(stderr, "%s requires an argument!\n", argv[i - 1]);
+						usage();
+					}
+
+					branchnum = get_branch(argv[i]);
+					if (branchnum == -1) {
+						fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", argv[i]);
+						dump_branches("\t");
+					}
+				} else if (!strcmp(argv[i], "--after")) {
+					i++;
+					if (i == argc) {
+						fprintf(stderr, "%s requires an argument!\n", argv[i - 1]);
+						usage();
+					}
+
+					branchnum = get_branch(argv[i]);
+					if (branchnum == -1) {
+						fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", argv[i]);
+						dump_branches("\t");
+					}
+					branchnum++;
+				} else if (!strcmp(argv[i], "--mode")) {
+					i++;
+					if (i == argc) {
+						fprintf(stderr, "%s requires an argument!\n", argv[i - 1]);
+						usage();
+					}
+
+					if (!strcmp(argv[i], "ro")) {
+						addargs.ab_perms = MAY_READ;
+					} else if (!strcmp(argv[i], "rw")) {
+						addargs.ab_perms = MAY_READ | MAY_WRITE;
+					} else {
+						fprintf(stderr, "Valid modes are ro and rw you specified: \"%s\"\n", argv[i]);
+						usage();
+					}
+				} else {
+					fprintf(stderr, "Unknown option: %s\n", argv[i]);
+					usage();
+				}
+			} else {
+				/* The options must go before the path */
+				if ((i + 1) != argc) {
+					fprintf(stderr, "The path of the branch to add must go after all options.\n");
+					usage();
+				}
+				branchpath = argv[i];
+			}
+		}
+		if (!branchpath) {
+			fprintf(stderr, "You must specify the path to add into the union!\n");
+			usage();
+		}
+
+                addargs.ab_branch = branchnum;
+                addargs.ab_path = branchpath;
+
+		errno = 0;
+                ret = ioctl(fd, UNIONFS_IOCTL_ADDBRANCH, &addargs);
+                if (ret < 0) {
+			switch(errno) {
+			case E2BIG:
+				fprintf(stderr, "Unionfs supports only %d branches.\n", FD_SETSIZE);
+				break;
+			}
+			fprintf(stderr, "Failed to add %s into %s: %s", branchpath, path, strerror(errno));
+                        exit(1);
+                }
+		break;
+	case MODE:
+		if (argc != 5) {
+			usage();
+		}
+
+		branchpath = argv[3];
+
+        	/* Set a branches writeable status. */
+                rdwrargs.rwb_branch = get_branch(branchpath);
+		if (rdwrargs.rwb_branch == -1) {
+			fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", branchpath);
+			dump_branches("\t");
+			exit(1);
+		}
+
+                if (!strcmp(argv[4], "ro")) {
+                        rdwrargs.rwb_perms = MAY_READ;
+                } else if (!strcmp(argv[4], "rw")) {
+                        rdwrargs.rwb_perms = MAY_READ | MAY_WRITE;
+                } else {
+			usage();
+                }
+
+                ret = ioctl(fd, UNIONFS_IOCTL_RDWRBRANCH, &rdwrargs);
+                if (ret < 0) {
+			fprintf(stderr, "Failed to set permissions for %s in %s: %s", branchpath, path, strerror(errno));
+                        exit(1);
+                }
+
+                goto out;
+		break;
+	case REMOVE:
+		if (argc != 4) {
+			usage();
+		}
+		branchpath = argv[3];
+
+		branchnum = get_branch(branchpath);
+		if (branchnum == -1) {
+			fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", branchpath);
+			dump_branches("\t");
+			exit(1);
+		}
+
+		errno = 0;
+                ret = ioctl(fd, UNIONFS_IOCTL_DELBRANCH, branchnum);
+                if (ret < 0) {
+			fprintf(stderr, "Failed to remove %s from %s: %s", branchpath, path, strerror(errno));
+			if (errno == EBUSY) {
+				fprintf(stderr, "Did you specify the union using its root directory?\n");
+			}
+                        exit(1);
+                }
+
+		break;
+	case LIST:
+		dump_branches("\t");
+		break;
+	}
+
+out:
+        close(fd);
+        exit(0);
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/uniondbg.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/uniondbg.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/uniondbg.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/uniondbg.c	2005-04-25 11:56:14.553653592 -0700
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: uniondbg.c,v 1.7 2005/01/10 14:14:07 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#define MAY_READ 4
+#define MAY_WRITE 2
+
+static int opt_d = 0;
+static int opt_c = 0;
+static int opt_g = 0;
+static int opt_s = 0;
+static int optcount;
+static const char *progname;
+
+
+void
+usage(void)
+{
+        fprintf(stderr, "Usage:\n"
+                "%s -d file [val]\n\tto set/get debugging values\n"
+                "%s -c file\n\tto print out branch reference counts (in kernel debug output)\n"
+                "%s -g file\n\tto increment the super block generation count\n"
+                "%s -s file\n\tto duplicate the super block\n", progname, progname, progname, progname);
+        exit(1);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+        int fd, ret, val = 0;
+        int i;
+
+        progname = argv[0];
+
+        /* check that minimum number of args were specified */
+        if (argc < 3)
+                usage();
+
+        if (strcmp(argv[1], "-d") == 0) {
+                if (argc > 4)
+                        usage();
+                opt_d++;
+                optcount++;
+        }
+        if (strcmp(argv[1], "-c") == 0) {
+                if (argc > 3)
+                        usage();
+                opt_c++;
+                optcount++;
+        }
+        if (strcmp(argv[1], "-g") == 0) {
+                if (argc > 3)
+                        usage();
+                opt_g++;
+                optcount++;
+        }
+        if (strcmp(argv[1], "-s") == 0) {
+                if (argc != 3)
+                        usage();
+                opt_s++;
+                optcount++;
+        }
+
+        /* check that at least one option was used */
+        if (!optcount)
+                usage();
+
+        /* open file on which ioctl will operate */
+        fd = open(argv[2], O_RDONLY);
+        if (fd < 0) {
+                perror(argv[2]);
+                exit(1);
+        }
+
+        /* if specified 3rd arg, want to set debug level */
+        if (opt_d) {
+                if (argc == 4) {
+                        val = atoi(argv[3]);
+                        ret = ioctl(fd, FIST_IOCTL_SET_DEBUG_VALUE, &val);
+                        if (ret < 0) {
+                                perror("ioctl set");
+                                exit(1);
+                        }
+                } else {
+                        ret = ioctl(fd, FIST_IOCTL_GET_DEBUG_VALUE, &val);
+                        if (ret < 0) {
+                                perror("ioctl get");
+                                exit(1);
+                        }
+                        printf("debug ioctl returned value %d\n", val);
+                }
+                goto out;
+        }
+
+        /* branch refcounts */
+        if (opt_c) {
+                int *counts;
+                ret = ioctl(fd, UNIONFS_IOCTL_BRANCH_COUNT, NULL);
+                if (ret < 0) {
+                        perror("ioctl branchcount (a)");
+                        exit(1);
+                }
+                counts = malloc(ret * sizeof(int));
+                if (!counts) {
+                        perror("malloc");
+                        exit(1);
+                }
+                ret = ioctl(fd, UNIONFS_IOCTL_BRANCH_COUNT, counts);
+                if (ret < 0) {
+                        perror("ioctl branchcount (b)");
+                        exit(1);
+                }
+                printf("%d total branches.\n", ret);
+                for (i = 0; i < ret; i++) {
+                        printf("%d: %d\n", i, counts[i]);
+                }
+                free(counts);
+                goto out;
+        }
+
+        /* Update generation number. */
+        if (opt_g) {
+                ret = ioctl(fd, UNIONFS_IOCTL_INCGEN, NULL);
+                if (ret < 0) {
+                        perror("ioctl incgen");
+                        exit(1);
+                }
+                printf("New generation %d\n", ret);
+                goto out;
+        }
+
+        /* Super duper */
+        if (opt_s) {
+                ret = ioctl(fd, UNIONFS_IOCTL_SUPERDUPER, 0);
+                if (ret < 0) {
+                        perror("ioctl superduper");
+                        exit(1);
+                }
+                goto out;
+        }
+
+out:
+        close(fd);
+        exit(0);
+}
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/unionfs.h linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/unionfs.h
--- linux-2.6.12-rc2-mm3/fs/unionfs/unionfs.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/unionfs.h	2005-04-25 11:56:14.570651008 -0700
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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: unionfs.h,v 1.101 2005/02/22 17:24:35 dquigley Exp $
+ */
+
+#ifndef __UNIONFS_H_
+#define __UNIONFS_H_
+
+#ifdef __KERNEL__
+
+#if (!defined(UNIONFS_UNSUPPORTED)) &&(LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9))
+#warning You are compiling Unionfs on an unsupported kernel version.
+#warning To compile Unionfs, you will need to define UNIONFS_UNSUPPORTED.
+#warning Try adding: EXTRACFLAGS=-DUNIONFS_UNSUPPORTED to fistdev.mk
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/spinlock.h>
+#define wq_write_lock_irqsave(lock,flags) spin_lock_irqsave(lock,flags)
+#define wq_write_unlock(lock) spin_unlock(lock)
+#define wq_write_lock_irq(lock) spin_lock_irq(lock)
+#define wq_write_unlock_irqrestore(lock,flags) spin_unlock_irqrestore(lock,flags)
+#endif
+
+/* fist file systems superblock magic */
+#define UNIONFS_SUPER_MAGIC 0xf15f
+
+/* unionfs root inode number */
+#define UNIONFS_ROOT_INO     1
+
+/* Mount time flags */
+#define MOUNT_FLAG(sb)     (stopd(sb)->usi_mount_flag)
+
+/* maximum number of parent directories to create. */
+/* XXX: This shouldn't be so small, and it should be kmalloced. */
+#define MAX_DIR_CREATE  10
+
+/* What type of flags to use for kmalloc, etc. */
+#define GFP_UNIONFS GFP_KERNEL
+
+/* A lock structure that a single task can lock N times and then unlock N times. */
+struct multilock {
+	spinlock_t ml_protect;
+	struct task_struct *ml_locker;
+	int ml_depth;
+	wait_queue_head_t ml_sleepers;
+};
+
+static inline void multilock_init(struct multilock *ml) {
+#ifdef MULTILOCK_TRACK
+	printk("MLI:%p\n", ml);
+#endif
+	ml->ml_protect = SPIN_LOCK_UNLOCKED;
+	ml->ml_locker = NULL;
+	ml->ml_depth = 0;
+	init_waitqueue_head(&ml->ml_sleepers);
+}
+
+#define multilock_lock(ml) __multilock_lock((ml), __FILE__, __FUNCTION__, __LINE__)
+static inline void __multilock_lock(struct multilock *ml, const char *file, const char *function, int line) {
+
+#ifdef MULTILOCK_TRACK
+	printk("ILK:%d:%p:%d (%s:%d)\n", current->pid, ml, ml->ml_depth, function, line);
+#endif
+
+retry:
+	spin_lock(&ml->ml_protect);
+	if (ml->ml_locker == NULL) {
+		ml->ml_locker = current;
+		ml->ml_depth = 1;
+	}
+	else if (ml->ml_locker == current) {
+		ml->ml_depth++;
+	} else {
+		/* SLEEP_ON_VAR */
+		wait_queue_t wait;
+		unsigned long flags;
+		init_waitqueue_entry(&wait, current);
+
+		/* We want to go to sleep */
+		current->state = TASK_UNINTERRUPTIBLE;
+
+		/* SLEEP_ON_HEAD */
+		wq_write_lock_irqsave(&ml->ml_sleepers.lock, flags);
+		__add_wait_queue(&ml->ml_sleepers, &wait);
+		wq_write_unlock(&ml->ml_sleepers.lock);
+
+		/* This line is why we can't use sleep_on, because we need to save our queue. */
+		spin_unlock(&ml->ml_protect);
+
+		schedule();
+
+		/* SLEEP_ON_TAIL */
+		wq_write_lock_irq(&ml->ml_sleepers.lock);
+		__remove_wait_queue(&ml->ml_sleepers, &wait);
+		wq_write_unlock_irqrestore(&ml->ml_sleepers.lock, flags);
+		goto retry;
+	}
+	spin_unlock(&ml->ml_protect);
+
+#ifdef MULTILOCK_TRACK
+	printk("OLK:%d:%p:%d\n", current->pid, ml, ml->ml_depth);
+#endif
+}
+
+#define multilock_unlock(ml) __multilock_unlock((ml), __FILE__, __FUNCTION__, __LINE__)
+static inline void __multilock_unlock(struct multilock *ml, const char *file, const char *function, int line) {
+#ifdef MULTILOCK_TRACK
+	printk("IUL:%d:%p:%d (%s:%d)\n", current->pid, ml, ml->ml_depth, function, line);
+#endif
+	spin_lock(&ml->ml_protect);
+	ASSERT2(ml->ml_depth > 0);
+	ASSERT2(ml->ml_locker == current);
+	ml->ml_depth--;
+	if (ml->ml_depth == 0) {
+		ml->ml_locker = NULL;
+		wake_up(&ml->ml_sleepers);
+	}
+	spin_unlock(&ml->ml_protect);
+#ifdef MULTILOCK_TRACK
+	printk("OUL:%d:%p:%d\n", current->pid, ml, ml->ml_depth);
+#endif
+}
+
+/* Operations vectors defined in specific files. */
+extern struct file_operations unionfs_main_fops;
+extern struct file_operations unionfs_dir_fops;
+extern struct inode_operations unionfs_main_iops;
+extern struct inode_operations unionfs_dir_iops;
+extern struct inode_operations unionfs_symlink_iops;
+extern struct super_operations unionfs_sops;
+extern struct dentry_operations unionfs_dops;
+extern struct export_operations unionfs_export_ops;
+
+/* How long should an entry be allowed to persist */
+#define RDCACHE_JIFFIES 5*HZ
+/* unionfs inode data in memory */
+struct unionfs_inode_info {
+    inode_t ** uii_inode;
+    int b_start;
+    int b_end;
+    atomic_t uii_generation;
+    int uii_stale;
+    /* Stuff for readdir over NFS. */
+    spinlock_t uii_rdlock;
+    struct list_head uii_readdircache;
+    int uii_rdversion;
+    int uii_rdcount;
+    int uii_hashsize;
+};
+
+/* unionfs dentry data in memory */
+struct unionfs_dentry_info {
+    struct multilock udi_lock;
+    dentry_t ** udi_dentry;
+    int udi_bstart;
+    int udi_bend;
+    int udi_bcount;
+    atomic_t udi_generation;
+};
+
+/* unionfs super-block data in memory */
+struct unionfs_sb_info {
+    struct super_block *usi_thissb;
+    super_block_t **usi_sb;
+    atomic_t *usi_sbcount;
+    vfs_mount_t **usi_hidden_mnt;
+    int *usi_branchperms;
+
+    int b_start;
+    int b_end;
+
+    uid_t copyupuid;
+    gid_t copyupgid;
+    umode_t copyupmode;
+
+    atomic_t usi_generation;
+    unsigned long usi_mount_flag;
+
+#ifdef SPLIT_VIEW_CACHES
+    struct super_block *usi_primary;
+    struct list_head usi_altsupers;
+#endif
+};
+
+/*
+ * structure for making the linked list of entrues by readdir on left branch
+ * to compare with entries on right branch
+ */
+struct filldir_node {
+    struct list_head file_list;   // list for directory entries
+    char *name;                   // name entry
+    int hash;                     // name hash
+    int namelen;                  // name len since name is not 0 terminated
+    int bindex;                   // we can check for duplicate whiteouts and files in the same branch in order to return -EIO.
+    int whiteout;		  // is this a whiteout entry?
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define DNAME_INLINE_LEN_MIN DNAME_INLINE_LEN
+#endif
+    char iname[DNAME_INLINE_LEN_MIN]; // Inline name, so we don't need to separately kmalloc small ones
+};
+
+/* Filldir cache creation/deletion. */
+void destroy_filldir_cache(void);
+int init_filldir_cache(void);
+
+/* Initialize and free readdir-specific  state. */
+int init_rdstate(struct file *file);
+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex);
+void free_rdstate(struct unionfs_dir_state *state);
+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen, int bindex, int whiteout);
+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, const char *name, int namelen);
+
+/* Directory hash table. */
+#define DIREOF (2147483647)
+struct unionfs_dir_state {
+    unsigned long uds_key;
+    unsigned long uds_offset;
+    int uds_bindex;
+    int uds_dirpos;
+    int uds_size; /* How big is the hash table? */
+    int uds_hashentries; /* How many entries have been inserted? */
+    unsigned long uds_access;
+    /* This cache list is used when the inode keeps us around. */
+    struct list_head uds_cache;
+    struct list_head uds_list[0];
+};
+
+/* file private data. */
+struct unionfs_file_info {
+    struct file ** ufi_file;
+    int b_start;
+    int b_end;
+    atomic_t ufi_generation;
+    struct unionfs_dir_state *rdstate;
+    unsigned long ufi_enddiroff;
+};
+
+/*
+ * MACROS:
+ */
+// File TO Private Data
+#define ftopd(file) ((struct unionfs_file_info *)((file)->private_data))
+#define ftopd_lhs(file) ((file)->private_data)
+// File TO Hidden File
+#define ftohf(file) (ftopd(file)->ufi_file[ftopd(file)->b_start])
+// File to Hidden File based on index
+#define ftohf_index(file, index) ((ftopd(file))->ufi_file[index])
+// File to Hidden File Pointers
+#define ftohf_ptr(file)  (ftopd(file)->ufi_file)
+// Macros to refer to branch indices
+#define fbstart(file) (ftopd(file)->b_start)
+#define fbend(file) (ftopd(file)->b_end)
+// Initialize private data for inode
+#define init_ftopd(file) \
+     {\
+        memset(ftopd(file), 0, sizeof(struct unionfs_file_info));\
+        fbstart(file) = -1; \
+        fbend(file) = -1;\
+     }
+/* Initialize array pointers for hidden files */
+#define init_ftohf_ptr(file, num) memset(ftohf_ptr(file), 0, (sizeof(file_t *) * num))
+
+// Inode TO Private Data
+#define itopd(ino) ((struct unionfs_inode_info *)(ino)->u.generic_ip)
+#define itopd_lhs(ino) ((ino)->u.generic_ip)
+// Inode TO Hidden Inode
+// #define itohi(ino) itopd(ino)->uii_inode[itopd(ino)->b_start]
+#define itohi(ino) __itohi(ino, __FILE__, __FUNCTION__, __LINE__)
+static inline struct inode *__itohi(struct inode *ino, const char *file, const char *function, int line) {
+    struct inode *hidden_inode;
+    ASSERT2(itopd(ino)->b_start >= 0);
+    hidden_inode = itopd(ino)->uii_inode[itopd(ino)->b_start];
+    ASSERT2((atomic_read(&hidden_inode->i_count)) > 0);
+    return hidden_inode;
+}
+
+// Inode To Hidden Inode based on index
+#define itohi_index(ino, index) (itopd(ino)->uii_inode[index])
+// Inode To Hidden Inode Pointers
+#define itohi_ptr(ino)  (itopd(ino)->uii_inode)
+#define vnode2lower itohi
+// Macros to refer to branch indices
+#define ibstart(ino) (itopd(ino)->b_start)
+#define ibend(ino) (itopd(ino)->b_end)
+// Initialize private data for inode
+#define init_itopd(ino)  \
+    {\
+        PASSERT(ino->i_sb);\
+        memset(itopd(ino), 0, sizeof(struct unionfs_inode_info));\
+        itopd(ino)->b_start = -1; \
+        itopd(ino)->b_end = -1;\
+        atomic_set(&itopd(ino)->uii_generation, atomic_read(&stopd(ino->i_sb)->usi_generation));\
+	itopd(ino)->uii_rdlock = SPIN_LOCK_UNLOCKED;\
+	itopd(ino)->uii_rdcount = 1;\
+	itopd(ino)->uii_hashsize = -1;\
+	INIT_LIST_HEAD(&itopd(ino)->uii_readdircache);\
+    }
+// Initialize array pointers for inode
+#define init_itohi_ptr(inode, num) memset(itohi_ptr(inode), 0, (sizeof(inode_t *) * num))
+
+// Superblock TO Private Data
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define stopd(super) ((struct unionfs_sb_info *)(super)->u.generic_sbp)
+#define stopd_lhs(super) ((super)->u.generic_sbp)
+#else
+#define stopd(super) ((struct unionfs_sb_info *)(super)->s_fs_info)
+#define stopd_lhs(super) ((super)->s_fs_info)
+#endif
+
+// Superblock TO Hidden Superblock
+#define stohs(super) (stopd(super)->usi_sb[stopd(super)->b_start])
+// Superblock to Hidden Superblock based on branch index
+#define stohs_index(super, index) (stopd(super)->usi_sb[index])
+// Superblock to Hidden Array of super blocks
+#define stohs_ptr(super) (stopd(super)->usi_sb)
+// Superblock to Hidden Mount point
+#define stohiddenmnt_index(super, index) (stopd(super)->usi_hidden_mnt[index])
+// Superblock to Hidden Mount Pointer
+#define stohiddenmnt_ptr(super) (stopd(super)->usi_hidden_mnt)
+// Macros to refer to the branch indices
+#define sbstart(sb) stopd(sb)->b_start
+#define sbend(sb) stopd(sb)->b_end
+#define sbmax(sb) (stopd(sb)->b_end + 1)
+// Initialize private data for super block
+
+#define init_stopd(sb) \
+    {\
+        memset(stopd(sb), 0, sizeof(struct unionfs_sb_info)); \
+	stopd(sb)->usi_thissb = sb; \
+        stopd(sb)->b_start = -1;\
+        stopd(sb)->b_end = -1;\
+	atomic_set(&stopd(sb)->usi_generation, 1);\
+	init_alt_super(sb); \
+    }
+
+#ifdef SPLIT_VIEW_CACHES
+#define init_alt_super(sb) \
+    {\
+	stopd(sb)->usi_primary = sb; \
+	INIT_LIST_HEAD(&stopd(sb)->usi_altsupers); \
+    }
+#else
+#define init_alt_super(sb)
+#endif
+
+// Initialize array pointers for hidden superblock
+#define init_stohs_ptr(sb, num) memset(stohs_ptr(sb), 0, (sizeof(super_block_t *) * num))
+#define init_stohiddenmnt_ptr(sb, num) memset(stohiddenmnt_ptr(sb), 0, (sizeof(vfs_mount_t *) * num))
+
+/* Get and put branches on the superblock. */
+#define branchget(sb, index) atomic_inc(&stopd(sb)->usi_sbcount[index]);
+#define branchput(sb, index) atomic_dec(&stopd(sb)->usi_sbcount[index]);
+
+#define lock_dpd(dentry) multilock_lock(&dtopd(dentry)->udi_lock)
+#define unlock_dpd(dentry) multilock_unlock(&dtopd(dentry)->udi_lock)
+
+#define lock_dpd2(dentry) __multilock_lock(&dtopd(dentry)->udi_lock, file, function, line)
+#define unlock_dpd2(dentry) __multilock_unlock(&dtopd(dentry)->udi_lock, file, function, line)
+
+// Dentry to Hidden Dentry Pointer
+#define dtohd_ptr(dentry) (dtopd(dentry)->udi_dentry)
+// Dentry TO Hidden Dentry (and private data --- for the future)
+#define dtopd(dent) __dtopd(dent, 1, __FILE__, __FUNCTION__, __LINE__)
+static inline struct unionfs_dentry_info *__dtopd(const struct dentry *dent, int check, const char *file, const char *function, int line) {
+	struct unionfs_dentry_info *ret;
+
+	PASSERT2(dent);
+	ret = (struct unionfs_dentry_info *)(dent)->d_fsdata;
+	/* We are really only interested in catching poison here. */
+	if (ret) {
+		PASSERT2(ret);
+		if (check) {
+			if ((ret->udi_bend > ret->udi_bcount) || (ret->udi_bend > sbmax(dent->d_sb))) {
+				printk("udi_bend = %d, udi_count = %d, sbmax = %d\n", ret->udi_bend, ret->udi_bcount, sbmax(dent->d_sb));
+			}
+			ASSERT2(ret->udi_bend <= ret->udi_bcount);
+			ASSERT2(ret->udi_bend <= sbmax(dent->d_sb));
+		}
+	}
+
+	return ret;
+}
+
+#define dtopd_nocheck(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata)
+#define dtopd_lhs(dent) ((dent)->d_fsdata)
+
+// Dentry to Hidden dentry
+// #define dtohd(dent) dtopd(dent)->udi_dentry[dtopd(dent)->b_start]
+#define dtohd(dent) __dtohd(dent, __FILE__, __FUNCTION__, __LINE__)
+static inline struct dentry *__dtohd(struct dentry *dent, const char *file, const char *function, int line) {
+    struct dentry *d;
+
+    lock_dpd2(dent);
+
+    PASSERT2(dent);
+    PASSERT2(dent->d_op);
+    ASSERT2(dent->d_op == &unionfs_dops);
+    ASSERT2(dent->d_sb->s_op == &unionfs_sops);
+    if (dent->d_inode) {
+	PASSERT2(dent->d_inode);
+	ASSERT(dent->d_inode->i_op == &unionfs_main_iops ||
+		dent->d_inode->i_op == &unionfs_dir_iops ||
+		dent->d_inode->i_op == &unionfs_symlink_iops);
+    }
+
+    ASSERT2(dtopd(dent)->udi_bstart >= 0);
+    d = dtopd(dent)->udi_dentry[dtopd(dent)->udi_bstart];
+    if (d) {
+	PASSERT2(d);
+    	ASSERT2((atomic_read(&d->d_count)) > 0);
+    }
+
+    unlock_dpd2(dent);
+
+    return d;
+}
+
+// Dentry to Hidden Dentry based on index
+#define set_dtohd_index(dent, index, val) __set_dtohd_index(dent, index, val, __FILE__, __FUNCTION__, __LINE__)
+static inline struct dentry *__set_dtohd_index(struct dentry *dent, int index, struct dentry *val, const char *file, const char *function, int line) {
+    PASSERT2(dent);
+
+    lock_dpd2(dent);
+
+    PASSERT2(dent->d_sb);
+    PASSERT2(dtopd(dent));
+    PASSERT2(dtopd(dent)->udi_dentry);
+    ASSERT2(index >= 0);
+    if (index > sbend(dent->d_sb)) {
+	printk("Dentry index out of super bounds: index=%d, sbend=%d\n", index, sbend(dent->d_sb));
+        ASSERT2(index <= sbend(dent->d_sb));
+    }
+    if (index > dtopd(dent)->udi_bcount) {
+	printk("Dentry index out of array bounds: index=%d, count=%d\n", index, dtopd(dent)->udi_bcount);
+	printk("Generation of dentry: %d\n", atomic_read(&dtopd(dent)->udi_generation));
+	printk("Generation of sb: %d\n", atomic_read(&stopd(dent->d_sb)->usi_generation));
+        ASSERT2(index <= dtopd(dent)->udi_bcount);
+    }
+    if (val) {
+	PASSERT2(val);
+    }
+    dtopd(dent)->udi_dentry[index] = val;
+
+    unlock_dpd2(dent);
+    return val;
+}
+
+#define dtohd_index(dent, index) __dtohd_index(dent, index, __FILE__, __FUNCTION__, __LINE__)
+static inline struct dentry *__dtohd_index(const struct dentry *dent, int index, const char *file, const char *function, int line) {
+    struct dentry *d;
+
+    PASSERT2(dent);
+    lock_dpd2(dent);
+    PASSERT2(dent->d_sb);
+    PASSERT2(dtopd(dent));
+    PASSERT2(dtopd(dent)->udi_dentry);
+    ASSERT2(index >= 0);
+    if (index > sbend(dent->d_sb)) {
+	printk("Dentry index out of super bounds: index=%d, sbend=%d\n", index, sbend(dent->d_sb));
+        ASSERT2(index <= sbend(dent->d_sb));
+    }
+    if (index > dtopd(dent)->udi_bcount) {
+	printk("Dentry index out of array bounds: index=%d, count=%d\n", index, dtopd(dent)->udi_bcount);
+	printk("Generation of dentry: %d\n", atomic_read(&dtopd(dent)->udi_generation));
+	printk("Generation of sb: %d\n", atomic_read(&stopd(dent->d_sb)->usi_generation));
+        ASSERT2(index <= dtopd(dent)->udi_bcount);
+    }
+    d = dtopd(dent)->udi_dentry[index];
+    if (d) {
+	PASSERT2(d);
+    	ASSERT2((atomic_read(&d->d_count)) > 0);
+    }
+    unlock_dpd2(dent);
+    return d;
+}
+// Macros to refer to branch indices
+
+#define dbend(dentry) __dbend(dentry, __FILE__, __FUNCTION__, __LINE__)
+static inline int __dbend(const struct dentry *dentry, const char *file, const char *function, int line) {
+    return dtopd(dentry)->udi_bend;
+}
+#define set_dbend(dentry, val) __set_dbend(dentry, val, __FILE__, __FUNCTION__, __LINE__)
+static inline int __set_dbend(const struct dentry *dentry, int val, const char *file, const char *function, int line) {
+    if (val < 0) {
+	ASSERT2(val == -1);
+    }
+    ASSERT2(val <= __dtopd(dentry, 0, file, function, line)->udi_bcount);
+    ASSERT2(val <= sbmax(dentry->d_sb));
+    dtopd(dentry)->udi_bend = val;
+    return __dtopd(dentry, 1, file, function, line)->udi_bend;
+}
+
+
+#define dbstart(dentry) __dbstart(dentry, __FILE__, __FUNCTION__, __LINE__)
+static inline int __dbstart(const struct dentry *dentry, const char *file, const char *function, int line) {
+    return dtopd(dentry)->udi_bstart;
+}
+#define set_dbstart(dentry, val) __set_dbstart(dentry, val, __FILE__, __FUNCTION__, __LINE__)
+static inline int __set_dbstart(const struct dentry *dentry, int val, const char *file, const char *function, int line) {
+    if (val < 0) {
+	ASSERT2(val == -1);
+    }
+    ASSERT2(val <= __dtopd(dentry, 0, file, function, line)->udi_bcount);
+    ASSERT2(val <= sbmax(dentry->d_sb));
+    __dtopd(dentry, 0, file, function, line)->udi_bstart = val;
+    return __dtopd(dentry, 1, file, function, line)->udi_bstart;
+}
+
+
+#define init_dtopd(dentry, sb) \
+    {\
+	multilock_init(&dtopd_nocheck(dentry)->udi_lock); \
+	reinit_dtopd(dentry, sb); \
+    }
+
+#define reinit_dtopd(dentry, sb) {\
+	dtopd_nocheck(dentry)->udi_dentry = NULL;\
+        dtopd_nocheck(dentry)->udi_bstart = -1;\
+        dtopd_nocheck(dentry)->udi_bend = -1;\
+        dtopd_nocheck(dentry)->udi_bcount = 0;\
+        atomic_set(&dtopd_nocheck(dentry)->udi_generation, atomic_read(&stopd(sb)->usi_generation));\
+    }
+#define init_dentry_info(ptr) { \
+        memset(ptr, 0, sizeof(struct unionfs_dentry_info));\
+        ptr->udi_bstart = -1;\
+        ptr->udi_bend = -1;\
+}
+// Initialize array pointers for sb
+#define init_dtohd_ptr(dent, num) do { \
+	struct dentry *d = dent; \
+	dtopd(d)->udi_bcount = num; \
+	memset(dtohd_ptr(d), 0, (sizeof(dentry_t *) * num)); \
+} while(0)
+
+#define sbt(sb) ((sb)->s_type->name)
+
+/*
+ * EXTERNALS:
+ */
+/* replicates the directory structure upto given dentry in given branch */
+extern dentry_t *unionfs_create_dirs(inode_t *dir, dentry_t *dentry, int bindex);
+
+/* partial lookup */
+extern int unionfs_partial_lookup(dentry_t *dentry);
+
+/* Pass an unionfs dentry and an index and it will try to create a whiteout in branch 'index'.
+   On error, it will proceed to a branch to the left */
+extern int create_whiteout(dentry_t *dentry, int start);
+extern int create_whiteout_parent(dentry_t *parent_dentry, const char *filename, int start);
+/* copies a file from dbstart to newbindex branch */
+extern int unionfs_copyup_file(inode_t *dir, file_t *file, int bstart, int newbindex, int size);
+
+/* copies a dentry from dbstart to newbindex branch */
+extern int unionfs_copyup_dentry_len(inode_t *dir, dentry_t *dentry, int bstart, int new_bindex, file_t **copyup_file, int len);
+
+extern int create_dir_whs(dentry_t *dentry, int cur_index) ;
+
+extern int remove_whiteouts(dentry_t *dentry, dentry_t *hidden_dentry, int bindex);
+
+/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */
+extern int check_empty(dentry_t *dentry, struct unionfs_dir_state **namelist);
+/* Delete whiteouts from this directory in branch bindex. */
+extern int delete_whiteouts(struct dentry *dentry, int bindex, struct unionfs_dir_state *namelist);
+
+/* Re-lookup a hidden dentry. */
+extern int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex);
+
+extern void unionfs_reinterpose(dentry_t *this_dentry);
+extern struct super_block *unionfs_duplicate_super(super_block_t *sb);
+
+
+/* The values for unionfs_interpose's flag. */
+#define INTERPOSE_DEFAULT	0
+#define INTERPOSE_LOOKUP	1
+#define INTERPOSE_REVAL		2
+#define INTERPOSE_REVAL_NEG	3
+#define INTERPOSE_LINK		4
+
+extern int unionfs_interpose(dentry_t *this_dentry, super_block_t *sb, int flag);
+
+/* Branch management ioctls. */
+int unionfs_ioctl_branchcount(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
+int unionfs_ioctl_incgen(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
+int unionfs_ioctl_addbranch(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
+int unionfs_ioctl_delbranch(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
+int unionfs_ioctl_rdwrbranch(inode_t *inode, file_t *unused_file, unsigned int cmd, unsigned long arg);
+int unionfs_ioctl_superduper(inode_t *inode, file_t *file, unsigned int cmd, unsigned long arg);
+
+
+#if defined(UNIONFS_XATTR) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
+/* Extended attribute functions. */
+extern void *xattr_alloc(size_t size, size_t limit);
+extern void xattr_free(void *ptr, size_t size);
+
+extern int unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size);
+extern int unionfs_removexattr(struct dentry *dentry, const char *name);
+extern int unionfs_listxattr(struct dentry *dentry, char *list, size_t size);
+
+#if defined(FIST_SETXATTR_CONSTVOID) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
+int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
+#else
+int unionfs_setxattr(struct dentry *dentry, const char *name, void *value, size_t size, int flags);
+#endif
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */
+
+#define copy_inode_size(dst, src) \
+    dst->i_size = src->i_size; \
+    dst->i_blocks = src->i_blocks;
+
+/* returns the sum of the n_link values of all the underlying inodes of the passed inode */
+static inline int
+get_nlinks(inode_t *inode)
+{
+    int sum_nlinks = 2, bindex;
+    inode_t *hidden_inode;
+
+    PASSERT(inode);
+    if (!S_ISDIR(inode->i_mode)) {
+	return itohi(inode)->i_nlink;
+    }
+
+    for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+        hidden_inode = itohi_index(inode, bindex);
+	if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode)) {
+	    continue;
+	}
+	sum_nlinks += (hidden_inode->i_nlink - 2);
+    }
+    return sum_nlinks;
+}
+
+static inline void
+fist_copy_attr_atime(inode_t *dest, const inode_t *src)
+{
+    PASSERT(dest);
+    PASSERT(src);
+    dest->i_atime = src->i_atime;
+}
+static inline void
+fist_copy_attr_times(inode_t *dest, const inode_t *src)
+{
+    PASSERT(dest);
+    PASSERT(src);
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+}
+static inline void
+fist_copy_attr_timesizes(inode_t *dest, const inode_t *src)
+{
+    PASSERT(dest);
+    PASSERT(src);
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+    copy_inode_size(dest, src);
+}
+static inline void
+fist_copy_attr_all(inode_t *dest, const inode_t *src)
+{
+
+    print_entry_location();
+    PASSERT(dest);
+    PASSERT(src);
+    dest->i_mode = src->i_mode;
+    dest->i_nlink = get_nlinks(dest);
+    dest->i_uid = src->i_uid;
+    dest->i_gid = src->i_gid;
+    dest->i_rdev = src->i_rdev;
+    dest->i_atime = src->i_atime;
+    dest->i_mtime = src->i_mtime;
+    dest->i_ctime = src->i_ctime;
+    dest->i_blksize = src->i_blksize;
+    dest->i_blkbits = src->i_blkbits;
+    copy_inode_size(dest, src);
+
+//DQ: This was a change I noticed in the templates. In 2.6 they removedi_attr_flags.
+//Which makes me think they rolled it into flags.
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+    dest->i_attr_flags = src->i_attr_flags;
+#else
+    dest->i_flags = src->i_flags;
+#endif
+
+    print_exit_location();
+}
+
+
+dentry_t *unionfs_lookup_backend(inode_t *dir, dentry_t *dentry, int interpose_flag);
+int is_stale_inode(struct inode * inode);
+void make_stale_inode(struct inode * inode);
+
+#define IS_SET(sb, check_flag) (check_flag & MOUNT_FLAG(sb))
+
+/* unionfs_permission, check if we should bypass error to facilitate copyup */
+#define IS_COPYUP_ERR(err) (err == -EROFS)
+
+/* unionfs_open, check if we need to copyup the file */
+#define IS_WRITE_FLAG(flag) (flag & (O_WRONLY | O_RDWR | O_APPEND))
+
+/* Is this file on a read-only branch? */
+static inline int __is_robranch_super(struct super_block *sb, int index, char *file, const char *function, int line) {
+    int err = 0;
+
+    print_util_entry_location();
+
+    ASSERT(index >= 0);
+    PASSERT(sb);
+    PASSERT(stopd(sb));
+    PASSERT(stopd(sb)->usi_branchperms);
+
+    if (!(stopd(sb)->usi_branchperms[index] & MAY_WRITE)) {
+	err = -EROFS;
+    }
+    print_util_exit_status(err);
+    return err;
+}
+
+/* Is this file on a read-only branch? */
+static inline int __is_robranch_index(struct dentry *dentry, int index, char *file, const char *function, int line) {
+    int err = 0;
+
+    print_util_entry_location();
+
+    PASSERT2(dentry);
+    ASSERT2(index >= 0);
+    PASSERT2(dtohd_index(dentry, index));
+    PASSERT2(dtohd_index(dentry, index)->d_inode);
+
+    if (!(stopd(dentry->d_sb)->usi_branchperms[index] & MAY_WRITE)) {
+	err = -EROFS;
+    } else if (IS_RDONLY(dtohd_index(dentry, index)->d_inode)) {
+	err = -EROFS;
+    }
+
+    print_util_exit_status(err);
+
+    return err;
+}
+static inline int __is_robranch(struct dentry *dentry, char *file, const char *function, int line) {
+    int index;
+    int err;
+
+    print_util_entry_location();
+
+    PASSERT2(dentry);
+    PASSERT2(dtopd(dentry));
+    index = dtopd(dentry)->udi_bstart;
+    ASSERT2(index >= 0);
+
+    err =  __is_robranch_index(dentry, index, file, function, line);
+
+    print_util_exit_status(err);
+
+    return err;
+}
+
+#define is_robranch(d) __is_robranch(d, __FILE__, __FUNCTION__, __LINE__)
+#define is_robranch_super(s, n) __is_robranch_super(s, n, __FILE__, __FUNCTION__, __LINE__)
+
+/* auto-generated extern definitions */
+
+#endif /* __KERNEL__ */
+
+/*
+ * Definitions for user and kernel code
+ */
+
+/* Definitions for various ways to handle errors.
+   Each flag's value is its bit position */
+
+/* 1 = on error, passup,  0 = try to recover */
+#define GLOBAL_ERR_PASSUP 	0
+
+/* 1 = delete first file/directory, 0 = delete all */
+#define DELETE_FIRST      	2
+
+/* 1 = delete whiteout, 0 = check for DELETE_FIRST */
+#define DELETE_WHITEOUT   	4
+
+/* 1 = use current user's permissions, 0 = use original owner's permissions */
+#define COPYUP_CURRENT_USER     8
+
+/* 1 = f/s mounter permission, 0 = check for COPYUP_OWNER */
+#define COPYUP_FS_MOUNTER	16
+
+/* 1 = set attributes for all underlying files, 0 = leftmost file */
+#define SETATTR_ALL             32
+
+#define VALID_MOUNT_FLAGS (GLOBAL_ERR_PASSUP | DELETE_FIRST | DELETE_WHITEOUT | COPYUP_OWNER | COPYUP_FS_MOUNTER | SETATTR_ALL)
+
+/* ioctls */
+
+#define EUNIONFS_NO_WHITEOUT   1025
+#endif	/* not __UNIONFS_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * End:
+ */
diff -Naur linux-2.6.12-rc2-mm3/fs/unionfs/xattr.c linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/xattr.c
--- linux-2.6.12-rc2-mm3/fs/unionfs/xattr.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.12-rc2-mm3-unionfs/fs/unionfs/xattr.c	2005-04-25 11:56:14.571650856 -0700
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2003-2005 Erez Zadok
+ * Copyright (c) 2003-2005 Charles P. Wright
+ * Copyright (c) 2003-2005 Mohammad Nayyer Zubair
+ * Copyright (c) 2003-2005 Puja Gupta
+ * Copyright (c) 2003-2005 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.11 2005/02/08 15:17:38 cwright Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include "fist.h"
+#include "unionfs.h"
+
+#if defined(UNIONFS_XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20))
+/* 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
+ */
+int unionfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) {
+	struct dentry *hidden_dentry = NULL;
+	int err = -ENOTSUPP;
+	/* Define these anyway so we don't need as much ifdef'ed code. */
+	char *encoded_name = NULL;
+	char *encoded_value = NULL;
+
+	print_entry_location();
+
+	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);
+
+	}
+
+	print_exit_status(err);
+	return err;
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_sem down
+ */
+int
+#if defined(FIST_SETXATTR_CONSTVOID) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
+unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
+#else
+unionfs_setxattr(struct dentry *dentry, const char *name, void *value, size_t size, int flags)
+#endif
+{
+	struct dentry *hidden_dentry = NULL;
+	int err = -ENOTSUPP;
+	/* Define these anyway, so we don't have as much ifdef'ed code. */
+	int classlen;
+
+	print_entry_location();
+
+	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);
+
+	classlen = strlen(UNIONFS_EAFUNC_CLASS);
+
+	if (!strncmp(name, UNIONFS_EAFUNC_CLASS, classlen)) {
+		if (!strcmp(name + classlen, UNIONFS_EAFUNC_DELBRANCH)) {
+			int ival;
+			if (sizeof(ival) != size) {
+				err = -EINVAL;
+				goto out;
+			} else {
+				ival = ntohl(*((int *)value));
+				/* Call the ioctl function*/
+				err = unionfs_ioctl_delbranch(dentry->d_inode, NULL, UNIONFS_IOCTL_DELBRANCH, ival);
+				goto out;
+			}
+		} else {
+			err = -ENOTTY;
+			goto out;
+		}
+	} else {
+		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);
+		}
+	}
+
+out:
+	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 = -ENOTSUPP;
+	char *encoded_name;
+	print_entry_location();
+
+	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);
+	}
+
+	print_exit_status(err);
+	return err;
+}
+
+/* BKL held by caller.
+ * dentry->d_inode->i_sem down
+ */
+int unionfs_listxattr(struct dentry *dentry, char *list, size_t size) {
+	struct dentry *hidden_dentry = NULL;
+	int err = -ENOTSUPP;
+	char *encoded_list = NULL;
+
+	print_entry_location();
+
+	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);
+	}
+
+	print_exit_status(err);
+	return err;
+}
+# endif /* UNIONFS_XATTR && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */
