diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs index 77e0a621cdce..454534779a5d 100644 --- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs +++ b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs @@ -63,7 +63,7 @@ internal MsBuildFileSetFactory( public async Task CreateAsync(CancellationToken cancellationToken) { - var watchList = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var watchList = Path.GetTempFileName(); try { var projectDir = Path.GetDirectoryName(_projectFile); diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj index 636f43d119b4..065f750f7200 100644 --- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj +++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj @@ -23,6 +23,7 @@ + diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs index 312d6fecd7d4..b063b9fc4a08 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.EnvironmentAbstractions { - internal class DirectoryWrapper: IDirectory + internal class DirectoryWrapper : IDirectory { public bool Exists(string path) { @@ -19,6 +19,11 @@ public ITemporaryDirectory CreateTemporaryDirectory() return new TemporaryDirectory(); } + public string CreateTemporarySubdirectory() + { + return CreateTemporaryDirectory().DirectoryPath; + } + public IEnumerable EnumerateDirectories(string path) { return Directory.EnumerateDirectories(path); diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs index 06ed28ab76d7..cf69976a0844 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs @@ -30,7 +30,7 @@ public string ToQuotedString() public override string ToString() { - return ToQuotedString(); + return Value; } public DirectoryPath GetDirectoryPath() diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs index 983bba34afc3..0a2e4e31e09f 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs @@ -24,5 +24,9 @@ internal interface IDirectory void Delete(string path, bool recursive); void Move(string source, string destination); + + + /// Returns a new directory created under the temp folder. Can be on the mock under test or the real temp file folder. + string CreateTemporarySubdirectory(); } } diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj index 041ba1315c78..6260c984866d 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj @@ -12,4 +12,7 @@ true + + + diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs index d43683e156f5..9ce8241c8967 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs @@ -1,8 +1,9 @@ // 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 Microsoft.Extensions.EnvironmentAbstractions; using System.IO; +using Microsoft.Extensions.EnvironmentAbstractions; +using Microsoft.DotNet; namespace Microsoft.DotNet.InternalAbstractions { @@ -12,8 +13,7 @@ internal class TemporaryDirectory : ITemporaryDirectory public TemporaryDirectory() { - DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - Directory.CreateDirectory(DirectoryPath); + DirectoryPath = Path.Combine(PathUtilities.CreateTempSubdirectory()); } public void Dispose() diff --git a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs index 4dee9748d079..68390f1f2ce2 100644 --- a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs +++ b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs @@ -85,7 +85,8 @@ public void CreateShim(FilePath targetExecutablePath, ToolCommandName commandNam ex); } }, - rollback: () => { + rollback: () => + { foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value))) { File.Delete(file.Value); @@ -97,12 +98,13 @@ public void RemoveShim(ToolCommandName commandName) { var files = new Dictionary(); TransactionalAction.Run( - action: () => { + action: () => + { try { foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value))) { - var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var tempPath = Path.Combine(_fileSystem.Directory.CreateTemporarySubdirectory(), Path.GetRandomFileName()); FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(file.Value, tempPath)); files[file.Value] = tempPath; } @@ -118,13 +120,15 @@ public void RemoveShim(ToolCommandName commandName) ex); } }, - commit: () => { + commit: () => + { foreach (var value in files.Values) { _fileSystem.File.Delete(value); } }, - rollback: () => { + rollback: () => + { foreach (var kvp in files) { FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(kvp.Value, kvp.Key)); diff --git a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs index 799c99fbec39..8b04671a631b 100644 --- a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs +++ b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs @@ -3,11 +3,9 @@ using System; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; using NuGet.Common; using NuGet.Configuration; @@ -18,8 +16,6 @@ namespace Microsoft.DotNet.Cli /// public static class SudoEnvironmentDirectoryOverride { - private const string SudoHomeDirectory = "/tmp/dotnet_sudo_home/"; - /// /// Not for security use. Detect if command is running under sudo /// via if SUDO_UID being set. @@ -38,22 +34,9 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult) { if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult)) { - if (!TempHomeIsOnlyRootWritable(SudoHomeDirectory)) - { - try - { - Directory.Delete(SudoHomeDirectory, recursive: true); - } - catch (DirectoryNotFoundException) - { - // Avoid read after write race condition - } - } - - Directory.CreateDirectory(SudoHomeDirectory); - + string sudoHome = PathUtilities.CreateTempSubdirectory(); var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME")); - Environment.SetEnvironmentVariable("HOME", SudoHomeDirectory); + Environment.SetEnvironmentVariable("HOME", sudoHome); CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride); } @@ -107,31 +90,5 @@ private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverrid private static bool IsRunningWorkloadCommand(ParseResult parseResult) => parseResult.RootSubCommandResult() == (WorkloadCommandParser.GetCommand().Name); - - private static bool TempHomeIsOnlyRootWritable(string path) - { - if (StatInterop.LStat(path, out StatInterop.FileStatus fileStat) != 0) - { - return false; - } - - return IsOwnedByRoot(fileStat) && GroupCannotWrite(fileStat) && - OtherUserCannotWrite(fileStat); - } - - private static bool OtherUserCannotWrite(StatInterop.FileStatus fileStat) - { - return (fileStat.Mode & (int) StatInterop.Permissions.S_IWOTH) == 0; - } - - private static bool GroupCannotWrite(StatInterop.FileStatus fileStat) - { - return (fileStat.Mode & (int) StatInterop.Permissions.S_IWGRP) == 0; - } - - private static bool IsOwnedByRoot(StatInterop.FileStatus fileStat) - { - return fileStat.Uid == 0; - } } } diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs index 1f3a37d9ee0f..f9d20bf96648 100644 --- a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs +++ b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs @@ -11,7 +11,6 @@ using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; -using NuGet.ProjectModel; using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage @@ -46,17 +45,18 @@ public IToolPackage InstallPackage( string rollbackDirectory = null; return TransactionalAction.Run( - action: () => { + action: () => + { try { var stageDirectory = _store.GetRandomStagingDirectory(); Directory.CreateDirectory(stageDirectory.Value); rollbackDirectory = stageDirectory.Value; - var tempProject = CreateTempProject( + string tempProject = CreateDirectoryWithTempProject( packageId: packageId, versionRange: versionRange, - targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, + targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, restoreDirectory: stageDirectory, assetJsonOutputDirectory: stageDirectory, rootConfigDirectory: packageLocation.RootConfigDirectory, @@ -65,13 +65,13 @@ public IToolPackage InstallPackage( try { _projectRestorer.Restore( - tempProject, + new FilePath(tempProject), packageLocation, verbosity: verbosity); } finally { - File.Delete(tempProject.Value); + File.Delete(tempProject); } var version = _store.GetStagedPackageVersion(stageDirectory, packageId); @@ -104,7 +104,8 @@ public IToolPackage InstallPackage( ex); } }, - rollback: () => { + rollback: () => + { if (!string.IsNullOrEmpty(rollbackDirectory) && Directory.Exists(rollbackDirectory)) { Directory.Delete(rollbackDirectory, true); @@ -126,16 +127,13 @@ public IToolPackage InstallPackageToExternalManagedLocation( string targetFramework = null, string verbosity = null) { - var tempDirectoryForAssetJson = new DirectoryPath(Path.GetTempPath()) - .WithSubDirectories(Path.GetRandomFileName()); - - Directory.CreateDirectory(tempDirectoryForAssetJson.Value); + var tempDirectoryForAssetJson = PathUtilities.CreateTempSubdirectory(); - var tempProject = CreateTempProject( + string tempProject = CreateDirectoryWithTempProject( packageId: packageId, versionRange: versionRange, targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, - assetJsonOutputDirectory: tempDirectoryForAssetJson, + assetJsonOutputDirectory: new DirectoryPath(tempDirectoryForAssetJson), restoreDirectory: null, rootConfigDirectory: packageLocation.RootConfigDirectory, additionalFeeds: packageLocation.AdditionalFeeds); @@ -143,19 +141,19 @@ public IToolPackage InstallPackageToExternalManagedLocation( try { _projectRestorer.Restore( - tempProject, + new FilePath(tempProject), packageLocation, verbosity: verbosity); } finally { - File.Delete(tempProject.Value); + File.Delete(tempProject); } - return ToolPackageInstance.CreateFromAssetFile(packageId, tempDirectoryForAssetJson); + return ToolPackageInstance.CreateFromAssetFile(packageId, new DirectoryPath(tempDirectoryForAssetJson)); } - private FilePath CreateTempProject( + private string CreateDirectoryWithTempProject( PackageId packageId, VersionRange versionRange, string targetFramework, @@ -164,16 +162,14 @@ private FilePath CreateTempProject( DirectoryPath? rootConfigDirectory, string[] additionalFeeds) { - var tempProject = _tempProject ?? new DirectoryPath(Path.GetTempPath()) - .WithSubDirectories(Path.GetRandomFileName()) - .WithFile("restore.csproj"); - - if (Path.GetExtension(tempProject.Value) != "csproj") + string tempProject; + if (_tempProject != null && _tempProject.HasValue) { - tempProject = new FilePath(Path.ChangeExtension(tempProject.Value, "csproj")); + tempProject = _tempProject.Value.Value; + Directory.CreateDirectory(Path.GetDirectoryName(tempProject)); } - - Directory.CreateDirectory(tempProject.GetDirectoryPath().Value); + else + tempProject = Path.Combine(PathUtilities.CreateTempSubdirectory(), "restore.csproj"); var tempProjectContent = new XDocument( new XElement("Project", @@ -203,7 +199,7 @@ private FilePath CreateTempProject( new XAttribute("Project", "Sdk.targets"), new XAttribute("Sdk", "Microsoft.NET.Sdk")))); - File.WriteAllText(tempProject.Value, tempProjectContent.ToString()); + File.WriteAllText(tempProject, tempProjectContent.ToString()); return tempProject; } diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs index d8df4fb955aa..52c7432fb321 100644 --- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs +++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs @@ -5,10 +5,8 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli; @@ -17,7 +15,6 @@ using Microsoft.DotNet.Configurer; using Microsoft.DotNet.ToolPackage; using Microsoft.DotNet.Workloads.Workload.Install; -using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord; using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.NET.Sdk.WorkloadManifestReader; using NuGet.Versioning; @@ -79,7 +76,7 @@ public InstallingWorkloadCommand( var sourceOption = parseResult.GetValueForOption(InstallingWorkloadCommandParser.SourceOption); _packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null : new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption); - + var sdkWorkloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _installedSdkVersion.ToString(), userProfileDir); _workloadResolver = workloadResolver ?? WorkloadResolver.Create(sdkWorkloadManifestProvider, _dotnetPath, _installedSdkVersion.ToString(), _userProfileDir); diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs index 7e06aae182b1..0b4c199f1b3e 100644 --- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -72,7 +71,7 @@ public ToolInstallGlobalOrToolPathCommand( _environmentPathInstruction = environmentPathInstruction ?? EnvironmentPathFactory.CreateEnvironmentPathInstruction(); _createShellShimRepository = createShellShimRepository ?? ShellShimRepositoryFactory.CreateShellShimRepository; - var tempDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-tool-install")); + var tempDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory()); var configOption = parseResult.GetValueForOption(ToolInstallCommandParser.ConfigOption); var sourceOption = parseResult.GetValueForOption(ToolInstallCommandParser.AddSourceOption); var packageSourceLocation = new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), additionalSourceFeeds: sourceOption); @@ -143,7 +142,7 @@ public override int Execute() } else { - framework = string.IsNullOrEmpty(_framework) ? + framework = string.IsNullOrEmpty(_framework) ? null : NuGetFramework.Parse(_framework); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs index e0ca3d540524..dc4cd2d3f51d 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; @@ -104,7 +103,7 @@ public WorkloadCommandBase(ParseResult parseResult, ? tempDirPath : !string.IsNullOrWhiteSpace(parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption)) ? parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption) - : Path.GetTempPath(); + : PathUtilities.CreateTempSubdirectory(); TempPackagesDirectory = new DirectoryPath(Path.Combine(TempDirectoryPath, "dotnet-sdk-advertising-temp")); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs index 956ed4bbec1f..245c526076f6 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs @@ -19,6 +19,7 @@ using System.Text.Json; using System.Threading.Tasks; + namespace Microsoft.DotNet.Workloads.Workload.Install { internal class FileBasedInstaller : IInstaller @@ -51,7 +52,7 @@ public FileBasedInstaller(IReporter reporter, { _userProfileDir = userProfileDir; _dotnetDir = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _tempPackagesDir = new DirectoryPath(tempDirPath ?? Path.GetTempPath()); + _tempPackagesDir = new DirectoryPath(tempDirPath ?? PathUtilities.CreateTempSubdirectory()); ILogger logger = verbosity.IsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger(); _restoreActionConfig = restoreActionConfig; _nugetPackageDownloader = nugetPackageDownloader ?? @@ -188,7 +189,7 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand Directory.Delete(dir, true); } } - }); + }); } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs index ed04a4548648..368d4716b184 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -116,7 +115,7 @@ public void GarbageCollectInstalledWorkloadPacks(DirectoryPath? offlineCache = n Log?.LogMessage("Starting garbage collection."); IEnumerable installedFeatureBands = GetInstalledFeatureBands(); IEnumerable installedWorkloads = RecordRepository.GetInstalledWorkloads(_sdkFeatureBand); - Dictionary<(WorkloadPackId id, string version),PackInfo> expectedWorkloadPacks = installedWorkloads + Dictionary<(WorkloadPackId id, string version), PackInfo> expectedWorkloadPacks = installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload)) .Distinct() .Select(pack => _workloadResolver.TryGetPackInfo(pack)) @@ -415,9 +414,9 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand RollBackMsiInstall(msiToInstall); } }); - + } - + } void RollBackMsiInstall(WorkloadDownload msiToRollback, DirectoryPath? offlineCache = null) @@ -492,17 +491,10 @@ public PackageId GetManifestPackageId(ManifestId manifestId, SdkFeatureBand feat public async Task ExtractManifestAsync(string nupkgPath, string targetPath) { Log?.LogMessage($"ExtractManifestAsync: Extracting '{nupkgPath}' to '{targetPath}'"); - - string extractionPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - if (Directory.Exists(extractionPath)) - { - Directory.Delete(extractionPath, true); - } + string extractionPath = PathUtilities.CreateTempSubdirectory(); try { - Directory.CreateDirectory(extractionPath); - Log?.LogMessage($"ExtractManifestAsync: Temporary extraction path: '{extractionPath}'"); await _nugetPackageDownloader.ExtractPackageAsync(nupkgPath, new DirectoryPath(extractionPath)); if (Directory.Exists(targetPath)) @@ -959,7 +951,7 @@ public static NetSdkMsiInstallerClient Create( if (nugetPackageDownloader == null) { - DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? Path.GetTempPath() : tempDirPath); + DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? PathUtilities.CreateTempSubdirectory() : tempDirPath); nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(), diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs index 048d3dc22889..36c42b47cee4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Net.Http; -using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; using Microsoft.DotNet.Cli; @@ -69,7 +68,7 @@ private static WorkloadManifestUpdater GetInstance(string userProfileDir) var sdkVersion = Product.Version; var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, sdkVersion, userProfileDir); var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetPath, sdkVersion, userProfileDir); - var tempPackagesDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-sdk-advertising-temp")); + var tempPackagesDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory()); var nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(), @@ -189,9 +188,9 @@ Dictionary Workloads } if (advertisingManifestVersionAndWorkloads != null && - ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0 + ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0 && advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.Equals(currentManifestVersion.sdkFeatureBand)) || - advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0)) + advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0)) { manifestUpdates.Add((new ManifestVersionUpdate(manifestId, currentManifestVersion.manifestVersion, currentManifestVersion.sdkFeatureBand.ToString(), advertisingManifestVersionAndWorkloads.Value.ManifestVersion, advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.ToString()), @@ -262,7 +261,7 @@ public async Task> GetManifestPackageDownloadsAsyn var newPackageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), installedSdkFeatureBand); (success, latestVersion) = await GetPackageVersion(newPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews); - + if (success) { downloads.Add(new WorkloadDownload(manifest.Id, newPackageId.ToString(), latestVersion.ToString())); @@ -307,7 +306,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, try { var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId); - + bool success; (success, packagePath) = await GetManifestPackageUpdate(_sdkFeatureBand, manifestId, includePreviews, offlineCache); if (!success) @@ -323,7 +322,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, _reporter.WriteLine(string.Format(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId)); return; } - + await _workloadManifestInstaller.ExtractManifestAsync(packagePath, adManifestPath); // add file that contains the advertisted manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not @@ -384,7 +383,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, { adManifestFeatureBand = new SdkFeatureBand(File.ReadAllText(adManifestFeatureBandPath)); } - + return (new ManifestVersion(manifest.Version), adManifestFeatureBand, manifest.Workloads.Values.OfType().ToDictionary(w => w.Id)); } @@ -470,12 +469,12 @@ private async Task NewerManifestPackageExists(ManifestId manifest) ManifestVersion manifestVersion; SdkFeatureBand manifestFeatureBand; var parts = manifest.Value.Split('/'); - + string manifestVersionString = (parts[0]); if (!FXVersion.TryParse(manifestVersionString, out FXVersion version)) { throw new FormatException(String.Format(LocalizableStrings.InvalidVersionForWorkload, manifest.Key, manifestVersionString)); - } + } manifestVersion = new ManifestVersion(parts[0]); if (parts.Length == 1) @@ -499,15 +498,15 @@ private bool BackgroundUpdatesAreDisabled() => private static string GetAdvertisingWorkloadsFilePath(string userProfileDir, SdkFeatureBand featureBand) => Path.Combine(userProfileDir, $".workloadAdvertisingUpdates{featureBand}"); - private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews) - { - string packagePath = await _nugetPackageDownloader.DownloadPackageAsync( - _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand), - packageSourceLocation: _packageSourceLocation, - includePreview: includePreviews); - - return packagePath; - } + private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews) + { + string packagePath = await _nugetPackageDownloader.DownloadPackageAsync( + _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand), + packageSourceLocation: _packageSourceLocation, + includePreview: includePreviews); + + return packagePath; + } private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, DirectoryPath? offlineCache = null) { @@ -527,7 +526,7 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m { if (offlineCache == null || !offlineCache.HasValue) { - try + try { string packagePath = await GetOnlinePackagePath(sdkFeatureBand, manifestId, includePreviews); return (true, packagePath); @@ -557,9 +556,9 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m } } - -private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => - Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString()); + + private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => + Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString()); } } diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj index 3c207d9c173d..849ad24e8c65 100644 --- a/src/Cli/dotnet/dotnet.csproj +++ b/src/Cli/dotnet/dotnet.csproj @@ -95,8 +95,7 @@ - + diff --git a/src/Common/PathUtilities.cs b/src/Common/PathUtilities.cs new file mode 100644 index 000000000000..dad9351b1ede --- /dev/null +++ b/src/Common/PathUtilities.cs @@ -0,0 +1,49 @@ +// 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.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.DotNet; + +static class PathUtilities +{ + const int S_IRUSR = 256; + const int S_IWUSR = 128; + const int S_IXUSR = 64; + const int S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; // 700 (octal) Permissions + + const int MAX_NUM_DIRECTORY_CREATE_RETRIES = 2; + + public static string CreateTempSubdirectory() + { + return CreateTempSubdirectoryRetry(0); + } + + [DllImport("libc", SetLastError = true)] + private static extern int mkdir(string pathname, int mode); + private static string CreateTempSubdirectoryRetry(int attemptNo) + { + string path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + int mkdirStatusCode = mkdir(path, S_IRWXU); + if (mkdirStatusCode != 0) + { + int errno = Marshal.GetLastWin32Error(); + if (Directory.Exists(path) && attemptNo < MAX_NUM_DIRECTORY_CREATE_RETRIES) + { + return CreateTempSubdirectoryRetry(attemptNo + 1); + } + else + throw new IOException($"Failed to create a temporary subdirectory {path} with mkdir, error code: {errno}"); + } + } + else + { + Directory.CreateDirectory(path); + } + return path; + } +} diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 5686a4a25ba4..5cd254c1e71f 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -14,6 +14,9 @@ // Work around https://github.com/dotnet/roslyn-analyzers/issues/6094 #pragma warning disable CA1420 +// Work around https://github.com/dotnet/roslyn-analyzers/issues/6094 +#pragma warning disable CA1420 + namespace Microsoft.DotNet.NativeWrapper { public static partial class Interop diff --git a/src/Tasks/Common/FileUtilities.cs b/src/Tasks/Common/FileUtilities.cs index 92375d98eeba..76f79690f00b 100644 --- a/src/Tasks/Common/FileUtilities.cs +++ b/src/Tasks/Common/FileUtilities.cs @@ -37,6 +37,5 @@ public static Version TryGetAssemblyVersion(string sourcePath) return s_assemblyExtensions.Contains(extension) ? GetAssemblyVersion(sourcePath) : null; } - } } diff --git a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs index 843f882ffdb6..31cbcfe77881 100644 --- a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs +++ b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs @@ -26,7 +26,7 @@ public GivenAFirstTimeUseNoticeSentinel() _fileSystemMockBuilder = FileSystemMockBuilder.Create(); } - [Fact(Skip ="Product.Version not set correctly when running tests")] + [Fact(Skip = "Product.Version not set correctly when running tests")] public void TheSentinelHasTheCurrentVersionInItsName() { FirstTimeUseNoticeSentinel.SENTINEL.Should().Contain($"{Product.Version}"); @@ -174,6 +174,11 @@ public ITemporaryDirectory CreateTemporaryDirectory() throw new NotImplementedException(); } + public string CreateTemporarySubdirectory() + { + throw new NotImplementedException(); + } + public IEnumerable EnumerateDirectories(string path) { throw new NotImplementedException(); diff --git a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs index 10e3f1930814..6802977214b2 100644 --- a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs +++ b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs @@ -173,6 +173,11 @@ public IEnumerable EnumerateFileSystemEntries(string path) throw new UnauthorizedAccessException(); } + public string CreateTemporarySubdirectory() + { + throw new NotImplementedException(); + } + public string GetCurrentDirectory() { throw new NotImplementedException(); diff --git a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs index aeba497e7ff5..db570d99bc2a 100644 --- a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs +++ b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs @@ -1059,7 +1059,7 @@ private static string GetTestLocalFeedPath() => private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker(); private const string TestPackageVersion = "1.0.4"; private static readonly PackageId TestPackageId = new PackageId("global.tool.console.demo"); - private static readonly IEnumerable TestFrameworks = new NuGetFramework[] { NuGetFramework.Parse("netcoreapp2.1")}; + private static readonly IEnumerable TestFrameworks = new NuGetFramework[] { NuGetFramework.Parse("netcoreapp2.1") }; public ToolPackageInstallerTests(ITestOutputHelper log) : base(log) { diff --git a/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs b/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs index 7eb9e5381989..0c47207c56f5 100644 --- a/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs +++ b/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs @@ -154,7 +154,7 @@ public void CreateDirectory(string path) else { DirectoryNode directoryNode = new DirectoryNode(); - directoryNode = (DirectoryNode) current.Subs.GetOrAdd(p , directoryNode); + directoryNode = (DirectoryNode)current.Subs.GetOrAdd(p, directoryNode); current = directoryNode; } } @@ -237,7 +237,7 @@ public DirectoryNode GetParentOfDirectoryNode(string path) } PathModel pathModel = CreateFullPathModel(path); - if (current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) ) + if (current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node)) { if (node is FileNode) { @@ -283,7 +283,7 @@ public PathModel(string path) } string[] pathArray = path.Split( - new[] {directorySeparatorChar, altDirectorySeparatorChar}, + new[] { directorySeparatorChar, altDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); Volume = volume; PathArray = pathArray; @@ -451,7 +451,7 @@ public void Move(string source, string destination) if (_files.TryGetNodeParent(destination, out DirectoryNode current) && current != null) { - sourceFileNode = (FileNode) current.Subs.GetOrAdd(new PathModel(destination).FileOrDirectoryName(), sourceFileNode); + sourceFileNode = (FileNode)current.Subs.GetOrAdd(new PathModel(destination).FileOrDirectoryName(), sourceFileNode); sourceParent.Subs.TryRemove(new PathModel(source).FileOrDirectoryName(), out _); } else @@ -525,9 +525,9 @@ public bool Exists(string path) if (_files.TryGetNodeParent(path, out DirectoryNode current)) { - PathModel pathModel = new PathModel(path); + PathModel pathModel = new PathModel(path); - return current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) + return current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) && node is DirectoryNode; } @@ -579,6 +579,11 @@ public void CreateDirectory(string path) _files.CreateDirectory(path); } + public string CreateTemporarySubdirectory() + { + return CreateTemporaryDirectory().DirectoryPath; + } + public void Delete(string path, bool recursive) { if (path == null) throw new ArgumentNullException(nameof(path));