From cef245d28ae8d50f3bf0ad7adedc461cd45b085c Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Thu, 16 Sep 2021 20:40:24 +0300 Subject: [PATCH] Use getpwuid(3) in corehost for the $HOME-less user (#59036) In other parts of dotnet/runtime repo [[1](https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/Native/Unix/System.Native/pal_networking.c#L3038-L3056)] [[2](https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/System.Private.CoreLib/src/System/IO/PersistedFiles.Unix.cs#L125-L137)] [[3](https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/Native/Unix/System.Native/pal_uid.c#L65-L68)], we attempt to retrieve user home directory from `getpwuid_r` when the environment variable `HOME` is not set. Use the same fallback in corehost. --- .../BundleExtractToSpecificPath.cs | 25 ++++++++++++++++--- src/native/corehost/hostmisc/pal.unix.cpp | 25 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs index 897408f0a3647..a222dcb9750ed 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs @@ -223,14 +223,33 @@ private void Bundle_extraction_to_nonexisting_default() [Fact] [SkipOnPlatform(TestPlatforms.Windows, "On Windows the default extraction path is determined by calling GetTempPath which looks at multiple places and can't really be undefined.")] - private void Bundle_extraction_default_undefined() + private void Bundle_extraction_fallsback_to_getpwuid_when_HOME_env_var_is_undefined() { + string home = Environment.GetEnvironmentVariable("HOME"); + // suppose we are testing on a system where HOME is not set, use System.Environment (which also fallsback to getpwuid) + if (string.IsNullOrEmpty(home)) + { + home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + } + + DirectoryInfo sharedExtractDirInfo = BundleHelper.GetExtractionDir(sharedTestState.TestFixture, sharedTestState.DefaultBundledAppBundler); + string sharedExtractDir = sharedExtractDirInfo.FullName; + string extractDirSubPath = sharedExtractDir.Substring(sharedExtractDir.LastIndexOf("extract/") + "extract/".Length); + string realExtractDir = Path.Combine(home, ".net", extractDirSubPath); + var expectedExtractDir = new DirectoryInfo(realExtractDir); + Command.Create(sharedTestState.DefaultBundledAppExecutablePath) .CaptureStdErr() .CaptureStdOut() .EnvironmentVariable("HOME", null) - .Execute().Should().Fail() - .And.HaveStdErrContaining("Failed to determine default extraction location. Environment variable '$HOME' is not defined."); + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("Hello World"); + + var extractedFiles = BundleHelper.GetExtractedFiles(sharedTestState.TestFixture, BundleOptions.BundleNativeBinaries); + expectedExtractDir.Should().HaveFiles(extractedFiles); } public class SharedTestState : SharedTestStateBase, IDisposable diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index 6f247f096cc99..a09e6ea04d525 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -352,7 +352,30 @@ bool get_extraction_base_parent_directory(pal::string_t& directory) } else { - trace::error(_X("Failed to determine default extraction location. Environment variable '$HOME' is not defined.")); + // fallback to the POSIX standard getpwuid() library function + struct passwd* pwuid = NULL; + errno = 0; + do + { + pwuid = getpwuid(getuid()); + } while (pwuid == NULL && errno == EINTR); + + if (pwuid != NULL) + { + directory.assign(pwuid->pw_dir); + if (is_read_write_able_directory(directory)) + { + return true; + } + else + { + trace::error(_X("Failed to determine default extraction location. Environment variable '$HOME' is not defined and directory reported by getpwuid() [%s] either doesn't exist or is not accessible for read/write."), pwuid->pw_dir); + } + } + else + { + trace::error(_X("Failed to determine default extraction location. Environment variable '$HOME' is not defined and getpwuid() returned NULL.")); + } } return false;