1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #include <sys/systm.h> 28 29 #include <nfs/nfs.h> 30 #include <nfs/export.h> 31 #include <sys/cmn_err.h> 32 33 #define PSEUDOFS_SUFFIX " (pseudo)" 34 35 /* 36 * A version of VOP_FID that deals with a remote VOP_FID for nfs. 37 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid() 38 * returns the filehandle of vp as its fid. When nfs uses fid to set the 39 * exportinfo filehandle template, a remote nfs filehandle would be too big for 40 * the fid of the exported directory. This routine remaps the value of the 41 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit. 42 * 43 * We need this fid mainly for setting up NFSv4 server namespace where an 44 * nfs filesystem is also part of it. Thus, need to be able to setup a pseudo 45 * exportinfo for an nfs node. 46 * 47 * e.g. mount a filesystem on top of a nfs dir, and then share the new mount 48 * (like exporting a local disk from a "diskless" client) 49 */ 50 int 51 vop_fid_pseudo(vnode_t *vp, fid_t *fidp) 52 { 53 struct vattr va; 54 int error; 55 56 error = VOP_FID(vp, fidp, NULL); 57 58 /* 59 * XXX nfs4_fid() does nothing and returns EREMOTE. 60 * XXX nfs3_fid()/nfs_fid() returns nfs filehandle as its fid 61 * which has a bigger length than local fid. 62 * NFS_FH4MAXDATA is the size of 63 * fhandle4_t.fh_xdata[NFS_FH4MAXDATA]. 64 * 65 * Note: nfs[2,3,4]_fid() only gets called for diskless clients. 66 */ 67 if (error == EREMOTE || 68 (error == 0 && fidp->fid_len > NFS_FH4MAXDATA)) { 69 70 va.va_mask = AT_NODEID; 71 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); 72 if (error) 73 return (error); 74 75 fidp->fid_len = sizeof (va.va_nodeid); 76 bcopy(&va.va_nodeid, fidp->fid_data, fidp->fid_len); 77 return (0); 78 } 79 80 return (error); 81 } 82 83 /* 84 * Get an nfsv4 vnode of the given fid from the visible list of an 85 * nfs filesystem or get the exi_vp if it is the root node. 86 */ 87 int 88 nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp) 89 { 90 fid_t exp_fid; 91 struct exp_visible *visp; 92 int error; 93 94 /* check if the given fid is in the visible list */ 95 96 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 97 if (EQFID(fidp, &visp->vis_fid)) { 98 VN_HOLD(visp->vis_vp); 99 *vpp = visp->vis_vp; 100 return (0); 101 } 102 } 103 104 /* check if the given fid is the same as the exported node */ 105 106 bzero(&exp_fid, sizeof (exp_fid)); 107 exp_fid.fid_len = MAXFIDSZ; 108 error = vop_fid_pseudo(exi->exi_vp, &exp_fid); 109 if (error) 110 return (error); 111 112 if (EQFID(fidp, &exp_fid)) { 113 VN_HOLD(exi->exi_vp); 114 *vpp = exi->exi_vp; 115 return (0); 116 } 117 118 return (ENOENT); 119 } 120 121 /* 122 * Create a pseudo export entry 123 * 124 * This is an export entry that's created as the 125 * side-effect of a "real" export. As a part of 126 * a real export, the pathname to the export is 127 * checked to see if all the directory components 128 * are accessible via an NFSv4 client, i.e. are 129 * exported. If treeclimb_export() finds an unexported 130 * mountpoint along the path, then it calls this 131 * function to export it. 132 * 133 * This pseudo export differs from a real export in that 134 * it only allows read-only access. A "visible" list of 135 * directories is added to filter lookup and readdir results 136 * to only contain dirnames which lead to descendant shares. 137 * 138 * A visible list has a per-file-system scope. Any exportinfo 139 * struct (real or pseudo) can have a visible list as long as 140 * a) its export root is VROOT 141 * b) a descendant of the export root is shared 142 */ 143 int 144 pseudo_exportfs(vnode_t *vp, struct exp_visible *vis_head, 145 struct exportdata *exdata, struct exportinfo **exi_retp) 146 { 147 struct exportinfo *exi; 148 struct exportdata *kex; 149 fid_t fid; 150 fsid_t fsid; 151 int error, vpathlen; 152 153 ASSERT(RW_WRITE_HELD(&exported_lock)); 154 155 /* 156 * Get the vfs id 157 */ 158 bzero(&fid, sizeof (fid)); 159 fid.fid_len = MAXFIDSZ; 160 error = vop_fid_pseudo(vp, &fid); 161 if (error) { 162 /* 163 * If VOP_FID returns ENOSPC then the fid supplied 164 * is too small. For now we simply return EREMOTE. 165 */ 166 if (error == ENOSPC) 167 error = EREMOTE; 168 return (error); 169 } 170 171 fsid = vp->v_vfsp->vfs_fsid; 172 exi = kmem_zalloc(sizeof (*exi), KM_SLEEP); 173 exi->exi_fsid = fsid; 174 exi->exi_fid = fid; 175 exi->exi_vp = vp; 176 VN_HOLD(exi->exi_vp); 177 exi->exi_visible = vis_head; 178 exi->exi_count = 1; 179 exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag & 180 VSW_VOLATILEDEV) ? 1 : 0; 181 mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL); 182 183 /* 184 * Build up the template fhandle 185 */ 186 exi->exi_fh.fh_fsid = fsid; 187 ASSERT(exi->exi_fid.fid_len <= sizeof (exi->exi_fh.fh_xdata)); 188 exi->exi_fh.fh_xlen = exi->exi_fid.fid_len; 189 bcopy(exi->exi_fid.fid_data, exi->exi_fh.fh_xdata, 190 exi->exi_fid.fid_len); 191 exi->exi_fh.fh_len = sizeof (exi->exi_fh.fh_data); 192 193 kex = &exi->exi_export; 194 kex->ex_flags = EX_PSEUDO; 195 196 vpathlen = vp->v_path ? strlen(vp->v_path) : 0; 197 kex->ex_pathlen = vpathlen + strlen(PSEUDOFS_SUFFIX); 198 kex->ex_path = kmem_alloc(kex->ex_pathlen + 1, KM_SLEEP); 199 200 if (vpathlen) 201 (void) strcpy(kex->ex_path, vp->v_path); 202 (void) strcpy(kex->ex_path + vpathlen, PSEUDOFS_SUFFIX); 203 204 /* Transfer the secinfo data from exdata to this new pseudo node */ 205 if (exdata) 206 srv_secinfo_exp2pseu(&exi->exi_export, exdata); 207 208 /* 209 * Initialize auth cache lock 210 */ 211 rw_init(&exi->exi_cache_lock, NULL, RW_DEFAULT, NULL); 212 213 /* 214 * Insert the new entry at the front of the export list 215 */ 216 export_link(exi); 217 218 /* 219 * If exi_retp is non-NULL return a pointer to the new 220 * exportinfo structure. 221 */ 222 if (exi_retp) 223 *exi_retp = exi; 224 225 return (0); 226 } 227 228 /* 229 * Free a list of visible directories 230 */ 231 void 232 free_visible(struct exp_visible *head) 233 { 234 struct exp_visible *visp, *next; 235 236 for (visp = head; visp; visp = next) { 237 if (visp->vis_vp != NULL) 238 VN_RELE(visp->vis_vp); 239 240 next = visp->vis_next; 241 srv_secinfo_list_free(visp->vis_secinfo, visp->vis_seccnt); 242 kmem_free(visp, sizeof (*visp)); 243 } 244 } 245 246 /* 247 * Connects newchild (or subtree with newchild in head) 248 * to the parent node. We always add it to the beginning 249 * of sibling list. 250 */ 251 static void 252 tree_add_child(treenode_t *parent, treenode_t *newchild) 253 { 254 newchild->tree_parent = parent; 255 newchild->tree_sibling = parent->tree_child_first; 256 parent->tree_child_first = newchild; 257 } 258 259 /* 260 * Add new node to the head of subtree pointed by 'n'. n can be NULL. 261 * Interconnects the new treenode with exp_visible and exportinfo 262 * if needed. 263 */ 264 static treenode_t * 265 tree_prepend_node(treenode_t *n, exp_visible_t *v, exportinfo_t *e) 266 { 267 treenode_t *tnode = kmem_zalloc(sizeof (*tnode), KM_SLEEP); 268 269 if (n) { 270 tnode->tree_child_first = n; 271 n->tree_parent = tnode; 272 } 273 if (v) { 274 tnode->tree_vis = v; 275 v->vis_tree = tnode; 276 } 277 if (e) { 278 tnode->tree_exi = e; 279 e->exi_tree = tnode; 280 } 281 return (tnode); 282 } 283 284 /* 285 * Removes node from the tree and frees the treenode struct. 286 * Does not free structures pointed by tree_exi and tree_vis, 287 * they should be already freed. 288 */ 289 static void 290 tree_remove_node(treenode_t *node) 291 { 292 treenode_t *parent = node->tree_parent; 293 treenode_t *s; /* s for sibling */ 294 295 if (parent == NULL) { 296 kmem_free(node, sizeof (*node)); 297 ns_root = NULL; 298 return; 299 } 300 /* This node is first child */ 301 if (parent->tree_child_first == node) { 302 parent->tree_child_first = node->tree_sibling; 303 /* This node is not first child */ 304 } else { 305 s = parent->tree_child_first; 306 while (s->tree_sibling != node) 307 s = s->tree_sibling; 308 s->tree_sibling = s->tree_sibling->tree_sibling; 309 } 310 kmem_free(node, sizeof (*node)); 311 } 312 313 /* 314 * Add a list of visible directories to a pseudo exportfs. 315 * 316 * When we export a new directory we need to add a new 317 * path segment through the pseudofs to reach the new 318 * directory. This new path is reflected in a list of 319 * directories added to the "visible" list. 320 * 321 * Here there are two lists of visible fids: one hanging off the 322 * pseudo exportinfo, and the one we want to add. It's possible 323 * that the two lists share a common path segment 324 * and have some common directories. We need to combine 325 * the lists so there's no duplicate entries. Where a common 326 * path component is found, the vis_count field is bumped. 327 * 328 * When the addition is complete, the supplied list is freed. 329 */ 330 331 static void 332 more_visible(struct exportinfo *exi, struct exp_visible *vis_head) 333 { 334 struct exp_visible *vp1, *vp2; 335 struct exp_visible *tail, *new; 336 treenode_t *subtree_head, *dupl, *dest; 337 int found; 338 339 dest = exi->exi_tree; 340 subtree_head = vis_head->vis_tree; 341 342 /* 343 * If exportinfo doesn't already have a visible 344 * list just assign the entire supplied list. 345 */ 346 if (exi->exi_visible == NULL) { 347 exi->exi_visible = vis_head; 348 tree_add_child(dest, subtree_head); 349 return; 350 } 351 352 /* 353 * The outer loop traverses the supplied list. 354 */ 355 for (vp1 = vis_head; vp1; vp1 = vp1->vis_next) { 356 357 /* 358 * Given an element from the list to be added, 359 * search the exportinfo visible list looking for a match. 360 * If a match is found, increment the reference count. 361 */ 362 found = 0; 363 364 for (vp2 = exi->exi_visible; vp2; vp2 = vp2->vis_next) { 365 366 tail = vp2; 367 368 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { 369 found = 1; 370 vp2->vis_count++; 371 VN_RELE(vp1->vis_vp); 372 vp1->vis_vp = NULL; 373 374 /* 375 * If the visible struct we want to add 376 * (vp1) has vis_exported set to 1, then 377 * the matching visible struct we just found 378 * must also have it's vis_exported field 379 * set to 1. 380 * 381 * For example, if /export/home was shared 382 * (and a mountpoint), then "export" and 383 * "home" would each have visible structs in 384 * the root pseudo exportinfo. The vis_exported 385 * for home would be 1, and vis_exported for 386 * export would be 0. Now, if /export was 387 * also shared, more_visible would find the 388 * existing visible struct for export, and 389 * see that vis_exported was 0. The code 390 * below will set it to 1. 391 * 392 * vp1 is from vis list passed in (vis_head) 393 * vp2 is from vis list on pseudo exportinfo 394 */ 395 if (vp1->vis_exported && !vp2->vis_exported) 396 vp2->vis_exported = 1; 397 /* 398 * Assuming that visibles in vis_head are sorted 399 * in same order as they appear in the shared 400 * path. If /a/b/c/d is being shared we will 401 * see 'a' before 'b' etc. 402 */ 403 dupl = vp1->vis_tree; 404 dest = vp2->vis_tree; 405 /* If node is shared, transfer exportinfo ptr */ 406 if (dupl->tree_exi) { 407 dest->tree_exi = dupl->tree_exi; 408 dest->tree_exi->exi_tree = dest; 409 } 410 subtree_head = dupl->tree_child_first; 411 kmem_free(dupl, sizeof (*dupl)); 412 break; 413 } 414 } 415 416 /* If not found - add to the end of the list */ 417 if (! found) { 418 new = kmem_zalloc(sizeof (*new), KM_SLEEP); 419 *new = *vp1; 420 tail->vis_next = new; 421 new->vis_next = NULL; 422 vp1->vis_vp = NULL; 423 /* Tell treenode that new visible is kmem_zalloc-ated */ 424 new->vis_tree->tree_vis = new; 425 } 426 } 427 428 /* 429 * Throw away the path list. vis_vp pointers in vis_head list 430 * are either VN_RELEed or reassigned, and are set to NULL. 431 * There is no need to VN_RELE in free_visible for this vis_head. 432 */ 433 free_visible(vis_head); 434 if (subtree_head) 435 tree_add_child(dest, subtree_head); 436 } 437 438 /* 439 * Remove one visible entry from the pseudo exportfs. 440 * 441 * When we unexport a directory, we have to remove path 442 * components from the visible list in the pseudo exportfs 443 * entry. The supplied visible contains one fid of one path 444 * component. The visible list of the export 445 * is checked against provided visible, matching fid has its 446 * reference count decremented. If a reference count drops to 447 * zero, then it means no paths now use this directory, so its 448 * fid can be removed from the visible list. 449 * 450 * When the last path is removed, the visible list will be null. 451 */ 452 static void 453 less_visible(struct exportinfo *exi, struct exp_visible *vp1) 454 { 455 struct exp_visible *vp2; 456 struct exp_visible *prev, *next; 457 458 for (vp2 = exi->exi_visible, prev = NULL; vp2; vp2 = next) { 459 460 next = vp2->vis_next; 461 462 if (EQFID(&vp1->vis_fid, &vp2->vis_fid)) { 463 /* 464 * Decrement the ref count. 465 * Remove the entry if it's zero. 466 */ 467 if (--vp2->vis_count <= 0) { 468 if (prev == NULL) 469 exi->exi_visible = next; 470 else 471 prev->vis_next = next; 472 VN_RELE(vp2->vis_vp); 473 srv_secinfo_list_free(vp2->vis_secinfo, 474 vp2->vis_seccnt); 475 kmem_free(vp2, sizeof (*vp1)); 476 } else { 477 /* 478 * If we're here, then the vp2 will 479 * remain in the vis list. If the 480 * vis entry corresponds to the object 481 * being unshared, then vis_exported 482 * needs to be set to 0. 483 * 484 * vp1 is a node from caller's list 485 * vp2 is node from exportinfo's list 486 * 487 * Only 1 node in the caller's list 488 * will have vis_exported set to 1, 489 * and it corresponds to the obj being 490 * unshared. It should always be the 491 * last element of the caller's list. 492 */ 493 if (vp1->vis_exported && 494 vp2->vis_exported) { 495 vp2->vis_exported = 0; 496 } 497 } 498 499 break; 500 } 501 prev = vp2; 502 } 503 } 504 505 /* 506 * This function checks the path to a new export to 507 * check whether all the pathname components are 508 * exported. It works by climbing the file tree one 509 * component at a time via "..", crossing mountpoints 510 * if necessary until an export entry is found, or the 511 * system root is reached. 512 * 513 * If an unexported mountpoint is found, then 514 * a new pseudo export is added and the pathname from 515 * the mountpoint down to the export is added to the 516 * visible list for the new pseudo export. If an existing 517 * pseudo export is found, then the pathname is added 518 * to its visible list. 519 * 520 * Note that there's some tests for exportdir. 521 * The exportinfo entry that's passed as a parameter 522 * is that of the real export and exportdir is set 523 * for this case. 524 * 525 * Here is an example of a possible setup: 526 * 527 * () - a new fs; fs mount point 528 * EXPORT - a real exported node 529 * PSEUDO - a pseudo node 530 * vis - visible list 531 * f# - security flavor# 532 * (f#) - security flavor# propagated from its descendents 533 * "" - covered vnode 534 * 535 * 536 * / 537 * | 538 * (a) PSEUDO (f1,f2) 539 * | vis: b,b,"c","n" 540 * | 541 * b 542 * ---------|------------------ 543 * | | 544 * (c) EXPORT,f1(f2) (n) PSEUDO (f1,f2) 545 * | vis: "e","d" | vis: m,m,,p,q,"o" 546 * | | 547 * ------------------ ------------------- 548 * | | | | | 549 * (d) (e) f m EXPORT,f1(f2) p 550 * EXPORT EXPORT | | 551 * f1 f2 | | 552 * | | | 553 * j (o) EXPORT,f2 q EXPORT f2 554 * 555 */ 556 int 557 treeclimb_export(struct exportinfo *exip) 558 { 559 vnode_t *dvp, *vp; 560 fid_t fid; 561 int error; 562 int exportdir; 563 struct exportinfo *exi = NULL; 564 struct exportinfo *new_exi = exip; 565 struct exp_visible *visp; 566 struct exp_visible *vis_head = NULL; 567 struct vattr va; 568 treenode_t *tree_head = NULL; 569 570 ASSERT(RW_WRITE_HELD(&exported_lock)); 571 572 vp = exip->exi_vp; 573 VN_HOLD(vp); 574 exportdir = 1; 575 576 for (;;) { 577 578 bzero(&fid, sizeof (fid)); 579 fid.fid_len = MAXFIDSZ; 580 error = vop_fid_pseudo(vp, &fid); 581 if (error) 582 break; 583 584 if (! exportdir) { 585 /* 586 * Check if this exportroot is a VROOT dir. If so, 587 * then attach the pseudonodes. If not, then 588 * continue .. traversal until we hit a VROOT 589 * export (pseudo or real). 590 */ 591 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 592 if (exi != NULL && vp->v_flag & VROOT) { 593 /* 594 * Found an export info 595 * 596 * Extend the list of visible 597 * directories whether it's a pseudo 598 * or a real export. 599 */ 600 more_visible(exi, vis_head); 601 break; /* and climb no further */ 602 } 603 } 604 605 /* 606 * If at the root of the filesystem, need 607 * to traverse across the mountpoint 608 * and continue the climb on the mounted-on 609 * filesystem. 610 */ 611 if (vp->v_flag & VROOT) { 612 613 if (! exportdir) { 614 /* 615 * Found the root directory of a filesystem 616 * that isn't exported. Need to export 617 * this as a pseudo export so that an NFS v4 618 * client can do lookups in it. 619 */ 620 error = pseudo_exportfs(vp, vis_head, NULL, 621 &new_exi); 622 if (error) 623 break; 624 vis_head = NULL; 625 } 626 627 if (VN_CMP(vp, rootdir)) { 628 /* at system root */ 629 /* 630 * If sharing "/", new_exi is shared exportinfo 631 * (exip). Otherwise, new_exi is exportinfo 632 * created in pseudo_exportfs() above. 633 */ 634 ns_root = tree_prepend_node(tree_head, 0, 635 new_exi); 636 break; 637 } 638 639 vp = untraverse(vp); 640 exportdir = 0; 641 continue; 642 } 643 644 /* 645 * Do a getattr to obtain the nodeid (inode num) 646 * for this vnode. 647 */ 648 va.va_mask = AT_NODEID; 649 error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); 650 if (error) { 651 if (new_exi && new_exi != exip) { 652 /* 653 * This exportinfo is not connected with 654 * treenode yet. Free it here. 655 */ 656 (void) export_unlink(&new_exi->exi_fsid, 657 &new_exi->exi_fid, new_exi->exi_vp, NULL); 658 exi_rele(new_exi); 659 } 660 break; 661 } 662 663 /* 664 * Add this directory fid to visible list 665 */ 666 visp = kmem_alloc(sizeof (*visp), KM_SLEEP); 667 VN_HOLD(vp); 668 visp->vis_vp = vp; 669 visp->vis_fid = fid; /* structure copy */ 670 visp->vis_ino = va.va_nodeid; 671 visp->vis_count = 1; 672 visp->vis_exported = exportdir; 673 visp->vis_secinfo = NULL; 674 visp->vis_seccnt = 0; 675 visp->vis_next = vis_head; 676 vis_head = visp; 677 678 679 /* 680 * Will set treenode's pointer to exportinfo to 681 * 1. shared exportinfo (exip) - if first visit here 682 * 2. freshly allocated pseudo export (if any) 683 * 3. null otherwise 684 */ 685 tree_head = tree_prepend_node(tree_head, visp, new_exi); 686 new_exi = NULL; 687 688 /* 689 * Now, do a ".." to find parent dir of vp. 690 */ 691 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), 692 NULL, NULL, NULL); 693 694 if (error == ENOTDIR && exportdir) { 695 dvp = exip->exi_dvp; 696 ASSERT(dvp != NULL); 697 VN_HOLD(dvp); 698 error = 0; 699 } 700 701 if (error) 702 break; 703 704 exportdir = 0; 705 VN_RELE(vp); 706 vp = dvp; 707 } 708 709 VN_RELE(vp); 710 711 /* 712 * We can have set error due to error in: 713 * 1. vop_fid_pseudo() 714 * 2. pseudo_exportfs() which can fail only in vop_fid_pseudo() 715 * 3. VOP_GETATTR() 716 * 4. VOP_LOOKUP() 717 * To cleanup, free pseudo exportinfos, visibles and treenodes. 718 */ 719 if (error) { 720 while (tree_head) { 721 treenode_t *t2 = tree_head; 722 exportinfo_t *e = tree_head->tree_exi; 723 exp_visible_t *v = tree_head->tree_vis; 724 /* exip will be freed in exportfs() */ 725 if (e && e != exip) { 726 (void) export_unlink(&e->exi_fsid, &e->exi_fid, 727 e->exi_vp, NULL); 728 exi_rele(e); 729 } 730 if (v) { 731 VN_RELE(v->vis_vp); 732 kmem_free(v, sizeof (*v)); 733 } 734 tree_head = tree_head->tree_child_first; 735 kmem_free(t2, sizeof (*t2)); 736 } 737 } 738 739 return (error); 740 } 741 742 /* 743 * Walk up the tree and: 744 * 1. release pseudo exportinfo if it has no child 745 * 2. release visible in parent's exportinfo 746 * 3. delete non-exported leaf nodes from tree 747 * 748 * Deleting of nodes will start only if the unshared 749 * node was a leaf node. 750 * Deleting of nodes will finish when we reach a node which 751 * has children or is a real export, then we might still need 752 * to continue releasing visibles, until we reach VROOT node. 753 */ 754 void 755 treeclimb_unexport(struct exportinfo *exip) 756 { 757 struct exportinfo *exi; 758 treenode_t *tnode, *old_nd; 759 760 ASSERT(RW_WRITE_HELD(&exported_lock)); 761 762 tnode = exip->exi_tree; 763 /* 764 * The unshared exportinfo was unlinked in unexport(). 765 * Zeroing tree_exi ensures that we will skip it. 766 */ 767 tnode->tree_exi = NULL; 768 769 while (tnode) { 770 771 /* Stop at VROOT node which is exported or has child */ 772 if (TREE_ROOT(tnode) && 773 (TREE_EXPORTED(tnode) || tnode->tree_child_first)) 774 break; 775 776 /* Release pseudo export if it has no child */ 777 if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) && 778 tnode->tree_child_first == 0) { 779 exi = tnode->tree_exi; 780 (void) export_unlink(&exi->exi_fsid, &exi->exi_fid, 781 exi->exi_vp, NULL); 782 exi_rele(tnode->tree_exi); 783 } 784 785 /* Release visible in parent's exportinfo */ 786 if (tnode->tree_vis) { 787 exi = vis2exi(tnode->tree_vis); 788 less_visible(exi, tnode->tree_vis); 789 } 790 791 /* Continue with parent */ 792 old_nd = tnode; 793 tnode = tnode->tree_parent; 794 795 /* Remove itself, if this is a leaf and non-exported node */ 796 if (old_nd->tree_child_first == NULL && !TREE_EXPORTED(old_nd)) 797 tree_remove_node(old_nd); 798 } 799 } 800 801 /* 802 * Traverse backward across mountpoint from the 803 * root vnode of a filesystem to its mounted-on 804 * vnode. 805 */ 806 vnode_t * 807 untraverse(vnode_t *vp) 808 { 809 vnode_t *tvp, *nextvp; 810 811 tvp = vp; 812 for (;;) { 813 if (! (tvp->v_flag & VROOT)) 814 break; 815 816 /* lock vfs to prevent unmount of this vfs */ 817 vfs_lock_wait(tvp->v_vfsp); 818 819 if ((nextvp = tvp->v_vfsp->vfs_vnodecovered) == NULL) { 820 vfs_unlock(tvp->v_vfsp); 821 break; 822 } 823 824 /* 825 * Hold nextvp to prevent unmount. After unlock vfs and 826 * rele tvp, any number of overlays could be unmounted. 827 * Putting a hold on vfs_vnodecovered will only allow 828 * tvp's vfs to be unmounted. Of course if caller placed 829 * extra hold on vp before calling untraverse, the following 830 * hold would not be needed. Since prev actions of caller 831 * are unknown, we need to hold here just to be safe. 832 */ 833 VN_HOLD(nextvp); 834 vfs_unlock(tvp->v_vfsp); 835 VN_RELE(tvp); 836 tvp = nextvp; 837 } 838 839 return (tvp); 840 } 841 842 /* 843 * Given an exportinfo, climb up to find the exportinfo for the VROOT 844 * of the filesystem. 845 * 846 * e.g. / 847 * | 848 * a (VROOT) pseudo-exportinfo 849 * | 850 * b 851 * | 852 * c #share /a/b/c 853 * | 854 * d 855 * 856 * where c is in the same filesystem as a. 857 * So, get_root_export(*exportinfo_for_c) returns exportinfo_for_a 858 * 859 * If d is shared, then c will be put into a's visible list. 860 * Note: visible list is per filesystem and is attached to the 861 * VROOT exportinfo. 862 */ 863 struct exportinfo * 864 get_root_export(struct exportinfo *exip) 865 { 866 vnode_t *dvp, *vp; 867 fid_t fid; 868 struct exportinfo *exi = exip; 869 int error; 870 871 vp = exi->exi_vp; 872 VN_HOLD(vp); 873 874 for (;;) { 875 876 if (vp->v_flag & VROOT) { 877 ASSERT(exi != NULL); 878 break; 879 } 880 881 /* 882 * Now, do a ".." to find parent dir of vp. 883 */ 884 error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), 885 NULL, NULL, NULL); 886 887 if (error) { 888 exi = NULL; 889 break; 890 } 891 892 VN_RELE(vp); 893 vp = dvp; 894 895 bzero(&fid, sizeof (fid)); 896 fid.fid_len = MAXFIDSZ; 897 error = vop_fid_pseudo(vp, &fid); 898 if (error) { 899 exi = NULL; 900 break; 901 } 902 903 exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); 904 } 905 906 VN_RELE(vp); 907 return (exi); 908 } 909 910 /* 911 * Return true if the supplied vnode has a sub-directory exported. 912 */ 913 int 914 has_visible(struct exportinfo *exi, vnode_t *vp) 915 { 916 struct exp_visible *visp; 917 fid_t fid; 918 bool_t vp_is_exported; 919 920 vp_is_exported = VN_CMP(vp, exi->exi_vp); 921 922 /* 923 * An exported root vnode has a sub-dir shared if it has a visible list. 924 * i.e. if it does not have a visible list, then there is no node in 925 * this filesystem leads to any other shared node. 926 */ 927 if (vp_is_exported && (vp->v_flag & VROOT)) 928 return (exi->exi_visible ? 1 : 0); 929 930 /* 931 * Only the exportinfo of a fs root node may have a visible list. 932 * Either it is a pseudo root node, or a real exported root node. 933 */ 934 if ((exi = get_root_export(exi)) == NULL) { 935 return (0); 936 } 937 938 if (!exi->exi_visible) 939 return (0); 940 941 /* Get the fid of the vnode */ 942 bzero(&fid, sizeof (fid)); 943 fid.fid_len = MAXFIDSZ; 944 if (vop_fid_pseudo(vp, &fid) != 0) { 945 return (0); 946 } 947 948 /* 949 * See if vp is in the visible list of the root node exportinfo. 950 */ 951 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 952 if (EQFID(&fid, &visp->vis_fid)) { 953 /* 954 * If vp is an exported non-root node with only 1 path 955 * count (for itself), it indicates no sub-dir shared 956 * using this vp as a path. 957 */ 958 if (vp_is_exported && visp->vis_count < 2) 959 break; 960 961 return (1); 962 } 963 } 964 965 return (0); 966 } 967 968 /* 969 * Returns true if the supplied vnode is visible 970 * in this export. If vnode is visible, return 971 * vis_exported in expseudo. 972 */ 973 int 974 nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo) 975 { 976 struct exp_visible *visp; 977 fid_t fid; 978 979 /* 980 * First check to see if vp is export root. 981 * 982 * A pseudo export root can never be exported 983 * (it would be a real export then); however, 984 * it is always visible. If a pseudo root object 985 * was exported by server admin, then the entire 986 * pseudo exportinfo (and all visible entries) would 987 * be destroyed. A pseudo exportinfo only exists 988 * to provide access to real (descendant) export(s). 989 * 990 * Previously, rootdir was special cased here; however, 991 * the export root special case handles the rootdir 992 * case also. 993 */ 994 if (VN_CMP(vp, exi->exi_vp)) { 995 *expseudo = 0; 996 return (1); 997 } 998 999 /* 1000 * Only a PSEUDO node has a visible list or an exported VROOT 1001 * node may have a visible list. 1002 */ 1003 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 1004 *expseudo = 0; 1005 return (0); 1006 } 1007 1008 /* Get the fid of the vnode */ 1009 1010 bzero(&fid, sizeof (fid)); 1011 fid.fid_len = MAXFIDSZ; 1012 if (vop_fid_pseudo(vp, &fid) != 0) { 1013 *expseudo = 0; 1014 return (0); 1015 } 1016 1017 /* 1018 * We can't trust VN_CMP() above because of LOFS. 1019 * Even though VOP_CMP will do the right thing for LOFS 1020 * objects, VN_CMP will short circuit out early when the 1021 * vnode ops ptrs are different. Just in case we're dealing 1022 * with LOFS, compare exi_fid/fsid here. 1023 * 1024 * expseudo is not set because this is not an export 1025 */ 1026 if (EQFID(&exi->exi_fid, &fid) && 1027 EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) { 1028 *expseudo = 0; 1029 return (1); 1030 } 1031 1032 1033 /* See if it matches any fid in the visible list */ 1034 1035 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 1036 if (EQFID(&fid, &visp->vis_fid)) { 1037 *expseudo = visp->vis_exported; 1038 return (1); 1039 } 1040 } 1041 1042 *expseudo = 0; 1043 1044 return (0); 1045 } 1046 1047 /* 1048 * Returns true if the supplied vnode is the 1049 * directory of an export point. 1050 */ 1051 int 1052 nfs_exported(struct exportinfo *exi, vnode_t *vp) 1053 { 1054 struct exp_visible *visp; 1055 fid_t fid; 1056 1057 /* 1058 * First check to see if vp is the export root 1059 * This check required for the case of lookup .. 1060 * where .. is a V_ROOT vnode and a pseudo exportroot. 1061 * Pseudo export root objects do not have an entry 1062 * in the visible list even though every V_ROOT 1063 * pseudonode is visible. It is safe to compare 1064 * vp here because pseudo_exportfs put a hold on 1065 * it when exi_vp was initialized. 1066 * 1067 * Note: VN_CMP() won't match for LOFS shares, but they're 1068 * handled below w/EQFID/EQFSID. 1069 */ 1070 if (VN_CMP(vp, exi->exi_vp)) 1071 return (1); 1072 1073 /* Get the fid of the vnode */ 1074 1075 bzero(&fid, sizeof (fid)); 1076 fid.fid_len = MAXFIDSZ; 1077 if (vop_fid_pseudo(vp, &fid) != 0) 1078 return (0); 1079 1080 if (EQFID(&fid, &exi->exi_fid) && 1081 EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) { 1082 return (1); 1083 } 1084 1085 /* See if it matches any fid in the visible list */ 1086 1087 for (visp = exi->exi_visible; visp; visp = visp->vis_next) { 1088 if (EQFID(&fid, &visp->vis_fid)) 1089 return (visp->vis_exported); 1090 } 1091 1092 return (0); 1093 } 1094 1095 /* 1096 * Returns true if the supplied inode is visible 1097 * in this export. This function is used by 1098 * readdir which uses inode numbers from the 1099 * directory. 1100 * 1101 * NOTE: this code does not match inode number for ".", 1102 * but it isn't required because NFS4 server rddir 1103 * skips . and .. entries. 1104 */ 1105 int 1106 nfs_visible_inode(struct exportinfo *exi, ino64_t ino, int *expseudo) 1107 { 1108 struct exp_visible *visp; 1109 1110 /* 1111 * Only a PSEUDO node has a visible list or an exported VROOT 1112 * node may have a visible list. 1113 */ 1114 if (! PSEUDO(exi) && (exi = get_root_export(exi)) == NULL) { 1115 *expseudo = 0; 1116 return (0); 1117 } 1118 1119 for (visp = exi->exi_visible; visp; visp = visp->vis_next) 1120 if ((u_longlong_t)ino == visp->vis_ino) { 1121 *expseudo = visp->vis_exported; 1122 return (1); 1123 } 1124 1125 *expseudo = 0; 1126 return (0); 1127 } 1128