Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use SubstituteName instead of PrintName since the latter is just for …
…display and can be misleading
  • Loading branch information
jozkee committed Sep 9, 2021
commit fbcfe3ed943be74cd9a0c61166a2d0dc67609e79
1 change: 1 addition & 0 deletions src/libraries/Common/src/System/IO/PathInternal.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ internal static partial class PathInternal
internal const string UncPathPrefix = @"\\";
internal const string UncExtendedPrefixToInsert = @"?\UNC\";
internal const string UncExtendedPathPrefix = @"\\?\UNC\";
internal const string UncNTPathPrefix = @"\??\UNC\";
internal const string DevicePathPrefix = @"\\.\";
internal const string ParentDirectoryPrefix = @"..\";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.IO.Enumeration;
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -158,7 +157,7 @@ public void ResolveLinkTarget_ReturnsNull_NotALink(bool returnFinalTarget)
}

[Theory]
[MemberData(nameof(ResolveLinkTarget_PathToTarget_Data))]
[MemberData(nameof(SymbolicLink_ResolveLinkTarget_PathToTarget_Data))]
public void ResolveLinkTarget_Succeeds(string pathToTarget, bool returnFinalTarget)
{
string linkPath = GetRandomLinkPath();
Expand Down Expand Up @@ -295,22 +294,6 @@ public void ResolveLinkTarget_ReturnFinalTarget_ChainOfLinks_ExceedsLimit_Throws
Assert.Throws<IOException>(() => ResolveLinkTarget(tail, returnFinalTarget: true));
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(@"\??\", false)]
[InlineData(@"\??\", true)]
[InlineData(@"\\?\", false)]
[InlineData(@"\\?\", true)]
public void ResolveLinkTarget_ExtendedPrefix_IsNotTrimmed(string prefix, bool returnFinalTarget)
{
string linkPath = GetRandomLinkPath();
string targetPathWithPrefix = Path.Join(prefix, GetRandomFilePath());
CreateSymbolicLink(linkPath, targetPathWithPrefix);

FileSystemInfo info = ResolveLinkTarget(linkPath, returnFinalTarget);
Assert.Equal(targetPathWithPrefix, info.FullName);
}

private string CreateChainOfLinks(string target, int length, bool relative)
{
string previousPath = target;
Expand Down Expand Up @@ -419,22 +402,22 @@ private void ResolveLinkTarget_ReturnFinalTarget(string link1Path, string link1T
Assert.True(link2Info.Exists);
Assert.True(link2Info.Attributes.HasFlag(FileAttributes.ReparsePoint));
AssertIsCorrectTypeAndDirectoryAttribute(link2Info);
Assert.Equal(link2Target, link2Info.LinkTarget);
AssertPathEquals_RelativeSegments(link2Target, link2Info.LinkTarget);

// link1 to link2
FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link1Target);
Assert.True(link1Info.Exists);
Assert.True(link1Info.Attributes.HasFlag(FileAttributes.ReparsePoint));
AssertIsCorrectTypeAndDirectoryAttribute(link1Info);
Assert.Equal(link1Target, link1Info.LinkTarget);
AssertPathEquals_RelativeSegments(link1Target, link1Info.LinkTarget);

// link1: do not follow symlinks
FileSystemInfo link1TargetInfo = ResolveLinkTarget(link1Path, returnFinalTarget: false);
Assert.True(link1TargetInfo.Exists);
AssertIsCorrectTypeAndDirectoryAttribute(link1TargetInfo);
Assert.True(link1TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint));
Assert.Equal(link2Path, link1TargetInfo.FullName);
Assert.Equal(link2Target, link1TargetInfo.LinkTarget);
AssertPathEquals_RelativeSegments(link2Target, link1TargetInfo.LinkTarget);

// link2: do not follow symlinks
FileSystemInfo link2TargetInfo = ResolveLinkTarget(link2Path, returnFinalTarget: false);
Expand All @@ -450,6 +433,19 @@ private void ResolveLinkTarget_ReturnFinalTarget(string link1Path, string link1T
AssertIsCorrectTypeAndDirectoryAttribute(finalTarget);
Assert.False(finalTarget.Attributes.HasFlag(FileAttributes.ReparsePoint));
Assert.Equal(filePath, finalTarget.FullName);

void AssertPathEquals_RelativeSegments(string expected, string actual)
{
#if WINDOWS
// DeviceIoControl canonicalizes the target path i.e: removes redundant segments.
int rootLength = PathInternal.GetRootLength(expected);
if (rootLength > 0)
{
expected = PathInternal.RemoveRelativeSegments(expected, rootLength);
}
#endif
Assert.Equal(expected, actual);
}
}

// Must call inside a remote executor
Expand Down Expand Up @@ -501,50 +497,5 @@ protected void CreateSymbolicLink_PathToTarget_RelativeToLinkPath_Internal(bool
(entry.Attributes & FileAttributes.ReparsePoint) != 0
}.FirstOrDefault();
}

public static IEnumerable<object[]> ResolveLinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData)
{
yield return new object[] { path, false };
yield return new object[] { path, true };
}
}
}

internal static IEnumerable<string> PathToTargetData
{
get
{
if (OperatingSystem.IsWindows())
{
//Non-rooted relative
yield return "foo";
yield return @".\foo";
yield return @"..\foo";
// Rooted relative
yield return @"\foo";
// Rooted absolute
yield return Path.Combine(Path.GetTempPath(), "foo");
// Extended DOS
yield return Path.Combine(@"\\?\", Path.GetTempPath(), "foo");
// UNC
yield return @"\\LOCALHOST\share\path";
}
else
{
//Non-rooted relative
yield return "foo";
yield return "./foo";
yield return "../foo";
// Rooted relative
yield return "/foo";
// Rooted absolute
Path.Combine(Path.GetTempPath(), "foo");
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using Xunit;

namespace System.IO.Tests
Expand Down Expand Up @@ -54,7 +52,7 @@ public void LinkTarget_ReturnsNull_NotALink()
}

[Theory]
[MemberData(nameof(LinkTarget_PathToTarget_Data))]
[MemberData(nameof(SymbolicLink_LinkTarget_PathToTarget_Data))]
public void LinkTarget_Succeeds(string pathToTarget)
{
FileSystemInfo linkInfo = CreateSymbolicLink(GetRandomLinkPath(), pathToTarget);
Expand Down Expand Up @@ -86,29 +84,5 @@ public void LinkTarget_RefreshesCorrectly()
Assert.Equal(newPathToTarget, linkInfo.LinkTarget);
Assert.Equal(newLinkInfo.LinkTarget, linkInfo.LinkTarget);
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[InlineData(@"\??\")]
[InlineData(@"\\?\")]
public void LinkTarget_ExtendedPrefix_IsNotTrimmed(string prefix)
{
string linkPath = GetRandomLinkPath();
string targetPathWithPrefix = Path.Join(prefix, GetRandomFilePath());

FileSystemInfo info = CreateSymbolicLink(linkPath, targetPathWithPrefix);
Assert.Equal(targetPathWithPrefix, info.LinkTarget);
}

public static IEnumerable<object[]> LinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData)
{
yield return new object[] { path };
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace System.IO.Tests
Expand Down Expand Up @@ -45,5 +47,95 @@ protected string ChangeCurrentDirectory()
Directory.SetCurrentDirectory(tempCwd);
return tempCwd;
}

public static IEnumerable<object[]> SymbolicLink_LinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData.Union(PathToTargetUncData))
{
yield return new object[] { path };
}
}
}

public static IEnumerable<object[]> SymbolicLink_ResolveLinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData.Union(PathToTargetUncData))
{
yield return new object[] { path, false };
yield return new object[] { path, true };
}
}
}

// Junctions doesn't support remote shares.
public static IEnumerable<object[]> Junction_LinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData)
{
yield return new object[] { path };
}
}
}

public static IEnumerable<object[]> Junction_ResolveLinkTarget_PathToTarget_Data
{
get
{
foreach (string path in PathToTargetData)
{
yield return new object[] { path, false };
yield return new object[] { path, true };
}
}
}

internal static IEnumerable<string> PathToTargetData
{
get
{
if (OperatingSystem.IsWindows())
{
//Non-rooted relative
yield return "foo";
yield return @".\foo";
yield return @"..\foo";
// Rooted relative
yield return @"\foo";
// Rooted absolute
yield return Path.Combine(Path.GetTempPath(), "foo");
// Extended DOS
yield return Path.Combine(@"\\?\", Path.GetTempPath(), "foo");
}
else
{
//Non-rooted relative
yield return "foo";
yield return "./foo";
yield return "../foo";
// Rooted relative
yield return "/foo";
// Rooted absolute
Path.Combine(Path.GetTempPath(), "foo");
}
}
}

internal static IEnumerable<string> PathToTargetUncData
{
get
{
if (OperatingSystem.IsWindows())
{
// UNC/Remote Share
yield return @"\\LOCALHOST\share\path";
}
}
}
}
}
42 changes: 39 additions & 3 deletions src/libraries/System.IO.FileSystem/tests/Junctions.Windows.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace System.IO.Tests
{
[PlatformSpecific(TestPlatforms.Windows)]
public class Junctions : BaseSymbolicLinks
{
protected DirectoryInfo CreateJunction(string junctionPath, string targetPath)
private DirectoryInfo CreateJunction(string junctionPath, string targetPath)
{
Assert.True(MountHelper.CreateJunction(junctionPath, targetPath));
DirectoryInfo junctionInfo = new(junctionPath);
Expand Down Expand Up @@ -67,5 +65,43 @@ public void Junction_ResolveLinkTarget_WithIndirection(bool returnFinalTarget)
Assert.Equal(expectedTargetPath, targetFromDirectoryInfo.FullName);
Assert.Equal(expectedTargetPath, targetFromDirectory.FullName);
}

[Theory]
[MemberData(nameof(Junction_ResolveLinkTarget_PathToTarget_Data))]
public void Junction_ResolveLinkTarget_Succeeds(string pathToTarget, bool returnFinalTarget)
{
string linkPath = GetRandomLinkPath();
FileSystemInfo linkInfo = CreateJunction(linkPath, pathToTarget);

// Junctions are always created with absolute targets, even if a relative path is passed.
string expectedTarget = Path.GetFullPath(pathToTarget);

Assert.True(linkInfo.Exists);
Assert.IsType<DirectoryInfo>(linkInfo);
Assert.True(linkInfo.Attributes.HasFlag(FileAttributes.Directory));
Assert.Equal(expectedTarget, linkInfo.LinkTarget);

FileSystemInfo? targetFromDirectoryInfo = linkInfo.ResolveLinkTarget(returnFinalTarget);
FileSystemInfo? targetFromDirectory = Directory.ResolveLinkTarget(linkPath, returnFinalTarget);

Assert.NotNull(targetFromDirectoryInfo);
Assert.NotNull(targetFromDirectory);

Assert.False(targetFromDirectoryInfo.Exists);
Assert.False(targetFromDirectory.Exists);


Assert.Equal(expectedTarget, targetFromDirectoryInfo.FullName);
Assert.Equal(expectedTarget, targetFromDirectory.FullName);
}

[Theory]
[MemberData(nameof(Junction_LinkTarget_PathToTarget_Data))]
public void Junction_LinkTarget_Succeeds(string pathToTarget)
{
FileSystemInfo linkInfo = CreateJunction(GetRandomLinkPath(), pathToTarget);
Assert.True(linkInfo.Exists);
Assert.Equal(Path.GetFullPath(pathToTarget), linkInfo.LinkTarget);
}
}
}
Loading