Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/afs/proc.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* /proc interface for AFS
3
*
4
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
5
* Written by David Howells ([email protected])
6
*/
7
8
#include <linux/slab.h>
9
#include <linux/module.h>
10
#include <linux/proc_fs.h>
11
#include <linux/seq_file.h>
12
#include <linux/sched.h>
13
#include <linux/uaccess.h>
14
#include "internal.h"
15
16
struct afs_vl_seq_net_private {
17
struct seq_net_private seq; /* Must be first */
18
struct afs_vlserver_list *vllist;
19
};
20
21
static inline struct afs_net *afs_seq2net(struct seq_file *m)
22
{
23
return afs_net(seq_file_net(m));
24
}
25
26
static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
27
{
28
return afs_net(seq_file_single_net(m));
29
}
30
31
/*
32
* Display the list of cells known to the namespace.
33
*/
34
static int afs_proc_cells_show(struct seq_file *m, void *v)
35
{
36
struct afs_vlserver_list *vllist;
37
struct afs_cell *cell;
38
39
if (v == SEQ_START_TOKEN) {
40
/* display header on line 1 */
41
seq_puts(m, "USE ACT TTL SV ST NAME\n");
42
return 0;
43
}
44
45
cell = list_entry(v, struct afs_cell, proc_link);
46
vllist = rcu_dereference(cell->vl_servers);
47
48
/* display one cell per line on subsequent lines */
49
seq_printf(m, "%3u %3u %6lld %2u %2u %s\n",
50
refcount_read(&cell->ref),
51
atomic_read(&cell->active),
52
cell->dns_expiry - ktime_get_real_seconds(),
53
vllist ? vllist->nr_servers : 0,
54
cell->state,
55
cell->name);
56
return 0;
57
}
58
59
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
60
__acquires(rcu)
61
{
62
rcu_read_lock();
63
return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
64
}
65
66
static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
67
{
68
return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
69
}
70
71
static void afs_proc_cells_stop(struct seq_file *m, void *v)
72
__releases(rcu)
73
{
74
rcu_read_unlock();
75
}
76
77
static const struct seq_operations afs_proc_cells_ops = {
78
.start = afs_proc_cells_start,
79
.next = afs_proc_cells_next,
80
.stop = afs_proc_cells_stop,
81
.show = afs_proc_cells_show,
82
};
83
84
/*
85
* handle writes to /proc/fs/afs/cells
86
* - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
87
*/
88
static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
89
{
90
struct seq_file *m = file->private_data;
91
struct afs_net *net = afs_seq2net(m);
92
char *name, *args;
93
int ret;
94
95
/* trim to first NL */
96
name = memchr(buf, '\n', size);
97
if (name)
98
*name = 0;
99
100
/* split into command, name and argslist */
101
name = strchr(buf, ' ');
102
if (!name)
103
goto inval;
104
do {
105
*name++ = 0;
106
} while(*name == ' ');
107
if (!*name)
108
goto inval;
109
110
args = strchr(name, ' ');
111
if (args) {
112
do {
113
*args++ = 0;
114
} while(*args == ' ');
115
if (!*args)
116
goto inval;
117
}
118
119
/* determine command to perform */
120
_debug("cmd=%s name=%s args=%s", buf, name, args);
121
122
if (strcmp(buf, "add") == 0) {
123
struct afs_cell *cell;
124
125
cell = afs_lookup_cell(net, name, strlen(name), args, true,
126
afs_cell_trace_use_lookup_add);
127
if (IS_ERR(cell)) {
128
ret = PTR_ERR(cell);
129
goto done;
130
}
131
132
if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
133
afs_unuse_cell(cell, afs_cell_trace_unuse_no_pin);
134
} else {
135
goto inval;
136
}
137
138
ret = 0;
139
140
done:
141
_leave(" = %d", ret);
142
return ret;
143
144
inval:
145
ret = -EINVAL;
146
printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
147
goto done;
148
}
149
150
/*
151
* Display the list of addr_prefs known to the namespace.
152
*/
153
static int afs_proc_addr_prefs_show(struct seq_file *m, void *v)
154
{
155
struct afs_addr_preference_list *preflist;
156
struct afs_addr_preference *pref;
157
struct afs_net *net = afs_seq2net_single(m);
158
union {
159
struct sockaddr_in sin;
160
struct sockaddr_in6 sin6;
161
} addr;
162
unsigned int i;
163
char buf[44]; /* Maximum ipv6 + max subnet is 43 */
164
165
rcu_read_lock();
166
preflist = rcu_dereference(net->address_prefs);
167
168
if (!preflist) {
169
seq_puts(m, "NO PREFS\n");
170
goto out;
171
}
172
173
seq_printf(m, "PROT SUBNET PRIOR (v=%u n=%u/%u/%u)\n",
174
preflist->version, preflist->ipv6_off, preflist->nr, preflist->max_prefs);
175
176
memset(&addr, 0, sizeof(addr));
177
178
for (i = 0; i < preflist->nr; i++) {
179
pref = &preflist->prefs[i];
180
181
addr.sin.sin_family = pref->family;
182
if (pref->family == AF_INET) {
183
memcpy(&addr.sin.sin_addr, &pref->ipv4_addr,
184
sizeof(addr.sin.sin_addr));
185
snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin, pref->subnet_mask);
186
seq_printf(m, "UDP %-43.43s %5u\n", buf, pref->prio);
187
} else {
188
memcpy(&addr.sin6.sin6_addr, &pref->ipv6_addr,
189
sizeof(addr.sin6.sin6_addr));
190
snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin6, pref->subnet_mask);
191
seq_printf(m, "UDP %-43.43s %5u\n", buf, pref->prio);
192
}
193
}
194
195
out:
196
rcu_read_unlock();
197
return 0;
198
}
199
200
/*
201
* Display the name of the current workstation cell.
202
*/
203
static int afs_proc_rootcell_show(struct seq_file *m, void *v)
204
{
205
struct afs_cell *cell;
206
struct afs_net *net;
207
208
net = afs_seq2net_single(m);
209
down_read(&net->cells_lock);
210
cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
211
if (cell)
212
seq_printf(m, "%s\n", cell->name);
213
up_read(&net->cells_lock);
214
return 0;
215
}
216
217
/*
218
* Set the current workstation cell and optionally supply its list of volume
219
* location servers.
220
*
221
* echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
222
*/
223
static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
224
{
225
struct seq_file *m = file->private_data;
226
struct afs_net *net = afs_seq2net_single(m);
227
char *s;
228
int ret;
229
230
ret = -EINVAL;
231
if (buf[0] == '.')
232
goto out;
233
if (memchr(buf, '/', size))
234
goto out;
235
236
/* trim to first NL */
237
s = memchr(buf, '\n', size);
238
if (s)
239
*s = 0;
240
241
/* determine command to perform */
242
_debug("rootcell=%s", buf);
243
244
ret = -EEXIST;
245
inode_lock(file_inode(file));
246
if (!rcu_access_pointer(net->ws_cell))
247
ret = afs_cell_init(net, buf);
248
else
249
printk("busy\n");
250
inode_unlock(file_inode(file));
251
252
out:
253
_leave(" = %d", ret);
254
return ret;
255
}
256
257
static const char afs_vol_types[3][3] = {
258
[AFSVL_RWVOL] = "RW",
259
[AFSVL_ROVOL] = "RO",
260
[AFSVL_BACKVOL] = "BK",
261
};
262
263
/*
264
* Display the list of volumes known to a cell.
265
*/
266
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
267
{
268
struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
269
270
/* Display header on line 1 */
271
if (v == SEQ_START_TOKEN) {
272
seq_puts(m, "USE VID TY NAME\n");
273
return 0;
274
}
275
276
seq_printf(m, "%3d %08llx %s %s\n",
277
refcount_read(&vol->ref), vol->vid,
278
afs_vol_types[vol->type],
279
vol->name);
280
281
return 0;
282
}
283
284
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
285
__acquires(cell->proc_lock)
286
{
287
struct afs_cell *cell = pde_data(file_inode(m->file));
288
289
rcu_read_lock();
290
return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
291
}
292
293
static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
294
loff_t *_pos)
295
{
296
struct afs_cell *cell = pde_data(file_inode(m->file));
297
298
return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
299
}
300
301
static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
302
__releases(cell->proc_lock)
303
{
304
rcu_read_unlock();
305
}
306
307
static const struct seq_operations afs_proc_cell_volumes_ops = {
308
.start = afs_proc_cell_volumes_start,
309
.next = afs_proc_cell_volumes_next,
310
.stop = afs_proc_cell_volumes_stop,
311
.show = afs_proc_cell_volumes_show,
312
};
313
314
static const char *const dns_record_sources[NR__dns_record_source + 1] = {
315
[DNS_RECORD_UNAVAILABLE] = "unav",
316
[DNS_RECORD_FROM_CONFIG] = "cfg",
317
[DNS_RECORD_FROM_DNS_A] = "A",
318
[DNS_RECORD_FROM_DNS_AFSDB] = "AFSDB",
319
[DNS_RECORD_FROM_DNS_SRV] = "SRV",
320
[DNS_RECORD_FROM_NSS] = "nss",
321
[NR__dns_record_source] = "[weird]"
322
};
323
324
static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
325
[DNS_LOOKUP_NOT_DONE] = "no-lookup",
326
[DNS_LOOKUP_GOOD] = "good",
327
[DNS_LOOKUP_GOOD_WITH_BAD] = "good/bad",
328
[DNS_LOOKUP_BAD] = "bad",
329
[DNS_LOOKUP_GOT_NOT_FOUND] = "not-found",
330
[DNS_LOOKUP_GOT_LOCAL_FAILURE] = "local-failure",
331
[DNS_LOOKUP_GOT_TEMP_FAILURE] = "temp-failure",
332
[DNS_LOOKUP_GOT_NS_FAILURE] = "ns-failure",
333
[NR__dns_lookup_status] = "[weird]"
334
};
335
336
/*
337
* Display the list of Volume Location servers we're using for a cell.
338
*/
339
static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
340
{
341
const struct afs_vl_seq_net_private *priv = m->private;
342
const struct afs_vlserver_list *vllist = priv->vllist;
343
const struct afs_vlserver_entry *entry;
344
const struct afs_vlserver *vlserver;
345
const struct afs_addr_list *alist;
346
int i;
347
348
if (v == SEQ_START_TOKEN) {
349
seq_printf(m, "# source %s, status %s\n",
350
dns_record_sources[vllist ? vllist->source : 0],
351
dns_lookup_statuses[vllist ? vllist->status : 0]);
352
return 0;
353
}
354
355
entry = v;
356
vlserver = entry->server;
357
alist = rcu_dereference(vlserver->addresses);
358
359
seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
360
vlserver->name, entry->priority, entry->weight,
361
dns_record_sources[alist ? alist->source : entry->source],
362
dns_lookup_statuses[alist ? alist->status : entry->status]);
363
if (alist) {
364
for (i = 0; i < alist->nr_addrs; i++)
365
seq_printf(m, " %c %pISpc\n",
366
alist->preferred == i ? '>' : '-',
367
rxrpc_kernel_remote_addr(alist->addrs[i].peer));
368
}
369
seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
370
seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
371
vlserver->probe.flags, vlserver->probe.error,
372
vlserver->probe.abort_code,
373
atomic_read(&vlserver->probe_outstanding));
374
return 0;
375
}
376
377
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
378
__acquires(rcu)
379
{
380
struct afs_vl_seq_net_private *priv = m->private;
381
struct afs_vlserver_list *vllist;
382
struct afs_cell *cell = pde_data(file_inode(m->file));
383
loff_t pos = *_pos;
384
385
rcu_read_lock();
386
387
vllist = rcu_dereference(cell->vl_servers);
388
priv->vllist = vllist;
389
390
if (pos < 0)
391
*_pos = pos = 0;
392
if (pos == 0)
393
return SEQ_START_TOKEN;
394
395
if (pos - 1 >= vllist->nr_servers)
396
return NULL;
397
398
return &vllist->servers[pos - 1];
399
}
400
401
static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
402
loff_t *_pos)
403
{
404
struct afs_vl_seq_net_private *priv = m->private;
405
struct afs_vlserver_list *vllist = priv->vllist;
406
loff_t pos;
407
408
pos = *_pos;
409
pos++;
410
*_pos = pos;
411
if (!vllist || pos - 1 >= vllist->nr_servers)
412
return NULL;
413
414
return &vllist->servers[pos - 1];
415
}
416
417
static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
418
__releases(rcu)
419
{
420
rcu_read_unlock();
421
}
422
423
static const struct seq_operations afs_proc_cell_vlservers_ops = {
424
.start = afs_proc_cell_vlservers_start,
425
.next = afs_proc_cell_vlservers_next,
426
.stop = afs_proc_cell_vlservers_stop,
427
.show = afs_proc_cell_vlservers_show,
428
};
429
430
/*
431
* Display the list of fileservers we're using within a namespace.
432
*/
433
static int afs_proc_servers_show(struct seq_file *m, void *v)
434
{
435
struct afs_endpoint_state *estate;
436
struct afs_addr_list *alist;
437
struct afs_server *server;
438
unsigned long failed;
439
int i;
440
441
if (v == SEQ_START_TOKEN) {
442
seq_puts(m, "UUID REF ACT CELL\n");
443
return 0;
444
}
445
446
server = list_entry(v, struct afs_server, proc_link);
447
seq_printf(m, "%pU %3d %3d %s\n",
448
&server->uuid,
449
refcount_read(&server->ref),
450
atomic_read(&server->active),
451
server->cell->name);
452
seq_printf(m, " - info: fl=%lx rtt=%u\n",
453
server->flags, server->rtt);
454
seq_printf(m, " - probe: last=%d\n",
455
(int)(jiffies - server->probed_at) / HZ);
456
457
estate = rcu_dereference(server->endpoint_state);
458
if (!estate)
459
goto out;
460
failed = estate->failed_set;
461
seq_printf(m, " - ESTATE pq=%x np=%u rsp=%lx f=%lx\n",
462
estate->probe_seq, atomic_read(&estate->nr_probing),
463
estate->responsive_set, estate->failed_set);
464
465
alist = estate->addresses;
466
seq_printf(m, " - ALIST v=%u ap=%u\n",
467
alist->version, alist->addr_pref_version);
468
for (i = 0; i < alist->nr_addrs; i++) {
469
const struct afs_address *addr = &alist->addrs[i];
470
471
seq_printf(m, " [%x] %pISpc%s rtt=%d err=%d p=%u\n",
472
i, rxrpc_kernel_remote_addr(addr->peer),
473
alist->preferred == i ? "*" :
474
test_bit(i, &failed) ? "!" : "",
475
rxrpc_kernel_get_srtt(addr->peer),
476
addr->last_error, addr->prio);
477
}
478
479
out:
480
return 0;
481
}
482
483
static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
484
__acquires(rcu)
485
{
486
rcu_read_lock();
487
return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
488
}
489
490
static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
491
{
492
return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
493
}
494
495
static void afs_proc_servers_stop(struct seq_file *m, void *v)
496
__releases(rcu)
497
{
498
rcu_read_unlock();
499
}
500
501
static const struct seq_operations afs_proc_servers_ops = {
502
.start = afs_proc_servers_start,
503
.next = afs_proc_servers_next,
504
.stop = afs_proc_servers_stop,
505
.show = afs_proc_servers_show,
506
};
507
508
/*
509
* Display the list of strings that may be substituted for the @sys pathname
510
* macro.
511
*/
512
static int afs_proc_sysname_show(struct seq_file *m, void *v)
513
{
514
struct afs_net *net = afs_seq2net(m);
515
struct afs_sysnames *sysnames = net->sysnames;
516
unsigned int i = (unsigned long)v - 1;
517
518
if (i < sysnames->nr)
519
seq_printf(m, "%s\n", sysnames->subs[i]);
520
return 0;
521
}
522
523
static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
524
__acquires(&net->sysnames_lock)
525
{
526
struct afs_net *net = afs_seq2net(m);
527
struct afs_sysnames *names;
528
529
read_lock(&net->sysnames_lock);
530
531
names = net->sysnames;
532
if (*pos >= names->nr)
533
return NULL;
534
return (void *)(unsigned long)(*pos + 1);
535
}
536
537
static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
538
{
539
struct afs_net *net = afs_seq2net(m);
540
struct afs_sysnames *names = net->sysnames;
541
542
*pos += 1;
543
if (*pos >= names->nr)
544
return NULL;
545
return (void *)(unsigned long)(*pos + 1);
546
}
547
548
static void afs_proc_sysname_stop(struct seq_file *m, void *v)
549
__releases(&net->sysnames_lock)
550
{
551
struct afs_net *net = afs_seq2net(m);
552
553
read_unlock(&net->sysnames_lock);
554
}
555
556
static const struct seq_operations afs_proc_sysname_ops = {
557
.start = afs_proc_sysname_start,
558
.next = afs_proc_sysname_next,
559
.stop = afs_proc_sysname_stop,
560
.show = afs_proc_sysname_show,
561
};
562
563
/*
564
* Allow the @sys substitution to be configured.
565
*/
566
static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
567
{
568
struct afs_sysnames *sysnames, *kill;
569
struct seq_file *m = file->private_data;
570
struct afs_net *net = afs_seq2net(m);
571
char *s, *p, *sub;
572
int ret, len;
573
574
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
575
if (!sysnames)
576
return -ENOMEM;
577
refcount_set(&sysnames->usage, 1);
578
kill = sysnames;
579
580
p = buf;
581
while ((s = strsep(&p, " \t\n"))) {
582
len = strlen(s);
583
if (len == 0)
584
continue;
585
ret = -ENAMETOOLONG;
586
if (len >= AFSNAMEMAX)
587
goto error;
588
589
if (len >= 4 &&
590
s[len - 4] == '@' &&
591
s[len - 3] == 's' &&
592
s[len - 2] == 'y' &&
593
s[len - 1] == 's')
594
/* Protect against recursion */
595
goto invalid;
596
597
if (s[0] == '.' &&
598
(len < 2 || (len == 2 && s[1] == '.')))
599
goto invalid;
600
601
if (memchr(s, '/', len))
602
goto invalid;
603
604
ret = -EFBIG;
605
if (sysnames->nr >= AFS_NR_SYSNAME)
606
goto out;
607
608
if (strcmp(s, afs_init_sysname) == 0) {
609
sub = (char *)afs_init_sysname;
610
} else {
611
ret = -ENOMEM;
612
sub = kmemdup(s, len + 1, GFP_KERNEL);
613
if (!sub)
614
goto out;
615
}
616
617
sysnames->subs[sysnames->nr] = sub;
618
sysnames->nr++;
619
}
620
621
if (sysnames->nr == 0) {
622
sysnames->subs[0] = sysnames->blank;
623
sysnames->nr++;
624
}
625
626
write_lock(&net->sysnames_lock);
627
kill = net->sysnames;
628
net->sysnames = sysnames;
629
write_unlock(&net->sysnames_lock);
630
ret = 0;
631
out:
632
afs_put_sysnames(kill);
633
return ret;
634
635
invalid:
636
ret = -EINVAL;
637
error:
638
goto out;
639
}
640
641
void afs_put_sysnames(struct afs_sysnames *sysnames)
642
{
643
int i;
644
645
if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
646
for (i = 0; i < sysnames->nr; i++)
647
if (sysnames->subs[i] != afs_init_sysname &&
648
sysnames->subs[i] != sysnames->blank)
649
kfree(sysnames->subs[i]);
650
kfree(sysnames);
651
}
652
}
653
654
/*
655
* Display general per-net namespace statistics
656
*/
657
static int afs_proc_stats_show(struct seq_file *m, void *v)
658
{
659
struct afs_net *net = afs_seq2net_single(m);
660
661
seq_puts(m, "kAFS statistics\n");
662
663
seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
664
atomic_read(&net->n_lookup),
665
atomic_read(&net->n_reval),
666
atomic_read(&net->n_inval),
667
atomic_read(&net->n_relpg));
668
669
seq_printf(m, "dir-data: rdpg=%u\n",
670
atomic_read(&net->n_read_dir));
671
672
seq_printf(m, "dir-edit: cr=%u rm=%u\n",
673
atomic_read(&net->n_dir_cr),
674
atomic_read(&net->n_dir_rm));
675
676
seq_printf(m, "file-rd : n=%u nb=%lu\n",
677
atomic_read(&net->n_fetches),
678
atomic_long_read(&net->n_fetch_bytes));
679
seq_printf(m, "file-wr : n=%u nb=%lu\n",
680
atomic_read(&net->n_stores),
681
atomic_long_read(&net->n_store_bytes));
682
return 0;
683
}
684
685
/*
686
* initialise /proc/fs/afs/<cell>/
687
*/
688
int afs_proc_cell_setup(struct afs_cell *cell)
689
{
690
struct proc_dir_entry *dir;
691
struct afs_net *net = cell->net;
692
693
_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
694
695
dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
696
if (!dir)
697
goto error_dir;
698
699
if (!proc_create_net_data("vlservers", 0444, dir,
700
&afs_proc_cell_vlservers_ops,
701
sizeof(struct afs_vl_seq_net_private),
702
cell) ||
703
!proc_create_net_data("volumes", 0444, dir,
704
&afs_proc_cell_volumes_ops,
705
sizeof(struct seq_net_private),
706
cell))
707
goto error_tree;
708
709
_leave(" = 0");
710
return 0;
711
712
error_tree:
713
remove_proc_subtree(cell->name, net->proc_afs);
714
error_dir:
715
_leave(" = -ENOMEM");
716
return -ENOMEM;
717
}
718
719
/*
720
* remove /proc/fs/afs/<cell>/
721
*/
722
void afs_proc_cell_remove(struct afs_cell *cell)
723
{
724
struct afs_net *net = cell->net;
725
726
_enter("");
727
remove_proc_subtree(cell->name, net->proc_afs);
728
_leave("");
729
}
730
731
/*
732
* initialise the /proc/fs/afs/ directory
733
*/
734
int afs_proc_init(struct afs_net *net)
735
{
736
struct proc_dir_entry *p;
737
738
_enter("");
739
740
p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
741
if (!p)
742
goto error_dir;
743
744
if (!proc_create_net_data_write("cells", 0644, p,
745
&afs_proc_cells_ops,
746
afs_proc_cells_write,
747
sizeof(struct seq_net_private),
748
NULL) ||
749
!proc_create_net_single_write("rootcell", 0644, p,
750
afs_proc_rootcell_show,
751
afs_proc_rootcell_write,
752
NULL) ||
753
!proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
754
sizeof(struct seq_net_private)) ||
755
!proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
756
!proc_create_net_data_write("sysname", 0644, p,
757
&afs_proc_sysname_ops,
758
afs_proc_sysname_write,
759
sizeof(struct seq_net_private),
760
NULL) ||
761
!proc_create_net_single_write("addr_prefs", 0644, p,
762
afs_proc_addr_prefs_show,
763
afs_proc_addr_prefs_write,
764
NULL))
765
goto error_tree;
766
767
net->proc_afs = p;
768
_leave(" = 0");
769
return 0;
770
771
error_tree:
772
proc_remove(p);
773
error_dir:
774
_leave(" = -ENOMEM");
775
return -ENOMEM;
776
}
777
778
/*
779
* clean up the /proc/fs/afs/ directory
780
*/
781
void afs_proc_cleanup(struct afs_net *net)
782
{
783
proc_remove(net->proc_afs);
784
net->proc_afs = NULL;
785
}
786
787