xref: /openbmc/linux/fs/afs/proc.c (revision bc05aa6e)
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_proc2net(struct file *f)
21 {
22 	return &__afs_net;
23 }
24 
25 static inline struct afs_net *afs_seq2net(struct seq_file *m)
26 {
27 	return &__afs_net; // TODO: use seq_file_net(m)
28 }
29 
30 static int afs_proc_cells_open(struct inode *inode, struct file *file);
31 static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
32 static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
33 static void afs_proc_cells_stop(struct seq_file *p, void *v);
34 static int afs_proc_cells_show(struct seq_file *m, void *v);
35 static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
36 				    size_t size, loff_t *_pos);
37 
38 static const struct seq_operations afs_proc_cells_ops = {
39 	.start	= afs_proc_cells_start,
40 	.next	= afs_proc_cells_next,
41 	.stop	= afs_proc_cells_stop,
42 	.show	= afs_proc_cells_show,
43 };
44 
45 static const struct file_operations afs_proc_cells_fops = {
46 	.open		= afs_proc_cells_open,
47 	.read		= seq_read,
48 	.write		= afs_proc_cells_write,
49 	.llseek		= seq_lseek,
50 	.release	= seq_release,
51 };
52 
53 static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
54 				      size_t size, loff_t *_pos);
55 static ssize_t afs_proc_rootcell_write(struct file *file,
56 				       const char __user *buf,
57 				       size_t size, loff_t *_pos);
58 
59 static const struct file_operations afs_proc_rootcell_fops = {
60 	.read		= afs_proc_rootcell_read,
61 	.write		= afs_proc_rootcell_write,
62 	.llseek		= no_llseek,
63 };
64 
65 static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file);
66 static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
67 static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
68 					loff_t *pos);
69 static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
70 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
71 
72 static const struct seq_operations afs_proc_cell_volumes_ops = {
73 	.start	= afs_proc_cell_volumes_start,
74 	.next	= afs_proc_cell_volumes_next,
75 	.stop	= afs_proc_cell_volumes_stop,
76 	.show	= afs_proc_cell_volumes_show,
77 };
78 
79 static const struct file_operations afs_proc_cell_volumes_fops = {
80 	.open		= afs_proc_cell_volumes_open,
81 	.read		= seq_read,
82 	.llseek		= seq_lseek,
83 	.release	= seq_release,
84 };
85 
86 static int afs_proc_cell_vlservers_open(struct inode *inode,
87 					struct file *file);
88 static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
89 static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
90 					  loff_t *pos);
91 static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
92 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
93 
94 static const struct seq_operations afs_proc_cell_vlservers_ops = {
95 	.start	= afs_proc_cell_vlservers_start,
96 	.next	= afs_proc_cell_vlservers_next,
97 	.stop	= afs_proc_cell_vlservers_stop,
98 	.show	= afs_proc_cell_vlservers_show,
99 };
100 
101 static const struct file_operations afs_proc_cell_vlservers_fops = {
102 	.open		= afs_proc_cell_vlservers_open,
103 	.read		= seq_read,
104 	.llseek		= seq_lseek,
105 	.release	= seq_release,
106 };
107 
108 static int afs_proc_servers_open(struct inode *inode, struct file *file);
109 static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos);
110 static void *afs_proc_servers_next(struct seq_file *p, void *v,
111 					loff_t *pos);
112 static void afs_proc_servers_stop(struct seq_file *p, void *v);
113 static int afs_proc_servers_show(struct seq_file *m, void *v);
114 
115 static const struct seq_operations afs_proc_servers_ops = {
116 	.start	= afs_proc_servers_start,
117 	.next	= afs_proc_servers_next,
118 	.stop	= afs_proc_servers_stop,
119 	.show	= afs_proc_servers_show,
120 };
121 
122 static const struct file_operations afs_proc_servers_fops = {
123 	.open		= afs_proc_servers_open,
124 	.read		= seq_read,
125 	.llseek		= seq_lseek,
126 	.release	= seq_release,
127 };
128 
129 /*
130  * initialise the /proc/fs/afs/ directory
131  */
132 int afs_proc_init(struct afs_net *net)
133 {
134 	_enter("");
135 
136 	net->proc_afs = proc_mkdir("fs/afs", NULL);
137 	if (!net->proc_afs)
138 		goto error_dir;
139 
140 	if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
141 	    !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
142 	    !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops))
143 		goto error_tree;
144 
145 	_leave(" = 0");
146 	return 0;
147 
148 error_tree:
149 	proc_remove(net->proc_afs);
150 error_dir:
151 	_leave(" = -ENOMEM");
152 	return -ENOMEM;
153 }
154 
155 /*
156  * clean up the /proc/fs/afs/ directory
157  */
158 void afs_proc_cleanup(struct afs_net *net)
159 {
160 	proc_remove(net->proc_afs);
161 	net->proc_afs = NULL;
162 }
163 
164 /*
165  * open "/proc/fs/afs/cells" which provides a summary of extant cells
166  */
167 static int afs_proc_cells_open(struct inode *inode, struct file *file)
168 {
169 	struct seq_file *m;
170 	int ret;
171 
172 	ret = seq_open(file, &afs_proc_cells_ops);
173 	if (ret < 0)
174 		return ret;
175 
176 	m = file->private_data;
177 	m->private = PDE_DATA(inode);
178 	return 0;
179 }
180 
181 /*
182  * set up the iterator to start reading from the cells list and return the
183  * first item
184  */
185 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
186 {
187 	struct afs_net *net = afs_seq2net(m);
188 
189 	rcu_read_lock();
190 	return seq_list_start_head(&net->proc_cells, *_pos);
191 }
192 
193 /*
194  * move to next cell in cells list
195  */
196 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
197 {
198 	struct afs_net *net = afs_seq2net(m);
199 
200 	return seq_list_next(v, &net->proc_cells, pos);
201 }
202 
203 /*
204  * clean up after reading from the cells list
205  */
206 static void afs_proc_cells_stop(struct seq_file *m, void *v)
207 {
208 	rcu_read_unlock();
209 }
210 
211 /*
212  * display a header line followed by a load of cell lines
213  */
214 static int afs_proc_cells_show(struct seq_file *m, void *v)
215 {
216 	struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
217 	struct afs_net *net = afs_seq2net(m);
218 
219 	if (v == &net->proc_cells) {
220 		/* display header on line 1 */
221 		seq_puts(m, "USE NAME\n");
222 		return 0;
223 	}
224 
225 	/* display one cell per line on subsequent lines */
226 	seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
227 	return 0;
228 }
229 
230 /*
231  * handle writes to /proc/fs/afs/cells
232  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
233  */
234 static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
235 				    size_t size, loff_t *_pos)
236 {
237 	struct afs_net *net = afs_proc2net(file);
238 	char *kbuf, *name, *args;
239 	int ret;
240 
241 	/* start by dragging the command into memory */
242 	if (size <= 1 || size >= PAGE_SIZE)
243 		return -EINVAL;
244 
245 	kbuf = memdup_user_nul(buf, size);
246 	if (IS_ERR(kbuf))
247 		return PTR_ERR(kbuf);
248 
249 	/* trim to first NL */
250 	name = memchr(kbuf, '\n', size);
251 	if (name)
252 		*name = 0;
253 
254 	/* split into command, name and argslist */
255 	name = strchr(kbuf, ' ');
256 	if (!name)
257 		goto inval;
258 	do {
259 		*name++ = 0;
260 	} while(*name == ' ');
261 	if (!*name)
262 		goto inval;
263 
264 	args = strchr(name, ' ');
265 	if (!args)
266 		goto inval;
267 	do {
268 		*args++ = 0;
269 	} while(*args == ' ');
270 	if (!*args)
271 		goto inval;
272 
273 	/* determine command to perform */
274 	_debug("cmd=%s name=%s args=%s", kbuf, name, args);
275 
276 	if (strcmp(kbuf, "add") == 0) {
277 		struct afs_cell *cell;
278 
279 		cell = afs_lookup_cell(net, name, strlen(name), args, true);
280 		if (IS_ERR(cell)) {
281 			ret = PTR_ERR(cell);
282 			goto done;
283 		}
284 
285 		set_bit(AFS_CELL_FL_NO_GC, &cell->flags);
286 		printk("kAFS: Added new cell '%s'\n", name);
287 	} else {
288 		goto inval;
289 	}
290 
291 	ret = size;
292 
293 done:
294 	kfree(kbuf);
295 	_leave(" = %d", ret);
296 	return ret;
297 
298 inval:
299 	ret = -EINVAL;
300 	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
301 	goto done;
302 }
303 
304 static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
305 				      size_t size, loff_t *_pos)
306 {
307 	return 0;
308 }
309 
310 /*
311  * handle writes to /proc/fs/afs/rootcell
312  * - to initialize rootcell: echo "cell.name:192.168.231.14"
313  */
314 static ssize_t afs_proc_rootcell_write(struct file *file,
315 				       const char __user *buf,
316 				       size_t size, loff_t *_pos)
317 {
318 	struct afs_net *net = afs_proc2net(file);
319 	char *kbuf, *s;
320 	int ret;
321 
322 	/* start by dragging the command into memory */
323 	if (size <= 1 || size >= PAGE_SIZE)
324 		return -EINVAL;
325 
326 	kbuf = memdup_user_nul(buf, size);
327 	if (IS_ERR(kbuf))
328 		return PTR_ERR(kbuf);
329 
330 	/* trim to first NL */
331 	s = memchr(kbuf, '\n', size);
332 	if (s)
333 		*s = 0;
334 
335 	/* determine command to perform */
336 	_debug("rootcell=%s", kbuf);
337 
338 	ret = afs_cell_init(net, kbuf);
339 	if (ret >= 0)
340 		ret = size;	/* consume everything, always */
341 
342 	kfree(kbuf);
343 	_leave(" = %d", ret);
344 	return ret;
345 }
346 
347 /*
348  * initialise /proc/fs/afs/<cell>/
349  */
350 int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
351 {
352 	struct proc_dir_entry *dir;
353 
354 	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
355 
356 	dir = proc_mkdir(cell->name, net->proc_afs);
357 	if (!dir)
358 		goto error_dir;
359 
360 	if (!proc_create_data("vlservers", 0, dir,
361 			      &afs_proc_cell_vlservers_fops, cell) ||
362 	    !proc_create_data("volumes", 0, dir,
363 			      &afs_proc_cell_volumes_fops, cell))
364 		goto error_tree;
365 
366 	_leave(" = 0");
367 	return 0;
368 
369 error_tree:
370 	remove_proc_subtree(cell->name, net->proc_afs);
371 error_dir:
372 	_leave(" = -ENOMEM");
373 	return -ENOMEM;
374 }
375 
376 /*
377  * remove /proc/fs/afs/<cell>/
378  */
379 void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell)
380 {
381 	_enter("");
382 
383 	remove_proc_subtree(cell->name, net->proc_afs);
384 
385 	_leave("");
386 }
387 
388 /*
389  * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells
390  */
391 static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
392 {
393 	struct afs_cell *cell;
394 	struct seq_file *m;
395 	int ret;
396 
397 	cell = PDE_DATA(inode);
398 	if (!cell)
399 		return -ENOENT;
400 
401 	ret = seq_open(file, &afs_proc_cell_volumes_ops);
402 	if (ret < 0)
403 		return ret;
404 
405 	m = file->private_data;
406 	m->private = cell;
407 
408 	return 0;
409 }
410 
411 /*
412  * set up the iterator to start reading from the cells list and return the
413  * first item
414  */
415 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
416 {
417 	struct afs_cell *cell = m->private;
418 
419 	_enter("cell=%p pos=%Ld", cell, *_pos);
420 
421 	read_lock(&cell->proc_lock);
422 	return seq_list_start_head(&cell->proc_volumes, *_pos);
423 }
424 
425 /*
426  * move to next cell in cells list
427  */
428 static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
429 					loff_t *_pos)
430 {
431 	struct afs_cell *cell = p->private;
432 
433 	_enter("cell=%p pos=%Ld", cell, *_pos);
434 	return seq_list_next(v, &cell->proc_volumes, _pos);
435 }
436 
437 /*
438  * clean up after reading from the cells list
439  */
440 static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
441 {
442 	struct afs_cell *cell = p->private;
443 
444 	read_unlock(&cell->proc_lock);
445 }
446 
447 static const char afs_vol_types[3][3] = {
448 	[AFSVL_RWVOL]	= "RW",
449 	[AFSVL_ROVOL]	= "RO",
450 	[AFSVL_BACKVOL]	= "BK",
451 };
452 
453 /*
454  * display a header line followed by a load of volume lines
455  */
456 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
457 {
458 	struct afs_cell *cell = m->private;
459 	struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
460 
461 	/* Display header on line 1 */
462 	if (v == &cell->proc_volumes) {
463 		seq_puts(m, "USE VID      TY\n");
464 		return 0;
465 	}
466 
467 	seq_printf(m, "%3d %08x %s\n",
468 		   atomic_read(&vol->usage), vol->vid,
469 		   afs_vol_types[vol->type]);
470 
471 	return 0;
472 }
473 
474 /*
475  * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume
476  * location server
477  */
478 static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
479 {
480 	struct afs_cell *cell;
481 	struct seq_file *m;
482 	int ret;
483 
484 	cell = PDE_DATA(inode);
485 	if (!cell)
486 		return -ENOENT;
487 
488 	ret = seq_open(file, &afs_proc_cell_vlservers_ops);
489 	if (ret<0)
490 		return ret;
491 
492 	m = file->private_data;
493 	m->private = cell;
494 
495 	return 0;
496 }
497 
498 /*
499  * set up the iterator to start reading from the cells list and return the
500  * first item
501  */
502 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
503 {
504 	struct afs_addr_list *alist;
505 	struct afs_cell *cell = m->private;
506 	loff_t pos = *_pos;
507 
508 	rcu_read_lock();
509 
510 	alist = rcu_dereference(cell->vl_addrs);
511 
512 	/* allow for the header line */
513 	if (!pos)
514 		return (void *) 1;
515 	pos--;
516 
517 	if (!alist || pos >= alist->nr_addrs)
518 		return NULL;
519 
520 	return alist->addrs + pos;
521 }
522 
523 /*
524  * move to next cell in cells list
525  */
526 static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
527 					  loff_t *_pos)
528 {
529 	struct afs_addr_list *alist;
530 	struct afs_cell *cell = p->private;
531 	loff_t pos;
532 
533 	alist = rcu_dereference(cell->vl_addrs);
534 
535 	pos = *_pos;
536 	(*_pos)++;
537 	if (!alist || pos >= alist->nr_addrs)
538 		return NULL;
539 
540 	return alist->addrs + pos;
541 }
542 
543 /*
544  * clean up after reading from the cells list
545  */
546 static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
547 {
548 	rcu_read_unlock();
549 }
550 
551 /*
552  * display a header line followed by a load of volume lines
553  */
554 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
555 {
556 	struct sockaddr_rxrpc *addr = v;
557 
558 	/* display header on line 1 */
559 	if (v == (void *)1) {
560 		seq_puts(m, "ADDRESS\n");
561 		return 0;
562 	}
563 
564 	/* display one cell per line on subsequent lines */
565 	seq_printf(m, "%pISp\n", &addr->transport);
566 	return 0;
567 }
568 
569 /*
570  * open "/proc/fs/afs/servers" which provides a summary of active
571  * servers
572  */
573 static int afs_proc_servers_open(struct inode *inode, struct file *file)
574 {
575 	return seq_open(file, &afs_proc_servers_ops);
576 }
577 
578 /*
579  * Set up the iterator to start reading from the server list and return the
580  * first item.
581  */
582 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
583 {
584 	struct afs_net *net = afs_seq2net(m);
585 
586 	rcu_read_lock();
587 	return seq_hlist_start_head_rcu(&net->fs_proc, *_pos);
588 }
589 
590 /*
591  * move to next cell in cells list
592  */
593 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
594 {
595 	struct afs_net *net = afs_seq2net(m);
596 
597 	return seq_hlist_next_rcu(v, &net->fs_proc, _pos);
598 }
599 
600 /*
601  * clean up after reading from the cells list
602  */
603 static void afs_proc_servers_stop(struct seq_file *p, void *v)
604 {
605 	rcu_read_unlock();
606 }
607 
608 /*
609  * display a header line followed by a load of volume lines
610  */
611 static int afs_proc_servers_show(struct seq_file *m, void *v)
612 {
613 	struct afs_server *server;
614 	struct afs_addr_list *alist;
615 
616 	if (v == SEQ_START_TOKEN) {
617 		seq_puts(m, "UUID                                 USE ADDR\n");
618 		return 0;
619 	}
620 
621 	server = list_entry(v, struct afs_server, proc_link);
622 	alist = rcu_dereference(server->addresses);
623 	seq_printf(m, "%pU %3d %pISp\n",
624 		   &server->uuid,
625 		   atomic_read(&server->usage),
626 		   &alist->addrs[alist->index].transport);
627 	return 0;
628 }
629