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 inline struct afs_net *afs_seq2net(struct seq_file *m)
  21{
  22        return afs_net(seq_file_net(m));
  23}
  24
  25static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
  26{
  27        return afs_net(seq_file_single_net(m));
  28}
  29
  30/*
  31 * Display the list of cells known to the namespace.
  32 */
  33static int afs_proc_cells_show(struct seq_file *m, void *v)
  34{
  35        struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
  36        struct afs_net *net = afs_seq2net(m);
  37
  38        if (v == &net->proc_cells) {
  39                /* display header on line 1 */
  40                seq_puts(m, "USE NAME\n");
  41                return 0;
  42        }
  43
  44        /* display one cell per line on subsequent lines */
  45        seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
  46        return 0;
  47}
  48
  49static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
  50        __acquires(rcu)
  51{
  52        rcu_read_lock();
  53        return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
  54}
  55
  56static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
  57{
  58        return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
  59}
  60
  61static void afs_proc_cells_stop(struct seq_file *m, void *v)
  62        __releases(rcu)
  63{
  64        rcu_read_unlock();
  65}
  66
  67static const struct seq_operations afs_proc_cells_ops = {
  68        .start  = afs_proc_cells_start,
  69        .next   = afs_proc_cells_next,
  70        .stop   = afs_proc_cells_stop,
  71        .show   = afs_proc_cells_show,
  72};
  73
  74/*
  75 * handle writes to /proc/fs/afs/cells
  76 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
  77 */
  78static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
  79{
  80        struct seq_file *m = file->private_data;
  81        struct afs_net *net = afs_seq2net(m);
  82        char *name, *args;
  83        int ret;
  84
  85        /* trim to first NL */
  86        name = memchr(buf, '\n', size);
  87        if (name)
  88                *name = 0;
  89
  90        /* split into command, name and argslist */
  91        name = strchr(buf, ' ');
  92        if (!name)
  93                goto inval;
  94        do {
  95                *name++ = 0;
  96        } while(*name == ' ');
  97        if (!*name)
  98                goto inval;
  99
 100        args = strchr(name, ' ');
 101        if (!args)
 102                goto inval;
 103        do {
 104                *args++ = 0;
 105        } while(*args == ' ');
 106        if (!*args)
 107                goto inval;
 108
 109        /* determine command to perform */
 110        _debug("cmd=%s name=%s args=%s", buf, name, args);
 111
 112        if (strcmp(buf, "add") == 0) {
 113                struct afs_cell *cell;
 114
 115                cell = afs_lookup_cell(net, name, strlen(name), args, true);
 116                if (IS_ERR(cell)) {
 117                        ret = PTR_ERR(cell);
 118                        goto done;
 119                }
 120
 121                if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
 122                        afs_put_cell(net, cell);
 123                printk("kAFS: Added new cell '%s'\n", name);
 124        } else {
 125                goto inval;
 126        }
 127
 128        ret = 0;
 129
 130done:
 131        _leave(" = %d", ret);
 132        return ret;
 133
 134inval:
 135        ret = -EINVAL;
 136        printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
 137        goto done;
 138}
 139
 140/*
 141 * Display the name of the current workstation cell.
 142 */
 143static int afs_proc_rootcell_show(struct seq_file *m, void *v)
 144{
 145        struct afs_cell *cell;
 146        struct afs_net *net;
 147
 148        net = afs_seq2net_single(m);
 149        if (rcu_access_pointer(net->ws_cell)) {
 150                rcu_read_lock();
 151                cell = rcu_dereference(net->ws_cell);
 152                if (cell)
 153                        seq_printf(m, "%s\n", cell->name);
 154                rcu_read_unlock();
 155        }
 156        return 0;
 157}
 158
 159/*
 160 * Set the current workstation cell and optionally supply its list of volume
 161 * location servers.
 162 *
 163 *      echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
 164 */
 165static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
 166{
 167        struct seq_file *m = file->private_data;
 168        struct afs_net *net = afs_seq2net_single(m);
 169        char *s;
 170        int ret;
 171
 172        ret = -EINVAL;
 173        if (buf[0] == '.')
 174                goto out;
 175        if (memchr(buf, '/', size))
 176                goto out;
 177
 178        /* trim to first NL */
 179        s = memchr(buf, '\n', size);
 180        if (s)
 181                *s = 0;
 182
 183        /* determine command to perform */
 184        _debug("rootcell=%s", buf);
 185
 186        ret = afs_cell_init(net, buf);
 187
 188out:
 189        _leave(" = %d", ret);
 190        return ret;
 191}
 192
 193static const char afs_vol_types[3][3] = {
 194        [AFSVL_RWVOL]   = "RW",
 195        [AFSVL_ROVOL]   = "RO",
 196        [AFSVL_BACKVOL] = "BK",
 197};
 198
 199/*
 200 * Display the list of volumes known to a cell.
 201 */
 202static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
 203{
 204        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 205        struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
 206
 207        /* Display header on line 1 */
 208        if (v == &cell->proc_volumes) {
 209                seq_puts(m, "USE VID      TY\n");
 210                return 0;
 211        }
 212
 213        seq_printf(m, "%3d %08x %s\n",
 214                   atomic_read(&vol->usage), vol->vid,
 215                   afs_vol_types[vol->type]);
 216
 217        return 0;
 218}
 219
 220static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
 221        __acquires(cell->proc_lock)
 222{
 223        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 224
 225        read_lock(&cell->proc_lock);
 226        return seq_list_start_head(&cell->proc_volumes, *_pos);
 227}
 228
 229static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
 230                                        loff_t *_pos)
 231{
 232        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 233
 234        return seq_list_next(v, &cell->proc_volumes, _pos);
 235}
 236
 237static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
 238        __releases(cell->proc_lock)
 239{
 240        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 241
 242        read_unlock(&cell->proc_lock);
 243}
 244
 245static const struct seq_operations afs_proc_cell_volumes_ops = {
 246        .start  = afs_proc_cell_volumes_start,
 247        .next   = afs_proc_cell_volumes_next,
 248        .stop   = afs_proc_cell_volumes_stop,
 249        .show   = afs_proc_cell_volumes_show,
 250};
 251
 252/*
 253 * Display the list of Volume Location servers we're using for a cell.
 254 */
 255static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
 256{
 257        struct sockaddr_rxrpc *addr = v;
 258
 259        /* display header on line 1 */
 260        if (v == (void *)1) {
 261                seq_puts(m, "ADDRESS\n");
 262                return 0;
 263        }
 264
 265        /* display one cell per line on subsequent lines */
 266        seq_printf(m, "%pISp\n", &addr->transport);
 267        return 0;
 268}
 269
 270static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
 271        __acquires(rcu)
 272{
 273        struct afs_addr_list *alist;
 274        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 275        loff_t pos = *_pos;
 276
 277        rcu_read_lock();
 278
 279        alist = rcu_dereference(cell->vl_addrs);
 280
 281        /* allow for the header line */
 282        if (!pos)
 283                return (void *) 1;
 284        pos--;
 285
 286        if (!alist || pos >= alist->nr_addrs)
 287                return NULL;
 288
 289        return alist->addrs + pos;
 290}
 291
 292static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
 293                                          loff_t *_pos)
 294{
 295        struct afs_addr_list *alist;
 296        struct afs_cell *cell = PDE_DATA(file_inode(m->file));
 297        loff_t pos;
 298
 299        alist = rcu_dereference(cell->vl_addrs);
 300
 301        pos = *_pos;
 302        (*_pos)++;
 303        if (!alist || pos >= alist->nr_addrs)
 304                return NULL;
 305
 306        return alist->addrs + pos;
 307}
 308
 309static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
 310        __releases(rcu)
 311{
 312        rcu_read_unlock();
 313}
 314
 315static const struct seq_operations afs_proc_cell_vlservers_ops = {
 316        .start  = afs_proc_cell_vlservers_start,
 317        .next   = afs_proc_cell_vlservers_next,
 318        .stop   = afs_proc_cell_vlservers_stop,
 319        .show   = afs_proc_cell_vlservers_show,
 320};
 321
 322/*
 323 * Display the list of fileservers we're using within a namespace.
 324 */
 325static int afs_proc_servers_show(struct seq_file *m, void *v)
 326{
 327        struct afs_server *server;
 328        struct afs_addr_list *alist;
 329        int i;
 330
 331        if (v == SEQ_START_TOKEN) {
 332                seq_puts(m, "UUID                                 USE ADDR\n");
 333                return 0;
 334        }
 335
 336        server = list_entry(v, struct afs_server, proc_link);
 337        alist = rcu_dereference(server->addresses);
 338        seq_printf(m, "%pU %3d %pISpc%s\n",
 339                   &server->uuid,
 340                   atomic_read(&server->usage),
 341                   &alist->addrs[0].transport,
 342                   alist->index == 0 ? "*" : "");
 343        for (i = 1; i < alist->nr_addrs; i++)
 344                seq_printf(m, "                                         %pISpc%s\n",
 345                           &alist->addrs[i].transport,
 346                           alist->index == i ? "*" : "");
 347        return 0;
 348}
 349
 350static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
 351        __acquires(rcu)
 352{
 353        rcu_read_lock();
 354        return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
 355}
 356
 357static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
 358{
 359        return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
 360}
 361
 362static void afs_proc_servers_stop(struct seq_file *m, void *v)
 363        __releases(rcu)
 364{
 365        rcu_read_unlock();
 366}
 367
 368static const struct seq_operations afs_proc_servers_ops = {
 369        .start  = afs_proc_servers_start,
 370        .next   = afs_proc_servers_next,
 371        .stop   = afs_proc_servers_stop,
 372        .show   = afs_proc_servers_show,
 373};
 374
 375/*
 376 * Display the list of strings that may be substituted for the @sys pathname
 377 * macro.
 378 */
 379static int afs_proc_sysname_show(struct seq_file *m, void *v)
 380{
 381        struct afs_net *net = afs_seq2net(m);
 382        struct afs_sysnames *sysnames = net->sysnames;
 383        unsigned int i = (unsigned long)v - 1;
 384
 385        if (i < sysnames->nr)
 386                seq_printf(m, "%s\n", sysnames->subs[i]);
 387        return 0;
 388}
 389
 390static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
 391        __acquires(&net->sysnames_lock)
 392{
 393        struct afs_net *net = afs_seq2net(m);
 394        struct afs_sysnames *names;
 395
 396        read_lock(&net->sysnames_lock);
 397
 398        names = net->sysnames;
 399        if (*pos >= names->nr)
 400                return NULL;
 401        return (void *)(unsigned long)(*pos + 1);
 402}
 403
 404static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
 405{
 406        struct afs_net *net = afs_seq2net(m);
 407        struct afs_sysnames *names = net->sysnames;
 408
 409        *pos += 1;
 410        if (*pos >= names->nr)
 411                return NULL;
 412        return (void *)(unsigned long)(*pos + 1);
 413}
 414
 415static void afs_proc_sysname_stop(struct seq_file *m, void *v)
 416        __releases(&net->sysnames_lock)
 417{
 418        struct afs_net *net = afs_seq2net(m);
 419
 420        read_unlock(&net->sysnames_lock);
 421}
 422
 423static const struct seq_operations afs_proc_sysname_ops = {
 424        .start  = afs_proc_sysname_start,
 425        .next   = afs_proc_sysname_next,
 426        .stop   = afs_proc_sysname_stop,
 427        .show   = afs_proc_sysname_show,
 428};
 429
 430/*
 431 * Allow the @sys substitution to be configured.
 432 */
 433static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
 434{
 435        struct afs_sysnames *sysnames, *kill;
 436        struct seq_file *m = file->private_data;
 437        struct afs_net *net = afs_seq2net(m);
 438        char *s, *p, *sub;
 439        int ret, len;
 440
 441        sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
 442        if (!sysnames)
 443                return -ENOMEM;
 444        refcount_set(&sysnames->usage, 1);
 445        kill = sysnames;
 446
 447        p = buf;
 448        while ((s = strsep(&p, " \t\n"))) {
 449                len = strlen(s);
 450                if (len == 0)
 451                        continue;
 452                ret = -ENAMETOOLONG;
 453                if (len >= AFSNAMEMAX)
 454                        goto error;
 455
 456                if (len >= 4 &&
 457                    s[len - 4] == '@' &&
 458                    s[len - 3] == 's' &&
 459                    s[len - 2] == 'y' &&
 460                    s[len - 1] == 's')
 461                        /* Protect against recursion */
 462                        goto invalid;
 463
 464                if (s[0] == '.' &&
 465                    (len < 2 || (len == 2 && s[1] == '.')))
 466                        goto invalid;
 467
 468                if (memchr(s, '/', len))
 469                        goto invalid;
 470
 471                ret = -EFBIG;
 472                if (sysnames->nr >= AFS_NR_SYSNAME)
 473                        goto out;
 474
 475                if (strcmp(s, afs_init_sysname) == 0) {
 476                        sub = (char *)afs_init_sysname;
 477                } else {
 478                        ret = -ENOMEM;
 479                        sub = kmemdup(s, len + 1, GFP_KERNEL);
 480                        if (!sub)
 481                                goto out;
 482                }
 483
 484                sysnames->subs[sysnames->nr] = sub;
 485                sysnames->nr++;
 486        }
 487
 488        if (sysnames->nr == 0) {
 489                sysnames->subs[0] = sysnames->blank;
 490                sysnames->nr++;
 491        }
 492
 493        write_lock(&net->sysnames_lock);
 494        kill = net->sysnames;
 495        net->sysnames = sysnames;
 496        write_unlock(&net->sysnames_lock);
 497        ret = 0;
 498out:
 499        afs_put_sysnames(kill);
 500        return ret;
 501
 502invalid:
 503        ret = -EINVAL;
 504error:
 505        goto out;
 506}
 507
 508void afs_put_sysnames(struct afs_sysnames *sysnames)
 509{
 510        int i;
 511
 512        if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
 513                for (i = 0; i < sysnames->nr; i++)
 514                        if (sysnames->subs[i] != afs_init_sysname &&
 515                            sysnames->subs[i] != sysnames->blank)
 516                                kfree(sysnames->subs[i]);
 517        }
 518}
 519
 520/*
 521 * Display general per-net namespace statistics
 522 */
 523static int afs_proc_stats_show(struct seq_file *m, void *v)
 524{
 525        struct afs_net *net = afs_seq2net_single(m);
 526
 527        seq_puts(m, "kAFS statistics\n");
 528
 529        seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
 530                   atomic_read(&net->n_lookup),
 531                   atomic_read(&net->n_reval),
 532                   atomic_read(&net->n_inval),
 533                   atomic_read(&net->n_relpg));
 534
 535        seq_printf(m, "dir-data: rdpg=%u\n",
 536                   atomic_read(&net->n_read_dir));
 537
 538        seq_printf(m, "dir-edit: cr=%u rm=%u\n",
 539                   atomic_read(&net->n_dir_cr),
 540                   atomic_read(&net->n_dir_rm));
 541
 542        seq_printf(m, "file-rd : n=%u nb=%lu\n",
 543                   atomic_read(&net->n_fetches),
 544                   atomic_long_read(&net->n_fetch_bytes));
 545        seq_printf(m, "file-wr : n=%u nb=%lu\n",
 546                   atomic_read(&net->n_stores),
 547                   atomic_long_read(&net->n_store_bytes));
 548        return 0;
 549}
 550
 551/*
 552 * initialise /proc/fs/afs/<cell>/
 553 */
 554int afs_proc_cell_setup(struct afs_cell *cell)
 555{
 556        struct proc_dir_entry *dir;
 557        struct afs_net *net = cell->net;
 558
 559        _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
 560
 561        dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
 562        if (!dir)
 563                goto error_dir;
 564
 565        if (!proc_create_net_data("vlservers", 0444, dir,
 566                                  &afs_proc_cell_vlservers_ops,
 567                                  sizeof(struct seq_net_private),
 568                                  cell) ||
 569            !proc_create_net_data("volumes", 0444, dir,
 570                                  &afs_proc_cell_volumes_ops,
 571                                  sizeof(struct seq_net_private),
 572                                  cell))
 573                goto error_tree;
 574
 575        _leave(" = 0");
 576        return 0;
 577
 578error_tree:
 579        remove_proc_subtree(cell->name, net->proc_afs);
 580error_dir:
 581        _leave(" = -ENOMEM");
 582        return -ENOMEM;
 583}
 584
 585/*
 586 * remove /proc/fs/afs/<cell>/
 587 */
 588void afs_proc_cell_remove(struct afs_cell *cell)
 589{
 590        struct afs_net *net = cell->net;
 591
 592        _enter("");
 593        remove_proc_subtree(cell->name, net->proc_afs);
 594        _leave("");
 595}
 596
 597/*
 598 * initialise the /proc/fs/afs/ directory
 599 */
 600int afs_proc_init(struct afs_net *net)
 601{
 602        struct proc_dir_entry *p;
 603
 604        _enter("");
 605
 606        p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
 607        if (!p)
 608                goto error_dir;
 609
 610        if (!proc_create_net_data_write("cells", 0644, p,
 611                                        &afs_proc_cells_ops,
 612                                        afs_proc_cells_write,
 613                                        sizeof(struct seq_net_private),
 614                                        NULL) ||
 615            !proc_create_net_single_write("rootcell", 0644, p,
 616                                          afs_proc_rootcell_show,
 617                                          afs_proc_rootcell_write,
 618                                          NULL) ||
 619            !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
 620                             sizeof(struct seq_net_private)) ||
 621            !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
 622            !proc_create_net_data_write("sysname", 0644, p,
 623                                        &afs_proc_sysname_ops,
 624                                        afs_proc_sysname_write,
 625                                        sizeof(struct seq_net_private),
 626                                        NULL))
 627                goto error_tree;
 628
 629        net->proc_afs = p;
 630        _leave(" = 0");
 631        return 0;
 632
 633error_tree:
 634        proc_remove(p);
 635error_dir:
 636        _leave(" = -ENOMEM");
 637        return -ENOMEM;
 638}
 639
 640/*
 641 * clean up the /proc/fs/afs/ directory
 642 */
 643void afs_proc_cleanup(struct afs_net *net)
 644{
 645        proc_remove(net->proc_afs);
 646        net->proc_afs = NULL;
 647}
 648