ovl: verify upper root dir matches lower root dir
authorAmir Goldstein <amir73il@gmail.com>
Wed, 21 Jun 2017 12:28:37 +0000 (15:28 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 4 Jul 2017 20:03:17 +0000 (22:03 +0200)
When inodes index feature is enabled, verify that the file handle stored
in upper root dir matches the lower root dir or fail to mount.

If upper root dir has no stored file handle, encode and store the lower
root dir file handle in overlay.origin xattr.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/copy_up.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c

index f9f51cce3c18176ea118ec834899e84dce0c7cfd..42807cb57da07056966afa802801818548b4c857 100644 (file)
@@ -233,7 +233,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
        return err;
 }
 
-static struct ovl_fh *ovl_encode_fh(struct dentry *lower)
+struct ovl_fh *ovl_encode_fh(struct dentry *lower)
 {
        struct ovl_fh *fh;
        int fh_type, fh_len, dwords;
index 6485beddaa1fb99124d5ab12722ce7c8ecc5b746..197b53d34861c376168e9aaf1b58b09b05ff764c 100644 (file)
@@ -88,13 +88,10 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
        return 1;
 }
 
-static struct dentry *ovl_get_origin(struct dentry *dentry,
-                                    struct vfsmount *mnt)
+static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 {
        int res;
        struct ovl_fh *fh = NULL;
-       struct dentry *origin = NULL;
-       int bytes;
 
        res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0);
        if (res < 0) {
@@ -106,7 +103,7 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
        if (res == 0)
                return NULL;
 
-       fh  = kzalloc(res, GFP_TEMPORARY);
+       fh = kzalloc(res, GFP_TEMPORARY);
        if (!fh)
                return ERR_PTR(-ENOMEM);
 
@@ -129,7 +126,29 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
            (fh->flags & OVL_FH_FLAG_BIG_ENDIAN) != OVL_FH_FLAG_CPU_ENDIAN)
                goto out;
 
-       bytes = (fh->len - offsetof(struct ovl_fh, fid));
+       return fh;
+
+out:
+       kfree(fh);
+       return NULL;
+
+fail:
+       pr_warn_ratelimited("overlayfs: failed to get origin (%i)\n", res);
+       goto out;
+invalid:
+       pr_warn_ratelimited("overlayfs: invalid origin (%*phN)\n", res, fh);
+       goto out;
+}
+
+static struct dentry *ovl_get_origin(struct dentry *dentry,
+                                    struct vfsmount *mnt)
+{
+       struct dentry *origin = NULL;
+       struct ovl_fh *fh = ovl_get_origin_fh(dentry);
+       int bytes;
+
+       if (IS_ERR_OR_NULL(fh))
+               return (struct dentry *)fh;
 
        /*
         * Make sure that the stored uuid matches the uuid of the lower
@@ -138,6 +157,7 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
        if (!uuid_equal(&fh->uuid, &mnt->mnt_sb->s_uuid))
                goto out;
 
+       bytes = (fh->len - offsetof(struct ovl_fh, fid));
        origin = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
                                    bytes >> 2, (int)fh->type,
                                    ovl_acceptable, NULL);
@@ -149,21 +169,17 @@ static struct dentry *ovl_get_origin(struct dentry *dentry,
        }
 
        if (ovl_dentry_weird(origin) ||
-           ((d_inode(origin)->i_mode ^ d_inode(dentry)->i_mode) & S_IFMT)) {
-               dput(origin);
-               origin = NULL;
+           ((d_inode(origin)->i_mode ^ d_inode(dentry)->i_mode) & S_IFMT))
                goto invalid;
-       }
 
 out:
        kfree(fh);
        return origin;
 
-fail:
-       pr_warn_ratelimited("overlayfs: failed to get origin (%i)\n", res);
-       goto out;
 invalid:
-       pr_warn_ratelimited("overlayfs: invalid origin (%*phN)\n", res, fh);
+       pr_warn_ratelimited("overlayfs: invalid origin (%pd2)\n", origin);
+       dput(origin);
+       origin = NULL;
        goto out;
 }
 
@@ -303,6 +319,65 @@ static int ovl_check_origin(struct dentry *dentry, struct dentry *upperdentry,
        return 0;
 }
 
+/*
+ * Verify that @fh matches the origin file handle stored in OVL_XATTR_ORIGIN.
+ * Return 0 on match, -ESTALE on mismatch, < 0 on error.
+ */
+static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh)
+{
+       struct ovl_fh *ofh = ovl_get_origin_fh(dentry);
+       int err = 0;
+
+       if (!ofh)
+               return -ENODATA;
+
+       if (IS_ERR(ofh))
+               return PTR_ERR(ofh);
+
+       if (fh->len != ofh->len || memcmp(fh, ofh, fh->len))
+               err = -ESTALE;
+
+       kfree(ofh);
+       return err;
+}
+
+/*
+ * Verify that an inode matches the origin file handle stored in upper inode.
+ *
+ * If @set is true and there is no stored file handle, encode and store origin
+ * file handle in OVL_XATTR_ORIGIN.
+ *
+ * Return 0 on match, -ESTALE on mismatch, < 0 on error.
+ */
+int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
+                     struct dentry *origin, bool set)
+{
+       struct inode *inode;
+       struct ovl_fh *fh;
+       int err;
+
+       fh = ovl_encode_fh(origin);
+       err = PTR_ERR(fh);
+       if (IS_ERR(fh))
+               goto fail;
+
+       err = ovl_verify_origin_fh(dentry, fh);
+       if (set && err == -ENODATA)
+               err = ovl_do_setxattr(dentry, OVL_XATTR_ORIGIN, fh, fh->len, 0);
+       if (err)
+               goto fail;
+
+out:
+       kfree(fh);
+       return err;
+
+fail:
+       inode = d_inode(origin);
+       pr_warn_ratelimited("overlayfs: failed to verify origin (%pd2, ino=%lu, err=%i)\n",
+                           origin, inode ? inode->i_ino : 0, err);
+       goto out;
+}
+
 /*
  * Returns next layer in stack starting from top.
  * Returns -1 if this is the last layer.
index 4e7a74e99d3cb70065fae7b5416178da674df00c..38ac84cba6ea72e6ddc856e497dba9b76b316a9d 100644 (file)
@@ -232,6 +232,8 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
 
 
 /* namei.c */
+int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt,
+                     struct dentry *origin, bool set);
 int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
 struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
 bool ovl_lower_positive(struct dentry *dentry);
@@ -290,3 +292,4 @@ int ovl_copy_up(struct dentry *dentry);
 int ovl_copy_up_flags(struct dentry *dentry, int flags);
 int ovl_copy_xattr(struct dentry *old, struct dentry *new);
 int ovl_set_attr(struct dentry *upper, struct kstat *stat);
+struct ovl_fh *ovl_encode_fh(struct dentry *lower);
index fa83b3245124cee633575cc333585c23506adf45..bfdcff0f31689892582b612d319d57d993442d47 100644 (file)
@@ -1048,6 +1048,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                ufs->same_sb = NULL;
 
        if (!(ovl_force_readonly(ufs)) && ufs->config.index) {
+               /* Verify lower root is upper root origin */
+               err = ovl_verify_origin(upperpath.dentry, ufs->lower_mnt[0],
+                                       stack[0].dentry, true);
+               if (err) {
+                       pr_err("overlayfs: failed to verify upper root origin\n");
+                       goto out_put_lower_mnt;
+               }
+
                ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
                                                   OVL_INDEXDIR_NAME, true);
                err = PTR_ERR(ufs->indexdir);