xref: /openbmc/linux/fs/afs/proc.c (revision f220d3eb)
1 /* /proc interface for AFS
2  *
3  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/slab.h>
13 #include <linux/module.h>
14 #include <linux/proc_fs.h>
15 #include <linux/seq_file.h>
16 #include <linux/sched.h>
17 #include <linux/uaccess.h>
18 #include "internal.h"
19 
20 static inline struct afs_net *afs_seq2net(struct seq_file *m)
21 {
22 	return afs_net(seq_file_net(m));
23 }
24 
25 static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
26 {
27 	return afs_net(seq_file_single_net(m));
28 }
29 
30 /*
31  * Display the list of cells known to the namespace.
32  */
33 static int afs_proc_cells_show(struct seq_file *m, void *v)
34 {
35 	struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
36 	struct afs_net *net = afs_seq2net(m);
37 
38 	if (v == &net->proc_cells) {
39 		/* display header on line 1 */
40 		seq_puts(m, "USE NAME\n");
41 		return 0;
42 	}
43 
44 	/* display one cell per line on subsequent lines */
45 	seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
46 	return 0;
47 }
48 
49 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
50 	__acquires(rcu)
51 {
52 	rcu_read_lock();
53 	return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
54 }
55 
56 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
57 {
58 	return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
59 }
60 
61 static void afs_proc_cells_stop(struct seq_file *m, void *v)
62 	__releases(rcu)
63 {
64 	rcu_read_unlock();
65 }
66 
67 static const struct seq_operations afs_proc_cells_ops = {
68 	.start	= afs_proc_cells_start,
69 	.next	= afs_proc_cells_next,
70 	.stop	= afs_proc_cells_stop,
71 	.show	= afs_proc_cells_show,
72 };
73 
74 /*
75  * handle writes to /proc/fs/afs/cells
76  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
77  */
78 static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
79 {
80 	struct seq_file *m = file->private_data;
81 	struct afs_net *net = afs_seq2net(m);
82 	char *name, *args;
83 	int ret;
84 
85 	/* trim to first NL */
86 	name = memchr(buf, '\n', size);
87 	if (name)
88 		*name = 0;
89 
90 	/* split into command, name and argslist */
91 	name = strchr(buf, ' ');
92 	if (!name)
93 		goto inval;
94 	do {
95 		*name++ = 0;
96 	} while(*name == ' ');
97 	if (!*name)
98 		goto inval;
99 
100 	args = strchr(name, ' ');
101 	if (!args)
102 		goto inval;
103 	do {
104 		*args++ = 0;
105 	} while(*args == ' ');
106 	if (!*args)
107 		goto inval;
108 
109 	/* determine command to perform */
110 	_debug("cmd=%s name=%s args=%s", buf, name, args);
111 
112 	if (strcmp(buf, "add") == 0) {
113 		struct afs_cell *cell;
114 
115 		cell = afs_lookup_cell(net, name, strlen(name), args, true);
116 		if (IS_ERR(cell)) {
117 			ret = PTR_ERR(cell);
118 			goto done;
119 		}
120 
121 		if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
122 			afs_put_cell(net, cell);
123 		printk("kAFS: Added new cell '%s'\n", name);
124 	} else {
125 		goto inval;
126 	}
127 
128 	ret = 0;
129 
130 done:
131 	_leave(" = %d", ret);
132 	return ret;
133 
134 inval:
135 	ret = -EINVAL;
136 	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
137 	goto done;
138 }
139 
140 /*
141  * Display the name of the current workstation cell.
142  */
143 static int afs_proc_rootcell_show(struct seq_file *m, void *v)
144 {
145 	struct afs_cell *cell;
146 	struct afs_net *net;
147 
148 	net = afs_seq2net_single(m);
149 	if (rcu_access_pointer(net->ws_cell)) {
150 		rcu_read_lock();
151 		cell = rcu_dereference(net->ws_cell);
152 		if (cell)
153 			seq_printf(m, "%s\n", cell->name);
154 		rcu_read_unlock();
155 	}
156 	return 0;
157 }
158 
159 /*
160  * Set the current workstation cell and optionally supply its list of volume
161  * location servers.
162  *
163  *	echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
164  */
165 static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
166 {
167 	struct seq_file *m = file->private_data;
168 	struct afs_net *net = afs_seq2net_single(m);
169 	char *s;
170 	int ret;
171 
172 	ret = -EINVAL;
173 	if (buf[0] == '.')
174 		goto out;
175 	if (memchr(buf, '/', size))
176 		goto out;
177 
178 	/* trim to first NL */
179 	s = memchr(buf, '\n', size);
180 	if (s)
181 		*s = 0;
182 
183 	/* determine command to perform */
184 	_debug("rootcell=%s", buf);
185 
186 	ret = afs_cell_init(net, buf);
187 
188 out:
189 	_leave(" = %d", ret);
190 	return ret;
191 }
192 
193 static const char afs_vol_types[3][3] = {
194 	[AFSVL_RWVOL]	= "RW",
195 	[AFSVL_ROVOL]	= "RO",
196 	[AFSVL_BACKVOL]	= "BK",
197 };
198 
199 /*
200  * Display the list of volumes known to a cell.
201  */
202 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
203 {
204 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
205 	struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
206 
207 	/* Display header on line 1 */
208 	if (v == &cell->proc_volumes) {
209 		seq_puts(m, "USE VID      TY\n");
210 		return 0;
211 	}
212 
213 	seq_printf(m, "%3d %08x %s\n",
214 		   atomic_read(&vol->usage), vol->vid,
215 		   afs_vol_types[vol->type]);
216 
217 	return 0;
218 }
219 
220 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
221 	__acquires(cell->proc_lock)
222 {
223 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
224 
225 	read_lock(&cell->proc_lock);
226 	return seq_list_start_head(&cell->proc_volumes, *_pos);
227 }
228 
229 static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
230 					loff_t *_pos)
231 {
232 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
233 
234 	return seq_list_next(v, &cell->proc_volumes, _pos);
235 }
236 
237 static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
238 	__releases(cell->proc_lock)
239 {
240 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
241 
242 	read_unlock(&cell->proc_lock);
243 }
244 
245 static const struct seq_operations afs_proc_cell_volumes_ops = {
246 	.start	= afs_proc_cell_volumes_start,
247 	.next	= afs_proc_cell_volumes_next,
248 	.stop	= afs_proc_cell_volumes_stop,
249 	.show	= afs_proc_cell_volumes_show,
250 };
251 
252 /*
253  * Display the list of Volume Location servers we're using for a cell.
254  */
255 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
256 {
257 	struct sockaddr_rxrpc *addr = v;
258 
259 	/* display header on line 1 */
260 	if (v == (void *)1) {
261 		seq_puts(m, "ADDRESS\n");
262 		return 0;
263 	}
264 
265 	/* display one cell per line on subsequent lines */
266 	seq_printf(m, "%pISp\n", &addr->transport);
267 	return 0;
268 }
269 
270 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
271 	__acquires(rcu)
272 {
273 	struct afs_addr_list *alist;
274 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
275 	loff_t pos = *_pos;
276 
277 	rcu_read_lock();
278 
279 	alist = rcu_dereference(cell->vl_addrs);
280 
281 	/* allow for the header line */
282 	if (!pos)
283 		return (void *) 1;
284 	pos--;
285 
286 	if (!alist || pos >= alist->nr_addrs)
287 		return NULL;
288 
289 	return alist->addrs + pos;
290 }
291 
292 static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
293 					  loff_t *_pos)
294 {
295 	struct afs_addr_list *alist;
296 	struct afs_cell *cell = PDE_DATA(file_inode(m->file));
297 	loff_t pos;
298 
299 	alist = rcu_dereference(cell->vl_addrs);
300 
301 	pos = *_pos;
302 	(*_pos)++;
303 	if (!alist || pos >= alist->nr_addrs)
304 		return NULL;
305 
306 	return alist->addrs + pos;
307 }
308 
309 static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
310 	__releases(rcu)
311 {
312 	rcu_read_unlock();
313 }
314 
315 static const struct seq_operations afs_proc_cell_vlservers_ops = {
316 	.start	= afs_proc_cell_vlservers_start,
317 	.next	= afs_proc_cell_vlservers_next,
318 	.stop	= afs_proc_cell_vlservers_stop,
319 	.show	= afs_proc_cell_vlservers_show,
320 };
321 
322 /*
323  * Display the list of fileservers we're using within a namespace.
324  */
325 static int afs_proc_servers_show(struct seq_file *m, void *v)
326 {
327 	struct afs_server *server;
328 	struct afs_addr_list *alist;
329 	int i;
330 
331 	if (v == SEQ_START_TOKEN) {
332 		seq_puts(m, "UUID                                 USE ADDR\n");
333 		return 0;
334 	}
335 
336 	server = list_entry(v, struct afs_server, proc_link);
337 	alist = rcu_dereference(server->addresses);
338 	seq_printf(m, "%pU %3d %pISpc%s\n",
339 		   &server->uuid,
340 		   atomic_read(&server->usage),
341 		   &alist->addrs[0].transport,
342 		   alist->index == 0 ? "*" : "");
343 	for (i = 1; i < alist->nr_addrs; i++)
344 		seq_printf(m, "                                         %pISpc%s\n",
345 			   &alist->addrs[i].transport,
346 			   alist->index == i ? "*" : "");
347 	return 0;
348 }
349 
350 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
351 	__acquires(rcu)
352 {
353 	rcu_read_lock();
354 	return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
355 }
356 
357 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
358 {
359 	return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
360 }
361 
362 static void afs_proc_servers_stop(struct seq_file *m, void *v)
363 	__releases(rcu)
364 {
365 	rcu_read_unlock();
366 }
367 
368 static const struct seq_operations afs_proc_servers_ops = {
369 	.start	= afs_proc_servers_start,
370 	.next	= afs_proc_servers_next,
371 	.stop	= afs_proc_servers_stop,
372 	.show	= afs_proc_servers_show,
373 };
374 
375 /*
376  * Display the list of strings that may be substituted for the @sys pathname
377  * macro.
378  */
379 static int afs_proc_sysname_show(struct seq_file *m, void *v)
380 {
381 	struct afs_net *net = afs_seq2net(m);
382 	struct afs_sysnames *sysnames = net->sysnames;
383 	unsigned int i = (unsigned long)v - 1;
384 
385 	if (i < sysnames->nr)
386 		seq_printf(m, "%s\n", sysnames->subs[i]);
387 	return 0;
388 }
389 
390 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
391 	__acquires(&net->sysnames_lock)
392 {
393 	struct afs_net *net = afs_seq2net(m);
394 	struct afs_sysnames *names;
395 
396 	read_lock(&net->sysnames_lock);
397 
398 	names = net->sysnames;
399 	if (*pos >= names->nr)
400 		return NULL;
401 	return (void *)(unsigned long)(*pos + 1);
402 }
403 
404 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
405 {
406 	struct afs_net *net = afs_seq2net(m);
407 	struct afs_sysnames *names = net->sysnames;
408 
409 	*pos += 1;
410 	if (*pos >= names->nr)
411 		return NULL;
412 	return (void *)(unsigned long)(*pos + 1);
413 }
414 
415 static void afs_proc_sysname_stop(struct seq_file *m, void *v)
416 	__releases(&net->sysnames_lock)
417 {
418 	struct afs_net *net = afs_seq2net(m);
419 
420 	read_unlock(&net->sysnames_lock);
421 }
422 
423 static const struct seq_operations afs_proc_sysname_ops = {
424 	.start	= afs_proc_sysname_start,
425 	.next	= afs_proc_sysname_next,
426 	.stop	= afs_proc_sysname_stop,
427 	.show	= afs_proc_sysname_show,
428 };
429 
430 /*
431  * Allow the @sys substitution to be configured.
432  */
433 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
434 {
435 	struct afs_sysnames *sysnames, *kill;
436 	struct seq_file *m = file->private_data;
437 	struct afs_net *net = afs_seq2net(m);
438 	char *s, *p, *sub;
439 	int ret, len;
440 
441 	sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
442 	if (!sysnames)
443 		return -ENOMEM;
444 	refcount_set(&sysnames->usage, 1);
445 	kill = sysnames;
446 
447 	p = buf;
448 	while ((s = strsep(&p, " \t\n"))) {
449 		len = strlen(s);
450 		if (len == 0)
451 			continue;
452 		ret = -ENAMETOOLONG;
453 		if (len >= AFSNAMEMAX)
454 			goto error;
455 
456 		if (len >= 4 &&
457 		    s[len - 4] == '@' &&
458 		    s[len - 3] == 's' &&
459 		    s[len - 2] == 'y' &&
460 		    s[len - 1] == 's')
461 			/* Protect against recursion */
462 			goto invalid;
463 
464 		if (s[0] == '.' &&
465 		    (len < 2 || (len == 2 && s[1] == '.')))
466 			goto invalid;
467 
468 		if (memchr(s, '/', len))
469 			goto invalid;
470 
471 		ret = -EFBIG;
472 		if (sysnames->nr >= AFS_NR_SYSNAME)
473 			goto out;
474 
475 		if (strcmp(s, afs_init_sysname) == 0) {
476 			sub = (char *)afs_init_sysname;
477 		} else {
478 			ret = -ENOMEM;
479 			sub = kmemdup(s, len + 1, GFP_KERNEL);
480 			if (!sub)
481 				goto out;
482 		}
483 
484 		sysnames->subs[sysnames->nr] = sub;
485 		sysnames->nr++;
486 	}
487 
488 	if (sysnames->nr == 0) {
489 		sysnames->subs[0] = sysnames->blank;
490 		sysnames->nr++;
491 	}
492 
493 	write_lock(&net->sysnames_lock);
494 	kill = net->sysnames;
495 	net->sysnames = sysnames;
496 	write_unlock(&net->sysnames_lock);
497 	ret = 0;
498 out:
499 	afs_put_sysnames(kill);
500 	return ret;
501 
502 invalid:
503 	ret = -EINVAL;
504 error:
505 	goto out;
506 }
507 
508 void afs_put_sysnames(struct afs_sysnames *sysnames)
509 {
510 	int i;
511 
512 	if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
513 		for (i = 0; i < sysnames->nr; i++)
514 			if (sysnames->subs[i] != afs_init_sysname &&
515 			    sysnames->subs[i] != sysnames->blank)
516 				kfree(sysnames->subs[i]);
517 	}
518 }
519 
520 /*
521  * Display general per-net namespace statistics
522  */
523 static int afs_proc_stats_show(struct seq_file *m, void *v)
524 {
525 	struct afs_net *net = afs_seq2net_single(m);
526 
527 	seq_puts(m, "kAFS statistics\n");
528 
529 	seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
530 		   atomic_read(&net->n_lookup),
531 		   atomic_read(&net->n_reval),
532 		   atomic_read(&net->n_inval),
533 		   atomic_read(&net->n_relpg));
534 
535 	seq_printf(m, "dir-data: rdpg=%u\n",
536 		   atomic_read(&net->n_read_dir));
537 
538 	seq_printf(m, "dir-edit: cr=%u rm=%u\n",
539 		   atomic_read(&net->n_dir_cr),
540 		   atomic_read(&net->n_dir_rm));
541 
542 	seq_printf(m, "file-rd : n=%u nb=%lu\n",
543 		   atomic_read(&net->n_fetches),
544 		   atomic_long_read(&net->n_fetch_bytes));
545 	seq_printf(m, "file-wr : n=%u nb=%lu\n",
546 		   atomic_read(&net->n_stores),
547 		   atomic_long_read(&net->n_store_bytes));
548 	return 0;
549 }
550 
551 /*
552  * initialise /proc/fs/afs/<cell>/
553  */
554 int afs_proc_cell_setup(struct afs_cell *cell)
555 {
556 	struct proc_dir_entry *dir;
557 	struct afs_net *net = cell->net;
558 
559 	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
560 
561 	dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
562 	if (!dir)
563 		goto error_dir;
564 
565 	if (!proc_create_net_data("vlservers", 0444, dir,
566 				  &afs_proc_cell_vlservers_ops,
567 				  sizeof(struct seq_net_private),
568 				  cell) ||
569 	    !proc_create_net_data("volumes", 0444, dir,
570 				  &afs_proc_cell_volumes_ops,
571 				  sizeof(struct seq_net_private),
572 				  cell))
573 		goto error_tree;
574 
575 	_leave(" = 0");
576 	return 0;
577 
578 error_tree:
579 	remove_proc_subtree(cell->name, net->proc_afs);
580 error_dir:
581 	_leave(" = -ENOMEM");
582 	return -ENOMEM;
583 }
584 
585 /*
586  * remove /proc/fs/afs/<cell>/
587  */
588 void afs_proc_cell_remove(struct afs_cell *cell)
589 {
590 	struct afs_net *net = cell->net;
591 
592 	_enter("");
593 	remove_proc_subtree(cell->name, net->proc_afs);
594 	_leave("");
595 }
596 
597 /*
598  * initialise the /proc/fs/afs/ directory
599  */
600 int afs_proc_init(struct afs_net *net)
601 {
602 	struct proc_dir_entry *p;
603 
604 	_enter("");
605 
606 	p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
607 	if (!p)
608 		goto error_dir;
609 
610 	if (!proc_create_net_data_write("cells", 0644, p,
611 					&afs_proc_cells_ops,
612 					afs_proc_cells_write,
613 					sizeof(struct seq_net_private),
614 					NULL) ||
615 	    !proc_create_net_single_write("rootcell", 0644, p,
616 					  afs_proc_rootcell_show,
617 					  afs_proc_rootcell_write,
618 					  NULL) ||
619 	    !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
620 			     sizeof(struct seq_net_private)) ||
621 	    !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
622 	    !proc_create_net_data_write("sysname", 0644, p,
623 					&afs_proc_sysname_ops,
624 					afs_proc_sysname_write,
625 					sizeof(struct seq_net_private),
626 					NULL))
627 		goto error_tree;
628 
629 	net->proc_afs = p;
630 	_leave(" = 0");
631 	return 0;
632 
633 error_tree:
634 	proc_remove(p);
635 error_dir:
636 	_leave(" = -ENOMEM");
637 	return -ENOMEM;
638 }
639 
640 /*
641  * clean up the /proc/fs/afs/ directory
642  */
643 void afs_proc_cleanup(struct afs_net *net)
644 {
645 	proc_remove(net->proc_afs);
646 	net->proc_afs = NULL;
647 }
648