ovl: decode pure upper file handles
authorAmir Goldstein <amir73il@gmail.com>
Thu, 18 Jan 2018 23:03:23 +0000 (01:03 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 24 Jan 2018 10:26:00 +0000 (11:26 +0100)
Decoding an upper file handle is done by decoding the upper dentry from
underlying upper fs, finding or allocating an overlay inode that is
hashed by the real upper inode and instantiating an overlay dentry with
that inode.

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

index 67b907ca9cdc232629d1275549089e800517d5c4..a7d57bf9c9d84e483fe8acc4ddc0e6d19563c3d1 100644 (file)
@@ -93,6 +93,103 @@ static int ovl_encode_inode_fh(struct inode *inode, u32 *fid, int *max_len,
        return type;
 }
 
+/*
+ * Find or instantiate an overlay dentry from real dentries.
+ */
+static struct dentry *ovl_obtain_alias(struct super_block *sb,
+                                      struct dentry *upper,
+                                      struct ovl_path *lowerpath)
+{
+       struct inode *inode;
+       struct dentry *dentry;
+       struct ovl_entry *oe;
+       void *fsdata = &oe;
+
+       /* TODO: obtain non pure-upper */
+       if (lowerpath)
+               return ERR_PTR(-EIO);
+
+       inode = ovl_get_inode(sb, dget(upper), NULL, NULL, 0);
+       if (IS_ERR(inode)) {
+               dput(upper);
+               return ERR_CAST(inode);
+       }
+
+       dentry = d_find_any_alias(inode);
+       if (!dentry) {
+               dentry = d_alloc_anon(inode->i_sb);
+               if (!dentry)
+                       goto nomem;
+               oe = ovl_alloc_entry(0);
+               if (!oe)
+                       goto nomem;
+
+               dentry->d_fsdata = oe;
+               ovl_dentry_set_upper_alias(dentry);
+       }
+
+       return d_instantiate_anon(dentry, inode);
+
+nomem:
+       iput(inode);
+       dput(dentry);
+       return ERR_PTR(-ENOMEM);
+}
+
+static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
+                                       struct ovl_fh *fh)
+{
+       struct ovl_fs *ofs = sb->s_fs_info;
+       struct dentry *dentry;
+       struct dentry *upper;
+
+       if (!ofs->upper_mnt)
+               return ERR_PTR(-EACCES);
+
+       upper = ovl_decode_fh(fh, ofs->upper_mnt);
+       if (IS_ERR_OR_NULL(upper))
+               return upper;
+
+       dentry = ovl_obtain_alias(sb, upper, NULL);
+       dput(upper);
+
+       return dentry;
+}
+
+static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
+                                      int fh_len, int fh_type)
+{
+       struct dentry *dentry = NULL;
+       struct ovl_fh *fh = (struct ovl_fh *) fid;
+       int len = fh_len << 2;
+       unsigned int flags = 0;
+       int err;
+
+       err = -EINVAL;
+       if (fh_type != OVL_FILEID)
+               goto out_err;
+
+       err = ovl_check_fh_len(fh, len);
+       if (err)
+               goto out_err;
+
+       /* TODO: decode non-upper */
+       flags = fh->flags;
+       if (flags & OVL_FH_FLAG_PATH_UPPER)
+               dentry = ovl_upper_fh_to_d(sb, fh);
+       err = PTR_ERR(dentry);
+       if (IS_ERR(dentry) && err != -ESTALE)
+               goto out_err;
+
+       return dentry;
+
+out_err:
+       pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n",
+                           len, fh_type, flags, err);
+       return ERR_PTR(err);
+}
+
 const struct export_operations ovl_export_operations = {
        .encode_fh      = ovl_encode_inode_fh,
+       .fh_to_dentry   = ovl_fh_to_dentry,
 };
index ca15893cfaa95abe1a7f298edcd1908183387e0e..a35c5eaa2c016c76767a3a0fbe7d812d563450d1 100644 (file)
@@ -107,7 +107,7 @@ static int ovl_acceptable(void *ctx, struct dentry *dentry)
  * 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)
+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;
@@ -171,7 +171,7 @@ invalid:
        goto out;
 }
 
-static struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
 {
        struct dentry *real;
        int bytes;
index f2baa2ccaacd96e167878a75425bf8bb47fff57e..401113a2e9c76d06af80fa8519374b99361c7d52 100644 (file)
@@ -264,6 +264,8 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
 
 
 /* namei.c */
+int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt);
 int ovl_verify_set_fh(struct dentry *dentry, const char *name,
                      struct dentry *real, bool is_upper, bool set);
 int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index);