Allow fsinfo() to retrieve information about a superblock, including the
values configured by the parameters passed at superblock creation.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 fs/nfs/fs_context.c |  163 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/nfs/internal.h   |    6 ++
 fs/nfs/nfs4super.c  |    3 +
 fs/nfs/super.c      |   77 ++++++++++++++++++++++++
 4 files changed, 248 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index d05271b91e38..f550b0e54833 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -17,6 +17,8 @@
 #include <linux/fs.h>
 #include <linux/fs_context.h>
 #include <linux/fs_parser.h>
+#include <linux/fsinfo.h>
+#include <linux/mount.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
@@ -1407,3 +1409,164 @@ MODULE_ALIAS_FS("nfs4");
 MODULE_ALIAS("nfs4");
 EXPORT_SYMBOL_GPL(nfs4_fs_type);
 #endif /* CONFIG_NFS_V4 */
+
+#ifdef CONFIG_FSINFO
+/*
+ * Allow the filesystem parameters to be queried.
+ */
+int nfs_fsinfo_parameters(struct fsinfo_kparams *params, struct path *path,
+                         const struct nfs_server *server)
+{
+       const struct nfs_client *client = server->nfs_client;
+       const struct sockaddr *sap = (const struct sockaddr 
*)&server->mountd_address;
+       unsigned int version = client->rpc_ops->version;
+       unsigned int sf = server->flags;
+       const char *b;
+       char *e;
+       int i;
+
+       static const struct proc_nfs_info {
+               int flag;
+               const char *str;
+               const char *nostr;
+       } nfs_info[] = {
+               { NFS_MOUNT_SOFT, "soft", "hard" },
+               { NFS_MOUNT_POSIX, "posix", "" },
+               { NFS_MOUNT_NOCTO, "nocto", "" },
+               { NFS_MOUNT_NOAC, "noac", "" },
+               { NFS_MOUNT_NONLM, "nolock", "" },
+               { NFS_MOUNT_NOACL, "noacl", "" },
+               { NFS_MOUNT_NORDIRPLUS, "nordirplus", "" },
+               { NFS_MOUNT_UNSHARED, "nosharecache", "" },
+               { NFS_MOUNT_NORESVPORT, "noresvport", "" },
+       };
+
+       rcu_read_lock();
+
+       b = params->scratch_buffer;
+       b = nfs_path(&e, path->mnt->mnt_root, params->scratch_buffer, 
params->buf_size, 0);
+       if (b < e)
+               fsinfo_note_param(params, "source", b);
+
+       if (version == 4)
+               fsinfo_note_paramf(params, "vers", "4.%u", 
client->cl_minorversion);
+       else
+               fsinfo_note_paramf(params, "vers", "%u", version);
+
+       fsinfo_note_paramf(params, "rsize", "%u", server->rsize);
+       fsinfo_note_paramf(params, "wsize", "%u", server->wsize);
+       if (server->bsize)
+               fsinfo_note_paramf(params, "bsize", "%u", server->bsize);
+       fsinfo_note_paramf(params, "namlen", "%u", server->namelen);
+
+       if (server->acregmin != NFS_DEF_ACREGMIN*HZ)
+               fsinfo_note_paramf(params, "acregmin", "%u", 
server->acregmin/HZ);
+       if (server->acregmax != NFS_DEF_ACREGMAX*HZ)
+               fsinfo_note_paramf(params, "acregmin", "%u", 
server->acregmax/HZ);
+       if (server->acdirmin != NFS_DEF_ACDIRMIN*HZ)
+               fsinfo_note_paramf(params, "acdirmin", "%u", 
server->acdirmin/HZ);
+       if (server->acdirmax != NFS_DEF_ACDIRMAX*HZ)
+               fsinfo_note_paramf(params, "acdirmin", "%u", 
server->acdirmax/HZ);
+
+       for (i = 0; i < ARRAY_SIZE(nfs_info); i++) {
+               if (sf & nfs_info[i].flag)
+                       b = nfs_info[i].str;
+               else
+                       b = nfs_info[i].nostr;
+               if (b[0])
+                       fsinfo_note_param(params, b, NULL);
+       }
+
+       fsinfo_note_param(params, "proto",
+                         rpc_peeraddr2str(server->client, RPC_DISPLAY_NETID));
+       if (version != 4 || server->port != NFS_PORT)
+               fsinfo_note_paramf(params, "port", "%u", server->port);
+
+       fsinfo_note_paramf(params, "timeo", "%lu",
+                          10U * server->client->cl_timeout->to_initval / HZ);
+       fsinfo_note_paramf(params, "retrans", "%u",
+                         server->client->cl_timeout->to_retries);
+       fsinfo_note_param(params, "sec",
+                         
nfs_pseudoflavour_to_name(server->client->cl_auth->au_flavor));
+
+       if (server->options & NFS_OPTION_FSCACHE)
+               fsinfo_note_param(params, "fsc", NULL);
+       if (server->options & NFS_OPTION_MIGRATION)
+               fsinfo_note_param(params, "migration", NULL);
+
+       if (server->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) {
+               if (server->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
+                       fsinfo_note_param(params, "lookupcache", "none");
+               else
+                       fsinfo_note_param(params, "lookupcache", "pos");
+       }
+
+       switch (server->flags & (NFS_MOUNT_LOCAL_FLOCK | 
NFS_MOUNT_LOCAL_FCNTL)) {
+       case 0:                         b = "none";     break;
+       case NFS_MOUNT_LOCAL_FLOCK:     b = "flock";    break;
+       case NFS_MOUNT_LOCAL_FCNTL:     b = "posix";    break;
+       default:                        b = "all";      break;
+       }
+       fsinfo_note_param(params, "local_lock", b);
+
+       if (version == 4)
+               fsinfo_note_param(params, "clientaddr", client->cl_ipaddr);
+
+       if (version != 4 && !(server->flags & NFS_MOUNT_LEGACY_INTERFACE)) {
+               switch (sap->sa_family) {
+               case AF_INET: {
+                       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+                       fsinfo_note_paramf(params, "mountaddr", "%pI4",
+                                          &sin->sin_addr.s_addr);
+                       break;
+               }
+               case AF_INET6: {
+                       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+                       fsinfo_note_paramf(params, "mountaddr", "%pI6c",
+                                          &sin6->sin6_addr);
+                       break;
+               }
+               }
+
+               if (server->mountd_port &&
+                   server->mountd_port != (unsigned short)NFS_UNSPEC_PORT)
+                       fsinfo_note_paramf(params, "mountport", "%u", 
server->mountd_port);
+
+               switch (sap->sa_family) {
+               case AF_INET:
+                       switch (server->mountd_protocol) {
+                       case IPPROTO_UDP:
+                               b = RPCBIND_NETID_UDP;
+                               break;
+                       case IPPROTO_TCP:
+                               b = RPCBIND_NETID_TCP;
+                               break;
+                       }
+                       break;
+               case AF_INET6:
+                       switch (server->mountd_protocol) {
+                       case IPPROTO_UDP:
+                               b = RPCBIND_NETID_UDP6;
+                               break;
+                       case IPPROTO_TCP:
+                               b = RPCBIND_NETID_TCP6;
+                               break;
+                       }
+                       break;
+               }
+
+               if (b)
+                       fsinfo_note_param(params, "mountproto", b);
+
+               if (server->mountd_version)
+                       fsinfo_note_paramf(params, "mountvers", "%u",
+                                          server->mountd_version);
+       }
+
+       fsinfo_note_param(params, "addr",
+                         rpc_peeraddr2str(server->nfs_client->cl_rpcclient, 
RPC_DISPLAY_ADDR));
+
+       rcu_read_unlock();
+       return params->usage;
+}
+#endif /* CONFIG_FSINFO */
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index da088f5611f0..c218e715881d 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -244,6 +244,10 @@ extern const struct svc_version nfs4_callback_version4;
 
 /* fs_context.c */
 extern struct file_system_type nfs_fs_type;
+#ifdef CONFIG_FSINFO
+extern int nfs_fsinfo_parameters(struct fsinfo_kparams *params, struct path 
*path,
+                                const struct nfs_server *server);
+#endif
 
 /* pagelist.c */
 extern int __init nfs_init_nfspagecache(void);
@@ -408,6 +412,7 @@ bool nfs_auth_info_match(const struct nfs_auth_info *, 
rpc_authflavor_t);
 int nfs_try_get_tree(struct fs_context *);
 int nfs_get_tree_common(struct fs_context *);
 void nfs_kill_super(struct super_block *);
+const char *nfs_pseudoflavour_to_name(rpc_authflavor_t);
 
 extern struct rpc_stat nfs_rpcstat;
 
@@ -455,6 +460,7 @@ extern void nfs_pageio_reset_read_mds(struct 
nfs_pageio_descriptor *pgio);
 /* super.c */
 void nfs_umount_begin(struct super_block *);
 int  nfs_statfs(struct dentry *, struct kstatfs *);
+int  nfs_fsinfo(struct path *, struct fsinfo_kparams *);
 int  nfs_show_options(struct seq_file *, struct dentry *);
 int  nfs_show_devname(struct seq_file *, struct dentry *);
 int  nfs_show_path(struct seq_file *, struct dentry *);
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 0240429ec596..22d8f2842ac1 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -31,6 +31,9 @@ static const struct super_operations nfs4_sops = {
        .show_devname   = nfs_show_devname,
        .show_path      = nfs_show_path,
        .show_stats     = nfs_show_stats,
+#ifdef CONFIG_FSINFO
+       .fsinfo         = nfs_fsinfo,
+#endif
 };
 
 struct nfs_subversion nfs_v4 = {
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index c455ebeeadc9..dde6c59d0210 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -54,6 +54,7 @@
 #include <linux/parser.h>
 #include <linux/nsproxy.h>
 #include <linux/rcupdate.h>
+#include <linux/fsinfo.h>
 
 #include <linux/uaccess.h>
 
@@ -81,6 +82,9 @@ const struct super_operations nfs_sops = {
        .show_devname   = nfs_show_devname,
        .show_path      = nfs_show_path,
        .show_stats     = nfs_show_stats,
+#ifdef CONFIG_FSINFO
+       .fsinfo         = nfs_fsinfo,
+#endif
 };
 EXPORT_SYMBOL_GPL(nfs_sops);
 
@@ -241,10 +245,81 @@ int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 }
 EXPORT_SYMBOL_GPL(nfs_statfs);
 
+#ifdef CONFIG_FSINFO
+/*
+ * Get filesystem information.
+ */
+int nfs_fsinfo(struct path *path, struct fsinfo_kparams *params)
+{
+       struct fsinfo_server_address *addr;
+       struct fsinfo_capabilities *caps;
+       struct nfs_server *server = NFS_SB(path->dentry->d_sb);
+       struct nfs_client *client = server->nfs_client;
+       struct rpc_clnt *clnt;
+       struct rpc_xprt *xprt;
+       const char *str;
+       unsigned int version = client->rpc_ops->version;
+
+       switch (params->request) {
+       case FSINFO_ATTR_CAPABILITIES:
+               caps = params->buffer;
+               fsinfo_set_cap(caps, FSINFO_CAP_IS_NETWORK_FS);
+               fsinfo_set_cap(caps, FSINFO_CAP_AUTOMOUNTS);
+               fsinfo_set_cap(caps, FSINFO_CAP_ADV_LOCKS);
+               fsinfo_set_cap(caps, FSINFO_CAP_UIDS);
+               fsinfo_set_cap(caps, FSINFO_CAP_GIDS);
+               fsinfo_set_cap(caps, FSINFO_CAP_O_SYNC);
+               fsinfo_set_cap(caps, FSINFO_CAP_O_DIRECT);
+               fsinfo_set_cap(caps, FSINFO_CAP_SYMLINKS);
+               fsinfo_set_cap(caps, FSINFO_CAP_HARD_LINKS);
+               fsinfo_set_cap(caps, FSINFO_CAP_DEVICE_FILES);
+               fsinfo_set_cap(caps, FSINFO_CAP_UNIX_SPECIALS);
+               fsinfo_set_cap(caps, FSINFO_CAP_HAS_ATIME);
+               fsinfo_set_cap(caps, FSINFO_CAP_HAS_CTIME);
+               fsinfo_set_cap(caps, FSINFO_CAP_HAS_MTIME);
+               if (version == 4) {
+                       fsinfo_set_cap(caps, FSINFO_CAP_LEASES);
+                       fsinfo_set_cap(caps, FSINFO_CAP_IVER_ALL_CHANGE);
+               }
+               return sizeof(*caps);
+
+       case FSINFO_ATTR_SERVER_NAME:
+               if (params->Nth || params->Mth)
+                       return -ENODATA;
+               str = client->cl_hostname;
+               goto string;
+
+       case FSINFO_ATTR_SERVER_ADDRESS:
+               if (params->Nth || params->Mth)
+                       return -ENODATA;
+               addr = params->buffer;
+               clnt = client->cl_rpcclient;
+               rcu_read_lock();
+               xprt = rcu_dereference(clnt->cl_xprt);
+               memcpy(&addr->address, &xprt->addr, xprt->addrlen);
+               rcu_read_unlock();
+               return sizeof(*addr);
+
+       case FSINFO_ATTR_PARAMETERS:
+               return nfs_fsinfo_parameters(params, path, server);
+
+       default:
+               return generic_fsinfo(path, params);
+       }
+
+string:
+       if (!str)
+               return 0;
+       strcpy(params->buffer, str);
+       return strlen(params->buffer);
+}
+EXPORT_SYMBOL_GPL(nfs_fsinfo);
+#endif /* CONFIG_FSINFO */
+
 /*
  * Map the security flavour number to a name
  */
-static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
+const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
 {
        static const struct {
                rpc_authflavor_t flavour;

Reply via email to