[PATCH 2/5] Cygwin: add ability to pass cwd to child process

Jeremy Drake cygwin@jdrake.com
Thu Jun 26 23:56:41 GMT 2025


This will be used by posix_spawn_fileaction_add_(f)chdir.

This implementation is not quite complete enough for posix_spawn, as it
also needs to treat relative paths to the program as relative to the
specified CWD.

Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
---
 winsup/cygwin/dcrt0.cc                    | 19 +++++-
 winsup/cygwin/local_includes/child_info.h |  5 +-
 winsup/cygwin/spawn.cc                    | 72 ++++++++++++++++++++---
 3 files changed, 84 insertions(+), 12 deletions(-)

diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index b0fb5c9c1e..6adc31495a 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -46,6 +46,7 @@ extern "C" void __sinit (_reent *);

 static int NO_COPY envc;
 static char NO_COPY **envp;
+static int NO_COPY cwdfd = AT_FDCWD;

 bool NO_COPY jit_debug;

@@ -656,6 +657,7 @@ child_info_spawn::handle_spawn ()
   __argv = moreinfo->argv;
   envp = moreinfo->envp;
   envc = moreinfo->envc;
+  cwdfd = moreinfo->cwdfd;
   if (!dynamically_loaded)
     cygheap->fdtab.fixup_after_exec ();
   if (__stdin >= 0)
@@ -842,7 +844,22 @@ dll_crt0_1 (void *)

   ProtectHandle (hMainThread);

-  cygheap->cwd.init ();
+  if (cwdfd >= 0)
+    {
+      int res = fchdir (cwdfd);
+      if (res < 0)
+	{
+	  /* if the error occurs after the calling process successfully
+	     returns, the child process shall exit with exit status 127. */
+	  /* why is this byteswapped? */
+	  set_api_fatal_return (0x7f00);
+	  api_fatal ("can't fchdir, %R", res);
+	}
+      close (cwdfd);
+      cwdfd = AT_FDCWD;
+    }
+  else
+    cygheap->cwd.init ();

   /* Initialize pthread mainthread when not forked and it is safe to call new,
      otherwise it is reinitalized in fixup_after_fork */
diff --git a/winsup/cygwin/local_includes/child_info.h b/winsup/cygwin/local_includes/child_info.h
index 902be8727b..95bdd5cd1c 100644
--- a/winsup/cygwin/local_includes/child_info.h
+++ b/winsup/cygwin/local_includes/child_info.h
@@ -33,7 +33,7 @@ enum child_status
 #define EXEC_MAGIC_SIZE sizeof(child_info)

 /* Change this value if you get a message indicating that it is out-of-sync. */
-#define CURR_CHILD_INFO_MAGIC 0x6ccb18aeU
+#define CURR_CHILD_INFO_MAGIC 0xeb5dce32U

 #include "pinfo.h"
 struct cchildren
@@ -128,6 +128,7 @@ public:
   int envc;
   char **envp;
   HANDLE myself_pinfo;
+  int cwdfd;
   int nchildren;
   cchildren children[0];
   static cygheap_exec_info *alloc ();
@@ -190,7 +191,7 @@ public:
   bool has_execed_cygwin () const { return iscygwin () && has_execed (); }
   operator HANDLE& () {return hExeced;}
   int worker (const char *, const char *const *, const char *const [],
-		     int, int = -1, int = -1, int = -1);
+		     int, int = -1, int = -1, int = -1, int = AT_FDCWD);
 };

 extern child_info_spawn ch_spawn;
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 9a7f0bbf73..8625725d49 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -281,7 +281,8 @@ extern DWORD mutex_timeout; /* defined in fhandler_termios.cc */
 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, int in__stderr)
+			  int in__stdin, int in__stdout, int in__stderr,
+			  int cwdfd)
 {
   bool rc;
   int res = -1;
@@ -362,7 +363,65 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       if (res)
 	__leave;

-      if (!real_path.iscygexec () && ::cygheap->cwd.get_error ())
+      LPWSTR cwd = NULL;
+      if (real_path.iscygexec ())
+	{
+	  moreinfo->argc = newargv.argc;
+	  moreinfo->argv = newargv;
+	  moreinfo->cwdfd = cwdfd;
+	}
+
+      if (cwdfd > 0)
+        {
+	  cygheap_fdget cfd (cwdfd);
+	  if (cfd < 0)
+	    {
+	      set_errno (EBADF);
+	      res = -1;
+	      __leave;
+	    }
+	  cfd->set_close_on_exec (!real_path.iscygexec ());
+	  if (!real_path.iscygexec ())
+	    {
+	      PUNICODE_STRING natcwd = cfd->pc.get_nt_native_path ();
+	      cwd = tp.w_get ();
+	      USHORT len = natcwd->Length / sizeof (WCHAR);
+	      if (RtlEqualUnicodePathPrefix (natcwd, &ro_u_natp, FALSE))
+		{
+		  cwd = cfd->pc.get_wide_win32_path (cwd);
+		  if (len < MAX_PATH + 2)
+		    {
+		      if (cwd[5] == L':')
+			cwd += 4;
+		      else
+			*(cwd += 6) = L'\\';
+		    }
+		  else
+		    {
+		      set_errno (ENAMETOOLONG);
+		      res = -1;
+		      __leave;
+		    }
+		}
+	      else if (len <
+			NT_MAX_PATH - ro_u_globalroot.Length / sizeof (WCHAR))
+		{
+		  UNICODE_STRING ucwd;
+
+		  RtlInitEmptyUnicodeString (&ucwd, cwd,
+					    (NT_MAX_PATH - 1) * sizeof (WCHAR));
+		  RtlCopyUnicodeString (&ucwd, &ro_u_globalroot);
+		  RtlAppendUnicodeStringToString (&ucwd, natcwd);
+		}
+	      else
+		{
+		  set_errno (ENAMETOOLONG);
+		  res = -1;
+		  __leave;
+		}
+	    }
+	}
+      else 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",
@@ -372,11 +431,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  __leave;
 	}

-      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 ()))
@@ -624,7 +678,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       TRUE,		/* inherit handles */
 			       c_flags,
 			       envblock,	/* environment */
-			       NULL,
+			       cwd,
 			       &si,
 			       &pi);
 	}
@@ -676,7 +730,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       TRUE,		/* inherit handles */
 			       c_flags,
 			       envblock,	/* environment */
-			       NULL,
+			       cwd,
 			       &si,
 			       &pi);
 	  if (hwst)
-- 
2.49.0.windows.1



More information about the Cygwin-patches mailing list