blob: 0c3285c8db95b4ec6457fdfe759e1997c9c69a7f [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 (dhowells@redhat.com)
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_seq2net(struct seq_file *m)
21{
David Howells5b86d4f2018-05-18 11:46:15 +010022 return afs_net(seq_file_net(m));
23}
24
25static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
26{
27 return afs_net(seq_file_single_net(m));
David Howellsf044c882017-11-02 15:27:45 +000028}
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Linus Torvalds1da177e2005-04-16 15:20:36 -070030/*
David Howells5d9de252018-05-18 11:46:15 +010031 * Display the list of cells known to the namespace.
David Howellsf0691682018-05-18 11:46:14 +010032 */
33static int afs_proc_cells_show(struct seq_file *m, void *v)
34{
35 struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
36 struct afs_net *net = afs_seq2net(m);
37
38 if (v == &net->proc_cells) {
39 /* display header on line 1 */
40 seq_puts(m, "USE NAME\n");
41 return 0;
42 }
43
44 /* display one cell per line on subsequent lines */
45 seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
46 return 0;
47}
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf2018-04-09 21:12:31 +010050 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -070051{
David Howells989782d2017-11-02 15:27:50 +000052 rcu_read_lock();
David Howells5d9de252018-05-18 11:46:15 +010053 return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
David Howellsec268152007-04-26 15:49:28 -070054}
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
David Howellsf044c882017-11-02 15:27:45 +000056static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -070057{
David Howells5d9de252018-05-18 11:46:15 +010058 return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
David Howellsec268152007-04-26 15:49:28 -070059}
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
David Howellsf044c882017-11-02 15:27:45 +000061static void afs_proc_cells_stop(struct seq_file *m, void *v)
David Howellsfe342cf2018-04-09 21:12:31 +010062 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063{
David Howells989782d2017-11-02 15:27:50 +000064 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -070065}
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
David Howells5d9de252018-05-18 11:46:15 +010067static const struct seq_operations afs_proc_cells_ops = {
68 .start = afs_proc_cells_start,
69 .next = afs_proc_cells_next,
70 .stop = afs_proc_cells_stop,
71 .show = afs_proc_cells_show,
72};
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 * handle writes to /proc/fs/afs/cells
76 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
77 */
David Howells5b86d4f2018-05-18 11:46:15 +010078static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
David Howells5b86d4f2018-05-18 11:46:15 +010080 struct seq_file *m = file->private_data;
81 struct afs_net *net = afs_seq2net(m);
82 char *name, *args;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 int ret;
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 /* trim to first NL */
David Howells5b86d4f2018-05-18 11:46:15 +010086 name = memchr(buf, '\n', size);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if (name)
88 *name = 0;
89
90 /* split into command, name and argslist */
David Howells5b86d4f2018-05-18 11:46:15 +010091 name = strchr(buf, ' ');
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 if (!name)
93 goto inval;
94 do {
95 *name++ = 0;
96 } while(*name == ' ');
97 if (!*name)
98 goto inval;
99
100 args = strchr(name, ' ');
101 if (!args)
102 goto inval;
103 do {
104 *args++ = 0;
105 } while(*args == ' ');
106 if (!*args)
107 goto inval;
108
109 /* determine command to perform */
David Howells5b86d4f2018-05-18 11:46:15 +0100110 _debug("cmd=%s name=%s args=%s", buf, name, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
David Howells5b86d4f2018-05-18 11:46:15 +0100112 if (strcmp(buf, "add") == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 struct afs_cell *cell;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
David Howells989782d2017-11-02 15:27:50 +0000115 cell = afs_lookup_cell(net, name, strlen(name), args, true);
David Howells08e0e7c2007-04-26 15:55:03 -0700116 if (IS_ERR(cell)) {
117 ret = PTR_ERR(cell);
118 goto done;
119 }
120
David Howells17814ae2018-04-09 21:12:31 +0100121 if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
122 afs_put_cell(net, cell);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 printk("kAFS: Added new cell '%s'\n", name);
David Howellsec268152007-04-26 15:49:28 -0700124 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 goto inval;
126 }
127
David Howells5b86d4f2018-05-18 11:46:15 +0100128 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
David Howellsec268152007-04-26 15:49:28 -0700130done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 _leave(" = %d", ret);
132 return ret;
133
David Howellsec268152007-04-26 15:49:28 -0700134inval:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 ret = -EINVAL;
136 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
137 goto done;
David Howellsec268152007-04-26 15:49:28 -0700138}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
David Howells5d9de252018-05-18 11:46:15 +0100140/*
David Howells5b86d4f2018-05-18 11:46:15 +0100141 * Display the name of the current workstation cell.
David Howells5d9de252018-05-18 11:46:15 +0100142 */
David Howells5b86d4f2018-05-18 11:46:15 +0100143static int afs_proc_rootcell_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
David Howells37ab6362018-04-06 14:17:23 +0100145 struct afs_cell *cell;
David Howells5b86d4f2018-05-18 11:46:15 +0100146 struct afs_net *net;
David Howells37ab6362018-04-06 14:17:23 +0100147
David Howells5b86d4f2018-05-18 11:46:15 +0100148 net = afs_seq2net_single(m);
149 if (rcu_access_pointer(net->ws_cell)) {
150 rcu_read_lock();
151 cell = rcu_dereference(net->ws_cell);
152 if (cell)
153 seq_printf(m, "%s\n", cell->name);
154 rcu_read_unlock();
155 }
156 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159/*
David Howells5d9de252018-05-18 11:46:15 +0100160 * Set the current workstation cell and optionally supply its list of volume
161 * location servers.
162 *
163 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 */
David Howells5b86d4f2018-05-18 11:46:15 +0100165static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
David Howells5b86d4f2018-05-18 11:46:15 +0100167 struct seq_file *m = file->private_data;
168 struct afs_net *net = afs_seq2net_single(m);
169 char *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 int ret;
171
David Howells6f8880d2018-04-09 21:12:31 +0100172 ret = -EINVAL;
David Howells5b86d4f2018-05-18 11:46:15 +0100173 if (buf[0] == '.')
David Howells6f8880d2018-04-09 21:12:31 +0100174 goto out;
David Howells5b86d4f2018-05-18 11:46:15 +0100175 if (memchr(buf, '/', size))
David Howells6f8880d2018-04-09 21:12:31 +0100176 goto out;
177
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 /* trim to first NL */
David Howells5b86d4f2018-05-18 11:46:15 +0100179 s = memchr(buf, '\n', size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if (s)
181 *s = 0;
182
183 /* determine command to perform */
David Howells5b86d4f2018-05-18 11:46:15 +0100184 _debug("rootcell=%s", buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
David Howells5b86d4f2018-05-18 11:46:15 +0100186 ret = afs_cell_init(net, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
David Howells6f8880d2018-04-09 21:12:31 +0100188out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 _leave(" = %d", ret);
190 return ret;
David Howellsec268152007-04-26 15:49:28 -0700191}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
David Howellsf0691682018-05-18 11:46:14 +0100193static const char afs_vol_types[3][3] = {
194 [AFSVL_RWVOL] = "RW",
195 [AFSVL_ROVOL] = "RO",
196 [AFSVL_BACKVOL] = "BK",
197};
198
199/*
David Howells5d9de252018-05-18 11:46:15 +0100200 * Display the list of volumes known to a cell.
David Howellsf0691682018-05-18 11:46:14 +0100201 */
202static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
203{
204 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
205 struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
206
207 /* Display header on line 1 */
208 if (v == &cell->proc_volumes) {
209 seq_puts(m, "USE VID TY\n");
210 return 0;
211 }
212
213 seq_printf(m, "%3d %08x %s\n",
214 atomic_read(&vol->usage), vol->vid,
215 afs_vol_types[vol->type]);
216
217 return 0;
218}
219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf2018-04-09 21:12:31 +0100221 __acquires(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
Christoph Hellwig353861c2018-04-13 20:45:09 +0200223 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
David Howellsd2ddc772017-11-02 15:27:50 +0000225 read_lock(&cell->proc_lock);
226 return seq_list_start_head(&cell->proc_volumes, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700227}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
David Howells5b86d4f2018-05-18 11:46:15 +0100229static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 loff_t *_pos)
231{
David Howells5b86d4f2018-05-18 11:46:15 +0100232 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
David Howellsd2ddc772017-11-02 15:27:50 +0000234 return seq_list_next(v, &cell->proc_volumes, _pos);
David Howellsec268152007-04-26 15:49:28 -0700235}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
David Howells5b86d4f2018-05-18 11:46:15 +0100237static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
David Howellsfe342cf2018-04-09 21:12:31 +0100238 __releases(cell->proc_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
David Howells5b86d4f2018-05-18 11:46:15 +0100240 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
David Howellsd2ddc772017-11-02 15:27:50 +0000242 read_unlock(&cell->proc_lock);
David Howellsec268152007-04-26 15:49:28 -0700243}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
David Howells5d9de252018-05-18 11:46:15 +0100245static const struct seq_operations afs_proc_cell_volumes_ops = {
246 .start = afs_proc_cell_volumes_start,
247 .next = afs_proc_cell_volumes_next,
248 .stop = afs_proc_cell_volumes_stop,
249 .show = afs_proc_cell_volumes_show,
250};
251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252/*
David Howells5d9de252018-05-18 11:46:15 +0100253 * Display the list of Volume Location servers we're using for a cell.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 */
David Howellsf0691682018-05-18 11:46:14 +0100255static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
David Howellsf0691682018-05-18 11:46:14 +0100257 struct sockaddr_rxrpc *addr = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
David Howellsf0691682018-05-18 11:46:14 +0100259 /* display header on line 1 */
260 if (v == (void *)1) {
261 seq_puts(m, "ADDRESS\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return 0;
263 }
264
David Howellsf0691682018-05-18 11:46:14 +0100265 /* display one cell per line on subsequent lines */
266 seq_printf(m, "%pISp\n", &addr->transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 return 0;
David Howellsec268152007-04-26 15:49:28 -0700268}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf2018-04-09 21:12:31 +0100271 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272{
David Howells8b2a4642017-11-02 15:27:50 +0000273 struct afs_addr_list *alist;
Christoph Hellwig353861c2018-04-13 20:45:09 +0200274 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 loff_t pos = *_pos;
276
David Howells8b2a4642017-11-02 15:27:50 +0000277 rcu_read_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
David Howells8b2a4642017-11-02 15:27:50 +0000279 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281 /* allow for the header line */
282 if (!pos)
283 return (void *) 1;
284 pos--;
285
David Howells8b2a4642017-11-02 15:27:50 +0000286 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 return NULL;
288
David Howells8b2a4642017-11-02 15:27:50 +0000289 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700290}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
David Howells5b86d4f2018-05-18 11:46:15 +0100292static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 loff_t *_pos)
294{
David Howells8b2a4642017-11-02 15:27:50 +0000295 struct afs_addr_list *alist;
David Howells5b86d4f2018-05-18 11:46:15 +0100296 struct afs_cell *cell = PDE_DATA(file_inode(m->file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 loff_t pos;
298
David Howells8b2a4642017-11-02 15:27:50 +0000299 alist = rcu_dereference(cell->vl_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 pos = *_pos;
302 (*_pos)++;
David Howells8b2a4642017-11-02 15:27:50 +0000303 if (!alist || pos >= alist->nr_addrs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 return NULL;
305
David Howells8b2a4642017-11-02 15:27:50 +0000306 return alist->addrs + pos;
David Howellsec268152007-04-26 15:49:28 -0700307}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
David Howells5b86d4f2018-05-18 11:46:15 +0100309static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
David Howellsfe342cf2018-04-09 21:12:31 +0100310 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311{
David Howells8b2a4642017-11-02 15:27:50 +0000312 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700313}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
David Howells5d9de252018-05-18 11:46:15 +0100315static const struct seq_operations afs_proc_cell_vlservers_ops = {
316 .start = afs_proc_cell_vlservers_start,
317 .next = afs_proc_cell_vlservers_next,
318 .stop = afs_proc_cell_vlservers_stop,
319 .show = afs_proc_cell_vlservers_show,
320};
321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322/*
David Howells5d9de252018-05-18 11:46:15 +0100323 * Display the list of fileservers we're using within a namespace.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 */
David Howellsf0691682018-05-18 11:46:14 +0100325static int afs_proc_servers_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
David Howellsf0691682018-05-18 11:46:14 +0100327 struct afs_server *server;
328 struct afs_addr_list *alist;
David Howells0aac4bce2018-06-02 22:20:31 +0100329 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
David Howellsf0691682018-05-18 11:46:14 +0100331 if (v == SEQ_START_TOKEN) {
332 seq_puts(m, "UUID USE ADDR\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 return 0;
334 }
335
David Howellsf0691682018-05-18 11:46:14 +0100336 server = list_entry(v, struct afs_server, proc_link);
337 alist = rcu_dereference(server->addresses);
David Howells0aac4bce2018-06-02 22:20:31 +0100338 seq_printf(m, "%pU %3d %pISpc%s\n",
David Howellsf0691682018-05-18 11:46:14 +0100339 &server->uuid,
340 atomic_read(&server->usage),
David Howells0aac4bce2018-06-02 22:20:31 +0100341 &alist->addrs[0].transport,
342 alist->index == 0 ? "*" : "");
343 for (i = 1; i < alist->nr_addrs; i++)
344 seq_printf(m, " %pISpc%s\n",
345 &alist->addrs[i].transport,
346 alist->index == i ? "*" : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 return 0;
David Howellsec268152007-04-26 15:49:28 -0700348}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
David Howellsd2ddc772017-11-02 15:27:50 +0000350static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
David Howellsfe342cf2018-04-09 21:12:31 +0100351 __acquires(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
David Howellsd2ddc772017-11-02 15:27:50 +0000353 rcu_read_lock();
David Howells5d9de252018-05-18 11:46:15 +0100354 return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
David Howellsec268152007-04-26 15:49:28 -0700355}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
David Howellsd2ddc772017-11-02 15:27:50 +0000357static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
David Howells5d9de252018-05-18 11:46:15 +0100359 return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
David Howellsec268152007-04-26 15:49:28 -0700360}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
David Howells5b86d4f2018-05-18 11:46:15 +0100362static void afs_proc_servers_stop(struct seq_file *m, void *v)
David Howellsfe342cf2018-04-09 21:12:31 +0100363 __releases(rcu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
David Howellsd2ddc772017-11-02 15:27:50 +0000365 rcu_read_unlock();
David Howellsec268152007-04-26 15:49:28 -0700366}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
David Howells5d9de252018-05-18 11:46:15 +0100368static const struct seq_operations afs_proc_servers_ops = {
369 .start = afs_proc_servers_start,
370 .next = afs_proc_servers_next,
371 .stop = afs_proc_servers_stop,
372 .show = afs_proc_servers_show,
373};
David Howells6f8880d2018-04-09 21:12:31 +0100374
David Howells6f8880d2018-04-09 21:12:31 +0100375/*
David Howells5d9de252018-05-18 11:46:15 +0100376 * Display the list of strings that may be substituted for the @sys pathname
377 * macro.
David Howells6f8880d2018-04-09 21:12:31 +0100378 */
David Howells5d9de252018-05-18 11:46:15 +0100379static int afs_proc_sysname_show(struct seq_file *m, void *v)
David Howells6f8880d2018-04-09 21:12:31 +0100380{
David Howells5d9de252018-05-18 11:46:15 +0100381 struct afs_net *net = afs_seq2net(m);
382 struct afs_sysnames *sysnames = net->sysnames;
383 unsigned int i = (unsigned long)v - 1;
David Howells6f8880d2018-04-09 21:12:31 +0100384
David Howells5d9de252018-05-18 11:46:15 +0100385 if (i < sysnames->nr)
386 seq_printf(m, "%s\n", sysnames->subs[i]);
David Howells6f8880d2018-04-09 21:12:31 +0100387 return 0;
388}
389
David Howells5d9de252018-05-18 11:46:15 +0100390static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
391 __acquires(&net->sysnames_lock)
392{
393 struct afs_net *net = afs_seq2net(m);
David Howells5b86d4f2018-05-18 11:46:15 +0100394 struct afs_sysnames *names;
David Howells5d9de252018-05-18 11:46:15 +0100395
396 read_lock(&net->sysnames_lock);
397
David Howells5b86d4f2018-05-18 11:46:15 +0100398 names = net->sysnames;
David Howells5d9de252018-05-18 11:46:15 +0100399 if (*pos >= names->nr)
400 return NULL;
401 return (void *)(unsigned long)(*pos + 1);
402}
403
404static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
405{
406 struct afs_net *net = afs_seq2net(m);
407 struct afs_sysnames *names = net->sysnames;
408
409 *pos += 1;
410 if (*pos >= names->nr)
411 return NULL;
412 return (void *)(unsigned long)(*pos + 1);
413}
414
415static void afs_proc_sysname_stop(struct seq_file *m, void *v)
416 __releases(&net->sysnames_lock)
417{
418 struct afs_net *net = afs_seq2net(m);
419
420 read_unlock(&net->sysnames_lock);
421}
422
423static const struct seq_operations afs_proc_sysname_ops = {
424 .start = afs_proc_sysname_start,
425 .next = afs_proc_sysname_next,
426 .stop = afs_proc_sysname_stop,
427 .show = afs_proc_sysname_show,
428};
429
David Howells6f8880d2018-04-09 21:12:31 +0100430/*
David Howells5d9de252018-05-18 11:46:15 +0100431 * Allow the @sys substitution to be configured.
David Howells6f8880d2018-04-09 21:12:31 +0100432 */
David Howells5b86d4f2018-05-18 11:46:15 +0100433static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
David Howells6f8880d2018-04-09 21:12:31 +0100434{
David Howells5b86d4f2018-05-18 11:46:15 +0100435 struct afs_sysnames *sysnames, *kill;
David Howells6f8880d2018-04-09 21:12:31 +0100436 struct seq_file *m = file->private_data;
David Howells5b86d4f2018-05-18 11:46:15 +0100437 struct afs_net *net = afs_seq2net(m);
438 char *s, *p, *sub;
David Howells6f8880d2018-04-09 21:12:31 +0100439 int ret, len;
440
David Howells5b86d4f2018-05-18 11:46:15 +0100441 sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
David Howells6f8880d2018-04-09 21:12:31 +0100442 if (!sysnames)
David Howells5b86d4f2018-05-18 11:46:15 +0100443 return -ENOMEM;
444 refcount_set(&sysnames->usage, 1);
445 kill = sysnames;
David Howells6f8880d2018-04-09 21:12:31 +0100446
David Howells5b86d4f2018-05-18 11:46:15 +0100447 p = buf;
David Howells6f8880d2018-04-09 21:12:31 +0100448 while ((s = strsep(&p, " \t\n"))) {
449 len = strlen(s);
450 if (len == 0)
451 continue;
452 ret = -ENAMETOOLONG;
453 if (len >= AFSNAMEMAX)
454 goto error;
455
456 if (len >= 4 &&
457 s[len - 4] == '@' &&
458 s[len - 3] == 's' &&
459 s[len - 2] == 'y' &&
460 s[len - 1] == 's')
461 /* Protect against recursion */
462 goto invalid;
463
464 if (s[0] == '.' &&
465 (len < 2 || (len == 2 && s[1] == '.')))
466 goto invalid;
467
468 if (memchr(s, '/', len))
469 goto invalid;
470
471 ret = -EFBIG;
472 if (sysnames->nr >= AFS_NR_SYSNAME)
473 goto out;
474
475 if (strcmp(s, afs_init_sysname) == 0) {
476 sub = (char *)afs_init_sysname;
477 } else {
478 ret = -ENOMEM;
479 sub = kmemdup(s, len + 1, GFP_KERNEL);
480 if (!sub)
481 goto out;
482 }
483
484 sysnames->subs[sysnames->nr] = sub;
485 sysnames->nr++;
486 }
487
David Howells5b86d4f2018-05-18 11:46:15 +0100488 if (sysnames->nr == 0) {
489 sysnames->subs[0] = sysnames->blank;
490 sysnames->nr++;
491 }
492
493 write_lock(&net->sysnames_lock);
494 kill = net->sysnames;
495 net->sysnames = sysnames;
496 write_unlock(&net->sysnames_lock);
497 ret = 0;
David Howells6f8880d2018-04-09 21:12:31 +0100498out:
David Howells5b86d4f2018-05-18 11:46:15 +0100499 afs_put_sysnames(kill);
David Howells6f8880d2018-04-09 21:12:31 +0100500 return ret;
501
502invalid:
503 ret = -EINVAL;
504error:
David Howells6f8880d2018-04-09 21:12:31 +0100505 goto out;
506}
507
David Howells5d9de252018-05-18 11:46:15 +0100508void afs_put_sysnames(struct afs_sysnames *sysnames)
509{
510 int i;
511
512 if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
513 for (i = 0; i < sysnames->nr; i++)
514 if (sysnames->subs[i] != afs_init_sysname &&
515 sysnames->subs[i] != sysnames->blank)
516 kfree(sysnames->subs[i]);
517 }
518}
519
David Howellsd55b4da2018-04-06 14:17:24 +0100520/*
521 * Display general per-net namespace statistics
522 */
523static int afs_proc_stats_show(struct seq_file *m, void *v)
524{
David Howells5b86d4f2018-05-18 11:46:15 +0100525 struct afs_net *net = afs_seq2net_single(m);
David Howellsd55b4da2018-04-06 14:17:24 +0100526
527 seq_puts(m, "kAFS statistics\n");
528
David Howellsf3ddee82018-04-06 14:17:25 +0100529 seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
David Howellsd55b4da2018-04-06 14:17:24 +0100530 atomic_read(&net->n_lookup),
531 atomic_read(&net->n_reval),
David Howellsf3ddee82018-04-06 14:17:25 +0100532 atomic_read(&net->n_inval),
533 atomic_read(&net->n_relpg));
David Howellsd55b4da2018-04-06 14:17:24 +0100534
535 seq_printf(m, "dir-data: rdpg=%u\n",
536 atomic_read(&net->n_read_dir));
David Howells63a46812018-04-06 14:17:25 +0100537
538 seq_printf(m, "dir-edit: cr=%u rm=%u\n",
539 atomic_read(&net->n_dir_cr),
540 atomic_read(&net->n_dir_rm));
David Howells76a5cb62018-04-06 14:17:26 +0100541
542 seq_printf(m, "file-rd : n=%u nb=%lu\n",
543 atomic_read(&net->n_fetches),
544 atomic_long_read(&net->n_fetch_bytes));
545 seq_printf(m, "file-wr : n=%u nb=%lu\n",
546 atomic_read(&net->n_stores),
547 atomic_long_read(&net->n_store_bytes));
David Howellsd55b4da2018-04-06 14:17:24 +0100548 return 0;
549}
David Howells10495a02018-05-18 11:46:14 +0100550
551/*
552 * initialise /proc/fs/afs/<cell>/
553 */
David Howells5b86d4f2018-05-18 11:46:15 +0100554int afs_proc_cell_setup(struct afs_cell *cell)
David Howells10495a02018-05-18 11:46:14 +0100555{
556 struct proc_dir_entry *dir;
David Howells5b86d4f2018-05-18 11:46:15 +0100557 struct afs_net *net = cell->net;
David Howells10495a02018-05-18 11:46:14 +0100558
559 _enter("%p{%s},%p", cell, cell->name, net->proc_afs);
560
David Howells5b86d4f2018-05-18 11:46:15 +0100561 dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
David Howells10495a02018-05-18 11:46:14 +0100562 if (!dir)
563 goto error_dir;
564
David Howells5b86d4f2018-05-18 11:46:15 +0100565 if (!proc_create_net_data("vlservers", 0444, dir,
566 &afs_proc_cell_vlservers_ops,
567 sizeof(struct seq_net_private),
568 cell) ||
569 !proc_create_net_data("volumes", 0444, dir,
570 &afs_proc_cell_volumes_ops,
571 sizeof(struct seq_net_private),
572 cell))
David Howells10495a02018-05-18 11:46:14 +0100573 goto error_tree;
574
575 _leave(" = 0");
576 return 0;
577
578error_tree:
579 remove_proc_subtree(cell->name, net->proc_afs);
580error_dir:
581 _leave(" = -ENOMEM");
582 return -ENOMEM;
583}
584
585/*
586 * remove /proc/fs/afs/<cell>/
587 */
David Howells5b86d4f2018-05-18 11:46:15 +0100588void afs_proc_cell_remove(struct afs_cell *cell)
David Howells10495a02018-05-18 11:46:14 +0100589{
David Howells5b86d4f2018-05-18 11:46:15 +0100590 struct afs_net *net = cell->net;
591
David Howells10495a02018-05-18 11:46:14 +0100592 _enter("");
David Howells10495a02018-05-18 11:46:14 +0100593 remove_proc_subtree(cell->name, net->proc_afs);
David Howells10495a02018-05-18 11:46:14 +0100594 _leave("");
595}
596
597/*
598 * initialise the /proc/fs/afs/ directory
599 */
600int afs_proc_init(struct afs_net *net)
601{
David Howells5b86d4f2018-05-18 11:46:15 +0100602 struct proc_dir_entry *p;
603
David Howells10495a02018-05-18 11:46:14 +0100604 _enter("");
605
David Howells5b86d4f2018-05-18 11:46:15 +0100606 p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
607 if (!p)
David Howells10495a02018-05-18 11:46:14 +0100608 goto error_dir;
609
David Howells5b86d4f2018-05-18 11:46:15 +0100610 if (!proc_create_net_data_write("cells", 0644, p,
611 &afs_proc_cells_ops,
612 afs_proc_cells_write,
613 sizeof(struct seq_net_private),
614 NULL) ||
615 !proc_create_net_single_write("rootcell", 0644, p,
616 afs_proc_rootcell_show,
617 afs_proc_rootcell_write,
618 NULL) ||
619 !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
620 sizeof(struct seq_net_private)) ||
621 !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
622 !proc_create_net_data_write("sysname", 0644, p,
623 &afs_proc_sysname_ops,
624 afs_proc_sysname_write,
625 sizeof(struct seq_net_private),
626 NULL))
David Howells10495a02018-05-18 11:46:14 +0100627 goto error_tree;
628
David Howells5b86d4f2018-05-18 11:46:15 +0100629 net->proc_afs = p;
David Howells10495a02018-05-18 11:46:14 +0100630 _leave(" = 0");
631 return 0;
632
633error_tree:
David Howells5b86d4f2018-05-18 11:46:15 +0100634 proc_remove(p);
David Howells10495a02018-05-18 11:46:14 +0100635error_dir:
636 _leave(" = -ENOMEM");
637 return -ENOMEM;
638}
639
640/*
641 * clean up the /proc/fs/afs/ directory
642 */
643void afs_proc_cleanup(struct afs_net *net)
644{
645 proc_remove(net->proc_afs);
646 net->proc_afs = NULL;
647}