1
2
3
4
5
6
7
8
9
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
20static inline struct afs_net *afs_seq2net(struct seq_file *m)
21{
22 return afs_net(seq_file_net(m));
23}
24
25static 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
32
33static 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
40 seq_puts(m, "USE NAME\n");
41 return 0;
42 }
43
44
45 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
46 return 0;
47}
48
49static 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
56static 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
61static void afs_proc_cells_stop(struct seq_file *m, void *v)
62 __releases(rcu)
63{
64 rcu_read_unlock();
65}
66
67static 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
76
77
78static 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
86 name = memchr(buf, '\n', size);
87 if (name)
88 *name = 0;
89
90
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
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
130done:
131 _leave(" = %d", ret);
132 return ret;
133
134inval:
135 ret = -EINVAL;
136 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
137 goto done;
138}
139
140
141
142
143static 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
161
162
163
164
165static 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
179 s = memchr(buf, '\n', size);
180 if (s)
181 *s = 0;
182
183
184 _debug("rootcell=%s", buf);
185
186 ret = afs_cell_init(net, buf);
187
188out:
189 _leave(" = %d", ret);
190 return ret;
191}
192
193static const char afs_vol_types[3][3] = {
194 [AFSVL_RWVOL] = "RW",
195 [AFSVL_ROVOL] = "RO",
196 [AFSVL_BACKVOL] = "BK",
197};
198
199
200
201
202static 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
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
220static 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
229static 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
237static 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
245static 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
254
255static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
256{
257 struct sockaddr_rxrpc *addr = v;
258
259
260 if (v == (void *)1) {
261 seq_puts(m, "ADDRESS\n");
262 return 0;
263 }
264
265
266 seq_printf(m, "%pISp\n", &addr->transport);
267 return 0;
268}
269
270static 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
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
292static 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
309static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
310 __releases(rcu)
311{
312 rcu_read_unlock();
313}
314
315static 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
324
325static 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
350static 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
357static 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
362static void afs_proc_servers_stop(struct seq_file *m, void *v)
363 __releases(rcu)
364{
365 rcu_read_unlock();
366}
367
368static 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
377
378
379static 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
390static 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
404static 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
415static 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
423static 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
432
433static 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
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;
498out:
499 afs_put_sysnames(kill);
500 return ret;
501
502invalid:
503 ret = -EINVAL;
504error:
505 goto out;
506}
507
508void 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
522
523static 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
553
554int 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
578error_tree:
579 remove_proc_subtree(cell->name, net->proc_afs);
580error_dir:
581 _leave(" = -ENOMEM");
582 return -ENOMEM;
583}
584
585
586
587
588void 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
599
600int 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
633error_tree:
634 proc_remove(p);
635error_dir:
636 _leave(" = -ENOMEM");
637 return -ENOMEM;
638}
639
640
641
642
643void afs_proc_cleanup(struct afs_net *net)
644{
645 proc_remove(net->proc_afs);
646 net->proc_afs = NULL;
647}
648