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 do { 103 *args++ = 0; 104 } while(*args == ' '); 105 if (!*args) 106 goto inval; 107 } 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 } else { 124 goto inval; 125 } 126 127 ret = 0; 128 129 done: 130 _leave(" = %d", ret); 131 return ret; 132 133 inval: 134 ret = -EINVAL; 135 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); 136 goto done; 137 } 138 139 /* 140 * Display the name of the current workstation cell. 141 */ 142 static int afs_proc_rootcell_show(struct seq_file *m, void *v) 143 { 144 struct afs_cell *cell; 145 struct afs_net *net; 146 147 net = afs_seq2net_single(m); 148 if (rcu_access_pointer(net->ws_cell)) { 149 rcu_read_lock(); 150 cell = rcu_dereference(net->ws_cell); 151 if (cell) 152 seq_printf(m, "%s\n", cell->name); 153 rcu_read_unlock(); 154 } 155 return 0; 156 } 157 158 /* 159 * Set the current workstation cell and optionally supply its list of volume 160 * location servers. 161 * 162 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell 163 */ 164 static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size) 165 { 166 struct seq_file *m = file->private_data; 167 struct afs_net *net = afs_seq2net_single(m); 168 char *s; 169 int ret; 170 171 ret = -EINVAL; 172 if (buf[0] == '.') 173 goto out; 174 if (memchr(buf, '/', size)) 175 goto out; 176 177 /* trim to first NL */ 178 s = memchr(buf, '\n', size); 179 if (s) 180 *s = 0; 181 182 /* determine command to perform */ 183 _debug("rootcell=%s", buf); 184 185 ret = afs_cell_init(net, buf); 186 187 out: 188 _leave(" = %d", ret); 189 return ret; 190 } 191 192 static const char afs_vol_types[3][3] = { 193 [AFSVL_RWVOL] = "RW", 194 [AFSVL_ROVOL] = "RO", 195 [AFSVL_BACKVOL] = "BK", 196 }; 197 198 /* 199 * Display the list of volumes known to a cell. 200 */ 201 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) 202 { 203 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 204 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link); 205 206 /* Display header on line 1 */ 207 if (v == &cell->proc_volumes) { 208 seq_puts(m, "USE VID TY\n"); 209 return 0; 210 } 211 212 seq_printf(m, "%3d %08x %s\n", 213 atomic_read(&vol->usage), vol->vid, 214 afs_vol_types[vol->type]); 215 216 return 0; 217 } 218 219 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) 220 __acquires(cell->proc_lock) 221 { 222 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 223 224 read_lock(&cell->proc_lock); 225 return seq_list_start_head(&cell->proc_volumes, *_pos); 226 } 227 228 static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v, 229 loff_t *_pos) 230 { 231 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 232 233 return seq_list_next(v, &cell->proc_volumes, _pos); 234 } 235 236 static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v) 237 __releases(cell->proc_lock) 238 { 239 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 240 241 read_unlock(&cell->proc_lock); 242 } 243 244 static const struct seq_operations afs_proc_cell_volumes_ops = { 245 .start = afs_proc_cell_volumes_start, 246 .next = afs_proc_cell_volumes_next, 247 .stop = afs_proc_cell_volumes_stop, 248 .show = afs_proc_cell_volumes_show, 249 }; 250 251 /* 252 * Display the list of Volume Location servers we're using for a cell. 253 */ 254 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) 255 { 256 struct sockaddr_rxrpc *addr = v; 257 258 /* display header on line 1 */ 259 if (v == (void *)1) { 260 seq_puts(m, "ADDRESS\n"); 261 return 0; 262 } 263 264 /* display one cell per line on subsequent lines */ 265 seq_printf(m, "%pISp\n", &addr->transport); 266 return 0; 267 } 268 269 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) 270 __acquires(rcu) 271 { 272 struct afs_addr_list *alist; 273 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 274 loff_t pos = *_pos; 275 276 rcu_read_lock(); 277 278 alist = rcu_dereference(cell->vl_addrs); 279 280 /* allow for the header line */ 281 if (!pos) 282 return (void *) 1; 283 pos--; 284 285 if (!alist || pos >= alist->nr_addrs) 286 return NULL; 287 288 return alist->addrs + pos; 289 } 290 291 static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v, 292 loff_t *_pos) 293 { 294 struct afs_addr_list *alist; 295 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 296 loff_t pos; 297 298 alist = rcu_dereference(cell->vl_addrs); 299 300 pos = *_pos; 301 (*_pos)++; 302 if (!alist || pos >= alist->nr_addrs) 303 return NULL; 304 305 return alist->addrs + pos; 306 } 307 308 static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v) 309 __releases(rcu) 310 { 311 rcu_read_unlock(); 312 } 313 314 static const struct seq_operations afs_proc_cell_vlservers_ops = { 315 .start = afs_proc_cell_vlservers_start, 316 .next = afs_proc_cell_vlservers_next, 317 .stop = afs_proc_cell_vlservers_stop, 318 .show = afs_proc_cell_vlservers_show, 319 }; 320 321 /* 322 * Display the list of fileservers we're using within a namespace. 323 */ 324 static int afs_proc_servers_show(struct seq_file *m, void *v) 325 { 326 struct afs_server *server; 327 struct afs_addr_list *alist; 328 int i; 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 %pISpc%s\n", 338 &server->uuid, 339 atomic_read(&server->usage), 340 &alist->addrs[0].transport, 341 alist->index == 0 ? "*" : ""); 342 for (i = 1; i < alist->nr_addrs; i++) 343 seq_printf(m, " %pISpc%s\n", 344 &alist->addrs[i].transport, 345 alist->index == i ? "*" : ""); 346 return 0; 347 } 348 349 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) 350 __acquires(rcu) 351 { 352 rcu_read_lock(); 353 return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos); 354 } 355 356 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos) 357 { 358 return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos); 359 } 360 361 static void afs_proc_servers_stop(struct seq_file *m, void *v) 362 __releases(rcu) 363 { 364 rcu_read_unlock(); 365 } 366 367 static const struct seq_operations afs_proc_servers_ops = { 368 .start = afs_proc_servers_start, 369 .next = afs_proc_servers_next, 370 .stop = afs_proc_servers_stop, 371 .show = afs_proc_servers_show, 372 }; 373 374 /* 375 * Display the list of strings that may be substituted for the @sys pathname 376 * macro. 377 */ 378 static int afs_proc_sysname_show(struct seq_file *m, void *v) 379 { 380 struct afs_net *net = afs_seq2net(m); 381 struct afs_sysnames *sysnames = net->sysnames; 382 unsigned int i = (unsigned long)v - 1; 383 384 if (i < sysnames->nr) 385 seq_printf(m, "%s\n", sysnames->subs[i]); 386 return 0; 387 } 388 389 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos) 390 __acquires(&net->sysnames_lock) 391 { 392 struct afs_net *net = afs_seq2net(m); 393 struct afs_sysnames *names; 394 395 read_lock(&net->sysnames_lock); 396 397 names = net->sysnames; 398 if (*pos >= names->nr) 399 return NULL; 400 return (void *)(unsigned long)(*pos + 1); 401 } 402 403 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos) 404 { 405 struct afs_net *net = afs_seq2net(m); 406 struct afs_sysnames *names = net->sysnames; 407 408 *pos += 1; 409 if (*pos >= names->nr) 410 return NULL; 411 return (void *)(unsigned long)(*pos + 1); 412 } 413 414 static void afs_proc_sysname_stop(struct seq_file *m, void *v) 415 __releases(&net->sysnames_lock) 416 { 417 struct afs_net *net = afs_seq2net(m); 418 419 read_unlock(&net->sysnames_lock); 420 } 421 422 static const struct seq_operations afs_proc_sysname_ops = { 423 .start = afs_proc_sysname_start, 424 .next = afs_proc_sysname_next, 425 .stop = afs_proc_sysname_stop, 426 .show = afs_proc_sysname_show, 427 }; 428 429 /* 430 * Allow the @sys substitution to be configured. 431 */ 432 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size) 433 { 434 struct afs_sysnames *sysnames, *kill; 435 struct seq_file *m = file->private_data; 436 struct afs_net *net = afs_seq2net(m); 437 char *s, *p, *sub; 438 int ret, len; 439 440 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); 441 if (!sysnames) 442 return -ENOMEM; 443 refcount_set(&sysnames->usage, 1); 444 kill = sysnames; 445 446 p = buf; 447 while ((s = strsep(&p, " \t\n"))) { 448 len = strlen(s); 449 if (len == 0) 450 continue; 451 ret = -ENAMETOOLONG; 452 if (len >= AFSNAMEMAX) 453 goto error; 454 455 if (len >= 4 && 456 s[len - 4] == '@' && 457 s[len - 3] == 's' && 458 s[len - 2] == 'y' && 459 s[len - 1] == 's') 460 /* Protect against recursion */ 461 goto invalid; 462 463 if (s[0] == '.' && 464 (len < 2 || (len == 2 && s[1] == '.'))) 465 goto invalid; 466 467 if (memchr(s, '/', len)) 468 goto invalid; 469 470 ret = -EFBIG; 471 if (sysnames->nr >= AFS_NR_SYSNAME) 472 goto out; 473 474 if (strcmp(s, afs_init_sysname) == 0) { 475 sub = (char *)afs_init_sysname; 476 } else { 477 ret = -ENOMEM; 478 sub = kmemdup(s, len + 1, GFP_KERNEL); 479 if (!sub) 480 goto out; 481 } 482 483 sysnames->subs[sysnames->nr] = sub; 484 sysnames->nr++; 485 } 486 487 if (sysnames->nr == 0) { 488 sysnames->subs[0] = sysnames->blank; 489 sysnames->nr++; 490 } 491 492 write_lock(&net->sysnames_lock); 493 kill = net->sysnames; 494 net->sysnames = sysnames; 495 write_unlock(&net->sysnames_lock); 496 ret = 0; 497 out: 498 afs_put_sysnames(kill); 499 return ret; 500 501 invalid: 502 ret = -EINVAL; 503 error: 504 goto out; 505 } 506 507 void afs_put_sysnames(struct afs_sysnames *sysnames) 508 { 509 int i; 510 511 if (sysnames && refcount_dec_and_test(&sysnames->usage)) { 512 for (i = 0; i < sysnames->nr; i++) 513 if (sysnames->subs[i] != afs_init_sysname && 514 sysnames->subs[i] != sysnames->blank) 515 kfree(sysnames->subs[i]); 516 } 517 } 518 519 /* 520 * Display general per-net namespace statistics 521 */ 522 static int afs_proc_stats_show(struct seq_file *m, void *v) 523 { 524 struct afs_net *net = afs_seq2net_single(m); 525 526 seq_puts(m, "kAFS statistics\n"); 527 528 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n", 529 atomic_read(&net->n_lookup), 530 atomic_read(&net->n_reval), 531 atomic_read(&net->n_inval), 532 atomic_read(&net->n_relpg)); 533 534 seq_printf(m, "dir-data: rdpg=%u\n", 535 atomic_read(&net->n_read_dir)); 536 537 seq_printf(m, "dir-edit: cr=%u rm=%u\n", 538 atomic_read(&net->n_dir_cr), 539 atomic_read(&net->n_dir_rm)); 540 541 seq_printf(m, "file-rd : n=%u nb=%lu\n", 542 atomic_read(&net->n_fetches), 543 atomic_long_read(&net->n_fetch_bytes)); 544 seq_printf(m, "file-wr : n=%u nb=%lu\n", 545 atomic_read(&net->n_stores), 546 atomic_long_read(&net->n_store_bytes)); 547 return 0; 548 } 549 550 /* 551 * initialise /proc/fs/afs/<cell>/ 552 */ 553 int afs_proc_cell_setup(struct afs_cell *cell) 554 { 555 struct proc_dir_entry *dir; 556 struct afs_net *net = cell->net; 557 558 _enter("%p{%s},%p", cell, cell->name, net->proc_afs); 559 560 dir = proc_net_mkdir(net->net, cell->name, net->proc_afs); 561 if (!dir) 562 goto error_dir; 563 564 if (!proc_create_net_data("vlservers", 0444, dir, 565 &afs_proc_cell_vlservers_ops, 566 sizeof(struct seq_net_private), 567 cell) || 568 !proc_create_net_data("volumes", 0444, dir, 569 &afs_proc_cell_volumes_ops, 570 sizeof(struct seq_net_private), 571 cell)) 572 goto error_tree; 573 574 _leave(" = 0"); 575 return 0; 576 577 error_tree: 578 remove_proc_subtree(cell->name, net->proc_afs); 579 error_dir: 580 _leave(" = -ENOMEM"); 581 return -ENOMEM; 582 } 583 584 /* 585 * remove /proc/fs/afs/<cell>/ 586 */ 587 void afs_proc_cell_remove(struct afs_cell *cell) 588 { 589 struct afs_net *net = cell->net; 590 591 _enter(""); 592 remove_proc_subtree(cell->name, net->proc_afs); 593 _leave(""); 594 } 595 596 /* 597 * initialise the /proc/fs/afs/ directory 598 */ 599 int afs_proc_init(struct afs_net *net) 600 { 601 struct proc_dir_entry *p; 602 603 _enter(""); 604 605 p = proc_net_mkdir(net->net, "afs", net->net->proc_net); 606 if (!p) 607 goto error_dir; 608 609 if (!proc_create_net_data_write("cells", 0644, p, 610 &afs_proc_cells_ops, 611 afs_proc_cells_write, 612 sizeof(struct seq_net_private), 613 NULL) || 614 !proc_create_net_single_write("rootcell", 0644, p, 615 afs_proc_rootcell_show, 616 afs_proc_rootcell_write, 617 NULL) || 618 !proc_create_net("servers", 0444, p, &afs_proc_servers_ops, 619 sizeof(struct seq_net_private)) || 620 !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) || 621 !proc_create_net_data_write("sysname", 0644, p, 622 &afs_proc_sysname_ops, 623 afs_proc_sysname_write, 624 sizeof(struct seq_net_private), 625 NULL)) 626 goto error_tree; 627 628 net->proc_afs = p; 629 _leave(" = 0"); 630 return 0; 631 632 error_tree: 633 proc_remove(p); 634 error_dir: 635 _leave(" = -ENOMEM"); 636 return -ENOMEM; 637 } 638 639 /* 640 * clean up the /proc/fs/afs/ directory 641 */ 642 void afs_proc_cleanup(struct afs_net *net) 643 { 644 proc_remove(net->proc_afs); 645 net->proc_afs = NULL; 646 } 647