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