xref: /linux/fs/afs/proc.c (revision e7c375b181600caf135cfd03eadbc45eb530f2cb)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* /proc interface for AFS
3  *
4  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  */
7 
8 #include <linux/slab.h>
9 #include <linux/module.h>
10 #include <linux/proc_fs.h>
11 #include <linux/seq_file.h>
12 #include <linux/sched.h>
13 #include <linux/uaccess.h>
14 #include "internal.h"
15 
16 struct afs_vl_seq_net_private {
17 	struct seq_net_private		seq;	/* Must be first */
18 	struct afs_vlserver_list	*vllist;
19 };
20 
afs_seq2net(struct seq_file * m)21 static inline struct afs_net *afs_seq2net(struct seq_file *m)
22 {
23 	return afs_net(seq_file_net(m));
24 }
25 
afs_seq2net_single(struct seq_file * m)26 static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
27 {
28 	return afs_net(seq_file_single_net(m));
29 }
30 
31 /*
32  * Display the list of cells known to the namespace.
33  */
afs_proc_cells_show(struct seq_file * m,void * v)34 static int afs_proc_cells_show(struct seq_file *m, void *v)
35 {
36 	struct afs_vlserver_list *vllist;
37 	struct afs_cell *cell;
38 
39 	if (v == SEQ_START_TOKEN) {
40 		/* display header on line 1 */
41 		seq_puts(m, "USE ACT    TTL SV ST NAME\n");
42 		return 0;
43 	}
44 
45 	cell = list_entry(v, struct afs_cell, proc_link);
46 	vllist = rcu_dereference(cell->vl_servers);
47 
48 	/* display one cell per line on subsequent lines */
49 	seq_printf(m, "%3u %3u %6lld %2u %2u %s\n",
50 		   refcount_read(&cell->ref),
51 		   atomic_read(&cell->active),
52 		   cell->dns_expiry - ktime_get_real_seconds(),
53 		   vllist ? vllist->nr_servers : 0,
54 		   cell->state,
55 		   cell->name);
56 	return 0;
57 }
58 
afs_proc_cells_start(struct seq_file * m,loff_t * _pos)59 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
60 	__acquires(rcu)
61 {
62 	rcu_read_lock();
63 	return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
64 }
65 
afs_proc_cells_next(struct seq_file * m,void * v,loff_t * pos)66 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
67 {
68 	return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
69 }
70 
afs_proc_cells_stop(struct seq_file * m,void * v)71 static void afs_proc_cells_stop(struct seq_file *m, void *v)
72 	__releases(rcu)
73 {
74 	rcu_read_unlock();
75 }
76 
77 static const struct seq_operations afs_proc_cells_ops = {
78 	.start	= afs_proc_cells_start,
79 	.next	= afs_proc_cells_next,
80 	.stop	= afs_proc_cells_stop,
81 	.show	= afs_proc_cells_show,
82 };
83 
84 /*
85  * handle writes to /proc/fs/afs/cells
86  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
87  */
afs_proc_cells_write(struct file * file,char * buf,size_t size)88 static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
89 {
90 	struct seq_file *m = file->private_data;
91 	struct afs_net *net = afs_seq2net(m);
92 	char *name, *args;
93 	int ret;
94 
95 	/* trim to first NL */
96 	name = memchr(buf, '\n', size);
97 	if (name)
98 		*name = 0;
99 
100 	/* split into command, name and argslist */
101 	name = strchr(buf, ' ');
102 	if (!name)
103 		goto inval;
104 	do {
105 		*name++ = 0;
106 	} while(*name == ' ');
107 	if (!*name)
108 		goto inval;
109 
110 	args = strchr(name, ' ');
111 	if (args) {
112 		do {
113 			*args++ = 0;
114 		} while(*args == ' ');
115 		if (!*args)
116 			goto inval;
117 	}
118 
119 	/* determine command to perform */
120 	_debug("cmd=%s name=%s args=%s", buf, name, args);
121 
122 	if (strcmp(buf, "add") == 0) {
123 		struct afs_cell *cell;
124 
125 		cell = afs_lookup_cell(net, name, strlen(name), args,
126 				       AFS_LOOKUP_CELL_PRELOAD,
127 				       afs_cell_trace_use_lookup_add);
128 		if (IS_ERR(cell)) {
129 			ret = PTR_ERR(cell);
130 			goto done;
131 		}
132 
133 		if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
134 			afs_unuse_cell(cell, afs_cell_trace_unuse_no_pin);
135 	} else {
136 		goto inval;
137 	}
138 
139 	ret = 0;
140 
141 done:
142 	_leave(" = %d", ret);
143 	return ret;
144 
145 inval:
146 	ret = -EINVAL;
147 	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
148 	goto done;
149 }
150 
151 /*
152  * Display the list of addr_prefs known to the namespace.
153  */
afs_proc_addr_prefs_show(struct seq_file * m,void * v)154 static int afs_proc_addr_prefs_show(struct seq_file *m, void *v)
155 {
156 	struct afs_addr_preference_list *preflist;
157 	struct afs_addr_preference *pref;
158 	struct afs_net *net = afs_seq2net_single(m);
159 	union {
160 		struct sockaddr_in sin;
161 		struct sockaddr_in6 sin6;
162 	} addr;
163 	unsigned int i;
164 	char buf[44]; /* Maximum ipv6 + max subnet is 43 */
165 
166 	rcu_read_lock();
167 	preflist = rcu_dereference(net->address_prefs);
168 
169 	if (!preflist) {
170 		seq_puts(m, "NO PREFS\n");
171 		goto out;
172 	}
173 
174 	seq_printf(m, "PROT SUBNET                                      PRIOR (v=%u n=%u/%u/%u)\n",
175 		   preflist->version, preflist->ipv6_off, preflist->nr, preflist->max_prefs);
176 
177 	memset(&addr, 0, sizeof(addr));
178 
179 	for (i = 0; i < preflist->nr; i++) {
180 		pref = &preflist->prefs[i];
181 
182 		addr.sin.sin_family = pref->family;
183 		if (pref->family == AF_INET) {
184 			memcpy(&addr.sin.sin_addr, &pref->ipv4_addr,
185 			       sizeof(addr.sin.sin_addr));
186 			snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin, pref->subnet_mask);
187 			seq_printf(m, "UDP  %-43.43s %5u\n", buf, pref->prio);
188 		} else {
189 			memcpy(&addr.sin6.sin6_addr, &pref->ipv6_addr,
190 			       sizeof(addr.sin6.sin6_addr));
191 			snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin6, pref->subnet_mask);
192 			seq_printf(m, "UDP  %-43.43s %5u\n", buf, pref->prio);
193 		}
194 	}
195 
196 out:
197 	rcu_read_unlock();
198 	return 0;
199 }
200 
201 /*
202  * Display the name of the current workstation cell.
203  */
afs_proc_rootcell_show(struct seq_file * m,void * v)204 static int afs_proc_rootcell_show(struct seq_file *m, void *v)
205 {
206 	struct afs_cell *cell;
207 	struct afs_net *net;
208 
209 	net = afs_seq2net_single(m);
210 	down_read(&net->cells_lock);
211 	cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
212 	if (cell)
213 		seq_printf(m, "%s\n", cell->name);
214 	up_read(&net->cells_lock);
215 	return 0;
216 }
217 
218 /*
219  * Set the current workstation cell and optionally supply its list of volume
220  * location servers.
221  *
222  *	echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
223  */
afs_proc_rootcell_write(struct file * file,char * buf,size_t size)224 static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
225 {
226 	struct seq_file *m = file->private_data;
227 	struct afs_net *net = afs_seq2net_single(m);
228 	char *s;
229 	int ret;
230 
231 	ret = -EINVAL;
232 	if (buf[0] == '.')
233 		goto out;
234 	if (memchr(buf, '/', size))
235 		goto out;
236 
237 	/* trim to first NL */
238 	s = memchr(buf, '\n', size);
239 	if (s)
240 		*s = 0;
241 
242 	/* determine command to perform */
243 	_debug("rootcell=%s", buf);
244 
245 	ret = -EEXIST;
246 	inode_lock(file_inode(file));
247 	if (!rcu_access_pointer(net->ws_cell))
248 		ret = afs_cell_init(net, buf);
249 	else
250 		printk("busy\n");
251 	inode_unlock(file_inode(file));
252 
253 out:
254 	_leave(" = %d", ret);
255 	return ret;
256 }
257 
258 static const char afs_vol_types[3][3] = {
259 	[AFSVL_RWVOL]	= "RW",
260 	[AFSVL_ROVOL]	= "RO",
261 	[AFSVL_BACKVOL]	= "BK",
262 };
263 
264 /*
265  * Display the list of volumes known to a cell.
266  */
afs_proc_cell_volumes_show(struct seq_file * m,void * v)267 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
268 {
269 	struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
270 
271 	/* Display header on line 1 */
272 	if (v == SEQ_START_TOKEN) {
273 		seq_puts(m, "USE VID      TY NAME\n");
274 		return 0;
275 	}
276 
277 	seq_printf(m, "%3d %08llx %s %s\n",
278 		   refcount_read(&vol->ref), vol->vid,
279 		   afs_vol_types[vol->type],
280 		   vol->name);
281 
282 	return 0;
283 }
284 
afs_proc_cell_volumes_start(struct seq_file * m,loff_t * _pos)285 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
286 	__acquires(cell->proc_lock)
287 {
288 	struct afs_cell *cell = pde_data(file_inode(m->file));
289 
290 	rcu_read_lock();
291 	return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
292 }
293 
afs_proc_cell_volumes_next(struct seq_file * m,void * v,loff_t * _pos)294 static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
295 					loff_t *_pos)
296 {
297 	struct afs_cell *cell = pde_data(file_inode(m->file));
298 
299 	return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
300 }
301 
afs_proc_cell_volumes_stop(struct seq_file * m,void * v)302 static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
303 	__releases(cell->proc_lock)
304 {
305 	rcu_read_unlock();
306 }
307 
308 static const struct seq_operations afs_proc_cell_volumes_ops = {
309 	.start	= afs_proc_cell_volumes_start,
310 	.next	= afs_proc_cell_volumes_next,
311 	.stop	= afs_proc_cell_volumes_stop,
312 	.show	= afs_proc_cell_volumes_show,
313 };
314 
315 static const char *const dns_record_sources[NR__dns_record_source + 1] = {
316 	[DNS_RECORD_UNAVAILABLE]	= "unav",
317 	[DNS_RECORD_FROM_CONFIG]	= "cfg",
318 	[DNS_RECORD_FROM_DNS_A]		= "A",
319 	[DNS_RECORD_FROM_DNS_AFSDB]	= "AFSDB",
320 	[DNS_RECORD_FROM_DNS_SRV]	= "SRV",
321 	[DNS_RECORD_FROM_NSS]		= "nss",
322 	[NR__dns_record_source]		= "[weird]"
323 };
324 
325 static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
326 	[DNS_LOOKUP_NOT_DONE]		= "no-lookup",
327 	[DNS_LOOKUP_GOOD]		= "good",
328 	[DNS_LOOKUP_GOOD_WITH_BAD]	= "good/bad",
329 	[DNS_LOOKUP_BAD]		= "bad",
330 	[DNS_LOOKUP_GOT_NOT_FOUND]	= "not-found",
331 	[DNS_LOOKUP_GOT_LOCAL_FAILURE]	= "local-failure",
332 	[DNS_LOOKUP_GOT_TEMP_FAILURE]	= "temp-failure",
333 	[DNS_LOOKUP_GOT_NS_FAILURE]	= "ns-failure",
334 	[NR__dns_lookup_status]		= "[weird]"
335 };
336 
337 /*
338  * Display the list of Volume Location servers we're using for a cell.
339  */
afs_proc_cell_vlservers_show(struct seq_file * m,void * v)340 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
341 {
342 	const struct afs_vl_seq_net_private *priv = m->private;
343 	const struct afs_vlserver_list *vllist = priv->vllist;
344 	const struct afs_vlserver_entry *entry;
345 	const struct afs_vlserver *vlserver;
346 	const struct afs_addr_list *alist;
347 	int i;
348 
349 	if (v == SEQ_START_TOKEN) {
350 		seq_printf(m, "# source %s, status %s\n",
351 			   dns_record_sources[vllist ? vllist->source : 0],
352 			   dns_lookup_statuses[vllist ? vllist->status : 0]);
353 		return 0;
354 	}
355 
356 	entry = v;
357 	vlserver = entry->server;
358 	alist = rcu_dereference(vlserver->addresses);
359 
360 	seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
361 		   vlserver->name, entry->priority, entry->weight,
362 		   dns_record_sources[alist ? alist->source : entry->source],
363 		   dns_lookup_statuses[alist ? alist->status : entry->status]);
364 	if (alist) {
365 		for (i = 0; i < alist->nr_addrs; i++)
366 			seq_printf(m, " %c %pISpc\n",
367 				   alist->preferred == i ? '>' : '-',
368 				   rxrpc_kernel_remote_addr(alist->addrs[i].peer));
369 	}
370 	seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
371 	seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
372 		   vlserver->probe.flags, vlserver->probe.error,
373 		   vlserver->probe.abort_code,
374 		   atomic_read(&vlserver->probe_outstanding));
375 	return 0;
376 }
377 
afs_proc_cell_vlservers_start(struct seq_file * m,loff_t * _pos)378 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
379 	__acquires(rcu)
380 {
381 	struct afs_vl_seq_net_private *priv = m->private;
382 	struct afs_vlserver_list *vllist;
383 	struct afs_cell *cell = pde_data(file_inode(m->file));
384 	loff_t pos = *_pos;
385 
386 	rcu_read_lock();
387 
388 	vllist = rcu_dereference(cell->vl_servers);
389 	priv->vllist = vllist;
390 
391 	if (pos < 0)
392 		*_pos = pos = 0;
393 	if (pos == 0)
394 		return SEQ_START_TOKEN;
395 
396 	if (pos - 1 >= vllist->nr_servers)
397 		return NULL;
398 
399 	return &vllist->servers[pos - 1];
400 }
401 
afs_proc_cell_vlservers_next(struct seq_file * m,void * v,loff_t * _pos)402 static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
403 					  loff_t *_pos)
404 {
405 	struct afs_vl_seq_net_private *priv = m->private;
406 	struct afs_vlserver_list *vllist = priv->vllist;
407 	loff_t pos;
408 
409 	pos = *_pos;
410 	pos++;
411 	*_pos = pos;
412 	if (!vllist || pos - 1 >= vllist->nr_servers)
413 		return NULL;
414 
415 	return &vllist->servers[pos - 1];
416 }
417 
afs_proc_cell_vlservers_stop(struct seq_file * m,void * v)418 static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
419 	__releases(rcu)
420 {
421 	rcu_read_unlock();
422 }
423 
424 static const struct seq_operations afs_proc_cell_vlservers_ops = {
425 	.start	= afs_proc_cell_vlservers_start,
426 	.next	= afs_proc_cell_vlservers_next,
427 	.stop	= afs_proc_cell_vlservers_stop,
428 	.show	= afs_proc_cell_vlservers_show,
429 };
430 
431 /*
432  * Display the list of fileservers we're using within a namespace.
433  */
afs_proc_servers_show(struct seq_file * m,void * v)434 static int afs_proc_servers_show(struct seq_file *m, void *v)
435 {
436 	struct afs_endpoint_state *estate;
437 	struct afs_addr_list *alist;
438 	struct afs_server *server;
439 	unsigned long failed;
440 	int i;
441 
442 	if (v == SEQ_START_TOKEN) {
443 		seq_puts(m, "UUID                                 REF ACT CELL\n");
444 		return 0;
445 	}
446 
447 	server = list_entry(v, struct afs_server, proc_link);
448 	seq_printf(m, "%pU %3d %3d %s\n",
449 		   &server->uuid,
450 		   refcount_read(&server->ref),
451 		   atomic_read(&server->active),
452 		   server->cell->name);
453 	seq_printf(m, "  - info: fl=%lx rtt=%u\n",
454 		   server->flags, server->rtt);
455 	seq_printf(m, "  - probe: last=%d\n",
456 		   (int)(jiffies - server->probed_at) / HZ);
457 
458 	estate = rcu_dereference(server->endpoint_state);
459 	if (!estate)
460 		goto out;
461 	failed = estate->failed_set;
462 	seq_printf(m, "  - ESTATE pq=%x np=%u rsp=%lx f=%lx\n",
463 		   estate->probe_seq, atomic_read(&estate->nr_probing),
464 		   estate->responsive_set, estate->failed_set);
465 
466 	alist = estate->addresses;
467 	seq_printf(m, "  - ALIST v=%u ap=%u\n",
468 		   alist->version, alist->addr_pref_version);
469 	for (i = 0; i < alist->nr_addrs; i++) {
470 		const struct afs_address *addr = &alist->addrs[i];
471 
472 		seq_printf(m, "    [%x] %pISpc%s rtt=%d err=%d p=%u\n",
473 			   i, rxrpc_kernel_remote_addr(addr->peer),
474 			   alist->preferred == i ? "*" :
475 			   test_bit(i, &failed) ? "!" : "",
476 			   rxrpc_kernel_get_srtt(addr->peer),
477 			   addr->last_error, addr->prio);
478 	}
479 
480 out:
481 	return 0;
482 }
483 
afs_proc_servers_start(struct seq_file * m,loff_t * _pos)484 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
485 	__acquires(rcu)
486 {
487 	rcu_read_lock();
488 	return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
489 }
490 
afs_proc_servers_next(struct seq_file * m,void * v,loff_t * _pos)491 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
492 {
493 	return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
494 }
495 
afs_proc_servers_stop(struct seq_file * m,void * v)496 static void afs_proc_servers_stop(struct seq_file *m, void *v)
497 	__releases(rcu)
498 {
499 	rcu_read_unlock();
500 }
501 
502 static const struct seq_operations afs_proc_servers_ops = {
503 	.start	= afs_proc_servers_start,
504 	.next	= afs_proc_servers_next,
505 	.stop	= afs_proc_servers_stop,
506 	.show	= afs_proc_servers_show,
507 };
508 
509 /*
510  * Display the list of strings that may be substituted for the @sys pathname
511  * macro.
512  */
afs_proc_sysname_show(struct seq_file * m,void * v)513 static int afs_proc_sysname_show(struct seq_file *m, void *v)
514 {
515 	struct afs_net *net = afs_seq2net(m);
516 	struct afs_sysnames *sysnames = net->sysnames;
517 	unsigned int i = (unsigned long)v - 1;
518 
519 	if (i < sysnames->nr)
520 		seq_printf(m, "%s\n", sysnames->subs[i]);
521 	return 0;
522 }
523 
afs_proc_sysname_start(struct seq_file * m,loff_t * pos)524 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
525 	__acquires(&net->sysnames_lock)
526 {
527 	struct afs_net *net = afs_seq2net(m);
528 	struct afs_sysnames *names;
529 
530 	read_lock(&net->sysnames_lock);
531 
532 	names = net->sysnames;
533 	if (*pos >= names->nr)
534 		return NULL;
535 	return (void *)(unsigned long)(*pos + 1);
536 }
537 
afs_proc_sysname_next(struct seq_file * m,void * v,loff_t * pos)538 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
539 {
540 	struct afs_net *net = afs_seq2net(m);
541 	struct afs_sysnames *names = net->sysnames;
542 
543 	*pos += 1;
544 	if (*pos >= names->nr)
545 		return NULL;
546 	return (void *)(unsigned long)(*pos + 1);
547 }
548 
afs_proc_sysname_stop(struct seq_file * m,void * v)549 static void afs_proc_sysname_stop(struct seq_file *m, void *v)
550 	__releases(&net->sysnames_lock)
551 {
552 	struct afs_net *net = afs_seq2net(m);
553 
554 	read_unlock(&net->sysnames_lock);
555 }
556 
557 static const struct seq_operations afs_proc_sysname_ops = {
558 	.start	= afs_proc_sysname_start,
559 	.next	= afs_proc_sysname_next,
560 	.stop	= afs_proc_sysname_stop,
561 	.show	= afs_proc_sysname_show,
562 };
563 
564 /*
565  * Allow the @sys substitution to be configured.
566  */
afs_proc_sysname_write(struct file * file,char * buf,size_t size)567 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
568 {
569 	struct afs_sysnames *sysnames, *kill;
570 	struct seq_file *m = file->private_data;
571 	struct afs_net *net = afs_seq2net(m);
572 	char *s, *p, *sub;
573 	int ret, len;
574 
575 	sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
576 	if (!sysnames)
577 		return -ENOMEM;
578 	refcount_set(&sysnames->usage, 1);
579 	kill = sysnames;
580 
581 	p = buf;
582 	while ((s = strsep(&p, " \t\n"))) {
583 		len = strlen(s);
584 		if (len == 0)
585 			continue;
586 		ret = -ENAMETOOLONG;
587 		if (len >= AFSNAMEMAX)
588 			goto error;
589 
590 		if (len >= 4 &&
591 		    s[len - 4] == '@' &&
592 		    s[len - 3] == 's' &&
593 		    s[len - 2] == 'y' &&
594 		    s[len - 1] == 's')
595 			/* Protect against recursion */
596 			goto invalid;
597 
598 		if (s[0] == '.' &&
599 		    (len < 2 || (len == 2 && s[1] == '.')))
600 			goto invalid;
601 
602 		if (memchr(s, '/', len))
603 			goto invalid;
604 
605 		ret = -EFBIG;
606 		if (sysnames->nr >= AFS_NR_SYSNAME)
607 			goto out;
608 
609 		if (strcmp(s, afs_init_sysname) == 0) {
610 			sub = (char *)afs_init_sysname;
611 		} else {
612 			ret = -ENOMEM;
613 			sub = kmemdup(s, len + 1, GFP_KERNEL);
614 			if (!sub)
615 				goto out;
616 		}
617 
618 		sysnames->subs[sysnames->nr] = sub;
619 		sysnames->nr++;
620 	}
621 
622 	if (sysnames->nr == 0) {
623 		sysnames->subs[0] = sysnames->blank;
624 		sysnames->nr++;
625 	}
626 
627 	write_lock(&net->sysnames_lock);
628 	kill = net->sysnames;
629 	net->sysnames = sysnames;
630 	write_unlock(&net->sysnames_lock);
631 	ret = 0;
632 out:
633 	afs_put_sysnames(kill);
634 	return ret;
635 
636 invalid:
637 	ret = -EINVAL;
638 error:
639 	goto out;
640 }
641 
afs_put_sysnames(struct afs_sysnames * sysnames)642 void afs_put_sysnames(struct afs_sysnames *sysnames)
643 {
644 	int i;
645 
646 	if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
647 		for (i = 0; i < sysnames->nr; i++)
648 			if (sysnames->subs[i] != afs_init_sysname &&
649 			    sysnames->subs[i] != sysnames->blank)
650 				kfree(sysnames->subs[i]);
651 		kfree(sysnames);
652 	}
653 }
654 
655 /*
656  * Display general per-net namespace statistics
657  */
afs_proc_stats_show(struct seq_file * m,void * v)658 static int afs_proc_stats_show(struct seq_file *m, void *v)
659 {
660 	struct afs_net *net = afs_seq2net_single(m);
661 
662 	seq_puts(m, "kAFS statistics\n");
663 
664 	seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
665 		   atomic_read(&net->n_lookup),
666 		   atomic_read(&net->n_reval),
667 		   atomic_read(&net->n_inval),
668 		   atomic_read(&net->n_relpg));
669 
670 	seq_printf(m, "dir-data: rdpg=%u\n",
671 		   atomic_read(&net->n_read_dir));
672 
673 	seq_printf(m, "dir-edit: cr=%u rm=%u\n",
674 		   atomic_read(&net->n_dir_cr),
675 		   atomic_read(&net->n_dir_rm));
676 
677 	seq_printf(m, "file-rd : n=%u nb=%lu\n",
678 		   atomic_read(&net->n_fetches),
679 		   atomic_long_read(&net->n_fetch_bytes));
680 	seq_printf(m, "file-wr : n=%u nb=%lu\n",
681 		   atomic_read(&net->n_stores),
682 		   atomic_long_read(&net->n_store_bytes));
683 	return 0;
684 }
685 
686 /*
687  * initialise /proc/fs/afs/<cell>/
688  */
afs_proc_cell_setup(struct afs_cell * cell)689 int afs_proc_cell_setup(struct afs_cell *cell)
690 {
691 	struct proc_dir_entry *dir;
692 	struct afs_net *net = cell->net;
693 
694 	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
695 
696 	dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
697 	if (!dir)
698 		goto error_dir;
699 
700 	if (!proc_create_net_data("vlservers", 0444, dir,
701 				  &afs_proc_cell_vlservers_ops,
702 				  sizeof(struct afs_vl_seq_net_private),
703 				  cell) ||
704 	    !proc_create_net_data("volumes", 0444, dir,
705 				  &afs_proc_cell_volumes_ops,
706 				  sizeof(struct seq_net_private),
707 				  cell))
708 		goto error_tree;
709 
710 	_leave(" = 0");
711 	return 0;
712 
713 error_tree:
714 	remove_proc_subtree(cell->name, net->proc_afs);
715 error_dir:
716 	_leave(" = -ENOMEM");
717 	return -ENOMEM;
718 }
719 
720 /*
721  * remove /proc/fs/afs/<cell>/
722  */
afs_proc_cell_remove(struct afs_cell * cell)723 void afs_proc_cell_remove(struct afs_cell *cell)
724 {
725 	struct afs_net *net = cell->net;
726 
727 	_enter("");
728 	remove_proc_subtree(cell->name, net->proc_afs);
729 	_leave("");
730 }
731 
732 /*
733  * initialise the /proc/fs/afs/ directory
734  */
afs_proc_init(struct afs_net * net)735 int afs_proc_init(struct afs_net *net)
736 {
737 	struct proc_dir_entry *p;
738 
739 	_enter("");
740 
741 	p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
742 	if (!p)
743 		goto error_dir;
744 
745 	if (!proc_create_net_data_write("cells", 0644, p,
746 					&afs_proc_cells_ops,
747 					afs_proc_cells_write,
748 					sizeof(struct seq_net_private),
749 					NULL) ||
750 	    !proc_create_net_single_write("rootcell", 0644, p,
751 					  afs_proc_rootcell_show,
752 					  afs_proc_rootcell_write,
753 					  NULL) ||
754 	    !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
755 			     sizeof(struct seq_net_private)) ||
756 	    !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
757 	    !proc_create_net_data_write("sysname", 0644, p,
758 					&afs_proc_sysname_ops,
759 					afs_proc_sysname_write,
760 					sizeof(struct seq_net_private),
761 					NULL) ||
762 	    !proc_create_net_single_write("addr_prefs", 0644, p,
763 					  afs_proc_addr_prefs_show,
764 					  afs_proc_addr_prefs_write,
765 					  NULL))
766 		goto error_tree;
767 
768 	net->proc_afs = p;
769 	_leave(" = 0");
770 	return 0;
771 
772 error_tree:
773 	proc_remove(p);
774 error_dir:
775 	_leave(" = -ENOMEM");
776 	return -ENOMEM;
777 }
778 
779 /*
780  * clean up the /proc/fs/afs/ directory
781  */
afs_proc_cleanup(struct afs_net * net)782 void afs_proc_cleanup(struct afs_net *net)
783 {
784 	proc_remove(net->proc_afs);
785 	net->proc_afs = NULL;
786 }
787