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

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
...@@ -87,9 +87,36 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry) ...@@ -87,9 +87,36 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
return 1; 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) static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
{ {
int res; int res, err;
struct ovl_fh *fh = NULL; struct ovl_fh *fh = NULL;
res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0); res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
...@@ -102,7 +129,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry) ...@@ -102,7 +129,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
if (res == 0) if (res == 0)
return NULL; return NULL;
fh = kzalloc(res, GFP_KERNEL); fh = kzalloc(res, GFP_KERNEL);
if (!fh) if (!fh)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -110,20 +137,12 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry) ...@@ -110,20 +137,12 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
if (res < 0) if (res < 0)
goto fail; goto fail;
if (res < sizeof(struct ovl_fh) || res < fh->len) err = ovl_check_fh_len(fh, res);
goto invalid; if (err < 0) {
if (err == -ENODATA)
if (fh->magic != OVL_FH_MAGIC) goto out;
goto invalid; 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; return fh;
...@@ -139,22 +158,17 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry) ...@@ -139,22 +158,17 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
goto out; goto out;
} }
static struct dentry *ovl_get_origin(struct dentry *dentry, static struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
struct vfsmount *mnt)
{ {
struct dentry *origin = NULL; struct dentry *origin;
struct ovl_fh *fh = ovl_get_origin_fh(dentry);
int bytes; int bytes;
if (IS_ERR_OR_NULL(fh))
return (struct dentry *)fh;
/* /*
* Make sure that the stored uuid matches the uuid of the lower * Make sure that the stored uuid matches the uuid of the lower
* layer where file handle will be decoded. * layer where file handle will be decoded.
*/ */
if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid)) if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid))
goto out; return NULL;
bytes = (fh->len - offsetof(struct ovl_fh, fid)); bytes = (fh->len - offsetof(struct ovl_fh, fid));
origin = exportfs_decode_fh(mnt, (struct fid *)fh->fid, origin = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
...@@ -164,22 +178,15 @@ static struct dentry *ovl_get_origin(struct dentry *dentry, ...@@ -164,22 +178,15 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
/* Treat stale file handle as "origin unknown" */ /* Treat stale file handle as "origin unknown" */
if (origin == ERR_PTR(-ESTALE)) if (origin == ERR_PTR(-ESTALE))
origin = NULL; origin = NULL;
goto out; return origin;
} }
if (ovl_dentry_weird(origin) || if (ovl_dentry_weird(origin)) {
((d_inode(origin)->i_mode ^ d_inode(dentry)->i_mode) & S_IFMT)) dput(origin);
goto invalid; return NULL;
}
out:
kfree(fh);
return origin; 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) 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, ...@@ -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 *lower, unsigned int numlower,
struct ovl_path **stackp, unsigned int *ctrp) struct ovl_path **stackp)
{ {
struct vfsmount *mnt; struct vfsmount *mnt;
struct dentry *origin = NULL; struct dentry *origin = NULL;
...@@ -294,18 +301,20 @@ static int ovl_check_origin(struct dentry *upperdentry, ...@@ -294,18 +301,20 @@ static int ovl_check_origin(struct dentry *upperdentry,
for (i = 0; i < numlower; i++) { for (i = 0; i < numlower; i++) {
mnt = lower[i].layer->mnt; mnt = lower[i].layer->mnt;
origin = ovl_get_origin(upperdentry, mnt); origin = ovl_decode_fh(fh, mnt);
if (IS_ERR(origin))
return PTR_ERR(origin);
if (origin) if (origin)
break; break;
} }
if (!origin) 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) if (!*stackp)
*stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL); *stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL);
if (!*stackp) { if (!*stackp) {
...@@ -313,9 +322,41 @@ static int ovl_check_origin(struct dentry *upperdentry, ...@@ -313,9 +322,41 @@ static int ovl_check_origin(struct dentry *upperdentry,
return -ENOMEM; return -ENOMEM;
} }
**stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer}; **stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer};
*ctrp = 1;
return 0; 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, ...@@ -389,7 +430,6 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
size_t len; size_t len;
struct ovl_path origin = { }; struct ovl_path origin = { };
struct ovl_path *stack = &origin; struct ovl_path *stack = &origin;
unsigned int ctr = 0;
int err; int err;
if (!d_inode(index)) if (!d_inode(index))
...@@ -420,16 +460,18 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower, ...@@ -420,16 +460,18 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower,
goto fail; goto fail;
err = -EINVAL; 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; goto fail;
err = ovl_verify_origin_fh(index, fh); err = ovl_verify_origin_fh(index, fh);
if (err) if (err)
goto fail; goto fail;
err = ovl_check_origin(index, lower, numlower, &stack, &ctr); err = ovl_check_origin_fh(fh, index, lower, numlower, &stack);
if (!err && !ctr)
err = -ESTALE;
if (err) if (err)
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment