2018-01-28 13:35:04 -08:00
|
|
|
|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
|
|
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using Microsoft.DotNet.Cli;
|
|
|
|
|
using Microsoft.DotNet.Cli.Utils;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
using Microsoft.DotNet.PlatformAbstractions;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
using Microsoft.DotNet.Tools;
|
2018-04-02 18:29:57 -07:00
|
|
|
|
using Microsoft.DotNet.Tools.Common;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
using Microsoft.Extensions.EnvironmentAbstractions;
|
2018-04-02 12:37:25 -07:00
|
|
|
|
using Newtonsoft.Json;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
|
|
|
|
namespace Microsoft.DotNet.ShellShim
|
|
|
|
|
{
|
|
|
|
|
internal class ShellShimRepository : IShellShimRepository
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private const string ApphostNameWithoutExtension = "apphost";
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
|
|
|
|
private readonly DirectoryPath _shimsDirectory;
|
2018-04-10 15:42:50 -07:00
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
|
private readonly IAppHostShellShimMaker _appHostShellShimMaker;
|
|
|
|
|
private readonly IFilePermissionSetter _filePermissionSetter;
|
|
|
|
|
|
|
|
|
|
public ShellShimRepository(
|
|
|
|
|
DirectoryPath shimsDirectory,
|
|
|
|
|
string appHostSourceDirectory = null,
|
|
|
|
|
IFileSystem fileSystem = null,
|
|
|
|
|
IAppHostShellShimMaker appHostShellShimMaker = null,
|
|
|
|
|
IFilePermissionSetter filePermissionSetter = null)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
|
|
|
|
_shimsDirectory = shimsDirectory;
|
2018-04-10 15:42:50 -07:00
|
|
|
|
_fileSystem = fileSystem ?? new FileSystemWrapper();
|
|
|
|
|
_appHostShellShimMaker = appHostShellShimMaker ?? new AppHostShellShimMaker(appHostSourceDirectory: appHostSourceDirectory);
|
|
|
|
|
_filePermissionSetter = filePermissionSetter ?? new FilePermissionSetter();
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 15:42:50 -07:00
|
|
|
|
public void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList<FilePath> packagedShims = null)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(targetExecutablePath.Value))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(CommonLocalizableStrings.CannotCreateShimForEmptyExecutablePath);
|
|
|
|
|
}
|
|
|
|
|
if (string.IsNullOrEmpty(commandName))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(CommonLocalizableStrings.CannotCreateShimForEmptyCommand);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ShimExists(commandName))
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.ShellShimConflict,
|
|
|
|
|
commandName));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransactionalAction.Run(
|
2018-04-02 12:37:25 -07:00
|
|
|
|
action: () =>
|
|
|
|
|
{
|
2018-01-28 13:35:04 -08:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
if (!_fileSystem.Directory.Exists(_shimsDirectory.Value))
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
_fileSystem.Directory.CreateDirectory(_shimsDirectory.Value);
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 15:42:50 -07:00
|
|
|
|
if (TryGetPackagedShim(packagedShims, commandName, out FilePath? packagedShim))
|
|
|
|
|
{
|
|
|
|
|
_fileSystem.File.Copy(packagedShim.Value.Value, GetShimPath(commandName).Value);
|
|
|
|
|
_filePermissionSetter.SetUserExecutionPermission(GetShimPath(commandName).Value);
|
|
|
|
|
}
|
|
|
|
|
else
|
2018-04-02 12:37:25 -07:00
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
_appHostShellShimMaker.CreateApphostShellShim(
|
|
|
|
|
targetExecutablePath,
|
|
|
|
|
GetShimPath(commandName));
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-10 15:42:50 -07:00
|
|
|
|
catch (FilePermissionSettingException ex)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, ex.Message));
|
|
|
|
|
}
|
2018-01-28 13:35:04 -08:00
|
|
|
|
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.FailedToCreateShellShim,
|
|
|
|
|
commandName,
|
|
|
|
|
ex.Message
|
|
|
|
|
),
|
|
|
|
|
ex);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
rollback: () => {
|
2018-04-10 15:42:50 -07:00
|
|
|
|
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
|
|
|
|
File.Delete(file.Value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveShim(string commandName)
|
|
|
|
|
{
|
|
|
|
|
var files = new Dictionary<string, string>();
|
|
|
|
|
TransactionalAction.Run(
|
|
|
|
|
action: () => {
|
|
|
|
|
try
|
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
|
|
|
|
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
2018-05-22 09:55:10 -07:00
|
|
|
|
FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(file.Value, tempPath));
|
2018-01-28 13:35:04 -08:00
|
|
|
|
files[file.Value] = tempPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.FailedToRemoveShellShim,
|
|
|
|
|
commandName,
|
|
|
|
|
ex.Message
|
|
|
|
|
),
|
|
|
|
|
ex);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
commit: () => {
|
|
|
|
|
foreach (var value in files.Values)
|
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
_fileSystem.File.Delete(value);
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
rollback: () => {
|
|
|
|
|
foreach (var kvp in files)
|
|
|
|
|
{
|
2018-05-22 09:55:10 -07:00
|
|
|
|
FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(kvp.Value, kvp.Key));
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
private class StartupOptions
|
|
|
|
|
{
|
|
|
|
|
public string appRoot { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class RootObject
|
|
|
|
|
{
|
|
|
|
|
public StartupOptions startupOptions { get; set; }
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ShimExists(string commandName)
|
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
return GetShimFiles(commandName).Any(p => _fileSystem.File.Exists(p.Value));
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable<FilePath> GetShimFiles(string commandName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(commandName))
|
|
|
|
|
{
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-02 12:37:25 -07:00
|
|
|
|
yield return GetShimPath(commandName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private FilePath GetShimPath(string commandName)
|
|
|
|
|
{
|
2018-01-28 13:35:04 -08:00
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
return new FilePath(_shimsDirectory.WithFile(commandName).Value +".exe");
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-04-02 12:37:25 -07:00
|
|
|
|
return _shimsDirectory.WithFile(commandName);
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-10 15:42:50 -07:00
|
|
|
|
private bool TryGetPackagedShim(
|
|
|
|
|
IReadOnlyList<FilePath> packagedShims,
|
|
|
|
|
string commandName,
|
|
|
|
|
out FilePath? packagedShim)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
packagedShim = null;
|
|
|
|
|
|
|
|
|
|
if (packagedShims != null && packagedShims.Count > 0)
|
2018-01-28 13:35:04 -08:00
|
|
|
|
{
|
2018-04-10 15:42:50 -07:00
|
|
|
|
FilePath[] candidatepackagedShim =
|
|
|
|
|
packagedShims
|
|
|
|
|
.Where(s => string.Equals(
|
|
|
|
|
Path.GetFileName(s.Value),
|
|
|
|
|
Path.GetFileName(GetShimPath(commandName).Value))).ToArray();
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
2018-04-10 15:42:50 -07:00
|
|
|
|
if (candidatepackagedShim.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
throw new ShellShimException(
|
|
|
|
|
string.Format(
|
|
|
|
|
CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
|
|
|
|
|
string.Join(';', candidatepackagedShim)));
|
|
|
|
|
}
|
2018-01-28 13:35:04 -08:00
|
|
|
|
|
2018-04-10 15:42:50 -07:00
|
|
|
|
if (candidatepackagedShim.Length == 1)
|
|
|
|
|
{
|
|
|
|
|
packagedShim = candidatepackagedShim.Single();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
2018-04-10 15:42:50 -07:00
|
|
|
|
|
|
|
|
|
return false;
|
2018-01-28 13:35:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|