]> www.infradead.org Git - users/willy/linux.git/commitdiff
vfs: Implement parameter value retrieval with fsinfo()
authorDavid Howells <dhowells@redhat.com>
Mon, 29 Oct 2018 12:53:13 +0000 (12:53 +0000)
committerDavid Howells <dhowells@redhat.com>
Mon, 29 Oct 2018 12:53:13 +0000 (12:53 +0000)
Implement parameter value retrieval with fsinfo() - akin to parsing
/proc/mounts.

This allows the values of each parameter to be retrieved in an order
corresponding to the Nth index used by FSINFO_ATTR_PARAM_SPECIFICATION.  If
a parameter isn't set, an empty string is returned.  Parameters may have
multiple values, which can be accessed using the Mth index.  For example,
set:

struct fsinfo_params params = {
.request = FSINFO_ATTR_PARAM_SPECIFICATION,
.Nth = 3,
};

this can be passed to fsinfo() to retrieve the type information for the
parameter #3.  Value #2 of parameter #3 can then be retrieved using:

struct fsinfo_params params = {
.request = FSINFO_ATTR_PARAM_SPECIFICATION,
.Nth = 3,
.Mth = 2,
};

fsinfo() returned -ENODATA if Nth is beyond the last parameter or Mth is
beyond the last value of that parameter.

Note that it is permissible for the filesystem to add extra values on the
end beyond the number of specs returned by FSINFO_ATTR_PARAM_SPECIFICATION.
This is used by cgroup-v1 to list the supported subsystem names after the
standard parameters.

Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/internal.h
fs/afs/super.c
fs/hugetlbfs/inode.c
fs/kernfs/mount.c
include/linux/kernfs.h
include/uapi/linux/fsinfo.h
kernel/cgroup/cgroup-v1.c
kernel/cgroup/cgroup.c
samples/vfs/test-fs-query.c

index 7a603398b69e673ce29b74c8f672632623bbdcc1..2ecfa32f26ac98b8652d9191e367a20918bba64e 100644 (file)
@@ -196,6 +196,7 @@ struct afs_super_info {
        struct afs_cell         *cell;          /* The cell in which the volume resides */
        struct afs_volume       *volume;        /* volume record */
        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)
index 1fe5026b1104afd08b7bdc00424dc5aee37e8b4d..14abf604e8d3c603716acd731d46c4fdd18b3309 100644 (file)
@@ -197,7 +197,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");
        return 0;
 }
@@ -444,7 +444,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;
@@ -483,6 +483,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;
 }
@@ -790,6 +792,7 @@ static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params)
        struct afs_volume *volume = as->volume;
        struct afs_cell *cell = as->cell;
        struct afs_net *net = afs_d2net(dentry);
+       const char *str = NULL;
        bool dyn_root = as->dyn_root;
        int ret;
 
@@ -894,7 +897,39 @@ static int afs_fsinfo(struct path *path, struct fsinfo_kparams *params)
                afs_put_serverlist(net, slist);
                return ret;
 
+       case FSINFO_ATTR_PARAMETER:
+               if (params->Mth)
+                       return -ENODATA;
+               switch (params->Nth) {
+               case Opt_source:
+                       if (dyn_root)
+                               return 0;
+                       return sprintf(params->buffer, "source=%c%s:%s%s",
+                                      volume->type == AFSVL_RWVOL ? '%' : '#',
+                                      cell->name,
+                                      volume->name,
+                                      volume->type == AFSVL_RWVOL ? "" :
+                                      volume->type == AFSVL_ROVOL ? ".readonly" :
+                                      ".backup");
+               case Opt_autocell:
+                       if (as->autocell)
+                               str = "autocell";
+                       goto string;
+               case Opt_dyn:
+                       if (dyn_root)
+                               str = "dyn";
+                       goto string;
+               default:
+                       return -ENODATA;
+               }
+
        default:
                return generic_fsinfo(path, params);
        }
+
+string:
+       if (!str)
+               return 0;
+       strcpy(params->buffer, str);
+       return strlen(params->buffer);
 }
index 700b009af8e4f481d812f8bb904fad0ee2b9b52a..762028994f4778ff48698f9f6c6a70e56b2dce0b 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/hugetlb.h>
 #include <linux/pagevec.h>
 #include <linux/fs_parser.h>
