blob: d6881774ea5758ed1bb9b635aaa823741c046c40 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#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>
NeilBrown70c3b762005-11-07 01:00:25 -080025#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
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 */
41enum {
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,
NeilBrown70c3b762005-11-07 01:00:25 -080053 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 NFSD_Leasetime,
NeilBrown0964a3d2005-06-23 22:04:32 -070060 NFSD_RecoveryDir,
NeilBrown70c3b762005-11-07 01:00:25 -080061#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070062};
63
64/*
65 * write() for these nodes.
66 */
67static ssize_t write_svc(struct file *file, char *buf, size_t size);
68static ssize_t write_add(struct file *file, char *buf, size_t size);
69static ssize_t write_del(struct file *file, char *buf, size_t size);
70static ssize_t write_export(struct file *file, char *buf, size_t size);
71static ssize_t write_unexport(struct file *file, char *buf, size_t size);
72static ssize_t write_getfd(struct file *file, char *buf, size_t size);
73static ssize_t write_getfs(struct file *file, char *buf, size_t size);
74static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
75static ssize_t write_threads(struct file *file, char *buf, size_t size);
NeilBrown70c3b762005-11-07 01:00:25 -080076static ssize_t write_versions(struct file *file, char *buf, size_t size);
77#ifdef CONFIG_NFSD_V4
Linus Torvalds1da177e2005-04-16 15:20:36 -070078static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
NeilBrown0964a3d2005-06-23 22:04:32 -070079static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
NeilBrown70c3b762005-11-07 01:00:25 -080080#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82static 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,
NeilBrown70c3b762005-11-07 01:00:25 -080092 [NFSD_Versions] = write_versions,
93#ifdef CONFIG_NFSD_V4
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 [NFSD_Leasetime] = write_leasetime,
NeilBrown0964a3d2005-06-23 22:04:32 -070095 [NFSD_RecoveryDir] = write_recoverydir,
NeilBrown70c3b762005-11-07 01:00:25 -080096#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070097};
98
99static 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
Tobias Klausere8c96f82006-03-24 03:15:34 -0800105 if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 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
NeilBrown73900222005-11-07 01:00:24 -0800120static 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
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -0800134static const struct file_operations transaction_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 .write = nfsctl_transaction_write,
NeilBrown73900222005-11-07 01:00:24 -0800136 .read = nfsctl_transaction_read,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 .release = simple_transaction_release,
138};
139
140extern struct seq_operations nfs_exports_op;
141static int exports_open(struct inode *inode, struct file *file)
142{
143 return seq_open(file, &nfs_exports_op);
144}
145
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -0800146static const struct file_operations exports_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 .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
160static 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
169static 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
178static 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
187static 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
196static 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
206static 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
240static 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
279static 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
335extern int nfsd_nrthreads(void);
336
337static 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
NeilBrown70c3b762005-11-07 01:00:25 -0800359static 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)
NeilBrown6658d3a2006-10-02 02:17:46 -0700373 /* Cannot change versions without updating
374 * nfsd_serv->sv_xdrsize, and reallocing
375 * rq_argp and rq_resp
376 */
NeilBrown70c3b762005-11-07 01:00:25 -0800377 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:
NeilBrown6658d3a2006-10-02 02:17:46 -0700395 nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET);
NeilBrown70c3b762005-11-07 01:00:25 -0800396 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 */
NeilBrown6658d3a2006-10-02 02:17:46 -0700406 nfsd_reset_versions();
NeilBrown70c3b762005-11-07 01:00:25 -0800407 }
408 /* Now write current state into reply buffer */
409 len = 0;
410 sep = "";
411 for (num=2 ; num <= 4 ; num++)
NeilBrown6658d3a2006-10-02 02:17:46 -0700412 if (nfsd_vers(num, NFSD_AVAIL)) {
NeilBrown70c3b762005-11-07 01:00:25 -0800413 len += sprintf(buf+len, "%s%c%d", sep,
NeilBrown6658d3a2006-10-02 02:17:46 -0700414 nfsd_vers(num, NFSD_TEST)?'+':'-',
NeilBrown70c3b762005-11-07 01:00:25 -0800415 num);
416 sep = " ";
417 }
418 len += sprintf(buf+len, "\n");
419 return len;
420}
421
422#ifdef CONFIG_NFSD_V4
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423extern time_t nfs4_leasetime(void);
424
425static 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
NeilBrown0964a3d2005-06-23 22:04:32 -0700446static 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}
NeilBrown70c3b762005-11-07 01:00:25 -0800464#endif
NeilBrown0964a3d2005-06-23 22:04:32 -0700465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466/*----------------------------------------------------------------------------*/
467/*
468 * populating the filesystem.
469 */
470
471static 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},
NeilBrown70c3b762005-11-07 01:00:25 -0800484 [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485#ifdef CONFIG_NFSD_V4
486 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
NeilBrown0964a3d2005-06-23 22:04:32 -0700487 [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488#endif
489 /* last one */ {""}
490 };
491 return simple_fill_super(sb, 0x6e667364, nfsd_files);
492}
493
David Howells454e2392006-06-23 02:02:57 -0700494static int nfsd_get_sb(struct file_system_type *fs_type,
495 int flags, const char *dev_name, void *data, struct vfsmount *mnt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
David Howells454e2392006-06-23 02:02:57 -0700497 return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
500static 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
507static 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 */
NeilBrownac4d8ff22005-06-23 22:03:30 -0700516 nfs4_state_init(); /* NFSv4 locking state */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 nfsd_idmap_init(); /* Name to ID mapping */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 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
536static 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();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 nfsd_idmap_shutdown();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 unregister_filesystem(&nfsd_fs_type);
546}
547
548MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
549MODULE_LICENSE("GPL");
550module_init(init_nfsd)
551module_exit(exit_nfsd)