コード例 #1
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;

}
コード例 #2
0
ファイル: unlink.c プロジェクト: rickgaiser/kernelloader
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_rmdir: ", 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 the first directory. */
		err = unionfs_rmdir_first(dir, dentry, namelist);
		/* create whiteout */
		if (!err) {
			err = create_whiteout(dentry, dbstart(dentry));
		} else {
			int new_err;

			if (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 {
		/* delete all. */
		err = unionfs_rmdir_all(dir, dentry, namelist);
	}

      out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
コード例 #3
0
static int u2fs_rmdir(struct inode *dir, struct dentry *dentry)
{
	struct dentry *lower_dentry;
	struct dentry *lower_dir_dentry;
	int err;
	struct path lower_path;
	
	/* creating whiteout if directory was in read-only */
	if((U2FS_D(dentry)->lower_path[LEFT].dentry) == NULL && 
		(U2FS_D(dentry)->lower_path[LEFT].mnt) == NULL){

		err = create_whiteout(dentry);	
		if(err)
			err = -EIO;
		goto out_whiteout;
	}
	
	u2fs_get_lower_path(dentry, &lower_path, LEFT);
	lower_dentry = lower_path.dentry;
	lower_dir_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;
	
	err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
	
	if (err)
		goto out;
	if (dentry->d_inode)
		clear_nlink(dentry->d_inode);
	fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
	fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
	set_nlink(dir, lower_dir_dentry->d_inode->i_nlink);

out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_dir_dentry);
	u2fs_put_lower_path(dentry, &lower_path);

	/* creating whiteout if file as existing in both branches */
	if((U2FS_D(dentry)->lower_path[RIGHT].dentry) != NULL && 
		(U2FS_D(dentry)->lower_path[RIGHT].mnt) != NULL){
		err = create_whiteout(dentry);	
		if(err)
			err = -EIO;
	}
	d_drop(dentry);
out_whiteout:
	return err;
}
コード例 #4
0
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	unionfs_lock_dentry(dentry);

	/* check if this unionfs directory is empty or not */
	err = check_empty(dentry, &namelist);
	if (err)
		goto out;

	err = unionfs_rmdir_first(dir, dentry, namelist);
	/* create whiteout */
	if (!err)
		err = create_whiteout(dentry, dbstart(dentry));
	else {
		int new_err;

		if (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;
	}

out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unionfs_unlock_dentry(dentry);
	return err;
}
コード例 #5
0
int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
{
	int err = 0;
	struct unionfs_dir_state *namelist = NULL;

	print_entry_location();
	lock_dentry(dentry);
	fist_print_dentry("IN unionfs_rmdir: ", dentry);

	/* check if this unionfs directory is empty or not */
	err = check_empty(dentry, &namelist);
	if (err) {
#if 0
		/* vfs_rmdir(our caller) unhashed the dentry.  This will recover
		 * the Unionfs inode number for the directory itself, but the 
		 * children are already lost.  It seems that tmpfs manages its
		 * way around this by upping the refcount on everything.
		 *
		 * Even if we do this, we still lose the inode numbers of the
		 * children.  The best way to fix this is to fix the VFS (or
		 * use persistent inode maps). */
		if (d_unhashed(dentry))
			d_rehash(dentry);
#endif
		goto out;
	}
#ifdef UNIONFS_DELETE_ALL
	if (IS_SET(dir->i_sb, DELETE_ALL)) {
		/* delete all. */
		err = unionfs_rmdir_all(dir, dentry, namelist);
	} else {		/* Delete the first directory. */
#endif
		err = unionfs_rmdir_first(dir, dentry, namelist);
		/* create whiteout */
		if (!err) {
			err = create_whiteout(dentry, dbstart(dentry));
		} else {
			int new_err;

			if (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;
		}

#ifdef UNIONFS_DELETE_ALL
	}
#endif
      out:
	/* call d_drop so the system "forgets" about us */
	if (!err)
		d_drop(dentry);

	if (namelist)
		free_rdstate(namelist);

	unlock_dentry(dentry);
	print_exit_status(err);
	return err;
}
コード例 #6
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;
}
コード例 #7
0
static int unionfs_rmdir_all(struct inode *dir, struct dentry *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;

	print_entry_location();
	fist_print_dentry("IN unionfs_rmdir_all: ", dentry);

	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);
		if (S_ISDIR(hidden_dentry->d_inode->i_mode)) {
			delete_whiteouts(dentry, bindex, namelist);
			if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
				err =
				    vfs_rmdir(hidden_dir_dentry->d_inode,
					      hidden_dentry);
			}
		} else {
			err = -EISDIR;
		}

		fist_copy_attr_times(dir, hidden_dir_dentry->d_inode);
		unlock_dir(hidden_dir_dentry);
		if (err) {
			int local_err =
			    unionfs_refresh_hidden_dentry(dentry, bindex);
			if (local_err) {
				err = local_err;
				goto out;
			}

			if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY
			    && err != -EISDIR)
				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 {
		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;
}
コード例 #8
0
ファイル: unlink.c プロジェクト: rickgaiser/kernelloader
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;
}
コード例 #9
0
/*
 * The locking rules in u2fs_rename are complex.  We could use a simpler
 * superblock-level name-space lock for renames and copy-ups.
 */
