int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
{
	int err, rerr, i;
	unsigned int mnt_flags;
	aufs_bindex_t bindex, bend, br_id;
	unsigned char do_wh, verbose;
	struct au_branch *br;
	struct au_wbr *wbr;

	err = 0;
	bindex = au_find_dbindex(sb->s_root, del->h_path.dentry);
	if (bindex < 0) {
		if (remount)
			goto out; /* success */
		err = -ENOENT;
		pr_err("%s no such branch\n", del->pathname);
		goto out;
	}
	AuDbg("bindex b%d\n", bindex);

	err = -EBUSY;
	mnt_flags = au_mntflags(sb);
	verbose = !!au_opt_test(mnt_flags, VERBOSE);
	bend = au_sbend(sb);
	if (unlikely(!bend)) {
		AuVerbose(verbose, "no more branches left\n");
		goto out;
	}
	br = au_sbr(sb, bindex);
	i = atomic_read(&br->br_count);
	if (unlikely(i)) {
		AuVerbose(verbose, "%d file(s) opened\n", i);
		goto out;
	}

	wbr = br->br_wbr;
	do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph);
	if (do_wh) {
		/* instead of WbrWhMustWriteLock(wbr) */
		SiMustWriteLock(sb);
		for (i = 0; i < AuBrWh_Last; i++) {
			dput(wbr->wbr_wh[i]);
			wbr->wbr_wh[i] = NULL;
		}
	}

	err = test_children_busy(sb->s_root, bindex, verbose);
	if (unlikely(err)) {
		if (do_wh)
			goto out_wh;
		goto out;
	}

	err = 0;
	br_id = br->br_id;
	if (!remount)
		au_br_do_del(sb, bindex, br);
	else {
		sysaufs_brs_del(sb, bindex);
		au_br_do_del(sb, bindex, br);
		sysaufs_brs_add(sb, bindex);
	}

	if (!bindex) {
		au_cpup_attr_all(sb->s_root->d_inode, /*force*/1);
		sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes;
	} else
		au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode);
	if (au_opt_test(mnt_flags, PLINK))
		au_plink_half_refresh(sb, br_id);

	if (au_xino_brid(sb) == br_id)
		au_xino_brid_set(sb, -1);
	goto out; /* success */

out_wh:
	/* revert */
	rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry);
	if (rerr)
		pr_warning("failed re-creating base whiteout, %s. (%d)\n",
			   del->pathname, rerr);
out:
	return err;
}
Exemplo n.º 2
0
static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo,
				  struct dentry *parent)
{
	struct dentry *h_d, *h_dp;
	struct au_hdentry tmp, *q;
	struct super_block *sb;
	aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq;

	AuRwMustWriteLock(&dinfo->di_rwsem);

	bend = dinfo->di_bend;
	bwh = dinfo->di_bwh;
	bdiropq = dinfo->di_bdiropq;
	for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
		h_d = p->hd_dentry;
		if (!h_d)
			continue;

		h_dp = dget_parent(h_d);
		if (h_dp == au_h_dptr(parent, bindex)) {
			dput(h_dp);
			continue;
		}

		new_bindex = au_find_dbindex(parent, h_dp);
		dput(h_dp);
		if (dinfo->di_bwh == bindex)
			bwh = new_bindex;
		if (dinfo->di_bdiropq == bindex)
			bdiropq = new_bindex;
		if (new_bindex < 0) {
			au_hdput(p);
			p->hd_dentry = NULL;
			continue;
		}

		/* swap two lower dentries, and loop again */
		q = dinfo->di_hdentry + new_bindex;
		tmp = *q;
		*q = *p;
		*p = tmp;
		if (tmp.hd_dentry) {
			bindex--;
			p--;
		}
	}

	sb = parent->d_sb;
	dinfo->di_bwh = -1;
	if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh))
		dinfo->di_bwh = bwh;

	dinfo->di_bdiropq = -1;
	if (bdiropq >= 0
	    && bdiropq <= au_sbend(sb)
	    && au_sbr_whable(sb, bdiropq))
		dinfo->di_bdiropq = bdiropq;

	bend = au_dbend(parent);
	p = dinfo->di_hdentry;
	for (bindex = 0; bindex <= bend; bindex++, p++)
		if (p->hd_dentry) {
			dinfo->di_bstart = bindex;
			break;
		}

	p = dinfo->di_hdentry + bend;
	for (bindex = bend; bindex >= 0; bindex--, p--)
		if (p->hd_dentry) {
			dinfo->di_bend = bindex;
			break;
		}
}
/*
 * returns:
 * 0: success, the caller will add it
 * plus: success, it is already unified, the caller should ignore it
 * minus: error
 */
static int test_add(struct super_block *sb, struct au_opt_add *add, int remount)
{
	int err;
	aufs_bindex_t bend, bindex;
	struct dentry *root;
	struct inode *inode, *h_inode;

	root = sb->s_root;
	bend = au_sbend(sb);
	if (unlikely(bend >= 0
		     && au_find_dbindex(root, add->path.dentry) >= 0)) {
		err = 1;
		if (!remount) {
			err = -EINVAL;
			pr_err("%s duplicated\n", add->pathname);
		}
		goto out;
	}

	err = -ENOSPC; /* -E2BIG; */
	if (unlikely(AUFS_BRANCH_MAX <= add->bindex
		     || AUFS_BRANCH_MAX - 1 <= bend)) {
		pr_err("number of branches exceeded %s\n", add->pathname);
		goto out;
	}

	err = -EDOM;
	if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
		pr_err("bad index %d\n", add->bindex);
		goto out;
	}

	inode = add->path.dentry->d_inode;
	err = -ENOENT;
	if (unlikely(!inode->i_nlink)) {
		pr_err("no existence %s\n", add->pathname);
		goto out;
	}

	err = -EINVAL;
	if (unlikely(inode->i_sb == sb)) {
		pr_err("%s must be outside\n", add->pathname);
		goto out;
	}

	if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) {
		pr_err("unsupported filesystem, %s (%s)\n",
		       add->pathname, au_sbtype(inode->i_sb));
		goto out;
	}

	err = test_br(add->path.dentry->d_inode, add->perm, add->pathname);
	if (unlikely(err))
		goto out;

	if (bend < 0)
		return 0; /* success */

	err = -EINVAL;
	for (bindex = 0; bindex <= bend; bindex++)
		if (unlikely(test_overlap(sb, add->path.dentry,
					  au_h_dptr(root, bindex)))) {
			pr_err("%s is overlapped\n", add->pathname);
			goto out;
		}

	err = 0;
	if (au_opt_test(au_mntflags(sb), WARN_PERM)) {
		h_inode = au_h_dptr(root, 0)->d_inode;
		if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO)
		    || h_inode->i_uid != inode->i_uid
		    || h_inode->i_gid != inode->i_gid)
			pr_warning("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
				   add->pathname,
				   inode->i_uid, inode->i_gid,
				   (inode->i_mode & S_IALLUGO),
				   h_inode->i_uid, h_inode->i_gid,
				   (h_inode->i_mode & S_IALLUGO));
	}

out:
	return err;
}