Skip to content

Commit

Permalink
Merged PR 461827: Create user specific NuGetScratch on Linux
Browse files Browse the repository at this point in the history
Create user specific NuGetScratch folder on Linux, also changed naming to append username on Linux platform.
  • Loading branch information
heng-liu committed Apr 4, 2023
1 parent dca9c3c commit 7fe6b81
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 35 deletions.
74 changes: 40 additions & 34 deletions src/NuGet.Core/NuGet.Common/PathUtil/NuGetEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,43 @@ public static class NuGetEnvironment

private static readonly Lazy<string> _getHome = new Lazy<string>(() => GetHome());

private static string _nuGetTempDirectory = null;
internal static string NuGetTempDirectory
{
get { return _nuGetTempDirectory ??= GetNuGetTempDirectory(); }
}

private static string GetNuGetTempDirectory()
{
var nuGetScratch = Environment.GetEnvironmentVariable("NUGET_SCRATCH");
if (string.IsNullOrEmpty(nuGetScratch))
{
#pragma warning disable RS0030 // Do not used banned APIs
// This is the only place in the product code we can use GetTempPath().
var tempPath = Path.GetTempPath();
#pragma warning restore RS0030 // Do not used banned APIs

// On Windows and Mac the temp directories are per-user, but on Linux it's /tmp for everyone, so append the username on Linux.
nuGetScratch = Path.Combine(tempPath,
RuntimeEnvironmentHelper.IsLinux ? "NuGetScratch" + Environment.UserName : "NuGetScratch");

if (RuntimeEnvironmentHelper.IsLinux)
{
Directory.CreateDirectory(nuGetScratch);
if (chmod(nuGetScratch, 0b111_000_000) != 0) //0b111_000_000 = 700 permissions
{
// Another user created a folder pretending to be us!
var errno = Marshal.GetLastWin32Error(); // fetch the errno before running any other operation
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
Strings.UnableToSetNuGetTempFolderPermission,
nuGetScratch,
errno));
}
}
}
return nuGetScratch;
}

public static string GetFolderPath(NuGetFolderPath folder)
{
switch (folder)
Expand Down Expand Up @@ -101,50 +138,19 @@ public static string GetFolderPath(NuGetFolderPath folder)

case NuGetFolderPath.Temp:
{
var nuGetScratch = Environment.GetEnvironmentVariable("NUGET_SCRATCH");
if (string.IsNullOrEmpty(nuGetScratch))
{
#pragma warning disable RS0030 // Do not used banned APIs
// This is the only place in the product code we can use GetTempPath().
var tempPath = Path.GetTempPath();
#pragma warning restore RS0030 // Do not used banned APIs
nuGetScratch = Path.Combine(tempPath, "NuGetScratch");

// On Windows and Mac the temp directories are per-user, but on Linux it's /tmp for everyone
if (RuntimeEnvironmentHelper.IsLinux)
{
// ConcurrencyUtility uses the lock subdirectory, so make sure it exists, and create with world write
string lockPath = Path.Combine(nuGetScratch, "lock");
if (!Directory.Exists(lockPath))
{
void CreateSharedDirectory(string path)
{
Directory.CreateDirectory(path);
if (chmod(path, 0x1ff) == -1) // 0x1ff == 777 permissions
{
// it's very unlikely we can't set the permissions of a directory we just created
var errno = Marshal.GetLastWin32Error(); // fetch the errno before running any other operation
throw new InvalidOperationException($"Unable to set permission while creating {path}, errno={errno}.");
}
}

CreateSharedDirectory(nuGetScratch);
CreateSharedDirectory(lockPath);
}
}
}
return nuGetScratch;
return NuGetTempDirectory;
}

default:
return null;
}
}

/// <summary>Only to be used for creating directories under /tmp on Linux. Do not use elsewhere.</summary>
/// <summary>Only to be used for setting permissions of directories under /tmp on Linux. Do not use elsewhere.</summary>
[DllImport("libc", SetLastError = true, CharSet = CharSet.Ansi)]
private static extern int chmod(string pathname, int mode);


#if IS_CORECLR

private static string GetFolderPath(SpecialFolder folder)
Expand Down
9 changes: 9 additions & 0 deletions src/NuGet.Core/NuGet.Common/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/NuGet.Core/NuGet.Common/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@
<data name="UnableToDetemineClientVersion" xml:space="preserve">
<value>Unable to determine the current NuGet client version.</value>
</data>
<data name="UnableToSetNuGetTempFolderPermission" xml:space="preserve">
<value>User is unable to set permission to 700 while creating {0}, errno={1}.</value>
<comment>{0} is a file path; {1} is an error code.</comment>
</data>
<data name="UnauthorizedLockFail" xml:space="preserve">
<value>Unable to obtain lock file access on '{0}' for operations on '{1}'. This may mean that a different user or administrator is holding this lock and that this process does not have permission to access it. If no other process is currently performing an operation on this file it may mean that an earlier NuGet process crashed and left an inaccessible lock file, in this case removing the file '{0}' will allow NuGet to continue.</value>
<comment>{0} and {1} are both file paths.</comment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ public static void Locals_Clear_Succeeds(string args)
var mockHttpCacheDirectory = Directory.CreateDirectory(Path.Combine(mockBaseDirectory.Path, @"http-cache"));
var mockTmpDirectory = Directory.CreateDirectory(Path.Combine(mockBaseDirectory.Path, @"temp"));
var mockPluginsCacheDirectory = Directory.CreateDirectory(Path.Combine(mockBaseDirectory.Path, @"plugins-cache"));
var mockTmpCacheDirectory = Directory.CreateDirectory(Path.Combine(mockTmpDirectory.FullName, @"NuGetScratch"));
var mockTmpCacheDirectory = Directory.CreateDirectory(Path.Combine(mockTmpDirectory.FullName,
RuntimeEnvironmentHelper.IsLinux ? "NuGetScratch" + Environment.UserName : "NuGetScratch"));

DotnetCliUtil.CreateTestFiles(mockGlobalPackagesDirectory.FullName);
DotnetCliUtil.CreateTestFiles(mockHttpCacheDirectory.FullName);
Expand Down
24 changes: 24 additions & 0 deletions test/NuGet.Core.Tests/NuGet.Common.Test/NuGetEnvironmentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NuGet.Common.Migrations;
using NuGet.Test.Utility;
using Xunit;

namespace NuGet.Common.Test
{
public class NuGetEnvironmentTests
{
[PlatformFact(Platform.Linux)]
public void GetFolderPath_Temp_Success()
{
var nuGetTempDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.Temp);
Assert.Equal("700", Migration1.GetPermissions(nuGetTempDirectory).ToString());
}
}
}

0 comments on commit 7fe6b81

Please sign in to comment.