kernfs: implement custom exportfs ops and fid type
authorTejun Heo <tj@kernel.org>
Mon, 4 Nov 2019 23:54:30 +0000 (15:54 -0800)
committerTejun Heo <tj@kernel.org>
Tue, 12 Nov 2019 16:18:04 +0000 (08:18 -0800)
The current kernfs exportfs implementation uses the generic_fh_*()
helpers and FILEID_INO32_GEN[_PARENT] which limits ino to 32bits.
Let's implement custom exportfs operations and fid type to remove the
restriction.

* FILEID_KERNFS is a single u64 value whose content is
  kernfs_node->id.  This is the only native fid type.

* For backward compatibility with blk_log_action() path which exposes
  (ino,gen) pairs which userland assembles into FILEID_INO32_GEN keys,
  combine the generic keys into 64bit IDs in the same order.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Namhyung Kim <namhyung@kernel.org>
fs/kernfs/mount.c
include/linux/exportfs.h

index 8aed2cccd002d6042f3b183266a4a95b31780bee..37a1e5df117a28c29b07bad27e77a4a1f5bf966e 100644 (file)
@@ -53,40 +53,84 @@ const struct super_operations kernfs_sops = {
        .show_path      = kernfs_sop_show_path,
 };
 
-static struct inode *kernfs_fh_get_inode(struct super_block *sb,
-               u64 ino, u32 generation)
+static int kernfs_encode_fh(struct inode *inode, __u32 *fh, int *max_len,
+                           struct inode *parent)
+{
+       struct kernfs_node *kn = inode->i_private;
+
+       if (*max_len < 2) {
+               *max_len = 2;
+               return FILEID_INVALID;
+       }
+
+       *max_len = 2;
+       *(u64 *)fh = kn->id;
+       return FILEID_KERNFS;
+}
+
+static struct dentry *__kernfs_fh_to_dentry(struct super_block *sb,
+                                           struct fid *fid, int fh_len,
+                                           int fh_type, bool get_parent)
 {
        struct kernfs_super_info *info = kernfs_info(sb);
-       struct inode *inode;
        struct kernfs_node *kn;
+       struct inode *inode;
+       u64 id;
 
-       if (ino == 0)
-               return ERR_PTR(-ESTALE);
+       if (fh_len < 2)
+               return NULL;
+
+       switch (fh_type) {
+       case FILEID_KERNFS:
+               id = *(u64 *)fid;
+               break;
+       case FILEID_INO32_GEN:
+       case FILEID_INO32_GEN_PARENT:
+               /*
+                * blk_log_action() exposes (ino,gen) pair without type and
+                * userland can call us with generic fid constructed from
+                * them.  Combine it back to ID.  See blk_log_action().
+                */
+               id = ((u64)fid->i32.gen << 32) | fid->i32.ino;
+               break;
+       default:
+               return NULL;
+       }
 
-       kn = kernfs_find_and_get_node_by_id(info->root,
-                                           ino | ((u64)generation << 32));
+       kn = kernfs_find_and_get_node_by_id(info->root, id);
        if (!kn)
                return ERR_PTR(-ESTALE);
+
+       if (get_parent) {
+               struct kernfs_node *parent;
+
+               parent = kernfs_get_parent(kn);
+               kernfs_put(kn);
+               kn = parent;
+               if (!kn)
+                       return ERR_PTR(-ESTALE);
+       }
+
        inode = kernfs_get_inode(sb, kn);
        kernfs_put(kn);
        if (!inode)
                return ERR_PTR(-ESTALE);
 
-       return inode;
+       return d_obtain_alias(inode);
 }
 
-static struct dentry *kernfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
-               int fh_len, int fh_type)
+static struct dentry *kernfs_fh_to_dentry(struct super_block *sb,
+                                         struct fid *fid, int fh_len,
+                                         int fh_type)
 {
-       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
-                                   kernfs_fh_get_inode);
+       return __kernfs_fh_to_dentry(sb, fid, fh_len, fh_type, false);
 }
 
-static struct dentry *kernfs_fh_to_parent(struct super_block *sb, struct fid *fid,
-               int fh_len, int fh_type)
+static struct dentry *kernfs_fh_to_parent(struct super_block *sb,
+                                         struct fid *fid, int fh_len,
+                                         int fh_type)
 {
-       return generic_fh_to_parent(sb, fid, fh_len, fh_type,
-                                   kernfs_fh_get_inode);
+       return __kernfs_fh_to_dentry(sb, fid, fh_len, fh_type, true);
 }
 
 static struct dentry *kernfs_get_parent_dentry(struct dentry *child)
@@ -97,6 +141,7 @@ static struct dentry *kernfs_get_parent_dentry(struct dentry *child)
 }
 
 static const struct export_operations kernfs_export_ops = {
+       .encode_fh      = kernfs_encode_fh,
        .fh_to_dentry   = kernfs_fh_to_dentry,
        .fh_to_parent   = kernfs_fh_to_parent,
        .get_parent     = kernfs_get_parent_dentry,
index cf6571fc9c014a5b9c4cd9f39e0264d0064516ca..d896b8657085f022b70566a191f5be219eca389f 100644 (file)
@@ -104,6 +104,11 @@ enum fid_type {
         */
        FILEID_LUSTRE = 0x97,
 
+       /*
+        * 64 bit unique kernfs id
+        */
+       FILEID_KERNFS = 0xfe,
+
        /*
         * Filesystems must not use 0xff file ID.
         */