+ if (fa && fa->__actions) {
+ struct fdop *op;
+ int fd;
+ for (op = fa->__actions; op->next; op = op->next);
+ for (; op; op = op->prev) {
+ /* It's possible that a file operation would clobber
+ * the pipe fd used for synchronizing with the
+ * parent. To avoid that, we dup the pipe onto
+ * an unoccupied fd. */
+ if (op->fd == p) {
+ ret = __syscall(SYS_dup, p);
+ if (ret < 0) goto fail;
+ __syscall(SYS_close, p);
+ p = ret;
+ }
+ switch(op->cmd) {
+ case FDOP_CLOSE:
+ if ((ret=__syscall(SYS_close, op->fd)))
+ goto fail;
+ break;
+ case FDOP_DUP2:
+ if ((ret=__syscall(SYS_dup2, op->srcfd, op->fd))<0)
+ goto fail;
+ break;
+ case FDOP_OPEN:
+ fd = __syscall(SYS_open, op->path,
+ op->oflag | O_LARGEFILE, op->mode);
+ if ((ret=fd) < 0) goto fail;
+ if (fd != op->fd) {
+ if ((ret=__syscall(SYS_dup2, fd, op->fd))<0)
+ goto fail;
+ __syscall(SYS_close, fd);
+ }
+ break;
+ }
+ }
+ }
+
+ /* Close-on-exec flag may have been lost if we moved the pipe
+ * to a different fd. We don't use F_DUPFD_CLOEXEC above because
+ * it would fail on older kernels and atomicity is not needed --
+ * in this process there are no threads or signal handlers. */
+ __syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC);
+
+ pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
+ ? &attr->__mask : &args->oldmask, 0);