/* * Get an nfsv4 vnode of the given fid from the visible list of an * nfs filesystem or get the exi_vp if it is the root node. */ int nfs4_vget_pseudo(struct exportinfo *exi, vnode_t **vpp, fid_t *fidp) { fid_t exp_fid; struct exp_visible *visp; int error; /* check if the given fid is in the visible list */ for (visp = exi->exi_visible; visp; visp = visp->vis_next) { if (EQFID(fidp, &visp->vis_fid)) { VN_HOLD(visp->vis_vp); *vpp = visp->vis_vp; return (0); } } /* check if the given fid is the same as the exported node */ bzero(&exp_fid, sizeof (exp_fid)); exp_fid.fid_len = MAXFIDSZ; error = vop_fid_pseudo(exi->exi_vp, &exp_fid); if (error) return (error); if (EQFID(fidp, &exp_fid)) { VN_HOLD(exi->exi_vp); *vpp = exi->exi_vp; return (0); } return (ENOENT); }
/* * Get the change attribute from visible and returns TRUE. * If the change value is not available returns FALSE. */ bool_t nfs_visible_change(struct exportinfo *exi, vnode_t *vp, timespec_t *change) { struct exp_visible *visp; fid_t fid; treenode_t *node; /* * First check to see if vp is export root. */ if (VN_CMP(vp, exi->exi_vp)) goto exproot; /* * Only a PSEUDO node has a visible list or an exported VROOT * node may have a visible list. */ if (!PSEUDO(exi)) exi = get_root_export(exi); /* Get the fid of the vnode */ bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; if (vop_fid_pseudo(vp, &fid) != 0) return (FALSE); /* * We can't trust VN_CMP() above because of LOFS. * Even though VOP_CMP will do the right thing for LOFS * objects, VN_CMP will short circuit out early when the * vnode ops ptrs are different. Just in case we're dealing * with LOFS, compare exi_fid/fsid here. */ if (EQFID(&exi->exi_fid, &fid) && EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) goto exproot; /* See if it matches any fid in the visible list */ for (visp = exi->exi_visible; visp; visp = visp->vis_next) { if (EQFID(&fid, &visp->vis_fid)) { *change = visp->vis_change; return (TRUE); } } return (FALSE); exproot: /* The VROOT export have its visible available through treenode */ node = exi->exi_tree; if (node != ns_root) { ASSERT(node->tree_vis != NULL); *change = node->tree_vis->vis_change; } else { ASSERT(node->tree_vis == NULL); *change = ns_root_change; } return (TRUE); }
/* * Return true if the supplied vnode has a sub-directory exported. */ int has_visible(struct exportinfo *exi, vnode_t *vp) { struct exp_visible *visp; fid_t fid; bool_t vp_is_exported; vp_is_exported = VN_CMP(vp, exi->exi_vp); /* * An exported root vnode has a sub-dir shared if it has a visible list. * i.e. if it does not have a visible list, then there is no node in * this filesystem leads to any other shared node. */ if (vp_is_exported && (vp->v_flag & VROOT)) return (exi->exi_visible ? 1 : 0); /* * Only the exportinfo of a fs root node may have a visible list. * Either it is a pseudo root node, or a real exported root node. */ exi = get_root_export(exi); if (!exi->exi_visible) return (0); /* Get the fid of the vnode */ bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; if (vop_fid_pseudo(vp, &fid) != 0) { return (0); } /* * See if vp is in the visible list of the root node exportinfo. */ for (visp = exi->exi_visible; visp; visp = visp->vis_next) { if (EQFID(&fid, &visp->vis_fid)) { /* * If vp is an exported non-root node with only 1 path * count (for itself), it indicates no sub-dir shared * using this vp as a path. */ if (vp_is_exported && visp->vis_count < 2) break; return (1); } } return (0); }
/* * Returns true if the supplied vnode is the * directory of an export point. */ int nfs_exported(struct exportinfo *exi, vnode_t *vp) { struct exp_visible *visp; fid_t fid; /* * First check to see if vp is the export root * This check required for the case of lookup .. * where .. is a V_ROOT vnode and a pseudo exportroot. * Pseudo export root objects do not have an entry * in the visible list even though every V_ROOT * pseudonode is visible. It is safe to compare * vp here because pseudo_exportfs put a hold on * it when exi_vp was initialized. * * Note: VN_CMP() won't match for LOFS shares, but they're * handled below w/EQFID/EQFSID. */ if (VN_CMP(vp, exi->exi_vp)) return (1); /* Get the fid of the vnode */ bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; if (vop_fid_pseudo(vp, &fid) != 0) return (0); if (EQFID(&fid, &exi->exi_fid) && EQFSID(&vp->v_vfsp->vfs_fsid, &exi->exi_fsid)) { return (1); } /* See if it matches any fid in the visible list */ for (visp = exi->exi_visible; visp; visp = visp->vis_next) { if (EQFID(&fid, &visp->vis_fid)) return (visp->vis_exported); } return (0); }
/* * Returns true if the supplied vnode is visible * in this export. If vnode is visible, return * vis_exported in expseudo. */ int nfs_visible(struct exportinfo *exi, vnode_t *vp, int *expseudo) { struct exp_visible *visp; fid_t fid; /* * First check to see if vp is export root. * * A pseudo export root can never be exported * (it would be a real export then); however, * it is always visible. If a pseudo root object * was exported by server admin, then the entire * pseudo exportinfo (and all visible entries) would * be destroyed. A pseudo exportinfo only exists * to provide access to real (descendant) export(s). * * Previously, rootdir was special cased here; however, * the export root special case handles the rootdir * case also. */ if (VN_CMP(vp, exi->exi_vp)) { *expseudo = 0; return (1); } /* * Only a PSEUDO node has a visible list or an exported VROOT * node may have a visible list. */ if (! PSEUDO(exi)) exi = get_root_export(exi); /* Get the fid of the vnode */ bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; if (vop_fid_pseudo(vp, &fid) != 0) { *expseudo = 0; return (0); } /* * We can't trust VN_CMP() above because of LOFS. * Even though VOP_CMP will do the right thing for LOFS * objects, VN_CMP will short circuit out early when the * vnode ops ptrs are different. Just in case we're dealing * with LOFS, compare exi_fid/fsid here. * * expseudo is not set because this is not an export */ if (EQFID(&exi->exi_fid, &fid) && EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid)) { *expseudo = 0; return (1); } /* See if it matches any fid in the visible list */ for (visp = exi->exi_visible; visp; visp = visp->vis_next) { if (EQFID(&fid, &visp->vis_fid)) { *expseudo = visp->vis_exported; return (1); } } *expseudo = 0; return (0); }
/* * This function checks the path to a new export to * check whether all the pathname components are * exported. It works by climbing the file tree one * component at a time via "..", crossing mountpoints * if necessary until an export entry is found, or the * system root is reached. * * If an unexported mountpoint is found, then * a new pseudo export is added and the pathname from * the mountpoint down to the export is added to the * visible list for the new pseudo export. If an existing * pseudo export is found, then the pathname is added * to its visible list. * * Note that there's some tests for exportdir. * The exportinfo entry that's passed as a parameter * is that of the real export and exportdir is set * for this case. * * Here is an example of a possible setup: * * () - a new fs; fs mount point * EXPORT - a real exported node * PSEUDO - a pseudo node * vis - visible list * f# - security flavor# * (f#) - security flavor# propagated from its descendents * "" - covered vnode * * * / * | * (a) PSEUDO (f1,f2) * | vis: b,b,"c","n" * | * b * ---------|------------------ * | | * (c) EXPORT,f1(f2) (n) PSEUDO (f1,f2) * | vis: "e","d" | vis: m,m,,p,q,"o" * | | * ------------------ ------------------- * | | | | | * (d) (e) f m EXPORT,f1(f2) p * EXPORT EXPORT | | * f1 f2 | | * | | | * j (o) EXPORT,f2 q EXPORT f2 * */ int treeclimb_export(struct exportinfo *exip) { vnode_t *dvp, *vp; fid_t fid; int error; int exportdir; struct exportinfo *exi = NULL; struct exportinfo *new_exi = exip; struct exp_visible *visp; struct exp_visible *vis_head = NULL; struct vattr va; treenode_t *tree_head = NULL; ASSERT(RW_WRITE_HELD(&exported_lock)); vp = exip->exi_vp; VN_HOLD(vp); exportdir = 1; for (;;) { bzero(&fid, sizeof (fid)); fid.fid_len = MAXFIDSZ; error = vop_fid_pseudo(vp, &fid); if (error) break; if (! exportdir) { /* * Check if this exportroot is a VROOT dir. If so, * then attach the pseudonodes. If not, then * continue .. traversal until we hit a VROOT * export (pseudo or real). */ exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp); if (exi != NULL && vp->v_flag & VROOT) { /* * Found an export info * * Extend the list of visible * directories whether it's a pseudo * or a real export. */ more_visible(exi, tree_head); break; /* and climb no further */ } } /* * If at the root of the filesystem, need * to traverse across the mountpoint * and continue the climb on the mounted-on * filesystem. */ if (vp->v_flag & VROOT) { if (! exportdir) { /* * Found the root directory of a filesystem * that isn't exported. Need to export * this as a pseudo export so that an NFS v4 * client can do lookups in it. */ new_exi = pseudo_exportfs(vp, &fid, vis_head, NULL); vis_head = NULL; } if (VN_CMP(vp, rootdir)) { /* at system root */ /* * If sharing "/", new_exi is shared exportinfo * (exip). Otherwise, new_exi is exportinfo * created in pseudo_exportfs() above. */ ns_root = tree_prepend_node(tree_head, 0, new_exi); break; } vp = untraverse(vp); exportdir = 0; continue; } /* * Do a getattr to obtain the nodeid (inode num) * for this vnode. */ va.va_mask = AT_NODEID; error = VOP_GETATTR(vp, &va, 0, CRED(), NULL); if (error) break; /* * Add this directory fid to visible list */ visp = kmem_alloc(sizeof (*visp), KM_SLEEP); VN_HOLD(vp); visp->vis_vp = vp; visp->vis_fid = fid; /* structure copy */ visp->vis_ino = va.va_nodeid; visp->vis_count = 1; visp->vis_exported = exportdir; visp->vis_secinfo = NULL; visp->vis_seccnt = 0; visp->vis_next = vis_head; vis_head = visp; /* * Will set treenode's pointer to exportinfo to * 1. shared exportinfo (exip) - if first visit here * 2. freshly allocated pseudo export (if any) * 3. null otherwise */ tree_head = tree_prepend_node(tree_head, visp, new_exi); new_exi = NULL; /* * Now, do a ".." to find parent dir of vp. */ error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(), NULL, NULL, NULL); if (error == ENOTDIR && exportdir) { dvp = exip->exi_dvp; ASSERT(dvp != NULL); VN_HOLD(dvp); error = 0; } if (error) break; exportdir = 0; VN_RELE(vp); vp = dvp; } VN_RELE(vp); /* * We can have set error due to error in: * 1. vop_fid_pseudo() * 2. VOP_GETATTR() * 3. VOP_LOOKUP() * We must free pseudo exportinfos, visibles and treenodes. * Visibles are referenced from treenode_t::tree_vis and * exportinfo_t::exi_visible. To avoid double freeing, only * exi_visible pointer is used, via exi_rele(), for the clean-up. */ if (error) { /* Free unconnected visibles, if there are any. */ if (vis_head) free_visible(vis_head); /* Connect unconnected exportinfo, if there is any. */ if (new_exi && new_exi != exip) tree_head = tree_prepend_node(tree_head, 0, new_exi); while (tree_head) { treenode_t *t2 = tree_head; exportinfo_t *e = tree_head->tree_exi; /* exip will be freed in exportfs() */ if (e && e != exip) { exp_kstats_delete(e->exi_kstats); avl_remove(&exi_id_tree, e); export_unlink(e); exi_rele(e); } tree_head = tree_head->tree_child_first; kmem_free(t2, sizeof (*t2)); } } return (error); }