diff --git a/MediaBrowser.Controller/IO/FileSystemHelper.cs b/MediaBrowser.Controller/IO/FileSystemHelper.cs index 3e390ca428e..44b7fadf5ed 100644 --- a/MediaBrowser.Controller/IO/FileSystemHelper.cs +++ b/MediaBrowser.Controller/IO/FileSystemHelper.cs @@ -63,6 +63,29 @@ public static class FileSystemHelper } } + /// + /// Resolves a single link hop for the specified path. + /// + /// + /// Returns null if the path is not a symbolic link or the filesystem does not support link resolution (e.g., exFAT). + /// + /// The file path to resolve. + /// + /// A representing the next link target if the path is a link; otherwise, null. + /// + private static FileInfo? Resolve(string path) + { + try + { + return File.ResolveLinkTarget(path, returnFinalTarget: false) as FileInfo; + } + catch (IOException) + { + // Filesystem doesn't support links (e.g., exFAT). + return null; + } + } + /// /// Gets the target of the specified file link. /// @@ -84,23 +107,26 @@ public static class FileSystemHelper if (!returnFinalTarget) { - return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo; + return Resolve(linkPath); } - if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo) - { - return null; - } - - if (!targetInfo.Exists) + var targetInfo = Resolve(linkPath); + if (targetInfo is null || !targetInfo.Exists) { return targetInfo; } var currentPath = targetInfo.FullName; var visited = new HashSet(StringComparer.Ordinal) { linkPath, currentPath }; - while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo) + + while (true) { + var linkInfo = Resolve(currentPath); + if (linkInfo is null) + { + break; + } + var targetPath = linkInfo.FullName; // If an infinite loop is detected, return the file info for the