1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2019 Hammerspace Inc 4 */ 5 6 #include <linux/module.h> 7 #include <linux/kobject.h> 8 #include <linux/sysfs.h> 9 #include <linux/fs.h> 10 #include <linux/slab.h> 11 #include <linux/netdevice.h> 12 #include <linux/string.h> 13 #include <linux/nfs_fs.h> 14 #include <linux/rcupdate.h> 15 16 #include "nfs4_fs.h" 17 #include "netns.h" 18 #include "sysfs.h" 19 20 struct kobject *nfs_client_kobj; 21 static struct kset *nfs_client_kset; 22 23 static void nfs_netns_object_release(struct kobject *kobj) 24 { 25 kfree(kobj); 26 } 27 28 static const struct kobj_ns_type_operations *nfs_netns_object_child_ns_type( 29 struct kobject *kobj) 30 { 31 return &net_ns_type_operations; 32 } 33 34 static struct kobj_type nfs_netns_object_type = { 35 .release = nfs_netns_object_release, 36 .sysfs_ops = &kobj_sysfs_ops, 37 .child_ns_type = nfs_netns_object_child_ns_type, 38 }; 39 40 static struct kobject *nfs_netns_object_alloc(const char *name, 41 struct kset *kset, struct kobject *parent) 42 { 43 struct kobject *kobj; 44 45 kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); 46 if (kobj) { 47 kobj->kset = kset; 48 if (kobject_init_and_add(kobj, &nfs_netns_object_type, 49 parent, "%s", name) == 0) 50 return kobj; 51 kobject_put(kobj); 52 } 53 return NULL; 54 } 55 56 int nfs_sysfs_init(void) 57 { 58 nfs_client_kset = kset_create_and_add("nfs", NULL, fs_kobj); 59 if (!nfs_client_kset) 60 return -ENOMEM; 61 nfs_client_kobj = nfs_netns_object_alloc("net", nfs_client_kset, NULL); 62 if (!nfs_client_kobj) { 63 kset_unregister(nfs_client_kset); 64 nfs_client_kset = NULL; 65 return -ENOMEM; 66 } 67 return 0; 68 } 69 70 void nfs_sysfs_exit(void) 71 { 72 kobject_put(nfs_client_kobj); 73 kset_unregister(nfs_client_kset); 74 } 75 76 static ssize_t nfs_netns_identifier_show(struct kobject *kobj, 77 struct kobj_attribute *attr, char *buf) 78 { 79 struct nfs_netns_client *c = container_of(kobj, 80 struct nfs_netns_client, 81 kobject); 82 return scnprintf(buf, PAGE_SIZE, "%s\n", c->identifier); 83 } 84 85 /* Strip trailing '\n' */ 86 static size_t nfs_string_strip(const char *c, size_t len) 87 { 88 while (len > 0 && c[len-1] == '\n') 89 --len; 90 return len; 91 } 92 93 static ssize_t nfs_netns_identifier_store(struct kobject *kobj, 94 struct kobj_attribute *attr, 95 const char *buf, size_t count) 96 { 97 struct nfs_netns_client *c = container_of(kobj, 98 struct nfs_netns_client, 99 kobject); 100 const char *old; 101 char *p; 102 size_t len; 103 104 len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); 105 if (!len) 106 return 0; 107 p = kmemdup_nul(buf, len, GFP_KERNEL); 108 if (!p) 109 return -ENOMEM; 110 old = xchg(&c->identifier, p); 111 if (old) { 112 synchronize_rcu(); 113 kfree(old); 114 } 115 return count; 116 } 117 118 static void nfs_netns_client_release(struct kobject *kobj) 119 { 120 struct nfs_netns_client *c = container_of(kobj, 121 struct nfs_netns_client, 122 kobject); 123 124 if (c->identifier) 125 kfree(c->identifier); 126 kfree(c); 127 } 128 129 static const void *nfs_netns_client_namespace(struct kobject *kobj) 130 { 131 return container_of(kobj, struct nfs_netns_client, kobject)->net; 132 } 133 134 static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, 135 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); 136 137 static struct attribute *nfs_netns_client_attrs[] = { 138 &nfs_netns_client_id.attr, 139 NULL, 140 }; 141 142 static struct kobj_type nfs_netns_client_type = { 143 .release = nfs_netns_client_release, 144 .default_attrs = nfs_netns_client_attrs, 145 .sysfs_ops = &kobj_sysfs_ops, 146 .namespace = nfs_netns_client_namespace, 147 }; 148 149 static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, 150 struct net *net) 151 { 152 struct nfs_netns_client *p; 153 154 p = kzalloc(sizeof(*p), GFP_KERNEL); 155 if (p) { 156 p->net = net; 157 p->kobject.kset = nfs_client_kset; 158 if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, 159 parent, "nfs_client") == 0) 160 return p; 161 kobject_put(&p->kobject); 162 } 163 return NULL; 164 } 165 166 void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) 167 { 168 struct nfs_netns_client *clp; 169 170 clp = nfs_netns_client_alloc(nfs_client_kobj, net); 171 if (clp) { 172 netns->nfs_client = clp; 173 kobject_uevent(&clp->kobject, KOBJ_ADD); 174 } 175 } 176 177 void nfs_netns_sysfs_destroy(struct nfs_net *netns) 178 { 179 struct nfs_netns_client *clp = netns->nfs_client; 180 181 if (clp) { 182 kobject_uevent(&clp->kobject, KOBJ_REMOVE); 183 kobject_del(&clp->kobject); 184 kobject_put(&clp->kobject); 185 netns->nfs_client = NULL; 186 } 187 } 188