Exemple #1
0
/*
 * test if the branch is deletable or not.
 */
static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex,
                            unsigned int sigen, const unsigned int verbose)
{
    int err, i, j, ndentry;
    aufs_bindex_t bstart, bend;
    struct au_dcsub_pages dpages;
    struct au_dpage *dpage;
    struct dentry *d;
    struct inode *inode;

    err = au_dpages_init(&dpages, GFP_NOFS);
    if (unlikely(err))
        goto out;
    err = au_dcsub_pages(&dpages, root, NULL, NULL);
    if (unlikely(err))
        goto out_dpages;

    for (i = 0; !err && i < dpages.ndpage; i++) {
        dpage = dpages.dpages + i;
        ndentry = dpage->ndentry;
        for (j = 0; !err && j < ndentry; j++) {
            d = dpage->dentries[j];
            AuDebugOn(!d->d_count);
            if (!au_digen_test(d, sigen)) {
                di_read_lock_child(d, AuLock_IR);
                if (unlikely(au_dbrange_test(d))) {
                    di_read_unlock(d, AuLock_IR);
                    continue;
                }
            } else {
                di_write_lock_child(d);
                if (unlikely(au_dbrange_test(d))) {
                    di_write_unlock(d);
                    continue;
                }
                err = au_reval_dpath(d, sigen);
                if (!err)
                    di_downgrade_lock(d, AuLock_IR);
                else {
                    di_write_unlock(d);
                    break;
                }
            }

            /* AuDbgDentry(d); */
            inode = d->d_inode;
            bstart = au_dbstart(d);
            bend = au_dbend(d);
            if (bstart <= bindex
                    && bindex <= bend
                    && au_h_dptr(d, bindex)
                    && ((inode && !S_ISDIR(inode->i_mode))
                        || bstart == bend)) {
                err = -EBUSY;
                AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d));
                AuDbgDentry(d);
            }
            di_read_unlock(d, AuLock_IR);
        }
    }

out_dpages:
    au_dpages_free(&dpages);
out:
    return err;
}
static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
{
	int err;
	struct au_pin pin;
	struct au_finfo *finfo;
	struct dentry *parent, *hi_wh;
	struct inode *inode;
	struct super_block *sb;
	struct au_cp_generic cpg = {
		.dentry	= file->f_dentry,
		.bdst	= -1,
		.bsrc	= -1,
		.len	= -1,
		.pin	= &pin,
		.flags	= AuCpup_DTIME
	};

	FiMustWriteLock(file);

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

	parent = dget_parent(cpg.dentry);
	if (au_test_ro(sb, cpg.bdst, inode)) {
		di_read_lock_parent(parent, !AuLock_IR);
		err = AuWbrCopyup(au_sbi(sb), cpg.dentry);
		cpg.bdst = 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, cpg.bdst);
	if (!S_ISDIR(inode->i_mode)
	    && au_opt_test(au_mntflags(sb), PLINK)
	    && au_plink_test(inode)
	    && !d_unhashed(cpg.dentry)
	    && cpg.bdst < au_dbstart(cpg.dentry)) {
		err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst);
		if (unlikely(err))
			goto out_unlock;

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

out_unlock:
	di_read_unlock(parent, AuLock_IR);
out_parent:
	dput(parent);
out:
	return err;
}

