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