[PATCH] knfsd: remove nfsd_versbits as intermediate storage for desired versions
[safe/jmp/linux-2.6] / fs / nfsd / nfsctl.c
1 /*
2  * linux/fs/nfsd/nfsctl.c
3  *
4  * Syscall interface to knfsd.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include <linux/module.h>
10
11 #include <linux/linkage.h>
12 #include <linux/time.h>
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/fcntl.h>
16 #include <linux/net.h>
17 #include <linux/in.h>
18 #include <linux/syscalls.h>
19 #include <linux/unistd.h>
20 #include <linux/slab.h>
21 #include <linux/proc_fs.h>
22 #include <linux/seq_file.h>
23 #include <linux/pagemap.h>
24 #include <linux/init.h>
25 #include <linux/string.h>
26
27 #include <linux/nfs.h>
28 #include <linux/nfsd_idmap.h>
29 #include <linux/sunrpc/svc.h>
30 #include <linux/nfsd/nfsd.h>
31 #include <linux/nfsd/cache.h>
32 #include <linux/nfsd/xdr.h>
33 #include <linux/nfsd/syscall.h>
34 #include <linux/nfsd/interface.h>
35
36 #include <asm/uaccess.h>
37
38 /*
39  *      We have a single directory with 9 nodes in it.
40  */
41 enum {
42         NFSD_Root = 1,
43         NFSD_Svc,
44         NFSD_Add,
45         NFSD_Del,
46         NFSD_Export,
47         NFSD_Unexport,
48         NFSD_Getfd,
49         NFSD_Getfs,
50         NFSD_List,
51         NFSD_Fh,
52         NFSD_Threads,
53         NFSD_Versions,
54         /*
55          * The below MUST come last.  Otherwise we leave a hole in nfsd_files[]
56          * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops
57          */
58 #ifdef CONFIG_NFSD_V4
59         NFSD_Leasetime,
60         NFSD_RecoveryDir,
61 #endif
62 };
63
64 /*
65  * write() for these nodes.
66  */
67 static ssize_t write_svc(struct file *file, char *buf, size_t size);
68 static ssize_t write_add(struct file *file, char *buf, size_t size);
69 static ssize_t write_del(struct file *file, char *buf, size_t size);
70 static ssize_t write_export(struct file *file, char *buf, size_t size);
71 static ssize_t write_unexport(struct file *file, char *buf, size_t size);
72 static ssize_t write_getfd(struct file *file, char *buf, size_t size);
73 static ssize_t write_getfs(struct file *file, char *buf, size_t size);
74 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
75 static ssize_t write_threads(struct file *file, char *buf, size_t size);
76 static ssize_t write_versions(struct file *file, char *buf, size_t size);
77 #ifdef CONFIG_NFSD_V4
78 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
79 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
80 #endif
81
82 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
83         [NFSD_Svc] = write_svc,
84         [NFSD_Add] = write_add,
85         [NFSD_Del] = write_del,
86         [NFSD_Export] = write_export,
87         [NFSD_Unexport] = write_unexport,
88         [NFSD_Getfd] = write_getfd,
89         [NFSD_Getfs] = write_getfs,
90         [NFSD_Fh] = write_filehandle,
91         [NFSD_Threads] = write_threads,
92         [NFSD_Versions] = write_versions,
93 #ifdef CONFIG_NFSD_V4
94         [NFSD_Leasetime] = write_leasetime,
95         [NFSD_RecoveryDir] = write_recoverydir,
96 #endif
97 };
98
99 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
100 {
101         ino_t ino =  file->f_dentry->d_inode->i_ino;
102         char *data;
103         ssize_t rv;
104
105         if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
106                 return -EINVAL;
107
108         data = simple_transaction_get(file, buf, size);
109         if (IS_ERR(data))
110                 return PTR_ERR(data);
111
112         rv =  write_op[ino](file, data, size);
113         if (rv>0) {
114                 simple_transaction_set(file, rv);
115                 rv = size;
116         }
117         return rv;
118 }
119
120 static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
121 {
122         if (! file->private_data) {
123                 /* An attempt to read a transaction file without writing
124                  * causes a 0-byte write so that the file can return
125                  * state information
126                  */
127                 ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos);
128                 if (rv < 0)
129                         return rv;
130         }
131         return simple_transaction_read(file, buf, size, pos);
132 }
133
134 static const struct file_operations transaction_ops = {
135         .write          = nfsctl_transaction_write,
136         .read           = nfsctl_transaction_read,
137         .release        = simple_transaction_release,
138 };
139
140 extern struct seq_operations nfs_exports_op;
141 static int exports_open(struct inode *inode, struct file *file)
142 {
143         return seq_open(file, &nfs_exports_op);
144 }
145
146 static const struct file_operations exports_operations = {
147         .open           = exports_open,
148         .read           = seq_read,
149         .llseek         = seq_lseek,
150         .release        = seq_release,
151 };
152
153 /*----------------------------------------------------------------------------*/
154 /*
155  * payload - write methods
156  * If the method has a response, the response should be put in buf,
157  * and the length returned.  Otherwise return 0 or and -error.
158  */
159
160 static ssize_t write_svc(struct file *file, char *buf, size_t size)
161 {
162         struct nfsctl_svc *data;
163         if (size < sizeof(*data))
164                 return -EINVAL;
165         data = (struct nfsctl_svc*) buf;
166         return nfsd_svc(data->svc_port, data->svc_nthreads);
167 }
168
169 static ssize_t write_add(struct file *file, char *buf, size_t size)
170 {
171         struct nfsctl_client *data;
172         if (size < sizeof(*data))
173                 return -EINVAL;
174         data = (struct nfsctl_client *)buf;
175         return exp_addclient(data);
176 }
177
178 static ssize_t write_del(struct file *file, char *buf, size_t size)
179 {
180         struct nfsctl_client *data;
181         if (size < sizeof(*data))
182                 return -EINVAL;
183         data = (struct nfsctl_client *)buf;
184         return exp_delclient(data);
185 }
186
187 static ssize_t write_export(struct file *file, char *buf, size_t size)
188 {
189         struct nfsctl_export *data;
190         if (size < sizeof(*data))
191                 return -EINVAL;
192         data = (struct nfsctl_export*)buf;
193         return exp_export(data);
194 }
195
196 static ssize_t write_unexport(struct file *file, char *buf, size_t size)
197 {
198         struct nfsctl_export *data;
199
200         if (size < sizeof(*data))
201                 return -EINVAL;
202         data = (struct nfsctl_export*)buf;
203         return exp_unexport(data);
204 }
205
206 static ssize_t write_getfs(struct file *file, char *buf, size_t size)
207 {
208         struct nfsctl_fsparm *data;
209         struct sockaddr_in *sin;
210         struct auth_domain *clp;
211         int err = 0;
212         struct knfsd_fh *res;
213
214         if (size < sizeof(*data))
215                 return -EINVAL;
216         data = (struct nfsctl_fsparm*)buf;
217         err = -EPROTONOSUPPORT;
218         if (data->gd_addr.sa_family != AF_INET)
219                 goto out;
220         sin = (struct sockaddr_in *)&data->gd_addr;
221         if (data->gd_maxlen > NFS3_FHSIZE)
222                 data->gd_maxlen = NFS3_FHSIZE;
223
224         res = (struct knfsd_fh*)buf;
225
226         exp_readlock();
227         if (!(clp = auth_unix_lookup(sin->sin_addr)))
228                 err = -EPERM;
229         else {
230                 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
231                 auth_domain_put(clp);
232         }
233         exp_readunlock();
234         if (err == 0)
235                 err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
236  out:
237         return err;
238 }
239
240 static ssize_t write_getfd(struct file *file, char *buf, size_t size)
241 {
242         struct nfsctl_fdparm *data;
243         struct sockaddr_in *sin;
244         struct auth_domain *clp;
245         int err = 0;
246         struct knfsd_fh fh;
247         char *res;
248
249         if (size < sizeof(*data))
250                 return -EINVAL;
251         data = (struct nfsctl_fdparm*)buf;
252         err = -EPROTONOSUPPORT;
253         if (data->gd_addr.sa_family != AF_INET)
254                 goto out;
255         err = -EINVAL;
256         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
257                 goto out;
258
259         res = buf;
260         sin = (struct sockaddr_in *)&data->gd_addr;
261         exp_readlock();
262         if (!(clp = auth_unix_lookup(sin->sin_addr)))
263                 err = -EPERM;
264         else {
265                 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
266                 auth_domain_put(clp);
267         }
268         exp_readunlock();
269
270         if (err == 0) {
271                 memset(res,0, NFS_FHSIZE);
272                 memcpy(res, &fh.fh_base, fh.fh_size);
273                 err = NFS_FHSIZE;
274         }
275  out:
276         return err;
277 }
278
279 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
280 {
281         /* request is:
282          *   domain path maxsize
283          * response is
284          *   filehandle
285          *
286          * qword quoting is used, so filehandle will be \x....
287          */
288         char *dname, *path;
289         int maxsize;
290         char *mesg = buf;
291         int len;
292         struct auth_domain *dom;
293         struct knfsd_fh fh;
294
295         if (buf[size-1] != '\n')
296                 return -EINVAL;
297         buf[size-1] = 0;
298
299         dname = mesg;
300         len = qword_get(&mesg, dname, size);
301         if (len <= 0) return -EINVAL;
302         
303         path = dname+len+1;
304         len = qword_get(&mesg, path, size);
305         if (len <= 0) return -EINVAL;
306
307         len = get_int(&mesg, &maxsize);
308         if (len)
309                 return len;
310
311         if (maxsize < NFS_FHSIZE)
312                 return -EINVAL;
313         if (maxsize > NFS3_FHSIZE)
314                 maxsize = NFS3_FHSIZE;
315
316         if (qword_get(&mesg, mesg, size)>0)
317                 return -EINVAL;
318
319         /* we have all the words, they are in buf.. */
320         dom = unix_domain_find(dname);
321         if (!dom)
322                 return -ENOMEM;
323
324         len = exp_rootfh(dom, path, &fh,  maxsize);
325         auth_domain_put(dom);
326         if (len)
327                 return len;
328         
329         mesg = buf; len = SIMPLE_TRANSACTION_LIMIT;
330         qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
331         mesg[-1] = '\n';
332         return mesg - buf;      
333 }
334
335 extern int nfsd_nrthreads(void);
336
337 static ssize_t write_threads(struct file *file, char *buf, size_t size)
338 {
339         /* if size > 0, look for a number of threads and call nfsd_svc
340          * then write out number of threads as reply
341          */
342         char *mesg = buf;
343         int rv;
344         if (size > 0) {
345                 int newthreads;
346                 rv = get_int(&mesg, &newthreads);
347                 if (rv)
348                         return rv;
349                 if (newthreads <0)
350                         return -EINVAL;
351                 rv = nfsd_svc(2049, newthreads);
352                 if (rv)
353                         return rv;
354         }
355         sprintf(buf, "%d\n", nfsd_nrthreads());
356         return strlen(buf);
357 }
358
359 static ssize_t write_versions(struct file *file, char *buf, size_t size)
360 {
361         /*
362          * Format:
363          *   [-/+]vers [-/+]vers ...
364          */
365         char *mesg = buf;
366         char *vers, sign;
367         int len, num;
368         ssize_t tlen = 0;
369         char *sep;
370
371         if (size>0) {
372                 if (nfsd_serv)
373                         /* Cannot change versions without updating
374                          * nfsd_serv->sv_xdrsize, and reallocing
375                          * rq_argp and rq_resp
376                          */
377                         return -EBUSY;
378                 if (buf[size-1] != '\n')
379                         return -EINVAL;
380                 buf[size-1] = 0;
381
382                 vers = mesg;
383                 len = qword_get(&mesg, vers, size);
384                 if (len <= 0) return -EINVAL;
385                 do {
386                         sign = *vers;
387                         if (sign == '+' || sign == '-')
388                                 num = simple_strtol((vers+1), NULL, 0);
389                         else
390                                 num = simple_strtol(vers, NULL, 0);
391                         switch(num) {
392                         case 2:
393                         case 3:
394                         case 4:
395                                 nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
396                                 break;
397                         default:
398                                 return -EINVAL;
399                         }
400                         vers += len + 1;
401                         tlen += len;
402                 } while ((len = qword_get(&mesg, vers, size)) > 0);
403                 /* If all get turned off, turn them back on, as
404                  * having no versions is BAD
405                  */
406                 nfsd_reset_versions();
407         }
408         /* Now write current state into reply buffer */
409         len = 0;
410         sep = "";
411         for (num=2 ; num <= 4 ; num++)
412                 if (nfsd_vers(num, NFSD_AVAIL)) {
413                         len += sprintf(buf+len, "%s%c%d", sep,
414                                        nfsd_vers(num, NFSD_TEST)?'+':'-',
415                                        num);
416                         sep = " ";
417                 }
418         len += sprintf(buf+len, "\n");
419         return len;
420 }
421
422 #ifdef CONFIG_NFSD_V4
423 extern time_t nfs4_leasetime(void);
424
425 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
426 {
427         /* if size > 10 seconds, call
428          * nfs4_reset_lease() then write out the new lease (seconds) as reply
429          */
430         char *mesg = buf;
431         int rv;
432
433         if (size > 0) {
434                 int lease;
435                 rv = get_int(&mesg, &lease);
436                 if (rv)
437                         return rv;
438                 if (lease < 10 || lease > 3600)
439                         return -EINVAL;
440                 nfs4_reset_lease(lease);
441         }
442         sprintf(buf, "%ld\n", nfs4_lease_time());
443         return strlen(buf);
444 }
445
446 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
447 {
448         char *mesg = buf;
449         char *recdir;
450         int len, status;
451
452         if (size > PATH_MAX || buf[size-1] != '\n')
453                 return -EINVAL;
454         buf[size-1] = 0;
455
456         recdir = mesg;
457         len = qword_get(&mesg, recdir, size);
458         if (len <= 0)
459                 return -EINVAL;
460
461         status = nfs4_reset_recoverydir(recdir);
462         return strlen(buf);
463 }
464 #endif
465
466 /*----------------------------------------------------------------------------*/
467 /*
468  *      populating the filesystem.
469  */
470
471 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
472 {
473         static struct tree_descr nfsd_files[] = {
474                 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
475                 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
476                 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
477                 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
478                 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
479                 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
480                 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
481                 [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
482                 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
483                 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
484                 [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
485 #ifdef CONFIG_NFSD_V4
486                 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
487                 [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
488 #endif
489                 /* last one */ {""}
490         };
491         return simple_fill_super(sb, 0x6e667364, nfsd_files);
492 }
493
494 static int nfsd_get_sb(struct file_system_type *fs_type,
495         int flags, const char *dev_name, void *data, struct vfsmount *mnt)
496 {
497         return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
498 }
499
500 static struct file_system_type nfsd_fs_type = {
501         .owner          = THIS_MODULE,
502         .name           = "nfsd",
503         .get_sb         = nfsd_get_sb,
504         .kill_sb        = kill_litter_super,
505 };
506
507 static int __init init_nfsd(void)
508 {
509         int retval;
510         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
511
512         nfsd_stat_init();       /* Statistics */
513         nfsd_cache_init();      /* RPC reply cache */
514         nfsd_export_init();     /* Exports table */
515         nfsd_lockd_init();      /* lockd->nfsd callbacks */
516         nfs4_state_init();      /* NFSv4 locking state */
517         nfsd_idmap_init();      /* Name to ID mapping */
518         if (proc_mkdir("fs/nfs", NULL)) {
519                 struct proc_dir_entry *entry;
520                 entry = create_proc_entry("fs/nfs/exports", 0, NULL);
521                 if (entry)
522                         entry->proc_fops =  &exports_operations;
523         }
524         retval = register_filesystem(&nfsd_fs_type);
525         if (retval) {
526                 nfsd_export_shutdown();
527                 nfsd_cache_shutdown();
528                 remove_proc_entry("fs/nfs/exports", NULL);
529                 remove_proc_entry("fs/nfs", NULL);
530                 nfsd_stat_shutdown();
531                 nfsd_lockd_shutdown();
532         }
533         return retval;
534 }
535
536 static void __exit exit_nfsd(void)
537 {
538         nfsd_export_shutdown();
539         nfsd_cache_shutdown();
540         remove_proc_entry("fs/nfs/exports", NULL);
541         remove_proc_entry("fs/nfs", NULL);
542         nfsd_stat_shutdown();
543         nfsd_lockd_shutdown();
544         nfsd_idmap_shutdown();
545         unregister_filesystem(&nfsd_fs_type);
546 }
547
548 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
549 MODULE_LICENSE("GPL");
550 module_init(init_nfsd)
551 module_exit(exit_nfsd)