static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
{
	int rerr;

	a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent,
				       a->br, /*nd*/NULL);
	rerr = PTR_ERR(a->h_path.dentry);
	if (IS_ERR(a->h_path.dentry)) {
		RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry));
		return;
	}
	if (a->h_path.dentry->d_inode) {
		d_drop(a->h_path.dentry);
		dput(a->h_path.dentry);
		return;
	}

	rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path);
	d_drop(a->h_path.dentry);
	dput(a->h_path.dentry);
	if (!rerr)
		au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst));
	else
		RevertFailure("rename %.*s", AuDLNPair(a->h_dst));
}
static void au_ren_rev_rename(int err, struct au_ren_args *a)
{
	int rerr;

	a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent,
				       a->br, /*nd*/NULL);
	rerr = PTR_ERR(a->h_path.dentry);
	if (IS_ERR(a->h_path.dentry)) {
		RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry));
		return;
	}

	rerr = vfsub_rename(a->dst_h_dir,
			    au_h_dptr(a->src_dentry, a->btgt),
			    a->src_h_dir, &a->h_path);
	d_drop(a->h_path.dentry);
	dput(a->h_path.dentry);
	/* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */
	if (rerr)
		RevertFailure("rename %.*s", AuDLNPair(a->src_dentry));
}
static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,
			      struct au_branch *br)
{
	int err;
	struct au_iattr ia;
	struct inode *h_inode;
	struct dentry *h_d;
	struct super_block *h_sb;

	err = 0;
	memset(&ia, -1, sizeof(ia));
	h_sb = h_dentry->d_sb;
	h_inode = h_dentry->d_inode;
	if (h_inode)
		au_iattr_save(&ia, h_inode);
	else if (au_test_nfs(h_sb) || au_test_fuse(h_sb))
		/* nfs d_revalidate may return 0 for negative dentry */
		/* fuse d_revalidate always return 0 for negative dentry */
		goto out;

	/* main purpose is namei.c:cached_lookup() and d_revalidate */
	h_d = au_lkup_one(&h_dentry->d_name, h_parent, br, /*nd*/NULL);
	err = PTR_ERR(h_d);
	if (IS_ERR(h_d))
		goto out;

	err = 0;
	if (unlikely(h_d != h_dentry
		     || h_d->d_inode != h_inode
		     || (h_inode && au_iattr_test(&ia, h_inode))))
		err = au_busy_or_stale();
	dput(h_d);

out:
	AuTraceErr(err);
	return err;
}
/*
 * returns positive/negative dentry, NULL or an error.
 * NULL means whiteout-ed or not-found.
 */
static struct dentry*
au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
	     aufs_bindex_t bindex, struct qstr *wh_name,
	     struct au_do_lookup_args *args)
{
	struct dentry *h_dentry;
	struct inode *h_inode, *inode;
	struct au_branch *br;
	int wh_found, opq;
	unsigned char wh_able;
	const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);

	wh_found = 0;
	br = au_sbr(dentry->d_sb, bindex);
	wh_able = !!au_br_whable(br->br_perm);
	if (wh_able)
		wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0);
	h_dentry = ERR_PTR(wh_found);
	if (!wh_found)
		goto real_lookup;
	if (unlikely(wh_found < 0))
		goto out;

	/* We found a whiteout */
	/* au_set_dbend(dentry, bindex); */
	au_set_dbwh(dentry, bindex);
	if (!allow_neg)
		return NULL; /* success */

real_lookup:
	h_dentry = au_lkup_one(&dentry->d_name, h_parent, br, args->nd);
	if (IS_ERR(h_dentry))
		goto out;

	h_inode = h_dentry->d_inode;
	if (!h_inode) {
		if (!allow_neg)
			goto out_neg;
	} else if (wh_found
		   || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
		goto out_neg;

	if (au_dbend(dentry) <= bindex)
		au_set_dbend(dentry, bindex);
	if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
		au_set_dbstart(dentry, bindex);
	au_set_h_dptr(dentry, bindex, h_dentry);

	inode = dentry->d_inode;
	if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able
	    || (inode && !S_ISDIR(inode->i_mode)))
		goto out; /* success */

	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
	opq = au_diropq_test(h_dentry, br);
	mutex_unlock(&h_inode->i_mutex);
	if (opq > 0)
		au_set_dbdiropq(dentry, bindex);
	else if (unlikely(opq < 0)) {
		au_set_h_dptr(dentry, bindex, NULL);
		h_dentry = ERR_PTR(opq);
	}
	goto out;

out_neg:
	dput(h_dentry);
	h_dentry = NULL;
out:
	return h_dentry;
}
static void au_call_lkup_one(void *args)
{
	struct au_lkup_one_args *a = args;
	*a->errp = au_lkup_one(a->name, a->h_parent, a->br, a->nd);
}
/*
 * returns the number of lower positive dentries,
 * otherwise an error.
 * can be called at unlinking with @type is zero.
 */
int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
		   struct nameidata *nd)
{
	int npositive, err;
	aufs_bindex_t bindex, btail, bdiropq;
	unsigned char isdir;
	struct qstr whname;
	struct au_do_lookup_args args = {
		.flags	= 0,
		.type	= type,
		.nd	= nd
	};
	const struct qstr *name = &dentry->d_name;
	struct dentry *parent;
	struct inode *inode;

	err = au_test_shwh(dentry->d_sb, name);
	if (unlikely(err))
		goto out;

	err = au_wh_name_alloc(&whname, name);
	if (unlikely(err))
		goto out;

	inode = dentry->d_inode;
	isdir = !!(inode && S_ISDIR(inode->i_mode));
	if (!type)
		au_fset_lkup(args.flags, ALLOW_NEG);

