Пример #1
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;
}
/*
 * Validate dentries for encrypted directories to make sure we aren't
 * potentially caching stale data after a key has been added or
 * removed.
 */
static int ext4_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct inode *dir = d_inode(dentry->d_parent);
	struct ext4_crypt_info *ci = EXT4_I(dir)->i_crypt_info;
	int dir_has_key, cached_with_key;

	if (!ext4_encrypted_inode(dir))
		return 0;

	if (ci && ci->ci_keyring_key &&
	    (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
					  (1 << KEY_FLAG_REVOKED) |
					  (1 << KEY_FLAG_DEAD))))
		ci = NULL;

	/* this should eventually be an flag in d_flags */
	cached_with_key = dentry->d_fsdata != NULL;
	dir_has_key = (ci != NULL);

	/*
	 * If the dentry was cached without the key, and it is a
	 * negative dentry, it might be a valid name.  We can't check
	 * if the key has since been made available due to locking
	 * reasons, so we fail the validation so ext4_lookup() can do
	 * this check.
	 *
	 * We also fail the validation if the dentry was created with
	 * the key present, but we no longer have the key, or vice versa.
	 */
	if ((!cached_with_key && d_is_negative(dentry)) ||
	    (!cached_with_key && dir_has_key) ||
	    (cached_with_key && !dir_has_key)) {
#if 0				/* Revalidation debug */
		char buf[80];
		char *cp = simple_dname(dentry, buf, sizeof(buf));

		if (IS_ERR(cp))
			cp = (char *) "???";
		pr_err("revalidate: %s %p %d %d %d\n", cp, dentry->d_fsdata,
		       cached_with_key, d_is_negative(dentry),
		       dir_has_key);
#endif
		return 0;
	}
	return 1;
}
Пример #3
0
int
gr_acl_handle_rename(struct dentry *new_dentry,
		     struct dentry *parent_dentry,
		     const struct vfsmount *parent_mnt,
		     struct dentry *old_dentry,
		     struct inode *old_parent_inode,
		     struct vfsmount *old_mnt, const struct filename *newname, unsigned int flags)
{
	__u32 comp1, comp2;
	int error = 0;

	if (unlikely(!gr_acl_is_enabled()))
		return 0;

	if (flags & RENAME_EXCHANGE) {
		comp1 = gr_search_file(new_dentry, GR_READ | GR_WRITE |
				       GR_AUDIT_READ | GR_AUDIT_WRITE |
				       GR_SUPPRESS, parent_mnt);
		comp2 =
		    gr_search_file(old_dentry,
				   GR_READ | GR_WRITE | GR_AUDIT_READ |
				   GR_AUDIT_WRITE | GR_SUPPRESS, old_mnt);
	} else if (d_is_negative(new_dentry)) {
		comp1 = gr_check_create(new_dentry, parent_dentry, parent_mnt,
					GR_READ | GR_WRITE | GR_CREATE | GR_AUDIT_READ |
					GR_AUDIT_WRITE | GR_AUDIT_CREATE | GR_SUPPRESS);
		comp2 = gr_search_file(old_dentry, GR_READ | GR_WRITE |
				       GR_DELETE | GR_AUDIT_DELETE |
				       GR_AUDIT_READ | GR_AUDIT_WRITE |
				       GR_SUPPRESS, old_mnt);
	} else {
		comp1 = gr_search_file(new_dentry, GR_READ | GR_WRITE |
				       GR_CREATE | GR_DELETE |
				       GR_AUDIT_CREATE | GR_AUDIT_DELETE |
				       GR_AUDIT_READ | GR_AUDIT_WRITE |
				       GR_SUPPRESS, parent_mnt);
		comp2 =
		    gr_search_file(old_dentry,
				   GR_READ | GR_WRITE | GR_AUDIT_READ |
				   GR_DELETE | GR_AUDIT_DELETE |
				   GR_AUDIT_WRITE | GR_SUPPRESS, old_mnt);
	}

	if (RENAME_CHECK_SUCCESS(comp1, comp2) &&
	    ((comp1 & GR_AUDITS) || (comp2 & GR_AUDITS)))
		gr_log_fs_rbac_str(GR_DO_AUDIT, GR_RENAME_ACL_MSG, old_dentry, old_mnt, newname->name);
	else if (!RENAME_CHECK_SUCCESS(comp1, comp2) && !(comp1 & GR_SUPPRESS)
		 && !(comp2 & GR_SUPPRESS)) {
		gr_log_fs_rbac_str(GR_DONT_AUDIT, GR_RENAME_ACL_MSG, old_dentry, old_mnt, newname->name);
		error = -EACCES;
	} else if (unlikely(!RENAME_CHECK_SUCCESS(comp1, comp2)))
		error = -EACCES;

	return error;
}
Пример #4
0
/*
 * Validate dentries for encrypted directories to make sure we aren't
 * potentially caching stale data after a key has been added or
 * removed.
 */
static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct dentry *dir;
	struct fscrypt_info *ci;
	int dir_has_key, cached_with_key;

	if (flags & LOOKUP_RCU)
		return -ECHILD;

	dir = dget_parent(dentry);
	if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) {
		dput(dir);
		return 0;
	}

	ci = d_inode(dir)->i_crypt_info;
	if (ci && ci->ci_keyring_key &&
	    (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
					  (1 << KEY_FLAG_REVOKED) |
					  (1 << KEY_FLAG_DEAD))))
		ci = NULL;

	/* this should eventually be an flag in d_flags */
	spin_lock(&dentry->d_lock);
	cached_with_key = dentry->d_flags & DCACHE_ENCRYPTED_WITH_KEY;
	spin_unlock(&dentry->d_lock);
	dir_has_key = (ci != NULL);
	dput(dir);

	/*
	 * If the dentry was cached without the key, and it is a
	 * negative dentry, it might be a valid name.  We can't check
	 * if the key has since been made available due to locking
	 * reasons, so we fail the validation so ext4_lookup() can do
	 * this check.
	 *
	 * We also fail the validation if the dentry was created with
	 * the key present, but we no longer have the key, or vice versa.
	 */
	if ((!cached_with_key && d_is_negative(dentry)) ||
			(!cached_with_key && dir_has_key) ||
			(cached_with_key && !dir_has_key))
		return 0;
	return 1;
}
Пример #5
0
__u32
gr_acl_handle_open(const struct dentry * dentry, const struct vfsmount * mnt,
		   int acc_mode)
{
	__u32 reqmode = GR_FIND;
	__u32 mode;

	if (unlikely(d_is_negative(dentry)))
		return reqmode;

	if (acc_mode & MAY_APPEND)
		reqmode |= GR_APPEND;
	else if (acc_mode & MAY_WRITE)
		reqmode |= GR_WRITE;
	if ((acc_mode & MAY_READ) && !d_is_dir(dentry))
		reqmode |= GR_READ;

	mode =
	    gr_search_file(dentry, reqmode | to_gr_audit(reqmode) | GR_SUPPRESS,
			   mnt);

	if (unlikely(((mode & reqmode) == reqmode) && mode & GR_AUDITS)) {
		gr_log_fs_rbac_mode2(GR_DO_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt,
			       reqmode & GR_READ ? " reading" : "",
			       reqmode & GR_WRITE ? " writing" : reqmode &
			       GR_APPEND ? " appending" : "");
		return reqmode;
	} else
	    if (unlikely((mode & reqmode) != reqmode && !(mode & GR_SUPPRESS)))
	{
		gr_log_fs_rbac_mode2(GR_DONT_AUDIT, GR_OPEN_ACL_MSG, dentry, mnt,
			       reqmode & GR_READ ? " reading" : "",
			       reqmode & GR_WRITE ? " writing" : reqmode &
			       GR_APPEND ? " appending" : "");
		return 0;
	} else if (unlikely((mode & reqmode) != reqmode))
		return 0;

	return reqmode;
}
Пример #6
0
__u32
gr_acl_handle_hidden_file(const struct dentry * dentry,
			  const struct vfsmount * mnt)
{
	__u32 mode;

	if (unlikely(d_is_negative(dentry)))
		return GR_FIND;

	mode =
	    gr_search_file(dentry, GR_FIND | GR_AUDIT_FIND | GR_SUPPRESS, mnt);

	if (unlikely(mode & GR_FIND && mode & GR_AUDIT_FIND)) {
		gr_log_fs_rbac_generic(GR_DO_AUDIT, GR_HIDDEN_ACL_MSG, dentry, mnt);
		return mode;
	} else if (unlikely(!(mode & GR_FIND) && !(mode & GR_SUPPRESS))) {
		gr_log_fs_rbac_generic(GR_DONT_AUDIT, GR_HIDDEN_ACL_MSG, dentry, mnt);
		return 0;
	} else if (unlikely(!(mode & GR_FIND)))
		return 0;

	return GR_FIND;
}
Пример #7
0
/*
 * By adding a dirty branch, a cached dentry may be affected in various ways.
 *
 * a dirty branch is added
 * - on the top of layers
 * - in the middle of layers
 * - to the bottom of layers
 *
 * on the added branch there exists
 * - a whiteout
 * - a diropq
 * - a same named entry
 *   + exist
 *     * negative --> positive
 *     * positive --> positive
 *	 - type is unchanged
 *	 - type is changed
 *   + doesn't exist
 *     * negative --> negative
 *     * positive --> negative (rejected by au_br_del() for non-dir case)
 * - none
 */
