From 1b3241121946c09d53601cbc6eb03921c56e1220 Mon Sep 17 00:00:00 2001 From: Kyse Date: Tue, 9 May 2017 15:07:24 -0400 Subject: [PATCH] New: Health Check warning if series folder is mounted with 'ro' option on linux Closes #1867 --- src/NzbDrone.Common/Disk/DriveInfoMount.cs | 8 +++-- src/NzbDrone.Common/Disk/IMount.cs | 2 ++ src/NzbDrone.Common/Disk/MountOptions.cs | 16 +++++++++ src/NzbDrone.Common/NzbDrone.Common.csproj | 1 + .../HealthCheck/Checks/MountCheck.cs | 36 +++++++++++++++++++ src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + src/NzbDrone.Mono/Disk/DiskProvider.cs | 13 ++++--- src/NzbDrone.Mono/Disk/ProcMount.cs | 7 ++-- src/NzbDrone.Mono/Disk/ProcMountProvider.cs | 2 +- 9 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/NzbDrone.Common/Disk/MountOptions.cs create mode 100644 src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs diff --git a/src/NzbDrone.Common/Disk/DriveInfoMount.cs b/src/NzbDrone.Common/Disk/DriveInfoMount.cs index ac039d719..5e246313b 100644 --- a/src/NzbDrone.Common/Disk/DriveInfoMount.cs +++ b/src/NzbDrone.Common/Disk/DriveInfoMount.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using NzbDrone.Common.Extensions; namespace NzbDrone.Common.Disk @@ -8,10 +9,11 @@ public class DriveInfoMount : IMount private readonly DriveInfo _driveInfo; private readonly DriveType _driveType; - public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown) + public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown, MountOptions mountOptions = null) { _driveInfo = driveInfo; _driveType = driveType; + MountOptions = mountOptions; } public long AvailableFreeSpace => _driveInfo.AvailableFreeSpace; @@ -33,6 +35,8 @@ public DriveType DriveType public bool IsReady => _driveInfo.IsReady; + public MountOptions MountOptions { get; private set; } + public string Name => _driveInfo.Name; public string RootDirectory => _driveInfo.RootDirectory.FullName; diff --git a/src/NzbDrone.Common/Disk/IMount.cs b/src/NzbDrone.Common/Disk/IMount.cs index 285673d69..3b15a4cb2 100644 --- a/src/NzbDrone.Common/Disk/IMount.cs +++ b/src/NzbDrone.Common/Disk/IMount.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; namespace NzbDrone.Common.Disk @@ -8,6 +9,7 @@ public interface IMount string DriveFormat { get; } DriveType DriveType { get; } bool IsReady { get; } + MountOptions MountOptions { get; } string Name { get; } string RootDirectory { get; } long TotalFreeSpace { get; } diff --git a/src/NzbDrone.Common/Disk/MountOptions.cs b/src/NzbDrone.Common/Disk/MountOptions.cs new file mode 100644 index 000000000..749c0a739 --- /dev/null +++ b/src/NzbDrone.Common/Disk/MountOptions.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace NzbDrone.Common.Disk +{ + public class MountOptions + { + private readonly Dictionary _options; + + public MountOptions(Dictionary options) + { + _options = options; + } + + public bool IsReadOnly => _options.ContainsKey("ro"); + } +} diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj index 3650d6d25..162cd5d36 100644 --- a/src/NzbDrone.Common/NzbDrone.Common.csproj +++ b/src/NzbDrone.Common/NzbDrone.Common.csproj @@ -85,6 +85,7 @@ + diff --git a/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs new file mode 100644 index 000000000..263ae2927 --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/MountCheck.cs @@ -0,0 +1,36 @@ +using System.Linq; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + public class MountCheck : HealthCheckBase + { + private readonly IDiskProvider _diskProvider; + private readonly ISeriesService _seriesService; + + public MountCheck(IDiskProvider diskProvider, ISeriesService seriesService) + { + _diskProvider = diskProvider; + _seriesService = seriesService; + } + + public override HealthCheck Check() + { + // Not best for optimization but due to possible symlinks and junctions, we get mounts based on series path so internals can handle mount resolution. + var mounts = _seriesService.GetAllSeries() + .Select(series => _diskProvider.GetMount(series.Path)) + .DistinctBy(m => m.RootDirectory) + .Where(m => m.MountOptions != null && m.MountOptions.IsReadOnly) + .ToList(); + + if (mounts.Any()) + { + return new HealthCheck(GetType(), HealthCheckResult.Error, "Mount containing a series path is mounted read-only: " + string.Join(",", mounts.Select(m => m.Name)), "#series-mount-ro"); + } + + return new HealthCheck(GetType()); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index cdc0c3ea3..29b2dcc0e 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -557,6 +557,7 @@ + diff --git a/src/NzbDrone.Mono/Disk/DiskProvider.cs b/src/NzbDrone.Mono/Disk/DiskProvider.cs index d9f3cba29..27bdaf60c 100644 --- a/src/NzbDrone.Mono/Disk/DiskProvider.cs +++ b/src/NzbDrone.Mono/Disk/DiskProvider.cs @@ -86,11 +86,14 @@ public override void SetPermissions(string path, string mask, string user, strin public override List GetMounts() { - return GetDriveInfoMounts().Select(d => new DriveInfoMount(d, FindDriveType.Find(d.DriveFormat))) - .Where(d => d.DriveType == DriveType.Fixed || d.DriveType == DriveType.Network || d.DriveType == DriveType.Removable) - .Concat(_procMountProvider.GetMounts()) - .DistinctBy(v => v.RootDirectory) - .ToList(); + return _procMountProvider.GetMounts() + .Concat(GetDriveInfoMounts() + .Select(d => new DriveInfoMount(d, FindDriveType.Find(d.DriveFormat))) + .Where(d => d.DriveType == DriveType.Fixed || + d.DriveType == DriveType.Network || d.DriveType == + DriveType.Removable)) + .DistinctBy(v => v.RootDirectory) + .ToList(); } public override long? GetTotalSize(string path) diff --git a/src/NzbDrone.Mono/Disk/ProcMount.cs b/src/NzbDrone.Mono/Disk/ProcMount.cs index 87e428112..a7157ac24 100644 --- a/src/NzbDrone.Mono/Disk/ProcMount.cs +++ b/src/NzbDrone.Mono/Disk/ProcMount.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using Mono.Unix; using NzbDrone.Common.Disk; @@ -10,12 +10,13 @@ public class ProcMount : IMount { private readonly UnixDriveInfo _unixDriveInfo; - public ProcMount(DriveType driveType, string name, string mount, string type, Dictionary options) + public ProcMount(DriveType driveType, string name, string mount, string type, MountOptions mountOptions) { DriveType = driveType; Name = name; RootDirectory = mount; DriveFormat = type; + MountOptions = mountOptions; _unixDriveInfo = new UnixDriveInfo(mount); } @@ -28,6 +29,8 @@ public ProcMount(DriveType driveType, string name, string mount, string type, Di public bool IsReady => _unixDriveInfo.IsReady; + public MountOptions MountOptions { get; private set; } + public string Name { get; private set; } public string RootDirectory { get; private set; } diff --git a/src/NzbDrone.Mono/Disk/ProcMountProvider.cs b/src/NzbDrone.Mono/Disk/ProcMountProvider.cs index caa9cc467..ded2d2b36 100644 --- a/src/NzbDrone.Mono/Disk/ProcMountProvider.cs +++ b/src/NzbDrone.Mono/Disk/ProcMountProvider.cs @@ -130,7 +130,7 @@ private IMount ParseLine(string line) driveType = DriveType.Network; } - return new ProcMount(driveType, name, mount, type, options); + return new ProcMount(driveType, name, mount, type, new MountOptions(options)); } private Dictionary ParseOptions(string options)