Add fsinfo support to the AFS filesystem.

Signed-off-by: David Howells <[email protected]>
---

 fs/afs/internal.h           |    1 
 fs/afs/super.c              |  180 +++++++++++++++++++++++++++++++++++++++++++
 fs/fsinfo.c                 |    3 +
 include/uapi/linux/fsinfo.h |   12 +++
 samples/vfs/test-fsinfo.c   |   33 ++++++++
 5 files changed, 227 insertions(+), 2 deletions(-)

diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 2073c1a3ab4b..da40ea036c6a 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -254,6 +254,7 @@ struct afs_super_info {
        struct afs_volume       *volume;        /* volume record */
        enum afs_flock_mode     flock_mode:8;   /* File locking emulation mode 
*/
        bool                    dyn_root;       /* True if dynamic root */
+       bool                    autocell;       /* True if autocell */
 };
 
 static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
diff --git a/fs/afs/super.c b/fs/afs/super.c
index f18911e8d770..47bc49be9bd3 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -26,6 +26,7 @@
 #include <linux/sched.h>
 #include <linux/nsproxy.h>
 #include <linux/magic.h>
+#include <linux/fsinfo.h>
 #include <net/net_namespace.h>
 #include "internal.h"
 
@@ -35,6 +36,9 @@ static struct inode *afs_alloc_inode(struct super_block *sb);
 static void afs_destroy_inode(struct inode *inode);
 static void afs_free_inode(struct inode *inode);
 static int afs_statfs(struct dentry *dentry, struct kstatfs *buf);
+#ifdef CONFIG_FSINFO
+static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params);
+#endif
 static int afs_show_devname(struct seq_file *m, struct dentry *root);
 static int afs_show_options(struct seq_file *m, struct dentry *root);
 static int afs_init_fs_context(struct fs_context *fc);
@@ -54,6 +58,9 @@ int afs_net_id;
 
 static const struct super_operations afs_super_ops = {
        .statfs         = afs_statfs,
+#ifdef CONFIG_FSINFO
+       .fsinfo         = afs_fsinfo,
+#endif
        .alloc_inode    = afs_alloc_inode,
        .drop_inode     = afs_drop_inode,
        .destroy_inode  = afs_destroy_inode,
@@ -199,7 +206,7 @@ static int afs_show_options(struct seq_file *m, struct 
dentry *root)
 
        if (as->dyn_root)
                seq_puts(m, ",dyn");
-       if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
+       if (as->autocell)
                seq_puts(m, ",autocell");
        switch (as->flock_mode) {
        case afs_flock_mode_unset:      break;
@@ -463,7 +470,7 @@ static int afs_fill_super(struct super_block *sb, struct 
afs_fs_context *ctx)
        if (IS_ERR(inode))
                return PTR_ERR(inode);
 
-       if (ctx->autocell || as->dyn_root)
+       if (as->autocell || as->dyn_root)
                set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags);
 
        ret = -ENOMEM;
@@ -503,6 +510,8 @@ static struct afs_super_info *afs_alloc_sbi(struct 
fs_context *fc)
                        as->cell = afs_get_cell(ctx->cell);
                        as->volume = __afs_get_volume(ctx->volume);
                }
+               if (ctx->autocell)
+                       as->autocell = true;
        }
        return as;
 }
@@ -765,3 +774,170 @@ static int afs_statfs(struct dentry *dentry, struct 
kstatfs *buf)
 
        return ret;
 }
