Exemple #1
0
/* make sure the file is idle */
static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a)
{
    int err, plinked;

    err = 0;
    plinked = !!au_opt_test(au_mntflags(a->sb), PLINK);
    if (au_dbstart(a->dentry) == a->mvd_bsrc
            && au_dcount(a->dentry) == 1
            && atomic_read(&a->inode->i_count) == 1
            /* && a->mvd_h_src_inode->i_nlink == 1 */
            && (!plinked || !au_plink_test(a->inode))
            && a->inode->i_nlink == 1)
        goto out;

    err = -EBUSY;
    AU_MVD_PR(dmsg,
              "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n",
              a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry),
              atomic_read(&a->inode->i_count), a->inode->i_nlink,
              a->mvd_h_src_inode->i_nlink,
              plinked, plinked ? au_plink_test(a->inode) : 0);

out:
    AuTraceErr(err);
    return err;
}
Exemple #2
0
void au_cpup_attr_nlink(struct inode *inode, int force)
{
	struct inode *h_inode;
	struct super_block *sb;
	aufs_bindex_t bindex, bend;

	sb = inode->i_sb;
	bindex = au_ibstart(inode);
	h_inode = au_h_iptr(inode, bindex);
	if (!force
	    && !S_ISDIR(h_inode->i_mode)
	    && au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(inode))
		return;

	/*
	 * 0 can happen in revalidating.
	 * h_inode->i_mutex is not held, but it is harmless since once i_nlink
	 * reaches 0, it will never become positive.
	 */
	set_nlink(inode, h_inode->i_nlink);

	/*
	 * fewer nlink makes find(1) noisy, but larger nlink doesn't.
	 * it may includes whplink directory.
	 */
	if (S_ISDIR(h_inode->i_mode)) {
		bend = au_ibend(inode);
		for (bindex++; bindex <= bend; bindex++) {
			h_inode = au_h_iptr(inode, bindex);
			if (h_inode)
				au_add_nlink(inode, h_inode);
		}
	}
}
Exemple #3
0
/*
 * simple tests for the del-entry operations.
 * following the checks in vfs, plus the parent-child relationship.
 */
int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
	       struct dentry *h_parent, int isdir)
{
	int err;
	umode_t h_mode;
	struct dentry *h_dentry, *h_latest;
	struct inode *h_inode;

	h_dentry = au_h_dptr(dentry, bindex);
	if (d_really_is_positive(dentry)) {
		err = -ENOENT;
		if (unlikely(d_is_negative(h_dentry)))
			goto out;
		h_inode = d_inode(h_dentry);
		if (unlikely(!h_inode->i_nlink))
			goto out;

		h_mode = h_inode->i_mode;
		if (!isdir) {
			err = -EISDIR;
			if (unlikely(S_ISDIR(h_mode)))
				goto out;
		} else if (unlikely(!S_ISDIR(h_mode))) {
			err = -ENOTDIR;
			goto out;
		}
	} else {
		/* rename(2) case */
		err = -EIO;
		if (unlikely(d_is_positive(h_dentry)))
			goto out;
	}

	err = -ENOENT;
	/* expected parent dir is locked */
	if (unlikely(h_parent != h_dentry->d_parent))
		goto out;
	err = 0;

	/*
	 * rmdir a dir may break the consistency on some filesystem.
	 * let's try heavy test.
	 */
	err = -EACCES;
	if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
		     && au_test_h_perm(d_inode(h_parent),
				       MAY_EXEC | MAY_WRITE)))
		goto out;

	h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
	err = -EIO;
	if (IS_ERR(h_latest))
		goto out;
	if (h_latest == h_dentry)
		err = 0;
	dput(h_latest);

