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