Skip to content

Use Microsoft.IO.Redist in Framework FileUtilities/NativeMethods on net472 (#13078) - #13428

Open
sachinsharma3191 wants to merge 5 commits into
dotnet:mainfrom
sachinsharma3191:dev/13078-microsoft-io-fileutilities
Open

sachinsharma3191 wants to merge 5 commits into
dotnet:mainfrom
sachinsharma3191:dev/13078-microsoft-io-fileutilities

Conversation

@sachinsharma3191

@sachinsharma3191 sachinsharma3191 commented Mar 21, 2026

Copy link
Copy Markdown

Summary

Follow-up test coverage for #13078. The Microsoft.IO.Redist integration in FileUtilities (NewPath.GetFullPath on .NET Framework, removal of FEATURE_LEGACY_GETCURRENTDIRECTORY, no length-based rejection in PathIsInvalid) has already landed in main. This PR contains test changes only; there are no product code changes.

Changes

Tests added to src/Framework.UnitTests/FileUtilities_Tests.cs:

  • PathIsInvalid_RejectsInvalidPathCharacters: a path containing | is reported as invalid.
  • PathIsInvalid_DoesNotRejectLongPaths: paths longer than MAX_PATH are not treated as invalid (all platforms).
  • PathIsInvalid_DoesNotRejectLeadingOrTrailingWhitespace: whitespace-padded paths are not rejected by PathIsInvalid (all platforms).
  • NormalizePath_OnNetFramework_UsesMicrosoftIoGetFullPath (net472, Windows): uses a path with an alternate-data-stream :. System.IO.Path.GetFullPath on .NET Framework throws NotSupportedException for that path and Microsoft.IO.Path.GetFullPath accepts it, so the test verifies that NormalizePath goes through Microsoft.IO.Redist.

Related to #13078

@sachinsharma3191

Copy link
Copy Markdown
Author

@dotnet-policy-service agree

sachinsharma3191 added a commit to sachinsharma3191/msbuild that referenced this pull request Mar 22, 2026
sachinsharma3191 added a commit to sachinsharma3191/msbuild that referenced this pull request Mar 22, 2026
@sachinsharma3191
sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch 3 times, most recently from 72bfc27 to 96d5dfb Compare March 22, 2026 01:44
@sachinsharma3191
sachinsharma3191 marked this pull request as ready for review March 23, 2026 03:18
@sachinsharma3191
sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch from b4db326 to ccc36bd Compare March 23, 2026 05:09
@AR-May AR-May self-assigned this Mar 24, 2026
Comment thread src/Framework/FileUtilities.cs Outdated
catch (PathTooLongException)
{
// Trigger the same exception for truly invalid characters even if path is long
if (path.Contains('|'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason only "|" is handled? I believe there are multiple invalid characters.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The custom invalid-character check has been removed entirely. GetFullPath no longer does ad-hoc filtering for | (or any specific character); it relies on the CLR's own validation (via Path.HasExtension) plus IsUNCPath, matching the implementation now in main. This branch has been merged up to main, where the Microsoft.IO.Redist integration already landed, so the ad-hoc character handling is gone.

Comment thread src/Framework/FileUtilities.cs Outdated

// Paths that exceed MAX_PATH (260) will cause GetFullPath to throw PathTooLongException on
// legacy Windows. Treat as invalid so callers skip NormalizePath and handle gracefully (e.g. RAR Regress314573).
if (path.Length >= NativeMethods.MAX_PATH)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PathIsInvalid usage is not limited to normalization. I am not yet sure about marking paths with trailing whitespaces as invalid, but marking all long paths as invalid may cause build failures on systems where long paths are allowed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The blanket MAX_PATH invalidation was removed. PathIsInvalid now only checks InvalidPathChars / InvalidFileNameChars and no longer rejects paths based on length, so long paths are not marked invalid on systems that allow them. This matches the current main behavior after merging up.

Comment thread src/Directory.BeforeCommon.targets Outdated
<DefineConstants>$(DefineConstants);FEATURE_INSTALLED_MSBUILD</DefineConstants>
<!-- Directory.GetCurrentDirectory The pre .Net 4.6.2 implementation of Directory.GetCurrentDirectory is slow and creates strings in its work. -->
<DefineConstants>$(DefineConstants);FEATURE_LEGACY_GETCURRENTDIRECTORY</DefineConstants>
<!-- <DefineConstants>$(DefineConstants);FEATURE_LEGACY_GETCURRENTDIRECTORY</DefineConstants> -->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: better remove the line alltogether.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The FEATURE_LEGACY_GETCURRENTDIRECTORY line has been removed entirely from Directory.BeforeCommon.targets. Only FEATURE_LEGACY_GETFULLPATH remains, consistent with main.

Comment thread src/Framework/FileUtilities.cs Outdated
This check can only be properly done after normalizing, so
\\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\
(an internal kernel path) because it provides aliases for drives.
string redistResult = NewPath.GetFullPath(path);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since exceptions are significantly more expensive than simple branching, relying on the ArgumentException fallback for many paths could noticeably slow down builds. It would be better to avoid using exceptions for control flow if we can.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The exception-based control flow (the try/catch ArgumentException fallback around the redist path) has been removed. GetFullPath now uses simple branching only, so there is no reliance on exceptions for the common path. This matches the implementation in main.

Comment thread src/Directory.Build.targets Outdated

<Error Condition="!Exists('$(TlbExpPath)')"
Text="TlbExp was not found at '$(TlbExpPath)'. Ensure the .NET Framework SDK tools are installed." />
<Warning Condition="!Exists('$(TlbExpPath)')"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why this needs to be downgraded from an error to a warning? I’m not sure how the change to this file relates to the changes in this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted. The error-to-warning downgrade for the TlbExp check was unrelated to this PR and has been dropped — Directory.Build.targets is back to the original <Error Condition=!Exists('$(TlbExpPath)') />. There is no longer any change to this file in the PR.

Comment thread src/Framework/FileUtilities.cs Outdated

throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC"));
// Re-validate UNC roots that Redist might accept but MSBuild tests expect to fail.
if (redistResult.StartsWith(@"\\", StringComparison.Ordinal) && (redistResult is @"\\" or @"\\\\" or @"\\localhost" or @"\\XXX\"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems UNC paths handling changed significantly. Can you explain those changes in detail? Are there any behavioral changes compared to the previous version?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. The custom UNC-handling changes have been removed. The original IsUNCPath logic (guarded by FEATURE_LEGACY_GETFULLPATH) is restored, so there is no behavioral change in UNC path handling compared to the previous version. This now matches main.

Comment thread src/Framework/NativeMethods.cs Outdated
#endif
}

#if FEATURE_LEGACY_GETCURRENTDIRECTORY

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that we are removing FEATURE_LEGACY_GETCURRENTDIRECTORY from project files. Do we still have this code path enabled anywhere?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No — it is no longer enabled anywhere. FEATURE_LEGACY_GETCURRENTDIRECTORY is not defined in any project file, and the corresponding code path has been removed from NativeMethods.cs. Only FEATURE_LEGACY_GETFULLPATH remains, matching main.

@DustinCampbell

Copy link
Copy Markdown
Member

I intentionally left it for later when moving FileUtilities to Microsoft.Build.Framework, so I'm glad to see this in the works!

Note: There are a lot of tests that will fail because they expect exceptions to be thrown on .NET Framework. Microsoft.IO.Redist doesn't do all of the path validation that was done by System.IO.Path on full framework. Instead, it matches the behavior on modern .NET. So, I would expect there to be significant test updates for those tests that expect thrown exceptions on .NET Framework.

cc @JeremyKuhne (the Microsoft.IO.Redist author/guru) in case he has any thoughts.

@JeremyKuhne

Copy link
Copy Markdown
Member

I would expect there to be significant test updates for those tests that expect thrown exceptions on .NET Framework.

GetFullPath is what normally throws for "bad" paths (often called indirectly when calling other IO APIs). It no longer checks for anything other than embedded nulls. Paths will throw when used by the OS and whatever filesystem is involved (unblocking a number of more complicated setups).

If there are any questions or issues feel free to tag me when they arise.

@AlesProkop

Copy link
Copy Markdown
Member

MSBuild triage: @sachinsharma3191 Are you still interested in merging this? If so, please address comments and merge conflicts.

@sachinsharma3191

Copy link
Copy Markdown
Author

Sure

@sachinsharma3191

Copy link
Copy Markdown
Author

Thanks @AR-May for the review. I merged the branch up to main and addressed each point below.

  1. FileUtilities.cs - only pipe character handled
    Fixed. The custom invalid-character check was removed. GetFullPath now uses CLR validation via Path.HasExtension plus IsUNCPath, matching main.

  2. FileUtilities.cs - PathIsInvalid and long paths
    Fixed. Blanket MAX_PATH invalidation was removed. PathIsInvalid now only checks InvalidPathChars and InvalidFileNameChars, so long paths are not rejected on systems that allow them.

  3. Directory.BeforeCommon.targets - remove the line
    Done. FEATURE_LEGACY_GETCURRENTDIRECTORY was removed entirely. Only FEATURE_LEGACY_GETFULLPATH remains, consistent with main.

  4. FileUtilities.cs - exception-based control flow
    Fixed. The try/catch ArgumentException fallback around the redist path was removed. GetFullPath uses simple branching only.

  5. Directory.Build.targets - error downgraded to warning
    Reverted. The unrelated TlbExp error-to-warning change was dropped. Directory.Build.targets is back to the original Error condition.

  6. FileUtilities.cs - UNC path handling
    Addressed. Custom UNC handling changes were removed and the original IsUNCPath logic was restored, so UNC behavior matches the previous version.

  7. NativeMethods.cs - FEATURE_LEGACY_GETCURRENTDIRECTORY
    No, it is not enabled anywhere. The define and code path were removed from project files and NativeMethods.cs.

Merge conflicts with main are resolved and the branch is up to date with main. The Microsoft.IO.Redist integration for Framework FileUtilities and NativeMethods on net472 is already present in main, so this PR now has no net code changes versus main.

@AlesProkop

Copy link
Copy Markdown
Member

triage: @AR-May. Could you please re-review.

@sachinsharma3191

Copy link
Copy Markdown
Author

Removed the earlier comment that incorrectly claimed tests were already added. I have now added and pushed real test coverage in commit 1486139.

Tests added in FileUtilities_Tests.cs:

  • PathIsInvalid_RejectsInvalidPathCharacters - verifies invalid characters such as pipe are rejected
  • PathIsInvalid_DoesNotRejectLongPaths - verifies paths longer than MAX_PATH are not treated as invalid
  • PathIsInvalid_DoesNotRejectLeadingOrTrailingWhitespace - verifies whitespace-padded paths are not rejected by PathIsInvalid
  • NormalizePath_RootedPathOnNetFramework_MatchesMicrosoftIoGetFullPath - on net472, verifies FileUtilities.NormalizePath matches Microsoft.IO.Path.GetFullPath

These cover the path validation and Microsoft.IO.Redist behavior discussed in review. CI on this PR should run the Framework unit tests; I could not run dotnet test locally because no SDK is installed in this environment.

@AR-May

AR-May commented Jul 2, 2026

Copy link
Copy Markdown
Member

@sachinsharma3191 it seems something went wrong with the PR update - now the only change is in the tests.

@sachinsharma3191

Copy link
Copy Markdown
Author

Local build and tests pass successfully.

Build Status: .\build.cmd -v quiet — SUCCEEDED (0 errors, 0 warnings)

Test Results:

  • PathIsInvalid tests: 6 passed (net472 + net10.0)

    • PathIsInvalid_RejectsInvalidPathCharacters ✓
    • PathIsInvalid_DoesNotRejectLongPaths ✓
    • PathIsInvalid_DoesNotRejectLeadingOrTrailingWhitespace ✓
  • NormalizePath test: 1 passed (net472, gated to #if NETFRAMEWORK)

    • NormalizePath_RootedPathOnNetFramework_MatchesMicrosoftIoGetFullPath ✓

Code Changes: Only test file modifications in src/Framework.UnitTests/FileUtilities_Tests.cs to cover Microsoft.IO.Redist behavior. No production code changes.

Validation: Branch contains only meaningful test coverage for the redist integration that landed in main. Ready for re-review.

cc @AR-May @AlesProkop

@AlesProkop
AlesProkop requested a review from AR-May July 7, 2026 07:16
Cover the net472 Framework path handling introduced in dotnet#13078:
- PathIsInvalid rejects invalid path characters
- PathIsInvalid does not reject long (>MAX_PATH) paths
- PathIsInvalid does not reject leading/trailing whitespace
- NormalizePath on .NET Framework matches Microsoft.IO.Path.GetFullPath
@sachinsharma3191
sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch from 1486139 to 547e421 Compare August 6, 2026 03:22

@AR-May AR-May left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests looks fine to me, I only have couple of comments. I was confused by the description expecting that there should be code changes and not realizing this is a follow-up test coverage PR now. Could you please adjust the description so it matches the PR content?

}

[WindowsOnlyFact]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do we need to limit this test and the next test to windows platform? PathIsInvalid uses MSBuild's hardcoded union of invalid chars across all OSes, so the platform should not make the difference here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — PathIsInvalid only uses MSBuild's hardcoded cross-OS character union, so the platform doesn't matter. Both PathIsInvalid_DoesNotRejectLongPaths and PathIsInvalid_DoesNotRejectLeadingOrTrailingWhitespace are now plain [Fact]s (dd56112).

[WindowsOnlyFact]
public void NormalizePath_RootedPathOnNetFramework_MatchesMicrosoftIoGetFullPath()
{
string path = @"c:\temp\subdir\..\..\windows";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the goal of this test was to ensure that Microsoft.IO.Path is used for normalization, then this test would not work. For the chosen path, System.IO.Path.GetFullPath  and  Microsoft.IO.Path.GetFullPath  return the identical string.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that path normalizes identically with both APIs. I changed the test (now NormalizePath_OnNetFramework_UsesMicrosoftIoGetFullPath) to use c:\temp\subdir\..\file.txt:stream. On .NET Framework, System.IO.Path.GetFullPath throws NotSupportedException for a ':' outside the volume-separator position, while Microsoft.IO.Path.GetFullPath accepts it. The test asserts both, so it fails if NormalizePath ever falls back to System.IO.Path (dd56112).

@JeremyKuhne

Copy link
Copy Markdown
Member

Apologies for the noise here. I mistakenly identified #13428 as the PR I was supposed to update. I pushed e6c31120a7 and resolved two review threads without confirming the PR. I’ve reverted that commit and reopened the threads.

…oft.IO normalization test discriminating

PathIsInvalid uses MSBuild's hardcoded union of invalid characters across
all OSes, so the long-path and whitespace tests no longer need to be
Windows-only.

The net472 NormalizePath test now uses a path with an alternate data
stream colon, which System.IO.Path.GetFullPath on .NET Framework rejects
with NotSupportedException but Microsoft.IO.Path.GetFullPath accepts, so
the test fails if NormalizePath stops using Microsoft.IO.Redist.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants