Example #1
0
/*
 * Don't grab the superblock read-lock in unionfs_permission, which prevents
 * a deadlock with the branch-management "add branch" code (which grabbed
 * the write lock).  It is safe to not grab the read lock here, because even
 * with branch management taking place, there is no chance that
 * unionfs_permission, or anything it calls, will use stale branch
 * information.
 */
static int unionfs_permission(struct inode *inode, int mask)
{
	struct inode *lower_inode = NULL;
	int err = 0;
	int bindex, bstart, bend;
	int is_file;
	const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);
	struct inode *inode_grabbed;

	inode_grabbed = igrab(inode);
	is_file = !S_ISDIR(inode->i_mode);

	if (!UNIONFS_I(inode)->lower_inodes) {
		if (is_file)	/* dirs can be unlinked but chdir'ed to */
			err = -ESTALE;	/* force revalidate */
		goto out;
	}
	bstart = ibstart(inode);
	bend = ibend(inode);
	if (unlikely(bstart < 0 || bend < 0)) {
		/*
		 * With branch-management, we can get a stale inode here.
		 * If so, we return ESTALE back to link_path_walk, which
		 * would discard the dcache entry and re-lookup the
		 * dentry+inode.  This should be equivalent to issuing
		 * __unionfs_d_revalidate_chain on nd.dentry here.
		 */
		if (is_file)	/* dirs can be unlinked but chdir'ed to */
			err = -ESTALE;	/* force revalidate */
		goto out;
	}

	for (bindex = bstart; bindex <= bend; bindex++) {
		lower_inode = unionfs_lower_inode_idx(inode, bindex);
		if (!lower_inode)
			continue;

		/*
		 * check the condition for D-F-D underlying files/directories,
		 * we don't have to check for files, if we are checking for
		 * directories.
		 */
		if (!is_file && !S_ISDIR(lower_inode->i_mode))
			continue;

		/*
		 * We check basic permissions, but we ignore any conditions
		 * such as readonly file systems or branches marked as
		 * readonly, because those conditions should lead to a
		 * copyup taking place later on.  However, if user never had
		 * access to the file, then no copyup could ever take place.
		 */
		err = __inode_permission(lower_inode, mask);
		if (err && err != -EACCES && err != EPERM && bindex > 0) {
			umode_t mode = lower_inode->i_mode;
			if ((is_robranch_super(inode->i_sb, bindex) ||
			     __is_rdonly(lower_inode)) &&
			    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
				err = 0;
			if (IS_COPYUP_ERR(err))
				err = 0;
		}

		/*
		 * NFS HACK: NFSv2/3 return EACCES on readonly-exported,
		 * locally readonly-mounted file systems, instead of EROFS
		 * like other file systems do.  So we have no choice here
		 * but to intercept this and ignore it for NFS branches
		 * marked readonly.  Specifically, we avoid using NFS's own
		 * "broken" ->permission method, and rely on
		 * generic_permission() to do basic checking for us.
		 */
		if (err && err == -EACCES &&
		    is_robranch_super(inode->i_sb, bindex) &&
		    lower_inode->i_sb->s_magic == NFS_SUPER_MAGIC)
			err = generic_permission(lower_inode, mask);

		/*
		 * The permissions are an intersection of the overall directory
		 * permissions, so we fail if one fails.
		 */
		if (err)
			goto out;

		/* only the leftmost file matters. */
		if (is_file || write_mask) {
			if (is_file && write_mask) {
				err = get_write_access(lower_inode);
				if (!err)
					put_write_access(lower_inode);
			}
			break;
		}
	}
	/* sync times which may have changed (asynchronously) below */
	unionfs_copy_attr_times(inode);

out:
	unionfs_check_inode(inode);
	iput(inode_grabbed);
	return err;
}
Example #2
0
int ovl_permission(struct inode *inode, int mask)
{
	struct ovl_entry *oe;
	struct dentry *alias = NULL;
	struct inode *realinode;
	struct dentry *realdentry;
	bool is_upper;
	int err;

	if (S_ISDIR(inode->i_mode)) {
		oe = inode->i_private;
	} else if (mask & MAY_NOT_BLOCK) {
		return -ECHILD;
	} else {
		/*
		 * For non-directories find an alias and get the info
		 * from there.
		 */
		alias = d_find_any_alias(inode);
		if (WARN_ON(!alias))
			return -ENOENT;

		oe = alias->d_fsdata;
	}

	realdentry = ovl_entry_real(oe, &is_upper);

	/* Careful in RCU walk mode */
	realinode = ACCESS_ONCE(realdentry->d_inode);
	if (!realinode) {
		WARN_ON(!(mask & MAY_NOT_BLOCK));
		err = -ENOENT;
		goto out_dput;
	}

	if (mask & MAY_WRITE) {
		umode_t mode = realinode->i_mode;

		/*
		 * Writes will always be redirected to upper layer, so
		 * ignore lower layer being read-only.
		 *
		 * If the overlay itself is read-only then proceed
		 * with the permission check, don't return EROFS.
		 * This will only happen if this is the lower layer of
		 * another overlayfs.
		 *
		 * If upper fs becomes read-only after the overlay was
		 * constructed return EROFS to prevent modification of
		 * upper layer.
		 */
		err = -EROFS;
		if (is_upper && !IS_RDONLY(inode) && IS_RDONLY(realinode) &&
		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
			goto out_dput;
	}

	err = __inode_permission(realinode, mask);
out_dput:
	dput(alias);
	return err;
}