1 /* spawn a new process running an executable.  Hurd version.
2    Copyright (C) 2001-2021 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
__spawni(pid_t * pid,const char * file,const posix_spawn_file_actions_t * file_actions,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],int xflags)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)
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       errno = EBADF;
220       return MACH_PORT_NULL;
221     }
222   inline error_t child_lookup (const char *file, int oflag, mode_t mode,
223 			       file_t *result)
224     {
225       return __hurd_file_name_lookup (&child_init_port, &child_fd, 0,
226 				      file, oflag, mode, result);
227     }
228   auto error_t child_chdir (const char *name)
229     {
230       file_t new_ccwdir;
231 
232       /* Append trailing "/." to directory name to force ENOTDIR if
233 	 it's not a directory and EACCES if we don't have search
234 	 permission.  */
235       len = strlen (name);
236       const char *lookup = name;
237       if (len >= 2 && name[len - 2] == '/' && name[len - 1] == '.')
238 	lookup = name;
239       else if (len == 0)
240 	/* Special-case empty file name according to POSIX.  */
241 	return __hurd_fail (ENOENT);
242       else
243 	{
244 	  char *n = alloca (len + 3);
245 	  memcpy (n, name, len);
246 	  n[len] = '/';
247 	  n[len + 1] = '.';
248 	  n[len + 2] = '\0';
249 	  lookup = n;
250 	}
251 
252       error_t err = child_lookup (lookup, 0, 0, &new_ccwdir);
253       if (!err)
254 	{
255 	  if (ccwdir != MACH_PORT_NULL)
256 	    __mach_port_deallocate (__mach_task_self (), ccwdir);
257 	  ccwdir = new_ccwdir;
258 	}
259 
260       return err;
261     }
262   inline error_t child_lookup_under (file_t startdir, const char *file,
263 				     int oflag, mode_t mode, file_t *result)
264     {
265       error_t use_init_port (int which, error_t (*operate) (mach_port_t))
266 	{
267 	  return (which == INIT_PORT_CWDIR ? (*operate) (startdir)
268 		  : child_init_port (which, operate));
269 	}
270 
271       return __hurd_file_name_lookup (&use_init_port, &child_fd, 0,
272 				      file, oflag, mode, result);
273     }
274   auto error_t child_fchdir (int fd)
275     {
276       file_t new_ccwdir;
277       error_t err;
278 
279       if ((unsigned int)fd >= dtablesize
280 	  || dtable[fd] == MACH_PORT_NULL)
281 	return EBADF;
282 
283       /* We look up "." to force ENOTDIR if it's not a directory and EACCES if
284          we don't have search permission.  */
285       if (dtable_cells[fd] != NULL)
286 	  err = HURD_PORT_USE (dtable_cells[fd],
287 		    ({
288 		      child_lookup_under (port, ".", O_NOTRANS, 0, &new_ccwdir);
289 		     }));
290       else
291 	  err = child_lookup_under (dtable[fd], ".", O_NOTRANS, 0, &new_ccwdir);
292 
293       if (!err)
294 	{
295 	  if (ccwdir != MACH_PORT_NULL)
296 	    __mach_port_deallocate (__mach_task_self (), ccwdir);
297 	  ccwdir = new_ccwdir;
298 	}
299 
300       return err;
301     }
302 
303 
304   /* Do this once.  */
305   flags = attrp == NULL ? 0 : attrp->__flags;
306 
307   /* Generate the new process.  We create a task that does not inherit our
308      memory, and then register it as our child like fork does.  See fork.c
309      for comments about the sequencing of these proc operations.  */
310 
311   err = __task_create (__mach_task_self (),
312 #ifdef KERN_INVALID_LEDGER
313 		       NULL, 0,	/* OSF Mach */
314 #endif
315 		       0, &task);
316   if (err)
317     return __hurd_fail (err);
318   // From here down we must deallocate TASK and PROC before returning.
319   proc = MACH_PORT_NULL;
320   auth = MACH_PORT_NULL;
321   err = __USEPORT (PROC, __proc_task2pid (port, task, &new_pid));
322   if (!err)
323     err = __USEPORT (PROC, __proc_task2proc (port, task, &proc));
324   if (!err)
325     err = __USEPORT (PROC, __proc_child (port, task));
326   if (err)
327     goto out;
328 
329   /* Load up the ints to give the new program.  */
330   memset (ints, 0, sizeof ints);
331   ints[INIT_UMASK] = _hurd_umask;
332   ints[INIT_TRACEMASK] = _hurdsig_traced;
333 
334   ss = _hurd_self_sigstate ();
335 
336 retry:
337   assert (! __spin_lock_locked (&ss->critical_section_lock));
338   __spin_lock (&ss->critical_section_lock);
339 
340   _hurd_sigstate_lock (ss);
341   ints[INIT_SIGMASK] = ss->blocked;
342   ints[INIT_SIGPENDING] = 0;
343   ints[INIT_SIGIGN] = 0;
344   /* Unless we were asked to reset all handlers to SIG_DFL,
345      pass down the set of signals that were set to SIG_IGN.  */
346   {
347     struct sigaction *actions = _hurd_sigstate_actions (ss);
348     if ((flags & POSIX_SPAWN_SETSIGDEF) == 0)
349       for (i = 1; i < NSIG; ++i)
350 	if (actions[i].sa_handler == SIG_IGN)
351 	  ints[INIT_SIGIGN] |= __sigmask (i);
352   }
353 
354   /* We hold the critical section lock until the exec has failed so that no
355      signal can arrive between when we pack the blocked and ignored signals,
356      and when the exec actually happens.  A signal handler could change what
357      signals are blocked and ignored.  Either the change will be reflected
358      in the exec, or the signal will never be delivered.  Setting the
359      critical section flag avoids anything we call trying to acquire the
360      sigstate lock.  */
361 
362   _hurd_sigstate_unlock (ss);
363 
364   /* Set signal mask.  */
365   if ((flags & POSIX_SPAWN_SETSIGMASK) != 0)
366     ints[INIT_SIGMASK] = attrp->__ss;
367 
368 #ifdef _POSIX_PRIORITY_SCHEDULING
369   /* Set the scheduling algorithm and parameters.  */
370 # error implement me
371   if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
372       == POSIX_SPAWN_SETSCHEDPARAM)
373     {
374       if (__sched_setparam (0, &attrp->__sp) == -1)
375 	_exit (SPAWN_ERROR);
376     }
377   else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
378     {
379       if (__sched_setscheduler (0, attrp->__policy,
380 				(flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
381 				? &attrp->__sp : NULL) == -1)
382 	_exit (SPAWN_ERROR);
383     }
384 #endif
385 
386   if (!err && (flags & POSIX_SPAWN_SETSID) != 0)
387     err = __proc_setsid (proc);
388 
389   /* Set the process group ID.  */
390   if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0)
391     err = __proc_setpgrp (proc, new_pid, attrp->__pgrp);
392 
393   /* Set the effective user and group IDs.  */
394   if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0)
395     {
396       /* We need a different auth port for the child.  */
397 
398       __mutex_lock (&_hurd_id.lock);
399       err = _hurd_check_ids (); /* Get _hurd_id up to date.  */
400       if (!err && _hurd_id.rid_auth == MACH_PORT_NULL)
401 	{
402 	  /* Set up _hurd_id.rid_auth.  This is a special auth server port
403 	     which uses the real uid and gid (the first aux uid and gid) as
404 	     the only effective uid and gid.  */
405 
406 	  if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1)
407 	    /* We do not have a real UID and GID.  Lose, lose, lose!  */
408 	    err = EGRATUITOUS;
409 
410 	  /* Create a new auth port using our real UID and GID (the first
411 	     auxiliary UID and GID) as the only effective IDs.  */
412 	  if (!err)
413 	    err = __USEPORT (AUTH,
414 			     __auth_makeauth (port,
415 					      NULL, MACH_MSG_TYPE_COPY_SEND, 0,
416 					      _hurd_id.aux.uids, 1,
417 					      _hurd_id.aux.uids,
418 					      _hurd_id.aux.nuids,
419 					      _hurd_id.aux.gids, 1,
420 					      _hurd_id.aux.gids,
421 					      _hurd_id.aux.ngids,
422 					      &_hurd_id.rid_auth));
423 	}
424       if (!err)
425 	{
426 	  /* Use the real-ID auth port in place of the normal one.  */
427 	  assert (_hurd_id.rid_auth != MACH_PORT_NULL);
428 	  auth = _hurd_id.rid_auth;
429 	  __mach_port_mod_refs (__mach_task_self (), auth,
430 				MACH_PORT_RIGHT_SEND, +1);
431 	}
432       __mutex_unlock (&_hurd_id.lock);
433     }
434   else
435     /* Copy our existing auth port.  */
436     err = __USEPORT (AUTH, __mach_port_mod_refs (__mach_task_self (),
437 						 (auth = port),
438 						 MACH_PORT_RIGHT_SEND, +1));
439 
440   if (err)
441     {
442       _hurd_critical_section_unlock (ss);
443 
444       if (err == EINTR)
445 	{
446 	  /* Got a signal while inside an RPC of the critical section, retry again */
447 	  __mach_port_deallocate (__mach_task_self (), auth);
448 	  auth = MACH_PORT_NULL;
449 	  goto retry;
450 	}
451 
452       goto out;
453     }
454 
455   /* Pack up the descriptor table to give the new program.
456      These descriptors will need to be reauthenticated below
457      if POSIX_SPAWN_RESETIDS is set.  */
458   __mutex_lock (&_hurd_dtable_lock);
459   dtablesize = _hurd_dtablesize;
460   orig_dtablesize = _hurd_dtablesize;
461   dtable = __alloca (dtablesize * sizeof (dtable[0]));
462   ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
463   dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
464   dtable_cloexec = __alloca (orig_dtablesize);
465   for (i = 0; i < dtablesize; ++i)
466     {
467       struct hurd_fd *const d = _hurd_dtable[i];
468       if (d == NULL)
469 	{
470 	  dtable[i] = MACH_PORT_NULL;
471 	  dtable_cells[i] = NULL;
472 	  continue;
473 	}
474       /* Note that this might return MACH_PORT_NULL.  */
475       dtable[i] = _hurd_port_get (&d->port, &ulink_dtable[i]);
476       dtable_cells[i] = &d->port;
477       dtable_cloexec[i] = (d->flags & FD_CLOEXEC) != 0;
478     }
479   __mutex_unlock (&_hurd_dtable_lock);
480 
481   /* Safe to let signals happen now.  */
482   _hurd_critical_section_unlock (ss);
483 
484   /* Execute the file actions.  */
485   if (file_actions != NULL)
486     for (i = 0; i < file_actions->__used; ++i)
487       {
488 	/* Close a file descriptor in the child.  */
489 	error_t do_close (int fd)
490 	  {
491 	    if ((unsigned int)fd < dtablesize
492 		&& dtable[fd] != MACH_PORT_NULL)
493 	      {
494 		if (dtable_cells[fd] == NULL)
495 		  __mach_port_deallocate (__mach_task_self (), dtable[fd]);
496 		else
497 		  {
498 		    _hurd_port_free (dtable_cells[fd],
499 				     &ulink_dtable[fd], dtable[fd]);
500 		  }
501 		dtable_cells[fd] = NULL;
502 		dtable[fd] = MACH_PORT_NULL;
503 		return 0;
504 	      }
505 	    return EBADF;
506 	  }
507 
508 	/* Close file descriptors in the child.  */
509 	error_t do_closefrom (int lowfd)
510 	  {
511 	    while ((unsigned int) lowfd < dtablesize)
512 	      {
513 		error_t err = do_close (lowfd);
514 		if (err != 0 && err != EBADF)
515 		  return err;
516 		lowfd++;
517 	      }
518 	    return 0;
519 	  }
520 
521 	/* Make sure the dtable can hold NEWFD.  */
522 #define EXPAND_DTABLE(newfd)						      \
523 	({								      \
524 	  if ((unsigned int)newfd >= dtablesize				      \
525 	      && newfd < _hurd_rlimits[RLIMIT_OFILE].rlim_cur)		      \
526 	    {								      \
527 	      /* We need to expand the dtable for the child.  */	      \
528 	      NEW_TABLE (dtable, newfd);				      \
529 	      NEW_ULINK_TABLE (ulink_dtable, newfd);			      \
530 	      NEW_TABLE (dtable_cells, newfd);				      \
531 	      dtablesize = newfd + 1;					      \
532 	    }								      \
533 	  ((unsigned int)newfd < dtablesize ? 0 : EMFILE);		      \
534 	})
535 #define NEW_TABLE(x, newfd) \
536   do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0]));	      \
537   memcpy (new_##x, x, dtablesize * sizeof (x[0]));			      \
538   memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \
539   x = new_##x; } while (0)
540 #define NEW_ULINK_TABLE(x, newfd) \
541   do { __typeof (x) new_##x = __alloca ((newfd + 1) * sizeof (x[0]));	      \
542   unsigned i;								      \
543   for (i = 0; i < dtablesize; i++)					      \
544     if (dtable_cells[i] != NULL)					      \
545       _hurd_port_move (dtable_cells[i], &new_##x[i], &x[i]);		      \
546     else								      \
547       memset (&new_##x[i], 0, sizeof (new_##x[i]));			      \
548   memset (&new_##x[dtablesize], 0, (newfd + 1 - dtablesize) * sizeof (x[0])); \
549   x = new_##x; } while (0)
550 
551 	struct __spawn_action *action = &file_actions->__actions[i];
552 
553 	switch (action->tag)
554 	  {
555 	  case spawn_do_close:
556 	    err = do_close (action->action.close_action.fd);
557 	    break;
558 
559 	  case spawn_do_dup2:
560 	    if ((unsigned int)action->action.dup2_action.fd < dtablesize
561 		&& dtable[action->action.dup2_action.fd] != MACH_PORT_NULL)
562 	      {
563 		const int fd = action->action.dup2_action.fd;
564 		const int newfd = action->action.dup2_action.newfd;
565 		// dup2 always clears any old FD_CLOEXEC flag on the new fd.
566 		if (newfd < orig_dtablesize)
567 		  dtable_cloexec[newfd] = 0;
568 		if (fd == newfd)
569 		  // Same is same as same was.
570 		  break;
571 		err = EXPAND_DTABLE (newfd);
572 		if (!err)
573 		  {
574 		    /* Close the old NEWFD and replace it with FD's
575 		       contents, which can be either an original
576 		       descriptor (DTABLE_CELLS[FD] != 0) or a new
577 		       right that we acquired in this function.  */
578 		    do_close (newfd);
579 		    dtable_cells[newfd] = dtable_cells[fd];
580 		    if (dtable_cells[newfd] != NULL)
581 		      dtable[newfd] = _hurd_port_get (dtable_cells[newfd],
582 						      &ulink_dtable[newfd]);
583 		    else
584 		      {
585 			dtable[newfd] = dtable[fd];
586 			err = __mach_port_mod_refs (__mach_task_self (),
587 						    dtable[fd],
588 						    MACH_PORT_RIGHT_SEND, +1);
589 		      }
590 		  }
591 	      }
592 	    else
593 	      // The old FD specified was bogus.
594 	      err = EBADF;
595 	    break;
596 
597 	  case spawn_do_open:
598 	    /* Open a file on behalf of the child.
599 
600 	       XXX note that this can subject the parent to arbitrary
601 	       delays waiting for the files to open.  I don't know what the
602 	       spec says about this.  If it's not permissible, then this
603 	       whole forkless implementation is probably untenable.  */
604 	    {
605 	      const int fd = action->action.open_action.fd;
606 
607 	      do_close (fd);
608 	      if (fd < orig_dtablesize)
609 		dtable_cloexec[fd] = 0;
610 	      err = EXPAND_DTABLE (fd);
611 	      if (err)
612 		break;
613 
614 	      err = child_lookup (action->action.open_action.path,
615 				  action->action.open_action.oflag,
616 				  action->action.open_action.mode,
617 				  &dtable[fd]);
618 	      dtable_cells[fd] = NULL;
619 	      break;
620 	    }
621 
622 	  case spawn_do_chdir:
623 	    err = child_chdir (action->action.chdir_action.path);
624 	    break;
625 
626 	  case spawn_do_fchdir:
627 	    err = child_fchdir (action->action.fchdir_action.fd);
628 	    break;
629 
630 	  case spawn_do_closefrom:
631 	    err = do_closefrom (action->action.closefrom_action.from);
632 	    break;
633 	  }
634 
635 	if (err)
636 	  goto out;
637       }
638 
639   /* Only now can we perform FD_CLOEXEC.  We had to leave the descriptors
640      unmolested for the file actions to use.  Note that the DTABLE_CLOEXEC
641      array is never expanded by file actions, so it might now have fewer
642      than DTABLESIZE elements.  */
643   for (i = 0; i < orig_dtablesize; ++i)
644     if (dtable[i] != MACH_PORT_NULL && dtable_cloexec[i])
645       {
646 	assert (dtable_cells[i] != NULL);
647 	_hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
648 	dtable[i] = MACH_PORT_NULL;
649       }
650 
651   /* Prune trailing null ports from the descriptor table.  */
652   while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
653     --dtablesize;
654 
655   if (flags & POSIX_SPAWN_RESETIDS)
656     {
657       /* Reauthenticate all the child's ports with its new auth handle.  */
658 
659       mach_port_t ref;
660       process_t newproc;
661 
662       /* Reauthenticate with the proc server.  */
663       ref = __mach_reply_port ();
664       err = __proc_reauthenticate (proc, ref, MACH_MSG_TYPE_MAKE_SEND);
665       if (!err)
666 	err = __auth_user_authenticate (auth,
667 					ref, MACH_MSG_TYPE_MAKE_SEND,
668 					&newproc);
669       __mach_port_destroy (__mach_task_self (), ref);
670       if (!err)
671 	{
672 	  __mach_port_deallocate (__mach_task_self (), proc);
673 	  proc = newproc;
674 	}
675 
676       if (!err)
677 	err = reauthenticate (INIT_PORT_CRDIR, &rcrdir);
678       if (!err)
679 	err = reauthenticate (INIT_PORT_CWDIR, &rcwdir);
680 
681       /* We must reauthenticate all the fds except those that came from
682 	 `spawn_do_open' file actions, which were opened using the child's
683 	 auth port to begin with.  */
684       for (i = 0; !err && i < dtablesize; ++i)
685 	err = reauthenticate_fd (i);
686     }
687   if (err)
688     goto out;
689 
690   /* Now we are ready to open the executable file using the child's ports.
691      We do this after performing all the file actions so the order of
692      events is the same as for a fork, exec sequence.  This affects things
693      like the meaning of a /dev/fd file name, as well as which error
694      conditions are diagnosed first and what side effects (file creation,
695      etc) can be observed before what errors.  */
696 
697   if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL)
698     /* The FILE parameter is actually a path.  */
699     err = child_lookup (relpath = file, O_EXEC, 0, &execfile);
700   else
701     {
702       /* We have to search for FILE on the path.  */
703       path = getenv ("PATH");
704       if (path == NULL)
705 	{
706 	  /* There is no `PATH' in the environment.
707 	     The default search path is the current directory
708 	     followed by the path `confstr' returns for `_CS_PATH'.  */
709 	  len = __confstr (_CS_PATH, (char *) NULL, 0);
710 	  path = (char *) __alloca (1 + len);
711 	  path[0] = ':';
712 	  (void) __confstr (_CS_PATH, path + 1, len);
713 	}
714 
715       len = strlen (file) + 1;
716       pathlen = strlen (path);
717       name = __alloca (pathlen + len + 1);
718       /* Copy the file name at the top.  */
719       name = (char *) memcpy (name + pathlen + 1, file, len);
720       /* And add the slash.  */
721       *--name = '/';
722 
723       p = path;
724       do
725 	{
726 	  char *startp;
727 
728 	  path = p;
729 	  p = __strchrnul (path, ':');
730 
731 	  if (p == path)
732 	    /* Two adjacent colons, or a colon at the beginning or the end
733 	       of `PATH' means to search the current directory.  */
734 	    startp = name + 1;
735 	  else
736 	    startp = (char *) memcpy (name - (p - path), path, p - path);
737 
738 	  /* Try to open this file name.  */
739 	  err = child_lookup (startp, O_EXEC, 0, &execfile);
740 	  switch (err)
741 	    {
742 	    case EACCES:
743 	    case ENOENT:
744 	    case ESTALE:
745 	    case ENOTDIR:
746 	      /* Those errors indicate the file is missing or not executable
747 		 by us, in which case we want to just try the next path
748 		 directory.  */
749 	      continue;
750 
751 	    case 0:		/* Success! */
752 	    default:
753 	      /* Some other error means we found an executable file, but
754 		 something went wrong executing it; return the error to our
755 		 caller.  */
756 	      break;
757 	    }
758 
759 	  // We only get here when we are done looking for the file.
760 	  relpath = startp;
761 	  break;
762 	}
763       while (*p++ != '\0');
764     }
765   if (err)
766     goto out;
767 
768   if (relpath[0] == '/')
769     {
770       /* Already an absolute path */
771       abspath = relpath;
772     }
773   else
774     {
775       /* Relative path */
776       char *cwd = __getcwd (NULL, 0);
777       if (cwd == NULL)
778 	goto out;
779 
780       res = __asprintf (&concat_name, "%s/%s", cwd, relpath);
781       free (cwd);
782       if (res == -1)
783 	goto out;
784 
785       abspath = concat_name;
786     }
787 
788   /* Almost there!  */
789   {
790     mach_port_t ports[_hurd_nports];
791     struct hurd_userlink ulink_ports[_hurd_nports];
792     char *args = NULL, *env = NULL;
793     size_t argslen = 0, envlen = 0;
794 
795     inline error_t exec (file_t file)
796       {
797 	error_t err = __file_exec_paths
798 	  (file, task,
799 	   __sigismember (&_hurdsig_traced, SIGKILL) ? EXEC_SIGTRAP : 0,
800 	   relpath, abspath, args, argslen, env, envlen,
801 	   dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
802 	   ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
803 	   ints, INIT_INT_MAX,
804 	   NULL, 0, NULL, 0);
805 
806 	/* Fallback for backwards compatibility.  This can just be removed
807 	   when __file_exec goes away.  */
808 	if (err == MIG_BAD_ID)
809 	  return __file_exec (file, task,
810 			      (__sigismember (&_hurdsig_traced, SIGKILL)
811 			      ? EXEC_SIGTRAP : 0),
812 			      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 	return err;
819       }
820 
821     /* Now we are out of things that can fail before the file_exec RPC,
822        for which everything else must be prepared.  The only thing left
823        to do is packing up the argument and environment strings,
824        and the array of init ports.  */
825 
826     if (argv != NULL)
827       err = __argz_create (argv, &args, &argslen);
828     if (!err && envp != NULL)
829       err = __argz_create (envp, &env, &envlen);
830 
831     /* Load up the ports to give to the new program.
832        Note the loop/switch below must parallel exactly to release refs.  */
833     for (i = 0; i < _hurd_nports; ++i)
834       {
835 	switch (i)
836 	  {
837 	  case INIT_PORT_AUTH:
838 	    ports[i] = auth;
839 	    continue;
840 	  case INIT_PORT_PROC:
841 	    ports[i] = proc;
842 	    continue;
843 	  case INIT_PORT_CRDIR:
844 	    if (flags & POSIX_SPAWN_RESETIDS)
845 	      {
846 		ports[i] = rcrdir;
847 		continue;
848 	      }
849 	    break;
850 	  case INIT_PORT_CWDIR:
851 	    if (flags & POSIX_SPAWN_RESETIDS)
852 	      {
853 		ports[i] = rcwdir;
854 		continue;
855 	      }
856 	    if (ccwdir != MACH_PORT_NULL)
857 	      {
858 		ports[i] = ccwdir;
859 		continue;
860 	      }
861 	    break;
862 	  }
863 	ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
864       }
865 
866     /* Finally, try executing the file we opened.  */
867     if (!err)
868       err = exec (execfile);
869     __mach_port_deallocate (__mach_task_self (), execfile);
870 
871     if ((err == ENOEXEC) && (xflags & SPAWN_XFLAGS_TRY_SHELL) != 0)
872       {
873 	/* The file is accessible but it is not an executable file.
874 	   Invoke the shell to interpret it as a script.  */
875 	err = 0;
876 	if (!argslen)
877 	  err = __argz_insert (&args, &argslen, args, relpath);
878 	if (!err)
879 	  err = __argz_insert (&args, &argslen, args, _PATH_BSHELL);
880 	if (!err)
881 	  err = child_lookup (_PATH_BSHELL, O_EXEC, 0, &execfile);
882 	if (!err)
883 	  {
884 	    err = exec (execfile);
885 	    __mach_port_deallocate (__mach_task_self (), execfile);
886 	  }
887       }
888 
889     /* Release the references just packed up in PORTS.
890        This switch must always parallel the one above that fills PORTS.  */
891     for (i = 0; i < _hurd_nports; ++i)
892       {
893 	switch (i)
894 	  {
895 	  case INIT_PORT_AUTH:
896 	  case INIT_PORT_PROC:
897 	    continue;
898 	  case INIT_PORT_CRDIR:
899 	    if (flags & POSIX_SPAWN_RESETIDS)
900 	      continue;
901 	    break;
902 	  case INIT_PORT_CWDIR:
903 	    if (flags & POSIX_SPAWN_RESETIDS)
904 	      continue;
905 	    if (ccwdir != MACH_PORT_NULL)
906 	      continue;
907 	    break;
908 	  }
909 	_hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
910       }
911 
912     free (args);
913     free (env);
914   }
915 
916   /* We did it!  We have a child!  */
917   if (pid != NULL)
918     *pid = new_pid;
919 
920  out:
921   /* Clean up all the references we are now holding.  */
922 
923   if (task != MACH_PORT_NULL)
924     {
925       if (err)
926 	/* We failed after creating the task, so kill it.  */
927 	__task_terminate (task);
928       __mach_port_deallocate (__mach_task_self (), task);
929     }
930   __mach_port_deallocate (__mach_task_self (), auth);
931   __mach_port_deallocate (__mach_task_self (), proc);
932   if (ccwdir != MACH_PORT_NULL)
933     __mach_port_deallocate (__mach_task_self (), ccwdir);
934   if (rcrdir != MACH_PORT_NULL)
935     __mach_port_deallocate (__mach_task_self (), rcrdir);
936   if (rcwdir != MACH_PORT_NULL)
937     __mach_port_deallocate (__mach_task_self (), rcwdir);
938 
939   if (ulink_dtable)
940     /* Release references to the file descriptor ports.  */
941     for (i = 0; i < dtablesize; ++i)
942       if (dtable[i] != MACH_PORT_NULL)
943 	{
944 	  if (dtable_cells[i] == NULL)
945 	    __mach_port_deallocate (__mach_task_self (), dtable[i]);
946 	  else
947 	    _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
948 	}
949 
950   free (concat_name);
951 
952   if (err)
953     /* This hack canonicalizes the error code that we return.  */
954     err = (__hurd_fail (err), errno);
955 
956   return err;
957 }
958