	npositive = 0;
	parent = dget_parent(dentry);
	btail = au_dbtaildir(parent);
	for (bindex = bstart; bindex <= btail; bindex++) {
		struct dentry *h_parent, *h_dentry;
		struct inode *h_inode, *h_dir;

		h_dentry = au_h_dptr(dentry, bindex);
		if (h_dentry) {
			if (h_dentry->d_inode)
				npositive++;
			if (type != S_IFDIR)
				break;
			continue;
		}
		h_parent = au_h_dptr(parent, bindex);
		if (!h_parent)
			continue;
		h_dir = h_parent->d_inode;
		if (!h_dir || !S_ISDIR(h_dir->i_mode))
			continue;

		mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
		h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
					&args);
		mutex_unlock(&h_dir->i_mutex);
		err = PTR_ERR(h_dentry);
		if (IS_ERR(h_dentry))
			goto out_parent;
		au_fclr_lkup(args.flags, ALLOW_NEG);

		if (au_dbwh(dentry) >= 0)
			break;
		if (!h_dentry)
			continue;
		h_inode = h_dentry->d_inode;
		if (!h_inode)
			continue;
		npositive++;
		if (!args.type)
			args.type = h_inode->i_mode & S_IFMT;
		if (args.type != S_IFDIR)
			break;
		else if (isdir) {
			/* the type of lower may be different */
			bdiropq = au_dbdiropq(dentry);
			if (bdiropq >= 0 && bdiropq <= bindex)
				break;
		}
	}

	if (npositive) {
		AuLabel(positive);
		au_update_dbstart(dentry);
	}
	err = npositive;
	if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
		     && au_dbstart(dentry) < 0)) {
		err = -EIO;
		AuIOErr("both of real entry and whiteout found, %.*s, err %d\n",
			AuDLNPair(dentry), err);
	}

out_parent:
	dput(parent);
	kfree(whname.name);
out:
	return err;
}

struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
			       struct au_branch *br)
{
	struct dentry *dentry;
	int wkq_err;

	if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC))
		dentry = au_lkup_one(name, parent, br, /*nd*/NULL);
	else {
		struct au_lkup_one_args args = {
			.errp		= &dentry,
			.name		= name,
			.h_parent	= parent,
			.br		= br,
			.nd		= NULL
		};

		wkq_err = au_wkq_wait(au_call_lkup_one, &args);
		if (unlikely(wkq_err))
			dentry = ERR_PTR(wkq_err);
	}

	return dentry;
}

/*
 * lookup @dentry on @bindex which should be negative.
 */
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
{
	int err;
	struct dentry *parent, *h_parent, *h_dentry;

	parent = dget_parent(dentry);
	h_parent = au_h_dptr(parent, bindex);
	h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent,
				   au_sbr(dentry->d_sb, bindex));
	err = PTR_ERR(h_dentry);
	if (IS_ERR(h_dentry))
		goto out;
	if (unlikely(h_dentry->d_inode)) {
		err = -EIO;
		AuIOErr("%.*s should be negative on b%d.\n",
			AuDLNPair(h_dentry), bindex);
		dput(h_dentry);
		goto out;
	}

	err = 0;
	if (bindex < au_dbstart(dentry))
		au_set_dbstart(dentry, bindex);
	if (au_dbend(dentry) < bindex)
		au_set_dbend(dentry, bindex);
	au_set_h_dptr(dentry, bindex, h_dentry);

out:
	dput(parent);
	return err;
}
Example #7
0
/* create a pseudo-link */
static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
		      struct dentry *h_dentry, struct au_branch *br)
{
	int err;
	struct path h_path = {
		.mnt = br->br_mnt
	};
	struct inode *h_dir;

	h_dir = h_parent->d_inode;
 again:
	h_path.dentry = au_lkup_one(tgt, h_parent, br, /*nd*/NULL);
	err = PTR_ERR(h_path.dentry);
	if (IS_ERR(h_path.dentry))
		goto out;

	err = 0;
	/* wh.plink dir is not monitored */
	if (h_path.dentry->d_inode
	    && h_path.dentry->d_inode != h_dentry->d_inode) {
		err = vfsub_unlink(h_dir, &h_path, /*force*/0);
		dput(h_path.dentry);
		h_path.dentry = NULL;
		if (!err)
			goto again;
	}
	if (!err && !h_path.dentry->d_inode)
		err = vfsub_link(h_dentry, h_dir, &h_path);
	dput(h_path.dentry);

 out:
	return err;
}

struct do_whplink_args {
	int *errp;
	struct qstr *tgt;
	struct dentry *h_parent;
	struct dentry *h_dentry;
	struct au_branch *br;
};

static void call_do_whplink(void *args)
{
	struct do_whplink_args *a = args;
	*a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br);
}

static int whplink(struct dentry *h_dentry, struct inode *inode,
		   aufs_bindex_t bindex, struct au_branch *br)
{
	int err, wkq_err;
	struct au_wbr *wbr;
	struct dentry *h_parent;
	struct inode *h_dir;
	char a[PLINK_NAME_LEN];
	struct qstr tgtname = {
		.name = a
	};

	wbr = au_sbr(inode->i_sb, bindex)->br_wbr;
	h_parent = wbr->wbr_plink;
	h_dir = h_parent->d_inode;
	tgtname.len = plink_name(a, sizeof(a), inode, bindex);

	/* always superio. */
	mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);
	if (!au_test_wkq(current)) {
		struct do_whplink_args args = {
			.errp		= &err,
			.tgt		= &tgtname,
			.h_parent	= h_parent,
			.h_dentry	= h_dentry,
			.br		= br
		};
		wkq_err = au_wkq_wait(call_do_whplink, &args);
		if (unlikely(wkq_err))
			err = wkq_err;
	} else