out:
	return err;
}
Exemple #4
0
static int au_ready_to_write_wh(struct file *file, loff_t len,
				aufs_bindex_t bcpup)
{
	int err;
	struct inode *inode, *h_inode;
	struct dentry *dentry, *h_dentry, *hi_wh;

	dentry = file->f_dentry;
	au_update_dbstart(dentry);
	inode = dentry->d_inode;
	h_inode = NULL;
	if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) {
		h_dentry = au_h_dptr(dentry, bcpup);
		if (h_dentry)
			h_inode = h_dentry->d_inode;
	}
	hi_wh = au_hi_wh(inode, bcpup);
	if (!hi_wh && !h_inode)
		err = au_sio_cpup_wh(dentry, bcpup, len, file);
	else
		/* already copied-up after unlink */
		err = au_reopen_wh(file, bcpup, hi_wh);

	if (!err
	    && inode->i_nlink > 1
	    && au_opt_test(au_mntflags(dentry->d_sb), PLINK))
		au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup));

	return err;
}
Exemple #5
0
/* dentry and super_block lock. call at entry point */
int aufs_read_lock(struct dentry *dentry, int flags)
{
	int err;
	struct super_block *sb;

	sb = dentry->d_sb;
	err = si_read_lock(sb, flags);
	if (unlikely(err))
		goto out;

	if (au_ftest_lock(flags, DW))
		di_write_lock_child(dentry);
	else
		di_read_lock_child(dentry, flags);

	if (au_ftest_lock(flags, GEN)) {
		err = au_digen_test(dentry, au_sigen(sb));
		if (!au_opt_test(au_mntflags(sb), UDBA_NONE))
			AuDebugOn(!err && au_dbrange_test(dentry));
		else if (!err)
			err = au_dbrange_test(dentry);
		if (unlikely(err))
			aufs_read_unlock(dentry, flags);
	}

out:
	return err;
}
static int au_test_shwh(struct super_block *sb, const struct qstr *name)
{
	if (unlikely(!au_opt_test(au_mntflags(sb), SHWH)
		     && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
		return -EPERM;
	return 0;
}
Exemple #7
0
void au_cpup_attr_nlink(struct inode *inode, int force)
{
	struct inode *h_inode;
	struct super_block *sb;
	aufs_bindex_t bindex, bend;

	LKTRTrace("i%lu\n", inode->i_ino);
	/* todo? IMustLock(inode); */
	AuDebugOn(!inode->i_mode);

	sb = inode->i_sb;
	bindex = au_ibstart(inode);
	h_inode = au_h_iptr(inode, bindex);

	if (!force
	    && !S_ISDIR(h_inode->i_mode)
	    && au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(sb, inode))
		return;

	inode->i_nlink = h_inode->i_nlink;

	/*
	 * fewer nlink makes find(1) noisy, but larger nlink doesn't.
	 * it may includes whplink directory.
	 */
	if (S_ISDIR(h_inode->i_mode)) {
		bend = au_ibend(inode);
		for (bindex++; bindex <= bend; bindex++) {
			h_inode = au_h_iptr(inode, bindex);
			if (h_inode)
				au_add_nlink(inode, h_inode);
		}
	}
}
static void au_ren_refresh(struct au_ren_args *a)
{
	aufs_bindex_t bend, bindex;
	struct dentry *d, *h_d;
	struct inode *i, *h_i;
	struct super_block *sb;

	d = a->dst_dentry;
	d_drop(d);
	if (a->h_dst)
		/* already dget-ed by au_ren_or_cpup() */
		au_set_h_dptr(d, a->btgt, a->h_dst);

	i = a->dst_inode;
	if (i) {
		if (!au_ftest_ren(a->flags, ISDIR))
			vfsub_drop_nlink(i);
		else {
			vfsub_dead_dir(i);
			au_cpup_attr_timesizes(i);
		}
		au_update_dbrange(d, /*do_put_zero*/1);
	} else {
		bend = a->btgt;
		for (bindex = au_dbstart(d); bindex < bend; bindex++)
			au_set_h_dptr(d, bindex, NULL);
		bend = au_dbend(d);
		for (bindex = a->btgt + 1; bindex <= bend; bindex++)
			au_set_h_dptr(d, bindex, NULL);
		au_update_dbrange(d, /*do_put_zero*/0);
	}

	d = a->src_dentry;
	au_set_dbwh(d, -1);
	bend = au_dbend(d);
	for (bindex = a->btgt + 1; bindex <= bend; bindex++) {
		h_d = au_h_dptr(d, bindex);
		if (h_d)
			au_set_h_dptr(d, bindex, NULL);
	}
	au_set_dbend(d, a->btgt);

	sb = d->d_sb;
	i = a->src_inode;
	if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
		return; /* success */

	bend = au_ibend(i);
	for (bindex = a->btgt + 1; bindex <= bend; bindex++) {
		h_i = au_h_iptr(i, bindex);
		if (h_i) {
			au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0);
			/* ignore this error */
			au_set_h_iptr(i, bindex, NULL, 0);
		}
	}
	au_set_ibend(i, a->btgt);
}
Exemple #9
0
/*
 * if valid returns 1, otherwise 0.
 */
static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	int valid, err;
	unsigned int sigen;
	unsigned char do_udba;
	struct super_block *sb;
	struct inode *inode;

	err = -EINVAL;
	sb = dentry->d_sb;
	inode = dentry->d_inode;
	aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW);
	sigen = au_sigen(sb);
	if (au_digen(dentry) != sigen) {
		AuDebugOn(IS_ROOT(dentry));
		if (inode)
			err = au_reval_dpath(dentry, sigen);
		if (unlikely(err))
			goto out_dgrade;
		AuDebugOn(au_digen(dentry) != sigen);
	}
	if (inode && au_iigen(inode) != sigen) {
		AuDebugOn(IS_ROOT(dentry));
		err = au_refresh_hinode(inode, dentry);
		if (unlikely(err))
			goto out_dgrade;
		AuDebugOn(au_iigen(inode) != sigen);
	}
	di_downgrade_lock(dentry, AuLock_IR);

	AuDebugOn(au_digen(dentry) != sigen);
	AuDebugOn(inode && au_iigen(inode) != sigen);
	err = -EINVAL;
	do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);
	if (do_udba && inode) {
		aufs_bindex_t bstart = au_ibstart(inode);

		if (bstart >= 0
		    && au_test_higen(inode, au_h_iptr(inode, bstart)))
			goto out;
	}

	err = h_d_revalidate(dentry, inode, nd, do_udba);
	if (unlikely(!err && do_udba && au_dbstart(dentry) < 0))
		/* both of real entry and whiteout found */
		err = -EIO;
	goto out;

 out_dgrade:
	di_downgrade_lock(dentry, AuLock_IR);
 out:
	aufs_read_unlock(dentry, AuLock_IR);
	AuTraceErr(err);
	valid = !err;
	if (!valid)
		AuDbg("%.*s invalid\n", AuDLNPair(dentry));
	return valid;
}
Exemple #10
0
int au_opts_remount(struct super_block *sb, struct au_opts *opts)
{
	int err, rerr;
	unsigned char no_dreval;
	struct inode *dir;
	struct au_opt_xino *opt_xino;
	struct au_opt *opt;
	struct au_sbinfo *sbinfo;

	SiMustWriteLock(sb);

	err = 0;
	dir = d_inode(sb->s_root);
	sbinfo = au_sbi(sb);
	opt_xino = NULL;
	opt = opts->opt;
	while (err >= 0 && opt->type != Opt_tail) {
		err = au_opt_simple(sb, opt, opts);
		if (!err)
			err = au_opt_br(sb, opt, opts);
		if (!err)
			err = au_opt_xino(sb, opt, &opt_xino, opts);
		opt++;
	}
	if (err > 0)
		err = 0;
	AuTraceErr(err);
	/* go on even err */

	no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL);
	rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0);
	if (unlikely(rerr && !err))
		err = rerr;

	if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL))
		au_fset_opts(opts->flags, REFRESH_IDOP);

	if (au_ftest_opts(opts->flags, TRUNC_XIB)) {
		rerr = au_xib_trunc(sb);
		if (unlikely(rerr && !err))
			err = rerr;
	}

	/* will be handled by the caller */
	if (!au_ftest_opts(opts->flags, REFRESH)
	    && (opts->given_udba
		|| au_opt_test(sbinfo->si_mntflags, XINO)
		|| au_ftest_opts(opts->flags, REFRESH_IDOP)
		    ))
		au_fset_opts(opts->flags, REFRESH);

	AuDbg("status 0x%x\n", opts->flags);
	return err;
}
Exemple #11
0
int main(int argc, char *argv[])
{
	int err;
	char *mntpnt;

	opt(argc, argv);
	if (optind == argc) {
		//usage();
		errno = EINVAL;
		AuFin(NULL);
	}

	mntpnt = realpath(argv[optind], NULL);
	if (!mntpnt)
		AuFin("%s", mntpnt);
	err = chdir(mntpnt);
	if (err)
		AuFin("%s", mntpnt);

	if (!au_opt_test(fhsmd.optflags, NODAEMON)) {
		au_do_syslog = 1;
		openlog(program_invocation_short_name, AuFhsmd_OPTION,
			AuFhsmd_FACILITY);
		err = daemon(/*nochdir*/0, /*noclose*/0);
		if (err)
			AuFin("daemon");
	}

	shm_names(mntpnt, fhsmd.name, sizeof(*fhsmd.name));
	free(mntpnt);
	comm_fd(fhsmd.name[AuName_LCOPY].a, &fhsmd.fd[AuFd_FHSM],
		&fhsmd.fd[AuFd_MSG]);
	INIT_LIST_HEAD(&fhsmd.in_ope);
	err = au_fhsmd_load();
	if (err)
		AuLogFin("au_fhsmd_load");

	err = au_epsigfd();
	if (err)
		AuLogFin("create_epsig");
	err = au_ep_add(fhsmd.fd[AuFd_MSG], EPOLLIN | EPOLLPRI);
	if (err)
		AuLogFin("au_ep_add");
	err = au_ep_add(fhsmd.fd[AuFd_FHSM], EPOLLIN | EPOLLPRI);
	if (err)
		AuLogFin("au_ep_add");

	/* main loop */
	err = au_fhsmd_loop();

	AuDbgFhsmLog("exit %d", err);
	return err;
}
Exemple #12
0
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)
{
	int err;

	SiMustAnyLock(sb);

	err = 0;
	if (au_opt_test(au_mntflags(sb), XINO)) {
		err = au_xino_path(seq, au_sbi(sb)->si_xib);
		seq_putc(seq, '\n');
	}
	return err;
}
Exemple #13
0
int au_xigen_new(struct inode *inode)
{
	int err;
	loff_t pos;
	ssize_t sz;
	struct super_block *sb;
	struct au_sbinfo *sbinfo;
	struct file *file;

	err = 0;
	/* todo: dirty, at mount time */
	if (inode->i_ino == AUFS_ROOT_INO)
		goto out;
	sb = inode->i_sb;
	SiMustAnyLock(sb);
	if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
		goto out;

	err = -EFBIG;
	pos = inode->i_ino;
	if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
		AuIOErr1("too large i%lld\n", pos);
		goto out;
	}
	pos *= sizeof(inode->i_generation);

	err = 0;
	sbinfo = au_sbi(sb);
	file = sbinfo->si_xigen;
	BUG_ON(!file);

	if (i_size_read(file->f_dentry->d_inode)
	    < pos + sizeof(inode->i_generation)) {
		inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
		sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
				 sizeof(inode->i_generation), &pos);
	} else
		sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
				sizeof(inode->i_generation), &pos);
	if (sz == sizeof(inode->i_generation))
		goto out; /* success */

	err = sz;
	if (unlikely(sz >= 0)) {
		err = -EIO;
		AuIOErr("xigen error (%zd)\n", sz);
	}

 out:
	return err;
}
Exemple #14
0
void au_plink_list(struct super_block *sb)
{
	struct au_sbinfo *sbinfo;
	struct list_head *plink_list;
	struct pseudo_link *plink;

	SiMustAnyLock(sb);

	sbinfo = au_sbi(sb);
	AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));

	plink_list = &sbinfo->si_plink.head;
	rcu_read_lock();
	list_for_each_entry_rcu(plink, plink_list, list)
		AuDbg("%lu\n", plink->inode->i_ino);
	rcu_read_unlock();
}
Exemple #15
0
static struct dentry *
au_h_dget_any(struct dentry *dentry, aufs_bindex_t *bindex)
{
	struct dentry *h_dentry;
	struct inode *inode, *h_inode;
	struct super_block *sb;
	aufs_bindex_t ib, db;

	/* must be positive dentry */
	inode = dentry->d_inode;
	LKTRTrace("%.*s, i%lu\n", AuDLNPair(dentry), inode->i_ino);

	sb = dentry->d_sb;
	db = au_dbstart(dentry);
	ib = au_ibstart(inode);
	if (db == ib) {
		*bindex = db;
		h_dentry = dget(au_h_dptr(dentry, db));
		if (h_dentry)
			goto out; /* success */
	}

	*bindex = ib;
	h_inode = au_h_iptr(inode, ib);
	h_dentry = d_find_alias(h_inode);
	if (h_dentry)
		goto out; /* success */

#if 0
	if (au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(sb, inode)) {
		h_dentry = au_plink_lkup(sb, ib, inode);
		if (IS_ERR(h_dentry))
			goto out;
		AuDebugOn(!h_dentry->d_inode);
		goto out; /* success */
	}
#endif

	h_dentry = dget(au_hi_wh(inode, ib));

 out:
	AuTraceErrPtr(h_dentry);
	return h_dentry;
}
/*
 * extended version of au_h_dptr().
 * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or
 * error.
 */
struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex)
{
	struct dentry *h_dentry;
	struct inode *inode, *h_inode;

	inode = dentry->d_inode;
	AuDebugOn(!inode);

	h_dentry = NULL;
	if (au_dbstart(dentry) <= bindex
	    && bindex <= au_dbend(dentry))
		h_dentry = au_h_dptr(dentry, bindex);
	if (h_dentry && !au_d_linkable(h_dentry)) {
		dget(h_dentry);
		goto out; /* success */
	}

	AuDebugOn(bindex < au_ibstart(inode));
	AuDebugOn(au_ibend(inode) < bindex);
	h_inode = au_h_iptr(inode, bindex);
	h_dentry = d_find_alias(h_inode);
	if (h_dentry) {
		if (!IS_ERR(h_dentry)) {
			if (!au_d_linkable(h_dentry))
				goto out; /* success */
			dput(h_dentry);
		} else
			goto out;
	}

	if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) {
		h_dentry = au_plink_lkup(inode, bindex);
		AuDebugOn(!h_dentry);
		if (!IS_ERR(h_dentry)) {
			if (!au_d_hashed_positive(h_dentry))
				goto out; /* success */
			dput(h_dentry);
			h_dentry = NULL;
		}
	}

out:
	AuDbgDentry(h_dentry);
	return h_dentry;
}
/* intialize a new branch */
static int au_br_init(struct au_branch *br, struct super_block *sb,
		      struct au_opt_add *add)
{
	int err;

