123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // See https://aka.ms/new-console-template for more information
- using Microsoft.Win32.SafeHandles;
- using System.Runtime.InteropServices;
- using System.Security.Cryptography;
- using System.Text;
- Console.WriteLine("Installer is suck");
- if (args.Length == 0)
- {
- Console.WriteLine("Usage: <inDir> <outDir>");
- return;
- }
- 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...");
- string hexHash;
- {
- using var inputStream = info.OpenRead();
- using var sha256 = SHA256.Create();
- var hash = sha256.ComputeHash(inputStream);
- hexHash = Convert.ToHexString(hash);
- }
- Console.WriteLine($" {hexHash}");
- var targetPath = Path.Combine(dirOut, $"{info.Length:0-000-000-000}_{hexHash}{info.Extension}");
- if (File.Exists(targetPath))
- {
- 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);
- }
- HardLinkHelper.CreateHardLink(file, targetPath);
- Console.WriteLine($" Collapse to {targetPath} <");
- }
- catch (Exception e)
- {
- 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;
- //}
- }
|