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