aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShadowghost <Shadowghost@users.noreply.github.com>2026-09-15 11:15:57 -0400
committerCody Robibero <cody@robibe.ro>2026-09-15 11:15:57 -0400
commit4e1c55031f1d0d63b1cbf44410f3189f0dbf5b73 (patch)
tree6981d8f216a3ff668fb701731ae7a27601f521a5
parentf62f3cf9b011e15e81d502f35fa9c7668a93aaa5 (diff)
Backport pull request #17929 from jellyfin/release-12.z
Fix ContainsSubPath check Original-merge: b55ea60da6b9589767ba48e46f2597a5e4c8629d Merged-by: crobibero <cody@robibe.ro> Backported-by: Cody Robibero <cody@robibe.ro>
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs13
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs35
2 files changed, 45 insertions, 3 deletions
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index ede9b27592..db743c8d31 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -489,11 +489,18 @@ namespace Emby.Server.Implementations.IO
ArgumentException.ThrowIfNullOrEmpty(parentPath);
ArgumentException.ThrowIfNullOrEmpty(path);
- return path.Contains(
- Path.TrimEndingDirectorySeparator(parentPath) + Path.DirectorySeparatorChar,
- _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
+ var parent = Path.TrimEndingDirectorySeparator(parentPath);
+
+ // The parent has to be an anchored prefix of the path, otherwise unrelated paths that merely
+ // contain the parent as a segment (e.g. /media and /data/media/tv) would be treated as related.
+ return path.Length > parent.Length
+ && path.StartsWith(parent, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)
+ && (Path.EndsInDirectorySeparator(parent) || IsDirectorySeparator(path[parent.Length]));
}
+ private static bool IsDirectorySeparator(char c)
+ => c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
+
/// <inheritdoc />
public virtual bool AreEqual(string path1, string path2)
{
diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
index 6cadfacce8..b39ca83483 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs
@@ -100,6 +100,41 @@ public partial class ManagedFileSystemTests
Assert.Equal(expectedFileName, _sut.GetValidFilename(filename));
}
+ [Theory]
+ [InlineData("/media", "/media/tv", true)]
+ [InlineData("/media", "/media/tv/show/episode.mkv", true)]
+ [InlineData("/media/", "/media/tv", true)]
+ [InlineData("/", "/media", true)]
+ [InlineData("/media", "/media", false)]
+ [InlineData("/media", "/media/", true)]
+ [InlineData("/media", "/data/media/tv", false)]
+ [InlineData("/media", "/mediastuff/tv", false)]
+ [InlineData("/data/media", "/data/media/tv", true)]
+ [InlineData("/media/tv", "/media", false)]
+ [InlineData("/MEDIA", "/media/tv", false)]
+ public void ContainsSubPath_Unix_ReturnsExpected(string parentPath, string path, bool expected)
+ {
+ Assert.SkipWhen(OperatingSystem.IsWindows(), "Unix-only test");
+
+ Assert.Equal(expected, _sut.ContainsSubPath(parentPath, path));
+ }
+
+ [Theory]
+ [InlineData(@"C:\media", @"C:\media\tv", true)]
+ [InlineData(@"C:\media\", @"C:\media\tv", true)]
+ [InlineData(@"C:\", @"C:\media", true)]
+ [InlineData(@"C:\media", @"C:\media", false)]
+ [InlineData(@"C:\media", @"C:\data\media\tv", false)]
+ [InlineData(@"C:\media", @"C:\mediastuff\tv", false)]
+ [InlineData(@"C:\MEDIA", @"C:\media\tv", true)]
+ [InlineData(@"C:\media", @"C:\media/tv", true)]
+ public void ContainsSubPath_Windows_ReturnsExpected(string parentPath, string path, bool expected)
+ {
+ Assert.SkipUnless(OperatingSystem.IsWindows(), "Windows-only test");
+
+ Assert.Equal(expected, _sut.ContainsSubPath(parentPath, path));
+ }
+
[Fact]
public void GetFileInfo_DanglingSymlink_ExistsFalse()
{