From 703bdb6d51ca9f840cab1d7b68c9ceb3c3d07733 Mon Sep 17 00:00:00 2001 From: JoC0de <53140583+JoC0de@users.noreply.github.com> Date: Sun, 5 Jan 2025 23:30:16 +0100 Subject: [PATCH] add special handeling of too long path's --- .../Editor/Helper/Md5HashHelper.cs | 49 ++++++++++++++++++ .../Editor/Helper/Md5HashHelper.cs.meta | 11 ++++ .../Helper/NugetPackageTextureHelper.cs | 36 +------------ .../Editor/PackageContentManager.cs | 50 ++++++++++++++++--- .../PackageSource/NugetPackageSourceV2.cs | 22 ++++---- 5 files changed, 117 insertions(+), 51 deletions(-) create mode 100644 src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs create mode 100644 src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs.meta diff --git a/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs b/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs new file mode 100644 index 00000000..472fea41 --- /dev/null +++ b/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs @@ -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 +{ + /// + /// Helper class for MD5 hashing. + /// + internal static class Md5HashHelper + { + /// + /// Computes the MD5 hash of and returns it as Base64 string with all chars that are not allowed on file-paths replaced. + /// + /// The string that is hashed. + /// The MD5 has of as a Base64 string with all chars that are not allowed on file-paths replaced. + /// If is null or a empty string. + [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()) + { + var data = md5.ComputeHash(Encoding.Default.GetBytes(value)); + return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_').TrimEnd('='); + } + } + } +} diff --git a/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs.meta b/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs.meta new file mode 100644 index 00000000..86ac4927 --- /dev/null +++ b/src/NuGetForUnity/Editor/Helper/Md5HashHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc68e365bd39a6943bfca8c9c13c4321 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/NuGetForUnity/Editor/Helper/NugetPackageTextureHelper.cs b/src/NuGetForUnity/Editor/Helper/NugetPackageTextureHelper.cs index fad02bfc..3bcabbeb 100644 --- a/src/NuGetForUnity/Editor/Helper/NugetPackageTextureHelper.cs +++ b/src/NuGetForUnity/Editor/Helper/NugetPackageTextureHelper.cs @@ -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 { /// @@ -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)); } } } diff --git a/src/NuGetForUnity/Editor/PackageContentManager.cs b/src/NuGetForUnity/Editor/PackageContentManager.cs index c59e99f4..3c8cf633 100644 --- a/src/NuGetForUnity/Editor/PackageContentManager.cs +++ b/src/NuGetForUnity/Editor/PackageContentManager.cs @@ -17,6 +17,8 @@ namespace NugetForUnity /// internal static class PackageContentManager { + private const int MaxPathLength = 260; + /// /// Deletes all files and folders associated with a package. /// @@ -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)) { @@ -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) { diff --git a/src/NuGetForUnity/Editor/PackageSource/NugetPackageSourceV2.cs b/src/NuGetForUnity/Editor/PackageSource/NugetPackageSourceV2.cs index 36aee09d..f314e22e 100644 --- a/src/NuGetForUnity/Editor/PackageSource/NugetPackageSourceV2.cs +++ b/src/NuGetForUnity/Editor/PackageSource/NugetPackageSourceV2.cs @@ -443,6 +443,17 @@ public void OnAfterDeserialize() } } + private static void CopyIsManuallyInstalled(List newPackages, ICollection packagesToUpdate) + { + foreach (var newPackage in newPackages) + { + newPackage.IsManuallyInstalled = + packagesToUpdate.FirstOrDefault(packageToUpdate => packageToUpdate.Id.Equals(newPackage.Id, StringComparison.OrdinalIgnoreCase)) + ?.IsManuallyInstalled ?? + false; + } + } + /// /// 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. @@ -519,16 +530,5 @@ private List GetUpdatesFallback( NugetLogger.LogVerbose("NugetPackageSource.GetUpdatesFallback took {0} ms", stopwatch.ElapsedMilliseconds); return updates; } - - private static void CopyIsManuallyInstalled(List newPackages, ICollection packagesToUpdate) - { - foreach (var newPackage in newPackages) - { - newPackage.IsManuallyInstalled = - packagesToUpdate.FirstOrDefault(packageToUpdate => packageToUpdate.Id.Equals(newPackage.Id, StringComparison.OrdinalIgnoreCase)) - ?.IsManuallyInstalled ?? - false; - } - } } }