at v4.16 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_proc2net(struct file *f) 21{ 22 return &__afs_net; 23} 24 25static inline struct afs_net *afs_seq2net(struct seq_file *m) 26{ 27 return &__afs_net; // TODO: use seq_file_net(m) 28} 29 30static int afs_proc_cells_open(struct inode *inode, struct file *file); 31static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); 32static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); 33static void afs_proc_cells_stop(struct seq_file *p, void *v); 34static int afs_proc_cells_show(struct seq_file *m, void *v); 35static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, 36 size_t size, loff_t *_pos); 37 38static const struct seq_operations afs_proc_cells_ops = { 39 .start = afs_proc_cells_start, 40 .next = afs_proc_cells_next, 41 .stop = afs_proc_cells_stop, 42 .show = afs_proc_cells_show, 43}; 44 45static const struct file_operations afs_proc_cells_fops = { 46 .open = afs_proc_cells_open, 47 .read = seq_read, 48 .write = afs_proc_cells_write, 49 .llseek = seq_lseek, 50 .release = seq_release, 51}; 52 53static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 54 size_t size, loff_t *_pos); 55static ssize_t afs_proc_rootcell_write(struct file *file, 56 const char __user *buf, 57 size_t size, loff_t *_pos); 58 59static const struct file_operations afs_proc_rootcell_fops = { 60 .read = afs_proc_rootcell_read, 61 .write = afs_proc_rootcell_write, 62 .llseek = no_llseek, 63}; 64 65static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file); 66static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); 67static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 68 loff_t *pos); 69static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); 70static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); 71 72static const struct seq_operations afs_proc_cell_volumes_ops = { 73 .start = afs_proc_cell_volumes_start, 74 .next = afs_proc_cell_volumes_next, 75 .stop = afs_proc_cell_volumes_stop, 76 .show = afs_proc_cell_volumes_show, 77}; 78 79static const struct file_operations afs_proc_cell_volumes_fops = { 80 .open = afs_proc_cell_volumes_open, 81 .read = seq_read, 82 .llseek = seq_lseek, 83 .release = seq_release, 84}; 85 86static int afs_proc_cell_vlservers_open(struct inode *inode, 87 struct file *file); 88static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); 89static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 90 loff_t *pos); 91static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); 92static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); 93 94static const struct seq_operations afs_proc_cell_vlservers_ops = { 95 .start = afs_proc_cell_vlservers_start, 96 .next = afs_proc_cell_vlservers_next, 97 .stop = afs_proc_cell_vlservers_stop, 98 .show = afs_proc_cell_vlservers_show, 99}; 100 101static const struct file_operations afs_proc_cell_vlservers_fops = { 102 .open = afs_proc_cell_vlservers_open, 103 .read = seq_read, 104 .llseek = seq_lseek, 105 .release = seq_release, 106}; 107 108static int afs_proc_servers_open(struct inode *inode, struct file *file); 109static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos); 110static void *afs_proc_servers_next(struct seq_file *p, void *v, 111 loff_t *pos); 112static void afs_proc_servers_stop(struct seq_file *p, void *v); 113static int afs_proc_servers_show(struct seq_file *m, void *v); 114 115static const struct seq_operations afs_proc_servers_ops = { 116 .start = afs_proc_servers_start, 117 .next = afs_proc_servers_next, 118 .stop = afs_proc_servers_stop, 119 .show = afs_proc_servers_show, 120}; 121 122static const struct file_operations afs_proc_servers_fops = { 123 .open = afs_proc_servers_open, 124 .read = seq_read, 125 .llseek = seq_lseek, 126 .release = seq_release, 127}; 128 129/* 130 * initialise the /proc/fs/afs/ directory 131 */ 132int afs_proc_init(struct afs_net *net) 133{ 134 _enter(""); 135 136 net->proc_afs = proc_mkdir("fs/afs", NULL); 137 if (!net->proc_afs) 138 goto error_dir; 139 140 if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) || 141 !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) || 142 !proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops)) 143 goto error_tree; 144 145 _leave(" = 0"); 146 return 0; 147 148error_tree: 149 proc_remove(net->proc_afs); 150error_dir: 151 _leave(" = -ENOMEM"); 152 return -ENOMEM; 153} 154 155/* 156 * clean up the /proc/fs/afs/ directory 157 */ 158void afs_proc_cleanup(struct afs_net *net) 159{ 160 proc_remove(net->proc_afs); 161 net->proc_afs = NULL; 162} 163 164/* 165 * open "/proc/fs/afs/cells" which provides a summary of extant cells 166 */ 167static int afs_proc_cells_open(struct inode *inode, struct file *file) 168{ 169 struct seq_file *m; 170 int ret; 171 172 ret = seq_open(file, &afs_proc_cells_ops); 173 if (ret < 0) 174 return ret; 175 176 m = file->private_data; 177 m->private = PDE_DATA(inode); 178 return 0; 179} 180 181/* 182 * set up the iterator to start reading from the cells list and return the 183 * first item 184 */ 185static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) 186{ 187 struct afs_net *net = afs_seq2net(m); 188 189 rcu_read_lock(); 190 return seq_list_start_head(&net->proc_cells, *_pos); 191} 192 193/* 194 * move to next cell in cells list 195 */ 196static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos) 197{ 198 struct afs_net *net = afs_seq2net(m); 199 200 return seq_list_next(v, &net->proc_cells, pos); 201} 202 203/* 204 * clean up after reading from the cells list 205 */ 206static void afs_proc_cells_stop(struct seq_file *m, void *v) 207{ 208 rcu_read_unlock(); 209} 210 211/* 212 * display a header line followed by a load of cell lines 213 */ 214static int afs_proc_cells_show(struct seq_file *m, void *v) 215{ 216 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link); 217 struct afs_net *net = afs_seq2net(m); 218 219 if (v == &net->proc_cells) { 220 /* display header on line 1 */ 221 seq_puts(m, "USE NAME\n"); 222 return 0; 223 } 224 225 /* display one cell per line on subsequent lines */ 226 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name); 227 return 0; 228} 229 230/* 231 * handle writes to /proc/fs/afs/cells 232 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]" 233 */ 234static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, 235 size_t size, loff_t *_pos) 236{ 237 struct afs_net *net = afs_proc2net(file); 238 char *kbuf, *name, *args; 239 int ret; 240 241 /* start by dragging the command into memory */ 242 if (size <= 1 || size >= PAGE_SIZE) 243 return -EINVAL; 244 245 kbuf = memdup_user_nul(buf, size); 246 if (IS_ERR(kbuf)) 247 return PTR_ERR(kbuf); 248 249 /* trim to first NL */ 250 name = memchr(kbuf, '\n', size); 251 if (name) 252 *name = 0; 253 254 /* split into command, name and argslist */ 255 name = strchr(kbuf, ' '); 256 if (!name) 257 goto inval; 258 do { 259 *name++ = 0; 260 } while(*name == ' '); 261 if (!*name) 262 goto inval; 263 264 args = strchr(name, ' '); 265 if (!args) 266 goto inval; 267 do { 268 *args++ = 0; 269 } while(*args == ' '); 270 if (!*args) 271 goto inval; 272 273 /* determine command to perform */ 274 _debug("cmd=%s name=%s args=%s", kbuf, name, args); 275 276 if (strcmp(kbuf, "add") == 0) { 277 struct afs_cell *cell; 278 279 cell = afs_lookup_cell(net, name, strlen(name), args, true); 280 if (IS_ERR(cell)) { 281 ret = PTR_ERR(cell); 282 goto done; 283 } 284 285 set_bit(AFS_CELL_FL_NO_GC, &cell->flags); 286 printk("kAFS: Added new cell '%s'\n", name); 287 } else { 288 goto inval; 289 } 290 291 ret = size; 292 293done: 294 kfree(kbuf); 295 _leave(" = %d", ret); 296 return ret; 297 298inval: 299 ret = -EINVAL; 300 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); 301 goto done; 302} 303 304static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 305 size_t size, loff_t *_pos) 306{ 307 return 0; 308} 309 310/* 311 * handle writes to /proc/fs/afs/rootcell 312 * - to initialize rootcell: echo "cell.name:192.168.231.14" 313 */ 314static ssize_t afs_proc_rootcell_write(struct file *file, 315 const char __user *buf, 316 size_t size, loff_t *_pos) 317{ 318 struct afs_net *net = afs_proc2net(file); 319 char *kbuf, *s; 320 int ret; 321 322 /* start by dragging the command into memory */ 323 if (size <= 1 || size >= PAGE_SIZE) 324 return -EINVAL; 325 326 kbuf = memdup_user_nul(buf, size); 327 if (IS_ERR(kbuf)) 328 return PTR_ERR(kbuf); 329 330 /* trim to first NL */ 331 s = memchr(kbuf, '\n', size); 332 if (s) 333 *s = 0; 334 335 /* determine command to perform */ 336 _debug("rootcell=%s", kbuf); 337 338 ret = afs_cell_init(net, kbuf); 339 if (ret >= 0) 340 ret = size; /* consume everything, always */ 341 342 kfree(kbuf); 343 _leave(" = %d", ret); 344 return ret; 345} 346 347/* 348 * initialise /proc/fs/afs/<cell>/ 349 */ 350int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell) 351{ 352 struct proc_dir_entry *dir; 353 354 _enter("%p{%s},%p", cell, cell->name, net->proc_afs); 355 356 dir = proc_mkdir(cell->name, net->proc_afs); 357 if (!dir) 358 goto error_dir; 359 360 if (!proc_create_data("vlservers", 0, dir, 361 &afs_proc_cell_vlservers_fops, cell) || 362 !proc_create_data("volumes", 0, dir, 363 &afs_proc_cell_volumes_fops, cell)) 364 goto error_tree; 365 366 _leave(" = 0"); 367 return 0; 368 369error_tree: 370 remove_proc_subtree(cell->name, net->proc_afs); 371error_dir: 372 _leave(" = -ENOMEM"); 373 return -ENOMEM; 374} 375 376/* 377 * remove /proc/fs/afs/<cell>/ 378 */ 379void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell) 380{ 381 _enter(""); 382 383 remove_proc_subtree(cell->name, net->proc_afs); 384 385 _leave(""); 386} 387 388/* 389 * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells 390 */ 391static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file) 392{ 393 struct afs_cell *cell; 394 struct seq_file *m; 395 int ret; 396 397 cell = PDE_DATA(inode); 398 if (!cell) 399 return -ENOENT; 400 401 ret = seq_open(file, &afs_proc_cell_volumes_ops); 402 if (ret < 0) 403 return ret; 404 405 m = file->private_data; 406 m->private = cell; 407 408 return 0; 409} 410 411/* 412 * set up the iterator to start reading from the cells list and return the 413 * first item 414 */ 415static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) 416{ 417 struct afs_cell *cell = m->private; 418 419 _enter("cell=%p pos=%Ld", cell, *_pos); 420 421 read_lock(&cell->proc_lock); 422 return seq_list_start_head(&cell->proc_volumes, *_pos); 423} 424 425/* 426 * move to next cell in cells list 427 */ 428static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 429 loff_t *_pos) 430{ 431 struct afs_cell *cell = p->private; 432 433 _enter("cell=%p pos=%Ld", cell, *_pos); 434 return seq_list_next(v, &cell->proc_volumes, _pos); 435} 436 437/* 438 * clean up after reading from the cells list 439 */ 440static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v) 441{ 442 struct afs_cell *cell = p->private; 443 444 read_unlock(&cell->proc_lock); 445} 446 447static const char afs_vol_types[3][3] = { 448 [AFSVL_RWVOL] = "RW", 449 [AFSVL_ROVOL] = "RO", 450 [AFSVL_BACKVOL] = "BK", 451}; 452 453/* 454 * display a header line followed by a load of volume lines 455 */ 456static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) 457{ 458 struct afs_cell *cell = m->private; 459 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link); 460 461 /* Display header on line 1 */ 462 if (v == &cell->proc_volumes) { 463 seq_puts(m, "USE VID TY\n"); 464 return 0; 465 } 466 467 seq_printf(m, "%3d %08x %s\n", 468 atomic_read(&vol->usage), vol->vid, 469 afs_vol_types[vol->type]); 470 471 return 0; 472} 473 474/* 475 * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume 476 * location server 477 */ 478static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file) 479{ 480 struct afs_cell *cell; 481 struct seq_file *m; 482 int ret; 483 484 cell = PDE_DATA(inode); 485 if (!cell) 486 return -ENOENT; 487 488 ret = seq_open(file, &afs_proc_cell_vlservers_ops); 489 if (ret<0) 490 return ret; 491 492 m = file->private_data; 493 m->private = cell; 494 495 return 0; 496} 497 498/* 499 * set up the iterator to start reading from the cells list and return the 500 * first item 501 */ 502static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) 503{ 504 struct afs_addr_list *alist; 505 struct afs_cell *cell = m->private; 506 loff_t pos = *_pos; 507 508 rcu_read_lock(); 509 510 alist = rcu_dereference(cell->vl_addrs); 511 512 /* allow for the header line */ 513 if (!pos) 514 return (void *) 1; 515 pos--; 516 517 if (!alist || pos >= alist->nr_addrs) 518 return NULL; 519 520 return alist->addrs + pos; 521} 522 523/* 524 * move to next cell in cells list 525 */ 526static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 527 loff_t *_pos) 528{ 529 struct afs_addr_list *alist; 530 struct afs_cell *cell = p->private; 531 loff_t pos; 532 533 alist = rcu_dereference(cell->vl_addrs); 534 535 pos = *_pos; 536 (*_pos)++; 537 if (!alist || pos >= alist->nr_addrs) 538 return NULL; 539 540 return alist->addrs + pos; 541} 542 543/* 544 * clean up after reading from the cells list 545 */ 546static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) 547{ 548 rcu_read_unlock(); 549} 550 551/* 552 * display a header line followed by a load of volume lines 553 */ 554static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) 555{ 556 struct sockaddr_rxrpc *addr = v; 557 558 /* display header on line 1 */ 559 if (v == (void *)1) { 560 seq_puts(m, "ADDRESS\n"); 561 return 0; 562 } 563 564 /* display one cell per line on subsequent lines */ 565 seq_printf(m, "%pISp\n", &addr->transport); 566 return 0; 567} 568 569/* 570 * open "/proc/fs/afs/servers" which provides a summary of active 571 * servers 572 */ 573static int afs_proc_servers_open(struct inode *inode, struct file *file) 574{ 575 return seq_open(file, &afs_proc_servers_ops); 576} 577 578/* 579 * Set up the iterator to start reading from the server list and return the 580 * first item. 581 */ 582static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) 583{ 584 struct afs_net *net = afs_seq2net(m); 585 586 rcu_read_lock(); 587 return seq_hlist_start_head_rcu(&net->fs_proc, *_pos); 588} 589 590/* 591 * move to next cell in cells list 592 */ 593static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos) 594{ 595 struct afs_net *net = afs_seq2net(m); 596 597 return seq_hlist_next_rcu(v, &net->fs_proc, _pos); 598} 599 600/* 601 * clean up after reading from the cells list 602 */ 603static void afs_proc_servers_stop(struct seq_file *p, void *v) 604{ 605 rcu_read_unlock(); 606} 607 608/* 609 * display a header line followed by a load of volume lines 610 */ 611static int afs_proc_servers_show(struct seq_file *m, void *v) 612{ 613 struct afs_server *server; 614 struct afs_addr_list *alist; 615 616 if (v == SEQ_START_TOKEN) { 617 seq_puts(m, "UUID USE ADDR\n"); 618 return 0; 619 } 620 621 server = list_entry(v, struct afs_server, proc_link); 622 alist = rcu_dereference(server->addresses); 623 seq_printf(m, "%pU %3d %pISp\n", 624 &server->uuid, 625 atomic_read(&server->usage), 626 &alist->addrs[alist->index].transport); 627 return 0; 628}