HOME 4 månader sedan
förälder
incheckning
d015d8e293
1 ändrade filer med 188 tillägg och 16 borttagningar
  1. 188 16
      InstallerIsSuck/Program.cs

+ 188 - 16
InstallerIsSuck/Program.cs

@@ -1,33 +1,35 @@
 // See https://aka.ms/new-console-template for more information
 
-
+using Microsoft.Win32.SafeHandles;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography;
-
-
-[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
-static extern bool CreateHardLink(
-    string lpFileName,
-    string lpExistingFileName,
-    IntPtr lpSecurityAttributes
-);
-
+using System.Text;
 
 Console.WriteLine("Installer is suck");
 
-
 if (args.Length == 0)
 {
     Console.WriteLine("Usage: <inDir> <outDir>");
     return;
 }
 
-var files = Directory.GetFiles(args[0]);
+var dirIn = Path.GetFullPath(args[0]);
+var dirOut = Path.GetFullPath(args[1]);
+
+var files = Directory.GetFiles(dirIn);
 foreach (var file in files)
 {
     try
     {
         Console.WriteLine($"> {file}");
+
+        var hl = HardLinkHelper.GetFileLinkCount(file);
+        if (hl > 1)
+        {
+            Console.WriteLine(" SKIP");
+            continue;
+        }
+
         var info = new FileInfo(file);
         Console.WriteLine($" Len: {info.Length:N0}");
         Console.Write(" SHA256...");
@@ -40,17 +42,25 @@ foreach (var file in files)
         }
         Console.WriteLine($" {hexHash}");
 
-        var targetPath = Path.Combine(args[1], $"{info.Length:0-000-000-000}_{hexHash}{info.Extension}");
+        var targetPath = Path.Combine(dirOut, $"{info.Length:0-000-000-000}_{hexHash}{info.Extension}");
         if (File.Exists(targetPath))
         {
-            File.Delete(file);
+            try
+            {
+                File.Delete(file);
+            }
+            catch (UnauthorizedAccessException)
+            {
+                Console.WriteLine($" Warn: Failure to delete {file}, moved to append ._del");
+                File.Move(file, file + "._del");
+            }
         }
         else
         {
             File.Move(file, targetPath);
         }
 
-        CreateHardLink(file, targetPath, nint.Zero);
+        HardLinkHelper.CreateHardLink(file, targetPath);
         Console.WriteLine($" Collapse to {targetPath} <");
     }
     catch (Exception e)
@@ -58,4 +68,166 @@ foreach (var file in files)
         Console.WriteLine($"Error:{e.Message}");
     }
     Console.WriteLine();
