< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.IO.ManagedFileSystem
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/IO/ManagedFileSystem.cs
Line coverage
51%
Covered lines: 129
Uncovered lines: 122
Coverable lines: 251
Total lines: 684
Line coverage: 51.3%
Branch coverage
51%
Covered branches: 45
Total branches: 88
Branch coverage: 51.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
IsShortcut(...)100%11100%
ResolveShortcut(...)0%620%
MakeAbsolutePath(...)66.66%301250%
CreateShortcut(...)0%620%
MoveDirectory(...)0%620%
GetFileSystemInfo(...)33.33%12.17644.44%
GetFileInfo(...)100%11100%
GetDirectoryInfo(...)100%11100%
GetFileSystemMetadata(...)75%9.12874.07%
GetValidFilename(...)100%22100%
GetCreationTimeUtc(...)100%1.22140%
GetCreationTimeUtc(...)100%210%
GetCreationTimeUtc(...)100%210%
GetLastWriteTimeUtc(...)100%210%
GetLastWriteTimeUtc(...)100%1.22140%
GetLastWriteTimeUtc(...)100%210%
SetHidden(...)0%7280%
SetAttributes(...)0%156120%
SwapFiles(...)100%210%
ContainsSubPath(...)50%22100%
AreEqual(...)50%22100%
GetFileNameWithoutExtension(...)0%620%
IsPathFile(...)75%4.25475%
DeleteFile(...)100%210%
GetDrives()100%210%
GetDirectories(...)100%210%
GetFiles(...)100%11100%
GetFiles(...)91.66%12.041293.75%
GetFileSystemEntries(...)100%11100%
ToMetadata(...)100%11100%
GetDirectoryPaths(...)100%11100%
GetFilePaths(...)100%11100%
GetFilePaths(...)83.33%47.161237.5%
GetFileSystemEntryPaths(...)100%1.22140%
DirectoryExists(...)100%210%
FileExists(...)100%210%
GetEnumerationOptions(...)100%11100%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/IO/ManagedFileSystem.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.IO;
 5using System.Linq;
 6using System.Security;
 7using Jellyfin.Extensions;
 8using MediaBrowser.Common.Configuration;
 9using MediaBrowser.Model.IO;
 10using Microsoft.Extensions.Logging;
 11
 12namespace Emby.Server.Implementations.IO
 13{
 14    /// <summary>
 15    /// Class ManagedFileSystem.
 16    /// </summary>
 17    public class ManagedFileSystem : IFileSystem
 18    {
 219        private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
 220        private static readonly char[] _invalidPathCharacters =
 221        {
 222            '\"', '<', '>', '|', '\0',
 223            (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
 224            (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
 225            (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
 226            (char)31, ':', '*', '?', '\\', '/'
 227        };
 28
 29        private readonly ILogger<ManagedFileSystem> _logger;
 30        private readonly List<IShortcutHandler> _shortcutHandlers;
 31        private readonly string _tempPath;
 32
 33        /// <summary>
 34        /// Initializes a new instance of the <see cref="ManagedFileSystem"/> class.
 35        /// </summary>
 36        /// <param name="logger">The <see cref="ILogger"/> instance to use.</param>
 37        /// <param name="applicationPaths">The <see cref="IApplicationPaths"/> instance to use.</param>
 38        /// <param name="shortcutHandlers">the <see cref="IShortcutHandler"/>'s to use.</param>
 39        public ManagedFileSystem(
 40            ILogger<ManagedFileSystem> logger,
 41            IApplicationPaths applicationPaths,
 42            IEnumerable<IShortcutHandler> shortcutHandlers)
 43        {
 4144            _logger = logger;
 4145            _tempPath = applicationPaths.TempDirectory;
 4146            _shortcutHandlers = shortcutHandlers.ToList();
 4147        }
 48
 49        /// <summary>
 50        /// Determines whether the specified filename is shortcut.
 51        /// </summary>
 52        /// <param name="filename">The filename.</param>
 53        /// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
 54        /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception>
 55        public virtual bool IsShortcut(string filename)
 56        {
 10057            ArgumentException.ThrowIfNullOrEmpty(filename);
 58
 10059            var extension = Path.GetExtension(filename);
 10060            return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase))
 61        }
 62
 63        /// <summary>
 64        /// Resolves the shortcut.
 65        /// </summary>
 66        /// <param name="filename">The filename.</param>
 67        /// <returns>System.String.</returns>
 68        /// <exception cref="ArgumentNullException"><paramref name="filename"/> is <c>null</c>.</exception>
 69        public virtual string? ResolveShortcut(string filename)
 70        {
 071            ArgumentException.ThrowIfNullOrEmpty(filename);
 72
 073            var extension = Path.GetExtension(filename);
 074            var handler = _shortcutHandlers.Find(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgno
 75
 076            return handler?.Resolve(filename);
 77        }
 78
 79        /// <inheritdoc />
 80        public virtual string MakeAbsolutePath(string folderPath, string filePath)
 81        {
 82            // path is actually a stream
 483            if (string.IsNullOrWhiteSpace(filePath))
 84            {
 085                return filePath;
 86            }
 87
 488            var isAbsolutePath = Path.IsPathRooted(filePath) && (!OperatingSystem.IsWindows() || filePath[0] != '\\');
 89
 490            if (isAbsolutePath)
 91            {
 92                // absolute local path
 193                return filePath;
 94            }
 95
 96            // unc path
 397            if (filePath.StartsWith(@"\\", StringComparison.Ordinal))
 98            {
 099                return filePath;
 100            }
 101
 3102            var filePathSpan = filePath.AsSpan();
 103
 104            // relative path on windows
 3105            if (filePath[0] == '\\')
 106            {
 0107                filePathSpan = filePathSpan.Slice(1);
 108            }
 109
 110            try
 111            {
 3112                return Path.GetFullPath(Path.Join(folderPath, filePathSpan));
 113            }
 0114            catch (ArgumentException)
 115            {
 0116                return filePath;
 117            }
 0118            catch (PathTooLongException)
 119            {
 0120                return filePath;
 121            }
 0122            catch (NotSupportedException)
 123            {
 0124                return filePath;
 125            }
 3126        }
 127
 128        /// <summary>
 129        /// Creates the shortcut.
 130        /// </summary>
 131        /// <param name="shortcutPath">The shortcut path.</param>
 132        /// <param name="target">The target.</param>
 133        /// <exception cref="ArgumentNullException">The shortcutPath or target is null.</exception>
 134        public virtual void CreateShortcut(string shortcutPath, string target)
 135        {
 0136            ArgumentException.ThrowIfNullOrEmpty(shortcutPath);
 0137            ArgumentException.ThrowIfNullOrEmpty(target);
 138
 0139            var extension = Path.GetExtension(shortcutPath);
 0140            var handler = _shortcutHandlers.Find(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgno
 141
 0142            if (handler is not null)
 143            {
 0144                handler.Create(shortcutPath, target);
 145            }
 146            else
 147            {
 0148                throw new NotImplementedException();
 149            }
 150        }
 151
 152        /// <inheritdoc />
 153        public void MoveDirectory(string source, string destination)
 154        {
 155            try
 156            {
 0157                Directory.Move(source, destination);
 0158            }
 0159            catch (IOException)
 160            {
 161                // Cross device move requires a copy
 0162                Directory.CreateDirectory(destination);
 0163                foreach (string file in Directory.GetFiles(source))
 164                {
 0165                    File.Copy(file, Path.Combine(destination, Path.GetFileName(file)), true);
 166                }
 167
 0168                Directory.Delete(source, true);
 0169            }
 0170        }
 171
 172        /// <summary>
 173        /// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
 174        /// </summary>
 175        /// <param name="path">A path to a file or directory.</param>
 176        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 177        /// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
 178        /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will refl
 179        public virtual FileSystemMetadata GetFileSystemInfo(string path)
 180        {
 181            // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
 36182            if (Path.HasExtension(path))
 183            {
 0184                var fileInfo = new FileInfo(path);
 185
 0186                if (fileInfo.Exists)
 187                {
 0188                    return GetFileSystemMetadata(fileInfo);
 189                }
 190
 0191                return GetFileSystemMetadata(new DirectoryInfo(path));
 192            }
 193            else
 194            {
 36195                var fileInfo = new DirectoryInfo(path);
 196
 36197                if (fileInfo.Exists)
 198                {
 36199                    return GetFileSystemMetadata(fileInfo);
 200                }
 201
 0202                return GetFileSystemMetadata(new FileInfo(path));
 203            }
 204        }
 205
 206        /// <summary>
 207        /// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
 208        /// </summary>
 209        /// <param name="path">A path to a file.</param>
 210        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 211        /// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> ob
 212        /// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> proper
 213        /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></r
 214        public virtual FileSystemMetadata GetFileInfo(string path)
 215        {
 1216            var fileInfo = new FileInfo(path);
 217
 1218            return GetFileSystemMetadata(fileInfo);
 219        }
 220
 221        /// <summary>
 222        /// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
 223        /// </summary>
 224        /// <param name="path">A path to a directory.</param>
 225        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 226        /// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object'
 227        /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetad
 228        /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></r
 229        public virtual FileSystemMetadata GetDirectoryInfo(string path)
 230        {
 112231            var fileInfo = new DirectoryInfo(path);
 232
 112233            return GetFileSystemMetadata(fileInfo);
 234        }
 235
 236        private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
 237        {
 272238            var result = new FileSystemMetadata
 272239            {
 272240                Exists = info.Exists,
 272241                FullName = info.FullName,
 272242                Extension = info.Extension,
 272243                Name = info.Name
 272244            };
 245
 272246            if (result.Exists)
 247            {
 272248                result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttrib
 249
 250                // if (!result.IsDirectory)
 251                // {
 252                //    result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
 253                // }
 254
 272255                if (info is FileInfo fileInfo)
 256                {
 13257                    result.Length = fileInfo.Length;
 258
 259                    // Issue #2354 get the size of files behind symbolic links. Also Enum.HasFlag is bad as it boxes!
 13260                    if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
 261                    {
 262                        try
 263                        {
 1264                            using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, F
 265                            {
 0266                                result.Length = RandomAccess.GetLength(fileHandle);
 0267                            }
 0268                        }
 1269                        catch (FileNotFoundException ex)
 270                        {
 271                            // Dangling symlinks cannot be detected before opening the file unfortunately...
 1272                            _logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the fil
 1273                            result.Exists = false;
 1274                        }
 0275                        catch (UnauthorizedAccessException ex)
 276                        {
 0277                            _logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fi
 0278                        }
 279                    }
 280                }
 281
 272282                result.CreationTimeUtc = GetCreationTimeUtc(info);
 272283                result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
 284            }
 285            else
 286            {
 0287                result.IsDirectory = info is DirectoryInfo;
 288            }
 289
 272290            return result;
 291        }
 292
 293        /// <summary>
 294        /// Takes a filename and removes invalid characters.
 295        /// </summary>
 296        /// <param name="filename">The filename.</param>
 297        /// <returns>System.String.</returns>
 298        /// <exception cref="ArgumentNullException">The filename is null.</exception>
 299        public string GetValidFilename(string filename)
 300        {
 6301            var first = filename.IndexOfAny(_invalidPathCharacters);
 6302            if (first == -1)
 303            {
 304                // Fast path for clean strings
 3305                return filename;
 306            }
 307
 3308            return string.Create(
 3309                filename.Length,
 3310                (filename, _invalidPathCharacters, first),
 3311                (chars, state) =>
 3312                {
 3313                    state.filename.AsSpan().CopyTo(chars);
 3314
 3315                    chars[state.first++] = ' ';
 3316
 3317                    var len = chars.Length;
 3318                    foreach (var c in state._invalidPathCharacters)
 3319                    {
 3320                        for (int i = state.first; i < len; i++)
 3321                        {
 3322                            if (chars[i] == c)
 3323                            {
 3324                                chars[i] = ' ';
 3325                            }
 3326                        }
 3327                    }
 3328                });
 329        }
 330
 331        /// <summary>
 332        /// Gets the creation time UTC.
 333        /// </summary>
 334        /// <param name="info">The info.</param>
 335        /// <returns>DateTime.</returns>
 336        public DateTime GetCreationTimeUtc(FileSystemInfo info)
 337        {
 338            // This could throw an error on some file systems that have dates out of range
 339            try
 340            {
 272341                return info.CreationTimeUtc;
 342            }
 0343            catch (Exception ex)
 344            {
 0345                _logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName);
 0346                return DateTime.MinValue;
 347            }
 272348        }
 349
 350        /// <inheritdoc />
 351        public virtual DateTime GetCreationTimeUtc(string path)
 352        {
 0353            return GetCreationTimeUtc(GetFileSystemInfo(path));
 354        }
 355
 356        /// <inheritdoc />
 357        public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info)
 358        {
 0359            return info.CreationTimeUtc;
 360        }
 361
 362        /// <inheritdoc />
 363        public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
 364        {
 0365            return info.LastWriteTimeUtc;
 366        }
 367
 368        /// <summary>
 369        /// Gets the creation time UTC.
 370        /// </summary>
 371        /// <param name="info">The info.</param>
 372        /// <returns>DateTime.</returns>
 373        public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
 374        {
 375            // This could throw an error on some file systems that have dates out of range
 376            try
 377            {
 272378                return info.LastWriteTimeUtc;
 379            }
 0380            catch (Exception ex)
 381            {
 0382                _logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName);
 0383                return DateTime.MinValue;
 384            }
 272385        }
 386
 387        /// <inheritdoc />
 388        public virtual DateTime GetLastWriteTimeUtc(string path)
 389        {
 0390            return GetLastWriteTimeUtc(GetFileSystemInfo(path));
 391        }
 392
 393        /// <inheritdoc />
 394        public virtual void SetHidden(string path, bool isHidden)
 395        {
 0396            if (!OperatingSystem.IsWindows())
 397            {
 0398                return;
 399            }
 400
 0401            var info = new FileInfo(path);
 402
 0403            if (info.Exists &&
 0404                (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden != isHidden)
 405            {
 0406                if (isHidden)
 407                {
 0408                    File.SetAttributes(path, info.Attributes | FileAttributes.Hidden);
 409                }
 410                else
 411                {
 0412                    File.SetAttributes(path, info.Attributes & ~FileAttributes.Hidden);
 413                }
 414            }
 0415        }
 416
 417        /// <inheritdoc />
 418        public virtual void SetAttributes(string path, bool isHidden, bool readOnly)
 419        {
 0420            if (!OperatingSystem.IsWindows())
 421            {
 0422                return;
 423            }
 424
 0425            var info = new FileInfo(path);
 426
 0427            if (!info.Exists)
 428            {
 0429                return;
 430            }
 431
 0432            if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly == readOnly
 0433                && (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden == isHidden)
 434            {
 0435                return;
 436            }
 437
 0438            var attributes = info.Attributes;
 439
 0440            if (readOnly)
 441            {
 0442                attributes |= FileAttributes.ReadOnly;
 443            }
 444            else
 445            {
 0446                attributes &= ~FileAttributes.ReadOnly;
 447            }
 448
 0449            if (isHidden)
 450            {
 0451                attributes |= FileAttributes.Hidden;
 452            }
 453            else
 454            {
 0455                attributes &= ~FileAttributes.Hidden;
 456            }
 457
 0458            File.SetAttributes(path, attributes);
 0459        }
 460
 461        /// <inheritdoc />
 462        public virtual void SwapFiles(string file1, string file2)
 463        {
 0464            ArgumentException.ThrowIfNullOrEmpty(file1);
 0465            ArgumentException.ThrowIfNullOrEmpty(file2);
 466
 0467            var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
 468
 469            // Copying over will fail against hidden files
 0470            SetHidden(file1, false);
 0471            SetHidden(file2, false);
 472
 0473            Directory.CreateDirectory(_tempPath);
 0474            File.Copy(file1, temp1, true);
 475
 0476            File.Copy(file2, file1, true);
 0477            File.Move(temp1, file2, true);
 0478        }
 479
 480        /// <inheritdoc />
 481        public virtual bool ContainsSubPath(string parentPath, string path)
 482        {
 1483            ArgumentException.ThrowIfNullOrEmpty(parentPath);
 1484            ArgumentException.ThrowIfNullOrEmpty(path);
 485
 1486            return path.Contains(
 1487                Path.TrimEndingDirectorySeparator(parentPath) + Path.DirectorySeparatorChar,
 1488                _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 489        }
 490
 491        /// <inheritdoc />
 492        public virtual bool AreEqual(string path1, string path2)
 493        {
 147494            return Path.TrimEndingDirectorySeparator(path1).Equals(
 147495                Path.TrimEndingDirectorySeparator(path2),
 147496                _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 497        }
 498
 499        /// <inheritdoc />
 500        public virtual string GetFileNameWithoutExtension(FileSystemMetadata info)
 501        {
 0502            if (info.IsDirectory)
 503            {
 0504                return info.Name;
 505            }
 506
 0507            return Path.GetFileNameWithoutExtension(info.FullName);
 508        }
 509
 510        /// <inheritdoc />
 511        public virtual bool IsPathFile(string path)
 512        {
 1680513            if (path.Contains("://", StringComparison.OrdinalIgnoreCase)
 1680514                && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
 515            {
 0516                return false;
 517            }
 518
 1680519            return true;
 520        }
 521
 522        /// <inheritdoc />
 523        public virtual void DeleteFile(string path)
 524        {
 0525            SetAttributes(path, false, false);
 0526            File.Delete(path);
 0527        }
 528
 529        /// <inheritdoc />
 530        public virtual IEnumerable<FileSystemMetadata> GetDrives()
 531        {
 532            // check for ready state to avoid waiting for drives to timeout
 533            // some drives on linux have no actual size or are used for other purposes
 0534            return DriveInfo.GetDrives()
 0535                .Where(
 0536                    d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType
 0537                        && d.IsReady
 0538                        && d.TotalSize != 0)
 0539                .Select(d => new FileSystemMetadata
 0540                {
 0541                    Name = d.Name,
 0542                    FullName = d.RootDirectory.FullName,
 0543                    IsDirectory = true
 0544                });
 545        }
 546
 547        /// <inheritdoc />
 548        public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
 549        {
 0550            return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
 551        }
 552
 553        /// <inheritdoc />
 554        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
 555        {
 1556            return GetFiles(path, null, false, recursive);
 557        }
 558
 559        /// <inheritdoc />
 560        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool ena
 561        {
 5562            var enumerationOptions = GetEnumerationOptions(recursive);
 563
 564            // On linux and osx the search pattern is case sensitive
 565            // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
 5566            if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions
 567            {
 0568                return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], enumerationOptions));
 569            }
 570
 5571            var files = new DirectoryInfo(path).EnumerateFiles("*", enumerationOptions);
 572
 5573            if (extensions is not null && extensions.Count > 0)
 574            {
 4575                files = files.Where(i =>
 4576                {
 4577                    var ext = i.Extension.AsSpan();
 4578                    if (ext.IsEmpty)
 4579                    {
 4580                        return false;
 4581                    }
 4582
 4583                    return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
 4584                });
 585            }
 586
 5587            return ToMetadata(files);
 588        }
 589
 590        /// <inheritdoc />
 591        public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
 592        {
 261593            var directoryInfo = new DirectoryInfo(path);
 261594            var enumerationOptions = GetEnumerationOptions(recursive);
 595
 261596            return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", enumerationOptions));
 597        }
 598
 599        private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
 600        {
 266601            return infos.Select(GetFileSystemMetadata);
 602        }
 603
 604        /// <inheritdoc />
 605        public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
 606        {
 26607            return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
 608        }
 609
 610        /// <inheritdoc />
 611        public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
 612        {
 3613            return GetFilePaths(path, null, false, recursive);
 614        }
 615
 616        /// <inheritdoc />
 617        public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExten
 618        {
 4619            var enumerationOptions = GetEnumerationOptions(recursive);
 620
 621            // On linux and osx the search pattern is case sensitive
 622            // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
 4623            if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions
 624            {
 1625                return Directory.EnumerateFiles(path, "*" + extensions[0], enumerationOptions);
 626            }
 627
 3628            var files = Directory.EnumerateFiles(path, "*", enumerationOptions);
 629
 3630            if (extensions is not null && extensions.Length > 0)
 631            {
 0632                files = files.Where(i =>
 0633                {
 0634                    var ext = Path.GetExtension(i.AsSpan());
 0635                    if (ext.IsEmpty)
 0636                    {
 0637                        return false;
 0638                    }
 0639
 0640                    return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
 0641                });
 642            }
 643
 3644            return files;
 645        }
 646
 647        /// <inheritdoc />
 648        public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
 649        {
 650            try
 651            {
 40652                return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
 653            }
 0654            catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException or SecurityExcept
 655            {
 0656                _logger.LogError(ex, "Failed to enumerate path {Path}", path);
 0657                return Enumerable.Empty<string>();
 658            }
 40659        }
 660
 661        /// <inheritdoc />
 662        public virtual bool DirectoryExists(string path)
 663        {
 0664            return Directory.Exists(path);
 665        }
 666
 667        /// <inheritdoc />
 668        public virtual bool FileExists(string path)
 669        {
 0670            return File.Exists(path);
 671        }
 672
 673        private EnumerationOptions GetEnumerationOptions(bool recursive)
 674        {
 336675            return new EnumerationOptions
 336676            {
 336677                RecurseSubdirectories = recursive,
 336678                IgnoreInaccessible = true,
 336679                // Don't skip any files.
 336680                AttributesToSkip = 0
 336681            };
 682        }
 683    }
 684}

Methods/Properties

.cctor()
.ctor(Microsoft.Extensions.Logging.ILogger`1<Emby.Server.Implementations.IO.ManagedFileSystem>,MediaBrowser.Common.Configuration.IApplicationPaths,System.Collections.Generic.IEnumerable`1<MediaBrowser.Model.IO.IShortcutHandler>)
IsShortcut(System.String)
ResolveShortcut(System.String)
MakeAbsolutePath(System.String,System.String)
CreateShortcut(System.String,System.String)
MoveDirectory(System.String,System.String)
GetFileSystemInfo(System.String)
GetFileInfo(System.String)
GetDirectoryInfo(System.String)
GetFileSystemMetadata(System.IO.FileSystemInfo)
GetValidFilename(System.String)
GetCreationTimeUtc(System.IO.FileSystemInfo)
GetCreationTimeUtc(System.String)
GetCreationTimeUtc(MediaBrowser.Model.IO.FileSystemMetadata)
GetLastWriteTimeUtc(MediaBrowser.Model.IO.FileSystemMetadata)
GetLastWriteTimeUtc(System.IO.FileSystemInfo)
GetLastWriteTimeUtc(System.String)
SetHidden(System.String,System.Boolean)
SetAttributes(System.String,System.Boolean,System.Boolean)
SwapFiles(System.String,System.String)
ContainsSubPath(System.String,System.String)
AreEqual(System.String,System.String)
GetFileNameWithoutExtension(MediaBrowser.Model.IO.FileSystemMetadata)
IsPathFile(System.String)
DeleteFile(System.String)
GetDrives()
GetDirectories(System.String,System.Boolean)
GetFiles(System.String,System.Boolean)
GetFiles(System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.Boolean,System.Boolean)
GetFileSystemEntries(System.String,System.Boolean)
ToMetadata(System.Collections.Generic.IEnumerable`1<System.IO.FileSystemInfo>)
GetDirectoryPaths(System.String,System.Boolean)
GetFilePaths(System.String,System.Boolean)
GetFilePaths(System.String,System.String[],System.Boolean,System.Boolean)
GetFileSystemEntryPaths(System.String,System.Boolean)
DirectoryExists(System.String)
FileExists(System.String)
GetEnumerationOptions(System.Boolean)