NFSD: Stricter buffer size checking in write_versions()
[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/smp_lock.h>
29 #include <linux/ctype.h>
30
31 #include <linux/nfs.h>
32 #include <linux/nfsd_idmap.h>
33 #include <linux/lockd/bind.h>
34 #include <linux/sunrpc/svc.h>
35 #include <linux/sunrpc/svcsock.h>
36 #include <linux/nfsd/nfsd.h>
37 #include <linux/nfsd/cache.h>
38 #include <linux/nfsd/xdr.h>
39 #include <linux/nfsd/syscall.h>
40 #include <linux/lockd/lockd.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
178 static struct file_operations pool_stats_operations = {
179         .open           = nfsd_pool_stats_open,
180         .read           = seq_read,
181         .llseek         = seq_lseek,
182         .release        = seq_release,
183         .owner          = THIS_MODULE,
184 };
185
186 /*----------------------------------------------------------------------------*/
187 /*
188  * payload - write methods
189  */
190
191 /**
192  * write_svc - Start kernel's NFSD server
193  *
194  * Deprecated.  /proc/fs/nfsd/threads is preferred.
195  * Function remains to support old versions of nfs-utils.
196  *
197  * Input:
198  *                      buf:    struct nfsctl_svc
199  *                              svc_port:       port number of this
200  *                                              server's listener
201  *                              svc_nthreads:   number of threads to start
202  *                      size:   size in bytes of passed in nfsctl_svc
203  * Output:
204  *      On success:     returns zero
205  *      On error:       return code is negative errno value
206  */
207 static ssize_t write_svc(struct file *file, char *buf, size_t size)
208 {
209         struct nfsctl_svc *data;
210         if (size < sizeof(*data))
211                 return -EINVAL;
212         data = (struct nfsctl_svc*) buf;
213         return nfsd_svc(data->svc_port, data->svc_nthreads);
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 IPv4 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  * Note: Only AF_INET client addresses are passed in
498  */
499 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
500 {
501         struct sockaddr_in sin = {
502                 .sin_family     = AF_INET,
503         };
504         int b1, b2, b3, b4;
505         char c;
506         char *fo_path;
507
508         /* sanity check */
509         if (size == 0)
510                 return -EINVAL;
511
512         if (buf[size-1] != '\n')
513                 return -EINVAL;
514
515         fo_path = buf;
516         if (qword_get(&buf, fo_path, size) < 0)
517                 return -EINVAL;
518
519         /* get ipv4 address */
520         if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
521                 return -EINVAL;
522         if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
523                 return -EINVAL;
524         sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
525
526         return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
527 }
528
529 /**
530  * write_unlock_fs - Release all locks on a local file system
531  *
532  * Experimental.
533  *
534  * Input:
535  *                      buf:    '\n'-terminated C string containing the
536  *                              absolute pathname of a local file system
537  *                      size:   length of C string in @buf
538  * Output:
539  *      On success:     returns zero if all specified locks were released;
540  *                      returns one if one or more locks were not released
541  *      On error:       return code is negative errno value
542  */
543 static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
544 {
545         struct path path;
546         char *fo_path;
547         int error;
548
549         /* sanity check */
550         if (size == 0)
551                 return -EINVAL;
552
553         if (buf[size-1] != '\n')
554                 return -EINVAL;
555
556         fo_path = buf;
557         if (qword_get(&buf, fo_path, size) < 0)
558                 return -EINVAL;
559
560         error = kern_path(fo_path, 0, &path);
561         if (error)
562                 return error;
563
564         /*
565          * XXX: Needs better sanity checking.  Otherwise we could end up
566          * releasing locks on the wrong file system.
567          *
568          * For example:
569          * 1.  Does the path refer to a directory?
570          * 2.  Is that directory a mount point, or
571          * 3.  Is that directory the root of an exported file system?
572          */
573         error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb);
574
575         path_put(&path);
576         return error;
577 }
578
579 /**
580  * write_filehandle - Get a variable-length NFS file handle by path
581  *
582  * On input, the buffer contains a '\n'-terminated C string comprised of
583  * three alphanumeric words separated by whitespace.  The string may
584  * contain escape sequences.
585  *
586  * Input:
587  *                      buf:
588  *                              domain:         client domain name
589  *                              path:           export pathname
590  *                              maxsize:        numeric maximum size of
591  *                                              @buf
592  *                      size:   length of C string in @buf
593  * Output:
594  *      On success:     passed-in buffer filled with '\n'-terminated C
595  *                      string containing a ASCII hex text version
596  *                      of the NFS file handle;
597  *                      return code is the size in bytes of the string
598  *      On error:       return code is negative errno value
599  */
600 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
601 {
602         char *dname, *path;
603         int uninitialized_var(maxsize);
604         char *mesg = buf;
605         int len;
606         struct auth_domain *dom;
607         struct knfsd_fh fh;
608
609         if (size == 0)
610                 return -EINVAL;
611
612         if (buf[size-1] != '\n')
613                 return -EINVAL;
614         buf[size-1] = 0;
615
616         dname = mesg;
617         len = qword_get(&mesg, dname, size);
618         if (len <= 0)
619                 return -EINVAL;
620         
621         path = dname+len+1;
622         len = qword_get(&mesg, path, size);
623         if (len <= 0)
624                 return -EINVAL;
625
626         len = get_int(&mesg, &maxsize);
627         if (len)
628                 return len;
629
630         if (maxsize < NFS_FHSIZE)
631                 return -EINVAL;
632         if (maxsize > NFS3_FHSIZE)
633                 maxsize = NFS3_FHSIZE;
634
635         if (qword_get(&mesg, mesg, size)>0)
636                 return -EINVAL;
637
638         /* we have all the words, they are in buf.. */
639         dom = unix_domain_find(dname);
640         if (!dom)
641                 return -ENOMEM;
642
643         len = exp_rootfh(dom, path, &fh,  maxsize);
644         auth_domain_put(dom);
645         if (len)
646                 return len;
647         
648         mesg = buf;
649         len = SIMPLE_TRANSACTION_LIMIT;
650         qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
651         mesg[-1] = '\n';
652         return mesg - buf;      
653 }
654
655 /**
656  * write_threads - Start NFSD, or report the current number of running threads
657  *
658  * Input:
659  *                      buf:            ignored
660  *                      size:           zero
661  * Output:
662  *      On success:     passed-in buffer filled with '\n'-terminated C
663  *                      string numeric value representing the number of
664  *                      running NFSD threads;
665  *                      return code is the size in bytes of the string
666  *      On error:       return code is zero
667  *
668  * OR
669  *
670  * Input:
671  *                      buf:            C string containing an unsigned
672  *                                      integer value representing the
673  *                                      number of NFSD threads to start
674  *                      size:           non-zero length of C string in @buf
675  * Output:
676  *      On success:     NFS service is started;
677  *                      passed-in buffer filled with '\n'-terminated C
678  *                      string numeric value representing the number of
679  *                      running NFSD threads;
680  *                      return code is the size in bytes of the string
681  *      On error:       return code is zero or a negative errno value
682  */
683 static ssize_t write_threads(struct file *file, char *buf, size_t size)
684 {
685         char *mesg = buf;
686         int rv;
687         if (size > 0) {
688                 int newthreads;
689                 rv = get_int(&mesg, &newthreads);
690                 if (rv)
691                         return rv;
692                 if (newthreads < 0)
693                         return -EINVAL;
694                 rv = nfsd_svc(NFS_PORT, newthreads);
695                 if (rv)
696                         return rv;
697         }
698         sprintf(buf, "%d\n", nfsd_nrthreads());
699         return strlen(buf);
700 }
701
702 /**
703  * write_pool_threads - Set or report the current number of threads per pool
704  *
705  * Input:
706  *                      buf:            ignored
707  *                      size:           zero
708  *
709  * OR
710  *
711  * Input:
712  *                      buf:            C string containing whitespace-
713  *                                      separated unsigned integer values
714  *                                      representing the number of NFSD
715  *                                      threads to start in each pool
716  *                      size:           non-zero length of C string in @buf
717  * Output:
718  *      On success:     passed-in buffer filled with '\n'-terminated C
719  *                      string containing integer values representing the
720  *                      number of NFSD threads in each pool;
721  *                      return code is the size in bytes of the string
722  *      On error:       return code is zero or a negative errno value
723  */
724 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
725 {
726         /* if size > 0, look for an array of number of threads per node
727          * and apply them  then write out number of threads per node as reply
728          */
729         char *mesg = buf;
730         int i;
731         int rv;
732         int len;
733         int npools;
734         int *nthreads;
735
736         mutex_lock(&nfsd_mutex);
737         npools = nfsd_nrpools();
738         if (npools == 0) {
739                 /*
740                  * NFS is shut down.  The admin can start it by
741                  * writing to the threads file but NOT the pool_threads
742                  * file, sorry.  Report zero threads.
743                  */
744                 mutex_unlock(&nfsd_mutex);
745                 strcpy(buf, "0\n");
746                 return strlen(buf);
747         }
748
749         nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
750         rv = -ENOMEM;
751         if (nthreads == NULL)
752                 goto out_free;
753
754         if (size > 0) {
755                 for (i = 0; i < npools; i++) {
756                         rv = get_int(&mesg, &nthreads[i]);
757                         if (rv == -ENOENT)
758                                 break;          /* fewer numbers than pools */
759                         if (rv)
760                                 goto out_free;  /* syntax error */
761                         rv = -EINVAL;
762                         if (nthreads[i] < 0)
763                                 goto out_free;
764                 }
765                 rv = nfsd_set_nrthreads(i, nthreads);
766                 if (rv)
767                         goto out_free;
768         }
769
770         rv = nfsd_get_nrthreads(npools, nthreads);
771         if (rv)
772                 goto out_free;
773
774         mesg = buf;
775         size = SIMPLE_TRANSACTION_LIMIT;
776         for (i = 0; i < npools && size > 0; i++) {
777                 snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' '));
778                 len = strlen(mesg);
779                 size -= len;
780                 mesg += len;
781         }
782
783         mutex_unlock(&nfsd_mutex);
784         return (mesg-buf);
785
786 out_free:
787         kfree(nthreads);
788         mutex_unlock(&nfsd_mutex);
789         return rv;
790 }
791
792 static ssize_t __write_versions(struct file *file, char *buf, size_t size)
793 {
794         char *mesg = buf;
795         char *vers, *minorp, sign;
796         int len, num, remaining;
797         unsigned minor;
798         ssize_t tlen = 0;
799         char *sep;
800
801         if (size>0) {
802                 if (nfsd_serv)
803                         /* Cannot change versions without updating
804                          * nfsd_serv->sv_xdrsize, and reallocing
805                          * rq_argp and rq_resp
806                          */
807                         return -EBUSY;
808                 if (buf[size-1] != '\n')
809                         return -EINVAL;
810                 buf[size-1] = 0;
811
812                 vers = mesg;
813                 len = qword_get(&mesg, vers, size);
814                 if (len <= 0) return -EINVAL;
815                 do {
816                         sign = *vers;
817                         if (sign == '+' || sign == '-')
818                                 num = simple_strtol((vers+1), &minorp, 0);
819                         else
820                                 num = simple_strtol(vers, &minorp, 0);
821                         if (*minorp == '.') {
822                                 if (num < 4)
823                                         return -EINVAL;
824                                 minor = simple_strtoul(minorp+1, NULL, 0);
825                                 if (minor == 0)
826                                         return -EINVAL;
827                                 if (nfsd_minorversion(minor, sign == '-' ?
828                                                      NFSD_CLEAR : NFSD_SET) < 0)
829                                         return -EINVAL;
830                                 goto next;
831                         }
832                         switch(num) {
833                         case 2:
834                         case 3:
835                         case 4:
836                                 nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
837                                 break;
838                         default:
839                                 return -EINVAL;
840                         }
841                 next:
842                         vers += len + 1;
843                 } while ((len = qword_get(&mesg, vers, size)) > 0);
844                 /* If all get turned off, turn them back on, as
845                  * having no versions is BAD
846                  */
847                 nfsd_reset_versions();
848         }
849
850         /* Now write current state into reply buffer */
851         len = 0;
852         sep = "";
853         remaining = SIMPLE_TRANSACTION_LIMIT;
854         for (num=2 ; num <= 4 ; num++)
855                 if (nfsd_vers(num, NFSD_AVAIL)) {
856                         len = snprintf(buf, remaining, "%s%c%d", sep,
857                                        nfsd_vers(num, NFSD_TEST)?'+':'-',
858                                        num);
859                         sep = " ";
860
861                         if (len > remaining)
862                                 break;
863                         remaining -= len;
864                         buf += len;
865                         tlen += len;
866                 }
867         if (nfsd_vers(4, NFSD_AVAIL))
868                 for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
869                      minor++) {
870                         len = snprintf(buf, remaining, " %c4.%u",
871                                         (nfsd_vers(4, NFSD_TEST) &&
872                                          nfsd_minorversion(minor, NFSD_TEST)) ?
873                                                 '+' : '-',
874                                         minor);
875
876                         if (len > remaining)
877                                 break;
878                         remaining -= len;
879                         buf += len;
880                         tlen += len;
881                 }
882
883         len = snprintf(buf, remaining, "\n");
884         if (len > remaining)
885                 return -EINVAL;
886         return tlen + len;
887 }
888
889 /**
890  * write_versions - Set or report the available NFS protocol versions
891  *
892  * Input:
893  *                      buf:            ignored
894  *                      size:           zero
895  * Output:
896  *      On success:     passed-in buffer filled with '\n'-terminated C
897  *                      string containing positive or negative integer
898  *                      values representing the current status of each
899  *                      protocol version;
900  *                      return code is the size in bytes of the string
901  *      On error:       return code is zero or a negative errno value
902  *
903  * OR
904  *
905  * Input:
906  *                      buf:            C string containing whitespace-
907  *                                      separated positive or negative
908  *                                      integer values representing NFS
909  *                                      protocol versions to enable ("+n")
910  *                                      or disable ("-n")
911  *                      size:           non-zero length of C string in @buf
912  * Output:
913  *      On success:     status of zero or more protocol versions has
914  *                      been updated; passed-in buffer filled with
915  *                      '\n'-terminated C string containing positive
916  *                      or negative integer values representing the
917  *                      current status of each protocol version;
918  *                      return code is the size in bytes of the string
919  *      On error:       return code is zero or a negative errno value
920  */
921 static ssize_t write_versions(struct file *file, char *buf, size_t size)
922 {
923         ssize_t rv;
924
925         mutex_lock(&nfsd_mutex);
926         rv = __write_versions(file, buf, size);
927         mutex_unlock(&nfsd_mutex);
928         return rv;
929 }
930
931 /*
932  * Zero-length write.  Return a list of NFSD's current listener
933  * transports.
934  */
935 static ssize_t __write_ports_names(char *buf)
936 {
937         if (nfsd_serv == NULL)
938                 return 0;
939         return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
940 }
941
942 /*
943  * A single 'fd' number was written, in which case it must be for
944  * a socket of a supported family/protocol, and we use it as an
945  * nfsd listener.
946  */
947 static ssize_t __write_ports_addfd(char *buf)
948 {
949         char *mesg = buf;
950         int fd, err;
951
952         err = get_int(&mesg, &fd);
953         if (err != 0 || fd < 0)
954                 return -EINVAL;
955
956         err = nfsd_create_serv();
957         if (err != 0)
958                 return err;
959
960         err = lockd_up();
961         if (err != 0)
962                 goto out;
963
964         err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
965         if (err < 0)
966                 lockd_down();
967
968 out:
969         /* Decrease the count, but don't shut down the service */
970         nfsd_serv->sv_nrthreads--;
971         return err;
972 }
973
974 /*
975  * A '-' followed by the 'name' of a socket means we close the socket.
976  */
977 static ssize_t __write_ports_delfd(char *buf)
978 {
979         char *toclose;
980         int len = 0;
981
982         toclose = kstrdup(buf + 1, GFP_KERNEL);
983         if (toclose == NULL)
984                 return -ENOMEM;
985
986         if (nfsd_serv != NULL)
987                 len = svc_sock_names(nfsd_serv, buf,
988                                         SIMPLE_TRANSACTION_LIMIT, toclose);
989         if (len >= 0)
990                 lockd_down();
991
992         kfree(toclose);
993         return len;
994 }
995
996 /*
997  * A transport listener is added by writing it's transport name and
998  * a port number.
999  */
1000 static ssize_t __write_ports_addxprt(char *buf)
1001 {
1002         char transport[16];
1003         int port, err;
1004
1005         if (sscanf(buf, "%15s %4u", transport, &port) != 2)
1006                 return -EINVAL;
1007
1008         if (port < 1 || port > USHORT_MAX)
1009                 return -EINVAL;
1010
1011         err = nfsd_create_serv();
1012         if (err != 0)
1013                 return err;
1014
1015         err = svc_create_xprt(nfsd_serv, transport,
1016                                 PF_INET, port, SVC_SOCK_ANONYMOUS);
1017         if (err < 0) {
1018                 /* Give a reasonable perror msg for bad transport string */
1019                 if (err == -ENOENT)
1020                         err = -EPROTONOSUPPORT;
1021                 return err;
1022         }
1023         return 0;
1024 }
1025
1026 /*
1027  * A transport listener is removed by writing a "-", it's transport
1028  * name, and it's port number.
1029  */
1030 static ssize_t __write_ports_delxprt(char *buf)
1031 {
1032         struct svc_xprt *xprt;
1033         char transport[16];
1034         int port;
1035
1036         if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
1037                 return -EINVAL;
1038
1039         if (port < 1 || port > USHORT_MAX || nfsd_serv == NULL)
1040                 return -EINVAL;
1041
1042         xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
1043         if (xprt == NULL)
1044                 return -ENOTCONN;
1045
1046         svc_close_xprt(xprt);
1047         svc_xprt_put(xprt);
1048         return 0;
1049 }
1050
1051 static ssize_t __write_ports(struct file *file, char *buf, size_t size)
1052 {
1053         if (size == 0)
1054                 return __write_ports_names(buf);
1055
1056         if (isdigit(buf[0]))
1057                 return __write_ports_addfd(buf);
1058
1059         if (buf[0] == '-' && isdigit(buf[1]))
1060                 return __write_ports_delfd(buf);
1061
1062         if (isalpha(buf[0]))
1063                 return __write_ports_addxprt(buf);
1064
1065         if (buf[0] == '-' && isalpha(buf[1]))
1066                 return __write_ports_delxprt(buf);
1067
1068         return -EINVAL;
1069 }
1070
1071 /**
1072  * write_ports - Pass a socket file descriptor or transport name to listen on
1073  *
1074  * Input:
1075  *                      buf:            ignored
1076  *                      size:           zero
1077  * Output:
1078  *      On success:     passed-in buffer filled with a '\n'-terminated C
1079  *                      string containing a whitespace-separated list of
1080  *                      named NFSD listeners;
1081  *                      return code is the size in bytes of the string
1082  *      On error:       return code is zero or a negative errno value
1083  *
1084  * OR
1085  *
1086  * Input:
1087  *                      buf:            C string containing an unsigned
1088  *                                      integer value representing a bound
1089  *                                      but unconnected socket that is to be
1090  *                                      used as an NFSD listener; listen(3)
1091  *                                      must be called for a SOCK_STREAM
1092  *                                      socket, otherwise it is ignored
1093  *                      size:           non-zero length of C string in @buf
1094  * Output:
1095  *      On success:     NFS service is started;
1096  *                      passed-in buffer filled with a '\n'-terminated C
1097  *                      string containing a unique alphanumeric name of
1098  *                      the listener;
1099  *                      return code is the size in bytes of the string
1100  *      On error:       return code is a negative errno value
1101  *
1102  * OR
1103  *
1104  * Input:
1105  *                      buf:            C string containing a "-" followed
1106  *                                      by an integer value representing a
1107  *                                      previously passed in socket file
1108  *                                      descriptor
1109  *                      size:           non-zero length of C string in @buf
1110  * Output:
1111  *      On success:     NFS service no longer listens on that socket;
1112  *                      passed-in buffer filled with a '\n'-terminated C
1113  *                      string containing a unique name of the listener;
1114  *                      return code is the size in bytes of the string
1115  *      On error:       return code is a negative errno value
1116  *
1117  * OR
1118  *
1119  * Input:
1120  *                      buf:            C string containing a transport
1121  *                                      name and an unsigned integer value
1122  *                                      representing the port to listen on,
1123  *                                      separated by whitespace
1124  *                      size:           non-zero length of C string in @buf
1125  * Output:
1126  *      On success:     returns zero; NFS service is started
1127  *      On error:       return code is a negative errno value
1128  *
1129  * OR
1130  *
1131  * Input:
1132  *                      buf:            C string containing a "-" followed
1133  *                                      by a transport name and an unsigned
1134  *                                      integer value representing the port
1135  *                                      to listen on, separated by whitespace
1136  *                      size:           non-zero length of C string in @buf
1137  * Output:
1138  *      On success:     returns zero; NFS service no longer listens
1139  *                      on that transport
1140  *      On error:       return code is a negative errno value
1141  */
1142 static ssize_t write_ports(struct file *file, char *buf, size_t size)
1143 {
1144         ssize_t rv;
1145
1146         mutex_lock(&nfsd_mutex);
1147         rv = __write_ports(file, buf, size);
1148         mutex_unlock(&nfsd_mutex);
1149         return rv;
1150 }
1151
1152
1153 int nfsd_max_blksize;
1154
1155 /**
1156  * write_maxblksize - Set or report the current NFS blksize
1157  *
1158  * Input:
1159  *                      buf:            ignored
1160  *                      size:           zero
1161  *
1162  * OR
1163  *
1164  * Input:
1165  *                      buf:            C string containing an unsigned
1166  *                                      integer value representing the new
1167  *                                      NFS blksize
1168  *                      size:           non-zero length of C string in @buf
1169  * Output:
1170  *      On success:     passed-in buffer filled with '\n'-terminated C string
1171  *                      containing numeric value of the current NFS blksize
1172  *                      setting;
1173  *                      return code is the size in bytes of the string
1174  *      On error:       return code is zero or a negative errno value
1175  */
1176 static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
1177 {
1178         char *mesg = buf;
1179         if (size > 0) {
1180                 int bsize;
1181                 int rv = get_int(&mesg, &bsize);
1182                 if (rv)
1183                         return rv;
1184                 /* force bsize into allowed range and
1185                  * required alignment.
1186                  */
1187                 if (bsize < 1024)
1188                         bsize = 1024;
1189                 if (bsize > NFSSVC_MAXBLKSIZE)
1190                         bsize = NFSSVC_MAXBLKSIZE;
1191                 bsize &= ~(1024-1);
1192                 mutex_lock(&nfsd_mutex);
1193                 if (nfsd_serv && nfsd_serv->sv_nrthreads) {
1194                         mutex_unlock(&nfsd_mutex);
1195                         return -EBUSY;
1196                 }
1197                 nfsd_max_blksize = bsize;
1198                 mutex_unlock(&nfsd_mutex);
1199         }
1200         return sprintf(buf, "%d\n", nfsd_max_blksize);
1201 }
1202
1203 #ifdef CONFIG_NFSD_V4
1204 extern time_t nfs4_leasetime(void);
1205
1206 static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
1207 {
1208         /* if size > 10 seconds, call
1209          * nfs4_reset_lease() then write out the new lease (seconds) as reply
1210          */
1211         char *mesg = buf;
1212         int rv, lease;
1213
1214         if (size > 0) {
1215                 if (nfsd_serv)
1216                         return -EBUSY;
1217                 rv = get_int(&mesg, &lease);
1218                 if (rv)
1219                         return rv;
1220                 if (lease < 10 || lease > 3600)
1221                         return -EINVAL;
1222                 nfs4_reset_lease(lease);
1223         }
1224         sprintf(buf, "%ld\n", nfs4_lease_time());
1225         return strlen(buf);
1226 }
1227
1228 /**
1229  * write_leasetime - Set or report the current NFSv4 lease time
1230  *
1231  * Input:
1232  *                      buf:            ignored
1233  *                      size:           zero
1234  *
1235  * OR
1236  *
1237  * Input:
1238  *                      buf:            C string containing an unsigned
1239  *                                      integer value representing the new
1240  *                                      NFSv4 lease expiry time
1241  *                      size:           non-zero length of C string in @buf
1242  * Output:
1243  *      On success:     passed-in buffer filled with '\n'-terminated C
1244  *                      string containing unsigned integer value of the
1245  *                      current lease expiry time;
1246  *                      return code is the size in bytes of the string
1247  *      On error:       return code is zero or a negative errno value
1248  */
1249 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
1250 {
1251         ssize_t rv;
1252
1253         mutex_lock(&nfsd_mutex);
1254         rv = __write_leasetime(file, buf, size);
1255         mutex_unlock(&nfsd_mutex);
1256         return rv;
1257 }
1258
1259 extern char *nfs4_recoverydir(void);
1260
1261 static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
1262 {
1263         char *mesg = buf;
1264         char *recdir;
1265         int len, status;
1266
1267         if (size > 0) {
1268                 if (nfsd_serv)
1269                         return -EBUSY;
1270                 if (size > PATH_MAX || buf[size-1] != '\n')
1271                         return -EINVAL;
1272                 buf[size-1] = 0;
1273
1274                 recdir = mesg;
1275                 len = qword_get(&mesg, recdir, size);
1276                 if (len <= 0)
1277                         return -EINVAL;
1278
1279                 status = nfs4_reset_recoverydir(recdir);
1280         }
1281
1282         return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
1283                                                         nfs4_recoverydir());
1284 }
1285
1286 /**
1287  * write_recoverydir - Set or report the pathname of the recovery directory
1288  *
1289  * Input:
1290  *                      buf:            ignored
1291  *                      size:           zero
1292  *
1293  * OR
1294  *
1295  * Input:
1296  *                      buf:            C string containing the pathname
1297  *                                      of the directory on a local file
1298  *                                      system containing permanent NFSv4
1299  *                                      recovery data
1300  *                      size:           non-zero length of C string in @buf
1301  * Output:
1302  *      On success:     passed-in buffer filled with '\n'-terminated C string
1303  *                      containing the current recovery pathname setting;
1304  *                      return code is the size in bytes of the string
1305  *      On error:       return code is zero or a negative errno value
1306  */
1307 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
1308 {
1309         ssize_t rv;
1310
1311         mutex_lock(&nfsd_mutex);
1312         rv = __write_recoverydir(file, buf, size);
1313         mutex_unlock(&nfsd_mutex);
1314         return rv;
1315 }
1316
1317 #endif
1318
1319 /*----------------------------------------------------------------------------*/
1320 /*
1321  *      populating the filesystem.
1322  */
1323
1324 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
1325 {
1326         static struct tree_descr nfsd_files[] = {
1327                 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
1328                 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
1329                 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
1330                 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
1331                 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
1332                 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
1333                 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
1334                 [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
1335                 [NFSD_FO_UnlockIP] = {"unlock_ip",
1336                                         &transaction_ops, S_IWUSR|S_IRUSR},
1337                 [NFSD_FO_UnlockFS] = {"unlock_filesystem",
1338                                         &transaction_ops, S_IWUSR|S_IRUSR},
1339                 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
1340                 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
1341                 [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
1342                 [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
1343                 [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
1344                 [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
1345                 [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
1346 #ifdef CONFIG_NFSD_V4
1347                 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
1348                 [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
1349 #endif
1350                 /* last one */ {""}
1351         };
1352         return simple_fill_super(sb, 0x6e667364, nfsd_files);
1353 }
1354
1355 static int nfsd_get_sb(struct file_system_type *fs_type,
1356         int flags, const char *dev_name, void *data, struct vfsmount *mnt)
1357 {
1358         return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
1359 }
1360
1361 static struct file_system_type nfsd_fs_type = {
1362         .owner          = THIS_MODULE,
1363         .name           = "nfsd",
1364         .get_sb         = nfsd_get_sb,
1365         .kill_sb        = kill_litter_super,
1366 };
1367
1368 #ifdef CONFIG_PROC_FS
1369 static int create_proc_exports_entry(void)
1370 {
1371         struct proc_dir_entry *entry;
1372
1373         entry = proc_mkdir("fs/nfs", NULL);
1374         if (!entry)
1375                 return -ENOMEM;
1376         entry = proc_create("exports", 0, entry, &exports_operations);
1377         if (!entry)
1378                 return -ENOMEM;
1379         return 0;
1380 }
1381 #else /* CONFIG_PROC_FS */
1382 static int create_proc_exports_entry(void)
1383 {
1384         return 0;
1385 }
1386 #endif
1387
1388 static int __init init_nfsd(void)
1389 {
1390         int retval;
1391         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
1392
1393         retval = nfs4_state_init(); /* nfs4 locking state */
1394         if (retval)
1395                 return retval;
1396         nfsd_stat_init();       /* Statistics */
1397         retval = nfsd_reply_cache_init();
1398         if (retval)
1399                 goto out_free_stat;
1400         retval = nfsd_export_init();
1401         if (retval)
1402                 goto out_free_cache;
1403         nfsd_lockd_init();      /* lockd->nfsd callbacks */
1404         retval = nfsd_idmap_init();
1405         if (retval)
1406                 goto out_free_lockd;
1407         retval = create_proc_exports_entry();
1408         if (retval)
1409                 goto out_free_idmap;
1410         retval = register_filesystem(&nfsd_fs_type);
1411         if (retval)
1412                 goto out_free_all;
1413         return 0;
1414 out_free_all:
1415         remove_proc_entry("fs/nfs/exports", NULL);
1416         remove_proc_entry("fs/nfs", NULL);
1417 out_free_idmap:
1418         nfsd_idmap_shutdown();
1419 out_free_lockd:
1420         nfsd_lockd_shutdown();
1421         nfsd_export_shutdown();
1422 out_free_cache:
1423         nfsd_reply_cache_shutdown();
1424 out_free_stat:
1425         nfsd_stat_shutdown();
1426         nfsd4_free_slabs();
1427         return retval;
1428 }
1429
1430 static void __exit exit_nfsd(void)
1431 {
1432         nfsd_export_shutdown();
1433         nfsd_reply_cache_shutdown();
1434         remove_proc_entry("fs/nfs/exports", NULL);
1435         remove_proc_entry("fs/nfs", NULL);
1436         nfsd_stat_shutdown();
1437         nfsd_lockd_shutdown();
1438         nfsd_idmap_shutdown();
1439         nfsd4_free_slabs();
1440         unregister_filesystem(&nfsd_fs_type);
1441 }
1442
1443 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
1444 MODULE_LICENSE("GPL");
1445 module_init(init_nfsd)
1446 module_exit(exit_nfsd)