static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo,
			       struct au_dinfo *tmp)
{
	int err;
	aufs_bindex_t bindex, bend;
	struct {
		struct dentry *dentry;
		struct inode *inode;
		mode_t mode;
	} orig_h, tmp_h;
	struct au_hdentry *hd;
	struct inode *inode, *h_inode;
	struct dentry *h_dentry;

	err = 0;
	AuDebugOn(dinfo->di_bstart < 0);
	orig_h.mode = 0;
	orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry;
	orig_h.inode = NULL;
	if (d_is_positive(orig_h.dentry)) {
		orig_h.inode = d_inode(orig_h.dentry);
		orig_h.mode = orig_h.inode->i_mode & S_IFMT;
	}
	memset(&tmp_h, 0, sizeof(tmp_h));
	if (tmp->di_bstart >= 0) {
		tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry;
		tmp_h.inode = NULL;
		if (d_is_positive(tmp_h.dentry)) {
			tmp_h.inode = d_inode(tmp_h.dentry);
			tmp_h.mode = tmp_h.inode->i_mode & S_IFMT;
		}
	}

	inode = NULL;
	if (d_really_is_positive(dentry))
		inode = d_inode(dentry);
	if (!orig_h.inode) {
		AuDbg("nagative originally\n");
		if (inode) {
			au_hide(dentry);
			goto out;
		}
		AuDebugOn(inode);
		AuDebugOn(dinfo->di_bstart != dinfo->di_bend);
		AuDebugOn(dinfo->di_bdiropq != -1);

		if (!tmp_h.inode) {
			AuDbg("negative --> negative\n");
			/* should have only one negative lower */
			if (tmp->di_bstart >= 0
			    && tmp->di_bstart < dinfo->di_bstart) {
				AuDebugOn(tmp->di_bstart != tmp->di_bend);
				AuDebugOn(dinfo->di_bstart != dinfo->di_bend);
				au_set_h_dptr(dentry, dinfo->di_bstart, NULL);
				au_di_cp(dinfo, tmp);
				hd = tmp->di_hdentry + tmp->di_bstart;
				au_set_h_dptr(dentry, tmp->di_bstart,
					      dget(hd->hd_dentry));
			}
			au_dbg_verify_dinode(dentry);
		} else {
			AuDbg("negative --> positive\n");
			/*
			 * similar to the behaviour of creating with bypassing
			 * aufs.
			 * unhash it in order to force an error in the
			 * succeeding create operation.
			 * we should not set S_DEAD here.
			 */
			d_drop(dentry);
			/* au_di_swap(tmp, dinfo); */
			au_dbg_verify_dinode(dentry);
		}
	} else {
		AuDbg("positive originally\n");
		/* inode may be NULL */
		AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode);
		if (!tmp_h.inode) {
			AuDbg("positive --> negative\n");
			/* or bypassing aufs */
			au_hide(dentry);
			if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart)
				dinfo->di_bwh = tmp->di_bwh;
			if (inode)
				err = au_refresh_hinode_self(inode);
			au_dbg_verify_dinode(dentry);
		} else if (orig_h.mode == tmp_h.mode) {
			AuDbg("positive --> positive, same type\n");
			if (!S_ISDIR(orig_h.mode)
			    && dinfo->di_bstart > tmp->di_bstart) {
				/*
				 * similar to the behaviour of removing and
				 * creating.
				 */
				au_hide(dentry);
				if (inode)
					err = au_refresh_hinode_self(inode);
				au_dbg_verify_dinode(dentry);
			} else {
				/* fill empty slots */
				if (dinfo->di_bstart > tmp->di_bstart)
					dinfo->di_bstart = tmp->di_bstart;
				if (dinfo->di_bend < tmp->di_bend)
					dinfo->di_bend = tmp->di_bend;
				dinfo->di_bwh = tmp->di_bwh;
				dinfo->di_bdiropq = tmp->di_bdiropq;
				hd = tmp->di_hdentry;
				bend = dinfo->di_bend;
				for (bindex = tmp->di_bstart; bindex <= bend;
				     bindex++) {
					if (au_h_dptr(dentry, bindex))
						continue;
					h_dentry = hd[bindex].hd_dentry;
					if (!h_dentry)
						continue;
					AuDebugOn(d_is_negative(h_dentry));
					h_inode = d_inode(h_dentry);
					AuDebugOn(orig_h.mode
						  != (h_inode->i_mode
						      & S_IFMT));
					au_set_h_dptr(dentry, bindex,
						      dget(h_dentry));
				}
				err = au_refresh_hinode(inode, dentry);
				au_dbg_verify_dinode(dentry);
			}
		} else {
			AuDbg("positive --> positive, different type\n");
			/* similar to the behaviour of removing and creating */
			au_hide(dentry);
			if (inode)
				err = au_refresh_hinode_self(inode);
			au_dbg_verify_dinode(dentry);
		}
	}

