Skip to content

Commit ddbb712

Browse files
authored
Support setting NamedPipeClientStream.ReadMode with PipeAccessRights ctor overload (#100001)
1 parent a57fa6f commit ddbb712

15 files changed

+334
-58
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
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+
4+
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.IO.Pipes.PipeAccessRights))]

src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,6 @@ public static class NamedPipeServerStreamAcl
1414
{
1515
public static System.IO.Pipes.NamedPipeServerStream Create(string pipeName, System.IO.Pipes.PipeDirection direction, int maxNumberOfServerInstances, System.IO.Pipes.PipeTransmissionMode transmissionMode, System.IO.Pipes.PipeOptions options, int inBufferSize, int outBufferSize, System.IO.Pipes.PipeSecurity? pipeSecurity, System.IO.HandleInheritability inheritability = System.IO.HandleInheritability.None, System.IO.Pipes.PipeAccessRights additionalAccessRights = default) { throw null; }
1616
}
17-
[System.FlagsAttribute]
18-
public enum PipeAccessRights
19-
{
20-
ReadData = 1,
21-
WriteData = 2,
22-
CreateNewInstance = 4,
23-
ReadExtendedAttributes = 8,
24-
WriteExtendedAttributes = 16,
25-
ReadAttributes = 128,
26-
WriteAttributes = 256,
27-
Write = 274,
28-
Delete = 65536,
29-
ReadPermissions = 131072,
30-
Read = 131209,
31-
ReadWrite = 131483,
32-
ChangePermissions = 262144,
33-
TakeOwnership = 524288,
34-
Synchronize = 1048576,
35-
FullControl = 2032031,
36-
AccessSystemSecurity = 16777216,
37-
}
3817
public sealed partial class PipeAccessRule : System.Security.AccessControl.AccessRule
3918
{
4019
public PipeAccessRule(System.Security.Principal.IdentityReference identity, System.IO.Pipes.PipeAccessRights rights, System.Security.AccessControl.AccessControlType type) : base (default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { }

src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
<ItemGroup>
77
<Compile Include="System.IO.Pipes.AccessControl.cs" />
8+
<Compile Include="System.IO.Pipes.AccessControl.TypeForwards.cs" />
89
</ItemGroup>
910

1011
<ItemGroup>

src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public sealed partial class NamedPipeClientStream : System.IO.Pipes.PipeStream
4545
public NamedPipeClientStream(System.IO.Pipes.PipeDirection direction, bool isAsync, bool isConnected, Microsoft.Win32.SafeHandles.SafePipeHandle safePipeHandle) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
4646
public NamedPipeClientStream(string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
4747
public NamedPipeClientStream(string serverName, string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
48+
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
49+
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeAccessRights desiredAccessRights, PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) : base(default(System.IO.Pipes.PipeDirection), default(int)) { }
4850
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
4951
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
5052
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
@@ -82,6 +84,27 @@ public void WaitForConnection() { }
8284
public System.Threading.Tasks.Task WaitForConnectionAsync() { throw null; }
8385
public System.Threading.Tasks.Task WaitForConnectionAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
8486
}
87+
[System.FlagsAttribute]
88+
public enum PipeAccessRights
89+
{
90+
ReadData = 1,
91+
WriteData = 2,
92+
CreateNewInstance = 4,
93+
ReadExtendedAttributes = 8,
94+
WriteExtendedAttributes = 16,
95+
ReadAttributes = 128,
96+
WriteAttributes = 256,
97+
Write = 274,
98+
Delete = 65536,
99+
ReadPermissions = 131072,
100+
Read = 131209,
101+
ReadWrite = 131483,
102+
ChangePermissions = 262144,
103+
TakeOwnership = 524288,
104+
Synchronize = 1048576,
105+
FullControl = 2032031,
106+
AccessSystemSecurity = 16777216,
107+
}
85108
public enum PipeDirection
86109
{
87110
In = 1,

src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
33
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4-
<!-- Exposed public in System.IO.Pipes.AccessControl but implemented in System.IO.Pipes. -->
54
<Suppression>
65
<DiagnosticId>CP0001</DiagnosticId>
76
<Target>T:System.IO.Pipes.AnonymousPipeServerStreamAcl</Target>
@@ -14,12 +13,6 @@
1413
<Left>ref/net9.0/System.IO.Pipes.dll</Left>
1514
<Right>runtimes/win/lib/net9.0/System.IO.Pipes.dll</Right>
1615
</Suppression>
17-
<Suppression>
18-
<DiagnosticId>CP0001</DiagnosticId>
19-
<Target>T:System.IO.Pipes.PipeAccessRights</Target>
20-
<Left>ref/net9.0/System.IO.Pipes.dll</Left>
21-
<Right>runtimes/win/lib/net9.0/System.IO.Pipes.dll</Right>
22-
</Suppression>
2316
<Suppression>
2417
<DiagnosticId>CP0001</DiagnosticId>
2518
<Target>T:System.IO.Pipes.PipeAccessRule</Target>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
<data name="ArgumentOutOfRange_NeedValidPipeAccessRights" xml:space="preserve">
121121
<value>Invalid PipeAccessRights value.</value>
122122
</data>
123+
<data name="PlatformNotSupported_PipeAccessRights" xml:space="preserve">
124+
<value>Specifying PipeAccessRights is not supported on this platform.</value>
125+
</data>
123126
<data name="Argument_NonContainerInvalidAnyFlag" xml:space="preserve">
124127
<value>This flag may not be set on a pipe.</value>
125128
</data>

src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<Compile Include="System\IO\Pipes\AnonymousPipeServerStream.cs" />
2020
<Compile Include="System\IO\Pipes\NamedPipeClientStream.cs" />
2121
<Compile Include="System\IO\Pipes\NamedPipeServerStream.cs" />
22+
<Compile Include="System\IO\Pipes\PipeAccessRights.cs" />
2223
<Compile Include="System\IO\Pipes\PipeDirection.cs" />
2324
<Compile Include="System\IO\Pipes\PipeOptions.cs" />
2425
<Compile Include="System\IO\Pipes\PipeState.cs" />
@@ -116,7 +117,6 @@
116117
<Compile Include="System\IO\Pipes\NamedPipeClientStream.Windows.cs" />
117118
<Compile Include="System\IO\Pipes\NamedPipeServerStream.Windows.cs" />
118119
<Compile Include="System\IO\Pipes\NamedPipeServerStream.Win32.cs" />
119-
<Compile Include="System\IO\Pipes\PipeAccessRights.cs" />
120120
<Compile Include="System\IO\Pipes\PipeAccessRule.cs" />
121121
<Compile Include="System\IO\Pipes\PipeAuditRule.cs" />
122122
<Compile Include="System\IO\Pipes\PipesAclExtensions.cs" />

src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Runtime.InteropServices;
88
using System.Runtime.Versioning;
99
using System.Security;
10+
using System.Security.Principal;
1011
using System.Threading;
1112
using Microsoft.Win32.SafeHandles;
1213

@@ -18,6 +19,16 @@ namespace System.IO.Pipes
1819
/// </summary>
1920
public sealed partial class NamedPipeClientStream : PipeStream
2021
{
22+
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
23+
public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights,
24+
PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability)
25+
: base(PipeDirection.InOut, 0)
26+
{
27+
throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeAccessRights);
28+
}
29+
30+
private static int AccessRightsFromDirection(PipeDirection _) => 0;
31+
2132
private bool TryConnect(int _ /* timeout */)
2233
{
2334
// timeout isn't used as Connect will be very fast,

src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5+
using System.Runtime.CompilerServices;
56
using System.Runtime.InteropServices;
67
using System.Runtime.Versioning;
78
using System.Security.Principal;
@@ -16,6 +17,81 @@ namespace System.IO.Pipes
1617
/// </summary>
1718
public sealed partial class NamedPipeClientStream : PipeStream
1819
{
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="NamedPipeClientStream"/> class with the specified pipe and server names,
22+
/// the desired <see cref="PipeAccessRights"/>, and the specified impersonation level and inheritability.
23+
/// </summary>
24+
/// <param name="serverName">The name of the remote computer to connect to, or "." to specify the local computer.</param>
25+
/// <param name="pipeName">The name of the pipe.</param>
26+
/// <param name="desiredAccessRights">One of the enumeration values that specifies the desired access rights of the pipe.</param>
27+
/// <param name="options">One of the enumeration values that determines how to open or create the pipe.</param>
28+
/// <param name="impersonationLevel">One of the enumeration values that determines the security impersonation level.</param>
29+
/// <param name="inheritability">One of the enumeration values that determines whether the underlying handle will be inheritable by child processes.</param>
30+
/// <exception cref="ArgumentNullException"><paramref name="pipeName"/> or <paramref name="serverName"/> is <c>null</c>.</exception>
31+
/// <exception cref="ArgumentException"><paramref name="pipeName"/> or <paramref name="serverName"/> is a zero-length string.</exception>
32+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="pipeName"/> is set to "anonymous".</exception>
33+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="desiredAccessRights"/> is not a valid <see cref="PipeAccessRights"/> value.</exception>
34+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="options"/> is not a valid <see cref="PipeOptions"/> value.</exception>
35+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="impersonationLevel"/> is not a valid <see cref="TokenImpersonationLevel"/> value.</exception>
36+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="inheritability"/> is not a valid <see cref="HandleInheritability"/> value.</exception>
37+
/// <remarks>
38+
/// The pipe direction for this constructor is determined by the <paramref name="desiredAccessRights"/> parameter.
39+
/// If the <paramref name="desiredAccessRights"/> parameter specifies <see cref="PipeAccessRights.ReadData"/>,
40+
/// the pipe direction is <see cref="PipeDirection.In"/>. If the <paramref name="desiredAccessRights"/> parameter
41+
/// specifies <see cref="PipeAccessRights.WriteData"/>, the pipe direction is <see cref="PipeDirection.Out"/>.
42+
/// If the value of <paramref name="desiredAccessRights"/> specifies both <see cref="PipeAccessRights.ReadData"/>
43+
/// and <see cref="PipeAccessRights.WriteData"/>, the pipe direction is <see cref="PipeDirection.InOut"/>.
44+
/// </remarks>
45+
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
46+
public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights,
47+
PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability)
48+
: this(serverName, pipeName, DirectionFromRights(desiredAccessRights), options, impersonationLevel, inheritability)
49+
{
50+
_accessRights = (int)desiredAccessRights;
51+
}
52+
53+
private static PipeDirection DirectionFromRights(PipeAccessRights desiredAccessRights, [CallerArgumentExpression(nameof(desiredAccessRights))] string? argumentName = null)
54+
{
55+
// Validate the desiredAccessRights parameter here to ensure an invalid value does not result
56+
// in an argument exception being thrown for the direction argument
57+
// Throw if there are any unrecognized bits
58+
// Throw if neither ReadData nor WriteData are specified, as this will result in an invalid PipeDirection
59+
if ((desiredAccessRights & ~(PipeAccessRights.FullControl | PipeAccessRights.AccessSystemSecurity)) != 0 ||
60+
((desiredAccessRights & (PipeAccessRights.ReadData | PipeAccessRights.WriteData)) == 0))
61+
{
62+
throw new ArgumentOutOfRangeException(argumentName, SR.ArgumentOutOfRange_NeedValidPipeAccessRights);
63+
}
64+
65+
PipeDirection direction = 0;
66+
67+
if ((desiredAccessRights & PipeAccessRights.ReadData) != 0)
68+
{
69+
direction |= PipeDirection.In;
70+
}
71+
if ((desiredAccessRights & PipeAccessRights.WriteData) != 0)
72+
{
73+
direction |= PipeDirection.Out;
74+
}
75+
76+
return direction;
77+
}
78+
79+
private static int AccessRightsFromDirection(PipeDirection direction)
80+
{
81+
int access = 0;
82+
83+
if ((PipeDirection.In & direction) != 0)
84+
{
85+
access |= Interop.Kernel32.GenericOperations.GENERIC_READ;
86+
}
87+
if ((PipeDirection.Out & direction) != 0)
88+
{
89+
access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE;
90+
}
91+
92+
return access;
93+
}
94+
1995
// Waits for a pipe instance to become available. This method may return before WaitForConnection is called
2096
// on the server end, but WaitForConnection will not return until we have returned. Any data written to the
2197
// pipe by us after we have connected but before the server has called WaitForConnection will be available
@@ -34,17 +110,7 @@ private bool TryConnect(int timeout)
34110
_pipeFlags |= (((int)_impersonationLevel - 1) << 16);
35111
}
36112

37-
int access = 0;
38-
if ((PipeDirection.In & _direction) != 0)
39-
{
40-
access |= Interop.Kernel32.GenericOperations.GENERIC_READ;
41-
}
42-
if ((PipeDirection.Out & _direction) != 0)
43-
{
44-
access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE;
45-
}
46-
47-
SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
113+
SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights);
48114

49115
if (handle.IsInvalid)
50116
{
@@ -81,7 +147,7 @@ private bool TryConnect(int timeout)
81147
}
82148

83149
// Pipe server should be free. Let's try to connect to it.
84-
handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
150+
handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights);
85151

86152
if (handle.IsInvalid)
87153
{

src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public sealed partial class NamedPipeClientStream : PipeStream
2424
private readonly PipeOptions _pipeOptions;
2525
private readonly HandleInheritability _inheritability;
2626
private readonly PipeDirection _direction;
27+
private readonly int _accessRights;
2728

2829
// Creates a named pipe client using default server (same machine, or "."), and PipeDirection.InOut
2930
public NamedPipeClientStream(string pipeName)
@@ -84,6 +85,7 @@ public NamedPipeClientStream(string serverName, string pipeName, PipeDirection d
8485
_inheritability = inheritability;
8586
_impersonationLevel = impersonationLevel;
8687
_pipeOptions = options;
88+
_accessRights = AccessRightsFromDirection(direction);
8789
}
8890

8991
// Create a NamedPipeClientStream from an existing server pipe handle.

0 commit comments

Comments
 (0)