Fixed: Replaced mono symlink resolve logic to better handle errors.

This commit is contained in:
Taloth Saldono 2016-02-11 01:13:07 +01:00
parent bd6a38173e
commit 1c92ea58da
5 changed files with 144 additions and 8 deletions

View File

@ -382,6 +382,8 @@ namespace NzbDrone.Common.Disk
} }
public virtual IMount GetMount(string path) public virtual IMount GetMount(string path)
{
try
{ {
var mounts = GetMounts(); var mounts = GetMounts();
@ -390,6 +392,12 @@ namespace NzbDrone.Common.Disk
.OrderByDescending(drive => drive.RootDirectory.Length) .OrderByDescending(drive => drive.RootDirectory.Length)
.FirstOrDefault(); .FirstOrDefault();
} }
catch (Exception ex)
{
Logger.DebugException(string.Format("Failed to get mount for path {0}", path), ex);
return null;
}
}
protected List<IMount> GetDriveInfoMounts() protected List<IMount> GetDriveInfoMounts()
{ {

View File

@ -16,10 +16,19 @@ namespace NzbDrone.Mono
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProvider)); private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProvider));
private readonly IProcMountProvider _procMountProvider; private readonly IProcMountProvider _procMountProvider;
private readonly ISymbolicLinkResolver _symLinkResolver;
public DiskProvider(IProcMountProvider procMountProvider) public DiskProvider(IProcMountProvider procMountProvider, ISymbolicLinkResolver symLinkResolver)
{ {
_procMountProvider = procMountProvider; _procMountProvider = procMountProvider;
_symLinkResolver = symLinkResolver;
}
public override IMount GetMount(string path)
{
path = _symLinkResolver.GetCompleteRealPath(path);
return base.GetMount(path);
} }
public override long? GetAvailableSpace(string path) public override long? GetAvailableSpace(string path)

View File

@ -74,6 +74,7 @@
<Compile Include="ProcMount.cs" /> <Compile Include="ProcMount.cs" />
<Compile Include="ProcMountProvider.cs" /> <Compile Include="ProcMountProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SymbolicLinkResolver.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj"> <ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">

View File

@ -2,11 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using NLog; using NLog;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using Mono.Unix;
namespace NzbDrone.Mono namespace NzbDrone.Mono
{ {

View File

@ -0,0 +1,120 @@
using System;
using System.Text;
using Mono.Unix;
using Mono.Unix.Native;
using NLog;
namespace NzbDrone.Mono
{
public interface ISymbolicLinkResolver
{
string GetCompleteRealPath(string path);
}
// Mono's own implementation doesn't handle exceptions very well.
// All of this code was copied from mono with minor changes.
public class SymbolicLinkResolver : ISymbolicLinkResolver
{
private readonly Logger _logger;
public SymbolicLinkResolver(Logger logger)
{
_logger = logger;
}
public string GetCompleteRealPath(string path)
{
if (path == null) return null;
try
{
string[] dirs;
int lastIndex;
GetPathComponents(path, out dirs, out lastIndex);
var realPath = new StringBuilder();
if (dirs.Length > 0)
{
var dir = UnixPath.IsPathRooted(path) ? "/" : "";
dir += dirs[0];
realPath.Append(GetRealPath(dir));
}
for (var i = 1; i < lastIndex; ++i)
{
realPath.Append("/").Append(dirs[i]);
var realSubPath = GetRealPath(realPath.ToString());
realPath.Remove(0, realPath.Length);
realPath.Append(realSubPath);
}
return realPath.ToString();
}
catch (Exception ex)
{
_logger.DebugException(string.Format("Failed to check for symlinks in the path {0}", path), ex);
return path;
}
}
private static void GetPathComponents(string path, out string[] components, out int lastIndex)
{
var dirs = path.Split(UnixPath.DirectorySeparatorChar);
var target = 0;
for (var i = 0; i < dirs.Length; ++i)
{
if (dirs[i] == "." || dirs[i] == string.Empty)
{
continue;
}
if (dirs[i] == "..")
{
if (target != 0)
{
target--;
}
else
{
target++;
}
}
else
{
dirs[target++] = dirs[i];
}
}
components = dirs;
lastIndex = target;
}
public string GetRealPath(string path)
{
do
{
var link = UnixPath.TryReadLink(path);
if (link == null)
{
var errno = Stdlib.GetLastError();
if (errno != Errno.EINVAL)
{
_logger.Trace("Checking path {0} for symlink returned error {1}, assuming it's not a symlink.", path, errno);
}
return path;
}
if (UnixPath.IsPathRooted(link))
{
path = link;
}
else
{
path = UnixPath.GetDirectoryName(path) + UnixPath.DirectorySeparatorChar + link;
path = UnixPath.GetCanonicalPath(path);
}
} while (true);
}
}
}