Blame hurd/hurdexec.c

Packit 6c4009
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <hurd.h>
Packit 6c4009
#include <hurd/fd.h>
Packit 6c4009
#include <hurd/signal.h>
Packit 6c4009
#include <hurd/id.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <argz.h>
Packit 6c4009
Packit 6c4009
/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
Packit 6c4009
   If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
Packit 6c4009
   ARGV and ENVP are terminated by NULL pointers.
Packit 6c4009
   Deprecated: use _hurd_exec_paths instead.  */
Packit 6c4009
error_t
Packit 6c4009
_hurd_exec (task_t task, file_t file,
Packit 6c4009
	    char *const argv[], char *const envp[])
Packit 6c4009
{
Packit 6c4009
  return _hurd_exec_paths (task, file, NULL, NULL, argv, envp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
link_warning (_hurd_exec,
Packit 6c4009
	      "_hurd_exec is deprecated, use _hurd_exec_paths instead");
Packit 6c4009
Packit 6c4009
/* Overlay TASK, executing FILE with arguments ARGV and environment ENVP.
Packit 6c4009
   If TASK == mach_task_self (), some ports are dealloc'd by the exec server.
Packit 6c4009
   ARGV and ENVP are terminated by NULL pointers.  PATH is the relative path to
Packit 6c4009
   FILE and ABSPATH is the absolute path to FILE. Passing NULL, though possible,
Packit 6c4009
   should be avoided, since then the exec server may not know the path to
Packit 6c4009
   FILE if FILE is a script, and will then pass /dev/fd/N to the
Packit 6c4009
   interpreter.  */
Packit 6c4009
error_t
Packit 6c4009
_hurd_exec_paths (task_t task, file_t file,
Packit 6c4009
		   const char *path, const char *abspath,
Packit 6c4009
		   char *const argv[], char *const envp[])
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
  char *args, *env;
Packit 6c4009
  size_t argslen, envlen;
Packit 6c4009
  int ints[INIT_INT_MAX];
Packit 6c4009
  mach_port_t ports[_hurd_nports];
Packit 6c4009
  struct hurd_userlink ulink_ports[_hurd_nports];
Packit 6c4009
  inline void free_port (unsigned int i)
Packit 6c4009
    {
Packit 6c4009
      _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]);
Packit 6c4009
    }
Packit 6c4009
  file_t *dtable;
Packit 6c4009
  unsigned int dtablesize, i;
Packit 6c4009
  struct hurd_port **dtable_cells;
Packit 6c4009
  struct hurd_userlink *ulink_dtable;
Packit 6c4009
  struct hurd_sigstate *ss;
Packit 6c4009
  mach_port_t *please_dealloc, *pdp;
Packit 6c4009
  int reauth = 0;
Packit 6c4009
Packit 6c4009
  /* XXX needs to be hurdmalloc XXX */
Packit 6c4009
  if (argv == NULL)
Packit 6c4009
    args = NULL, argslen = 0;
Packit 6c4009
  else if (err = __argz_create (argv, &args, &argslen))
Packit 6c4009
    return err;
Packit 6c4009
  if (envp == NULL)
Packit 6c4009
    env = NULL, envlen = 0;
Packit 6c4009
  else if (err = __argz_create (envp, &env, &envlen))
Packit 6c4009
    goto outargs;
Packit 6c4009
Packit 6c4009
  /* Load up the ports to give to the new program.  */
Packit 6c4009
  for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
    if (i == INIT_PORT_PROC && task != __mach_task_self ())
Packit 6c4009
      {
Packit 6c4009
	/* This is another task, so we need to ask the proc server
Packit 6c4009
	   for the right proc server port for it.  */
Packit 6c4009
	if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i])))
Packit 6c4009
	  {
Packit 6c4009
	    while (--i > 0)
Packit 6c4009
	      free_port (i);
Packit 6c4009
	    goto outenv;
Packit 6c4009
	  }
Packit 6c4009
      }
Packit 6c4009
    else
Packit 6c4009
      ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]);
Packit 6c4009
Packit 6c4009
Packit 6c4009
  /* Load up the ints to give the new program.  */
Packit 6c4009
  for (i = 0; i < INIT_INT_MAX; ++i)
Packit 6c4009
    switch (i)
