xref: /openbmc/linux/fs/nfsd/nfsctl.c (revision 64c70b1c)
1 /*
2  * linux/fs/nfsd/nfsctl.c
3  *
4  * Syscall interface to knfsd.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8 
9 #include <linux/module.h>
10 
11 #include <linux/linkage.h>
12 #include <linux/time.h>
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/fcntl.h>
16 #include <linux/net.h>
17 #include <linux/in.h>
18 #include <linux/syscalls.h>
19 #include <linux/unistd.h>
20 #include <linux/slab.h>
21 #include <linux/proc_fs.h>
22 #include <linux/seq_file.h>
23 #include <linux/pagemap.h>
24 #include <linux/init.h>
25 #include <linux/string.h>
26 #include <linux/smp_lock.h>
27 #include <linux/ctype.h>
28 
29 #include <linux/nfs.h>
30 #include <linux/nfsd_idmap.h>
31 #include <linux/lockd/bind.h>
32 #include <linux/sunrpc/svc.h>
33 #include <linux/sunrpc/svcsock.h>
34 #include <linux/nfsd/nfsd.h>
35 #include <linux/nfsd/cache.h>
36 #include <linux/nfsd/xdr.h>
37 #include <linux/nfsd/syscall.h>
38 #include <linux/nfsd/interface.h>
39 
40 #include <asm/uaccess.h>
41 
42 /*
43  *	We have a single directory with 9 nodes in it.
44  */
45 enum {
46 	NFSD_Root = 1,
47 	NFSD_Svc,
48 	NFSD_Add,
49 	NFSD_Del,
50 	NFSD_Export,
51 	NFSD_Unexport,
52 	NFSD_Getfd,
53 	NFSD_Getfs,
54 	NFSD_List,
55 	NFSD_Fh,
56 	NFSD_Threads,
57 	NFSD_Pool_Threads,
58 	NFSD_Versions,
59 	NFSD_Ports,
60 	NFSD_MaxBlkSize,
61 	/*
62 	 * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
63 	 * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
64 	 */
65 #ifdef CONFIG_NFSD_V4
66 	NFSD_Leasetime,
67 	NFSD_RecoveryDir,
68 #endif
69 };
70 
71 /*
72  * write() for these nodes.
73  */
74 static ssize_t write_svc(struct file *file, char *buf, size_t size);
75 static ssize_t write_add(struct file *file, char *buf, size_t size);
76 static ssize_t write_del(struct file *file, char *buf, size_t size);
77 static ssize_t write_export(struct file *file, char *buf, size_t size);
78 static ssize_t write_unexport(struct file *file, char *buf, size_t size);
79 static ssize_t write_getfd(struct file *file, char *buf, size_t size);
80 static ssize_t write_getfs(struct file *file, char *buf, size_t size);
81 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
82 static ssize_t write_threads(struct file *file, char *buf, size_t size);
83 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
84 static ssize_t write_versions(struct file *file, char *buf, size_t size);
85 static ssize_t write_ports(struct file *file, char *buf, size_t size);
86 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
87 #ifdef CONFIG_NFSD_V4
88 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
89 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
90 #endif
91 
92 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
93 	[NFSD_Svc] = write_svc,
94 	[NFSD_Add] = write_add,
95 	[NFSD_Del] = write_del,
96 	[NFSD_Export] = write_export,
97 	[NFSD_Unexport] = write_unexport,
98 	[NFSD_Getfd] = write_getfd,
99 	[NFSD_Getfs] = write_getfs,
100 	[NFSD_Fh] = write_filehandle,
101 	[NFSD_Threads] = write_threads,
102 	[NFSD_Pool_Threads] = write_pool_threads,
103 	[NFSD_Versions] = write_versions,
104 	[NFSD_Ports] = write_ports,
105 	[NFSD_MaxBlkSize] = write_maxblksize,
106 #ifdef CONFIG_NFSD_V4
107 	[NFSD_Leasetime] = write_leasetime,
108 	[NFSD_RecoveryDir] = write_recoverydir,
109 #endif
110 };
111 
112 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
113 {
114 	ino_t ino =  file->f_path.dentry->d_inode->i_ino;
115 	char *data;
116 	ssize_t rv;
117 
118 	if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
119 		return -EINVAL;
120 
121 	data = simple_transaction_get(file, buf, size);
122 	if (IS_ERR(data))
123 		return PTR_ERR(data);
124 
125 	rv =  write_op[ino](file, data, size);
126 	if (rv >= 0) {
127 		simple_transaction_set(file, rv);
128 		rv = size;
129 	}
130 	return rv;
131 }
132 
133 static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
134 {
135 	if (! file->private_data) {
136 		/* An attempt to read a transaction file without writing
137 		 * causes a 0-byte write so that the file can return
138 		 * state information
139 		 */
140 		ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
141 		if (rv < 0)
142 			return rv;
143 	}
144 	return simple_transaction_read(file, buf, size, pos);
145 }
146 
147 static const struct file_operations transaction_ops = {
148 	.write		= nfsctl_transaction_write,
149 	.read		= nfsctl_transaction_read,
150 	.release	= simple_transaction_release,
151 };
152 
153 extern struct seq_operations nfs_exports_op;
154 static int exports_open(struct inode *inode, struct file *file)
155 {
156 	return seq_open(file, &nfs_exports_op);
157 }
158 
159 static const struct file_operations exports_operations = {
160 	.open		= exports_open,
161 	.read		= seq_read,
162 	.llseek		= seq_lseek,
163 	.release	= seq_release,
164 };
165 
166 /*----------------------------------------------------------------------------*/
167 /*
168  * payload - write methods
169  * If the method has a response, the response should be put in buf,
170  * and the length returned.  Otherwise return 0 or and -error.
171  */
172 
173 static ssize_t write_svc(struct file *file, char *buf, size_t size)
174 {
175 	struct nfsctl_svc *data;
176 	if (size < sizeof(*data))
177 		return -EINVAL;
178 	data = (struct nfsctl_svc*) buf;
179 	return nfsd_svc(data->svc_port, data->svc_nthreads);
180 }
181 
182 static ssize_t write_add(struct file *file, char *buf, size_t size)
183 {
184 	struct nfsctl_client *data;
185 	if (size < sizeof(*data))
186 		return -EINVAL;
187 	data = (struct nfsctl_client *)buf;
188 	return exp_addclient(data);
189 }
190 
191 static ssize_t write_del(struct file *file, char *buf, size_t size)
192 {
193 	struct nfsctl_client *data;
194 	if (size < sizeof(*data))
195 		return -EINVAL;
196 	data = (struct nfsctl_client *)buf;
197 	return exp_delclient(data);
198 }
199 
200 static ssize_t write_export(struct file *file, char *buf, size_t size)
201 {
202 	struct nfsctl_export *data;
203 	if (size < sizeof(*data))
204 		return -EINVAL;
205 	data = (struct nfsctl_export*)buf;
206 	return exp_export(data);
207 }
208 
209 static ssize_t write_unexport(struct file *file, char *buf, size_t size)
210 {
211 	struct nfsctl_export *data;
212 
213 	if (size < sizeof(*data))
214 		return -EINVAL;
215 	data = (struct nfsctl_export*)buf;
216 	return exp_unexport(data);
217 }
218 
219 static ssize_t write_getfs(struct file *file, char *buf, size_t size)
220 {
221 	struct nfsctl_fsparm *data;
222 	struct sockaddr_in *sin;
223 	struct auth_domain *clp;
224 	int err = 0;
225 	struct knfsd_fh *res;
226 
227 	if (size < sizeof(*data))
228 		return -EINVAL;
229 	data = (struct nfsctl_fsparm*)buf;
230 	err = -EPROTONOSUPPORT;
231 	if (data->gd_addr.sa_family != AF_INET)
232 		goto out;
233 	sin = (struct sockaddr_in *)&data->gd_addr;
234 	if (data->gd_maxlen > NFS3_FHSIZE)
235 		data->gd_maxlen = NFS3_FHSIZE;
236 
237 	res = (struct knfsd_fh*)buf;
238 
239 	exp_readlock();
240 	if (!(clp = auth_unix_lookup(sin->sin_addr)))
241 		err = -EPERM;
242 	else {
243 		err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
244 		auth_domain_put(clp);
245 	}
246 	exp_readunlock();
247 	if (err == 0)
248 		err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
249  out:
250 	return err;
251 }
252 
253 static ssize_t write_getfd(struct file *file, char *buf, size_t size)
254 {
255 	struct nfsctl_fdparm *data;
256 	struct sockaddr_in *sin;
257 	struct auth_domain *clp;
258 	int err = 0;
259 	struct knfsd_fh fh;
260 	char *res;
261 
262 	if (size < sizeof(*data))
263 		return -EINVAL;
264 	data = (struct nfsctl_fdparm*)buf;
265 	err = -EPROTONOSUPPORT;
266 	if (data->gd_addr.sa_family != AF_INET)
267 		goto out;
268 	err = -EINVAL;
269 	if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
270 		goto out;
271 
272 	res = buf;
273 	sin = (struct sockaddr_in *)&data->gd_addr;
274 	exp_readlock();
275 	if (!(clp = auth_unix_lookup(sin->sin_addr)))
276 		err = -EPERM;
277 	else {
278 		err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
279 		auth_domain_put(clp);
280 	}
281 	exp_readunlock();
282 
283 	if (err == 0) {
284 		memset(res,0, NFS_FHSIZE);
285 		memcpy(res, &fh.fh_base, fh.fh_size);
286 		err = NFS_FHSIZE;
287 	}
288  out:
289 	return err;
290 }
291 
292 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
293 {
294 	/* request is:
295 	 *   domain path maxsize
296 	 * response is
297 	 *   filehandle
298 	 *
299 	 * qword quoting is used, so filehandle will be \x....
300 	 */
301 	char *dname, *path;
302 	int maxsize;
303 	char *mesg = buf;
304 	int len;
305 	struct auth_domain *dom;
306 	struct knfsd_fh fh;
307 
308 	if (buf[size-1] != '\n')
309 		return -EINVAL;
310 	buf[size-1] = 0;
311 
312 	dname = mesg;
313 	len = qword_get(&mesg, dname, size);
314 	if (len <= 0) return -EINVAL;
315 
316 	path = dname+len+1;
317 	len = qword_get(&mesg, path, size);
318 	if (len <= 0) return -EINVAL;
319 
320 	len = get_int(&mesg, &maxsize);
321 	if (len)
322 		return len;
323 
324 	if (maxsize < NFS_FHSIZE)
325 		return -EINVAL;
326 	if (maxsize > NFS3_FHSIZE)
327 		maxsize = NFS3_FHSIZE;
328 
329 	if (qword_get(&mesg, mesg, size)>0)
330 		return -EINVAL;
331 
332 	/* we have all the words, they are in buf.. */
333 	dom = unix_domain_find(dname);
334 	if (!dom)
335 		return -ENOMEM;
336 
337 	len = exp_rootfh(dom, path, &fh,  maxsize);
338 	auth_domain_put(dom);
339 	if (len)
340 		return len;
341 
342 	mesg = buf; len = SIMPLE_TRANSACTION_LIMIT;
343 	qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
344 	mesg[-1] = '\n';
345 	return mesg - buf;
346 }
347 
348 extern int nfsd_nrthreads(void);
349 
350 static ssize_t write_threads(struct file *file, char *buf, size_t size)
351 {
352 	/* if size > 0, look for a number of threads and call nfsd_svc
353 	 * then write out number of threads as reply
354 	 */
355 	char *mesg = buf;
356 	int rv;
357 	if (size > 0) {
358 		int newthreads;
359 		rv = get_int(&mesg, &newthreads);
360 		if (rv)
361 			return rv;
362 		if (newthreads <0)
363 			return -EINVAL;
364 		rv = nfsd_svc(2049, newthreads);
365 		if (rv)
366 			return rv;
367 	}
368 	sprintf(buf, "%d\n", nfsd_nrthreads());
369 	return strlen(buf);
370 }
371 
372 extern int nfsd_nrpools(void);
373 extern int nfsd_get_nrthreads(int n, int *);
374 extern int nfsd_set_nrthreads(int n, int *);
375 
376 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
377 {
378 	/* if size > 0, look for an array of number of threads per node
379 	 * and apply them  then write out number of threads per node as reply
380 	 */
381 	char *mesg = buf;
382 	int i;
383 	int rv;
384 	int len;
385     	int npools = nfsd_nrpools();
386 	int *nthreads;
387 
388 	if (npools == 0) {
389 		/*
390 		 * NFS is shut down.  The admin can start it by
391 		 * writing to the threads file but NOT the pool_threads
392 		 * file, sorry.  Report zero threads.
393 		 */
394 		strcpy(buf, "0\n");
395 		return strlen(buf);
396 	}
397 
398 	nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
399 	if (nthreads == NULL)
400 		return -ENOMEM;
401 
402 	if (size > 0) {
403 		for (i = 0; i < npools; i++) {
404 			rv = get_int(&mesg, &nthreads[i]);
405 			if (rv == -ENOENT)
406 				break;		/* fewer numbers than pools */
407 			if (rv)
408 				goto out_free;	/* syntax error */
409 			rv = -EINVAL;
410 			if (nthreads[i] < 0)
411 				goto out_free;
412 		}
413 		rv = nfsd_set_nrthreads(i, nthreads);
414 		if (rv)
415 			goto out_free;
416 	}
417 
418 	rv = nfsd_get_nrthreads(npools, nthreads);
419 	if (rv)
420 		goto out_free;
421 
422 	mesg = buf;
423 	size = SIMPLE_TRANSACTION_LIMIT;
424 	for (i = 0; i < npools && size > 0; i++) {
425 		snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
426 		len = strlen(mesg);
427 		size -= len;
428 		mesg += len;
429 	}
430 
431 	return (mesg-buf);
432 
433 out_free:
434 	kfree(nthreads);
435 	return rv;
436 }
437 
438 static ssize_t write_versions(struct file *file, char *buf, size_t size)
439 {
440 	/*
441 	 * Format:
442 	 *   [-/+]vers [-/+]vers ...
443 	 */
444 	char *mesg = buf;
445 	char *vers, sign;
446 	int len, num;
447 	ssize_t tlen = 0;
448 	char *sep;
449 
450 	if (size>0) {
451 		if (nfsd_serv)
452 			/* Cannot change versions without updating
453 			 * nfsd_serv->sv_xdrsize, and reallocing
454 			 * rq_argp and rq_resp
455 			 */
456 			return -EBUSY;
457 		if (buf[size-1] != '\n')
458 			return -EINVAL;
459 		buf[size-1] = 0;
460 
461 		vers = mesg;
462 		len = qword_get(&mesg, vers, size);
463 		if (len <= 0) return -EINVAL;
464 		do {
465 			sign = *vers;
466 			if (sign == '+' || sign == '-')
467 				num = simple_strtol((vers+1), NULL, 0);
468 			else
469 				num = simple_strtol(vers, NULL, 0);
470 			switch(num) {
471 			case 2:
472 			case 3:
473 			case 4:
474 				nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
475 				break;
476 			default:
477 				return -EINVAL;
478 			}
479 			vers += len + 1;
480 			tlen += len;
481 		} while ((len = qword_get(&mesg, vers, size)) > 0);
482 		/* If all get turned off, turn them back on, as
483 		 * having no versions is BAD
484 		 */
485 		nfsd_reset_versions();
486 	}
487 	/* Now write current state into reply buffer */
488 	len = 0;
489 	sep = "";
490 	for (num=2 ; num <= 4 ; num++)
491 		if (nfsd_vers(num, NFSD_AVAIL)) {
492 			len += sprintf(buf+len, "%s%c%d", sep,
493 				       nfsd_vers(num, NFSD_TEST)?'+':'-',
494 				       num);
495 			sep = " ";
496 		}
497 	len += sprintf(buf+len, "\n");
498 	return len;
499 }
500 
501 static ssize_t write_ports(struct file *file, char *buf, size_t size)
502 {
503 	if (size == 0) {
504 		int len = 0;
505 		lock_kernel();
506 		if (nfsd_serv)
507 			len = svc_sock_names(buf, nfsd_serv, NULL);
508 		unlock_kernel();
509 		return len;
510 	}
511 	/* Either a single 'fd' number is written, in which
512 	 * case it must be for a socket of a supported family/protocol,
513 	 * and we use it as an nfsd socket, or
514 	 * A '-' followed by the 'name' of a socket in which case
515 	 * we close the socket.
516 	 */
517 	if (isdigit(buf[0])) {
518 		char *mesg = buf;
519 		int fd;
520 		int err;
521 		err = get_int(&mesg, &fd);
522 		if (err)
523 			return -EINVAL;
524 		if (fd < 0)
525 			return -EINVAL;
526 		err = nfsd_create_serv();
527 		if (!err) {
528 			int proto = 0;
529 			err = svc_addsock(nfsd_serv, fd, buf, &proto);
530 			if (err >= 0) {
531 				err = lockd_up(proto);
532 				if (err < 0)
533 					svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
534 			}
535 			/* Decrease the count, but don't shutdown the
536 			 * the service
537 			 */
538 			lock_kernel();
539 			nfsd_serv->sv_nrthreads--;
540 			unlock_kernel();
541 		}
542 		return err < 0 ? err : 0;
543 	}
544 	if (buf[0] == '-') {
545 		char *toclose = kstrdup(buf+1, GFP_KERNEL);
546 		int len = 0;
547 		if (!toclose)
548 			return -ENOMEM;
549 		lock_kernel();
550 		if (nfsd_serv)
551 			len = svc_sock_names(buf, nfsd_serv, toclose);
552 		unlock_kernel();
553 		if (len >= 0)
554 			lockd_down();
555 		kfree(toclose);
556 		return len;
557 	}
558 	return -EINVAL;
559 }
560 
561 int nfsd_max_blksize;
562 
563 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
564 {
565 	char *mesg = buf;
566 	if (size > 0) {
567 		int bsize;
568 		int rv = get_int(&mesg, &bsize);
569 		if (rv)
570 			return rv;
571 		/* force bsize into allowed range and
572 		 * required alignment.
573 		 */
574 		if (bsize < 1024)
575 			bsize = 1024;
576 		if (bsize > NFSSVC_MAXBLKSIZE)
577 			bsize = NFSSVC_MAXBLKSIZE;
578 		bsize &= ~(1024-1);
579 		lock_kernel();
580 		if (nfsd_serv && nfsd_serv->sv_nrthreads) {
581 			unlock_kernel();
582 			return -EBUSY;
583 		}
584 		nfsd_max_blksize = bsize;
585 		unlock_kernel();
586 	}
587 	return sprintf(buf, "%d\n", nfsd_max_blksize);
588 }
589 
590 #ifdef CONFIG_NFSD_V4
591 extern time_t nfs4_leasetime(void);
592 
593 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
594 {
595 	/* if size > 10 seconds, call
596 	 * nfs4_reset_lease() then write out the new lease (seconds) as reply
597 	 */
598 	char *mesg = buf;
599 	int rv;
600 
601 	if (size > 0) {
602 		int lease;
603 		rv = get_int(&mesg, &lease);
604 		if (rv)
605 			return rv;
606 		if (lease < 10 || lease > 3600)
607 			return -EINVAL;
608 		nfs4_reset_lease(lease);
609 	}
610 	sprintf(buf, "%ld\n", nfs4_lease_time());
611 	return strlen(buf);
612 }
613 
614 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
615 {
616 	char *mesg = buf;
617 	char *recdir;
618 	int len, status;
619 
620 	if (size > PATH_MAX || buf[size-1] != '\n')
621 		return -EINVAL;
622 	buf[size-1] = 0;
623 
624 	recdir = mesg;
625 	len = qword_get(&mesg, recdir, size);
626 	if (len <= 0)
627 		return -EINVAL;
628 
629 	status = nfs4_reset_recoverydir(recdir);
630 	return strlen(buf);
631 }
632 #endif
633 
634 /*----------------------------------------------------------------------------*/
635 /*
636  *	populating the filesystem.
637  */
638 
639 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
640 {
641 	static struct tree_descr nfsd_files[] = {
642 		[NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
643 		[NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
644 		[NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
645 		[NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
646 		[NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
647 		[NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
648 		[NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
649 		[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
650 		[NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
651 		[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
652 		[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
653 		[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
654 		[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
655 		[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
656 #ifdef CONFIG_NFSD_V4
657 		[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
658 		[NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
659 #endif
660 		/* last one */ {""}
661 	};
662 	return simple_fill_super(sb, 0x6e667364, nfsd_files);
663 }
664 
665 static int nfsd_get_sb(struct file_system_type *fs_type,
666 	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
667 {
668 	return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
669 }
670 
671 static struct file_system_type nfsd_fs_type = {
672 	.owner		= THIS_MODULE,
673 	.name		= "nfsd",
674 	.get_sb		= nfsd_get_sb,
675 	.kill_sb	= kill_litter_super,
676 };
677 
678 static int __init init_nfsd(void)
679 {
680 	int retval;
681 	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
682 
683 	nfsd_stat_init();	/* Statistics */
684 	nfsd_cache_init();	/* RPC reply cache */
685 	nfsd_export_init();	/* Exports table */
686 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
687 	nfs4_state_init();	/* NFSv4 locking state */
688 	nfsd_idmap_init();      /* Name to ID mapping */
689 	if (proc_mkdir("fs/nfs", NULL)) {
690 		struct proc_dir_entry *entry;
691 		entry = create_proc_entry("fs/nfs/exports", 0, NULL);
692 		if (entry)
693 			entry->proc_fops =  &exports_operations;
694 	}
695 	retval = register_filesystem(&nfsd_fs_type);
696 	if (retval) {
697 		nfsd_export_shutdown();
698 		nfsd_cache_shutdown();
699 		remove_proc_entry("fs/nfs/exports", NULL);
700 		remove_proc_entry("fs/nfs", NULL);
701 		nfsd_stat_shutdown();
702 		nfsd_lockd_shutdown();
703 	}
704 	return retval;
705 }
706 
707 static void __exit exit_nfsd(void)
708 {
709 	nfsd_export_shutdown();
710 	nfsd_cache_shutdown();
711 	remove_proc_entry("fs/nfs/exports", NULL);
712 	remove_proc_entry("fs/nfs", NULL);
713 	nfsd_stat_shutdown();
714 	nfsd_lockd_shutdown();
715 	nfsd_idmap_shutdown();
716 	unregister_filesystem(&nfsd_fs_type);
717 }
718 
719 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
720 MODULE_LICENSE("GPL");
721 module_init(init_nfsd)
722 module_exit(exit_nfsd)
723