Re: [PATCH 08/14] ovl: encode/decode struct ovl_fh format file handles

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Oct 17, 2017 at 7:44 PM, Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> Encode an overlay inode as struct ovl_fh encoding of the real inode.
> Pure upper file handles are explicitly marked with a flag in ovl_fh
> header.
>
> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
> ---
>  fs/overlayfs/copy_up.c   | 18 +++++-----
>  fs/overlayfs/export.c    | 86 +++++++++++++++++++++++++++++++++---------------
>  fs/overlayfs/namei.c     | 12 ++++---
>  fs/overlayfs/overlayfs.h | 10 +++++-
>  4 files changed, 85 insertions(+), 41 deletions(-)
>
> diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
> index 0a2c2c3743b1..9af3a6b62038 100644
> --- a/fs/overlayfs/copy_up.c
> +++ b/fs/overlayfs/copy_up.c
> @@ -233,25 +233,21 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
>         return err;
>  }
>
> -struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper)
> +struct ovl_fh *ovl_encode_fh(struct dentry *dentry, bool is_upper,
> +                            bool connectable)
>  {
>         struct ovl_fh *fh;
>         int fh_type, fh_len, dwords;
>         void *buf;
>         int buflen = MAX_HANDLE_SZ;
> -       uuid_t *uuid = &lower->d_sb->s_uuid;
> +       uuid_t *uuid = &dentry->d_sb->s_uuid;
>
>         buf = kmalloc(buflen, GFP_KERNEL);
>         if (!buf)
>                 return ERR_PTR(-ENOMEM);
>
> -       /*
> -        * We encode a non-connectable file handle for non-dir, because we
> -        * only need to find the lower inode number and we don't want to pay
> -        * the price or reconnecting the dentry.
> -        */
>         dwords = buflen >> 2;
> -       fh_type = exportfs_encode_fh(lower, buf, &dwords, 0);
> +       fh_type = exportfs_encode_fh(dentry, buf, &dwords, connectable);
>         buflen = (dwords << 2);
>
>         fh = ERR_PTR(-EIO);
> @@ -296,12 +292,16 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *origin,
>         int err;
>
>         /*
> +        * We encode a non-connectable file handle for non-dir, because we
> +        * only need to find the lower inode number and we don't want to pay
> +        * the price of reconnecting the dentry on lookup.
> +        *
>          * When lower layer doesn't support export operations store a 'null' fh,
>          * so we can use the overlay.origin xattr to distignuish between a copy
>          * up and a pure upper inode.
>          */
>         if (ovl_can_decode_fh(origin->d_sb)) {
> -               fh = ovl_encode_fh(origin, is_upper);
> +               fh = ovl_encode_fh(origin, is_upper, false);
>                 if (IS_ERR(fh))
>                         return PTR_ERR(fh);
>         }
> diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
> index 9ec2fe659e38..47ef0302dbd3 100644
> --- a/fs/overlayfs/export.c
> +++ b/fs/overlayfs/export.c
> @@ -41,7 +41,8 @@ static bool ovl_is_pure_upper_or_root(struct dentry *dentry, int connectable)
>  static int ovl_dentry_to_fh(struct dentry *dentry, struct fid *fid,
>                             int *max_len, int connectable)
>  {
> -       int type;
> +       const struct ovl_fh *fh;
> +       int len = *max_len << 2;
>
>         /*
>          * Overlay root dir inode is hashed and encoded as pure upper, because
> @@ -49,20 +50,27 @@ static int ovl_dentry_to_fh(struct dentry *dentry, struct fid *fid,
>          * that root dir is not indexed, because root dentry is pinned to cache.
>          *
>          * TODO: handle encoding of non pure upper.
> +        *       Parent and child may not be on the same layer, so encode
> +        *       connectable file handle as an array of self ovl_fh and
> +        *       parent ovl_fh (type OVL_FILEID_WITH_PARENT).
>          */
>         if (!ovl_is_pure_upper_or_root(dentry, connectable))
>                 return FILEID_INVALID;
>
> -       /*
> -        * Ask real fs to encode the inode of the real upper dentry.
> -        * When decoding we ask real fs for the upper dentry and use
> -        * the real inode to get the overlay inode.
> -        */
> -       type = exportfs_encode_fh(ovl_dentry_upper(dentry), fid, max_len,
> -                                 connectable);
> +       fh = ovl_encode_fh(ovl_dentry_upper(dentry), true, connectable);
> +       if (IS_ERR(fh))
> +               return FILEID_INVALID;
>
> -       /* TODO: encode an ovl_fh struct and return OVL file handle type */
> -       return type;
> +       if (fh->len > len) {
> +               kfree(fh);
> +               return FILEID_INVALID;
> +       }
> +
> +       memcpy((char *)fid, (char *)fh, len);
> +       *max_len = len >> 2;

FYI, there are 2 bugs cancelling each other here.
memcpy should copy fh->len
and max_len should be round up to dwords from fh->len

I pushed a fix patch to branch ovl-nfs-export-v1

> +       kfree(fh);
> +
> +       return OVL_FILEID_WITHOUT_PARENT;
>  }
>
>  /* Find an alias of inode. If @dir is non NULL, find a child alias */
> @@ -171,24 +179,44 @@ static struct dentry *ovl_fh_to_d(struct super_block *sb, struct fid *fid,
>  {
>         struct ovl_fs *ofs = sb->s_fs_info;
>         struct vfsmount *mnt = ofs->upper_mnt;
> -       const struct export_operations *real_op;
> -       struct dentry *(*fh_to_d)(struct super_block *, struct fid *, int, int);
>         struct dentry *upper;
> +       struct ovl_fh *fh = (struct ovl_fh *) fid;
> +       int err;
>
> -       /* TODO: handle decoding of non pure upper */
> -       if (!mnt || !mnt->mnt_sb->s_export_op)
> -               return NULL;
> +       /* TODO: handle file handle with parent from different layer */
> +       if (fh_type != OVL_FILEID_WITHOUT_PARENT)
> +               return ERR_PTR(-EINVAL);
> +
> +       err = ovl_check_fh_len(fh, fh_len << 2);
> +       if (err)
> +               return ERR_PTR(err);
>
> -       real_op = mnt->mnt_sb->s_export_op;
> -       fh_to_d = to_parent ? real_op->fh_to_parent : real_op->fh_to_dentry;
> -       if (!fh_to_d)
> +       /* TODO: handle decoding of non pure upper */
> +       if (!mnt || !(fh->flags & OVL_FH_FLAG_PATH_UPPER))
>                 return NULL;
>
> -       /* TODO: decode ovl_fh format file handle */
> -       upper = fh_to_d(mnt->mnt_sb, fid, fh_len, fh_type);
> +       upper = ovl_decode_fh(fh, mnt);
>         if (IS_ERR_OR_NULL(upper))
>                 return upper;
>
> +       /*
> +        * ovl_decode_fh() will return a connected dentry if the encoded real
> +        * file handle was connectable (the case of pure upper ancestry).
> +        * fh_to_parent() needs to instantiate an overlay dentry from real
> +        * upper parent in that case.
> +        */
> +       if (to_parent) {
> +               struct dentry *parent;
> +
> +               if (upper->d_flags & DCACHE_DISCONNECTED) {
> +                       dput(upper);
> +                       return NULL;
> +               }
> +               parent = dget_parent(upper);
> +               dput(upper);
> +               upper = parent;
> +       }
> +
>         /* Find or instantiate a pure upper dentry */
>         return ovl_obtain_alias(sb, upper, NULL);
>  }
> @@ -207,21 +235,25 @@ static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid,
>
>  static struct dentry *ovl_get_parent(struct dentry *dentry)
>  {
> -       const struct export_operations *real_op;
>         struct dentry *upper;
>
>         /* TODO: handle connecting of non pure upper */
>         if (ovl_dentry_lower(dentry))
>                 return ERR_PTR(-EACCES);
>
> +       /*
> +        * When ovl_fh_to_d() returns an overlay dentry, its real upper
> +        * dentry should be positive and connected. The reconnecting of
> +        * the upper dentry is done by ovl_decode_fh() when decoding the
> +        * real upper file handle, so here we have the upper dentry parent
> +        * and we need to instantiate an overlay dentry with upper dentry
> +        * parent.
> +        */
>         upper = ovl_dentry_upper(dentry);
> -       real_op = upper->d_sb->s_export_op;
> -       if (!real_op || !real_op->get_parent)
> -               return ERR_PTR(-EACCES);
> +       if (!upper || (upper->d_flags & DCACHE_DISCONNECTED))
> +               return ERR_PTR(-ESTALE);
>
> -       upper = real_op->get_parent(upper);
> -       if (IS_ERR(upper))
> -               return upper;
> +       upper = dget_parent(upper);
>
>         /* Find or instantiate a pure upper dentry */
>         return ovl_obtain_alias(dentry->d_sb, upper, NULL);
> diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
> index d5313fb02e73..4f20af22bd0b 100644
> --- a/fs/overlayfs/namei.c
> +++ b/fs/overlayfs/namei.c
> @@ -108,7 +108,7 @@ static int ovl_acceptable(void *mnt, 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;
> @@ -172,7 +172,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
>         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 *origin;
>         int bytes;
> @@ -412,7 +412,7 @@ int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
>         struct ovl_fh *fh;
>         int err;
>
> -       fh = ovl_encode_fh(origin, is_upper);
> +       fh = ovl_encode_fh(origin, is_upper, false);
>         err = PTR_ERR(fh);
>         if (IS_ERR(fh))
>                 goto fail;
> @@ -574,7 +574,11 @@ int ovl_get_index_name(struct dentry *origin, struct qstr *name)
>         struct ovl_fh *fh;
>         char *n, *s;
>
> -       fh = ovl_encode_fh(origin, false);
> +       /*
> +        * We encode a non-connectable file handle for index, because the index
> +        * must be unqiue and invariant of lower hardlink aliases.
> +        */
> +       fh = ovl_encode_fh(origin, false, false);
>         if (IS_ERR(fh))
>                 return PTR_ERR(fh);
>
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index 66a6447a0c2a..75a8b10d4e10 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -72,6 +72,11 @@ enum ovl_index {
>  #error Endianness not defined
>  #endif
>
> +/* The type returned by overlay encode_fh when encoding an ovl_fh handle */
> +#define OVL_FILEID_WITHOUT_PARENT      0xf1
> +/* The type returned by overlay encode_fh when encoding two ovl_fh handles */
> +#define OVL_FILEID_WITH_PARENT         0xf2
> +
>  /* On-disk and in-memeory format for redirect by file handle */
>  struct ovl_fh {
>         u8 version;     /* 0 */
> @@ -266,6 +271,8 @@ static inline bool ovl_is_opaquedir(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_origin(struct dentry *dentry, struct dentry *origin,
>                       bool is_upper, bool set);
>  int ovl_verify_index(struct dentry *index, struct vfsmount *mnt,
> @@ -341,7 +348,8 @@ 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, bool is_upper);
> +struct ovl_fh *ovl_encode_fh(struct dentry *dentry, bool is_upper,
> +                            bool connectable);
>
>  /* export.c */
>  extern const struct export_operations ovl_export_operations;
> --
> 2.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystems Devel]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux