+ int error = 0;
+ size_t pathlen;
+ struct nfs_exportfs *nxfs, *nxfs2, *nxfs3;
+ struct nfs_export *nx, *nx2, *nx3;
+ struct nfs_filehandle nfh;
+ struct nameidata mnd, xnd;
+ vnode_t mvp = NULL, xvp = NULL;
+ mount_t mp = NULL;
+ char path[MAXPATHLEN];
+ int expisroot;
+
+ if (unxa->nxa_flags == NXA_CHECK) {
+ /* just check if the path is an NFS-exportable file system */
+ error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
+ if (error)
+ return (error);
+ NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
+ UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
+ error = namei(&mnd);
+ if (error)
+ return (error);
+ mvp = mnd.ni_vp;
+ mp = vnode_mount(mvp);
+ /* make sure it's the root of a file system */
+ if (!vnode_isvroot(mvp))
+ error = EINVAL;
+ /* make sure the file system is NFS-exportable */
+ if (!error) {
+ nfh.nfh_len = NFSV3_MAX_FID_SIZE;
+ error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
+ }
+ if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE))
+ error = EIO;
+ if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED))
+ error = EISDIR;
+ vnode_put(mvp);
+ nameidone(&mnd);
+ return (error);
+ }
+
+ /* all other operations: must be super user */
+ if ((error = vfs_context_suser(ctx)))
+ return (error);
+
+ if (unxa->nxa_flags & NXA_DELETE_ALL) {
+ /* delete all exports on all file systems */
+ lck_rw_lock_exclusive(&nfsrv_export_rwlock);
+ while ((nxfs = LIST_FIRST(&nfsrv_exports))) {
+ mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
+ if (mp) {
+ vfs_clearflags(mp, MNT_EXPORTED);
+ mount_iterdrop(mp);
+ mp = NULL;
+ }
+ /* delete all exports on this file system */
+ while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
+ LIST_REMOVE(nx, nx_next);
+ LIST_REMOVE(nx, nx_hash);
+ /* delete all netopts for this export */
+ nfsrv_free_addrlist(nx, NULL);
+ nx->nx_flags &= ~NX_DEFAULTEXPORT;
+ if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
+ kauth_cred_unref(&nx->nx_defopt.nxo_cred);
+ }
+ /* free active user list for this export */
+ nfsrv_free_user_list(&nx->nx_user_list);
+ FREE(nx->nx_path, M_TEMP);
+ FREE(nx, M_TEMP);
+ }
+ LIST_REMOVE(nxfs, nxfs_next);
+ FREE(nxfs->nxfs_path, M_TEMP);
+ FREE(nxfs, M_TEMP);
+ }
+ if (nfsrv_export_hashtbl) {
+ /* all exports deleted, clean up export hash table */
+ FREE(nfsrv_export_hashtbl, M_TEMP);
+ nfsrv_export_hashtbl = NULL;
+ }
+ lck_rw_done(&nfsrv_export_rwlock);
+ return (0);
+ }
+
+ error = copyinstr(unxa->nxa_fspath, path, MAXPATHLEN, &pathlen);
+ if (error)
+ return (error);
+
+ lck_rw_lock_exclusive(&nfsrv_export_rwlock);
+
+ /* init export hash table if not already */
+ if (!nfsrv_export_hashtbl) {
+ if (nfsrv_export_hash_size <= 0)
+ nfsrv_export_hash_size = NFSRVEXPHASHSZ;
+ nfsrv_export_hashtbl = hashinit(nfsrv_export_hash_size, M_TEMP, &nfsrv_export_hash);
+ }
+
+ // first check if we've already got an exportfs with the given ID
+ LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
+ if (nxfs->nxfs_id == unxa->nxa_fsid)
+ break;
+ }
+ if (nxfs) {
+ /* verify exported FS path matches given path */
+ if (strncmp(path, nxfs->nxfs_path, MAXPATHLEN)) {
+ error = EEXIST;
+ goto unlock_out;
+ }
+ if ((unxa->nxa_flags & (NXA_ADD|NXA_OFFLINE)) == NXA_ADD) {
+ /* if adding, verify that the mount is still what we expect */
+ mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
+ if (mp) {
+ mount_ref(mp, 0);
+ mount_iterdrop(mp);
+ }
+ /* find exported FS root vnode */
+ NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
+ UIO_SYSSPACE, CAST_USER_ADDR_T(nxfs->nxfs_path), ctx);
+ error = namei(&mnd);
+ if (error)
+ goto unlock_out;
+ mvp = mnd.ni_vp;
+ /* make sure it's (still) the root of a file system */
+ if (!vnode_isvroot(mvp)) {
+ error = EINVAL;
+ goto out;
+ }
+ /* sanity check: this should be same mount */
+ if (mp != vnode_mount(mvp)) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+ } else {
+ /* no current exported file system with that ID */
+ if (!(unxa->nxa_flags & NXA_ADD)) {
+ error = ENOENT;
+ goto unlock_out;
+ }
+
+ /* find exported FS root vnode */
+ NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
+ UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
+ error = namei(&mnd);
+ if (error) {
+ if (!(unxa->nxa_flags & NXA_OFFLINE))
+ goto unlock_out;
+ } else {
+ mvp = mnd.ni_vp;
+ /* make sure it's the root of a file system */
+ if (!vnode_isvroot(mvp)) {
+ /* bail if not marked offline */
+ if (!(unxa->nxa_flags & NXA_OFFLINE)) {
+ error = EINVAL;
+ goto out;
+ }
+ vnode_put(mvp);
+ nameidone(&mnd);
+ mvp = NULL;
+ } else {
+ mp = vnode_mount(mvp);
+ mount_ref(mp, 0);
+
+ /* make sure the file system is NFS-exportable */
+ nfh.nfh_len = NFSV3_MAX_FID_SIZE;
+ error = VFS_VPTOFH(mvp, (int*)&nfh.nfh_len, &nfh.nfh_fid[0], NULL);
+ if (!error && (nfh.nfh_len > (int)NFSV3_MAX_FID_SIZE))
+ error = EIO;
+ if (!error && !(mp->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED))
+ error = EISDIR;
+ if (error)
+ goto out;
+ }
+ }
+
+ /* add an exportfs for it */
+ MALLOC(nxfs, struct nfs_exportfs *, sizeof(struct nfs_exportfs), M_TEMP, M_WAITOK);
+ if (!nxfs) {
+ error = ENOMEM;
+ goto out;
+ }
+ bzero(nxfs, sizeof(struct nfs_exportfs));
+ nxfs->nxfs_id = unxa->nxa_fsid;
+ MALLOC(nxfs->nxfs_path, char*, pathlen, M_TEMP, M_WAITOK);
+ if (!nxfs->nxfs_path) {
+ FREE(nxfs, M_TEMP);
+ error = ENOMEM;
+ goto out;
+ }
+ bcopy(path, nxfs->nxfs_path, pathlen);
+ /* insert into list in reverse-sorted order */
+ nxfs3 = NULL;
+ LIST_FOREACH(nxfs2, &nfsrv_exports, nxfs_next) {
+ if (strncmp(nxfs->nxfs_path, nxfs2->nxfs_path, MAXPATHLEN) > 0)
+ break;
+ nxfs3 = nxfs2;
+ }
+ if (nxfs2)
+ LIST_INSERT_BEFORE(nxfs2, nxfs, nxfs_next);
+ else if (nxfs3)
+ LIST_INSERT_AFTER(nxfs3, nxfs, nxfs_next);
+ else
+ LIST_INSERT_HEAD(&nfsrv_exports, nxfs, nxfs_next);
+
+ /* make sure any quotas are enabled before we export the file system */
+ if (mp)
+ enablequotas(mp, ctx);
+ }
+
+ if (unxa->nxa_exppath) {
+ error = copyinstr(unxa->nxa_exppath, path, MAXPATHLEN, &pathlen);
+ if (error)
+ goto out;
+ LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
+ if (nx->nx_id == unxa->nxa_expid)
+ break;
+ }
+ if (nx) {
+ /* verify exported FS path matches given path */
+ if (strncmp(path, nx->nx_path, MAXPATHLEN)) {
+ error = EEXIST;
+ goto out;
+ }
+ } else {
+ /* no current export with that ID */
+ if (!(unxa->nxa_flags & NXA_ADD)) {
+ error = ENOENT;
+ goto out;
+ }
+ /* add an export for it */
+ MALLOC(nx, struct nfs_export *, sizeof(struct nfs_export), M_TEMP, M_WAITOK);
+ if (!nx) {
+ error = ENOMEM;
+ goto out1;
+ }
+ bzero(nx, sizeof(struct nfs_export));
+ nx->nx_id = unxa->nxa_expid;
+ nx->nx_fs = nxfs;
+ microtime(&nx->nx_exptime);
+ MALLOC(nx->nx_path, char*, pathlen, M_TEMP, M_WAITOK);
+ if (!nx->nx_path) {
+ error = ENOMEM;
+ FREE(nx, M_TEMP);
+ nx = NULL;
+ goto out1;
+ }
+ bcopy(path, nx->nx_path, pathlen);
+ /* initialize the active user list */
+ nfsrv_init_user_list(&nx->nx_user_list);
+ /* insert into list in reverse-sorted order */
+ nx3 = NULL;
+ LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
+ if (strncmp(nx->nx_path, nx2->nx_path, MAXPATHLEN) > 0)
+ break;
+ nx3 = nx2;
+ }
+ if (nx2)
+ LIST_INSERT_BEFORE(nx2, nx, nx_next);
+ else if (nx3)
+ LIST_INSERT_AFTER(nx3, nx, nx_next);
+ else
+ LIST_INSERT_HEAD(&nxfs->nxfs_exports, nx, nx_next);
+ /* insert into hash */
+ LIST_INSERT_HEAD(NFSRVEXPHASH(nxfs->nxfs_id, nx->nx_id), nx, nx_hash);
+
+ /*
+ * We don't allow/support nested exports. Check if the new entry
+ * nests with the entries before and after or if there's an
+ * entry for the file system root and subdirs.
+ */
+ error = 0;
+ if ((nx3 && !strncmp(nx3->nx_path, nx->nx_path, pathlen - 1) &&
+ (nx3->nx_path[pathlen-1] == '/')) ||
+ (nx2 && !strncmp(nx2->nx_path, nx->nx_path, strlen(nx2->nx_path)) &&
+ (nx->nx_path[strlen(nx2->nx_path)] == '/')))
+ error = EINVAL;
+ if (!error) {
+ /* check export conflict with fs root export and vice versa */
+ expisroot = !nx->nx_path[0] ||
+ ((nx->nx_path[0] == '.') && !nx->nx_path[1]);
+ LIST_FOREACH(nx2, &nxfs->nxfs_exports, nx_next) {
+ if (expisroot) {
+ if (nx2 != nx)
+ break;
+ } else if (!nx2->nx_path[0])
+ break;
+ else if ((nx2->nx_path[0] == '.') && !nx2->nx_path[1])
+ break;
+ }
+ if (nx2)
+ error = EINVAL;
+ }
+ if (error) {
+ /*
+ * Don't actually return an error because mountd is
+ * probably about to delete the conflicting export.
+ * This can happen when a new export momentarily conflicts
+ * with an old export while the transition is being made.
+ * Theoretically, mountd could be written to avoid this
+ * transient situation - but it would greatly increase the
+ * complexity of mountd for very little overall benefit.
+ */
+ printf("nfsrv_export: warning: nested exports: %s/%s\n",
+ nxfs->nxfs_path, nx->nx_path);
+ error = 0;
+ }
+ nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
+ }
+ /* make sure file handle is set up */
+ if ((nx->nx_fh.nfh_xh.nxh_version != htonl(NFS_FH_VERSION)) ||
+ (nx->nx_fh.nfh_xh.nxh_flags & NXHF_INVALIDFH)) {
+ /* try to set up export root file handle */
+ nx->nx_fh.nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
+ nx->nx_fh.nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
+ nx->nx_fh.nfh_xh.nxh_expid = htonl(nx->nx_id);
+ nx->nx_fh.nfh_xh.nxh_flags = 0;
+ nx->nx_fh.nfh_xh.nxh_reserved = 0;
+ nx->nx_fh.nfh_fhp = (u_char*)&nx->nx_fh.nfh_xh;
+ bzero(&nx->nx_fh.nfh_fid[0], NFSV2_MAX_FID_SIZE);
+ if (mvp) {
+ /* find export root vnode */
+ if (!nx->nx_path[0] || ((nx->nx_path[0] == '.') && !nx->nx_path[1])) {
+ /* exporting file system's root directory */
+ xvp = mvp;
+ vnode_get(xvp);
+ } else {
+ xnd.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+ xnd.ni_op = OP_LOOKUP;
+#endif
+ xnd.ni_cnd.cn_flags = LOCKLEAF;
+ xnd.ni_pathlen = pathlen - 1;
+ xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf = path;
+ xnd.ni_startdir = mvp;
+ xnd.ni_usedvp = mvp;
+ xnd.ni_cnd.cn_context = ctx;
+ while ((error = lookup(&xnd)) == ERECYCLE) {
+ xnd.ni_cnd.cn_flags = LOCKLEAF;
+ xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf;
+ xnd.ni_usedvp = xnd.ni_dvp = xnd.ni_startdir = mvp;
+ }
+ if (error)
+ goto out1;
+ xvp = xnd.ni_vp;
+ }
+
+ if (vnode_vtype(xvp) != VDIR) {
+ error = EINVAL;
+ vnode_put(xvp);
+ goto out1;
+ }
+
+ /* grab file handle */
+ nx->nx_fh.nfh_len = NFSV3_MAX_FID_SIZE;
+ error = VFS_VPTOFH(xvp, (int*)&nx->nx_fh.nfh_len, &nx->nx_fh.nfh_fid[0], NULL);
+ if (!error && (nx->nx_fh.nfh_len > (int)NFSV3_MAX_FID_SIZE)) {
+ error = EIO;
+ } else {
+ nx->nx_fh.nfh_xh.nxh_fidlen = nx->nx_fh.nfh_len;
+ nx->nx_fh.nfh_len += sizeof(nx->nx_fh.nfh_xh);
+ }
+
+ vnode_put(xvp);
+ if (error)
+ goto out1;
+ } else {
+ nx->nx_fh.nfh_xh.nxh_flags = NXHF_INVALIDFH;
+ nx->nx_fh.nfh_xh.nxh_fidlen = 0;
+ nx->nx_fh.nfh_len = sizeof(nx->nx_fh.nfh_xh);
+ }
+ }
+ } else {
+ nx = NULL;
+ }
+
+ /* perform the export changes */
+ if (unxa->nxa_flags & NXA_DELETE) {
+ if (!nx) {
+ /* delete all exports on this file system */
+ while ((nx = LIST_FIRST(&nxfs->nxfs_exports))) {
+ LIST_REMOVE(nx, nx_next);
+ LIST_REMOVE(nx, nx_hash);
+ /* delete all netopts for this export */
+ nfsrv_free_addrlist(nx, NULL);
+ nx->nx_flags &= ~NX_DEFAULTEXPORT;
+ if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
+ kauth_cred_unref(&nx->nx_defopt.nxo_cred);
+ }
+ /* delete active user list for this export */
+ nfsrv_free_user_list(&nx->nx_user_list);
+ FREE(nx->nx_path, M_TEMP);
+ FREE(nx, M_TEMP);
+ }
+ goto out1;
+ } else if (!unxa->nxa_netcount) {
+ /* delete all netopts for this export */
+ nfsrv_free_addrlist(nx, NULL);
+ nx->nx_flags &= ~NX_DEFAULTEXPORT;
+ if (IS_VALID_CRED(nx->nx_defopt.nxo_cred)) {
+ kauth_cred_unref(&nx->nx_defopt.nxo_cred);
+ }
+ } else {
+ /* delete only the netopts for the given addresses */
+ error = nfsrv_free_addrlist(nx, unxa);
+ if (error)
+ goto out1;
+ }
+ }
+ if (unxa->nxa_flags & NXA_ADD) {
+ /*
+ * If going offline set the export time so that when
+ * coming back on line we will present a new write verifier
+ * to the client.
+ */
+ if (unxa->nxa_flags & NXA_OFFLINE)
+ microtime(&nx->nx_exptime);
+
+ error = nfsrv_hang_addrlist(nx, unxa);
+ if (!error && mp)
+ vfs_setflags(mp, MNT_EXPORTED);
+ }
+
+out1:
+ if (nx && !nx->nx_expcnt) {
+ /* export has no export options */
+ LIST_REMOVE(nx, nx_next);
+ LIST_REMOVE(nx, nx_hash);
+ /* delete active user list for this export */
+ nfsrv_free_user_list(&nx->nx_user_list);
+ FREE(nx->nx_path, M_TEMP);
+ FREE(nx, M_TEMP);
+ }
+ if (LIST_EMPTY(&nxfs->nxfs_exports)) {
+ /* exported file system has no more exports */
+ LIST_REMOVE(nxfs, nxfs_next);
+ FREE(nxfs->nxfs_path, M_TEMP);
+ FREE(nxfs, M_TEMP);
+ if (mp)
+ vfs_clearflags(mp, MNT_EXPORTED);
+ }
+
+out:
+ if (mvp) {
+ vnode_put(mvp);
+ nameidone(&mnd);
+ }
+unlock_out:
+ if (mp)
+ mount_drop(mp, 0);
+ lck_rw_done(&nfsrv_export_rwlock);
+ return (error);
+}
+
+struct nfs_export_options *
+nfsrv_export_lookup(struct nfs_export *nx, mbuf_t nam)
+{
+ struct nfs_export_options *nxo = NULL;
+ struct nfs_netopt *no = NULL;
+ struct radix_node_head *rnh;
+ struct sockaddr *saddr;
+
+ /* Lookup in the export list first. */
+ if (nam != NULL) {
+ saddr = mbuf_data(nam);
+ rnh = nx->nx_rtable[saddr->sa_family];
+ if (rnh != NULL) {
+ no = (struct nfs_netopt *)
+ (*rnh->rnh_matchaddr)((caddr_t)saddr, rnh);
+ if (no && no->no_rnodes->rn_flags & RNF_ROOT)
+ no = NULL;
+ if (no)
+ nxo = &no->no_opt;
+ }
+ }
+ /* If no address match, use the default if it exists. */
+ if ((nxo == NULL) && (nx->nx_flags & NX_DEFAULTEXPORT))
+ nxo = &nx->nx_defopt;
+ return (nxo);
+}
+
+/* find an export for the given handle */
+struct nfs_export *
+nfsrv_fhtoexport(struct nfs_filehandle *nfhp)
+{
+ struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
+ struct nfs_export *nx;
+ uint32_t fsid, expid;
+
+ if (!nfsrv_export_hashtbl)
+ return (NULL);
+ fsid = ntohl(nxh->nxh_fsid);
+ expid = ntohl(nxh->nxh_expid);
+ nx = NFSRVEXPHASH(fsid, expid)->lh_first;
+ for (; nx; nx = LIST_NEXT(nx, nx_hash)) {
+ if (nx->nx_fs->nxfs_id != fsid)
+ continue;
+ if (nx->nx_id != expid)
+ continue;
+ break;
+ }
+ return (nx);
+}
+
+/*
+ * nfsrv_fhtovp() - convert FH to vnode and export info
+ */
+int
+nfsrv_fhtovp(
+ struct nfs_filehandle *nfhp,
+ struct nfsrv_descript *nd,
+ vnode_t *vpp,
+ struct nfs_export **nxp,
+ struct nfs_export_options **nxop)
+{
+ struct nfs_exphandle *nxh = (struct nfs_exphandle*)nfhp->nfh_fhp;
+ struct nfs_export_options *nxo;
+ u_char *fidp;
+ int error;
+ struct mount *mp;
+ mbuf_t nam = NULL;
+ uint32_t v;
+ int i, valid;
+
+ *vpp = NULL;
+ *nxp = NULL;
+ *nxop = NULL;
+
+ if (nd != NULL)
+ nam = nd->nd_nam;
+
+ v = ntohl(nxh->nxh_version);
+ if (v != NFS_FH_VERSION) {
+ /* file handle format not supported */
+ return (ESTALE);
+ }
+ if (nfhp->nfh_len > NFSV3_MAX_FH_SIZE)
+ return (EBADRPC);
+ if (nfhp->nfh_len < (int)sizeof(struct nfs_exphandle))
+ return (ESTALE);
+ v = ntohs(nxh->nxh_flags);
+ if (v & NXHF_INVALIDFH)
+ return (ESTALE);
+
+ *nxp = nfsrv_fhtoexport(nfhp);
+ if (!*nxp)
+ return (ESTALE);
+
+ /* Get the export option structure for this <export, client> tuple. */
+ *nxop = nxo = nfsrv_export_lookup(*nxp, nam);
+ if (nam && (*nxop == NULL))
+ return (EACCES);
+
+ if (nd != NULL) {
+ /* Validate the security flavor of the request */
+ for (i = 0, valid = 0; i < nxo->nxo_sec.count; i++) {
+ if (nd->nd_sec == nxo->nxo_sec.flavors[i]) {
+ valid = 1;
+ break;
+ }
+ }
+ if (!valid) {
+ /*
+ * RFC 2623 section 2.3.2 recommends no authentication
+ * requirement for certain NFS procedures used for mounting.
+ * This allows an unauthenticated superuser on the client
+ * to do mounts for the benefit of authenticated users.
+ */
+ if (nd->nd_vers == NFS_VER2)
+ if (nd->nd_procnum == NFSV2PROC_GETATTR ||
+ nd->nd_procnum == NFSV2PROC_STATFS)
+ valid = 1;
+ if (nd->nd_vers == NFS_VER3)
+ if (nd->nd_procnum == NFSPROC_FSINFO)
+ valid = 1;
+
+ if (!valid)
+ return (NFSERR_AUTHERR | AUTH_REJECTCRED);
+ }
+ }
+
+ if (nxo && (nxo->nxo_flags & NX_OFFLINE))
+ return ((nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER);
+
+ /* find mount structure */
+ mp = vfs_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
+ if (mp) {
+ error = vfs_busy(mp, LK_NOWAIT);
+ mount_iterdrop(mp);
+ if (error)
+ mp = NULL;
+ }
+ if (!mp) {
+ /*
+ * We have an export, but no mount?
+ * Perhaps the export just hasn't been marked offline yet.
+ */
+ return ((nd == NULL || nd->nd_vers == NFS_VER2) ? ESTALE : NFSERR_TRYLATER);
+ }
+
+ fidp = nfhp->nfh_fhp + sizeof(*nxh);
+ error = VFS_FHTOVP(mp, nxh->nxh_fidlen, fidp, vpp, NULL);
+ vfs_unbusy(mp);
+ if (error)
+ return (error);
+ /* vnode pointer should be good at this point or ... */
+ if (*vpp == NULL)
+ return (ESTALE);
+ return (0);
+}
+
+/*
+ * nfsrv_credcheck() - check/map credentials according
+ * to given export options.
+ */
+int
+nfsrv_credcheck(
+ struct nfsrv_descript *nd,
+ vfs_context_t ctx,
+ __unused struct nfs_export *nx,
+ struct nfs_export_options *nxo)
+{
+ if (nxo && nxo->nxo_cred) {
+ if ((nxo->nxo_flags & NX_MAPALL) ||
+ ((nxo->nxo_flags & NX_MAPROOT) && !suser(nd->nd_cr, NULL))) {
+ kauth_cred_ref(nxo->nxo_cred);
+ kauth_cred_unref(&nd->nd_cr);
+ nd->nd_cr = nxo->nxo_cred;
+ }
+ }
+ ctx->vc_ucred = nd->nd_cr;
+ return (0);
+}
+
+/*
+ * nfsrv_vptofh() - convert vnode to file handle for given export
+ *
+ * If the caller is passing in a vnode for a ".." directory entry,
+ * they can pass a directory NFS file handle (dnfhp) which will be
+ * checked against the root export file handle. If it matches, we
+ * refuse to provide the file handle for the out-of-export directory.
+ */
+int
+nfsrv_vptofh(
+ struct nfs_export *nx,
+ int nfsvers,
+ struct nfs_filehandle *dnfhp,
+ vnode_t vp,
+ vfs_context_t ctx,
+ struct nfs_filehandle *nfhp)
+{
+ int error;
+ uint32_t maxfidsize;
+
+ nfhp->nfh_fhp = (u_char*)&nfhp->nfh_xh;
+ nfhp->nfh_xh.nxh_version = htonl(NFS_FH_VERSION);
+ nfhp->nfh_xh.nxh_fsid = htonl(nx->nx_fs->nxfs_id);
+ nfhp->nfh_xh.nxh_expid = htonl(nx->nx_id);
+ nfhp->nfh_xh.nxh_flags = 0;
+ nfhp->nfh_xh.nxh_reserved = 0;
+
+ if (nfsvers == NFS_VER2)
+ bzero(&nfhp->nfh_fid[0], NFSV2_MAX_FID_SIZE);
+
+ /* if directory FH matches export root, return invalid FH */
+ if (dnfhp && nfsrv_fhmatch(dnfhp, &nx->nx_fh)) {
+ if (nfsvers == NFS_VER2)
+ nfhp->nfh_len = NFSX_V2FH;
+ else
+ nfhp->nfh_len = sizeof(nfhp->nfh_xh);
+ nfhp->nfh_xh.nxh_fidlen = 0;
+ nfhp->nfh_xh.nxh_flags = htons(NXHF_INVALIDFH);
+ return (0);
+ }
+
+ if (nfsvers == NFS_VER2)
+ maxfidsize = NFSV2_MAX_FID_SIZE;
+ else
+ maxfidsize = NFSV3_MAX_FID_SIZE;
+ nfhp->nfh_len = maxfidsize;
+
+ error = VFS_VPTOFH(vp, (int*)&nfhp->nfh_len, &nfhp->nfh_fid[0], ctx);
+ if (error)
+ return (error);
+ if (nfhp->nfh_len > maxfidsize)
+ return (EOVERFLOW);
+ nfhp->nfh_xh.nxh_fidlen = nfhp->nfh_len;
+ nfhp->nfh_len += sizeof(nfhp->nfh_xh);
+ if ((nfsvers == NFS_VER2) && (nfhp->nfh_len < NFSX_V2FH))
+ nfhp->nfh_len = NFSX_V2FH;
+