Browse Source

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

Fix .ignore handling for directories

Original-merge: e8150428b62668e062a3432960f98684d3b352cb

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

Backported-by: Bond_009 <bond.009@outlook.com>
theguymadmax 3 days ago
parent
commit
5b3f29946b
1 changed files with 39 additions and 58 deletions
  1. 39 58
      Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs

+ 39 - 58
Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs

@@ -1,6 +1,5 @@
 using System;
 using System.IO;
-using System.Runtime.InteropServices;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Resolvers;
@@ -13,28 +12,24 @@ namespace Emby.Server.Implementations.Library;
 /// </summary>
 public class DotIgnoreIgnoreRule : IResolverIgnoreRule
 {
+    private static readonly bool IsWindows = OperatingSystem.IsWindows();
+
     private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
     {
-        var ignoreFile = new FileInfo(Path.Join(directory.FullName, ".ignore"));
-        if (ignoreFile.Exists)
-        {
-            return ignoreFile;
-        }
-
-        var parentDir = directory.Parent;
-        if (parentDir is null)
+        for (var current = directory; current is not null; current = current.Parent)
         {
-            return null;
+            var ignorePath = Path.Join(current.FullName, ".ignore");
+            if (File.Exists(ignorePath))
+            {
+                return new FileInfo(ignorePath);
+            }
         }
 
-        return FindIgnoreFile(parentDir);
+        return null;
     }
 
     /// <inheritdoc />
-    public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent)
-    {
-        return IsIgnored(fileInfo, parent);
-    }
+    public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
 
     /// <summary>
     /// Checks whether or not the file is ignored.
@@ -44,72 +39,58 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
     /// <returns>True if the file should be ignored.</returns>
     public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
     {
-        if (fileInfo.IsDirectory)
-        {
-            var dirIgnoreFile = FindIgnoreFile(new DirectoryInfo(fileInfo.FullName));
-            if (dirIgnoreFile is null)
-            {
-                return false;
-            }
-
-            // Fast path in case the ignore files isn't a symlink and is empty
-            if (dirIgnoreFile.LinkTarget is null && dirIgnoreFile.Length == 0)
-            {
-                return true;
-            }
-
-            // ignore the directory only if the .ignore file is empty
-            // evaluate individual files otherwise
-            return string.IsNullOrWhiteSpace(GetFileContent(dirIgnoreFile));
-        }
+        var searchDirectory = fileInfo.IsDirectory
+            ? new DirectoryInfo(fileInfo.FullName)
+            : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
 
-        var parentDirPath = Path.GetDirectoryName(fileInfo.FullName);
-        if (string.IsNullOrEmpty(parentDirPath))
+        if (string.IsNullOrEmpty(searchDirectory.FullName))
         {
             return false;
         }
 
-        var folder = new DirectoryInfo(parentDirPath);
-        var ignoreFile = FindIgnoreFile(folder);
+        var ignoreFile = FindIgnoreFile(searchDirectory);
         if (ignoreFile is null)
         {
             return false;
         }
 
-        string ignoreFileString = GetFileContent(ignoreFile);
-
-        if (string.IsNullOrWhiteSpace(ignoreFileString))
+        // Fast path in case the ignore files isn't a symlink and is empty
+        if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
         {
             // Ignore directory if we just have the file
             return true;
         }
 
+        var content = GetFileContent(ignoreFile);
+        return string.IsNullOrWhiteSpace(content)
+            || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
+    }
+
+    private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
+    {
         // If file has content, base ignoring off the content .gitignore-style rules
-        var ignoreRules = ignoreFileString.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+        var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
         var ignore = new Ignore.Ignore();
-        ignore.Add(ignoreRules);
+        ignore.Add(rules);
 
-        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+         // Mitigate the problem of the Ignore library not handling Windows paths correctly.
+         // See https://github.com/jellyfin/jellyfin/issues/15484
+        var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
+
+        // Add trailing slash for directories to match "folder/"
+        if (isDirectory)
         {
-            // Mitigate the problem of the Ignore library not handling Windows paths correctly.
-            // See https://github.com/jellyfin/jellyfin/issues/15484
-            return ignore.IsIgnored(fileInfo.FullName.NormalizePath('/'));
+            pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
         }
 
-        return ignore.IsIgnored(fileInfo.FullName);
+        return ignore.IsIgnored(pathToCheck);
     }
 
-    private static string GetFileContent(FileInfo dirIgnoreFile)
+    private static string GetFileContent(FileInfo ignoreFile)
     {
-        dirIgnoreFile = FileSystemHelper.ResolveLinkTarget(dirIgnoreFile, returnFinalTarget: true) ?? dirIgnoreFile;
-        if (!dirIgnoreFile.Exists)
-        {
-            return string.Empty;
-        }
-
-        using (var reader = dirIgnoreFile.OpenText())
-        {
-            return reader.ReadToEnd();
-        }
+        ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
+        return ignoreFile.Exists
+            ? File.ReadAllText(ignoreFile.FullName)
+            : string.Empty;
     }
 }