提交 94c6b0e0 编写于 作者: A Adeel Mujahid 提交者: Swaroop Sridhar

Set apphost and bundle file permission to 755₈ (dotnet/core-setup#8510)

Set apphost and bundle file permission to 755₈

When building a .net core 3 app, the SDK currently simply copies the apphost template (including its permissions) from the install-location. 

This caused two problems in Unix systems:
* If the dotnet install location is write-protected, the build fails when SDK tries update the apphost (to set the app-path, etc.)  (dotnet/core-setup#8511)
* The built apphost can only be run by the owner (dotnet/core-setup#7062)

This change explicitly sets the file permissions of the Apphost in the SDK to fix the above issues.




Commit migrated from https://github.com/dotnet/core-setup/commit/a6a9202dc186f622f5df7125b12d95d9107c193a
上级 5f914e37
cmake_minimum_required (VERSION 3.14)
project(corehost)
include(../settings.cmake)
include(../functions.cmake)
add_subdirectory(cli)
......@@ -2,8 +2,10 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.ComponentModel;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
......@@ -19,7 +21,7 @@ public static class HostWriter
/// hash value embedded in default apphost executable in a place where the path to the app binary should be stored.
/// </summary>
private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);
private static readonly byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);
/// <summary>
/// Create an AppHost with embedded configuration of app binary location
......@@ -43,6 +45,7 @@ public static class HostWriter
}
BinaryUtils.CopyFile(appHostSourceFilePath, appHostDestinationFilePath);
bool appHostIsPEImage = false;
void RewriteAppHost()
......@@ -70,7 +73,7 @@ void RewriteAppHost()
}
void UpdateResources()
{
{
if (assemblyToCopyResorcesFrom != null && appHostIsPEImage)
{
if (ResourceUpdater.IsSupportedOS())
......@@ -89,6 +92,24 @@ void UpdateResources()
try
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var filePermissionOctal = Convert.ToInt32("755", 8); // -rwxr-xr-x
const int EINTR = 4;
int chmodReturnCode = 0;
do
{
chmodReturnCode = chmod(appHostDestinationFilePath, filePermissionOctal);
}
while (chmodReturnCode == -1 && Marshal.GetLastWin32Error() == EINTR);
if (chmodReturnCode == -1)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {appHostDestinationFilePath}.");
}
}
RetryUtil.RetryOnIOError(RewriteAppHost);
RetryUtil.RetryOnWin32Error(UpdateResources);
......@@ -133,14 +154,14 @@ void UpdateResources()
};
// Re-write the destination apphost with the proper contents.
RetryUtil.RetryOnIOError(() =>
RetryUtil.RetryOnIOError(() =>
BinaryUtils.SearchAndReplace(appHostPath,
bundleHeaderPlaceholder,
BitConverter.GetBytes(bundleHeaderOffset),
pad0s:false));
BitConverter.GetBytes(bundleHeaderOffset),
pad0s: false));
// Memory-mapped write does not updating last write time
RetryUtil.RetryOnIOError(() =>
RetryUtil.RetryOnIOError(() =>
File.SetLastWriteTimeUtc(appHostPath, DateTime.UtcNow));
}
......@@ -183,5 +204,8 @@ void FindBundleHeader()
return headerOffset != 0;
}
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string pathname, int mode);
}
}
......@@ -12,4 +12,14 @@
<TestInfraTargetFramework>netcoreapp3.0</TestInfraTargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetOSTrait Condition="'$(OSGroup)' == 'Windows_NT'">nonwindowstests</TargetOSTrait>
<TargetOSTrait Condition="'$(OSGroup)' == 'Linux'">nonlinuxtests</TargetOSTrait>
<TargetOSTrait Condition="'$(OSGroup)' == 'OSX'">nonosxtests</TargetOSTrait>
<TargetOSTrait Condition="'$(OSGroup)' == 'FreeBSD'">nonfreebsdtests</TargetOSTrait>
<TargetOSTrait Condition="'$(OSGroup)' == 'NetBSD'">nonnetbsdtests</TargetOSTrait>
<TestRunnerAdditionalArguments Condition="'$(TargetOSTrait)' != ''">-notrait category=$(TargetOSTrait)</TestRunnerAdditionalArguments>
</PropertyGroup>
</Project>
......@@ -21,9 +21,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.3.0" />
</ItemGroup>
......
// 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 System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using FluentAssertions;
using Xunit;
......@@ -160,6 +166,42 @@ public void ItFailsToSetGUISubsystemWithWrongDefault()
}
}
[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void ItGeneratesExecutableImage()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";
chmod(sourceAppHostMock, Convert.ToInt32("444", 8)) // make it readonly: -r--r--r--
.Should()
.NotBe(-1);
GetLastError()
.Should()
.NotBe(4); // EINTR
GetFilePermissionValue(sourceAppHostMock)
.Should()
.Be(Convert.ToInt32("444", 8));
HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath,
windowsGraphicalUserInterface: true);
GetFilePermissionValue(destinationFilePath)
.Should()
.Be(Convert.ToInt32("755", 8));
}
int GetLastError() => Marshal.GetLastWin32Error();
}
private string PrepareAppHostMockFile(TestDirectory testDirectory, Action<byte[]> customize = null)
{
// For now we're testing the AppHost on Windows PE files only.
......@@ -206,6 +248,64 @@ private string PrepareAppHostMockFile(TestDirectory testDirectory, Action<byte[]
0, 112, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 3, 0, 96, 193, 0, 0, 24,
0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0 };
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string pathname, int mode);
private static int GetFilePermissionValue(string path)
{
var modeValue = CoreFxFileStatusProvider.GetFileMode(path);
// st_mode is typically a 16-bits value, high 4 bits are filetype and low 12
// bits are permission. we will clear first 20 bits (a byte and a nibble) with
// the following mask:
modeValue &= 0x1ff;
modeValue
.Should()
.BeInRange(0, 511);
return modeValue;
}
private static class CoreFxFileStatusProvider
{
private static FieldInfo s_fileSystem_fileStatusField, s_fileStatus_fileStatusField, s_fileStatusModeField;
static CoreFxFileStatusProvider()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
s_fileSystem_fileStatusField = typeof(FileSystemInfo).GetField("_fileStatus", BindingFlags.NonPublic | BindingFlags.Instance);
s_fileStatus_fileStatusField = s_fileSystem_fileStatusField.FieldType.GetField("_fileStatus", BindingFlags.NonPublic | BindingFlags.Instance);
s_fileStatusModeField = s_fileStatus_fileStatusField.FieldType.GetField("Mode", BindingFlags.NonPublic | BindingFlags.Instance);
}
catch (Exception ex)
{
throw new Exception("Cannot setup _fileStatus via private reflection from CoreFX. Verify if the FileSystem._fileStatus._fileStatus.Mode chain is intact in CoreFX, otherwise adjust this implementation", ex);
}
}
}
public static int GetFileMode(string path)
{
try
{
var fileInfo = new FileInfo(path);
_ = fileInfo.IsReadOnly; // this is to implicitly initialize FileInfo -> FileSystem -> fielStatus instance
return (int)s_fileStatusModeField.GetValue(
s_fileStatus_fileStatusField.GetValue(
s_fileSystem_fileStatusField.GetValue(fileInfo)));
}
catch (Exception ex)
{
throw new Exception("Cannot get stat (2) st_mode via private reflection from CoreFX. Verify if the FileSystem._fileStatus.Initialize logic is exercised via FileInfo.IsReadOnly in CoreFX, otherwise adjust this implementation.", ex);
}
}
}
private class TestDirectory : IDisposable
{
public string Path { get; private set; }
......@@ -233,4 +333,4 @@ public void Dispose()
}
}
}
}
\ No newline at end of file
}
......@@ -10,7 +10,6 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.DotNet.InternalAbstractions;
namespace Microsoft.DotNet.Cli.Build.Framework
{
......@@ -251,7 +250,7 @@ public Command WorkingDirectory(string projectDirectory)
public Command WithUserProfile(string userprofile)
{
string userDir;
if (InternalAbstractions.RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
userDir = "USERPROFILE";
}
......@@ -383,7 +382,8 @@ private void ReportExecEnd(int exitCode, bool fExpectedToFail)
bool success = exitCode == 0;
string msgExpectedToFail = "";
if (fExpectedToFail) {
if (fExpectedToFail)
{
success = !success;
msgExpectedToFail = "failed as expected and ";
}
......
......@@ -13,10 +13,9 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.4" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.1.1" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.0.0" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.0" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsPackageVersion)" />
<PackageReference Include="xunit.core" Version="$(XUnitPackageVersion)" ExcludeAssets="build" />
</ItemGroup>
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册