blob: 6585f4bec0d37db1313aceb435a9eab72ca33ca2 [file] [log] [blame]
David Howellsec268152007-04-26 15:49:28 -07001/* /proc interface for AFS
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells ([email protected])
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/slab.h>
13#include <linux/module.h>
14#include <linux/proc_fs.h>
15#include <linux/seq_file.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040016#include <linux/sched.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080017#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "internal.h"
19
David Howells0a5143f2018-10-20 00:57:57 +010020struct afs_vl_seq_net_private {
21 struct seq_net_private seq; /* Must be first */
22 struct afs_vlserver_list *vllist;
23};
24
David Howellsf044c882017-11-02 15:27:45 +000025static inline struct afs_net *afs_seq2net(struct seq_file *m)
26{
David Howells5b86d4f2018-05-18 11:46:15 +010027 return afs_net(seq_file_net(m));
28}
29
30static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
31{
32 return afs_net(seq_file_single_net(m));
David Howellsf044c882017-11-02 15:27:45 +000033}
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/*
David Howells5d9de252018-05-18 11:46:15 +010036 * Display the list of cells known to the namespace.
David Howellsf0691682018-05-18 11:46:14 +010037 */
38static int afs_proc_cells_show(struct seq_file *m, void *v)
39{
40 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
David Howellsf0691682018-05-18 11:46:14 +010041
David Howells6b3944e2018-10-11 22:45:49 +010042 if (v == SEQ_START_TOKEN) {
David Howellsf0691682018-05-18 11:46:14 +010043 /* display header on line 1 */
44 seq_puts(m, "USE NAME\n");
45 return 0;
46 }
47
48 /* display one cell per line on subsequent lines */
49 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
50 return 0;
51}
52
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +010054 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -070055{
David Howells989782d2017-11-02 15:27:50 +000056 rcu_read_lock();
David Howells6b3944e2018-10-11 22:45:49 +010057 return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
David Howellsec268152007-04-26 15:49:28 -070058}
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
David Howellsf044c882017-11-02 15:27:45 +000060static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -070061{
David Howells6b3944e2018-10-11 22:45:49 +010062 return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
David Howellsec268152007-04-26 15:49:28 -070063}
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
David Howellsf044c882017-11-02 15:27:45 +000065static void afs_proc_cells_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +010066 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -070067{
David Howells989782d2017-11-02 15:27:50 +000068 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -070069}
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
David Howells5d9de252018-05-18 11:46:15 +010071static const struct seq_operations afs_proc_cells_ops = {
72 .start = afs_proc_cells_start,
73 .next = afs_proc_cells_next,
74 .stop = afs_proc_cells_stop,
75 .show = afs_proc_cells_show,
76};
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 * handle writes to /proc/fs/afs/cells
80 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
81 */
David Howells5b86d4f2018-05-18 11:46:15 +010082static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083{
David Howells5b86d4f2018-05-18 11:46:15 +010084 struct seq_file *m = file->private_data;
85 struct afs_net *net = afs_seq2net(m);
86 char *name, *args;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 int ret;
88
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 /* trim to first NL */
David Howells5b86d4f2018-05-18 11:46:15 +010090 name = memchr(buf, '\n', size);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 if (name)
92 *name = 0;
93
94 /* split into command, name and argslist */
David Howells5b86d4f2018-05-18 11:46:15 +010095 name = strchr(buf, ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 if (!name)
97 goto inval;
98 do {
99 *name++ = 0;
100 } while(*name == ' ');
101 if (!*name)
102 goto inval;
103
104 args = strchr(name, ' ');
David Howellsecfe9512018-09-07 23:55:17 +0100105 if (args) {
106 do {
107 *args++ = 0;
108 } while(*args == ' ');
109 if (!*args)
110 goto inval;
111 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
113 /* determine command to perform */
David Howells5b86d4f2018-05-18 11:46:15 +0100114 _debug("cmd=%s name=%s args=%s", buf, name, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
David Howells5b86d4f2018-05-18 11:46:15 +0100116 if (strcmp(buf, "add") == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 struct afs_cell *cell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
David Howells989782d2017-11-02 15:27:50 +0000119 cell = afs_lookup_cell(net, name, strlen(name), args, true);
David Howells08e0e7c2007-04-26 15:55:03 -0700120 if (IS_ERR(cell)) {
121 ret = PTR_ERR(cell);
122 goto done;
123 }
124
David Howells17814ae2018-04-09 21:12:31 +0100125 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
126 afs_put_cell(net, cell);
David Howellsec268152007-04-26 15:49:28 -0700127 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 goto inval;
129 }
130
David Howells5b86d4f2018-05-18 11:46:15 +0100131 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
David Howellsec268152007-04-26 15:49:28 -0700133done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 _leave(" = %d", ret);
135 return ret;
136
David Howellsec268152007-04-26 15:49:28 -0700137inval:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 ret = -EINVAL;
139 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
140 goto done;
David Howellsec268152007-04-26 15:49:28 -0700141}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
David Howells5d9de252018-05-18 11:46:15 +0100143/*
David Howells5b86d4f2018-05-18 11:46:15 +0100144 * Display the name of the current workstation cell.
David Howells5d9de252018-05-18 11:46:15 +0100145 */
David Howells5b86d4f2018-05-18 11:46:15 +0100146static int afs_proc_rootcell_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
David Howells37ab6362018-04-06 14:17:23 +0100148 struct afs_cell *cell;
David Howells5b86d4f2018-05-18 11:46:15 +0100149 struct afs_net *net;
David Howells37ab6362018-04-06 14:17:23 +0100150
David Howells5b86d4f2018-05-18 11:46:15 +0100151 net = afs_seq2net_single(m);
152 if (rcu_access_pointer(net->ws_cell)) {
153 rcu_read_lock();
154 cell = rcu_dereference(net->ws_cell);
155 if (cell)
156 seq_printf(m, "%s\n", cell->name);
157 rcu_read_unlock();
158 }
159 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160}
161
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162/*
David Howells5d9de252018-05-18 11:46:15 +0100163 * Set the current workstation cell and optionally supply its list of volume
164 * location servers.
165 *
166 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 */
David Howells5b86d4f2018-05-18 11:46:15 +0100168static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
David Howells5b86d4f2018-05-18 11:46:15 +0100170 struct seq_file *m = file->private_data;
171 struct afs_net *net = afs_seq2net_single(m);
172 char *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 int ret;
174
David Howells6f8880d2018-04-09 21:12:31 +0100175 ret = -EINVAL;
David Howells5b86d4f2018-05-18 11:46:15 +0100176 if (buf[0] == '.')
David Howells6f8880d2018-04-09 21:12:31 +0100177 goto out;
David Howells5b86d4f2018-05-18 11:46:15 +0100178 if (memchr(buf, '/', size))
David Howells6f8880d2018-04-09 21:12:31 +0100179 goto out;
180
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 /* trim to first NL */
David Howells5b86d4f2018-05-18 11:46:15 +0100182 s = memchr(buf, '\n', size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 if (s)
184 *s = 0;
185
186 /* determine command to perform */
David Howells5b86d4f2018-05-18 11:46:15 +0100187 _debug("rootcell=%s", buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
David Howells5b86d4f2018-05-18 11:46:15 +0100189 ret = afs_cell_init(net, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
David Howells6f8880d2018-04-09 21:12:31 +0100191out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 _leave(" = %d", ret);
193 return ret;
David Howellsec268152007-04-26 15:49:28 -0700194}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
David Howellsf0691682018-05-18 11:46:14 +0100196static const char afs_vol_types[3][3] = {
197 [AFSVL_RWVOL] = "RW",
198 [AFSVL_ROVOL] = "RO",
199 [AFSVL_BACKVOL] = "BK",
200};
201
202/*
David Howells5d9de252018-05-18 11:46:15 +0100203 * Display the list of volumes known to a cell.
David Howellsf0691682018-05-18 11:46:14 +0100204 */
205static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
206{
207 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
208 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
209
210 /* Display header on line 1 */
211 if (v == &cell->proc_volumes) {
212 seq_puts(m, "USE VID TY\n");
213 return 0;
214 }
215
216 seq_printf(m, "%3d %08x %s\n",
217 atomic_read(&vol->usage), vol->vid,
218 afs_vol_types[vol->type]);
219
220 return 0;
221}
222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100224 __acquires(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200226 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
David Howellsd2ddc772017-11-02 15:27:50 +0000228 read_lock(&cell->proc_lock);
229 return seq_list_start_head(&cell->proc_volumes, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700230}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
David Howells5b86d4f2018-05-18 11:46:15 +0100232static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 loff_t *_pos)
234{
David Howells5b86d4f2018-05-18 11:46:15 +0100235 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
David Howellsd2ddc772017-11-02 15:27:50 +0000237 return seq_list_next(v, &cell->proc_volumes, _pos);
David Howellsec268152007-04-26 15:49:28 -0700238}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
David Howells5b86d4f2018-05-18 11:46:15 +0100240static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100241 __releases(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
David Howells5b86d4f2018-05-18 11:46:15 +0100243 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
David Howellsd2ddc772017-11-02 15:27:50 +0000245 read_unlock(&cell->proc_lock);
David Howellsec268152007-04-26 15:49:28 -0700246}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
David Howells5d9de252018-05-18 11:46:15 +0100248static const struct seq_operations afs_proc_cell_volumes_ops = {
249 .start = afs_proc_cell_volumes_start,
250 .next = afs_proc_cell_volumes_next,
251 .stop = afs_proc_cell_volumes_stop,
252 .show = afs_proc_cell_volumes_show,
253};
254
David Howells0a5143f2018-10-20 00:57:57 +0100255static const char *const dns_record_sources[NR__dns_record_source + 1] = {
256 [DNS_RECORD_UNAVAILABLE] = "unav",
257 [DNS_RECORD_FROM_CONFIG] = "cfg",
258 [DNS_RECORD_FROM_DNS_A] = "A",
259 [DNS_RECORD_FROM_DNS_AFSDB] = "AFSDB",
260 [DNS_RECORD_FROM_DNS_SRV] = "SRV",
261 [DNS_RECORD_FROM_NSS] = "nss",
262 [NR__dns_record_source] = "[weird]"
263};
264
265static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
266 [DNS_LOOKUP_NOT_DONE] = "no-lookup",
267 [DNS_LOOKUP_GOOD] = "good",
268 [DNS_LOOKUP_GOOD_WITH_BAD] = "good/bad",
269 [DNS_LOOKUP_BAD] = "bad",
270 [DNS_LOOKUP_GOT_NOT_FOUND] = "not-found",
271 [DNS_LOOKUP_GOT_LOCAL_FAILURE] = "local-failure",
272 [DNS_LOOKUP_GOT_TEMP_FAILURE] = "temp-failure",
273 [DNS_LOOKUP_GOT_NS_FAILURE] = "ns-failure",
274 [NR__dns_lookup_status] = "[weird]"
275};
276
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277/*
David Howells5d9de252018-05-18 11:46:15 +0100278 * Display the list of Volume Location servers we're using for a cell.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 */
David Howellsf0691682018-05-18 11:46:14 +0100280static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
David Howells0a5143f2018-10-20 00:57:57 +0100282 const struct afs_vl_seq_net_private *priv = m->private;
283 const struct afs_vlserver_list *vllist = priv->vllist;
284 const struct afs_vlserver_entry *entry;
285 const struct afs_vlserver *vlserver;
286 const struct afs_addr_list *alist;
287 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
David Howells0a5143f2018-10-20 00:57:57 +0100289 if (v == SEQ_START_TOKEN) {
290 seq_printf(m, "# source %s, status %s\n",
291 dns_record_sources[vllist->source],
292 dns_lookup_statuses[vllist->status]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 return 0;
294 }
295
David Howells0a5143f2018-10-20 00:57:57 +0100296 entry = v;
297 vlserver = entry->server;
298 alist = rcu_dereference(vlserver->addresses);
299
300 seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
301 vlserver->name, entry->priority, entry->weight,
302 dns_record_sources[alist ? alist->source : entry->source],
303 dns_lookup_statuses[alist ? alist->status : entry->status]);
304 if (alist) {
305 for (i = 0; i < alist->nr_addrs; i++)
306 seq_printf(m, " %c %pISpc\n",
307 alist->index == i ? '>' : '-',
308 &alist->addrs[i].transport);
309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 return 0;
David Howellsec268152007-04-26 15:49:28 -0700311}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100314 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
David Howells0a5143f2018-10-20 00:57:57 +0100316 struct afs_vl_seq_net_private *priv = m->private;
317 struct afs_vlserver_list *vllist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200318 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 loff_t pos = *_pos;
320
David Howells8b2a4642017-11-02 15:27:50 +0000321 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
David Howells0a5143f2018-10-20 00:57:57 +0100323 vllist = rcu_dereference(cell->vl_servers);
324 priv->vllist = vllist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
David Howells0a5143f2018-10-20 00:57:57 +0100326 if (pos < 0)
327 *_pos = pos = 0;
328 if (pos == 0)
329 return SEQ_START_TOKEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
David Howells0a5143f2018-10-20 00:57:57 +0100331 if (!vllist || pos - 1 >= vllist->nr_servers)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 return NULL;
333
David Howells0a5143f2018-10-20 00:57:57 +0100334 return &vllist->servers[pos - 1];
David Howellsec268152007-04-26 15:49:28 -0700335}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
David Howells5b86d4f2018-05-18 11:46:15 +0100337static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 loff_t *_pos)
339{
David Howells0a5143f2018-10-20 00:57:57 +0100340 struct afs_vl_seq_net_private *priv = m->private;
341 struct afs_vlserver_list *vllist = priv->vllist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 loff_t pos;
343
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 pos = *_pos;
David Howells0a5143f2018-10-20 00:57:57 +0100345 pos++;
346 *_pos = pos;
347 if (!vllist || pos - 1 >= vllist->nr_servers)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 return NULL;
349
David Howells0a5143f2018-10-20 00:57:57 +0100350 return &vllist->servers[pos - 1];
David Howellsec268152007-04-26 15:49:28 -0700351}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
David Howells5b86d4f2018-05-18 11:46:15 +0100353static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100354 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
David Howells8b2a4642017-11-02 15:27:50 +0000356 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700357}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
David Howells5d9de252018-05-18 11:46:15 +0100359static const struct seq_operations afs_proc_cell_vlservers_ops = {
360 .start = afs_proc_cell_vlservers_start,
361 .next = afs_proc_cell_vlservers_next,
362 .stop = afs_proc_cell_vlservers_stop,
363 .show = afs_proc_cell_vlservers_show,
364};
365
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366/*
David Howells5d9de252018-05-18 11:46:15 +0100367 * Display the list of fileservers we're using within a namespace.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 */
David Howellsf0691682018-05-18 11:46:14 +0100369static int afs_proc_servers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370{
David Howellsf0691682018-05-18 11:46:14 +0100371 struct afs_server *server;
372 struct afs_addr_list *alist;
David Howells0aac4bce2018-06-02 22:20:31 +0100373 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
David Howellsf0691682018-05-18 11:46:14 +0100375 if (v == SEQ_START_TOKEN) {
376 seq_puts(m, "UUID USE ADDR\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 return 0;
378 }
379
David Howellsf0691682018-05-18 11:46:14 +0100380 server = list_entry(v, struct afs_server, proc_link);
381 alist = rcu_dereference(server->addresses);
David Howells0aac4bce2018-06-02 22:20:31 +0100382 seq_printf(m, "%pU %3d %pISpc%s\n",
David Howellsf0691682018-05-18 11:46:14 +0100383 &server->uuid,
384 atomic_read(&server->usage),
David Howells0aac4bce2018-06-02 22:20:31 +0100385 &alist->addrs[0].transport,
386 alist->index == 0 ? "*" : "");
387 for (i = 1; i < alist->nr_addrs; i++)
388 seq_printf(m, " %pISpc%s\n",
389 &alist->addrs[i].transport,
390 alist->index == i ? "*" : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 return 0;
David Howellsec268152007-04-26 15:49:28 -0700392}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
David Howellsd2ddc772017-11-02 15:27:50 +0000394static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100395 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
David Howellsd2ddc772017-11-02 15:27:50 +0000397 rcu_read_lock();
David Howells5d9de252018-05-18 11:46:15 +0100398 return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700399}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
David Howellsd2ddc772017-11-02 15:27:50 +0000401static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
David Howells5d9de252018-05-18 11:46:15 +0100403 return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
David Howellsec268152007-04-26 15:49:28 -0700404}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
David Howells5b86d4f2018-05-18 11:46:15 +0100406static void afs_proc_servers_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100407 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
David Howellsd2ddc772017-11-02 15:27:50 +0000409 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700410}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
David Howells5d9de252018-05-18 11:46:15 +0100412static const struct seq_operations afs_proc_servers_ops = {
413 .start = afs_proc_servers_start,
414 .next = afs_proc_servers_next,
415 .stop = afs_proc_servers_stop,
416 .show = afs_proc_servers_show,
417};
David Howells6f8880d2018-04-09 21:12:31 +0100418
David Howells6f8880d2018-04-09 21:12:31 +0100419/*
David Howells5d9de252018-05-18 11:46:15 +0100420 * Display the list of strings that may be substituted for the @sys pathname
421 * macro.
David Howells6f8880d2018-04-09 21:12:31 +0100422 */
David Howells5d9de252018-05-18 11:46:15 +0100423static int afs_proc_sysname_show(struct seq_file *m, void *v)
David Howells6f8880d2018-04-09 21:12:31 +0100424{
David Howells5d9de252018-05-18 11:46:15 +0100425 struct afs_net *net = afs_seq2net(m);
426 struct afs_sysnames *sysnames = net->sysnames;
427 unsigned int i = (unsigned long)v - 1;
David Howells6f8880d2018-04-09 21:12:31 +0100428
David Howells5d9de252018-05-18 11:46:15 +0100429 if (i < sysnames->nr)
430 seq_printf(m, "%s\n", sysnames->subs[i]);
David Howells6f8880d2018-04-09 21:12:31 +0100431 return 0;
432}
433
David Howells5d9de252018-05-18 11:46:15 +0100434static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
435 __acquires(&net->sysnames_lock)
436{
437 struct afs_net *net = afs_seq2net(m);
David Howells5b86d4f2018-05-18 11:46:15 +0100438 struct afs_sysnames *names;
David Howells5d9de252018-05-18 11:46:15 +0100439
440 read_lock(&net->sysnames_lock);
441
David Howells5b86d4f2018-05-18 11:46:15 +0100442 names = net->sysnames;
David Howells5d9de252018-05-18 11:46:15 +0100443 if (*pos >= names->nr)
444 return NULL;
445 return (void *)(unsigned long)(*pos + 1);
446}
447
448static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
449{
450 struct afs_net *net = afs_seq2net(m);
451 struct afs_sysnames *names = net->sysnames;
452
453 *pos += 1;
454 if (*pos >= names->nr)
455 return NULL;
456 return (void *)(unsigned long)(*pos + 1);
457}
458
459static void afs_proc_sysname_stop(struct seq_file *m, void *v)
460 __releases(&net->sysnames_lock)
461{
462 struct afs_net *net = afs_seq2net(m);
463
464 read_unlock(&net->sysnames_lock);
465}
466
467static const struct seq_operations afs_proc_sysname_ops = {
468 .start = afs_proc_sysname_start,
469 .next = afs_proc_sysname_next,
470 .stop = afs_proc_sysname_stop,
471 .show = afs_proc_sysname_show,
472};
473
David Howells6f8880d2018-04-09 21:12:31 +0100474/*
David Howells5d9de252018-05-18 11:46:15 +0100475 * Allow the @sys substitution to be configured.
David Howells6f8880d2018-04-09 21:12:31 +0100476 */
David Howells5b86d4f2018-05-18 11:46:15 +0100477static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
David Howells6f8880d2018-04-09 21:12:31 +0100478{
David Howells5b86d4f2018-05-18 11:46:15 +0100479 struct afs_sysnames *sysnames, *kill;
David Howells6f8880d2018-04-09 21:12:31 +0100480 struct seq_file *m = file->private_data;
David Howells5b86d4f2018-05-18 11:46:15 +0100481 struct afs_net *net = afs_seq2net(m);
482 char *s, *p, *sub;
David Howells6f8880d2018-04-09 21:12:31 +0100483 int ret, len;
484
David Howells5b86d4f2018-05-18 11:46:15 +0100485 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
David Howells6f8880d2018-04-09 21:12:31 +0100486 if (!sysnames)
David Howells5b86d4f2018-05-18 11:46:15 +0100487 return -ENOMEM;
488 refcount_set(&sysnames->usage, 1);
489 kill = sysnames;
David Howells6f8880d2018-04-09 21:12:31 +0100490
David Howells5b86d4f2018-05-18 11:46:15 +0100491 p = buf;
David Howells6f8880d2018-04-09 21:12:31 +0100492 while ((s = strsep(&p, " \t\n"))) {
493 len = strlen(s);
494 if (len == 0)
495 continue;
496 ret = -ENAMETOOLONG;
497 if (len >= AFSNAMEMAX)
498 goto error;
499
500 if (len >= 4 &&
501 s[len - 4] == '@' &&
502 s[len - 3] == 's' &&
503 s[len - 2] == 'y' &&
504 s[len - 1] == 's')
505 /* Protect against recursion */
506 goto invalid;
507
508 if (s[0] == '.' &&
509 (len < 2 || (len == 2 && s[1] == '.')))
510 goto invalid;
511
512 if (memchr(s, '/', len))
513 goto invalid;
514
515 ret = -EFBIG;
516 if (sysnames->nr >= AFS_NR_SYSNAME)
517 goto out;
518
519 if (strcmp(s, afs_init_sysname) == 0) {
520 sub = (char *)afs_init_sysname;
521 } else {
522 ret = -ENOMEM;
523 sub = kmemdup(s, len + 1, GFP_KERNEL);
524 if (!sub)
525 goto out;
526 }
527
528 sysnames->subs[sysnames->nr] = sub;
529 sysnames->nr++;
530 }
531
David Howells5b86d4f2018-05-18 11:46:15 +0100532 if (sysnames->nr == 0) {
533 sysnames->subs[0] = sysnames->blank;
534 sysnames->nr++;
535 }
536
537 write_lock(&net->sysnames_lock);
538 kill = net->sysnames;
539 net->sysnames = sysnames;
540 write_unlock(&net->sysnames_lock);
541 ret = 0;
David Howells6f8880d2018-04-09 21:12:31 +0100542out:
David Howells5b86d4f2018-05-18 11:46:15 +0100543 afs_put_sysnames(kill);
David Howells6f8880d2018-04-09 21:12:31 +0100544 return ret;
545
546invalid:
547 ret = -EINVAL;
548error:
David Howells6f8880d2018-04-09 21:12:31 +0100549 goto out;
550}
551
David Howells5d9de252018-05-18 11:46:15 +0100552void afs_put_sysnames(struct afs_sysnames *sysnames)
553{
554 int i;
555
556 if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
557 for (i = 0; i < sysnames->nr; i++)
558 if (sysnames->subs[i] != afs_init_sysname &&
559 sysnames->subs[i] != sysnames->blank)
560 kfree(sysnames->subs[i]);
561 }
562}
563
David Howellsd55b4da2018-04-06 14:17:24 +0100564/*
565 * Display general per-net namespace statistics
566 */
567static int afs_proc_stats_show(struct seq_file *m, void *v)
568{
David Howells5b86d4f2018-05-18 11:46:15 +0100569 struct afs_net *net = afs_seq2net_single(m);
David Howellsd55b4da2018-04-06 14:17:24 +0100570
571 seq_puts(m, "kAFS statistics\n");
572
David Howellsf3ddee82018-04-06 14:17:25 +0100573 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
David Howellsd55b4da2018-04-06 14:17:24 +0100574 atomic_read(&net->n_lookup),
575 atomic_read(&net->n_reval),
David Howellsf3ddee82018-04-06 14:17:25 +0100576 atomic_read(&net->n_inval),
577 atomic_read(&net->n_relpg));
David Howellsd55b4da2018-04-06 14:17:24 +0100578
579 seq_printf(m, "dir-data: rdpg=%u\n",
580 atomic_read(&net->n_read_dir));
David Howells63a46812018-04-06 14:17:25 +0100581
582 seq_printf(m, "dir-edit: cr=%u rm=%u\n",
583 atomic_read(&net->n_dir_cr),
584 atomic_read(&net->n_dir_rm));
David Howells76a5cb62018-04-06 14:17:26 +0100585
586 seq_printf(m, "file-rd : n=%u nb=%lu\n",
587 atomic_read(&net->n_fetches),
588 atomic_long_read(&net->n_fetch_bytes));
589 seq_printf(m, "file-wr : n=%u nb=%lu\n",
590 atomic_read(&net->n_stores),
591 atomic_long_read(&net->n_store_bytes));
David Howellsd55b4da2018-04-06 14:17:24 +0100592 return 0;
593}
David Howells10495a02018-05-18 11:46:14 +0100594
595/*
596 * initialise /proc/fs/afs/<cell>/
597 */
David Howells5b86d4f2018-05-18 11:46:15 +0100598int afs_proc_cell_setup(struct afs_cell *cell)
David Howells10495a02018-05-18 11:46:14 +0100599{
600 struct proc_dir_entry *dir;
David Howells5b86d4f2018-05-18 11:46:15 +0100601 struct afs_net *net = cell->net;
David Howells10495a02018-05-18 11:46:14 +0100602
603 _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
604
David Howells5b86d4f2018-05-18 11:46:15 +0100605 dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
David Howells10495a02018-05-18 11:46:14 +0100606 if (!dir)
607 goto error_dir;
608
David Howells5b86d4f2018-05-18 11:46:15 +0100609 if (!proc_create_net_data("vlservers", 0444, dir,
610 &afs_proc_cell_vlservers_ops,
David Howells0a5143f2018-10-20 00:57:57 +0100611 sizeof(struct afs_vl_seq_net_private),
David Howells5b86d4f2018-05-18 11:46:15 +0100612 cell) ||
613 !proc_create_net_data("volumes", 0444, dir,
614 &afs_proc_cell_volumes_ops,
615 sizeof(struct seq_net_private),
616 cell))
David Howells10495a02018-05-18 11:46:14 +0100617 goto error_tree;
618
619 _leave(" = 0");
620 return 0;
621
622error_tree:
623 remove_proc_subtree(cell->name, net->proc_afs);
624error_dir:
625 _leave(" = -ENOMEM");
626 return -ENOMEM;
627}
628
629/*
630 * remove /proc/fs/afs/<cell>/
631 */
David Howells5b86d4f2018-05-18 11:46:15 +0100632void afs_proc_cell_remove(struct afs_cell *cell)
David Howells10495a02018-05-18 11:46:14 +0100633{
David Howells5b86d4f2018-05-18 11:46:15 +0100634 struct afs_net *net = cell->net;
635
David Howells10495a02018-05-18 11:46:14 +0100636 _enter("");
David Howells10495a02018-05-18 11:46:14 +0100637 remove_proc_subtree(cell->name, net->proc_afs);
David Howells10495a02018-05-18 11:46:14 +0100638 _leave("");
639}
640
641/*
642 * initialise the /proc/fs/afs/ directory
643 */
644int afs_proc_init(struct afs_net *net)
645{
David Howells5b86d4f2018-05-18 11:46:15 +0100646 struct proc_dir_entry *p;
647
David Howells10495a02018-05-18 11:46:14 +0100648 _enter("");
649
David Howells5b86d4f2018-05-18 11:46:15 +0100650 p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
651 if (!p)
David Howells10495a02018-05-18 11:46:14 +0100652 goto error_dir;
653
David Howells5b86d4f2018-05-18 11:46:15 +0100654 if (!proc_create_net_data_write("cells", 0644, p,
655 &afs_proc_cells_ops,
656 afs_proc_cells_write,
657 sizeof(struct seq_net_private),
658 NULL) ||
659 !proc_create_net_single_write("rootcell", 0644, p,
660 afs_proc_rootcell_show,
661 afs_proc_rootcell_write,
662 NULL) ||
663 !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
664 sizeof(struct seq_net_private)) ||
665 !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
666 !proc_create_net_data_write("sysname", 0644, p,
667 &afs_proc_sysname_ops,
668 afs_proc_sysname_write,
669 sizeof(struct seq_net_private),
670 NULL))
David Howells10495a02018-05-18 11:46:14 +0100671 goto error_tree;
672
David Howells5b86d4f2018-05-18 11:46:15 +0100673 net->proc_afs = p;
David Howells10495a02018-05-18 11:46:14 +0100674 _leave(" = 0");
675 return 0;
676
677error_tree:
David Howells5b86d4f2018-05-18 11:46:15 +0100678 proc_remove(p);
David Howells10495a02018-05-18 11:46:14 +0100679error_dir:
680 _leave(" = -ENOMEM");
681 return -ENOMEM;
682}
683
684/*
685 * clean up the /proc/fs/afs/ directory
686 */
687void afs_proc_cleanup(struct afs_net *net)
688{
689 proc_remove(net->proc_afs);
690 net->proc_afs = NULL;
691}