[PATCH 16/25] nfs: Support fsinfo() [ver #13]

From: David Howells
Date: Tue May 28 2019 - 11:16:48 EST


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

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

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;