Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add special handeling of too long path's #692

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line

#region No ReShaper

using System;
using System.Security.Cryptography;
using System.Text;
using JetBrains.Annotations;

// ReSharper disable All
// needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C#
using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;

// ReSharper restore All

#endregion

#pragma warning restore SA1512,SA1124 // Single-line comments should not be followed by blank line

namespace NugetForUnity.Helper
{
/// <summary>
/// Helper class for MD5 hashing.
/// </summary>
internal static class Md5HashHelper
{
/// <summary>
/// Computes the MD5 hash of <paramref name="value" /> and returns it as Base64 string with all chars that are not allowed on file-paths replaced.
/// </summary>
/// <param name="value">The string that is hashed.</param>
/// <returns>The MD5 has of <paramref name="value" /> as a Base64 string with all chars that are not allowed on file-paths replaced.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="value" /> is null or a empty string.</exception>
[SuppressMessage("Design", "CA5351", Justification = "Only use MD5 hash as cache key / not security relevant.")]
[NotNull]
public static string GetFileNameSafeHash([NotNull] string value)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException(nameof(value));
}

using (var md5 = new MD5CryptoServiceProvider())

Check warning on line 42 in src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs

View workflow job for this annotation

GitHub Actions / Pack .NET Core Global Tool (CLI) and PluginAPI

'MD5CryptoServiceProvider' is obsolete: 'Derived cryptographic types are obsolete. Use the Create method on the base type instead.' (https://aka.ms/dotnet-warnings/SYSLIB0021)

Check warning on line 42 in src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs

View workflow job for this annotation

GitHub Actions / Pack .NET Core Global Tool (CLI) and PluginAPI

'MD5CryptoServiceProvider' is obsolete: 'Derived cryptographic types are obsolete. Use the Create method on the base type instead.' (https://aka.ms/dotnet-warnings/SYSLIB0021)

Check warning on line 42 in src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs

View workflow job for this annotation

GitHub Actions / Pack .NET Core Global Tool (CLI) and PluginAPI

'MD5CryptoServiceProvider' is obsolete: 'Derived cryptographic types are obsolete. Use the Create method on the base type instead.' (https://aka.ms/dotnet-warnings/SYSLIB0021)
{
var data = md5.ComputeHash(Encoding.Default.GetBytes(value));
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
}
}
}
11 changes: 11 additions & 0 deletions src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs.meta

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

36 changes: 2 additions & 34 deletions src/NuGetForUnity/Editor/Helper/NugetPackageTextureHelper.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
#pragma warning disable SA1512,SA1124 // Single-line comments should not be followed by blank line

#if UNITY_2022_1_OR_NEWER
#if UNITY_2022_1_OR_NEWER
using UnityEditor;
#endif

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Networking;

#region No ReShaper

// ReSharper disable All
// needed because 'JetBrains.Annotations.NotNull' and 'System.Diagnostics.CodeAnalysis.NotNull' collide if this file is compiled with a never version of Unity / C#
using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;

// ReSharper restore All

#endregion

#pragma warning restore SA1512,SA1124 // Single-line comments should not be followed by blank line

namespace NugetForUnity.Helper
{
/// <summary>
Expand Down Expand Up @@ -129,23 +113,7 @@ private static void CacheTextureOnDisk([NotNull] string cacheFilePath, [NotNull]
[NotNull]
private static string GetCacheFilePath([NotNull] string url)
{
return Path.Combine(Application.temporaryCachePath, GetHash(url));
}

[SuppressMessage("Design", "CA5351", Justification = "Only use MD5 hash as cache key / not security relevant.")]
[NotNull]
private static string GetHash([NotNull] string s)
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentNullException(nameof(s));
}

using (var md5 = new MD5CryptoServiceProvider())
{
var data = md5.ComputeHash(Encoding.Default.GetBytes(s));
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
return Path.Combine(Application.temporaryCachePath, Md5HashHelper.GetFileNameSafeHash(url));
}
}
}
50 changes: 44 additions & 6 deletions src/NuGetForUnity/Editor/PackageContentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace NugetForUnity
/// </summary>
internal static class PackageContentManager
{
private const int MaxPathLength = 260;

/// <summary>
/// Deletes all files and folders associated with a package.
/// </summary>
Expand Down Expand Up @@ -316,15 +318,31 @@ internal static string ExtractPackageEntry([NotNull] ZipArchiveEntry entry, [Not
return null;
}

var directory = Path.GetDirectoryName(filePath) ?? throw new InvalidOperationException($"Failed to get directory name of '{filePath}'");
Directory.CreateDirectory(directory);
if (Directory.Exists(filePath))
try
{
Debug.LogWarning($"The path {filePath} refers to an existing directory. Overwriting it may lead to data loss.");
return null;
if (Directory.Exists(filePath))
{
Debug.LogWarning($"The path {filePath} refers to an existing directory. Overwriting it may lead to data loss.");
return null;
}

var directory = Path.GetDirectoryName(filePath) ??
throw new InvalidOperationException($"Failed to get directory name of '{filePath}'");
Directory.CreateDirectory(directory);

entry.ExtractToFile(filePath, true);
}
catch (Exception exception) when (exception is PathTooLongException ||
(exception is DirectoryNotFoundException && filePath.Length >= MaxPathLength))
{
// path is to long (normally on windows) -> try to use shorter path
// we only do this when we get a exception because it can be that we are on a system that supports longer paths
var longFilePath = filePath;
filePath = DetermineShorterFilePath(entry, baseDir);

entry.ExtractToFile(filePath, true);
NugetLogger.LogVerbose("The target file path '{0}' was to long -> we shortened it to '{1}'.", longFilePath, filePath);
entry.ExtractToFile(filePath, true);
}

if (filePath.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) && !PortableSymbolFileHelper.IsPortableSymbolFile(filePath))
{
Expand Down Expand Up @@ -402,6 +420,26 @@ internal static void MoveInstalledPackages(string oldPath, string newPath)
}
}

private static string DetermineShorterFilePath(ZipArchiveEntry entry, string baseDir)
{
var filePath = Path.GetFullPath(Path.Combine(baseDir, entry.Name));
if (filePath.Length < MaxPathLength && !File.Exists(filePath))
{
// placing the file in the base-directory is enough to make the path usable.
return filePath;
}

filePath = Path.GetFullPath(Path.Combine(baseDir, Md5HashHelper.GetFileNameSafeHash(entry.FullName)));
if (filePath.Length + entry.Name.Length + 1 < MaxPathLength)
{
// we have enough space to keep the file name
return $"{filePath}_{entry.Name}";
}

// only add the file extension
return filePath + Path.GetExtension(entry.Name);
}

[NotNull]
private static string GetPackageOutsideInstallDirectory([NotNull] INugetPackageIdentifier package)
{
Expand Down
22 changes: 11 additions & 11 deletions src/NuGetForUnity/Editor/PackageSource/NugetPackageSourceV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,17 @@ public void OnAfterDeserialize()
}
}