static int u2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
			 struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *lower_old_dentry = NULL;
	struct dentry *lower_new_dentry = NULL;
	struct dentry *lower_old_dir_dentry = NULL;
	struct dentry *lower_new_dir_dentry = NULL;
	struct dentry *trap = NULL;
	struct dentry *ret = NULL;
	struct path lower_old_path, lower_new_path;
	
	/* creating parent directories if destination is read-only */
	if((U2FS_D(old_dentry)->lower_path[LEFT].dentry) == NULL && 
		(U2FS_D(old_dentry)->lower_path[LEFT].mnt) == NULL){

		err = create_whiteout(old_dentry);	
		if(err){
			err = -EIO;
			goto out_copyup;
		}

		err = copyup_dentry(old_dir, old_dentry,
			  old_dentry->d_name.name, old_dentry->d_name.len,
			  NULL, i_size_read(old_dentry->d_inode));
		if(err)
			goto out_copyup;
	}
	
	if((U2FS_D(new_dentry)->lower_path[LEFT].dentry) == NULL && 
		(U2FS_D(new_dentry)->lower_path[LEFT].mnt) == NULL){

		ret = create_parents(new_dir, new_dentry, 
					new_dentry->d_name.name);

		if (!ret || IS_ERR(ret)) {
					err = PTR_ERR(ret);
					if (!IS_COPYUP_ERR(err))
						printk(KERN_ERR
					      	 "u2fs: create_parents for "
				     		  "u2fs_rename failed"
					      	 "err=%d\n", err);
					goto out_copyup;
		}
		u2fs_postcopyup_setmnt(new_dentry);
		u2fs_put_reset_lower_path(new_dentry, RIGHT);
		
		if(err)
			goto out_copyup;
	}
	
	u2fs_get_lower_path(old_dentry, &lower_old_path, LEFT);
	u2fs_get_lower_path(new_dentry, &lower_new_path, LEFT);

	lower_old_dentry = lower_old_path.dentry;
	lower_new_dentry = lower_new_path.dentry;
	lower_old_dir_dentry = dget_parent(lower_old_dentry);
	lower_new_dir_dentry = dget_parent(lower_new_dentry);

	trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	/* source should not be ancestor of target */
	if (trap == lower_old_dentry) {
		err = -EINVAL;
		goto out;
	}
	/* target should not be ancestor of source */
	if (trap == lower_new_dentry) {
		err = -ENOTEMPTY;
		goto out;
	}

	err = mnt_want_write(lower_old_path.mnt);
	if (err)
		goto out;
	err = mnt_want_write(lower_new_path.mnt);
	if (err)
		goto out_drop_old_write;

	err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
			 lower_new_dir_dentry->d_inode, lower_new_dentry);
	if (err)
		goto out_err;

	fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
	fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode);
	if (new_dir != old_dir) {
		fsstack_copy_attr_all(old_dir,
				      lower_old_dir_dentry->d_inode);
		fsstack_copy_inode_size(old_dir,
					lower_old_dir_dentry->d_inode);
	}

