Imported Upstream version 6.4.0.150

Former-commit-id: 2cf3acd45014a53dda66c13f7378a88695d3c93e
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-08-02 08:37:10 +00:00
parent 7eed0321b0
commit 345224e2bc
67 changed files with 352 additions and 91 deletions

View File

@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Symlink", SetLastError = true)]
internal static extern int Symlink(string target, string linkPath);
}
}

View File

@@ -637,6 +637,7 @@ namespace System
}
catch (ArgumentException) { }
catch (InvalidTimeZoneException) { }
try
{
return new TimeZoneInfo(rawData, id, dstDisabled: true); // create a TimeZoneInfo instance from the TZif data w/o DST support
@@ -644,7 +645,6 @@ namespace System
catch (ArgumentException) { }
catch (InvalidTimeZoneException) { }
}
return null;
}
@@ -912,7 +912,7 @@ namespace System
index++;
}
if (index == 0)
if (rulesList.Count == 0 && index < dts.Length)
{
TZifType transitionType = TZif_GetEarlyDateTransitionType(transitionTypes);
DateTime endTransitionDate = dts[index];
@@ -929,6 +929,12 @@ namespace System
default(TransitionTime),
baseUtcDelta,
noDaylightTransitions: true);
if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r))
{
NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r);
}
rulesList.Add(r);
}
else if (index < dts.Length)
@@ -966,6 +972,12 @@ namespace System
default(TransitionTime),
baseUtcDelta,
noDaylightTransitions: true);
if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r))
{
NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r);
}
rulesList.Add(r);
}
else
@@ -978,8 +990,14 @@ namespace System
if (!string.IsNullOrEmpty(futureTransitionsPosixFormat))
{
AdjustmentRule r = TZif_CreateAdjustmentRuleForPosixFormat(futureTransitionsPosixFormat, startTransitionDate, timeZoneBaseUtcOffset);
if (r != null)
{
if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r))
{
NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r);
}
rulesList.Add(r);
}
}
@@ -1000,6 +1018,12 @@ namespace System
default(TransitionTime),
baseUtcDelta,
noDaylightTransitions: true);
if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r))
{
NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r);
}
rulesList.Add(r);
}
}

View File

@@ -1921,12 +1921,6 @@ namespace System
return result;
}
/// <summary>
/// Helper function that validates the TimeSpan is within +/- 14.0 hours
/// </summary>
internal static bool UtcOffsetOutOfRange(TimeSpan offset) =>
offset.TotalHours < -14.0 || offset.TotalHours > 14.0;
/// <summary>
/// Helper function that performs all of the validation checks for the
/// factory methods and deserialization callback.
@@ -1976,11 +1970,7 @@ namespace System
throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls);
}
// FUTURE: check to see if this rule supports Daylight Saving Time
// adjustmentRulesSupportDst = adjustmentRulesSupportDst || current.SupportsDaylightSavingTime;
// FUTURE: test baseUtcOffset + current.StandardDelta
if (UtcOffsetOutOfRange(baseUtcOffset + current.DaylightDelta))
if (!IsValidAdjustmentRuleOffest(baseUtcOffset, current))
{
throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta);
}
@@ -1993,5 +1983,82 @@ namespace System
}
}
}
private static readonly TimeSpan MaxOffset = TimeSpan.FromHours(14.0);
private static readonly TimeSpan MinOffset = -MaxOffset;
/// <summary>
/// Helper function that validates the TimeSpan is within +/- 14.0 hours
/// </summary>
internal static bool UtcOffsetOutOfRange(TimeSpan offset) =>
offset < MinOffset || offset > MaxOffset;
private static TimeSpan GetUtcOffset(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule)
{
return baseUtcOffset
+ adjustmentRule.BaseUtcOffsetDelta
+ (adjustmentRule.HasDaylightSaving ? adjustmentRule.DaylightDelta : TimeSpan.Zero);
}
/// <summary>
/// Helper function that performs adjustment rule validation
/// </summary>
private static bool IsValidAdjustmentRuleOffest(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule)
{
TimeSpan utcOffset = GetUtcOffset(baseUtcOffset, adjustmentRule);
return !UtcOffsetOutOfRange(utcOffset);
}
/// <summary>
/// Normalize adjustment rule offset so that it is within valid range
/// This method should not be called at all but is here in case something changes in the future
/// or if really old time zones are present on the OS (no combination is known at the moment)
/// </summary>
private static void NormalizeAdjustmentRuleOffset(TimeSpan baseUtcOffset, ref AdjustmentRule adjustmentRule)
{
// Certain time zones such as:
// Time Zone start date end date offset
// -----------------------------------------------------
// America/Yakutat 0001-01-01 1867-10-18 14:41:00
// America/Yakutat 1867-10-18 1900-08-20 14:41:00
// America/Sitka 0001-01-01 1867-10-18 14:58:00
// America/Sitka 1867-10-18 1900-08-20 14:58:00
// Asia/Manila 0001-01-01 1844-12-31 -15:56:00
// Pacific/Guam 0001-01-01 1845-01-01 -14:21:00
// Pacific/Saipan 0001-01-01 1845-01-01 -14:21:00
//
// have larger offset than currently supported by framework.
// If for whatever reason we find that time zone exceeding max
// offset of 14h this function will truncate it to the max valid offset.
// Updating max offset may cause problems with interacting with SQL server
// which uses SQL DATETIMEOFFSET field type which was originally designed to be
// bit-for-bit compatible with DateTimeOffset.
TimeSpan utcOffset = GetUtcOffset(baseUtcOffset, adjustmentRule);
// utc base offset delta increment
TimeSpan adjustment = TimeSpan.Zero;
if (utcOffset > MaxOffset)
{
adjustment = MaxOffset - utcOffset;
}
else if (utcOffset < MinOffset)
{
adjustment = MinOffset - utcOffset;
}
if (adjustment != TimeSpan.Zero)
{
adjustmentRule = AdjustmentRule.CreateAdjustmentRule(
adjustmentRule.DateStart,
adjustmentRule.DateEnd,
adjustmentRule.DaylightDelta,
adjustmentRule.DaylightTransitionStart,
adjustmentRule.DaylightTransitionEnd,
adjustmentRule.BaseUtcOffsetDelta + adjustment,
adjustmentRule.NoDaylightTransitions);
}
}
}
}