out:
	return err;
}
Пример #8
0
/*
 * 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;
	struct au_branch *br;
	int wh_found, opq;
	unsigned char wh_able;
	const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
	const unsigned char ignore_perm = !!au_ftest_lkup(args->flags,
							  IGNORE_PERM);

	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, /*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:
	if (!ignore_perm)
		h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
	else
		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
	if (IS_ERR(h_dentry)) {
		if (PTR_ERR(h_dentry) == -ENAMETOOLONG
		    && !allow_neg)
			h_dentry = NULL;
		goto out;
	}

	h_inode = d_inode(h_dentry);
	if (d_is_negative(h_dentry)) {
		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);

	if (!d_is_dir(h_dentry)
	    || !wh_able
	    || (d_really_is_positive(dentry) && !d_is_dir(dentry)))
		goto out; /* success */

	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
	opq = au_diropq_test(h_dentry);
	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;
}
Пример #9
0
/*
 * 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)
{
	int npositive, err;
	aufs_bindex_t bindex, btail, bdiropq;
	unsigned char isdir, dirperm1;
	struct qstr whname;
	struct au_do_lookup_args args = {
		.flags		= 0,
		.type		= type
	};
	const struct qstr *name = &dentry->d_name;
	struct dentry *parent;
	struct super_block *sb;

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

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

	isdir = !!d_is_dir(dentry);
	if (!type)
		au_fset_lkup(args.flags, ALLOW_NEG);
	dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);

	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 (d_is_positive(h_dentry))
				npositive++;
			if (type != S_IFDIR)
				break;
			continue;
		}
		h_parent = au_h_dptr(parent, bindex);
		if (!h_parent || !d_is_dir(h_parent))
			continue;

		h_dir = d_inode(h_parent);
		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;
		if (h_dentry)
			au_fclr_lkup(args.flags, ALLOW_NEG);
		if (dirperm1)
			au_fset_lkup(args.flags, IGNORE_PERM);

		if (au_dbwh(dentry) >= 0)
			break;
		if (!h_dentry)
			continue;
		if (d_is_negative(h_dentry))
			continue;
		h_inode = d_inode(h_dentry);
		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(sb), UDBA_NONE)
		     && au_dbstart(dentry) < 0)) {
		err = -EIO;
		AuIOErr("both of real entry and whiteout found, %pd, err %d\n",
			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 dentry *dentry;
	int wkq_err;

	if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC))
		dentry = vfsub_lkup_one(name, parent);
	else {
		struct vfsub_lkup_one_args args = {
			.errp	= &dentry,
			.name	= name,
			.parent	= parent
		};

		wkq_err = au_wkq_wait(vfsub_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 wh)
{
	int err;
	struct dentry *parent, *h_parent, *h_dentry;
	struct au_branch *br;

	parent = dget_parent(dentry);
	h_parent = au_h_dptr(parent, bindex);
	br = au_sbr(dentry->d_sb, bindex);
	if (wh)
		h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
	else
		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
	err = PTR_ERR(h_dentry);
	if (IS_ERR(h_dentry))
		goto out;
	if (unlikely(d_is_positive(h_dentry))) {
		err = -EIO;
		AuIOErr("%pd should be negative on b%d.\n", 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;
}