Packit 6c4009
      {
Packit 6c4009
      case INIT_UMASK:
Packit 6c4009
	ints[i] = _hurd_umask;
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      case INIT_SIGMASK:
Packit 6c4009
      case INIT_SIGIGN:
Packit 6c4009
      case INIT_SIGPENDING:
Packit 6c4009
	/* We will set these all below.  */
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      case INIT_TRACEMASK:
Packit 6c4009
	ints[i] = _hurdsig_traced;
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      default:
Packit 6c4009
	ints[i] = 0;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  ss = _hurd_self_sigstate ();
Packit 6c4009
Packit 6c4009
  assert (! __spin_lock_locked (&ss->critical_section_lock));
Packit 6c4009
  __spin_lock (&ss->critical_section_lock);
Packit 6c4009
Packit 6c4009
  __spin_lock (&ss->lock);
Packit 6c4009
  ints[INIT_SIGMASK] = ss->blocked;
Packit 6c4009
  ints[INIT_SIGPENDING] = ss->pending;
Packit 6c4009
  ints[INIT_SIGIGN] = 0;
Packit 6c4009
  for (i = 1; i < NSIG; ++i)
Packit 6c4009
    if (ss->actions[i].sa_handler == SIG_IGN)
Packit 6c4009
      ints[INIT_SIGIGN] |= __sigmask (i);
Packit 6c4009
Packit 6c4009
  /* We hold the sigstate lock until the exec has failed so that no signal
Packit 6c4009
     can arrive between when we pack the blocked and ignored signals, and
Packit 6c4009
     when the exec actually happens.  A signal handler could change what
Packit 6c4009
     signals are blocked and ignored.  Either the change will be reflected
Packit 6c4009
     in the exec, or the signal will never be delivered.  Setting the
Packit 6c4009
     critical section flag avoids anything we call trying to acquire the
Packit 6c4009
     sigstate lock.  */
Packit 6c4009
Packit 6c4009
  __spin_unlock (&ss->lock);
Packit 6c4009
Packit 6c4009
  /* Pack up the descriptor table to give the new program.  */
Packit 6c4009
  __mutex_lock (&_hurd_dtable_lock);
Packit 6c4009
Packit 6c4009
  dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize;
Packit 6c4009
Packit 6c4009
  if (task == __mach_task_self ())
Packit 6c4009
    /* Request the exec server to deallocate some ports from us if the exec
Packit 6c4009
       succeeds.  The init ports and descriptor ports will arrive in the
Packit 6c4009
       new program's exec_startup message.  If we failed to deallocate
Packit 6c4009
       them, the new program would have duplicate user references for them.
Packit 6c4009
       But we cannot deallocate them ourselves, because we must still have
Packit 6c4009
       them after a failed exec call.  */
Packit 6c4009
    please_dealloc = __alloca ((_hurd_nports + 3 + (3 * dtablesize))
Packit 6c4009
				* sizeof (mach_port_t));
Packit 6c4009
  else
Packit 6c4009
    please_dealloc = NULL;
Packit 6c4009
  pdp = please_dealloc;
Packit 6c4009
Packit 6c4009
  if (_hurd_dtable != NULL)
Packit 6c4009
    {
Packit 6c4009
      dtable = __alloca (dtablesize * sizeof (dtable[0]));
Packit 6c4009
      ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0]));
Packit 6c4009
      dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0]));
Packit 6c4009
      for (i = 0; i < dtablesize; ++i)
Packit 6c4009
	{
Packit 6c4009
	  struct hurd_fd *const d = _hurd_dtable[i];
Packit 6c4009
	  if (d == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      dtable[i] = MACH_PORT_NULL;
Packit 6c4009
	      continue;
Packit 6c4009
	    }
Packit 6c4009
	  __spin_lock (&d->port.lock);
Packit 6c4009
	  if (d->flags & FD_CLOEXEC)
Packit 6c4009
	    {
Packit 6c4009
	      /* This descriptor is marked to be closed on exec.
Packit 6c4009
		 So don't pass it to the new program.  */
Packit 6c4009
	      dtable[i] = MACH_PORT_NULL;
Packit 6c4009
	      if (pdp && d->port.port != MACH_PORT_NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* We still need to deallocate the ports.  */
Packit 6c4009
		  *pdp++ = d->port.port;
Packit 6c4009
		  if (d->ctty.port != MACH_PORT_NULL)
Packit 6c4009
		    *pdp++ = d->ctty.port;
Packit 6c4009
		}
Packit 6c4009
	      __spin_unlock (&d->port.lock);
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      if (pdp && d->ctty.port != MACH_PORT_NULL)
Packit 6c4009
		/* All the elements of DTABLE are added to PLEASE_DEALLOC
Packit 6c4009
		   below, so we needn't add the port itself.
Packit 6c4009
		   But we must deallocate the ctty port as well as
Packit 6c4009
		   the normal port that got installed in DTABLE[I].  */
Packit 6c4009
		*pdp++ = d->ctty.port;
Packit 6c4009
	      dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]);
Packit 6c4009
	      dtable_cells[i] = &d->port;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      dtable = _hurd_init_dtable;
Packit 6c4009
      ulink_dtable = NULL;
Packit 6c4009
      dtable_cells = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Prune trailing null ports from the descriptor table.  */
Packit 6c4009
  while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL)
Packit 6c4009
    --dtablesize;
Packit 6c4009
Packit 6c4009
  /* See if we need to diddle the auth port of the new program.
Packit 6c4009
     The purpose of this is to get the effect setting the saved-set UID and
Packit 6c4009
     GID to the respective effective IDs after the exec, as POSIX.1 requires.
Packit 6c4009
     Note that we don't reauthenticate with the proc server; that would be a
Packit 6c4009
     no-op since it only keeps track of the effective UIDs, and if it did
Packit 6c4009
     keep track of the available IDs we would have the problem that we'd be
Packit 6c4009
     changing the IDs before the exec and have to change them back after a
Packit 6c4009
     failure.  Arguably we could skip all the reauthentications because the
Packit 6c4009
     available IDs have no bearing on any filesystem.  But the conservative
Packit 6c4009
     approach is to reauthenticate all the io ports so that no state anywhere
Packit 6c4009
     reflects that our whole ID set differs from what we've set it to.  */
Packit 6c4009
  __mutex_lock (&_hurd_id.lock);
Packit 6c4009
  err = _hurd_check_ids ();
Packit 6c4009
  if (err == 0 && ((_hurd_id.aux.nuids >= 2 && _hurd_id.gen.nuids >= 1
Packit 6c4009
		    && _hurd_id.aux.uids[1] != _hurd_id.gen.uids[0])
Packit 6c4009
		   || (_hurd_id.aux.ngids >= 2 && _hurd_id.gen.ngids >= 1
Packit 6c4009
		       && _hurd_id.aux.gids[1] != _hurd_id.gen.gids[0])))
Packit 6c4009
    {
Packit 6c4009
      /* We have euid != svuid or egid != svgid.  POSIX.1 says that exec
Packit 6c4009
	 sets svuid = euid and svgid = egid.  So we must get a new auth
Packit 6c4009
	 port and reauthenticate everything with it.  We'll pass the new
Packit 6c4009
	 ports in file_exec_paths instead of our own ports.  */
Packit 6c4009
Packit 6c4009
      auth_t newauth;
Packit 6c4009
Packit 6c4009
      _hurd_id.aux.uids[1] = _hurd_id.gen.uids[0];
Packit 6c4009
      _hurd_id.aux.gids[1] = _hurd_id.gen.gids[0];
Packit 6c4009
      _hurd_id.valid = 0;
Packit 6c4009
      if (_hurd_id.rid_auth != MACH_PORT_NULL)
Packit 6c4009
	{
Packit 6c4009
	  __mach_port_deallocate (__mach_task_self (), _hurd_id.rid_auth);
Packit 6c4009
	  _hurd_id.rid_auth = MACH_PORT_NULL;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      err = __auth_makeauth (ports[INIT_PORT_AUTH],
Packit 6c4009
			     NULL, MACH_MSG_TYPE_COPY_SEND, 0,
Packit 6c4009
			     _hurd_id.gen.uids, _hurd_id.gen.nuids,
Packit 6c4009
			     _hurd_id.aux.uids, _hurd_id.aux.nuids,
Packit 6c4009
			     _hurd_id.gen.gids, _hurd_id.gen.ngids,
Packit 6c4009
			     _hurd_id.aux.gids, _hurd_id.aux.ngids,
Packit 6c4009
			     &newauth);
Packit 6c4009
      if (err == 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Now we have to reauthenticate the ports with this new ID.
Packit 6c4009
	   */
Packit 6c4009
Packit 6c4009
	  inline error_t reauth_io (io_t port, io_t *newport)
Packit 6c4009
	    {
Packit 6c4009
	      mach_port_t ref = __mach_reply_port ();
Packit 6c4009
	      *newport = MACH_PORT_NULL;
Packit 6c4009
	      error_t err = __io_reauthenticate (port,
Packit 6c4009
						 ref, MACH_MSG_TYPE_MAKE_SEND);
Packit 6c4009
	      if (!err)
Packit 6c4009
		err = __auth_user_authenticate (newauth,
Packit 6c4009
						ref, MACH_MSG_TYPE_MAKE_SEND,
Packit 6c4009
						newport);
Packit 6c4009
	      __mach_port_destroy (__mach_task_self (), ref);
Packit 6c4009
	      return err;
Packit 6c4009
	    }
Packit 6c4009
	  inline void reauth_port (unsigned int idx)
Packit 6c4009
	    {
Packit 6c4009
	      io_t newport;
Packit 6c4009
	      err = reauth_io (ports[idx], &newport) ?: err;
Packit 6c4009
	      if (pdp)
Packit 6c4009
		*pdp++ = ports[idx]; /* XXX presumed still in _hurd_ports */
Packit 6c4009
	      free_port (idx);
Packit 6c4009
	      ports[idx] = newport;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (pdp)
Packit 6c4009
	    *pdp++ = ports[INIT_PORT_AUTH];
Packit 6c4009
	  free_port (INIT_PORT_AUTH);
Packit 6c4009
	  ports[INIT_PORT_AUTH] = newauth;
Packit 6c4009
Packit 6c4009
	  reauth_port (INIT_PORT_CRDIR);
Packit 6c4009
	  reauth_port (INIT_PORT_CWDIR);
Packit 6c4009
Packit 6c4009
	  if (!err)
Packit 6c4009
	    {
Packit 6c4009
	      /* Now we'll reauthenticate each file descriptor.  */
Packit 6c4009
	      if (ulink_dtable == NULL)
Packit 6c4009
		{
Packit 6c4009
		  assert (dtable == _hurd_init_dtable);
Packit 6c4009
		  dtable = __alloca (dtablesize * sizeof (dtable[0]));
Packit 6c4009
		  for (i = 0; i < dtablesize; ++i)
Packit 6c4009
		    if (_hurd_init_dtable[i] != MACH_PORT_NULL)
Packit 6c4009
		      {
Packit 6c4009
			if (pdp)
Packit 6c4009
			  *pdp++ = _hurd_init_dtable[i];
Packit 6c4009
			err = reauth_io (_hurd_init_dtable[i], &dtable[i]);
Packit 6c4009
			if (err)
Packit 6c4009
			  {
Packit 6c4009
			    while (++i < dtablesize)
Packit 6c4009
			      dtable[i] = MACH_PORT_NULL;
Packit 6c4009
			    break;
Packit 6c4009
			  }
Packit 6c4009
		      }
Packit 6c4009
		    else
Packit 6c4009
		      dtable[i] = MACH_PORT_NULL;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  if (pdp)
Packit 6c4009
		    {
Packit 6c4009
		      /* Ask to deallocate all the old fd ports,
Packit 6c4009
			 since we will have new ones in DTABLE.  */
Packit 6c4009
		      memcpy (pdp, dtable, dtablesize * sizeof pdp[0]);
Packit 6c4009
		      pdp += dtablesize;
Packit 6c4009
		    }
Packit 6c4009
		  for (i = 0; i < dtablesize; ++i)
Packit 6c4009
		    if (dtable[i] != MACH_PORT_NULL)
Packit 6c4009
		      {
Packit 6c4009
			io_t newport;
Packit 6c4009
			err = reauth_io (dtable[i], &newport);
Packit 6c4009
			_hurd_port_free (dtable_cells[i], &ulink_dtable[i],
Packit 6c4009
					 dtable[i]);
Packit 6c4009
			dtable[i] = newport;
Packit 6c4009
			if (err)
Packit 6c4009
			  {
Packit 6c4009
			    while (++i < dtablesize)
Packit 6c4009
			      _hurd_port_free (dtable_cells[i],
Packit 6c4009
					       &ulink_dtable[i], dtable[i]);
Packit 6c4009
			    break;
Packit 6c4009
			  }
Packit 6c4009
		      }
Packit 6c4009
		  ulink_dtable = NULL;
Packit 6c4009
		  dtable_cells = NULL;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      reauth = 1;
Packit 6c4009
    }
Packit 6c4009
  __mutex_unlock (&_hurd_id.lock);
Packit 6c4009
Packit 6c4009
  /* The information is all set up now.  Try to exec the file.  */
Packit 6c4009
  if (!err)
Packit 6c4009
    {
Packit 6c4009
      int flags;
Packit 6c4009
Packit 6c4009
      if (pdp)
Packit 6c4009
	{
Packit 6c4009
	  /* Request the exec server to deallocate some ports from us if
Packit 6c4009
	     the exec succeeds.  The init ports and descriptor ports will
Packit 6c4009
	     arrive in the new program's exec_startup message.  If we
Packit 6c4009
	     failed to deallocate them, the new program would have
Packit 6c4009
	     duplicate user references for them.  But we cannot deallocate
Packit 6c4009
	     them ourselves, because we must still have them after a failed
Packit 6c4009
	     exec call.  */
Packit 6c4009
Packit 6c4009
	  for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
	    *pdp++ = ports[i];
Packit 6c4009
	  for (i = 0; i < dtablesize; ++i)
Packit 6c4009
	    *pdp++ = dtable[i];
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      flags = 0;
Packit 6c4009
#ifdef EXEC_SIGTRAP
Packit 6c4009
      /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is
Packit 6c4009
	 propagated through exec by INIT_TRACEMASK, so this checks if
Packit 6c4009
	 PTRACE_TRACEME has been called in this process in any of its
Packit 6c4009
	 current or prior lives.  */
Packit 6c4009
      if (__sigismember (&_hurdsig_traced, SIGKILL))
Packit 6c4009
	flags |= EXEC_SIGTRAP;
Packit 6c4009
#endif
Packit 6c4009
      err = __file_exec_paths (file, task, flags,
Packit 6c4009
			       path ? path : "",
Packit 6c4009
			       abspath ? abspath : "",
Packit 6c4009
			       args, argslen, env, envlen,
Packit 6c4009
			       dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
Packit 6c4009
			       ports, MACH_MSG_TYPE_COPY_SEND,
Packit 6c4009
			       _hurd_nports,
Packit 6c4009
			       ints, INIT_INT_MAX,
Packit 6c4009
			       please_dealloc, pdp - please_dealloc,
Packit 6c4009
			       &_hurd_msgport,
Packit 6c4009
			       task == __mach_task_self () ? 1 : 0);
Packit 6c4009
      /* Fall back for backwards compatibility.  This can just be removed
Packit 6c4009
         when __file_exec goes away.  */
Packit 6c4009
      if (err == MIG_BAD_ID)
Packit 6c4009
	err = __file_exec (file, task, flags,
Packit 6c4009
			   args, argslen, env, envlen,
Packit 6c4009
			   dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize,
Packit 6c4009
			   ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports,
Packit 6c4009
			   ints, INIT_INT_MAX,
Packit 6c4009
			   please_dealloc, pdp - please_dealloc,
Packit 6c4009
			   &_hurd_msgport,
Packit 6c4009
			   task == __mach_task_self () ? 1 : 0);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Release references to the standard ports.  */
Packit 6c4009
  for (i = 0; i < _hurd_nports; ++i)
Packit 6c4009
    if ((i == INIT_PORT_PROC && task != __mach_task_self ())
Packit 6c4009
	|| (reauth && (i == INIT_PORT_AUTH
Packit 6c4009
		       || i == INIT_PORT_CRDIR || i == INIT_PORT_CWDIR)))
Packit 6c4009
      __mach_port_deallocate (__mach_task_self (), ports[i]);
Packit 6c4009
    else
Packit 6c4009
      free_port (i);
Packit 6c4009
Packit 6c4009
  /* Release references to the file descriptor ports.  */
Packit 6c4009
  if (ulink_dtable != NULL)
Packit 6c4009
    {
Packit 6c4009
      for (i = 0; i < dtablesize; ++i)
Packit 6c4009
	if (dtable[i] != MACH_PORT_NULL)
Packit 6c4009
	  _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]);
Packit 6c4009
    }
Packit 6c4009
  else if (dtable && dtable != _hurd_init_dtable)
Packit 6c4009
    for (i = 0; i < dtablesize; ++i)
Packit 6c4009
      __mach_port_deallocate (__mach_task_self (), dtable[i]);
Packit 6c4009
Packit 6c4009
  /* Release lock on the file descriptor table. */
Packit 6c4009
  __mutex_unlock (&_hurd_dtable_lock);
Packit 6c4009
Packit 6c4009
  /* Safe to let signals happen now.  */
Packit 6c4009
  _hurd_critical_section_unlock (ss);
Packit 6c4009
Packit 6c4009
 outargs:
Packit 6c4009
  free (args);
Packit 6c4009
 outenv:
Packit 6c4009
  free (env);
Packit 6c4009
  return err;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (_hurd_exec_paths)