[PATCH v6 4/4] Do not rely on `getenv ("HOME")`'s path conversion

Johannes Schindelin johannes.schindelin@gmx.de
Tue Apr 4 15:07:59 GMT 2023


In the very early code path where `dll_crt0_1 ()` calls
`user_shared->initialize ()`, the Cygwin runtime calls `internal_pwsid ()`
to initialize the user name in preparation for reading the `fstab` file.

In case `db_home: env` is defined in `/etc/nsswitch.conf`, we need to
look at the environment variable `HOME` and use it, if set.

When all of this happens, though, the `pinfo_init ()` function has had no
change to run yet (and therefore, `environ_init ()`). At this stage,
therefore, `getenv ()`'s `findenv_func ()` call still finds `getearly ()`
and we get the _verbatim_ value of `HOME`. That is, the Windows form.
But we need the "POSIX" form.

To add insult to injury, later calls to `getpwuid (getuid ())` will
receive a cached version of the home directory via
`cygheap->pg.pwd_cache.win.find_user ()` thanks to the first
`internal_pwsid ()` call caching the result via
`add_user_from_cygserver ()`, read: we will never receive the converted
`HOME` but always the Windows variant.

So, contrary to the assumptions made in 27376c60a9 (Allow deriving the
current user's home directory via the HOME variable, 2023-03-28), we
cannot assume that `getenv ("HOME")` returned a "POSIX" path.

This is a real problem. Even setting aside that common callers of
`getpwuid ()` (such as OpenSSH) are unable to handle Windows paths in the
`pw_dir` attribute, the Windows path never makes it back to the caller
unscathed. The value returned from `fetch_home_env ()` is not actually
used as-is. Instead, the `fetch_account_from_windows ()` method uses it
to write a pseudo `/etc/passwd`-formatted line that is _then_ parsed via
the `pwdgrp::parse_passwd ()` method which sees no problem with
misinterpreting the colon after the drive letter as a field separator of
that `/etc/passwd`-formatted line, and instead of a Windows path, we now
have a mere drive letter.

Let's detect when the `HOME` value is still in Windows format in
`fetch_home_env ()`, and convert it in that case.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 winsup/cygwin/uinfo.cc | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc
index 5e2d88bcd7..bc9e926159 100644
--- a/winsup/cygwin/uinfo.cc
+++ b/winsup/cygwin/uinfo.cc
@@ -929,7 +929,13 @@ fetch_home_env (void)
   /* If `HOME` is set, prefer it */
   const char *home = getenv ("HOME");
   if (home)
-    return strdup (home);
+    {
+      /* In the very early code path of `user_info::initialize ()`, the value
+         of the environment variable `HOME` is still in its Windows form. */
+      if (isdrive (home))
+	return (char *) cygwin_create_path (CCP_WIN_A_TO_POSIX, home);
+      return strdup (home);
+    }

   /* If `HOME` is unset, fall back to `HOMEDRIVE``HOMEPATH`
      (without a directory separator, as `HOMEPATH` starts with one). */
--
2.40.0.windows.1


More information about the Cygwin-patches mailing list