Exemple #1
0
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		   struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *wh_dentry;

	double_lock_dentry(old_dentry, new_dentry);

	if (!S_ISDIR(old_dentry->d_inode->i_mode))
		err = unionfs_partial_lookup(old_dentry);
	else
		err = may_rename_dir(old_dentry);

	if (err)
		goto out;

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

	/*
	 * if new_dentry is already hidden because of whiteout,
	 * simply override it even if the whiteouted dir is not empty.
	 */
	wh_dentry = lookup_whiteout(new_dentry);
	if (!IS_ERR(wh_dentry))
		dput(wh_dentry);
	else 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(new_dentry->d_inode->i_mode)) {
			struct unionfs_dir_state *namelist;
			/* check if this unionfs directory is empty or not */
			err = check_empty(new_dentry, &namelist);
			if (err)
				goto out;

			if (!is_robranch(new_dentry))
				err = delete_whiteouts(new_dentry,
						       dbstart(new_dentry),
						       namelist);

			free_rdstate(namelist);

			if (err)
				goto out;
		}
	}
	err = do_unionfs_rename(old_dir, old_dentry, new_dir, new_dentry);

out:
	if (err)
		/* clear the new_dentry stuff created */
		d_drop(new_dentry);
	else
		/* force re-lookup since the dir on ro branch is not renamed,
		   and hidden dentries still indicate the un-renamed ones. */
		if (S_ISDIR(old_dentry->d_inode->i_mode))
			atomic_dec(&UNIONFS_D(old_dentry)->generation);

	unionfs_unlock_dentry(new_dentry);
	unionfs_unlock_dentry(old_dentry);
	return err;
}
Exemple #2
0
/*
 * The locking rules in unionfs_rename are complex.  We could use a simpler
 * superblock-level name-space lock for renames and copy-ups.
 */
int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		   struct inode *new_dir, struct dentry *new_dentry)
{
	int err = 0;
	struct dentry *wh_dentry;
	struct dentry *old_parent, *new_parent;
	int valid = true;

	unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
	old_parent = dget_parent(old_dentry);
	new_parent = dget_parent(new_dentry);
	/* un/lock parent dentries only if they differ from old/new_dentry */
	if (old_parent != old_dentry &&
	    old_parent != new_dentry)
		unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT);
	if (new_parent != old_dentry &&
	    new_parent != new_dentry &&
	    new_parent != old_parent)
		unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD);
	unionfs_double_lock_dentry(old_dentry, new_dentry);

	valid = __unionfs_d_revalidate(old_dentry, old_parent, false);
	if (!valid) {
		err = -ESTALE;
		goto out;
	}
	if (!d_deleted(new_dentry) && new_dentry->d_inode) {
		valid = __unionfs_d_revalidate(new_dentry, new_parent, false);
		if (!valid) {
			err = -ESTALE;
			goto out;
		}
	}

	if (!S_ISDIR(old_dentry->d_inode->i_mode))
		err = unionfs_partial_lookup(old_dentry, old_parent);
	else
		err = may_rename_dir(old_dentry, old_parent);

	if (err)
		goto out;

	err = unionfs_partial_lookup(new_dentry, new_parent);
	if (err)
		goto out;

	/*
	 * if new_dentry is already lower because of whiteout,
	 * simply override it even if the whited-out dir is not empty.
	 */
	wh_dentry = find_first_whiteout(new_dentry);
	if (!IS_ERR(wh_dentry)) {
		dput(wh_dentry);
	} else 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(new_dentry->d_inode->i_mode)) {
			struct unionfs_dir_state *namelist = NULL;
			/* check if this unionfs directory is empty or not */
			err = check_empty(new_dentry, new_parent, &namelist);
			if (err)
				goto out;

			if (!is_robranch(new_dentry))
				err = delete_whiteouts(new_dentry,
						       dbstart(new_dentry),
						       namelist);

			free_rdstate(namelist);

			if (err)
				goto out;
		}
	}

	err = do_unionfs_rename(old_dir, old_dentry, old_parent,
				new_dir, new_dentry, new_parent);
	if (err)
		goto out;

	/*
	 * force re-lookup since the dir on ro branch is not renamed, and
	 * lower dentries still indicate the un-renamed ones.
	 */
	if (S_ISDIR(old_dentry->d_inode->i_mode))
		atomic_dec(&UNIONFS_D(old_dentry)->generation);
	else
		unionfs_postcopyup_release(old_dentry);
	if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) {
		unionfs_postcopyup_release(new_dentry);
		unionfs_postcopyup_setmnt(new_dentry);
		if (!unionfs_lower_inode(new_dentry->d_inode)) {
			/*
			 * If we get here, it means that no copyup was
			 * needed, and that a file by the old name already
			 * existing on the destination branch; that file got
			 * renamed earlier in this function, so all we need
			 * to do here is set the lower inode.
			 */
			struct inode *inode;
			inode = unionfs_lower_inode(old_dentry->d_inode);
			igrab(inode);
			unionfs_set_lower_inode_idx(new_dentry->d_inode,
						    dbstart(new_dentry),
						    inode);
		}
	}
	/* if all of this renaming succeeded, update our times */
	unionfs_copy_attr_times(old_dentry->d_inode);
	unionfs_copy_attr_times(new_dentry->d_inode);
	unionfs_check_inode(old_dir);
	unionfs_check_inode(new_dir);
	unionfs_check_dentry(old_dentry);
	unionfs_check_dentry(new_dentry);

out:
	if (err)		/* clear the new_dentry stuff created */
		d_drop(new_dentry);

	unionfs_double_unlock_dentry(old_dentry, new_dentry);
	if (new_parent != old_dentry &&
	    new_parent != new_dentry &&
	    new_parent != old_parent)
		unionfs_unlock_dentry(new_parent);
	if (old_parent != old_dentry &&
	    old_parent != new_dentry)
		unionfs_unlock_dentry(old_parent);
	dput(new_parent);
	dput(old_parent);
	unionfs_read_unlock(old_dentry->d_sb);

	return err;
}