aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/IO/FileSystemHelper.cs
diff options
context:
space:
mode:
authorrevam <revam@users.noreply.github.com>2025-11-17 14:08:47 -0500
committerBond_009 <bond.009@outlook.com>2025-11-17 14:08:47 -0500
commit5ea3910af96ead74d9267ec239e47a798a33e78f (patch)
treeb767f3b27be32e554752f9beeee75028c3b31ee7 /MediaBrowser.Controller/IO/FileSystemHelper.cs
parent06fb300cff5d63404a888209ab82b192b9e46be4 (diff)
Backport pull request #15263 from jellyfin/release-10.11.z
Resolve symlinks for static media source infos Original-merge: 3b2d64995aab63ebaa6832c059a3cc0bdebe90dc Merged-by: crobibero <cody@robibe.ro> Backported-by: Bond_009 <bond.009@outlook.com>
Diffstat (limited to 'MediaBrowser.Controller/IO/FileSystemHelper.cs')
-rw-r--r--MediaBrowser.Controller/IO/FileSystemHelper.cs74
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);
+ }
}