Hi Takashi,
On Thu, 18 Dec 2025, Takashi Yano wrote:
> After the commit f74dc93c6359, WSL cannot start by distribution name
> such as debian.exe, which has '.exe' extention but actually is an app
> execution alias. This is because the commit f74dc93c6359 disabled to
> follow windows reparse point by adding PC_SYM_NOFOLLOW_REP flag in
> spawn.cc, that path is used for sapwning a process. As a result, the
> path, that is_console_app () received, had been the reparse point of
> app execution alias, then it returned false for the the path due to
> open-failure because CreateFileW() cannot open an app execution alias,
> while it can open normal reparse point. If is_console_app() returns
> false, standard handles for console app (such as WSL) would not be
> setup. This causes that the console input cannot be transfered to the
> non-cygwin app.
Just a suggestion: Start by describing the bug instead of leading with the
commit that caused the bug. Something along the lines "Microsoft Store
apps are run via 'app execution aliases', i.e. special reparse points.
Cygwin usually treats those like symbolic links. However, unlike proper
symbolic links, app execution aliases are not resolved when trying to read
the file contents via `CreateFile()`/`ReadFile()` [...]".
> This patch fixes the issue by locally converting the path, which is
> a path to the app execution alias, once again using PC_SYM_FOLLOW
> (without PC_SYM_NOFOLLOW_REP) option path_conv for using inside of
> is_console_app() to resolve the reparse point here, if the path is
> an app execution alias.
>
> Fixes: f74dc93c6359 ("fix native symlink spawn passing wrong arg0")
> Reviewed-by: Johannes Schindelin <[email protected]>
> Signed-off-by: Takashi Yano <[email protected]>
> ---
> winsup/cygwin/fhandler/termios.cc | 23 ++++++++++++++++++-----
> winsup/cygwin/local_includes/fhandler.h | 2 +-
> winsup/cygwin/spawn.cc | 2 +-
> 3 files changed, 20 insertions(+), 7 deletions(-)
>
> diff --git a/winsup/cygwin/fhandler/termios.cc
> b/winsup/cygwin/fhandler/termios.cc
> index f99ae6c80..694a5c20f 100644
> --- a/winsup/cygwin/fhandler/termios.cc
> +++ b/winsup/cygwin/fhandler/termios.cc
> @@ -702,13 +702,26 @@ fhandler_termios::fstat (struct stat *buf)
> }
>
> static bool
> -is_console_app (const WCHAR *filename)
> +is_console_app (path_conv &pc)
> {
> - wchar_t *e = wcsrchr (filename, L'.');
> + tmp_pathbuf tp;
> + WCHAR *native_path = tp.w_get ();
> + pc.get_wide_win32_path (native_path);
> +
> + wchar_t *e = wcsrchr (native_path, L'.');
> if (e && (wcscasecmp (e, L".bat") == 0 || wcscasecmp (e, L".cmd") == 0))
> return true;
> +
> + if (pc.is_app_execution_alias ())
> + {
> + UNICODE_STRING upath;
> + RtlInitUnicodeString (&upath, native_path);
> + path_conv target (&upath, PC_SYM_FOLLOW);
> + target.get_wide_win32_path (native_path);
> + }
> +
It might make sense to move this `is_app_execution_alias()` block before
looking at the file extension, not that it will matter a lot in pratices
because as far as I understand, app execution aliases are only ever
created for `.exe` files, with the same base name as the target (or at
least with the same file extension).
Ciao,
Johannes
> HANDLE h;
> - h = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ,
> + h = CreateFileW (native_path, GENERIC_READ, FILE_SHARE_READ,
> NULL, OPEN_EXISTING, 0, NULL);
> if (h == INVALID_HANDLE_VALUE)
> return true;
> @@ -761,7 +774,7 @@ fhandler_termios::ioctl (unsigned int cmd, void *varg)
>
> void
> fhandler_termios::spawn_worker::setup (bool iscygwin, HANDLE h_stdin,
> - const WCHAR *runpath, bool nopcon,
> + path_conv &pc, bool nopcon,
> bool reset_sendsig,
> const WCHAR *envblock)
> {
> @@ -800,7 +813,7 @@ fhandler_termios::spawn_worker::setup (bool iscygwin,
> HANDLE h_stdin,
> ptys->setup_locale ();
> }
> }
> - if (!iscygwin && ptys_primary && is_console_app (runpath))
> + if (!iscygwin && ptys_primary && is_console_app (pc))
> {
> if (h_stdin == ptys_primary->get_handle_nat ())
> stdin_is_ptys = true;
> diff --git a/winsup/cygwin/local_includes/fhandler.h
> b/winsup/cygwin/local_includes/fhandler.h
> index 0de82163e..16f55b4f7 100644
> --- a/winsup/cygwin/local_includes/fhandler.h
> +++ b/winsup/cygwin/local_includes/fhandler.h
> @@ -2036,7 +2036,7 @@ class fhandler_termios: public fhandler_base
> spawn_worker () :
> ptys_need_cleanup (false), cons_need_cleanup (false),
> stdin_is_ptys (false), ptys_ttyp (NULL) {}
> - void setup (bool iscygwin, HANDLE h_stdin, const WCHAR *runpath,
> + void setup (bool iscygwin, HANDLE h_stdin, path_conv &pc,
> bool nopcon, bool reset_sendsig, const WCHAR *envblock);
> bool need_cleanup () { return ptys_need_cleanup || cons_need_cleanup; }
> void cleanup ();
> diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
> index 71add8755..7d993d081 100644
> --- a/winsup/cygwin/spawn.cc
> +++ b/winsup/cygwin/spawn.cc
> @@ -579,7 +579,7 @@ child_info_spawn::worker (const char *prog_arg, const
> char *const *argv,
>
> bool no_pcon = mode != _P_OVERLAY && mode != _P_WAIT;
> term_spawn_worker.setup (iscygwin (), handle (fileno_stdin, false),
> - runpath, no_pcon, reset_sendsig, envblock);
> + real_path, no_pcon, reset_sendsig, envblock);
>
> /* Set up needed handles for stdio */
> si.dwFlags = STARTF_USESTDHANDLES;
> --
> 2.51.0
>
>