龙腾猫跃 5 luni în urmă
părinte
comite
8c961fd64d

+ 2 - 2
Shared/Utils/DirectoryUtils.cs

@@ -39,7 +39,7 @@ public static class DirectoryUtils {
     public static IEnumerable<string> GetFiles(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
         if (!DirectoryUtils.Exists(path)) return [];
         return Directory.EnumerateFiles(PathUtils.ForApi(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
-            Select(PathUtils.WithoutLongPath);
+            Select(PathUtils.RemoveExtendedPrefix);
     }
 
     /// <summary>
@@ -49,7 +49,7 @@ public static class DirectoryUtils {
     public static IEnumerable<string> GetDirectories(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
         if (!DirectoryUtils.Exists(path)) return [];
         return Directory.EnumerateDirectories(PathUtils.ForApi(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
-            Select(PathUtils.WithoutLongPath);
+            Select(PathUtils.RemoveExtendedPrefix);
     }
 
     /// <summary>

+ 2 - 2
Shared/Utils/FileUtils.cs

@@ -197,7 +197,7 @@ public static class FileUtils {
             IFileOperation? op = null;
             try {
                 var iid = typeof(IShellItem).GUID;
-                Marshal.ThrowExceptionForHR(SHCreateItemFromParsingName(PathUtils.WithoutLongPath(target), IntPtr.Zero, ref iid, out item));
+                Marshal.ThrowExceptionForHR(SHCreateItemFromParsingName(PathUtils.RemoveExtendedPrefix(target), IntPtr.Zero, ref iid, out item));
                 op = (IFileOperation) new FileOperation();
                 op.SetOperationFlags(0x0040 | 0x0010 | 0x0004); // FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT
                 op.DeleteItem(item, IntPtr.Zero);
@@ -318,7 +318,7 @@ public static class FileUtils {
             if (string.IsNullOrEmpty(entry.Name)) continue; // 跳过文件夹条目(ZipArchive 会将文件夹也作为一个 entry,但它们的 Name 为空)
             // ZipSlip 修复
             string outputFilePath = PathUtils.ForCompare(Path.Combine(outputDirectory, entry.FullName));
-            if (!outputFilePath.StartsWithF(PathUtils.WithSeparator(PathUtils.ForCompare(outputDirectory)), ignoreCase:true))
+            if (!outputFilePath.StartsWithF(PathUtils.AddSlashSuffix(PathUtils.ForCompare(outputDirectory)), ignoreCase:true))
                 throw new ZipSlipException($"Zip 文件项 {entry.FullName} 的路径在压缩包之外,这可能导致安全问题");
             // 实际的解压
             using var entryStream = entry.Open();

+ 19 - 22
Shared/Utils/PathUtils.cs

@@ -6,13 +6,13 @@ public static class PathUtils {
     /// <summary>
     /// 确保路径的末尾包含任意文件夹分隔符。
     /// </summary>
-    public static string WithSeparator(string folder) 
+    public static string AddSlashSuffix(string folder) 
         => folder + (folder.EndsWithF(Path.DirectorySeparatorChar) || folder.EndsWithF(Path.AltDirectorySeparatorChar) ? "" : Path.DirectorySeparatorChar);
 
     /// <summary>
     /// 确保路径的结尾不包含文件夹分隔符。
     /// </summary>
-    public static string WithoutSeparator(string folder) 
+    public static string RemoveSlashSuffix(string folder) 
         => folder.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
 
     #endregion
@@ -28,7 +28,6 @@ public static class PathUtils {
         fullName = PathUtils.ForCompare(fullName);
         if (!fullName.Contains(":")) return fullName;
         if (fullName.Length <= 200) return fullName;
-
         // 保留文件名
         string pathToKeep = "";
         string pathToShorten = fullName;
@@ -37,7 +36,6 @@ public static class PathUtils {
             pathToKeep = PathUtils.GetLastPart(fullName);
             pathToShorten = PathUtils.RemoveLastPart(fullName);
         }
-
         // 逐级向上寻找已存在的文件夹,将不存在的部分挪到 suffix,不再缩短
         while (!DirectoryUtils.Exists(pathToShorten) && !FileUtils.Exists(pathToShorten)) { // 如果路径不存在
             string parentPath = Path.GetDirectoryName(pathToShorten);
@@ -46,25 +44,24 @@ public static class PathUtils {
             pathToShorten = parentPath;
         }
         if (pathToShorten.Length <= 10) return fullName;
-
-        // 缩短路径
+        // 实际缩短路径
         char[] buffer = new char[260];
         int result = GetShortPathNameW(PathUtils.ForApi(pathToShorten), buffer, buffer.Length);
         if (result == 0) return fullName;
         string shortPath = new(buffer, 0, result);
-        return PathUtils.WithoutSeparator(PathUtils.WithoutLongPath(Path.Combine(shortPath, pathToKeep)));
+        return PathUtils.RemoveSlashSuffix(PathUtils.RemoveExtendedPrefix(Path.Combine(shortPath, pathToKeep)));
     }
     [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
     private static extern int GetShortPathNameW(string lpszLongPath, [Out] char[] buffer, int cchBuffer);
 
     /// <summary>
-    /// 将完整路径转换为以 <c>\\?\</c> 开头的标准长路径格式。
-    /// 这会去除路径末尾的分隔符,且将 / 替换为 \。
+    /// 将完整路径转换为以 <c>\\?\</c> 开头的扩展格式。
+    /// 这会去除路径末尾的分隔符,将 / 替换为 \。
     /// </summary>
-    public static string WithLongPath(string path) {
+    public static string ToExtendedFormat(string path) {
         if (string.IsNullOrWhiteSpace(path)) return path;
         if (path.StartsWithF(@"\\?\")) return path; // 已经是长路径
-        path = WithoutSeparator(path).Replace('/', '\\'); // API 要求这个格式……
+        path = RemoveSlashSuffix(path).Replace('/', '\\'); // API 要求这个格式……
         if (path.StartsWithF(@"\\")) {
             return $@"\\?\UNC\{path.Substring(2)}";
         } else {
@@ -75,7 +72,7 @@ public static class PathUtils {
     /// <summary>
     /// 去除路径开头的 <c>\\?\</c>。
     /// </summary>
-    public static string WithoutLongPath(string path) {
+    public static string RemoveExtendedPrefix(string path) {
         if (path.StartsWithF(@"\\?\UNC\")) return @"\\" + path.AfterLast(@"\\?\UNC\");
         if (path.StartsWithF(@"\\?\")) return path.AfterLast(@"\\?\");
         return path;
@@ -96,10 +93,10 @@ public static class PathUtils {
     /// </code></summary>
     public static string RemoveLastPart(string path) {
         if (path!.Contains("://")) { // 网络路径
-            path = PathUtils.WithoutSeparator(path.BeforeFirst("#").BeforeFirst("?")); // 去除参数
-            return PathUtils.WithoutSeparator(path).BeforeLast("/");
+            path = PathUtils.RemoveSlashSuffix(path.BeforeFirst("#").BeforeFirst("?")); // 去除参数
+            return PathUtils.RemoveSlashSuffix(path).BeforeLast("/");
         } else { // 文件路径
-            return PathUtils.WithoutSeparator(path!).BeforeLast("\\");
+            return PathUtils.RemoveSlashSuffix(path!).BeforeLast("\\");
         }
     }
 
@@ -113,10 +110,10 @@ public static class PathUtils {
     /// </code></summary>
     public static string GetLastPart(string path) {
         if (path!.Contains("://")) { // 网络路径
-            path = PathUtils.WithoutSeparator(path.BeforeFirst("#").BeforeFirst("?")); // 去除参数
+            path = PathUtils.RemoveSlashSuffix(path.BeforeFirst("#").BeforeFirst("?")); // 去除参数
             return path.AfterLast("/");
         } else { // 文件路径
-            return PathUtils.WithoutSeparator(path).AfterLast("\\");
+            return PathUtils.RemoveSlashSuffix(path).AfterLast("\\");
         }
     }
 
@@ -152,7 +149,7 @@ public static class PathUtils {
     /// </summary>
     public static char? GetDiskName(string? path) {
         if (string.IsNullOrEmpty(path)) return null;
-        path = PathUtils.WithoutLongPath(path!);
+        path = PathUtils.RemoveExtendedPrefix(path!);
         if (path.StartsWithF(@"\\") || path.StartsWithF("/")) return null;
         return char.ToUpper(path[0]);
     }
@@ -162,17 +159,17 @@ public static class PathUtils {
     /// <para/>具体而言:将短路径展开,去除前导的 <c>\\?\</c>,将分隔符改为 \,去除末尾的分隔符。
     /// </summary>
     public static string ForCompare(string path) 
-        => PathUtils.WithoutSeparator(PathUtils.WithoutLongPath(Path.GetFullPath(path)).Replace("/", @"\"));
+        => PathUtils.RemoveSlashSuffix(PathUtils.RemoveExtendedPrefix(Path.GetFullPath(path)).Replace("/", @"\"));
 
     /// <summary>
     /// 将路径转换为兼容各种 Windows API 的格式。
     /// <para/>具体而言:将分隔符改为 \。添加前导的 <c>\\?\</c>。若为驱动器则添加末尾分隔符,否则去除分隔符。
     /// </summary>
     public static string ForApi(string path) {
-        path = PathUtils.WithLongPath(path);
+        path = PathUtils.ToExtendedFormat(path);
         if (path.EndsWithF(":")) path += @"\"; // 驱动器路径必须保留末尾的分隔符
         if (path.EndsWithF(@":\")) return path;
-        return PathUtils.WithoutSeparator(path);
+        return PathUtils.RemoveSlashSuffix(path);
     }
 
     #endregion
@@ -182,7 +179,7 @@ public static class PathUtils {
     /// <summary>
     /// 程序可执行文件的所在文件夹,以 \ 结尾。
     /// </summary>
-    public static string CurrentFolder => PathUtils.WithSeparator(AppDomain.CurrentDomain.BaseDirectory);
+    public static string CurrentFolder => PathUtils.AddSlashSuffix(AppDomain.CurrentDomain.BaseDirectory);
 
     #endregion
 

+ 3 - 3
Tests/Utils/FileUtilsTest.cs

@@ -13,8 +13,8 @@ public class FileUtilsTest : TestWithFolder {
         await Assert.That(progress).IsEqualTo(1);
         await Assert.That(DirectoryUtils.Exists(Path.Combine(output, "文件夹"))).IsTrue();
         await Assert.That(DirectoryUtils.Exists(Path.Combine(output, "空文件夹"))).IsFalse();
-        await Assert.That(File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, "fabricloader.log")))).Contains("FabricLoader");
-        await Assert.That(File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, "文件夹", "中文文件.txt")))).Contains("测试内容");
+        await Assert.That(File.ReadAllText(PathUtils.ToExtendedFormat(Path.Combine(output, "fabricloader.log")))).Contains("FabricLoader");
+        await Assert.That(File.ReadAllText(PathUtils.ToExtendedFormat(Path.Combine(output, "文件夹", "中文文件.txt")))).Contains("测试内容");
     }
 
     [Test]
@@ -24,7 +24,7 @@ public class FileUtilsTest : TestWithFolder {
         string output = Path.Combine(tempFolder, "Extracted");
         FileUtils.ExtractToDirectory(GetTestFile(testFile), output, p => progress = p);
         await Assert.That(progress).IsEqualTo(1);
-        await Assert.That(File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, PathUtils.GetFileNameWithoutExtension(testFile))))).Contains(containsText);
+        await Assert.That(File.ReadAllText(PathUtils.ToExtendedFormat(Path.Combine(output, PathUtils.GetFileNameWithoutExtension(testFile))))).Contains(containsText);
     }
 
     [Test]