linux/fs/afs/proc.c
<<
>>
Prefs
   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