static void au_do_refresh_dir(struct file *file)
{
	aufs_bindex_t bindex, bend, new_bindex, brid;
	struct au_hfile *p, tmp, *q;
	struct au_finfo *finfo;
	struct super_block *sb;
	struct au_fidir *fidir;

	FiMustWriteLock(file);

	sb = file->f_dentry->d_sb;
	finfo = au_fi(file);
	fidir = finfo->fi_hdir;
	AuDebugOn(!fidir);
	p = fidir->fd_hfile + finfo->fi_btop;
	brid = p->hf_br->br_id;
	bend = fidir->fd_bbot;
	for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) {
		if (!p->hf_file)
			continue;

		new_bindex = au_br_index(sb, p->hf_br->br_id);
		if (new_bindex == bindex)
			continue;
		if (new_bindex < 0) {
			au_set_h_fptr(file, bindex, NULL);
			continue;
		}

		/* swap two lower inode, and loop again */
		q = fidir->fd_hfile + new_bindex;
		tmp = *q;
		*q = *p;
		*p = tmp;
		if (tmp.hf_file) {
			bindex--;
			p--;
		}
	}

	p = fidir->fd_hfile;
	if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) {
		bend = au_sbend(sb);
		for (finfo->fi_btop = 0; finfo->fi_btop <= bend;
		     finfo->fi_btop++, p++)
			if (p->hf_file) {
				if (file_inode(p->hf_file))
					break;
				au_hfput(p, file);
			}
	} else {
		bend = au_br_index(sb, brid);
		for (finfo->fi_btop = 0; finfo->fi_btop < bend;
		     finfo->fi_btop++, p++)
			if (p->hf_file)
				au_hfput(p, file);
		bend = au_sbend(sb);
	}

	p = fidir->fd_hfile + bend;
	for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop;
	     fidir->fd_bbot--, p--)
		if (p->hf_file) {
			if (file_inode(p->hf_file))
				break;
			au_hfput(p, file);
		}
	AuDebugOn(fidir->fd_bbot < finfo->fi_btop);
}
static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
{
	int err, fd;
	aufs_bindex_t wbi, bindex, bend;
	struct file *h_file;
	struct super_block *sb;
	struct dentry *root;
	struct au_branch *br;
	struct aufs_wbr_fd wbrfd = {
		.oflags	= au_dir_roflags,
		.brid	= -1
	};
	const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
		| O_NOATIME | O_CLOEXEC;

	AuDebugOn(wbrfd.oflags & ~valid);

	if (arg) {
		err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
		if (unlikely(err)) {
			err = -EFAULT;
			goto out;
		}

		err = -EINVAL;
		AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
		wbrfd.oflags |= au_dir_roflags;
		AuDbg("0%o\n", wbrfd.oflags);
		if (unlikely(wbrfd.oflags & ~valid))
			goto out;
	}

	fd = get_unused_fd();
	err = fd;
	if (unlikely(fd < 0))
		goto out;

	h_file = ERR_PTR(-EINVAL);
	wbi = 0;
	br = NULL;
	sb = path->dentry->d_sb;
	root = sb->s_root;
	aufs_read_lock(root, AuLock_IR);
	bend = au_sbend(sb);
	if (wbrfd.brid >= 0) {
		wbi = au_br_index(sb, wbrfd.brid);
		if (unlikely(wbi < 0 || wbi > bend))
			goto out_unlock;
	}

	h_file = ERR_PTR(-ENOENT);
	br = au_sbr(sb, wbi);
	if (!au_br_writable(br->br_perm)) {
		if (arg)
			goto out_unlock;

		bindex = wbi + 1;
		wbi = -1;
		for (; bindex <= bend; bindex++) {
			br = au_sbr(sb, bindex);
			if (au_br_writable(br->br_perm)) {
				wbi = bindex;
				br = au_sbr(sb, wbi);
				break;
			}
		}
	}
	AuDbg("wbi %d\n", wbi);
	if (wbi >= 0)
		h_file = au_h_open(root, wbi, wbrfd.oflags, NULL,
				   /*force_wr*/0);

out_unlock:
	aufs_read_unlock(root, AuLock_IR);
	err = PTR_ERR(h_file);
	if (IS_ERR(h_file))
		goto out_fd;

	atomic_dec(&br->br_count); /* cf. au_h_open() */
	fd_install(fd, h_file);
	err = fd;
	goto out; /* success */

out_fd:
	put_unused_fd(fd);
out:
	AuTraceErr(err);
	return err;
}

/* ---------------------------------------------------------------------- */