View File

@@ -1451,3 +1451,8 @@ int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length,
while ((ret = fcntl (ToFileDescriptor(fd), F_SETLK, &lockArgs)) < 0 && errno == EINTR);
return ret;
}
int32_t SystemNative_Symlink(const char* target, const char* linkPath)
{
return symlink(target, linkPath);
}

View File

@@ -750,4 +750,11 @@ DLLEXPORT int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid);
*/
DLLEXPORT int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType);
/**
* Creates a symbolic link at "linkPath", pointing at "target".
* "target" may or may not exist (dangling symbolic links are valid filesystem objects)
* Returns 0 on success; otherwise, returns -1 and errno is set.
*/
DLLEXPORT int32_t SystemNative_Symlink(const char* target, const char* linkPath);
END_EXTERN_C

View File

@@ -12,6 +12,32 @@ namespace System.IO
{
internal const int DefaultBufferSize = 4096;
private static bool CopyDanglingSymlink(string sourceFullPath, string destFullPath)
{
// Check if the source is a dangling symlink. In those cases, we just want to copy the link
Interop.Sys.FileStatus ignored;
if (! (Interop.Sys.Stat(sourceFullPath, out ignored) < 0 &&
Interop.Sys.LStat(sourceFullPath, out ignored) == 0))
{
return false;
}
Interop.ErrorInfo errorInfo;
// get the target of the symlink
string linkTarget = Interop.Sys.ReadLink(sourceFullPath);
if (linkTarget == null)
{
errorInfo = Interop.Sys.GetLastErrorInfo();
throw Interop.GetExceptionForIoErrno(errorInfo, sourceFullPath);
}
if (Interop.Sys.Symlink(linkTarget, destFullPath) == 0)
return true;
errorInfo = Interop.Sys.GetLastErrorInfo();
throw Interop.GetExceptionForIoErrno(errorInfo, destFullPath);
}
public static void CopyFile(string sourceFullPath, string destFullPath, bool overwrite)
{
// The destination path may just be a directory into which the file should be copied.
@@ -21,6 +47,9 @@ namespace System.IO
destFullPath = Path.Combine(destFullPath, Path.GetFileName(sourceFullPath));
}
if (CopyDanglingSymlink(sourceFullPath, destFullPath))
return;
// Copy the contents of the file from the source to the destination, creating the destination in the process
using (var src = new FileStream(sourceFullPath, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.None))
using (var dst = new FileStream(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, DefaultBufferSize, FileOptions.None))
@@ -31,6 +60,9 @@ namespace System.IO
private static void LinkOrCopyFile (string sourceFullPath, string destFullPath)
{
if (CopyDanglingSymlink(sourceFullPath, destFullPath))
return;
if (Interop.Sys.Link(sourceFullPath, destFullPath) >= 0)
return;

View File

@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;
using System.Runtime.InteropServices;
namespace System.IO.Tests
{
@@ -48,6 +49,22 @@ namespace System.IO.Tests
Assert.Throws<IOException>(() => Copy(testFile, testFile));
}
[DllImport("libc", SetLastError = true)]
private static extern int symlink(string target, string linkpath);
[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void DanglingSymlinkCopy()
{
string dangling_symlink = GetTestFileName();
string missing_target = GetTestFileName();
string dangling_symlink_new_location = GetTestFileName();
Assert.False(File.Exists(missing_target));
Assert.Equal(symlink(missing_target, dangling_symlink), 0);
Copy(dangling_symlink, dangling_symlink_new_location);
Assert.True(File.Exists(dangling_symlink_new_location)); // File.Exists returns true for dangling symlinks
}
[Fact]
[SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "CoreFX FileStream not yet imported")]
public void NonExistentPath()

View File

@@ -4,6 +4,7 @@
using Xunit;
using System.Linq;
using System.Runtime.InteropServices;
namespace System.IO.Tests
{
@@ -174,6 +175,22 @@ namespace System.IO.Tests
Assert.False(File.Exists(testFileSource.FullName));
}
[DllImport("libc", SetLastError = true)]
private static extern int symlink(string target, string linkpath);
[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void DanglingSymlinkMove()
{
string dangling_symlink = GetTestFileName();
string missing_target = GetTestFileName();
string dangling_symlink_new_location = GetTestFileName();
Assert.False(File.Exists(missing_target));
Assert.Equal(symlink(missing_target, dangling_symlink), 0);
Move(dangling_symlink, dangling_symlink_new_location);
Assert.True(File.Exists(dangling_symlink_new_location)); // File.Exists returns true for dangling symlinks
}
[Fact]
public void FileNameWithSignificantWhitespace()
{