-}
+}
+
+public static class HardLinkHelper
+{
+    // P/Invoke 声明
+
+    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+    private static extern IntPtr FindFirstFileNameW(
+        string lpFileName,
+        uint dwFlags,
+        ref uint lpdwBufferSize,
+        StringBuilder lpLinkName
+    );
+
+    [DllImport("kernel32.dll", SetLastError = true)]
+    private static extern bool FindNextFileNameW(
+        IntPtr hFindStream,
+        ref uint lpdwBufferSize,
+        StringBuilder lpLinkName
+    );
+
+    [DllImport("kernel32.dll", SetLastError = true)]
+    private static extern bool FindClose(IntPtr hFindStream);
+
+    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
+    private static extern bool CreateHardLink(
+        string lpFileName,
+        string lpExistingFileName,
+        IntPtr lpSecurityAttributes
+    );
+
+    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+    private static extern bool GetFileInformationByHandle(
+        SafeFileHandle hFile,
+        out BY_HANDLE_FILE_INFORMATION lpFileInformation
+    );
+
+    [StructLayout(LayoutKind.Sequential)]
+    private struct BY_HANDLE_FILE_INFORMATION
+    {
+        public uint FileAttributes;
+        public uint FileCreationTimeLow;
+        public uint FileCreationTimeHigh;
+        public uint LastAccessTimeLow;
+        public uint LastAccessTimeHigh;
+        public uint LastWriteTimeLow;
+        public uint LastWriteTimeHigh;
+        public uint VolumeSerialNumber;
+        public uint FileSizeHigh;
+        public uint FileSizeLow;
+        public uint NumberOfLinks;  // 硬链接数量
+        public uint FileIndexHigh;
+        public uint FileIndexLow;
+    }
+
+    [DllImport("kernel32.dll")]
+    private static extern bool GetVolumePathName(string lpszFileName,
+        [Out] StringBuilder lpszVolumePathName, uint cchBufferLength);
+
+    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+    private static extern SafeFileHandle CreateFile(
+        string lpFileName,
+        [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
+        [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
+        IntPtr lpSecurityAttributes,
+        [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
+        [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
+        IntPtr hTemplateFile);
+
+    [DllImport("kernel32.dll", SetLastError = true)]
+    [return: MarshalAs(UnmanagedType.Bool)]
+    private static extern bool CloseHandle(SafeHandle hObject);
+
+    [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
+    private static extern bool PathAppend([In, Out] StringBuilder pszPath, string pszMore);
+
+    public static bool CreateHardLink(string link, string to)
+    {
+        return CreateHardLink(link, to, nint.Zero);
+    }
+
+    public static int GetFileLinkCount(string filepath)
+    {
+        int result = 0;
+        SafeFileHandle handle = CreateFile(filepath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
+        BY_HANDLE_FILE_INFORMATION fileInfo = new BY_HANDLE_FILE_INFORMATION();
+        if (GetFileInformationByHandle(handle, out fileInfo))
+            result = (int)fileInfo.NumberOfLinks;
+        CloseHandle(handle);
+        return result;
+    }
+
+    //public static string[] GetFileSiblingHardLinks(string filepath)
+    //{
+    //    List<string> result = new List<string>();
+    //    uint stringLength = 256;
+    //    StringBuilder sb = new StringBuilder(1024);
+    //    GetVolumePathName(filepath, sb, stringLength);
+    //    string volume = sb.ToString();
+    //    sb.Length = 0; stringLength = 1024;
+    //    IntPtr findHandle = FindFirstFileNameW(filepath, 0, ref stringLength, sb);
+    //    if (findHandle.ToInt64() != -1)
+    //    {
+    //        do
+    //        {
+    //            StringBuilder pathSb = new StringBuilder(volume, 1024);
+    //            PathAppend(pathSb, sb.ToString());
+    //            result.Add(pathSb.ToString());
+    //            sb.Length = 0; stringLength = 1024;
+    //        } while (FindNextFileNameW(findHandle, ref stringLength, sb));
+    //        FindClose(findHandle);
+    //        return result.ToArray();
+    //    }
+    //    return null;
+    //}
+
+    ///// <summary>
+    ///// 获取指定文件的所有硬链接路径
+    ///// </summary>
+    ///// <param name="filePath">目标文件的路径</param>
+    ///// <returns>文件的所有硬链接路径集合</returns>
+    //public static IEnumerable<string> GetHardLinks(string filePath)
+    //{
+    //    var hardLinks = new List<string>();
+    //    try
+    //    {
+    //        // 获取文件信息
+    //        using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read))
+    //        {
+    //            BY_HANDLE_FILE_INFORMATION fileInfo;
+    //            if (GetFileInformationByHandle(fileStream.SafeFileHandle, out fileInfo))
+    //            {
+    //                Console.WriteLine($"硬链接数量: {fileInfo.NumberOfLinks}");
+    //                if (fileInfo.NumberOfLinks > 1)
+    //                {
+    //                    uint bufferSize = 1024;
+    //                    StringBuilder linkName = new StringBuilder((int)bufferSize);
+    //                    IntPtr handle = FindFirstFileNameW(filePath, 0, ref bufferSize, linkName);
+    //                    if (handle != IntPtr.Zero)
+    //                    {
+    //                        do
+    //                        {
+    //                            hardLinks.Add(linkName.ToString());
+    //                            bufferSize = 1024;
+    //                            linkName.Clear();
+    //                        } while (FindNextFileNameW(handle, ref bufferSize, linkName));
+    //                        FindClose(handle);
+    //                    }
+    //                }
+    //            }
+    //            else
+    //            {
+    //                Console.WriteLine("无法获取文件信息.");
+    //            }
+    //        }
+    //    }
+    //    catch (Exception ex)
+    //    {
+    //        Console.WriteLine($"错误: {ex.Message}");
+    //    }
+    //    return hardLinks;
+    //}
+}