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 <asm/uaccess.h> 18#include "internal.h" 19 20static struct proc_dir_entry *proc_afs; 21 22 23static int afs_proc_cells_open(struct inode *inode, struct file *file); 24static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); 25static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); 26static void afs_proc_cells_stop(struct seq_file *p, void *v); 27static int afs_proc_cells_show(struct seq_file *m, void *v); 28static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, 29 size_t size, loff_t *_pos); 30 31static 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 38static 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 46static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 47 size_t size, loff_t *_pos); 48static ssize_t afs_proc_rootcell_write(struct file *file, 49 const char __user *buf, 50 size_t size, loff_t *_pos); 51 52static 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 58static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file); 59static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); 60static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 61 loff_t *pos); 62static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); 63static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); 64 65static 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 72static 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 79static int afs_proc_cell_vlservers_open(struct inode *inode, 80 struct file *file); 81static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); 82static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 83 loff_t *pos); 84static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); 85static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); 86 87static 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 94static 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 101static int afs_proc_cell_servers_open(struct inode *inode, struct file *file); 102static void *afs_proc_cell_servers_start(struct seq_file *p, loff_t *pos); 103static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, 104 loff_t *pos); 105static void afs_proc_cell_servers_stop(struct seq_file *p, void *v); 106static int afs_proc_cell_servers_show(struct seq_file *m, void *v); 107 108static 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 115static 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 */ 125int 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 140error_tree: 141 remove_proc_subtree("fs/afs", NULL); 142error_dir: 143 _leave(" = -ENOMEM"); 144 return -ENOMEM; 145} 146 147/* 148 * clean up the /proc/fs/afs/ directory 149 */ 150void 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 */ 158static 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 */ 177static 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 */ 187static 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 */ 195static 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 */ 203static 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 */ 223static 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 = kmalloc(size + 1, GFP_KERNEL); 234 if (!kbuf) 235 return -ENOMEM; 236 237 ret = -EFAULT; 238 if (copy_from_user(kbuf, buf, size) != 0) 239 goto done; 240 kbuf[size] = 0; 241 242 /* trim to first NL */ 243 name = memchr(kbuf, '\n', size); 244 if (name) 245 *name = 0; 246 247 /* split into command, name and argslist */ 248 name = strchr(kbuf, ' '); 249 if (!name) 250 goto inval; 251 do { 252 *name++ = 0; 253 } while(*name == ' '); 254 if (!*name) 255 goto inval; 256 257 args = strchr(name, ' '); 258 if (!args) 259 goto inval; 260 do { 261 *args++ = 0; 262 } while(*args == ' '); 263 if (!*args) 264 goto inval; 265 266 /* determine command to perform */ 267 _debug("cmd=%s name=%s args=%s", kbuf, name, args); 268 269 if (strcmp(kbuf, "add") == 0) { 270 struct afs_cell *cell; 271 272 cell = afs_cell_create(name, strlen(name), args, false); 273 if (IS_ERR(cell)) { 274 ret = PTR_ERR(cell); 275 goto done; 276 } 277 278 afs_put_cell(cell); 279 printk("kAFS: Added new cell '%s'\n", name); 280 } else { 281 goto inval; 282 } 283 284 ret = size; 285 286done: 287 kfree(kbuf); 288 _leave(" = %d", ret); 289 return ret; 290 291inval: 292 ret = -EINVAL; 293 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); 294 goto done; 295} 296 297static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 298 size_t size, loff_t *_pos) 299{ 300 return 0; 301} 302 303/* 304 * handle writes to /proc/fs/afs/rootcell 305 * - to initialize rootcell: echo "cell.name:192.168.231.14" 306 */ 307static ssize_t afs_proc_rootcell_write(struct file *file, 308 const char __user *buf, 309 size_t size, loff_t *_pos) 310{ 311 char *kbuf, *s; 312 int ret; 313 314 /* start by dragging the command into memory */ 315 if (size <= 1 || size >= PAGE_SIZE) 316 return -EINVAL; 317 318 ret = -ENOMEM; 319 kbuf = kmalloc(size + 1, GFP_KERNEL); 320 if (!kbuf) 321 goto nomem; 322 323 ret = -EFAULT; 324 if (copy_from_user(kbuf, buf, size) != 0) 325 goto infault; 326 kbuf[size] = 0; 327 328 /* trim to first NL */ 329 s = memchr(kbuf, '\n', size); 330 if (s) 331 *s = 0; 332 333 /* determine command to perform */ 334 _debug("rootcell=%s", kbuf); 335 336 ret = afs_cell_init(kbuf); 337 if (ret >= 0) 338 ret = size; /* consume everything, always */ 339 340infault: 341 kfree(kbuf); 342nomem: 343 _leave(" = %d", ret); 344 return ret; 345} 346 347/* 348 * initialise /proc/fs/afs/<cell>/ 349 */ 350int afs_proc_cell_setup(struct afs_cell *cell) 351{ 352 struct proc_dir_entry *dir; 353 354 _enter("%p{%s}", cell, cell->name); 355 356 dir = proc_mkdir(cell->name, proc_afs); 357 if (!dir) 358 goto error_dir; 359 360 if (!proc_create_data("servers", 0, dir, 361 &afs_proc_cell_servers_fops, cell) || 362 !proc_create_data("vlservers", 0, dir, 363 &afs_proc_cell_vlservers_fops, cell) || 364 !proc_create_data("volumes", 0, dir, 365 &afs_proc_cell_volumes_fops, cell)) 366 goto error_tree; 367 368 _leave(" = 0"); 369 return 0; 370 371error_tree: 372 remove_proc_subtree(cell->name, proc_afs); 373error_dir: 374 _leave(" = -ENOMEM"); 375 return -ENOMEM; 376} 377 378/* 379 * remove /proc/fs/afs/<cell>/ 380 */ 381void afs_proc_cell_remove(struct afs_cell *cell) 382{ 383 _enter(""); 384 385 remove_proc_subtree(cell->name, proc_afs); 386 387 _leave(""); 388} 389 390/* 391 * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells 392 */ 393static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file) 394{ 395 struct afs_cell *cell; 396 struct seq_file *m; 397 int ret; 398 399 cell = PDE_DATA(inode); 400 if (!cell) 401 return -ENOENT; 402 403 ret = seq_open(file, &afs_proc_cell_volumes_ops); 404 if (ret < 0) 405 return ret; 406 407 m = file->private_data; 408 m->private = cell; 409 410 return 0; 411} 412 413/* 414 * set up the iterator to start reading from the cells list and return the 415 * first item 416 */ 417static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) 418{ 419 struct afs_cell *cell = m->private; 420 421 _enter("cell=%p pos=%Ld", cell, *_pos); 422 423 /* lock the list against modification */ 424 down_read(&cell->vl_sem); 425 return seq_list_start_head(&cell->vl_list, *_pos); 426} 427 428/* 429 * move to next cell in cells list 430 */ 431static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 432 loff_t *_pos) 433{ 434 struct afs_cell *cell = p->private; 435 436 _enter("cell=%p pos=%Ld", cell, *_pos); 437 return seq_list_next(v, &cell->vl_list, _pos); 438} 439 440/* 441 * clean up after reading from the cells list 442 */ 443static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v) 444{ 445 struct afs_cell *cell = p->private; 446 447 up_read(&cell->vl_sem); 448} 449 450static const char afs_vlocation_states[][4] = { 451 [AFS_VL_NEW] = "New", 452 [AFS_VL_CREATING] = "Crt", 453 [AFS_VL_VALID] = "Val", 454 [AFS_VL_NO_VOLUME] = "NoV", 455 [AFS_VL_UPDATING] = "Upd", 456 [AFS_VL_VOLUME_DELETED] = "Del", 457 [AFS_VL_UNCERTAIN] = "Unc", 458}; 459 460/* 461 * display a header line followed by a load of volume lines 462 */ 463static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) 464{ 465 struct afs_cell *cell = m->private; 466 struct afs_vlocation *vlocation = 467 list_entry(v, struct afs_vlocation, link); 468 469 /* display header on line 1 */ 470 if (v == &cell->vl_list) { 471 seq_puts(m, "USE STT VLID[0] VLID[1] VLID[2] NAME\n"); 472 return 0; 473 } 474 475 /* display one cell per line on subsequent lines */ 476 seq_printf(m, "%3d %s %08x %08x %08x %s\n", 477 atomic_read(&vlocation->usage), 478 afs_vlocation_states[vlocation->state], 479 vlocation->vldb.vid[0], 480 vlocation->vldb.vid[1], 481 vlocation->vldb.vid[2], 482 vlocation->vldb.name); 483 484 return 0; 485} 486 487/* 488 * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume 489 * location server 490 */ 491static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file) 492{ 493 struct afs_cell *cell; 494 struct seq_file *m; 495 int ret; 496 497 cell = PDE_DATA(inode); 498 if (!cell) 499 return -ENOENT; 500 501 ret = seq_open(file, &afs_proc_cell_vlservers_ops); 502 if (ret<0) 503 return ret; 504 505 m = file->private_data; 506 m->private = cell; 507 508 return 0; 509} 510 511/* 512 * set up the iterator to start reading from the cells list and return the 513 * first item 514 */ 515static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) 516{ 517 struct afs_cell *cell = m->private; 518 loff_t pos = *_pos; 519 520 _enter("cell=%p pos=%Ld", cell, *_pos); 521 522 /* lock the list against modification */ 523 down_read(&cell->vl_sem); 524 525 /* allow for the header line */ 526 if (!pos) 527 return (void *) 1; 528 pos--; 529 530 if (pos >= cell->vl_naddrs) 531 return NULL; 532 533 return &cell->vl_addrs[pos]; 534} 535 536/* 537 * move to next cell in cells list 538 */ 539static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 540 loff_t *_pos) 541{ 542 struct afs_cell *cell = p->private; 543 loff_t pos; 544 545 _enter("cell=%p{nad=%u} pos=%Ld", cell, cell->vl_naddrs, *_pos); 546 547 pos = *_pos; 548 (*_pos)++; 549 if (pos >= cell->vl_naddrs) 550 return NULL; 551 552 return &cell->vl_addrs[pos]; 553} 554 555/* 556 * clean up after reading from the cells list 557 */ 558static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) 559{ 560 struct afs_cell *cell = p->private; 561 562 up_read(&cell->vl_sem); 563} 564 565/* 566 * display a header line followed by a load of volume lines 567 */ 568static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) 569{ 570 struct in_addr *addr = v; 571 572 /* display header on line 1 */ 573 if (v == (struct in_addr *) 1) { 574 seq_puts(m, "ADDRESS\n"); 575 return 0; 576 } 577 578 /* display one cell per line on subsequent lines */ 579 seq_printf(m, "%pI4\n", &addr->s_addr); 580 return 0; 581} 582 583/* 584 * open "/proc/fs/afs/<cell>/servers" which provides a summary of active 585 * servers 586 */ 587static int afs_proc_cell_servers_open(struct inode *inode, struct file *file) 588{ 589 struct afs_cell *cell; 590 struct seq_file *m; 591 int ret; 592 593 cell = PDE_DATA(inode); 594 if (!cell) 595 return -ENOENT; 596 597 ret = seq_open(file, &afs_proc_cell_servers_ops); 598 if (ret < 0) 599 return ret; 600 601 m = file->private_data; 602 m->private = cell; 603 return 0; 604} 605 606/* 607 * set up the iterator to start reading from the cells list and return the 608 * first item 609 */ 610static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) 611 __acquires(m->private->servers_lock) 612{ 613 struct afs_cell *cell = m->private; 614 615 _enter("cell=%p pos=%Ld", cell, *_pos); 616 617 /* lock the list against modification */ 618 read_lock(&cell->servers_lock); 619 return seq_list_start_head(&cell->servers, *_pos); 620} 621 622/* 623 * move to next cell in cells list 624 */ 625static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, 626 loff_t *_pos) 627{ 628 struct afs_cell *cell = p->private; 629 630 _enter("cell=%p pos=%Ld", cell, *_pos); 631 return seq_list_next(v, &cell->servers, _pos); 632} 633 634/* 635 * clean up after reading from the cells list 636 */ 637static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) 638 __releases(p->private->servers_lock) 639{ 640 struct afs_cell *cell = p->private; 641 642 read_unlock(&cell->servers_lock); 643} 644 645/* 646 * display a header line followed by a load of volume lines 647 */ 648static int afs_proc_cell_servers_show(struct seq_file *m, void *v) 649{ 650 struct afs_cell *cell = m->private; 651 struct afs_server *server = list_entry(v, struct afs_server, link); 652 char ipaddr[20]; 653 654 /* display header on line 1 */ 655 if (v == &cell->servers) { 656 seq_puts(m, "USE ADDR STATE\n"); 657 return 0; 658 } 659 660 /* display one cell per line on subsequent lines */ 661 sprintf(ipaddr, "%pI4", &server->addr); 662 seq_printf(m, "%3d %-15.15s %5d\n", 663 atomic_read(&server->usage), ipaddr, server->fs_state); 664 665 return 0; 666} 667