Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b03464e

Browse files
author
Paulo Janotti
authored
Named pipe in Unix respects absolute path (#27826)
* Named pipe in Unix respects absolute path This was added for two reasons: 1. Allow users to workaround the hard limit on Unix domain sockets that can be very limiting if the $TMPDIR has a long path; 2. Make easy to make that works both on Unix and Windows without having to check platform, e.g.: "/tmp/myAppPipe" works on both platforms.
1 parent 122cad4 commit b03464e

File tree

6 files changed

+99
-8
lines changed

6 files changed

+99
-8
lines changed

src/System.IO.Pipes/src/Resources/Strings.resx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@
267267
<data name="PlatformNotSupported_RemotePipes" xml:space="preserve">
268268
<value>Access to remote named pipes is not supported on this platform.</value>
269269
</data>
270-
<data name="PlatformNotSupproted_InvalidNameChars" xml:space="preserve">
271-
<value>The name of a pipe on this platform must only include characters valid in file names.</value>
270+
<data name="PlatformNotSupported_InvalidPipeNameChars" xml:space="preserve">
271+
<value>The name of a pipe on this platform must be a valid file name or a valid absolute path to a file name.</value>
272272
</data>
273273
<data name="ObjectDisposed_StreamClosed" xml:space="preserve">
274274
<value>Cannot access a closed Stream.</value>

src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public abstract partial class PipeStream : Stream
2626
/// <summary>Characters that can't be used in a pipe's name.</summary>
2727
private static readonly char[] s_invalidFileNameChars = Path.GetInvalidFileNameChars();
2828

29+
/// <summary>Characters that can't be used in an absolute path pipe's name.</summary>
30+
private static readonly char[] s_invalidPathNameChars = Path.GetInvalidPathChars();
31+
2932
/// <summary>Prefix to prepend to all pipe names.</summary>
3033
private static readonly string s_pipePrefix = Path.Combine(Path.GetTempPath(), "CoreFxPipe_");
3134

@@ -43,16 +46,27 @@ internal static string GetPipePath(string serverName, string pipeName)
4346
throw new ArgumentOutOfRangeException(nameof(pipeName), SR.ArgumentOutOfRange_AnonymousReserved);
4447
}
4548

49+
// Since pipes are stored as files in the system we support either an absolute path to a file name
50+
// or a file name. The support of absolute path was added to allow working around the limited
51+
// length available for the pipe name when concatenated with the temp path, while being
52+
// cross-platform with Windows (which has only '\' as an invalid char).
53+
if (Path.IsPathRooted(pipeName))
54+
{
55+
if (pipeName.IndexOfAny(s_invalidPathNameChars) >= 0 || pipeName[pipeName.Length - 1] == Path.DirectorySeparatorChar)
56+
throw new PlatformNotSupportedException(SR.PlatformNotSupported_InvalidPipeNameChars);
57+
58+
// Caller is in full control of file location.
59+
return pipeName;
60+
}
61+
4662
if (pipeName.IndexOfAny(s_invalidFileNameChars) >= 0)
4763
{
48-
// Since pipes are stored as files in the file system, we don't support
49-
// pipe names that are actually paths or that otherwise have invalid
50-
// filename characters in them.
51-
throw new PlatformNotSupportedException(SR.PlatformNotSupproted_InvalidNameChars);
64+
throw new PlatformNotSupportedException(SR.PlatformNotSupported_InvalidPipeNameChars);
5265
}
5366

54-
// Return the pipe path. The pipe is created directly under %TMPDIR%. We previously
55-
// didn't put it into a subdirectory because it only existed on disk for the duration
67+
// The pipe is created directly under Path.GetTempPath() with "CoreFXPipe_" prefix.
68+
//
69+
// We previously didn't put it into a subdirectory because it only existed on disk for the duration
5670
// between when the server started listening in WaitForConnection and when the client
5771
// connected, after which the pipe was deleted. We now create the pipe when the
5872
// server stream is created, which leaves it on disk longer, but we can't change the

