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 <linux/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 = 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
 281done:
 282        kfree(kbuf);
 283        _leave(" = %d", ret);
 284        return ret;
 285
 286inval:
 287        ret = -EINVAL;
 288        printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
 289        goto done;
 290}
 291
 292static 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 */
 302static 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 */
 337int 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
 358error_tree:
 359        remove_proc_subtree(cell->name, proc_afs);
 360error_dir:
 361        _leave(" = -ENOMEM");
 362        return -ENOMEM;
 363}
 364
 365/*
 366 * remove /proc/fs/afs/<cell>/
 367 */
 368void 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 */
 380static 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 */
 404static 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 */
 418static 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 */
 430static 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
 437static 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 */
 450static 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 */
 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_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 */
 526static 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 */
 545static 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 */
 555static 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 */
 574static 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 */
 597static 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 */
 612static 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 */
 624static 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 */
 635static 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