Skip to content

Commit

Permalink
Detect Unix paths on Unix and Dos&Unc paths on Windows (dotnet/corefx…
Browse files Browse the repository at this point in the history
…#15925)

* Uri: detect Unix path on Unix, detect Dos&Unc path on Windows

* Update System.IO.Packaging/tests

* Update System.Xml.XmlSchema.XmlSchemaValidatorApi.Tests

* Update Common.Tests

* Update System.Private.Uri.Functional.Tests

* Update System.Private.Uri.ExtendedFunctional.Tests

* Update System.Runtime/tests

* Uri.MethodsTests.cs: comment out failing inputs

* Restore uri minimal length of 3

* Require unix paths to start with a /

* Allow Unix path file uri to start with '\'

* FileWebRequestTest: revert extra slashes added (file:/// back to file://)

* Uri: fix empty newHost bug and revert workaround in IriTest

* Annotate PlatformSpecific tests

* IdentityHelperTests: use file path that works on Windows and Linux

* System.IO.Packaging/tests: rename s_LocalFile to s_fullPathToLocalFile

* Uri: update comments on minimal Uri length check, don't check length twice on Windows

* Use PlatformDetection.IsWindows

* Ensure Roslyn gets rid of UnixPath checks on Windows by adding IsWindowsSystem (const bool) checks

* Uri: Fix uapaot build by including Uri.Windows.cs

* IdentityHelperTests: rename GetNormalizedUrlHash_Unix -> GetNormalizedUrlHash

* Uri.MethodsTests: don't require GetHashCode to return different values when Uris only differ in case

* s_IsWindowsSystem -> s_isWindowsSystem

* Detect Dos and Unc paths on Unix

* Update tests

* Clean-up

* Clean-up II

* Clean-up III

* Clean-up IV

* Clean-up V

* Move Flags.UnixPath to the end of the Flags enum


Commit migrated from dotnet/corefx@7df6b74
  • Loading branch information
tmds authored and stephentoub committed Mar 4, 2017
1 parent b8aa4c0 commit 7129659
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 77 deletions.
Expand Up @@ -303,7 +303,6 @@ public void PathWithReservedDeviceNameAsPath_ReturnsFalse()
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // UNC paths
public void UncPathWithoutShareNameAsPath_ReturnsFalse()
{
Assert.All((IOInputs.GetUncPathsWithoutShareName()), (component) =>
Expand Down
1 change: 0 additions & 1 deletion src/libraries/System.IO.FileSystem/tests/File/Exists.cs
Expand Up @@ -203,7 +203,6 @@ public void PathWithReservedDeviceNameAsPath_ReturnsFalse()
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // UNC paths
public void UncPathWithoutShareNameAsPath_ReturnsFalse()
{
Assert.All((IOInputs.GetUncPathsWithoutShareName()), (component) =>
Expand Down
Expand Up @@ -84,6 +84,10 @@ public static IEnumerable<string> GetUncPathsWithoutShareName()
{
foreach (char slash in new[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar })
{
if (!PlatformDetection.IsWindows && slash == '/') // Unc paths must start with '\' on Unix
{
continue;
}
string slashes = new string(slash, 2);
yield return slashes;
yield return slashes + " ";
Expand Down
Expand Up @@ -58,6 +58,7 @@
<Compile Include="$(CommonPath)\System\Diagnostics\Debug.Windows.cs">
<Link>Common\System\Diagnostics\Debug.Windows.cs</Link>
</Compile>
<Compile Include="System\Uri.Windows.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true' and ('$(TargetGroup)'=='netcoreapp' OR '$(TargetGroup)'=='uap')">
<Compile Include="$(CommonPath)\System\Diagnostics\Debug.Unix.cs">
Expand Down Expand Up @@ -102,12 +103,14 @@
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeFileHandleHelper.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeFileHandleHelper.Unix.cs</Link>
</Compile>
<Compile Include="System\Uri.Unix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)'=='uapaot' or '$(TargetGroup)' == 'netcoreapp1.2corert'">
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
<Compile Include="System\Uri.Windows.cs" />
</ItemGroup>
<ItemGroup>
<ReferenceFromRuntime Include="System.Private.CoreLib" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>
7 changes: 7 additions & 0 deletions src/libraries/System.Private.Uri/src/System/Uri.Unix.cs
@@ -0,0 +1,7 @@
namespace System
{
public partial class Uri
{
private const bool IsWindowsSystem = false;
}
}
7 changes: 7 additions & 0 deletions src/libraries/System.Private.Uri/src/System/Uri.Windows.cs
@@ -0,0 +1,7 @@
namespace System
{
public partial class Uri
{
private const bool IsWindowsSystem = true;
}
}
41 changes: 34 additions & 7 deletions src/libraries/System.Private.Uri/src/System/Uri.cs
Expand Up @@ -111,6 +111,7 @@ private enum Flags : ulong
QueryIriCanonical = 0x20000000000,
FragmentIriCanonical = 0x40000000000,
IriCanonical = 0x78000000000,
UnixPath = 0x100000000000,
}

private Flags _flags;
Expand Down Expand Up @@ -170,6 +171,11 @@ private bool IsUncPath
get { return (_flags & Flags.UncPath) != 0; }
}

private bool IsUnixPath
{
get { return (_flags & Flags.UnixPath) != 0; }
}

private Flags HostType
{
get { return _flags & Flags.HostTypeMask; }
Expand Down Expand Up @@ -2028,6 +2034,14 @@ private unsafe ParsingError PrivateParseMinimal()
++length;
}

// Unix Path
if (!IsWindowsSystem && InFact(Flags.UnixPath))
{
_flags |= Flags.BasicHostType;
_flags |= (Flags)idx;
return ParsingError.None;
}

// Old Uri parser tries to figure out on a DosPath in all cases.
// Hence http://c:/ is treated as DosPath without the host while it should be a host "c", port 80
//
Expand Down Expand Up @@ -2086,6 +2100,7 @@ private unsafe ParsingError PrivateParseMinimal()
}
}
}
// UNC share?
else if (_syntax.InFact(UriSyntaxFlags.FileLikeUri) && (i - idx >= 2 && i - idx != 3 &&
i < length && pUriString[i] != '?' && pUriString[i] != '#'))
{
Expand Down Expand Up @@ -3618,6 +3633,14 @@ private unsafe void ParseRemaining()
++idx;
}

// Unix: Unix path?
if (!IsWindowsSystem && idx < length && uriString[idx] == '/')
{
flags |= (Flags.UnixPath | Flags.ImplicitFile | Flags.AuthorityFound);
syntax = UriParser.UnixFileUri;
return idx;
}

// sets the recognizer for well known registered schemes
// file, ftp, http, https, uuid, etc
// Note that we don't support one-letter schemes that will be put into a DOS path bucket
Expand Down Expand Up @@ -3673,7 +3696,7 @@ private unsafe void ParseRemaining()
}
else if ((c = uriString[idx]) == '/' || c == '\\')
{
//UNC share ?
//UNC share?
if ((c = uriString[idx + 1]) == '\\' || c == '/')
{
flags |= (Flags.UncPath | Flags.ImplicitFile | Flags.AuthorityFound);
Expand Down Expand Up @@ -4000,12 +4023,6 @@ private static unsafe ParsingError CheckSchemeSyntax(char* ptr, ushort length, r
bool hostNotUnicodeNormalized = ((flags & Flags.HostUnicodeNormalized) == 0); // perf
UriSyntaxFlags syntaxFlags = syntax.Flags;

// need to build new Iri'zed string
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
newHost = _originalUnicodeString.Substring(0, startInput);
}

//Special case is an empty authority
if (idx == length || ((ch = pString[idx]) == '/' || (ch == '\\' && StaticIsFile(syntax)) || ch == '#' || ch == '?'))
{
Expand All @@ -4028,6 +4045,12 @@ private static unsafe ParsingError CheckSchemeSyntax(char* ptr, ushort length, r
return idx;
}

// need to build new Iri'zed string
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
newHost = _originalUnicodeString.Substring(0, startInput);
}

string userInfoString = null;
// Attempt to parse user info first

Expand Down Expand Up @@ -5242,6 +5265,10 @@ private static string CombineUri(Uri basePart, string relativePart, UriFormat ur
path = Compress(path, 3, ref length, basePart.Syntax);
return new string(path, 1, length - 1) + extra;
}
else if (!IsWindowsSystem && basePart.IsUnixPath)
{
left = basePart.GetParts(UriComponents.Host, UriFormat.Unescaped);
}
else
{
left = @"\\" + basePart.GetParts(UriComponents.Host, UriFormat.Unescaped);
Expand Down
5 changes: 3 additions & 2 deletions src/libraries/System.Private.Uri/src/System/UriExt.cs
Expand Up @@ -45,10 +45,11 @@ private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatExcep
// V1 compat
// A relative Uri wins over implicit UNC path unless the UNC path is of the form "\\something" and
// uriKind != Absolute
// A relative Uri wins over implicit Unix path unless uriKind == Absolute
if (NotAny(Flags.DosPath) &&
uriKind != UriKind.Absolute &&
(uriKind == UriKind.Relative || (_string.Length >= 2 && (_string[0] != '\\' || _string[1] != '\\'))))

((uriKind == UriKind.Relative || (_string.Length >= 2 && (_string[0] != '\\' || _string[1] != '\\')))
|| (!IsWindowsSystem && InFact(Flags.UnixPath))))
{
_syntax = null; //make it be relative Uri
_flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/System.Private.Uri/src/System/UriSyntax.cs
Expand Up @@ -87,6 +87,7 @@ public abstract partial class UriParser
internal static UriParser WssUri;
internal static UriParser FtpUri;
internal static UriParser FileUri;
internal static UriParser UnixFileUri;
internal static UriParser GopherUri;
internal static UriParser NntpUri;
internal static UriParser NewsUri;
Expand Down Expand Up @@ -125,6 +126,7 @@ static UriParser()
s_table[FtpUri.SchemeName] = FtpUri; //FTP

FileUri = new BuiltInUriParser("file", NoDefaultPort, s_fileSyntaxFlags);
UnixFileUri = new BuiltInUriParser("file", NoDefaultPort, s_unixFileSyntaxFlags);
s_table[FileUri.SchemeName] = FileUri; //FILE

GopherUri = new BuiltInUriParser("gopher", 70, GopherSyntaxFlags);
Expand Down Expand Up @@ -470,6 +472,8 @@ internal bool InternalIsWellFormedOriginalString(Uri thisUri)
UriSyntaxFlags.AllowIdn |
UriSyntaxFlags.AllowIriParsing;

private static readonly UriSyntaxFlags s_unixFileSyntaxFlags =
s_fileSyntaxFlags & ~UriSyntaxFlags.ConvertPathSlashes;

private const UriSyntaxFlags VsmacrosSyntaxFlags =
UriSyntaxFlags.MustHaveAuthority |
Expand Down
Expand Up @@ -17,11 +17,13 @@ namespace System.PrivateUri.Tests
/// </summary>
public class IriRelativeFileResolutionTest
{
private static readonly bool s_isWindowsSystem = PlatformDetection.IsWindows;

[Fact]
public void IriRelativeResolution_CompareImplcitAndExplicitFileWithNoUnicode_AllPropertiesTheSame()
{
string nonUnicodeImplicitTestFile = @"c:\path\path3\test.txt";
string nonUnicodeImplicitFileBase = @"c:\path\file.txt";
string nonUnicodeImplicitTestFile = s_isWindowsSystem ? @"c:\path\path3\test.txt" : "/path/path3/test.txt";
string nonUnicodeImplicitFileBase = s_isWindowsSystem ? @"c:\path\file.txt" : "/path/file.txt";

string testResults;
int errorCount = RelatavizeRestoreCompareImplicitVsExplicitFiles(nonUnicodeImplicitTestFile,
Expand All @@ -32,8 +34,8 @@ public void IriRelativeResolution_CompareImplcitAndExplicitFileWithNoUnicode_All
[Fact]
public void IriRelativeResolution_CompareImplcitAndExplicitFileWithReservedChar_AllPropertiesTheSame()
{
string nonUnicodeImplicitTestFile = @"c:\path\path3\test.txt%25%";
string nonUnicodeImplicitFileBase = @"c:\path\file.txt";
string nonUnicodeImplicitTestFile = s_isWindowsSystem ? @"c:\path\path3\test.txt%25%" : "/path/path3/test.txt%25%";
string nonUnicodeImplicitFileBase = s_isWindowsSystem ? @"c:\path\file.txt" : "/path/file.txt";

string testResults;
int errorCount = RelatavizeRestoreCompareImplicitVsExplicitFiles(nonUnicodeImplicitTestFile,
Expand All @@ -45,8 +47,8 @@ public void IriRelativeResolution_CompareImplcitAndExplicitFileWithReservedChar_
[Fact]
public void IriRelativeResolution_CompareImplcitAndExplicitFileWithUnicodeIriOn_AllPropertiesTheSame()
{
string unicodeImplicitTestFile = @"c:\path\\u30AF\path3\\u30EB\u30DE.text";
string nonUnicodeImplicitFileBase = @"c:\path\file.txt";
string unicodeImplicitTestFile = s_isWindowsSystem ? @"c:\path\\u30AF\path3\\u30EB\u30DE.text" : "/path//u30AF/path3//u30EB/u30DE.text";
string nonUnicodeImplicitFileBase = s_isWindowsSystem ? @"c:\path\file.txt" : "/path/file.txt";

string testResults;
int errorCount = RelatavizeRestoreCompareImplicitVsExplicitFiles(unicodeImplicitTestFile,
Expand All @@ -57,13 +59,13 @@ public void IriRelativeResolution_CompareImplcitAndExplicitFileWithUnicodeIriOn_
[Fact]
public void IriRelativeResolution_CompareImplcitAndExplicitFileWithUnicodeAndReservedCharIriOn_AllPropertiesTheSame()
{
string unicodeImplicitTestFile = @"c:\path\\u30AF\path3\\u30EB\u30DE.text%25%";
string nonUnicodeImplicitFileBase = @"c:\path\file.txt";
string unicodeImplicitTestFile = s_isWindowsSystem ? @"c:\path\\u30AF\path3\\u30EB\u30DE.text%25%" : "/path//u30AF/path3//u30EB/u30DE.text%25%";
string nonUnicodeImplicitFileBase = s_isWindowsSystem ? @"c:\path\file.txt" : "/path/file.txt";

string testResults;
int errorCount = RelatavizeRestoreCompareImplicitVsExplicitFiles(unicodeImplicitTestFile,
nonUnicodeImplicitFileBase, out testResults);
Assert.True((errorCount == 4), testResults);
Assert.True((errorCount == (s_isWindowsSystem ? 4 : 0)), testResults);
// AbsolutePath, AbsoluteUri, LocalPath, PathAndQuery
}

Expand All @@ -81,6 +83,7 @@ public void IriRelativeResolution_CompareImplcitAndExplicitUncWithNoUnicode_AllP
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Unc paths must start with '\' on Unix
public void IriRelativeResolution_CompareImplcitAndExplicitUncForwardSlashesWithNoUnicode_AllPropertiesTheSame()
{
string nonUnicodeImplicitTestUnc = @"//c/path/path3/test.txt";
Expand Down Expand Up @@ -109,9 +112,10 @@ public void IriRelativeResolution_CompareImplcitAndExplicitUncWithUnicodeIriOn_A
public static int RelatavizeRestoreCompareImplicitVsExplicitFiles(string original,
string baseString, out string errors)
{
string fileSchemePrefix = s_isWindowsSystem ? "file:///" : "file://";
Uri implicitTestUri = new Uri(original);
Uri implicitBaseUri = new Uri(baseString);
Uri explicitBaseUri = new Uri("file:///" + baseString);
Uri explicitBaseUri = new Uri(fileSchemePrefix + baseString);

Uri rel = implicitBaseUri.MakeRelativeUri(implicitTestUri);
Uri implicitResultUri = new Uri(implicitBaseUri, rel);
Expand All @@ -125,7 +129,7 @@ public void IriRelativeResolution_CompareImplcitAndExplicitUncWithUnicodeIriOn_A
{
string implicitValue = info.GetValue(implicitResultUri, null).ToString();
string explicitValue = info.GetValue(explicitResultUri, null).ToString();
if (!(implicitValue.Equals(explicitValue) || ("file:///" + implicitValue).Equals(explicitValue)))
if (!(implicitValue.Equals(explicitValue) || (fileSchemePrefix + implicitValue).Equals(explicitValue)))
{
errorCount++;
testResults.Append("Property mismatch: " + info.Name + ", implicit value: " + implicitValue
Expand All @@ -149,14 +153,17 @@ public void IriRelativeResolution_CompareImplcitAndExplicitUncWithUnicodeIriOn_A
[Fact]
public void IriRelativeResolution_CompareImplcitAndOriginalFileWithNoUnicode_AllPropertiesTheSame()
{
string nonUnicodeImplicitTestFile = @"c:\path\path3\test.txt";
string nonUnicodeImplicitFileBase = @"c:\path\file.txt";
string nonUnicodeImplicitTestFile = s_isWindowsSystem ? @"c:\path\path3\test.txt" : "/path/path3/test.txt";
string nonUnicodeImplicitFileBase = s_isWindowsSystem ? @"c:\path\file.txt" : "/path/file.txt";

string testResults;
int errorCount = RelatavizeRestoreCompareVsOriginal(nonUnicodeImplicitTestFile,
nonUnicodeImplicitFileBase, out testResults);
Assert.True((errorCount == 1), testResults);
Assert.True(IsOriginalString(testResults), testResults);
Assert.True((errorCount == (s_isWindowsSystem ? 1 : 0)), testResults);
if (s_isWindowsSystem)
{
Assert.True(IsOriginalString(testResults), testResults);
}
}

[Fact]
Expand All @@ -173,6 +180,7 @@ public void IriRelativeResolution_CompareUncAndOriginalFileWithNoUnicode_AllProp
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Unc paths must start with '\' on Unix
public void IriRelativeResolution_CompareUncForwardSlashesAndOriginalFileWithNoUnicode_AllPropertiesTheSame()
{
string nonUnicodeUncTestFile = @"//c/path/path3/test.txt";
Expand Down
Expand Up @@ -30,6 +30,9 @@
<Compile Include="$(CommonTestPath)\System\ThreadCultureChange.cs">
<Link>Common\System\ThreadCultureChange.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)\System\PlatformDetection.cs">
<Link>Common\System\PlatformDetection.cs</Link>
</Compile>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
Expand Up @@ -478,6 +478,19 @@ public void UriFile_ImplicitDosFile_QueryNotAllowed()
Assert.Equal(String.Empty, testUri.Fragment);
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Unix path
public void UriFile_ImplicitUnixFile_QueryNotAllowed()
{
string input = "/path/path?query";
Uri testUri = new Uri(input);
Assert.Equal("file:///path/path%3Fquery", testUri.AbsoluteUri);
Assert.Equal("/path/path%3Fquery", testUri.AbsolutePath);
Assert.Equal("/path/path?query", testUri.LocalPath);
Assert.Equal(String.Empty, testUri.Query);
Assert.Equal(String.Empty, testUri.Fragment);
}

[Fact]
public void UriFile_ImplicitUncFile_QueryNotAllowed()
{
Expand All @@ -502,6 +515,19 @@ public void UriFile_ImplicitDosFile_FragmentNotAllowed()
Assert.Equal(String.Empty, testUri.Fragment);
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Unix path
public void UriFile_ImplicitUnixFile_FragmentNotAllowed()
{
string input = "/path/path#fragment#";
Uri testUri = new Uri(input);
Assert.Equal("file:///path/path%23fragment%23", testUri.AbsoluteUri);
Assert.Equal("/path/path%23fragment%23", testUri.AbsolutePath);
Assert.Equal("/path/path#fragment#", testUri.LocalPath);
Assert.Equal(String.Empty, testUri.Query);
Assert.Equal(String.Empty, testUri.Fragment);
}

[Fact]
public void UriFile_ImplicitUncFile_FragmentNotAllowed()
{
Expand Down

0 comments on commit 7129659

Please sign in to comment.