(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
spawni.c
       1  /* spawn a new process running an executable.  Hurd version.
       2     Copyright (C) 2001-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; see the file COPYING.LIB.  If
      17     not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <errno.h>
      20  #include <fcntl.h>
      21  #include <paths.h>
      22  #include <spawn.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <stdio.h>
      26  #include <unistd.h>
      27  #include <hurd.h>
      28  #include <hurd/signal.h>
      29  #include <hurd/fd.h>
      30  #include <hurd/id.h>
      31  #include <hurd/lookup.h>
      32  #include <hurd/resource.h>
      33  #include <assert.h>
      34  #include <argz.h>
      35  #include "spawn_int.h"
      36  
      37  /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
      38     Before running the process perform the actions described in FILE-ACTIONS. */
      39  int
      40  __spawni (pid_t *pid, const char *file,
      41  	  const posix_spawn_file_actions_t *file_actions,
      42  	  const posix_spawnattr_t *attrp,
      43  	  char *const argv[], char *const envp[],
      44  	  int xflags)
      45  {
      46    pid_t new_pid;
      47    char *path, *p, *name;
      48    char *concat_name = NULL;
      49    const char *relpath, *abspath;
      50    int res;
      51    size_t len;
      52    size_t pathlen;
      53    short int flags;
      54  
      55    /* The generic POSIX.1 implementation of posix_spawn uses fork and exec.
      56       In traditional POSIX systems (Unix, Linux, etc), the only way to
      57       create a new process is by fork, which also copies all the things from
      58       the parent process that will be immediately wiped and replaced by the
      59       exec.
      60  
      61       This Hurd implementation works by doing an exec on a fresh task,
      62       without ever doing all the work of fork.  The only work done by fork
      63       that remains visible after an exec is registration with the proc
      64       server, and the inheritance of various values and ports.  All those
      65       inherited values and ports are what get collected up and passed in the
      66       file_exec_paths RPC by an exec call.  So we do the proc server
      67       registration here, following the model of fork (see fork.c).  We then
      68       collect up the inherited values and ports from this (parent) process
      69       following the model of exec (see hurd/hurdexec.c), modify or replace each
      70       value that fork would (plus the specific changes demanded by ATTRP and
      71       FILE_ACTIONS), and make the file_exec_paths RPC on the requested
      72       executable file with the child process's task port rather than our own.
      73       This should be indistinguishable from the fork + exec implementation,
      74       except that all errors will be detected here (in the parent process)
      75       and return proper errno codes rather than the child dying with 127.
      76  
      77       XXX The one exception to this supposed indistinguishableness is that
      78       when posix_spawn_file_actions_addopen has been used, the parent
      79       process can do various filesystem RPCs on the child's behalf, rather
      80       than the child process doing it.  If these block due to a broken or
      81       malicious filesystem server or just a blocked network fs or a serial
      82       port waiting for carrier detect (!!), the parent's posix_spawn call
      83       can block arbitrarily rather than just the child blocking.  Possible
      84       solutions include:
      85       * punt to plain fork + exec implementation if addopen was used
      86       ** easy to do
      87       ** gives up all benefits of this implementation in that case
      88       * if addopen was used, don't do any file actions at all here;
      89         instead, exec an installed helper program e.g.:
      90  	/libexec/spawn-helper close 3 dup2 1 2 open 0 /file 0x123 0666 exec /bin/foo foo a1 a2
      91       ** extra exec might be more or less overhead than fork
      92       * could do some weird half-fork thing where the child would inherit
      93         our vm and run some code here, but not do the full work of fork
      94  
      95       XXX Actually, the parent opens the executable file on behalf of
      96       the child, and that has all the same issues.
      97  
      98       I am favoring the half-fork solution.  That is, we do task_create with
      99       vm inheritance, and we setjmp/longjmp the child like fork does.  But
     100       rather than all the fork hair, the parent just packs up init/dtable
     101       ports and does a single IPC to a receive right inserted in the child.  */
     102  
     103    error_t err;
     104    task_t task;
     105    file_t execfile;
     106    process_t proc;
     107    auth_t auth;
     108    int ints[INIT_INT_MAX];
     109    file_t *dtable;
     110    unsigned int dtablesize, orig_dtablesize, i;
     111    struct hurd_port **dtable_cells;
     112    char *dtable_cloexec;
     113    struct hurd_userlink *ulink_dtable = NULL;
     114    struct hurd_sigstate *ss;
     115  
     116    /* Child current working dir */
     117    file_t ccwdir = MACH_PORT_NULL;
     118  
     119    /* For POSIX_SPAWN_RESETIDS, this reauthenticates our root/current
     120       directory ports with the new AUTH port.  */
     121    file_t rcrdir = MACH_PORT_NULL, rcwdir = MACH_PORT_NULL;
     122    error_t reauthenticate (int which, file_t *result)
     123      {
     124        error_t err;
     125        mach_port_t ref;
     126        if (*result != MACH_PORT_NULL)
     127  	return 0;
     128        ref = __mach_reply_port ();
     129        if (which == INIT_PORT_CWDIR && ccwdir != MACH_PORT_NULL)
     130  	{
     131  	  err = __io_reauthenticate (ccwdir, ref, MACH_MSG_TYPE_MAKE_SEND);
     132  	  if (!err)
     133  	    err = __auth_user_authenticate (auth,
     134  					    ref, MACH_MSG_TYPE_MAKE_SEND,
     135  					    result);
     136  	}
     137        else
     138  	err = HURD_PORT_USE
     139  	  (&_hurd_ports[which],
     140  	   ({
     141  	     err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND);
     142  	     if (!err)
     143  	       err = __auth_user_authenticate (auth,
     144  					       ref, MACH_MSG_TYPE_MAKE_SEND,
     145  					       result);
     146  	     err;
     147  	   }));
     148        __mach_port_destroy (__mach_task_self (), ref);
     149        return err;
     150      }
     151  
     152    /* Reauthenticate one of our file descriptors for the child.  A null
     153       element of DTABLE_CELLS indicates a descriptor that was already
     154       reauthenticated, or was newly opened on behalf of the child.  */
     155    error_t reauthenticate_fd (int fd)
     156      {
     157        if (dtable_cells[fd] != NULL && dtable[fd] != MACH_PORT_NULL)
     158  	{
     159  	  file_t newfile;
     160  	  mach_port_t ref = __mach_reply_port ();
     161  	  error_t err = __io_reauthenticate (dtable[fd],
     162  					     ref, MACH_MSG_TYPE_MAKE_SEND);
     163  	  if (!err)
     164  	    err = __auth_user_authenticate (auth,
     165  					    ref, MACH_MSG_TYPE_MAKE_SEND,
     166  					    &newfile);
     167  	  __mach_port_destroy (__mach_task_self (), ref);
     168  	  if (err)
     169  	    return err;
     170  	  _hurd_port_free (dtable_cells[fd], &ulink_dtable[fd], dtable[fd]);
     171  	  dtable_cells[fd] = NULL;
     172  	  dtable[fd] = newfile;
     173  	}
     174        return 0;
     175      }
     176  
     177    /* These callbacks are for looking up file names on behalf of the child.  */
     178    error_t child_init_port (int which, error_t (*operate) (mach_port_t))
     179      {
     180        if (flags & POSIX_SPAWN_RESETIDS)
     181  	switch (which)
     182  	  {
     183  	  case INIT_PORT_AUTH:
     184  	    return (*operate) (auth);
     185  	  case INIT_PORT_CRDIR:
     186  	    return (reauthenticate (INIT_PORT_CRDIR, &rcrdir)
     187  		    ?: (*operate) (rcrdir));
     188  	  case INIT_PORT_CWDIR:
     189  	    return (reauthenticate (INIT_PORT_CWDIR, &rcwdir)
     190  		    ?: (*operate) (rcwdir));
     191  	  }
     192        else
     193  	switch (which)
     194  	  {
     195  	  case INIT_PORT_CWDIR:
     196  	    if (ccwdir != MACH_PORT_NULL)
     197  	      return (*operate) (ccwdir);
     198  	    break;
     199  	  }
     200        assert (which != INIT_PORT_PROC);
     201        return _hurd_ports_use (which, operate);
     202      }
     203    file_t child_fd (int fd)
     204      {
     205        if ((unsigned int) fd < dtablesize && dtable[fd] != MACH_PORT_NULL)
     206  	{
     207  	  if (flags & POSIX_SPAWN_RESETIDS)
     208  	    {
     209  	      /* Reauthenticate this descriptor right now,
     210  		 since it is going to be used on behalf of the child.  */
     211  	      errno = reauthenticate_fd (fd);
     212  	      if (errno)
     213  		return MACH_PORT_NULL;
     214  	    }
     215  	  __mach_port_mod_refs (__mach_task_self (), dtable[fd],
     216  				MACH_PORT_RIGHT_SEND, +1);
     217  	  return dtable[fd];
     218  	}
     219        return __hurd_fail (EBADF), MACH_PORT_NULL;
     220      }
     221    inline error_t child_lookup (const char *file, int oflag, mode_t mode,
     222  			       file_t *result)
     223      {
     224        return __hurd_file_name_lookup (&child_init_port, &child_fd, 0,
     225  				      file, oflag, mode, result);
     226      }
     227    auto error_t child_chdir (const char *name)
     228      {
     229        file_t new_ccwdir;
     230  
     231        /* Append trailing "/." to directory name to force ENOTDIR if
     232  	 it's not a directory and EACCES if we don't have search
     233  	 permission.  */
     234        len = strlen (name);
     235        const char *lookup = name;
     236        if (len >= 2 && name[len - 2] == '/' && name[len - 1] == '.')
     237  	lookup = name;
     238        else if (len == 0)
     239  	/* Special-case empty file name according to POSIX.  */
     240  	return __hurd_fail (ENOENT);
     241        else
     242  	{
     243  	  char *n = alloca (len + 3);
     244  	  memcpy (n, name, len);
     245  	  n[len] = '/';
     246  	  n[len + 1] = '.';
     247  	  n[len + 2] = '\0';
     248  	  lookup = n;
     249  	}
     250  
     251        error_t err = child_lookup (lookup, 0, 0, &new_ccwdir);
     252        if (!err)
     253  	{
     254  	  if (ccwdir != MACH_PORT_NULL)
     255  	    __mach_port_deallocate (__mach_task_self (), ccwdir);
     256  	  ccwdir = new_ccwdir;
     257  	}
     258  
     259        return err;
     260      }
     261    inline error_t child_lookup_under (file_t startdir, const char *file,
     262  				     int oflag, mode_t mode, file_t *result)
     263      {
     264        error_t use_init_port (int which, error_t (*operate) (mach_port_t))
     265  	{
     266  	  return (which == INIT_PORT_CWDIR ? (*operate) (startdir)
     267  		  : child_init_port (which, operate));
     268  	}
     269  
     270        return __hurd_file_name_lookup (&use_init_port, &child_fd, 0,
     271  				      file, oflag, mode, result);
     272      }
     273    auto error_t child_fchdir (int fd)
     274      {
     275        file_t new_ccwdir;
     276        error_t err;
     277  
     278        if ((unsigned int)fd >= dtablesize
     279  	  || dtable[fd] == MACH_PORT_NULL)
     280  	return EBADF;
     281  
     282        /* We look up "." to force ENOTDIR if it's not a directory and EACCES if
     283           we don't have search permission.  */
     284        if (dtable_cells[fd] != NULL)
     285  	  err = HURD_PORT_USE (dtable_cells[fd],
     286  		    ({
     287  		      child_lookup_under (port, ".", O_NOTRANS, 0, &new_ccwdir);
     288  		     }));
     289        else
     290  	  err = child_lookup_under (dtable[fd], ".", O_NOTRANS, 0, &new_ccwdir);
     291  
     292        if (!err)
     293  	{
     294  	  if (ccwdir != MACH_PORT_NULL)
     295  	    __mach_port_deallocate (__mach_task_self (), ccwdir);
     296  	  ccwdir = new_ccwdir;
     297  	}
     298  
     299        return err;
     300      }
     301  
     302  
     303    /* Do this once.  */
     304    flags = attrp == NULL ? 0 : attrp->__flags;
     305  
     306    /* Generate the new process.  We create a task that does not inherit our
     307       memory, and then register it as our child like fork does.  See fork.c
     308       for comments about the sequencing of these proc operations.  */
     309  
     310    err = __task_create (__mach_task_self (),
     311  #ifdef KERN_INVALID_LEDGER
     312  		       NULL, 0,	/* OSF Mach */
     313  #endif
     314  		       0, &task);
     315    if (err)
     316      return __hurd_fail (err);
     317    // From here down we must deallocate TASK and PROC before returning.
     318    proc = MACH_PORT_NULL;
     319    auth = MACH_PORT_NULL;
     320    err = __USEPORT (PROC, __proc_task2pid (port, task, &new_pid));
     321    if (!err)
     322      err = __USEPORT (PROC, __proc_task2proc (port, task, &proc));
     323    if (!err)
     324      err = __USEPORT (PROC, __proc_child (port, task));
     325    if (err)
     326      goto out;
     327  
     328    /* Load up the ints to give the new program.  */
     329    memset (ints, 0, sizeof ints);
     330    ints[INIT_UMASK] = _hurd_umask;
     331    ints[INIT_TRACEMASK] = _hurdsig_traced;
     332  
     333    ss = _hurd_self_sigstate ();
     334  
     335  retry:
     336    assert (! __spin_lock_locked (&ss->critical_section_lock));
     337    __spin_lock (&ss->critical_section_lock);
     338  
     339    _hurd_sigstate_lock (ss);
     340    ints[INIT_SIGMASK] = ss->blocked;
     341    ints[INIT_SIGPENDING] = 0;
     342    ints[INIT_SIGIGN] = 0;
     343    /* Unless we were asked to reset all handlers to SIG_DFL,
     344       pass down the set of signals that were set to SIG_IGN.  */
     345    {
     346      struct sigaction *actions = _hurd_sigstate_actions (ss);
     347      if ((flags & POSIX_SPAWN_SETSIGDEF) == 0)
     348        for (i = 1; i < NSIG; ++i)
     349  	if (actions[i].sa_handler == SIG_IGN)
     350  	  ints[INIT_SIGIGN] |= __sigmask (i);
     351    }
     352  
     353    /* We hold the critical section lock until the exec has failed so that no
     354       signal can arrive between when we pack the blocked and ignored signals,
     355       and when the exec actually happens.  A signal handler could change what
     356       signals are blocked and ignored.  Either the change will be reflected
     357       in the exec, or the signal will never be delivered.  Setting the
     358       critical section flag avoids anything we call trying to acquire the
     359       sigstate lock.  */
     360  
     361    _hurd_sigstate_unlock (ss);
     362  
     363    /* Set signal mask.  */
     364    if ((flags & POSIX_SPAWN_SETSIGMASK) != 0)
     365      ints[INIT_SIGMASK] = attrp->__ss;
     366  
     367  #ifdef _POSIX_PRIORITY_SCHEDULING
     368    /* Set the scheduling algorithm and parameters.  */
     369  # error implement me
     370    if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
     371        == POSIX_SPAWN_SETSCHEDPARAM)
     372      {
     373        if (__sched_setparam (0, &attrp->__sp) == -1)
     374  	_exit (SPAWN_ERROR);
     375      }
     376    else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
     377      {
     378        if (__sched_setscheduler (0, attrp->__policy,
     379  				(flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
     380  				? &attrp->__sp : NULL) == -1)
     381  	_exit (SPAWN_ERROR);
     382      }
     383  #endif
     384  
     385    if (!err && (flags & POSIX_SPAWN_SETSID) != 0)
     386      err = __proc_setsid (proc);
     387  
     388    /* Set the process group ID.  */
     389    if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0)
     390      err = __proc_setpgrp (proc, new_pid, attrp->__pgrp);
     391  
     392    /* Set the effective user and group IDs.  */
     393    if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0)
     394      {
     395        /* We need a different auth port for the child.  */
     396  
     397        __mutex_lock (&_hurd_id.lock);
     398        err = _hurd_check_ids (); /* Get _hurd_id up to date.  */
     399        if (!err && _hurd_id.rid_auth == MACH_PORT_NULL)
     400  	{
     401  	  /* Set up _hurd_id.rid_auth.  This is a special auth server port
     402  	     which uses the real uid and gid (the first aux uid and gid) as
     403  	     the only effective uid and gid.  */
     404  
     405  	  if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1)
     406  	    /* We do not have a real UID and GID.  Lose, lose, lose!  */
     407  	    err = EGRATUITOUS;
     408  
     409  	  /* Create a new auth port using our real UID and GID (the first
     410  	     auxiliary UID and GID) as the only effective IDs.  */
     411  	  if (!err)
     412  	    err = __USEPORT (AUTH,
     413  			     __auth_makeauth (port,
     414  					      NULL, MACH_MSG_TYPE_COPY_SEND, 0,
     415  					      _hurd_id.aux.uids, 1,
     416  					      _hurd_id.aux.uids,
     417  					      _hurd_id.aux.nuids,
     418  					      _hurd_id.aux.gids, 1,
     419  					      _hurd_id.aux.gids,
     420  					      _hurd_id.aux.ngids,
     421  					      &_hurd_id.rid_auth));
     422  	}
     423        if (!err)
     424  	{
     425  	  /* Use the real-ID auth port in place of the normal one.  */
     426  	  assert (_hurd_id.rid_auth != MACH_PORT_NULL);
     427  	  auth = _hurd_id.rid_auth;
     428  	  __mach_port_mod_refs (__mach_task_self (), auth,
     429  				MACH_PORT_RIGHT_SEND, +1);
     430  	}
     431        __mutex_unlock (&_hurd_id.lock);
     432      }
     433    else
     434      /* Copy our existing auth port.  */
     435      err = __USEPORT (AUTH, __mach_port_mod_refs (__mach_task_self (),
     436  						 (auth = port),
     437  						 MACH_PORT_RIGHT_SEND, +1));
     438  
     439    if (err)
     440      {
     441        _hurd_critical_section_unlock (ss);
     442  
     443        if (err == EINTR)
     444  	{
     445  	  /* Got a signal while inside an RPC of the critical section, retry again */
     446  	  __mach_port_deallocate (__mach_task_self (), auth);
     447  	  auth = MACH_PORT_NULL;
     448  	  goto retry;
     449  	}
     450  
     451        goto out;
     452      }
     453  
     454    /* Pack up the descriptor table to give the new program.
     455       These descriptors will need to be reauthenticated below
     456       if POSIX_SPAWN_RESETIDS is set.  */
     457    __mutex_lock (&_hurd_dtable_lock);
     458    dtablesize = _hurd_dtablesize;
     459    orig_dtablesize = _hurd_dtablesize;
     460    dtable = __alloca (dtablesize * sizeof (dtable[0]));
     461    ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
     462    dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
     463    dtable_cloexec = __alloca (orig_dtablesize);
     464    for (i = 0; i < dtablesize; ++i)
     465      {
     466        struct hurd_fd *const d = _hurd_dtable[i];
     467        if (d == NULL)
     468  	{
     469  	  dtable[i] = MACH_PORT_NULL;
     470  	  dtable_cells[i] = NULL;
     471  	  continue;
     472  	}
     473        /* Note that this might return MACH_PORT_NULL.  */
     474        dtable[i] = _hurd_port_get (&d->port, &ulink_dtable[i]);
     475        dtable_cells[i] = &d->port;
     476        dtable_cloexec[i] = (d->flags & FD_CLOEXEC) != 0;
     477      }
     478    __mutex_unlock (&_hurd_dtable_lock);
     479  
     480    /* Safe to let signals happen now.  */
     481    _hurd_critical_section_unlock (ss);
     482  
     483    /* Execute the file actions.  */
     484    if (file_actions != NULL)
     485      for (i = 0; i < file_actions->__used; ++i)
     486        {
     487  	/* Close a file descriptor in the child.  */
     488  	error_t do_close (int fd)
     489  	  {
     490  	    if ((unsigned int)fd < dtablesize
     491  		&& dtable[fd] != MACH_PORT_NULL)
     492  	      {
     493  		if (dtable_cells[fd] == NULL)
     494  		  __mach_port_deallocate (__mach_task_self (), dtable[fd]);
     495  		else
     496  		  {
     497  		    _hurd_port_free (dtable_cells[fd],
     498  				     &ulink_dtable[fd], dtable[fd]);
     499  		  }
     500  		dtable_cells[fd] = NULL;
     501  		dtable[fd] = MACH_PORT_NULL;
     502  		return 0;
     503  	      }
     504  	    return EBADF;
     505  	  }
     506  
     507  	/* Close file descriptors in the child.  */
     508  	error_t do_closefrom (int lowfd)
     509  	  {
     510  	    while ((unsigned int) lowfd < dtablesize)
     511  	      {
     512  		error_t err = do_close (lowfd);
     513  		if (err != 0 && err != EBADF)
     514  		  return err;
     515  		lowfd++;
     516  	      }
     517  	    return 0;
     518  	  }
     519  
     520  	/* Make sure the dtable can hold NEWFD.  */
     521  #define EXPAND_DTABLE(newfd)						      \
     522  	({								      \
     523  	  if ((unsigned int)newfd >= dtablesize				      \
     524  	      && newfd < _hurd_rlimits[RLIMIT_OFILE].rlim_cur)		      \
     525  	    {								      \
     526  	      /* We need to expand the dtable for the child.  */	      \
     527  	      NEW_TABLE (dtable, newfd);				      \
     528  	      NEW_ULINK_TABLE (ulink_dtable, newfd);			      \
     529  	      NEW_TABLE (dtable_cells, newfd);				      \
     530  	      dtablesize = newfd + 1;					      \
     531  	    }								      \
     532  	  ((unsigned int)newfd < dtablesize ? 0 : EMFILE);		      \
     533  	})
     534  #define NEW_TABLE(x, newfd) \
     535    do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0]));	      \
     536    memcpy (new_##x, x, dtablesize * sizeof (x[0]));			      \
     537    memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \
     538    x = new_##x; } while (0)
     539  #define NEW_ULINK_TABLE(x, newfd) \
     540    do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0]));	      \
     541    unsigned i;								      \
     542    for (i = 0; i < dtablesize; i++)					      \
     543      if (dtable_cells[i] != NULL)					      \
     544        _hurd_port_move (dtable_cells[i], &new_##x[i], &x[i]);		      \
     545      else								      \
     546        memset (&new_##x[i], 0, sizeof (new_##x[i]));			      \
     547    memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \
     548    x = new_##x; } while (0)
     549  
     550  	struct __spawn_action *action = &file_actions->__actions[i];
     551  
     552  	switch (action->tag)
     553  	  {
     554  	  case spawn_do_close:
     555  	    err = do_close (action->action.close_action.fd);
     556  	    break;
     557  
     558  	  case spawn_do_dup2:
     559  	    if ((unsigned int)action->action.dup2_action.fd < dtablesize
     560  		&& dtable[action->action.dup2_action.fd] != MACH_PORT_NULL)
     561  	      {
     562  		const int fd = action->action.dup2_action.fd;
     563  		const int newfd = action->action.dup2_action.newfd;
     564  		// dup2 always clears any old FD_CLOEXEC flag on the new fd.
     565  		if (newfd < orig_dtablesize)
     566  		  dtable_cloexec[newfd] = 0;
     567  		if (fd == newfd)
     568  		  // Same is same as same was.
     569  		  break;
     570  		err = EXPAND_DTABLE (newfd);
     571  		if (!err)
     572  		  {
     573  		    /* Close the old NEWFD and replace it with FD's
     574  		       contents, which can be either an original
     575  		       descriptor (DTABLE_CELLS[FD] != 0) or a new
     576  		       right that we acquired in this function.  */
     577  		    do_close (newfd);
     578  		    dtable_cells[newfd] = dtable_cells[fd];
     579  		    if (dtable_cells[newfd] != NULL)
     580  		      dtable[newfd] = _hurd_port_get (dtable_cells[newfd],
     581  						      &ulink_dtable[newfd]);
     582  		    else
     583  		      {
     584  			dtable[newfd] = dtable[fd];
     585  			err = __mach_port_mod_refs (__mach_task_self (),
     586  						    dtable[fd],
     587  						    MACH_PORT_RIGHT_SEND, +1);
     588  		      }
     589  		  }
     590  	      }
     591  	    else
     592  	      // The old FD specified was bogus.
     593  	      err = EBADF;
     594  	    break;
     595  
     596  	  case spawn_do_open:
     597  	    /* Open a file on behalf of the child.
     598  
     599  	       XXX note that this can subject the parent to arbitrary
     600  	       delays waiting for the files to open.  I don't know what the
     601  	       spec says about this.  If it's not permissible, then this
     602  	       whole forkless implementation is probably untenable.  */
     603  	    {
     604  	      const int fd = action->action.open_action.fd;
     605  
     606  	      do_close (fd);
     607  	      if (fd < orig_dtablesize)
     608  		dtable_cloexec[fd] = 0;
     609  	      err = EXPAND_DTABLE (fd);
     610  	      if (err)
     611  		break;
     612  
     613  	      err = child_lookup (action->action.open_action.path,
     614  				  action->action.open_action.oflag,
     615  				  action->action.open_action.mode,
     616  				  &dtable[fd]);
     617  	      dtable_cells[fd] = NULL;
     618  	      break;
     619  	    }
     620  
     621  	  case spawn_do_chdir:
     622  	    err = child_chdir (action->action.chdir_action.path);
     623  	    break;
     624  
     625  	  case spawn_do_fchdir:
     626  	    err = child_fchdir (action->action.fchdir_action.fd);
     627  	    break;
     628  
     629  	  case spawn_do_closefrom:
     630  	    err = do_closefrom (action->action.closefrom_action.from);
     631  	    break;
     632  
     633  	  case spawn_do_tcsetpgrp:
     634  	    {
     635  	      pid_t pgrp;
     636  	      /* Check if it is possible to avoid an extra syscall.  */
     637  	      if ((attrp->__flags & POSIX_SPAWN_SETPGROUP)
     638  		  != 0 && attrp->__pgrp != 0)
     639  		pgrp = attrp->__pgrp;
     640  	      else
     641  		err = __proc_getpgrp (proc, new_pid, &pgrp);
     642  	      if (!err)
     643  		err = __tcsetpgrp (action->action.setpgrp_action.fd, pgrp);
     644  	    }
     645  	  }
     646  
     647  	if (err)
     648  	  goto out;
     649        }
     650  
     651    /* Only now can we perform FD_CLOEXEC.  We had to leave the descriptors
     652       unmolested for the file actions to use.  Note that the DTABLE_CLOEXEC
     653       array is never expanded by file actions, so it might now have fewer
     654       than DTABLESIZE elements.  */
     655    for (i = 0; i < orig_dtablesize; ++i)
     656      if (dtable[i] != MACH_PORT_NULL && dtable_cloexec[i])
     657        {
     658  	assert (dtable_cells[i] != NULL);
     659  	_hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
     660  	dtable[i] = MACH_PORT_NULL;
     661        }
     662  
     663    /* Prune trailing null ports from the descriptor table.  */
     664    while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
     665      --dtablesize;
     666  
     667    if (flags & POSIX_SPAWN_RESETIDS)
     668      {
     669        /* Reauthenticate all the child's ports with its new auth handle.  */
     670  
     671        mach_port_t ref;
     672        process_t newproc;
     673  
     674        /* Reauthenticate with the proc server.  */
     675        ref = __mach_reply_port ();
     676        err = __proc_reauthenticate (proc, ref, MACH_MSG_TYPE_MAKE_SEND);
     677        if (!err)
     678  	err = __auth_user_authenticate (auth,
     679  					ref, MACH_MSG_TYPE_MAKE_SEND,
     680  					&newproc);
     681        __mach_port_destroy (__mach_task_self (), ref);
     682        if (!err)
     683  	{
     684  	  __mach_port_deallocate (__mach_task_self (), proc);
     685  	  proc = newproc;
     686  	}
     687  
     688        if (!err)
     689  	err = reauthenticate (INIT_PORT_CRDIR, &rcrdir);
     690        if (!err)
     691  	err = reauthenticate (INIT_PORT_CWDIR, &rcwdir);
     692  
     693        /* We must reauthenticate all the fds except those that came from
     694  	 `spawn_do_open' file actions, which were opened using the child's
     695  	 auth port to begin with.  */
     696        for (i = 0; !err && i < dtablesize; ++i)
     697  	err = reauthenticate_fd (i);
     698      }
     699    if (err)
     700      goto out;
     701  
     702    /* Now we are ready to open the executable file using the child's ports.
     703       We do this after performing all the file actions so the order of
     704       events is the same as for a fork, exec sequence.  This affects things
     705       like the meaning of a /dev/fd file name, as well as which error
     706       conditions are diagnosed first and what side effects (file creation,
     707       etc) can be observed before what errors.  */
     708  
     709    if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL)
     710      /* The FILE parameter is actually a path.  */
     711      err = child_lookup (relpath = file, O_EXEC, 0, &execfile);
     712    else
     713      {
     714        /* We have to search for FILE on the path.  */
     715        path = getenv ("PATH");
     716        if (path == NULL)
     717  	{
     718  	  /* There is no `PATH' in the environment.
     719  	     The default search path is the current directory
     720  	     followed by the path `confstr' returns for `_CS_PATH'.  */
     721  	  len = __confstr (_CS_PATH, (char *) NULL, 0);
     722  	  path = (char *) __alloca (1 + len);
     723  	  path[0] = ':';
     724  	  (void) __confstr (_CS_PATH, path + 1, len);
     725  	}
     726  
     727        len = strlen (file) + 1;
     728        pathlen = strlen (path);
     729        name = __alloca (pathlen + len + 1);
     730        /* Copy the file name at the top.  */
     731        name = (char *) memcpy (name + pathlen + 1, file, len);
     732        /* And add the slash.  */
     733        *--name = '/';
     734  
     735        p = path;
     736        do
     737  	{
     738  	  char *startp;
     739  
     740  	  path = p;
     741  	  p = __strchrnul (path, ':');
     742  
     743  	  if (p == path)
     744  	    /* Two adjacent colons, or a colon at the beginning or the end
     745  	       of `PATH' means to search the current directory.  */
     746  	    startp = name + 1;
     747  	  else
     748  	    startp = (char *) memcpy (name - (p - path), path, p - path);
     749  
     750  	  /* Try to open this file name.  */
     751  	  err = child_lookup (startp, O_EXEC, 0, &execfile);
     752  	  switch (err)
     753  	    {
     754  	    case EACCES:
     755  	    case ENOENT:
     756  	    case ESTALE:
     757  	    case ENOTDIR:
     758  	      /* Those errors indicate the file is missing or not executable
     759  		 by us, in which case we want to just try the next path
     760  		 directory.  */
     761  	      continue;
     762  
     763  	    case 0:		/* Success! */
     764  	    default:
     765  	      /* Some other error means we found an executable file, but
     766  		 something went wrong executing it; return the error to our
     767  		 caller.  */
     768  	      break;
     769  	    }
     770  
     771  	  // We only get here when we are done looking for the file.
     772  	  relpath = startp;
     773  	  break;
     774  	}
     775        while (*p++ != '\0');
     776      }
     777    if (err)
     778      goto out;
     779  
     780    if (relpath[0] == '/')
     781      {
     782        /* Already an absolute path */
     783        abspath = relpath;
     784      }
     785    else
     786      {
     787        /* Relative path */
     788        char *cwd = __getcwd (NULL, 0);
     789        if (cwd == NULL)
     790  	goto out;
     791  
     792        res = __asprintf (&concat_name, "%s/%s", cwd, relpath);
     793        free (cwd);
     794        if (res == -1)
     795  	goto out;
     796  
     797        abspath = concat_name;
     798      }
     799  
     800    /* Almost there!  */
     801    {
     802      mach_port_t ports[_hurd_nports];
     803      struct hurd_userlink ulink_ports[_hurd_nports];
     804      char *args = NULL, *env = NULL;
     805      size_t argslen = 0, envlen = 0;
     806  
     807      inline error_t exec (file_t file)
     808        {
     809  	error_t err = __file_exec_paths
     810  	  (file, task,
     811  	   __sigismember (&_hurdsig_traced, SIGKILL) ? EXEC_SIGTRAP : 0,
     812  	   relpath, abspath, args, argslen, env, envlen,
     813  	   dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
     814  	   ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
     815  	   ints, INIT_INT_MAX,
     816  	   NULL, 0, NULL, 0);
     817  
     818  	/* Fallback for backwards compatibility.  This can just be removed
     819  	   when __file_exec goes away.  */
     820  	if (err == MIG_BAD_ID)
     821  	  return __file_exec (file, task,
     822  			      (__sigismember (&_hurdsig_traced, SIGKILL)
     823  			      ? EXEC_SIGTRAP : 0),
     824  			      args, argslen, env, envlen,
     825  			      dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
     826  			      ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
     827  			      ints, INIT_INT_MAX,
     828  			      NULL, 0, NULL, 0);
     829  
     830  	return err;
     831        }
     832  
     833      /* Now we are out of things that can fail before the file_exec RPC,
     834         for which everything else must be prepared.  The only thing left
     835         to do is packing up the argument and environment strings,
     836         and the array of init ports.  */
     837  
     838      if (argv != NULL)
     839        err = __argz_create (argv, &args, &argslen);
     840      if (!err && envp != NULL)
     841        err = __argz_create (envp, &env, &envlen);
     842  
     843      /* Load up the ports to give to the new program.
     844         Note the loop/switch below must parallel exactly to release refs.  */
     845      for (i = 0; i < _hurd_nports; ++i)
     846        {
     847  	switch (i)
     848  	  {
     849  	  case INIT_PORT_AUTH:
     850  	    ports[i] = auth;
     851  	    continue;
     852  	  case INIT_PORT_PROC:
     853  	    ports[i] = proc;
     854  	    continue;
     855  	  case INIT_PORT_CRDIR:
     856  	    if (flags & POSIX_SPAWN_RESETIDS)
     857  	      {
     858  		ports[i] = rcrdir;
     859  		continue;
     860  	      }
     861  	    break;
     862  	  case INIT_PORT_CWDIR:
     863  	    if (flags & POSIX_SPAWN_RESETIDS)
     864  	      {
     865  		ports[i] = rcwdir;
     866  		continue;
     867  	      }
     868  	    if (ccwdir != MACH_PORT_NULL)
     869  	      {
     870  		ports[i] = ccwdir;
     871  		continue;
     872  	      }
     873  	    break;
     874  	  }
     875  	ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
     876        }
     877  
     878      /* Finally, try executing the file we opened.  */
     879      if (!err)
     880        err = exec (execfile);
     881      __mach_port_deallocate (__mach_task_self (), execfile);
     882  
     883      if ((err == ENOEXEC) && (xflags & SPAWN_XFLAGS_TRY_SHELL) != 0)
     884        {
     885  	/* The file is accessible but it is not an executable file.
     886  	   Invoke the shell to interpret it as a script.  */
     887  	err = 0;
     888  	if (!argslen)
     889  	  err = __argz_insert (&args, &argslen, args, relpath);
     890  	if (!err)
     891  	  err = __argz_insert (&args, &argslen, args, _PATH_BSHELL);
     892  	if (!err)
     893  	  err = child_lookup (_PATH_BSHELL, O_EXEC, 0, &execfile);
     894  	if (!err)
     895  	  {
     896  	    err = exec (execfile);
     897  	    __mach_port_deallocate (__mach_task_self (), execfile);
     898  	  }
     899        }
     900  
     901      /* Release the references just packed up in PORTS.
     902         This switch must always parallel the one above that fills PORTS.  */
     903      for (i = 0; i < _hurd_nports; ++i)
     904        {
     905  	switch (i)
     906  	  {
     907  	  case INIT_PORT_AUTH:
     908  	  case INIT_PORT_PROC:
     909  	    continue;
     910  	  case INIT_PORT_CRDIR:
     911  	    if (flags & POSIX_SPAWN_RESETIDS)
     912  	      continue;
     913  	    break;
     914  	  case INIT_PORT_CWDIR:
     915  	    if (flags & POSIX_SPAWN_RESETIDS)
     916  	      continue;
     917  	    if (ccwdir != MACH_PORT_NULL)
     918  	      continue;
     919  	    break;
     920  	  }
     921  	_hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
     922        }
     923  
     924      free (args);
     925      free (env);
     926    }
     927  
     928    /* We did it!  We have a child!  */
     929    if (pid != NULL)
     930      *pid = new_pid;
     931  
     932   out:
     933    /* Clean up all the references we are now holding.  */
     934  
     935    if (task != MACH_PORT_NULL)
     936      {
     937        if (err)
     938  	/* We failed after creating the task, so kill it.  */
     939  	__task_terminate (task);
     940        __mach_port_deallocate (__mach_task_self (), task);
     941      }
     942    __mach_port_deallocate (__mach_task_self (), auth);
     943    __mach_port_deallocate (__mach_task_self (), proc);
     944    if (ccwdir != MACH_PORT_NULL)
     945      __mach_port_deallocate (__mach_task_self (), ccwdir);
     946    if (rcrdir != MACH_PORT_NULL)
     947      __mach_port_deallocate (__mach_task_self (), rcrdir);
     948    if (rcwdir != MACH_PORT_NULL)
     949      __mach_port_deallocate (__mach_task_self (), rcwdir);
     950  
     951    if (ulink_dtable)
     952      /* Release references to the file descriptor ports.  */
     953      for (i = 0; i < dtablesize; ++i)
     954        if (dtable[i] != MACH_PORT_NULL)
     955  	{
     956  	  if (dtable_cells[i] == NULL)
     957  	    __mach_port_deallocate (__mach_task_self (), dtable[i]);
     958  	  else
     959  	    _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
     960  	}
     961  
     962    free (concat_name);
     963  
     964    if (err)
     965      /* This hack canonicalizes the error code that we return.  */
     966      err = (__hurd_fail (err), errno);
     967  
     968    return err;
     969  }