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