DotIgnoreIgnoreRule.cs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. using System;
  2. using System.IO;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Controller.IO;
  5. using MediaBrowser.Controller.Resolvers;
  6. using MediaBrowser.Model.IO;
  7. namespace Emby.Server.Implementations.Library;
  8. /// <summary>
  9. /// Resolver rule class for ignoring files via .ignore.
  10. /// </summary>
  11. public class DotIgnoreIgnoreRule : IResolverIgnoreRule
  12. {
  13. private static readonly bool IsWindows = OperatingSystem.IsWindows();
  14. private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
  15. {
  16. for (var current = directory; current is not null; current = current.Parent)
  17. {
  18. var ignorePath = Path.Join(current.FullName, ".ignore");
  19. if (File.Exists(ignorePath))
  20. {
  21. return new FileInfo(ignorePath);
  22. }
  23. }
  24. return null;
  25. }
  26. /// <inheritdoc />
  27. public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
  28. /// <summary>
  29. /// Checks whether or not the file is ignored.
  30. /// </summary>
  31. /// <param name="fileInfo">The file information.</param>
  32. /// <param name="parent">The parent BaseItem.</param>
  33. /// <returns>True if the file should be ignored.</returns>
  34. public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
  35. {
  36. var searchDirectory = fileInfo.IsDirectory
  37. ? new DirectoryInfo(fileInfo.FullName)
  38. : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
  39. if (string.IsNullOrEmpty(searchDirectory.FullName))
  40. {
  41. return false;
  42. }
  43. var ignoreFile = FindIgnoreFile(searchDirectory);
  44. if (ignoreFile is null)
  45. {
  46. return false;
  47. }
  48. // Fast path in case the ignore files isn't a symlink and is empty
  49. if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
  50. {
  51. // Ignore directory if we just have the file
  52. return true;
  53. }
  54. var content = GetFileContent(ignoreFile);
  55. return string.IsNullOrWhiteSpace(content)
  56. || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
  57. }
  58. private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
  59. {
  60. // If file has content, base ignoring off the content .gitignore-style rules
  61. var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
  62. var ignore = new Ignore.Ignore();
  63. ignore.Add(rules);
  64. // Mitigate the problem of the Ignore library not handling Windows paths correctly.
  65. // See https://github.com/jellyfin/jellyfin/issues/15484
  66. var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
  67. // Add trailing slash for directories to match "folder/"
  68. if (isDirectory)
  69. {
  70. pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
  71. }
  72. return ignore.IsIgnored(pathToCheck);
  73. }
  74. private static string GetFileContent(FileInfo ignoreFile)
  75. {
  76. ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
  77. return ignoreFile.Exists
  78. ? File.ReadAllText(ignoreFile.FullName)
  79. : string.Empty;
  80. }
  81. }