blob: 3aad32762989434798e02e6cf365a8211b8348c9 [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
30static int afs_proc_cells_open(struct inode *inode, struct file *file);
31static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
32static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
33static void afs_proc_cells_stop(struct seq_file *p, void *v);
34static int afs_proc_cells_show(struct seq_file *m, void *v);
35static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
36 size_t size, loff_t *_pos);
37
James Morris88e9d342009-09-22 16:43:43 -070038static const struct seq_operations afs_proc_cells_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 .start = afs_proc_cells_start,
40 .next = afs_proc_cells_next,
41 .stop = afs_proc_cells_stop,
42 .show = afs_proc_cells_show,
43};
44
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -080045static const struct file_operations afs_proc_cells_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 .open = afs_proc_cells_open,
47 .read = seq_read,
48 .write = afs_proc_cells_write,
49 .llseek = seq_lseek,
50 .release = seq_release,
51};
52
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
54 size_t size, loff_t *_pos);
55static ssize_t afs_proc_rootcell_write(struct file *file,
56 const char __user *buf,
57 size_t size, loff_t *_pos);
58
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -080059static const struct file_operations afs_proc_rootcell_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 .read = afs_proc_rootcell_read,
61 .write = afs_proc_rootcell_write,
62 .llseek = no_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -070063};
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
66static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
67 loff_t *pos);
68static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
69static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
70
James Morris88e9d342009-09-22 16:43:43 -070071static const struct seq_operations afs_proc_cell_volumes_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 .start = afs_proc_cell_volumes_start,
73 .next = afs_proc_cell_volumes_next,
74 .stop = afs_proc_cell_volumes_stop,
75 .show = afs_proc_cell_volumes_show,
76};
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
79static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
80 loff_t *pos);
81static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
82static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
83
James Morris88e9d342009-09-22 16:43:43 -070084static const struct seq_operations afs_proc_cell_vlservers_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 .start = afs_proc_cell_vlservers_start,
86 .next = afs_proc_cell_vlservers_next,
87 .stop = afs_proc_cell_vlservers_stop,
88 .show = afs_proc_cell_vlservers_show,
89};
90
David Howellsd2ddc772017-11-02 15:27:50 +000091static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos);
92static void *afs_proc_servers_next(struct seq_file *p, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 loff_t *pos);
David Howellsd2ddc772017-11-02 15:27:50 +000094static void afs_proc_servers_stop(struct seq_file *p, void *v);
95static int afs_proc_servers_show(struct seq_file *m, void *v);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
David Howellsd2ddc772017-11-02 15:27:50 +000097static const struct seq_operations afs_proc_servers_ops = {
98 .start = afs_proc_servers_start,
99 .next = afs_proc_servers_next,
100 .stop = afs_proc_servers_stop,
101 .show = afs_proc_servers_show,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102};
103
David Howells6f8880d2018-04-09 21:12:31 +0100104static int afs_proc_sysname_open(struct inode *inode, struct file *file);
105static int afs_proc_sysname_release(struct inode *inode, struct file *file);
106static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
107static void *afs_proc_sysname_next(struct seq_file *p, void *v,
108 loff_t *pos);
109static void afs_proc_sysname_stop(struct seq_file *p, void *v);
110static int afs_proc_sysname_show(struct seq_file *m, void *v);
111static ssize_t afs_proc_sysname_write(struct file *file,
112 const char __user *buf,
113 size_t size, loff_t *_pos);
114
115static const struct seq_operations afs_proc_sysname_ops = {
116 .start = afs_proc_sysname_start,
117 .next = afs_proc_sysname_next,
118 .stop = afs_proc_sysname_stop,
119 .show = afs_proc_sysname_show,
120};
121
122static const struct file_operations afs_proc_sysname_fops = {
123 .open = afs_proc_sysname_open,
124 .read = seq_read,
125 .llseek = seq_lseek,
126 .release = afs_proc_sysname_release,
127 .write = afs_proc_sysname_write,
128};
129
Christoph Hellwig353861c2018-04-13 20:45:09 +0200130static int afs_proc_stats_show(struct seq_file *m, void *v);
David Howellsd55b4da2018-04-06 14:17:24 +0100131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132/*
133 * initialise the /proc/fs/afs/ directory
134 */
David Howellsf044c882017-11-02 15:27:45 +0000135int afs_proc_init(struct afs_net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 _enter("");
138
David Howellsf044c882017-11-02 15:27:45 +0000139 net->proc_afs = proc_mkdir("fs/afs", NULL);
140 if (!net->proc_afs)
David Howellsec268152007-04-26 15:49:28 -0700141 goto error_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
David Howellsf044c882017-11-02 15:27:45 +0000143 if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
David Howellsd2ddc772017-11-02 15:27:50 +0000144 !proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
Christoph Hellwig353861c2018-04-13 20:45:09 +0200145 !proc_create_seq("servers", 0644, net->proc_afs, &afs_proc_servers_ops) ||
146 !proc_create_single("stats", 0644, net->proc_afs, afs_proc_stats_show) ||
David Howells6f8880d2018-04-09 21:12:31 +0100147 !proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops))
Al Virob42d5702013-11-22 01:53:47 -0500148 goto error_tree;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 _leave(" = 0");
151 return 0;
152
Al Virob42d5702013-11-22 01:53:47 -0500153error_tree:
David Howellsf044c882017-11-02 15:27:45 +0000154 proc_remove(net->proc_afs);
David Howellsec268152007-04-26 15:49:28 -0700155error_dir:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 _leave(" = -ENOMEM");
157 return -ENOMEM;
David Howellsec268152007-04-26 15:49:28 -0700158}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160/*
161 * clean up the /proc/fs/afs/ directory
162 */
David Howellsf044c882017-11-02 15:27:45 +0000163void afs_proc_cleanup(struct afs_net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164{
David Howellsf044c882017-11-02 15:27:45 +0000165 proc_remove(net->proc_afs);
166 net->proc_afs = NULL;
David Howellsec268152007-04-26 15:49:28 -0700167}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169/*
170 * open "/proc/fs/afs/cells" which provides a summary of extant cells
171 */
172static int afs_proc_cells_open(struct inode *inode, struct file *file)
173{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200174 return seq_open(file, &afs_proc_cells_ops);
David Howellsec268152007-04-26 15:49:28 -0700175}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177/*
178 * set up the iterator to start reading from the cells list and return the
179 * first item
180 */
181static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100182 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
David Howellsf044c882017-11-02 15:27:45 +0000184 struct afs_net *net = afs_seq2net(m);
185
David Howells989782d2017-11-02 15:27:50 +0000186 rcu_read_lock();
David Howellsf044c882017-11-02 15:27:45 +0000187 return seq_list_start_head(&net->proc_cells, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700188}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190/*
191 * move to next cell in cells list
192 */
David Howellsf044c882017-11-02 15:27:45 +0000193static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194{
David Howellsf044c882017-11-02 15:27:45 +0000195 struct afs_net *net = afs_seq2net(m);
196
197 return seq_list_next(v, &net->proc_cells, pos);
David Howellsec268152007-04-26 15:49:28 -0700198}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200/*
201 * clean up after reading from the cells list
202 */
David Howellsf044c882017-11-02 15:27:45 +0000203static void afs_proc_cells_stop(struct seq_file *m, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100204 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
David Howells989782d2017-11-02 15:27:50 +0000206 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700207}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209/*
210 * display a header line followed by a load of cell lines
211 */
212static int afs_proc_cells_show(struct seq_file *m, void *v)
213{
214 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
David Howellsf044c882017-11-02 15:27:45 +0000215 struct afs_net *net = afs_seq2net(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
David Howellsf044c882017-11-02 15:27:45 +0000217 if (v == &net->proc_cells) {
David Howellsec268152007-04-26 15:49:28 -0700218 /* display header on line 1 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 seq_puts(m, "USE NAME\n");
220 return 0;
221 }
222
223 /* display one cell per line on subsequent lines */
David Howells989782d2017-11-02 15:27:50 +0000224 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 return 0;
David Howellsec268152007-04-26 15:49:28 -0700226}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228/*
229 * handle writes to /proc/fs/afs/cells
230 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
231 */
232static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
233 size_t size, loff_t *_pos)
234{
David Howellsf044c882017-11-02 15:27:45 +0000235 struct afs_net *net = afs_proc2net(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 char *kbuf, *name, *args;
237 int ret;
238
239 /* start by dragging the command into memory */
240 if (size <= 1 || size >= PAGE_SIZE)
241 return -EINVAL;
242
Al Viro16e5c1f2015-12-24 00:06:05 -0500243 kbuf = memdup_user_nul(buf, size);
244 if (IS_ERR(kbuf))
245 return PTR_ERR(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 /* trim to first NL */
248 name = memchr(kbuf, '\n', size);
249 if (name)
250 *name = 0;
251
252 /* split into command, name and argslist */
253 name = strchr(kbuf, ' ');
254 if (!name)
255 goto inval;
256 do {
257 *name++ = 0;
258 } while(*name == ' ');
259 if (!*name)
260 goto inval;
261
262 args = strchr(name, ' ');
263 if (!args)
264 goto inval;
265 do {
266 *args++ = 0;
267 } while(*args == ' ');
268 if (!*args)
269 goto inval;
270
271 /* determine command to perform */
272 _debug("cmd=%s name=%s args=%s", kbuf, name, args);
273
274 if (strcmp(kbuf, "add") == 0) {
275 struct afs_cell *cell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
David Howells989782d2017-11-02 15:27:50 +0000277 cell = afs_lookup_cell(net, name, strlen(name), args, true);
David Howells08e0e7c2007-04-26 15:55:03 -0700278 if (IS_ERR(cell)) {
279 ret = PTR_ERR(cell);
280 goto done;
281 }
282
David Howells17814ae2018-04-09 21:12:31 +0100283 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
284 afs_put_cell(net, cell);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 printk("kAFS: Added new cell '%s'\n", name);
David Howellsec268152007-04-26 15:49:28 -0700286 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 goto inval;
288 }
289
290 ret = size;
291
David Howellsec268152007-04-26 15:49:28 -0700292done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 kfree(kbuf);
294 _leave(" = %d", ret);
295 return ret;
296
David Howellsec268152007-04-26 15:49:28 -0700297inval:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 ret = -EINVAL;
299 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
300 goto done;
David Howellsec268152007-04-26 15:49:28 -0700301}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
304 size_t size, loff_t *_pos)
305{
David Howells37ab6362018-04-06 14:17:23 +0100306 struct afs_cell *cell;
307 struct afs_net *net = afs_proc2net(file);
308 unsigned int seq = 0;
309 char name[AFS_MAXCELLNAME + 1];
310 int len;
311
312 if (*_pos > 0)
313 return 0;
314 if (!net->ws_cell)
315 return 0;
316
317 rcu_read_lock();
318 do {
319 read_seqbegin_or_lock(&net->cells_lock, &seq);
320 len = 0;
321 cell = rcu_dereference_raw(net->ws_cell);
322 if (cell) {
323 len = cell->name_len;
324 memcpy(name, cell->name, len);
325 }
326 } while (need_seqretry(&net->cells_lock, seq));
327 done_seqretry(&net->cells_lock, seq);
328 rcu_read_unlock();
329
330 if (!len)
331 return 0;
332
333 name[len++] = '\n';
334 if (len > size)
335 len = size;
336 if (copy_to_user(buf, name, len) != 0)
337 return -EFAULT;
338 *_pos = 1;
339 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340}
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342/*
343 * handle writes to /proc/fs/afs/rootcell
344 * - to initialize rootcell: echo "cell.name:192.168.231.14"
345 */
346static ssize_t afs_proc_rootcell_write(struct file *file,
347 const char __user *buf,
348 size_t size, loff_t *_pos)
349{
David Howellsf044c882017-11-02 15:27:45 +0000350 struct afs_net *net = afs_proc2net(file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 char *kbuf, *s;
352 int ret;
353
354 /* start by dragging the command into memory */
355 if (size <= 1 || size >= PAGE_SIZE)
356 return -EINVAL;
357
Al Viro16e5c1f2015-12-24 00:06:05 -0500358 kbuf = memdup_user_nul(buf, size);
359 if (IS_ERR(kbuf))
360 return PTR_ERR(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
David Howells6f8880d2018-04-09 21:12:31 +0100362 ret = -EINVAL;
363 if (kbuf[0] == '.')
364 goto out;
365 if (memchr(kbuf, '/', size))
366 goto out;
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 /* trim to first NL */
369 s = memchr(kbuf, '\n', size);
370 if (s)
371 *s = 0;
372
373 /* determine command to perform */
374 _debug("rootcell=%s", kbuf);
375
David Howellsf044c882017-11-02 15:27:45 +0000376 ret = afs_cell_init(net, kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (ret >= 0)
378 ret = size; /* consume everything, always */
379
David Howells6f8880d2018-04-09 21:12:31 +0100380out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 kfree(kbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 _leave(" = %d", ret);
383 return ret;
David Howellsec268152007-04-26 15:49:28 -0700384}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386/*
387 * initialise /proc/fs/afs/<cell>/
388 */
David Howellsf044c882017-11-02 15:27:45 +0000389int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
Al Virob42d5702013-11-22 01:53:47 -0500391 struct proc_dir_entry *dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
David Howells989782d2017-11-02 15:27:50 +0000393 _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
David Howellsf044c882017-11-02 15:27:45 +0000395 dir = proc_mkdir(cell->name, net->proc_afs);
Al Virob42d5702013-11-22 01:53:47 -0500396 if (!dir)
David Howellsec268152007-04-26 15:49:28 -0700397 goto error_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
Christoph Hellwig353861c2018-04-13 20:45:09 +0200399 if (!proc_create_seq_data("vlservers", 0, dir,
400 &afs_proc_cell_vlservers_ops, cell))
401 goto error_tree;
402 if (!proc_create_seq_data("volumes", 0, dir, &afs_proc_cell_volumes_ops,
403 cell))
Al Virob42d5702013-11-22 01:53:47 -0500404 goto error_tree;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406 _leave(" = 0");
407 return 0;
408
Al Virob42d5702013-11-22 01:53:47 -0500409error_tree:
David Howellsf044c882017-11-02 15:27:45 +0000410 remove_proc_subtree(cell->name, net->proc_afs);
David Howellsec268152007-04-26 15:49:28 -0700411error_dir:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 _leave(" = -ENOMEM");
413 return -ENOMEM;
David Howellsec268152007-04-26 15:49:28 -0700414}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416/*
417 * remove /proc/fs/afs/<cell>/
418 */
David Howellsf044c882017-11-02 15:27:45 +0000419void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 _enter("");
422
David Howellsf044c882017-11-02 15:27:45 +0000423 remove_proc_subtree(cell->name, net->proc_afs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 _leave("");
David Howellsec268152007-04-26 15:49:28 -0700426}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 * set up the iterator to start reading from the cells list and return the
430 * first item
431 */
432static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100433 __acquires(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200435 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 _enter("cell=%p pos=%Ld", cell, *_pos);
438
David Howellsd2ddc772017-11-02 15:27:50 +0000439 read_lock(&cell->proc_lock);
440 return seq_list_start_head(&cell->proc_volumes, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700441}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443/*
444 * move to next cell in cells list
445 */
446static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
447 loff_t *_pos)
448{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200449 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
451 _enter("cell=%p pos=%Ld", cell, *_pos);
David Howellsd2ddc772017-11-02 15:27:50 +0000452 return seq_list_next(v, &cell->proc_volumes, _pos);
David Howellsec268152007-04-26 15:49:28 -0700453}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455/*
456 * clean up after reading from the cells list
457 */
458static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100459 __releases(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200461 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462
David Howellsd2ddc772017-11-02 15:27:50 +0000463 read_unlock(&cell->proc_lock);
David Howellsec268152007-04-26 15:49:28 -0700464}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
David Howellsd2ddc772017-11-02 15:27:50 +0000466static const char afs_vol_types[3][3] = {
467 [AFSVL_RWVOL] = "RW",
468 [AFSVL_ROVOL] = "RO",
469 [AFSVL_BACKVOL] = "BK",
David Howells08e0e7c2007-04-26 15:55:03 -0700470};
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472/*
473 * display a header line followed by a load of volume lines
474 */
475static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
476{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200477 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
David Howellsd2ddc772017-11-02 15:27:50 +0000478 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
David Howellsd2ddc772017-11-02 15:27:50 +0000480 /* Display header on line 1 */
481 if (v == &cell->proc_volumes) {
482 seq_puts(m, "USE VID TY\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 return 0;
484 }
485
David Howellsd2ddc772017-11-02 15:27:50 +0000486 seq_printf(m, "%3d %08x %s\n",
487 atomic_read(&vol->usage), vol->vid,
488 afs_vol_types[vol->type]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
490 return 0;
David Howellsec268152007-04-26 15:49:28 -0700491}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 * set up the iterator to start reading from the cells list and return the
495 * first item
496 */
497static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100498 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499{
David Howells8b2a4642017-11-02 15:27:50 +0000500 struct afs_addr_list *alist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200501 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 loff_t pos = *_pos;
503
David Howells8b2a4642017-11-02 15:27:50 +0000504 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
David Howells8b2a4642017-11-02 15:27:50 +0000506 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 /* allow for the header line */
509 if (!pos)
510 return (void *) 1;
511 pos--;
512
David Howells8b2a4642017-11-02 15:27:50 +0000513 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return NULL;
515
David Howells8b2a4642017-11-02 15:27:50 +0000516 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700517}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519/*
520 * move to next cell in cells list
521 */
522static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
523 loff_t *_pos)
524{
David Howells8b2a4642017-11-02 15:27:50 +0000525 struct afs_addr_list *alist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200526 struct afs_cell *cell = PDE_DATA(file_inode(p->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 loff_t pos;
528
David Howells8b2a4642017-11-02 15:27:50 +0000529 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
531 pos = *_pos;
532 (*_pos)++;
David Howells8b2a4642017-11-02 15:27:50 +0000533 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 return NULL;
535
David Howells8b2a4642017-11-02 15:27:50 +0000536 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700537}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539/*
540 * clean up after reading from the cells list
541 */
542static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100543 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
David Howells8b2a4642017-11-02 15:27:50 +0000545 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700546}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548/*
549 * display a header line followed by a load of volume lines
550 */
551static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
552{
David Howells4d9df982017-11-02 15:27:47 +0000553 struct sockaddr_rxrpc *addr = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555 /* display header on line 1 */
David Howells4d9df982017-11-02 15:27:47 +0000556 if (v == (void *)1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 seq_puts(m, "ADDRESS\n");
558 return 0;
559 }
560
561 /* display one cell per line on subsequent lines */
David Howells4d9df982017-11-02 15:27:47 +0000562 seq_printf(m, "%pISp\n", &addr->transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 return 0;
David Howellsec268152007-04-26 15:49:28 -0700564}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566/*
David Howellsd2ddc772017-11-02 15:27:50 +0000567 * Set up the iterator to start reading from the server list and return the
568 * first item.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 */
David Howellsd2ddc772017-11-02 15:27:50 +0000570static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf72018-04-09 21:12:31 +0100571 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
David Howellsd2ddc772017-11-02 15:27:50 +0000573 struct afs_net *net = afs_seq2net(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
David Howellsd2ddc772017-11-02 15:27:50 +0000575 rcu_read_lock();
576 return seq_hlist_start_head_rcu(&net->fs_proc, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700577}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579/*
580 * move to next cell in cells list
581 */
David Howellsd2ddc772017-11-02 15:27:50 +0000582static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
David Howellsd2ddc772017-11-02 15:27:50 +0000584 struct afs_net *net = afs_seq2net(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
David Howellsd2ddc772017-11-02 15:27:50 +0000586 return seq_hlist_next_rcu(v, &net->fs_proc, _pos);
David Howellsec268152007-04-26 15:49:28 -0700587}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589/*
590 * clean up after reading from the cells list
591 */
David Howellsd2ddc772017-11-02 15:27:50 +0000592static void afs_proc_servers_stop(struct seq_file *p, void *v)
David Howellsfe342cf72018-04-09 21:12:31 +0100593 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
David Howellsd2ddc772017-11-02 15:27:50 +0000595 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700596}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598/*
599 * display a header line followed by a load of volume lines
600 */
David Howellsd2ddc772017-11-02 15:27:50 +0000601static int afs_proc_servers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
David Howellsd2ddc772017-11-02 15:27:50 +0000603 struct afs_server *server;
604 struct afs_addr_list *alist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
David Howellsd2ddc772017-11-02 15:27:50 +0000606 if (v == SEQ_START_TOKEN) {
607 seq_puts(m, "UUID USE ADDR\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return 0;
609 }
610
David Howellsd2ddc772017-11-02 15:27:50 +0000611 server = list_entry(v, struct afs_server, proc_link);
612 alist = rcu_dereference(server->addresses);
613 seq_printf(m, "%pU %3d %pISp\n",
614 &server->uuid,
615 atomic_read(&server->usage),
616 &alist->addrs[alist->index].transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 return 0;
David Howellsec268152007-04-26 15:49:28 -0700618}
David Howells6f8880d2018-04-09 21:12:31 +0100619
620void afs_put_sysnames(struct afs_sysnames *sysnames)
621{
622 int i;
623
624 if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
625 for (i = 0; i < sysnames->nr; i++)
626 if (sysnames->subs[i] != afs_init_sysname &&
627 sysnames->subs[i] != sysnames->blank)
628 kfree(sysnames->subs[i]);
629 }
630}
631
632/*
633 * Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we
634 * assume the caller wants to change the substitution list and we allocate a
635 * buffer to hold the list.
636 */
637static int afs_proc_sysname_open(struct inode *inode, struct file *file)
638{
639 struct afs_sysnames *sysnames;
640 struct seq_file *m;
641 int ret;
642
643 ret = seq_open(file, &afs_proc_sysname_ops);
644 if (ret < 0)
645 return ret;
646
647 if (file->f_mode & FMODE_WRITE) {
648 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
649 if (!sysnames) {
650 seq_release(inode, file);
651 return -ENOMEM;
652 }
653
654 refcount_set(&sysnames->usage, 1);
655 m = file->private_data;
656 m->private = sysnames;
657 }
658
659 return 0;
660}
661
662/*
663 * Handle writes to /proc/fs/afs/sysname to set the @sys substitution.
664 */
665static ssize_t afs_proc_sysname_write(struct file *file,
666 const char __user *buf,
667 size_t size, loff_t *_pos)
668{
669 struct afs_sysnames *sysnames;
670 struct seq_file *m = file->private_data;
671 char *kbuf = NULL, *s, *p, *sub;
672 int ret, len;
673
674 sysnames = m->private;
675 if (!sysnames)
676 return -EINVAL;
677 if (sysnames->error)
678 return sysnames->error;
679
680 if (size >= PAGE_SIZE - 1) {
681 sysnames->error = -EINVAL;
682 return -EINVAL;
683 }
684 if (size == 0)
685 return 0;
686
687 kbuf = memdup_user_nul(buf, size);
688 if (IS_ERR(kbuf))
689 return PTR_ERR(kbuf);
690
691 inode_lock(file_inode(file));
692
693 p = kbuf;
694 while ((s = strsep(&p, " \t\n"))) {
695 len = strlen(s);
696 if (len == 0)
697 continue;
698 ret = -ENAMETOOLONG;
699 if (len >= AFSNAMEMAX)
700 goto error;
701
702 if (len >= 4 &&
703 s[len - 4] == '@' &&
704 s[len - 3] == 's' &&
705 s[len - 2] == 'y' &&
706 s[len - 1] == 's')
707 /* Protect against recursion */
708 goto invalid;
709
710 if (s[0] == '.' &&
711 (len < 2 || (len == 2 && s[1] == '.')))
712 goto invalid;
713
714 if (memchr(s, '/', len))
715 goto invalid;
716
717 ret = -EFBIG;
718 if (sysnames->nr >= AFS_NR_SYSNAME)
719 goto out;
720
721 if (strcmp(s, afs_init_sysname) == 0) {
722 sub = (char *)afs_init_sysname;
723 } else {
724 ret = -ENOMEM;
725 sub = kmemdup(s, len + 1, GFP_KERNEL);
726 if (!sub)
727 goto out;
728 }
729
730 sysnames->subs[sysnames->nr] = sub;
731 sysnames->nr++;
732 }
733
734 ret = size; /* consume everything, always */
735out:
736 inode_unlock(file_inode(file));
737 kfree(kbuf);
738 return ret;
739
740invalid:
741 ret = -EINVAL;
742error:
743 sysnames->error = ret;
744 goto out;
745}
746
747static int afs_proc_sysname_release(struct inode *inode, struct file *file)
748{
749 struct afs_sysnames *sysnames, *kill = NULL;
750 struct seq_file *m = file->private_data;
751 struct afs_net *net = afs_seq2net(m);
752
753 sysnames = m->private;
754 if (sysnames) {
755 if (!sysnames->error) {
756 kill = sysnames;
757 if (sysnames->nr == 0) {
758 sysnames->subs[0] = sysnames->blank;
759 sysnames->nr++;
760 }
761 write_lock(&net->sysnames_lock);
762 kill = net->sysnames;
763 net->sysnames = sysnames;
764 write_unlock(&net->sysnames_lock);
765 }
766 afs_put_sysnames(kill);
767 }
768
769 return seq_release(inode, file);
770}
771
772static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
773 __acquires(&net->sysnames_lock)
774{
775 struct afs_net *net = afs_seq2net(m);
776 struct afs_sysnames *names = net->sysnames;
777
778 read_lock(&net->sysnames_lock);
779
780 if (*pos >= names->nr)
781 return NULL;
782 return (void *)(unsigned long)(*pos + 1);
783}
784
785static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
786{
787 struct afs_net *net = afs_seq2net(m);
788 struct afs_sysnames *names = net->sysnames;
789
790 *pos += 1;
791 if (*pos >= names->nr)
792 return NULL;
793 return (void *)(unsigned long)(*pos + 1);
794}
795
796static void afs_proc_sysname_stop(struct seq_file *m, void *v)
797 __releases(&net->sysnames_lock)
798{
799 struct afs_net *net = afs_seq2net(m);
800
801 read_unlock(&net->sysnames_lock);
802}
803
804static int afs_proc_sysname_show(struct seq_file *m, void *v)
805{
806 struct afs_net *net = afs_seq2net(m);
807 struct afs_sysnames *sysnames = net->sysnames;
808 unsigned int i = (unsigned long)v - 1;
809
810 if (i < sysnames->nr)
811 seq_printf(m, "%s\n", sysnames->subs[i]);
812 return 0;
813}
David Howellsd55b4da2018-04-06 14:17:24 +0100814
815/*
816 * Display general per-net namespace statistics
817 */
818static int afs_proc_stats_show(struct seq_file *m, void *v)
819{
820 struct afs_net *net = afs_seq2net(m);
821
822 seq_puts(m, "kAFS statistics\n");
823
David Howellsf3ddee82018-04-06 14:17:25 +0100824 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
David Howellsd55b4da2018-04-06 14:17:24 +0100825 atomic_read(&net->n_lookup),
826 atomic_read(&net->n_reval),
David Howellsf3ddee82018-04-06 14:17:25 +0100827 atomic_read(&net->n_inval),
828 atomic_read(&net->n_relpg));
David Howellsd55b4da2018-04-06 14:17:24 +0100829
830 seq_printf(m, "dir-data: rdpg=%u\n",
831 atomic_read(&net->n_read_dir));
David Howells63a46812018-04-06 14:17:25 +0100832
833 seq_printf(m, "dir-edit: cr=%u rm=%u\n",
834 atomic_read(&net->n_dir_cr),
835 atomic_read(&net->n_dir_rm));
David Howells76a5cb62018-04-06 14:17:26 +0100836
837 seq_printf(m, "file-rd : n=%u nb=%lu\n",
838 atomic_read(&net->n_fetches),
839 atomic_long_read(&net->n_fetch_bytes));
840 seq_printf(m, "file-wr : n=%u nb=%lu\n",
841 atomic_read(&net->n_stores),
842 atomic_long_read(&net->n_store_bytes));
David Howellsd55b4da2018-04-06 14:17:24 +0100843 return 0;
844}