File Index
Files and directories
Sign in
FILE

proc.c

File preview with syntax highlighting.

linux-6.19.14/fs/afs/proc.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* /proc interface for AFS
  3 *
  4 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
  5 * Written by David Howells (dhowells@redhat.com)
  6 */
  7
  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>
 14#include "internal.h"
 15
 16struct afs_vl_seq_net_private {
 17	struct seq_net_private		seq;	/* Must be first */
 18	struct afs_vlserver_list	*vllist;
 19};
 20
 21static inline struct afs_net *afs_seq2net(struct seq_file *m)
 22{
 23	return afs_net(seq_file_net(m));
 24}
 25
 26static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
 27{
 28	return afs_net(seq_file_single_net(m));
 29}
 30
 31/*
 32 * Display the list of cells known to the namespace.
 33 */
 34static int afs_proc_cells_show(struct seq_file *m, void *v)
 35{
 36	struct afs_vlserver_list *vllist;
 37	struct afs_cell *cell;
 38
 39	if (v == SEQ_START_TOKEN) {
 40		/* display header on line 1 */
 41		seq_puts(m, "USE ACT    TTL SV ST NAME\n");
 42		return 0;
 43	}
 44
 45	cell = list_entry(v, struct afs_cell, proc_link);
 46	vllist = rcu_dereference(cell->vl_servers);
 47
 48	/* display one cell per line on subsequent lines */
 49	seq_printf(m, "%3u %3u %6lld %2u %2u %s\n",
 50		   refcount_read(&cell->ref),
 51		   atomic_read(&cell->active),
 52		   cell->dns_expiry - ktime_get_real_seconds(),
 53		   vllist ? vllist->nr_servers : 0,
 54		   cell->state,
 55		   cell->name);
 56	return 0;
 57}
 58
 59static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
 60	__acquires(rcu)
 61{
 62	rcu_read_lock();
 63	return seq_hlist_start_head_rcu(&afs_seq2net(m)->proc_cells, *_pos);
 64}
 65
 66static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
 67{
 68	return seq_hlist_next_rcu(v, &afs_seq2net(m)->proc_cells, pos);
 69}
 70
 71static void afs_proc_cells_stop(struct seq_file *m, void *v)
 72	__releases(rcu)
 73{
 74	rcu_read_unlock();
 75}
 76
 77static const struct seq_operations afs_proc_cells_ops = {
 78	.start	= afs_proc_cells_start,
 79	.next	= afs_proc_cells_next,
 80	.stop	= afs_proc_cells_stop,
 81	.show	= afs_proc_cells_show,
 82};
 83
 84/*
 85 * handle writes to /proc/fs/afs/cells
 86 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
 87 */
 88static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
 89{
 90	struct seq_file *m = file->private_data;
 91	struct afs_net *net = afs_seq2net(m);
 92	char *name, *args;
 93	int ret;
 94
 95	/* trim to first NL */
 96	name = memchr(buf, '\n', size);
 97	if (name)
 98		*name = 0;
 99
100	/* split into command, name and argslist */
101	name = strchr(buf, ' ');
102	if (!name)
103		goto inval;
104	do {
105		*name++ = 0;
106	} while(*name == ' ');
107	if (!*name)
108		goto inval;
109
110	args = strchr(name, ' ');
111	if (args) {
112		do {
113			*args++ = 0;
114		} while(*args == ' ');
115		if (!*args)
116			goto inval;
117	}
118
119	/* determine command to perform */
120	_debug("cmd=%s name=%s args=%s", buf, name, args);
121
122	if (strcmp(buf, "add") == 0) {
123		struct afs_cell *cell;
124
125		cell = afs_lookup_cell(net, name, strlen(name), args,
126				       AFS_LOOKUP_CELL_PRELOAD,
127				       afs_cell_trace_use_lookup_add);
128		if (IS_ERR(cell)) {
129			ret = PTR_ERR(cell);
130			goto done;
131		}
132
133		if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
134			afs_unuse_cell(cell, afs_cell_trace_unuse_no_pin);
135	} else {
136		goto inval;
137	}
138
139	ret = 0;
140
141done:
142	_leave(" = %d", ret);
143	return ret;
144
145inval:
146	ret = -EINVAL;
147	printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
148	goto done;
149}
150
151/*
152 * Display the list of addr_prefs known to the namespace.
153 */
154static int afs_proc_addr_prefs_show(struct seq_file *m, void *v)
155{
156	struct afs_addr_preference_list *preflist;
157	struct afs_addr_preference *pref;
158	struct afs_net *net = afs_seq2net_single(m);
159	union {
160		struct sockaddr_in sin;
161		struct sockaddr_in6 sin6;
162	} addr;
163	unsigned int i;
164	char buf[44]; /* Maximum ipv6 + max subnet is 43 */
165
166	rcu_read_lock();
167	preflist = rcu_dereference(net->address_prefs);
168
169	if (!preflist) {
170		seq_puts(m, "NO PREFS\n");
171		goto out;
172	}
173
174	seq_printf(m, "PROT SUBNET                                      PRIOR (v=%u n=%u/%u/%u)\n",
175		   preflist->version, preflist->ipv6_off, preflist->nr, preflist->max_prefs);
176
177	memset(&addr, 0, sizeof(addr));
178
179	for (i = 0; i < preflist->nr; i++) {
180		pref = &preflist->prefs[i];
181
182		addr.sin.sin_family = pref->family;
183		if (pref->family == AF_INET) {
184			memcpy(&addr.sin.sin_addr, &pref->ipv4_addr,
185			       sizeof(addr.sin.sin_addr));
186			snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin, pref->subnet_mask);
187			seq_printf(m, "UDP  %-43.43s %5u\n", buf, pref->prio);
188		} else {
189			memcpy(&addr.sin6.sin6_addr, &pref->ipv6_addr,
190			       sizeof(addr.sin6.sin6_addr));
191			snprintf(buf, sizeof(buf), "%pISc/%u", &addr.sin6, pref->subnet_mask);
192			seq_printf(m, "UDP  %-43.43s %5u\n", buf, pref->prio);
193		}
194	}
195
196out:
197	rcu_read_unlock();
198	return 0;
199}
200
201/*
202 * Display the name of the current workstation cell.
203 */
204static int afs_proc_rootcell_show(struct seq_file *m, void *v)
205{
206	struct afs_cell *cell;
207	struct afs_net *net;
208
209	net = afs_seq2net_single(m);
210	down_read(&net->cells_lock);
211	cell = rcu_dereference_protected(net->ws_cell, lockdep_is_held(&net->cells_lock));
212	if (cell)
213		seq_printf(m, "%s\n", cell->name);
214	up_read(&net->cells_lock);
215	return 0;
216}
217
218/*
219 * Set the current workstation cell and optionally supply its list of volume
220 * location servers.
221 *
222 *	echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
223 */
224static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
225{
226	struct seq_file *m = file->private_data;
227	struct afs_net *net = afs_seq2net_single(m);
228	char *s;
229	int ret;
230
231	ret = -EINVAL;
232	if (buf[0] == '.')
233		goto out;
234	if (memchr(buf, '/', size))
235		goto out;
236
237	/* trim to first NL */
238	s = memchr(buf, '\n', size);
239	if (s)
240		*s = 0;
241
242	/* determine command to perform */
243	_debug("rootcell=%s", buf);
244
245	ret = -EEXIST;
246	inode_lock(file_inode(file));
247	if (!rcu_access_pointer(net->ws_cell))
248		ret = afs_cell_init(net, buf);
249	else
250		printk("busy\n");
251	inode_unlock(file_inode(file));
252
253out:
254	_leave(" = %d", ret);
255	return ret;
256}
257
258static const char afs_vol_types[3][3] = {
259	[AFSVL_RWVOL]	= "RW",
260	[AFSVL_ROVOL]	= "RO",
261	[AFSVL_BACKVOL]	= "BK",
262};
263
264/*
265 * Display the list of volumes known to a cell.
266 */
267static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
268{
269	struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
270
271	/* Display header on line 1 */
272	if (v == SEQ_START_TOKEN) {
273		seq_puts(m, "USE VID      TY NAME\n");
274		return 0;
275	}
276
277	seq_printf(m, "%3d %08llx %s %s\n",
278		   refcount_read(&vol->ref), vol->vid,
279		   afs_vol_types[vol->type],
280		   vol->name);
281
282	return 0;
283}
284
285static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
286	__acquires(cell->proc_lock)
287{
288	struct afs_cell *cell = pde_data(file_inode(m->file));
289
290	rcu_read_lock();
291	return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
292}
293
294static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
295					loff_t *_pos)
296{
297	struct afs_cell *cell = pde_data(file_inode(m->file));
298
299	return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
300}
301
302static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
303	__releases(cell->proc_lock)
304{
305	rcu_read_unlock();
306}
307
308static const struct seq_operations afs_proc_cell_volumes_ops = {
309	.start	= afs_proc_cell_volumes_start,
310	.next	= afs_proc_cell_volumes_next,
311	.stop	= afs_proc_cell_volumes_stop,
312	.show	= afs_proc_cell_volumes_show,
313};
314
315static const char *const dns_record_sources[NR__dns_record_source + 1] = {
316	[DNS_RECORD_UNAVAILABLE]	= "unav",
317	[DNS_RECORD_FROM_CONFIG]	= "cfg",
318	[DNS_RECORD_FROM_DNS_A]		= "A",
319	[DNS_RECORD_FROM_DNS_AFSDB]	= "AFSDB",
320	[DNS_RECORD_FROM_DNS_SRV]	= "SRV",
321	[DNS_RECORD_FROM_NSS]		= "nss",
322	[NR__dns_record_source]		= "[weird]"
323};
324
325static const char *const dns_lookup_statuses[NR__dns_lookup_status + 1] = {
326	[DNS_LOOKUP_NOT_DONE]		= "no-lookup",
327	[DNS_LOOKUP_GOOD]		= "good",
328	[DNS_LOOKUP_GOOD_WITH_BAD]	= "good/bad",
329	[DNS_LOOKUP_BAD]		= "bad",
330	[DNS_LOOKUP_GOT_NOT_FOUND]	= "not-found",
331	[DNS_LOOKUP_GOT_LOCAL_FAILURE]	= "local-failure",
332	[DNS_LOOKUP_GOT_TEMP_FAILURE]	= "temp-failure",
333	[DNS_LOOKUP_GOT_NS_FAILURE]	= "ns-failure",
334	[NR__dns_lookup_status]		= "[weird]"
335};
336
337/*
338 * Display the list of Volume Location servers we're using for a cell.
339 */
340static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
341{
342	const struct afs_vl_seq_net_private *priv = m->private;
343	const struct afs_vlserver_list *vllist = priv->vllist;
344	const struct afs_vlserver_entry *entry;
345	const struct afs_vlserver *vlserver;
346	const struct afs_addr_list *alist;
347	int i;
348
349	if (v == SEQ_START_TOKEN) {
350		seq_printf(m, "# source %s, status %s\n",
351			   dns_record_sources[vllist ? vllist->source : 0],
352			   dns_lookup_statuses[vllist ? vllist->status : 0]);
353		return 0;
354	}
355
356	entry = v;
357	vlserver = entry->server;
358	alist = rcu_dereference(vlserver->addresses);
359
360	seq_printf(m, "%s [p=%hu w=%hu s=%s,%s]:\n",
361		   vlserver->name, entry->priority, entry->weight,
362		   dns_record_sources[alist ? alist->source : entry->source],
363		   dns_lookup_statuses[alist ? alist->status : entry->status]);
364	if (alist) {
365		for (i = 0; i < alist->nr_addrs; i++)
366			seq_printf(m, " %c %pISpc\n",
367				   alist->preferred == i ? '>' : '-',
368				   rxrpc_kernel_remote_addr(alist->addrs[i].peer));
369	}
370	seq_printf(m, " info: fl=%lx rtt=%d\n", vlserver->flags, vlserver->rtt);
371	seq_printf(m, " probe: fl=%x e=%d ac=%d out=%d\n",
372		   vlserver->probe.flags, vlserver->probe.error,
373		   vlserver->probe.abort_code,
374		   atomic_read(&vlserver->probe_outstanding));
375	return 0;
376}
377
378static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
379	__acquires(rcu)
380{
381	struct afs_vl_seq_net_private *priv = m->private;
382	struct afs_vlserver_list *vllist;
383	struct afs_cell *cell = pde_data(file_inode(m->file));
384	loff_t pos = *_pos;
385
386	rcu_read_lock();
387
388	vllist = rcu_dereference(cell->vl_servers);
389	priv->vllist = vllist;
390
391	if (pos < 0)
392		*_pos = pos = 0;
393	if (pos == 0)
394		return SEQ_START_TOKEN;
395
396	if (pos - 1 >= vllist->nr_servers)
397		return NULL;
398
399	return &vllist->servers[pos - 1];
400}
401
402static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
403					  loff_t *_pos)
404{
405	struct afs_vl_seq_net_private *priv = m->private;
406	struct afs_vlserver_list *vllist = priv->vllist;
407	loff_t pos;
408
409	pos = *_pos;
410	pos++;
411	*_pos = pos;
412	if (!vllist || pos - 1 >= vllist->nr_servers)
413		return NULL;
414
415	return &vllist->servers[pos - 1];
416}
417
418static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
419	__releases(rcu)
420{
421	rcu_read_unlock();
422}
423
424static const struct seq_operations afs_proc_cell_vlservers_ops = {
425	.start	= afs_proc_cell_vlservers_start,
426	.next	= afs_proc_cell_vlservers_next,
427	.stop	= afs_proc_cell_vlservers_stop,
428	.show	= afs_proc_cell_vlservers_show,
429};
430
431/*
432 * Display the list of fileservers we're using within a namespace.
433 */
434static int afs_proc_servers_show(struct seq_file *m, void *v)
435{
436	struct afs_endpoint_state *estate;
437	struct afs_addr_list *alist;
438	struct afs_server *server;
439	unsigned long failed;
440	int i;
441
442	if (v == SEQ_START_TOKEN) {
443		seq_puts(m, "UUID                                 REF ACT CELL\n");
444		return 0;
445	}
446
447	server = list_entry(v, struct afs_server, proc_link);
448	seq_printf(m, "%pU %3d %3d %s\n",
449		   &server->uuid,
450		   refcount_read(&server->ref),
451		   atomic_read(&server->active),
452		   server->cell->name);
453	seq_printf(m, "  - info: fl=%lx rtt=%u\n",
454		   server->flags, server->rtt);
455	seq_printf(m, "  - probe: last=%d\n",
456		   (int)(jiffies - server->probed_at) / HZ);
457
458	estate = rcu_dereference(server->endpoint_state);
459	if (!estate)
460		goto out;
461	failed = estate->failed_set;
462	seq_printf(m, "  - ESTATE pq=%x np=%u rsp=%lx f=%lx\n",
463		   estate->probe_seq, atomic_read(&estate->nr_probing),
464		   estate->responsive_set, estate->failed_set);
465
466	alist = estate->addresses;
467	seq_printf(m, "  - ALIST v=%u ap=%u\n",
468		   alist->version, alist->addr_pref_version);
469	for (i = 0; i < alist->nr_addrs; i++) {
470		const struct afs_address *addr = &alist->addrs[i];
471
472		seq_printf(m, "    [%x] %pISpc%s rtt=%d err=%d p=%u\n",
473			   i, rxrpc_kernel_remote_addr(addr->peer),
474			   alist->preferred == i ? "*" :
475			   test_bit(i, &failed) ? "!" : "",
476			   rxrpc_kernel_get_srtt(addr->peer),
477			   addr->last_error, addr->prio);
478	}
479
480out:
481	return 0;
482}
483
484static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
485	__acquires(rcu)
486{
487	rcu_read_lock();
488	return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
489}
490
491static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
492{
493	return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
494}
495
496static void afs_proc_servers_stop(struct seq_file *m, void *v)
497	__releases(rcu)
498{
499	rcu_read_unlock();
500}
501
502static const struct seq_operations afs_proc_servers_ops = {
503	.start	= afs_proc_servers_start,
504	.next	= afs_proc_servers_next,
505	.stop	= afs_proc_servers_stop,
506	.show	= afs_proc_servers_show,
507};
508
509/*
510 * Display the list of strings that may be substituted for the @sys pathname
511 * macro.
512 */
513static int afs_proc_sysname_show(struct seq_file *m, void *v)
514{
515	struct afs_net *net = afs_seq2net(m);
516	struct afs_sysnames *sysnames = net->sysnames;
517	unsigned int i = (unsigned long)v - 1;
518
519	if (i < sysnames->nr)
520		seq_printf(m, "%s\n", sysnames->subs[i]);
521	return 0;
522}
523
524static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
525	__acquires(&net->sysnames_lock)
526{
527	struct afs_net *net = afs_seq2net(m);
528	struct afs_sysnames *names;
529
530	read_lock(&net->sysnames_lock);
531
532	names = net->sysnames;
533	if (*pos >= names->nr)
534		return NULL;
535	return (void *)(unsigned long)(*pos + 1);
536}
537
538static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
539{
540	struct afs_net *net = afs_seq2net(m);
541	struct afs_sysnames *names = net->sysnames;
542
543	*pos += 1;
544	if (*pos >= names->nr)
545		return NULL;
546	return (void *)(unsigned long)(*pos + 1);
547}
548
549static void afs_proc_sysname_stop(struct seq_file *m, void *v)
550	__releases(&net->sysnames_lock)
551{
552	struct afs_net *net = afs_seq2net(m);
553
554	read_unlock(&net->sysnames_lock);
555}
556
557static const struct seq_operations afs_proc_sysname_ops = {
558	.start	= afs_proc_sysname_start,
559	.next	= afs_proc_sysname_next,
560	.stop	= afs_proc_sysname_stop,
561	.show	= afs_proc_sysname_show,
562};
563
564/*
565 * Allow the @sys substitution to be configured.
566 */
567static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
568{
569	struct afs_sysnames *sysnames, *kill;
570	struct seq_file *m = file->private_data;
571	struct afs_net *net = afs_seq2net(m);
572	char *s, *p, *sub;
573	int ret, len;
574
575	sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
576	if (!sysnames)
577		return -ENOMEM;
578	refcount_set(&sysnames->usage, 1);
579	kill = sysnames;
580
581	p = buf;
582	while ((s = strsep(&p, " \t\n"))) {
583		len = strlen(s);
584		if (len == 0)
585			continue;
586		ret = -ENAMETOOLONG;
587		if (len >= AFSNAMEMAX)
588			goto error;
589
590		if (len >= 4 &&
591		    s[len - 4] == '@' &&
592		    s[len - 3] == 's' &&
593		    s[len - 2] == 'y' &&
594		    s[len - 1] == 's')
595			/* Protect against recursion */
596			goto invalid;
597
598		if (s[0] == '.' &&
599		    (len < 2 || (len == 2 && s[1] == '.')))
600			goto invalid;
601
602		if (memchr(s, '/', len))
603			goto invalid;
604
605		ret = -EFBIG;
606		if (sysnames->nr >= AFS_NR_SYSNAME)
607			goto out;
608
609		if (strcmp(s, afs_init_sysname) == 0) {
610			sub = (char *)afs_init_sysname;
611		} else {
612			ret = -ENOMEM;
613			sub = kmemdup(s, len + 1, GFP_KERNEL);
614			if (!sub)
615				goto out;
616		}
617
618		sysnames->subs[sysnames->nr] = sub;
619		sysnames->nr++;
620	}
621
622	if (sysnames->nr == 0) {
623		sysnames->subs[0] = sysnames->blank;
624		sysnames->nr++;
625	}
626
627	write_lock(&net->sysnames_lock);
628	kill = net->sysnames;
629	net->sysnames = sysnames;
630	write_unlock(&net->sysnames_lock);
631	ret = 0;
632out:
633	afs_put_sysnames(kill);
634	return ret;
635
636invalid:
637	ret = -EINVAL;
638error:
639	goto out;
640}
641
642void afs_put_sysnames(struct afs_sysnames *sysnames)
643{
644	int i;
645
646	if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
647		for (i = 0; i < sysnames->nr; i++)
648			if (sysnames->subs[i] != afs_init_sysname &&
649			    sysnames->subs[i] != sysnames->blank)
650				kfree(sysnames->subs[i]);
651		kfree(sysnames);
652	}
653}
654
655/*
656 * Display general per-net namespace statistics
657 */
658static int afs_proc_stats_show(struct seq_file *m, void *v)
659{
660	struct afs_net *net = afs_seq2net_single(m);
661
662	seq_puts(m, "kAFS statistics\n");
663
664	seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
665		   atomic_read(&net->n_lookup),
666		   atomic_read(&net->n_reval),
667		   atomic_read(&net->n_inval),
668		   atomic_read(&net->n_relpg));
669
670	seq_printf(m, "dir-data: rdpg=%u\n",
671		   atomic_read(&net->n_read_dir));
672
673	seq_printf(m, "dir-edit: cr=%u rm=%u\n",
674		   atomic_read(&net->n_dir_cr),
675		   atomic_read(&net->n_dir_rm));
676
677	seq_printf(m, "file-rd : n=%u nb=%lu\n",
678		   atomic_read(&net->n_fetches),
679		   atomic_long_read(&net->n_fetch_bytes));
680	seq_printf(m, "file-wr : n=%u nb=%lu\n",
681		   atomic_read(&net->n_stores),
682		   atomic_long_read(&net->n_store_bytes));
683	return 0;
684}
685
686/*
687 * initialise /proc/fs/afs/<cell>/
688 */
689int afs_proc_cell_setup(struct afs_cell *cell)
690{
691	struct proc_dir_entry *dir;
692	struct afs_net *net = cell->net;
693
694	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
695
696	dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
697	if (!dir)
698		goto error_dir;
699
700	if (!proc_create_net_data("vlservers", 0444, dir,
701				  &afs_proc_cell_vlservers_ops,
702				  sizeof(struct afs_vl_seq_net_private),
703				  cell) ||
704	    !proc_create_net_data("volumes", 0444, dir,
705				  &afs_proc_cell_volumes_ops,
706				  sizeof(struct seq_net_private),
707				  cell))
708		goto error_tree;
709
710	_leave(" = 0");
711	return 0;
712
713error_tree:
714	remove_proc_subtree(cell->name, net->proc_afs);
715error_dir:
716	_leave(" = -ENOMEM");
717	return -ENOMEM;
718}
719
720/*
721 * remove /proc/fs/afs/<cell>/
722 */
723void afs_proc_cell_remove(struct afs_cell *cell)
724{
725	struct afs_net *net = cell->net;
726
727	_enter("");
728	remove_proc_subtree(cell->name, net->proc_afs);
729	_leave("");
730}
731
732/*
733 * initialise the /proc/fs/afs/ directory
734 */
735int afs_proc_init(struct afs_net *net)
736{
737	struct proc_dir_entry *p;
738
739	_enter("");
740
741	p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
742	if (!p)
743		goto error_dir;
744
745	if (!proc_create_net_data_write("cells", 0644, p,
746					&afs_proc_cells_ops,
747					afs_proc_cells_write,
748					sizeof(struct seq_net_private),
749					NULL) ||
750	    !proc_create_net_single_write("rootcell", 0644, p,
751					  afs_proc_rootcell_show,
752					  afs_proc_rootcell_write,
753					  NULL) ||
754	    !proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
755			     sizeof(struct seq_net_private)) ||
756	    !proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
757	    !proc_create_net_data_write("sysname", 0644, p,
758					&afs_proc_sysname_ops,
759					afs_proc_sysname_write,
760					sizeof(struct seq_net_private),
761					NULL) ||
762	    !proc_create_net_single_write("addr_prefs", 0644, p,
763					  afs_proc_addr_prefs_show,
764					  afs_proc_addr_prefs_write,
765					  NULL))
766		goto error_tree;
767
768	net->proc_afs = p;
769	_leave(" = 0");
770	return 0;
771
772error_tree:
773	proc_remove(p);
774error_dir:
775	_leave(" = -ENOMEM");
776	return -ENOMEM;
777}
778
779/*
780 * clean up the /proc/fs/afs/ directory
781 */
782void afs_proc_cleanup(struct afs_net *net)
783{
784	proc_remove(net->proc_afs);
785	net->proc_afs = NULL;
786}
~/ Read-only file preview.