Skip to content

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

Open

Description

@s5bug

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

Activity

  1. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Dec 5, 2023
  2. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Dec 5, 2023
  3. danmoseley commented on Dec 5, 2023

    @danmoseley
    Contributor

    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)

  4. s5bug commented on Dec 5, 2023

    @s5bug
    Author

    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\\", "..\\..")
    );
  5. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Dec 5, 2023
  6. huoyaoyuan commented on Dec 5, 2023

    @huoyaoyuan
    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.

  7. s5bug commented on Dec 5, 2023

    @s5bug
    Author

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

  8. jozkee commented on Dec 11, 2023

    @jozkee
    Member

    What about Path.Combine

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

  9. added this to the Future milestone on Dec 11, 2023
  10. ghost removed
    untriagedNew issue has not been triaged by the area owner
    on Dec 11, 2023
  11. added
    breaking-changeIssue or PR that represents a breaking API or functional change over a previous release.
    untriagedNew issue has not been triaged by the area owner
    on Dec 11, 2023
  12. ghost added
    needs-breaking-change-doc-createdBreaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet
    on Dec 11, 2023
  13. removed
    untriagedNew issue has not been triaged by the area owner
    needs-breaking-change-doc-createdBreaking changes need an issue opened with https://github.com/dotnet/docs/issues/new?template=dotnet
    on Dec 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.IObreaking-changeIssue or PR that represents a breaking API or functional change over a previous release.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions