Skip to content

Commit

Permalink
Merge #4144 Fix symlink tests on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Aug 5, 2024
2 parents 5b5911e + 558a607 commit 8c06ebd
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ All notable changes to this project will be documented in this file.
- [Multiple] Store GitHub Discussions links and display in UIs (#4111 by: HebaruSan)
- [GUI] Chinese translation fixes (#4115, #4116, #4131 by: zhouyiqing0304; reviewed: HebaruSan)
- [Multiple] Visually indicate to users that they should click Refresh (#4133 by: HebaruSan)
- [Multiple] Option to clone smaller instances with junction points (Windows) or symbolic links (Unix) (#4129 by: HebaruSan)
- [Multiple] Option to clone smaller instances with junction points (Windows) or symbolic links (Unix) (#4129, #4144 by: HebaruSan)

### Bugfixes

Expand Down
46 changes: 45 additions & 1 deletion Core/DirectoryLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,42 @@ public static bool TryGetTarget(string link, out string target)
return !string.IsNullOrEmpty(target);
}

public static void Remove(string link)
{
if (Platform.IsWindows)
{
if (Directory.Exists(link))
{
var h = CreateFile(link, GenericWrite, FileShare.Write, IntPtr.Zero,
FileMode.Open, BackupSemantics | OpenReparsePoint, IntPtr.Zero);
if (!h.IsInvalid)
{
var junctionInfo = ReparseDataBuffer.Empty();
if (!DeviceIoControl(h, FSCTL_DELETE_REPARSE_POINT,
ref junctionInfo, 8,
null, 0,
out _, IntPtr.Zero))
{
throw new Kraken($"Failed to remove junction at {link}: {Marshal.GetLastWin32Error()}");
}
h.Close();
Directory.Delete(link);
}
}
}
else
{
File.Delete(link);
}
}

private const uint GenericWrite = 0x40000000u;
private const uint BackupSemantics = 0x02000000u;
private const uint OpenReparsePoint = 0x00200000u;
private const uint FSCTL_SET_REPARSE_POINT = 0x000900A4u;
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003u;
private const uint FSCTL_SET_REPARSE_POINT = 0x000900A4u;
private const uint FSCTL_GET_REPARSE_POINT = 0x000900A8u;
private const uint FSCTL_DELETE_REPARSE_POINT = 0x000900ACu;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(SafeFileHandle hDevice,
Expand Down Expand Up @@ -130,6 +160,20 @@ private struct ReparseDataBuffer
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8184)]
public string PathBuffer;

public static ReparseDataBuffer Empty()
{
return new ReparseDataBuffer
{
ReparseTag = IO_REPARSE_TAG_MOUNT_POINT,
ReparseDataLength = 0,
SubstituteNameOffset = 0,
SubstituteNameLength = 0,
PrintNameOffset = 0,
PrintNameLength = 0,
PathBuffer = "",
};
}

public static ReparseDataBuffer FromPath(string target, out int byteCount)
{
var fullTarget = $@"\??\{Path.GetFullPath(target)}";
Expand Down
2 changes: 2 additions & 0 deletions Tests/Core/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public void CopyDirectory_WithSymlinks_MakesSymlinks()
var fi3 = new FileInfo(Path.Combine(tempDir, "KSP-0.25", "GameData", "README.md"));
Assert.IsFalse(fi3.Attributes.HasFlag(FileAttributes.ReparsePoint),
"KSP-0.25/GameData/README.md should not be a symlink");

DirectoryLink.Remove(fi2.FullName);
}

[Test]
Expand Down

0 comments on commit 8c06ebd

Please sign in to comment.