1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* /proc interface for AFS
4 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
8 #include <linux/slab.h>
9 #include <linux/module.h>
10 #include <linux/proc_fs.h>
11 #include <linux/seq_file.h>
12 #include <linux/sched.h>
13 #include <linux/uaccess.h>
16 struct afs_vl_seq_net_private
{
17 struct seq_net_private seq
; /* Must be first */
18 struct afs_vlserver_list
*vllist
;
21 static inline struct afs_net
*afs_seq2net(struct seq_file
*m
)
23 return afs_net(seq_file_net(m
));
26 static inline struct afs_net
*afs_seq2net_single(struct seq_file
*m
)
28 return afs_net(seq_file_single_net(m
));
32 * Display the list of cells known to the namespace.
34 static int afs_proc_cells_show(struct seq_file
*m
, void *v
)
36 struct afs_vlserver_list
*vllist
;
37 struct afs_cell
*cell
;
39 if (v
== SEQ_START_TOKEN
) {
40 /* display header on line 1 */
41 seq_puts(m
, "USE TTL SV NAME\n");
45 cell
= list_entry(v
, struct afs_cell
, proc_link
);
46 vllist
= rcu_dereference(cell
->vl_servers
);
48 /* display one cell per line on subsequent lines */
49 seq_printf(m
, "%3u %6lld %2u %s\n",
50 atomic_read(&cell
->usage
),
51 cell
->dns_expiry
- ktime_get_real_seconds(),
57 static void *afs_proc_cells_start(struct seq_file
*m
, loff_t
*_pos
)
61 return seq_hlist_start_head_rcu(&afs_seq2net(m
)->proc_cells
, *_pos
);
64 static void *afs_proc_cells_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
66 return seq_hlist_next_rcu(v
, &afs_seq2net(m
)->proc_cells
, pos
);
69 static void afs_proc_cells_stop(struct seq_file
*m
, void *v
)
75 static const struct seq_operations afs_proc_cells_ops
= {
76 .start
= afs_proc_cells_start
,
77 .next
= afs_proc_cells_next
,
78 .stop
= afs_proc_cells_stop
,
79 .show
= afs_proc_cells_show
,
83 * handle writes to /proc/fs/afs/cells
84 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
86 static int afs_proc_cells_write(struct file
*file
, char *buf
, size_t size
)
88 struct seq_file
*m
= file
->private_data
;
89 struct afs_net
*net
= afs_seq2net(m
);
93 /* trim to first NL */
94 name
= memchr(buf
, '\n', size
);
98 /* split into command, name and argslist */
99 name
= strchr(buf
, ' ');
104 } while(*name
== ' ');
108 args
= strchr(name
, ' ');
112 } while(*args
== ' ');
117 /* determine command to perform */
118 _debug("cmd=%s name=%s args=%s", buf
, name
, args
);
120 if (strcmp(buf
, "add") == 0) {
121 struct afs_cell
*cell
;
123 cell
= afs_lookup_cell(net
, name
, strlen(name
), args
, true);
129 if (test_and_set_bit(AFS_CELL_FL_NO_GC
, &cell
->flags
))
130 afs_put_cell(net
, cell
);
138 _leave(" = %d", ret
);
143 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
148 * Display the name of the current workstation cell.
150 static int afs_proc_rootcell_show(struct seq_file
*m
, void *v
)
152 struct afs_cell
*cell
;
155 net
= afs_seq2net_single(m
);
156 if (rcu_access_pointer(net
->ws_cell
)) {
158 cell
= rcu_dereference(net
->ws_cell
);
160 seq_printf(m
, "%s\n", cell
->name
);
167 * Set the current workstation cell and optionally supply its list of volume
170 * echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
172 static int afs_proc_rootcell_write(struct file
*file
, char *buf
, size_t size
)
174 struct seq_file
*m
= file
->private_data
;
175 struct afs_net
*net
= afs_seq2net_single(m
);
182 if (memchr(buf
, '/', size
))
185 /* trim to first NL */
186 s
= memchr(buf
, '\n', size
);
190 /* determine command to perform */
191 _debug("rootcell=%s", buf
);
193 ret
= afs_cell_init(net
, buf
);
196 _leave(" = %d", ret
);
200 static const char afs_vol_types
[3][3] = {
201 [AFSVL_RWVOL
] = "RW",
202 [AFSVL_ROVOL
] = "RO",
203 [AFSVL_BACKVOL
] = "BK",
207 * Display the list of volumes known to a cell.
209 static int afs_proc_cell_volumes_show(struct seq_file
*m
, void *v
)
211 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
212 struct afs_volume
*vol
= list_entry(v
, struct afs_volume
, proc_link
);
214 /* Display header on line 1 */
215 if (v
== &cell
->proc_volumes
) {
216 seq_puts(m
, "USE VID TY NAME\n");
220 seq_printf(m
, "%3d %08llx %s %s\n",
221 atomic_read(&vol
->usage
), vol
->vid
,
222 afs_vol_types
[vol
->type
],
228 static void *afs_proc_cell_volumes_start(struct seq_file
*m
, loff_t
*_pos
)
229 __acquires(cell
->proc_lock
)
231 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
233 read_lock(&cell
->proc_lock
);
234 return seq_list_start_head(&cell
->proc_volumes
, *_pos
);
237 static void *afs_proc_cell_volumes_next(struct seq_file
*m
, void *v
,
240 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
242 return seq_list_next(v
, &cell
->proc_volumes
, _pos
);
245 static void afs_proc_cell_volumes_stop(struct seq_file
*m
, void *v
)
246 __releases(cell
->proc_lock
)
248 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
250 read_unlock(&cell
->proc_lock
);
253 static const struct seq_operations afs_proc_cell_volumes_ops
= {
254 .start
= afs_proc_cell_volumes_start
,
255 .next
= afs_proc_cell_volumes_next
,
256 .stop
= afs_proc_cell_volumes_stop
,
257 .show
= afs_proc_cell_volumes_show
,
260 static const char *const dns_record_sources
[NR__dns_record_source
+ 1] = {
261 [DNS_RECORD_UNAVAILABLE
] = "unav",
262 [DNS_RECORD_FROM_CONFIG
] = "cfg",
263 [DNS_RECORD_FROM_DNS_A
] = "A",
264 [DNS_RECORD_FROM_DNS_AFSDB
] = "AFSDB",
265 [DNS_RECORD_FROM_DNS_SRV
] = "SRV",
266 [DNS_RECORD_FROM_NSS
] = "nss",
267 [NR__dns_record_source
] = "[weird]"
270 static const char *const dns_lookup_statuses
[NR__dns_lookup_status
+ 1] = {
271 [DNS_LOOKUP_NOT_DONE
] = "no-lookup",
272 [DNS_LOOKUP_GOOD
] = "good",
273 [DNS_LOOKUP_GOOD_WITH_BAD
] = "good/bad",
274 [DNS_LOOKUP_BAD
] = "bad",
275 [DNS_LOOKUP_GOT_NOT_FOUND
] = "not-found",
276 [DNS_LOOKUP_GOT_LOCAL_FAILURE
] = "local-failure",
277 [DNS_LOOKUP_GOT_TEMP_FAILURE
] = "temp-failure",
278 [DNS_LOOKUP_GOT_NS_FAILURE
] = "ns-failure",
279 [NR__dns_lookup_status
] = "[weird]"
283 * Display the list of Volume Location servers we're using for a cell.
285 static int afs_proc_cell_vlservers_show(struct seq_file
*m
, void *v
)
287 const struct afs_vl_seq_net_private
*priv
= m
->private;
288 const struct afs_vlserver_list
*vllist
= priv
->vllist
;
289 const struct afs_vlserver_entry
*entry
;
290 const struct afs_vlserver
*vlserver
;
291 const struct afs_addr_list
*alist
;
294 if (v
== SEQ_START_TOKEN
) {
295 seq_printf(m
, "# source %s, status %s\n",
296 dns_record_sources
[vllist
? vllist
->source
: 0],
297 dns_lookup_statuses
[vllist
? vllist
->status
: 0]);
302 vlserver
= entry
->server
;
303 alist
= rcu_dereference(vlserver
->addresses
);
305 seq_printf(m
, "%s [p=%hu w=%hu s=%s,%s]:\n",
306 vlserver
->name
, entry
->priority
, entry
->weight
,
307 dns_record_sources
[alist
? alist
->source
: entry
->source
],
308 dns_lookup_statuses
[alist
? alist
->status
: entry
->status
]);
310 for (i
= 0; i
< alist
->nr_addrs
; i
++)
311 seq_printf(m
, " %c %pISpc\n",
312 alist
->preferred
== i
? '>' : '-',
313 &alist
->addrs
[i
].transport
);
318 static void *afs_proc_cell_vlservers_start(struct seq_file
*m
, loff_t
*_pos
)
321 struct afs_vl_seq_net_private
*priv
= m
->private;
322 struct afs_vlserver_list
*vllist
;
323 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
328 vllist
= rcu_dereference(cell
->vl_servers
);
329 priv
->vllist
= vllist
;
334 return SEQ_START_TOKEN
;
336 if (pos
- 1 >= vllist
->nr_servers
)
339 return &vllist
->servers
[pos
- 1];
342 static void *afs_proc_cell_vlservers_next(struct seq_file
*m
, void *v
,
345 struct afs_vl_seq_net_private
*priv
= m
->private;
346 struct afs_vlserver_list
*vllist
= priv
->vllist
;
352 if (!vllist
|| pos
- 1 >= vllist
->nr_servers
)
355 return &vllist
->servers
[pos
- 1];
358 static void afs_proc_cell_vlservers_stop(struct seq_file
*m
, void *v
)
364 static const struct seq_operations afs_proc_cell_vlservers_ops
= {
365 .start
= afs_proc_cell_vlservers_start
,
366 .next
= afs_proc_cell_vlservers_next
,
367 .stop
= afs_proc_cell_vlservers_stop
,
368 .show
= afs_proc_cell_vlservers_show
,
372 * Display the list of fileservers we're using within a namespace.
374 static int afs_proc_servers_show(struct seq_file
*m
, void *v
)
376 struct afs_server
*server
;
377 struct afs_addr_list
*alist
;
380 if (v
== SEQ_START_TOKEN
) {
381 seq_puts(m
, "UUID USE ADDR\n");
385 server
= list_entry(v
, struct afs_server
, proc_link
);
386 alist
= rcu_dereference(server
->addresses
);
387 seq_printf(m
, "%pU %3d %pISpc%s\n",
389 atomic_read(&server
->usage
),
390 &alist
->addrs
[0].transport
,
391 alist
->preferred
== 0 ? "*" : "");
392 for (i
= 1; i
< alist
->nr_addrs
; i
++)
393 seq_printf(m
, " %pISpc%s\n",
394 &alist
->addrs
[i
].transport
,
395 alist
->preferred
== i
? "*" : "");
399 static void *afs_proc_servers_start(struct seq_file
*m
, loff_t
*_pos
)
403 return seq_hlist_start_head_rcu(&afs_seq2net(m
)->fs_proc
, *_pos
);
406 static void *afs_proc_servers_next(struct seq_file
*m
, void *v
, loff_t
*_pos
)
408 return seq_hlist_next_rcu(v
, &afs_seq2net(m
)->fs_proc
, _pos
);
411 static void afs_proc_servers_stop(struct seq_file
*m
, void *v
)
417 static const struct seq_operations afs_proc_servers_ops
= {
418 .start
= afs_proc_servers_start
,
419 .next
= afs_proc_servers_next
,
420 .stop
= afs_proc_servers_stop
,
421 .show
= afs_proc_servers_show
,
425 * Display the list of strings that may be substituted for the @sys pathname
428 static int afs_proc_sysname_show(struct seq_file
*m
, void *v
)
430 struct afs_net
*net
= afs_seq2net(m
);
431 struct afs_sysnames
*sysnames
= net
->sysnames
;
432 unsigned int i
= (unsigned long)v
- 1;
434 if (i
< sysnames
->nr
)
435 seq_printf(m
, "%s\n", sysnames
->subs
[i
]);
439 static void *afs_proc_sysname_start(struct seq_file
*m
, loff_t
*pos
)
440 __acquires(&net
->sysnames_lock
)
442 struct afs_net
*net
= afs_seq2net(m
);
443 struct afs_sysnames
*names
;
445 read_lock(&net
->sysnames_lock
);
447 names
= net
->sysnames
;
448 if (*pos
>= names
->nr
)
450 return (void *)(unsigned long)(*pos
+ 1);
453 static void *afs_proc_sysname_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
455 struct afs_net
*net
= afs_seq2net(m
);
456 struct afs_sysnames
*names
= net
->sysnames
;
459 if (*pos
>= names
->nr
)
461 return (void *)(unsigned long)(*pos
+ 1);
464 static void afs_proc_sysname_stop(struct seq_file
*m
, void *v
)
465 __releases(&net
->sysnames_lock
)
467 struct afs_net
*net
= afs_seq2net(m
);
469 read_unlock(&net
->sysnames_lock
);
472 static const struct seq_operations afs_proc_sysname_ops
= {
473 .start
= afs_proc_sysname_start
,
474 .next
= afs_proc_sysname_next
,
475 .stop
= afs_proc_sysname_stop
,
476 .show
= afs_proc_sysname_show
,
480 * Allow the @sys substitution to be configured.
482 static int afs_proc_sysname_write(struct file
*file
, char *buf
, size_t size
)
484 struct afs_sysnames
*sysnames
, *kill
;
485 struct seq_file
*m
= file
->private_data
;
486 struct afs_net
*net
= afs_seq2net(m
);
490 sysnames
= kzalloc(sizeof(*sysnames
), GFP_KERNEL
);
493 refcount_set(&sysnames
->usage
, 1);
497 while ((s
= strsep(&p
, " \t\n"))) {
502 if (len
>= AFSNAMEMAX
)
510 /* Protect against recursion */
514 (len
< 2 || (len
== 2 && s
[1] == '.')))
517 if (memchr(s
, '/', len
))
521 if (sysnames
->nr
>= AFS_NR_SYSNAME
)
524 if (strcmp(s
, afs_init_sysname
) == 0) {
525 sub
= (char *)afs_init_sysname
;
528 sub
= kmemdup(s
, len
+ 1, GFP_KERNEL
);
533 sysnames
->subs
[sysnames
->nr
] = sub
;
537 if (sysnames
->nr
== 0) {
538 sysnames
->subs
[0] = sysnames
->blank
;
542 write_lock(&net
->sysnames_lock
);
543 kill
= net
->sysnames
;
544 net
->sysnames
= sysnames
;
545 write_unlock(&net
->sysnames_lock
);
548 afs_put_sysnames(kill
);
557 void afs_put_sysnames(struct afs_sysnames
*sysnames
)
561 if (sysnames
&& refcount_dec_and_test(&sysnames
->usage
)) {
562 for (i
= 0; i
< sysnames
->nr
; i
++)
563 if (sysnames
->subs
[i
] != afs_init_sysname
&&
564 sysnames
->subs
[i
] != sysnames
->blank
)
565 kfree(sysnames
->subs
[i
]);
570 * Display general per-net namespace statistics
572 static int afs_proc_stats_show(struct seq_file
*m
, void *v
)
574 struct afs_net
*net
= afs_seq2net_single(m
);
576 seq_puts(m
, "kAFS statistics\n");
578 seq_printf(m
, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
579 atomic_read(&net
->n_lookup
),
580 atomic_read(&net
->n_reval
),
581 atomic_read(&net
->n_inval
),
582 atomic_read(&net
->n_relpg
));
584 seq_printf(m
, "dir-data: rdpg=%u\n",
585 atomic_read(&net
->n_read_dir
));
587 seq_printf(m
, "dir-edit: cr=%u rm=%u\n",
588 atomic_read(&net
->n_dir_cr
),
589 atomic_read(&net
->n_dir_rm
));
591 seq_printf(m
, "file-rd : n=%u nb=%lu\n",
592 atomic_read(&net
->n_fetches
),
593 atomic_long_read(&net
->n_fetch_bytes
));
594 seq_printf(m
, "file-wr : n=%u nb=%lu\n",
595 atomic_read(&net
->n_stores
),
596 atomic_long_read(&net
->n_store_bytes
));
601 * initialise /proc/fs/afs/<cell>/
603 int afs_proc_cell_setup(struct afs_cell
*cell
)
605 struct proc_dir_entry
*dir
;
606 struct afs_net
*net
= cell
->net
;
608 _enter("%p{%s},%p", cell
, cell
->name
, net
->proc_afs
);
610 dir
= proc_net_mkdir(net
->net
, cell
->name
, net
->proc_afs
);
614 if (!proc_create_net_data("vlservers", 0444, dir
,
615 &afs_proc_cell_vlservers_ops
,
616 sizeof(struct afs_vl_seq_net_private
),
618 !proc_create_net_data("volumes", 0444, dir
,
619 &afs_proc_cell_volumes_ops
,
620 sizeof(struct seq_net_private
),
628 remove_proc_subtree(cell
->name
, net
->proc_afs
);
630 _leave(" = -ENOMEM");
635 * remove /proc/fs/afs/<cell>/
637 void afs_proc_cell_remove(struct afs_cell
*cell
)
639 struct afs_net
*net
= cell
->net
;
642 remove_proc_subtree(cell
->name
, net
->proc_afs
);
647 * initialise the /proc/fs/afs/ directory
649 int afs_proc_init(struct afs_net
*net
)
651 struct proc_dir_entry
*p
;
655 p
= proc_net_mkdir(net
->net
, "afs", net
->net
->proc_net
);
659 if (!proc_create_net_data_write("cells", 0644, p
,
661 afs_proc_cells_write
,
662 sizeof(struct seq_net_private
),
664 !proc_create_net_single_write("rootcell", 0644, p
,
665 afs_proc_rootcell_show
,
666 afs_proc_rootcell_write
,
668 !proc_create_net("servers", 0444, p
, &afs_proc_servers_ops
,
669 sizeof(struct seq_net_private
)) ||
670 !proc_create_net_single("stats", 0444, p
, afs_proc_stats_show
, NULL
) ||
671 !proc_create_net_data_write("sysname", 0644, p
,
672 &afs_proc_sysname_ops
,
673 afs_proc_sysname_write
,
674 sizeof(struct seq_net_private
),
685 _leave(" = -ENOMEM");
690 * clean up the /proc/fs/afs/ directory
692 void afs_proc_cleanup(struct afs_net
*net
)
694 proc_remove(net
->proc_afs
);
695 net
->proc_afs
= NULL
;