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: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Amir Goldstein 2017-10-24 15:12:15 +03:00 committed by Miklos Szeredi
parent d583ed7d13
commit 2e1a532883
1 changed files with 92 additions and 50 deletions

View File

@ -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);
@ -102,7 +129,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
if (res == 0)
return NULL;
fh = kzalloc(res, GFP_KERNEL);
fh = kzalloc(res, GFP_KERNEL);
if (!fh)
return ERR_PTR(-ENOMEM);
@ -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)
err = ovl_check_fh_len(fh, res);
if (err < 0) {
if (err == -ENODATA)
goto out;
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)
goto out;
}
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,
struct ovl_path *lower, unsigned int numlower,
struct ovl_path **stackp, unsigned int *ctrp)
static int ovl_check_origin_fh(struct ovl_fh *fh, struct dentry *upperdentry,
struct ovl_path *lower, unsigned int numlower,
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;