[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