]> Git Repo - linux.git/blob - fs/afs/proc.c
afs: Handle CONFIG_PROC_FS=n
[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 ([email protected])
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
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 %pISp\n",
338                    &server->uuid,
339                    atomic_read(&server->usage),
340                    &alist->addrs[alist->index].transport);
341         return 0;
342 }
343
344 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
345         __acquires(rcu)
346 {
347         rcu_read_lock();
348         return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
349 }
350
351 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
352 {
353         return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
354 }
355
356 static void afs_proc_servers_stop(struct seq_file *m, void *v)
357         __releases(rcu)
358 {
359         rcu_read_unlock();
360 }
361
362 static const struct seq_operations afs_proc_servers_ops = {
363         .start  = afs_proc_servers_start,
364         .next   = afs_proc_servers_next,
365         .stop   = afs_proc_servers_stop,
366         .show   = afs_proc_servers_show,
367 };
368
369 /*
370  * Display the list of strings that may be substituted for the @sys pathname
371  * macro.
372  */
373 static int afs_proc_sysname_show(struct seq_file *m, void *v)
374 {
375         struct afs_net *net = afs_seq2net(m);
376         struct afs_sysnames *sysnames = net->sysnames;
377         unsigned int i = (unsigned long)v - 1;
378
379         if (i < sysnames->nr)
380                 seq_printf(m, "%s\n", sysnames->subs[i]);
381         return 0;
382 }
383
384 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
385         __acquires(&net->sysnames_lock)
386 {
387         struct afs_net *net = afs_seq2net(m);
388         struct afs_sysnames *names;
389
390         read_lock(&net->sysnames_lock);
391
392         names = net->sysnames;
393         if (*pos >= names->nr)
394                 return NULL;
395         return (void *)(unsigned long)(*pos + 1);
396 }
397
398 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
399 {
400         struct afs_net *net = afs_seq2net(m);
401         struct afs_sysnames *names = net->sysnames;
402
403         *pos += 1;
404         if (*pos >= names->nr)
405                 return NULL;
406         return (void *)(unsigned long)(*pos + 1);
407 }
408
409 static void afs_proc_sysname_stop(struct seq_file *m, void *v)
410         __releases(&net->sysnames_lock)
411 {
412         struct afs_net *net = afs_seq2net(m);
413
414         read_unlock(&net->sysnames_lock);
415 }
416
417 static const struct seq_operations afs_proc_sysname_ops = {
418         .start  = afs_proc_sysname_start,
419         .next   = afs_proc_sysname_next,
420         .stop   = afs_proc_sysname_stop,
421         .show   = afs_proc_sysname_show,
422 };
423
424 /*
425  * Allow the @sys substitution to be configured.
426  */
427 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
428 {
429         struct afs_sysnames *sysnames, *kill;
430         struct seq_file *m = file->private_data;
431         struct afs_net *net = afs_seq2net(m);
432         char *s, *p, *sub;
433         int ret, len;
434
435         sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
436         if (!sysnames)
437                 return -ENOMEM;
438         refcount_set(&sysnames->usage, 1);
439         kill = sysnames;
440
441         p = buf;
442         while ((s = strsep(&p, " \t\n"))) {
443                 len = strlen(s);
444                 if (len == 0)
445                         continue;
446                 ret = -ENAMETOOLONG;
447                 if (len >= AFSNAMEMAX)
448                         goto error;
449
450                 if (len >= 4 &&
451                     s[len - 4] == '@' &&
452                     s[len - 3] == 's' &&
453                     s[len - 2] == 'y' &&
454                     s[len - 1] == 's')
455                         /* Protect against recursion */
456                         goto invalid;
457
458                 if (s[0] == '.' &&
459                     (len < 2 || (len == 2 && s[1] == '.')))
460                         goto invalid;
461
462                 if (memchr(s, '/', len))
463                         goto invalid;
464
465                 ret = -EFBIG;
466                 if (sysnames->nr >= AFS_NR_SYSNAME)
467                         goto out;
468
469                 if (strcmp(s, afs_init_sysname) == 0) {
470                         sub = (char *)afs_init_sysname;
471                 } else {
472                         ret = -ENOMEM;
473                         sub = kmemdup(s, len + 1, GFP_KERNEL);
474                         if (!sub)
475                                 goto out;
476                 }
477
478                 sysnames->subs[sysnames->nr] = sub;
479                 sysnames->nr++;
480         }
481
482         if (sysnames->nr == 0) {
483                 sysnames->subs[0] = sysnames->blank;
484                 sysnames->nr++;
485         }
486
487         write_lock(&net->sysnames_lock);
488         kill = net->sysnames;
489         net->sysnames = sysnames;
490         write_unlock(&net->sysnames_lock);
491         ret = 0;
492 out:
493         afs_put_sysnames(kill);
494         return ret;
495
496 invalid:
497         ret = -EINVAL;
498 error:
499         goto out;
500 }
501
502 void afs_put_sysnames(struct afs_sysnames *sysnames)
503 {
504         int i;
505
506         if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
507                 for (i = 0; i < sysnames->nr; i++)
508                         if (sysnames->subs[i] != afs_init_sysname &&
509                             sysnames->subs[i] != sysnames->blank)
510                                 kfree(sysnames->subs[i]);
511         }
512 }
513
514 /*
515  * Display general per-net namespace statistics
516  */
517 static int afs_proc_stats_show(struct seq_file *m, void *v)
518 {
519         struct afs_net *net = afs_seq2net_single(m);
520
521         seq_puts(m, "kAFS statistics\n");
522
523         seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
524                    atomic_read(&net->n_lookup),
525                    atomic_read(&net->n_reval),
526                    atomic_read(&net->n_inval),
527                    atomic_read(&net->n_relpg));
528
529         seq_printf(m, "dir-data: rdpg=%u\n",
530                    atomic_read(&net->n_read_dir));
531
532         seq_printf(m, "dir-edit: cr=%u rm=%u\n",
533                    atomic_read(&net->n_dir_cr),
534                    atomic_read(&net->n_dir_rm));
535
536         seq_printf(m, "file-rd : n=%u nb=%lu\n",
537                    atomic_read(&net->n_fetches),
538                    atomic_long_read(&net->n_fetch_bytes));
539         seq_printf(m, "file-wr : n=%u nb=%lu\n",
540                    atomic_read(&net->n_stores),
541                    atomic_long_read(&net->n_store_bytes));
542         return 0;
543 }
544
545 /*
546  * initialise /proc/fs/afs/<cell>/
547  */
548 int afs_proc_cell_setup(struct afs_cell *cell)
549 {
550         struct proc_dir_entry *dir;
551         struct afs_net *net = cell->net;
552
553         _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
554
555         dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
556         if (!dir)
557                 goto error_dir;
558
559         if (!proc_create_net_data("vlservers", 0444, dir,
560                                   &afs_proc_cell_vlservers_ops,
561                                   sizeof(struct seq_net_private),
562                                   cell) ||
563             !proc_create_net_data("volumes", 0444, dir,
564                                   &afs_proc_cell_volumes_ops,
565                                   sizeof(struct seq_net_private),
566                                   cell))
567                 goto error_tree;
568
569         _leave(" = 0");
570         return 0;
571
572 error_tree:
573         remove_proc_subtree(cell->name, net->proc_afs);
574 error_dir:
575         _leave(" = -ENOMEM");
576         return -ENOMEM;
577 }
578
579 /*
580  * remove /proc/fs/afs/<cell>/
581  */
582 void afs_proc_cell_remove(struct afs_cell *cell)
583 {
584         struct afs_net *net = cell->net;
585
586         _enter("");
587         remove_proc_subtree(cell->name, net->proc_afs);
588         _leave("");
589 }
590
591 /*
592  * initialise the /proc/fs/afs/ directory
593  */
594 int afs_proc_init(struct afs_net *net)
595 {
596         struct proc_dir_entry *p;
597
598         _enter("");
599
600         p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
601         if (!p)
602                 goto error_dir;
603
604         if (!proc_create_net_data_write("cells", 0644, p,
605                                         &afs_proc_cells_ops,
606                                         afs_proc_cells_write,
607                                         sizeof(struct seq_net_private),
608                                         NULL) ||
609             !proc_create_net_single_write("rootcell", 0644, p,
610                                           afs_proc_rootcell_show,
611                                           afs_proc_rootcell_write,
612                                           NULL) ||
613             !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
614                              sizeof(struct seq_net_private)) ||
615             !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
616             !proc_create_net_data_write("sysname", 0644, p,
617                                         &afs_proc_sysname_ops,
618                                         afs_proc_sysname_write,
619                                         sizeof(struct seq_net_private),
620                                         NULL))
621                 goto error_tree;
622
623         net->proc_afs = p;
624         _leave(" = 0");
625         return 0;
626
627 error_tree:
628         proc_remove(p);
629 error_dir:
630         _leave(" = -ENOMEM");
631         return -ENOMEM;
632 }
633
634 /*
635  * clean up the /proc/fs/afs/ directory
636  */
637 void afs_proc_cleanup(struct afs_net *net)
638 {
639         proc_remove(net->proc_afs);
640         net->proc_afs = NULL;
641 }
This page took 0.069358 seconds and 4 git commands to generate.