From bcb27e4507e800d2d2e8a59563d024aaac0bf4f6 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 25 Nov 2025 15:17:27 +0100 Subject: [PATCH 1/9] Add test for verly long paths. --- .../Wasm.Build.Tests/Blazor/BuildPublishTests.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index 63f2fd2a3bd145..b7394b19fbe553 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -33,8 +33,6 @@ public static TheoryData TestDataForDefaultTemplate_WithWor data.Add(Configuration.Debug, true); } - // [ActiveIssue("https://github.com/dotnet/runtime/issues/103625", TestPlatforms.Windows)] - // when running locally the path might be longer than 260 chars and these tests can fail with AOT data.Add(Configuration.Release, false); // Release relinks by default data.Add(Configuration.Release, true); return data; @@ -58,6 +56,17 @@ public void DefaultTemplate_AOT_WithWorkload(Configuration config, bool testUnic PublishProject(info, config, new PublishOptions(AOT: true, UseCache: false)); } + [Fact] + public void DefaultTemplate_AOT_WithLongPath() + { + Configuration config = Configuration.Release; + string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_TEST_OF_EXTREMELY_LONG_PATH"; + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, longIdPrefix, appendUnicodeToPath: true); + BlazorBuild(info, config); + + PublishProject(info, config, new PublishOptions(AOT: true, UseCache: false)); + } + // Disabling for now - publish folder can have more than one dotnet*hash*js, and not sure // how to pick which one to check, for the test //[Theory] From c61af0f5e97fd2c34ceaaa6b0d388a76d8aedb3d Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 25 Nov 2025 17:19:53 +0100 Subject: [PATCH 2/9] Prolong the prefix to 221 chars to make sure the path will exceed 260. --- src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index b7394b19fbe553..49f2fe1644a0c3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -60,7 +60,7 @@ public void DefaultTemplate_AOT_WithWorkload(Configuration config, bool testUnic public void DefaultTemplate_AOT_WithLongPath() { Configuration config = Configuration.Release; - string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_TEST_OF_EXTREMELY_LONG_PATH"; + string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_TEST_OF_EXTREMELY_LONG_PATH_THAT_EXCEEDS_MAX_PATH_LIMIT_TO_ENSURE_LONG_PATH_SUPPORT_WORKS_CORRECTLY_WITH_AOT_COMPILATION_ON_WINDOWS_SYSTEMS_THAT_HAVE_THE_260_CHARACTER_LIMITATION"; ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, longIdPrefix, appendUnicodeToPath: true); BlazorBuild(info, config); From cdbd6675dfc3d8a3b16425b4a18bdd5925e2fd03 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 26 Nov 2025 08:24:55 +0100 Subject: [PATCH 3/9] Fix long paths on Windows. --- src/mono/mono/eglib/gfile-win32.c | 6 ++- src/mono/mono/eglib/gfile.c | 10 ++-- src/mono/mono/eglib/glib.h | 4 ++ src/mono/mono/eglib/gpath.c | 48 ++++++++++++++++++++ src/mono/mono/metadata/image.c | 6 ++- src/mono/mono/utils/mono-path.c | 6 +++ src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 11 +++-- 7 files changed, 82 insertions(+), 9 deletions(-) diff --git a/src/mono/mono/eglib/gfile-win32.c b/src/mono/mono/eglib/gfile-win32.c index 2d86027d164fc5..da189722c77c7b 100644 --- a/src/mono/mono/eglib/gfile-win32.c +++ b/src/mono/mono/eglib/gfile-win32.c @@ -93,12 +93,16 @@ gboolean g_file_test (const gchar *filename, GFileTest test) { gunichar2* utf16_filename = NULL; + gchar *filename_with_prefix = NULL; DWORD attr; if (filename == NULL || test == 0) return FALSE; - utf16_filename = u8to16 (filename); + filename_with_prefix = g_path_make_long_compatible (filename); + utf16_filename = u8to16 (filename_with_prefix); + g_free (filename_with_prefix); + attr = GetFileAttributesW (utf16_filename); g_free (utf16_filename); diff --git a/src/mono/mono/eglib/gfile.c b/src/mono/mono/eglib/gfile.c index 5720a54a712658..b17d98a84d444b 100644 --- a/src/mono/mono/eglib/gfile.c +++ b/src/mono/mono/eglib/gfile.c @@ -127,10 +127,13 @@ g_fopen (const gchar *path, const gchar *mode) return NULL; #ifdef HOST_WIN32 - if (is_ascii_string (path) && is_ascii_string (mode)) { - fp = fopen (path, mode); + gchar *path_mod; + path_mod = g_path_make_long_compatible(path); + + if (is_ascii_string (path_mod) && is_ascii_string (mode)) { + fp = fopen (path_mod, mode); } else { - gunichar2 *wPath = g_utf8_to_utf16 (path, -1, 0, 0, 0); + gunichar2 *wPath = g_utf8_to_utf16 (path_mod, -1, 0, 0, 0); gunichar2 *wMode = g_utf8_to_utf16 (mode, -1, 0, 0, 0); if (!wPath || !wMode) @@ -140,6 +143,7 @@ g_fopen (const gchar *path, const gchar *mode) g_free (wPath); g_free (wMode); } + g_free (path_mod); #else fp = fopen (path, mode); #endif diff --git a/src/mono/mono/eglib/glib.h b/src/mono/mono/eglib/glib.h index 6d14a1721b2108..d728d441ece122 100644 --- a/src/mono/mono/eglib/glib.h +++ b/src/mono/mono/eglib/glib.h @@ -898,6 +898,10 @@ gchar *g_path_get_basename (const char *filename); gchar *g_get_current_dir (void); gboolean g_path_is_absolute (const char *filename); +#ifdef G_OS_WIN32 +gchar *g_path_make_long_compatible (const gchar *path); +#endif + const gchar *g_get_tmp_dir (void); gboolean g_ensure_directory_exists (const gchar *filename); diff --git a/src/mono/mono/eglib/gpath.c b/src/mono/mono/eglib/gpath.c index 96e39c3588dddd..c4c5d34ef0b399 100644 --- a/src/mono/mono/eglib/gpath.c +++ b/src/mono/mono/eglib/gpath.c @@ -39,6 +39,54 @@ #include #endif +#ifdef G_OS_WIN32 + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +/* Helper function to check if a Windows path needs the \\?\ prefix for long path support. + * Returns TRUE if: + * - The path is long enough to potentially hit MAX_PATH limit + * - The path doesn't already have the \\?\ prefix + * - The path is an absolute Windows path (e.g., C:\path) + */ +static gboolean +g_path_needs_long_prefix (const gchar *path) +{ + if (!path || strlen(path) <= 2) + return FALSE; + + /* Only add prefix for paths that are approaching or exceeding MAX_PATH */ + if (strlen(path) < MAX_PATH) + return FALSE; + + if (strncmp(path, "\\\\?\\", 4) == 0) + return FALSE; + + if (path[1] == ':' && (path[2] == '\\' || path[2] == '/')) + return TRUE; + + return FALSE; +} + +/* Makes a path compatible with long path support by adding \\?\ prefix if needed. Caller must free the result. */ +gchar * +g_path_make_long_compatible (const gchar *path) +{ + if (!path) + return NULL; + + if (!g_path_needs_long_prefix(path)) + return g_strdup(path); + + gchar *prefixed = g_malloc(strlen(path) + 5); + strcpy(prefixed, "\\\\?\\"); + strcat(prefixed, path); + return prefixed; +} +#endif + gchar * g_build_path (const gchar *separator, const gchar *first_element, ...) { diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 7c14b5a97dba4e..6952cc8b93a512 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -1765,17 +1765,19 @@ mono_image_open_a_lot_parameterized (MonoLoadedImages *li, MonoAssemblyLoadConte */ mono_images_lock (); image = (MonoImage *)g_hash_table_lookup (loaded_images, absfname); - g_free (absfname); if (image) { // Image already loaded mono_image_addref (image); mono_images_unlock (); + g_free (absfname); return image; } mono_images_unlock (); // Image not loaded, load it now - image = do_mono_image_open (alc, fname, status, options); + // Use absfname (the resolved absolute path) instead of fname (which may be relative) + image = do_mono_image_open (alc, absfname, status, options); + g_free (absfname); if (image == NULL) return NULL; diff --git a/src/mono/mono/utils/mono-path.c b/src/mono/mono/utils/mono-path.c index 616fa183d5a559..62d4839c85ecac 100644 --- a/src/mono/mono/utils/mono-path.c +++ b/src/mono/mono/utils/mono-path.c @@ -99,6 +99,12 @@ mono_path_canonicalize (const char *path) abspath [len+1] = 0; } +#ifdef HOST_WIN32 + gchar *prefixed = g_path_make_long_compatible(abspath); + g_free(abspath); + abspath = prefixed; +#endif + return abspath; } diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 9f3101b38b6234..67e2fff1453d96 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -1004,16 +1004,21 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st } else { + string assemblyPath; if (string.IsNullOrEmpty(WorkingDirectory)) { - processArgs.Add('"' + assemblyFilename + '"'); + // Pass the full assembly path to the AOT compiler to support long paths (> MAX_PATH) + assemblyPath = assembly; } else { // If WorkingDirectory is supplied, the caller could be passing in a relative path - // Use the original ItemSpec that was passed in. - processArgs.Add('"' + assemblyItem.ItemSpec + '"'); + // Convert to absolute path to ensure long path support works correctly + assemblyPath = Path.IsPathRooted(assemblyItem.ItemSpec) + ? assemblyItem.ItemSpec + : Path.GetFullPath(Path.Combine(WorkingDirectory, assemblyItem.ItemSpec)); } + processArgs.Add('"' + assemblyPath + '"'); } monoPaths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}"; From 8ac2341c3a57af6c948e1a51b24cce4a902d9dd9 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 26 Nov 2025 08:25:42 +0100 Subject: [PATCH 4/9] Feedback: use repeater to create long path. --- src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index 49f2fe1644a0c3..148b29ded47d4b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -60,7 +60,7 @@ public void DefaultTemplate_AOT_WithWorkload(Configuration config, bool testUnic public void DefaultTemplate_AOT_WithLongPath() { Configuration config = Configuration.Release; - string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_TEST_OF_EXTREMELY_LONG_PATH_THAT_EXCEEDS_MAX_PATH_LIMIT_TO_ENSURE_LONG_PATH_SUPPORT_WORKS_CORRECTLY_WITH_AOT_COMPILATION_ON_WINDOWS_SYSTEMS_THAT_HAVE_THE_260_CHARACTER_LIMITATION"; + string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_{new string('x', 300)}"; ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, longIdPrefix, appendUnicodeToPath: true); BlazorBuild(info, config); From 0124bf0c85e499f1bdf6be990d4ba788f798509b Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 26 Nov 2025 08:39:36 +0100 Subject: [PATCH 5/9] Add support to long UNC paths. --- src/mono/mono/eglib/gpath.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/eglib/gpath.c b/src/mono/mono/eglib/gpath.c index c4c5d34ef0b399..2c96ecaf39dbc6 100644 --- a/src/mono/mono/eglib/gpath.c +++ b/src/mono/mono/eglib/gpath.c @@ -49,7 +49,7 @@ * Returns TRUE if: * - The path is long enough to potentially hit MAX_PATH limit * - The path doesn't already have the \\?\ prefix - * - The path is an absolute Windows path (e.g., C:\path) + * - The path is an absolute Windows path (e.g., C:\path) or UNC path (e.g., \\server\share) */ static gboolean g_path_needs_long_prefix (const gchar *path) @@ -67,6 +67,9 @@ g_path_needs_long_prefix (const gchar *path) if (path[1] == ':' && (path[2] == '\\' || path[2] == '/')) return TRUE; + if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') + return TRUE; + return FALSE; } @@ -80,6 +83,15 @@ g_path_make_long_compatible (const gchar *path) if (!g_path_needs_long_prefix(path)) return g_strdup(path); + /* Handle UNC paths: \\server\share becomes \\?\UNC\server\share */ + if (path[0] == '\\' && path[1] == '\\') { + gchar *prefixed = g_malloc(strlen(path) + 7); + strcpy(prefixed, "\\\\?\\UNC\\"); + strcat(prefixed, path + 2); + return prefixed; + } + + /* Handle regular absolute paths: C:\path becomes \\?\C:\path */ gchar *prefixed = g_malloc(strlen(path) + 5); strcpy(prefixed, "\\\\?\\"); strcat(prefixed, path); From f068bb6795915e37e5ad9baa9356cda9da470afb Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Thu, 27 Nov 2025 16:40:38 +0100 Subject: [PATCH 6/9] Test respects the WBT mount point when exercising the MAX_PATH. --- .../Blazor/BuildPublishTests.cs | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index 148b29ded47d4b..e461884764a94b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -10,6 +10,7 @@ using Xunit.Sdk; using Microsoft.Playwright; using System.Runtime.InteropServices; +using System; #nullable enable @@ -60,13 +61,47 @@ public void DefaultTemplate_AOT_WithWorkload(Configuration config, bool testUnic public void DefaultTemplate_AOT_WithLongPath() { Configuration config = Configuration.Release; - string longIdPrefix = $"blz_aot_{config}_{GetRandomId()}_{new string('x', 300)}"; - ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, longIdPrefix, appendUnicodeToPath: true); - BlazorBuild(info, config); + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, "lp", appendUnicodeToPath: true); + + // Move the test project into a nested directory to create a long path that exceeds MAX_PATH + info = NestProjectInLongPath(info); + BlazorBuild(info, config); PublishProject(info, config, new PublishOptions(AOT: true, UseCache: false)); } + private ProjectInfo NestProjectInLongPath(ProjectInfo info) + { + string testProjectDir = Path.GetDirectoryName(_projectDir)!; + string testProjectDirName = Path.GetFileName(testProjectDir); + string baseDir = Path.GetDirectoryName(testProjectDir)!; + + // The problematic path is in a form of: + // {_projectDir}\obj\..\Microsoft_Extensions_DependencyInjection_dll_compiled_methods.txt + // Its length is at least 100 characters, so we can subtract it from the nesting target + const int longestBuildSubpathLength = 100; + const int windowsMaxPath = 260; + + int currentLength = _projectDir.Length; + int targetPathLength = windowsMaxPath - longestBuildSubpathLength; + int additionalLength = Math.Max(0, targetPathLength - currentLength); + + if (additionalLength == 0) + return info; + + string dirName = new string('x', additionalLength); + string nestedPath = Path.Combine(baseDir, dirName); + Directory.CreateDirectory(nestedPath); + + string newTestProjectDir = Path.Combine(nestedPath, testProjectDirName); + Directory.Move(testProjectDir, newTestProjectDir); + + _projectDir = Path.Combine(newTestProjectDir, "App"); + string nestedProjectFilePath = Path.Combine(_projectDir, "BlazorBasicTestApp.csproj"); + + return new ProjectInfo(info.ProjectName, nestedProjectFilePath, info.LogPath, info.NugetDir); + } + // Disabling for now - publish folder can have more than one dotnet*hash*js, and not sure // how to pick which one to check, for the test //[Theory] From f113ae9fab3e2293a97eb015272ec9d562c81a97 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Fri, 28 Nov 2025 13:51:28 +0100 Subject: [PATCH 7/9] Feedback: support long Win env vars and long relative drive paths. --- src/mono/mono/eglib/gpath.c | 119 ++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 12 deletions(-) diff --git a/src/mono/mono/eglib/gpath.c b/src/mono/mono/eglib/gpath.c index 2c96ecaf39dbc6..e8107ddaa95e73 100644 --- a/src/mono/mono/eglib/gpath.c +++ b/src/mono/mono/eglib/gpath.c @@ -33,6 +33,7 @@ #ifdef G_OS_WIN32 #include +#include #endif #ifdef HAVE_UNISTD_H @@ -49,7 +50,8 @@ * Returns TRUE if: * - The path is long enough to potentially hit MAX_PATH limit * - The path doesn't already have the \\?\ prefix - * - The path is an absolute Windows path (e.g., C:\path) or UNC path (e.g., \\server\share) + * - The path is an absolute Windows path (e.g., C:\path), UNC path (e.g., \\server\share), + * or drive-relative path (e.g., \Windows\System32) */ static gboolean g_path_needs_long_prefix (const gchar *path) @@ -70,32 +72,125 @@ g_path_needs_long_prefix (const gchar *path) if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') return TRUE; + if (path[0] == '\\' && path[1] != '\\') + return TRUE; + + return FALSE; +} + +/* Helper function to check if a path needs special expansion to absolute path. + * Returns TRUE if the path contains: + * - Environment variables (%) that need expansion + * - Drive-relative paths (\Windows\System32) that need drive letter prepended + * Returns FALSE for normal relative paths and already-absolute paths. + */ +static gboolean +g_path_needs_expansion (const gchar *path) +{ + if (!path) + return FALSE; + + if (strchr(path, '%') != NULL) + return TRUE; + + if (path[0] == '\\' && path[1] != '\\') + return TRUE; + return FALSE; } +static gchar * +g_path_to_absolute (const gchar *path) +{ + if (!path) + return NULL; + + gchar *result = NULL; + + /* Expand environment variables */ + gchar *expanded_path = NULL; + if (strchr(path, '%') != NULL) { + DWORD expanded_len = ExpandEnvironmentStringsA(path, NULL, 0); + if (expanded_len > 0) { + expanded_path = g_malloc(expanded_len); + if (ExpandEnvironmentStringsA(path, expanded_path, expanded_len) == 0) { + g_free(expanded_path); + expanded_path = NULL; + } + } + } + + const gchar *work_path = expanded_path ? expanded_path : path; + + /* Handle drive-relative paths */ + if (work_path[0] == '\\' && work_path[1] != '\\') { + char current_dir[MAX_PATH]; + if (GetCurrentDirectoryA(MAX_PATH, current_dir) > 0 && current_dir[1] == ':') { + result = g_malloc(strlen(work_path) + 3); + result[0] = current_dir[0]; + result[1] = ':'; + strcpy(result + 2, work_path); + } + } + /* Convert relative to absolute */ + else if (work_path[0] != '\\' && (strlen(work_path) < 2 || work_path[1] != ':')) { + DWORD full_path_len = GetFullPathNameA(work_path, 0, NULL, NULL); + if (full_path_len > 0) { + result = g_malloc(full_path_len); + if (GetFullPathNameA(work_path, full_path_len, result, NULL) == 0) { + g_free(result); + result = NULL; + } + } + } + /* Path is already absolute */ + else { + result = g_strdup(work_path); + } + + g_free(expanded_path); + return result; +} + /* Makes a path compatible with long path support by adding \\?\ prefix if needed. Caller must free the result. */ gchar * g_path_make_long_compatible (const gchar *path) { if (!path) return NULL; + + gchar *work_path; - if (!g_path_needs_long_prefix(path)) + if (g_path_needs_expansion(path)) { + work_path = g_path_to_absolute(path); + if (!work_path) + return NULL; /* Conversion failed */ + } else { + work_path = g_strdup(path); + } + + gchar *result; + + if (!g_path_needs_long_prefix(work_path)) { + g_free(work_path); return g_strdup(path); + } /* Handle UNC paths: \\server\share becomes \\?\UNC\server\share */ - if (path[0] == '\\' && path[1] == '\\') { - gchar *prefixed = g_malloc(strlen(path) + 7); - strcpy(prefixed, "\\\\?\\UNC\\"); - strcat(prefixed, path + 2); - return prefixed; + if (work_path[0] == '\\' && work_path[1] == '\\') { + result = g_malloc(strlen(work_path) + 7); + strcpy(result, "\\\\?\\UNC\\"); + strcat(result, work_path + 2); + g_free(work_path); + return result; } - /* Handle regular absolute paths: C:\path becomes \\?\C:\path */ - gchar *prefixed = g_malloc(strlen(path) + 5); - strcpy(prefixed, "\\\\?\\"); - strcat(prefixed, path); - return prefixed; + /* Handle absolute paths: C:\path becomes \\?\C:\path */ + result = g_malloc(strlen(work_path) + 5); + strcpy(result, "\\\\?\\"); + strcat(result, work_path); + g_free(work_path); + return result; } #endif From 5fe383d85acadf1c79a9bebeb731c2c10b1df074 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 1 Dec 2025 14:28:30 +0100 Subject: [PATCH 8/9] Feedback: do not expand env vars. --- src/mono/mono/eglib/gpath.c | 89 ++++++++----------------------------- 1 file changed, 18 insertions(+), 71 deletions(-) diff --git a/src/mono/mono/eglib/gpath.c b/src/mono/mono/eglib/gpath.c index e8107ddaa95e73..78917d6bfba05c 100644 --- a/src/mono/mono/eglib/gpath.c +++ b/src/mono/mono/eglib/gpath.c @@ -78,78 +78,26 @@ g_path_needs_long_prefix (const gchar *path) return FALSE; } -/* Helper function to check if a path needs special expansion to absolute path. - * Returns TRUE if the path contains: - * - Environment variables (%) that need expansion - * - Drive-relative paths (\Windows\System32) that need drive letter prepended - * Returns FALSE for normal relative paths and already-absolute paths. +/* Helper function to convert a drive-relative path to an absolute path. + * For example, \Windows\System32 becomes C:\Windows\System32. + * Caller must free the result with g_free(). */ -static gboolean -g_path_needs_expansion (const gchar *path) -{ - if (!path) - return FALSE; - - if (strchr(path, '%') != NULL) - return TRUE; - - if (path[0] == '\\' && path[1] != '\\') - return TRUE; - - return FALSE; -} - static gchar * -g_path_to_absolute (const gchar *path) +g_path_drive_relative_to_absolute (const gchar *path) { if (!path) - return NULL; - - gchar *result = NULL; - - /* Expand environment variables */ - gchar *expanded_path = NULL; - if (strchr(path, '%') != NULL) { - DWORD expanded_len = ExpandEnvironmentStringsA(path, NULL, 0); - if (expanded_len > 0) { - expanded_path = g_malloc(expanded_len); - if (ExpandEnvironmentStringsA(path, expanded_path, expanded_len) == 0) { - g_free(expanded_path); - expanded_path = NULL; - } - } - } - - const gchar *work_path = expanded_path ? expanded_path : path; - - /* Handle drive-relative paths */ - if (work_path[0] == '\\' && work_path[1] != '\\') { - char current_dir[MAX_PATH]; - if (GetCurrentDirectoryA(MAX_PATH, current_dir) > 0 && current_dir[1] == ':') { - result = g_malloc(strlen(work_path) + 3); - result[0] = current_dir[0]; - result[1] = ':'; - strcpy(result + 2, work_path); - } - } - /* Convert relative to absolute */ - else if (work_path[0] != '\\' && (strlen(work_path) < 2 || work_path[1] != ':')) { - DWORD full_path_len = GetFullPathNameA(work_path, 0, NULL, NULL); - if (full_path_len > 0) { - result = g_malloc(full_path_len); - if (GetFullPathNameA(work_path, full_path_len, result, NULL) == 0) { - g_free(result); - result = NULL; - } - } - } - /* Path is already absolute */ - else { - result = g_strdup(work_path); + return g_strdup(path); + + char current_dir[MAX_PATH]; + if (GetCurrentDirectoryA(MAX_PATH, current_dir) > 0 && current_dir[1] == ':') { + gchar *result = g_malloc(strlen(path) + 3); + result[0] = current_dir[0]; + result[1] = ':'; + strcpy(result + 2, path); + return result; } - - g_free(expanded_path); - return result; + + return g_strdup(path); } /* Makes a path compatible with long path support by adding \\?\ prefix if needed. Caller must free the result. */ @@ -161,10 +109,9 @@ g_path_make_long_compatible (const gchar *path) gchar *work_path; - if (g_path_needs_expansion(path)) { - work_path = g_path_to_absolute(path); - if (!work_path) - return NULL; /* Conversion failed */ + /* Drive-relative paths (e.g., \Windows\System32) need to be converted to absolute paths first */ + if (path[0] == '\\' && path[1] != '\\') { + work_path = g_path_drive_relative_to_absolute(path); } else { work_path = g_strdup(path); } From 133b80e91180c2b2a5a0c112226a2ae88af4932e Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:07:50 +0100 Subject: [PATCH 9/9] Update src/tasks/AotCompilerTask/MonoAOTCompiler.cs Co-authored-by: Jan Kotas --- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 67e2fff1453d96..a9b8cbf04e6d74 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -1014,7 +1014,7 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st { // If WorkingDirectory is supplied, the caller could be passing in a relative path // Convert to absolute path to ensure long path support works correctly - assemblyPath = Path.IsPathRooted(assemblyItem.ItemSpec) + assemblyPath = Path.IsPathFullyQualified(assemblyItem.ItemSpec) ? assemblyItem.ItemSpec : Path.GetFullPath(Path.Combine(WorkingDirectory, assemblyItem.ItemSpec)); }