1 #define MSNFS /* HACK HACK */ 2 /* 3 * linux/fs/nfsd/export.c 4 * 5 * NFS exporting and validation. 6 * 7 * We maintain a list of clients, each of which has a list of 8 * exports. To export an fs to a given client, you first have 9 * to create the client entry with NFSCTL_ADDCLIENT, which 10 * creates a client control block and adds it to the hash 11 * table. Then, you call NFSCTL_EXPORT for each fs. 12 * 13 * 14 * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de> 15 */ 16 17 #include <linux/unistd.h> 18 #include <linux/slab.h> 19 #include <linux/sched.h> 20 #include <linux/stat.h> 21 #include <linux/in.h> 22 #include <linux/seq_file.h> 23 #include <linux/syscalls.h> 24 #include <linux/rwsem.h> 25 #include <linux/dcache.h> 26 #include <linux/namei.h> 27 #include <linux/mount.h> 28 #include <linux/hash.h> 29 #include <linux/module.h> 30 31 #include <linux/sunrpc/svc.h> 32 #include <linux/nfsd/nfsd.h> 33 #include <linux/nfsd/nfsfh.h> 34 #include <linux/nfsd/syscall.h> 35 #include <linux/lockd/bind.h> 36 37 #define NFSDDBG_FACILITY NFSDDBG_EXPORT 38 39 typedef struct auth_domain svc_client; 40 typedef struct svc_export svc_export; 41 42 static void exp_do_unexport(svc_export *unexp); 43 static int exp_verify_string(char *cp, int max); 44 45 /* 46 * We have two caches. 47 * One maps client+vfsmnt+dentry to export options - the export map 48 * The other maps client+filehandle-fragment to export options. - the expkey map 49 * 50 * The export options are actually stored in the first map, and the 51 * second map contains a reference to the entry in the first map. 52 */ 53 54 #define EXPKEY_HASHBITS 8 55 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) 56 #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) 57 static struct cache_head *expkey_table[EXPKEY_HASHMAX]; 58 59 static void expkey_put(struct kref *ref) 60 { 61 struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); 62 63 if (test_bit(CACHE_VALID, &key->h.flags) && 64 !test_bit(CACHE_NEGATIVE, &key->h.flags)) { 65 dput(key->ek_dentry); 66 mntput(key->ek_mnt); 67 } 68 auth_domain_put(key->ek_client); 69 kfree(key); 70 } 71 72 static void expkey_request(struct cache_detail *cd, 73 struct cache_head *h, 74 char **bpp, int *blen) 75 { 76 /* client fsidtype \xfsid */ 77 struct svc_expkey *ek = container_of(h, struct svc_expkey, h); 78 char type[5]; 79 80 qword_add(bpp, blen, ek->ek_client->name); 81 snprintf(type, 5, "%d", ek->ek_fsidtype); 82 qword_add(bpp, blen, type); 83 qword_addhex(bpp, blen, (char*)ek->ek_fsid, key_len(ek->ek_fsidtype)); 84 (*bpp)[-1] = '\n'; 85 } 86 87 static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old); 88 static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *); 89 static struct cache_detail svc_expkey_cache; 90 91 static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) 92 { 93 /* client fsidtype fsid [path] */ 94 char *buf; 95 int len; 96 struct auth_domain *dom = NULL; 97 int err; 98 int fsidtype; 99 char *ep; 100 struct svc_expkey key; 101 struct svc_expkey *ek; 102 103 if (mesg[mlen-1] != '\n') 104 return -EINVAL; 105 mesg[mlen-1] = 0; 106 107 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 108 err = -ENOMEM; 109 if (!buf) goto out; 110 111 err = -EINVAL; 112 if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) 113 goto out; 114 115 err = -ENOENT; 116 dom = auth_domain_find(buf); 117 if (!dom) 118 goto out; 119 dprintk("found domain %s\n", buf); 120 121 err = -EINVAL; 122 if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) 123 goto out; 124 fsidtype = simple_strtoul(buf, &ep, 10); 125 if (*ep) 126 goto out; 127 dprintk("found fsidtype %d\n", fsidtype); 128 if (key_len(fsidtype)==0) /* invalid type */ 129 goto out; 130 if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) 131 goto out; 132 dprintk("found fsid length %d\n", len); 133 if (len != key_len(fsidtype)) 134 goto out; 135 136 /* OK, we seem to have a valid key */ 137 key.h.flags = 0; 138 key.h.expiry_time = get_expiry(&mesg); 139 if (key.h.expiry_time == 0) 140 goto out; 141 142 key.ek_client = dom; 143 key.ek_fsidtype = fsidtype; 144 memcpy(key.ek_fsid, buf, len); 145 146 ek = svc_expkey_lookup(&key); 147 err = -ENOMEM; 148 if (!ek) 149 goto out; 150 151 /* now we want a pathname, or empty meaning NEGATIVE */ 152 err = -EINVAL; 153 if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) 154 goto out; 155 dprintk("Path seems to be <%s>\n", buf); 156 err = 0; 157 if (len == 0) { 158 set_bit(CACHE_NEGATIVE, &key.h.flags); 159 ek = svc_expkey_update(&key, ek); 160 if (ek) 161 cache_put(&ek->h, &svc_expkey_cache); 162 else err = -ENOMEM; 163 } else { 164 struct nameidata nd; 165 err = path_lookup(buf, 0, &nd); 166 if (err) 167 goto out; 168 169 dprintk("Found the path %s\n", buf); 170 key.ek_mnt = nd.mnt; 171 key.ek_dentry = nd.dentry; 172 173 ek = svc_expkey_update(&key, ek); 174 if (ek) 175 cache_put(&ek->h, &svc_expkey_cache); 176 else 177 err = -ENOMEM; 178 path_release(&nd); 179 } 180 cache_flush(); 181 out: 182 if (dom) 183 auth_domain_put(dom); 184 kfree(buf); 185 return err; 186 } 187 188 static int expkey_show(struct seq_file *m, 189 struct cache_detail *cd, 190 struct cache_head *h) 191 { 192 struct svc_expkey *ek ; 193 194 if (h ==NULL) { 195 seq_puts(m, "#domain fsidtype fsid [path]\n"); 196 return 0; 197 } 198 ek = container_of(h, struct svc_expkey, h); 199 seq_printf(m, "%s %d 0x%08x", ek->ek_client->name, 200 ek->ek_fsidtype, ek->ek_fsid[0]); 201 if (ek->ek_fsidtype != 1) 202 seq_printf(m, "%08x", ek->ek_fsid[1]); 203 if (ek->ek_fsidtype == 2) 204 seq_printf(m, "%08x", ek->ek_fsid[2]); 205 if (test_bit(CACHE_VALID, &h->flags) && 206 !test_bit(CACHE_NEGATIVE, &h->flags)) { 207 seq_printf(m, " "); 208 seq_path(m, ek->ek_mnt, ek->ek_dentry, "\\ \t\n"); 209 } 210 seq_printf(m, "\n"); 211 return 0; 212 } 213 214 static inline int expkey_match (struct cache_head *a, struct cache_head *b) 215 { 216 struct svc_expkey *orig = container_of(a, struct svc_expkey, h); 217 struct svc_expkey *new = container_of(b, struct svc_expkey, h); 218 219 if (orig->ek_fsidtype != new->ek_fsidtype || 220 orig->ek_client != new->ek_client || 221 memcmp(orig->ek_fsid, new->ek_fsid, key_len(orig->ek_fsidtype)) != 0) 222 return 0; 223 return 1; 224 } 225 226 static inline void expkey_init(struct cache_head *cnew, 227 struct cache_head *citem) 228 { 229 struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); 230 struct svc_expkey *item = container_of(citem, struct svc_expkey, h); 231 232 kref_get(&item->ek_client->ref); 233 new->ek_client = item->ek_client; 234 new->ek_fsidtype = item->ek_fsidtype; 235 new->ek_fsid[0] = item->ek_fsid[0]; 236 new->ek_fsid[1] = item->ek_fsid[1]; 237 new->ek_fsid[2] = item->ek_fsid[2]; 238 } 239 240 static inline void expkey_update(struct cache_head *cnew, 241 struct cache_head *citem) 242 { 243 struct svc_expkey *new = container_of(cnew, struct svc_expkey, h); 244 struct svc_expkey *item = container_of(citem, struct svc_expkey, h); 245 246 new->ek_mnt = mntget(item->ek_mnt); 247 new->ek_dentry = dget(item->ek_dentry); 248 } 249 250 static struct cache_head *expkey_alloc(void) 251 { 252 struct svc_expkey *i = kmalloc(sizeof(*i), GFP_KERNEL); 253 if (i) 254 return &i->h; 255 else 256 return NULL; 257 } 258 259 static struct cache_detail svc_expkey_cache = { 260 .owner = THIS_MODULE, 261 .hash_size = EXPKEY_HASHMAX, 262 .hash_table = expkey_table, 263 .name = "nfsd.fh", 264 .cache_put = expkey_put, 265 .cache_request = expkey_request, 266 .cache_parse = expkey_parse, 267 .cache_show = expkey_show, 268 .match = expkey_match, 269 .init = expkey_init, 270 .update = expkey_update, 271 .alloc = expkey_alloc, 272 }; 273 274 static struct svc_expkey * 275 svc_expkey_lookup(struct svc_expkey *item) 276 { 277 struct cache_head *ch; 278 int hash = item->ek_fsidtype; 279 char * cp = (char*)item->ek_fsid; 280 int len = key_len(item->ek_fsidtype); 281 282 hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); 283 hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); 284 hash &= EXPKEY_HASHMASK; 285 286 ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, 287 hash); 288 if (ch) 289 return container_of(ch, struct svc_expkey, h); 290 else 291 return NULL; 292 } 293 294 static struct svc_expkey * 295 svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) 296 { 297 struct cache_head *ch; 298 int hash = new->ek_fsidtype; 299 char * cp = (char*)new->ek_fsid; 300 int len = key_len(new->ek_fsidtype); 301 302 hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); 303 hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); 304 hash &= EXPKEY_HASHMASK; 305 306 ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, 307 &old->h, hash); 308 if (ch) 309 return container_of(ch, struct svc_expkey, h); 310 else 311 return NULL; 312 } 313 314 315 #define EXPORT_HASHBITS 8 316 #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) 317 #define EXPORT_HASHMASK (EXPORT_HASHMAX -1) 318 319 static struct cache_head *export_table[EXPORT_HASHMAX]; 320 321 static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc) 322 { 323 int i; 324 325 for (i = 0; i < fsloc->locations_count; i++) { 326 kfree(fsloc->locations[i].path); 327 kfree(fsloc->locations[i].hosts); 328 } 329 kfree(fsloc->locations); 330 } 331 332 static void svc_export_put(struct kref *ref) 333 { 334 struct svc_export *exp = container_of(ref, struct svc_export, h.ref); 335 dput(exp->ex_dentry); 336 mntput(exp->ex_mnt); 337 auth_domain_put(exp->ex_client); 338 kfree(exp->ex_path); 339 nfsd4_fslocs_free(&exp->ex_fslocs); 340 kfree(exp); 341 } 342 343 static void svc_export_request(struct cache_detail *cd, 344 struct cache_head *h, 345 char **bpp, int *blen) 346 { 347 /* client path */ 348 struct svc_export *exp = container_of(h, struct svc_export, h); 349 char *pth; 350 351 qword_add(bpp, blen, exp->ex_client->name); 352 pth = d_path(exp->ex_dentry, exp->ex_mnt, *bpp, *blen); 353 if (IS_ERR(pth)) { 354 /* is this correct? */ 355 (*bpp)[0] = '\n'; 356 return; 357 } 358 qword_add(bpp, blen, pth); 359 (*bpp)[-1] = '\n'; 360 } 361 362 static struct svc_export *svc_export_update(struct svc_export *new, 363 struct svc_export *old); 364 static struct svc_export *svc_export_lookup(struct svc_export *); 365 366 static int check_export(struct inode *inode, int flags) 367 { 368 369 /* We currently export only dirs and regular files. 370 * This is what umountd does. 371 */ 372 if (!S_ISDIR(inode->i_mode) && 373 !S_ISREG(inode->i_mode)) 374 return -ENOTDIR; 375 376 /* There are two requirements on a filesystem to be exportable. 377 * 1: We must be able to identify the filesystem from a number. 378 * either a device number (so FS_REQUIRES_DEV needed) 379 * or an FSID number (so NFSEXP_FSID needed). 380 * 2: We must be able to find an inode from a filehandle. 381 * This means that s_export_op must be set. 382 */ 383 if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) && 384 !(flags & NFSEXP_FSID)) { 385 dprintk("exp_export: export of non-dev fs without fsid\n"); 386 return -EINVAL; 387 } 388 if (!inode->i_sb->s_export_op) { 389 dprintk("exp_export: export of invalid fs type.\n"); 390 return -EINVAL; 391 } 392 393 /* Ok, we can export it */; 394 if (!inode->i_sb->s_export_op->find_exported_dentry) 395 inode->i_sb->s_export_op->find_exported_dentry = 396 find_exported_dentry; 397 return 0; 398 399 } 400 401 #ifdef CONFIG_NFSD_V4 402 403 static int 404 fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) 405 { 406 int len; 407 int migrated, i, err; 408 409 len = qword_get(mesg, buf, PAGE_SIZE); 410 if (len != 5 || memcmp(buf, "fsloc", 5)) 411 return 0; 412 413 /* listsize */ 414 err = get_int(mesg, &fsloc->locations_count); 415 if (err) 416 return err; 417 if (fsloc->locations_count > MAX_FS_LOCATIONS) 418 return -EINVAL; 419 if (fsloc->locations_count == 0) 420 return 0; 421 422 fsloc->locations = kzalloc(fsloc->locations_count 423 * sizeof(struct nfsd4_fs_location), GFP_KERNEL); 424 if (!fsloc->locations) 425 return -ENOMEM; 426 for (i=0; i < fsloc->locations_count; i++) { 427 /* colon separated host list */ 428 err = -EINVAL; 429 len = qword_get(mesg, buf, PAGE_SIZE); 430 if (len <= 0) 431 goto out_free_all; 432 err = -ENOMEM; 433 fsloc->locations[i].hosts = kstrdup(buf, GFP_KERNEL); 434 if (!fsloc->locations[i].hosts) 435 goto out_free_all; 436 err = -EINVAL; 437 /* slash separated path component list */ 438 len = qword_get(mesg, buf, PAGE_SIZE); 439 if (len <= 0) 440 goto out_free_all; 441 err = -ENOMEM; 442 fsloc->locations[i].path = kstrdup(buf, GFP_KERNEL); 443 if (!fsloc->locations[i].path) 444 goto out_free_all; 445 } 446 /* migrated */ 447 err = get_int(mesg, &migrated); 448 if (err) 449 goto out_free_all; 450 err = -EINVAL; 451 if (migrated < 0 || migrated > 1) 452 goto out_free_all; 453 fsloc->migrated = migrated; 454 return 0; 455 out_free_all: 456 nfsd4_fslocs_free(fsloc); 457 return err; 458 } 459 460 #else /* CONFIG_NFSD_V4 */ 461 static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; } 462 #endif 463 464 static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) 465 { 466 /* client path expiry [flags anonuid anongid fsid] */ 467 char *buf; 468 int len; 469 int err; 470 struct auth_domain *dom = NULL; 471 struct nameidata nd; 472 struct svc_export exp, *expp; 473 int an_int; 474 475 nd.dentry = NULL; 476 exp.ex_path = NULL; 477 478 if (mesg[mlen-1] != '\n') 479 return -EINVAL; 480 mesg[mlen-1] = 0; 481 482 buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 483 err = -ENOMEM; 484 if (!buf) goto out; 485 486 /* client */ 487 len = qword_get(&mesg, buf, PAGE_SIZE); 488 err = -EINVAL; 489 if (len <= 0) goto out; 490 491 err = -ENOENT; 492 dom = auth_domain_find(buf); 493 if (!dom) 494 goto out; 495 496 /* path */ 497 err = -EINVAL; 498 if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) 499 goto out; 500 err = path_lookup(buf, 0, &nd); 501 if (err) goto out_no_path; 502 503 exp.h.flags = 0; 504 exp.ex_client = dom; 505 exp.ex_mnt = nd.mnt; 506 exp.ex_dentry = nd.dentry; 507 exp.ex_path = kstrdup(buf, GFP_KERNEL); 508 err = -ENOMEM; 509 if (!exp.ex_path) 510 goto out; 511 512 /* expiry */ 513 err = -EINVAL; 514 exp.h.expiry_time = get_expiry(&mesg); 515 if (exp.h.expiry_time == 0) 516 goto out; 517 518 /* fs locations */ 519 exp.ex_fslocs.locations = NULL; 520 exp.ex_fslocs.locations_count = 0; 521 exp.ex_fslocs.migrated = 0; 522 523 /* flags */ 524 err = get_int(&mesg, &an_int); 525 if (err == -ENOENT) 526 set_bit(CACHE_NEGATIVE, &exp.h.flags); 527 else { 528 if (err || an_int < 0) goto out; 529 exp.ex_flags= an_int; 530 531 /* anon uid */ 532 err = get_int(&mesg, &an_int); 533 if (err) goto out; 534 exp.ex_anon_uid= an_int; 535 536 /* anon gid */ 537 err = get_int(&mesg, &an_int); 538 if (err) goto out; 539 exp.ex_anon_gid= an_int; 540 541 /* fsid */ 542 err = get_int(&mesg, &an_int); 543 if (err) goto out; 544 exp.ex_fsid = an_int; 545 546 err = check_export(nd.dentry->d_inode, exp.ex_flags); 547 if (err) goto out; 548 549 err = fsloc_parse(&mesg, buf, &exp.ex_fslocs); 550 if (err) 551 goto out; 552 } 553 554 expp = svc_export_lookup(&exp); 555 if (expp) 556 expp = svc_export_update(&exp, expp); 557 else 558 err = -ENOMEM; 559 cache_flush(); 560 if (expp == NULL) 561 err = -ENOMEM; 562 else 563 exp_put(expp); 564 out: 565 kfree(exp.ex_path); 566 if (nd.dentry) 567 path_release(&nd); 568 out_no_path: 569 if (dom) 570 auth_domain_put(dom); 571 kfree(buf); 572 return err; 573 } 574 575 static void exp_flags(struct seq_file *m, int flag, int fsid, 576 uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); 577 578 static int svc_export_show(struct seq_file *m, 579 struct cache_detail *cd, 580 struct cache_head *h) 581 { 582 struct svc_export *exp ; 583 584 if (h ==NULL) { 585 seq_puts(m, "#path domain(flags)\n"); 586 return 0; 587 } 588 exp = container_of(h, struct svc_export, h); 589 seq_path(m, exp->ex_mnt, exp->ex_dentry, " \t\n\\"); 590 seq_putc(m, '\t'); 591 seq_escape(m, exp->ex_client->name, " \t\n\\"); 592 seq_putc(m, '('); 593 if (test_bit(CACHE_VALID, &h->flags) && 594 !test_bit(CACHE_NEGATIVE, &h->flags)) 595 exp_flags(m, exp->ex_flags, exp->ex_fsid, 596 exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs); 597 seq_puts(m, ")\n"); 598 return 0; 599 } 600 static int svc_export_match(struct cache_head *a, struct cache_head *b) 601 { 602 struct svc_export *orig = container_of(a, struct svc_export, h); 603 struct svc_export *new = container_of(b, struct svc_export, h); 604 return orig->ex_client == new->ex_client && 605 orig->ex_dentry == new->ex_dentry && 606 orig->ex_mnt == new->ex_mnt; 607 } 608 609 static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) 610 { 611 struct svc_export *new = container_of(cnew, struct svc_export, h); 612 struct svc_export *item = container_of(citem, struct svc_export, h); 613 614 kref_get(&item->ex_client->ref); 615 new->ex_client = item->ex_client; 616 new->ex_dentry = dget(item->ex_dentry); 617 new->ex_mnt = mntget(item->ex_mnt); 618 new->ex_path = NULL; 619 new->ex_fslocs.locations = NULL; 620 new->ex_fslocs.locations_count = 0; 621 new->ex_fslocs.migrated = 0; 622 } 623 624 static void export_update(struct cache_head *cnew, struct cache_head *citem) 625 { 626 struct svc_export *new = container_of(cnew, struct svc_export, h); 627 struct svc_export *item = container_of(citem, struct svc_export, h); 628 629 new->ex_flags = item->ex_flags; 630 new->ex_anon_uid = item->ex_anon_uid; 631 new->ex_anon_gid = item->ex_anon_gid; 632 new->ex_fsid = item->ex_fsid; 633 new->ex_path = item->ex_path; 634 item->ex_path = NULL; 635 new->ex_fslocs.locations = item->ex_fslocs.locations; 636 item->ex_fslocs.locations = NULL; 637 new->ex_fslocs.locations_count = item->ex_fslocs.locations_count; 638 item->ex_fslocs.locations_count = 0; 639 new->ex_fslocs.migrated = item->ex_fslocs.migrated; 640 item->ex_fslocs.migrated = 0; 641 } 642 643 static struct cache_head *svc_export_alloc(void) 644 { 645 struct svc_export *i = kmalloc(sizeof(*i), GFP_KERNEL); 646 if (i) 647 return &i->h; 648 else 649 return NULL; 650 } 651 652 struct cache_detail svc_export_cache = { 653 .owner = THIS_MODULE, 654 .hash_size = EXPORT_HASHMAX, 655 .hash_table = export_table, 656 .name = "nfsd.export", 657 .cache_put = svc_export_put, 658 .cache_request = svc_export_request, 659 .cache_parse = svc_export_parse, 660 .cache_show = svc_export_show, 661 .match = svc_export_match, 662 .init = svc_export_init, 663 .update = export_update, 664 .alloc = svc_export_alloc, 665 }; 666 667 static struct svc_export * 668 svc_export_lookup(struct svc_export *exp) 669 { 670 struct cache_head *ch; 671 int hash; 672 hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); 673 hash ^= hash_ptr(exp->ex_dentry, EXPORT_HASHBITS); 674 hash ^= hash_ptr(exp->ex_mnt, EXPORT_HASHBITS); 675 676 ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, 677 hash); 678 if (ch) 679 return container_of(ch, struct svc_export, h); 680 else 681 return NULL; 682 } 683 684 static struct svc_export * 685 svc_export_update(struct svc_export *new, struct svc_export *old) 686 { 687 struct cache_head *ch; 688 int hash; 689 hash = hash_ptr(old->ex_client, EXPORT_HASHBITS); 690 hash ^= hash_ptr(old->ex_dentry, EXPORT_HASHBITS); 691 hash ^= hash_ptr(old->ex_mnt, EXPORT_HASHBITS); 692 693 ch = sunrpc_cache_update(&svc_export_cache, &new->h, 694 &old->h, 695 hash); 696 if (ch) 697 return container_of(ch, struct svc_export, h); 698 else 699 return NULL; 700 } 701 702 703 static struct svc_expkey * 704 exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp) 705 { 706 struct svc_expkey key, *ek; 707 int err; 708 709 if (!clp) 710 return NULL; 711 712 key.ek_client = clp; 713 key.ek_fsidtype = fsid_type; 714 memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); 715 716 ek = svc_expkey_lookup(&key); 717 if (ek != NULL) 718 if ((err = cache_check(&svc_expkey_cache, &ek->h, reqp))) 719 ek = ERR_PTR(err); 720 return ek; 721 } 722 723 static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv, 724 struct svc_export *exp) 725 { 726 struct svc_expkey key, *ek; 727 728 key.ek_client = clp; 729 key.ek_fsidtype = fsid_type; 730 memcpy(key.ek_fsid, fsidv, key_len(fsid_type)); 731 key.ek_mnt = exp->ex_mnt; 732 key.ek_dentry = exp->ex_dentry; 733 key.h.expiry_time = NEVER; 734 key.h.flags = 0; 735 736 ek = svc_expkey_lookup(&key); 737 if (ek) 738 ek = svc_expkey_update(&key,ek); 739 if (ek) { 740 cache_put(&ek->h, &svc_expkey_cache); 741 return 0; 742 } 743 return -ENOMEM; 744 } 745 746 /* 747 * Find the client's export entry matching xdev/xino. 748 */ 749 static inline struct svc_expkey * 750 exp_get_key(svc_client *clp, dev_t dev, ino_t ino) 751 { 752 u32 fsidv[3]; 753 754 if (old_valid_dev(dev)) { 755 mk_fsid_v0(fsidv, dev, ino); 756 return exp_find_key(clp, 0, fsidv, NULL); 757 } 758 mk_fsid_v3(fsidv, dev, ino); 759 return exp_find_key(clp, 3, fsidv, NULL); 760 } 761 762 /* 763 * Find the client's export entry matching fsid 764 */ 765 static inline struct svc_expkey * 766 exp_get_fsid_key(svc_client *clp, int fsid) 767 { 768 u32 fsidv[2]; 769 770 mk_fsid_v1(fsidv, fsid); 771 772 return exp_find_key(clp, 1, fsidv, NULL); 773 } 774 775 svc_export * 776 exp_get_by_name(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, 777 struct cache_req *reqp) 778 { 779 struct svc_export *exp, key; 780 781 if (!clp) 782 return NULL; 783 784 key.ex_client = clp; 785 key.ex_mnt = mnt; 786 key.ex_dentry = dentry; 787 788 exp = svc_export_lookup(&key); 789 if (exp != NULL) { 790 int err; 791 792 err = cache_check(&svc_export_cache, &exp->h, reqp); 793 switch (err) { 794 case 0: break; 795 case -EAGAIN: 796 case -ETIMEDOUT: 797 exp = ERR_PTR(err); 798 break; 799 default: 800 exp = NULL; 801 } 802 } 803 804 return exp; 805 } 806 807 /* 808 * Find the export entry for a given dentry. 809 */ 810 struct svc_export * 811 exp_parent(svc_client *clp, struct vfsmount *mnt, struct dentry *dentry, 812 struct cache_req *reqp) 813 { 814 svc_export *exp; 815 816 dget(dentry); 817 exp = exp_get_by_name(clp, mnt, dentry, reqp); 818 819 while (exp == NULL && !IS_ROOT(dentry)) { 820 struct dentry *parent; 821 822 parent = dget_parent(dentry); 823 dput(dentry); 824 dentry = parent; 825 exp = exp_get_by_name(clp, mnt, dentry, reqp); 826 } 827 dput(dentry); 828 return exp; 829 } 830 831 /* 832 * Hashtable locking. Write locks are placed only by user processes 833 * wanting to modify export information. 834 * Write locking only done in this file. Read locking 835 * needed externally. 836 */ 837 838 static DECLARE_RWSEM(hash_sem); 839 840 void 841 exp_readlock(void) 842 { 843 down_read(&hash_sem); 844 } 845 846 static inline void 847 exp_writelock(void) 848 { 849 down_write(&hash_sem); 850 } 851 852 void 853 exp_readunlock(void) 854 { 855 up_read(&hash_sem); 856 } 857 858 static inline void 859 exp_writeunlock(void) 860 { 861 up_write(&hash_sem); 862 } 863 864 static void exp_fsid_unhash(struct svc_export *exp) 865 { 866 struct svc_expkey *ek; 867 868 if ((exp->ex_flags & NFSEXP_FSID) == 0) 869 return; 870 871 ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid); 872 if (ek && !IS_ERR(ek)) { 873 ek->h.expiry_time = get_seconds()-1; 874 cache_put(&ek->h, &svc_expkey_cache); 875 } 876 svc_expkey_cache.nextcheck = get_seconds(); 877 } 878 879 static int exp_fsid_hash(svc_client *clp, struct svc_export *exp) 880 { 881 u32 fsid[2]; 882 883 if ((exp->ex_flags & NFSEXP_FSID) == 0) 884 return 0; 885 886 mk_fsid_v1(fsid, exp->ex_fsid); 887 return exp_set_key(clp, 1, fsid, exp); 888 } 889 890 static int exp_hash(struct auth_domain *clp, struct svc_export *exp) 891 { 892 u32 fsid[2]; 893 struct inode *inode = exp->ex_dentry->d_inode; 894 dev_t dev = inode->i_sb->s_dev; 895 896 if (old_valid_dev(dev)) { 897 mk_fsid_v0(fsid, dev, inode->i_ino); 898 return exp_set_key(clp, 0, fsid, exp); 899 } 900 mk_fsid_v3(fsid, dev, inode->i_ino); 901 return exp_set_key(clp, 3, fsid, exp); 902 } 903 904 static void exp_unhash(struct svc_export *exp) 905 { 906 struct svc_expkey *ek; 907 struct inode *inode = exp->ex_dentry->d_inode; 908 909 ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino); 910 if (ek && !IS_ERR(ek)) { 911 ek->h.expiry_time = get_seconds()-1; 912 cache_put(&ek->h, &svc_expkey_cache); 913 } 914 svc_expkey_cache.nextcheck = get_seconds(); 915 } 916 917 /* 918 * Export a file system. 919 */ 920 int 921 exp_export(struct nfsctl_export *nxp) 922 { 923 svc_client *clp; 924 struct svc_export *exp = NULL; 925 struct svc_export new; 926 struct svc_expkey *fsid_key = NULL; 927 struct nameidata nd; 928 int err; 929 930 /* Consistency check */ 931 err = -EINVAL; 932 if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || 933 !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) 934 goto out; 935 936 dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", 937 nxp->ex_client, nxp->ex_path, 938 (unsigned)nxp->ex_dev, (long)nxp->ex_ino, 939 nxp->ex_flags); 940 941 /* Try to lock the export table for update */ 942 exp_writelock(); 943 944 /* Look up client info */ 945 if (!(clp = auth_domain_find(nxp->ex_client))) 946 goto out_unlock; 947 948 949 /* Look up the dentry */ 950 err = path_lookup(nxp->ex_path, 0, &nd); 951 if (err) 952 goto out_unlock; 953 err = -EINVAL; 954 955 exp = exp_get_by_name(clp, nd.mnt, nd.dentry, NULL); 956 957 memset(&new, 0, sizeof(new)); 958 959 /* must make sure there won't be an ex_fsid clash */ 960 if ((nxp->ex_flags & NFSEXP_FSID) && 961 (fsid_key = exp_get_fsid_key(clp, nxp->ex_dev)) && 962 !IS_ERR(fsid_key) && 963 fsid_key->ek_mnt && 964 (fsid_key->ek_mnt != nd.mnt || fsid_key->ek_dentry != nd.dentry) ) 965 goto finish; 966 967 if (exp) { 968 /* just a flags/id/fsid update */ 969 970 exp_fsid_unhash(exp); 971 exp->ex_flags = nxp->ex_flags; 972 exp->ex_anon_uid = nxp->ex_anon_uid; 973 exp->ex_anon_gid = nxp->ex_anon_gid; 974 exp->ex_fsid = nxp->ex_dev; 975 976 err = exp_fsid_hash(clp, exp); 977 goto finish; 978 } 979 980 err = check_export(nd.dentry->d_inode, nxp->ex_flags); 981 if (err) goto finish; 982 983 err = -ENOMEM; 984 985 dprintk("nfsd: creating export entry %p for client %p\n", exp, clp); 986 987 new.h.expiry_time = NEVER; 988 new.h.flags = 0; 989 new.ex_path = kstrdup(nxp->ex_path, GFP_KERNEL); 990 if (!new.ex_path) 991 goto finish; 992 new.ex_client = clp; 993 new.ex_mnt = nd.mnt; 994 new.ex_dentry = nd.dentry; 995 new.ex_flags = nxp->ex_flags; 996 new.ex_anon_uid = nxp->ex_anon_uid; 997 new.ex_anon_gid = nxp->ex_anon_gid; 998 new.ex_fsid = nxp->ex_dev; 999 1000 exp = svc_export_lookup(&new); 1001 if (exp) 1002 exp = svc_export_update(&new, exp); 1003 1004 if (!exp) 1005 goto finish; 1006 1007 if (exp_hash(clp, exp) || 1008 exp_fsid_hash(clp, exp)) { 1009 /* failed to create at least one index */ 1010 exp_do_unexport(exp); 1011 cache_flush(); 1012 } else 1013 err = 0; 1014 finish: 1015 if (new.ex_path) 1016 kfree(new.ex_path); 1017 if (exp) 1018 exp_put(exp); 1019 if (fsid_key && !IS_ERR(fsid_key)) 1020 cache_put(&fsid_key->h, &svc_expkey_cache); 1021 if (clp) 1022 auth_domain_put(clp); 1023 path_release(&nd); 1024 out_unlock: 1025 exp_writeunlock(); 1026 out: 1027 return err; 1028 } 1029 1030 /* 1031 * Unexport a file system. The export entry has already 1032 * been removed from the client's list of exported fs's. 1033 */ 1034 static void 1035 exp_do_unexport(svc_export *unexp) 1036 { 1037 unexp->h.expiry_time = get_seconds()-1; 1038 svc_export_cache.nextcheck = get_seconds(); 1039 exp_unhash(unexp); 1040 exp_fsid_unhash(unexp); 1041 } 1042 1043 1044 /* 1045 * unexport syscall. 1046 */ 1047 int 1048 exp_unexport(struct nfsctl_export *nxp) 1049 { 1050 struct auth_domain *dom; 1051 svc_export *exp; 1052 struct nameidata nd; 1053 int err; 1054 1055 /* Consistency check */ 1056 if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || 1057 !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) 1058 return -EINVAL; 1059 1060 exp_writelock(); 1061 1062 err = -EINVAL; 1063 dom = auth_domain_find(nxp->ex_client); 1064 if (!dom) { 1065 dprintk("nfsd: unexport couldn't find %s\n", nxp->ex_client); 1066 goto out_unlock; 1067 } 1068 1069 err = path_lookup(nxp->ex_path, 0, &nd); 1070 if (err) 1071 goto out_domain; 1072 1073 err = -EINVAL; 1074 exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); 1075 path_release(&nd); 1076 if (!exp) 1077 goto out_domain; 1078 1079 exp_do_unexport(exp); 1080 exp_put(exp); 1081 err = 0; 1082 1083 out_domain: 1084 auth_domain_put(dom); 1085 cache_flush(); 1086 out_unlock: 1087 exp_writeunlock(); 1088 return err; 1089 } 1090 1091 /* 1092 * Obtain the root fh on behalf of a client. 1093 * This could be done in user space, but I feel that it adds some safety 1094 * since its harder to fool a kernel module than a user space program. 1095 */ 1096 int 1097 exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) 1098 { 1099 struct svc_export *exp; 1100 struct nameidata nd; 1101 struct inode *inode; 1102 struct svc_fh fh; 1103 int err; 1104 1105 err = -EPERM; 1106 /* NB: we probably ought to check that it's NUL-terminated */ 1107 if (path_lookup(path, 0, &nd)) { 1108 printk("nfsd: exp_rootfh path not found %s", path); 1109 return err; 1110 } 1111 inode = nd.dentry->d_inode; 1112 1113 dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n", 1114 path, nd.dentry, clp->name, 1115 inode->i_sb->s_id, inode->i_ino); 1116 exp = exp_parent(clp, nd.mnt, nd.dentry, NULL); 1117 if (IS_ERR(exp)) { 1118 err = PTR_ERR(exp); 1119 goto out; 1120 } 1121 if (!exp) { 1122 dprintk("nfsd: exp_rootfh export not found.\n"); 1123 goto out; 1124 } 1125 1126 /* 1127 * fh must be initialized before calling fh_compose 1128 */ 1129 fh_init(&fh, maxsize); 1130 if (fh_compose(&fh, exp, nd.dentry, NULL)) 1131 err = -EINVAL; 1132 else 1133 err = 0; 1134 memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh)); 1135 fh_put(&fh); 1136 exp_put(exp); 1137 out: 1138 path_release(&nd); 1139 return err; 1140 } 1141 1142 struct svc_export * 1143 exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, 1144 struct cache_req *reqp) 1145 { 1146 struct svc_export *exp; 1147 struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); 1148 if (!ek || IS_ERR(ek)) 1149 return ERR_PTR(PTR_ERR(ek)); 1150 1151 exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); 1152 cache_put(&ek->h, &svc_expkey_cache); 1153 1154 if (!exp || IS_ERR(exp)) 1155 return ERR_PTR(PTR_ERR(exp)); 1156 return exp; 1157 } 1158 1159 1160 /* 1161 * Called when we need the filehandle for the root of the pseudofs, 1162 * for a given NFSv4 client. The root is defined to be the 1163 * export point with fsid==0 1164 */ 1165 __be32 1166 exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, 1167 struct cache_req *creq) 1168 { 1169 struct svc_export *exp; 1170 __be32 rv; 1171 u32 fsidv[2]; 1172 1173 mk_fsid_v1(fsidv, 0); 1174 1175 exp = exp_find(clp, 1, fsidv, creq); 1176 if (IS_ERR(exp)) 1177 return nfserrno(PTR_ERR(exp)); 1178 if (exp == NULL) 1179 return nfserr_perm; 1180 rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); 1181 exp_put(exp); 1182 return rv; 1183 } 1184 1185 /* Iterator */ 1186 1187 static void *e_start(struct seq_file *m, loff_t *pos) 1188 __acquires(svc_export_cache.hash_lock) 1189 { 1190 loff_t n = *pos; 1191 unsigned hash, export; 1192 struct cache_head *ch; 1193 1194 exp_readlock(); 1195 read_lock(&svc_export_cache.hash_lock); 1196 if (!n--) 1197 return SEQ_START_TOKEN; 1198 hash = n >> 32; 1199 export = n & ((1LL<<32) - 1); 1200 1201 1202 for (ch=export_table[hash]; ch; ch=ch->next) 1203 if (!export--) 1204 return ch; 1205 n &= ~((1LL<<32) - 1); 1206 do { 1207 hash++; 1208 n += 1LL<<32; 1209 } while(hash < EXPORT_HASHMAX && export_table[hash]==NULL); 1210 if (hash >= EXPORT_HASHMAX) 1211 return NULL; 1212 *pos = n+1; 1213 return export_table[hash]; 1214 } 1215 1216 static void *e_next(struct seq_file *m, void *p, loff_t *pos) 1217 { 1218 struct cache_head *ch = p; 1219 int hash = (*pos >> 32); 1220 1221 if (p == SEQ_START_TOKEN) 1222 hash = 0; 1223 else if (ch->next == NULL) { 1224 hash++; 1225 *pos += 1LL<<32; 1226 } else { 1227 ++*pos; 1228 return ch->next; 1229 } 1230 *pos &= ~((1LL<<32) - 1); 1231 while (hash < EXPORT_HASHMAX && export_table[hash] == NULL) { 1232 hash++; 1233 *pos += 1LL<<32; 1234 } 1235 if (hash >= EXPORT_HASHMAX) 1236 return NULL; 1237 ++*pos; 1238 return export_table[hash]; 1239 } 1240 1241 static void e_stop(struct seq_file *m, void *p) 1242 __releases(svc_export_cache.hash_lock) 1243 { 1244 read_unlock(&svc_export_cache.hash_lock); 1245 exp_readunlock(); 1246 } 1247 1248 static struct flags { 1249 int flag; 1250 char *name[2]; 1251 } expflags[] = { 1252 { NFSEXP_READONLY, {"ro", "rw"}}, 1253 { NFSEXP_INSECURE_PORT, {"insecure", ""}}, 1254 { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}}, 1255 { NFSEXP_ALLSQUASH, {"all_squash", ""}}, 1256 { NFSEXP_ASYNC, {"async", "sync"}}, 1257 { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}}, 1258 { NFSEXP_NOHIDE, {"nohide", ""}}, 1259 { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, 1260 { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, 1261 { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, 1262 #ifdef MSNFS 1263 { NFSEXP_MSNFS, {"msnfs", ""}}, 1264 #endif 1265 { 0, {"", ""}} 1266 }; 1267 1268 static void exp_flags(struct seq_file *m, int flag, int fsid, 1269 uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) 1270 { 1271 int first = 0; 1272 struct flags *flg; 1273 1274 for (flg = expflags; flg->flag; flg++) { 1275 int state = (flg->flag & flag)?0:1; 1276 if (*flg->name[state]) 1277 seq_printf(m, "%s%s", first++?",":"", flg->name[state]); 1278 } 1279 if (flag & NFSEXP_FSID) 1280 seq_printf(m, "%sfsid=%d", first++?",":"", fsid); 1281 if (anonu != (uid_t)-2 && anonu != (0x10000-2)) 1282 seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); 1283 if (anong != (gid_t)-2 && anong != (0x10000-2)) 1284 seq_printf(m, "%sanongid=%d", first++?",":"", anong); 1285 if (fsloc && fsloc->locations_count > 0) { 1286 char *loctype = (fsloc->migrated) ? "refer" : "replicas"; 1287 int i; 1288 1289 seq_printf(m, "%s%s=", first++?",":"", loctype); 1290 seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); 1291 seq_putc(m, '@'); 1292 seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); 1293 for (i = 1; i < fsloc->locations_count; i++) { 1294 seq_putc(m, ';'); 1295 seq_escape(m, fsloc->locations[i].path, ",;@ \t\n\\"); 1296 seq_putc(m, '@'); 1297 seq_escape(m, fsloc->locations[i].hosts, ",;@ \t\n\\"); 1298 } 1299 } 1300 } 1301 1302 static int e_show(struct seq_file *m, void *p) 1303 { 1304 struct cache_head *cp = p; 1305 struct svc_export *exp = container_of(cp, struct svc_export, h); 1306 1307 if (p == SEQ_START_TOKEN) { 1308 seq_puts(m, "# Version 1.1\n"); 1309 seq_puts(m, "# Path Client(Flags) # IPs\n"); 1310 return 0; 1311 } 1312 1313 cache_get(&exp->h); 1314 if (cache_check(&svc_export_cache, &exp->h, NULL)) 1315 return 0; 1316 cache_put(&exp->h, &svc_export_cache); 1317 return svc_export_show(m, &svc_export_cache, cp); 1318 } 1319 1320 struct seq_operations nfs_exports_op = { 1321 .start = e_start, 1322 .next = e_next, 1323 .stop = e_stop, 1324 .show = e_show, 1325 }; 1326 1327 /* 1328 * Add or modify a client. 1329 * Change requests may involve the list of host addresses. The list of 1330 * exports and possibly existing uid maps are left untouched. 1331 */ 1332 int 1333 exp_addclient(struct nfsctl_client *ncp) 1334 { 1335 struct auth_domain *dom; 1336 int i, err; 1337 1338 /* First, consistency check. */ 1339 err = -EINVAL; 1340 if (! exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) 1341 goto out; 1342 if (ncp->cl_naddr > NFSCLNT_ADDRMAX) 1343 goto out; 1344 1345 /* Lock the hashtable */ 1346 exp_writelock(); 1347 1348 dom = unix_domain_find(ncp->cl_ident); 1349 1350 err = -ENOMEM; 1351 if (!dom) 1352 goto out_unlock; 1353 1354 /* Insert client into hashtable. */ 1355 for (i = 0; i < ncp->cl_naddr; i++) 1356 auth_unix_add_addr(ncp->cl_addrlist[i], dom); 1357 1358 auth_unix_forget_old(dom); 1359 auth_domain_put(dom); 1360 1361 err = 0; 1362 1363 out_unlock: 1364 exp_writeunlock(); 1365 out: 1366 return err; 1367 } 1368 1369 /* 1370 * Delete a client given an identifier. 1371 */ 1372 int 1373 exp_delclient(struct nfsctl_client *ncp) 1374 { 1375 int err; 1376 struct auth_domain *dom; 1377 1378 err = -EINVAL; 1379 if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) 1380 goto out; 1381 1382 /* Lock the hashtable */ 1383 exp_writelock(); 1384 1385 dom = auth_domain_find(ncp->cl_ident); 1386 /* just make sure that no addresses work 1387 * and that it will expire soon 1388 */ 1389 if (dom) { 1390 err = auth_unix_forget_old(dom); 1391 auth_domain_put(dom); 1392 } 1393 1394 exp_writeunlock(); 1395 out: 1396 return err; 1397 } 1398 1399 /* 1400 * Verify that string is non-empty and does not exceed max length. 1401 */ 1402 static int 1403 exp_verify_string(char *cp, int max) 1404 { 1405 int i; 1406 1407 for (i = 0; i < max; i++) 1408 if (!cp[i]) 1409 return i; 1410 cp[i] = 0; 1411 printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp); 1412 return 0; 1413 } 1414 1415 /* 1416 * Initialize the exports module. 1417 */ 1418 void 1419 nfsd_export_init(void) 1420 { 1421 dprintk("nfsd: initializing export module.\n"); 1422 1423 cache_register(&svc_export_cache); 1424 cache_register(&svc_expkey_cache); 1425 1426 } 1427 1428 /* 1429 * Flush exports table - called when last nfsd thread is killed 1430 */ 1431 void 1432 nfsd_export_flush(void) 1433 { 1434 exp_writelock(); 1435 cache_purge(&svc_expkey_cache); 1436 cache_purge(&svc_export_cache); 1437 exp_writeunlock(); 1438 } 1439 1440 /* 1441 * Shutdown the exports module. 1442 */ 1443 void 1444 nfsd_export_shutdown(void) 1445 { 1446 1447 dprintk("nfsd: shutting down export module.\n"); 1448 1449 exp_writelock(); 1450 1451 if (cache_unregister(&svc_expkey_cache)) 1452 printk(KERN_ERR "nfsd: failed to unregister expkey cache\n"); 1453 if (cache_unregister(&svc_export_cache)) 1454 printk(KERN_ERR "nfsd: failed to unregister export cache\n"); 1455 svcauth_unix_purge(); 1456 1457 exp_writeunlock(); 1458 dprintk("nfsd: export shutdown complete.\n"); 1459 } 1460