afs: Show all of a server's addresses in /proc/fs/afs/servers
[muen/linux.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 <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 }