|
@@ -1,33 +1,35 @@
|
|
|
|
|
|
|
|
|
-
|
|
|
+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
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+ [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;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|