From 94c6b0e04560739c82d86e4d108e858e90e2a197 Mon Sep 17 00:00:00 2001 From: Adeel Mujahid Date: Fri, 18 Oct 2019 00:47:27 +0300 Subject: [PATCH] =?UTF-8?q?Set=20apphost=20and=20bundle=20file=20permissio?= =?UTF-8?q?n=20to=20755=E2=82=88=20(dotnet/core-setup#8510)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/installer/corehost/CMakeLists.txt | 2 + .../AppHost/HostWriter.cs | 36 +++++-- src/installer/test/Directory.Build.props | 10 ++ .../HostActivation.Tests.csproj | 3 +- .../AppHostUpdateTests.cs | 102 +++++++++++++++++- src/installer/test/TestUtils/Command.cs | 6 +- src/installer/test/TestUtils/TestUtils.csproj | 7 +- 7 files changed, 150 insertions(+), 16 deletions(-) diff --git a/src/installer/corehost/CMakeLists.txt b/src/installer/corehost/CMakeLists.txt index 85f68324192..2b8473f47e9 100644 --- a/src/installer/corehost/CMakeLists.txt +++ b/src/installer/corehost/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required (VERSION 3.14) +project(corehost) + include(../settings.cmake) include(../functions.cmake) add_subdirectory(cli) diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs index 9922c6103c6..3215ed415b3 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs @@ -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. /// private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; - private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder); + private static readonly byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder); /// /// 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); } } diff --git a/src/installer/test/Directory.Build.props b/src/installer/test/Directory.Build.props index 4b971c5b5b9..b3eaee01f0d 100644 --- a/src/installer/test/Directory.Build.props +++ b/src/installer/test/Directory.Build.props @@ -12,4 +12,14 @@ netcoreapp3.0 + + nonwindowstests + nonlinuxtests + nonosxtests + nonfreebsdtests + nonnetbsdtests + + -notrait category=$(TargetOSTrait) + + diff --git a/src/installer/test/HostActivation.Tests/HostActivation.Tests.csproj b/src/installer/test/HostActivation.Tests/HostActivation.Tests.csproj index 76c372036ea..47fbf5659a8 100644 --- a/src/installer/test/HostActivation.Tests/HostActivation.Tests.csproj +++ b/src/installer/test/HostActivation.Tests/HostActivation.Tests.csproj @@ -21,9 +21,8 @@ - - + diff --git a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs index aaf2ae7e452..a48ea539319 100644 --- a/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs +++ b/src/installer/test/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs @@ -1,7 +1,13 @@ +// 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 customize = null) { // For now we're testing the AppHost on Windows PE files only. @@ -206,6 +248,64 @@ private string PrepareAppHostMockFile(TestDirectory testDirectory, Action 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 +} diff --git a/src/installer/test/TestUtils/Command.cs b/src/installer/test/TestUtils/Command.cs index b818c2bdd76..47ef88d77eb 100644 --- a/src/installer/test/TestUtils/Command.cs +++ b/src/installer/test/TestUtils/Command.cs @@ -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 "; } diff --git a/src/installer/test/TestUtils/TestUtils.csproj b/src/installer/test/TestUtils/TestUtils.csproj index c8513a05797..a608e308e13 100644 --- a/src/installer/test/TestUtils/TestUtils.csproj +++ b/src/installer/test/TestUtils/TestUtils.csproj @@ -13,10 +13,9 @@ - - - - + + + -- GitLab