11541Srgrimes/*- 21541Srgrimes * Copyright (c) 1989, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * (c) UNIX System Laboratories, Inc. 551138Salfred * All or some portions of this file are derived from material licensed 6191675Sjamie * to the University of California by American Telephone and Telegraph 71541Srgrimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with 81541Srgrimes * the permission of UNIX System Laboratories, Inc. 91541Srgrimes * 1064002Speter * Redistribution and use in source and binary forms, with or without 111541Srgrimes * modification, are permitted provided that the following conditions 121541Srgrimes * are met: 131541Srgrimes * 1. Redistributions of source code must retain the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer. 151541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161541Srgrimes * notice, this list of conditions and the following disclaimer in the 171541Srgrimes * documentation and/or other materials provided with the distribution. 181541Srgrimes * 4. Neither the name of the University nor the names of its contributors 191541Srgrimes * may be used to endorse or promote products derived from this software 201541Srgrimes * without specific prior written permission. 211541Srgrimes * 221541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27171210Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321541Srgrimes * SUCH DAMAGE. 331541Srgrimes * 341541Srgrimes * @(#)vfs_subr.c 8.31 (Berkeley) 5/26/95 351541Srgrimes */ 361541Srgrimes 371541Srgrimes#include <sys/cdefs.h> 381541Srgrimes__FBSDID("$FreeBSD$"); 391541Srgrimes 401541Srgrimes#include <sys/param.h> 411541Srgrimes#include <sys/dirent.h> 421541Srgrimes#include <sys/domain.h> 431541Srgrimes#include <sys/jail.h> 441541Srgrimes#include <sys/kernel.h> 451541Srgrimes#include <sys/lock.h> 461541Srgrimes#include <sys/malloc.h> 471541Srgrimes#include <sys/mbuf.h> 481541Srgrimes#include <sys/mount.h> 491541Srgrimes#include <sys/mutex.h> 501541Srgrimes#include <sys/rwlock.h> 511541Srgrimes#include <sys/refcount.h> 521541Srgrimes#include <sys/signalvar.h> 531541Srgrimes#include <sys/socket.h> 541541Srgrimes#include <sys/systm.h> 5552150Smarcel#include <sys/vnode.h> 561541Srgrimes 5752150Smarcel#include <net/radix.h> 581541Srgrimes 591541Srgrimesstatic MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure"); 601541Srgrimes 6152150Smarcelstatic void vfs_free_addrlist(struct netexport *nep); 621541Srgrimesstatic int vfs_free_netcred(struct radix_node *rn, void *w); 631541Srgrimesstatic int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, 641541Srgrimes struct export_args *argp); 651541Srgrimesstatic struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *); 661541Srgrimes 671541Srgrimes/* 681541Srgrimes * Network address lookup element 691541Srgrimes */ 701541Srgrimesstruct netcred { 711541Srgrimes struct radix_node netc_rnodes[2]; 721541Srgrimes int netc_exflags; 731541Srgrimes struct ucred *netc_anon; 741541Srgrimes int netc_numsecflavors; 751541Srgrimes int netc_secflavors[MAXSECFLAVORS]; 761541Srgrimes}; 771541Srgrimes 781541Srgrimes/* 791541Srgrimes * Network export information 801541Srgrimes */ 811541Srgrimesstruct netexport { 821541Srgrimes struct netcred ne_defexported; /* Default export */ 831541Srgrimes struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ 841541Srgrimes}; 851541Srgrimes 861541Srgrimes/* 871541Srgrimes * Build hash lists of net addresses and hang them off the mount point. 881541Srgrimes * Called by vfs_export() to set up the lists of export addresses. 891541Srgrimes */ 901541Srgrimesstatic int 911541Srgrimesvfs_hang_addrlist(struct mount *mp, struct netexport *nep, 921541Srgrimes struct export_args *argp) 931541Srgrimes{ 941541Srgrimes register struct netcred *np; 951541Srgrimes register struct radix_node_head *rnh; 961541Srgrimes register int i; 971541Srgrimes struct radix_node *rn; 981541Srgrimes struct sockaddr *saddr, *smask = 0; 991541Srgrimes struct domain *dom; 1001541Srgrimes int error; 1011541Srgrimes 1021541Srgrimes /* 1031541Srgrimes * XXX: This routine converts from a `struct xucred' 1041541Srgrimes * (argp->ex_anon) to a `struct ucred' (np->netc_anon). This 1051541Srgrimes * operation is questionable; for example, what should be done 1061541Srgrimes * with fields like cr_uidinfo and cr_prison? Currently, this 1071541Srgrimes * routine does not touch them (leaves them as NULL). 1081541Srgrimes */ 1091541Srgrimes if (argp->ex_anon.cr_version != XUCRED_VERSION) { 110105950Speter vfs_mount_error(mp, "ex_anon.cr_version: %d != %d", 1111541Srgrimes argp->ex_anon.cr_version, XUCRED_VERSION); 1121541Srgrimes return (EINVAL); 1131541Srgrimes } 1141541Srgrimes 1151541Srgrimes if (argp->ex_addrlen == 0) { 1161541Srgrimes if (mp->mnt_flag & MNT_DEFEXPORTED) { 1171541Srgrimes vfs_mount_error(mp, 11852150Smarcel "MNT_DEFEXPORTED already set for mount %p", mp); 1191541Srgrimes return (EPERM); 1201541Srgrimes } 1211541Srgrimes np = &nep->ne_defexported; 1221541Srgrimes np->netc_exflags = argp->ex_flags; 1231541Srgrimes np->netc_anon = crget(); 1241541Srgrimes np->netc_anon->cr_uid = argp->ex_anon.cr_uid; 1251541Srgrimes crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, 1261541Srgrimes argp->ex_anon.cr_groups); 1271541Srgrimes np->netc_anon->cr_prison = &prison0; 1281541Srgrimes prison_hold(np->netc_anon->cr_prison); 1291541Srgrimes np->netc_numsecflavors = argp->ex_numsecflavors; 1301541Srgrimes bcopy(argp->ex_secflavors, np->netc_secflavors, 1311541Srgrimes sizeof(np->netc_secflavors)); 1328019Sache MNT_ILOCK(mp); 1338019Sache mp->mnt_flag |= MNT_DEFEXPORTED; 1341541Srgrimes MNT_IUNLOCK(mp); 1351541Srgrimes return (0); 1361541Srgrimes } 1371541Srgrimes 1381541Srgrimes#if MSIZE <= 256 1391541Srgrimes if (argp->ex_addrlen > MLEN) { 1401541Srgrimes vfs_mount_error(mp, "ex_addrlen %d is greater than %d", 1411541Srgrimes argp->ex_addrlen, MLEN); 1421541Srgrimes return (EINVAL); 1431541Srgrimes } 1441541Srgrimes#endif 1451541Srgrimes 1461541Srgrimes i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; 1471541Srgrimes np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO); 1481541Srgrimes saddr = (struct sockaddr *) (np + 1); 1491541Srgrimes if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen))) 1501541Srgrimes goto out; 1511541Srgrimes if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) { 1521541Srgrimes error = EINVAL; 1531541Srgrimes vfs_mount_error(mp, "Invalid saddr->sa_family: %d"); 1541541Srgrimes goto out; 1551541Srgrimes } 1561541Srgrimes if (saddr->sa_len > argp->ex_addrlen) 157177634Sdfr saddr->sa_len = argp->ex_addrlen; 1581541Srgrimes if (argp->ex_masklen) { 1591541Srgrimes smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen); 160171210Speter error = copyin(argp->ex_mask, smask, argp->ex_masklen); 161171210Speter if (error) 162127891Sdfr goto out; 1631541Srgrimes if (smask->sa_len > argp->ex_masklen) 164184790Sed smask->sa_len = argp->ex_masklen; 165184790Sed } 166184790Sed i = saddr->sa_family; 1671549Srgrimes if ((rnh = nep->ne_rtable[i]) == NULL) { 1682442Sdg /* 1692729Sdfr * Seems silly to initialize every AF when most are not used, 1702729Sdfr * do so on demand here 1711541Srgrimes */ 172171210Speter for (dom = domains; dom; dom = dom->dom_next) { 173171210Speter KASSERT(((i == AF_INET) || (i == AF_INET6)), 174178888Sjulian ("unexpected protocol in vfs_hang_addrlist")); 1752297Swollman if (dom->dom_family == i && dom->dom_rtattach) { 1761541Srgrimes /* 1771541Srgrimes * XXX MRT 1781541Srgrimes * The INET and INET6 domains know the 1791541Srgrimes * offset already. We don't need to send it 1801541Srgrimes * So we just use it as a flag to say that 1811541Srgrimes * we are or are not setting up a real routing 1821541Srgrimes * table. Only IP and IPV6 need have this 1831541Srgrimes * be 0 so all other protocols can stay the 1841541Srgrimes * same (ABI compatible). 1851541Srgrimes */ 1861541Srgrimes dom->dom_rtattach( 187171210Speter (void **) &nep->ne_rtable[i], 0); 1881541Srgrimes break; 189171210Speter } 190171210Speter } 191171210Speter if ((rnh = nep->ne_rtable[i]) == NULL) { 1921541Srgrimes error = ENOBUFS; 1931541Srgrimes vfs_mount_error(mp, "%s %s %d", 1941541Srgrimes "Unable to initialize radix node head ", 19535938Sdyson "for address family", i); 19635938Sdyson goto out; 19728400Speter } 19829349Speter } 19912865Speter RADIX_NODE_HEAD_LOCK(rnh); 20012865Speter rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes); 20112865Speter RADIX_NODE_HEAD_UNLOCK(rnh); 20212865Speter if (rn == NULL || np != (struct netcred *)rn) { /* already exists */ 20312865Speter error = EPERM; 20412865Speter vfs_mount_error(mp, "Invalid radix node head, rn: %p %p", 20512865Speter rn, np); 20612865Speter goto out; 20712865Speter } 20812865Speter np->netc_exflags = argp->ex_flags; 20912865Speter np->netc_anon = crget(); 21025582Speter np->netc_anon->cr_uid = argp->ex_anon.cr_uid; 21125582Speter crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, 21225582Speter argp->ex_anon.cr_groups); 213156138Sdavidxu np->netc_anon->cr_prison = &prison0; 214156138Sdavidxu prison_hold(np->netc_anon->cr_prison); 215156138Sdavidxu np->netc_numsecflavors = argp->ex_numsecflavors; 216156138Sdavidxu bcopy(argp->ex_secflavors, np->netc_secflavors, 217156138Sdavidxu sizeof(np->netc_secflavors)); 21825582Speter return (0); 219137875Smarksout: 22014220Speter free(np, M_NETADDR); 22114220Speter return (error); 22229349Speter} 22324452Speter 22424440Speter/* Helper for vfs_free_addrlist. */ 225151868Sdavidxu/* ARGSUSED */ 226151868Sdavidxustatic int 227151868Sdavidxuvfs_free_netcred(struct radix_node *rn, void *w) 22835938Sdyson{ 22935938Sdyson struct radix_node_head *rnh = (struct radix_node_head *) w; 23035938Sdyson struct ucred *cred; 23135938Sdyson 23235938Sdyson (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh); 23335938Sdyson cred = ((struct netcred *)rn)->netc_anon; 23435938Sdyson if (cred != NULL) 23535938Sdyson crfree(cred); 236147814Sjhb free(rn, M_NETADDR); 237147814Sjhb return (0); 238171210Speter} 23951138Salfred 24051138Salfred/* 24125537Sdfr * Free the net address hash lists that are hanging off the mount points. 24225537Sdfr */ 24325537Sdfrstatic void 24425537Sdfrvfs_free_addrlist(struct netexport *nep) 24525537Sdfr{ 24625537Sdfr int i; 24725537Sdfr struct radix_node_head *rnh; 24825537Sdfr struct ucred *cred; 24925537Sdfr 25025537Sdfr for (i = 0; i <= AF_MAX; i++) { 25128400Speter if ((rnh = nep->ne_rtable[i])) { 25256115Speter RADIX_NODE_HEAD_LOCK(rnh); 25356115Speter (*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh); 25436034Speter RADIX_NODE_HEAD_UNLOCK(rnh); 25526671Sdyson RADIX_NODE_HEAD_DESTROY(rnh); 25626671Sdyson free(rnh, M_RTABLE); 25726671Sdyson nep->ne_rtable[i] = NULL; /* not SMP safe XXX */ 25826671Sdyson } 259151868Sdavidxu } 260151868Sdavidxu cred = nep->ne_defexported.netc_anon; 261151868Sdavidxu if (cred != NULL) 26226671Sdyson crfree(cred); 26369514Sjake 26469514Sjake} 26526671Sdyson 26626671Sdyson/* 26729391Sphk * High level function to manipulate export options on a mount point 26834925Sdufault * and the passed in netexport. 26934925Sdufault * Struct export_args *argp is the variable used to twiddle options, 27034925Sdufault * the structure is described in sys/mount.h 27134925Sdufault */ 27234925Sdufaultint 27334925Sdufaultvfs_export(struct mount *mp, struct export_args *argp) 27434925Sdufault{ 27534925Sdufault struct netexport *nep; 27635938Sdyson int error; 277171210Speter 27841089Speter if (argp->ex_numsecflavors < 0 27946155Sphk || argp->ex_numsecflavors >= MAXSECFLAVORS) 28051791Smarcel return (EINVAL); 28151791Smarcel 282171210Speter error = 0; 28351791Smarcel lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL); 284171210Speter nep = mp->mnt_export; 285112895Sjeff if (argp->ex_flags & MNT_DELEXPORT) { 286112895Sjeff if (nep == NULL) { 28756271Srwatson error = ENOENT; 28856271Srwatson goto out; 28956271Srwatson } 29056271Srwatson if (mp->mnt_flag & MNT_EXPUBLIC) { 29156271Srwatson vfs_setpublicfs(NULL, NULL, NULL); 29256271Srwatson MNT_ILOCK(mp); 29356271Srwatson mp->mnt_flag &= ~MNT_EXPUBLIC; 29456271Srwatson MNT_IUNLOCK(mp); 29554803Srwatson } 29654803Srwatson vfs_free_addrlist(nep); 29754803Srwatson mp->mnt_export = NULL; 29854803Srwatson free(nep, M_MOUNT); 29955943Sjasone nep = NULL; 30056115Speter MNT_ILOCK(mp); 30156115Speter mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); 30259288Sjlemon MNT_IUNLOCK(mp); 30359288Sjlemon } 30475039Srwatson if (argp->ex_flags & MNT_EXPORTED) { 30575039Srwatson if (nep == NULL) { 30675039Srwatson nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO); 30775427Srwatson mp->mnt_export = nep; 30883652Speter } 30983796Srwatson if (argp->ex_flags & MNT_EXPUBLIC) { 31085891Sphk if ((error = vfs_setpublicfs(mp, nep, argp)) != 0) 311100897Srwatson goto out; 312100897Srwatson MNT_ILOCK(mp); 313100897Srwatson mp->mnt_flag |= MNT_EXPUBLIC; 314100897Srwatson MNT_IUNLOCK(mp); 315100897Srwatson } 316100897Srwatson if ((error = vfs_hang_addrlist(mp, nep, argp))) 31794936Smux goto out; 31896084Smux MNT_ILOCK(mp); 31997372Smarcel mp->mnt_flag |= MNT_EXPORTED; 32099856Salfred MNT_IUNLOCK(mp); 321101426Srwatson } 322122540Smckusick 323122540Smckusickout: 324122540Smckusick lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); 325122540Smckusick /* 326103575Salfred * Once we have executed the vfs_export() command, we do 327103575Salfred * not want to keep the "export" option around in the 328103575Salfred * options list, since that will cause subsequent MNT_UPDATE 329103575Salfred * calls to fail. The export information is saved in 330103575Salfred * mp->mnt_export, so we can safely delete the "export" mount option 331103575Salfred * here. 332103575Salfred */ 333103575Salfred vfs_deleteopt(mp->mnt_optnew, "export"); 334103575Salfred vfs_deleteopt(mp->mnt_opt, "export"); 335105692Srwatson return (error); 336105692Srwatson} 337105692Srwatson 338104731Srwatson/* 339104731Srwatson * Set the publicly exported filesystem (WebNFS). Currently, only 340104731Srwatson * one public filesystem is possible in the spec (RFC 2054 and 2055) 341106467Srwatson */ 342105950Speterint 343105950Spetervfs_setpublicfs(struct mount *mp, struct netexport *nep, 344106978Sdeischen struct export_args *argp) 345106978Sdeischen{ 346106978Sdeischen int error; 347107914Sdillon struct vnode *rvp; 348108406Srwatson char *cp; 349108406Srwatson 350108406Srwatson /* 351108406Srwatson * mp == NULL -> invalidate the current info, the FS is 352112895Sjeff * no longer exported. May be called from either vfs_export 353112902Sjeff * or unmount, so check if it hasn't already been done. 354112902Sjeff */ 355112902Sjeff if (mp == NULL) { 356112902Sjeff if (nfs_pub.np_valid) { 357112909Sjeff nfs_pub.np_valid = 0; 358112909Sjeff if (nfs_pub.np_index != NULL) { 359113276Smike free(nfs_pub.np_index, M_TEMP); 360115800Srwatson nfs_pub.np_index = NULL; 361115800Srwatson } 362115800Srwatson } 363125369Sdeischen return (0); 364127484Smtm } 365127484Smtm 366132117Sphk /* 367136831Srwatson * Only one allowed at a time. 368136831Srwatson */ 369136831Srwatson if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount) 370136831Srwatson return (EBUSY); 371136831Srwatson 372136831Srwatson /* 373136831Srwatson * Get real filehandle for root of exported FS. 374136831Srwatson */ 375136831Srwatson bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle)); 376139013Sdavidxu nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid; 377145435Sdavidxu 378151317Sdavidxu if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp))) 379156138Sdavidxu return (error); 380156138Sdavidxu 381156138Sdavidxu if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid))) 382156138Sdavidxu return (error); 383156138Sdavidxu 384156138Sdavidxu vput(rvp); 385153681Sphk 386155328Sdavidxu /* 387157039Sdavidxu * If an indexfile was specified, pull it in. 388162498Sdavidxu */ 389163953Srrs if (argp->ex_indexfile != NULL) { 390163953Srrs if (nfs_pub.np_index != NULL) 391163953Srrs nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP, 392163953Srrs M_WAITOK); 393171210Speter error = copyinstr(argp->ex_indexfile, nfs_pub.np_index, 394171210Speter MAXNAMLEN, (size_t *)0); 395171210Speter if (!error) { 396171210Speter /* 397171210Speter * Check for illegal filenames. 398171210Speter */ 399171861Sdavidxu for (cp = nfs_pub.np_index; *cp; cp++) { 400175165Sjhb if (*cp == '/') { 401175165Sjhb error = EINVAL; 402176731Sjeff break; 403176731Sjeff } 404176731Sjeff } 405176731Sjeff } 406176731Sjeff if (error) { 407177790Skib free(nfs_pub.np_index, M_TEMP); 408177790Skib nfs_pub.np_index = NULL; 409177790Skib return (error); 410177790Skib } 411177790Skib } 412177790Skib 413177790Skib nfs_pub.np_mount = mp; 414177790Skib nfs_pub.np_valid = 1; 415177790Skib return (0); 416177790Skib} 417177790Skib 418177790Skib/* 419177790Skib * Used by the filesystems to determine if a given network address 420177790Skib * (passed in 'nam') is present in their exports list, returns a pointer 421177790Skib * to struct netcred so that the filesystem can examine it for 422181905Sed * access rights (read/write/etc). 423184589Sdfr */ 424191675Sjamiestatic struct netcred * 425191675Sjamievfs_export_lookup(struct mount *mp, struct sockaddr *nam) 426191675Sjamie{ 427191675Sjamie struct netexport *nep; 428 register struct netcred *np; 429 register struct radix_node_head *rnh; 430 struct sockaddr *saddr; 431 432 nep = mp->mnt_export; 433 if (nep == NULL) 434 return (NULL); 435 np = NULL; 436 if (mp->mnt_flag & MNT_EXPORTED) { 437 /* 438 * Lookup in the export list first. 439 */ 440 if (nam != NULL) { 441 saddr = nam; 442 rnh = nep->ne_rtable[saddr->sa_family]; 443 if (rnh != NULL) { 444 RADIX_NODE_HEAD_RLOCK(rnh); 445 np = (struct netcred *) 446 (*rnh->rnh_matchaddr)(saddr, rnh); 447 RADIX_NODE_HEAD_RUNLOCK(rnh); 448 if (np && np->netc_rnodes->rn_flags & RNF_ROOT) 449 np = NULL; 450 } 451 } 452 /* 453 * If no address match, use the default if it exists. 454 */ 455 if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED) 456 np = &nep->ne_defexported; 457 } 458 return (np); 459} 460 461/* 462 * XXX: This comment comes from the deprecated ufs_check_export() 463 * XXX: and may not entirely apply, but lacking something better: 464 * This is the generic part of fhtovp called after the underlying 465 * filesystem has validated the file handle. 466 * 467 * Verify that a host should have access to a filesystem. 468 */ 469 470int 471vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, 472 struct ucred **credanonp, int *numsecflavors, int **secflavors) 473{ 474 struct netcred *np; 475 476 lockmgr(&mp->mnt_explock, LK_SHARED, NULL); 477 np = vfs_export_lookup(mp, nam); 478 if (np == NULL) { 479 lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); 480 *credanonp = NULL; 481 return (EACCES); 482 } 483 *extflagsp = np->netc_exflags; 484 if ((*credanonp = np->netc_anon) != NULL) 485 crhold(*credanonp); 486 if (numsecflavors) 487 *numsecflavors = np->netc_numsecflavors; 488 if (secflavors) 489 *secflavors = np->netc_secflavors; 490 lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); 491 return (0); 492} 493 494