/* * Note: This routine is single threaded * Protected by FIFOOPEN flag (i.e. flk_lock is not held) * Upon successful completion, the original fifo is unlocked * and FIFOOPEN is cleared for the original vpp. * The new fifo returned has FIFOOPEN set. */ static int fifo_connld(struct vnode **vpp, int flag, cred_t *crp) { struct vnode *vp1; struct vnode *vp2; struct fifonode *oldfnp; struct fifonode *fn_dest; int error; struct file *filep; struct fifolock *fn_lock; cred_t *c; /* * Get two vnodes that will represent the pipe ends for the new pipe. */ makepipe(&vp1, &vp2); /* * Allocate a file descriptor and file pointer for one of the pipe * ends. The file descriptor will be used to send that pipe end to * the process on the other end of this stream. Note that we get * the file structure only, there is no file list entry allocated. */ if (error = falloc(vp1, FWRITE|FREAD, &filep, NULL)) { VN_RELE(vp1); VN_RELE(vp2); return (error); } mutex_exit(&filep->f_tlock); oldfnp = VTOF(*vpp); fn_lock = oldfnp->fn_lock; fn_dest = oldfnp->fn_dest; /* * Create two new stream heads and attach them to the two vnodes for * the new pipe. */ if ((error = fifo_stropen(&vp1, FREAD|FWRITE, filep->f_cred, 0, 0)) != 0 || (error = fifo_stropen(&vp2, flag, filep->f_cred, 0, 0)) != 0) { #if DEBUG cmn_err(CE_NOTE, "fifo stropen failed error 0x%x", error); #endif /* * this will call fifo_close and VN_RELE on vp1 */ (void) closef(filep); VN_RELE(vp2); return (error); } /* * twist the ends of the pipe together */ strmate(vp1, vp2); /* * Set our end to busy in open * Note: Don't need lock around this because we're the only * one who knows about it */ VTOF(vp2)->fn_flag |= FIFOOPEN; mutex_enter(&fn_lock->flk_lock); fn_dest->fn_flag |= FIFOSEND; /* * check to make sure neither end of pipe has gone away */ if (!(fn_dest->fn_flag & FIFOISOPEN)) { error = ENXIO; fn_dest->fn_flag &= ~FIFOSEND; mutex_exit(&fn_lock->flk_lock); /* * this will call fifo_close and VN_RELE on vp1 */ goto out; } mutex_exit(&fn_lock->flk_lock); /* * Tag the sender's credential on the pipe descriptor. */ crhold(VTOF(vp1)->fn_pcredp = crp); VTOF(vp1)->fn_cpid = curproc->p_pid; /* * send the file descriptor to other end of pipe */ if (error = do_sendfp((*vpp)->v_stream, filep, crp)) { mutex_enter(&fn_lock->flk_lock); fn_dest->fn_flag &= ~FIFOSEND; mutex_exit(&fn_lock->flk_lock); /* * this will call fifo_close and VN_RELE on vp1 */ goto out; } mutex_enter(&fn_lock->flk_lock); /* * Wait for other end to receive file descriptor * FIFOCLOSE indicates that one or both sides of the pipe * have gone away. */ while ((fn_dest->fn_flag & (FIFOCLOSE | FIFOSEND)) == FIFOSEND) { if (!cv_wait_sig(&oldfnp->fn_wait_cv, &fn_lock->flk_lock)) { error = EINTR; fn_dest->fn_flag &= ~FIFOSEND; mutex_exit(&fn_lock->flk_lock); goto out; } } /* * If either end of pipe has gone away and the other end did not * receive pipe, reject the connld open */ if ((fn_dest->fn_flag & FIFOSEND)) { error = ENXIO; fn_dest->fn_flag &= ~FIFOSEND; mutex_exit(&fn_lock->flk_lock); goto out; } oldfnp->fn_flag &= ~FIFOOPEN; cv_broadcast(&oldfnp->fn_wait_cv); mutex_exit(&fn_lock->flk_lock); VN_RELE(*vpp); *vpp = vp2; (void) closef(filep); return (0); out: c = filep->f_cred; crhold(c); (void) closef(filep); VTOF(vp2)->fn_flag &= ~FIFOOPEN; (void) fifo_close(vp2, flag, 1, (offset_t)0, c, NULL); crfree(c); VN_RELE(vp2); return (error); }
/* * pipe(2) system call. * Create a pipe by connecting two streams together. Associate * each end of the pipe with a vnode, a file descriptor and * one of the streams. */ longlong_t pipe() { vnode_t *vp1, *vp2; struct file *fp1, *fp2; int error = 0; int fd1, fd2; rval_t r; /* * Allocate and initialize two vnodes. */ makepipe(&vp1, &vp2); /* * Allocate and initialize two file table entries and two * file pointers. Each file pointer is open for read and * write. */ if (error = falloc(vp1, FWRITE|FREAD, &fp1, &fd1)) { VN_RELE(vp1); VN_RELE(vp2); return ((longlong_t)set_errno(error)); } if (error = falloc(vp2, FWRITE|FREAD, &fp2, &fd2)) goto out2; /* * Create two stream heads and attach to each vnode. */ if (error = fifo_stropen(&vp1, FWRITE|FREAD, fp1->f_cred, 0, 0)) goto out; if (error = fifo_stropen(&vp2, FWRITE|FREAD, fp2->f_cred, 0, 0)) { (void) VOP_CLOSE(vp1, FWRITE|FREAD, 1, (offset_t)0, fp1->f_cred); goto out; } strmate(vp1, vp2); VTOF(vp1)->fn_ino = VTOF(vp2)->fn_ino = fifogetid(); /* * Now fill in the entries that falloc reserved */ mutex_exit(&fp1->f_tlock); mutex_exit(&fp2->f_tlock); setf(fd1, fp1); setf(fd2, fp2); /* * Return the file descriptors to the user. They now * point to two different vnodes which have different * stream heads. */ r.r_val1 = fd1; r.r_val2 = fd2; return (r.r_vals); out: unfalloc(fp2); setf(fd2, NULL); out2: unfalloc(fp1); setf(fd1, NULL); VN_RELE(vp1); VN_RELE(vp2); return ((longlong_t)set_errno(error)); }
/* * Stream a pipe/FIFO. * The FIFOCONNLD flag is used when CONNLD has been pushed on the stream. * If the flag is set, a new vnode is created by calling fifo_connld(). * Connld logic was moved to fifo_connld() to speed up the open * operation, simplify the connld/fifo interaction, and remove inherent * race conditions between the connld module and fifos. * This routine is single threaded for two reasons. * 1) connld requests are synchronous; that is, they must block * until the server does an I_RECVFD (oh, well). Single threading is * the simplest way to accomplish this. * 2) fifo_close() must not send M_HANGUP or M_ERROR while we are * in stropen. Stropen() has a tendency to reset things and * we would like streams to remember that a hangup occurred. */ int fifo_stropen(vnode_t **vpp, int flag, cred_t *crp, int dotwist, int lockheld) { int error = 0; vnode_t *oldvp = *vpp; fifonode_t *fnp = VTOF(*vpp); dev_t pdev = 0; int firstopen = 0; fifolock_t *fn_lock; fn_lock = fnp->fn_lock; if (!lockheld) mutex_enter(&fn_lock->flk_lock); ASSERT(MUTEX_HELD(&fnp->fn_lock->flk_lock)); /* * FIFO is in the process of opening. Wait for it * to complete before starting another open on it * This prevents races associated with connld open */ while (fnp->fn_flag & FIFOOPEN) { if (!cv_wait_sig(&fnp->fn_wait_cv, &fn_lock->flk_lock)) { fifo_cleanup(oldvp, flag); if (!lockheld) mutex_exit(&fn_lock->flk_lock); return (EINTR); } } /* * The other end of the pipe is almost closed so * reject any other open on this end of the pipe * This only happens with a pipe mounted under namefs */ if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE)) { fifo_cleanup(oldvp, flag); cv_broadcast(&fnp->fn_wait_cv); if (!lockheld) mutex_exit(&fn_lock->flk_lock); return (ENXIO); } fnp->fn_flag |= FIFOOPEN; /* * can't allow close to happen while we are * in the middle of stropen(). * M_HANGUP and M_ERROR could leave the stream in a strange state */ while (fn_lock->flk_ocsync) cv_wait(&fn_lock->flk_wait_cv, &fn_lock->flk_lock); fn_lock->flk_ocsync = 1; if (fnp->fn_flag & FIFOCONNLD) { /* * This is a reopen, so we should release the fifo lock * just in case some strange module pushed on connld * has some odd side effect. * Note: this stropen is on the oldvp. It will * have no impact on the connld vp returned and * strclose() will only be called when we release * flk_ocsync */ mutex_exit(&fn_lock->flk_lock); if ((error = stropen(oldvp, &pdev, flag, crp)) != 0) { mutex_enter(&fn_lock->flk_lock); fifo_cleanup(oldvp, flag); fn_lock->flk_ocsync = 0; cv_broadcast(&fn_lock->flk_wait_cv); goto out; } /* * streams open done, allow close on other end if * required. Do this now.. it could * be a very long time before fifo_connld returns. */ mutex_enter(&fn_lock->flk_lock); /* * we need to fake an open here so that if this * end of the pipe closes, we don't loose the * stream head (kind of like single threading * open and close for this end of the pipe) * We'll need to call fifo_close() to do clean * up in case this end of the pipe was closed * down while we were in fifo_connld() */ ASSERT(fnp->fn_open > 0); fnp->fn_open++; fn_lock->flk_ocsync = 0; cv_broadcast(&fn_lock->flk_wait_cv); mutex_exit(&fn_lock->flk_lock); /* * Connld has been pushed onto the pipe * Create new pipe on behalf of connld */ if (error = fifo_connld(vpp, flag, crp)) { (void) fifo_close(oldvp, flag, 1, 0, crp, NULL); mutex_enter(&fn_lock->flk_lock); goto out; } /* * undo fake open. We need to call fifo_close * because some other thread could have done * a close and detach of the named pipe while * we were in fifo_connld(), so * we want to make sure the close completes (yuk) */ (void) fifo_close(oldvp, flag, 1, 0, crp, NULL); /* * fifo_connld has changed the vp, so we * need to re-initialize locals */ fnp = VTOF(*vpp); fn_lock = fnp->fn_lock; mutex_enter(&fn_lock->flk_lock); } else { /* * release lock in case there are modules pushed that * could have some strange side effect */ mutex_exit(&fn_lock->flk_lock); /* * If this is the first open of a fifo (dotwist * will be non-zero) we will need to twist the queues. */ if (oldvp->v_stream == NULL) firstopen = 1; /* * normal open of pipe/fifo */ if ((error = stropen(oldvp, &pdev, flag, crp)) != 0) { mutex_enter(&fn_lock->flk_lock); fifo_cleanup(oldvp, flag); ASSERT(fnp->fn_open != 0 || oldvp->v_stream == NULL); fn_lock->flk_ocsync = 0; cv_broadcast(&fn_lock->flk_wait_cv); goto out; } mutex_enter(&fn_lock->flk_lock); /* * twist the ends of the fifo together */ if (dotwist && firstopen) strmate(*vpp, *vpp); /* * Show that this open has succeeded * and allow closes or other opens to proceed */ fnp->fn_open++; fn_lock->flk_ocsync = 0; cv_broadcast(&fn_lock->flk_wait_cv); } out: fnp->fn_flag &= ~FIFOOPEN; if (error == 0) { fnp->fn_flag |= FIFOISOPEN; /* * If this is a FIFO and has the close flag set * and there are now writers, clear the close flag * Note: close flag only gets set when last writer * on a FIFO goes away. */ if (((fnp->fn_flag & (ISPIPE|FIFOCLOSE)) == FIFOCLOSE) && fnp->fn_wcnt > 0) fnp->fn_flag &= ~FIFOCLOSE; } cv_broadcast(&fnp->fn_wait_cv); if (!lockheld) mutex_exit(&fn_lock->flk_lock); return (error); }