knfsd: Replace lock_kernel with a mutex in nfsd pool stats.
[safe/jmp/linux-2.6] / fs / nfsd / nfsctl.c
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/namei.h>
16 #include <linux/fcntl.h>
17 #include <linux/net.h>
18 #include <linux/in.h>
19 #include <linux/syscalls.h>
20 #include <linux/unistd.h>
21 #include <linux/slab.h>
22 #include <linux/proc_fs.h>
23 #include <linux/seq_file.h>
24 #include <linux/pagemap.h>
25 #include <linux/init.h>
26 #include <linux/inet.h>
27 #include <linux/string.h>
28 #include <linux/ctype.h>
29
30 #include <linux/nfs.h>
31 #include <linux/nfsd_idmap.h>
32 #include <linux/lockd/bind.h>
33 #include <linux/sunrpc/svc.h>
34 #include <linux/sunrpc/svcsock.h>
35 #include <linux/nfsd/nfsd.h>
36 #include <linux/nfsd/cache.h>
37 #include <linux/nfsd/xdr.h>
38 #include <linux/nfsd/syscall.h>
39 #include <linux/lockd/lockd.h>
40 #include <linux/sunrpc/clnt.h>
41
42 #include <asm/uaccess.h>
43 #include <net/ipv6.h>
44
45 /*
46  *      We have a single directory with 9 nodes in it.
47  */
48 enum {
49         NFSD_Root = 1,
50         NFSD_Svc,
51         NFSD_Add,
52         NFSD_Del,
53         NFSD_Export,
54         NFSD_Unexport,
55         NFSD_Getfd,
56         NFSD_Getfs,
57         NFSD_List,
58         NFSD_Fh,
59         NFSD_FO_UnlockIP,
60         NFSD_FO_UnlockFS,
61         NFSD_Threads,
62         NFSD_Pool_Threads,
63         NFSD_Pool_Stats,
64         NFSD_Versions,
65         NFSD_Ports,
66         NFSD_MaxBlkSize,
67         /*
68          * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
69          * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
70          */
71 #ifdef CONFIG_NFSD_V4
72         NFSD_Leasetime,
73         NFSD_RecoveryDir,
74 #endif
75 };
76
77 /*
78  * write() for these nodes.
79  */
80 static ssize_t write_svc(struct file *file, char *buf, size_t size);
81 static ssize_t write_add(struct file *file, char *buf, size_t size);
82 static ssize_t write_del(struct file *file, char *buf, size_t size);
83 static ssize_t write_export(struct file *file, char *buf, size_t size);
84 static ssize_t write_unexport(struct file *file, char *buf, size_t size);
85 static ssize_t write_getfd(struct file *file, char *buf, size_t size);
86 static ssize_t write_getfs(struct file *file, char *buf, size_t size);
87 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
88 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
89 static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
90 static ssize_t write_threads(struct file *file, char *buf, size_t size);
91 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size);
92 static ssize_t write_versions(struct file *file, char *buf, size_t size);
93 static ssize_t write_ports(struct file *file, char *buf, size_t size);
94 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
95 #ifdef CONFIG_NFSD_V4
96 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
97 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
98 #endif
99
100 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
101         [NFSD_Svc] = write_svc,
102         [NFSD_Add] = write_add,
103         [NFSD_Del] = write_del,
104         [NFSD_Export] = write_export,
105         [NFSD_Unexport] = write_unexport,
106         [NFSD_Getfd] = write_getfd,
107         [NFSD_Getfs] = write_getfs,
108         [NFSD_Fh] = write_filehandle,
109         [NFSD_FO_UnlockIP] = write_unlock_ip,
110         [NFSD_FO_UnlockFS] = write_unlock_fs,
111         [NFSD_Threads] = write_threads,
112         [NFSD_Pool_Threads] = write_pool_threads,
113         [NFSD_Versions] = write_versions,
114         [NFSD_Ports] = write_ports,
115         [NFSD_MaxBlkSize] = write_maxblksize,
116 #ifdef CONFIG_NFSD_V4
117         [NFSD_Leasetime] = write_leasetime,
118         [NFSD_RecoveryDir] = write_recoverydir,
119 #endif
120 };
121
122 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
123 {
124         ino_t ino =  file->f_path.dentry->d_inode->i_ino;
125         char *data;
126         ssize_t rv;
127
128         if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
129                 return -EINVAL;
130
131         data = simple_transaction_get(file, buf, size);
132         if (IS_ERR(data))
133                 return PTR_ERR(data);
134
135         rv =  write_op[ino](file, data, size);
136         if (rv >= 0) {
137                 simple_transaction_set(file, rv);
138                 rv = size;
139         }
140         return rv;
141 }
142
143 static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
144 {
145         if (! file->private_data) {
146                 /* An attempt to read a transaction file without writing
147                  * causes a 0-byte write so that the file can return
148                  * state information
149                  */
150                 ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
151                 if (rv < 0)
152                         return rv;
153         }
154         return simple_transaction_read(file, buf, size, pos);
155 }
156
157 static const struct file_operations transaction_ops = {
158         .write          = nfsctl_transaction_write,
159         .read           = nfsctl_transaction_read,
160         .release        = simple_transaction_release,
161 };
162
163 static int exports_open(struct inode *inode, struct file *file)
164 {
165         return seq_open(file, &nfs_exports_op);
166 }
167
168 static const struct file_operations exports_operations = {
169         .open           = exports_open,
170         .read           = seq_read,
171         .llseek         = seq_lseek,
172         .release        = seq_release,
173         .owner          = THIS_MODULE,
174 };
175
176 extern int nfsd_pool_stats_open(struct inode *inode, struct file *file);
177 extern int nfsd_pool_stats_release(struct inode *inode, struct file *file);
178
179 static struct file_operations pool_stats_operations = {
180         .open           = nfsd_pool_stats_open,
181         .read           = seq_read,
182         .llseek         = seq_lseek,
183         .release        = nfsd_pool_stats_release,
184         .owner          = THIS_MODULE,
185 };
186
187 /*----------------------------------------------------------------------------*/
188 /*
189  * payload - write methods
190  */
191
192 /**
193  * write_svc - Start kernel's NFSD server
194  *
195  * Deprecated.  /proc/fs/nfsd/threads is preferred.
196  * Function remains to support old versions of nfs-utils.
197  *
198  * Input:
199  *                      buf:    struct nfsctl_svc
200  *                              svc_port:       port number of this
201  *                                              server's listener
202  *                              svc_nthreads:   number of threads to start
203  *                      size:   size in bytes of passed in nfsctl_svc
204  * Output:
205  *      On success:     returns zero
206  *      On error:       return code is negative errno value
207  */
208 static ssize_t write_svc(struct file *file, char *buf, size_t size)
209 {
210         struct nfsctl_svc *data;
211         int err;
212         if (size < sizeof(*data))
213                 return -EINVAL;
214         data = (struct nfsctl_svc*) buf;
215         err = nfsd_svc(data->svc_port, data->svc_nthreads);
216         if (err < 0)
217                 return err;
218         return 0;
219 }
220
221 /**
222  * write_add - Add or modify client entry in auth unix cache
223  *
224  * Deprecated.  /proc/net/rpc/auth.unix.ip is preferred.
225  * Function remains to support old versions of nfs-utils.
226  *
227  * Input:
228  *                      buf:    struct nfsctl_client
229  *                              cl_ident:       '\0'-terminated C string
230  *                                              containing domain name
231  *                                              of client
232  *                              cl_naddr:       no. of items in cl_addrlist
233  *                              cl_addrlist:    array of client addresses
234  *                              cl_fhkeytype:   ignored
235  *                              cl_fhkeylen:    ignored
236  *                              cl_fhkey:       ignored
237  *                      size:   size in bytes of passed in nfsctl_client
238  * Output:
239  *      On success:     returns zero
240  *      On error:       return code is negative errno value
241  *
242  * Note: Only AF_INET client addresses are passed in, since
243  * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
244  */
245 static ssize_t write_add(struct file *file, char *buf, size_t size)
246 {
247         struct nfsctl_client *data;
248         if (size < sizeof(*data))
249                 return -EINVAL;
250         data = (struct nfsctl_client *)buf;
251         return exp_addclient(data);
252 }
253
254 /**
255  * write_del - Remove client from auth unix cache
256  *
257  * Deprecated.  /proc/net/rpc/auth.unix.ip is preferred.
258  * Function remains to support old versions of nfs-utils.
259  *
260  * Input:
261  *                      buf:    struct nfsctl_client
262  *                              cl_ident:       '\0'-terminated C string
263  *                                              containing domain name
264  *                                              of client
265  *                              cl_naddr:       ignored
266  *                              cl_addrlist:    ignored
267  *                              cl_fhkeytype:   ignored
268  *                              cl_fhkeylen:    ignored
269  *                              cl_fhkey:       ignored
270  *                      size:   size in bytes of passed in nfsctl_client
271  * Output:
272  *      On success:     returns zero
273  *      On error:       return code is negative errno value
274  *
275  * Note: Only AF_INET client addresses are passed in, since
276  * nfsctl_client.cl_addrlist contains only in_addr fields for addresses.
277  */
278 static ssize_t write_del(struct file *file, char *buf, size_t size)
279 {
280         struct nfsctl_client *data;
281         if (size < sizeof(*data))
282                 return -EINVAL;
283         data = (struct nfsctl_client *)buf;
284         return exp_delclient(data);
285 }
286
287 /**
288  * write_export - Export part or all of a local file system
289  *
290  * Deprecated.  /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
291  * Function remains to support old versions of nfs-utils.
292  *
293  * Input:
294  *                      buf:    struct nfsctl_export
295  *                              ex_client:      '\0'-terminated C string
296  *                                              containing domain name
297  *                                              of client allowed to access
298  *                                              this export
299  *                              ex_path:        '\0'-terminated C string
300  *                                              containing pathname of
301  *                                              directory in local file system
302  *                              ex_dev:         fsid to use for this export
303  *                              ex_ino:         ignored
304  *                              ex_flags:       export flags for this export
305  *                              ex_anon_uid:    UID to use for anonymous
306  *                                              requests
307  *                              ex_anon_gid:    GID to use for anonymous
308  *                                              requests
309  *                      size:   size in bytes of passed in nfsctl_export
310  * Output:
311  *      On success:     returns zero
312  *      On error:       return code is negative errno value
313  */
314 static ssize_t write_export(struct file *file, char *buf, size_t size)
315 {
316         struct nfsctl_export *data;
317         if (size < sizeof(*data))
318                 return -EINVAL;
319         data = (struct nfsctl_export*)buf;
320         return exp_export(data);
321 }
322
323 /**
324  * write_unexport - Unexport a previously exported file system
325  *
326  * Deprecated.  /proc/net/rpc/{nfsd.export,nfsd.fh} are preferred.
327  * Function remains to support old versions of nfs-utils.
328  *
329  * Input:
330  *                      buf:    struct nfsctl_export
331  *                              ex_client:      '\0'-terminated C string
332  *                                              containing domain name
333  *                                              of client no longer allowed
334  *                                              to access this export
335  *                              ex_path:        '\0'-terminated C string
336  *                                              containing pathname of
337  *                                              directory in local file system
338  *                              ex_dev:         ignored
339  *                              ex_ino:         ignored
340  *                              ex_flags:       ignored
341  *                              ex_anon_uid:    ignored
342  *                              ex_anon_gid:    ignored
343  *                      size:   size in bytes of passed in nfsctl_export
344  * Output:
345  *      On success:     returns zero
346  *      On error:       return code is negative errno value
347  */
348 static ssize_t write_unexport(struct file *file, char *buf, size_t size)
349 {
350         struct nfsctl_export *data;
351
352         if (size < sizeof(*data))
353                 return -EINVAL;
354         data = (struct nfsctl_export*)buf;
355         return exp_unexport(data);
356 }
357
358 /**
359  * write_getfs - Get a variable-length NFS file handle by path
360  *
361  * Deprecated.  /proc/fs/nfsd/filehandle is preferred.
362  * Function remains to support old versions of nfs-utils.
363  *
364  * Input:
365  *                      buf:    struct nfsctl_fsparm
366  *                              gd_addr:        socket address of client
367  *                              gd_path:        '\0'-terminated C string
368  *                                              containing pathname of
369  *                                              directory in local file system
370  *                              gd_maxlen:      maximum size of returned file
371  *                                              handle
372  *                      size:   size in bytes of passed in nfsctl_fsparm
373  * Output:
374  *      On success:     passed-in buffer filled with a knfsd_fh structure
375  *                      (a variable-length raw NFS file handle);
376  *                      return code is the size in bytes of the file handle
377  *      On error:       return code is negative errno value
378  *
379  * Note: Only AF_INET client addresses are passed in, since gd_addr
380  * is the same size as a struct sockaddr_in.
381  */
382 static ssize_t write_getfs(struct file *file, char *buf, size_t size)
383 {
384         struct nfsctl_fsparm *data;
385         struct sockaddr_in *sin;
386         struct auth_domain *clp;
387         int err = 0;
388         struct knfsd_fh *res;
389         struct in6_addr in6;
390
391         if (size < sizeof(*data))
392                 return -EINVAL;
393         data = (struct nfsctl_fsparm*)buf;
394         err = -EPROTONOSUPPORT;
395         if (data->gd_addr.sa_family != AF_INET)
396                 goto out;
397         sin = (struct sockaddr_in *)&data->gd_addr;
398         if (data->gd_maxlen > NFS3_FHSIZE)
399                 data->gd_maxlen = NFS3_FHSIZE;
400
401         res = (struct knfsd_fh*)buf;
402
403         exp_readlock();
404
405         ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
406
407         clp = auth_unix_lookup(&in6);
408         if (!clp)
409                 err = -EPERM;
410         else {
411                 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
412                 auth_domain_put(clp);
413         }
414         exp_readunlock();
415         if (err == 0)
416                 err = res->fh_size + offsetof(struct knfsd_fh, fh_base);
417  out:
418         return err;
419 }
420
421 /**
422  * write_getfd - Get a fixed-length NFS file handle by path (used by mountd)
423  *
424  * Deprecated.  /proc/fs/nfsd/filehandle is preferred.
425  * Function remains to support old versions of nfs-utils.
426  *
427  * Input:
428  *                      buf:    struct nfsctl_fdparm
429  *                              gd_addr:        socket address of client
430  *                              gd_path:        '\0'-terminated C string
431  *                                              containing pathname of
432  *                                              directory in local file system
433  *                              gd_version:     fdparm structure version
434  *                      size:   size in bytes of passed in nfsctl_fdparm
435  * Output:
436  *      On success:     passed-in buffer filled with nfsctl_res
437  *                      (a fixed-length raw NFS file handle);
438  *                      return code is the size in bytes of the file handle
439  *      On error:       return code is negative errno value
440  *
441  * Note: Only AF_INET client addresses are passed in, since gd_addr
442  * is the same size as a struct sockaddr_in.
443  */
444 static ssize_t write_getfd(struct file *file, char *buf, size_t size)
445 {
446         struct nfsctl_fdparm *data;
447         struct sockaddr_in *sin;
448         struct auth_domain *clp;
449         int err = 0;
450         struct knfsd_fh fh;
451         char *res;
452         struct in6_addr in6;
453
454         if (size < sizeof(*data))
455                 return -EINVAL;
456         data = (struct nfsctl_fdparm*)buf;
457         err = -EPROTONOSUPPORT;
458         if (data->gd_addr.sa_family != AF_INET)
459                 goto out;
460         err = -EINVAL;
461         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
462                 goto out;
463
464         res = buf;
465         sin = (struct sockaddr_in *)&data->gd_addr;
466         exp_readlock();
467
468         ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
469
470         clp = auth_unix_lookup(&in6);
471         if (!clp)
472                 err = -EPERM;
473         else {
474                 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
475                 auth_domain_put(clp);
476         }
477         exp_readunlock();
478
479         if (err == 0) {
480                 memset(res,0, NFS_FHSIZE);
481                 memcpy(res, &fh.fh_base, fh.fh_size);
482                 err = NFS_FHSIZE;
483         }
484  out:
485         return err;
486 }
487
488 /**
489  * write_unlock_ip - Release all locks used by a client
490  *
491  * Experimental.
492  *
493  * Input:
494  *                      buf:    '\n'-terminated C string containing a
495  *                              presentation format IP address
496  *                      size:   length of C string in @buf
497  * Output:
498  *      On success:     returns zero if all specified locks were released;
499  *                      returns one if one or more locks were not released
500  *      On error:       return code is negative errno value
501  */
502 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
503 {
504         struct sockaddr_storage address;
505         struct sockaddr *sap = (struct sockaddr *)&address;
506         size_t salen = sizeof(address);
507         char *fo_path;
508
509         /* sanity check */
510         if (size == 0)
511                 return -EINVAL;
512
513         if (buf[size-1] != '\n')
514                 return -EINVAL;
515
516         fo_path = buf;
517         if (qword_get(&buf, fo_path, size) < 0)
518                 return -EINVAL;
519
520         if (rpc_pton(fo_path, size, sap, salen) == 0)
521                 return -EINVAL;
522
523         return nlmsvc_unlock_all_by_ip(sap);
524 }
525
526 /**
527  * write_unlock_fs - Release all locks on a local file system
528  *
529  * Experimental.
530  *
531  * Input:
532  *                      buf:    '\n'-terminated C string containing the
533  *                              absolute pathname of a local file system
534  *                      size:   length of C string in @buf
535  * Output:
536  *      On success:     returns zero if all specified locks were released;
537  *                      returns one if one or more locks were not released
538  *      On error:       return code is negative errno value
539  */
540 static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
541 {
542         struct path path;
543         char *fo_path;
544         int error;
545
546         /* sanity check */
547         if (size == 0)
548                 return -EINVAL;
549
550         if (buf[size-1] != '\n')
551                 return -EINVAL;
552
553         fo_path = buf;
554         if (qword_get(&buf, fo_path, size) < 0)
555                 return -EINVAL;
556
557         error = kern_path(fo_path, 0, &path);
558         if (error)
559                 return error;
560
561         /*
562          * XXX: Needs better sanity checking.  Otherwise we could end up
563          * releasing locks on the wrong file system.
564          *
565          * For example:
566          * 1.  Does the path refer to a directory?
567          * 2.  Is that directory a mount point, or
568          * 3.  Is that directory the root of an exported file system?
569          */
570         error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
571
572         path_put(&path);
573         return error;
574 }
575
576 /**
577  * write_filehandle - Get a variable-length NFS file handle by path
578  *
579  * On input, the buffer contains a '\n'-terminated C string comprised of
580  * three alphanumeric words separated by whitespace.  The string may
581  * contain escape sequences.
582  *
583  * Input:
584  *                      buf:
585  *                              domain:         client domain name
586  *                              path:           export pathname
587  *                              maxsize:        numeric maximum size of
588  *                                              @buf
589  *                      size:   length of C string in @buf
590  * Output:
591  *      On success:     passed-in buffer filled with '\n'-terminated C
592  *                      string containing a ASCII hex text version
593  *                      of the NFS file handle;
594  *                      return code is the size in bytes of the string
595  *      On error:       return code is negative errno value
596  */
597 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
598 {
599         char *dname, *path;
600         int uninitialized_var(maxsize);
601         char *mesg = buf;
602         int len;
603         struct auth_domain *dom;
604         struct knfsd_fh fh;
605
606         if (size == 0)
607                 return -EINVAL;
608
609         if (buf[size-1] != '\n')
610                 return -EINVAL;
611         buf[size-1] = 0;
612
613         dname = mesg;
614         len = qword_get(&mesg, dname, size);
615         if (len <= 0)
616                 return -EINVAL;
617         
618         path = dname+len+1;
619         len = qword_get(&mesg, path, size);
620         if (len <= 0)
621                 return -EINVAL;
622
623         len = get_int(&mesg, &maxsize);
624         if (len)
625                 return len;
626
627         if (maxsize < NFS_FHSIZE)
628                 return -EINVAL;
629         if (maxsize > NFS3_FHSIZE)
630                 maxsize = NFS3_FHSIZE;
631
632         if (qword_get(&mesg, mesg, size)>0)
633                 return -EINVAL;
634
635         /* we have all the words, they are in buf.. */
636         dom = unix_domain_find(dname);
637         if (!dom)
638                 return -ENOMEM;
639
640         len = exp_rootfh(dom, path, &fh,  maxsize);
641         auth_domain_put(dom);
642         if (len)
643                 return len;
644         
645         mesg = buf;
646         len = SIMPLE_TRANSACTION_LIMIT;
647         qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
648         mesg[-1] = '\n';
649         return mesg - buf;      
650 }
651
652 /**
653  * write_threads - Start NFSD, or report the current number of running threads
654  *
655  * Input:
656  *                      buf:            ignored
657  *                      size:           zero
658  * Output:
659  *      On success:     passed-in buffer filled with '\n'-terminated C
660  *                      string numeric value representing the number of
661  *                      running NFSD threads;
662  *                      return code is the size in bytes of the string
663  *      On error:       return code is zero
664  *
665  * OR
666  *
667  * Input:
668  *                      buf:            C string containing an unsigned
669  *                                      integer value representing the
670  *                                      number of NFSD threads to start
671  *                      size:           non-zero length of C string in @buf
672  * Output:
673  *      On success:     NFS service is started;
674  *                      passed-in buffer filled with '\n'-terminated C
675  *                      string numeric value representing the number of
676  *                      running NFSD threads;
677  *                      return code is the size in bytes of the string
678  *      On error:       return code is zero or a negative errno value
679  */
680 static ssize_t write_threads(struct file *file, char *buf, size_t size)
681 {
682         char *mesg = buf;
683         int rv;
684         if (size > 0) {
685                 int newthreads;
686                 rv = get_int(&mesg, &newthreads);
687                 if (rv)
688                         return rv;
689                 if (newthreads < 0)
690                         return -EINVAL;
691                 rv = nfsd_svc(NFS_PORT, newthreads);
692                 if (rv < 0)
693                         return rv;
694         } else
695                 rv = nfsd_nrthreads();
696
697         return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv);
698 }
699
700 /**
701  * write_pool_threads - Set or report the current number of threads per pool
702  *
703  * Input:
704  *                      buf:            ignored
705  *                      size:           zero
706  *
707  * OR
708  *
709  * Input:
710  *                      buf:            C string containing whitespace-
711  *                                      separated unsigned integer values
712  *                                      representing the number of NFSD
713  *                                      threads to start in each pool
714  *                      size:           non-zero length of C string in @buf
715  * Output:
716  *      On success:     passed-in buffer filled with '\n'-terminated C
717  *                      string containing integer values representing the
718  *                      number of NFSD threads in each pool;
719  *                      return code is the size in bytes of the string
720  *      On error:       return code is zero or a negative errno value
721  */
722 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
723 {
724         /* if size > 0, look for an array of number of threads per node
725          * and apply them  then write out number of threads per node as reply
726          */
727         char *mesg = buf;
728         int i;
729         int rv;
730         int len;
731         int npools;
732         int *nthreads;
733
734         mutex_lock(&nfsd_mutex);
735         npools = nfsd_nrpools();
736         if (npools == 0) {
737                 /*
738                  * NFS is shut down.  The admin can start it by
739                  * writing to the threads file but NOT the pool_threads
740                  * file, sorry.  Report zero threads.
741                  */
742                 mutex_unlock(&nfsd_mutex);
743                 strcpy(buf, "0\n");
744                 return strlen(buf);
745         }
746
747         nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
748         rv = -ENOMEM;
749         if (nthreads == NULL)
750                 goto out_free;
751
752         if (size > 0) {
753                 for (i = 0; i < npools; i++) {
754                         rv = get_int(&mesg, &nthreads[i]);
755                         if (rv == -ENOENT)
756                                 break;          /* fewer numbers than pools */
757                         if (rv)
758                                 goto out_free;  /* syntax error */
759                         rv = -EINVAL;
760                         if (nthreads[i] < 0)
761                                 goto out_free;
762                 }
763                 rv = nfsd_set_nrthreads(i, nthreads);
764                 if (rv)
765                         goto out_free;
766         }
767
768         rv = nfsd_get_nrthreads(npools, nthreads);
769         if (rv)
770                 goto out_free;
771
772         mesg = buf;
773         size = SIMPLE_TRANSACTION_LIMIT;
774         for (i = 0; i < npools && size > 0; i++) {
775                 snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
776                 len = strlen(mesg);
777                 size -= len;
778                 mesg += len;
779         }
780         rv = mesg - buf;
781 out_free:
782         kfree(nthreads);
783         mutex_unlock(&nfsd_mutex);
784         return rv;
785 }
786
787 static ssize_t __write_versions(struct file *file, char *buf, size_t size)
788 {
789         char *mesg = buf;
790         char *vers, *minorp, sign;
791         int len, num, remaining;
792         unsigned minor;
793         ssize_t tlen = 0;
794         char *sep;
795
796         if (size>0) {
797                 if (nfsd_serv)
798                         /* Cannot change versions without updating
799                          * nfsd_serv->sv_xdrsize, and reallocing
800                          * rq_argp and rq_resp
801                          */
802                         return -EBUSY;
803                 if (buf[size-1] != '\n')
804                         return -EINVAL;
805                 buf[size-1] = 0;
806
807                 vers = mesg;
808                 len = qword_get(&mesg, vers, size);
809                 if (len <= 0) return -EINVAL;
810                 do {
811                         sign = *vers;
812                         if (sign == '+' || sign == '-')
813                                 num = simple_strtol((vers+1), &minorp, 0);
814                         else
815                                 num = simple_strtol(vers, &minorp, 0);
816                         if (*minorp == '.') {
817                                 if (num < 4)
818                                         return -EINVAL;
819                                 minor = simple_strtoul(minorp+1, NULL, 0);
820                                 if (minor == 0)
821                                         return -EINVAL;
822                                 if (nfsd_minorversion(minor, sign == '-' ?
823                                                      NFSD_CLEAR : NFSD_SET) < 0)
824                                         return -EINVAL;
825                                 goto next;
826                         }
827                         switch(num) {
828                         case 2:
829                         case 3:
830                         case 4:
831                                 nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
832                                 break;
833                         default:
834                                 return -EINVAL;
835                         }
836                 next:
837                         vers += len + 1;
838                 } while ((len = qword_get(&mesg, vers, size)) > 0);
839                 /* If all get turned off, turn them back on, as
840                  * having no versions is BAD
841                  */
842                 nfsd_reset_versions();
843         }
844
845         /* Now write current state into reply buffer */
846         len = 0;
847         sep = "";
848         remaining = SIMPLE_TRANSACTION_LIMIT;
849         for (num=2 ; num <= 4 ; num++)
850                 if (nfsd_vers(num, NFSD_AVAIL)) {
851                         len = snprintf(buf, remaining, "%s%c%d", sep,
852                                        nfsd_vers(num, NFSD_TEST)?'+':'-',
853                                        num);
854                         sep = " ";
855
856                         if (len > remaining)
857                                 break;
858                         remaining -= len;
859                         buf += len;
860                         tlen += len;
861                 }
862         if (nfsd_vers(4, NFSD_AVAIL))
863                 for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
864                      minor++) {
865                         len = snprintf(buf, remaining, " %c4.%u",
866                                         (nfsd_vers(4, NFSD_TEST) &&
867                                          nfsd_minorversion(minor, NFSD_TEST)) ?
868                                                 '+' : '-',
869                                         minor);
870
871                         if (len > remaining)
872                                 break;
873                         remaining -= len;
874                         buf += len;
875                         tlen += len;
876                 }
877
878         len = snprintf(buf, remaining, "\n");
879         if (len > remaining)
880                 return -EINVAL;
881         return tlen + len;
882 }
883
884 /**
885  * write_versions - Set or report the available NFS protocol versions
886  *
887  * Input:
888  *                      buf:            ignored
889  *                      size:           zero
890  * Output:
891  *      On success:     passed-in buffer filled with '\n'-terminated C
892  *                      string containing positive or negative integer
893  *                      values representing the current status of each
894  *                      protocol version;
895  *                      return code is the size in bytes of the string
896  *      On error:       return code is zero or a negative errno value
897  *
898  * OR
899  *
900  * Input:
901  *                      buf:            C string containing whitespace-
902  *                                      separated positive or negative
903  *                                      integer values representing NFS
904  *                                      protocol versions to enable ("+n")
905  *                                      or disable ("-n")
906  *                      size:           non-zero length of C string in @buf
907  * Output:
908  *      On success:     status of zero or more protocol versions has
909  *                      been updated; passed-in buffer filled with
910  *                      '\n'-terminated C string containing positive
911  *                      or negative integer values representing the
912  *                      current status of each protocol version;
913  *                      return code is the size in bytes of the string
914  *      On error:       return code is zero or a negative errno value
915  */
916 static ssize_t write_versions(struct file *file, char *buf, size_t size)
917 {
918         ssize_t rv;
919
920         mutex_lock(&nfsd_mutex);
921         rv = __write_versions(file, buf, size);
922         mutex_unlock(&nfsd_mutex);
923         return rv;
924 }
925
926 /*
927  * Zero-length write.  Return a list of NFSD's current listener
928  * transports.
929  */
930 static ssize_t __write_ports_names(char *buf)
931 {
932         if (nfsd_serv == NULL)
933                 return 0;
934         return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
935 }
936
937 /*
938  * A single 'fd' number was written, in which case it must be for
939  * a socket of a supported family/protocol, and we use it as an
940  * nfsd listener.
941  */
942 static ssize_t __write_ports_addfd(char *buf)
943 {
944         char *mesg = buf;
945         int fd, err;
946
947         err = get_int(&mesg, &fd);
948         if (err != 0 || fd < 0)
949                 return -EINVAL;
950
951         err = nfsd_create_serv();
952         if (err != 0)
953                 return err;
954
955         err = lockd_up();
956         if (err != 0)
957                 goto out;
958
959         err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
960         if (err < 0)
961                 lockd_down();
962
963 out:
964         /* Decrease the count, but don't shut down the service */
965         nfsd_serv->sv_nrthreads--;
966         return err;
967 }
968
969 /*
970  * A '-' followed by the 'name' of a socket means we close the socket.
971  */
972 static ssize_t __write_ports_delfd(char *buf)
973 {
974         char *toclose;
975         int len = 0;
976
977         toclose = kstrdup(buf + 1, GFP_KERNEL);
978         if (toclose == NULL)
979                 return -ENOMEM;
980
981         if (nfsd_serv != NULL)
982                 len = svc_sock_names(nfsd_serv, buf,
983                                         SIMPLE_TRANSACTION_LIMIT, toclose);
984         if (len >= 0)
985                 lockd_down();
986
987         kfree(toclose);
988         return len;
989 }
990
991 /*
992  * A transport listener is added by writing it's transport name and
993  * a port number.
994  */
995 static ssize_t __write_ports_addxprt(char *buf)
996 {
997         char transport[16];
998         int port, err;
999
1000         if (sscanf(buf, "%15s %4u", transport, &port) != 2)
1001                 return -EINVAL;
1002
1003         if (port < 1 || port > USHORT_MAX)
1004                 return -EINVAL;
1005
1006         err = nfsd_create_serv();
1007         if (err != 0)
1008                 return err;
1009
1010         err = svc_create_xprt(nfsd_serv, transport,
1011                                 PF_INET, port, SVC_SOCK_ANONYMOUS);
1012         if (err < 0) {
1013                 /* Give a reasonable perror msg for bad transport string */
1014                 if (err == -ENOENT)
1015                         err = -EPROTONOSUPPORT;
1016                 return err;
1017         }
1018         return 0;
1019 }
1020
1021 /*
1022  * A transport listener is removed by writing a "-", it's transport
1023  * name, and it's port number.
1024  */
1025 static ssize_t __write_ports_delxprt(char *buf)
1026 {
1027         struct svc_xprt *xprt;
1028         char transport[16];
1029         int port;
1030
1031         if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
1032                 return -EINVAL;
1033
1034         if (port < 1 || port > USHORT_MAX || nfsd_serv == NULL)
1035                 return -EINVAL;
1036
1037         xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
1038         if (xprt == NULL)
1039                 return -ENOTCONN;
1040
1041         svc_close_xprt(xprt);
1042         svc_xprt_put(xprt);
1043         return 0;
1044 }
1045
1046 static ssize_t __write_ports(struct file *file, char *buf, size_t size)
1047 {
1048         if (size == 0)
1049                 return __write_ports_names(buf);
1050
1051         if (isdigit(buf[0]))
1052                 return __write_ports_addfd(buf);
1053
1054         if (buf[0] == '-' && isdigit(buf[1]))
1055                 return __write_ports_delfd(buf);
1056
1057         if (isalpha(buf[0]))
1058                 return __write_ports_addxprt(buf);
1059
1060         if (buf[0] == '-' && isalpha(buf[1]))
1061                 return __write_ports_delxprt(buf);
1062
1063         return -EINVAL;
1064 }
1065
1066 /**
1067  * write_ports - Pass a socket file descriptor or transport name to listen on
1068  *
1069  * Input:
1070  *                      buf:            ignored
1071  *                      size:           zero
1072  * Output:
1073  *      On success:     passed-in buffer filled with a '\n'-terminated C
1074  *                      string containing a whitespace-separated list of
1075  *                      named NFSD listeners;
1076  *                      return code is the size in bytes of the string
1077  *      On error:       return code is zero or a negative errno value
1078  *
1079  * OR
1080  *
1081  * Input:
1082  *                      buf:            C string containing an unsigned
1083  *                                      integer value representing a bound
1084  *                                      but unconnected socket that is to be
1085  *                                      used as an NFSD listener; listen(3)
1086  *                                      must be called for a SOCK_STREAM
1087  *                                      socket, otherwise it is ignored
1088  *                      size:           non-zero length of C string in @buf
1089  * Output:
1090  *      On success:     NFS service is started;
1091  *                      passed-in buffer filled with a '\n'-terminated C
1092  *                      string containing a unique alphanumeric name of
1093  *                      the listener;
1094  *                      return code is the size in bytes of the string
1095  *      On error:       return code is a negative errno value
1096  *
1097  * OR
1098  *
1099  * Input:
1100  *                      buf:            C string containing a "-" followed
1101  *                                      by an integer value representing a
1102  *                                      previously passed in socket file
1103  *                                      descriptor
1104  *                      size:           non-zero length of C string in @buf
1105  * Output:
1106  *      On success:     NFS service no longer listens on that socket;
1107  *                      passed-in buffer filled with a '\n'-terminated C
1108  *                      string containing a unique name of the listener;
1109  *                      return code is the size in bytes of the string
1110  *      On error:       return code is a negative errno value
1111  *
1112  * OR
1113  *
1114  * Input:
1115  *                      buf:            C string containing a transport
1116  *                                      name and an unsigned integer value
1117  *                                      representing the port to listen on,
1118  *                                      separated by whitespace
1119  *                      size:           non-zero length of C string in @buf
1120  * Output:
1121  *      On success:     returns zero; NFS service is started
1122  *      On error:       return code is a negative errno value
1123  *
1124  * OR
1125  *
1126  * Input:
1127  *                      buf:            C string containing a "-" followed
1128  *                                      by a transport name and an unsigned
1129  *                                      integer value representing the port
1130  *                                      to listen on, separated by whitespace
1131  *                      size:           non-zero length of C string in @buf
1132  * Output:
1133  *      On success:     returns zero; NFS service no longer listens
1134  *                      on that transport
1135  *      On error:       return code is a negative errno value
1136  */
1137 static ssize_t write_ports(struct file *file, char *buf, size_t size)
1138 {
1139         ssize_t rv;
1140
1141         mutex_lock(&nfsd_mutex);
1142         rv = __write_ports(file, buf, size);
1143         mutex_unlock(&nfsd_mutex);
1144         return rv;
1145 }
1146
1147
1148 int nfsd_max_blksize;
1149
1150 /**
1151  * write_maxblksize - Set or report the current NFS blksize
1152  *
1153  * Input:
1154  *                      buf:            ignored
1155  *                      size:           zero
1156  *
1157  * OR
1158  *
1159  * Input:
1160  *                      buf:            C string containing an unsigned
1161  *                                      integer value representing the new
1162  *                                      NFS blksize
1163  *                      size:           non-zero length of C string in @buf
1164  * Output:
1165  *      On success:     passed-in buffer filled with '\n'-terminated C string
1166  *                      containing numeric value of the current NFS blksize
1167  *                      setting;
1168  *                      return code is the size in bytes of the string
1169  *      On error:       return code is zero or a negative errno value
1170  */
1171 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
1172 {
1173         char *mesg = buf;
1174         if (size > 0) {
1175                 int bsize;
1176                 int rv = get_int(&mesg, &bsize);
1177                 if (rv)
1178                         return rv;
1179                 /* force bsize into allowed range and
1180                  * required alignment.
1181                  */
1182                 if (bsize < 1024)
1183                         bsize = 1024;
1184                 if (bsize > NFSSVC_MAXBLKSIZE)
1185                         bsize = NFSSVC_MAXBLKSIZE;
1186                 bsize &= ~(1024-1);
1187                 mutex_lock(&nfsd_mutex);
1188                 if (nfsd_serv && nfsd_serv->sv_nrthreads) {
1189                         mutex_unlock(&nfsd_mutex);
1190                         return -EBUSY;
1191                 }
1192                 nfsd_max_blksize = bsize;
1193                 mutex_unlock(&nfsd_mutex);
1194         }
1195
1196         return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
1197                                                         nfsd_max_blksize);
1198 }
1199
1200 #ifdef CONFIG_NFSD_V4
1201 extern time_t nfs4_leasetime(void);
1202
1203 static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
1204 {
1205         /* if size > 10 seconds, call
1206          * nfs4_reset_lease() then write out the new lease (seconds) as reply
1207          */
1208         char *mesg = buf;
1209         int rv, lease;
1210
1211         if (size > 0) {
1212                 if (nfsd_serv)
1213                         return -EBUSY;
1214                 rv = get_int(&mesg, &lease);
1215                 if (rv)
1216                         return rv;
1217                 if (lease < 10 || lease > 3600)
1218                         return -EINVAL;
1219                 nfs4_reset_lease(lease);
1220         }
1221
1222         return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n",
1223                                                         nfs4_lease_time());
1224 }
1225
1226 /**
1227  * write_leasetime - Set or report the current NFSv4 lease time
1228  *
1229  * Input:
1230  *                      buf:            ignored
1231  *                      size:           zero
1232  *
1233  * OR
1234  *
1235  * Input:
1236  *                      buf:            C string containing an unsigned
1237  *                                      integer value representing the new
1238  *                                      NFSv4 lease expiry time
1239  *                      size:           non-zero length of C string in @buf
1240  * Output:
1241  *      On success:     passed-in buffer filled with '\n'-terminated C
1242  *                      string containing unsigned integer value of the
1243  *                      current lease expiry time;
1244  *                      return code is the size in bytes of the string
1245  *      On error:       return code is zero or a negative errno value
1246  */
1247 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
1248 {
1249         ssize_t rv;
1250
1251         mutex_lock(&nfsd_mutex);
1252         rv = __write_leasetime(file, buf, size);
1253         mutex_unlock(&nfsd_mutex);
1254         return rv;
1255 }
1256
1257 extern char *nfs4_recoverydir(void);
1258
1259 static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
1260 {
1261         char *mesg = buf;
1262         char *recdir;
1263         int len, status;
1264
1265         if (size > 0) {
1266                 if (nfsd_serv)
1267                         return -EBUSY;
1268                 if (size > PATH_MAX || buf[size-1] != '\n')
1269                         return -EINVAL;
1270                 buf[size-1] = 0;
1271
1272                 recdir = mesg;
1273                 len = qword_get(&mesg, recdir, size);
1274                 if (len <= 0)
1275                         return -EINVAL;
1276
1277                 status = nfs4_reset_recoverydir(recdir);
1278         }
1279
1280         return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
1281                                                         nfs4_recoverydir());
1282 }
1283
1284 /**
1285  * write_recoverydir - Set or report the pathname of the recovery directory
1286  *
1287  * Input:
1288  *                      buf:            ignored
1289  *                      size:           zero
1290  *
1291  * OR
1292  *
1293  * Input:
1294  *                      buf:            C string containing the pathname
1295  *                                      of the directory on a local file
1296  *                                      system containing permanent NFSv4
1297  *                                      recovery data
1298  *                      size:           non-zero length of C string in @buf
1299  * Output:
1300  *      On success:     passed-in buffer filled with '\n'-terminated C string
1301  *                      containing the current recovery pathname setting;
1302  *                      return code is the size in bytes of the string
1303  *      On error:       return code is zero or a negative errno value
1304  */
1305 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
1306 {
1307         ssize_t rv;
1308
1309         mutex_lock(&nfsd_mutex);
1310         rv = __write_recoverydir(file, buf, size);
1311         mutex_unlock(&nfsd_mutex);
1312         return rv;
1313 }
1314
1315 #endif
1316
1317 /*----------------------------------------------------------------------------*/
1318 /*
1319  *      populating the filesystem.
1320  */
1321
1322 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
1323 {
1324         static struct tree_descr nfsd_files[] = {
1325                 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
1326                 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
1327                 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
1328                 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
1329                 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
1330                 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
1331                 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
1332                 [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
1333                 [NFSD_FO_UnlockIP] = {"unlock_ip",
1334                                         &transaction_ops, S_IWUSR|S_IRUSR},
1335                 [NFSD_FO_UnlockFS] = {"unlock_filesystem",
1336                                         &transaction_ops, S_IWUSR|S_IRUSR},
1337                 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
1338                 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
1339                 [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
1340                 [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
1341                 [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
1342                 [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
1343                 [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
1344 #ifdef CONFIG_NFSD_V4
1345                 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
1346                 [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
1347 #endif
1348                 /* last one */ {""}
1349         };
1350         return simple_fill_super(sb, 0x6e667364, nfsd_files);
1351 }
1352
1353 static int nfsd_get_sb(struct file_system_type *fs_type,
1354         int flags, const char *dev_name, void *data, struct vfsmount *mnt)
1355 {
1356         return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
1357 }
1358
1359 static struct file_system_type nfsd_fs_type = {
1360         .owner          = THIS_MODULE,
1361         .name           = "nfsd",
1362         .get_sb         = nfsd_get_sb,
1363         .kill_sb        = kill_litter_super,
1364 };
1365
1366 #ifdef CONFIG_PROC_FS
1367 static int create_proc_exports_entry(void)
1368 {
1369         struct proc_dir_entry *entry;
1370
1371         entry = proc_mkdir("fs/nfs", NULL);
1372         if (!entry)
1373                 return -ENOMEM;
1374         entry = proc_create("exports", 0, entry, &exports_operations);
1375         if (!entry)
1376                 return -ENOMEM;
1377         return 0;
1378 }
1379 #else /* CONFIG_PROC_FS */
1380 static int create_proc_exports_entry(void)
1381 {
1382         return 0;
1383 }
1384 #endif
1385
1386 static int __init init_nfsd(void)
1387 {
1388         int retval;
1389         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
1390
1391         retval = nfs4_state_init(); /* nfs4 locking state */
1392         if (retval)
1393                 return retval;
1394         nfsd_stat_init();       /* Statistics */
1395         retval = nfsd_reply_cache_init();
1396         if (retval)
1397                 goto out_free_stat;
1398         retval = nfsd_export_init();
1399         if (retval)
1400                 goto out_free_cache;
1401         nfsd_lockd_init();      /* lockd->nfsd callbacks */
1402         retval = nfsd_idmap_init();
1403         if (retval)
1404                 goto out_free_lockd;
1405         retval = create_proc_exports_entry();
1406         if (retval)
1407                 goto out_free_idmap;
1408         retval = register_filesystem(&nfsd_fs_type);
1409         if (retval)
1410                 goto out_free_all;
1411         return 0;
1412 out_free_all:
1413         remove_proc_entry("fs/nfs/exports", NULL);
1414         remove_proc_entry("fs/nfs", NULL);
1415 out_free_idmap:
1416         nfsd_idmap_shutdown();
1417 out_free_lockd:
1418         nfsd_lockd_shutdown();
1419         nfsd_export_shutdown();
1420 out_free_cache:
1421         nfsd_reply_cache_shutdown();
1422 out_free_stat:
1423         nfsd_stat_shutdown();
1424         nfsd4_free_slabs();
1425         return retval;
1426 }
1427
1428 static void __exit exit_nfsd(void)
1429 {
1430         nfsd_export_shutdown();
1431         nfsd_reply_cache_shutdown();
1432         remove_proc_entry("fs/nfs/exports", NULL);
1433         remove_proc_entry("fs/nfs", NULL);
1434         nfsd_stat_shutdown();
1435         nfsd_lockd_shutdown();
1436         nfsd_idmap_shutdown();
1437         nfsd4_free_slabs();
1438         unregister_filesystem(&nfsd_fs_type);
1439 }
1440
1441 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
1442 MODULE_LICENSE("GPL");
1443 module_init(init_nfsd)
1444 module_exit(exit_nfsd)