< 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
56%
Covered lines: 147
Uncovered lines: 112
Coverable lines: 259
Total lines: 709
Line coverage: 56.7%
Branch coverage
53%
Covered branches: 48
Total branches: 90
Branch coverage: 53.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

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        {
 4244            _logger = logger;
 4245            _tempPath = applicationPaths.TempDirectory;
 4246            _shortcutHandlers = shortcutHandlers.ToList();
 4247        }
 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        {
 12957            ArgumentException.ThrowIfNullOrEmpty(filename);
 58
 12959            var extension = Path.GetExtension(filename);
 12960            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            {
 2157                Directory.Move(source, destination);
 1158            }
 1159            catch (IOException)
 160            {
 161                // Cross device move requires a copy
 1162                Directory.CreateDirectory(destination);
 1163                var sourceDir = new DirectoryInfo(source);
 6164                foreach (var file in sourceDir.EnumerateFiles())
 165                {
 2166                    file.CopyTo(Path.Combine(destination, file.Name), true);
 167                }
 168
 1169                sourceDir.Delete(true);
 1170            }
 2171        }
 172
 173        /// <summary>
 174        /// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path.
 175        /// </summary>
 176        /// <param name="path">A path to a file or directory.</param>
 177        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 178        /// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's
 179        /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will refl
 180        public virtual FileSystemMetadata GetFileSystemInfo(string path)
 181        {
 182            // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
 131183            if (Path.HasExtension(path))
 184            {
 0185                var fileInfo = new FileInfo(path);
 186
 0187                if (fileInfo.Exists)
 188                {
 0189                    return GetFileSystemMetadata(fileInfo);
 190                }
 191
 0192                return GetFileSystemMetadata(new DirectoryInfo(path));
 193            }
 194            else
 195            {
 131196                var fileInfo = new DirectoryInfo(path);
 197
 131198                if (fileInfo.Exists)
 199                {
 131200                    return GetFileSystemMetadata(fileInfo);
 201                }
 202
 0203                return GetFileSystemMetadata(new FileInfo(path));
 204            }
 205        }
 206
 207        /// <summary>
 208        /// Returns a <see cref="FileSystemMetadata"/> object for the specified file path.
 209        /// </summary>
 210        /// <param name="path">A path to a file.</param>
 211        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 212        /// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> ob
 213        /// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> proper
 214        /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></r
 215        public virtual FileSystemMetadata GetFileInfo(string path)
 216        {
 1217            var fileInfo = new FileInfo(path);
 218
 1219            return GetFileSystemMetadata(fileInfo);
 220        }
 221
 222        /// <summary>
 223        /// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path.
 224        /// </summary>
 225        /// <param name="path">A path to a directory.</param>
 226        /// <returns>A <see cref="FileSystemMetadata"/> object.</returns>
 227        /// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object'
 228        /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetad
 229        /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></r
 230        public virtual FileSystemMetadata GetDirectoryInfo(string path)
 231        {
 114232            var fileInfo = new DirectoryInfo(path);
 233
 114234            return GetFileSystemMetadata(fileInfo);
 235        }
 236
 237        private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
 238        {
 405239            var result = new FileSystemMetadata
 405240            {
 405241                Exists = info.Exists,
 405242                FullName = info.FullName,
 405243                Extension = info.Extension,
 405244                Name = info.Name
 405245            };
 246
 405247            if (result.Exists)
 248            {
 405249                result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttrib
 250
 251                // if (!result.IsDirectory)
 252                // {
 253                //    result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
 254                // }
 255
 405256                if (info is FileInfo fileInfo)
 257                {
 44258                    result.Length = fileInfo.Length;
 259
 260                    // Issue #2354 get the size of files behind symbolic links. Also Enum.HasFlag is bad as it boxes!
 44261                    if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
 262                    {
 263                        try
 264                        {
 1265                            using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, F
 266                            {
 0267                                result.Length = RandomAccess.GetLength(fileHandle);
 0268                            }
 0269                        }
 1270                        catch (FileNotFoundException ex)
 271                        {
 272                            // Dangling symlinks cannot be detected before opening the file unfortunately...
 1273                            _logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the fil
 1274                            result.Exists = false;
 1275                        }
 0276                        catch (UnauthorizedAccessException ex)
 277                        {
 0278                            _logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fi
 0279                        }
 0280                        catch (IOException ex)
 281                        {
 282                            // IOException generally means the file is not accessible due to filesystem issues
 283                            // Catch this exception and mark the file as not exist to ignore it
 0284                            _logger.LogError(ex, "Reading the file at {Path} failed due to an IO Exception. Marking the 
 0285                            result.Exists = false;
 0286                        }
 287                    }
 288                }
 289
 405290                result.CreationTimeUtc = GetCreationTimeUtc(info);
 405291                result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
 292            }
 293            else
 294            {
 0295                result.IsDirectory = info is DirectoryInfo;
 296            }
 297
 405298            return result;
 299        }
 300
 301        /// <summary>
 302        /// Takes a filename and removes invalid characters.
 303        /// </summary>
 304        /// <param name="filename">The filename.</param>
 305        /// <returns>System.String.</returns>
 306        /// <exception cref="ArgumentNullException">The filename is null.</exception>
 307        public string GetValidFilename(string filename)
 308        {
 7309            var first = filename.IndexOfAny(_invalidPathCharacters);
 7310            if (first == -1)
 311            {
 312                // Fast path for clean strings
 4313                return filename;
 314            }
 315
 3316            return string.Create(
 3317                filename.Length,
 3318                (filename, _invalidPathCharacters, first),
 3319                (chars, state) =>
 3320                {
 3321                    state.filename.AsSpan().CopyTo(chars);
 3322
 3323                    chars[state.first++] = ' ';
 3324
 3325                    var len = chars.Length;
 3326                    foreach (var c in state._invalidPathCharacters)
 3327                    {
 3328                        for (int i = state.first; i < len; i++)
 3329                        {
 3330                            if (chars[i] == c)
 3331                            {
 3332                                chars[i] = ' ';
 3333                            }
 3334                        }
 3335                    }
 3336                });
 337        }
 338
 339        /// <summary>
 340        /// Gets the creation time UTC.
 341        /// </summary>
 342        /// <param name="info">The info.</param>
 343        /// <returns>DateTime.</returns>
 344        public DateTime GetCreationTimeUtc(FileSystemInfo info)
 345        {
 346            // This could throw an error on some file systems that have dates out of range
 347            try
 348            {
 405349                return info.CreationTimeUtc;
 350            }
 0351            catch (Exception ex)
 352            {
 0353                _logger.LogError(ex, "Error determining CreationTimeUtc for {FullName}", info.FullName);
 0354                return DateTime.MinValue;
 355            }
 405356        }
 357
 358        /// <inheritdoc />
 359        public virtual DateTime GetCreationTimeUtc(string path)
 360        {
 0361            return GetCreationTimeUtc(GetFileSystemInfo(path));
 362        }
 363
 364        /// <inheritdoc />
 365        public virtual DateTime GetCreationTimeUtc(FileSystemMetadata info)
 366        {
 0367            return info.CreationTimeUtc;
 368        }
 369
 370        /// <inheritdoc />
 371        public virtual DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
 372        {
 2373            return info.LastWriteTimeUtc;
 374        }
 375
 376        /// <summary>
 377        /// Gets the creation time UTC.
 378        /// </summary>
 379        /// <param name="info">The info.</param>
 380        /// <returns>DateTime.</returns>
 381        public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
 382        {
 383            // This could throw an error on some file systems that have dates out of range
 384            try
 385            {
 405386                return info.LastWriteTimeUtc;
 387            }
 0388            catch (Exception ex)
 389            {
 0390                _logger.LogError(ex, "Error determining LastAccessTimeUtc for {FullName}", info.FullName);
 0391                return DateTime.MinValue;
 392            }
 405393        }
 394
 395        /// <inheritdoc />
 396        public virtual DateTime GetLastWriteTimeUtc(string path)
 397        {
 0398            return GetLastWriteTimeUtc(GetFileSystemInfo(path));
 399        }
 400
 401        /// <inheritdoc />
 402        public virtual void SetHidden(string path, bool isHidden)
 403        {
 0404            if (!OperatingSystem.IsWindows())
 405            {
 0406                return;
 407            }
 408
 0409            var info = new FileInfo(path);
 410
 0411            if (info.Exists &&
 0412                (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden != isHidden)
 413            {
 0414                if (isHidden)
 415                {
 0416                    File.SetAttributes(path, info.Attributes | FileAttributes.Hidden);
 417                }
 418                else
 419                {
 0420                    File.SetAttributes(path, info.Attributes & ~FileAttributes.Hidden);
 421                }
 422            }
 0423        }
 424
 425        /// <inheritdoc />
 426        public virtual void SetAttributes(string path, bool isHidden, bool readOnly)
 427        {
 2428            if (!OperatingSystem.IsWindows())
 429            {
 2430                return;
 431            }
 432
 0433            var info = new FileInfo(path);
 434
 0435            if (!info.Exists)
 436            {
 0437                return;
 438            }
 439
 0440            if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly == readOnly
 0441                && (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden == isHidden)
 442            {
 0443                return;
 444            }
 445
 0446            var attributes = info.Attributes;
 447
 0448            if (readOnly)
 449            {
 0450                attributes |= FileAttributes.ReadOnly;
 451            }
 452            else
 453            {
 0454                attributes &= ~FileAttributes.ReadOnly;
 455            }
 456
 0457            if (isHidden)
 458            {
 0459                attributes |= FileAttributes.Hidden;
 460            }
 461            else
 462            {
 0463                attributes &= ~FileAttributes.Hidden;
 464            }
 465
 0466            File.SetAttributes(path, attributes);
 0467        }
 468
 469        /// <inheritdoc />
 470        public virtual void SwapFiles(string file1, string file2)
 471        {
 0472            ArgumentException.ThrowIfNullOrEmpty(file1);
 0473            ArgumentException.ThrowIfNullOrEmpty(file2);
 474
 0475            var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
 476
 477            // Copying over will fail against hidden files
 0478            SetHidden(file1, false);
 0479            SetHidden(file2, false);
 480
 0481            Directory.CreateDirectory(_tempPath);
 0482            File.Copy(file1, temp1, true);
 483
 0484            File.Copy(file2, file1, true);
 0485            File.Move(temp1, file2, true);
 0486        }
 487
 488        /// <inheritdoc />
 489        public virtual bool ContainsSubPath(string parentPath, string path)
 490        {
 1491            ArgumentException.ThrowIfNullOrEmpty(parentPath);
 1492            ArgumentException.ThrowIfNullOrEmpty(path);
 493
 1494            return path.Contains(
 1495                Path.TrimEndingDirectorySeparator(parentPath) + Path.DirectorySeparatorChar,
 1496                _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 497        }
 498
 499        /// <inheritdoc />
 500        public virtual bool AreEqual(string path1, string path2)
 501        {
 171502            return Path.TrimEndingDirectorySeparator(path1).Equals(
 171503                Path.TrimEndingDirectorySeparator(path2),
 171504                _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
 505        }
 506
 507        /// <inheritdoc />
 508        public virtual string GetFileNameWithoutExtension(FileSystemMetadata info)
 509        {
 0510            if (info.IsDirectory)
 511            {
 0512                return info.Name;
 513            }
 514
 0515            return Path.GetFileNameWithoutExtension(info.FullName);
 516        }
 517
 518        /// <inheritdoc />
 519        public virtual bool IsPathFile(string path)
 520        {
 2045521            if (path.Contains("://", StringComparison.OrdinalIgnoreCase)
 2045522                && !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
 523            {
 0524                return false;
 525            }
 526
 2045527            return true;
 528        }
 529
 530        /// <inheritdoc />
 531        public virtual void DeleteFile(string path)
 532        {
 2533            SetAttributes(path, false, false);
 2534            File.Delete(path);
 2535        }
 536
 537        /// <inheritdoc />
 538        public virtual IEnumerable<FileSystemMetadata> GetDrives()
 539        {
 540            // check for ready state to avoid waiting for drives to timeout
 541            // some drives on linux have no actual size or are used for other purposes
 0542            return DriveInfo.GetDrives()
 0543                .Where(
 0544                    d => (d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType
 0545                         && d.IsReady
 0546                         && d.TotalSize != 0)
 0547                .Select(d => new FileSystemMetadata
 0548                {
 0549                    Name = d.Name,
 0550                    FullName = d.RootDirectory.FullName,
 0551                    IsDirectory = true
 0552                });
 553        }
 554
 555        /// <inheritdoc />
 556        public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
 557        {
 0558            return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
 559        }
 560
 561        /// <inheritdoc />
 562        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
 563        {
 2564            return GetFiles(path, "*", recursive);
 565        }
 566
 567        /// <inheritdoc />
 568        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, string searchPattern, bool recursive = fals
 569        {
 2570            return GetFiles(path, searchPattern, null, false, recursive);
 571        }
 572
 573        /// <inheritdoc />
 574        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string>? extensions, bool ena
 575        {
 13576            return GetFiles(path, "*", extensions, enableCaseSensitiveExtensions, recursive);
 577        }
 578
 579        /// <inheritdoc />
 580        public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, string searchPattern, IReadOnlyList<string>
 581        {
 15582            var enumerationOptions = GetEnumerationOptions(recursive);
 583
 584            // On linux and macOS the search pattern is case-sensitive
 585            // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
 15586            if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions
 587            {
 0588                searchPattern = searchPattern.EndsWith(extensions[0], StringComparison.Ordinal) ? searchPattern : search
 589
 0590                return ToMetadata(new DirectoryInfo(path).EnumerateFiles(searchPattern, enumerationOptions));
 591            }
 592
 15593            var files = new DirectoryInfo(path).EnumerateFiles(searchPattern, enumerationOptions);
 594
 15595            if (extensions is not null && extensions.Count > 0)
 596            {
 13597                files = files.Where(i =>
 13598                {
 13599                    var ext = i.Extension.AsSpan();
 13600                    if (ext.IsEmpty)
 13601                    {
 13602                        return false;
 13603                    }
 13604
 13605                    return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
 13606                });
 607            }
 608
 15609            return ToMetadata(files);
 610        }
 611
 612        /// <inheritdoc />
 613        public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
 614        {
 615            // Note: any of unhandled exceptions thrown by this method may cause the caller to believe the whole path is
 616            // But what causing the exception may be a single file under that path. This could lead to unexpected behavi
 617            // For example, the scanner will remove everything in that path due to unhandled errors.
 247618            var directoryInfo = new DirectoryInfo(path);
 247619            var enumerationOptions = GetEnumerationOptions(recursive);
 620
 247621            return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", enumerationOptions));
 622        }
 623
 624        private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
 625        {
 262626            return infos.Select(GetFileSystemMetadata);
 627        }
 628
 629        /// <inheritdoc />
 630        public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
 631        {
 26632            return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
 633        }
 634
 635        /// <inheritdoc />
 636        public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
 637        {
 3638            return GetFilePaths(path, null, false, recursive);
 639        }
 640
 641        /// <inheritdoc />
 642        public virtual IEnumerable<string> GetFilePaths(string path, string[]? extensions, bool enableCaseSensitiveExten
 643        {
 4644            var enumerationOptions = GetEnumerationOptions(recursive);
 645
 646            // On linux and macOS the search pattern is case-sensitive
 647            // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
 4648            if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions
 649            {
 1650                return Directory.EnumerateFiles(path, "*" + extensions[0], enumerationOptions);
 651            }
 652
 3653            var files = Directory.EnumerateFiles(path, "*", enumerationOptions);
 654
 3655            if (extensions is not null && extensions.Length > 0)
 656            {
 0657                files = files.Where(i =>
 0658                {
 0659                    var ext = Path.GetExtension(i.AsSpan());
 0660                    if (ext.IsEmpty)
 0661                    {
 0662                        return false;
 0663                    }
 0664
 0665                    return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase);
 0666                });
 667            }
 668
 3669            return files;
 670        }
 671
 672        /// <inheritdoc />
 673        public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
 674        {
 675            try
 676            {
 26677                return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
 678            }
 0679            catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException or SecurityExcept
 680            {
 0681                _logger.LogError(ex, "Failed to enumerate path {Path}", path);
 0682                return Enumerable.Empty<string>();
 683            }
 26684        }
 685
 686        /// <inheritdoc />
 687        public virtual bool DirectoryExists(string path)
 688        {
 0689            return Directory.Exists(path);
 690        }
 691
 692        /// <inheritdoc />
 693        public virtual bool FileExists(string path)
 694        {
 0695            return File.Exists(path);
 696        }
 697
 698        private EnumerationOptions GetEnumerationOptions(bool recursive)
 699        {
 318700            return new EnumerationOptions
 318701            {
 318702                RecurseSubdirectories = recursive,
 318703                IgnoreInaccessible = true,
 318704                // Don't skip any files.
 318705                AttributesToSkip = 0
 318706            };
 707        }
 708    }
 709}

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.String,System.Boolean)
GetFiles(System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.Boolean,System.Boolean)
GetFiles(System.String,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)