From d3ed5a9ede4ba138fa352c1496743fba1dacd912 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 31 May 2021 18:07:31 +0200 Subject: [PATCH] Socket: delete unix local endpoint filename on Close (#52103) Fixes #45537 --- .../NamedPipeTest.UnixDomainSockets.cs | 3 +- .../src/System/Net/Sockets/Socket.cs | 90 +++--- .../Net/Sockets/UnixDomainSocketEndPoint.cs | 28 ++ .../FunctionalTests/UnixDomainSocketTest.cs | 288 +++++++++--------- 4 files changed, 226 insertions(+), 183 deletions(-) diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs index 7bab6b302d4de..ff7fa1509fe02 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs @@ -49,8 +49,7 @@ public async Task NamedPipeClient_Connects_With_UnixDomainSocketEndPointServer() } } - Assert.True(File.Exists(pipeName)); - try { File.Delete(pipeName); } catch { } + Assert.False(File.Exists(pipeName)); } } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index 2bf4645591cfb..30b5c05c4979b 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -25,8 +25,11 @@ public partial class Socket : IDisposable private SafeSocketHandle _handle; - // _rightEndPoint is null if the socket has not been bound. Otherwise, it is any EndPoint of the - // correct type (IPEndPoint, etc). + // _rightEndPoint is null if the socket has not been bound. Otherwise, it is an EndPoint of the + // correct type (IPEndPoint, etc). The Bind operation sets _rightEndPoint. Other operations must only set + // it when the value is still null. + // This enables tracking the file created by UnixDomainSocketEndPoint when the Socket is bound, + // and to delete that file when the Socket gets disposed. internal EndPoint? _rightEndPoint; internal EndPoint? _remoteEndPoint; @@ -284,7 +287,7 @@ public int Available { // Update the state if we've become connected after a non-blocking connect. _isConnected = true; - _rightEndPoint = _nonBlockingConnectRightEndPoint; + _rightEndPoint ??= _nonBlockingConnectRightEndPoint; UpdateLocalEndPointOnConnect(); _nonBlockingConnectInProgress = false; } @@ -331,7 +334,7 @@ public int Available { // Update the state if we've become connected after a non-blocking connect. _isConnected = true; - _rightEndPoint = _nonBlockingConnectRightEndPoint; + _rightEndPoint ??= _nonBlockingConnectRightEndPoint; UpdateLocalEndPointOnConnect(); _nonBlockingConnectInProgress = false; } @@ -438,7 +441,7 @@ public bool Connected { // Update the state if we've become connected after a non-blocking connect. _isConnected = true; - _rightEndPoint = _nonBlockingConnectRightEndPoint; + _rightEndPoint ??= _nonBlockingConnectRightEndPoint; UpdateLocalEndPointOnConnect(); _nonBlockingConnectInProgress = false; } @@ -799,11 +802,11 @@ private void DoBind(EndPoint endPointSnapshot, Internals.SocketAddress socketAdd UpdateStatusAfterSocketErrorAndThrowException(errorCode); } - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = endPointSnapshot; - } + // Save a copy of the EndPoint so we can use it for Create(). + // For UnixDomainSocketEndPoint, track the file to delete on Dispose. + _rightEndPoint = endPointSnapshot is UnixDomainSocketEndPoint unixEndPoint ? + unixEndPoint.CreateBoundEndPoint() : + endPointSnapshot; } // Establishes a connection to a remote system. @@ -1357,11 +1360,8 @@ public int SendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, if (SocketType == SocketType.Dgram) SocketsTelemetry.Log.DatagramSent(); } - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = remoteEP; - } + // Save a copy of the EndPoint so we can use it for Create(). + _rightEndPoint ??= remoteEP; if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer, offset, size); return bytesTransferred; @@ -1639,11 +1639,8 @@ public int ReceiveMessageFrom(byte[] buffer, int offset, int size, ref SocketFla catch { } - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = endPointSnapshot; - } + // Save a copy of the EndPoint so we can use it for Create(). + _rightEndPoint ??= endPointSnapshot; } if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, errorCode); @@ -1733,11 +1730,8 @@ public int ReceiveMessageFrom(Span buffer, ref SocketFlags socketFlags, re catch { } - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = endPointSnapshot; - } + // Save a copy of the EndPoint so we can use it for Create(). + _rightEndPoint ??= endPointSnapshot; } if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, errorCode); @@ -1796,11 +1790,8 @@ public int ReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFl catch { } - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = endPointSnapshot; - } + // Save a copy of the EndPoint so we can use it for Create(). + _rightEndPoint ??= endPointSnapshot; } if (socketException != null) @@ -3121,10 +3112,7 @@ private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationT e.StartOperationCommon(this, SocketAsyncOperation.SendTo); EndPoint? oldEndPoint = _rightEndPoint; - if (_rightEndPoint == null) - { - _rightEndPoint = endPointSnapshot; - } + _rightEndPoint ??= endPointSnapshot; SocketError socketError; try @@ -3133,7 +3121,7 @@ private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationT } catch { - _rightEndPoint = null; + _rightEndPoint = oldEndPoint; _localEndPoint = null; // Clear in-use flag on event args object. e.Complete(); @@ -3229,11 +3217,8 @@ private void DoConnect(EndPoint endPointSnapshot, Internals.SocketAddress socket if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterConnect(SocketError.Success); - if (_rightEndPoint == null) - { - // Save a copy of the EndPoint so we can use it for Create(). - _rightEndPoint = endPointSnapshot; - } + // Save a copy of the EndPoint so we can use it for Create(). + _rightEndPoint ??= endPointSnapshot; if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"connection to:{endPointSnapshot}"); @@ -3361,6 +3346,18 @@ protected virtual void Dispose(bool disposing) { } } + + // Delete file of bound UnixDomainSocketEndPoint. + if (_rightEndPoint is UnixDomainSocketEndPoint unixEndPoint && + unixEndPoint.BoundFileName is not null) + { + try + { + File.Delete(unixEndPoint.BoundFileName); + } + catch + { } + } } // Clean up any cached data @@ -3615,9 +3612,20 @@ internal Socket UpdateAcceptSocket(Socket socket, EndPoint remoteEP) socket._addressFamily = _addressFamily; socket._socketType = _socketType; socket._protocolType = _protocolType; - socket._rightEndPoint = _rightEndPoint; socket._remoteEndPoint = remoteEP; + // If the _rightEndpoint tracks a UnixDomainSocketEndPoint to delete + // then create a new EndPoint. + if (_rightEndPoint is UnixDomainSocketEndPoint unixEndPoint && + unixEndPoint.BoundFileName is not null) + { + socket._rightEndPoint = unixEndPoint.CreateUnboundEndPoint(); + } + else + { + socket._rightEndPoint = _rightEndPoint; + } + // If the listener socket was bound to a wildcard address, then the `accept` system call // will assign a specific address to the accept socket's local endpoint instead of a // wildcard address. In that case we should not copy listener's wildcard local endpoint. diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs index 8ce031120f049..f400873433c6e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Text; +using System.IO; namespace System.Net.Sockets { @@ -14,13 +15,22 @@ public sealed partial class UnixDomainSocketEndPoint : EndPoint private readonly string _path; private readonly byte[] _encodedPath; + // Tracks the file Socket should delete on Dispose. + internal string? BoundFileName { get; } + public UnixDomainSocketEndPoint(string path) + : this(path, null) + { } + + private UnixDomainSocketEndPoint(string path, string? boundFileName) { if (path == null) { throw new ArgumentNullException(nameof(path)); } + BoundFileName = boundFileName; + // Pathname socket addresses should be null-terminated. // Linux abstract socket addresses start with a zero byte, they must not be null-terminated. bool isAbstract = IsAbstract(path); @@ -120,6 +130,24 @@ public override string ToString() } } + internal UnixDomainSocketEndPoint CreateBoundEndPoint() + { + if (IsAbstract(_path)) + { + return this; + } + return new UnixDomainSocketEndPoint(_path, Path.GetFullPath(_path)); + } + + internal UnixDomainSocketEndPoint CreateUnboundEndPoint() + { + if (IsAbstract(_path) || BoundFileName is null) + { + return this; + } + return new UnixDomainSocketEndPoint(_path, null); + } + private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0'; private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index da2d4d67ba39a..9ed4e703f552d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Xunit; using Xunit.Abstractions; @@ -79,8 +80,7 @@ public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_Success() { server.Dispose(); - try { File.Delete(path); } - catch { } + Assert.False(File.Exists(path)); } } @@ -105,10 +105,14 @@ public async Task Socket_ConnectAsyncUnixDomainSocketEndPoint_NotServer() if (willRaiseEvent) { await complete.Task; + + Assert.Equal( + OperatingSystem.IsWindows() ? SocketError.ConnectionRefused : SocketError.AddressNotAvailable, + args.SocketError); } Assert.Equal( - OperatingSystem.IsWindows() ? SocketError.ConnectionRefused : SocketError.AddressNotAvailable, + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? SocketError.ConnectionRefused : SocketError.AddressNotAvailable, args.SocketError); } } @@ -125,36 +129,30 @@ public void Socket_SendReceive_Success() { string path = GetRandomNonExistingFilePath(); var endPoint = new UnixDomainSocketEndPoint(path); - try + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); + server.Bind(endPoint); + server.Listen(1); - client.Connect(endPoint); - using (Socket accepted = server.Accept()) + client.Connect(endPoint); + using (Socket accepted = server.Accept()) + { + var data = new byte[1]; + for (int i = 0; i < 10; i++) { - var data = new byte[1]; - for (int i = 0; i < 10; i++) - { - data[0] = (byte)i; + data[0] = (byte)i; - accepted.Send(data); - data[0] = 0; + accepted.Send(data); + data[0] = 0; - Assert.Equal(1, client.Receive(data)); - Assert.Equal(i, data[0]); - } + Assert.Equal(1, client.Receive(data)); + Assert.Equal(i, data[0]); } } } - finally - { - try { File.Delete(path); } - catch { } - } + + Assert.False(File.Exists(path)); } [ConditionalFact(nameof(PlatformSupportsUnixDomainSockets))] @@ -163,49 +161,43 @@ public void Socket_SendReceive_Clone_Success() { string path = GetRandomNonExistingFilePath(); var endPoint = new UnixDomainSocketEndPoint(path); - try + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { - using var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); - using var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); - { - server.Bind(endPoint); - server.Listen(1); - client.Connect(endPoint); + server.Bind(endPoint); + server.Listen(1); + client.Connect(endPoint); - using (Socket accepted = server.Accept()) - { - using var clientClone = new Socket(client.SafeHandle); - using var acceptedClone = new Socket(accepted.SafeHandle); + using (Socket accepted = server.Accept()) + { + using var clientClone = new Socket(client.SafeHandle); + using var acceptedClone = new Socket(accepted.SafeHandle); - _log.WriteLine($"accepted: LocalEndPoint={accepted.LocalEndPoint} RemoteEndPoint={accepted.RemoteEndPoint}"); - _log.WriteLine($"acceptedClone: LocalEndPoint={acceptedClone.LocalEndPoint} RemoteEndPoint={acceptedClone.RemoteEndPoint}"); + _log.WriteLine($"accepted: LocalEndPoint={accepted.LocalEndPoint} RemoteEndPoint={accepted.RemoteEndPoint}"); + _log.WriteLine($"acceptedClone: LocalEndPoint={acceptedClone.LocalEndPoint} RemoteEndPoint={acceptedClone.RemoteEndPoint}"); - Assert.True(clientClone.Connected); - Assert.True(acceptedClone.Connected); - Assert.Equal(client.LocalEndPoint.ToString(), clientClone.LocalEndPoint.ToString()); - Assert.Equal(client.RemoteEndPoint.ToString(), clientClone.RemoteEndPoint.ToString()); - Assert.Equal(accepted.LocalEndPoint.ToString(), acceptedClone.LocalEndPoint.ToString()); - Assert.Equal(accepted.RemoteEndPoint.ToString(), acceptedClone.RemoteEndPoint.ToString()); + Assert.True(clientClone.Connected); + Assert.True(acceptedClone.Connected); + Assert.Equal(client.LocalEndPoint.ToString(), clientClone.LocalEndPoint.ToString()); + Assert.Equal(client.RemoteEndPoint.ToString(), clientClone.RemoteEndPoint.ToString()); + Assert.Equal(accepted.LocalEndPoint.ToString(), acceptedClone.LocalEndPoint.ToString()); + Assert.Equal(accepted.RemoteEndPoint.ToString(), acceptedClone.RemoteEndPoint.ToString()); - var data = new byte[1]; - for (int i = 0; i < 10; i++) - { - data[0] = (byte)i; + var data = new byte[1]; + for (int i = 0; i < 10; i++) + { + data[0] = (byte)i; - acceptedClone.Send(data); - data[0] = 0; + acceptedClone.Send(data); + data[0] = 0; - Assert.Equal(1, clientClone.Receive(data)); - Assert.Equal(i, data[0]); - } + Assert.Equal(1, clientClone.Receive(data)); + Assert.Equal(i, data[0]); } } } - finally - { - try { File.Delete(path); } - catch { } - } + + Assert.False(File.Exists(path)); } [ConditionalFact(nameof(PlatformSupportsUnixDomainSockets))] @@ -214,36 +206,30 @@ public async Task Socket_SendReceiveAsync_Success() { string path = GetRandomNonExistingFilePath(); var endPoint = new UnixDomainSocketEndPoint(path); - try + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); + server.Bind(endPoint); + server.Listen(1); - await client.ConnectAsync(endPoint); - using (Socket accepted = await server.AcceptAsync()) + await client.ConnectAsync(endPoint); + using (Socket accepted = await server.AcceptAsync()) + { + var data = new byte[1]; + for (int i = 0; i < 10; i++) { - var data = new byte[1]; - for (int i = 0; i < 10; i++) - { - data[0] = (byte)i; + data[0] = (byte)i; - await accepted.SendAsync(new ArraySegment(data), SocketFlags.None); - data[0] = 0; + await accepted.SendAsync(new ArraySegment(data), SocketFlags.None); + data[0] = 0; - Assert.Equal(1, await client.ReceiveAsync(new ArraySegment(data), SocketFlags.None)); - Assert.Equal(i, data[0]); - } + Assert.Equal(1, await client.ReceiveAsync(new ArraySegment(data), SocketFlags.None)); + Assert.Equal(i, data[0]); } } } - finally - { - try { File.Delete(path); } - catch { } - } + + Assert.False(File.Exists(path)); } [ActiveIssue("https://github.com/dotnet/runtime/issues/26189", TestPlatforms.Windows)] @@ -261,53 +247,47 @@ public async Task Socket_SendReceiveAsync_PropagateToStream_Success(int iteratio string path = GetRandomNonExistingFilePath(); var endPoint = new UnixDomainSocketEndPoint(path); - try + using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { - using (var server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - using (var client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(endPoint); - server.Listen(1); + server.Bind(endPoint); + server.Listen(1); - Task serverAccept = server.AcceptAsync(); - await Task.WhenAll(serverAccept, client.ConnectAsync(endPoint)); + Task serverAccept = server.AcceptAsync(); + await Task.WhenAll(serverAccept, client.ConnectAsync(endPoint)); - Task clientReceives = Task.Run(async () => + Task clientReceives = Task.Run(async () => + { + byte[] buffer = new byte[readBufferSize]; + while (true) { - byte[] buffer = new byte[readBufferSize]; - while (true) + int bytesRead = await client.ReceiveAsync(new Memory(buffer), SocketFlags.None); + if (bytesRead == 0) { - int bytesRead = await client.ReceiveAsync(new Memory(buffer), SocketFlags.None); - if (bytesRead == 0) - { - break; - } - Assert.InRange(bytesRead, 1, writeBuffer.Length - readData.Length); - readData.Write(buffer, 0, bytesRead); + break; } - }); + Assert.InRange(bytesRead, 1, writeBuffer.Length - readData.Length); + readData.Write(buffer, 0, bytesRead); + } + }); - using (Socket accepted = await serverAccept) + using (Socket accepted = await serverAccept) + { + for (int iter = 0; iter < iterations; iter++) { - for (int iter = 0; iter < iterations; iter++) - { - Task sendTask = accepted.SendAsync(new ArraySegment(writeBuffer, iter * writeBufferSize, writeBufferSize), SocketFlags.None); - await await Task.WhenAny(clientReceives, sendTask); - Assert.Equal(writeBufferSize, await sendTask); - } + Task sendTask = accepted.SendAsync(new ArraySegment(writeBuffer, iter * writeBufferSize, writeBufferSize), SocketFlags.None); + await await Task.WhenAny(clientReceives, sendTask); + Assert.Equal(writeBufferSize, await sendTask); } - - await clientReceives; } - Assert.Equal(writeBuffer.Length, readData.Length); - AssertExtensions.SequenceEqual(writeBuffer, readData.ToArray()); - } - finally - { - try { File.Delete(path); } - catch { } + await clientReceives; } + + Assert.Equal(writeBuffer.Length, readData.Length); + AssertExtensions.SequenceEqual(writeBuffer, readData.ToArray()); + + Assert.False(File.Exists(path)); } [ConditionalTheory(nameof(PlatformSupportsUnixDomainSockets))] @@ -437,37 +417,27 @@ public void UnixDomainSocketEndPoint_RemoteEndPointEqualsBindAddress(bool abstra expectedClientAddress = clientAddress; } - try + using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) { - using (Socket server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - server.Bind(new UnixDomainSocketEndPoint(serverAddress)); - server.Listen(1); + server.Bind(new UnixDomainSocketEndPoint(serverAddress)); + server.Listen(1); - using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + using (Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + // Bind the client. + client.Bind(new UnixDomainSocketEndPoint(clientAddress)); + client.Connect(new UnixDomainSocketEndPoint(serverAddress)); + using (Socket acceptedClient = server.Accept()) { - // Bind the client. - client.Bind(new UnixDomainSocketEndPoint(clientAddress)); - client.Connect(new UnixDomainSocketEndPoint(serverAddress)); - using (Socket acceptedClient = server.Accept()) - { - // Verify the client address on the server. - EndPoint clientAddressOnServer = acceptedClient.RemoteEndPoint; - Assert.True(string.CompareOrdinal(expectedClientAddress, clientAddressOnServer.ToString()) == 0); - } + // Verify the client address on the server. + EndPoint clientAddressOnServer = acceptedClient.RemoteEndPoint; + Assert.True(string.CompareOrdinal(expectedClientAddress, clientAddressOnServer.ToString()) == 0); } } } - finally - { - if (!abstractAddress) - { - try { File.Delete(serverAddress); } - catch { } - try { File.Delete(clientAddress); } - catch { } - } - } + + Assert.False(File.Exists(serverAddress)); + Assert.False(File.Exists(clientAddress)); } [ConditionalFact(nameof(PlatformSupportsUnixDomainSockets))] @@ -501,6 +471,44 @@ public void Socket_CreateUnixDomainSocket_Throws_OnWindows() Assert.Throws(() => new UnixDomainSocketEndPoint("hello")); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void UnixDomainSocketEndPoint_RelativePathDeletesFile() + { + if (!PlatformSupportsUnixDomainSockets) + { + return; + } + RemoteExecutor.Invoke(() => + { + using (Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) + { + // Bind to a relative path. + string path = GetRandomNonExistingFilePath(); + string wd = Path.GetDirectoryName(path); + Directory.SetCurrentDirectory(wd); + socket.Bind(new UnixDomainSocketEndPoint(Path.GetFileName(path))); + Assert.True(File.Exists(path)); + + string otherDir = GetRandomNonExistingFilePath(); + Directory.CreateDirectory(otherDir); + try + { + // Change to another directory. + Directory.SetCurrentDirectory(Path.GetDirectoryName(path)); + + // Dispose deletes file from original path. + socket.Dispose(); + Assert.False(File.Exists(path)); + } + finally + { + Directory.SetCurrentDirectory(wd); + Directory.Delete(otherDir); + } + } + }).Dispose(); + } + private static string GetRandomNonExistingFilePath() { string result;