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