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