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