Browse Source

Backport pull request #15568 from jellyfin/release-10.11.z

Fix ResolveLinkTarget crashing on exFAT drives

Original-merge: fbb9a0b2c7c5afbc56be76a4eb11a1045f0ab0f0

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
theguymadmax 2 days ago
parent
commit
5d46278584
1 changed files with 34 additions and 8 deletions
  1. 34 8
      MediaBrowser.Controller/IO/FileSystemHelper.cs

+ 34 - 8
MediaBrowser.Controller/IO/FileSystemHelper.cs

@@ -63,6 +63,29 @@ public static class FileSystemHelper
         }
     }
 
+    /// <summary>
+    /// Resolves a single link hop for the specified path.
+    /// </summary>
+    /// <remarks>
+    /// Returns <c>null</c> if the path is not a symbolic link or the filesystem does not support link resolution (e.g., exFAT).
+    /// </remarks>
+    /// <param name="path">The file path to resolve.</param>
+    /// <returns>
+    /// A <see cref="FileInfo"/> representing the next link target if the path is a link; otherwise, <c>null</c>.
+    /// </returns>
+    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;
+        }
+    }
+
     /// <summary>
     /// Gets the target of the specified file link.
     /// </summary>
@@ -84,23 +107,26 @@ public static class FileSystemHelper
 
         if (!returnFinalTarget)
         {
-            return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo;
-        }
-
-        if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo)
-        {
-            return null;
+            return Resolve(linkPath);
         }
 
-        if (!targetInfo.Exists)
+        var targetInfo = Resolve(linkPath);
+        if (targetInfo is null || !targetInfo.Exists)
         {
             return targetInfo;
         }
 
         var currentPath = targetInfo.FullName;
         var visited = new HashSet<string>(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