out_err:
	mnt_drop_write(lower_new_path.mnt);
out_drop_old_write:
	mnt_drop_write(lower_old_path.mnt);
out:
	unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
	dput(lower_old_dir_dentry);
	dput(lower_new_dir_dentry);
	u2fs_put_lower_path(old_dentry, &lower_old_path);
	u2fs_put_lower_path(new_dentry, &lower_new_path);
out_copyup:
	return err;
}
コード例 #10
0
static int u2fs_unlink(struct inode *dir, struct dentry *dentry)
{
	int err=0;
	struct dentry *lower_dentry = NULL;
	struct inode *lower_dir_inode;
	struct inode *lower_inode;
	struct dentry *lower_dir_dentry = NULL;
	struct path lower_path;
	
	/* creating whiteout if file unlinked from read-only branch */
	if((U2FS_D(dentry)->lower_path[LEFT].dentry) == NULL && 
		(U2FS_D(dentry)->lower_path[LEFT].mnt) == NULL){

		err = create_whiteout(dentry);	
		if(err)
			err = -EIO;
		goto out_whiteout;
	}
	else{
		u2fs_get_lower_path(dentry, &lower_path, LEFT);
		lower_dir_inode = u2fs_lower_inode(dir, LEFT);
		lower_inode = u2fs_lower_inode(dentry->d_inode, LEFT);
	}

	lower_dentry = lower_path.dentry;
	dget(lower_dentry);
	lower_dir_dentry = lock_parent(lower_dentry);

	err = mnt_want_write(lower_path.mnt);
	if (err)
		goto out_unlock;
	err = vfs_unlink(lower_dir_inode, lower_dentry);

	/*
	 * Note: unlinking on top of NFS can cause silly-renamed files.
	 * Trying to delete such files results in EBUSY from NFS
	 * below.  Silly-renamed files will get deleted by NFS later on, so
	 * we just need to detect them here and treat such EBUSY errors as
	 * if the upper file was successfully deleted.
	 */
	if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)
		err = 0;
	if (err)
		goto out;
	fsstack_copy_attr_times(dir, lower_dir_inode);
	fsstack_copy_inode_size(dir, lower_dir_inode);
	set_nlink(dentry->d_inode, lower_inode->i_nlink);
	dentry->d_inode->i_ctime = dir->i_ctime;
	
out:
	mnt_drop_write(lower_path.mnt);
out_unlock:
	unlock_dir(lower_dir_dentry);
	dput(lower_dentry);
	u2fs_put_lower_path(dentry, &lower_path);

	/* checking for same file in right branch */
	if((U2FS_D(dentry)->lower_path[RIGHT].dentry) != NULL && 
		(U2FS_D(dentry)->lower_path[RIGHT].mnt) != NULL){

		err = create_whiteout(dentry);
		if(err)
			err = -EIO;
	}
	d_drop(dentry);
out_whiteout:
	return err;
}
コード例 #11
0
ファイル: rename.c プロジェクト: robacklin/ts7800
/*
 * Main rename code.  This is sufficiently complex, that it's documented in
 * Documentation/filesystems/unionfs/rename.txt.  This routine calls
 * __unionfs_rename() above to perform some of the work.
 */
static int do_unionfs_rename(struct inode *old_dir,
			     struct dentry *old_dentry,
			     struct dentry *old_parent,
			     struct inode *new_dir,
			     struct dentry *new_dentry,
			     struct dentry *new_parent)
{
	int err = 0;
	int bindex, bwh_old;
	int old_bstart, old_bend;
	int new_bstart, new_bend;
	int do_copyup = -1;
	int local_err = 0;
	int eio = 0;
	int revert = 0;

	old_bstart = dbstart(old_dentry);
	bwh_old = old_bstart;
	old_bend = dbend(old_dentry);

	new_bstart = dbstart(new_dentry);
	new_bend = dbend(new_dentry);

	/* Rename source to destination. */
	err = __unionfs_rename(old_dir, old_dentry, old_parent,
			       new_dir, new_dentry, new_parent,
			       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;

		BUG_ON(bindex < 0);
		unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
		if (!unlink_dentry)
			continue;

		unlink_dir_dentry = lock_parent(unlink_dentry);
		err = is_robranch_super(old_dir->i_sb, bindex);
		if (!err)
			err = vfs_unlink(unlink_dir_dentry->d_inode,
					 unlink_dentry);

		fsstack_copy_attr_times(new_parent->d_inode,
					unlink_dir_dentry->d_inode);
		/* propagate number of hard-links */
		new_parent->d_inode->i_nlink =
			unionfs_get_nlinks(new_parent->d_inode);

		unlock_dir(unlink_dir_dentry);
		if (!err) {
			if (bindex != new_bstart) {
				dput(unlink_dentry);
				unionfs_set_lower_dentry_idx(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 = copyup_dentry(old_parent->d_inode,
					    old_dentry, old_bstart, bindex,
					    old_dentry->d_name.name,
					    old_dentry->d_name.len, NULL,
					    i_size_read(old_dentry->d_inode));
			/* if copyup failed, try next branch to the left */
			if (err)
				continue;
			bwh_old = bindex;
			err = __unionfs_rename(old_dir, old_dentry, old_parent,
					       new_dir, new_dentry, new_parent,
					       bindex);
			break;
		}
	}

	/* make it opaque */
	if (S_ISDIR(old_dentry->d_inode->i_mode)) {
		err = make_dir_opaque(old_dentry, dbstart(old_dentry));
		if (err)
			goto revert;
	}

	/*
	 * 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)) {
		if (bwh_old < 0) {
			printk(KERN_ERR "unionfs: rename error (bwh_old=%d)\n",
			       bwh_old);
			err = -EIO;
			goto out;
		}
		err = create_whiteout(old_dentry, bwh_old);
		if (err) {
			/* can't fix anything now, so we exit with -EIO */
			printk(KERN_ERR "unionfs: can't create a whiteout for "
			       "%s in rename!\n", old_dentry->d_name.name);
			err = -EIO;
		}
	}

out:
	return err;

revert:
	/* Do revert here. */
	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the new refresh failed\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 old_bstart);
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the old refresh failed\n");
		eio = -EIO;
		goto revert_out;
	}

	if (!unionfs_lower_dentry_idx(new_dentry, bindex) ||
	    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object disappeared from under us!\n");
		eio = -EIO;
		goto revert_out;
	}

	if (unionfs_lower_dentry_idx(old_dentry, bindex) &&
	    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {
		printk(KERN_ERR "unionfs: revert failed in rename: "
		       "the object was created underneath us!\n");
		eio = -EIO;
		goto revert_out;
	}

	local_err = __unionfs_rename(new_dir, new_dentry, new_parent,
				     old_dir, old_dentry, old_parent,
				     old_bstart);

	/* If we can't fix it, then we cop-out with -EIO. */
	if (local_err) {
		printk(KERN_ERR "unionfs: revert failed in rename!\n");
		eio = -EIO;
	}

	local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
						 bindex);
	if (local_err)
		eio = -EIO;
	local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
						 bindex);
	if (local_err)
		eio = -EIO;

revert_out:
	if (eio)
		err = eio;
	return err;
}