Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.19-rc6 646 lines 15 kB view raw
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 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 * Display the list of cells known to the namespace. 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 /* 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 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 * handle writes to /proc/fs/afs/cells 76 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]" 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 /* 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 129done: 130 _leave(" = %d", ret); 131 return ret; 132 133inval: 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 */ 142static 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 */ 164static 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 187out: 188 _leave(" = %d", ret); 189 return ret; 190} 191 192static 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 */ 201static 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 219static 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 228static 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 236static 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 244static 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 */ 254static 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 269static 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 291static 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 308static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v) 309 __releases(rcu) 310{ 311 rcu_read_unlock(); 312} 313 314static 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 */ 324static 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 349static 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 356static 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 361static void afs_proc_servers_stop(struct seq_file *m, void *v) 362 __releases(rcu) 363{ 364 rcu_read_unlock(); 365} 366 367static 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 */ 378static 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 389static 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 403static 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 414static 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 422static 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 */ 432static 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; 497out: 498 afs_put_sysnames(kill); 499 return ret; 500 501invalid: 502 ret = -EINVAL; 503error: 504 goto out; 505} 506 507void 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 */ 522static 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 */ 553int 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 577error_tree: 578 remove_proc_subtree(cell->name, net->proc_afs); 579error_dir: 580 _leave(" = -ENOMEM"); 581 return -ENOMEM; 582} 583 584/* 585 * remove /proc/fs/afs/<cell>/ 586 */ 587void 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 */ 599int 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 632error_tree: 633 proc_remove(p); 634error_dir: 635 _leave(" = -ENOMEM"); 636 return -ENOMEM; 637} 638 639/* 640 * clean up the /proc/fs/afs/ directory 641 */ 642void afs_proc_cleanup(struct afs_net *net) 643{ 644 proc_remove(net->proc_afs); 645 net->proc_afs = NULL; 646}