+#include <linux/fsinfo.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
 #include <linux/dnotify.h>
@@ -947,6 +948,70 @@ static int hugetlbfs_show_options(struct seq_file *m, struct dentry *root)
        return 0;
 }
 
+static int hugetlbfs_fsinfo(struct path *path, struct fsinfo_kparams *params)
+{
+       struct dentry *dentry = path->dentry;
+       struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb);
+       struct hugepage_subpool *spool = sbinfo->spool;
+       unsigned long hpage_size = huge_page_size(sbinfo->hstate);
+       unsigned hpage_shift = huge_page_shift(sbinfo->hstate);
+       char mod;
+
+       switch (params->request) {
+       case FSINFO_ATTR_PARAMETER:
+               if (params->Mth)
+                       return -ENODATA;
+               switch (params->Nth) {
+               case Opt_uid:
+                       if (!uid_eq(sbinfo->uid, GLOBAL_ROOT_UID))
+                               return sprintf(params->buffer, "uid=%u",
+                                              from_kuid_munged(&init_user_ns,
+                                                               sbinfo->uid));
+                       return 0;
+               case Opt_gid:
+                       if (!gid_eq(sbinfo->gid, GLOBAL_ROOT_GID))
+                               return sprintf(params->buffer, "gid=%u",
+                                              from_kgid_munged(&init_user_ns,
+                                                               sbinfo->gid));
+                       return 0;
+
+               case Opt_size:
+                       if (!spool || spool->max_hpages == -1)
+                               return 0;
+                       return sprintf(params->buffer, "size=%llu",
+                                      (unsigned long long)spool->max_hpages << hpage_shift);
+               case Opt_min_size:
+                       if (!spool || spool->min_hpages == -1)
+                               return 0;
+                       return sprintf(params->buffer, "min_size=%llu",
+                                      (unsigned long long)spool->min_hpages << hpage_shift);
+               case Opt_pagesize:
+                       hpage_size /= 1024;
+                       mod = 'K';
+                       if (hpage_size >= 1024) {
+                               hpage_size /= 1024;
+                               mod = 'M';
+                       }
+                       return sprintf(params->buffer, "pagesize=%lu%c",
+                                      hpage_size, mod);
+
+               case Opt_mode:
+                       if (sbinfo->mode == 0755)
+                               return 0;
+                       return sprintf(params->buffer, "mode=%o", sbinfo->mode);
+               case Opt_nr_inodes:
+                       if (sbinfo->max_inodes == -1)
+                               return 0;
+                       return sprintf(params->buffer, "nr_inodes=%lu",
+                                      sbinfo->max_inodes);
+               default:
+                       return -ENODATA;
+               }
+       default:
+               return generic_fsinfo(path, params);
+       }
+}
+
 static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb);
