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