+
+#ifdef CONFIG_FSINFO
+static const struct fsinfo_timestamp_info afs_timestamp_info = {
+       .atime = {
+               .minimum        = 0,
+               .maximum        = UINT_MAX,
+               .gran_mantissa  = 1,
+               .gran_exponent  = 0,
+       },
+       .mtime = {
+               .minimum        = 0,
+               .maximum        = UINT_MAX,
+               .gran_mantissa  = 1,
+               .gran_exponent  = 0,
+       },
+       .ctime = {
+               .minimum        = 0,
+               .maximum        = UINT_MAX,
+               .gran_mantissa  = 1,
+               .gran_exponent  = 0,
+       },
+       .btime = {
+               .minimum        = 0,
+               .maximum        = UINT_MAX,
+               .gran_mantissa  = 1,
+               .gran_exponent  = 0,
+       },
+};
+
+/*
+ * Get filesystem information.
+ */
+static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params)
+{
+       struct fsinfo_timestamp_info *tsinfo;
+       struct fsinfo_server_address *addr;
+       struct fsinfo_capabilities *caps;
+       struct fsinfo_supports *sup;
+       struct dentry *dentry = path->dentry;
+       struct afs_server_list *slist;
+       struct afs_super_info *as = AFS_FS_S(dentry->d_sb);
+       struct afs_addr_list *alist;
+       struct afs_server *server;
+       struct afs_volume *volume = as->volume;
+       struct afs_cell *cell = as->cell;
+       struct afs_net *net = afs_d2net(dentry);
+       bool dyn_root = as->dyn_root;
+       int ret;
+
+       switch (params->request) {
+       case FSINFO_ATTR_TIMESTAMP_INFO:
+               tsinfo = params->buffer;
+               *tsinfo = afs_timestamp_info;
+               return sizeof(*tsinfo);
+
+       case FSINFO_ATTR_SUPPORTS:
+               sup = params->buffer;
+               sup->stx_mask = (STATX_TYPE | STATX_MODE |
+                                STATX_NLINK |
+                                STATX_UID | STATX_GID |
+                                STATX_MTIME | STATX_INO |
+                                STATX_SIZE);
+               sup->stx_attributes = STATX_ATTR_AUTOMOUNT;
+               return sizeof(*sup);
+
+       case FSINFO_ATTR_CAPABILITIES:
+               caps = params->buffer;
+               if (dyn_root) {
+                       fsinfo_set_cap(caps, FSINFO_CAP_IS_AUTOMOUNTER_FS);
+                       fsinfo_set_cap(caps, FSINFO_CAP_AUTOMOUNTS);
+               } else {
+                       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_VOLUME_ID);
+                       fsinfo_set_cap(caps, FSINFO_CAP_VOLUME_NAME);
+                       fsinfo_set_cap(caps, FSINFO_CAP_IVER_MONO_INCR);
+                       fsinfo_set_cap(caps, FSINFO_CAP_SYMLINKS);
+                       fsinfo_set_cap(caps, FSINFO_CAP_HARD_LINKS_1DIR);
+                       fsinfo_set_cap(caps, FSINFO_CAP_HAS_MTIME);
+               }
+               return sizeof(*caps);
+
+       case FSINFO_ATTR_VOLUME_NAME:
+               if (dyn_root)
+                       return -EOPNOTSUPP;
+               memcpy(params->buffer, volume->name, volume->name_len);
+               return volume->name_len;
+
+       case FSINFO_ATTR_CELL_NAME:
+               if (dyn_root)
+                       return -EOPNOTSUPP;
+               memcpy(params->buffer, cell->name, cell->name_len);
+               return cell->name_len;
+
+       case FSINFO_ATTR_SERVER_NAME:
+               if (dyn_root)
+                       return -EOPNOTSUPP;
+               read_lock(&volume->servers_lock);
+               slist = afs_get_serverlist(volume->servers);
+               read_unlock(&volume->servers_lock);
+
+               if (params->Nth < slist->nr_servers) {
+                       server = slist->servers[params->Nth].server;
+                       ret = sprintf(params->buffer, "%pU", &server->uuid);
+               } else {
+                       ret = -ENODATA;
+               }
+
+               afs_put_serverlist(net, slist);
+               return ret;
+
+       case FSINFO_ATTR_SERVER_ADDRESS:
+               addr = params->buffer;
+               if (dyn_root)
+                       return -EOPNOTSUPP;
+               read_lock(&volume->servers_lock);
+               slist = afs_get_serverlist(volume->servers);
+               read_unlock(&volume->servers_lock);
+
+               ret = -ENODATA;
+               if (params->Nth >= slist->nr_servers)
+                       goto put_slist;
+               server = slist->servers[params->Nth].server;
+
+               read_lock(&server->fs_lock);
+               alist = afs_get_addrlist(rcu_access_pointer(server->addresses));
+               read_unlock(&server->fs_lock);
+               if (!alist)
+                       goto put_slist;
+
+               if (params->Mth >= alist->nr_addrs)
+                       goto put_alist;
+
+               memcpy(addr, &alist->addrs[params->Mth],
+                      sizeof(struct sockaddr_rxrpc));
+               ret = sizeof(*addr);
+
+       put_alist:
+               afs_put_addrlist(alist);
+       put_slist:
+               afs_put_serverlist(net, slist);
+               return ret;
+
+       case FSINFO_ATTR_PARAMETERS:
+               fsinfo_note_sb_params(params, dentry->d_sb->s_flags);
+               if (!dyn_root)
+                       fsinfo_note_paramf(params, "source", "%c%s:%s%s",
+                                          volume->type == AFSVL_RWVOL ? '%' : 
'#',
+                                          cell->name,
+                                          volume->name,
+                                          volume->type == AFSVL_RWVOL ? "" :
+                                          volume->type == AFSVL_ROVOL ? 
".readonly" :
+                                          ".backup");
+               if (as->autocell)
+                       fsinfo_note_param(params, "autocell", NULL);
+               if (dyn_root)
+                       fsinfo_note_param(params, "dyn", NULL);
+               return params->usage;
+
+       default:
+               return generic_fsinfo(path, params);
+       }
+}
+#endif /* CONFIG_FSINFO */
diff --git a/fs/fsinfo.c b/fs/fsinfo.c
index 61f00f375fd2..c48df3028d60 100644
--- a/fs/fsinfo.c
+++ b/fs/fsinfo.c
@@ -635,6 +635,9 @@ static const struct fsinfo_attr_info 
fsinfo_buffer_info[FSINFO_ATTR__NR] = {
        FSINFO_STRING           (MOUNT_DEVNAME,         mount_devname),
        FSINFO_STRUCT_ARRAY     (MOUNT_CHILDREN,        mount_child),
        FSINFO_STRING_N         (MOUNT_SUBMOUNT,        mount_submount),
+       FSINFO_STRING_N         (SERVER_NAME,           server_name),
+       FSINFO_STRUCT_NM        (SERVER_ADDRESS,        server_address),
+       FSINFO_STRING           (CELL_NAME,             cell_name),
 };
 
 /**
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
index 88e1d004ac6c..12e94201a5d1 100644
--- a/include/uapi/linux/fsinfo.h
+++ b/include/uapi/linux/fsinfo.h
@@ -36,6 +36,9 @@ enum fsinfo_attribute {
        FSINFO_ATTR_MOUNT_DEVNAME       = 18,   /* Mount object device name 
(string) */
        FSINFO_ATTR_MOUNT_CHILDREN      = 19,   /* Submount list (array) */
        FSINFO_ATTR_MOUNT_SUBMOUNT      = 20,   /* Relative path of Nth 
submount (string) */
+       FSINFO_ATTR_SERVER_NAME         = 21,   /* Name of the Nth server 
(string) */
+       FSINFO_ATTR_SERVER_ADDRESS      = 22,   /* Mth address of the Nth 
server */
+       FSINFO_ATTR_CELL_NAME           = 23,   /* Cell name (string) */
        FSINFO_ATTR__NR
 };
 