private static void CopyIsManuallyInstalled(List<INugetPackage> newPackages, ICollection<INugetPackage> packagesToUpdate)
{
foreach (var newPackage in newPackages)
{
newPackage.IsManuallyInstalled =
packagesToUpdate.FirstOrDefault(packageToUpdate => packageToUpdate.Id.Equals(newPackage.Id, StringComparison.OrdinalIgnoreCase))
?.IsManuallyInstalled ??
false;
}
}

/// <summary>
/// Builds a list of NugetPackages from the XML returned from the HTTP GET request issued at the given URL.
/// Note that NuGet uses an Atom-feed (XML Syndicaton) superset called OData.
Expand Down Expand Up @@ -519,16 +530,5 @@ private List<INugetPackage> GetUpdatesFallback(
NugetLogger.LogVerbose("NugetPackageSource.GetUpdatesFallback took {0} ms", stopwatch.ElapsedMilliseconds);
return updates;
}

private static void CopyIsManuallyInstalled(List<INugetPackage> newPackages, ICollection<INugetPackage> packagesToUpdate)
{
foreach (var newPackage in newPackages)
{
newPackage.IsManuallyInstalled =
packagesToUpdate.FirstOrDefault(packageToUpdate => packageToUpdate.Id.Equals(newPackage.Id, StringComparison.OrdinalIgnoreCase))
?.IsManuallyInstalled ??
false;
}
}
}
}
Loading