VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.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
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_Leasetime,
54 };
55
56 /*
57  * write() for these nodes.
58  */
59 static ssize_t write_svc(struct file *file, char *buf, size_t size);
60 static ssize_t write_add(struct file *file, char *buf, size_t size);
61 static ssize_t write_del(struct file *file, char *buf, size_t size);
62 static ssize_t write_export(struct file *file, char *buf, size_t size);
63 static ssize_t write_unexport(struct file *file, char *buf, size_t size);
64 static ssize_t write_getfd(struct file *file, char *buf, size_t size);
65 static ssize_t write_getfs(struct file *file, char *buf, size_t size);
66 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
67 static ssize_t write_threads(struct file *file, char *buf, size_t size);
68 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
69
70 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
71         [NFSD_Svc] = write_svc,
72         [NFSD_Add] = write_add,
73         [NFSD_Del] = write_del,
74         [NFSD_Export] = write_export,
75         [NFSD_Unexport] = write_unexport,
76         [NFSD_Getfd] = write_getfd,
77         [NFSD_Getfs] = write_getfs,
78         [NFSD_Fh] = write_filehandle,
79         [NFSD_Threads] = write_threads,
80         [NFSD_Leasetime] = write_leasetime,
81 };
82
83 /* an argresp is stored in an allocated page and holds the 
84  * size of the argument or response, along with its content
85  */
86 struct argresp {
87         ssize_t size;
88         char data[0];
89 };
90
91 /*
92  * transaction based IO methods.
93  * The file expects a single write which triggers the transaction, and then
94  * possibly a read which collects the result - which is stored in a 
95  * file-local buffer.
96  */
97 static ssize_t TA_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
98 {
99         ino_t ino =  file->f_dentry->d_inode->i_ino;
100         struct argresp *ar;
101         ssize_t rv = 0;
102
103         if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
104                 return -EINVAL;
105         if (file->private_data) 
106                 return -EINVAL; /* only one write allowed per open */
107         if (size > PAGE_SIZE - sizeof(struct argresp))
108                 return -EFBIG;
109
110         ar = kmalloc(PAGE_SIZE, GFP_KERNEL);
111         if (!ar)
112                 return -ENOMEM;
113         ar->size = 0;
114         down(&file->f_dentry->d_inode->i_sem);
115         if (file->private_data)
116                 rv = -EINVAL;
117         else
118                 file->private_data = ar;
119         up(&file->f_dentry->d_inode->i_sem);
120         if (rv) {
121                 kfree(ar);
122                 return rv;
123         }
124         if (copy_from_user(ar->data, buf, size))
125                 return -EFAULT;
126         
127         rv =  write_op[ino](file, ar->data, size);
128         if (rv>0) {
129                 ar->size = rv;
130                 rv = size;
131         }
132         return rv;
133 }
134
135
136 static ssize_t TA_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
137 {
138         struct argresp *ar;
139         ssize_t rv = 0;
140         
141         if (file->private_data == NULL)
142                 rv = TA_write(file, buf, 0, pos);
143         if (rv < 0)
144                 return rv;
145
146         ar = file->private_data;
147         if (!ar)
148                 return 0;
149         if (*pos >= ar->size)
150                 return 0;
151         if (*pos + size > ar->size)
152                 size = ar->size - *pos;
153         if (copy_to_user(buf, ar->data + *pos, size))
154                 return -EFAULT;
155         *pos += size;
156         return size;
157 }
158
159 static int TA_open(struct inode *inode, struct file *file)
160 {
161         file->private_data = NULL;
162         return 0;
163 }
164
165 static int TA_release(struct inode *inode, struct file *file)
166 {
167         void *p = file->private_data;
168         file->private_data = NULL;
169         kfree(p);
170         return 0;
171 }
172
173 static struct file_operations transaction_ops = {
174         .write          = TA_write,
175         .read           = TA_read,
176         .open           = TA_open,
177         .release        = TA_release,
178 };
179
180 extern struct seq_operations nfs_exports_op;
181 static int exports_open(struct inode *inode, struct file *file)
182 {
183         return seq_open(file, &nfs_exports_op);
184 }
185
186 static struct file_operations exports_operations = {
187         .open           = exports_open,
188         .read           = seq_read,
189         .llseek         = seq_lseek,
190         .release        = seq_release,
191 };
192
193 /*----------------------------------------------------------------------------*/
194 /*
195  * payload - write methods
196  * If the method has a response, the response should be put in buf,
197  * and the length returned.  Otherwise return 0 or and -error.
198  */
199
200 static ssize_t write_svc(struct file *file, char *buf, size_t size)
201 {
202         struct nfsctl_svc *data;
203         if (size < sizeof(*data))
204                 return -EINVAL;
205         data = (struct nfsctl_svc*) buf;
206         return nfsd_svc(data->svc_port, data->svc_nthreads);
207 }
208
209 static ssize_t write_add(struct file *file, char *buf, size_t size)
210 {
211         struct nfsctl_client *data;
212         if (size < sizeof(*data))
213                 return -EINVAL;
214         data = (struct nfsctl_client *)buf;
215         return exp_addclient(data);
216 }
217
218 static ssize_t write_del(struct file *file, char *buf, size_t size)
219 {
220         struct nfsctl_client *data;
221         if (size < sizeof(*data))
222                 return -EINVAL;
223         data = (struct nfsctl_client *)buf;
224         return exp_delclient(data);
225 }
226
227 static ssize_t write_export(struct file *file, char *buf, size_t size)
228 {
229         struct nfsctl_export *data;
230         if (size < sizeof(*data))
231                 return -EINVAL;
232         data = (struct nfsctl_export*)buf;
233         return exp_export(data);
234 }
235
236 static ssize_t write_unexport(struct file *file, char *buf, size_t size)
237 {
238         struct nfsctl_export *data;
239
240         if (size < sizeof(*data))
241                 return -EINVAL;
242         data = (struct nfsctl_export*)buf;
243         return exp_unexport(data);
244 }
245
246 static ssize_t write_getfs(struct file *file, char *buf, size_t size)
247 {
248         struct nfsctl_fsparm *data;
249         struct sockaddr_in *sin;
250         struct auth_domain *clp;
251         int err = 0;
252         struct knfsd_fh *res;
253
254         if (size < sizeof(*data))
255                 return -EINVAL;
256         data = (struct nfsctl_fsparm*)buf;
257         err = -EPROTONOSUPPORT;
258         if (data->gd_addr.sa_family != AF_INET)
259                 goto out;
260         sin = (struct sockaddr_in *)&data->gd_addr;
261         if (data->gd_maxlen > NFS3_FHSIZE)
262                 data->gd_maxlen = NFS3_FHSIZE;
263
264         res = (struct knfsd_fh*)buf;
265
266         exp_readlock();
267         if (!(clp = auth_unix_lookup(sin->sin_addr)))
268                 err = -EPERM;
269         else {
270                 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
271                 auth_domain_put(clp);
272         }
273         exp_readunlock();
274         if (err == 0)
275                 err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
276  out:
277         return err;
278 }
279
280 static ssize_t write_getfd(struct file *file, char *buf, size_t size)
281 {
282         struct nfsctl_fdparm *data;
283         struct sockaddr_in *sin;
284         struct auth_domain *clp;
285         int err = 0;
286         struct knfsd_fh fh;
287         char *res;
288
289         if (size < sizeof(*data))
290                 return -EINVAL;
291         data = (struct nfsctl_fdparm*)buf;
292         err = -EPROTONOSUPPORT;
293         if (data->gd_addr.sa_family != AF_INET)
294                 goto out;
295         err = -EINVAL;
296         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
297                 goto out;
298
299         res = buf;
300         sin = (struct sockaddr_in *)&data->gd_addr;
301         exp_readlock();
302         if (!(clp = auth_unix_lookup(sin->sin_addr)))
303                 err = -EPERM;
304         else {
305                 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
306                 auth_domain_put(clp);
307         }
308         exp_readunlock();
309
310         if (err == 0) {
311                 memset(res,0, NFS_FHSIZE);
312                 memcpy(res, &fh.fh_base, fh.fh_size);
313                 err = NFS_FHSIZE;
314         }
315  out:
316         return err;
317 }
318
319 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
320 {
321         /* request is:
322          *   domain path maxsize
323          * response is
324          *   filehandle
325          *
326          * qword quoting is used, so filehandle will be \x....
327          */
328         char *dname, *path;
329         int maxsize;
330         char *mesg = buf;
331         int len;
332         struct auth_domain *dom;
333         struct knfsd_fh fh;
334
335         if (buf[size-1] != '\n')
336                 return -EINVAL;
337         buf[size-1] = 0;
338
339         dname = mesg;
340         len = qword_get(&mesg, dname, size);
341         if (len <= 0) return -EINVAL;
342         
343         path = dname+len+1;
344         len = qword_get(&mesg, path, size);
345         if (len <= 0) return -EINVAL;
346
347         len = get_int(&mesg, &maxsize);
348         if (len)
349                 return len;
350
351         if (maxsize < NFS_FHSIZE)
352                 return -EINVAL;
353         if (maxsize > NFS3_FHSIZE)
354                 maxsize = NFS3_FHSIZE;
355
356         if (qword_get(&mesg, mesg, size)>0)
357                 return -EINVAL;
358
359         /* we have all the words, they are in buf.. */
360         dom = unix_domain_find(dname);
361         if (!dom)
362                 return -ENOMEM;
363
364         len = exp_rootfh(dom, path, &fh,  maxsize);
365         auth_domain_put(dom);
366         if (len)
367                 return len;
368         
369         mesg = buf; len = PAGE_SIZE-sizeof(struct argresp);
370         qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
371         mesg[-1] = '\n';
372         return mesg - buf;      
373 }
374
375 extern int nfsd_nrthreads(void);
376
377 static ssize_t write_threads(struct file *file, char *buf, size_t size)
378 {
379         /* if size > 0, look for a number of threads and call nfsd_svc
380          * then write out number of threads as reply
381          */
382         char *mesg = buf;
383         int rv;
384         if (size > 0) {
385                 int newthreads;
386                 rv = get_int(&mesg, &newthreads);
387                 if (rv)
388                         return rv;
389                 if (newthreads <0)
390                         return -EINVAL;
391                 rv = nfsd_svc(2049, newthreads);
392                 if (rv)
393                         return rv;
394         }
395         sprintf(buf, "%d\n", nfsd_nrthreads());
396         return strlen(buf);
397 }
398
399 extern time_t nfs4_leasetime(void);
400
401 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
402 {
403         /* if size > 10 seconds, call
404          * nfs4_reset_lease() then write out the new lease (seconds) as reply
405          */
406         char *mesg = buf;
407         int rv;
408
409         if (size > 0) {
410                 int lease;
411                 rv = get_int(&mesg, &lease);
412                 if (rv)
413                         return rv;
414                 if (lease < 10 || lease > 3600)
415                         return -EINVAL;
416                 nfs4_reset_lease(lease);
417         }
418         sprintf(buf, "%ld\n", nfs4_lease_time());
419         return strlen(buf);
420 }
421
422 /*----------------------------------------------------------------------------*/
423 /*
424  *      populating the filesystem.
425  */
426
427 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
428 {
429         static struct tree_descr nfsd_files[] = {
430                 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
431                 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
432                 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
433                 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
434                 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
435                 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
436                 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
437                 [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
438                 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
439                 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
440 #ifdef CONFIG_NFSD_V4
441                 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
442 #endif
443                 /* last one */ {""}
444         };
445         return simple_fill_super(sb, 0x6e667364, nfsd_files);
446 }
447
448 static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
449         int flags, const char *dev_name, void *data)
450 {
451         return get_sb_single(fs_type, flags, data, nfsd_fill_super);
452 }
453
454 static struct file_system_type nfsd_fs_type = {
455         .owner          = THIS_MODULE,
456         .name           = "nfsd",
457         .get_sb         = nfsd_get_sb,
458         .kill_sb        = kill_litter_super,
459 };
460
461 static int __init init_nfsd(void)
462 {
463         int retval;
464         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
465
466         nfsd_stat_init();       /* Statistics */
467         nfsd_cache_init();      /* RPC reply cache */
468         nfsd_export_init();     /* Exports table */
469         nfsd_lockd_init();      /* lockd->nfsd callbacks */
470 #ifdef CONFIG_NFSD_V4
471         nfsd_idmap_init();      /* Name to ID mapping */
472 #endif /* CONFIG_NFSD_V4 */
473         if (proc_mkdir("fs/nfs", NULL)) {
474                 struct proc_dir_entry *entry;
475                 entry = create_proc_entry("fs/nfs/exports", 0, NULL);
476                 if (entry)
477                         entry->proc_fops =  &exports_operations;
478         }
479         retval = register_filesystem(&nfsd_fs_type);
480         if (retval) {
481                 nfsd_export_shutdown();
482                 nfsd_cache_shutdown();
483                 remove_proc_entry("fs/nfs/exports", NULL);
484                 remove_proc_entry("fs/nfs", NULL);
485                 nfsd_stat_shutdown();
486                 nfsd_lockd_shutdown();
487         }
488         return retval;
489 }
490
491 static void __exit exit_nfsd(void)
492 {
493         nfsd_export_shutdown();
494         nfsd_cache_shutdown();
495         remove_proc_entry("fs/nfs/exports", NULL);
496         remove_proc_entry("fs/nfs", NULL);
497         nfsd_stat_shutdown();
498         nfsd_lockd_shutdown();
499 #ifdef CONFIG_NFSD_V4
500         nfsd_idmap_shutdown();
501 #endif /* CONFIG_NFSD_V4 */
502         unregister_filesystem(&nfsd_fs_type);
503 }
504
505 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
506 MODULE_LICENSE("GPL");
507 module_init(init_nfsd)
508 module_exit(exit_nfsd)