Remove trailing slashes from symbolic link paths

This commit is contained in:
Caelan Sayler
2025-08-17 11:23:03 +01:00
committed by Caelan
parent 031bd9b63a
commit 78e6b6f683
2 changed files with 30 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
namespace Velopack.Util namespace Velopack.Util
@@ -59,7 +60,7 @@ namespace Velopack.Util
{ {
var isLink = TryGetLinkFsi(linkPath, out var fsi); var isLink = TryGetLinkFsi(linkPath, out var fsi);
if (fsi != null && !isLink) { if (fsi != null && !isLink) {
throw new IOException("Path is not a junction point / symlink."); ThrowPathNotASymlinkException(linkPath);
} else { } else {
fsi?.Delete(); fsi?.Delete();
} }
@@ -73,6 +74,7 @@ namespace Velopack.Util
public static string GetTarget(string linkPath, bool relative = false) public static string GetTarget(string linkPath, bool relative = false)
{ {
var target = GetUnresolvedTarget(linkPath); var target = GetUnresolvedTarget(linkPath);
if (relative) { if (relative) {
if (Path.IsPathRooted(target)) { if (Path.IsPathRooted(target)) {
return PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, target); return PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, target);
@@ -97,6 +99,8 @@ namespace Velopack.Util
private static void CreateSymlink(string linkPath, string targetPath, SymbolicLinkFlag mode) private static void CreateSymlink(string linkPath, string targetPath, SymbolicLinkFlag mode)
{ {
linkPath = linkPath.TrimEnd('\\', '/');
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
if (mode == SymbolicLinkFlag.File) { if (mode == SymbolicLinkFlag.File) {
File.CreateSymbolicLink(linkPath, targetPath); File.CreateSymbolicLink(linkPath, targetPath);
@@ -118,23 +122,30 @@ namespace Velopack.Util
private static string GetUnresolvedTarget(string linkPath) private static string GetUnresolvedTarget(string linkPath)
{ {
linkPath = linkPath.TrimEnd('\\', '/');
if (!TryGetLinkFsi(linkPath, out var fsi)) { if (!TryGetLinkFsi(linkPath, out var fsi)) {
throw new IOException("Path does not exist or is not a junction point / symlink."); ThrowPathNotASymlinkException(linkPath);
} }
string target;
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
return fsi!.LinkTarget!; target = fsi!.LinkTarget!;
#else #else
if (VelopackRuntimeInfo.IsWindows) { if (VelopackRuntimeInfo.IsWindows) {
return WindowsReadLink(linkPath); target = WindowsReadLink(linkPath);
} else if (VelopackRuntimeInfo.IsLinux || VelopackRuntimeInfo.IsOSX) {
target = UnixReadLink(linkPath);
} else {
throw new NotSupportedException("Symbolic links are not supported on this platform.");
} }
if (VelopackRuntimeInfo.IsLinux || VelopackRuntimeInfo.IsOSX) {
return UnixReadLink(linkPath);
}
throw new NotSupportedException();
#endif #endif
if (String.IsNullOrEmpty(target)) {
ThrowPathNotASymlinkException(linkPath);
}
return target;
} }
private static bool TryGetLinkFsi(string path, out FileSystemInfo? fsi) private static bool TryGetLinkFsi(string path, out FileSystemInfo? fsi)
@@ -152,5 +163,13 @@ namespace Velopack.Util
return (fsi.Attributes & FileAttributes.ReparsePoint) != 0; return (fsi.Attributes & FileAttributes.ReparsePoint) != 0;
} }
#if NET6_0_OR_GREATER
[DoesNotReturn]
#endif
private static void ThrowPathNotASymlinkException(string path)
{
throw new IOException($"The path '{path}' is not a symbolic link or junction point.");
}
} }
} }

View File

@@ -599,7 +599,7 @@ public class SymbolicLinkTests
Assert.Contains("junction", ex2.Message.ToLower()); Assert.Contains("junction", ex2.Message.ToLower());
var ex3 = Assert.Throws<IOException>(() => SymbolicLink.GetTarget(nonExistent)); var ex3 = Assert.Throws<IOException>(() => SymbolicLink.GetTarget(nonExistent));
Assert.Contains("does not exist", ex3.Message.ToLower()); Assert.Contains("junction", ex2.Message.ToLower());
} }
[Fact] [Fact]