+ /* for mirror mounts, we can just use the file handle passed in */
+ if (nmp->nm_fh) {
+ dirfh.fh_len = nmp->nm_fh->fh_len;
+ bcopy(nmp->nm_fh->fh_data, dirfh.fh_data, dirfh.fh_len);
+ NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, NULL, 0);
+ goto gotfh;
+ }
+
+ /* otherwise, we need to get the fh for the directory we are mounting */
+
+ /* if no components, just get root */
+ if (fspath.np_compcount == 0) {
+nocomponents:
+ // PUTROOTFH + GETATTR(FH)
+ NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, NULL, 0);
+ numops = 2;
+ nfsm_chain_build_alloc_init(error, &nmreq, 9 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "mount", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTROOTFH);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ NFS4_DEFAULT_ATTRIBUTES(bitmap);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_FILEHANDLE);
+ nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
+ vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
+ if (!error)
+ error = nfs_request_async_finish(req, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTROOTFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ nfsmout_if(error);
+ NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
+ error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, &dirfh, NULL, NULL);
+ if (!error && !NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
+ printf("nfs: mount didn't return filehandle?\n");
+ error = EBADRPC;
+ }
+ nfsmout_if(error);
+ nfsm_chain_cleanup(&nmrep);
+ nfsm_chain_null(&nmreq);
+ NVATTR_CLEANUP(&nvattr);
+ goto gotfh;
+ }
+
+ /* look up each path component */
+ for (comp=0; comp < fspath.np_compcount; ) {
+ isdotdot = 0;
+ if (fspath.np_components[comp][0] == '.') {
+ if (fspath.np_components[comp][1] == '\0') {
+ /* skip "." */
+ comp++;
+ continue;
+ }
+ /* treat ".." specially */
+ if ((fspath.np_components[comp][1] == '.') &&
+ (fspath.np_components[comp][2] == '\0'))
+ isdotdot = 1;
+ if (isdotdot && (dirfh.fh_len == 0)) {
+ /* ".." in root directory is same as "." */
+ comp++;
+ continue;
+ }
+ }
+ // PUT(ROOT)FH + LOOKUP(P) + GETFH + GETATTR
+ if (dirfh.fh_len == 0)
+ NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, isdotdot ? NULL : fspath.np_components[comp], 0);
+ else
+ NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, isdotdot ? NULL : fspath.np_components[comp], 0);
+ numops = 4;
+ nfsm_chain_build_alloc_init(error, &nmreq, 18 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "mount", numops);
+ numops--;
+ if (dirfh.fh_len) {
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
+ nfsm_chain_add_fh(error, &nmreq, NFS_VER4, dirfh.fh_data, dirfh.fh_len);
+ } else {
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTROOTFH);
+ }
+ numops--;
+ if (isdotdot) {
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUPP);
+ } else {
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
+ nfsm_chain_add_name(error, &nmreq,
+ fspath.np_components[comp], strlen(fspath.np_components[comp]), nmp);
+ }
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETFH);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ NFS4_DEFAULT_ATTRIBUTES(bitmap);
+ /* if no namedattr support or component is ".zfs", clear NFS_FATTR_NAMED_ATTR */
+ if (NMFLAG(nmp, NONAMEDATTR) || !strcmp(fspath.np_components[comp], ".zfs"))
+ NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
+ nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
+ vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
+ if (!error)
+ error = nfs_request_async_finish(req, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, dirfh.fh_len ? NFS_OP_PUTFH : NFS_OP_PUTROOTFH);
+ nfsm_chain_op_check(error, &nmrep, isdotdot ? NFS_OP_LOOKUPP : NFS_OP_LOOKUP);
+ nfsmout_if(error);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETFH);
+ nfsm_chain_get_32(error, &nmrep, fh.fh_len);
+ nfsm_chain_get_opaque(error, &nmrep, fh.fh_len, fh.fh_data);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ if (!error) {
+ NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
+ error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL, &nfsls);
+ }
+ nfsm_chain_cleanup(&nmrep);
+ nfsm_chain_null(&nmreq);
+ if (error) {
+ /* LOOKUP succeeded but GETATTR failed? This could be a referral. */
+ /* Try the lookup again with a getattr for fs_locations. */
+ nfs_fs_locations_cleanup(&nfsls);
+ error = nfs4_get_fs_locations(nmp, NULL, dirfh.fh_data, dirfh.fh_len, fspath.np_components[comp], ctx, &nfsls);
+ if (!error && (nfsls.nl_numlocs < 1))
+ error = ENOENT;
+ nfsmout_if(error);
+ if (++loopcnt > MAXSYMLINKS) {
+ /* too many symlink/referral redirections */
+ error = ELOOP;
+ goto nfsmout;
+ }
+ /* tear down the current connection */
+ nfs_disconnect(nmp);
+ /* replace fs locations */
+ nfs_fs_locations_cleanup(&nmp->nm_locations);
+ nmp->nm_locations = nfsls;
+ bzero(&nfsls, sizeof(nfsls));
+ /* initiate a connection using the new fs locations */
+ error = nfs_mount_connect(nmp);
+ if (!error && !(nmp->nm_locations.nl_current.nli_flags & NLI_VALID))
+ error = EIO;
+ nfsmout_if(error);
+ /* add new server's remote path to beginning of our path and continue */
+ nfsp = &nmp->nm_locations.nl_locations[nmp->nm_locations.nl_current.nli_loc]->nl_path;
+ bzero(&fspath2, sizeof(fspath2));
+ fspath2.np_compcount = (fspath.np_compcount - comp - 1) + nfsp->np_compcount;
+ if (fspath2.np_compcount > 0) {
+ MALLOC(fspath2.np_components, char **, fspath2.np_compcount*sizeof(char*), M_TEMP, M_WAITOK|M_ZERO);
+ if (!fspath2.np_components) {
+ error = ENOMEM;
+ goto nfsmout;
+ }
+ for (comp2=0; comp2 < nfsp->np_compcount; comp2++) {
+ int slen = strlen(nfsp->np_components[comp2]);
+ MALLOC(fspath2.np_components[comp2], char *, slen+1, M_TEMP, M_WAITOK|M_ZERO);
+ if (!fspath2.np_components[comp2]) {
+ /* clean up fspath2, then error out */
+ while (comp2 > 0) {
+ comp2--;
+ FREE(fspath2.np_components[comp2], M_TEMP);
+ }
+ FREE(fspath2.np_components, M_TEMP);
+ error = ENOMEM;
+ goto nfsmout;
+ }
+ strlcpy(fspath2.np_components[comp2], nfsp->np_components[comp2], slen+1);
+ }
+ if ((fspath.np_compcount - comp - 1) > 0)
+ bcopy(&fspath.np_components[comp+1], &fspath2.np_components[nfsp->np_compcount], (fspath.np_compcount - comp - 1)*sizeof(char*));
+ /* free up unused parts of old path (prior components and component array) */
+ do {
+ FREE(fspath.np_components[comp], M_TEMP);
+ } while (comp-- > 0);
+ FREE(fspath.np_components, M_TEMP);
+ /* put new path in place */
+ fspath = fspath2;
+ }
+ /* reset dirfh and component index */
+ dirfh.fh_len = 0;
+ comp = 0;
+ NVATTR_CLEANUP(&nvattr);
+ if (fspath.np_compcount == 0)
+ goto nocomponents;
+ continue;
+ }
+ nfsmout_if(error);
+ /* if file handle is for a symlink, then update the path with the symlink contents */
+ if (NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_TYPE) && (nvattr.nva_type == VLNK)) {
+ if (++loopcnt > MAXSYMLINKS)
+ error = ELOOP;
+ else
+ error = nfs4_mount_update_path_with_symlink(nmp, &fspath, comp, &dirfh, &depth, &fh, ctx);
+ nfsmout_if(error);
+ /* directory file handle is either left the same or reset to root (if link was absolute) */
+ /* path traversal starts at beginning of the path again */
+ comp = 0;
+ NVATTR_CLEANUP(&nvattr);
+ nfs_fs_locations_cleanup(&nfsls);
+ continue;
+ }
+ NVATTR_CLEANUP(&nvattr);
+ nfs_fs_locations_cleanup(&nfsls);
+ /* not a symlink... */
+ if ((nmp->nm_state & NFSSTA_NEEDSECINFO) && (comp == (fspath.np_compcount-1)) && !isdotdot) {
+ /* need to get SECINFO for the directory being mounted */
+ if (dirfh.fh_len == 0)
+ NFSREQ_SECINFO_SET(&si, NULL, NULL, 0, isdotdot ? NULL : fspath.np_components[comp], 0);
+ else
+ NFSREQ_SECINFO_SET(&si, NULL, dirfh.fh_data, dirfh.fh_len, isdotdot ? NULL : fspath.np_components[comp], 0);
+ sec.count = NX_MAX_SEC_FLAVORS;
+ error = nfs4_secinfo_rpc(nmp, &si, vfs_context_ucred(ctx), sec.flavors, &sec.count);
+ /* [sigh] some implementations return "illegal" error for unsupported ops */
+ if (error == NFSERR_OP_ILLEGAL)
+ error = 0;
+ nfsmout_if(error);
+ /* set our default security flavor to the first in the list */
+ if (sec.count)
+ nmp->nm_auth = sec.flavors[0];
+ nmp->nm_state &= ~NFSSTA_NEEDSECINFO;
+ }
+ /* advance directory file handle, component index, & update depth */
+ dirfh = fh;
+ comp++;
+ if (!isdotdot) /* going down the hierarchy */
+ depth++;
+ else if (--depth <= 0) /* going up the hierarchy */
+ dirfh.fh_len = 0; /* clear dirfh when we hit root */
+ }
+
+gotfh:
+ /* get attrs for mount point root */
+ numops = NMFLAG(nmp, NONAMEDATTR) ? 2 : 3; // PUTFH + GETATTR + OPENATTR
+ nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "mount", numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
+ nfsm_chain_add_fh(error, &nmreq, NFS_VER4, dirfh.fh_data, dirfh.fh_len);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ NFS4_DEFAULT_ATTRIBUTES(bitmap);
+ /* if no namedattr support or last component is ".zfs", clear NFS_FATTR_NAMED_ATTR */
+ if (NMFLAG(nmp, NONAMEDATTR) || ((fspath.np_compcount > 0) && !strcmp(fspath.np_components[fspath.np_compcount-1], ".zfs")))
+ NFS_BITMAP_CLR(bitmap, NFS_FATTR_NAMED_ATTR);
+ nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
+ if (!NMFLAG(nmp, NONAMEDATTR)) {
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_OPENATTR);
+ nfsm_chain_add_32(error, &nmreq, 0);
+ }
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request_async(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
+ vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
+ if (!error)
+ error = nfs_request_async_finish(req, &nmrep, &xid, &status);
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ nfsmout_if(error);
+ NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
+ error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL, NULL);
+ nfsmout_if(error);
+ if (!NMFLAG(nmp, NONAMEDATTR)) {
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_OPENATTR);
+ if (error == ENOENT)
+ error = 0;
+ /* [sigh] some implementations return "illegal" error for unsupported ops */
+ if (error || !NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_NAMED_ATTR)) {
+ nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_NAMED_ATTR;
+ } else {
+ nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_NAMED_ATTR;
+ }
+ } else {
+ nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_NAMED_ATTR;
+ }
+ if (NMFLAG(nmp, NOACL)) /* make sure ACL support is turned off */
+ nmp->nm_fsattr.nfsa_flags &= ~NFS_FSFLAG_ACL;
+ if (NMFLAG(nmp, ACLONLY) && !(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL))
+ NFS_BITMAP_CLR(nmp->nm_flags, NFS_MFLAG_ACLONLY);
+ if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_FH_EXPIRE_TYPE)) {
+ uint32_t fhtype = ((nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_FHTYPE_MASK) >> NFS_FSFLAG_FHTYPE_SHIFT);
+ if (fhtype != NFS_FH_PERSISTENT)
+ printf("nfs: warning: non-persistent file handles! for %s\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ }
+
+ /* make sure it's a directory */
+ if (!NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_TYPE) || (nvattr.nva_type != VDIR)) {
+ error = ENOTDIR;
+ goto nfsmout;
+ }
+
+ /* save the NFS fsid */
+ nmp->nm_fsid = nvattr.nva_fsid;
+
+ /* create the root node */
+ error = nfs_nget(nmp->nm_mountp, NULL, NULL, dirfh.fh_data, dirfh.fh_len, &nvattr, &xid, rq.r_auth, NG_MARKROOT, npp);
+ nfsmout_if(error);
+
+ if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL)
+ vfs_setextendedsecurity(nmp->nm_mountp);
+
+ /* adjust I/O sizes to server limits */
+ if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXREAD) && (nmp->nm_fsattr.nfsa_maxread > 0)) {
+ if (nmp->nm_fsattr.nfsa_maxread < (uint64_t)nmp->nm_rsize) {
+ nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread & ~(NFS_FABLKSIZE - 1);
+ if (nmp->nm_rsize == 0)
+ nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread;
+ }
+ }
+ if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXWRITE) && (nmp->nm_fsattr.nfsa_maxwrite > 0)) {
+ if (nmp->nm_fsattr.nfsa_maxwrite < (uint64_t)nmp->nm_wsize) {
+ nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite & ~(NFS_FABLKSIZE - 1);
+ if (nmp->nm_wsize == 0)
+ nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite;
+ }
+ }
+
+ /* set up lease renew timer */
+ nmp->nm_renew_timer = thread_call_allocate(nfs4_renew_timer, nmp);
+ interval = nmp->nm_fsattr.nfsa_lease / 2;
+ if (interval < 1)
+ interval = 1;
+ nfs_interval_timer_start(nmp->nm_renew_timer, interval * 1000);
+
+nfsmout:
+ if (fspath.np_components) {
+ for (comp=0; comp < fspath.np_compcount; comp++)
+ if (fspath.np_components[comp])
+ FREE(fspath.np_components[comp], M_TEMP);
+ FREE(fspath.np_components, M_TEMP);
+ }
+ NVATTR_CLEANUP(&nvattr);
+ nfs_fs_locations_cleanup(&nfsls);
+ if (*npp)
+ nfs_node_unlock(*npp);
+ nfsm_chain_cleanup(&nmreq);
+ nfsm_chain_cleanup(&nmrep);
+ return (error);
+}
+
+/*
+ * Thread to handle initial NFS mount connection.
+ */
+void
+nfs_mount_connect_thread(void *arg, __unused wait_result_t wr)
+{
+ struct nfsmount *nmp = arg;
+ int error = 0, savederror = 0, slpflag = (NMFLAG(nmp, INTR) ? PCATCH : 0);
+ int done = 0, timeo, tries, maxtries;
+
+ if (NM_OMFLAG(nmp, MNTQUICK)) {
+ timeo = 8;
+ maxtries = 1;
+ } else {
+ timeo = 30;
+ maxtries = 2;
+ }
+
+ for (tries = 0; tries < maxtries; tries++) {
+ error = nfs_connect(nmp, 1, timeo);
+ switch (error) {
+ case ETIMEDOUT:
+ case EAGAIN:
+ case EPIPE:
+ case EADDRNOTAVAIL:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ENETRESET:
+ case ECONNABORTED:
+ case ECONNRESET:
+ case EISCONN:
+ case ENOTCONN:
+ case ESHUTDOWN:
+ case ECONNREFUSED:
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ /* just keep retrying on any of these errors */
+ break;
+ case 0:
+ default:
+ /* looks like we got an answer... */
+ done = 1;
+ break;
+ }
+
+ /* save the best error */
+ if (nfs_connect_error_class(error) >= nfs_connect_error_class(savederror))
+ savederror = error;
+ if (done) {
+ error = savederror;
+ break;
+ }
+
+ /* pause before next attempt */
+ if ((error = nfs_sigintr(nmp, NULL, current_thread(), 0)))
+ break;
+ error = tsleep(nmp, PSOCK|slpflag, "nfs_mount_connect_retry", 2*hz);
+ if (error && (error != EWOULDBLOCK))
+ break;
+ error = savederror;
+ }
+
+ /* update status of mount connect */
+ lck_mtx_lock(&nmp->nm_lock);
+ if (!nmp->nm_mounterror)
+ nmp->nm_mounterror = error;
+ nmp->nm_state &= ~NFSSTA_MOUNT_THREAD;
+ lck_mtx_unlock(&nmp->nm_lock);
+ wakeup(&nmp->nm_nss);
+}
+
+int
+nfs_mount_connect(struct nfsmount *nmp)
+{
+ int error = 0, slpflag;
+ thread_t thd;
+ struct timespec ts = { 2, 0 };
+
+ /*
+ * Set up the socket. Perform initial search for a location/server/address to
+ * connect to and negotiate any unspecified mount parameters. This work is
+ * done on a kernel thread to satisfy reserved port usage needs.
+ */
+ slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
+ lck_mtx_lock(&nmp->nm_lock);
+ /* set flag that the thread is running */
+ nmp->nm_state |= NFSSTA_MOUNT_THREAD;
+ if (kernel_thread_start(nfs_mount_connect_thread, nmp, &thd) != KERN_SUCCESS) {
+ nmp->nm_state &= ~NFSSTA_MOUNT_THREAD;
+ nmp->nm_mounterror = EIO;
+ printf("nfs mount %s start socket connect thread failed\n", vfs_statfs(nmp->nm_mountp)->f_mntfromname);
+ } else {
+ thread_deallocate(thd);
+ }
+
+ /* wait until mount connect thread is finished/gone */
+ while (nmp->nm_state & NFSSTA_MOUNT_THREAD) {
+ error = msleep(&nmp->nm_nss, &nmp->nm_lock, slpflag|PSOCK, "nfsconnectthread", &ts);
+ if ((error && (error != EWOULDBLOCK)) || ((error = nfs_sigintr(nmp, NULL, current_thread(), 1)))) {
+ /* record error */
+ if (!nmp->nm_mounterror)
+ nmp->nm_mounterror = error;
+ /* signal the thread that we are aborting */
+ nmp->nm_sockflags |= NMSOCK_UNMOUNT;
+ if (nmp->nm_nss)
+ wakeup(nmp->nm_nss);
+ /* and continue waiting on it to finish */
+ slpflag = 0;
+ }
+ }
+ lck_mtx_unlock(&nmp->nm_lock);
+
+ /* grab mount connect status */
+ error = nmp->nm_mounterror;
+
+ return (error);
+}
+
+/*
+ * Common code to mount an NFS file system.
+ */
+int
+mountnfs(
+ char *xdrbuf,
+ mount_t mp,
+ vfs_context_t ctx,
+ vnode_t *vpp)
+{
+ struct nfsmount *nmp;
+ nfsnode_t np;
+ int error = 0;
+ struct vfsstatfs *sbp;
+ struct xdrbuf xb;
+ uint32_t i, val, vers = 0, minorvers, maxio, iosize, len;
+ uint32_t *mattrs;
+ uint32_t *mflags_mask;
+ uint32_t *mflags;
+ uint32_t argslength, attrslength;
+ struct nfs_location_index firstloc = { NLI_VALID, 0, 0, 0 };