src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ public static void NotSupportedPipePath_Throws_PlatformNotSupportedException()
7575

7676
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream("foobar" + hostName, "foobar"));
7777
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0]));
78+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "/tmp/foo\0bar"));
79+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "/tmp/foobar/"));
80+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "/"));
81+
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "\0"));
7882
}
7983

8084
[Theory]

src/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.netcoreapp.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ public static void CreateServer_ConnectClient()
4141
}
4242
}
4343

44+
[Fact]
45+
public static void CreateServer_ConnectClient_UsingUnixAbsolutePath()
46+
{
47+
string name = Path.Combine("/tmp", GetUniquePipeName());
48+
using (var server = new NamedPipeServerStream(name, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly))
49+
{
50+
using (var client = new NamedPipeClientStream(".", name, PipeDirection.InOut, PipeOptions.CurrentUserOnly))
51+
{
52+
client.Connect();
53+
}
54+
}
55+
}
56+
4457
[Theory]
4558
[InlineData(PipeOptions.None, PipeOptions.CurrentUserOnly)]
4659
[InlineData(PipeOptions.CurrentUserOnly, PipeOptions.None)]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Net.Sockets;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace System.IO.Pipes.Tests
10+
{
11+
public class NamedPipeTest_UnixDomainSockets : NamedPipeTestBase
12+
{
13+
[Fact]
14+
[PlatformSpecific(TestPlatforms.AnyUnix)]
15+
public void NamedPipeServer_Connects_With_UnixDomainSocketEndPointClient()
16+
{
17+
string absoluteHomePath = Path.GetFullPath(Environment.GetEnvironmentVariable("HOME"));
18+
string pipeName = Path.Combine(absoluteHomePath, "pipe-tests-corefx-" + Path.GetRandomFileName());
19+
var endPoint = new UnixDomainSocketEndPoint(pipeName);
20+
21+
using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly))
22+
using (var sockectClient = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified))
23+
{
24+
sockectClient.Connect(endPoint);
25+
Assert.True(File.Exists(pipeName));
26+
}
27+
28+
Assert.False(File.Exists(pipeName));
29+
}
30+
31+
[Fact]
32+
[PlatformSpecific(TestPlatforms.AnyUnix)]
33+
public async Task NamedPipeClient_Connects_With_UnixDomainSocketEndPointServer()
34+
{
35+
string absoluteHomePath = Path.GetFullPath(Environment.GetEnvironmentVariable("HOME"));
36+
string pipeName = Path.Combine(absoluteHomePath, "pipe-tests-corefx-" + Path.GetRandomFileName());
37+
var endPoint = new UnixDomainSocketEndPoint(pipeName);
38+
39+
using (var socketServer = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified))
40+
using (var pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.None))
41+
{
42+
socketServer.Bind(endPoint);
43+
socketServer.Listen(1);
44+
45+
var pipeConnectTask = pipeClient.ConnectAsync(15_000);
46+
using (Socket accepted = socketServer.Accept())
47+
{
48+
await pipeConnectTask;
49+
Assert.True(File.Exists(pipeName));
50+
}
51+
}
52+
53+
Assert.True(File.Exists(pipeName));
54+
try { File.Delete(pipeName); } catch { }
55+
}
56+
}
57+
}

src/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' and '$(TargetsWindows)' == 'true'">
4646
<Compile Include="NamedPipeTests\NamedPipeTest.CurrentUserOnly.netcoreapp.Windows.cs" />
4747
</ItemGroup>
48+
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' and '$(TargetsUnix)' == 'true'">
49+
<Compile Include="NamedPipeTests\NamedPipeTest.UnixDomainSockets.cs" />
50+
</ItemGroup>
4851
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
4952
<Compile Include="Interop.Windows.cs" />
5053
<Compile Include="NamedPipeTests\NamedPipeTest.RunAsClient.Windows.cs" />

0 commit comments

Comments
 (0)