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 #include <linux/smp_lock.h> 27 #include <linux/ctype.h> 28 29 #include <linux/nfs.h> 30 #include <linux/nfsd_idmap.h> 31 #include <linux/lockd/bind.h> 32 #include <linux/sunrpc/svc.h> 33 #include <linux/sunrpc/svcsock.h> 34 #include <linux/nfsd/nfsd.h> 35 #include <linux/nfsd/cache.h> 36 #include <linux/nfsd/xdr.h> 37 #include <linux/nfsd/syscall.h> 38 #include <linux/nfsd/interface.h> 39 40 #include <asm/uaccess.h> 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_Pool_Threads, 58 NFSD_Versions, 59 NFSD_Ports, 60 /* 61 * The below MUST come last. Otherwise we leave a hole in nfsd_files[] 62 * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops 63 */ 64 #ifdef CONFIG_NFSD_V4 65 NFSD_Leasetime, 66 NFSD_RecoveryDir, 67 #endif 68 }; 69 70 /* 71 * write() for these nodes. 72 */ 73 static ssize_t write_svc(struct file *file, char *buf, size_t size); 74 static ssize_t write_add(struct file *file, char *buf, size_t size); 75 static ssize_t write_del(struct file *file, char *buf, size_t size); 76 static ssize_t write_export(struct file *file, char *buf, size_t size); 77 static ssize_t write_unexport(struct file *file, char *buf, size_t size); 78 static ssize_t write_getfd(struct file *file, char *buf, size_t size); 79 static ssize_t write_getfs(struct file *file, char *buf, size_t size); 80 static ssize_t write_filehandle(struct file *file, char *buf, size_t size); 81 static ssize_t write_threads(struct file *file, char *buf, size_t size); 82 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); 83 static ssize_t write_versions(struct file *file, char *buf, size_t size); 84 static ssize_t write_ports(struct file *file, char *buf, size_t size); 85 #ifdef CONFIG_NFSD_V4 86 static ssize_t write_leasetime(struct file *file, char *buf, size_t size); 87 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); 88 #endif 89 90 static ssize_t (*write_op[])(struct file *, char *, size_t) = { 91 [NFSD_Svc] = write_svc, 92 [NFSD_Add] = write_add, 93 [NFSD_Del] = write_del, 94 [NFSD_Export] = write_export, 95 [NFSD_Unexport] = write_unexport, 96 [NFSD_Getfd] = write_getfd, 97 [NFSD_Getfs] = write_getfs, 98 [NFSD_Fh] = write_filehandle, 99 [NFSD_Threads] = write_threads, 100 [NFSD_Pool_Threads] = write_pool_threads, 101 [NFSD_Versions] = write_versions, 102 [NFSD_Ports] = write_ports, 103 #ifdef CONFIG_NFSD_V4 104 [NFSD_Leasetime] = write_leasetime, 105 [NFSD_RecoveryDir] = write_recoverydir, 106 #endif 107 }; 108 109 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) 110 { 111 ino_t ino = file->f_dentry->d_inode->i_ino; 112 char *data; 113 ssize_t rv; 114 115 if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) 116 return -EINVAL; 117 118 data = simple_transaction_get(file, buf, size); 119 if (IS_ERR(data)) 120 return PTR_ERR(data); 121 122 rv = write_op[ino](file, data, size); 123 if (rv>0) { 124 simple_transaction_set(file, rv); 125 rv = size; 126 } 127 return rv; 128 } 129 130 static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) 131 { 132 if (! file->private_data) { 133 /* An attempt to read a transaction file without writing 134 * causes a 0-byte write so that the file can return 135 * state information 136 */ 137 ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); 138 if (rv < 0) 139 return rv; 140 } 141 return simple_transaction_read(file, buf, size, pos); 142 } 143 144 static const struct file_operations transaction_ops = { 145 .write = nfsctl_transaction_write, 146 .read = nfsctl_transaction_read, 147 .release = simple_transaction_release, 148 }; 149 150 extern struct seq_operations nfs_exports_op; 151 static int exports_open(struct inode *inode, struct file *file) 152 { 153 return seq_open(file, &nfs_exports_op); 154 } 155 156 static const struct file_operations exports_operations = { 157 .open = exports_open, 158 .read = seq_read, 159 .llseek = seq_lseek, 160 .release = seq_release, 161 }; 162 163 /*----------------------------------------------------------------------------*/ 164 /* 165 * payload - write methods 166 * If the method has a response, the response should be put in buf, 167 * and the length returned. Otherwise return 0 or and -error. 168 */ 169 170 static ssize_t write_svc(struct file *file, char *buf, size_t size) 171 { 172 struct nfsctl_svc *data; 173 if (size < sizeof(*data)) 174 return -EINVAL; 175 data = (struct nfsctl_svc*) buf; 176 return nfsd_svc(data->svc_port, data->svc_nthreads); 177 } 178 179 static ssize_t write_add(struct file *file, char *buf, size_t size) 180 { 181 struct nfsctl_client *data; 182 if (size < sizeof(*data)) 183 return -EINVAL; 184 data = (struct nfsctl_client *)buf; 185 return exp_addclient(data); 186 } 187 188 static ssize_t write_del(struct file *file, char *buf, size_t size) 189 { 190 struct nfsctl_client *data; 191 if (size < sizeof(*data)) 192 return -EINVAL; 193 data = (struct nfsctl_client *)buf; 194 return exp_delclient(data); 195 } 196 197 static ssize_t write_export(struct file *file, char *buf, size_t size) 198 { 199 struct nfsctl_export *data; 200 if (size < sizeof(*data)) 201 return -EINVAL; 202 data = (struct nfsctl_export*)buf; 203 return exp_export(data); 204 } 205 206 static ssize_t write_unexport(struct file *file, char *buf, size_t size) 207 { 208 struct nfsctl_export *data; 209 210 if (size < sizeof(*data)) 211 return -EINVAL; 212 data = (struct nfsctl_export*)buf; 213 return exp_unexport(data); 214 } 215 216 static ssize_t write_getfs(struct file *file, char *buf, size_t size) 217 { 218 struct nfsctl_fsparm *data; 219 struct sockaddr_in *sin; 220 struct auth_domain *clp; 221 int err = 0; 222 struct knfsd_fh *res; 223 224 if (size < sizeof(*data)) 225 return -EINVAL; 226 data = (struct nfsctl_fsparm*)buf; 227 err = -EPROTONOSUPPORT; 228 if (data->gd_addr.sa_family != AF_INET) 229 goto out; 230 sin = (struct sockaddr_in *)&data->gd_addr; 231 if (data->gd_maxlen > NFS3_FHSIZE) 232 data->gd_maxlen = NFS3_FHSIZE; 233 234 res = (struct knfsd_fh*)buf; 235 236 exp_readlock(); 237 if (!(clp = auth_unix_lookup(sin->sin_addr))) 238 err = -EPERM; 239 else { 240 err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); 241 auth_domain_put(clp); 242 } 243 exp_readunlock(); 244 if (err == 0) 245 err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; 246 out: 247 return err; 248 } 249 250 static ssize_t write_getfd(struct file *file, char *buf, size_t size) 251 { 252 struct nfsctl_fdparm *data; 253 struct sockaddr_in *sin; 254 struct auth_domain *clp; 255 int err = 0; 256 struct knfsd_fh fh; 257 char *res; 258 259 if (size < sizeof(*data)) 260 return -EINVAL; 261 data = (struct nfsctl_fdparm*)buf; 262 err = -EPROTONOSUPPORT; 263 if (data->gd_addr.sa_family != AF_INET) 264 goto out; 265 err = -EINVAL; 266 if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) 267 goto out; 268 269 res = buf; 270 sin = (struct sockaddr_in *)&data->gd_addr; 271 exp_readlock(); 272 if (!(clp = auth_unix_lookup(sin->sin_addr))) 273 err = -EPERM; 274 else { 275 err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); 276 auth_domain_put(clp); 277 } 278 exp_readunlock(); 279 280 if (err == 0) { 281 memset(res,0, NFS_FHSIZE); 282 memcpy(res, &fh.fh_base, fh.fh_size); 283 err = NFS_FHSIZE; 284 } 285 out: 286 return err; 287 } 288 289 static ssize_t write_filehandle(struct file *file, char *buf, size_t size) 290 { 291 /* request is: 292 * domain path maxsize 293 * response is 294 * filehandle 295 * 296 * qword quoting is used, so filehandle will be \x.... 297 */ 298 char *dname, *path; 299 int maxsize; 300 char *mesg = buf; 301 int len; 302 struct auth_domain *dom; 303 struct knfsd_fh fh; 304 305 if (buf[size-1] != '\n') 306 return -EINVAL; 307 buf[size-1] = 0; 308 309 dname = mesg; 310 len = qword_get(&mesg, dname, size); 311 if (len <= 0) return -EINVAL; 312 313 path = dname+len+1; 314 len = qword_get(&mesg, path, size); 315 if (len <= 0) return -EINVAL; 316 317 len = get_int(&mesg, &maxsize); 318 if (len) 319 return len; 320 321 if (maxsize < NFS_FHSIZE) 322 return -EINVAL; 323 if (maxsize > NFS3_FHSIZE) 324 maxsize = NFS3_FHSIZE; 325 326 if (qword_get(&mesg, mesg, size)>0) 327 return -EINVAL; 328 329 /* we have all the words, they are in buf.. */ 330 dom = unix_domain_find(dname); 331 if (!dom) 332 return -ENOMEM; 333 334 len = exp_rootfh(dom, path, &fh, maxsize); 335 auth_domain_put(dom); 336 if (len) 337 return len; 338 339 mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; 340 qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); 341 mesg[-1] = '\n'; 342 return mesg - buf; 343 } 344 345 extern int nfsd_nrthreads(void); 346 347 static ssize_t write_threads(struct file *file, char *buf, size_t size) 348 { 349 /* if size > 0, look for a number of threads and call nfsd_svc 350 * then write out number of threads as reply 351 */ 352 char *mesg = buf; 353 int rv; 354 if (size > 0) { 355 int newthreads; 356 rv = get_int(&mesg, &newthreads); 357 if (rv) 358 return rv; 359 if (newthreads <0) 360 return -EINVAL; 361 rv = nfsd_svc(2049, newthreads); 362 if (rv) 363 return rv; 364 } 365 sprintf(buf, "%d\n", nfsd_nrthreads()); 366 return strlen(buf); 367 } 368 369 extern int nfsd_nrpools(void); 370 extern int nfsd_get_nrthreads(int n, int *); 371 extern int nfsd_set_nrthreads(int n, int *); 372 373 static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) 374 { 375 /* if size > 0, look for an array of number of threads per node 376 * and apply them then write out number of threads per node as reply 377 */ 378 char *mesg = buf; 379 int i; 380 int rv; 381 int len; 382 int npools = nfsd_nrpools(); 383 int *nthreads; 384 385 if (npools == 0) { 386 /* 387 * NFS is shut down. The admin can start it by 388 * writing to the threads file but NOT the pool_threads 389 * file, sorry. Report zero threads. 390 */ 391 strcpy(buf, "0\n"); 392 return strlen(buf); 393 } 394 395 nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); 396 if (nthreads == NULL) 397 return -ENOMEM; 398 399 if (size > 0) { 400 for (i = 0; i < npools; i++) { 401 rv = get_int(&mesg, &nthreads[i]); 402 if (rv == -ENOENT) 403 break; /* fewer numbers than pools */ 404 if (rv) 405 goto out_free; /* syntax error */ 406 rv = -EINVAL; 407 if (nthreads[i] < 0) 408 goto out_free; 409 } 410 rv = nfsd_set_nrthreads(i, nthreads); 411 if (rv) 412 goto out_free; 413 } 414 415 rv = nfsd_get_nrthreads(npools, nthreads); 416 if (rv) 417 goto out_free; 418 419 mesg = buf; 420 size = SIMPLE_TRANSACTION_LIMIT; 421 for (i = 0; i < npools && size > 0; i++) { 422 snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); 423 len = strlen(mesg); 424 size -= len; 425 mesg += len; 426 } 427 428 return (mesg-buf); 429 430 out_free: 431 kfree(nthreads); 432 return rv; 433 } 434 435 static ssize_t write_versions(struct file *file, char *buf, size_t size) 436 { 437 /* 438 * Format: 439 * [-/+]vers [-/+]vers ... 440 */ 441 char *mesg = buf; 442 char *vers, sign; 443 int len, num; 444 ssize_t tlen = 0; 445 char *sep; 446 447 if (size>0) { 448 if (nfsd_serv) 449 /* Cannot change versions without updating 450 * nfsd_serv->sv_xdrsize, and reallocing 451 * rq_argp and rq_resp 452 */ 453 return -EBUSY; 454 if (buf[size-1] != '\n') 455 return -EINVAL; 456 buf[size-1] = 0; 457 458 vers = mesg; 459 len = qword_get(&mesg, vers, size); 460 if (len <= 0) return -EINVAL; 461 do { 462 sign = *vers; 463 if (sign == '+' || sign == '-') 464 num = simple_strtol((vers+1), NULL, 0); 465 else 466 num = simple_strtol(vers, NULL, 0); 467 switch(num) { 468 case 2: 469 case 3: 470 case 4: 471 nfsd_vers(num, sign == '-' ? NFSD_CLEAR : NFSD_SET); 472 break; 473 default: 474 return -EINVAL; 475 } 476 vers += len + 1; 477 tlen += len; 478 } while ((len = qword_get(&mesg, vers, size)) > 0); 479 /* If all get turned off, turn them back on, as 480 * having no versions is BAD 481 */ 482 nfsd_reset_versions(); 483 } 484 /* Now write current state into reply buffer */ 485 len = 0; 486 sep = ""; 487 for (num=2 ; num <= 4 ; num++) 488 if (nfsd_vers(num, NFSD_AVAIL)) { 489 len += sprintf(buf+len, "%s%c%d", sep, 490 nfsd_vers(num, NFSD_TEST)?'+':'-', 491 num); 492 sep = " "; 493 } 494 len += sprintf(buf+len, "\n"); 495 return len; 496 } 497 498 static ssize_t write_ports(struct file *file, char *buf, size_t size) 499 { 500 if (size == 0) { 501 int len = 0; 502 lock_kernel(); 503 if (nfsd_serv) 504 len = svc_sock_names(buf, nfsd_serv, NULL); 505 unlock_kernel(); 506 return len; 507 } 508 /* Either a single 'fd' number is written, in which 509 * case it must be for a socket of a supported family/protocol, 510 * and we use it as an nfsd socket, or 511 * A '-' followed by the 'name' of a socket in which case 512 * we close the socket. 513 */ 514 if (isdigit(buf[0])) { 515 char *mesg = buf; 516 int fd; 517 int err; 518 err = get_int(&mesg, &fd); 519 if (err) 520 return -EINVAL; 521 if (fd < 0) 522 return -EINVAL; 523 err = nfsd_create_serv(); 524 if (!err) { 525 int proto = 0; 526 err = lockd_up(proto); 527 if (!err) { 528 err = svc_addsock(nfsd_serv, fd, buf, &proto); 529 if (err) 530 lockd_down(); 531 } 532 /* Decrease the count, but don't shutdown the 533 * the service 534 */ 535 nfsd_serv->sv_nrthreads--; 536 } 537 return err; 538 } 539 if (buf[0] == '-') { 540 char *toclose = kstrdup(buf+1, GFP_KERNEL); 541 int len = 0; 542 if (!toclose) 543 return -ENOMEM; 544 lock_kernel(); 545 if (nfsd_serv) 546 len = svc_sock_names(buf, nfsd_serv, toclose); 547 unlock_kernel(); 548 kfree(toclose); 549 return len; 550 } 551 return -EINVAL; 552 } 553 554 #ifdef CONFIG_NFSD_V4 555 extern time_t nfs4_leasetime(void); 556 557 static ssize_t write_leasetime(struct file *file, char *buf, size_t size) 558 { 559 /* if size > 10 seconds, call 560 * nfs4_reset_lease() then write out the new lease (seconds) as reply 561 */ 562 char *mesg = buf; 563 int rv; 564 565 if (size > 0) { 566 int lease; 567 rv = get_int(&mesg, &lease); 568 if (rv) 569 return rv; 570 if (lease < 10 || lease > 3600) 571 return -EINVAL; 572 nfs4_reset_lease(lease); 573 } 574 sprintf(buf, "%ld\n", nfs4_lease_time()); 575 return strlen(buf); 576 } 577 578 static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) 579 { 580 char *mesg = buf; 581 char *recdir; 582 int len, status; 583 584 if (size > PATH_MAX || buf[size-1] != '\n') 585 return -EINVAL; 586 buf[size-1] = 0; 587 588 recdir = mesg; 589 len = qword_get(&mesg, recdir, size); 590 if (len <= 0) 591 return -EINVAL; 592 593 status = nfs4_reset_recoverydir(recdir); 594 return strlen(buf); 595 } 596 #endif 597 598 /*----------------------------------------------------------------------------*/ 599 /* 600 * populating the filesystem. 601 */ 602 603 static int nfsd_fill_super(struct super_block * sb, void * data, int silent) 604 { 605 static struct tree_descr nfsd_files[] = { 606 [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR}, 607 [NFSD_Add] = {".add", &transaction_ops, S_IWUSR}, 608 [NFSD_Del] = {".del", &transaction_ops, S_IWUSR}, 609 [NFSD_Export] = {".export", &transaction_ops, S_IWUSR}, 610 [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR}, 611 [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR}, 612 [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR}, 613 [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, 614 [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, 615 [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, 616 [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, 617 [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, 618 [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, 619 #ifdef CONFIG_NFSD_V4 620 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, 621 [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, 622 #endif 623 /* last one */ {""} 624 }; 625 return simple_fill_super(sb, 0x6e667364, nfsd_files); 626 } 627 628 static int nfsd_get_sb(struct file_system_type *fs_type, 629 int flags, const char *dev_name, void *data, struct vfsmount *mnt) 630 { 631 return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt); 632 } 633 634 static struct file_system_type nfsd_fs_type = { 635 .owner = THIS_MODULE, 636 .name = "nfsd", 637 .get_sb = nfsd_get_sb, 638 .kill_sb = kill_litter_super, 639 }; 640 641 static int __init init_nfsd(void) 642 { 643 int retval; 644 printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); 645 646 nfsd_stat_init(); /* Statistics */ 647 nfsd_cache_init(); /* RPC reply cache */ 648 nfsd_export_init(); /* Exports table */ 649 nfsd_lockd_init(); /* lockd->nfsd callbacks */ 650 nfs4_state_init(); /* NFSv4 locking state */ 651 nfsd_idmap_init(); /* Name to ID mapping */ 652 if (proc_mkdir("fs/nfs", NULL)) { 653 struct proc_dir_entry *entry; 654 entry = create_proc_entry("fs/nfs/exports", 0, NULL); 655 if (entry) 656 entry->proc_fops = &exports_operations; 657 } 658 retval = register_filesystem(&nfsd_fs_type); 659 if (retval) { 660 nfsd_export_shutdown(); 661 nfsd_cache_shutdown(); 662 remove_proc_entry("fs/nfs/exports", NULL); 663 remove_proc_entry("fs/nfs", NULL); 664 nfsd_stat_shutdown(); 665 nfsd_lockd_shutdown(); 666 } 667 return retval; 668 } 669 670 static void __exit exit_nfsd(void) 671 { 672 nfsd_export_shutdown(); 673 nfsd_cache_shutdown(); 674 remove_proc_entry("fs/nfs/exports", NULL); 675 remove_proc_entry("fs/nfs", NULL); 676 nfsd_stat_shutdown(); 677 nfsd_lockd_shutdown(); 678 nfsd_idmap_shutdown(); 679 unregister_filesystem(&nfsd_fs_type); 680 } 681 682 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); 683 MODULE_LICENSE("GPL"); 684 module_init(init_nfsd) 685 module_exit(exit_nfsd) 686