	err = 0;
	memset(&br->br_xino, 0, sizeof(br->br_xino));
	mutex_init(&br->br_xino.xi_nondir_mtx);
	br->br_perm = add->perm;
	br->br_mnt = add->path.mnt; /* set first, mntget() later */
	spin_lock_init(&br->br_dykey_lock);
	memset(br->br_dykey, 0, sizeof(br->br_dykey));
	atomic_set(&br->br_count, 0);
	br->br_xino_upper = AUFS_XINO_TRUNC_INIT;
	atomic_set(&br->br_xino_running, 0);
	br->br_id = au_new_br_id(sb);
	AuDebugOn(br->br_id < 0);

	if (au_br_writable(add->perm)) {
		err = au_wbr_init(br, sb, add->perm, &add->path);
		if (unlikely(err))
			goto out_err;
	}

	if (au_opt_test(au_mntflags(sb), XINO)) {
		err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino,
				 au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1);
		if (unlikely(err)) {
			AuDebugOn(br->br_xino.xi_file);
			goto out_err;
		}
	}

	sysaufs_br_init(br);
	mntget(add->path.mnt);
	goto out; /* success */

out_err:
	br->br_mnt = NULL;
out:
	return err;
}
Exemple #18
0
int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st)
{
	int err;
	aufs_bindex_t bindex;
	struct inode *inode;
	struct dentry *h_dentry;
	struct super_block *sb;
	unsigned int mnt_flags;

	LKTRTrace("%.*s\n", AuDLNPair(dentry));

	inode = dentry->d_inode;
	sb = dentry->d_sb;
	aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR);

	/* todo: refine it */
	mnt_flags = au_mntflags(sb);
	if (au_opt_test(mnt_flags, PLINK) && au_plink_test(sb, inode))
		goto plinked;

	h_dentry = au_h_dget_any(dentry, &bindex);
	err = PTR_ERR(h_dentry);
	if (IS_ERR(h_dentry))
		goto out;

	err = -ENOENT;
	if (h_dentry->d_inode)
		err = vfsub_getattr(au_sbr_mnt(sb, bindex), h_dentry, st,
				    au_test_dlgt(mnt_flags));
	dput(h_dentry);
	if (!err) {
		au_cpup_attr_all(inode);
	plinked:
		generic_fillattr(inode, st);
	}

 out:
	aufs_read_unlock(dentry, AuLock_IR);
	AuTraceErr(err);
	return err;
}
Exemple #19
0
/*
 * initialize the whiteout base file/dir for @br.
 */
int au_wh_init(struct au_branch *br, struct super_block *sb)
{
	int err, i;
	const unsigned char do_plink
		= !!au_opt_test(au_mntflags(sb), PLINK);
	struct inode *h_dir;
	struct path path = br->br_path;
	struct dentry *h_root = path.dentry;
	struct au_wbr *wbr = br->br_wbr;
	static const struct qstr base_name[] = {
		[AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME,
					  sizeof(AUFS_BASE_NAME) - 1),
		[AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME,
					   sizeof(AUFS_PLINKDIR_NAME) - 1),
		[AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME,
					  sizeof(AUFS_ORPHDIR_NAME) - 1)
	};
	struct au_wh_base base[] = {
		[AuBrWh_BASE] = {
			.name	= base_name + AuBrWh_BASE,
			.dentry	= NULL
		},
Exemple #20
0
/* is the inode pseudo-linked? */
int au_plink_test(struct inode *inode)
{
	int found;
	struct au_sbinfo *sbinfo;
	struct list_head *plink_list;
	struct pseudo_link *plink;

	sbinfo = au_sbi(inode->i_sb);
	AuRwMustAnyLock(&sbinfo->si_rwsem);
	AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK));

	found = 0;
	plink_list = &sbinfo->si_plink.head;
	rcu_read_lock();
	list_for_each_entry_rcu(plink, plink_list, list)
		if (plink->inode == inode) {
			found = 1;
			break;
		}
	rcu_read_unlock();
	return found;
}
Exemple #21
0
void au_cpup_attr_nlink(struct inode *inode, int force)
{
	struct inode *h_inode;
	struct super_block *sb;
	aufs_bindex_t bindex, bend;

	sb = inode->i_sb;
	bindex = au_ibstart(inode);
	h_inode = au_h_iptr(inode, bindex);
	if (!force
	    && !S_ISDIR(h_inode->i_mode)
	    && au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(inode))
		return;

	/*
	 * 0 can happen in revalidating.
	 * h_inode->i_mutex may not be held here, but it is harmless since once
	 * i_nlink reaches 0, it will never become positive except O_TMPFILE
	 * case.
	 * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause
	 *	 the incorrect link count.
	 */
	set_nlink(inode, h_inode->i_nlink);

	/*
	 * fewer nlink makes find(1) noisy, but larger nlink doesn't.
	 * it may includes whplink directory.
	 */
	if (S_ISDIR(h_inode->i_mode)) {
		bend = au_ibend(inode);
		for (bindex++; bindex <= bend; bindex++) {
			h_inode = au_h_iptr(inode, bindex);
			if (h_inode)
				au_add_nlink(inode, h_inode);
		}
	}
}
Exemple #22
0
void au_xigen_inc(struct inode *inode)
{
	loff_t pos;
	ssize_t sz;
	__u32 igen;
	struct super_block *sb;
	struct au_sbinfo *sbinfo;

	sb = inode->i_sb;
	AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));

	sbinfo = au_sbi(sb);
	pos = inode->i_ino;
	pos *= sizeof(igen);
	igen = inode->i_generation + 1;
	sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
			 sizeof(igen), &pos);
	if (sz == sizeof(igen))
		return; /* success */

	if (unlikely(sz >= 0))
		AuIOErr("xigen error (%zd)\n", sz);
}
Exemple #23
0
/*
 * simple tests for the adding inode operations.
 * following the checks in vfs, plus the parent-child relationship.
 */
