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);