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\n");
220 seq_printf(m
, "%3d %08llx %s\n",
221 atomic_read(&vol
->usage
), vol
->vid
,
222 afs_vol_types
[vol
->type
]);
227 static void *afs_proc_cell_volumes_start(struct seq_file
*m
, loff_t
*_pos
)
228 __acquires(cell
->proc_lock
)
230 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
232 read_lock(&cell
->proc_lock
);
233 return seq_list_start_head(&cell
->proc_volumes
, *_pos
);
236 static void *afs_proc_cell_volumes_next(struct seq_file
*m
, void *v
,
239 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
241 return seq_list_next(v
, &cell
->proc_volumes
, _pos
);
244 static void afs_proc_cell_volumes_stop(struct seq_file
*m
, void *v
)
245 __releases(cell
->proc_lock
)
247 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
249 read_unlock(&cell
->proc_lock
);
252 static const struct seq_operations afs_proc_cell_volumes_ops
= {
253 .start
= afs_proc_cell_volumes_start
,
254 .next
= afs_proc_cell_volumes_next
,
255 .stop
= afs_proc_cell_volumes_stop
,
256 .show
= afs_proc_cell_volumes_show
,
259 static const char *const dns_record_sources
[NR__dns_record_source
+ 1] = {
260 [DNS_RECORD_UNAVAILABLE
] = "unav",
261 [DNS_RECORD_FROM_CONFIG
] = "cfg",
262 [DNS_RECORD_FROM_DNS_A
] = "A",
263 [DNS_RECORD_FROM_DNS_AFSDB
] = "AFSDB",
264 [DNS_RECORD_FROM_DNS_SRV
] = "SRV",
265 [DNS_RECORD_FROM_NSS
] = "nss",
266 [NR__dns_record_source
] = "[weird]"
269 static const char *const dns_lookup_statuses
[NR__dns_lookup_status
+ 1] = {
270 [DNS_LOOKUP_NOT_DONE
] = "no-lookup",
271 [DNS_LOOKUP_GOOD
] = "good",
272 [DNS_LOOKUP_GOOD_WITH_BAD
] = "good/bad",
273 [DNS_LOOKUP_BAD
] = "bad",
274 [DNS_LOOKUP_GOT_NOT_FOUND
] = "not-found",
275 [DNS_LOOKUP_GOT_LOCAL_FAILURE
] = "local-failure",
276 [DNS_LOOKUP_GOT_TEMP_FAILURE
] = "temp-failure",
277 [DNS_LOOKUP_GOT_NS_FAILURE
] = "ns-failure",
278 [NR__dns_lookup_status
] = "[weird]"
282 * Display the list of Volume Location servers we're using for a cell.
284 static int afs_proc_cell_vlservers_show(struct seq_file
*m
, void *v
)
286 const struct afs_vl_seq_net_private
*priv
= m
->private;
287 const struct afs_vlserver_list
*vllist
= priv
->vllist
;
288 const struct afs_vlserver_entry
*entry
;
289 const struct afs_vlserver
*vlserver
;
290 const struct afs_addr_list
*alist
;
293 if (v
== SEQ_START_TOKEN
) {
294 seq_printf(m
, "# source %s, status %s\n",
295 dns_record_sources
[vllist
? vllist
->source
: 0],
296 dns_lookup_statuses
[vllist
? vllist
->status
: 0]);
301 vlserver
= entry
->server
;
302 alist
= rcu_dereference(vlserver
->addresses
);
304 seq_printf(m
, "%s [p=%hu w=%hu s=%s,%s]:\n",
305 vlserver
->name
, entry
->priority
, entry
->weight
,
306 dns_record_sources
[alist
? alist
->source
: entry
->source
],
307 dns_lookup_statuses
[alist
? alist
->status
: entry
->status
]);
309 for (i
= 0; i
< alist
->nr_addrs
; i
++)
310 seq_printf(m
, " %c %pISpc\n",
311 alist
->preferred
== i
? '>' : '-',
312 &alist
->addrs
[i
].transport
);
317 static void *afs_proc_cell_vlservers_start(struct seq_file
*m
, loff_t
*_pos
)
320 struct afs_vl_seq_net_private
*priv
= m
->private;
321 struct afs_vlserver_list
*vllist
;
322 struct afs_cell
*cell
= PDE_DATA(file_inode(m
->file
));
327 vllist
= rcu_dereference(cell
->vl_servers
);
328 priv
->vllist
= vllist
;
333 return SEQ_START_TOKEN
;
335 if (pos
- 1 >= vllist
->nr_servers
)
338 return &vllist
->servers
[pos
- 1];
341 static void *afs_proc_cell_vlservers_next(struct seq_file
*m
, void *v
,
344 struct afs_vl_seq_net_private
*priv
= m
->private;
345 struct afs_vlserver_list
*vllist
= priv
->vllist
;
351 if (!vllist
|| pos
- 1 >= vllist
->nr_servers
)
354 return &vllist
->servers
[pos
- 1];
357 static void afs_proc_cell_vlservers_stop(struct seq_file
*m
, void *v
)
363 static const struct seq_operations afs_proc_cell_vlservers_ops
= {
364 .start
= afs_proc_cell_vlservers_start
,
365 .next
= afs_proc_cell_vlservers_next
,
366 .stop
= afs_proc_cell_vlservers_stop
,
367 .show
= afs_proc_cell_vlservers_show
,
371 * Display the list of fileservers we're using within a namespace.
373 static int afs_proc_servers_show(struct seq_file
*m
, void *v
)
375 struct afs_server
*server
;
376 struct afs_addr_list
*alist
;
379 if (v
== SEQ_START_TOKEN
) {
380 seq_puts(m
, "UUID USE ADDR\n");
384 server
= list_entry(v
, struct afs_server
, proc_link
);
385 alist
= rcu_dereference(server
->addresses
);
386 seq_printf(m
, "%pU %3d %pISpc%s\n",
388 atomic_read(&server
->usage
),
389 &alist
->addrs
[0].transport
,
390 alist
->preferred
== 0 ? "*" : "");
391 for (i
= 1; i
< alist
->nr_addrs
; i
++)
392 seq_printf(m
, " %pISpc%s\n",
393 &alist
->addrs
[i
].transport
,
394 alist
->preferred
== i
? "*" : "");
398 static void *afs_proc_servers_start(struct seq_file
*m
, loff_t
*_pos
)
402 return seq_hlist_start_head_rcu(&afs_seq2net(m
)->fs_proc
, *_pos
);
405 static void *afs_proc_servers_next(struct seq_file
*m
, void *v
, loff_t
*_pos
)
407 return seq_hlist_next_rcu(v
, &afs_seq2net(m
)->fs_proc
, _pos
);
410 static void afs_proc_servers_stop(struct seq_file
*m
, void *v
)
416 static const struct seq_operations afs_proc_servers_ops
= {
417 .start
= afs_proc_servers_start
,
418 .next
= afs_proc_servers_next
,
419 .stop
= afs_proc_servers_stop
,
420 .show
= afs_proc_servers_show
,
424 * Display the list of strings that may be substituted for the @sys pathname
427 static int afs_proc_sysname_show(struct seq_file
*m
, void *v
)
429 struct afs_net
*net
= afs_seq2net(m
);
430 struct afs_sysnames
*sysnames
= net
->sysnames
;
431 unsigned int i
= (unsigned long)v
- 1;
433 if (i
< sysnames
->nr
)
434 seq_printf(m
, "%s\n", sysnames
->subs
[i
]);
438 static void *afs_proc_sysname_start(struct seq_file
*m
, loff_t
*pos
)
439 __acquires(&net
->sysnames_lock
)
441 struct afs_net
*net
= afs_seq2net(m
);
442 struct afs_sysnames
*names
;
444 read_lock(&net
->sysnames_lock
);
446 names
= net
->sysnames
;
447 if (*pos
>= names
->nr
)
449 return (void *)(unsigned long)(*pos
+ 1);
452 static void *afs_proc_sysname_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
454 struct afs_net
*net
= afs_seq2net(m
);
455 struct afs_sysnames
*names
= net
->sysnames
;
458 if (*pos
>= names
->nr
)
460 return (void *)(unsigned long)(*pos
+ 1);
463 static void afs_proc_sysname_stop(struct seq_file
*m
, void *v
)
464 __releases(&net
->sysnames_lock
)
466 struct afs_net
*net
= afs_seq2net(m
);
468 read_unlock(&net
->sysnames_lock
);
471 static const struct seq_operations afs_proc_sysname_ops
= {
472 .start
= afs_proc_sysname_start
,
473 .next
= afs_proc_sysname_next
,
474 .stop
= afs_proc_sysname_stop
,
475 .show
= afs_proc_sysname_show
,
479 * Allow the @sys substitution to be configured.
481 static int afs_proc_sysname_write(struct file
*file
, char *buf
, size_t size
)
483 struct afs_sysnames
*sysnames
, *kill
;
484 struct seq_file
*m
= file
->private_data
;
485 struct afs_net
*net
= afs_seq2net(m
);
489 sysnames
= kzalloc(sizeof(*sysnames
), GFP_KERNEL
);
492 refcount_set(&sysnames
->usage
, 1);
496 while ((s
= strsep(&p
, " \t\n"))) {
501 if (len
>= AFSNAMEMAX
)
509 /* Protect against recursion */
513 (len
< 2 || (len
== 2 && s
[1] == '.')))
516 if (memchr(s
, '/', len
))
520 if (sysnames
->nr
>= AFS_NR_SYSNAME
)
523 if (strcmp(s
, afs_init_sysname
) == 0) {
524 sub
= (char *)afs_init_sysname
;
527 sub
= kmemdup(s
, len
+ 1, GFP_KERNEL
);
532 sysnames
->subs
[sysnames
->nr
] = sub
;
536 if (sysnames
->nr
== 0) {
537 sysnames
->subs
[0] = sysnames
->blank
;
541 write_lock(&net
->sysnames_lock
);
542 kill
= net
->sysnames
;
543 net
->sysnames
= sysnames
;
544 write_unlock(&net
->sysnames_lock
);
547 afs_put_sysnames(kill
);
556 void afs_put_sysnames(struct afs_sysnames
*sysnames
)
560 if (sysnames
&& refcount_dec_and_test(&sysnames
->usage
)) {
561 for (i
= 0; i
< sysnames
->nr
; i
++)
562 if (sysnames
->subs
[i
] != afs_init_sysname
&&
563 sysnames
->subs
[i
] != sysnames
->blank
)
564 kfree(sysnames
->subs
[i
]);
569 * Display general per-net namespace statistics
571 static int afs_proc_stats_show(struct seq_file
*m
, void *v
)
573 struct afs_net
*net
= afs_seq2net_single(m
);
575 seq_puts(m
, "kAFS statistics\n");
577 seq_printf(m
, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
578 atomic_read(&net
->n_lookup
),
579 atomic_read(&net
->n_reval
),
580 atomic_read(&net
->n_inval
),
581 atomic_read(&net
->n_relpg
));
583 seq_printf(m
, "dir-data: rdpg=%u\n",
584 atomic_read(&net
->n_read_dir
));
586 seq_printf(m
, "dir-edit: cr=%u rm=%u\n",
587 atomic_read(&net
->n_dir_cr
),
588 atomic_read(&net
->n_dir_rm
));
590 seq_printf(m
, "file-rd : n=%u nb=%lu\n",
591 atomic_read(&net
->n_fetches
),
592 atomic_long_read(&net
->n_fetch_bytes
));
593 seq_printf(m
, "file-wr : n=%u nb=%lu\n",
594 atomic_read(&net
->n_stores
),
595 atomic_long_read(&net
->n_store_bytes
));
600 * initialise /proc/fs/afs/<cell>/
602 int afs_proc_cell_setup(struct afs_cell
*cell
)
604 struct proc_dir_entry
*dir
;
605 struct afs_net
*net
= cell
->net
;
607 _enter("%p{%s},%p", cell
, cell
->name
, net
->proc_afs
);
609 dir
= proc_net_mkdir(net
->net
, cell
->name
, net
->proc_afs
);
613 if (!proc_create_net_data("vlservers", 0444, dir
,
614 &afs_proc_cell_vlservers_ops
,
615 sizeof(struct afs_vl_seq_net_private
),
617 !proc_create_net_data("volumes", 0444, dir
,
618 &afs_proc_cell_volumes_ops
,
619 sizeof(struct seq_net_private
),
627 remove_proc_subtree(cell
->name
, net
->proc_afs
);
629 _leave(" = -ENOMEM");
634 * remove /proc/fs/afs/<cell>/
636 void afs_proc_cell_remove(struct afs_cell
*cell
)
638 struct afs_net
*net
= cell
->net
;
641 remove_proc_subtree(cell
->name
, net
->proc_afs
);
646 * initialise the /proc/fs/afs/ directory
648 int afs_proc_init(struct afs_net
*net
)
650 struct proc_dir_entry
*p
;
654 p
= proc_net_mkdir(net
->net
, "afs", net
->net
->proc_net
);
658 if (!proc_create_net_data_write("cells", 0644, p
,
660 afs_proc_cells_write
,
661 sizeof(struct seq_net_private
),
663 !proc_create_net_single_write("rootcell", 0644, p
,
664 afs_proc_rootcell_show
,
665 afs_proc_rootcell_write
,
667 !proc_create_net("servers", 0444, p
, &afs_proc_servers_ops
,
668 sizeof(struct seq_net_private
)) ||
669 !proc_create_net_single("stats", 0444, p
, afs_proc_stats_show
, NULL
) ||
670 !proc_create_net_data_write("sysname", 0644, p
,
671 &afs_proc_sysname_ops
,
672 afs_proc_sysname_write
,
673 sizeof(struct seq_net_private
),
684 _leave(" = -ENOMEM");
689 * clean up the /proc/fs/afs/ directory
691 void afs_proc_cleanup(struct afs_net
*net
)
693 proc_remove(net
->proc_afs
);
694 net
->proc_afs
= NULL
;