]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_socket.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_socket.c
index 9b55ac48f845fcbf2e5f2e868f7518b1da5e7b5f..3e9aac142c44aac96a3b65603da0b8aeb5e63a7f 100644 (file)
@@ -84,6 +84,8 @@
 
 #include <sys/time.h>
 #include <kern/clock.h>
+#include <kern/task.h>
+#include <kern/thread.h>
 #include <sys/user.h>
 
 #include <netinet/in.h>
@@ -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) {