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_proc2net(struct file *f) 21 { 22 return &__afs_net; 23 } 24 25 static inline struct afs_net *afs_seq2net(struct seq_file *m) 26 { 27 return &__afs_net; // TODO: use seq_file_net(m) 28 } 29 30 static int afs_proc_cells_open(struct inode *inode, struct file *file); 31 static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); 32 static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); 33 static void afs_proc_cells_stop(struct seq_file *p, void *v); 34 static int afs_proc_cells_show(struct seq_file *m, void *v); 35 static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, 36 size_t size, loff_t *_pos); 37 38 static 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 45 static 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 53 static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 54 size_t size, loff_t *_pos); 55 static ssize_t afs_proc_rootcell_write(struct file *file, 56 const char __user *buf, 57 size_t size, loff_t *_pos); 58 59 static 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 65 static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file); 66 static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); 67 static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 68 loff_t *pos); 69 static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); 70 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); 71 72 static 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 79 static 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 86 static int afs_proc_cell_vlservers_open(struct inode *inode, 87 struct file *file); 88 static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); 89 static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 90 loff_t *pos); 91 static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); 92 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); 93 94 static 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 101 static 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 108 static int afs_proc_servers_open(struct inode *inode, struct file *file); 109 static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos); 110 static void *afs_proc_servers_next(struct seq_file *p, void *v, 111 loff_t *pos); 112 static void afs_proc_servers_stop(struct seq_file *p, void *v); 113 static int afs_proc_servers_show(struct seq_file *m, void *v); 114 115 static 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 122 static 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 */ 132 int 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 148 error_tree: 149 proc_remove(net->proc_afs); 150 error_dir: 151 _leave(" = -ENOMEM"); 152 return -ENOMEM; 153 } 154 155 /* 156 * clean up the /proc/fs/afs/ directory 157 */ 158 void 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 */ 167 static 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 */ 185 static 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 */ 196 static 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 */ 206 static 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 */ 214 static 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 */ 234 static 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 293 done: 294 kfree(kbuf); 295 _leave(" = %d", ret); 296 return ret; 297 298 inval: 299 ret = -EINVAL; 300 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); 301 goto done; 302 } 303 304 static 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 */ 314 static 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 */ 350 int 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 369 error_tree: 370 remove_proc_subtree(cell->name, net->proc_afs); 371 error_dir: 372 _leave(" = -ENOMEM"); 373 return -ENOMEM; 374 } 375 376 /* 377 * remove /proc/fs/afs/<cell>/ 378 */ 379 void 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 */ 391 static 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 */ 415 static 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 */ 428 static 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 */ 440 static 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 447 static 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 */ 456 static 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 */ 478 static 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 */ 502 static 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 */ 526 static 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 */ 546 static 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 */ 554 static 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 */ 573 static 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 */ 582 static 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 */ 593 static 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 */ 603 static 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 */ 611 static 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 } 629