blob: b20098b7b7ee5fc9a3ba6216b325880afb6f51dc [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 Howellsf044c882017-11-02 15:27:45 +000020static inline struct afs_net *afs_proc2net(struct file *f)
21{
22 return &__afs_net;
23}
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
David Howellsf044c882017-11-02 15:27:45 +000025static inline struct afs_net *afs_seq2net(struct seq_file *m)
26{
27 return &__afs_net; // TODO: use seq_file_net(m)
28}
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Linus Torvalds1da177e2005-04-16 15:20:36 -070030static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
31static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
32static void afs_proc_cells_stop(struct seq_file *p, void *v);
33static int afs_proc_cells_show(struct seq_file *m, void *v);
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
James Morris88e9d342009-09-22 16:43:43 -070035static const struct seq_operations afs_proc_cells_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 .start = afs_proc_cells_start,
37 .next = afs_proc_cells_next,
38 .stop = afs_proc_cells_stop,
39 .show = afs_proc_cells_show,
40};
41
Linus Torvalds1da177e2005-04-16 15:20:36 -070042static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
43static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
44 loff_t *pos);
45static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
46static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
47
James Morris88e9d342009-09-22 16:43:43 -070048static const struct seq_operations afs_proc_cell_volumes_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 .start = afs_proc_cell_volumes_start,
50 .next = afs_proc_cell_volumes_next,
51 .stop = afs_proc_cell_volumes_stop,
52 .show = afs_proc_cell_volumes_show,
53};
54
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
56static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
57 loff_t *pos);
58static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
59static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
60
James Morris88e9d342009-09-22 16:43:43 -070061static const struct seq_operations afs_proc_cell_vlservers_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 .start = afs_proc_cell_vlservers_start,
63 .next = afs_proc_cell_vlservers_next,
64 .stop = afs_proc_cell_vlservers_stop,
65 .show = afs_proc_cell_vlservers_show,
66};
67
David Howellsd2ddc772017-11-02 15:27:50 +000068static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos);
69static void *afs_proc_servers_next(struct seq_file *p, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 loff_t *pos);
David Howellsd2ddc772017-11-02 15:27:50 +000071static void afs_proc_servers_stop(struct seq_file *p, void *v);
72static int afs_proc_servers_show(struct seq_file *m, void *v);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
David Howellsd2ddc772017-11-02 15:27:50 +000074static const struct seq_operations afs_proc_servers_ops = {
75 .start = afs_proc_servers_start,
76 .next = afs_proc_servers_next,
77 .stop = afs_proc_servers_stop,
78 .show = afs_proc_servers_show,
Linus Torvalds1da177e2005-04-16 15:20:36 -070079};
80
David Howells6f8880d2018-04-09 21:12:31 +010081static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
82static void *afs_proc_sysname_next(struct seq_file *p, void *v,
83 loff_t *pos);
84static void afs_proc_sysname_stop(struct seq_file *p, void *v);
85static int afs_proc_sysname_show(struct seq_file *m, void *v);
David Howells6f8880d2018-04-09 21:12:31 +010086
87static const struct seq_operations afs_proc_sysname_ops = {
88 .start = afs_proc_sysname_start,
89 .next = afs_proc_sysname_next,
90 .stop = afs_proc_sysname_stop,
91 .show = afs_proc_sysname_show,
92};
93
Linus Torvalds1da177e2005-04-16 15:20:36 -070094/*
David Howellsf0691682018-05-18 11:46:14 +010095 * display a header line followed by a load of cell lines
96 */
97static int afs_proc_cells_show(struct seq_file *m, void *v)
98{
99 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
100 struct afs_net *net = afs_seq2net(m);
101
102 if (v == &net->proc_cells) {
103 /* display header on line 1 */
104 seq_puts(m, "USE NAME\n");
105 return 0;
106 }
107
108 /* display one cell per line on subsequent lines */
109 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
110 return 0;
111}
112
113/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 * set up the iterator to start reading from the cells list and return the
115 * first item
116 */
117static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100118 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119{
David Howellsf044c882017-11-02 15:27:45 +0000120 struct afs_net *net = afs_seq2net(m);
121
David Howells989782d2017-11-02 15:27:50 +0000122 rcu_read_lock();
David Howellsf044c882017-11-02 15:27:45 +0000123 return seq_list_start_head(&net->proc_cells, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700124}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126/*
127 * move to next cell in cells list
128 */
David Howellsf044c882017-11-02 15:27:45 +0000129static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
David Howellsf044c882017-11-02 15:27:45 +0000131 struct afs_net *net = afs_seq2net(m);
132
133 return seq_list_next(v, &net->proc_cells, pos);
David Howellsec268152007-04-26 15:49:28 -0700134}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136/*
137 * clean up after reading from the cells list
138 */
David Howellsf044c882017-11-02 15:27:45 +0000139static void afs_proc_cells_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100140 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
David Howells989782d2017-11-02 15:27:50 +0000142 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700143}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 * handle writes to /proc/fs/afs/cells
147 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
148 */
149static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
150 size_t size, loff_t *_pos)
151{
David Howellsf044c882017-11-02 15:27:45 +0000152 struct afs_net *net = afs_proc2net(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 char *kbuf, *name, *args;
154 int ret;
155
156 /* start by dragging the command into memory */
157 if (size <= 1 || size >= PAGE_SIZE)
158 return -EINVAL;
159
Al Viro16e5c1f2015-12-24 00:06:05 -0500160 kbuf = memdup_user_nul(buf, size);
161 if (IS_ERR(kbuf))
162 return PTR_ERR(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164 /* trim to first NL */
165 name = memchr(kbuf, '\n', size);
166 if (name)
167 *name = 0;
168
169 /* split into command, name and argslist */
170 name = strchr(kbuf, ' ');
171 if (!name)
172 goto inval;
173 do {
174 *name++ = 0;
175 } while(*name == ' ');
176 if (!*name)
177 goto inval;
178
179 args = strchr(name, ' ');
180 if (!args)
181 goto inval;
182 do {
183 *args++ = 0;
184 } while(*args == ' ');
185 if (!*args)
186 goto inval;
187
188 /* determine command to perform */
189 _debug("cmd=%s name=%s args=%s", kbuf, name, args);
190
191 if (strcmp(kbuf, "add") == 0) {
192 struct afs_cell *cell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193
David Howells989782d2017-11-02 15:27:50 +0000194 cell = afs_lookup_cell(net, name, strlen(name), args, true);
David Howells08e0e7c2007-04-26 15:55:03 -0700195 if (IS_ERR(cell)) {
196 ret = PTR_ERR(cell);
197 goto done;
198 }
199
David Howells17814ae2018-04-09 21:12:31 +0100200 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
201 afs_put_cell(net, cell);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 printk("kAFS: Added new cell '%s'\n", name);
David Howellsec268152007-04-26 15:49:28 -0700203 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 goto inval;
205 }
206
207 ret = size;
208
David Howellsec268152007-04-26 15:49:28 -0700209done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 kfree(kbuf);
211 _leave(" = %d", ret);
212 return ret;
213
David Howellsec268152007-04-26 15:49:28 -0700214inval:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 ret = -EINVAL;
216 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
217 goto done;
David Howellsec268152007-04-26 15:49:28 -0700218}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
David Howells22ade7e2018-05-18 11:46:14 +0100220static int afs_proc_cells_open(struct inode *inode, struct file *file)
221{
222 return seq_open(file, &afs_proc_cells_ops);
223}
224
225static const struct file_operations afs_proc_cells_fops = {
226 .open = afs_proc_cells_open,
227 .read = seq_read,
228 .write = afs_proc_cells_write,
229 .llseek = seq_lseek,
230 .release = seq_release,
231};
232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
234 size_t size, loff_t *_pos)
235{
David Howells37ab6362018-04-06 14:17:23 +0100236 struct afs_cell *cell;
237 struct afs_net *net = afs_proc2net(file);
238 unsigned int seq = 0;
239 char name[AFS_MAXCELLNAME + 1];
240 int len;
241
242 if (*_pos > 0)
243 return 0;
244 if (!net->ws_cell)
245 return 0;
246
247 rcu_read_lock();
248 do {
249 read_seqbegin_or_lock(&net->cells_lock, &seq);
250 len = 0;
251 cell = rcu_dereference_raw(net->ws_cell);
252 if (cell) {
253 len = cell->name_len;
254 memcpy(name, cell->name, len);
255 }
256 } while (need_seqretry(&net->cells_lock, seq));
257 done_seqretry(&net->cells_lock, seq);
258 rcu_read_unlock();
259
260 if (!len)
261 return 0;
262
263 name[len++] = '\n';
264 if (len > size)
265 len = size;
266 if (copy_to_user(buf, name, len) != 0)
267 return -EFAULT;
268 *_pos = 1;
269 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272/*
273 * handle writes to /proc/fs/afs/rootcell
274 * - to initialize rootcell: echo "cell.name:192.168.231.14"
275 */
276static ssize_t afs_proc_rootcell_write(struct file *file,
277 const char __user *buf,
278 size_t size, loff_t *_pos)
279{
David Howellsf044c882017-11-02 15:27:45 +0000280 struct afs_net *net = afs_proc2net(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 char *kbuf, *s;
282 int ret;
283
284 /* start by dragging the command into memory */
285 if (size <= 1 || size >= PAGE_SIZE)
286 return -EINVAL;
287
Al Viro16e5c1f2015-12-24 00:06:05 -0500288 kbuf = memdup_user_nul(buf, size);
289 if (IS_ERR(kbuf))
290 return PTR_ERR(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
David Howells6f8880d2018-04-09 21:12:31 +0100292 ret = -EINVAL;
293 if (kbuf[0] == '.')
294 goto out;
295 if (memchr(kbuf, '/', size))
296 goto out;
297
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 /* trim to first NL */
299 s = memchr(kbuf, '\n', size);
300 if (s)
301 *s = 0;
302
303 /* determine command to perform */
304 _debug("rootcell=%s", kbuf);
305
David Howellsf044c882017-11-02 15:27:45 +0000306 ret = afs_cell_init(net, kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 if (ret >= 0)
308 ret = size; /* consume everything, always */
309
David Howells6f8880d2018-04-09 21:12:31 +0100310out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 kfree(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 _leave(" = %d", ret);
313 return ret;
David Howellsec268152007-04-26 15:49:28 -0700314}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
David Howells22ade7e2018-05-18 11:46:14 +0100316static const struct file_operations afs_proc_rootcell_fops = {
317 .read = afs_proc_rootcell_read,
318 .write = afs_proc_rootcell_write,
319 .llseek = no_llseek,
320};
321
David Howellsf0691682018-05-18 11:46:14 +0100322static const char afs_vol_types[3][3] = {
323 [AFSVL_RWVOL] = "RW",
324 [AFSVL_ROVOL] = "RO",
325 [AFSVL_BACKVOL] = "BK",
326};
327
328/*
329 * display a header line followed by a load of volume lines
330 */
331static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
332{
333 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
334 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
335
336 /* Display header on line 1 */
337 if (v == &cell->proc_volumes) {
338 seq_puts(m, "USE VID TY\n");
339 return 0;
340 }
341
342 seq_printf(m, "%3d %08x %s\n",
343 atomic_read(&vol->usage), vol->vid,
344 afs_vol_types[vol->type]);
345
346 return 0;
347}
348
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 * set up the iterator to start reading from the cells list and return the
351 * first item
352 */
353static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100354 __acquires(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200356 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 _enter("cell=%p pos=%Ld", cell, *_pos);
359
David Howellsd2ddc772017-11-02 15:27:50 +0000360 read_lock(&cell->proc_lock);
361 return seq_list_start_head(&cell->proc_volumes, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700362}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364/*
365 * move to next cell in cells list
366 */
367static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
368 loff_t *_pos)
369{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200370 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 _enter("cell=%p pos=%Ld", cell, *_pos);
David Howellsd2ddc772017-11-02 15:27:50 +0000373 return seq_list_next(v, &cell->proc_volumes, _pos);
David Howellsec268152007-04-26 15:49:28 -0700374}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376/*
377 * clean up after reading from the cells list
378 */
379static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100380 __releases(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200382 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
David Howellsd2ddc772017-11-02 15:27:50 +0000384 read_unlock(&cell->proc_lock);
David Howellsec268152007-04-26 15:49:28 -0700385}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387/*
388 * display a header line followed by a load of volume lines
389 */
David Howellsf0691682018-05-18 11:46:14 +0100390static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391{
David Howellsf0691682018-05-18 11:46:14 +0100392 struct sockaddr_rxrpc *addr = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
David Howellsf0691682018-05-18 11:46:14 +0100394 /* display header on line 1 */
395 if (v == (void *)1) {
396 seq_puts(m, "ADDRESS\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 return 0;
398 }
399
David Howellsf0691682018-05-18 11:46:14 +0100400 /* display one cell per line on subsequent lines */
401 seq_printf(m, "%pISp\n", &addr->transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 return 0;
David Howellsec268152007-04-26 15:49:28 -0700403}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 * set up the iterator to start reading from the cells list and return the
407 * first item
408 */
409static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100410 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
David Howells8b2a4642017-11-02 15:27:50 +0000412 struct afs_addr_list *alist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200413 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 loff_t pos = *_pos;
415
David Howells8b2a4642017-11-02 15:27:50 +0000416 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
David Howells8b2a4642017-11-02 15:27:50 +0000418 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 /* allow for the header line */
421 if (!pos)
422 return (void *) 1;
423 pos--;
424
David Howells8b2a4642017-11-02 15:27:50 +0000425 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 return NULL;
427
David Howells8b2a4642017-11-02 15:27:50 +0000428 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700429}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431/*
432 * move to next cell in cells list
433 */
434static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
435 loff_t *_pos)
436{
David Howells8b2a4642017-11-02 15:27:50 +0000437 struct afs_addr_list *alist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200438 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 loff_t pos;
440
David Howells8b2a4642017-11-02 15:27:50 +0000441 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 pos = *_pos;
444 (*_pos)++;
David Howells8b2a4642017-11-02 15:27:50 +0000445 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 return NULL;
447
David Howells8b2a4642017-11-02 15:27:50 +0000448 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700449}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451/*
452 * clean up after reading from the cells list
453 */
454static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100455 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
David Howells8b2a4642017-11-02 15:27:50 +0000457 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700458}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460/*
461 * display a header line followed by a load of volume lines
462 */
David Howellsf0691682018-05-18 11:46:14 +0100463static int afs_proc_servers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
David Howellsf0691682018-05-18 11:46:14 +0100465 struct afs_server *server;
466 struct afs_addr_list *alist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
David Howellsf0691682018-05-18 11:46:14 +0100468 if (v == SEQ_START_TOKEN) {
469 seq_puts(m, "UUID USE ADDR\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 return 0;
471 }
472
David Howellsf0691682018-05-18 11:46:14 +0100473 server = list_entry(v, struct afs_server, proc_link);
474 alist = rcu_dereference(server->addresses);
475 seq_printf(m, "%pU %3d %pISp\n",
476 &server->uuid,
477 atomic_read(&server->usage),
478 &alist->addrs[alist->index].transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 return 0;
David Howellsec268152007-04-26 15:49:28 -0700480}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482/*
David Howellsd2ddc772017-11-02 15:27:50 +0000483 * Set up the iterator to start reading from the server list and return the
484 * first item.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 */
David Howellsd2ddc772017-11-02 15:27:50 +0000486static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100487 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
David Howellsd2ddc772017-11-02 15:27:50 +0000489 struct afs_net *net = afs_seq2net(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
David Howellsd2ddc772017-11-02 15:27:50 +0000491 rcu_read_lock();
492 return seq_hlist_start_head_rcu(&net->fs_proc, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700493}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495/*
496 * move to next cell in cells list
497 */
David Howellsd2ddc772017-11-02 15:27:50 +0000498static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
David Howellsd2ddc772017-11-02 15:27:50 +0000500 struct afs_net *net = afs_seq2net(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
David Howellsd2ddc772017-11-02 15:27:50 +0000502 return seq_hlist_next_rcu(v, &net->fs_proc, _pos);
David Howellsec268152007-04-26 15:49:28 -0700503}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505/*
506 * clean up after reading from the cells list
507 */
David Howellsd2ddc772017-11-02 15:27:50 +0000508static void afs_proc_servers_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100509 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
David Howellsd2ddc772017-11-02 15:27:50 +0000511 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700512}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
David Howells6f8880d2018-04-09 21:12:31 +0100514void afs_put_sysnames(struct afs_sysnames *sysnames)
515{
516 int i;
517
518 if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
519 for (i = 0; i < sysnames->nr; i++)
520 if (sysnames->subs[i] != afs_init_sysname &&
521 sysnames->subs[i] != sysnames->blank)
522 kfree(sysnames->subs[i]);
523 }
524}
525
526/*
527 * Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we
528 * assume the caller wants to change the substitution list and we allocate a
529 * buffer to hold the list.
530 */
531static int afs_proc_sysname_open(struct inode *inode, struct file *file)
532{
533 struct afs_sysnames *sysnames;
534 struct seq_file *m;
535 int ret;
536
537 ret = seq_open(file, &afs_proc_sysname_ops);
538 if (ret < 0)
539 return ret;
540
541 if (file->f_mode & FMODE_WRITE) {
542 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
543 if (!sysnames) {
544 seq_release(inode, file);
545 return -ENOMEM;
546 }
547
548 refcount_set(&sysnames->usage, 1);
549 m = file->private_data;
550 m->private = sysnames;
551 }
552
553 return 0;
554}
555
556/*
557 * Handle writes to /proc/fs/afs/sysname to set the @sys substitution.
558 */
559static ssize_t afs_proc_sysname_write(struct file *file,
560 const char __user *buf,
561 size_t size, loff_t *_pos)
562{
563 struct afs_sysnames *sysnames;
564 struct seq_file *m = file->private_data;
565 char *kbuf = NULL, *s, *p, *sub;
566 int ret, len;
567
568 sysnames = m->private;
569 if (!sysnames)
570 return -EINVAL;
571 if (sysnames->error)
572 return sysnames->error;
573
574 if (size >= PAGE_SIZE - 1) {
575 sysnames->error = -EINVAL;
576 return -EINVAL;
577 }
578 if (size == 0)
579 return 0;
580
581 kbuf = memdup_user_nul(buf, size);
582 if (IS_ERR(kbuf))
583 return PTR_ERR(kbuf);
584
585 inode_lock(file_inode(file));
586
587 p = kbuf;
588 while ((s = strsep(&p, " \t\n"))) {
589 len = strlen(s);
590 if (len == 0)
591 continue;
592 ret = -ENAMETOOLONG;
593 if (len >= AFSNAMEMAX)
594 goto error;
595
596 if (len >= 4 &&
597 s[len - 4] == '@' &&
598 s[len - 3] == 's' &&
599 s[len - 2] == 'y' &&
600 s[len - 1] == 's')
601 /* Protect against recursion */
602 goto invalid;
603
604 if (s[0] == '.' &&
605 (len < 2 || (len == 2 && s[1] == '.')))
606 goto invalid;
607
608 if (memchr(s, '/', len))
609 goto invalid;
610
611 ret = -EFBIG;
612 if (sysnames->nr >= AFS_NR_SYSNAME)
613 goto out;
614
615 if (strcmp(s, afs_init_sysname) == 0) {
616 sub = (char *)afs_init_sysname;
617 } else {
618 ret = -ENOMEM;
619 sub = kmemdup(s, len + 1, GFP_KERNEL);
620 if (!sub)
621 goto out;
622 }
623
624 sysnames->subs[sysnames->nr] = sub;
625 sysnames->nr++;
626 }
627
628 ret = size; /* consume everything, always */
629out:
630 inode_unlock(file_inode(file));
631 kfree(kbuf);
632 return ret;
633
634invalid:
635 ret = -EINVAL;
636error:
637 sysnames->error = ret;
638 goto out;
639}
640
641static int afs_proc_sysname_release(struct inode *inode, struct file *file)
642{
643 struct afs_sysnames *sysnames, *kill = NULL;
644 struct seq_file *m = file->private_data;
645 struct afs_net *net = afs_seq2net(m);
646
647 sysnames = m->private;
648 if (sysnames) {
649 if (!sysnames->error) {
650 kill = sysnames;
651 if (sysnames->nr == 0) {
652 sysnames->subs[0] = sysnames->blank;
653 sysnames->nr++;
654 }
655 write_lock(&net->sysnames_lock);
656 kill = net->sysnames;
657 net->sysnames = sysnames;
658 write_unlock(&net->sysnames_lock);
659 }
660 afs_put_sysnames(kill);
661 }
662
663 return seq_release(inode, file);
664}
665
David Howellsf0691682018-05-18 11:46:14 +0100666static int afs_proc_sysname_show(struct seq_file *m, void *v)
667{
668 struct afs_net *net = afs_seq2net(m);
669 struct afs_sysnames *sysnames = net->sysnames;
670 unsigned int i = (unsigned long)v - 1;
671
672 if (i < sysnames->nr)
673 seq_printf(m, "%s\n", sysnames->subs[i]);
674 return 0;
675}
676
David Howells6f8880d2018-04-09 21:12:31 +0100677static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
678 __acquires(&net->sysnames_lock)
679{
680 struct afs_net *net = afs_seq2net(m);
681 struct afs_sysnames *names = net->sysnames;
682
683 read_lock(&net->sysnames_lock);
684
685 if (*pos >= names->nr)
686 return NULL;
687 return (void *)(unsigned long)(*pos + 1);
688}
689
690static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
691{
692 struct afs_net *net = afs_seq2net(m);
693 struct afs_sysnames *names = net->sysnames;
694
695 *pos += 1;
696 if (*pos >= names->nr)
697 return NULL;
698 return (void *)(unsigned long)(*pos + 1);
699}
700
701static void afs_proc_sysname_stop(struct seq_file *m, void *v)
702 __releases(&net->sysnames_lock)
703{
704 struct afs_net *net = afs_seq2net(m);
705
706 read_unlock(&net->sysnames_lock);
707}
708
David Howells22ade7e2018-05-18 11:46:14 +0100709static const struct file_operations afs_proc_sysname_fops = {
710 .open = afs_proc_sysname_open,
711 .read = seq_read,
712 .llseek = seq_lseek,
713 .release = afs_proc_sysname_release,
714 .write = afs_proc_sysname_write,
715};
716
David Howellsd55b4da2018-04-06 14:17:24 +0100717/*
718 * Display general per-net namespace statistics
719 */
720static int afs_proc_stats_show(struct seq_file *m, void *v)
721{
722 struct afs_net *net = afs_seq2net(m);
723
724 seq_puts(m, "kAFS statistics\n");
725
David Howellsf3ddee82018-04-06 14:17:25 +0100726 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
David Howellsd55b4da2018-04-06 14:17:24 +0100727 atomic_read(&net->n_lookup),
728 atomic_read(&net->n_reval),
David Howellsf3ddee82018-04-06 14:17:25 +0100729 atomic_read(&net->n_inval),
730 atomic_read(&net->n_relpg));
David Howellsd55b4da2018-04-06 14:17:24 +0100731
732 seq_printf(m, "dir-data: rdpg=%u\n",
733 atomic_read(&net->n_read_dir));
David Howells63a46812018-04-06 14:17:25 +0100734
735 seq_printf(m, "dir-edit: cr=%u rm=%u\n",
736 atomic_read(&net->n_dir_cr),
737 atomic_read(&net->n_dir_rm));
David Howells76a5cb62018-04-06 14:17:26 +0100738
739 seq_printf(m, "file-rd : n=%u nb=%lu\n",
740 atomic_read(&net->n_fetches),
741 atomic_long_read(&net->n_fetch_bytes));
742 seq_printf(m, "file-wr : n=%u nb=%lu\n",
743 atomic_read(&net->n_stores),
744 atomic_long_read(&net->n_store_bytes));
David Howellsd55b4da2018-04-06 14:17:24 +0100745 return 0;
746}
David Howells10495a02018-05-18 11:46:14 +0100747
748/*
749 * initialise /proc/fs/afs/<cell>/
750 */
751int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
752{
753 struct proc_dir_entry *dir;
754
755 _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
756
757 dir = proc_mkdir(cell->name, net->proc_afs);
758 if (!dir)
759 goto error_dir;
760
761 if (!proc_create_seq_data("vlservers", 0, dir,
762 &afs_proc_cell_vlservers_ops, cell))
763 goto error_tree;
764 if (!proc_create_seq_data("volumes", 0, dir,
765 &afs_proc_cell_volumes_ops, cell))
766 goto error_tree;
767
768 _leave(" = 0");
769 return 0;
770
771error_tree:
772 remove_proc_subtree(cell->name, net->proc_afs);
773error_dir:
774 _leave(" = -ENOMEM");
775 return -ENOMEM;
776}
777
778/*
779 * remove /proc/fs/afs/<cell>/
780 */
781void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell)
782{
783 _enter("");
784
785 remove_proc_subtree(cell->name, net->proc_afs);
786
787 _leave("");
788}
789
790/*
791 * initialise the /proc/fs/afs/ directory
792 */
793int afs_proc_init(struct afs_net *net)
794{
795 _enter("");
796
797 net->proc_afs = proc_mkdir("fs/afs", NULL);
798 if (!net->proc_afs)
799 goto error_dir;
800
801 if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
802 !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
803 !proc_create_seq("servers", 0644, net->proc_afs, &afs_proc_servers_ops) ||
804 !proc_create_single("stats", 0644, net->proc_afs, afs_proc_stats_show) ||
805 !proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops))
806 goto error_tree;
807
808 _leave(" = 0");
809 return 0;
810
811error_tree:
812 proc_remove(net->proc_afs);
813error_dir:
814 _leave(" = -ENOMEM");
815 return -ENOMEM;
816}
817
818/*
819 * clean up the /proc/fs/afs/ directory
820 */
821void afs_proc_cleanup(struct afs_net *net)
822{
823 proc_remove(net->proc_afs);
824 net->proc_afs = NULL;
825}