[PATCH] knfsd: nfsd4: clean up state initialization
[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/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 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
84 {
85         ino_t ino =  file->f_dentry->d_inode->i_ino;
86         char *data;
87         ssize_t rv;
88
89         if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino])
90                 return -EINVAL;
91
92         data = simple_transaction_get(file, buf, size);
93         if (IS_ERR(data))
94                 return PTR_ERR(data);
95
96         rv =  write_op[ino](file, data, size);
97         if (rv>0) {
98                 simple_transaction_set(file, rv);
99                 rv = size;
100         }
101         return rv;
102 }
103
104 static struct file_operations transaction_ops = {
105         .write          = nfsctl_transaction_write,
106         .read           = simple_transaction_read,
107         .release        = simple_transaction_release,
108 };
109
110 extern struct seq_operations nfs_exports_op;
111 static int exports_open(struct inode *inode, struct file *file)
112 {
113         return seq_open(file, &nfs_exports_op);
114 }
115
116 static struct file_operations exports_operations = {
117         .open           = exports_open,
118         .read           = seq_read,
119         .llseek         = seq_lseek,
120         .release        = seq_release,
121 };
122
123 /*----------------------------------------------------------------------------*/
124 /*
125  * payload - write methods
126  * If the method has a response, the response should be put in buf,
127  * and the length returned.  Otherwise return 0 or and -error.
128  */
129
130 static ssize_t write_svc(struct file *file, char *buf, size_t size)
131 {
132         struct nfsctl_svc *data;
133         if (size < sizeof(*data))
134                 return -EINVAL;
135         data = (struct nfsctl_svc*) buf;
136         return nfsd_svc(data->svc_port, data->svc_nthreads);
137 }
138
139 static ssize_t write_add(struct file *file, char *buf, size_t size)
140 {
141         struct nfsctl_client *data;
142         if (size < sizeof(*data))
143                 return -EINVAL;
144         data = (struct nfsctl_client *)buf;
145         return exp_addclient(data);
146 }
147
148 static ssize_t write_del(struct file *file, char *buf, size_t size)
149 {
150         struct nfsctl_client *data;
151         if (size < sizeof(*data))
152                 return -EINVAL;
153         data = (struct nfsctl_client *)buf;
154         return exp_delclient(data);
155 }
156
157 static ssize_t write_export(struct file *file, char *buf, size_t size)
158 {
159         struct nfsctl_export *data;
160         if (size < sizeof(*data))
161                 return -EINVAL;
162         data = (struct nfsctl_export*)buf;
163         return exp_export(data);
164 }
165
166 static ssize_t write_unexport(struct file *file, char *buf, size_t size)
167 {
168         struct nfsctl_export *data;
169
170         if (size < sizeof(*data))
171                 return -EINVAL;
172         data = (struct nfsctl_export*)buf;
173         return exp_unexport(data);
174 }
175
176 static ssize_t write_getfs(struct file *file, char *buf, size_t size)
177 {
178         struct nfsctl_fsparm *data;
179         struct sockaddr_in *sin;
180         struct auth_domain *clp;
181         int err = 0;
182         struct knfsd_fh *res;
183
184         if (size < sizeof(*data))
185                 return -EINVAL;
186         data = (struct nfsctl_fsparm*)buf;
187         err = -EPROTONOSUPPORT;
188         if (data->gd_addr.sa_family != AF_INET)
189                 goto out;
190         sin = (struct sockaddr_in *)&data->gd_addr;
191         if (data->gd_maxlen > NFS3_FHSIZE)
192                 data->gd_maxlen = NFS3_FHSIZE;
193
194         res = (struct knfsd_fh*)buf;
195
196         exp_readlock();
197         if (!(clp = auth_unix_lookup(sin->sin_addr)))
198                 err = -EPERM;
199         else {
200                 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
201                 auth_domain_put(clp);
202         }
203         exp_readunlock();
204         if (err == 0)
205                 err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base;
206  out:
207         return err;
208 }
209
210 static ssize_t write_getfd(struct file *file, char *buf, size_t size)
211 {
212         struct nfsctl_fdparm *data;
213         struct sockaddr_in *sin;
214         struct auth_domain *clp;
215         int err = 0;
216         struct knfsd_fh fh;
217         char *res;
218
219         if (size < sizeof(*data))
220                 return -EINVAL;
221         data = (struct nfsctl_fdparm*)buf;
222         err = -EPROTONOSUPPORT;
223         if (data->gd_addr.sa_family != AF_INET)
224                 goto out;
225         err = -EINVAL;
226         if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
227                 goto out;
228
229         res = buf;
230         sin = (struct sockaddr_in *)&data->gd_addr;
231         exp_readlock();
232         if (!(clp = auth_unix_lookup(sin->sin_addr)))
233                 err = -EPERM;
234         else {
235                 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
236                 auth_domain_put(clp);
237         }
238         exp_readunlock();
239
240         if (err == 0) {
241                 memset(res,0, NFS_FHSIZE);
242                 memcpy(res, &fh.fh_base, fh.fh_size);
243                 err = NFS_FHSIZE;
244         }
245  out:
246         return err;
247 }
248
249 static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
250 {
251         /* request is:
252          *   domain path maxsize
253          * response is
254          *   filehandle
255          *
256          * qword quoting is used, so filehandle will be \x....
257          */
258         char *dname, *path;
259         int maxsize;
260         char *mesg = buf;
261         int len;
262         struct auth_domain *dom;
263         struct knfsd_fh fh;
264
265         if (buf[size-1] != '\n')
266                 return -EINVAL;
267         buf[size-1] = 0;
268
269         dname = mesg;
270         len = qword_get(&mesg, dname, size);
271         if (len <= 0) return -EINVAL;
272         
273         path = dname+len+1;
274         len = qword_get(&mesg, path, size);
275         if (len <= 0) return -EINVAL;
276
277         len = get_int(&mesg, &maxsize);
278         if (len)
279                 return len;
280
281         if (maxsize < NFS_FHSIZE)
282                 return -EINVAL;
283         if (maxsize > NFS3_FHSIZE)
284                 maxsize = NFS3_FHSIZE;
285
286         if (qword_get(&mesg, mesg, size)>0)
287                 return -EINVAL;
288
289         /* we have all the words, they are in buf.. */
290         dom = unix_domain_find(dname);
291         if (!dom)
292                 return -ENOMEM;
293
294         len = exp_rootfh(dom, path, &fh,  maxsize);
295         auth_domain_put(dom);
296         if (len)
297                 return len;
298         
299         mesg = buf; len = SIMPLE_TRANSACTION_LIMIT;
300         qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size);
301         mesg[-1] = '\n';
302         return mesg - buf;      
303 }
304
305 extern int nfsd_nrthreads(void);
306
307 static ssize_t write_threads(struct file *file, char *buf, size_t size)
308 {
309         /* if size > 0, look for a number of threads and call nfsd_svc
310          * then write out number of threads as reply
311          */
312         char *mesg = buf;
313         int rv;
314         if (size > 0) {
315                 int newthreads;
316                 rv = get_int(&mesg, &newthreads);
317                 if (rv)
318                         return rv;
319                 if (newthreads <0)
320                         return -EINVAL;
321                 rv = nfsd_svc(2049, newthreads);
322                 if (rv)
323                         return rv;
324         }
325         sprintf(buf, "%d\n", nfsd_nrthreads());
326         return strlen(buf);
327 }
328
329 extern time_t nfs4_leasetime(void);
330
331 static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
332 {
333         /* if size > 10 seconds, call
334          * nfs4_reset_lease() then write out the new lease (seconds) as reply
335          */
336         char *mesg = buf;
337         int rv;
338
339         if (size > 0) {
340                 int lease;
341                 rv = get_int(&mesg, &lease);
342                 if (rv)
343                         return rv;
344                 if (lease < 10 || lease > 3600)
345                         return -EINVAL;
346                 nfs4_reset_lease(lease);
347         }
348         sprintf(buf, "%ld\n", nfs4_lease_time());
349         return strlen(buf);
350 }
351
352 /*----------------------------------------------------------------------------*/
353 /*
354  *      populating the filesystem.
355  */
356
357 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
358 {
359         static struct tree_descr nfsd_files[] = {
360                 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
361                 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
362                 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
363                 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR},
364                 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
365                 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
366                 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
367                 [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
368                 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
369                 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
370 #ifdef CONFIG_NFSD_V4
371                 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
372 #endif
373                 /* last one */ {""}
374         };
375         return simple_fill_super(sb, 0x6e667364, nfsd_files);
376 }
377
378 static struct super_block *nfsd_get_sb(struct file_system_type *fs_type,
379         int flags, const char *dev_name, void *data)
380 {
381         return get_sb_single(fs_type, flags, data, nfsd_fill_super);
382 }
383
384 static struct file_system_type nfsd_fs_type = {
385         .owner          = THIS_MODULE,
386         .name           = "nfsd",
387         .get_sb         = nfsd_get_sb,
388         .kill_sb        = kill_litter_super,
389 };
390
391 static int __init init_nfsd(void)
392 {
393         int retval;
394         printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
395
396         nfsd_stat_init();       /* Statistics */
397         nfsd_cache_init();      /* RPC reply cache */
398         nfsd_export_init();     /* Exports table */
399         nfsd_lockd_init();      /* lockd->nfsd callbacks */
400         nfs4_state_init();      /* NFSv4 locking state */
401 #ifdef CONFIG_NFSD_V4
402         nfsd_idmap_init();      /* Name to ID mapping */
403 #endif /* CONFIG_NFSD_V4 */
404         if (proc_mkdir("fs/nfs", NULL)) {
405                 struct proc_dir_entry *entry;
406                 entry = create_proc_entry("fs/nfs/exports", 0, NULL);
407                 if (entry)
408                         entry->proc_fops =  &exports_operations;
409         }
410         retval = register_filesystem(&nfsd_fs_type);
411         if (retval) {
412                 nfsd_export_shutdown();
413                 nfsd_cache_shutdown();
414                 remove_proc_entry("fs/nfs/exports", NULL);
415                 remove_proc_entry("fs/nfs", NULL);
416                 nfsd_stat_shutdown();
417                 nfsd_lockd_shutdown();
418         }
419         return retval;
420 }
421
422 static void __exit exit_nfsd(void)
423 {
424         nfsd_export_shutdown();
425         nfsd_cache_shutdown();
426         remove_proc_entry("fs/nfs/exports", NULL);
427         remove_proc_entry("fs/nfs", NULL);
428         nfsd_stat_shutdown();
429         nfsd_lockd_shutdown();
430 #ifdef CONFIG_NFSD_V4
431         nfsd_idmap_shutdown();
432 #endif /* CONFIG_NFSD_V4 */
433         unregister_filesystem(&nfsd_fs_type);
434 }
435
436 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
437 MODULE_LICENSE("GPL");
438 module_init(init_nfsd)
439 module_exit(exit_nfsd)