diff options
Diffstat (limited to 'MediaBrowser.Controller/IO/FileSystemHelper.cs')
| -rw-r--r-- | MediaBrowser.Controller/IO/FileSystemHelper.cs | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/IO/FileSystemHelper.cs b/MediaBrowser.Controller/IO/FileSystemHelper.cs index 1a33c3aa8..324aea7e3 100644 --- a/MediaBrowser.Controller/IO/FileSystemHelper.cs +++ b/MediaBrowser.Controller/IO/FileSystemHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Model.IO; @@ -61,4 +62,77 @@ public static class FileSystemHelper } } } + + /// <summary> + /// Gets the target of the specified file link. + /// </summary> + /// <remarks> + /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128. + /// </remarks> + /// <param name="linkPath">The path of the file link.</param> + /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param> + /// <returns> + /// A <see cref="FileInfo"/> if the <paramref name="linkPath"/> is a link, regardless of if the target exists; otherwise, <c>null</c>. + /// </returns> + public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + // Check if the file exists so the native resolve handler won't throw at us. + if (!File.Exists(linkPath)) + { + return null; + } + + if (!returnFinalTarget) + { + return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo; + } + + if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo) + { + return null; + } + + var currentPath = targetInfo.FullName; + var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath }; + while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo) + { + var targetPath = linkInfo.FullName; + + // If an infinite loop is detected, return the file info for the + // first link in the loop we encountered. + if (!visited.Add(targetPath)) + { + return new FileInfo(targetPath); + } + + targetInfo = linkInfo; + currentPath = targetPath; + + // Exit if the target doesn't exist, so the native resolve handler won't throw at us. + if (!targetInfo.Exists) + { + break; + } + } + + return targetInfo; + } + + /// <summary> + /// Gets the target of the specified file link. + /// </summary> + /// <remarks> + /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128. + /// </remarks> + /// <param name="fileInfo">The file info of the file link.</param> + /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param> + /// <returns> + /// A <see cref="FileInfo"/> if the <paramref name="fileInfo"/> is a link, regardless of if the target exists; otherwise, <c>null</c>. + /// </returns> + public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false) + { + ArgumentNullException.ThrowIfNull(fileInfo); + + return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget); + } } |