@@ -304,4 +307,13 @@ struct fsinfo_mount_child {
        __u32           notify_counter; /* Number of notifications generated on 
mount. */
 };
 
+/*
+ * Information struct for fsinfo(fsinfo_attr_server_addresses).
+ *
+ * Find the Mth address of the Nth server for a network mount.
+ */
+struct fsinfo_server_address {
+       struct __kernel_sockaddr_storage address;
+};
+
 #endif /* _UAPI_LINUX_FSINFO_H */
diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
index ab32a15d4c5b..6d856c1c8bad 100644
--- a/samples/vfs/test-fsinfo.c
+++ b/samples/vfs/test-fsinfo.c
@@ -87,6 +87,9 @@ static const struct fsinfo_attr_info 
fsinfo_buffer_info[FSINFO_ATTR__NR] = {
        FSINFO_STRING           (MOUNT_DEVNAME,         mount_devname),
        FSINFO_STRUCT_ARRAY     (MOUNT_CHILDREN,        mount_child),
        FSINFO_STRING_N         (MOUNT_SUBMOUNT,        mount_submount),
+       FSINFO_STRING_N         (SERVER_NAME,           server_name),
+       FSINFO_STRUCT_NM        (SERVER_ADDRESS,        server_address),
+       FSINFO_STRING           (CELL_NAME,             cell_name),
 };
 
 #define FSINFO_NAME(X,Y) [FSINFO_ATTR_##X] = #Y
@@ -112,6 +115,9 @@ static const char *fsinfo_attr_names[FSINFO_ATTR__NR] = {
        FSINFO_NAME             (MOUNT_DEVNAME,         mount_devname),
        FSINFO_NAME             (MOUNT_CHILDREN,        mount_children),
        FSINFO_NAME             (MOUNT_SUBMOUNT,        mount_submount),
+       FSINFO_NAME             (SERVER_NAME,           server_name),
+       FSINFO_NAME             (SERVER_ADDRESS,        server_address),
+       FSINFO_NAME             (CELL_NAME,             cell_name),
 };
 
 union reply {
@@ -126,6 +132,7 @@ union reply {
        struct fsinfo_volume_uuid uuid;
        struct fsinfo_mount_info mount_info;
        struct fsinfo_mount_child mount_children[1];
+       struct fsinfo_server_address srv_addr;
 };
 
 static void dump_hex(unsigned int *data, int from, int to)
@@ -329,6 +336,31 @@ static void dump_attr_VOLUME_UUID(union reply *r, int size)
               f->uuid[14], f->uuid[15]);
 }
 
+static void dump_attr_SERVER_ADDRESS(union reply *r, int size)
+{
+       struct fsinfo_server_address *f = &r->srv_addr;
+       struct sockaddr_in6 *sin6;
+       struct sockaddr_in *sin;
+       char buf[1024];
+
+       switch (f->address.ss_family) {
+       case AF_INET:
+               sin = (struct sockaddr_in *)&f->address;
+               if (!inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf)))
+                       break;
+               printf("IPv4: %s\n", buf);
+               return;
+       case AF_INET6:
+               sin6 = (struct sockaddr_in6 *)&f->address;
+               if (!inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf)))
+                       break;
+               printf("IPv6: %s\n", buf);
+               return;
+       }
+
+       printf("family=%u\n", f->address.ss_family);
+}
+
 static void dump_attr_MOUNT_INFO(union reply *r, int size)
 {
        struct fsinfo_mount_info *f = &r->mount_info;
@@ -369,6 +401,7 @@ static const dumper_t fsinfo_attr_dumper[FSINFO_ATTR__NR] = 
{
        FSINFO_DUMPER(VOLUME_UUID),
        FSINFO_DUMPER(MOUNT_INFO),
        FSINFO_DUMPER(MOUNT_CHILDREN),
+       FSINFO_DUMPER(SERVER_ADDRESS),
 };
 
 static void dump_fsinfo(enum fsinfo_attribute attr,

Reply via email to