int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
	       struct dentry *h_parent, int isdir, struct au_ndx *ndx)
{
	int err, exist;
	struct dentry *h_dentry;
	struct inode *h_inode;
	umode_t h_mode;

	LKTRTrace("%.*s/%.*s, b%d, dir %d\n",
		  AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir);

	exist = !!dentry->d_inode;
	h_dentry = au_h_dptr(dentry, bindex);
	h_inode = h_dentry->d_inode;
	if (!exist) {
		err = -EEXIST;
		if (unlikely(h_inode))
			goto out;
	} else {
		/* rename(2) case */
		err = -EIO;
		if (unlikely(!h_inode || !h_inode->i_nlink))
			goto out;

		h_mode = h_inode->i_mode;
		if (!isdir) {
			err = -EISDIR;
			if (unlikely(S_ISDIR(h_mode)))
				goto out;
		} else if (unlikely(!S_ISDIR(h_mode))) {
			err = -ENOTDIR;
			goto out;
		}
	}

	err = -EIO;
	/* expected parent dir is locked */
	if (unlikely(h_parent != h_dentry->d_parent))
		goto out;
	err = 0;

	if (au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY)) {
		struct dentry *h_latest;
		struct qstr *qstr = &dentry->d_name;

		err = -EACCES;
		if (unlikely(au_test_h_perm
			     (h_parent->d_inode, MAY_EXEC | MAY_WRITE,
			      au_ftest_ndx(ndx->flags, DLGT))))
			goto out;

		h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len,
					   ndx);
		err = PTR_ERR(h_latest);
		if (IS_ERR(h_latest))
			goto out;
		err = -EIO;
		dput(h_latest);
		/* fuse d_revalidate always return 0 for negative dentries */
		if (h_latest == h_dentry || au_test_fuse(h_dentry->d_sb))
			err = 0;
	}

 out:
	AuTraceErr(err);
	return err;
}
Exemple #24
0
/*
 * initial procedure of adding a new entry.
 * prepare writable branch and the parent dir, lock it,
 * lookup whiteout for the new entry.
 */
static struct dentry*
lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
		  struct dentry *src_dentry, struct au_pin *pin,
		  struct au_wr_dir_args *wr_dir_args)
{
	struct dentry *wh_dentry, *h_parent;
	struct super_block *sb;
	int err;
	unsigned int mnt_flags;
	unsigned char pin_flags;
	aufs_bindex_t bstart, bcpup;
	struct au_ndx ndx;

	LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry);

	bstart = au_dbstart(dentry);
	err = au_wr_dir(dentry, src_dentry, wr_dir_args);
	bcpup = err;
	wh_dentry = ERR_PTR(err);
	if (unlikely(err < 0))
		goto out;

	sb = dentry->d_sb;
	mnt_flags = au_mntflags(sb);
	pin_flags = AuPin_DI_LOCKED | AuPin_MNT_WRITE;
	if (dt && au_opt_test(mnt_flags, UDBA_INOTIFY))
		au_fset_pin(pin_flags, DO_GPARENT);
	err = au_pin(pin, dentry, bcpup, pin_flags);
	wh_dentry = ERR_PTR(err);
	if (unlikely(err))
		goto out;

	ndx.nfsmnt = au_nfsmnt(sb, bcpup);
	ndx.flags = 0;
	if (au_test_dlgt(mnt_flags))
		au_fset_ndx(ndx.flags, DLGT);
	ndx.nd = NULL;
	/* ndx.br = NULL; */
	/* ndx.nd_file = NULL; */

	h_parent = au_pinned_h_parent(pin);
	if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) {
		struct nameidata nd;

		if (ndx.nfsmnt) {
			/* todo: dirty? */
			ndx.nd = &nd;
			ndx.br = au_sbr(sb, bcpup);
			memset(&nd, 0, sizeof(nd));
			nd.flags = LOOKUP_CREATE;
			nd.intent.open.flags = O_EXCL;
		}
		err = au_may_add(dentry, bcpup, h_parent,
				 au_ftest_wrdir(wr_dir_args->flags, ISDIR),
				 &ndx);
		wh_dentry = ERR_PTR(err);
		if (unlikely(err))
			goto out_unpin;
		ndx.nd = NULL;
		ndx.br = NULL;
	}

	if (dt)
		au_dtime_store(dt, au_pinned_parent(pin), h_parent,
			       au_pinned_hdir(pin), au_pinned_hgdir(pin));

	wh_dentry = NULL;
	if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry))
		goto out; /* success */

	wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx);

 out_unpin:
	if (IS_ERR(wh_dentry))
		au_unpin(pin);
 out:
	AuTraceErrPtr(wh_dentry);
	return wh_dentry;
}
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;
}
Exemple #26
0
int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
		   unsigned int pending)
{
	int err;
	aufs_bindex_t bindex, bend;
	unsigned char do_plink, skip, do_free;
	struct au_branch *br;
	struct au_wbr *wbr;
	struct dentry *root;
	struct inode *dir, *h_dir;
	struct au_sbinfo *sbinfo;
	struct au_hinode *hdir;

	SiMustAnyLock(sb);

	sbinfo = au_sbi(sb);
	AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA));

	if (!(sb_flags & MS_RDONLY)) {
		if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))
			pr_warn("first branch should be rw\n");
		if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH)))
			pr_warn("shwh should be used with ro\n");
	}

	if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY)
	    && !au_opt_test(sbinfo->si_mntflags, XINO))
		pr_warn("udba=*notify requires xino\n");

	err = 0;
	root = sb->s_root;
	dir = root->d_inode;
	do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK);
	bend = au_sbend(sb);
	for (bindex = 0; !err && bindex <= bend; bindex++) {
		skip = 0;
		h_dir = au_h_iptr(dir, bindex);
		br = au_sbr(sb, bindex);
		do_free = 0;

		wbr = br->br_wbr;
		if (wbr)
			wbr_wh_read_lock(wbr);

		if (!au_br_writable(br->br_perm)) {
			do_free = !!wbr;
			skip = (!wbr
				|| (!wbr->wbr_whbase
				    && !wbr->wbr_plink
				    && !wbr->wbr_orph));
		} else if (!au_br_wh_linkable(br->br_perm)) {
			/* skip = (!br->br_whbase && !br->br_orph); */
			skip = (!wbr || !wbr->wbr_whbase);
			if (skip && wbr) {
				if (do_plink)
					skip = !!wbr->wbr_plink;
				else
					skip = !wbr->wbr_plink;
			}
		} else {
			/* skip = (br->br_whbase && br->br_ohph); */
			skip = (wbr && wbr->wbr_whbase);
			if (skip) {
				if (do_plink)
					skip = !!wbr->wbr_plink;
				else
					skip = !wbr->wbr_plink;
			}
		}
		if (wbr)
			wbr_wh_read_unlock(wbr);

		if (skip)
			continue;

		hdir = au_hi(dir, bindex);
		au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
		if (wbr)
			wbr_wh_write_lock(wbr);
		err = au_wh_init(br, sb);
		if (wbr)
			wbr_wh_write_unlock(wbr);
		au_hn_imtx_unlock(hdir);

		if (!err && do_free) {
			kfree(wbr);
			br->br_wbr = NULL;
		}
	}

	return err;
}
Exemple #27
0
static int set_inode(struct inode *inode, struct dentry *dentry)
{
	int err;
	unsigned int flags;
	umode_t mode;
	aufs_bindex_t bindex, bstart, btail;
	unsigned char isdir;
	struct dentry *h_dentry;
	struct inode *h_inode;
	struct au_iinfo *iinfo;

	IiMustWriteLock(inode);

	err = 0;
	isdir = 0;
	bstart = au_dbstart(dentry);
	h_inode = au_h_dptr(dentry, bstart)->d_inode;
	mode = h_inode->i_mode;
	switch (mode & S_IFMT) {
	case S_IFREG:
		btail = au_dbtail(dentry);
		inode->i_op = &aufs_iop;
		inode->i_fop = &aufs_file_fop;
		inode->i_mapping->a_ops = &aufs_aop;
		break;
	case S_IFDIR:
		isdir = 1;
		btail = au_dbtaildir(dentry);
		inode->i_op = &aufs_dir_iop;
		inode->i_fop = &aufs_dir_fop;
		break;
	case S_IFLNK:
		btail = au_dbtail(dentry);
		inode->i_op = &aufs_symlink_iop;
		break;
	case S_IFBLK:
	case S_IFCHR:
	case S_IFIFO:
	case S_IFSOCK:
		btail = au_dbtail(dentry);
		inode->i_op = &aufs_iop;
		au_init_special_fop(inode, mode, h_inode->i_rdev);
		break;
	default:
		AuIOErr("Unknown file type 0%o\n", mode);
		err = -EIO;
		goto out;
	}

	/* do not set inotify for whiteouted dirs (SHWH mode) */
	flags = au_hi_flags(inode, isdir);
	if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)
	    && au_ftest_hi(flags, HINOTIFY)
	    && dentry->d_name.len > AUFS_WH_PFX_LEN
	    && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
		au_fclr_hi(flags, HINOTIFY);
	iinfo = au_ii(inode);
	iinfo->ii_bstart = bstart;
	iinfo->ii_bend = btail;
	for (bindex = bstart; bindex <= btail; bindex++) {
		h_dentry = au_h_dptr(dentry, bindex);
		if (h_dentry)
			au_set_h_iptr(inode, bindex,
				      au_igrab(h_dentry->d_inode), flags);
	}
	au_cpup_attr_all(inode, /*force*/1);

 out:
	return err;
}
Exemple #28
0
int au_opts_mount(struct super_block *sb, struct au_opts *opts)
{
	int err;
	unsigned int tmp;
	aufs_bindex_t bindex, bend;
	struct au_opt *opt;
	struct au_opt_xino *opt_xino, xino;
	struct au_sbinfo *sbinfo;
	struct au_branch *br;

	SiMustWriteLock(sb);

	err = 0;
	opt_xino = NULL;
	opt = opts->opt;
	while (err >= 0 && opt->type != Opt_tail)
		err = au_opt_simple(sb, opt++, opts);
	if (err > 0)
		err = 0;
	else if (unlikely(err < 0))
		goto out;

	/* disable xino and udba temporary */
	sbinfo = au_sbi(sb);
	tmp = sbinfo->si_mntflags;
	au_opt_clr(sbinfo->si_mntflags, XINO);
	au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL);

	opt = opts->opt;
	while (err >= 0 && opt->type != Opt_tail)
		err = au_opt_br(sb, opt++, opts);
	if (err > 0)
		err = 0;
	else if (unlikely(err < 0))
		goto out;

	bend = au_sbend(sb);
	if (unlikely(bend < 0)) {
		err = -EINVAL;
		pr_err("no branches\n");
		goto out;
	}

	if (au_opt_test(tmp, XINO))
		au_opt_set(sbinfo->si_mntflags, XINO);
	opt = opts->opt;
	while (!err && opt->type != Opt_tail)
		err = au_opt_xino(sb, opt++, &opt_xino, opts);
	if (unlikely(err))
		goto out;

	err = au_opts_verify(sb, sb->s_flags, tmp);
	if (unlikely(err))
		goto out;

	/* restore xino */
	if (au_opt_test(tmp, XINO) && !opt_xino) {
		xino.file = au_xino_def(sb);
		err = PTR_ERR(xino.file);
		if (IS_ERR(xino.file))
			goto out;

		err = au_xino_set(sb, &xino, /*remount*/0);
		fput(xino.file);
		if (unlikely(err))
			goto out;
	}

	/* restore udba */
	tmp &= AuOptMask_UDBA;
	sbinfo->si_mntflags &= ~AuOptMask_UDBA;
	sbinfo->si_mntflags |= tmp;
	bend = au_sbend(sb);
	for (bindex = 0; bindex <= bend; bindex++) {
		br = au_sbr(sb, bindex);
		err = au_hnotify_reset_br(tmp, br, br->br_perm);
		if (unlikely(err))
			AuIOErr("hnotify failed on br %d, %d, ignored\n",
				bindex, err);
		/* go on even if err */
	}
	if (au_opt_test(tmp, UDBA_HNOTIFY)) {
		struct inode *dir = sb->s_root->d_inode;
		au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO);
	}

