Commit 2e1a5328 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi
Browse files

ovl: factor out ovl_check_origin_fh()



Re-factor ovl_check_origin() and ovl_get_origin(), so origin fh xattr is
read from upper inode only once during lookup with multiple lower layers
and only once when verifying index entry origin.

Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent d583ed7d
Loading
Loading
Loading
Loading
+92 −50
Original line number Diff line number Diff line
@@ -87,9 +87,36 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
	return 1;
}

/*
 * Check validity of an overlay file handle buffer.
 *
 * Return 0 for a valid file handle.
 * Return -ENODATA for "origin unknown".
 * Return <0 for an invalid file handle.
 */
static int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
{
	if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len)
		return -EINVAL;

	if (fh->magic != OVL_FH_MAGIC)
		return -EINVAL;

	/* Treat larger version and unknown flags as "origin unknown" */
	if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
		return -ENODATA;

	/* Treat endianness mismatch as "origin unknown" */
	if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
	    (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
		return -ENODATA;

	return 0;
}

static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
{
	int res;
	int res, err;
	struct ovl_fh *fh = NULL;

	res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
@@ -110,20 +137,12 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
	if (res < 0)
		goto fail;

	if (res < sizeof(struct ovl_fh) || res < fh->len)
		goto invalid;

	if (fh->magic != OVL_FH_MAGIC)
		goto invalid;

	/* Treat larger version and unknown flags as "origin unknown" */
	if (fh->version > OVL_FH_VERSION || fh->flags & ~OVL_FH_FLAG_ALL)
		goto out;

	/* Treat endianness mismatch as "origin unknown" */
	if (!(fh->flags & OVL_FH_FLAG_ANY_ENDIAN) &&
	    (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
	err = ovl_check_fh_len(fh, res);
	if (err < 0) {
		if (err == -ENODATA)
			goto out;
		goto invalid;
	}

	return fh;

@@ -139,22 +158,17 @@ invalid:
	goto out;
}

static struct dentry *ovl_get_origin(struct dentry *dentry,
				     struct vfsmount *mnt)
static struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
{
	struct dentry *origin = NULL;
	struct ovl_fh *fh = ovl_get_origin_fh(dentry);
	struct dentry *origin;
	int bytes;

	if (IS_ERR_OR_NULL(fh))
		return (struct dentry *)fh;

	/*
	 * Make sure that the stored uuid matches the uuid of the lower
	 * layer where file handle will be decoded.
	 */
	if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid))
		goto out;
		return NULL;

	bytes = (fh->len - offsetof(struct ovl_fh, fid));
	origin = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
@@ -164,22 +178,15 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
		/* Treat stale file handle as "origin unknown" */
		if (origin == ERR_PTR(-ESTALE))
			origin = NULL;
		goto out;
		return origin;
	}

	if (ovl_dentry_weird(origin) ||
	    ((d_inode(origin)->i_mode ^ d_inode(dentry)->i_mode) & S_IFMT))
		goto invalid;
	if (ovl_dentry_weird(origin)) {
		dput(origin);
		return NULL;
	}

out:
	kfree(fh);
	return origin;

invalid:
	pr_warn_ratelimited("overlayfs: invalid origin (%pd2)\n", origin);
	dput(origin);
	origin = NULL;
	goto out;
}

static bool ovl_is_opaquedir(struct dentry *dentry)
@@ -284,9 +291,9 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
}


static int ovl_check_origin(struct dentry *upperdentry,
static int ovl_check_origin_fh(struct ovl_fh *fh, struct dentry *upperdentry,
			       struct ovl_path *lower, unsigned int numlower,
			    struct ovl_path **stackp, unsigned int *ctrp)
			       struct ovl_path **stackp)
{
	struct vfsmount *mnt;
	struct dentry *origin = NULL;
@@ -294,18 +301,20 @@ static int ovl_check_origin(struct dentry *upperdentry,

	for (i = 0; i < numlower; i++) {
		mnt = lower[i].layer->mnt;
		origin = ovl_get_origin(upperdentry, mnt);
		if (IS_ERR(origin))
			return PTR_ERR(origin);

		origin = ovl_decode_fh(fh, mnt);
		if (origin)
			break;
	}

	if (!origin)
		return 0;
		return -ESTALE;
	else if (IS_ERR(origin))
		return PTR_ERR(origin);

	if (!ovl_is_whiteout(upperdentry) &&
	    ((d_inode(origin)->i_mode ^ d_inode(upperdentry)->i_mode) & S_IFMT))
		goto invalid;

	BUG_ON(*ctrp);
	if (!*stackp)
		*stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL);
	if (!*stackp) {
@@ -313,9 +322,41 @@ static int ovl_check_origin(struct dentry *upperdentry,
		return -ENOMEM;
	}
	**stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer};
	*ctrp = 1;

	return 0;

invalid:
	pr_warn_ratelimited("overlayfs: invalid origin (%pd2, ftype=%x, origin ftype=%x).\n",
			    upperdentry, d_inode(upperdentry)->i_mode & S_IFMT,
			    d_inode(origin)->i_mode & S_IFMT);
	dput(origin);
	return -EIO;
}

static int ovl_check_origin(struct dentry *upperdentry,
			    struct ovl_path *lower, unsigned int numlower,
			    struct ovl_path **stackp, unsigned int *ctrp)
{
	struct ovl_fh *fh = ovl_get_origin_fh(upperdentry);
	int err;

	if (IS_ERR_OR_NULL(fh))
		return PTR_ERR(fh);

	err = ovl_check_origin_fh(fh, upperdentry, lower, numlower, stackp);
	kfree(fh);

	if (err) {
		if (err == -ESTALE)
			return 0;
		return err;
	}

	if (WARN_ON(*ctrp))
		return -EIO;

	*ctrp = 1;
	return 0;
}

/*
@@ -389,7 +430,6 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
	size_t len;
	struct ovl_path origin = { };
	struct ovl_path *stack = &origin;
	unsigned int ctr = 0;
	int err;

	if (!d_inode(index))
@@ -420,16 +460,18 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
		goto fail;

	err = -EINVAL;
	if (hex2bin((u8 *)fh, index->d_name.name, len) || len != fh->len)
	if (hex2bin((u8 *)fh, index->d_name.name, len))
		goto fail;

	err = ovl_check_fh_len(fh, len);
	if (err)
		goto fail;

	err = ovl_verify_origin_fh(index, fh);
	if (err)
		goto fail;

	err = ovl_check_origin(index, lower, numlower, &stack, &ctr);
	if (!err && !ctr)
		err = -ESTALE;
	err = ovl_check_origin_fh(fh, index, lower, numlower, &stack);
	if (err)
		goto fail;