X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..4a2492630c73add3c3aa8a805ba4ff343d4a58ea:/bsd/nfs/nfs_socket.c diff --git a/bsd/nfs/nfs_socket.c b/bsd/nfs/nfs_socket.c index 9b55ac48f..3e9aac142 100644 --- a/bsd/nfs/nfs_socket.c +++ b/bsd/nfs/nfs_socket.c @@ -84,6 +84,8 @@ #include #include +#include +#include #include #include @@ -189,6 +191,11 @@ static int nfs_reconnect __P((struct nfsreq *rep)); static void nfs_repbusy(struct nfsreq *rep); static struct nfsreq * nfs_repnext(struct nfsreq *rep); static void nfs_repdequeue(struct nfsreq *rep); + +/* XXX */ +boolean_t current_thread_aborted(void); +kern_return_t thread_terminate(thread_act_t); + #ifndef NFS_NOSERVER static int nfsrv_getstream __P((struct nfssvc_sock *,int)); @@ -349,20 +356,139 @@ nfsdup(struct nfsreq *rep) } #endif /* NFSDIAG */ + +/* + * attempt to bind a socket to a reserved port + */ +static int +nfs_bind_resv(struct nfsmount *nmp) +{ + struct socket *so = nmp->nm_so; + struct sockaddr_in sin; + int error; + u_short tport; + + if (!so) + return (EINVAL); + + sin.sin_len = sizeof (struct sockaddr_in); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + tport = IPPORT_RESERVED - 1; + sin.sin_port = htons(tport); + + while (((error = sobind(so, (struct sockaddr *) &sin)) == EADDRINUSE) && + (--tport > IPPORT_RESERVED / 2)) + sin.sin_port = htons(tport); + return (error); +} + +/* + * variables for managing the nfs_bind_resv_thread + */ +int nfs_resv_mounts = 0; +static int nfs_bind_resv_thread_state = 0; +#define NFS_BIND_RESV_THREAD_STATE_INITTED 1 +#define NFS_BIND_RESV_THREAD_STATE_RUNNING 2 +static struct slock nfs_bind_resv_slock; +struct nfs_bind_resv_request { + TAILQ_ENTRY(nfs_bind_resv_request) brr_chain; + struct nfsmount *brr_nmp; + int brr_error; +}; +static TAILQ_HEAD(, nfs_bind_resv_request) nfs_bind_resv_request_queue; + +/* + * thread to handle any reserved port bind requests + */ +static void +nfs_bind_resv_thread(void) +{ + struct nfs_bind_resv_request *brreq; + boolean_t funnel_state; + + funnel_state = thread_funnel_set(network_flock, TRUE); + nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_RUNNING; + + while (nfs_resv_mounts > 0) { + simple_lock(&nfs_bind_resv_slock); + while ((brreq = TAILQ_FIRST(&nfs_bind_resv_request_queue))) { + TAILQ_REMOVE(&nfs_bind_resv_request_queue, brreq, brr_chain); + simple_unlock(&nfs_bind_resv_slock); + brreq->brr_error = nfs_bind_resv(brreq->brr_nmp); + wakeup(brreq); + simple_lock(&nfs_bind_resv_slock); + } + simple_unlock(&nfs_bind_resv_slock); + (void)tsleep((caddr_t)&nfs_bind_resv_request_queue, PSOCK, + "nfs_bind_resv_request_queue", 0); + } + + nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_INITTED; + (void) thread_funnel_set(network_flock, funnel_state); + (void) thread_terminate(current_act()); +} + +int +nfs_bind_resv_thread_wake(void) +{ + if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_RUNNING) + return (EIO); + wakeup(&nfs_bind_resv_request_queue); + return (0); +} + +/* + * underprivileged procs call this to request nfs_bind_resv_thread + * to perform the reserved port binding for them. + */ +static int +nfs_bind_resv_nopriv(struct nfsmount *nmp) +{ + struct nfs_bind_resv_request brreq; + int error; + + if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_RUNNING) { + if (nfs_bind_resv_thread_state < NFS_BIND_RESV_THREAD_STATE_INITTED) { + simple_lock_init(&nfs_bind_resv_slock); + TAILQ_INIT(&nfs_bind_resv_request_queue); + nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_INITTED; + } + kernel_thread(kernel_task, nfs_bind_resv_thread); + nfs_bind_resv_thread_state = NFS_BIND_RESV_THREAD_STATE_RUNNING; + } + + brreq.brr_nmp = nmp; + brreq.brr_error = 0; + + simple_lock(&nfs_bind_resv_slock); + TAILQ_INSERT_TAIL(&nfs_bind_resv_request_queue, &brreq, brr_chain); + simple_unlock(&nfs_bind_resv_slock); + + error = nfs_bind_resv_thread_wake(); + if (error) { + TAILQ_REMOVE(&nfs_bind_resv_request_queue, &brreq, brr_chain); + /* Note: we might be able to simply restart the thread */ + return (error); + } + + (void) tsleep((caddr_t)&brreq, PSOCK, "nfsbindresv", 0); + + return (brreq.brr_error); +} + /* * Initialize sockets and congestion for a new NFS connection. * We do not free the sockaddr if error. */ int nfs_connect(nmp, rep) - register struct nfsmount *nmp; + struct nfsmount *nmp; struct nfsreq *rep; { - register struct socket *so; + struct socket *so; int s, error, rcvreserve, sndreserve; struct sockaddr *saddr; - struct sockaddr_in sin; - u_short tport; thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL); nmp->nm_so = (struct socket *)0; @@ -379,18 +505,22 @@ nfs_connect(nmp, rep) * Some servers require that the client port be a reserved port number. */ if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) { - sin.sin_len = sizeof (struct sockaddr_in); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - tport = IPPORT_RESERVED - 1; - sin.sin_port = htons(tport); - - while ((error = sobind(so, (struct sockaddr *) &sin) == EADDRINUSE) && - (--tport > IPPORT_RESERVED / 2)) - sin.sin_port = htons(tport); - if (error) { - goto bad; + struct proc *p; + /* + * sobind() requires current_proc() to have superuser privs. + * If this bind is part of a reconnect, and the current proc + * doesn't have superuser privs, we hand the sobind() off to + * a kernel thread to process. + */ + if ((nmp->nm_state & NFSSTA_MOUNTED) && + (p = current_proc()) && suser(p->p_ucred, &p->p_acflag)) { + /* request nfs_bind_resv_thread() to do bind */ + error = nfs_bind_resv_nopriv(nmp); + } else { + error = nfs_bind_resv(nmp); } + if (error) + goto bad; } /* @@ -541,6 +671,13 @@ nfs_reconnect(rep) if (error == EIO) return (EIO); nfs_down(rep, "can not connect", error); + if (!(nmp->nm_state & NFSSTA_MOUNTED)) { + /* we're not yet completely mounted and */ + /* we can't reconnect, so we fail */ + return (error); + } + if ((error = nfs_sigintr(rep->r_nmp, rep, rep->r_procp))) + return (error); (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0); } @@ -1908,6 +2045,13 @@ nfs_timer(arg) rep->r_lastmsg + nmp->nm_tprintf_delay < now.tv_sec) { rep->r_lastmsg = now.tv_sec; nfs_down(rep, "not responding", 0); + if (!(nmp->nm_state & NFSSTA_MOUNTED)) { + /* we're not yet completely mounted and */ + /* we can't complete an RPC, so we fail */ + nfsstats.rpctimeouts++; + nfs_softterm(rep); + continue; + } } if (rep->r_rtt >= 0) { rep->r_rtt++; @@ -2306,7 +2450,7 @@ nfsrv_rcv(so, arg, waitflag) register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg; register struct mbuf *m; struct mbuf *mp, *mhck; - struct sockaddr *nam=0; + struct sockaddr *nam; struct uio auio; int flags, ns_nflag=0, error; struct sockaddr_in *sin; @@ -2372,8 +2516,9 @@ nfsrv_rcv(so, arg, waitflag) } else { do { auio.uio_resid = 1000000000; - flags = MSG_DONTWAIT; + flags = MSG_DONTWAIT | MSG_NEEDSA; nam = 0; + mp = 0; error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags); @@ -2384,7 +2529,6 @@ nfsrv_rcv(so, arg, waitflag) sin = mtod(mhck, struct sockaddr_in *); bcopy(nam, sin, sizeof(struct sockaddr_in)); mhck->m_hdr.mh_len = sizeof(struct sockaddr_in); - FREE(nam, M_SONAME); m = mhck; m->m_next = mp; @@ -2397,6 +2541,9 @@ nfsrv_rcv(so, arg, waitflag) slp->ns_recend = m; m->m_nextpkt = (struct mbuf *)0; } + if (nam) { + FREE(nam, M_SONAME); + } if (error) { if ((so->so_proto->pr_flags & PR_CONNREQUIRED) && error != EWOULDBLOCK) {