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 ssize_t ret; 83 84 rcu_read_lock(); 85 ret = scnprintf(buf, PAGE_SIZE, "%s\n", rcu_dereference(c->identifier)); 86 rcu_read_unlock(); 87 return ret; 88 } 89 90 /* Strip trailing '\n' */ 91 static size_t nfs_string_strip(const char *c, size_t len) 92 { 93 while (len > 0 && c[len-1] == '\n') 94 --len; 95 return len; 96 } 97 98 static ssize_t nfs_netns_identifier_store(struct kobject *kobj, 99 struct kobj_attribute *attr, 100 const char *buf, size_t count) 101 { 102 struct nfs_netns_client *c = container_of(kobj, 103 struct nfs_netns_client, 104 kobject); 105 const char *old; 106 char *p; 107 size_t len; 108 109 len = nfs_string_strip(buf, min_t(size_t, count, CONTAINER_ID_MAXLEN)); 110 if (!len) 111 return 0; 112 p = kmemdup_nul(buf, len, GFP_KERNEL); 113 if (!p) 114 return -ENOMEM; 115 old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1); 116 if (old) { 117 synchronize_rcu(); 118 kfree(old); 119 } 120 return count; 121 } 122 123 static void nfs_netns_client_release(struct kobject *kobj) 124 { 125 struct nfs_netns_client *c = container_of(kobj, 126 struct nfs_netns_client, 127 kobject); 128 129 kfree(rcu_dereference_raw(c->identifier)); 130 kfree(c); 131 } 132 133 static const void *nfs_netns_client_namespace(struct kobject *kobj) 134 { 135 return container_of(kobj, struct nfs_netns_client, kobject)->net; 136 } 137 138 static struct kobj_attribute nfs_netns_client_id = __ATTR(identifier, 139 0644, nfs_netns_identifier_show, nfs_netns_identifier_store); 140 141 static struct attribute *nfs_netns_client_attrs[] = { 142 &nfs_netns_client_id.attr, 143 NULL, 144 }; 145 146 static struct kobj_type nfs_netns_client_type = { 147 .release = nfs_netns_client_release, 148 .default_attrs = nfs_netns_client_attrs, 149 .sysfs_ops = &kobj_sysfs_ops, 150 .namespace = nfs_netns_client_namespace, 151 }; 152 153 static struct nfs_netns_client *nfs_netns_client_alloc(struct kobject *parent, 154 struct net *net) 155 { 156 struct nfs_netns_client *p; 157 158 p = kzalloc(sizeof(*p), GFP_KERNEL); 159 if (p) { 160 p->net = net; 161 p->kobject.kset = nfs_client_kset; 162 if (kobject_init_and_add(&p->kobject, &nfs_netns_client_type, 163 parent, "nfs_client") == 0) 164 return p; 165 kobject_put(&p->kobject); 166 } 167 return NULL; 168 } 169 170 void nfs_netns_sysfs_setup(struct nfs_net *netns, struct net *net) 171 { 172 struct nfs_netns_client *clp; 173 174 clp = nfs_netns_client_alloc(nfs_client_kobj, net); 175 if (clp) { 176 netns->nfs_client = clp; 177 kobject_uevent(&clp->kobject, KOBJ_ADD); 178 } 179 } 180 181 void nfs_netns_sysfs_destroy(struct nfs_net *netns) 182 { 183 struct nfs_netns_client *clp = netns->nfs_client; 184 185 if (clp) { 186 kobject_uevent(&clp->kobject, KOBJ_REMOVE); 187 kobject_del(&clp->kobject); 188 kobject_put(&clp->kobject); 189 netns->nfs_client = NULL; 190 } 191 } 192