Skip to content

Commit

Permalink
Relax LinkTarget so it always returns null when steps on an error
Browse files Browse the repository at this point in the history
  • Loading branch information
jozkee committed Jul 14, 2021
1 parent 8377b0d commit f515710
Showing 1 changed file with 14 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -427,34 +427,33 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i
{
string? targetPath = returnFinalTarget ?
GetFinalLinkTarget(linkPath, isDirectory) :
GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: true, returnFullPath: true);
GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: true, returnFullPath: true);

return targetPath == null ? null :
isDirectory ? new DirectoryInfo(targetPath) : new FileInfo(targetPath);
}

internal static string? GetLinkTarget(string linkPath, bool isDirectory)
=> GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: false);
=> GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: false);

/// <summary>
/// Gets reparse point information associated to <paramref name="linkPath"/>.
/// </summary>
/// <returns>The immediate link target, absolute or relative or null if the file is not a supported link.</returns>
internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnUnreachable, bool returnFullPath)
internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnError, bool returnFullPath)
{
using SafeFileHandle handle = OpenSafeFileHandle(linkPath,
Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS |
Interop.Kernel32.FileOperations.FILE_FLAG_OPEN_REPARSE_POINT);

if (handle.IsInvalid)
{
int error = Marshal.GetLastWin32Error();

if (!throwOnUnreachable && IsPathUnreachableError(error))
if (!throwOnError)
{
return null;
}

int error = Marshal.GetLastWin32Error();
// File not found doesn't make much sense coming from a directory.
if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND)
{
Expand All @@ -479,6 +478,11 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i

if (!success)
{
if (!throwOnError)
{
return null;
}

int error = Marshal.GetLastWin32Error();
// The file or directory is not a reparse point.
if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT)
Expand Down Expand Up @@ -600,13 +604,15 @@ uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer)
{
// Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span.
// I am not sure if it's possible to change CreateFile's param to ROS<char> and avoid all these allocations.
string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: true);

// We don't throw on error since we already did all the proper validations before.
string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: true);
string? prev = null;

while (current != null)
{
prev = current;
current = GetImmediateLinkTarget(current, isDirectory, throwOnUnreachable: false, returnFullPath: true);
current = GetImmediateLinkTarget(current, isDirectory, throwOnError: false, returnFullPath: true);
}

return prev;
Expand Down

0 comments on commit f515710

Please sign in to comment.