2
0

FileSystemHelper.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using MediaBrowser.Model.IO;
  6. using Microsoft.Extensions.Logging;
  7. namespace MediaBrowser.Controller.IO;
  8. /// <summary>
  9. /// Helper methods for file system management.
  10. /// </summary>
  11. public static class FileSystemHelper
  12. {
  13. /// <summary>
  14. /// Deletes the file.
  15. /// </summary>
  16. /// <param name="fileSystem">The fileSystem.</param>
  17. /// <param name="path">The path.</param>
  18. /// <param name="logger">The logger.</param>
  19. public static void DeleteFile(IFileSystem fileSystem, string path, ILogger logger)
  20. {
  21. try
  22. {
  23. fileSystem.DeleteFile(path);
  24. }
  25. catch (UnauthorizedAccessException ex)
  26. {
  27. logger.LogError(ex, "Error deleting file {Path}", path);
  28. }
  29. catch (IOException ex)
  30. {
  31. logger.LogError(ex, "Error deleting file {Path}", path);
  32. }
  33. }
  34. /// <summary>
  35. /// Recursively delete empty folders.
  36. /// </summary>
  37. /// <param name="fileSystem">The fileSystem.</param>
  38. /// <param name="path">The path.</param>
  39. /// <param name="logger">The logger.</param>
  40. public static void DeleteEmptyFolders(IFileSystem fileSystem, string path, ILogger logger)
  41. {
  42. foreach (var directory in fileSystem.GetDirectoryPaths(path))
  43. {
  44. DeleteEmptyFolders(fileSystem, directory, logger);
  45. if (!fileSystem.GetFileSystemEntryPaths(directory).Any())
  46. {
  47. try
  48. {
  49. Directory.Delete(directory, false);
  50. }
  51. catch (UnauthorizedAccessException ex)
  52. {
  53. logger.LogError(ex, "Error deleting directory {Path}", directory);
  54. }
  55. catch (IOException ex)
  56. {
  57. logger.LogError(ex, "Error deleting directory {Path}", directory);
  58. }
  59. }
  60. }
  61. }
  62. /// <summary>
  63. /// Gets the target of the specified file link.
  64. /// </summary>
  65. /// <remarks>
  66. /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
  67. /// </remarks>
  68. /// <param name="linkPath">The path of the file link.</param>
  69. /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
  70. /// <returns>
  71. /// A <see cref="FileInfo"/> if the <paramref name="linkPath"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
  72. /// </returns>
  73. public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false)
  74. {
  75. // Check if the file exists so the native resolve handler won't throw at us.
  76. if (!File.Exists(linkPath))
  77. {
  78. return null;
  79. }
  80. if (!returnFinalTarget)
  81. {
  82. return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo;
  83. }
  84. if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo)
  85. {
  86. return null;
  87. }
  88. if (!targetInfo.Exists)
  89. {
  90. return targetInfo;
  91. }
  92. var currentPath = targetInfo.FullName;
  93. var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath };
  94. while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo)
  95. {
  96. var targetPath = linkInfo.FullName;
  97. // If an infinite loop is detected, return the file info for the
  98. // first link in the loop we encountered.
  99. if (!visited.Add(targetPath))
  100. {
  101. return new FileInfo(targetPath);
  102. }
  103. targetInfo = linkInfo;
  104. currentPath = targetPath;
  105. // Exit if the target doesn't exist, so the native resolve handler won't throw at us.
  106. if (!targetInfo.Exists)
  107. {
  108. break;
  109. }
  110. }
  111. return targetInfo;
  112. }
  113. /// <summary>
  114. /// Gets the target of the specified file link.
  115. /// </summary>
  116. /// <remarks>
  117. /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
  118. /// </remarks>
  119. /// <param name="fileInfo">The file info of the file link.</param>
  120. /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
  121. /// <returns>
  122. /// A <see cref="FileInfo"/> if the <paramref name="fileInfo"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
  123. /// </returns>
  124. public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false)
  125. {
  126. ArgumentNullException.ThrowIfNull(fileInfo);
  127. return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget);
  128. }
  129. }