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