diff --git a/ConvertTask.cs b/ConvertTask.cs index dd00f11..b3bb54b 100644 --- a/ConvertTask.cs +++ b/ConvertTask.cs @@ -8,7 +8,6 @@ using Heliosphere.Util; using Newtonsoft.Json; using StrawberryShake; -using Windows.Win32; namespace Heliosphere; @@ -116,6 +115,10 @@ internal async Task Run() { throw new SecurityException("path from mod was attempting to leave the files directory"); } + if (outputPath.Equals(existingPath, StringComparison.InvariantCultureIgnoreCase)) { + continue; + } + if (File.Exists(outputPath)) { continue; } @@ -123,9 +126,7 @@ internal async Task Run() { var parent = PathHelper.GetParent(outputPath); Directory.CreateDirectory(parent); - if (!PInvoke.CreateHardLink(@$"\\?\{outputPath}", @$"\\?\{existingPath}")) { - throw new IOException($"failed to create hard link: {existingPath} -> {outputPath}"); - } + FileHelper.CreateHardLink(existingPath, outputPath); } finished += 1; diff --git a/DownloadTask.cs b/DownloadTask.cs index 29bb5d0..15735a7 100644 --- a/DownloadTask.cs +++ b/DownloadTask.cs @@ -18,7 +18,6 @@ using Newtonsoft.Json.Linq; using Penumbra.Api.Enums; using StrawberryShake; -using Windows.Win32; using ZstdSharp; namespace Heliosphere; @@ -714,7 +713,7 @@ private static async Task DuplicateFile(string filesDir, IEnumerable out async Task DuplicateInner(string dest) { dest = Path.Join(filesDir, dest); - if (path == dest) { + if (path.Equals(dest, StringComparison.InvariantCultureIgnoreCase)) { return; } @@ -729,9 +728,7 @@ async Task DuplicateInner(string dest) { var parent = PathHelper.GetParent(dest); Plugin.Resilience.Execute(() => Directory.CreateDirectory(parent)); - if (!PInvoke.CreateHardLink(@$"\\?\{dest}", @$"\\?\{path}")) { - throw new IOException($"failed to create hard link {path} -> {dest}"); - } + FileHelper.CreateHardLink(path, dest); } } diff --git a/Util/FileHelper.cs b/Util/FileHelper.cs index 73f35de..962f630 100644 --- a/Util/FileHelper.cs +++ b/Util/FileHelper.cs @@ -1,4 +1,6 @@ +using System.Runtime.InteropServices; using Heliosphere.Exceptions; +using Windows.Win32; namespace Heliosphere.Util; @@ -89,4 +91,22 @@ private static async Task WrapAsync(string path, Func> act throw new AlreadyInUseException(io, path, procs); } } + + internal static void CreateHardLink(string source, string destination) { + const string prefix = @"\\?\"; + + var prefixedSource = source.StartsWith(prefix) + ? source + : prefix + source; + var prefixedDestination = destination.StartsWith(prefix) + ? destination + : prefix + destination; + + if (PInvoke.CreateHardLink(prefixedDestination, prefixedSource)) { + return; + } + + var error = Marshal.GetLastWin32Error(); + throw new IOException($"Failed to create hard link (0x{error:X}): {source} -> {destination}"); + } }