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 struct afs_vl_seq_net_private { 21 struct seq_net_private seq; /* Must be first */ 22 struct afs_vlserver_list *vllist; 23 }; 24 25 static inline struct afs_net *afs_seq2net(struct seq_file *m) 26 { 27 return afs_net(seq_file_net(m)); 28 } 29 30 static inline struct afs_net *afs_seq2net_single(struct seq_file *m) 31 { 32 return afs_net(seq_file_single_net(m)); 33 } 34 35 /* 36 * Display the list of cells known to the namespace. 37 */ 38 static int afs_proc_cells_show(struct seq_file *m, void *v) 39 { 40 struct afs_vlserver_list *vllist; 41 struct afs_cell *cell; 42 43 if (v == SEQ_START_TOKEN) { 44 /* display header on line 1 */ 45 seq_puts(m, "USE TTL SV NAME\n"); 46 return 0; 47 } 48 49 cell = list_entry(v, struct afs_cell, proc_link); 50 vllist = rcu_dereference(cell->vl_servers); 51 52 /* display one cell per line on subsequent lines */ 53 seq_printf(m, "%3u %6lld %2u %s\n", 54 atomic_read(&cell->usage), 55 cell->dns_expiry - ktime_get_real_seconds(), 56 vllist ? vllist->nr_servers : 0, 57 cell->name); 58 return 0; 59 } 60 61 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) 62 __acquires(rcu) 63 { 64 rcu_read_lock(); 65 return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos); 66 } 67 68 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos) 69 { 70 return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos); 71 } 72 73 static void afs_proc_cells_stop(struct seq_file *m, void *v) 74 __releases(rcu) 75 { 76 rcu_read_unlock(); 77 } 78 79 static const struct seq_operations afs_proc_cells_ops = { 80 .start = afs_proc_cells_start, 81 .next = afs_proc_cells_next, 82 .stop = afs_proc_cells_stop, 83 .show = afs_proc_cells_show, 84 }; 85 86 /* 87 * handle writes to /proc/fs/afs/cells 88 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]" 89 */ 90 static int afs_proc_cells_write(struct file *file, char *buf, size_t size) 91 { 92 struct seq_file *m = file->private_data; 93 struct afs_net *net = afs_seq2net(m); 94 char *name, *args; 95 int ret; 96 97 /* trim to first NL */ 98 name = memchr(buf, '\n', size); 99 if (name) 100 *name = 0; 101 102 /* split into command, name and argslist */ 103 name = strchr(buf, ' '); 104 if (!name) 105 goto inval; 106 do { 107 *name++ = 0; 108 } while(*name == ' '); 109 if (!*name) 110 goto inval; 111 112 args = strchr(name, ' '); 113 if (args) { 114 do { 115 *args++ = 0; 116 } while(*args == ' '); 117 if (!*args) 118 goto inval; 119 } 120 121 /* determine command to perform */ 122 _debug("cmd=%s name=%s args=%s", buf, name, args); 123 124 if (strcmp(buf, "add") == 0) { 125 struct afs_cell *cell; 126 127 cell = afs_lookup_cell(net, name, strlen(name), args, true); 128 if (IS_ERR(cell)) { 129 ret = PTR_ERR(cell); 130 goto done; 131 } 132 133 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags)) 134 afs_put_cell(net, cell); 135 } else { 136 goto inval; 137 } 138 139 ret = 0; 140 141 done: 142 _leave(" = %d", ret); 143 return ret; 144 145 inval: 146 ret = -EINVAL; 147 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); 148 goto done; 149 } 150 151 /* 152 * Display the name of the current workstation cell. 153 */ 154 static int afs_proc_rootcell_show(struct seq_file *m, void *v) 155 { 156 struct afs_cell *cell; 157 struct afs_net *net; 158 159 net = afs_seq2net_single(m); 160 if (rcu_access_pointer(net->ws_cell)) { 161 rcu_read_lock(); 162 cell = rcu_dereference(net->ws_cell); 163 if (cell) 164 seq_printf(m, "%s\n", cell->name); 165 rcu_read_unlock(); 166 } 167 return 0; 168 } 169 170 /* 171 * Set the current workstation cell and optionally supply its list of volume 172 * location servers. 173 * 174 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell 175 */ 176 static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size) 177 { 178 struct seq_file *m = file->private_data; 179 struct afs_net *net = afs_seq2net_single(m); 180 char *s; 181 int ret; 182 183 ret = -EINVAL; 184 if (buf[0] == '.') 185 goto out; 186 if (memchr(buf, '/', size)) 187 goto out; 188 189 /* trim to first NL */ 190 s = memchr(buf, '\n', size); 191 if (s) 192 *s = 0; 193 194 /* determine command to perform */ 195 _debug("rootcell=%s", buf); 196 197 ret = afs_cell_init(net, buf); 198 199 out: 200 _leave(" = %d", ret); 201 return ret; 202 } 203 204 static const char afs_vol_types[3][3] = { 205 [AFSVL_RWVOL] = "RW", 206 [AFSVL_ROVOL] = "RO", 207 [AFSVL_BACKVOL] = "BK", 208 }; 209 210 /* 211 * Display the list of volumes known to a cell. 212 */ 213 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) 214 { 215 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 216 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link); 217 218 /* Display header on line 1 */ 219 if (v == &cell->proc_volumes) { 220 seq_puts(m, "USE VID TY\n"); 221 return 0; 222 } 223 224 seq_printf(m, "%3d %08llx %s\n", 225 atomic_read(&vol->usage), vol->vid, 226 afs_vol_types[vol->type]); 227 228 return 0; 229 } 230 231 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) 232 __acquires(cell->proc_lock) 233 { 234 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 235 236 read_lock(&cell->proc_lock); 237 return seq_list_start_head(&cell->proc_volumes, *_pos); 238 } 239 240 static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v, 241 loff_t *_pos) 242 { 243 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 244 245 return seq_list_next(v, &cell->proc_volumes, _pos); 246 } 247 248 static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v) 249 __releases(cell->proc_lock) 250 { 251 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 252 253 read_unlock(&cell->proc_lock); 254 } 255 256 static const struct seq_operations afs_proc_cell_volumes_ops = { 257 .start = afs_proc_cell_volumes_start, 258 .next = afs_proc_cell_volumes_next, 259 .stop = afs_proc_cell_volumes_stop, 260 .show = afs_proc_cell_volumes_show, 261 }; 262 263 static const char *const dns_record_sources[NR__dns_record_source + 1] = { 264 [DNS_RECORD_UNAVAILABLE] = "unav", 265 [DNS_RECORD_FROM_CONFIG] = "cfg", 266 [DNS_RECORD_FROM_DNS_A] = "A", 267 [DNS_RECORD_FROM_DNS_AFSDB] = "AFSDB", 268 [DNS_RECORD_FROM_DNS_SRV] = "SRV", 269 [DNS_RECORD_FROM_NSS] = "nss", 270 [NR__dns_record_source] = "[weird]" 271 }; 272 273 static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = { 274 [DNS_LOOKUP_NOT_DONE] = "no-lookup", 275 [DNS_LOOKUP_GOOD] = "good", 276 [DNS_LOOKUP_GOOD_WITH_BAD] = "good/bad", 277 [DNS_LOOKUP_BAD] = "bad", 278 [DNS_LOOKUP_GOT_NOT_FOUND] = "not-found", 279 [DNS_LOOKUP_GOT_LOCAL_FAILURE] = "local-failure", 280 [DNS_LOOKUP_GOT_TEMP_FAILURE] = "temp-failure", 281 [DNS_LOOKUP_GOT_NS_FAILURE] = "ns-failure", 282 [NR__dns_lookup_status] = "[weird]" 283 }; 284 285 /* 286 * Display the list of Volume Location servers we're using for a cell. 287 */ 288 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) 289 { 290 const struct afs_vl_seq_net_private *priv = m->private; 291 const struct afs_vlserver_list *vllist = priv->vllist; 292 const struct afs_vlserver_entry *entry; 293 const struct afs_vlserver *vlserver; 294 const struct afs_addr_list *alist; 295 int i; 296 297 if (v == SEQ_START_TOKEN) { 298 seq_printf(m, "# source %s, status %s\n", 299 dns_record_sources[vllist->source], 300 dns_lookup_statuses[vllist->status]); 301 return 0; 302 } 303 304 entry = v; 305 vlserver = entry->server; 306 alist = rcu_dereference(vlserver->addresses); 307 308 seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n", 309 vlserver->name, entry->priority, entry->weight, 310 dns_record_sources[alist ? alist->source : entry->source], 311 dns_lookup_statuses[alist ? alist->status : entry->status]); 312 if (alist) { 313 for (i = 0; i < alist->nr_addrs; i++) 314 seq_printf(m, " %c %pISpc\n", 315 alist->preferred == i ? '>' : '-', 316 &alist->addrs[i].transport); 317 } 318 return 0; 319 } 320 321 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) 322 __acquires(rcu) 323 { 324 struct afs_vl_seq_net_private *priv = m->private; 325 struct afs_vlserver_list *vllist; 326 struct afs_cell *cell = PDE_DATA(file_inode(m->file)); 327 loff_t pos = *_pos; 328 329 rcu_read_lock(); 330 331 vllist = rcu_dereference(cell->vl_servers); 332 priv->vllist = vllist; 333 334 if (pos < 0) 335 *_pos = pos = 0; 336 if (pos == 0) 337 return SEQ_START_TOKEN; 338 339 if (!vllist || pos - 1 >= vllist->nr_servers) 340 return NULL; 341 342 return &vllist->servers[pos - 1]; 343 } 344 345 static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v, 346 loff_t *_pos) 347 { 348 struct afs_vl_seq_net_private *priv = m->private; 349 struct afs_vlserver_list *vllist = priv->vllist; 350 loff_t pos; 351 352 pos = *_pos; 353 pos++; 354 *_pos = pos; 355 if (!vllist || pos - 1 >= vllist->nr_servers) 356 return NULL; 357 358 return &vllist->servers[pos - 1]; 359 } 360 361 static void afs_proc_cell_vlservers_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_cell_vlservers_ops = { 368 .start = afs_proc_cell_vlservers_start, 369 .next = afs_proc_cell_vlservers_next, 370 .stop = afs_proc_cell_vlservers_stop, 371 .show = afs_proc_cell_vlservers_show, 372 }; 373 374 /* 375 * Display the list of fileservers we're using within a namespace. 376 */ 377 static int afs_proc_servers_show(struct seq_file *m, void *v) 378 { 379 struct afs_server *server; 380 struct afs_addr_list *alist; 381 int i; 382 383 if (v == SEQ_START_TOKEN) { 384 seq_puts(m, "UUID USE ADDR\n"); 385 return 0; 386 } 387 388 server = list_entry(v, struct afs_server, proc_link); 389 alist = rcu_dereference(server->addresses); 390 seq_printf(m, "%pU %3d %pISpc%s\n", 391 &server->uuid, 392 atomic_read(&server->usage), 393 &alist->addrs[0].transport, 394 alist->preferred == 0 ? "*" : ""); 395 for (i = 1; i < alist->nr_addrs; i++) 396 seq_printf(m, " %pISpc%s\n", 397 &alist->addrs[i].transport, 398 alist->preferred == i ? "*" : ""); 399 return 0; 400 } 401 402 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) 403 __acquires(rcu) 404 { 405 rcu_read_lock(); 406 return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos); 407 } 408 409 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos) 410 { 411 return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos); 412 } 413 414 static void afs_proc_servers_stop(struct seq_file *m, void *v) 415 __releases(rcu) 416 { 417 rcu_read_unlock(); 418 } 419 420 static const struct seq_operations afs_proc_servers_ops = { 421 .start = afs_proc_servers_start, 422 .next = afs_proc_servers_next, 423 .stop = afs_proc_servers_stop, 424 .show = afs_proc_servers_show, 425 }; 426 427 /* 428 * Display the list of strings that may be substituted for the @sys pathname 429 * macro. 430 */ 431 static int afs_proc_sysname_show(struct seq_file *m, void *v) 432 { 433 struct afs_net *net = afs_seq2net(m); 434 struct afs_sysnames *sysnames = net->sysnames; 435 unsigned int i = (unsigned long)v - 1; 436 437 if (i < sysnames->nr) 438 seq_printf(m, "%s\n", sysnames->subs[i]); 439 return 0; 440 } 441 442 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos) 443 __acquires(&net->sysnames_lock) 444 { 445 struct afs_net *net = afs_seq2net(m); 446 struct afs_sysnames *names; 447 448 read_lock(&net->sysnames_lock); 449 450 names = net->sysnames; 451 if (*pos >= names->nr) 452 return NULL; 453 return (void *)(unsigned long)(*pos + 1); 454 } 455 456 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos) 457 { 458 struct afs_net *net = afs_seq2net(m); 459 struct afs_sysnames *names = net->sysnames; 460 461 *pos += 1; 462 if (*pos >= names->nr) 463 return NULL; 464 return (void *)(unsigned long)(*pos + 1); 465 } 466 467 static void afs_proc_sysname_stop(struct seq_file *m, void *v) 468 __releases(&net->sysnames_lock) 469 { 470 struct afs_net *net = afs_seq2net(m); 471 472 read_unlock(&net->sysnames_lock); 473 } 474 475 static const struct seq_operations afs_proc_sysname_ops = { 476 .start = afs_proc_sysname_start, 477 .next = afs_proc_sysname_next, 478 .stop = afs_proc_sysname_stop, 479 .show = afs_proc_sysname_show, 480 }; 481 482 /* 483 * Allow the @sys substitution to be configured. 484 */ 485 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size) 486 { 487 struct afs_sysnames *sysnames, *kill; 488 struct seq_file *m = file->private_data; 489 struct afs_net *net = afs_seq2net(m); 490 char *s, *p, *sub; 491 int ret, len; 492 493 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); 494 if (!sysnames) 495 return -ENOMEM; 496 refcount_set(&sysnames->usage, 1); 497 kill = sysnames; 498 499 p = buf; 500 while ((s = strsep(&p, " \t\n"))) { 501 len = strlen(s); 502 if (len == 0) 503 continue; 504 ret = -ENAMETOOLONG; 505 if (len >= AFSNAMEMAX) 506 goto error; 507 508 if (len >= 4 && 509 s[len - 4] == '@' && 510 s[len - 3] == 's' && 511 s[len - 2] == 'y' && 512 s[len - 1] == 's') 513 /* Protect against recursion */ 514 goto invalid; 515 516 if (s[0] == '.' && 517 (len < 2 || (len == 2 && s[1] == '.'))) 518 goto invalid; 519 520 if (memchr(s, '/', len)) 521 goto invalid; 522 523 ret = -EFBIG; 524 if (sysnames->nr >= AFS_NR_SYSNAME) 525 goto out; 526 527 if (strcmp(s, afs_init_sysname) == 0) { 528 sub = (char *)afs_init_sysname; 529 } else { 530 ret = -ENOMEM; 531 sub = kmemdup(s, len + 1, GFP_KERNEL); 532 if (!sub) 533 goto out; 534 } 535 536 sysnames->subs[sysnames->nr] = sub; 537 sysnames->nr++; 538 } 539 540 if (sysnames->nr == 0) { 541 sysnames->subs[0] = sysnames->blank; 542 sysnames->nr++; 543 } 544 545 write_lock(&net->sysnames_lock); 546 kill = net->sysnames; 547 net->sysnames = sysnames; 548 write_unlock(&net->sysnames_lock); 549 ret = 0; 550 out: 551 afs_put_sysnames(kill); 552 return ret; 553 554 invalid: 555 ret = -EINVAL; 556 error: 557 goto out; 558 } 559 560 void afs_put_sysnames(struct afs_sysnames *sysnames) 561 { 562 int i; 563 564 if (sysnames && refcount_dec_and_test(&sysnames->usage)) { 565 for (i = 0; i < sysnames->nr; i++) 566 if (sysnames->subs[i] != afs_init_sysname && 567 sysnames->subs[i] != sysnames->blank) 568 kfree(sysnames->subs[i]); 569 } 570 } 571 572 /* 573 * Display general per-net namespace statistics 574 */ 575 static int afs_proc_stats_show(struct seq_file *m, void *v) 576 { 577 struct afs_net *net = afs_seq2net_single(m); 578 579 seq_puts(m, "kAFS statistics\n"); 580 581 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n", 582 atomic_read(&net->n_lookup), 583 atomic_read(&net->n_reval), 584 atomic_read(&net->n_inval), 585 atomic_read(&net->n_relpg)); 586 587 seq_printf(m, "dir-data: rdpg=%u\n", 588 atomic_read(&net->n_read_dir)); 589 590 seq_printf(m, "dir-edit: cr=%u rm=%u\n", 591 atomic_read(&net->n_dir_cr), 592 atomic_read(&net->n_dir_rm)); 593 594 seq_printf(m, "file-rd : n=%u nb=%lu\n", 595 atomic_read(&net->n_fetches), 596 atomic_long_read(&net->n_fetch_bytes)); 597 seq_printf(m, "file-wr : n=%u nb=%lu\n", 598 atomic_read(&net->n_stores), 599 atomic_long_read(&net->n_store_bytes)); 600 return 0; 601 } 602 603 /* 604 * initialise /proc/fs/afs/<cell>/ 605 */ 606 int afs_proc_cell_setup(struct afs_cell *cell) 607 { 608 struct proc_dir_entry *dir; 609 struct afs_net *net = cell->net; 610 611 _enter("%p{%s},%p", cell, cell->name, net->proc_afs); 612 613 dir = proc_net_mkdir(net->net, cell->name, net->proc_afs); 614 if (!dir) 615 goto error_dir; 616 617 if (!proc_create_net_data("vlservers", 0444, dir, 618 &afs_proc_cell_vlservers_ops, 619 sizeof(struct afs_vl_seq_net_private), 620 cell) || 621 !proc_create_net_data("volumes", 0444, dir, 622 &afs_proc_cell_volumes_ops, 623 sizeof(struct seq_net_private), 624 cell)) 625 goto error_tree; 626 627 _leave(" = 0"); 628 return 0; 629 630 error_tree: 631 remove_proc_subtree(cell->name, net->proc_afs); 632 error_dir: 633 _leave(" = -ENOMEM"); 634 return -ENOMEM; 635 } 636 637 /* 638 * remove /proc/fs/afs/<cell>/ 639 */ 640 void afs_proc_cell_remove(struct afs_cell *cell) 641 { 642 struct afs_net *net = cell->net; 643 644 _enter(""); 645 remove_proc_subtree(cell->name, net->proc_afs); 646 _leave(""); 647 } 648 649 /* 650 * initialise the /proc/fs/afs/ directory 651 */ 652 int afs_proc_init(struct afs_net *net) 653 { 654 struct proc_dir_entry *p; 655 656 _enter(""); 657 658 p = proc_net_mkdir(net->net, "afs", net->net->proc_net); 659 if (!p) 660 goto error_dir; 661 662 if (!proc_create_net_data_write("cells", 0644, p, 663 &afs_proc_cells_ops, 664 afs_proc_cells_write, 665 sizeof(struct seq_net_private), 666 NULL) || 667 !proc_create_net_single_write("rootcell", 0644, p, 668 afs_proc_rootcell_show, 669 afs_proc_rootcell_write, 670 NULL) || 671 !proc_create_net("servers", 0444, p, &afs_proc_servers_ops, 672 sizeof(struct seq_net_private)) || 673 !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) || 674 !proc_create_net_data_write("sysname", 0644, p, 675 &afs_proc_sysname_ops, 676 afs_proc_sysname_write, 677 sizeof(struct seq_net_private), 678 NULL)) 679 goto error_tree; 680 681 net->proc_afs = p; 682 _leave(" = 0"); 683 return 0; 684 685 error_tree: 686 proc_remove(p); 687 error_dir: 688 _leave(" = -ENOMEM"); 689 return -ENOMEM; 690 } 691 692 /* 693 * clean up the /proc/fs/afs/ directory 694 */ 695 void afs_proc_cleanup(struct afs_net *net) 696 { 697 proc_remove(net->proc_afs); 698 net->proc_afs = NULL; 699 } 700