out:
	return err;
}
Exemple #29
0
static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
{
	int err;
	aufs_bindex_t bstart;
	struct au_pin pin;
	struct au_finfo *finfo;
	struct dentry *dentry, *parent, *hi_wh;
	struct inode *inode;
	struct super_block *sb;

	FiMustWriteLock(file);

	err = 0;
	finfo = au_fi(file);
	dentry = file->f_dentry;
	sb = dentry->d_sb;
	inode = dentry->d_inode;
	bstart = au_ibstart(inode);
	if (bstart == finfo->fi_btop || IS_ROOT(dentry))
		goto out;

	parent = dget_parent(dentry);
	if (au_test_ro(sb, bstart, inode)) {
		di_read_lock_parent(parent, !AuLock_IR);
		err = AuWbrCopyup(au_sbi(sb), dentry);
		bstart = err;
		di_read_unlock(parent, !AuLock_IR);
		if (unlikely(err < 0))
			goto out_parent;
		err = 0;
	}

	di_read_lock_parent(parent, AuLock_IR);
	hi_wh = au_hi_wh(inode, bstart);
	if (!S_ISDIR(inode->i_mode)
	    && au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(inode)
	    && !d_unhashed(dentry)) {
		err = au_test_and_cpup_dirs(dentry, bstart);
		if (unlikely(err))
			goto out_unlock;

		/* always superio. */
		err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,
			     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
		if (!err)
			err = au_sio_cpup_simple(dentry, bstart, -1,
						 AuCpup_DTIME);
		au_unpin(&pin);
	} else if (hi_wh) {
		/* already copied-up after unlink */
		err = au_reopen_wh(file, bstart, hi_wh);
		*need_reopen = 0;
	}

out_unlock:
	di_read_unlock(parent, AuLock_IR);
out_parent:
	dput(parent);
out:
	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;
}