Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexis Christoforides <alexis@thenull.net>2019-08-01 07:38:43 +0300
committerGitHub <noreply@github.com>2019-08-01 07:38:43 +0300
commit8e3d1356d179c219f134c9b2c075f21ab77e0b86 (patch)
treec9e2cc5fd64a693875027732479a8e8e8bda8d58
parent97a0b3fddb29164e7b1a735b54d3c0d5d59f9915 (diff)
parent7715668a23e07252639622f3e7a7b2002a2d47ff (diff)
Merge pull request #314 from mono/fix-gh-14971
Allow copying/moving/replacing broken symlinks
-rw-r--r--src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Symlink.cs16
-rw-r--r--src/Native/Unix/System.Native/pal_io.c5
-rw-r--r--src/Native/Unix/System.Native/pal_io.h7
-rw-r--r--src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs32
-rw-r--r--src/System.IO.FileSystem/tests/File/Copy.cs17
-rw-r--r--src/System.IO.FileSystem/tests/File/Move.cs17
6 files changed, 94 insertions, 0 deletions
diff --git a/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Symlink.cs b/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Symlink.cs
new file mode 100644
index 0000000000..68bebb152c
--- /dev/null
+++ b/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.Symlink.cs
@@ -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);
+ }
+}
diff --git a/src/Native/Unix/System.Native/pal_io.c b/src/Native/Unix/System.Native/pal_io.c
index d68eb4b583..f2011d61c4 100644
--- a/src/Native/Unix/System.Native/pal_io.c
+++ b/src/Native/Unix/System.Native/pal_io.c
@@ -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);
+}
diff --git a/src/Native/Unix/System.Native/pal_io.h b/src/Native/Unix/System.Native/pal_io.h
index 9f80c8b00c..aab12e6ed3 100644
--- a/src/Native/Unix/System.Native/pal_io.h
+++ b/src/Native/Unix/System.Native/pal_io.h
@@ -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
diff --git a/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs b/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs
index daddcedb3d..283c85fc93 100644
--- a/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs
+++ b/src/System.IO.FileSystem/src/System/IO/FileSystem.Unix.cs
@@ -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 is 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;
diff --git a/src/System.IO.FileSystem/tests/File/Copy.cs b/src/System.IO.FileSystem/tests/File/Copy.cs
index 81338ceb91..2cf108e8db 100644
--- a/src/System.IO.FileSystem/tests/File/Copy.cs
+++ b/src/System.IO.FileSystem/tests/File/Copy.cs
@@ -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()
diff --git a/src/System.IO.FileSystem/tests/File/Move.cs b/src/System.IO.FileSystem/tests/File/Move.cs
index e9da920edd..6548b4f564 100644
--- a/src/System.IO.FileSystem/tests/File/Move.cs
+++ b/src/System.IO.FileSystem/tests/File/Move.cs
@@ -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()
{