]>
Commit | Line | Data |
---|---|---|
ec26815a | 1 | /* /proc interface for AFS |
1da177e4 LT |
2 | * |
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 | ||
1da177e4 LT |
12 | #include <linux/slab.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/proc_fs.h> | |
15 | #include <linux/seq_file.h> | |
e8edc6e0 | 16 | #include <linux/sched.h> |
7c0f6ba6 | 17 | #include <linux/uaccess.h> |
1da177e4 LT |
18 | #include "internal.h" |
19 | ||
f044c884 DH |
20 | static inline struct afs_net *afs_proc2net(struct file *f) |
21 | { | |
22 | return &__afs_net; | |
23 | } | |
1da177e4 | 24 | |
f044c884 DH |
25 | static inline struct afs_net *afs_seq2net(struct seq_file *m) |
26 | { | |
27 | return &__afs_net; // TODO: use seq_file_net(m) | |
28 | } | |
1da177e4 | 29 | |
1da177e4 LT |
30 | static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); |
31 | static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); | |
32 | static void afs_proc_cells_stop(struct seq_file *p, void *v); | |
33 | static int afs_proc_cells_show(struct seq_file *m, void *v); | |
1da177e4 | 34 | |
88e9d34c | 35 | static const struct seq_operations afs_proc_cells_ops = { |
1da177e4 LT |
36 | .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 | ||
1da177e4 LT |
42 | static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); |
43 | static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, | |
44 | loff_t *pos); | |
45 | static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); | |
46 | static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); | |
47 | ||
88e9d34c | 48 | static const struct seq_operations afs_proc_cell_volumes_ops = { |
1da177e4 LT |
49 | .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 | ||
1da177e4 LT |
55 | static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); |
56 | static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, | |
57 | loff_t *pos); | |
58 | static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); | |
59 | static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); | |
60 | ||
88e9d34c | 61 | static const struct seq_operations afs_proc_cell_vlservers_ops = { |
1da177e4 LT |
62 | .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 | ||
d2ddc776 DH |
68 | static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos); |
69 | static void *afs_proc_servers_next(struct seq_file *p, void *v, | |
1da177e4 | 70 | loff_t *pos); |
d2ddc776 DH |
71 | static void afs_proc_servers_stop(struct seq_file *p, void *v); |
72 | static int afs_proc_servers_show(struct seq_file *m, void *v); | |
73 | ||
74 | static 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, | |
1da177e4 LT |
79 | }; |
80 | ||
6f8880d8 DH |
81 | static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos); |
82 | static void *afs_proc_sysname_next(struct seq_file *p, void *v, | |
83 | loff_t *pos); | |
84 | static void afs_proc_sysname_stop(struct seq_file *p, void *v); | |
85 | static int afs_proc_sysname_show(struct seq_file *m, void *v); | |
6f8880d8 DH |
86 | |
87 | static 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 | ||
f0691689 DH |
94 | /* |
95 | * display a header line followed by a load of cell lines | |
96 | */ | |
97 | static 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 | ||
1da177e4 LT |
113 | /* |
114 | * set up the iterator to start reading from the cells list and return the | |
115 | * first item | |
116 | */ | |
117 | static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) | |
fe342cf7 | 118 | __acquires(rcu) |
1da177e4 | 119 | { |
f044c884 DH |
120 | struct afs_net *net = afs_seq2net(m); |
121 | ||
989782dc | 122 | rcu_read_lock(); |
f044c884 | 123 | return seq_list_start_head(&net->proc_cells, *_pos); |
ec26815a | 124 | } |
1da177e4 | 125 | |
1da177e4 LT |
126 | /* |
127 | * move to next cell in cells list | |
128 | */ | |
f044c884 | 129 | static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos) |
1da177e4 | 130 | { |
f044c884 DH |
131 | struct afs_net *net = afs_seq2net(m); |
132 | ||
133 | return seq_list_next(v, &net->proc_cells, pos); | |
ec26815a | 134 | } |
1da177e4 | 135 | |
1da177e4 LT |
136 | /* |
137 | * clean up after reading from the cells list | |
138 | */ | |
f044c884 | 139 | static void afs_proc_cells_stop(struct seq_file *m, void *v) |
fe342cf7 | 140 | __releases(rcu) |
1da177e4 | 141 | { |
989782dc | 142 | rcu_read_unlock(); |
ec26815a | 143 | } |
1da177e4 | 144 | |
1da177e4 LT |
145 | /* |
146 | * handle writes to /proc/fs/afs/cells | |
147 | * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]" | |
148 | */ | |
149 | static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, | |
150 | size_t size, loff_t *_pos) | |
151 | { | |
f044c884 | 152 | struct afs_net *net = afs_proc2net(file); |
1da177e4 LT |
153 | 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 | ||
16e5c1fc AV |
160 | kbuf = memdup_user_nul(buf, size); |
161 | if (IS_ERR(kbuf)) | |
162 | return PTR_ERR(kbuf); | |
1da177e4 LT |
163 | |
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; | |
08e0e7c8 | 193 | |
989782dc | 194 | cell = afs_lookup_cell(net, name, strlen(name), args, true); |
08e0e7c8 DH |
195 | if (IS_ERR(cell)) { |
196 | ret = PTR_ERR(cell); | |
1da177e4 | 197 | goto done; |
08e0e7c8 | 198 | } |
1da177e4 | 199 | |
17814aef DH |
200 | if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags)) |
201 | afs_put_cell(net, cell); | |
1da177e4 | 202 | printk("kAFS: Added new cell '%s'\n", name); |
ec26815a | 203 | } else { |
1da177e4 LT |
204 | goto inval; |
205 | } | |
206 | ||
207 | ret = size; | |
208 | ||
ec26815a | 209 | done: |
1da177e4 LT |
210 | kfree(kbuf); |
211 | _leave(" = %d", ret); | |
212 | return ret; | |
213 | ||
ec26815a | 214 | inval: |
1da177e4 LT |
215 | ret = -EINVAL; |
216 | printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n"); | |
217 | goto done; | |
ec26815a | 218 | } |
1da177e4 | 219 | |
22ade7e7 DH |
220 | static int afs_proc_cells_open(struct inode *inode, struct file *file) |
221 | { | |
222 | return seq_open(file, &afs_proc_cells_ops); | |
223 | } | |
224 | ||
225 | static 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 | ||
1da177e4 LT |
233 | static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, |
234 | size_t size, loff_t *_pos) | |
235 | { | |
37ab6368 DH |
236 | 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; | |
1da177e4 LT |
270 | } |
271 | ||
1da177e4 LT |
272 | /* |
273 | * handle writes to /proc/fs/afs/rootcell | |
274 | * - to initialize rootcell: echo "cell.name:192.168.231.14" | |
275 | */ | |
276 | static ssize_t afs_proc_rootcell_write(struct file *file, | |
277 | const char __user *buf, | |
278 | size_t size, loff_t *_pos) | |
279 | { | |
f044c884 | 280 | struct afs_net *net = afs_proc2net(file); |
1da177e4 LT |
281 | 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 | ||
16e5c1fc AV |
288 | kbuf = memdup_user_nul(buf, size); |
289 | if (IS_ERR(kbuf)) | |
290 | return PTR_ERR(kbuf); | |
1da177e4 | 291 | |
6f8880d8 DH |
292 | ret = -EINVAL; |
293 | if (kbuf[0] == '.') | |
294 | goto out; | |
295 | if (memchr(kbuf, '/', size)) | |
296 | goto out; | |
297 | ||
1da177e4 LT |
298 | /* 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 | ||
f044c884 | 306 | ret = afs_cell_init(net, kbuf); |
1da177e4 LT |
307 | if (ret >= 0) |
308 | ret = size; /* consume everything, always */ | |
309 | ||
6f8880d8 | 310 | out: |
1da177e4 | 311 | kfree(kbuf); |
1da177e4 LT |
312 | _leave(" = %d", ret); |
313 | return ret; | |
ec26815a | 314 | } |
1da177e4 | 315 | |
22ade7e7 DH |
316 | static 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 | ||
f0691689 DH |
322 | static 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 | */ | |
331 | static 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 | ||
1da177e4 LT |
349 | /* |
350 | * set up the iterator to start reading from the cells list and return the | |
351 | * first item | |
352 | */ | |
353 | static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) | |
fe342cf7 | 354 | __acquires(cell->proc_lock) |
1da177e4 | 355 | { |
353861cf | 356 | struct afs_cell *cell = PDE_DATA(file_inode(m->file)); |
1da177e4 LT |
357 | |
358 | _enter("cell=%p pos=%Ld", cell, *_pos); | |
359 | ||
d2ddc776 DH |
360 | read_lock(&cell->proc_lock); |
361 | return seq_list_start_head(&cell->proc_volumes, *_pos); | |
ec26815a | 362 | } |
1da177e4 | 363 | |
1da177e4 LT |
364 | /* |
365 | * move to next cell in cells list | |
366 | */ | |
367 | static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, | |
368 | loff_t *_pos) | |
369 | { | |
353861cf | 370 | struct afs_cell *cell = PDE_DATA(file_inode(p->file)); |
1da177e4 LT |
371 | |
372 | _enter("cell=%p pos=%Ld", cell, *_pos); | |
d2ddc776 | 373 | return seq_list_next(v, &cell->proc_volumes, _pos); |
ec26815a | 374 | } |
1da177e4 | 375 | |
1da177e4 LT |
376 | /* |
377 | * clean up after reading from the cells list | |
378 | */ | |
379 | static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v) | |
fe342cf7 | 380 | __releases(cell->proc_lock) |
1da177e4 | 381 | { |
353861cf | 382 | struct afs_cell *cell = PDE_DATA(file_inode(p->file)); |
1da177e4 | 383 | |
d2ddc776 | 384 | read_unlock(&cell->proc_lock); |
ec26815a | 385 | } |
1da177e4 | 386 | |
1da177e4 LT |
387 | /* |
388 | * display a header line followed by a load of volume lines | |
389 | */ | |
f0691689 | 390 | static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) |
1da177e4 | 391 | { |
f0691689 | 392 | struct sockaddr_rxrpc *addr = v; |
1da177e4 | 393 | |
f0691689 DH |
394 | /* display header on line 1 */ |
395 | if (v == (void *)1) { | |
396 | seq_puts(m, "ADDRESS\n"); | |
1da177e4 LT |
397 | return 0; |
398 | } | |
399 | ||
f0691689 DH |
400 | /* display one cell per line on subsequent lines */ |
401 | seq_printf(m, "%pISp\n", &addr->transport); | |
1da177e4 | 402 | return 0; |
ec26815a | 403 | } |
1da177e4 | 404 | |
1da177e4 LT |
405 | /* |
406 | * set up the iterator to start reading from the cells list and return the | |
407 | * first item | |
408 | */ | |
409 | static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) | |
fe342cf7 | 410 | __acquires(rcu) |
1da177e4 | 411 | { |
8b2a464c | 412 | struct afs_addr_list *alist; |
353861cf | 413 | struct afs_cell *cell = PDE_DATA(file_inode(m->file)); |
1da177e4 LT |
414 | loff_t pos = *_pos; |
415 | ||
8b2a464c | 416 | rcu_read_lock(); |
1da177e4 | 417 | |
8b2a464c | 418 | alist = rcu_dereference(cell->vl_addrs); |
1da177e4 LT |
419 | |
420 | /* allow for the header line */ | |
421 | if (!pos) | |
422 | return (void *) 1; | |
423 | pos--; | |
424 | ||
8b2a464c | 425 | if (!alist || pos >= alist->nr_addrs) |
1da177e4 LT |
426 | return NULL; |
427 | ||
8b2a464c | 428 | return alist->addrs + pos; |
ec26815a | 429 | } |
1da177e4 | 430 | |
1da177e4 LT |
431 | /* |
432 | * move to next cell in cells list | |
433 | */ | |
434 | static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, | |
435 | loff_t *_pos) | |
436 | { | |
8b2a464c | 437 | struct afs_addr_list *alist; |
353861cf | 438 | struct afs_cell *cell = PDE_DATA(file_inode(p->file)); |
1da177e4 LT |
439 | loff_t pos; |
440 | ||
8b2a464c | 441 | alist = rcu_dereference(cell->vl_addrs); |
1da177e4 LT |
442 | |
443 | pos = *_pos; | |
444 | (*_pos)++; | |
8b2a464c | 445 | if (!alist || pos >= alist->nr_addrs) |
1da177e4 LT |
446 | return NULL; |
447 | ||
8b2a464c | 448 | return alist->addrs + pos; |
ec26815a | 449 | } |
1da177e4 | 450 | |
1da177e4 LT |
451 | /* |
452 | * clean up after reading from the cells list | |
453 | */ | |
454 | static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) | |
fe342cf7 | 455 | __releases(rcu) |
1da177e4 | 456 | { |
8b2a464c | 457 | rcu_read_unlock(); |
ec26815a | 458 | } |
1da177e4 | 459 | |
1da177e4 LT |
460 | /* |
461 | * display a header line followed by a load of volume lines | |
462 | */ | |
f0691689 | 463 | static int afs_proc_servers_show(struct seq_file *m, void *v) |
1da177e4 | 464 | { |
f0691689 DH |
465 | struct afs_server *server; |
466 | struct afs_addr_list *alist; | |
1da177e4 | 467 | |
f0691689 DH |
468 | if (v == SEQ_START_TOKEN) { |
469 | seq_puts(m, "UUID USE ADDR\n"); | |
1da177e4 LT |
470 | return 0; |
471 | } | |
472 | ||
f0691689 DH |
473 | 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); | |
1da177e4 | 479 | return 0; |
ec26815a | 480 | } |
1da177e4 | 481 | |
1da177e4 | 482 | /* |
d2ddc776 DH |
483 | * Set up the iterator to start reading from the server list and return the |
484 | * first item. | |
1da177e4 | 485 | */ |
d2ddc776 | 486 | static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) |
fe342cf7 | 487 | __acquires(rcu) |
1da177e4 | 488 | { |
d2ddc776 | 489 | struct afs_net *net = afs_seq2net(m); |
1da177e4 | 490 | |
d2ddc776 DH |
491 | rcu_read_lock(); |
492 | return seq_hlist_start_head_rcu(&net->fs_proc, *_pos); | |
ec26815a | 493 | } |
1da177e4 | 494 | |
1da177e4 LT |
495 | /* |
496 | * move to next cell in cells list | |
497 | */ | |
d2ddc776 | 498 | static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos) |
1da177e4 | 499 | { |
d2ddc776 | 500 | struct afs_net *net = afs_seq2net(m); |
1da177e4 | 501 | |
d2ddc776 | 502 | return seq_hlist_next_rcu(v, &net->fs_proc, _pos); |
ec26815a | 503 | } |
1da177e4 | 504 | |
1da177e4 LT |
505 | /* |
506 | * clean up after reading from the cells list | |
507 | */ | |
d2ddc776 | 508 | static void afs_proc_servers_stop(struct seq_file *p, void *v) |
fe342cf7 | 509 | __releases(rcu) |
1da177e4 | 510 | { |
d2ddc776 | 511 | rcu_read_unlock(); |
ec26815a | 512 | } |
1da177e4 | 513 | |
6f8880d8 DH |
514 | void 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 | */ | |
531 | static 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 | */ | |
559 | static 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 */ | |
629 | out: | |
630 | inode_unlock(file_inode(file)); | |
631 | kfree(kbuf); | |
632 | return ret; | |
633 | ||
634 | invalid: | |
635 | ret = -EINVAL; | |
636 | error: | |
637 | sysnames->error = ret; | |
638 | goto out; | |
639 | } | |
640 | ||
641 | static 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 | ||
f0691689 DH |
666 | static 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 | ||
6f8880d8 DH |
677 | static 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 | ||
690 | static 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 | ||
701 | static 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 | ||
22ade7e7 DH |
709 | static 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 | ||
d55b4da4 DH |
717 | /* |
718 | * Display general per-net namespace statistics | |
719 | */ | |
720 | static 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 | ||
f3ddee8d | 726 | seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n", |
d55b4da4 DH |
727 | atomic_read(&net->n_lookup), |
728 | atomic_read(&net->n_reval), | |
f3ddee8d DH |
729 | atomic_read(&net->n_inval), |
730 | atomic_read(&net->n_relpg)); | |
d55b4da4 DH |
731 | |
732 | seq_printf(m, "dir-data: rdpg=%u\n", | |
733 | atomic_read(&net->n_read_dir)); | |
63a4681f DH |
734 | |
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)); | |
76a5cb6f DH |
738 | |
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)); | |
d55b4da4 DH |
745 | return 0; |
746 | } | |
10495a00 DH |
747 | |
748 | /* | |
749 | * initialise /proc/fs/afs/<cell>/ | |
750 | */ | |
751 | int 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 | ||
771 | error_tree: | |
772 | remove_proc_subtree(cell->name, net->proc_afs); | |
773 | error_dir: | |
774 | _leave(" = -ENOMEM"); | |
775 | return -ENOMEM; | |
776 | } | |
777 | ||
778 | /* | |
779 | * remove /proc/fs/afs/<cell>/ | |
780 | */ | |
781 | void 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 | */ | |
793 | int 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 | ||
811 | error_tree: | |
812 | proc_remove(net->proc_afs); | |
813 | error_dir: | |
814 | _leave(" = -ENOMEM"); | |
815 | return -ENOMEM; | |
816 | } | |
817 | ||
818 | /* | |
819 | * clean up the /proc/fs/afs/ directory | |
820 | */ | |
821 | void afs_proc_cleanup(struct afs_net *net) | |
822 | { | |
823 | proc_remove(net->proc_afs); | |
824 | net->proc_afs = NULL; | |
825 | } |