Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* /proc interface for AFS
0003  *
0004  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
0005  * Written by David Howells (dhowells@redhat.com)
0006  */
0007 
0008 #include <linux/slab.h>
0009 #include <linux/module.h>
0010 #include <linux/proc_fs.h>
0011 #include <linux/seq_file.h>
0012 #include <linux/sched.h>
0013 #include <linux/uaccess.h>
0014 #include "internal.h"
0015 
0016 struct afs_vl_seq_net_private {
0017     struct seq_net_private      seq;    /* Must be first */
0018     struct afs_vlserver_list    *vllist;
0019 };
0020 
0021 static inline struct afs_net *afs_seq2net(struct seq_file *m)
0022 {
0023     return afs_net(seq_file_net(m));
0024 }
0025 
0026 static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
0027 {
0028     return afs_net(seq_file_single_net(m));
0029 }
0030 
0031 /*
0032  * Display the list of cells known to the namespace.
0033  */
0034 static int afs_proc_cells_show(struct seq_file *m, void *v)
0035 {
0036     struct afs_vlserver_list *vllist;
0037     struct afs_cell *cell;
0038 
0039     if (v == SEQ_START_TOKEN) {
0040         /* display header on line 1 */
0041         seq_puts(m, "USE ACT    TTL SV ST NAME\n");
0042         return 0;
0043     }
0044 
0045     cell = list_entry(v, struct afs_cell, proc_link);
0046     vllist = rcu_dereference(cell->vl_servers);
0047 
0048     /* display one cell per line on subsequent lines */
0049     seq_printf(m, "%3u %3u %6lld %2u %2u %s\n",
0050            refcount_read(&cell->ref),
0051            atomic_read(&cell->active),
0052            cell->dns_expiry - ktime_get_real_seconds(),
0053            vllist ? vllist->nr_servers : 0,
0054            cell->state,
0055            cell->name);
0056     return 0;
0057 }
0058 
0059 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
0060     __acquires(rcu)
0061 {
0062     rcu_read_lock();
0063     return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
0064 }
0065 
0066 static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
0067 {
0068     return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
0069 }
0070 
0071 static void afs_proc_cells_stop(struct seq_file *m, void *v)
0072     __releases(rcu)
0073 {
0074     rcu_read_unlock();
0075 }
0076 
0077 static const struct seq_operations afs_proc_cells_ops = {
0078     .start  = afs_proc_cells_start,
0079     .next   = afs_proc_cells_next,
0080     .stop   = afs_proc_cells_stop,
0081     .show   = afs_proc_cells_show,
0082 };
0083 
0084 /*
0085  * handle writes to /proc/fs/afs/cells
0086  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
0087  */
0088 static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
0089 {
0090     struct seq_file *m = file->private_data;
0091     struct afs_net *net = afs_seq2net(m);
0092     char *name, *args;
0093     int ret;
0094 
0095     /* trim to first NL */
0096     name = memchr(buf, '\n', size);
0097     if (name)
0098         *name = 0;
0099 
0100     /* split into command, name and argslist */
0101     name = strchr(buf, ' ');
0102     if (!name)
0103         goto inval;
0104     do {
0105         *name++ = 0;
0106     } while(*name == ' ');
0107     if (!*name)
0108         goto inval;
0109 
0110     args = strchr(name, ' ');
0111     if (args) {
0112         do {
0113             *args++ = 0;
0114         } while(*args == ' ');
0115         if (!*args)
0116             goto inval;
0117     }
0118 
0119     /* determine command to perform */
0120     _debug("cmd=%s name=%s args=%s", buf, name, args);
0121 
0122     if (strcmp(buf, "add") == 0) {
0123         struct afs_cell *cell;
0124 
0125         cell = afs_lookup_cell(net, name, strlen(name), args, true);
0126         if (IS_ERR(cell)) {
0127             ret = PTR_ERR(cell);
0128             goto done;
0129         }
0130 
0131         if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
0132             afs_unuse_cell(net, cell, afs_cell_trace_unuse_no_pin);
0133     } else {
0134         goto inval;
0135     }
0136 
0137     ret = 0;
0138 
0139 done:
0140     _leave(" = %d", ret);
0141     return ret;
0142 
0143 inval:
0144     ret = -EINVAL;
0145     printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
0146     goto done;
0147 }
0148 
0149 /*
0150  * Display the name of the current workstation cell.
0151  */
0152 static int afs_proc_rootcell_show(struct seq_file *m, void *v)
0153 {
0154     struct afs_cell *cell;
0155     struct afs_net *net;
0156 
0157     net = afs_seq2net_single(m);
0158     down_read(&net->cells_lock);
0159     cell = net->ws_cell;
0160     if (cell)
0161         seq_printf(m, "%s\n", cell->name);
0162     up_read(&net->cells_lock);
0163     return 0;
0164 }
0165 
0166 /*
0167  * Set the current workstation cell and optionally supply its list of volume
0168  * location servers.
0169  *
0170  *  echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
0171  */
0172 static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
0173 {
0174     struct seq_file *m = file->private_data;
0175     struct afs_net *net = afs_seq2net_single(m);
0176     char *s;
0177     int ret;
0178 
0179     ret = -EINVAL;
0180     if (buf[0] == '.')
0181         goto out;
0182     if (memchr(buf, '/', size))
0183         goto out;
0184 
0185     /* trim to first NL */
0186     s = memchr(buf, '\n', size);
0187     if (s)
0188         *s = 0;
0189 
0190     /* determine command to perform */
0191     _debug("rootcell=%s", buf);
0192 
0193     ret = afs_cell_init(net, buf);
0194 
0195 out:
0196     _leave(" = %d", ret);
0197     return ret;
0198 }
0199 
0200 static const char afs_vol_types[3][3] = {
0201     [AFSVL_RWVOL]   = "RW",
0202     [AFSVL_ROVOL]   = "RO",
0203     [AFSVL_BACKVOL] = "BK",
0204 };
0205 
0206 /*
0207  * Display the list of volumes known to a cell.
0208  */
0209 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
0210 {
0211     struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
0212 
0213     /* Display header on line 1 */
0214     if (v == SEQ_START_TOKEN) {
0215         seq_puts(m, "USE VID      TY NAME\n");
0216         return 0;
0217     }
0218 
0219     seq_printf(m, "%3d %08llx %s %s\n",
0220            refcount_read(&vol->ref), vol->vid,
0221            afs_vol_types[vol->type],
0222            vol->name);
0223 
0224     return 0;
0225 }
0226 
0227 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
0228     __acquires(cell->proc_lock)
0229 {
0230     struct afs_cell *cell = pde_data(file_inode(m->file));
0231 
0232     rcu_read_lock();
0233     return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
0234 }
0235 
0236 static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
0237                     loff_t *_pos)
0238 {
0239     struct afs_cell *cell = pde_data(file_inode(m->file));
0240 
0241     return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
0242 }
0243 
0244 static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
0245     __releases(cell->proc_lock)
0246 {
0247     rcu_read_unlock();
0248 }
0249 
0250 static const struct seq_operations afs_proc_cell_volumes_ops = {
0251     .start  = afs_proc_cell_volumes_start,
0252     .next   = afs_proc_cell_volumes_next,
0253     .stop   = afs_proc_cell_volumes_stop,
0254     .show   = afs_proc_cell_volumes_show,
0255 };
0256 
0257 static const char *const dns_record_sources[NR__dns_record_source + 1] = {
0258     [DNS_RECORD_UNAVAILABLE]    = "unav",
0259     [DNS_RECORD_FROM_CONFIG]    = "cfg",
0260     [DNS_RECORD_FROM_DNS_A]     = "A",
0261     [DNS_RECORD_FROM_DNS_AFSDB] = "AFSDB",
0262     [DNS_RECORD_FROM_DNS_SRV]   = "SRV",
0263     [DNS_RECORD_FROM_NSS]       = "nss",
0264     [NR__dns_record_source]     = "[weird]"
0265 };
0266 
0267 static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
0268     [DNS_LOOKUP_NOT_DONE]       = "no-lookup",
0269     [DNS_LOOKUP_GOOD]       = "good",
0270     [DNS_LOOKUP_GOOD_WITH_BAD]  = "good/bad",
0271     [DNS_LOOKUP_BAD]        = "bad",
0272     [DNS_LOOKUP_GOT_NOT_FOUND]  = "not-found",
0273     [DNS_LOOKUP_GOT_LOCAL_FAILURE]  = "local-failure",
0274     [DNS_LOOKUP_GOT_TEMP_FAILURE]   = "temp-failure",
0275     [DNS_LOOKUP_GOT_NS_FAILURE] = "ns-failure",
0276     [NR__dns_lookup_status]     = "[weird]"
0277 };
0278 
0279 /*
0280  * Display the list of Volume Location servers we're using for a cell.
0281  */
0282 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
0283 {
0284     const struct afs_vl_seq_net_private *priv = m->private;
0285     const struct afs_vlserver_list *vllist = priv->vllist;
0286     const struct afs_vlserver_entry *entry;
0287     const struct afs_vlserver *vlserver;
0288     const struct afs_addr_list *alist;
0289     int i;
0290 
0291     if (v == SEQ_START_TOKEN) {
0292         seq_printf(m, "# source %s, status %s\n",
0293                dns_record_sources[vllist ? vllist->source : 0],
0294                dns_lookup_statuses[vllist ? vllist->status : 0]);
0295         return 0;
0296     }
0297 
0298     entry = v;
0299     vlserver = entry->server;
0300     alist = rcu_dereference(vlserver->addresses);
0301 
0302     seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
0303            vlserver->name, entry->priority, entry->weight,
0304            dns_record_sources[alist ? alist->source : entry->source],
0305            dns_lookup_statuses[alist ? alist->status : entry->status]);
0306     if (alist) {
0307         for (i = 0; i < alist->nr_addrs; i++)
0308             seq_printf(m, " %c %pISpc\n",
0309                    alist->preferred == i ? '>' : '-',
0310                    &alist->addrs[i].transport);
0311     }
0312     seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
0313     seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
0314            vlserver->probe.flags, vlserver->probe.error,
0315            vlserver->probe.abort_code,
0316            atomic_read(&vlserver->probe_outstanding));
0317     return 0;
0318 }
0319 
0320 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
0321     __acquires(rcu)
0322 {
0323     struct afs_vl_seq_net_private *priv = m->private;
0324     struct afs_vlserver_list *vllist;
0325     struct afs_cell *cell = pde_data(file_inode(m->file));
0326     loff_t pos = *_pos;
0327 
0328     rcu_read_lock();
0329 
0330     vllist = rcu_dereference(cell->vl_servers);
0331     priv->vllist = vllist;
0332 
0333     if (pos < 0)
0334         *_pos = pos = 0;
0335     if (pos == 0)
0336         return SEQ_START_TOKEN;
0337 
0338     if (pos - 1 >= vllist->nr_servers)
0339         return NULL;
0340 
0341     return &vllist->servers[pos - 1];
0342 }
0343 
0344 static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
0345                       loff_t *_pos)
0346 {
0347     struct afs_vl_seq_net_private *priv = m->private;
0348     struct afs_vlserver_list *vllist = priv->vllist;
0349     loff_t pos;
0350 
0351     pos = *_pos;
0352     pos++;
0353     *_pos = pos;
0354     if (!vllist || pos - 1 >= vllist->nr_servers)
0355         return NULL;
0356 
0357     return &vllist->servers[pos - 1];
0358 }
0359 
0360 static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
0361     __releases(rcu)
0362 {
0363     rcu_read_unlock();
0364 }
0365 
0366 static const struct seq_operations afs_proc_cell_vlservers_ops = {
0367     .start  = afs_proc_cell_vlservers_start,
0368     .next   = afs_proc_cell_vlservers_next,
0369     .stop   = afs_proc_cell_vlservers_stop,
0370     .show   = afs_proc_cell_vlservers_show,
0371 };
0372 
0373 /*
0374  * Display the list of fileservers we're using within a namespace.
0375  */
0376 static int afs_proc_servers_show(struct seq_file *m, void *v)
0377 {
0378     struct afs_server *server;
0379     struct afs_addr_list *alist;
0380     int i;
0381 
0382     if (v == SEQ_START_TOKEN) {
0383         seq_puts(m, "UUID                                 REF ACT\n");
0384         return 0;
0385     }
0386 
0387     server = list_entry(v, struct afs_server, proc_link);
0388     alist = rcu_dereference(server->addresses);
0389     seq_printf(m, "%pU %3d %3d\n",
0390            &server->uuid,
0391            refcount_read(&server->ref),
0392            atomic_read(&server->active));
0393     seq_printf(m, "  - info: fl=%lx rtt=%u brk=%x\n",
0394            server->flags, server->rtt, server->cb_s_break);
0395     seq_printf(m, "  - probe: last=%d out=%d\n",
0396            (int)(jiffies - server->probed_at) / HZ,
0397            atomic_read(&server->probe_outstanding));
0398     seq_printf(m, "  - ALIST v=%u rsp=%lx f=%lx\n",
0399            alist->version, alist->responded, alist->failed);
0400     for (i = 0; i < alist->nr_addrs; i++)
0401         seq_printf(m, "    [%x] %pISpc%s\n",
0402                i, &alist->addrs[i].transport,
0403                alist->preferred == i ? "*" : "");
0404     return 0;
0405 }
0406 
0407 static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
0408     __acquires(rcu)
0409 {
0410     rcu_read_lock();
0411     return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
0412 }
0413 
0414 static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
0415 {
0416     return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
0417 }
0418 
0419 static void afs_proc_servers_stop(struct seq_file *m, void *v)
0420     __releases(rcu)
0421 {
0422     rcu_read_unlock();
0423 }
0424 
0425 static const struct seq_operations afs_proc_servers_ops = {
0426     .start  = afs_proc_servers_start,
0427     .next   = afs_proc_servers_next,
0428     .stop   = afs_proc_servers_stop,
0429     .show   = afs_proc_servers_show,
0430 };
0431 
0432 /*
0433  * Display the list of strings that may be substituted for the @sys pathname
0434  * macro.
0435  */
0436 static int afs_proc_sysname_show(struct seq_file *m, void *v)
0437 {
0438     struct afs_net *net = afs_seq2net(m);
0439     struct afs_sysnames *sysnames = net->sysnames;
0440     unsigned int i = (unsigned long)v - 1;
0441 
0442     if (i < sysnames->nr)
0443         seq_printf(m, "%s\n", sysnames->subs[i]);
0444     return 0;
0445 }
0446 
0447 static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
0448     __acquires(&net->sysnames_lock)
0449 {
0450     struct afs_net *net = afs_seq2net(m);
0451     struct afs_sysnames *names;
0452 
0453     read_lock(&net->sysnames_lock);
0454 
0455     names = net->sysnames;
0456     if (*pos >= names->nr)
0457         return NULL;
0458     return (void *)(unsigned long)(*pos + 1);
0459 }
0460 
0461 static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
0462 {
0463     struct afs_net *net = afs_seq2net(m);
0464     struct afs_sysnames *names = net->sysnames;
0465 
0466     *pos += 1;
0467     if (*pos >= names->nr)
0468         return NULL;
0469     return (void *)(unsigned long)(*pos + 1);
0470 }
0471 
0472 static void afs_proc_sysname_stop(struct seq_file *m, void *v)
0473     __releases(&net->sysnames_lock)
0474 {
0475     struct afs_net *net = afs_seq2net(m);
0476 
0477     read_unlock(&net->sysnames_lock);
0478 }
0479 
0480 static const struct seq_operations afs_proc_sysname_ops = {
0481     .start  = afs_proc_sysname_start,
0482     .next   = afs_proc_sysname_next,
0483     .stop   = afs_proc_sysname_stop,
0484     .show   = afs_proc_sysname_show,
0485 };
0486 
0487 /*
0488  * Allow the @sys substitution to be configured.
0489  */
0490 static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
0491 {
0492     struct afs_sysnames *sysnames, *kill;
0493     struct seq_file *m = file->private_data;
0494     struct afs_net *net = afs_seq2net(m);
0495     char *s, *p, *sub;
0496     int ret, len;
0497 
0498     sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
0499     if (!sysnames)
0500         return -ENOMEM;
0501     refcount_set(&sysnames->usage, 1);
0502     kill = sysnames;
0503 
0504     p = buf;
0505     while ((s = strsep(&p, " \t\n"))) {
0506         len = strlen(s);
0507         if (len == 0)
0508             continue;
0509         ret = -ENAMETOOLONG;
0510         if (len >= AFSNAMEMAX)
0511             goto error;
0512 
0513         if (len >= 4 &&
0514             s[len - 4] == '@' &&
0515             s[len - 3] == 's' &&
0516             s[len - 2] == 'y' &&
0517             s[len - 1] == 's')
0518             /* Protect against recursion */
0519             goto invalid;
0520 
0521         if (s[0] == '.' &&
0522             (len < 2 || (len == 2 && s[1] == '.')))
0523             goto invalid;
0524 
0525         if (memchr(s, '/', len))
0526             goto invalid;
0527 
0528         ret = -EFBIG;
0529         if (sysnames->nr >= AFS_NR_SYSNAME)
0530             goto out;
0531 
0532         if (strcmp(s, afs_init_sysname) == 0) {
0533             sub = (char *)afs_init_sysname;
0534         } else {
0535             ret = -ENOMEM;
0536             sub = kmemdup(s, len + 1, GFP_KERNEL);
0537             if (!sub)
0538                 goto out;
0539         }
0540 
0541         sysnames->subs[sysnames->nr] = sub;
0542         sysnames->nr++;
0543     }
0544 
0545     if (sysnames->nr == 0) {
0546         sysnames->subs[0] = sysnames->blank;
0547         sysnames->nr++;
0548     }
0549 
0550     write_lock(&net->sysnames_lock);
0551     kill = net->sysnames;
0552     net->sysnames = sysnames;
0553     write_unlock(&net->sysnames_lock);
0554     ret = 0;
0555 out:
0556     afs_put_sysnames(kill);
0557     return ret;
0558 
0559 invalid:
0560     ret = -EINVAL;
0561 error:
0562     goto out;
0563 }
0564 
0565 void afs_put_sysnames(struct afs_sysnames *sysnames)
0566 {
0567     int i;
0568 
0569     if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
0570         for (i = 0; i < sysnames->nr; i++)
0571             if (sysnames->subs[i] != afs_init_sysname &&
0572                 sysnames->subs[i] != sysnames->blank)
0573                 kfree(sysnames->subs[i]);
0574         kfree(sysnames);
0575     }
0576 }
0577 
0578 /*
0579  * Display general per-net namespace statistics
0580  */
0581 static int afs_proc_stats_show(struct seq_file *m, void *v)
0582 {
0583     struct afs_net *net = afs_seq2net_single(m);
0584 
0585     seq_puts(m, "kAFS statistics\n");
0586 
0587     seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
0588            atomic_read(&net->n_lookup),
0589            atomic_read(&net->n_reval),
0590            atomic_read(&net->n_inval),
0591            atomic_read(&net->n_relpg));
0592 
0593     seq_printf(m, "dir-data: rdpg=%u\n",
0594            atomic_read(&net->n_read_dir));
0595 
0596     seq_printf(m, "dir-edit: cr=%u rm=%u\n",
0597            atomic_read(&net->n_dir_cr),
0598            atomic_read(&net->n_dir_rm));
0599 
0600     seq_printf(m, "file-rd : n=%u nb=%lu\n",
0601            atomic_read(&net->n_fetches),
0602            atomic_long_read(&net->n_fetch_bytes));
0603     seq_printf(m, "file-wr : n=%u nb=%lu\n",
0604            atomic_read(&net->n_stores),
0605            atomic_long_read(&net->n_store_bytes));
0606     return 0;
0607 }
0608 
0609 /*
0610  * initialise /proc/fs/afs/<cell>/
0611  */
0612 int afs_proc_cell_setup(struct afs_cell *cell)
0613 {
0614     struct proc_dir_entry *dir;
0615     struct afs_net *net = cell->net;
0616 
0617     _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
0618 
0619     dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
0620     if (!dir)
0621         goto error_dir;
0622 
0623     if (!proc_create_net_data("vlservers", 0444, dir,
0624                   &afs_proc_cell_vlservers_ops,
0625                   sizeof(struct afs_vl_seq_net_private),
0626                   cell) ||
0627         !proc_create_net_data("volumes", 0444, dir,
0628                   &afs_proc_cell_volumes_ops,
0629                   sizeof(struct seq_net_private),
0630                   cell))
0631         goto error_tree;
0632 
0633     _leave(" = 0");
0634     return 0;
0635 
0636 error_tree:
0637     remove_proc_subtree(cell->name, net->proc_afs);
0638 error_dir:
0639     _leave(" = -ENOMEM");
0640     return -ENOMEM;
0641 }
0642 
0643 /*
0644  * remove /proc/fs/afs/<cell>/
0645  */
0646 void afs_proc_cell_remove(struct afs_cell *cell)
0647 {
0648     struct afs_net *net = cell->net;
0649 
0650     _enter("");
0651     remove_proc_subtree(cell->name, net->proc_afs);
0652     _leave("");
0653 }
0654 
0655 /*
0656  * initialise the /proc/fs/afs/ directory
0657  */
0658 int afs_proc_init(struct afs_net *net)
0659 {
0660     struct proc_dir_entry *p;
0661 
0662     _enter("");
0663 
0664     p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
0665     if (!p)
0666         goto error_dir;
0667 
0668     if (!proc_create_net_data_write("cells", 0644, p,
0669                     &afs_proc_cells_ops,
0670                     afs_proc_cells_write,
0671                     sizeof(struct seq_net_private),
0672                     NULL) ||
0673         !proc_create_net_single_write("rootcell", 0644, p,
0674                       afs_proc_rootcell_show,
0675                       afs_proc_rootcell_write,
0676                       NULL) ||
0677         !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
0678                  sizeof(struct seq_net_private)) ||
0679         !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
0680         !proc_create_net_data_write("sysname", 0644, p,
0681                     &afs_proc_sysname_ops,
0682                     afs_proc_sysname_write,
0683                     sizeof(struct seq_net_private),
0684                     NULL))
0685         goto error_tree;
0686 
0687     net->proc_afs = p;
0688     _leave(" = 0");
0689     return 0;
0690 
0691 error_tree:
0692     proc_remove(p);
0693 error_dir:
0694     _leave(" = -ENOMEM");
0695     return -ENOMEM;
0696 }
0697 
0698 /*
0699  * clean up the /proc/fs/afs/ directory
0700  */
0701 void afs_proc_cleanup(struct afs_net *net)
0702 {
0703     proc_remove(net->proc_afs);
0704     net->proc_afs = NULL;
0705 }