Move all the NFS-related code into a different file. This is just
preparatory work to be able to use the LOOKUP_HANDLE file handles as the NFS
handles.
Signed-off-by: Luis Henriques <luis@igalia.com>
---
fs/fuse/Makefile | 2 +-
fs/fuse/dir.c | 1 +
fs/fuse/export.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++
fs/fuse/fuse_i.h | 6 ++
fs/fuse/inode.c | 167 +--------------------------------------------
5 files changed, 183 insertions(+), 167 deletions(-)
create mode 100644 fs/fuse/export.c
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 22ad9538dfc4..1d1401658278 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
fuse-y := trace.o # put trace.o first so we see ftrace errors sooner
fuse-y += dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o
-fuse-y += iomode.o
+fuse-y += iomode.o export.o
fuse-$(CONFIG_FUSE_DAX) += dax.o
fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o backing.o
fuse-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index a6edb444180f..a885f1dc61eb 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -190,6 +190,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
args->opcode = FUSE_LOOKUP_HANDLE;
args->out_argvar = true;
+ args->out_argvar_idx = 0;
if (dir)
fi = get_fuse_inode(dir);
diff --git a/fs/fuse/export.c b/fs/fuse/export.c
new file mode 100644
index 000000000000..4a9c95fe578e
--- /dev/null
+++ b/fs/fuse/export.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FUSE NFS export support.
+ *
+ * Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
+ */
+
+#include "fuse_i.h"
+#include <linux/exportfs.h>
+
+struct fuse_inode_handle {
+ u64 nodeid;
+ u32 generation;
+};
+
+static struct dentry *fuse_get_dentry(struct super_block *sb,
+ struct fuse_inode_handle *handle)
+{
+ struct fuse_conn *fc = get_fuse_conn_super(sb);
+ struct inode *inode;
+ struct dentry *entry;
+ int err = -ESTALE;
+
+ if (handle->nodeid == 0)
+ goto out_err;
+
+ inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid);
+ if (!inode) {
+ struct fuse_entry_out *outarg;
+ const struct qstr name = QSTR_INIT(".", 1);
+
+ if (!fc->export_support)
+ goto out_err;
+
+ outarg = fuse_entry_out_alloc(fc);
+ if (!outarg) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ err = fuse_lookup_name(sb, handle->nodeid, NULL, &name, outarg,
+ &inode);
+ kfree(outarg);
+ if (err && err != -ENOENT)
+ goto out_err;
+ if (err || !inode) {
+ err = -ESTALE;
+ goto out_err;
+ }
+ err = -EIO;
+ if (get_node_id(inode) != handle->nodeid)
+ goto out_iput;
+ }
+ err = -ESTALE;
+ if (inode->i_generation != handle->generation)
+ goto out_iput;
+
+ entry = d_obtain_alias(inode);
+ if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID)
+ fuse_invalidate_entry_cache(entry);
+
+ return entry;
+
+ out_iput:
+ iput(inode);
+ out_err:
+ return ERR_PTR(err);
+}
+
+static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
+ struct inode *parent)
+{
+ int len = parent ? 6 : 3;
+ u64 nodeid;
+ u32 generation;
+
+ if (*max_len < len) {
+ *max_len = len;
+ return FILEID_INVALID;
+ }
+
+ nodeid = get_fuse_inode(inode)->nodeid;
+ generation = inode->i_generation;
+
+ fh[0] = (u32)(nodeid >> 32);
+ fh[1] = (u32)(nodeid & 0xffffffff);
+ fh[2] = generation;
+
+ if (parent) {
+ nodeid = get_fuse_inode(parent)->nodeid;
+ generation = parent->i_generation;
+
+ fh[3] = (u32)(nodeid >> 32);
+ fh[4] = (u32)(nodeid & 0xffffffff);
+ fh[5] = generation;
+ }
+
+ *max_len = len;
+ return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
+}
+
+static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
+ struct fid *fid, int fh_len, int fh_type)
+{
+ struct fuse_inode_handle handle;
+
+ if ((fh_type != FILEID_INO64_GEN &&
+ fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
+ return NULL;
+
+ handle.nodeid = (u64) fid->raw[0] << 32;
+ handle.nodeid |= (u64) fid->raw[1];
+ handle.generation = fid->raw[2];
+ return fuse_get_dentry(sb, &handle);
+}
+
+static struct dentry *fuse_fh_to_parent(struct super_block *sb,
+ struct fid *fid, int fh_len, int fh_type)
+{
+ struct fuse_inode_handle parent;
+
+ if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
+ return NULL;
+
+ parent.nodeid = (u64) fid->raw[3] << 32;
+ parent.nodeid |= (u64) fid->raw[4];
+ parent.generation = fid->raw[5];
+ return fuse_get_dentry(sb, &parent);
+}
+
+static struct dentry *fuse_get_parent(struct dentry *child)
+{
+ struct inode *child_inode = d_inode(child);
+ struct fuse_conn *fc = get_fuse_conn(child_inode);
+ struct inode *inode;
+ struct dentry *parent;
+ struct fuse_entry_out *outarg;
+ int err;
+
+ if (!fc->export_support)
+ return ERR_PTR(-ESTALE);
+
+ outarg = fuse_entry_out_alloc(fc);
+ if (!outarg)
+ return ERR_PTR(-ENOMEM);
+
+ err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode),
+ child_inode, &dotdot_name, outarg, &inode);
+ kfree(outarg);
+ if (err) {
+ if (err == -ENOENT)
+ return ERR_PTR(-ESTALE);
+ return ERR_PTR(err);
+ }
+
+ parent = d_obtain_alias(inode);
+ if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID)
+ fuse_invalidate_entry_cache(parent);
+
+ return parent;
+}
+
+/* only for fid encoding; no support for file handle */
+const struct export_operations fuse_export_fid_operations = {
+ .encode_fh = fuse_encode_fh,
+};
+
+const struct export_operations fuse_export_operations = {
+ .fh_to_dentry = fuse_fh_to_dentry,
+ .fh_to_parent = fuse_fh_to_parent,
+ .encode_fh = fuse_encode_fh,
+ .get_parent = fuse_get_parent,
+};
+
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index d0f3c81b5612..b6afd909c857 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1164,6 +1164,8 @@ extern const struct file_operations fuse_dev_operations;
extern const struct dentry_operations fuse_dentry_operations;
+extern int fuse_inode_eq(struct inode *inode, void *_nodeidp);
+
/**
* Get a filled in inode
*/
@@ -1647,4 +1649,8 @@ extern void fuse_sysctl_unregister(void);
#define fuse_sysctl_unregister() do { } while (0)
#endif /* CONFIG_SYSCTL */
+/* export.c */
+extern const struct export_operations fuse_export_operations;
+extern const struct export_operations fuse_export_fid_operations;
+
#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index f565f7e8118d..60715d6476c9 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -23,7 +23,6 @@
#include <linux/statfs.h>
#include <linux/random.h>
#include <linux/sched.h>
-#include <linux/exportfs.h>
#include <linux/posix_acl.h>
#include <linux/pid_namespace.h>
#include <uapi/linux/magic.h>
@@ -476,7 +475,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
}
-static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
+int fuse_inode_eq(struct inode *inode, void *_nodeidp)
{
u64 nodeid = *(u64 *) _nodeidp;
if (get_node_id(inode) == nodeid)
@@ -1116,170 +1115,6 @@ static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned int mo
return fuse_iget(sb, FUSE_ROOT_ID, 0, &attr, 0, 0, 0, NULL); // XXX
}
-struct fuse_inode_handle {
- u64 nodeid;
- u32 generation;
-};
-
-static struct dentry *fuse_get_dentry(struct super_block *sb,
- struct fuse_inode_handle *handle)
-{
- struct fuse_conn *fc = get_fuse_conn_super(sb);
- struct inode *inode;
- struct dentry *entry;
- int err = -ESTALE;
-
- if (handle->nodeid == 0)
- goto out_err;
-
- inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid);
- if (!inode) {
- struct fuse_entry_out *outarg;
- const struct qstr name = QSTR_INIT(".", 1);
-
- if (!fc->export_support)
- goto out_err;
-
- outarg = fuse_entry_out_alloc(fc);
- if (!outarg) {
- err = -ENOMEM;
- goto out_err;
- }
-
- err = fuse_lookup_name(sb, handle->nodeid, NULL, &name, outarg,
- &inode);
- kfree(outarg);
- if (err && err != -ENOENT)
- goto out_err;
- if (err || !inode) {
- err = -ESTALE;
- goto out_err;
- }
- err = -EIO;
- if (get_node_id(inode) != handle->nodeid)
- goto out_iput;
- }
- err = -ESTALE;
- if (inode->i_generation != handle->generation)
- goto out_iput;
-
- entry = d_obtain_alias(inode);
- if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID)
- fuse_invalidate_entry_cache(entry);
-
- return entry;
-
- out_iput:
- iput(inode);
- out_err:
- return ERR_PTR(err);
-}
-
-static int fuse_encode_fh(struct inode *inode, u32 *fh, int *max_len,
- struct inode *parent)
-{
- int len = parent ? 6 : 3;
- u64 nodeid;
- u32 generation;
-
- if (*max_len < len) {
- *max_len = len;
- return FILEID_INVALID;
- }
-
- nodeid = get_fuse_inode(inode)->nodeid;
- generation = inode->i_generation;
-
- fh[0] = (u32)(nodeid >> 32);
- fh[1] = (u32)(nodeid & 0xffffffff);
- fh[2] = generation;
-
- if (parent) {
- nodeid = get_fuse_inode(parent)->nodeid;
- generation = parent->i_generation;
-
- fh[3] = (u32)(nodeid >> 32);
- fh[4] = (u32)(nodeid & 0xffffffff);
- fh[5] = generation;
- }
-
- *max_len = len;
- return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
-}
-
-static struct dentry *fuse_fh_to_dentry(struct super_block *sb,
- struct fid *fid, int fh_len, int fh_type)
-{
- struct fuse_inode_handle handle;
-
- if ((fh_type != FILEID_INO64_GEN &&
- fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
- return NULL;
-
- handle.nodeid = (u64) fid->raw[0] << 32;
- handle.nodeid |= (u64) fid->raw[1];
- handle.generation = fid->raw[2];
- return fuse_get_dentry(sb, &handle);
-}
-
-static struct dentry *fuse_fh_to_parent(struct super_block *sb,
- struct fid *fid, int fh_len, int fh_type)
-{
- struct fuse_inode_handle parent;
-
- if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
- return NULL;
-
- parent.nodeid = (u64) fid->raw[3] << 32;
- parent.nodeid |= (u64) fid->raw[4];
- parent.generation = fid->raw[5];
- return fuse_get_dentry(sb, &parent);
-}
-
-static struct dentry *fuse_get_parent(struct dentry *child)
-{
- struct inode *child_inode = d_inode(child);
- struct fuse_conn *fc = get_fuse_conn(child_inode);
- struct inode *inode;
- struct dentry *parent;
- struct fuse_entry_out *outarg;
- int err;
-
- if (!fc->export_support)
- return ERR_PTR(-ESTALE);
-
- outarg = fuse_entry_out_alloc(fc);
- if (!outarg)
- return ERR_PTR(-ENOMEM);
-
- err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode),
- child_inode, &dotdot_name, outarg, &inode);
- kfree(outarg);
- if (err) {
- if (err == -ENOENT)
- return ERR_PTR(-ESTALE);
- return ERR_PTR(err);
- }
-
- parent = d_obtain_alias(inode);
- if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID)
- fuse_invalidate_entry_cache(parent);
-
- return parent;
-}
-
-/* only for fid encoding; no support for file handle */
-static const struct export_operations fuse_export_fid_operations = {
- .encode_fh = fuse_encode_fh,
-};
-
-static const struct export_operations fuse_export_operations = {
- .fh_to_dentry = fuse_fh_to_dentry,
- .fh_to_parent = fuse_fh_to_parent,
- .encode_fh = fuse_encode_fh,
- .get_parent = fuse_get_parent,
-};
-
static const struct super_operations fuse_super_operations = {
.alloc_inode = fuse_alloc_inode,
.free_inode = fuse_free_inode,
On Fri, Dec 12, 2025 at 7:13 PM Luis Henriques <luis@igalia.com> wrote: > > Move all the NFS-related code into a different file. This is just > preparatory work to be able to use the LOOKUP_HANDLE file handles as the NFS > handles. > > Signed-off-by: Luis Henriques <luis@igalia.com> Very nice. Apart from minor nit below, feel free to add: Reviewed-by: Amir Goldstein <amir73il@gmail.com> > --- > fs/fuse/Makefile | 2 +- > fs/fuse/dir.c | 1 + > fs/fuse/export.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ > fs/fuse/fuse_i.h | 6 ++ > fs/fuse/inode.c | 167 +-------------------------------------------- > 5 files changed, 183 insertions(+), 167 deletions(-) > create mode 100644 fs/fuse/export.c > > diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile > index 22ad9538dfc4..1d1401658278 100644 > --- a/fs/fuse/Makefile > +++ b/fs/fuse/Makefile > @@ -12,7 +12,7 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o > > fuse-y := trace.o # put trace.o first so we see ftrace errors sooner > fuse-y += dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o > -fuse-y += iomode.o > +fuse-y += iomode.o export.o > fuse-$(CONFIG_FUSE_DAX) += dax.o > fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o backing.o > fuse-$(CONFIG_SYSCTL) += sysctl.o > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index a6edb444180f..a885f1dc61eb 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -190,6 +190,7 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, > > args->opcode = FUSE_LOOKUP_HANDLE; > args->out_argvar = true; > + args->out_argvar_idx = 0; > This change looks out of place. Keep in mind that it may take me some time to get to the rest of the patches, but this one was a low hanging review. Thanks, Amir.
© 2016 - 2025 Red Hat, Inc.