aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/IO/ManagedFileSystem.cs
diff options
context:
space:
mode:
authorBrian J. Murrell <brian@interlinx.bc.ca>2021-11-29 17:53:26 -0500
committerBrian J. Murrell <brian@interlinx.bc.ca>2021-11-29 17:53:26 -0500
commit757970bfc17b0eb1566b45fbe700dcb16423b190 (patch)
tree63fbc171621f5ec7ae156f341d9b1df37643deac /Emby.Server.Implementations/IO/ManagedFileSystem.cs
parenta3a4689af22693b535e80b98624831866fda2a61 (diff)
parentc677b4f6b7f7e874097aa2cee866d9ed1e574178 (diff)
Merge remote-tracking branch 'origin/master' into HEAD
Diffstat (limited to 'Emby.Server.Implementations/IO/ManagedFileSystem.cs')
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs163
1 files changed, 94 insertions, 69 deletions
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 3893a1577..777cd2cd4 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -1,17 +1,12 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
-using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations.IO
{
@@ -20,22 +15,26 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public class ManagedFileSystem : IFileSystem
{
- protected ILogger<ManagedFileSystem> Logger;
+ private readonly ILogger<ManagedFileSystem> _logger;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private readonly string _tempPath;
- private readonly bool _isEnvironmentCaseInsensitive;
+ private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedFileSystem"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger"/> instance to use.</param>
+ /// <param name="applicationPaths">The <see cref="IApplicationPaths"/> instance to use.</param>
public ManagedFileSystem(
ILogger<ManagedFileSystem> logger,
IApplicationPaths applicationPaths)
{
- Logger = logger;
+ _logger = logger;
_tempPath = applicationPaths.TempDirectory;
-
- _isEnvironmentCaseInsensitive = OperatingSystem.Id == OperatingSystemId.Windows;
}
+ /// <inheritdoc />
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -46,7 +45,7 @@ namespace Emby.Server.Implementations.IO
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
- /// <exception cref="ArgumentNullException">filename</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception>
public virtual bool IsShortcut(string filename)
{
if (string.IsNullOrEmpty(filename))
@@ -55,7 +54,7 @@ namespace Emby.Server.Implementations.IO
}
var extension = Path.GetExtension(filename);
- return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
+ return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
@@ -63,8 +62,8 @@ namespace Emby.Server.Implementations.IO
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">filename</exception>
- public virtual string ResolveShortcut(string filename)
+ /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception>
+ public virtual string? ResolveShortcut(string filename)
{
if (string.IsNullOrEmpty(filename))
{
@@ -72,11 +71,12 @@ namespace Emby.Server.Implementations.IO
}
var extension = Path.GetExtension(filename);
- var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
+ var handler = _shortcutHandlers.Find(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
return handler?.Resolve(filename);
}
+ /// <inheritdoc />
public virtual string MakeAbsolutePath(string folderPath, string filePath)
{
// path is actually a stream
@@ -238,33 +238,31 @@ namespace Emby.Server.Implementations.IO
result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
// if (!result.IsDirectory)
- //{
+ // {
// result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
- //}
+ // }
if (info is FileInfo fileInfo)
{
result.Length = fileInfo.Length;
- // Issue #2354 get the size of files behind symbolic links
- if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
+ // Issue #2354 get the size of files behind symbolic links. Also Enum.HasFlag is bad as it boxes!
+ if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
{
try
{
- using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
+ using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
- result.Length = thisFileStream.Length;
+ result.Length = RandomAccess.GetLength(fileHandle);
}
}
catch (FileNotFoundException ex)
{
// Dangling symlinks cannot be detected before opening the file unfortunately...
- Logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName);
+ _logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName);
result.Exists = false;
}
}
-
- result.DirectoryName = fileInfo.DirectoryName;
}
result.CreationTimeUtc = GetCreationTimeUtc(info);
@@ -303,16 +301,37 @@ namespace Emby.Server.Implementations.IO
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentNullException">The filename is null.</exception>
- public virtual string GetValidFilename(string filename)
+ public string GetValidFilename(string filename)
{
- var builder = new StringBuilder(filename);
-
- foreach (var c in Path.GetInvalidFileNameChars())
+ var invalid = Path.GetInvalidFileNameChars();
+ var first = filename.IndexOfAny(invalid);
+ if (first == -1)
{
- builder = builder.Replace(c, ' ');
+ // Fast path for clean strings
+ return filename;
}
- return builder.ToString();
+ return string.Create(
+ filename.Length,
+ (filename, invalid, first),
+ (chars, state) =>
+ {
+ state.filename.AsSpan().CopyTo(chars);
+
+ chars[state.first++] = ' ';
+
+ var len = chars.Length;
+ foreach (var c in state.invalid)
+ {
+ for (int i = state.first; i < len; i++)
+ {
+ if (chars[i] == c)
+ {
+ chars[i] = ' ';
+ }
+ }
+ }
+ });
}
/// <summary>
@@ -329,7 +348,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName);
+ _logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName);
return DateTime.MinValue;
}
}
@@ -344,11 +363,13 @@ namespace Emby.Server.Implementations.IO
return GetCreationTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info)
{
return info.CreationTimeUtc;
}
+ /// <inheritdoc />
public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
{
return info.LastWriteTimeUtc;
@@ -368,7 +389,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName);
+ _logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName);
return DateTime.MinValue;
}
}
@@ -383,9 +404,10 @@ namespace Emby.Server.Implementations.IO
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
}
+ /// <inheritdoc />
public virtual void SetHidden(string path, bool isHidden)
{
- if (OperatingSystem.Id != OperatingSystemId.Windows)
+ if (!OperatingSystem.IsWindows())
{
return;
}
@@ -407,9 +429,10 @@ namespace Emby.Server.Implementations.IO
}
}
- public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
+ /// <inheritdoc />
+ public virtual void SetAttributes(string path, bool isHidden, bool readOnly)
{
- if (OperatingSystem.Id != OperatingSystemId.Windows)
+ if (!OperatingSystem.IsWindows())
{
return;
}
@@ -421,16 +444,16 @@ namespace Emby.Server.Implementations.IO
return;
}
- if (info.IsReadOnly == isReadOnly && info.IsHidden == isHidden)
+ if (info.IsReadOnly == readOnly && info.IsHidden == isHidden)
{
return;
}
var attributes = File.GetAttributes(path);
- if (isReadOnly)
+ if (readOnly)
{
- attributes = attributes | FileAttributes.ReadOnly;
+ attributes |= FileAttributes.ReadOnly;
}
else
{
@@ -439,7 +462,7 @@ namespace Emby.Server.Implementations.IO
if (isHidden)
{
- attributes = attributes | FileAttributes.Hidden;
+ attributes |= FileAttributes.Hidden;
}
else
{
@@ -484,6 +507,7 @@ namespace Emby.Server.Implementations.IO
File.Copy(temp1, file2, true);
}
+ /// <inheritdoc />
public virtual bool ContainsSubPath(string parentPath, string path)
{
if (string.IsNullOrEmpty(parentPath))
@@ -501,6 +525,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string NormalizePath(string path)
{
if (string.IsNullOrEmpty(path))
@@ -516,6 +541,7 @@ namespace Emby.Server.Implementations.IO
return Path.TrimEndingDirectorySeparator(path);
}
+ /// <inheritdoc />
public virtual bool AreEqual(string path1, string path2)
{
if (path1 == null && path2 == null)
@@ -534,6 +560,7 @@ namespace Emby.Server.Implementations.IO
_isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
+ /// <inheritdoc />
public virtual string GetFileNameWithoutExtension(FileSystemMetadata info)
{
if (info.IsDirectory)
@@ -544,11 +571,11 @@ namespace Emby.Server.Implementations.IO
return Path.GetFileNameWithoutExtension(info.FullName);
}
+ /// <inheritdoc />
public virtual bool IsPathFile(string path)
{
- // Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
- if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
- !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
+ if (path.Contains("://", StringComparison.OrdinalIgnoreCase)
+ && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -556,17 +583,23 @@ namespace Emby.Server.Implementations.IO
return true;
}
+ /// <inheritdoc />
public virtual void DeleteFile(string path)
{
SetAttributes(path, false, false);
File.Delete(path);
}
+ /// <inheritdoc />
public virtual List<FileSystemMetadata> GetDrives()
{
// check for ready state to avoid waiting for drives to timeout
// some drives on linux have no actual size or are used for other purposes
- return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram)
+ return DriveInfo.GetDrives()
+ .Where(
+ d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable)
+ && d.IsReady
+ && d.TotalSize != 0)
.Select(d => new FileSystemMetadata
{
Name = d.Name,
@@ -575,17 +608,20 @@ namespace Emby.Server.Implementations.IO
}).ToList();
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
{
return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
{
return GetFiles(path, null, false, recursive);
}
- public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string> extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
+ /// <inheritdoc />
+ public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -602,26 +638,26 @@ namespace Emby.Server.Implementations.IO
{
files = files.Where(i =>
{
- var ext = i.Extension;
- if (ext == null)
+ var ext = i.Extension.AsSpan();
+ if (ext.IsEmpty)
{
return false;
}
- return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
});
}
return ToMetadata(files);
}
+ /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
{
var directoryInfo = new DirectoryInfo(path);
var enumerationOptions = GetEnumerationOptions(recursive);
- return ToMetadata(directoryInfo.EnumerateDirectories("*", enumerationOptions))
- .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", enumerationOptions)));
+ return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", enumerationOptions));
}
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
@@ -629,17 +665,20 @@ namespace Emby.Server.Implementations.IO
return infos.Select(GetFileSystemMetadata);
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
{
return GetFilePaths(path, null, false, recursive);
}
- public virtual IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
+ /// <inheritdoc />
+ public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
var enumerationOptions = GetEnumerationOptions(recursive);
@@ -656,19 +695,20 @@ namespace Emby.Server.Implementations.IO
{
files = files.Where(i =>
{
- var ext = Path.GetExtension(i);
- if (ext == null)
+ var ext = Path.GetExtension(i.AsSpan());
+ if (ext.IsEmpty)
{
return false;
}
- return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+ return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
});
}
return files;
}
+ /// <inheritdoc />
public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
{
return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
@@ -684,20 +724,5 @@ namespace Emby.Server.Implementations.IO
AttributesToSkip = 0
};
}
-
- private static void RunProcess(string path, string args, string workingDirectory)
- {
- using (var process = Process.Start(new ProcessStartInfo
- {
- Arguments = args,
- FileName = path,
- CreateNoWindow = true,
- WorkingDirectory = workingDirectory,
- WindowStyle = ProcessWindowStyle.Normal
- }))
- {
- process.WaitForExit();
- }
- }
}
}