mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 04:59:35 -07:00
Fixed: Workaround for mono 6.x file copy/move issues
This commit is contained in:
parent
a1e0b39495
commit
c54140169b
1 changed files with 136 additions and 12 deletions
|
@ -8,8 +8,8 @@ using Mono.Unix.Native;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
|
||||||
|
|
||||||
namespace NzbDrone.Mono.Disk
|
namespace NzbDrone.Mono.Disk
|
||||||
{
|
{
|
||||||
|
@ -19,24 +19,26 @@ namespace NzbDrone.Mono.Disk
|
||||||
// `unchecked((uint)-1)` and `uint.MaxValue` are the same thing.
|
// `unchecked((uint)-1)` and `uint.MaxValue` are the same thing.
|
||||||
private const uint UNCHANGED_ID = uint.MaxValue;
|
private const uint UNCHANGED_ID = uint.MaxValue;
|
||||||
|
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(DiskProvider));
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private readonly IProcMountProvider _procMountProvider;
|
private readonly IProcMountProvider _procMountProvider;
|
||||||
private readonly ISymbolicLinkResolver _symLinkResolver;
|
private readonly ISymbolicLinkResolver _symLinkResolver;
|
||||||
|
|
||||||
public DiskProvider(IProcMountProvider procMountProvider,
|
public DiskProvider(IProcMountProvider procMountProvider,
|
||||||
ISymbolicLinkResolver symLinkResolver)
|
ISymbolicLinkResolver symLinkResolver,
|
||||||
: this(new FileSystem(), procMountProvider, symLinkResolver)
|
Logger logger)
|
||||||
|
: this(new FileSystem(), procMountProvider, symLinkResolver, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiskProvider(IFileSystem fileSystem,
|
public DiskProvider(IFileSystem fileSystem,
|
||||||
IProcMountProvider procMountProvider,
|
IProcMountProvider procMountProvider,
|
||||||
ISymbolicLinkResolver symLinkResolver)
|
ISymbolicLinkResolver symLinkResolver,
|
||||||
|
Logger logger)
|
||||||
: base(fileSystem)
|
: base(fileSystem)
|
||||||
{
|
{
|
||||||
_procMountProvider = procMountProvider;
|
_procMountProvider = procMountProvider;
|
||||||
_symLinkResolver = symLinkResolver;
|
_symLinkResolver = symLinkResolver;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IMount GetMount(string path)
|
public override IMount GetMount(string path)
|
||||||
|
@ -50,13 +52,13 @@ namespace NzbDrone.Mono.Disk
|
||||||
{
|
{
|
||||||
Ensure.That(path, () => path).IsValidPath();
|
Ensure.That(path, () => path).IsValidPath();
|
||||||
|
|
||||||
Logger.Debug($"path: {path}");
|
_logger.Debug($"path: {path}");
|
||||||
|
|
||||||
var mount = GetMount(path);
|
var mount = GetMount(path);
|
||||||
|
|
||||||
if (mount == null)
|
if (mount == null)
|
||||||
{
|
{
|
||||||
Logger.Debug("Unable to get free space for '{0}', unable to find suitable drive", path);
|
_logger.Debug("Unable to get free space for '{0}', unable to find suitable drive", path);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ namespace NzbDrone.Mono.Disk
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Debug(ex, "Failed to copy permissions from {0} to {1}", sourcePath, targetPath);
|
_logger.Debug(ex, "Failed to copy permissions from {0} to {1}", sourcePath, targetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +180,12 @@ namespace NzbDrone.Mono.Disk
|
||||||
newFile.CreateSymbolicLinkTo(fullPath);
|
newFile.CreateSymbolicLinkTo(fullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 0)) ||
|
||||||
|
PlatformInfo.Platform == PlatformType.NetCore) &&
|
||||||
|
(!FileExists(destination) || overwrite))
|
||||||
|
{
|
||||||
|
TransferFilePatched(source, destination, overwrite, false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
base.CopyFileInternal(source, destination, overwrite);
|
base.CopyFileInternal(source, destination, overwrite);
|
||||||
|
@ -219,12 +227,128 @@ namespace NzbDrone.Mono.Disk
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 0)) ||
|
||||||
|
PlatformInfo.Platform == PlatformType.NetCore)
|
||||||
|
{
|
||||||
|
TransferFilePatched(source, destination, overwrite, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
base.MoveFileInternal(source, destination, overwrite);
|
base.MoveFileInternal(source, destination, overwrite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TransferFilePatched(string source, string destination, bool overwrite, bool move)
|
||||||
|
{
|
||||||
|
// Mono 6.x throws errors if permissions or timestamps cannot be set
|
||||||
|
// - In 6.0 it'll leave a full length file
|
||||||
|
// - In 6.6 it'll leave a zero length file
|
||||||
|
// Catch the exception and attempt to handle these edgecases
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (move)
|
||||||
|
{
|
||||||
|
base.MoveFileInternal(source, destination, overwrite);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.CopyFileInternal(source, destination, overwrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
var srcInfo = new FileInfo(source);
|
||||||
|
var dstInfo = new FileInfo(destination);
|
||||||
|
var exists = dstInfo.Exists && srcInfo.Exists;
|
||||||
|
|
||||||
|
if (PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 6) &&
|
||||||
|
exists && dstInfo.Length == 0 && srcInfo.Length != 0)
|
||||||
|
{
|
||||||
|
// mono >=6.6 bug: zero length file since chmod happens at the start
|
||||||
|
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Trace("Copying content from {0} to {1} ({2} bytes)", source, destination, srcInfo.Length);
|
||||||
|
using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read))
|
||||||
|
using (var dstStream = new FileStream(destination, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
srcStream.CopyTo(dstStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If it fails again then bail
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (((PlatformInfo.Platform == PlatformType.Mono &&
|
||||||
|
PlatformInfo.GetVersion() >= new Version(6, 0) &&
|
||||||
|
PlatformInfo.GetVersion() < new Version(6, 6)) ||
|
||||||
|
PlatformInfo.Platform == PlatformType.NetCore) &&
|
||||||
|
exists && dstInfo.Length == srcInfo.Length)
|
||||||
|
{
|
||||||
|
// mono 6.0, mono 6.4 and netcore 3.1 bug: full length file since utime and chmod happens at the end
|
||||||
|
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||||
|
|
||||||
|
// Check at least part of the file since UnauthorizedAccess can happen due to legitimate reasons too
|
||||||
|
var checkLength = (int)Math.Min(64 * 1024, dstInfo.Length);
|
||||||
|
if (checkLength > 0)
|
||||||
|
{
|
||||||
|
var srcData = new byte[checkLength];
|
||||||
|
var dstData = new byte[checkLength];
|
||||||
|
|
||||||
|
_logger.Trace("Check last {0} bytes from {1}", checkLength, destination);
|
||||||
|
|
||||||
|
using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read))
|
||||||
|
using (var dstStream = new FileStream(destination, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
srcStream.Position = srcInfo.Length - checkLength;
|
||||||
|
dstStream.Position = dstInfo.Length - checkLength;
|
||||||
|
|
||||||
|
srcStream.Read(srcData, 0, checkLength);
|
||||||
|
dstStream.Read(dstData, 0, checkLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < checkLength; i++)
|
||||||
|
{
|
||||||
|
if (srcData[i] != dstData[i])
|
||||||
|
{
|
||||||
|
// Files aren't the same, the UnauthorizedAccess was unrelated
|
||||||
|
_logger.Trace("Copy was incomplete, rethrowing original error");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Trace("Copy was complete, finishing {0} operation", move ? "move" : "copy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Unrecognized situation, the UnauthorizedAccess was unrelated
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dstInfo.LastWriteTimeUtc = srcInfo.LastWriteTimeUtc;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_logger.Debug("Unable to change last modified date for {0}, skipping.", destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move)
|
||||||
|
{
|
||||||
|
_logger.Trace("Removing source file {0}", source);
|
||||||
|
File.Delete(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool TryCreateHardLink(string source, string destination)
|
public override bool TryCreateHardLink(string source, string destination)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -241,14 +365,14 @@ namespace NzbDrone.Mono.Disk
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Debug(ex, string.Format("Hardlink '{0}' to '{1}' failed.", source, destination));
|
_logger.Debug(ex, string.Format("Hardlink '{0}' to '{1}' failed.", source, destination));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPermissions(string path, string mask)
|
private void SetPermissions(string path, string mask)
|
||||||
{
|
{
|
||||||
Logger.Debug("Setting permissions: {0} on {1}", mask, path);
|
_logger.Debug("Setting permissions: {0} on {1}", mask, path);
|
||||||
|
|
||||||
var filePermissions = NativeConvert.FromOctalPermissionString(mask);
|
var filePermissions = NativeConvert.FromOctalPermissionString(mask);
|
||||||
|
|
||||||
|
@ -264,7 +388,7 @@ namespace NzbDrone.Mono.Disk
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(user) && string.IsNullOrWhiteSpace(group))
|
if (string.IsNullOrWhiteSpace(user) && string.IsNullOrWhiteSpace(group))
|
||||||
{
|
{
|
||||||
Logger.Debug("User and Group for chown not configured, skipping chown.");
|
_logger.Debug("User and Group for chown not configured, skipping chown.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue