1211 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1211 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* spawn.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
 | |
|    2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <process.h>
 | |
| #include <sys/wait.h>
 | |
| #include <wchar.h>
 | |
| #include <ctype.h>
 | |
| #include <sys/cygwin.h>
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "sigproc.h"
 | |
| #include "pinfo.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "dtable.h"
 | |
| #include "cygheap.h"
 | |
| #include "child_info.h"
 | |
| #include "environ.h"
 | |
| #include "cygtls.h"
 | |
| #include "tls_pbuf.h"
 | |
| #include "winf.h"
 | |
| #include "ntdll.h"
 | |
| 
 | |
| static const suffix_info exe_suffixes[] =
 | |
| {
 | |
|   suffix_info ("", 1),
 | |
|   suffix_info (".exe", 1),
 | |
|   suffix_info (".com"),
 | |
|   suffix_info (NULL)
 | |
| };
 | |
| 
 | |
| /* Add .exe to PROG if not already present and see if that exists.
 | |
|    If not, return PROG (converted from posix to win32 rules if necessary).
 | |
|    The result is always BUF.
 | |
| 
 | |
|    Returns (possibly NULL) suffix */
 | |
| 
 | |
| static const char *
 | |
| perhaps_suffix (const char *prog, path_conv& buf, int& err, unsigned opt)
 | |
| {
 | |
|   const char *ext;
 | |
| 
 | |
|   err = 0;
 | |
|   debug_printf ("prog '%s'", prog);
 | |
|   buf.check (prog, PC_SYM_FOLLOW | PC_NULLEMPTY | PC_POSIX,
 | |
| 	     (opt & FE_DLL) ? stat_suffixes : exe_suffixes);
 | |
| 
 | |
|   if (buf.isdir ())
 | |
|     {
 | |
|       err = EACCES;
 | |
|       ext = NULL;
 | |
|     }
 | |
|   else if (!buf.exists ())
 | |
|     {
 | |
|       err = ENOENT;
 | |
|       ext = NULL;
 | |
|     }
 | |
|   else if (buf.known_suffix ())
 | |
|     ext = buf.get_win32 () + (buf.known_suffix () - buf.get_win32 ());
 | |
|   else
 | |
|     ext = strchr (buf.get_win32 (), '\0');
 | |
| 
 | |
|   debug_printf ("buf %s, suffix found '%s'", (char *) buf.get_win32 (), ext);
 | |
|   return ext;
 | |
| }
 | |
| 
 | |
| /* Find an executable name, possibly by appending known executable suffixes
 | |
|    to it.  The path_conv struct 'buf' is filled and contains both, win32 and
 | |
|    posix path of the target file.  Any found suffix is returned in known_suffix.
 | |
|    Eventually the posix path in buf is overwritten with the exact path as it
 | |
|    gets constructed for the path search.  The reason is that the path is used
 | |
|    to create argv[0] in av::setup, and this requires that the filename stays
 | |
|    intact, instead of being resolved if the file is a symlink.
 | |
| 
 | |
|    If the file is not found and !FE_NNF then the POSIX version of name is
 | |
|    placed in buf and returned.  Otherwise the contents of buf is undefined
 | |
|    and NULL is returned.  */
 | |
| const char * __reg3
 | |
| find_exec (const char *name, path_conv& buf, const char *search,
 | |
| 	   unsigned opt, const char **known_suffix)
 | |
| {
 | |
|   const char *suffix = "";
 | |
|   const char *retval = NULL;
 | |
|   tmp_pathbuf tp;
 | |
|   char *tmp_path;
 | |
|   char *tmp = tp.c_get ();
 | |
|   bool has_slash = !!strpbrk (name, "/\\");
 | |
|   int err = 0;
 | |
| 
 | |
|   debug_printf ("find_exec (%s)", name);
 | |
| 
 | |
|   /* Check to see if file can be opened as is first. */
 | |
|   if ((has_slash || opt & FE_CWD)
 | |
|       && (suffix = perhaps_suffix (name, buf, err, opt)) != NULL)
 | |
|     {
 | |
|       /* Overwrite potential symlink target with original path.
 | |
| 	 See comment preceeding this method. */
 | |
|       tmp_path = tmp;
 | |
|       if (!has_slash)
 | |
| 	tmp_path = stpcpy (tmp, "./");
 | |
|       stpcpy (tmp_path, name);
 | |
|       buf.set_posix (tmp);
 | |
|       retval = buf.get_posix ();
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   const char *path;
 | |
|   /* If it starts with a slash, it's a PATH-like pathlist.  Otherwise it's
 | |
|      the name of an environment variable. */
 | |
|   if (strchr (search, '/'))
 | |
|     *stpncpy (tmp, search, NT_MAX_PATH - 1) = '\0';
 | |
|   else if (has_slash || isdrive (name) || !(path = getenv (search)) || !*path)
 | |
|     goto errout;
 | |
|   else
 | |
|     *stpncpy (tmp, path, NT_MAX_PATH - 1) = '\0';
 | |
| 
 | |
|   path = tmp;
 | |
|   debug_printf ("searchpath %s", path);
 | |
| 
 | |
|   tmp_path = tp.c_get ();
 | |
|   do
 | |
|     {
 | |
|       char *eotmp = strccpy (tmp_path, &path, ':');
 | |
|       /* An empty path or '.' means the current directory, but we've
 | |
| 	 already tried that.  */
 | |
|       if ((opt & FE_CWD) && (tmp_path[0] == '\0'
 | |
| 			     || (tmp_path[0] == '.' && tmp_path[1] == '\0')))
 | |
| 	continue;
 | |
| 
 | |
|       *eotmp++ = '/';
 | |
|       stpcpy (eotmp, name);
 | |
| 
 | |
|       debug_printf ("trying %s", tmp_path);
 | |
| 
 | |
|       int err1;
 | |
| 
 | |
|       if ((suffix = perhaps_suffix (tmp_path, buf, err1, opt)) != NULL)
 | |
| 	{
 | |
| 	  if (buf.has_acls () && check_file_access (buf, X_OK, true))
 | |
| 	    continue;
 | |
| 	  /* Overwrite potential symlink target with original path.
 | |
| 	     See comment preceeding this method. */
 | |
| 	  buf.set_posix (tmp_path);
 | |
| 	  retval = buf.get_posix ();
 | |
| 	  goto out;
 | |
| 	}
 | |
| 
 | |
|     }
 | |
|   while (*path && *++path);
 | |
| 
 | |
|  errout:
 | |
|   /* Couldn't find anything in the given path.
 | |
|      Take the appropriate action based on FE_NNF. */
 | |
|   if (!(opt & FE_NNF))
 | |
|     {
 | |
|       buf.check (name, PC_SYM_FOLLOW | PC_POSIX);
 | |
|       retval = buf.get_posix ();
 | |
|     }
 | |
| 
 | |
|  out:
 | |
|   debug_printf ("%s = find_exec (%s)", (char *) buf.get_posix (), name);
 | |
|   if (known_suffix)
 | |
|     *known_suffix = suffix ?: strchr (buf.get_win32 (), '\0');
 | |
|   if (!retval && err)
 | |
|     set_errno (err);
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| /* Utility for child_info_spawn::worker.  */
 | |
| 
 | |
| static HANDLE
 | |
| handle (int fd, bool writing)
 | |
| {
 | |
|   HANDLE h;
 | |
|   cygheap_fdget cfd (fd);
 | |
| 
 | |
|   if (cfd < 0)
 | |
|     h = INVALID_HANDLE_VALUE;
 | |
|   else if (cfd->close_on_exec ())
 | |
|     h = INVALID_HANDLE_VALUE;
 | |
|   else if (!writing)
 | |
|     h = cfd->get_handle ();
 | |
|   else
 | |
|     h = cfd->get_output_handle ();
 | |
| 
 | |
|   return h;
 | |
| }
 | |
| 
 | |
| int
 | |
| iscmd (const char *argv0, const char *what)
 | |
| {
 | |
|   int n;
 | |
|   n = strlen (argv0) - strlen (what);
 | |
|   if (n >= 2 && argv0[1] != ':')
 | |
|     return 0;
 | |
|   return n >= 0 && strcasematch (argv0 + n, what) &&
 | |
| 	 (n == 0 || isdirsep (argv0[n - 1]));
 | |
| }
 | |
| 
 | |
| #define ILLEGAL_SIG_FUNC_PTR ((_sig_func_ptr) (-2))
 | |
| struct system_call_handle
 | |
| {
 | |
|   _sig_func_ptr oldint;
 | |
|   _sig_func_ptr oldquit;
 | |
|   sigset_t oldmask;
 | |
|   bool is_system_call ()
 | |
|   {
 | |
|     return oldint != ILLEGAL_SIG_FUNC_PTR;
 | |
|   }
 | |
|   system_call_handle (bool issystem)
 | |
|   {
 | |
|     if (!issystem)
 | |
|       oldint = ILLEGAL_SIG_FUNC_PTR;
 | |
|     else
 | |
|       {
 | |
| 	sig_send (NULL, __SIGHOLD);
 | |
| 	oldint = NULL;
 | |
|       }
 | |
|   }
 | |
|   void arm()
 | |
|   {
 | |
|     if (is_system_call ())
 | |
|       {
 | |
| 	sigset_t child_block;
 | |
| 	oldint = signal (SIGINT,  SIG_IGN);
 | |
| 	oldquit = signal (SIGQUIT, SIG_IGN);
 | |
| 	sigemptyset (&child_block);
 | |
| 	sigaddset (&child_block, SIGCHLD);
 | |
| 	sigprocmask (SIG_BLOCK, &child_block, &oldmask);
 | |
| 	sig_send (NULL, __SIGNOHOLD);
 | |
|       }
 | |
|   }
 | |
|   ~system_call_handle ()
 | |
|   {
 | |
|     if (is_system_call ())
 | |
|       {
 | |
| 	signal (SIGINT, oldint);
 | |
| 	signal (SIGQUIT, oldquit);
 | |
| 	sigprocmask (SIG_SETMASK, &oldmask, NULL);
 | |
|       }
 | |
|   }
 | |
| # undef cleanup
 | |
| };
 | |
| 
 | |
| child_info_spawn NO_COPY ch_spawn;
 | |
| 
 | |
| int
 | |
| child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 | |
| 			  const char *const envp[], int mode,
 | |
| 			  int in__stdin, int in__stdout)
 | |
| {
 | |
|   bool rc;
 | |
|   pid_t cygpid;
 | |
|   int res = -1;
 | |
| 
 | |
|   /* Check if we have been called from exec{lv}p or spawn{lv}p and mask
 | |
|      mode to keep only the spawn mode. */
 | |
|   bool p_type_exec = !!(mode & _P_PATH_TYPE_EXEC);
 | |
|   mode = _P_MODE (mode);
 | |
| 
 | |
|   if (prog_arg == NULL)
 | |
|     {
 | |
|       syscall_printf ("prog_arg is NULL");
 | |
|       set_errno (EFAULT);	/* As on Linux. */
 | |
|       return -1;
 | |
|     }
 | |
|   if (!prog_arg[0])
 | |
|     {
 | |
|       syscall_printf ("prog_arg is empty");
 | |
|       set_errno (ENOENT);	/* Per POSIX */
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   syscall_printf ("mode = %d, prog_arg = %.9500s", mode, prog_arg);
 | |
| 
 | |
|   /* FIXME: This is no error condition on Linux. */
 | |
|   if (argv == NULL)
 | |
|     {
 | |
|       syscall_printf ("argv is NULL");
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   av newargv;
 | |
|   linebuf cmd;
 | |
|   PWCHAR envblock = NULL;
 | |
|   path_conv real_path;
 | |
|   bool reset_sendsig = false;
 | |
| 
 | |
|   tmp_pathbuf tp;
 | |
|   PWCHAR runpath = tp.w_get ();
 | |
|   int c_flags;
 | |
| 
 | |
|   bool null_app_name = false;
 | |
|   STARTUPINFOW si = {};
 | |
|   int looped = 0;
 | |
| 
 | |
|   system_call_handle system_call (mode == _P_SYSTEM);
 | |
| 
 | |
|   __try
 | |
|     {
 | |
|       child_info_types chtype;
 | |
|       if (mode == _P_OVERLAY)
 | |
| 	chtype = _CH_EXEC;
 | |
|       else
 | |
| 	chtype = _CH_SPAWN;
 | |
| 
 | |
|       moreinfo = cygheap_exec_info::alloc ();
 | |
| 
 | |
|       /* CreateProcess takes one long string that is the command line (sigh).
 | |
| 	 We need to quote any argument that has whitespace or embedded "'s.  */
 | |
| 
 | |
|       int ac;
 | |
|       for (ac = 0; argv[ac]; ac++)
 | |
| 	/* nothing */;
 | |
| 
 | |
|       int err;
 | |
|       const char *ext;
 | |
|       if ((ext = perhaps_suffix (prog_arg, real_path, err, FE_NADA)) == NULL)
 | |
| 	{
 | |
| 	  set_errno (err);
 | |
| 	  res = -1;
 | |
| 	  __leave;
 | |
| 	}
 | |
| 
 | |
|       res = newargv.setup (prog_arg, real_path, ext, ac, argv, p_type_exec);
 | |
| 
 | |
|       if (res)
 | |
| 	__leave;
 | |
| 
 | |
|       if (!real_path.iscygexec () && ::cygheap->cwd.get_error ())
 | |
| 	{
 | |
| 	  small_printf ("Error: Current working directory %s.\n"
 | |
| 			"Can't start native Windows application from here.\n\n",
 | |
| 			::cygheap->cwd.get_error_desc ());
 | |
| 	  set_errno (::cygheap->cwd.get_error ());
 | |
| 	  res = -1;
 | |
| 	  __leave;
 | |
| 	}
 | |
| 
 | |
|       if (ac == 3 && argv[1][0] == '/' && tolower (argv[1][1]) == 'c' &&
 | |
| 	  (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
 | |
| 	{
 | |
| 	  real_path.check (prog_arg);
 | |
| 	  cmd.add ("\"");
 | |
| 	  if (!real_path.error)
 | |
| 	    cmd.add (real_path.get_win32 ());
 | |
| 	  else
 | |
| 	    cmd.add (argv[0]);
 | |
| 	  cmd.add ("\"");
 | |
| 	  cmd.add (" ");
 | |
| 	  cmd.add (argv[1]);
 | |
| 	  cmd.add (" ");
 | |
| 	  cmd.add (argv[2]);
 | |
| 	  real_path.set_path (argv[0]);
 | |
| 	  null_app_name = true;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (real_path.iscygexec ())
 | |
| 	    {
 | |
| 	      moreinfo->argc = newargv.argc;
 | |
| 	      moreinfo->argv = newargv;
 | |
| 	    }
 | |
| 	  if ((wincmdln || !real_path.iscygexec ())
 | |
| 	       && !cmd.fromargv (newargv, real_path.get_win32 (),
 | |
| 				 real_path.iscygexec ()))
 | |
| 	    {
 | |
| 	      res = -1;
 | |
| 	      __leave;
 | |
| 	    }
 | |
| 
 | |
| 
 | |
| 	  if (mode != _P_OVERLAY || !real_path.iscygexec ()
 | |
| 	      || !DuplicateHandle (GetCurrentProcess (), myself.shared_handle (),
 | |
| 				   GetCurrentProcess (), &moreinfo->myself_pinfo,
 | |
| 				   0, TRUE, DUPLICATE_SAME_ACCESS))
 | |
| 	    moreinfo->myself_pinfo = NULL;
 | |
| 	  else
 | |
| 	    VerifyHandle (moreinfo->myself_pinfo);
 | |
| 	}
 | |
| 
 | |
|       PROCESS_INFORMATION pi;
 | |
|       pi.hProcess = pi.hThread = NULL;
 | |
|       pi.dwProcessId = pi.dwThreadId = 0;
 | |
| 
 | |
|       /* Set up needed handles for stdio */
 | |
|       si.dwFlags = STARTF_USESTDHANDLES;
 | |
|       si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false);
 | |
|       si.hStdOutput = handle ((in__stdout < 0 ? 1 : in__stdout), true);
 | |
|       si.hStdError = handle (2, true);
 | |
| 
 | |
|       si.cb = sizeof (si);
 | |
| 
 | |
|       c_flags = GetPriorityClass (GetCurrentProcess ());
 | |
|       sigproc_printf ("priority class %d", c_flags);
 | |
| 
 | |
|       c_flags |= CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT;
 | |
| 
 | |
|       if (wincap.has_program_compatibility_assistant ())
 | |
| 	{
 | |
| 	  /* We're adding the CREATE_BREAKAWAY_FROM_JOB flag here to workaround
 | |
| 	     issues with the "Program Compatibility Assistant (PCA) Service"
 | |
| 	     starting with Windows Vista.  For some reason, when starting long
 | |
| 	     running sessions from mintty(*), the affected svchost.exe process
 | |
| 	     takes more and more memory and at one point takes over the CPU.  At
 | |
| 	     this point the machine becomes unresponsive.  The only way to get
 | |
| 	     back to normal is to stop the entire mintty session, or to stop the
 | |
| 	     PCA service.  However, a process which is controlled by PCA is part
 | |
| 	     of a compatibility job, which allows child processes to break away
 | |
| 	     from the job.  This helps to avoid this issue.
 | |
| 
 | |
| 	     First we call IsProcessInJob.  It fetches the information whether or
 | |
| 	     not we're part of a job 20 times faster than QueryInformationJobObject.
 | |
| 
 | |
| 	     (*) Note that this is not mintty's fault.  It has just been observed
 | |
| 	     with mintty in the first place.  See the archives for more info:
 | |
| 	     http://cygwin.com/ml/cygwin-developers/2012-02/msg00018.html */
 | |
| 
 | |
| 	  JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo;
 | |
| 	  BOOL is_in_job;
 | |
| 
 | |
| 	  if (IsProcessInJob (GetCurrentProcess (), NULL, &is_in_job)
 | |
| 	      && is_in_job
 | |
| 	      && QueryInformationJobObject (NULL, JobObjectBasicLimitInformation,
 | |
| 					 &jobinfo, sizeof jobinfo, NULL)
 | |
| 	      && (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK
 | |
| 					| JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
 | |
| 	    {
 | |
| 	      debug_printf ("Add CREATE_BREAKAWAY_FROM_JOB");
 | |
| 	      c_flags |= CREATE_BREAKAWAY_FROM_JOB;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if (mode == _P_DETACH)
 | |
| 	c_flags |= DETACHED_PROCESS;
 | |
|       else
 | |
| 	fhandler_console::need_invisible ();
 | |
| 
 | |
|       if (mode != _P_OVERLAY)
 | |
| 	myself->exec_sendsig = NULL;
 | |
|       else
 | |
| 	{
 | |
| 	  /* Reset sendsig so that any process which wants to send a signal
 | |
| 	     to this pid will wait for the new process to become active.
 | |
| 	     Save the old value in case the exec fails.  */
 | |
| 	  if (!myself->exec_sendsig)
 | |
| 	    {
 | |
| 	      myself->exec_sendsig = myself->sendsig;
 | |
| 	      myself->exec_dwProcessId = myself->dwProcessId;
 | |
| 	      myself->sendsig = NULL;
 | |
| 	      reset_sendsig = true;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if (null_app_name)
 | |
| 	runpath = NULL;
 | |
|       else
 | |
| 	{
 | |
| 	  USHORT len = real_path.get_nt_native_path ()->Length / sizeof (WCHAR);
 | |
| 	  if (RtlEqualUnicodePathPrefix (real_path.get_nt_native_path (),
 | |
| 					 &ro_u_natp, FALSE))
 | |
| 	    {
 | |
| 	      runpath = real_path.get_wide_win32_path (runpath);
 | |
| 	      /* If the executable path length is < MAX_PATH, make sure the long
 | |
| 		 path win32 prefix is removed from the path to make subsequent
 | |
| 		 not long path aware native Win32 child processes happy. */
 | |
| 	      if (len < MAX_PATH + 4)
 | |
| 		{
 | |
| 		  if (runpath[5] == ':')
 | |
| 		    runpath += 4;
 | |
| 		  else if (len < MAX_PATH + 6)
 | |
| 		    *(runpath += 6) = L'\\';
 | |
| 		}
 | |
| 	    }
 | |
| 	  else if (len < NT_MAX_PATH - ro_u_globalroot.Length / sizeof (WCHAR))
 | |
| 	    {
 | |
| 	      UNICODE_STRING rpath;
 | |
| 
 | |
| 	      RtlInitEmptyUnicodeString (&rpath, runpath,
 | |
| 					 (NT_MAX_PATH - 1) * sizeof (WCHAR));
 | |
| 	      RtlCopyUnicodeString (&rpath, &ro_u_globalroot);
 | |
| 	      RtlAppendUnicodeStringToString (&rpath,
 | |
| 					      real_path.get_nt_native_path ());
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      set_errno (ENAMETOOLONG);
 | |
| 	      res = -1;
 | |
| 	      __leave;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       cygbench ("spawn-worker");
 | |
| 
 | |
|       if (!real_path.iscygexec())
 | |
| 	::cygheap->fdtab.set_file_pointers_for_exec ();
 | |
| 
 | |
|       /* If we switch the user, merge the user's Windows environment. */
 | |
|       bool switch_user = ::cygheap->user.issetuid ()
 | |
| 			 && (::cygheap->user.saved_uid
 | |
| 			     != ::cygheap->user.real_uid);
 | |
|       moreinfo->envp = build_env (envp, envblock, moreinfo->envc,
 | |
| 				  real_path.iscygexec (),
 | |
| 				  switch_user ? ::cygheap->user.primary_token ()
 | |
| 					      : NULL);
 | |
|       if (!moreinfo->envp || !envblock)
 | |
| 	{
 | |
| 	  set_errno (E2BIG);
 | |
| 	  res = -1;
 | |
| 	  __leave;
 | |
| 	}
 | |
|       set (chtype, real_path.iscygexec ());
 | |
|       __stdin = in__stdin;
 | |
|       __stdout = in__stdout;
 | |
|       record_children ();
 | |
| 
 | |
|       si.lpReserved2 = (LPBYTE) this;
 | |
|       si.cbReserved2 = sizeof (*this);
 | |
| 
 | |
|       /* Depends on set call above.
 | |
| 	 Some file types might need extra effort in the parent after CreateProcess
 | |
| 	 and before copying the datastructures to the child.  So we have to start
 | |
| 	 the child in suspend state, unfortunately, to avoid a race condition. */
 | |
|       if (!newargv.win16_exe
 | |
| 	  && (!iscygwin () || mode != _P_OVERLAY
 | |
| 	      || ::cygheap->fdtab.need_fixup_before ()))
 | |
| 	c_flags |= CREATE_SUSPENDED;
 | |
|       /* If a native application should be spawned, we test here if the spawning
 | |
| 	 process is running in a console and, if so, if it's a foreground or
 | |
| 	 background process.  If it's a background process, we start the native
 | |
| 	 process with the CREATE_NEW_PROCESS_GROUP flag set.  This lets the native
 | |
| 	 process ignore Ctrl-C by default.  If we don't do that, pressing Ctrl-C
 | |
| 	 in a console will break native processes running in the background,
 | |
| 	 because the Ctrl-C event is sent to all processes in the console, unless
 | |
| 	 they ignore it explicitely.  CREATE_NEW_PROCESS_GROUP does that for us. */
 | |
|       if (!iscygwin () && fhandler_console::exists ()
 | |
| 	  && fhandler_console::tc_getpgid () != myself->pgid)
 | |
| 	c_flags |= CREATE_NEW_PROCESS_GROUP;
 | |
|       refresh_cygheap ();
 | |
| 
 | |
|       if (mode == _P_DETACH)
 | |
| 	/* all set */;
 | |
|       else if (mode != _P_OVERLAY || !my_wr_proc_pipe)
 | |
| 	prefork ();
 | |
|       else
 | |
| 	wr_proc_pipe = my_wr_proc_pipe;
 | |
| 
 | |
|       /* Don't allow child to inherit these handles if it's not a Cygwin program.
 | |
| 	 wr_proc_pipe will be injected later.  parent won't be used by the child
 | |
| 	 so there is no reason for the child to have it open as it can confuse
 | |
| 	 ps into thinking that children of windows processes are all part of
 | |
| 	 the same "execed" process.
 | |
| 	 FIXME: Someday, make it so that parent is never created when starting
 | |
| 	 non-Cygwin processes. */
 | |
|       if (!iscygwin ())
 | |
| 	{
 | |
| 	  SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, 0);
 | |
| 	  SetHandleInformation (parent, HANDLE_FLAG_INHERIT, 0);
 | |
| 	}
 | |
|       /* FIXME: racy */
 | |
|       if (mode != _P_OVERLAY)
 | |
| 	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, 0);
 | |
|       parent_winpid = GetCurrentProcessId ();
 | |
| 
 | |
|     loop:
 | |
|       /* When ruid != euid we create the new process under the current original
 | |
| 	 account and impersonate in child, this way maintaining the different
 | |
| 	 effective vs. real ids.
 | |
| 	 FIXME: If ruid != euid and ruid != saved_uid we currently give
 | |
| 	 up on ruid. The new process will have ruid == euid. */
 | |
|       ::cygheap->user.deimpersonate ();
 | |
| 
 | |
|       if (!real_path.iscygexec () && mode == _P_OVERLAY)
 | |
| 	myself->process_state |= PID_NOTCYGWIN;
 | |
| 
 | |
|       wchar_t wcmd[(size_t) cmd];
 | |
|       if (!::cygheap->user.issetuid ()
 | |
| 	  || (::cygheap->user.saved_uid == ::cygheap->user.real_uid
 | |
| 	      && ::cygheap->user.saved_gid == ::cygheap->user.real_gid
 | |
| 	      && !::cygheap->user.groups.issetgroups ()
 | |
| 	      && !::cygheap->user.setuid_to_restricted))
 | |
| 	{
 | |
| 	  rc = CreateProcessW (runpath,	  /* image name - with full path */
 | |
| 			       cmd.wcs (wcmd),/* what was passed to exec */
 | |
| 			       &sec_none_nih, /* process security attrs */
 | |
| 			       &sec_none_nih, /* thread security attrs */
 | |
| 			       TRUE,	  /* inherit handles from parent */
 | |
| 			       c_flags,
 | |
| 			       envblock,	  /* environment */
 | |
| 			       NULL,
 | |
| 			       &si,
 | |
| 			       &pi);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* Give access to myself */
 | |
| 	  if (mode == _P_OVERLAY)
 | |
| 	    myself.set_acl();
 | |
| 
 | |
| 	  WCHAR wstname[1024] = { L'\0' };
 | |
| 	  HWINSTA hwst_orig = NULL, hwst = NULL;
 | |
| 	  HDESK hdsk_orig = NULL, hdsk = NULL;
 | |
| 	  PSECURITY_ATTRIBUTES sa;
 | |
| 	  DWORD n;
 | |
| 
 | |
| 	  hwst_orig = GetProcessWindowStation ();
 | |
| 	  hdsk_orig = GetThreadDesktop (GetCurrentThreadId ());
 | |
| 	  GetUserObjectInformationW (hwst_orig, UOI_NAME, wstname, 1024, &n);
 | |
| 	  /* Prior to Vista it was possible to start a service with the
 | |
| 	     "Interact with desktop" flag.  This started the service in the
 | |
| 	     interactive window station of the console.  A big security
 | |
| 	     risk, but we don't want to disable this behaviour for older
 | |
| 	     OSes because it's still heavily used by some users.  They have
 | |
| 	     been warned. */
 | |
| 	  if (!::cygheap->user.setuid_to_restricted
 | |
| 	      && wcscasecmp (wstname, L"WinSta0") != 0)
 | |
| 	    {
 | |
| 	      WCHAR sid[128];
 | |
| 
 | |
| 	      sa = sec_user ((PSECURITY_ATTRIBUTES) alloca (1024),
 | |
| 			     ::cygheap->user.sid ());
 | |
| 	      /* We're creating a window station per user, not per logon session.
 | |
| 		 First of all we might not have a valid logon session for
 | |
| 		 the user (logon by create_token), and second, it doesn't
 | |
| 		 make sense in terms of security to create a new window
 | |
| 		 station for every logon of the same user.  It just fills up
 | |
| 		 the system with window stations for no good reason. */
 | |
| 	      hwst = CreateWindowStationW (::cygheap->user.get_windows_id (sid),
 | |
| 					   0, GENERIC_READ | GENERIC_WRITE, sa);
 | |
| 	      if (!hwst)
 | |
| 		system_printf ("CreateWindowStation failed, %E");
 | |
| 	      else if (!SetProcessWindowStation (hwst))
 | |
| 		system_printf ("SetProcessWindowStation failed, %E");
 | |
| 	      else if (!(hdsk = CreateDesktopW (L"Default", NULL, NULL, 0,
 | |
| 						GENERIC_ALL, sa)))
 | |
| 		system_printf ("CreateDesktop failed, %E");
 | |
| 	      else
 | |
| 		{
 | |
| 		  wcpcpy (wcpcpy (wstname, sid), L"\\Default");
 | |
| 		  si.lpDesktop = wstname;
 | |
| 		  debug_printf ("Desktop: %W", si.lpDesktop);
 | |
| 		}
 | |
| 	    }
 | |
| 
 | |
| 	  rc = CreateProcessAsUserW (::cygheap->user.primary_token (),
 | |
| 			       runpath,	  /* image name - with full path */
 | |
| 			       cmd.wcs (wcmd),/* what was passed to exec */
 | |
| 			       &sec_none_nih, /* process security attrs */
 | |
| 			       &sec_none_nih, /* thread security attrs */
 | |
| 			       TRUE,	  /* inherit handles from parent */
 | |
| 			       c_flags,
 | |
| 			       envblock,	  /* environment */
 | |
| 			       NULL,
 | |
| 			       &si,
 | |
| 			       &pi);
 | |
| 	  if (hwst)
 | |
| 	    {
 | |
| 	      SetProcessWindowStation (hwst_orig);
 | |
| 	      CloseWindowStation (hwst);
 | |
| 	    }
 | |
| 	  if (hdsk)
 | |
| 	    {
 | |
| 	      SetThreadDesktop (hdsk_orig);
 | |
| 	      CloseDesktop (hdsk);
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if (mode != _P_OVERLAY)
 | |
| 	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT,
 | |
| 			      HANDLE_FLAG_INHERIT);
 | |
| 
 | |
|       /* Set errno now so that debugging messages from it appear before our
 | |
| 	 final debugging message [this is a general rule for debugging
 | |
| 	 messages].  */
 | |
|       if (!rc)
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  syscall_printf ("CreateProcess failed, %E");
 | |
| 	  /* If this was a failed exec, restore the saved sendsig. */
 | |
| 	  if (reset_sendsig)
 | |
| 	    {
 | |
| 	      myself->sendsig = myself->exec_sendsig;
 | |
| 	      myself->exec_sendsig = NULL;
 | |
| 	    }
 | |
| 	  myself->process_state &= ~PID_NOTCYGWIN;
 | |
| 	  /* Reset handle inheritance to default when the execution of a non-Cygwin
 | |
| 	     process fails.  Only need to do this for _P_OVERLAY since the handle will
 | |
| 	     be closed otherwise.  Don't need to do this for 'parent' since it will
 | |
| 	     be closed in every case.  See FIXME above. */
 | |
| 	  if (!iscygwin () && mode == _P_OVERLAY)
 | |
| 	    SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT,
 | |
| 				  HANDLE_FLAG_INHERIT);
 | |
| 	  if (wr_proc_pipe == my_wr_proc_pipe)
 | |
| 	    wr_proc_pipe = NULL;	/* We still own it: don't nuke in destructor */
 | |
| 
 | |
| 	  /* Restore impersonation. In case of _P_OVERLAY this isn't
 | |
| 	     allowed since it would overwrite child data. */
 | |
| 	  if (mode != _P_OVERLAY)
 | |
| 	    ::cygheap->user.reimpersonate ();
 | |
| 
 | |
| 	  res = -1;
 | |
| 	  __leave;
 | |
| 	}
 | |
| 
 | |
|       /* The CREATE_SUSPENDED case is handled below */
 | |
|       if (iscygwin () && !(c_flags & CREATE_SUSPENDED))
 | |
| 	strace.write_childpid (pi.dwProcessId);
 | |
| 
 | |
|       /* Fixup the parent data structures if needed and resume the child's
 | |
| 	 main thread. */
 | |
|       if (::cygheap->fdtab.need_fixup_before ())
 | |
| 	::cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
 | |
| 
 | |
|       if (mode != _P_OVERLAY)
 | |
| 	cygpid = cygwin_pid (pi.dwProcessId);
 | |
|       else
 | |
| 	cygpid = myself->pid;
 | |
| 
 | |
|       /* We print the original program name here so the user can see that too.  */
 | |
|       syscall_printf ("pid %d, prog_arg %s, cmd line %.9500s)",
 | |
| 		      rc ? cygpid : (unsigned int) -1, prog_arg, (const char *) cmd);
 | |
| 
 | |
|       /* Name the handle similarly to proc_subproc. */
 | |
|       ProtectHandle1 (pi.hProcess, childhProc);
 | |
| 
 | |
|       if (mode == _P_OVERLAY)
 | |
| 	{
 | |
| 	  myself->dwProcessId = pi.dwProcessId;
 | |
| 	  strace.execing = 1;
 | |
| 	  myself.hProcess = hExeced = pi.hProcess;
 | |
| 	  real_path.get_wide_win32_path (myself->progname); // FIXME: race?
 | |
| 	  sigproc_printf ("new process name %W", myself->progname);
 | |
| 	  if (!iscygwin ())
 | |
| 	    close_all_files ();
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  myself->set_has_pgid_children ();
 | |
| 	  ProtectHandle (pi.hThread);
 | |
| 	  pinfo child (cygpid,
 | |
| 		       PID_IN_USE | (real_path.iscygexec () ? 0 : PID_NOTCYGWIN));
 | |
| 	  if (!child)
 | |
| 	    {
 | |
| 	      syscall_printf ("pinfo failed");
 | |
| 	      if (get_errno () != ENOMEM)
 | |
| 		set_errno (EAGAIN);
 | |
| 	      res = -1;
 | |
| 	      __leave;
 | |
| 	    }
 | |
| 	  child->dwProcessId = pi.dwProcessId;
 | |
| 	  child.hProcess = pi.hProcess;
 | |
| 
 | |
| 	  real_path.get_wide_win32_path (child->progname);
 | |
| 	  /* FIXME: This introduces an unreferenced, open handle into the child.
 | |
| 	     The purpose is to keep the pid shared memory open so that all of
 | |
| 	     the fields filled out by child.remember do not disappear and so there
 | |
| 	     is not a brief period during which the pid is not available.
 | |
| 	     However, we should try to find another way to do this eventually. */
 | |
| 	  DuplicateHandle (GetCurrentProcess (), child.shared_handle (),
 | |
| 			   pi.hProcess, NULL, 0, 0, DUPLICATE_SAME_ACCESS);
 | |
| 	  child->start_time = time (NULL); /* Register child's starting time. */
 | |
| 	  child->nice = myself->nice;
 | |
| 	  postfork (child);
 | |
| 	  if (!child.remember (mode == _P_DETACH))
 | |
| 	    {
 | |
| 	      /* FIXME: Child in strange state now */
 | |
| 	      CloseHandle (pi.hProcess);
 | |
| 	      ForceCloseHandle (pi.hThread);
 | |
| 	      res = -1;
 | |
| 	      __leave;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       /* Start the child running */
 | |
|       if (c_flags & CREATE_SUSPENDED)
 | |
| 	{
 | |
| 	  /* Inject a non-inheritable wr_proc_pipe handle into child so that we
 | |
| 	     can accurately track when the child exits without keeping this
 | |
| 	     process waiting around for it to exit.  */
 | |
| 	  if (!iscygwin ())
 | |
| 	    DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, NULL,
 | |
| 			     0, false, DUPLICATE_SAME_ACCESS);
 | |
| 	  ResumeThread (pi.hThread);
 | |
| 	  if (iscygwin ())
 | |
| 	    strace.write_childpid (pi.dwProcessId);
 | |
| 	}
 | |
|       ForceCloseHandle (pi.hThread);
 | |
| 
 | |
|       sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
 | |
| 
 | |
|       bool synced;
 | |
|       if ((mode == _P_DETACH || mode == _P_NOWAIT) && !iscygwin ())
 | |
| 	synced = false;
 | |
|       else
 | |
| 	/* Just mark a non-cygwin process as 'synced'.  We will still eventually
 | |
| 	   wait for it to exit in maybe_set_exit_code_from_windows(). */
 | |
| 	synced = iscygwin () ? sync (pi.dwProcessId, pi.hProcess, INFINITE) : true;
 | |
| 
 | |
|       switch (mode)
 | |
| 	{
 | |
| 	case _P_OVERLAY:
 | |
| 	  myself.hProcess = pi.hProcess;
 | |
| 	  if (!synced)
 | |
| 	    {
 | |
| 	      if (!proc_retry (pi.hProcess))
 | |
| 		{
 | |
| 		  looped++;
 | |
| 		  goto loop;
 | |
| 		}
 | |
| 	      close_all_files (true);
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      if (iscygwin ())
 | |
| 		close_all_files (true);
 | |
| 	      if (!my_wr_proc_pipe
 | |
| 		  && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT)
 | |
| 		wait_for_myself ();
 | |
| 	    }
 | |
| 	  myself.exit (EXITCODE_NOSET);
 | |
| 	  break;
 | |
| 	case _P_WAIT:
 | |
| 	case _P_SYSTEM:
 | |
| 	  system_call.arm ();
 | |
| 	  if (waitpid (cygpid, &res, 0) != cygpid)
 | |
| 	    res = -1;
 | |
| 	  break;
 | |
| 	case _P_DETACH:
 | |
| 	  res = 0;	/* Lost all memory of this child. */
 | |
| 	  break;
 | |
| 	case _P_NOWAIT:
 | |
| 	case _P_NOWAITO:
 | |
| 	case _P_VFORK:
 | |
| 	  res = cygpid;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
|   __except (NO_ERROR)
 | |
|     {
 | |
|       if (get_errno () == ENOMEM)
 | |
| 	set_errno (E2BIG);
 | |
|       else
 | |
| 	set_errno (EFAULT);
 | |
|       res = -1;
 | |
|     }
 | |
|   __endtry
 | |
|   this->cleanup ();
 | |
|   if (envblock)
 | |
|     free (envblock);
 | |
|   return (int) res;
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| cwait (int *result, int pid, int)
 | |
| {
 | |
|   return waitpid (pid, result, 0);
 | |
| }
 | |
| 
 | |
| /*
 | |
| * Helper function for spawn runtime calls.
 | |
| * Doesn't search the path.
 | |
| */
 | |
| 
 | |
| extern "C" int
 | |
| spawnve (int mode, const char *path, const char *const *argv,
 | |
|        const char *const *envp)
 | |
| {
 | |
|   static char *const empty_env[] = { NULL };
 | |
| 
 | |
|   int ret;
 | |
| 
 | |
|   syscall_printf ("spawnve (%s, %s, %p)", path, argv[0], envp);
 | |
| 
 | |
|   if (!envp)
 | |
|     envp = empty_env;
 | |
| 
 | |
|   switch (_P_MODE (mode))
 | |
|     {
 | |
|     case _P_OVERLAY:
 | |
|       ch_spawn.worker (path, argv, envp, mode);
 | |
|       /* Errno should be set by worker.  */
 | |
|       ret = -1;
 | |
|       break;
 | |
|     case _P_VFORK:
 | |
|     case _P_NOWAIT:
 | |
|     case _P_NOWAITO:
 | |
|     case _P_WAIT:
 | |
|     case _P_DETACH:
 | |
|     case _P_SYSTEM:
 | |
|       ret = ch_spawn.worker (path, argv, envp, mode);
 | |
|       break;
 | |
|     default:
 | |
|       set_errno (EINVAL);
 | |
|       ret = -1;
 | |
|       break;
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
| * spawn functions as implemented in the MS runtime library.
 | |
| * Most of these based on (and copied from) newlib/libc/posix/execXX.c
 | |
| */
 | |
| 
 | |
| extern "C" int
 | |
| spawnl (int mode, const char *path, const char *arg0, ...)
 | |
| {
 | |
|   int i;
 | |
|   va_list args;
 | |
|   const char *argv[256];
 | |
| 
 | |
|   va_start (args, arg0);
 | |
|   argv[0] = arg0;
 | |
|   i = 1;
 | |
| 
 | |
|   do
 | |
|       argv[i] = va_arg (args, const char *);
 | |
|   while (argv[i++] != NULL);
 | |
| 
 | |
|   va_end (args);
 | |
| 
 | |
|   return spawnve (mode, path, (char * const  *) argv, cur_environ ());
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnle (int mode, const char *path, const char *arg0, ...)
 | |
| {
 | |
|   int i;
 | |
|   va_list args;
 | |
|   const char * const *envp;
 | |
|   const char *argv[256];
 | |
| 
 | |
|   va_start (args, arg0);
 | |
|   argv[0] = arg0;
 | |
|   i = 1;
 | |
| 
 | |
|   do
 | |
|     argv[i] = va_arg (args, const char *);
 | |
|   while (argv[i++] != NULL);
 | |
| 
 | |
|   envp = va_arg (args, const char * const *);
 | |
|   va_end (args);
 | |
| 
 | |
|   return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnlp (int mode, const char *file, const char *arg0, ...)
 | |
| {
 | |
|   int i;
 | |
|   va_list args;
 | |
|   const char *argv[256];
 | |
|   path_conv buf;
 | |
| 
 | |
|   va_start (args, arg0);
 | |
|   argv[0] = arg0;
 | |
|   i = 1;
 | |
| 
 | |
|   do
 | |
|       argv[i] = va_arg (args, const char *);
 | |
|   while (argv[i++] != NULL);
 | |
| 
 | |
|   va_end (args);
 | |
| 
 | |
|   return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf),
 | |
| 		  (char * const *) argv, cur_environ ());
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnlpe (int mode, const char *file, const char *arg0, ...)
 | |
| {
 | |
|   int i;
 | |
|   va_list args;
 | |
|   const char * const *envp;
 | |
|   const char *argv[256];
 | |
|   path_conv buf;
 | |
| 
 | |
|   va_start (args, arg0);
 | |
|   argv[0] = arg0;
 | |
|   i = 1;
 | |
| 
 | |
|   do
 | |
|     argv[i] = va_arg (args, const char *);
 | |
|   while (argv[i++] != NULL);
 | |
| 
 | |
|   envp = va_arg (args, const char * const *);
 | |
|   va_end (args);
 | |
| 
 | |
|   return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf),
 | |
| 		  (char * const *) argv, envp);
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnv (int mode, const char *path, const char * const *argv)
 | |
| {
 | |
|   return spawnve (mode, path, argv, cur_environ ());
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnvp (int mode, const char *file, const char * const *argv)
 | |
| {
 | |
|   path_conv buf;
 | |
|   return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf), argv,
 | |
| 		  cur_environ ());
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| spawnvpe (int mode, const char *file, const char * const *argv,
 | |
| 	  const char * const *envp)
 | |
| {
 | |
|   path_conv buf;
 | |
|   return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf), argv, envp);
 | |
| }
 | |
| 
 | |
| int
 | |
| av::setup (const char *prog_arg, path_conv& real_path, const char *ext,
 | |
| 	   int argc, const char *const *argv, bool p_type_exec)
 | |
| {
 | |
|   const char *p;
 | |
|   bool exeext = ascii_strcasematch (ext, ".exe");
 | |
|   new (this) av (argc, argv);
 | |
|   if ((exeext && real_path.iscygexec ()) || ascii_strcasematch (ext, ".bat")
 | |
|       || (!*ext && ((p = ext - 4) > real_path.get_win32 ())
 | |
| 	  && (ascii_strcasematch (p, ".bat") || ascii_strcasematch (p, ".cmd")
 | |
| 	      || ascii_strcasematch (p, ".btm"))))
 | |
|     /* no extra checks needed */;
 | |
|   else
 | |
|     while (1)
 | |
|       {
 | |
| 	char *pgm = NULL;
 | |
| 	char *arg1 = NULL;
 | |
| 	char *ptr, *buf;
 | |
| 	OBJECT_ATTRIBUTES attr;
 | |
| 	IO_STATUS_BLOCK io;
 | |
| 	HANDLE h;
 | |
| 	NTSTATUS status;
 | |
| 	LARGE_INTEGER size;
 | |
| 
 | |
| 	status = NtOpenFile (&h, SYNCHRONIZE | GENERIC_READ,
 | |
| 			     real_path.get_object_attr (attr, sec_none_nih),
 | |
| 			     &io, FILE_SHARE_VALID_FLAGS,
 | |
| 			     FILE_SYNCHRONOUS_IO_NONALERT
 | |
| 			     | FILE_OPEN_FOR_BACKUP_INTENT
 | |
| 			     | FILE_NON_DIRECTORY_FILE);
 | |
| 	if (!NT_SUCCESS (status))
 | |
| 	  {
 | |
| 	    /* File is not readable?  Doesn't mean it's not executable.
 | |
| 	       Test for executability and if so, just assume the file is
 | |
| 	       a cygwin executable and go ahead. */
 | |
| 	    if (status == STATUS_ACCESS_DENIED && real_path.has_acls ()
 | |
| 		&& check_file_access (real_path, X_OK, true) == 0)
 | |
| 	      {
 | |
| 		real_path.set_cygexec (true);
 | |
| 		break;
 | |
| 	      }
 | |
| 	    SetLastError (RtlNtStatusToDosError (status));
 | |
| 	    goto err;
 | |
| 	  }
 | |
| 	if (!GetFileSizeEx (h, &size))
 | |
| 	  {
 | |
| 	    NtClose (h);
 | |
| 	    goto err;
 | |
| 	  }
 | |
| 	if (size.QuadPart > (LONGLONG) wincap.allocation_granularity ())
 | |
| 	  size.LowPart = wincap.allocation_granularity ();
 | |
| 
 | |
| 	HANDLE hm = CreateFileMapping (h, &sec_none_nih, PAGE_READONLY,
 | |
| 				       0, 0, NULL);
 | |
| 	NtClose (h);
 | |
| 	if (!hm)
 | |
| 	  {
 | |
| 	    /* ERROR_FILE_INVALID indicates very likely an empty file. */
 | |
| 	    if (GetLastError () == ERROR_FILE_INVALID)
 | |
| 	      {
 | |
| 		debug_printf ("zero length file, treat as script.");
 | |
| 		goto just_shell;
 | |
| 	      }
 | |
| 	    goto err;
 | |
| 	  }
 | |
| 	/* Try to map the first 64K of the image.  That's enough for the local
 | |
| 	   tests, and it's enough for hook_or_detect_cygwin to compute the IAT
 | |
| 	   address. */
 | |
| 	buf = (char *) MapViewOfFile (hm, FILE_MAP_READ, 0, 0, size.LowPart);
 | |
| 	if (!buf)
 | |
| 	  {
 | |
| 	    CloseHandle (hm);
 | |
| 	    goto err;
 | |
| 	  }
 | |
| 
 | |
| 	{
 | |
| 	  __try
 | |
| 	    {
 | |
| 	      if (buf[0] == 'M' && buf[1] == 'Z')
 | |
| 		{
 | |
| 		  WORD subsys;
 | |
| 		  unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8);
 | |
| 		  win16_exe = off < sizeof (IMAGE_DOS_HEADER);
 | |
| 		  if (!win16_exe)
 | |
| 		    real_path.set_cygexec (hook_or_detect_cygwin (buf, NULL,
 | |
| 								  subsys, hm));
 | |
| 		  else
 | |
| 		    real_path.set_cygexec (false);
 | |
| 		  UnmapViewOfFile (buf);
 | |
| 		  CloseHandle (hm);
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	  __except (NO_ERROR)
 | |
| 	    {
 | |
| 	      UnmapViewOfFile (buf);
 | |
| 	      CloseHandle (hm);
 | |
| 	      real_path.set_cygexec (false);
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  __endtry
 | |
| 	}
 | |
| 	CloseHandle (hm);
 | |
| 
 | |
| 	debug_printf ("%s is possibly a script", real_path.get_win32 ());
 | |
| 
 | |
| 	ptr = buf;
 | |
| 	if (*ptr++ == '#' && *ptr++ == '!')
 | |
| 	  {
 | |
| 	    ptr += strspn (ptr, " \t");
 | |
| 	    size_t len = strcspn (ptr, "\r\n");
 | |
| 	    while (ptr[len - 1] == ' ' || ptr[len - 1] == '\t')
 | |
| 	      len--;
 | |
| 	    if (len)
 | |
| 	      {
 | |
| 		char *namebuf = (char *) alloca (len + 1);
 | |
| 		memcpy (namebuf, ptr, len);
 | |
| 		namebuf[len] = '\0';
 | |
| 		for (ptr = pgm = namebuf; *ptr; ptr++)
 | |
| 		  if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
 | |
| 		    {
 | |
| 		      /* Null terminate the initial command and step over any
 | |
| 			 additional white space.  If we've hit the end of the
 | |
| 			 line, exit the loop.  Otherwise, we've found the first
 | |
| 			 argument. Position the current pointer on the last known
 | |
| 			 white space. */
 | |
| 		      *ptr = '\0';
 | |
| 		      char *newptr = ptr + 1;
 | |
| 		      newptr += strspn (newptr, " \t");
 | |
| 		      if (!*newptr)
 | |
| 			break;
 | |
| 		      arg1 = newptr;
 | |
| 		      ptr = newptr - 1;
 | |
| 		    }
 | |
| 	      }
 | |
| 	  }
 | |
| 	UnmapViewOfFile (buf);
 | |
|   just_shell:
 | |
| 	if (!pgm)
 | |
| 	  {
 | |
| 	    if (!p_type_exec)
 | |
| 	      {
 | |
| 		/* Not called from exec[lv]p.  Don't try to treat as script. */
 | |
| 		debug_printf ("%s is not a valid executable",
 | |
| 			      real_path.get_win32 ());
 | |
| 		set_errno (ENOEXEC);
 | |
| 		return -1;
 | |
| 	      }
 | |
| 	    if (ascii_strcasematch (ext, ".com"))
 | |
| 	      break;
 | |
| 	    pgm = (char *) "/bin/sh";
 | |
| 	    arg1 = NULL;
 | |
| 	  }
 | |
| 
 | |
| 	/* Check if script is executable.  Otherwise we start non-executable
 | |
| 	   scripts successfully, which is incorrect behaviour. */
 | |
| 	if (real_path.has_acls ()
 | |
| 	    && check_file_access (real_path, X_OK, true) < 0)
 | |
| 	  return -1;	/* errno is already set. */
 | |
| 
 | |
| 	/* Replace argv[0] with the full path to the script if this is the
 | |
| 	   first time through the loop. */
 | |
| 	replace0_maybe (prog_arg);
 | |
| 
 | |
| 	/* pointers:
 | |
| 	 * pgm	interpreter name
 | |
| 	 * arg1	optional string
 | |
| 	 */
 | |
| 	if (arg1)
 | |
| 	  unshift (arg1);
 | |
| 
 | |
| 	find_exec (pgm, real_path, "PATH", FE_NADA, &ext);
 | |
| 	unshift (real_path.get_posix ());
 | |
|       }
 | |
|   if (real_path.iscygexec ())
 | |
|     dup_all ();
 | |
|   return 0;
 | |
| 
 | |
| err:
 | |
|   __seterrno ();
 | |
|   return -1;
 | |
| }
 |