int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
		struct inode *_dst_dir, struct dentry *_dst_dentry)
{
	int err, flags;
	/* reduce stack space */
	struct au_ren_args *a;

	AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry));
	IMustLock(_src_dir);
	IMustLock(_dst_dir);

	err = -ENOMEM;
	BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
	a = kzalloc(sizeof(*a), GFP_NOFS);
	if (unlikely(!a))
		goto out;

	a->src_dir = _src_dir;
	a->src_dentry = _src_dentry;
	a->src_inode = a->src_dentry->d_inode;
	a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */
	a->dst_dir = _dst_dir;
	a->dst_dentry = _dst_dentry;
	a->dst_inode = a->dst_dentry->d_inode;
	a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
	if (a->dst_inode) {
		IMustLock(a->dst_inode);
		au_igrab(a->dst_inode);
	}

	err = -ENOTDIR;
	flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN;
	if (S_ISDIR(a->src_inode->i_mode)) {
		au_fset_ren(a->flags, ISDIR);
		if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode)))
			goto out_free;
		err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
						AuLock_DIR | flags);
	} else
		err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
						flags);
	if (unlikely(err))
		goto out_free;

	err = au_d_hashed_positive(a->src_dentry);
	if (unlikely(err))
		goto out_unlock;
	err = -ENOENT;
	if (a->dst_inode) {
		/*
		 * If it is a dir, VFS unhash dst_dentry before this
		 * function. It means we cannot rely upon d_unhashed().
		 */
		if (unlikely(!a->dst_inode->i_nlink))
			goto out_unlock;
		if (!S_ISDIR(a->dst_inode->i_mode)) {
			err = au_d_hashed_positive(a->dst_dentry);
			if (unlikely(err))
				goto out_unlock;
		} else if (unlikely(IS_DEADDIR(a->dst_inode)))
			goto out_unlock;
	} else if (unlikely(d_unhashed(a->dst_dentry)))
		goto out_unlock;

	au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
	di_write_lock_parent(a->dst_parent);

	/* which branch we process */
	err = au_ren_wbr(a);
	if (unlikely(err < 0))
		goto out_parent;
	a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);
	a->h_path.mnt = a->br->br_mnt;

	/* are they available to be renamed */
	err = au_ren_may_dir(a);
	if (unlikely(err))
		goto out_children;

	/* prepare the writable parent dir on the same branch */
	if (a->dst_bstart == a->btgt) {
		au_fset_ren(a->flags, WHDST);
	} else {
		err = au_cpup_dirs(a->dst_dentry, a->btgt);
		if (unlikely(err))
			goto out_children;
	}

	if (a->src_dir != a->dst_dir) {
		/*
		 * this temporary unlock is safe,
		 * because both dir->i_mutex are locked.
		 */
		di_write_unlock(a->dst_parent);
		di_write_lock_parent(a->src_parent);
		err = au_wr_dir_need_wh(a->src_dentry,
					au_ftest_ren(a->flags, ISDIR),
					&a->btgt);
		di_write_unlock(a->src_parent);
		di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1);
		au_fclr_ren(a->flags, ISSAMEDIR);
	} else
		err = au_wr_dir_need_wh(a->src_dentry,
					au_ftest_ren(a->flags, ISDIR),
					&a->btgt);
	if (unlikely(err < 0))
		goto out_children;
	if (err)
		au_fset_ren(a->flags, WHSRC);

	/* lock them all */
	err = au_ren_lock(a);
	if (unlikely(err))
		goto out_children;

	if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE))
		err = au_may_ren(a);
	else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN))
		err = -ENAMETOOLONG;
	if (unlikely(err))
		goto out_hdir;

	/* store timestamps to be revertible */
	au_ren_dt(a);

	/* here we go */
	err = do_rename(a);
	if (unlikely(err))
		goto out_dt;

	/* update dir attributes */
	au_ren_refresh_dir(a);

	/* dput/iput all lower dentries */
	au_ren_refresh(a);

	goto out_hdir; /* success */

out_dt:
	au_ren_rev_dt(err, a);
out_hdir:
	au_ren_unlock(a);
out_children:
	au_nhash_wh_free(&a->whlist);
	if (err && a->dst_inode && a->dst_bstart != a->btgt) {
		AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt);
		au_set_h_dptr(a->dst_dentry, a->btgt, NULL);
		au_set_dbstart(a->dst_dentry, a->dst_bstart);
	}
out_parent:
	if (!err)
		d_move(a->src_dentry, a->dst_dentry);
	else {
		au_update_dbstart(a->dst_dentry);
		if (!a->dst_inode)
			d_drop(a->dst_dentry);
	}
	if (au_ftest_ren(a->flags, ISSAMEDIR))
		di_write_unlock(a->dst_parent);
	else
		di_write_unlock2(a->src_parent, a->dst_parent);
out_unlock:
	aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);
out_free:
	iput(a->dst_inode);
	if (a->thargs)
		au_whtmp_rmdir_free(a->thargs);
	kfree(a);
out:
	AuTraceErr(err);
	return err;
}
示例#2
0
void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
{
	di_write_unlock2(d1, d2);
	si_read_unlock(d1->d_sb);
}
示例#3
0
void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
{
	AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb);
	di_write_unlock2(d1, d2);
	si_read_unlock(d1->d_sb);
}