using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.IO;
///
/// Helper methods for file system management.
///
public static class FileSystemHelper
{
///
/// Deletes the file.
///
/// The fileSystem.
/// The path.
/// The logger.
public static void DeleteFile(IFileSystem fileSystem, string path, ILogger logger)
{
try
{
fileSystem.DeleteFile(path);
}
catch (UnauthorizedAccessException ex)
{
logger.LogError(ex, "Error deleting file {Path}", path);
}
catch (IOException ex)
{
logger.LogError(ex, "Error deleting file {Path}", path);
}
}
///
/// Recursively delete empty folders.
///
/// The fileSystem.
/// The path.
/// The logger.
public static void DeleteEmptyFolders(IFileSystem fileSystem, string path, ILogger logger)
{
foreach (var directory in fileSystem.GetDirectoryPaths(path))
{
DeleteEmptyFolders(fileSystem, directory, logger);
if (!fileSystem.GetFileSystemEntryPaths(directory).Any())
{
try
{
Directory.Delete(directory, false);
}
catch (UnauthorizedAccessException ex)
{
logger.LogError(ex, "Error deleting directory {Path}", directory);
}
catch (IOException ex)
{
logger.LogError(ex, "Error deleting directory {Path}", directory);
}
}
}
}
///
/// Gets the target of the specified file link.
///
///
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
///
/// The path of the file link.
/// true to follow links to the final target; false to return the immediate next link.
///
/// A if the is a link, regardless of if the target exists; otherwise, null.
///
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(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;
}
///
/// Gets the target of the specified file link.
///
///
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
///
/// The file info of the file link.
/// true to follow links to the final target; false to return the immediate next link.
///
/// A if the is a link, regardless of if the target exists; otherwise, null.
///
public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false)
{
ArgumentNullException.ThrowIfNull(fileInfo);
return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget);
}
}