#include <sys/time.h>
#include <kern/clock.h>
+#include <kern/task.h>
+#include <kern/thread.h>
#include <sys/user.h>
#include <netinet/in.h>
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));
}
#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;
* 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;
}
/*
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);
}
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++;
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;
} 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);
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;
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) {