diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index a2a0d963e1f09..14580eec0f795 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -332,6 +332,37 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere } } + [Fact] + public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold() + { + var project = sharedTestState.PortableAppFixture_Published + .Copy(); + + var appExeName = Path.GetFileName(project.TestProject.AppExe); + var outputDir = project.TestProject.OutputDirectory; + + // Move the portable app to a path such that the length of the executable's fullpath + // is just 1 char behind MAX_PATH (260) so that the runtimeconfig(.dev).json files + // break this threshold. This will cause hostfxr to normalize these paths -- here we + // are checking that the updated paths are used. + var tmp = Path.GetTempPath(); + var dirName = new string('a', 259 - tmp.Length - appExeName.Length - 1); + var newDir = Path.Combine(tmp, dirName); + var appExe = Path.Combine(newDir, appExeName); + Debug.Assert(appExe.Length == 259); + Directory.CreateDirectory(newDir); + foreach (var file in Directory.GetFiles(outputDir, "*.*", SearchOption.TopDirectoryOnly)) + File.Copy(file, Path.Combine(newDir, Path.GetFileName(file)), true); + + Command.Create(appExe) + .DotNetRoot(project.BuiltDotnet.BinPath) + .EnableTracingAndCaptureOutputs() + .MultilevelLookup(false) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + [Fact] public void ComputedTPADoesntEndWithPathSeparator() { @@ -699,7 +730,7 @@ public SharedTestState() PortableAppFixture_Published = new TestProjectFixture("PortableApp", RepoDirectories) .EnsureRestored() - .PublishProject(); + .PublishProject(extraArgs: "/p:UseAppHost=true"); MockApp = new TestApp(SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "portableAppActivation")), "App"); Directory.CreateDirectory(MockApp.Location); diff --git a/src/native/corehost/deps_format.cpp b/src/native/corehost/deps_format.cpp index 8ab0f22caff2c..fdfc1869ca8b0 100644 --- a/src/native/corehost/deps_format.cpp +++ b/src/native/corehost/deps_format.cpp @@ -439,7 +439,7 @@ bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ve bool deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph) { m_deps_file = deps_path; - m_file_exists = bundle::info_t::config_t::probe(deps_path) || pal::file_exists(deps_path); + m_file_exists = bundle::info_t::config_t::probe(deps_path) || pal::realpath(&m_deps_file, true); json_parser_t json; if (!m_file_exists) @@ -449,7 +449,7 @@ bool deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_pa return true; } - if (!json.parse_file(deps_path)) + if (!json.parse_file(m_deps_file)) { return false; } diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index c992f92da8294..87a5508badbf5 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -645,11 +645,15 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out) // Return if path is valid and file exists, return true and adjust path as appropriate. bool pal::realpath(string_t* path, bool skip_error_logging) { + if (path->empty()) + { + return false; + } + if (LongFile::IsNormalized(*path)) { WIN32_FILE_ATTRIBUTE_DATA data; - if (path->empty() // An empty path doesn't exist - || GetFileAttributesExW(path->c_str(), GetFileExInfoStandard, &data) != 0) + if (GetFileAttributesExW(path->c_str(), GetFileExInfoStandard, &data) != 0) { return true; } @@ -713,11 +717,6 @@ bool pal::realpath(string_t* path, bool skip_error_logging) bool pal::file_exists(const string_t& path) { - if (path.empty()) - { - return false; - } - string_t tmp(path); return pal::realpath(&tmp, true); } diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 986c44c9bad36..fe6a0546cff92 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -334,9 +334,9 @@ bool runtime_config_t::ensure_dev_config_parsed() trace::verbose(_X("Attempting to read dev runtime config: %s"), m_dev_path.c_str()); pal::string_t retval; - if (!pal::file_exists(m_dev_path)) + if (!pal::realpath(&m_dev_path, true)) { - // Not existing is valid. + // It is valid for the runtimeconfig.dev.json to not exist. return true; } @@ -402,7 +402,7 @@ bool runtime_config_t::ensure_parsed() trace::verbose(_X("Did not successfully parse the runtimeconfig.dev.json")); } - if (!bundle::info_t::config_t::probe(m_path) && !pal::file_exists(m_path)) + if (!bundle::info_t::config_t::probe(m_path) && !pal::realpath(&m_path, true)) { // Not existing is not an error. return true;