Skip to content

Breaking change: The behaviour of System.IO.Path.GetFullPath has changed for some reserved filenames. #78834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tg73 opened this issue Nov 24, 2022 · 10 comments
Labels
area-System.IO documentation Documentation bug or enhancement, does not impact product or test code
Milestone

Comments

@tg73
Copy link

tg73 commented Nov 24, 2022

Description

Compare the two invocations of System.IO.Path.GetFullPath( "COM3.1.txt" ) detailed below. One returns \\.\COM3, the other returns C:\COM3.1.txt. The older system returns \\.\COM3 as our code expects, the newer system does not. This is a breaking change. I am not able to determine exactly what is responsible for this change - it could be the netfx version, the OS version, or some windows update. The filename COM3.1.txt is used as an example, but other variations are affected in the same way, for example COM3.txt, COM1.txt, PRN.txt.

Both sample systems are x64.

On a sample Win 10 system:

PS C:\> [io.path]::getfullpath("COM3.1.txt")
\\.\COM3
PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1682
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1682
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PS C:\> [Environment]::OSVersion

Platform ServicePack Version      VersionString
-------- ----------- -------      -------------
 Win32NT             10.0.19045.0 Microsoft Windows NT 10.0.19045.0

PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Version
4.8.04084
PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release
528372

On a sample Win 11 system:

PS C:\> [io.path]::getfullpath("COM3.1.txt")
C:\COM3.1.txt
PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.22621.608
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22621.608
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PS C:\> [Environment]::OSVersion

Platform ServicePack Version      VersionString
-------- ----------- -------      -------------
 Win32NT             10.0.22621.0 Microsoft Windows NT 10.0.22621.0

PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Version
4.8.09032
PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release
533320

Reproduction Steps

See above.

Expected behavior

System.IO.Path.GetFullPath( "COM3.1.txt" ) should return \\.\COM3, as it has done historically.

Actual behavior

System.IO.Path.GetFullPath( "COM3.1.txt" ) returns C:\COM3.1.txt on some likely more-modern or more-updated environments.

Regression?

Yes, it is a regression.

Known Workarounds

No response

Configuration

See description.

Other information

No response

@ghost
Copy link

ghost commented Nov 24, 2022

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Nov 24, 2022
@ghost
Copy link

ghost commented Nov 24, 2022

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Compare the two invocations of System.IO.Path.GetFullPath( "COM3.1.txt" ) detailed below. One returns \\.\COM3, the other returns C:\COM3.1.txt. The older system returns \\.\COM3 as our code expects, the newer system does not. This is a breaking change. I am not able to determine exactly what is responsible for this change - it could be the netfx version, the OS version, or some windows update. The filename COM3.1.txt is used as an example, but other variations are affected in the same way, for example COM3.txt, COM1.txt, PRN.txt.

Both sample systems are x64.

On a sample Win 10 system:

PS C:\> [io.path]::getfullpath("COM3.1.txt")
\\.\COM3
PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1682
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1682
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PS C:\> [Environment]::OSVersion

Platform ServicePack Version      VersionString
-------- ----------- -------      -------------
 Win32NT             10.0.19045.0 Microsoft Windows NT 10.0.19045.0

PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Version
4.8.04084
PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release
528372

On a sample Win 11 system:

PS C:\> [io.path]::getfullpath("COM3.1.txt")
C:\COM3.1.txt
PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.22621.608
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22621.608
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

PS C:\> [Environment]::OSVersion

Platform ServicePack Version      VersionString
-------- ----------- -------      -------------
 Win32NT             10.0.22621.0 Microsoft Windows NT 10.0.22621.0

PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Version
4.8.09032
PS C:\> (Get-ItemProperty "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release
533320

Reproduction Steps

See above.

Expected behavior

System.IO.Path.GetFullPath( "COM3.1.txt" ) should return \\.\COM3, as it has done historically.

Actual behavior

System.IO.Path.GetFullPath( "COM3.1.txt" ) returns C:\COM3.1.txt on some likely more-modern or more-updated environments.

Regression?

Yes, it is a regression.

Known Workarounds

No response

Configuration

See description.

Other information

No response

Author: tg73
Assignees: -
Labels:

area-System.IO, untriaged

Milestone: -

@jozkee jozkee closed this as completed Nov 24, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Nov 24, 2022
@jozkee jozkee reopened this Nov 24, 2022
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Nov 24, 2022
@danmoseley
Copy link
Member

Could you try sooner older .NET Core versions to narrow down when it changed?

@tg73
Copy link
Author

tg73 commented Nov 24, 2022

I've realised I might have put this in the wrong repo. I'm encountering the issue with .NET Framework 4.x - and hence the challenge with identifying exactly what part of the Windows installation has caused the change.

If this is the wrong repo, could an admin move it if possible, or advise and I'll repost in the correct repo.

@danmoseley
Copy link
Member

For what it's worth, I get the "c:" result for all versions of .NET Framework and .NET Core on my machine. Could it depend on what serial ports exist on the machine (from the OS point of view) and how they are registered? Path.GetFullPath("COM3") gives \\.\COM3 as you expect.

I am guessing that .NET Core behaves the same as .NET Framework on whatever machine is hitting this, is that right?

What does HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM have?

Also, is this breaking something or are you just curious?

@tg73
Copy link
Author

tg73 commented Nov 24, 2022

@danmoseley try it with PRN.txt, AUX.txt or other reserved names. Unless you rollback a Windows update, you only have one 4.x version of the runtime installed on your machine, even if you target a different version. You need to try a variety of different machines with different installed versions etc. Using GetFullPath in this way is a known technique for identifying reserved filenames. I discovered the behaviour change because our unit tests broke, and code changes were required to accommodate the changes behaviour. See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file.

@danmoseley
Copy link
Member

Unless you rollback a Windows update, you only have one 4.x version of the runtime installed on your machine, even if you target a different version. You need to try a variety of different machines with different installed versions etc.

I used 2.0. It is side by side.

@danmoseley
Copy link
Member

danmoseley commented Nov 25, 2022

Anyway, I found the relevant change in the OS. It makes the code within GetFullPathNameW be more strict when it decides whether an input is a legacy DOS device name. Previously, extensions were ignored, now they are not. The change was apparently to make it possible to work with files from other OS, eg., in the Linux kernel repo there is a file named aux.c.

As you suspected, it's not a change in .NET. Shall we close this?

@tg73
Copy link
Author

tg73 commented Nov 25, 2022

I used 2.0. It is side by side.

Ok! Now that's what I call checking for a regression...

Anyway, I found the relevant change in the OS.

Do you know what footprint (eg, OS version) this change has? It would be interesting and useful to know.

As you suspected, it's not a change in .NET. Shall we close this?

Given that methods like GetInvalidPathChars return a platform-specific result, I would agree that in this matter, GetFullPath is behaving in the same spirit. However, despite quite lengthy discussion of what paths can look like, including platform differences, I can find no mention in the docs docs of the reserved filename behaviour on Windows platforms. Also, I would argue that differing behaviour across relatively similar Windows platforms would come as quite a surprise to most developers (versus differing behaviour between Windows and Linux, for example). So perhaps the best resolution to this issue would be to improve the docs, including to note the unusually-differing behaviour of GetFullPath across relatively similar Windows platforms, and the affected footprint?

@jozkee jozkee added the documentation Documentation bug or enhancement, does not impact product or test code label Dec 21, 2022
@jozkee jozkee added this to the Future milestone Dec 21, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Dec 21, 2022
@danmoseley
Copy link
Member

@tg73 missed your comment. We'd welcome a PR to improve the doc if you would like. (click pencil icon or edit button on doc page)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.IO documentation Documentation bug or enhancement, does not impact product or test code
Projects
None yet
Development

No branches or pull requests

4 participants