summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/export.c97
-rw-r--r--fs/overlayfs/namei.c4
-rw-r--r--fs/overlayfs/overlayfs.h2
3 files changed, 101 insertions, 2 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
index 67b907ca9cdc..a7d57bf9c9d8 100644
--- a/fs/overlayfs/export.c
+++ b/fs/overlayfs/export.c
@@ -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,
};
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index ca15893cfaa9..a35c5eaa2c01 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -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;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index f2baa2ccaacd..401113a2e9c7 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -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);