Skip to content
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

Path functions behaving differently on UNC device paths than implied from documentation #95619

Open
s5bug opened this issue Dec 5, 2023 · 7 comments
Labels
area-System.IO breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.
Milestone

Comments

@s5bug
Copy link

s5bug commented Dec 5, 2023

Description

File path formats on Windows systems states:

For device UNCs, the server/share portion forms the volume. For example, in \\?\server1\utilities\\filecomparer\, the server/share portion is server1\utilities. This is significant when calling a method such as Path.GetFullPath(String, String) with relative directory segments; it is never possible to navigate past the volume.

The way this reads to me is that \\?\server1\utilities\\ should be considered the "root" of \\?\server1\utilities\\filecomparer\.

Specifically, the documentation states

  • "the server/share portion forms the volume"
  • "in [...] the server/share portion is server1\utilities"
  • "...it is never possible to navigate past the volume"

This seems to not be respected:

Reproduction Steps

> dotnet fsi

Microsoft (R) F# Interactive version 12.4.0.0 for F# 7.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> open System.IO;;
> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

Expected behavior

> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\utilities\\"

> Path.GetPathRoot("""\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\utilities\\"

> Path.GetDirectoryName("""\\?\server1\utilities\\""");;
val it: string = <null>

Actual behavior

> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

> Path.GetPathRoot("""\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

> Path.GetDirectoryName("""\\?\server1\utilities\\""");;
val it: string = "\\?\server1\utilities"

Regression?

I don't know how to test old .NET versions, I'm not a long-time .NET user.

Known Workarounds

No response

Configuration

> dotnet --version
7.0.100
> Get-ComputerInfo -Property "Os*"

OsName                                     : Microsoft Windows 10 Home
OsType                                     : WINNT
OsOperatingSystemSKU                       : WindowsHome
OsVersion                                  : 10.0.19045
OsArchitecture                                          : 64-bit
OsLanguage                                              : en-US

I would be pretty sure it's Windows (Path)-specific.

Other information

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Dec 5, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 5, 2023
@danmoseley
Copy link
Member

FYI a good way to do a test on .NET Framework (ie to check old behavior) is http://sharplab.io (pick it from the drop-down)

@s5bug
Copy link
Author

s5bug commented Dec 5, 2023

It seems sharplab is not a fan of System.IO.Path:

Unbreakable.AssemblyGuardException: Type System.IO.Path is not allowed.

Example Code

using System;
using System.IO;

Console.WriteLine(
  Path.Combine("\\\\?\\server1\\utilities\\\\filecomparer\\", "..\\..")
);

@huoyaoyuan huoyaoyuan added area-System.IO and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Dec 5, 2023
@ghost
Copy link

ghost commented Dec 5, 2023

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

Issue Details

Description

File path formats on Windows systems states:

For device UNCs, the server/share portion forms the volume. For example, in \\?\server1\utilities\\filecomparer\, the server/share portion is server1\utilities. This is significant when calling a method such as Path.GetFullPath(String, String) with relative directory segments; it is never possible to navigate past the volume.

The way this reads to me is that \\?\server1\utilities\\ should be considered the "root" of \\?\server1\utilities\\filecomparer\.

Specifically, the documentation states

  • "the server/share portion forms the volume"
  • "in [...] the server/share portion is server1\utilities"
  • "...it is never possible to navigate past the volume"

This seems to not be respected:

Reproduction Steps

> dotnet fsi

Microsoft (R) F# Interactive version 12.4.0.0 for F# 7.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> open System.IO;;
> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

Expected behavior

> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\utilities\\"

> Path.GetPathRoot("""\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\utilities\\"

> Path.GetDirectoryName("""\\?\server1\utilities\\""");;
val it: string = <null>

Actual behavior

> Path.GetFullPath("""..\..""", """\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

> Path.GetPathRoot("""\\?\server1\utilities\\filecomparer\""");;
val it: string = "\\?\server1\"

> Path.GetDirectoryName("""\\?\server1\utilities\\""");;
val it: string = "\\?\server1\utilities"

Regression?

I don't know how to test old .NET versions, I'm not a long-time .NET user.

Known Workarounds

No response

Configuration

> dotnet --version
7.0.100
> Get-ComputerInfo -Property "Os*"

OsName                                     : Microsoft Windows 10 Home
OsType                                     : WINNT
OsOperatingSystemSKU                       : WindowsHome
OsVersion                                  : 10.0.19045
OsArchitecture                                          : 64-bit
OsLanguage                                              : en-US

I would be pretty sure it's Windows (Path)-specific.

Other information

No response

Author: s5bug
Assignees: -
Labels:

area-System.IO, untriaged

Milestone: -

@huoyaoyuan
Copy link
Member

Path.GetPathRoot(@"\\?\server1\utilities\\filecomparer\") returns empty string on .NET Framework. Path.GetFullPath(path, basePath) isn't supported on .NET Framework. So this is likely not a regression.

@s5bug
Copy link
Author

s5bug commented Dec 5, 2023

What about Path.Combine for .NET Framework, as shown in the example code?

@jozkee
Copy link
Member

jozkee commented Dec 11, 2023

What about Path.Combine

Path.Combine doesn't resolve relative segments, what you are referring to is being discussed in #2162.

@jozkee jozkee added this to the Future milestone Dec 11, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Dec 11, 2023
@jozkee jozkee added breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. untriaged New issue has not been triaged by the area owner labels Dec 11, 2023
@ghost ghost added the needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet label Dec 11, 2023
@ghost

This comment was marked as off-topic.

@jozkee jozkee removed untriaged New issue has not been triaged by the area owner needs-breaking-change-doc-created Breaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet labels Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.IO breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.
Projects
None yet
Development

No branches or pull requests

4 participants