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 } 648