long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
{
	long err;
	struct dentry *dentry;

	switch (cmd) {
	case AUFS_CTL_RDU:
	case AUFS_CTL_RDU_INO:
		err = au_rdu_ioctl(file, cmd, arg);
		break;

	case AUFS_CTL_WBR_FD:
		err = au_wbr_fd(&file->f_path, (void __user *)arg);
		break;

	case AUFS_CTL_IBUSY:
		err = au_ibusy_ioctl(file, arg);
		break;

	case AUFS_CTL_BRINFO:
		err = au_brinfo_ioctl(file, arg);
		break;

	case AUFS_CTL_FHSM_FD:
		dentry = file->f_dentry;
		if (IS_ROOT(dentry))
			err = au_fhsm_fd(dentry->d_sb, arg);
		else
			err = -ENOTTY;
		break;

	default:
		/* do not call the lower */
		AuDbg("0x%x\n", cmd);
		err = -ENOTTY;
	}

	AuTraceErr(err);
	return err;
}
static int au_cmoo(struct dentry *dentry)
{
	int err, cmoo;
	unsigned int udba;
	struct path h_path;
	struct au_pin pin;
	struct au_cp_generic cpg = {
		.dentry	= dentry,
		.bdst	= -1,
		.bsrc	= -1,
		.len	= -1,
		.pin	= &pin,
		.flags	= AuCpup_DTIME | AuCpup_HOPEN
	};
	struct inode *inode, *delegated;
	struct super_block *sb;
	struct au_sbinfo *sbinfo;
	struct au_fhsm *fhsm;
	pid_t pid;
	struct au_branch *br;
	struct dentry *parent;
	struct au_hinode *hdir;

	DiMustWriteLock(dentry);
	inode = dentry->d_inode;
	IiMustWriteLock(inode);

	err = 0;
	if (IS_ROOT(dentry))
		goto out;
	cpg.bsrc = au_dbstart(dentry);
	if (!cpg.bsrc)
		goto out;

	sb = dentry->d_sb;
	sbinfo = au_sbi(sb);
	fhsm = &sbinfo->si_fhsm;
	pid = au_fhsm_pid(fhsm);
	if (pid
	    && (current->pid == pid
		|| current->real_parent->pid == pid))
		goto out;

	br = au_sbr(sb, cpg.bsrc);
	cmoo = au_br_cmoo(br->br_perm);
	if (!cmoo)
		goto out;
	if (!S_ISREG(inode->i_mode))
		cmoo &= AuBrAttr_COO_ALL;
	if (!cmoo)
		goto out;

	parent = dget_parent(dentry);
	di_write_lock_parent(parent);
	err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1);
	cpg.bdst = err;
	if (unlikely(err < 0)) {
		err = 0;	/* there is no upper writable branch */
		goto out_dgrade;
	}
	AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst);

	/* do not respect the coo attrib for the target branch */
	err = au_cpup_dirs(dentry, cpg.bdst);
	if (unlikely(err))
		goto out_dgrade;

	di_downgrade_lock(parent, AuLock_IR);
	udba = au_opt_udba(sb);
	err = au_pin(&pin, dentry, cpg.bdst, udba,
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	if (unlikely(err))
		goto out_parent;

	err = au_sio_cpup_simple(&cpg);
	au_unpin(&pin);
	if (unlikely(err))
		goto out_parent;
	if (!(cmoo & AuBrWAttr_MOO))
		goto out_parent; /* success */

	err = au_pin(&pin, dentry, cpg.bsrc, udba,
		     AuPin_DI_LOCKED | AuPin_MNT_WRITE);
	if (unlikely(err))
		goto out_parent;

	h_path.mnt = au_br_mnt(br);
	h_path.dentry = au_h_dptr(dentry, cpg.bsrc);
	hdir = au_hi(parent->d_inode, cpg.bsrc);
	delegated = NULL;
	err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1);
	au_unpin(&pin);
	/* todo: keep h_dentry or not? */
	if (unlikely(err == -EWOULDBLOCK)) {
		pr_warn("cannot retry for NFSv4 delegation"
			" for an internal unlink\n");
		iput(delegated);
	}
	if (unlikely(err)) {
		pr_err("unlink %pd after coo failed (%d), ignored\n",
		       dentry, err);
		err = 0;
	}
	goto out_parent; /* success */

out_dgrade:
	di_downgrade_lock(parent, AuLock_IR);
out_parent:
	di_read_unlock(parent, AuLock_IR);
	dput(parent);
out:
	AuTraceErr(err);
	return err;
}

int au_do_open(struct file *file, struct au_do_open_args *args)
{
	int err, no_lock = args->no_lock;
	struct dentry *dentry;
	struct au_finfo *finfo;

	if (!no_lock)
		err = au_finfo_init(file, args->fidir);
	else {
		lockdep_off();
		err = au_finfo_init(file, args->fidir);
		lockdep_on();
	}
	if (unlikely(err))
		goto out;

	dentry = file->f_dentry;
	AuDebugOn(IS_ERR_OR_NULL(dentry));
	if (!no_lock) {
		di_write_lock_child(dentry);
		err = au_cmoo(dentry);
		di_downgrade_lock(dentry, AuLock_IR);
		if (!err)
			err = args->open(file, vfsub_file_flags(file), NULL);
		di_read_unlock(dentry, AuLock_IR);
	} else {
		err = au_cmoo(dentry);
		if (!err)
			err = args->open(file, vfsub_file_flags(file),
					 args->h_file);
		if (!err && au_fbstart(file) != au_dbstart(dentry))
			/*
			 * cmoo happens after h_file was opened.
			 * need to refresh file later.
			 */
			atomic_dec(&au_fi(file)->fi_generation);
	}

	finfo = au_fi(file);
	if (!err) {
		finfo->fi_file = file;
		au_sphl_add(&finfo->fi_hlist,
			    &au_sbi(file->f_dentry->d_sb)->si_files);
	}
	if (!no_lock)
		fi_write_unlock(file);
	else {
		lockdep_off();
		fi_write_unlock(file);
		lockdep_on();
	}
	if (unlikely(err)) {
		finfo->fi_hdir = NULL;
		au_finfo_fin(file);
	}

out:
	return err;
}