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