Skip to content

Commit

Permalink
don't fail LongModuleFileNamesAreSupported test if we can't load asse…
Browse files Browse the repository at this point in the history
…mbly on x86 (dotnet#57471)
  • Loading branch information
adamsitnik authored Aug 17, 2021
1 parent ead035b commit 14b34eb
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 155 deletions.
12 changes: 3 additions & 9 deletions src/libraries/System.Diagnostics.Process/tests/Interop.Unix.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace System.Diagnostics.Tests
internal static partial class Interop
{
internal static partial class Interop
{
[DllImport("libc")]
internal static extern int getsid(int pid);
}
[DllImport("libc")]
internal static extern int getsid(int pid);
}
212 changes: 105 additions & 107 deletions src/libraries/System.Diagnostics.Process/tests/Interop.cs
Original file line number Diff line number Diff line change
@@ -1,145 +1,143 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace System.Diagnostics.Tests
internal static partial class Interop
{
internal static partial class Interop
[StructLayout(LayoutKind.Sequential, Size = 40)]
public struct PROCESS_MEMORY_COUNTERS
{
[StructLayout(LayoutKind.Sequential, Size = 40)]
public struct PROCESS_MEMORY_COUNTERS
{
public uint cb;
public uint PageFaultCount;
public uint PeakWorkingSetSize;
public uint WorkingSetSize;
public uint QuotaPeakPagedPoolUsage;
public uint QuotaPagedPoolUsage;
public uint QuotaPeakNonPagedPoolUsage;
public uint QuotaNonPagedPoolUsage;
public uint PagefileUsage;
public uint PeakPagefileUsage;
}
public uint cb;
public uint PageFaultCount;
public uint PeakWorkingSetSize;
public uint WorkingSetSize;
public uint QuotaPeakPagedPoolUsage;
public uint QuotaPagedPoolUsage;
public uint QuotaPeakNonPagedPoolUsage;
public uint QuotaNonPagedPoolUsage;
public uint PagefileUsage;
public uint PeakPagefileUsage;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USER_INFO_1
{
public string usri1_name;
public string usri1_password;
public uint usri1_password_age;
public uint usri1_priv;
public string usri1_home_dir;
public string usri1_comment;
public uint usri1_flags;
public string usri1_script_path;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USER_INFO_1
{
public string usri1_name;
public string usri1_password;
public uint usri1_password_age;
public uint usri1_priv;
public string usri1_home_dir;
public string usri1_comment;
public uint usri1_flags;
public string usri1_script_path;
}

[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_USER
{
public SID_AND_ATTRIBUTES User;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_USER
{
public SID_AND_ATTRIBUTES User;
}

[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public int Attributes;
}

[DllImport("kernel32.dll")]
public static extern bool GetProcessWorkingSetSizeEx(SafeProcessHandle hProcess, out IntPtr lpMinimumWorkingSetSize, out IntPtr lpMaximumWorkingSetSize, out uint flags);
[DllImport("kernel32.dll")]
public static extern bool GetProcessWorkingSetSizeEx(SafeProcessHandle hProcess, out IntPtr lpMinimumWorkingSetSize, out IntPtr lpMaximumWorkingSetSize, out uint flags);

[DllImport("kernel32.dll")]
internal static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId);
[DllImport("kernel32.dll")]
internal static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId);

[DllImport("kernel32.dll")]
public static extern int GetProcessId(SafeProcessHandle nativeHandle);
[DllImport("kernel32.dll")]
public static extern int GetProcessId(SafeProcessHandle nativeHandle);

[DllImport("kernel32.dll")]
internal static extern int GetConsoleCP();
[DllImport("kernel32.dll")]
internal static extern int GetConsoleCP();

[DllImport("kernel32.dll")]
internal static extern int GetConsoleOutputCP();
[DllImport("kernel32.dll")]
internal static extern int GetConsoleOutputCP();

[DllImport("kernel32.dll")]
internal static extern int SetConsoleCP(int codePage);
[DllImport("kernel32.dll")]
internal static extern int SetConsoleCP(int codePage);

[DllImport("kernel32.dll")]
internal static extern int SetConsoleOutputCP(int codePage);
[DllImport("kernel32.dll")]
internal static extern int SetConsoleOutputCP(int codePage);

[DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint NetUserAdd(string servername, uint level, ref USER_INFO_1 buf, out uint parm_err);
[DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint NetUserAdd(string servername, uint level, ref USER_INFO_1 buf, out uint parm_err);

[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
internal static extern uint NetUserDel(string servername, string username);
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
internal static extern uint NetUserDel(string servername, string username);

[DllImport("advapi32.dll")]
internal static extern bool OpenProcessToken(SafeProcessHandle ProcessHandle, uint DesiredAccess, out SafeProcessHandle TokenHandle);
[DllImport("advapi32.dll")]
internal static extern bool OpenProcessToken(SafeProcessHandle ProcessHandle, uint DesiredAccess, out SafeProcessHandle TokenHandle);

[DllImport("advapi32.dll")]
internal static extern bool GetTokenInformation(SafeProcessHandle TokenHandle, uint TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);
[DllImport("advapi32.dll")]
internal static extern bool GetTokenInformation(SafeProcessHandle TokenHandle, uint TokenInformationClass, IntPtr TokenInformation, int TokenInformationLength, ref int ReturnLength);

[DllImport("shell32.dll")]
internal static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
[DllImport("shell32.dll")]
internal static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

internal static void NetUserAdd(string username, string password)
{
USER_INFO_1 userInfo = new USER_INFO_1();
userInfo.usri1_name = username;
userInfo.usri1_password = password;
userInfo.usri1_priv = 1;
internal static void NetUserAdd(string username, string password)
{
USER_INFO_1 userInfo = new USER_INFO_1();
userInfo.usri1_name = username;
userInfo.usri1_password = password;
userInfo.usri1_priv = 1;

uint parm_err;
uint result = NetUserAdd(null, 1, ref userInfo, out parm_err);
uint parm_err;
uint result = NetUserAdd(null, 1, ref userInfo, out parm_err);

if (result != ExitCodes.NERR_Success)
{
// most likely result == ERROR_ACCESS_DENIED
// due to running without elevated privileges
throw new Win32Exception((int)result);
}
if (result != ExitCodes.NERR_Success)
{
// most likely result == ERROR_ACCESS_DENIED
// due to running without elevated privileges
throw new Win32Exception((int)result);
}
}

internal static bool ProcessTokenToSid(SafeProcessHandle token, out SecurityIdentifier sid)
internal static bool ProcessTokenToSid(SafeProcessHandle token, out SecurityIdentifier sid)
{
bool ret = false;
sid = null;
IntPtr tu = IntPtr.Zero;
try
{
bool ret = false;
sid = null;
IntPtr tu = IntPtr.Zero;
try
{
TOKEN_USER tokUser;
const int bufLength = 256;

tu = Marshal.AllocHGlobal(bufLength);
int cb = bufLength;
ret = GetTokenInformation(token, 1, tu, cb, ref cb);
if (ret)
{
tokUser = Marshal.PtrToStructure<TOKEN_USER>(tu);
sid = new SecurityIdentifier(tokUser.User.Sid);
}
return ret;
}
catch (Exception)
{
return false;
}
finally
TOKEN_USER tokUser;
const int bufLength = 256;

tu = Marshal.AllocHGlobal(bufLength);
int cb = bufLength;
ret = GetTokenInformation(token, 1, tu, cb, ref cb);
if (ret)
{
if (tu != IntPtr.Zero)
Marshal.FreeHGlobal(tu);
tokUser = Marshal.PtrToStructure<TOKEN_USER>(tu);
sid = new SecurityIdentifier(tokUser.User.Sid);
}
return ret;
}

internal static class ExitCodes
catch (Exception)
{
return false;
}
finally
{
internal const uint NERR_Success = 0;
internal const uint NERR_UserNotFound = 2221;
if (tu != IntPtr.Zero)
Marshal.FreeHGlobal(tu);
}
}

internal static class ExitCodes
{
internal const uint NERR_Success = 0;
internal const uint NERR_UserNotFound = 2221;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Xunit;

namespace System.Diagnostics.Tests
{
public partial class ProcessModuleTests : ProcessTestBase
{
[ConditionalFact(typeof(PathFeatures), nameof(PathFeatures.AreAllLongPathsAvailable))]
public void LongModuleFileNamesAreSupported()
{
// To be able to test Long Path support for ProcessModule.FileName we need a .dll that has a path > 260 chars.
// Since Long Paths support can be disabled (see the ConditionalFact attribute usage above),
// we just copy "LongName.dll" from bin to a temp directory with a long name and load it from there.
// Loading from new path is possible because the type exposed by the assembly is not referenced in any explicit way.
const string libraryName = "LongPath.dll";
const int minPathLength = 261;

string testBinPath = Path.GetDirectoryName(typeof(ProcessModuleTests).Assembly.Location);
string libraryToCopy = Path.Combine(testBinPath, libraryName);
Assert.True(File.Exists(libraryToCopy), $"{libraryName} was not present in bin folder '{testBinPath}'");

string directoryWithLongName = Path.Combine(TestDirectory, new string('a', Math.Max(1, minPathLength - TestDirectory.Length)));
Directory.CreateDirectory(directoryWithLongName);

string longNamePath = Path.Combine(directoryWithLongName, libraryName);
Assert.True(longNamePath.Length > minPathLength);

File.Copy(libraryToCopy, longNamePath);
Assert.True(File.Exists(longNamePath));

IntPtr moduleHandle = Interop.Kernel32.LoadLibrary(longNamePath);
if (moduleHandle == IntPtr.Zero)
{
Assert.Equal(126, Marshal.GetLastWin32Error()); // ERROR_MOD_NOT_FOUND
Assert.Equal(Architecture.X86, RuntimeInformation.ProcessArchitecture);
return; // we have failed to load the module on x86, we skip the rest of the test (it's best effort)
}

try
{
string[] modulePaths = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().Select(module => module.FileName).ToArray();
Assert.Contains(longNamePath, modulePaths);
}
finally
{
Interop.Kernel32.FreeLibrary(moduleHandle);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Diagnostics.Tests
{
public class ProcessModuleTests : ProcessTestBase
public partial class ProcessModuleTests : ProcessTestBase
{
[Fact]
public void TestModuleProperties()
Expand Down Expand Up @@ -91,40 +89,5 @@ public void ModulesAreDisposedWhenProcessIsDisposed()
process.Dispose();
Assert.Equal(expectedCount, disposedCount);
}

public static bool Is_LongModuleFileNamesAreSupported_TestEnabled
=> PathFeatures.AreAllLongPathsAvailable() // we want to test long paths
&& !PlatformDetection.IsMonoRuntime // Assembly.LoadFile used the way this test is implemented fails on Mono
&& OperatingSystem.IsWindowsVersionAtLeast(8); // it's specific to Windows and does not work on Windows 7

[ConditionalFact(typeof(ProcessModuleTests), nameof(Is_LongModuleFileNamesAreSupported_TestEnabled))]
public void LongModuleFileNamesAreSupported()
{
// To be able to test Long Path support for ProcessModule.FileName we need a .dll that has a path > 260 chars.
// Since Long Paths support can be disabled (see the ConditionalFact attribute usage above),
// we just copy "LongName.dll" from bin to a temp directory with a long name and load it from there.
// Loading from new path is possible because the type exposed by the assembly is not referenced in any explicit way.
const string libraryName = "LongPath.dll";
const int minPathLength = 261;

string testBinPath = Path.GetDirectoryName(typeof(ProcessModuleTests).Assembly.Location);
string libraryToCopy = Path.Combine(testBinPath, libraryName);
Assert.True(File.Exists(libraryToCopy), $"{libraryName} was not present in bin folder '{testBinPath}'");

string directoryWithLongName = Path.Combine(TestDirectory, new string('a', Math.Max(1, minPathLength - TestDirectory.Length)));
Directory.CreateDirectory(directoryWithLongName);

string longNamePath = Path.Combine(directoryWithLongName, libraryName);
Assert.True(longNamePath.Length > minPathLength);

File.Copy(libraryToCopy, longNamePath);
Assert.True(File.Exists(longNamePath));

Assembly loaded = Assembly.LoadFile(longNamePath);
Assert.Equal(longNamePath, loaded.Location);

string[] modulePaths = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().Select(module => module.FileName).ToArray();
Assert.Contains(longNamePath, modulePaths);
}
}
}
Loading

0 comments on commit 14b34eb

Please sign in to comment.