@@ -1106,6 +1171,7 @@ static const struct super_operations hugetlbfs_ops = {
        .statfs         = hugetlbfs_statfs,
        .put_super      = hugetlbfs_put_super,
        .show_options   = hugetlbfs_show_options,
+       .fsinfo         = hugetlbfs_fsinfo,
 };
 
 /*
index 56742632956c8ed1d91165004a7b99d91be11e28..1bd43f6947f36e5ace0b456931766e5df49653fe 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/namei.h>
 #include <linux/seq_file.h>
 #include <linux/exportfs.h>
+#include <linux/fsinfo.h>
 
 #include "kernfs-internal.h"
 
@@ -55,6 +56,20 @@ static int kernfs_sop_show_path(struct seq_file *sf, struct dentry *dentry)
        return 0;
 }
 
+static int kernfs_sop_fsinfo(struct path *path, struct fsinfo_kparams *params)
+{
+       struct kernfs_root *root = kernfs_root(kernfs_dentry_node(path->dentry));
+       struct kernfs_syscall_ops *scops = root->syscall_ops;
+       int ret;
+
+       if (scops && scops->fsinfo) {
+               ret = scops->fsinfo(root, params);
+               if (ret != -EAGAIN)
+                       return ret;
+       }
+       return generic_fsinfo(path, params);
+}
+
 const struct super_operations kernfs_sops = {
        .statfs         = simple_statfs,
        .drop_inode     = generic_delete_inode,
@@ -62,6 +77,7 @@ const struct super_operations kernfs_sops = {
 
        .show_options   = kernfs_sop_show_options,
        .show_path      = kernfs_sop_show_path,
+       .fsinfo         = kernfs_sop_fsinfo,
 };
 
 /*
index 051709212f5573f59f7fb0984208eabc313ddd01..631b52861763af37946aba28a778c5b92868790f 100644 (file)
@@ -27,6 +27,7 @@ struct vm_area_struct;
 struct super_block;
 struct file_system_type;
 struct fs_context;
+struct fsinfo_kparams;
 
 struct kernfs_fs_context;
 struct kernfs_open_node;
@@ -172,6 +173,7 @@ struct kernfs_node {
 struct kernfs_syscall_ops {
        int (*reconfigure)(struct kernfs_root *root, struct fs_context *fc);
        int (*show_options)(struct seq_file *sf, struct kernfs_root *root);
+       int (*fsinfo)(struct kernfs_root *root, struct fsinfo_kparams *params);
 
        int (*mkdir)(struct kernfs_node *parent, const char *name,
                     umode_t mode);
index ec2ff86f49ced2bdcc8afa14ca0db57d88bfd7cd..50e6cd50fe639850a46b9a666083b47b4b4d7461 100644 (file)
@@ -265,6 +265,7 @@ enum fsinfo_param_specification_type {
        FSINFO_PARAM_SPEC_IS_U32_OCTAL,
        FSINFO_PARAM_SPEC_IS_U32_HEX,
        FSINFO_PARAM_SPEC_IS_S32,
+       FSINFO_PARAM_SPEC_IS_U64,
        FSINFO_PARAM_SPEC_IS_ENUM,
        FSINFO_PARAM_SPEC_IS_STRING,
        FSINFO_PARAM_SPEC_IS_BLOB,
index d5ae888b8c57f21f89372d6be56821d21f28f3df..d20128d00fbe57808788218c6e80027c24e40c68 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/cgroupstats.h>
 #include <linux/fs_parser.h>
+#include <linux/fsinfo.h>
 
 #include <trace/events/cgroup.h>
 
@@ -948,6 +949,76 @@ const struct fs_parameter_description cgroup1_fs_parameters = {
        .no_source      = true,
 };
 
+static int cgroup1_fsinfo(struct kernfs_root *kf_root, struct fsinfo_kparams *params)
+{
+       struct cgroup_root *root = cgroup_root_from_kf(kf_root);
+       struct cgroup_subsys *ss;
+       const char *str = NULL;
+       unsigned int Mth;
+       int ret = 0, ssid;
+
+       switch (params->request) {
+       case FSINFO_ATTR_PARAMETER:
+               if (params->Mth && params->Nth != nr__cgroup1_params)
+                       return -ENODATA;
+               switch (params->Nth) {
+               case Opt_all:
+                       return 0;
+               case Opt_clone_children:
+                       if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags))
+                               str = "clone_children";
+                       goto string;
+               case Opt_cpuset_v2_mode:
+                       if (root->flags & CGRP_ROOT_CPUSET_V2_MODE)
+                               str = "noprefix";
+                       goto string;
+               case Opt_name:
+                       if (strlen(root->name))
+                               return sprintf(params->buffer, "name=%s", root->name);
+                       return 0;
+               case Opt_none:
+                       return 0;
+               case Opt_noprefix:
+                       if (root->flags & CGRP_ROOT_NOPREFIX)
+                               str = "noprefix";
+                       goto string;
+               case Opt_release_agent:
+                       spin_lock(&release_agent_path_lock);
+                       if (strlen(root->release_agent_path))
+                               ret = sprintf(params->buffer, "release_agent=%s",
+                                             root->release_agent_path);
+                       spin_unlock(&release_agent_path_lock);
+                       return ret;
+               case Opt_xattr:
+                       if (root->flags & CGRP_ROOT_XATTR)
+                               str = "noprefix";
+                       goto string;
+               case nr__cgroup1_params:
+                       Mth = params->Mth;
+                       for_each_subsys(ss, ssid) {
+                               if (Mth == 0) {
+                                       if (root->subsys_mask & (1 << ssid))
+                                               str = ss->legacy_name;
+                                       goto string;
+                               }
+                               Mth--;
+                       }
+                       return -ENODATA;
+               default:
+                       return -ENODATA;
+               }
+
+       default:
+               return -EAGAIN; /* Tell kernfs to call generic_fsinfo() */
+       }
+
+string:
+       if (!str)
+               return 0;
+       strcpy(params->buffer, str);
+       return strlen(params->buffer);
+}
+
 int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
        struct cgroup_fs_context *ctx = cgroup_fc2context(fc);
