Esempio n. 1
0
static void
update(
        float (* state)[NHOR][NVER],
        float (* state_next)[NHOR][NVER],
        float (* dstate)[NHOR][NVER],
        float (* dstate_next)[NHOR][NVER]
        ) {
    float dt = 0.01;
    float v, u, d2vdx2, d2vdy2, dvdt, dudt ,I;
    for (int idy = 0; idy < NVER; idy++) {
        for (int idx = 0; idx < NVER; idx++) {
            v = GET(0, 0);
            u = DGET(0, 0);
            d2vdx2 = (GET(0, 1) - 2*v + GET(0, -1));
            d2vdy2 = (GET(1, 0) - 2*v + GET(-1, 0));
            I = 2*(d2vdx2 + d2vdy2);
            if (I < 0) {
                I = 0;
            }
            dvdt = 0.04 * v*v + 5*v + 140 - u + I;
            dudt = 0.02 * (0.2*v - u);
            v = v + dvdt * dt;
            u = u + dudt * dt;
            if (v >= 30) {
                v = -65;
                if (u < 10) {
                    u = u + 8;
                }
            }
            NEXT(0, 0) = v;
            DNEXT(0, 0) = u;
        }
    }
}
Esempio n. 2
0
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
{
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry;
	int bindex;
	int err = 0;

	print_entry_location();

	if ((err = unionfs_partial_lookup(dentry)))
		goto out;

	bindex = dbstart(dentry);

	hidden_dentry = dtohd_index(dentry, bindex);
	if (!hidden_dentry)
		goto out;

	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);
	unlock_dir(hidden_dir_dentry);

	if (err) {
		if (!IS_COPYUP_ERR(err))
			goto out;
	}

	if (err) {
		if (dbstart(dentry) == 0) {
			goto out;
		}
		err = create_whiteout(dentry, dbstart(dentry) - 1);
	} else if (dbopaque(dentry) != -1) {
		/* There is a hidden lower-priority file with the same name. */
		err = create_whiteout(dentry, dbopaque(dentry));
	} else {
		err = create_whiteout(dentry, dbstart(dentry));
	}
      out:
	if (!err)
		dentry->d_inode->i_nlink--;
	/* We don't want to leave negative leftover dentries for revalidate. */
	if (!err && (dbopaque(dentry) != -1))
		update_bstart(dentry);

	print_exit_status(err);
	return err;

}
Esempio n. 3
0
static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry,
			       struct unionfs_dir_state *namelist)
{
	int err;
	struct dentry *hidden_dentry;
	struct dentry *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);

	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;
}
Esempio n. 4
0
int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
			int bstart, int new_bindex, char *name,
			int namelen, struct file **copyup_file, int len)
{
	struct dentry *new_hidden_dentry;
	struct dentry *old_hidden_dentry = NULL;
	struct super_block *sb;
	struct file *input_file = NULL;
	struct file *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();
	verify_locked(dentry);
	fist_print_dentry("IN: copyup_named_dentry", 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 = create_parents_named(dir, dentry, name, new_bindex);
	PASSERT(new_hidden_dentry);
	if (IS_ERR(new_hidden_dentry)) {
		err = PTR_ERR(new_hidden_dentry);
		goto out;
	}

	fist_print_generic_dentry("Copyup Object", new_hidden_dentry);

	/* 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, we must 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)) {
#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:
	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 =
	     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 = copyup_xattrs(old_hidden_dentry, new_hidden_dentry)))
		goto out;
#endif

	/* do not allow files getting deleted to be reinterposed */
	if (!d_deleted(dentry))
		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
		 */
		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);
		}
	}

	fist_print_dentry("OUT: copyup_dentry", dentry);
	fist_print_inode("OUT: copyup_dentry", dentry->d_inode);

	print_exit_status(err);
	return err;
}
Esempio n. 5
0
static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry)
{
	struct dentry *hidden_dentry;
	struct dentry *hidden_dir_dentry;
	int bstart, bend, bindex;
	int err = 0;
	int global_err = 0;

	print_entry_location();

	if ((err = unionfs_partial_lookup(dentry)))
		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);
		unlock_dir(hidden_dir_dentry);

		if (err) {
			/* passup the last error we got */
			if (!IS_COPYUP_ERR(err))
				goto out;
			global_err = err;
		}
	}

	/* 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));
		}
	} else if (dbopaque(dentry) != -1) {
		/* There is a hidden lower-priority file with the same name. */
		err = create_whiteout(dentry, dbopaque(dentry));
	}
      out:
	/* propagate number of hard-links */
	if (dentry->d_inode->i_nlink != 0) {
		dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode);
		if (!err && global_err)
			dentry->d_inode->i_nlink--;
	}
	/* We don't want to leave negative leftover dentries for revalidate. */
	if (!err && (global_err || dbopaque(dentry) != -1))
		update_bstart(dentry);

	print_exit_status(err);
	return err;
}
Esempio n. 6
0
static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *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(dentry->d_name.len + sizeof(".wh."), 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))) {
		if (S_ISREG(hidden_old_dentry->d_inode->i_mode)) {
			err = vfs_rename(hidden_old_dir_dentry->d_inode,
					 hidden_old_dentry,
					 hidden_new_dir_dentry->d_inode,
					 hidden_wh_dentry);
		} else {
			err = vfs_unlink(hidden_old_dir_dentry->d_inode,
					 hidden_old_dentry);
			if (!err)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
				err = vfs_create(hidden_new_dir_dentry->d_inode,
						 hidden_wh_dentry, 0666, NULL);
#else
				err = vfs_create(hidden_new_dir_dentry->d_inode,
						 hidden_wh_dentry, 0666);
#endif
		}
	}

	double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry);
	DPUT(hidden_old_dentry);
	DPUT(hidden_wh_dentry);

	if (!err) {
		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;
		}
		PASSERT(hidden_wh_dentry->d_inode);
		down(&hidden_wh_dentry->d_inode->i_sem);
		newattrs.ia_valid = ATTR_CTIME;
		if (hidden_wh_dentry->d_inode->i_size != 0) {
			newattrs.ia_valid |= ATTR_SIZE;
			newattrs.ia_size = 0;
		}
		/* We discard this error, because the entry is whited out
		 * even if we fail here. */
		notify_change(hidden_wh_dentry, &newattrs);
		up(&hidden_wh_dentry->d_inode->i_sem);
		DPUT(hidden_wh_dentry);
	}

	if (err) {
		if (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:
	KFREE(name);
	print_exit_status(err);
	return err;
}
Esempio n. 7
0
static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
		     struct inode *new_dir, struct dentry *new_dentry,
		     int bindex, struct dentry **wh_old)
{
	int err = 0;
	struct dentry *hidden_old_dentry;
	struct dentry *hidden_new_dentry;
	struct dentry *hidden_old_dir_dentry;
	struct dentry *hidden_new_dir_dentry;
	struct dentry *hidden_wh_dentry;
	struct dentry *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);

	if (!hidden_new_dentry) {
		hidden_new_dentry =
		    create_parents(new_dentry->d_parent->d_inode, new_dentry,
				   bindex);
		if (IS_ERR(hidden_new_dentry)) {
			fist_dprint(7,
				    "error creating directory tree for rename, bindex = %d\n",
				    bindex);
			err = PTR_ERR(hidden_new_dentry);
			goto out;
		}
	}

	wh_name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len);
	if (IS_ERR(wh_name)) {
		err = PTR_ERR(wh_name);
		goto out;
	}

	hidden_wh_dentry =
	    LOOKUP_ONE_LEN(wh_name, hidden_new_dentry->d_parent,
			   new_dentry->d_name.len + WHLEN);
	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;
		}

		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;
	} 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);

	lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);

	err = is_robranch_super(old_dentry->d_sb, bindex);
	if (err)
		goto out_unlock;

	/* ready to whiteout for old_dentry.
	   caller will create the actual whiteout,
	   and must dput(*wh_old) */
	if (wh_old) {
		char *whname;
		whname = alloc_whname(old_dentry->d_name.name,
				      old_dentry->d_name.len);
		err = PTR_ERR(whname);
		if (IS_ERR(whname))
			goto out_unlock;
		*wh_old = LOOKUP_ONE_LEN(whname, hidden_old_dir_dentry,
					 old_dentry->d_name.len + WHLEN);
		KFREE(whname);
		err = PTR_ERR(*wh_old);
		if (IS_ERR(*wh_old)) {
			*wh_old = NULL;
			goto out_unlock;
		}
	}

		fist_print_dentry("NEWBEF", new_dentry);
		fist_print_dentry("OLDBEF", old_dentry);
	err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry,
			 hidden_new_dir_dentry->d_inode, hidden_new_dentry);
		fist_print_dentry("NEWAFT", new_dentry);
		fist_print_dentry("OLDAFT", old_dentry);

      out_unlock:
	unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);

	DPUT(hidden_old_dir_dentry);
	DPUT(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);
	}

	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;
}
Esempio n. 8
0
static int unionfs_create(struct inode *parent, struct dentry *dentry,
			  int mode, struct nameidata *nd)
{
	int err = 0;
	struct dentry *hidden_dentry = NULL;
	struct dentry *whiteout_dentry = NULL;
	struct dentry *new_hidden_dentry;
	struct dentry *hidden_parent_dentry = NULL;
	int bindex = 0, bstart;
	char *name = NULL;

	print_entry_location();
	lock_dentry(dentry);
	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 = alloc_whname(dentry->d_name.name, dentry->d_name.len);
	if (IS_ERR(name)) {
		err = PTR_ERR(name);
		goto out;
	}

	whiteout_dentry =
	    LOOKUP_ONE_LEN(name, hidden_dentry->d_parent,
			   dentry->d_name.len + WHLEN);
	if (IS_ERR(whiteout_dentry)) {
		err = PTR_ERR(whiteout_dentry);
		whiteout_dentry = NULL;
		goto out;
	}

	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 it still has a reference count, then we should
		 * actually unlink the whiteout so that user's data isn't
		 * hosed over.
		 */
		struct dentry *hidden_dir_dentry;
		struct iattr newattrs;

		down(&whiteout_dentry->d_inode->i_sem);
		newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME
		    | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE
		    | ATTR_KILL_SUID | ATTR_KILL_SGID;

		newattrs.ia_mode = mode & ~current->fs->umask;
		newattrs.ia_uid = current->fsuid;
		newattrs.ia_gid = current->fsgid;

		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);

		if (err)
			printk(KERN_WARNING
			       "unionfs: %s:%d: notify_change failed: %d, ignoring..\n",
			       __FILE__, __LINE__, err);

		new_hidden_dentry = dtohd(dentry);
		DGET(new_hidden_dentry);

		hidden_dir_dentry = GET_PARENT(whiteout_dentry);
		lock_rename(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);
		}

		unlock_rename(hidden_dir_dentry, hidden_dir_dentry);
		DPUT(hidden_dir_dentry);

		DPUT(new_hidden_dentry);

		if (err) {
			/* 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. */
			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 = create_parents(parent, dentry, bindex);
			if (!hidden_dentry || IS_ERR(hidden_dentry)) {
				if (IS_ERR(hidden_dentry))
					err = PTR_ERR(hidden_dentry);
				continue;
			}
		}

		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
			err = vfs_create(hidden_parent_dentry->d_inode,
					 hidden_dentry, mode, nd);
		}
		if (err || !hidden_dentry->d_inode) {
			unlock_dir(hidden_parent_dentry);

			/* break out of for loop if the error wasn't  -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;
		}
	}

      out:
	DPUT(whiteout_dentry);
	KFREE(name);

	fist_print_dentry("OUT unionfs_create :", dentry);
	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}