Esempio n. 1
0
/*
 * when removing a dir, rename it to a unique temporary whiteout-ed name first
 * in order to be revertible and save time for removing many child whiteouts
 * under the dir.
 * returns 1 when there are too many child whiteout and caller should remove
 * them asynchronously. returns 0 when the number of children is enough small to
 * remove now or the branch fs is a remote fs.
 * otherwise return an error.
 */
static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
			   struct au_nhash *whlist, struct inode *dir)
{
	int rmdir_later, err, dirwh;
	struct dentry *h_dentry;
	struct super_block *sb;
	struct inode *inode;

	sb = dentry->d_sb;
	SiMustAnyLock(sb);
	h_dentry = au_h_dptr(dentry, bindex);
	err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
	if (unlikely(err))
		goto out;

	/* stop monitoring */
	inode = d_inode(dentry);
	au_hn_free(au_hi(inode, bindex));

	if (!au_test_fs_remote(h_dentry->d_sb)) {
		dirwh = au_sbi(sb)->si_dirwh;
		rmdir_later = (dirwh <= 1);
		if (!rmdir_later)
			rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
							      dirwh);
		if (rmdir_later)
			return rmdir_later;
	}

	err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
	if (unlikely(err)) {
		AuIOErr("rmdir %pd, b%d failed, %d. ignored\n",
			h_dentry, bindex, err);
		err = 0;
	}

out:
	AuTraceErr(err);
	return err;
}
static int do_rename(struct au_ren_args *a)
{
	int err;
	struct dentry *d, *h_d;

	/* prepare workqueue args for asynchronous rmdir */
	h_d = a->dst_h_dentry;
	if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) {
		err = -ENOMEM;
		a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS);
		if (unlikely(!a->thargs))
			goto out;
		a->h_dst = dget(h_d);
	}

	/* create whiteout for src_dentry */
	if (au_ftest_ren(a->flags, WHSRC)) {
		a->src_bwh = au_dbwh(a->src_dentry);
		AuDebugOn(a->src_bwh >= 0);
		a->src_wh_dentry
			= au_wh_create(a->src_dentry, a->btgt, a->src_h_parent);
		err = PTR_ERR(a->src_wh_dentry);
		if (IS_ERR(a->src_wh_dentry))
			goto out_thargs;
	}

	/* lookup whiteout for dentry */
	if (au_ftest_ren(a->flags, WHDST)) {
		h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name,
				 a->br);
		err = PTR_ERR(h_d);
		if (IS_ERR(h_d))
			goto out_whsrc;
		if (!h_d->d_inode)
			dput(h_d);
		else
			a->dst_wh_dentry = h_d;
	}

	/* rename dentry to tmpwh */
	if (a->thargs) {
		err = au_whtmp_ren(a->dst_h_dentry, a->br);
		if (unlikely(err))
			goto out_whdst;

		d = a->dst_dentry;
		au_set_h_dptr(d, a->btgt, NULL);
		err = au_lkup_neg(d, a->btgt);
		if (unlikely(err))
			goto out_whtmp;
		a->dst_h_dentry = au_h_dptr(d, a->btgt);
	}

	/* cpup src */
	if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) {
		struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;
		struct file *h_file;

		mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
		AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart);
		h_file = au_h_open_pre(a->src_dentry, a->src_bstart);
		if (IS_ERR(h_file)) {
			err = PTR_ERR(h_file);
			h_file = NULL;
		} else
			err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1,
						 !AuCpup_DTIME);
		mutex_unlock(h_mtx);
		au_h_open_post(a->src_dentry, a->src_bstart, h_file);
		if (unlikely(err))
			goto out_whtmp;
	}

	/* rename by vfs_rename or cpup */
	d = a->dst_dentry;
	if (au_ftest_ren(a->flags, ISDIR)
	    && (a->dst_wh_dentry
		|| au_dbdiropq(d) == a->btgt
		/* hide the lower to keep xino */
		|| a->btgt < au_dbend(d)
		|| au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ)))
		au_fset_ren(a->flags, DIROPQ);
	err = au_ren_or_cpup(a);
	if (unlikely(err))
		/* leave the copied-up one */
		goto out_whtmp;

	/* make dir opaque */
	if (au_ftest_ren(a->flags, DIROPQ)) {
		err = au_ren_diropq(a);
		if (unlikely(err))
			goto out_rename;
	}

	/* update target timestamps */
	AuDebugOn(au_dbstart(a->src_dentry) != a->btgt);
	a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt);
	vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
	a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime;

	/* remove whiteout for dentry */
	if (a->dst_wh_dentry) {
		a->h_path.dentry = a->dst_wh_dentry;
		err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path,
					  a->dst_dentry);
		if (unlikely(err))
			goto out_diropq;
	}

	/* remove whtmp */
	if (a->thargs)
		au_ren_del_whtmp(a); /* ignore this error */

	err = 0;
	goto out_success;

out_diropq:
	if (au_ftest_ren(a->flags, DIROPQ))
		au_ren_rev_diropq(err, a);
out_rename:
	if (!au_ftest_ren(a->flags, CPUP))
		au_ren_rev_rename(err, a);
	else
		au_ren_rev_cpup(err, a);
	dput(a->h_dst);
out_whtmp:
	if (a->thargs)
		au_ren_rev_whtmp(err, a);
out_whdst:
	dput(a->dst_wh_dentry);
	a->dst_wh_dentry = NULL;
out_whsrc:
	if (a->src_wh_dentry)
		au_ren_rev_whsrc(err, a);
out_success:
	dput(a->src_wh_dentry);
	dput(a->dst_wh_dentry);
out_thargs:
	if (a->thargs) {
		dput(a->h_dst);
		au_whtmp_rmdir_free(a->thargs);
		a->thargs = NULL;
	}
out:
	return err;
}