@@ -1138,6 +1209,7 @@ static int cgroup1_reconfigure(struct kernfs_root *kf_root, struct fs_context *f
 struct kernfs_syscall_ops cgroup1_kf_syscall_ops = {
        .rename                 = cgroup1_rename,
        .show_options           = cgroup1_show_options,
+       .fsinfo                 = cgroup1_fsinfo,
        .reconfigure            = cgroup1_reconfigure,
        .mkdir                  = cgroup_mkdir,
        .rmdir                  = cgroup_rmdir,
index 3c3c40cad2578b57ef4a1592a4be143ffeebff40..6d147377d318808f0797b20fcc6fb5964c6efd01 100644 (file)
@@ -55,6 +55,7 @@
 #include <linux/nsproxy.h>
 #include <linux/file.h>
 #include <linux/fs_parser.h>
+#include <linux/fsinfo.h>
 #include <linux/sched/cputime.h>
 #include <net/sock.h>
 
@@ -1786,6 +1787,35 @@ static int cgroup_show_options(struct seq_file *seq, struct kernfs_root *kf_root
        return 0;
 }
 
+static int cgroup_fsinfo(struct kernfs_root *kf_root, struct fsinfo_kparams *params)
+{
+       const char *str = NULL;
+
+       switch (params->request) {
+       case FSINFO_ATTR_PARAMETER:
+               if (params->Mth)
+                       return -ENODATA;
+               switch (params->Nth) {
+               case Opt_nsdelegate:
+                       if (current->nsproxy->cgroup_ns == &init_cgroup_ns &&
+                           cgrp_dfl_root.flags & CGRP_ROOT_NS_DELEGATE)
+                               str = "nsdelegate";
+                       goto string;
+               default:
+                       return -ENODATA;
+               }
+
+       default:
+               return -EAGAIN; /* Tell kernfs to call generic_fsinfo() */
+       }
+
+string:
+       if (!str)
+               return 0;
+       strcpy(params->buffer, str);
+       return strlen(params->buffer);
+}
+
 static void apply_cgroup_root_flags(unsigned int root_flags)
 {
        if (current->nsproxy->cgroup_ns == &init_cgroup_ns) {
@@ -5260,6 +5290,7 @@ int cgroup_rmdir(struct kernfs_node *kn)
 
 static struct kernfs_syscall_ops cgroup_kf_syscall_ops = {
        .show_options           = cgroup_show_options,
+       .fsinfo                 = cgroup_fsinfo,
        .reconfigure            = cgroup_reconfigure,
        .mkdir                  = cgroup_mkdir,
        .rmdir                  = cgroup_rmdir,
index 2e821749d700f67c681943c15c23e995dc9baf08..4635bf1eb3d4ee03f4d7c9fd9b186bca3c7f7556 100644 (file)
@@ -53,6 +53,7 @@ static const char *param_types[NR__FSINFO_PARAM_SPEC] = {
        [FSINFO_PARAM_SPEC_IS_U32_OCTAL]        = "octal",
        [FSINFO_PARAM_SPEC_IS_U32_HEX]          = "hex",
        [FSINFO_PARAM_SPEC_IS_S32]              = "s32",
+       [FSINFO_PARAM_SPEC_IS_U64]              = "u64",
        [FSINFO_PARAM_SPEC_IS_ENUM]             = "enum",
        [FSINFO_PARAM_SPEC_IS_STRING]           = "string",
        [FSINFO_PARAM_SPEC_IS_BLOB]             = "binary",