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