1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

New: Now checks the file size of moved episodes to verify if the transfer was completed successfully to be able to detect errors with mounted network storage.

This commit is contained in:
Taloth Saldono 2014-11-15 01:42:22 +01:00
parent aa4fca7177
commit 5effca92b8
20 changed files with 747 additions and 289 deletions

View File

@ -10,22 +10,6 @@ namespace NzbDrone.Common.Test.DiskTests
{
public abstract class DiskProviderFixtureBase<TSubject> : TestBase<TSubject> where TSubject : class, IDiskProvider
{
public DirectoryInfo GetFilledTempFolder()
{
var tempFolder = GetTempFilePath();
Directory.CreateDirectory(tempFolder);
File.WriteAllText(Path.Combine(tempFolder, Path.GetRandomFileName()), "RootFile");
var subDir = Path.Combine(tempFolder, Path.GetRandomFileName());
Directory.CreateDirectory(subDir);
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile1");
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile2");
return new DirectoryInfo(tempFolder);
}
[Test]
public void directory_exist_should_be_able_to_find_existing_folder()
{
@ -101,65 +85,9 @@ public void MoveFile_should_not_move_overwrite_itself()
File.WriteAllText(source, "SourceFile1");
Subject.MoveFile(source, source, true);
Assert.Throws<IOException>(() => Subject.MoveFile(source, source, true));
File.Exists(source).Should().BeTrue();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void CopyFolder_should_copy_folder()
{
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(source.FullName, destination.FullName);
VerifyCopy(source.FullName, destination.FullName);
}
[Test]
public void CopyFolder_should_overwrite_existing_folder()
{
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(source.FullName, destination.FullName);
//Delete Random File
destination.GetFiles("*.*", SearchOption.AllDirectories).First().Delete();
Subject.CopyFolder(source.FullName, destination.FullName);
VerifyCopy(source.FullName, destination.FullName);
}
[Test]
public void MoveFolder_should_move_folder()
{
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(original.FullName, source.FullName);
Subject.MoveFolder(source.FullName, destination.FullName);
VerifyMove(original.FullName, source.FullName, destination.FullName);
}
[Test]
public void MoveFolder_should_overwrite_existing_folder()
{
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(original.FullName, source.FullName);
Subject.CopyFolder(original.FullName, destination.FullName);
Subject.MoveFolder(source.FullName, destination.FullName);
VerifyMove(original.FullName, source.FullName, destination.FullName);
}
[Test]
@ -194,72 +122,6 @@ public void should_be_able_to_delete_directory_with_read_only_file()
Directory.Exists(sourceDir).Should().BeFalse();
}
[Test]
public void should_be_able_to_hardlink_file()
{
var sourceDir = GetTempFilePath();
var source = Path.Combine(sourceDir, "test.txt");
var destination = Path.Combine(sourceDir, "destination.txt");
Directory.CreateDirectory(sourceDir);
Subject.WriteAllText(source, "SourceFile");
var result = Subject.TransferFile(source, destination, TransferMode.HardLink);
result.Should().Be(TransferMode.HardLink);
File.AppendAllText(source, "Test");
File.ReadAllText(destination).Should().Be("SourceFileTest");
}
private void DoHardLinkRename(FileShare fileShare)
{
var sourceDir = GetTempFilePath();
var source = Path.Combine(sourceDir, "test.txt");
var destination = Path.Combine(sourceDir, "destination.txt");
var rename = Path.Combine(sourceDir, "rename.txt");
Directory.CreateDirectory(sourceDir);
Subject.WriteAllText(source, "SourceFile");
Subject.TransferFile(source, destination, TransferMode.HardLink);
using (var stream = new FileStream(source, FileMode.Open, FileAccess.Read, fileShare))
{
stream.ReadByte();
Subject.MoveFile(destination, rename);
stream.ReadByte();
}
File.Exists(rename).Should().BeTrue();
File.Exists(destination).Should().BeFalse();
File.AppendAllText(source, "Test");
File.ReadAllText(rename).Should().Be("SourceFileTest");
}
[Test]
public void should_be_able_to_rename_open_hardlinks_with_fileshare_delete()
{
DoHardLinkRename(FileShare.Delete);
}
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_none()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.None));
}
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_write()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.Read));
}
[Test]
public void empty_folder_should_return_folder_modified_date()
{
@ -338,14 +200,6 @@ public void should_be_set_last_file_write()
Subject.FileGetLastWrite(testFile).Should().Be(lastWriteTime);
}
[Test]
[Explicit]
public void check_last_write()
{
Console.WriteLine(Subject.FolderGetLastWrite(GetFilledTempFolder().FullName));
Console.WriteLine(GetFilledTempFolder().LastWriteTimeUtc);
}
[Test]
public void GetParentFolder_should_remove_trailing_slash_before_getting_parent_folder()
{
@ -355,22 +209,51 @@ public void GetParentFolder_should_remove_trailing_slash_before_getting_parent_f
Subject.GetParentFolder(path).Should().Be(parent);
}
private void VerifyCopy(string source, string destination)
private void DoHardLinkRename(FileShare fileShare)
{
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
var sourceDir = GetTempFilePath();
var source = Path.Combine(sourceDir, "test.txt");
var destination = Path.Combine(sourceDir, "destination.txt");
var rename = Path.Combine(sourceDir, "rename.txt");
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
}
Directory.CreateDirectory(sourceDir);
private void VerifyMove(string source, string from, string destination)
File.WriteAllText(source, "SourceFile");
Subject.TryCreateHardLink(source, destination).Should().BeTrue();
using (var stream = new FileStream(source, FileMode.Open, FileAccess.Read, fileShare))
{
Directory.Exists(from).Should().BeFalse();
stream.ReadByte();
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
Subject.MoveFile(destination, rename);
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
stream.ReadByte();
}
File.Exists(rename).Should().BeTrue();
File.Exists(destination).Should().BeFalse();
File.AppendAllText(source, "Test");
File.ReadAllText(rename).Should().Be("SourceFileTest");
}
[Test]
public void should_be_able_to_rename_open_hardlinks_with_fileshare_delete()
{
DoHardLinkRename(FileShare.Delete);
}
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_none()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.None));
}
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_write()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.Read));
}
}
}

View File

@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Test.Common;
using FluentAssertions;
namespace NzbDrone.Common.Test.DiskTests
{
[TestFixture]
public class DiskTransferServiceFixture : TestBase<DiskTransferService>
{
private readonly String _sourcePath = @"C:\source\my.video.mkv".AsOsAgnostic();
private readonly String _targetPath = @"C:\target\my.video.mkv".AsOsAgnostic();
private readonly String _backupPath = @"C:\source\my.video.mkv.backup~".AsOsAgnostic();
private readonly String _tempTargetPath = @"C:\target\my.video.mkv.partial~".AsOsAgnostic();
[SetUp]
public void SetUp()
{
Mocker.GetMock<IDiskProvider>(MockBehavior.Strict);
WithEmulatedDiskProvider();
WithExistingFile(_sourcePath);
}
[Test]
public void should_hardlink_only()
{
WithSuccessfulHardlink(_sourcePath, _targetPath);
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.HardLink);
result.Should().Be(TransferMode.HardLink);
}
[Test]
public void should_throw_if_hardlink_only_failed()
{
WithFailedHardlink();
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.HardLink));
}
[Test]
public void should_retry_if_partial_copy()
{
WithSuccessfulHardlink(_sourcePath, _backupPath);
var retry = 0;
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CopyFile(_sourcePath, _tempTargetPath, false))
.Callback(() =>
{
WithExistingFile(_tempTargetPath, true, 900);
if (retry++ == 1) WithExistingFile(_tempTargetPath, true, 1000);
});
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Copy);
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_retry_twice_if_partial_copy()
{
var retry = 0;
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CopyFile(_sourcePath, _tempTargetPath, false))
.Callback(() =>
{
WithExistingFile(_tempTargetPath, true, 900);
if (retry++ == 3) throw new Exception("Test Failed, retried too many times.");
});
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Copy));
ExceptionVerification.ExpectedWarns(2);
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void should_hardlink_before_move()
{
WithSuccessfulHardlink(_sourcePath, _backupPath);
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.TryCreateHardLink(_sourcePath, _backupPath), Times.Once());
}
[Test]
public void should_remove_source_after_move()
{
WithSuccessfulHardlink(_sourcePath, _backupPath);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(_backupPath, _tempTargetPath, false))
.Callback(() => WithExistingFile(_tempTargetPath, true));
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
VerifyDeletedFile(_sourcePath);
}
[Test]
public void should_remove_backup_if_move_throws()
{
WithSuccessfulHardlink(_sourcePath, _backupPath);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(_backupPath, _tempTargetPath, false))
.Throws(new IOException("Blackbox IO error"));
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move));
VerifyDeletedFile(_backupPath);
}
[Test]
public void should_remove_partial_if_move_fails()
{
WithSuccessfulHardlink(_sourcePath, _backupPath);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(_backupPath, _tempTargetPath, false))
.Callback(() =>
{
WithExistingFile(_backupPath, false);
WithExistingFile(_tempTargetPath, true, 900);
});
Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
VerifyDeletedFile(_tempTargetPath);
}
[Test]
public void should_fallback_to_copy_if_hardlink_failed()
{
WithFailedHardlink();
var result = Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.CopyFile(_sourcePath, _tempTargetPath, false), Times.Once());
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.MoveFile(_tempTargetPath, _targetPath, false), Times.Once());
VerifyDeletedFile(_sourcePath);
}
[Test]
public void CopyFolder_should_copy_folder()
{
WithRealDiskProvider();
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy);
VerifyCopyFolder(source.FullName, destination.FullName);
}
[Test]
public void CopyFolder_should_overwrite_existing_folder()
{
WithRealDiskProvider();
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy);
//Delete Random File
destination.GetFiles("*.*", SearchOption.AllDirectories).First().Delete();
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy);
VerifyCopyFolder(source.FullName, destination.FullName);
}
[Test]
public void MoveFolder_should_move_folder()
{
WithRealDiskProvider();
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Move);
VerifyMoveFolder(original.FullName, source.FullName, destination.FullName);
}
[Test]
public void MoveFolder_should_overwrite_existing_folder()
{
WithRealDiskProvider();
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
Subject.TransferFolder(original.FullName, destination.FullName, TransferMode.Copy);
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Move);
VerifyMoveFolder(original.FullName, source.FullName, destination.FullName);
}
[Test]
public void should_throw_if_destination_is_readonly()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), false))
.Throws(new IOException("Access denied"));
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Copy));
}
[Test]
public void should_throw_if_destination_is_child_of_source()
{
var childPath = Path.Combine(_sourcePath, "child");
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, childPath, TransferMode.Move));
}
public DirectoryInfo GetFilledTempFolder()
{
var tempFolder = GetTempFilePath();
Directory.CreateDirectory(tempFolder);
File.WriteAllText(Path.Combine(tempFolder, Path.GetRandomFileName()), "RootFile");
var subDir = Path.Combine(tempFolder, Path.GetRandomFileName());
Directory.CreateDirectory(subDir);
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile1");
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile2");
return new DirectoryInfo(tempFolder);
}
private void WithExistingFile(string path, bool exists = true, int size = 1000)
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(path))
.Returns(exists);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileSize(path))
.Returns(size);
}
private void WithSuccessfulHardlink(string source, string target)
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.TryCreateHardLink(source, target))
.Callback(() => WithExistingFile(target))
.Returns(true);
}
private void WithFailedHardlink()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.TryCreateHardLink(It.IsAny<String>(), It.IsAny<String>()))
.Returns(false);
}
private void WithEmulatedDiskProvider()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.IsAny<string>()))
.Returns(false);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), false))
.Callback<string, string, bool>((s, d, o) =>
{
WithExistingFile(d);
});
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(It.IsAny<string>(), It.IsAny<string>(), false))
.Callback<string, string, bool>((s, d, o) =>
{
WithExistingFile(s, false);
WithExistingFile(d);
});
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.DeleteFile(It.IsAny<string>()))
.Callback<string>(v =>
{
WithExistingFile(v, false);
});
}
private void WithRealDiskProvider()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FolderExists(It.IsAny<string>()))
.Returns<string>(v => Directory.Exists(v));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.IsAny<string>()))
.Returns<string>(v => File.Exists(v));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CreateFolder(It.IsAny<string>()))
.Callback<string>(v => Directory.CreateDirectory(v));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.DeleteFolder(It.IsAny<string>(), It.IsAny<bool>()))
.Callback<string, bool>((v,r) => Directory.Delete(v, r));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.DeleteFile(It.IsAny<string>()))
.Callback<string>(v => File.Delete(v));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetDirectoryInfos(It.IsAny<string>()))
.Returns<string>(v => new DirectoryInfo(v).GetDirectories().ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileInfos(It.IsAny<string>()))
.Returns<string>(v => new DirectoryInfo(v).GetFiles().ToList());
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.GetFileSize(It.IsAny<string>()))
.Returns<string>(v => new FileInfo(v).Length);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.TryCreateHardLink(It.IsAny<string>(), It.IsAny<string>()))
.Returns(false);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.CopyFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback<string, string, bool>((s, d, o) => File.Copy(s, d, o));
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.MoveFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback<string, string, bool>((s,d,o) => {
if (File.Exists(d) && o) File.Delete(d);
File.Move(s, d);
});
}
private void VerifyCopyFolder(string source, string destination)
{
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
}
private void VerifyMoveFolder(string source, string from, string destination)
{
Directory.Exists(from).Should().BeFalse();
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
}
private void VerifyDeletedFile(String filePath)
{
var path = filePath;
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.DeleteFile(path), Times.Once());
}
}
}

View File

@ -72,6 +72,7 @@
<Compile Include="DiskTests\DiskProviderFixtureBase.cs" />
<Compile Include="DiskTests\FreeSpaceFixtureBase.cs" />
<Compile Include="DiskTests\IsParentFixtureBase.cs" />
<Compile Include="DiskTests\DiskTransferServiceFixture.cs" />
<Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentProviderTest.cs" />
<Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" />

View File

@ -168,62 +168,6 @@ public void CreateFolder(string path)
Directory.CreateDirectory(path);
}
public void CopyFolder(string source, string destination)
{
Ensure.That(source, () => source).IsValidPath();
Ensure.That(destination, () => destination).IsValidPath();
TransferFolder(source, destination, TransferMode.Copy);
}
public void MoveFolder(string source, string destination)
{
Ensure.That(source, () => source).IsValidPath();
Ensure.That(destination, () => destination).IsValidPath();
try
{
TransferFolder(source, destination, TransferMode.Move);
DeleteFolder(source, true);
}
catch (Exception e)
{
e.Data.Add("Source", source);
e.Data.Add("Destination", destination);
throw;
}
}
public void TransferFolder(string source, string destination, TransferMode mode)
{
Ensure.That(source, () => source).IsValidPath();
Ensure.That(destination, () => destination).IsValidPath();
Logger.ProgressDebug("{0} {1} -> {2}", mode, source, destination);
var sourceFolder = new DirectoryInfo(source);
var targetFolder = new DirectoryInfo(destination);
if (!targetFolder.Exists)
{
targetFolder.Create();
}
foreach (var subDir in sourceFolder.GetDirectories())
{
TransferFolder(subDir.FullName, Path.Combine(destination, subDir.Name), mode);
}
foreach (var sourceFile in sourceFolder.GetFiles("*.*", SearchOption.TopDirectoryOnly))
{
var destFile = Path.Combine(destination, sourceFile.Name);
Logger.ProgressDebug("{0} {1} -> {2}", mode, sourceFile, destFile);
TransferFile(sourceFile.FullName, destFile, mode, true);
}
}
public void DeleteFile(string path)
{
Ensure.That(path, () => path).IsValidPath();
@ -236,23 +180,25 @@ public void DeleteFile(string path)
public void CopyFile(string source, string destination, bool overwrite = false)
{
TransferFile(source, destination, TransferMode.Copy, overwrite);
Ensure.That(source, () => source).IsValidPath();
Ensure.That(destination, () => destination).IsValidPath();
if (source.PathEquals(destination))
{
throw new IOException(string.Format("Source and destination can't be the same {0}", source));
}
File.Copy(source, destination, overwrite);
}
public void MoveFile(string source, string destination, bool overwrite = false)
{
TransferFile(source, destination, TransferMode.Move, overwrite);
}
public TransferMode TransferFile(string source, string destination, TransferMode mode, bool overwrite)
{
Ensure.That(source, () => source).IsValidPath();
Ensure.That(destination, () => destination).IsValidPath();
if (source.PathEquals(destination))
{
Logger.Warn("Source and destination can't be the same {0}", source);
return TransferMode.None;
throw new IOException(string.Format("Source and destination can't be the same {0}", source));
}
if (FileExists(destination) && overwrite)
@ -260,33 +206,8 @@ public TransferMode TransferFile(string source, string destination, TransferMode
DeleteFile(destination);
}
if (mode.HasFlag(TransferMode.HardLink))
{
bool createdHardlink = TryCreateHardLink(source, destination);
if (createdHardlink)
{
return TransferMode.HardLink;
}
if (!mode.HasFlag(TransferMode.Copy))
{
throw new IOException("Hardlinking from '" + source + "' to '" + destination + "' failed.");
}
}
if (mode.HasFlag(TransferMode.Copy))
{
File.Copy(source, destination, overwrite);
return TransferMode.Copy;
}
if (mode.HasFlag(TransferMode.Move))
{
RemoveReadOnly(source);
File.Move(source, destination);
return TransferMode.Move;
}
return TransferMode.None;
}
public abstract bool TryCreateHardLink(string source, string destination);

View File

@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using NLog;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Disk
{
public interface IDiskTransferService
{
TransferMode TransferFolder(String sourcePath, String targetPath, TransferMode mode, bool verified = true);
TransferMode TransferFile(String sourcePath, String targetPath, TransferMode mode, bool overwrite = false, bool verified = true);
}
public class DiskTransferService : IDiskTransferService
{
private const Int32 RetryCount = 2;
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public DiskTransferService(IDiskProvider diskProvider, Logger logger)
{
_diskProvider = diskProvider;
_logger = logger;
}
public TransferMode TransferFolder(String sourcePath, String targetPath, TransferMode mode, bool verified = true)
{
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
Ensure.That(targetPath, () => targetPath).IsValidPath();
if (!_diskProvider.FolderExists(targetPath))
{
_diskProvider.CreateFolder(targetPath);
}
var result = mode;
foreach (var subDir in _diskProvider.GetDirectoryInfos(sourcePath))
{
result &= TransferFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name), mode, verified);
}
foreach (var sourceFile in _diskProvider.GetFileInfos(sourcePath))
{
var destFile = Path.Combine(targetPath, sourceFile.Name);
result &= TransferFile(sourceFile.FullName, destFile, mode, true, verified);
}
if (mode.HasFlag(TransferMode.Move))
{
_diskProvider.DeleteFolder(sourcePath, true);
}
return result;
}
public TransferMode TransferFile(String sourcePath, String targetPath, TransferMode mode, bool overwrite = false, bool verified = true)
{
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
Ensure.That(targetPath, () => targetPath).IsValidPath();
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
if (sourcePath.PathEquals(targetPath))
{
throw new IOException(string.Format("Source and destination can't be the same {0}", sourcePath));
}
if (sourcePath.IsParentPath(targetPath))
{
throw new IOException(string.Format("Destination cannot be a child of the source [{0}] => [{1}]", sourcePath, targetPath));
}
if (_diskProvider.FileExists(targetPath) && overwrite)
{
_diskProvider.DeleteFile(targetPath);
}
if (mode.HasFlag(TransferMode.HardLink))
{
var createdHardlink = _diskProvider.TryCreateHardLink(sourcePath, targetPath);
if (createdHardlink)
{
return TransferMode.HardLink;
}
if (!mode.HasFlag(TransferMode.Copy))
{
throw new IOException("Hardlinking from '" + sourcePath + "' to '" + targetPath + "' failed.");
}
}
if (verified)
{
if (mode.HasFlag(TransferMode.Copy))
{
if (TryCopyFile(sourcePath, targetPath))
{
return TransferMode.Copy;
}
}
if (mode.HasFlag(TransferMode.Move))
{
if (TryMoveFile(sourcePath, targetPath))
{
return TransferMode.Move;
}
}
throw new IOException(String.Format("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath));
}
else
{
if (mode.HasFlag(TransferMode.Copy))
{
_diskProvider.CopyFile(sourcePath, targetPath);
return TransferMode.Copy;
}
if (mode.HasFlag(TransferMode.Move))
{
_diskProvider.MoveFile(sourcePath, targetPath);
return TransferMode.Move;
}
}
return TransferMode.None;
}
private Boolean TryCopyFile(String sourcePath, String targetPath)
{
var originalSize = _diskProvider.GetFileSize(sourcePath);
var tempTargetPath = targetPath + ".partial~";
for (var i = 0; i <= RetryCount; i++)
{
_diskProvider.CopyFile(sourcePath, tempTargetPath);
if (_diskProvider.FileExists(tempTargetPath))
{
var targetSize = _diskProvider.GetFileSize(tempTargetPath);
if (targetSize == originalSize)
{
_diskProvider.MoveFile(tempTargetPath, targetPath);
return true;
}
}
Thread.Sleep(5000);
_diskProvider.DeleteFile(tempTargetPath);
if (i == RetryCount)
{
_logger.Error("Failed to completely transfer [{0}] to [{1}], aborting.", sourcePath, targetPath, i + 1, RetryCount);
}
else
{
_logger.Warn("Failed to completely transfer [{0}] to [{1}], retrying [{2}/{3}].", sourcePath, targetPath, i + 1, RetryCount);
}
}
return false;
}
private Boolean TryMoveFile(String sourcePath, String targetPath)
{
var originalSize = _diskProvider.GetFileSize(sourcePath);
var backupPath = sourcePath + ".backup~";
var tempTargetPath = targetPath + ".partial~";
if (_diskProvider.FileExists(backupPath))
{
_logger.Trace("Removing old backup.");
_diskProvider.DeleteFile(backupPath);
}
if (_diskProvider.FileExists(tempTargetPath))
{
_logger.Trace("Removing old partial.");
_diskProvider.DeleteFile(tempTargetPath);
}
try
{
_logger.Trace("Attempting to move hardlinked backup.");
if (_diskProvider.TryCreateHardLink(sourcePath, backupPath))
{
_diskProvider.MoveFile(backupPath, tempTargetPath);
if (_diskProvider.FileExists(tempTargetPath))
{
var targetSize = _diskProvider.GetFileSize(tempTargetPath);
if (targetSize == originalSize)
{
_diskProvider.MoveFile(tempTargetPath, targetPath);
_logger.Trace("Hardlink move succeeded, deleting source.");
_diskProvider.DeleteFile(sourcePath);
return true;
}
}
Thread.Sleep(5000);
_diskProvider.DeleteFile(tempTargetPath);
}
}
finally
{
if (_diskProvider.FileExists(backupPath))
{
_diskProvider.DeleteFile(backupPath);
}
}
_logger.Trace("Hardlink move failed, reverting to copy.");
if (TryCopyFile(sourcePath, targetPath))
{
_logger.Trace("Copy succeeded, deleting source.");
_diskProvider.DeleteFile(sourcePath);
return true;
}
_logger.Trace("Copy failed.");
return false;
}
}
}

View File

@ -25,13 +25,9 @@ public interface IDiskProvider
long GetFolderSize(string path);
long GetFileSize(string path);
void CreateFolder(string path);
void CopyFolder(string source, string destination);
void MoveFolder(string source, string destination);
void TransferFolder(string source, string destination, TransferMode transferMode);
void DeleteFile(string path);
void CopyFile(string source, string destination, bool overwrite = false);
void MoveFile(string source, string destination, bool overwrite = false);
TransferMode TransferFile(string source, string destination, TransferMode transferMode, bool overwrite = false);
bool TryCreateHardLink(string source, string destination);
void DeleteFolder(string path, bool recursive);
string ReadAllText(string filePath);

View File

@ -79,6 +79,7 @@
<Compile Include="Disk\OsPath.cs" />
<Compile Include="Disk\DiskProviderBase.cs" />
<Compile Include="Disk\IDiskProvider.cs" />
<Compile Include="Disk\DiskTransferService.cs" />
<Compile Include="Disk\TransferMode.cs" />
<Compile Include="EnsureThat\Ensure.cs" />
<Compile Include="EnsureThat\EnsureBoolExtensions.cs" />

View File

@ -45,7 +45,8 @@ public void should_use_move_when_recycleBin_is_configured()
Mocker.Resolve<RecycleBinProvider>().DeleteFolder(path);
Mocker.GetMock<IDiskProvider>().Verify(v => v.MoveFolder(path, @"C:\Test\Recycle Bin\30 Rock".AsOsAgnostic()), Times.Once());
Mocker.GetMock<IDiskTransferService>()
.Verify(v => v.TransferFolder(path, @"C:\Test\Recycle Bin\30 Rock".AsOsAgnostic(), TransferMode.Move, true), Times.Once());
}
[Test]

View File

@ -44,7 +44,7 @@ public void should_use_move_when_recycleBin_is_configured()
Mocker.Resolve<RecycleBinProvider>().DeleteFile(path);
Mocker.GetMock<IDiskProvider>().Verify(v => v.MoveFile(path, @"C:\Test\Recycle Bin\S01E01.avi".AsOsAgnostic(), true), Times.Once());
Mocker.GetMock<IDiskTransferService>().Verify(v => v.TransferFile(path, @"C:\Test\Recycle Bin\S01E01.avi".AsOsAgnostic(), TransferMode.Move, false, true), Times.Once());
}
[Test]
@ -60,7 +60,7 @@ public void should_use_alternative_name_if_already_exists()
Mocker.Resolve<RecycleBinProvider>().DeleteFile(path);
Mocker.GetMock<IDiskProvider>().Verify(v => v.MoveFile(path, @"C:\Test\Recycle Bin\S01E01_2.avi".AsOsAgnostic(), true), Times.Once());
Mocker.GetMock<IDiskTransferService>().Verify(v => v.TransferFile(path, @"C:\Test\Recycle Bin\S01E01_2.avi".AsOsAgnostic(), TransferMode.Move, false, true), Times.Once());
}
[Test]

View File

@ -39,8 +39,8 @@ public void Setup()
private void GivenFailedMove()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.MoveFolder(It.IsAny<String>(), It.IsAny<String>()))
Mocker.GetMock<IDiskTransferService>()
.Setup(s => s.TransferFolder(It.IsAny<String>(), It.IsAny<String>(), TransferMode.Move, true))
.Throws<IOException>();
}

View File

@ -135,7 +135,8 @@ public void Should_copy_update_client_to_root_of_sandbox()
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IDiskProvider>().Verify(c => c.MoveFolder(updateClientFolder, _sandboxFolder));
Mocker.GetMock<IDiskTransferService>()
.Verify(c => c.TransferFolder(updateClientFolder, _sandboxFolder, TransferMode.Move, false));
}
[Test]

View File

@ -25,6 +25,7 @@ public interface IBackupService
public class BackupService : IBackupService, IExecute<BackupCommand>
{
private readonly IMainDatabase _maindDb;
private readonly IDiskTransferService _diskTransferService;
private readonly IDiskProvider _diskProvider;
private readonly IAppFolderInfo _appFolderInfo;
private readonly IArchiveService _archiveService;
@ -35,12 +36,14 @@ public class BackupService : IBackupService, IExecute<BackupCommand>
private static readonly Regex BackupFileRegex = new Regex(@"nzbdrone_backup_[._0-9]+\.zip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public BackupService(IMainDatabase maindDb,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IAppFolderInfo appFolderInfo,
IArchiveService archiveService,
Logger logger)
{
_maindDb = maindDb;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_appFolderInfo = appFolderInfo;
_archiveService = archiveService;
@ -115,7 +118,7 @@ private void BackupDatabase()
var databaseFile = _appFolderInfo.GetNzbDroneDatabase();
var tempDatabaseFile = Path.Combine(_backupTempFolder, Path.GetFileName(databaseFile));
_diskProvider.CopyFile(databaseFile, tempDatabaseFile, true);
_diskTransferService.TransferFile(databaseFile, tempDatabaseFile, TransferMode.Copy);
unitOfWork.Commit();
}
@ -128,7 +131,7 @@ private void BackupConfigFile()
var configFile = _appFolderInfo.GetConfigPath();
var tempConfigFile = Path.Combine(_backupTempFolder, Path.GetFileName(configFile));
_diskProvider.CopyFile(configFile, tempConfigFile, true);
_diskTransferService.TransferFile(configFile, tempConfigFile, TransferMode.Copy);
}
private void CleanupOldBackups(BackupType backupType)

View File

@ -27,6 +27,7 @@ public class EpisodeFileMovingService : IMoveEpisodeFiles
private readonly IEpisodeService _episodeService;
private readonly IUpdateEpisodeFileService _updateEpisodeFileService;
private readonly IBuildFileNames _buildFileNames;
private readonly IDiskTransferService _diskTransferService;
private readonly IDiskProvider _diskProvider;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
private readonly IEventAggregator _eventAggregator;
@ -36,6 +37,7 @@ public class EpisodeFileMovingService : IMoveEpisodeFiles
public EpisodeFileMovingService(IEpisodeService episodeService,
IUpdateEpisodeFileService updateEpisodeFileService,
IBuildFileNames buildFileNames,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IMediaFileAttributeService mediaFileAttributeService,
IEventAggregator eventAggregator,
@ -45,6 +47,7 @@ public EpisodeFileMovingService(IEpisodeService episodeService,
_episodeService = episodeService;
_updateEpisodeFileService = updateEpisodeFileService;
_buildFileNames = buildFileNames;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_mediaFileAttributeService = mediaFileAttributeService;
_eventAggregator = eventAggregator;
@ -112,8 +115,7 @@ private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Ep
throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
}
_logger.Debug("{0} [{1}] > [{2}]", mode, episodeFilePath, destinationFilename);
_diskProvider.TransferFile(episodeFilePath, destinationFilename, mode);
_diskTransferService.TransferFile(episodeFilePath, destinationFilename, mode);
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename);

View File

@ -22,13 +22,18 @@ public interface IRecycleBinProvider
public class RecycleBinProvider : IHandleAsync<SeriesDeletedEvent>, IExecute<CleanUpRecycleBinCommand>, IRecycleBinProvider
{
private readonly IDiskTransferService _diskTransferService;
private readonly IDiskProvider _diskProvider;
private readonly IConfigService _configService;
private readonly Logger _logger;
public RecycleBinProvider(IDiskProvider diskProvider, IConfigService configService, Logger logger)
public RecycleBinProvider(IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IConfigService configService,
Logger logger)
{
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_configService = configService;
_logger = logger;
@ -51,7 +56,7 @@ public void DeleteFolder(string path)
var destination = Path.Combine(recyclingBin, new DirectoryInfo(path).Name);
_logger.Debug("Moving '{0}' to '{1}'", path, destination);
_diskProvider.MoveFolder(path, destination);
_diskTransferService.TransferFolder(path, destination, TransferMode.Move);
_logger.Debug("Setting last accessed: {0}", path);
_diskProvider.FolderSetLastWriteTime(destination, DateTime.UtcNow);
@ -106,7 +111,7 @@ public void DeleteFile(string path)
}
_logger.Debug("Moving '{0}' to '{1}'", path, destination);
_diskProvider.MoveFile(path, destination, true);
_diskTransferService.TransferFile(path, destination, TransferMode.Move);
//TODO: Better fix than this for non-Windows?
if (OsInfo.IsWindows)

View File

@ -27,6 +27,7 @@ public class MetadataService : IHandle<MediaCoversUpdatedEvent>,
private readonly ICleanMetadataService _cleanMetadataService;
private readonly IMediaFileService _mediaFileService;
private readonly IEpisodeService _episodeService;
private readonly IDiskTransferService _diskTransferService;
private readonly IDiskProvider _diskProvider;
private readonly IHttpClient _httpClient;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
@ -38,6 +39,7 @@ public MetadataService(IMetadataFactory metadataFactory,
ICleanMetadataService cleanMetadataService,
IMediaFileService mediaFileService,
IEpisodeService episodeService,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IHttpClient httpClient,
IMediaFileAttributeService mediaFileAttributeService,
@ -49,6 +51,7 @@ public MetadataService(IMetadataFactory metadataFactory,
_cleanMetadataService = cleanMetadataService;
_mediaFileService = mediaFileService;
_episodeService = episodeService;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_httpClient = httpClient;
_mediaFileAttributeService = mediaFileAttributeService;
@ -218,7 +221,7 @@ private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Series series, E
var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
if (!fullPath.PathEquals(existingFullPath))
{
_diskProvider.MoveFile(existingFullPath, fullPath);
_diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move);
existingMetadata.RelativePath = episodeMetadata.RelativePath;
}
}
@ -339,7 +342,7 @@ private List<MetadataFile> ProcessEpisodeImages(IMetadata consumer, Series serie
var existingFullPath = Path.Combine(series.Path, existingMetadata.RelativePath);
if (!fullPath.PathEquals(existingFullPath))
{
_diskProvider.MoveFile(fullPath, fullPath);
_diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move);
existingMetadata.RelativePath = image.RelativePath;
return new List<MetadataFile>{ existingMetadata };

View File

@ -16,19 +16,19 @@ public class MoveSeriesService : IExecute<MoveSeriesCommand>
{
private readonly ISeriesService _seriesService;
private readonly IBuildFileNames _filenameBuilder;
private readonly IDiskProvider _diskProvider;
private readonly IDiskTransferService _diskTransferService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
public MoveSeriesService(ISeriesService seriesService,
IBuildFileNames filenameBuilder,
IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IEventAggregator eventAggregator,
Logger logger)
{
_seriesService = seriesService;
_filenameBuilder = filenameBuilder;
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_eventAggregator = eventAggregator;
_logger = logger;
}
@ -50,7 +50,7 @@ public void Execute(MoveSeriesCommand message)
//TODO: Move to transactional disk operations
try
{
_diskProvider.MoveFolder(source, destination);
_diskTransferService.TransferFolder(source, destination, TransferMode.Move);
}
catch (IOException ex)
{

View File

@ -24,6 +24,7 @@ public class InstallUpdateService : IExecute<ApplicationUpdateCommand>
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskProvider _diskProvider;
private readonly IDiskTransferService _diskTransferService;
private readonly IHttpClient _httpClient;
private readonly IArchiveService _archiveService;
private readonly IProcessProvider _processProvider;
@ -34,9 +35,13 @@ public class InstallUpdateService : IExecute<ApplicationUpdateCommand>
private readonly IBackupService _backupService;
public InstallUpdateService(ICheckUpdateService checkUpdateService, IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider, IHttpClient httpClient,
IArchiveService archiveService, IProcessProvider processProvider,
public InstallUpdateService(ICheckUpdateService checkUpdateService,
IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IHttpClient httpClient,
IArchiveService archiveService,
IProcessProvider processProvider,
IVerifyUpdates updateVerifier,
IStartupContext startupContext,
IConfigFileProvider configFileProvider,
@ -51,6 +56,7 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService, IAppFolderIn
_checkUpdateService = checkUpdateService;
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_httpClient = httpClient;
_archiveService = archiveService;
_processProvider = processProvider;
@ -113,8 +119,7 @@ private void InstallUpdate(UpdatePackage updatePackage)
}
_logger.Info("Preparing client");
_diskProvider.MoveFolder(_appFolderInfo.GetUpdateClientFolder(),
updateSandboxFolder);
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move, false);
_logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath());
_logger.ProgressInfo("Sonarr will restart shortly.");

View File

@ -13,13 +13,13 @@ public interface IBackupAndRestore
public class BackupAndRestore : IBackupAndRestore
{
private readonly IDiskProvider _diskProvider;
private readonly IDiskTransferService _diskTransferService;
private readonly IAppFolderInfo _appFolderInfo;
private readonly Logger _logger;
public BackupAndRestore(IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, Logger logger)
public BackupAndRestore(IDiskTransferService diskTransferService, IAppFolderInfo appFolderInfo, Logger logger)
{
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_appFolderInfo = appFolderInfo;
_logger = logger;
}
@ -27,13 +27,13 @@ public BackupAndRestore(IDiskProvider diskProvider, IAppFolderInfo appFolderInfo
public void Backup(string source)
{
_logger.Info("Creating backup of existing installation");
_diskProvider.CopyFolder(source, _appFolderInfo.GetUpdateBackUpFolder());
_diskTransferService.TransferFolder(source, _appFolderInfo.GetUpdateBackUpFolder(), TransferMode.Copy, false);
}
public void Restore(string target)
{
_logger.Info("Attempting to rollback upgrade");
_diskProvider.CopyFolder(_appFolderInfo.GetUpdateBackUpFolder(), target);
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateBackUpFolder(), target, TransferMode.Copy, false);
}
}
}

View File

@ -14,13 +14,18 @@ public interface IBackupAppData
public class BackupAppData : IBackupAppData
{
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskTransferService _diskTransferService;
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public BackupAppData(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
public BackupAppData(IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
Logger logger)
{
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_logger = logger;
}
@ -33,9 +38,8 @@ public void Backup()
try
{
_diskProvider.CopyFile(_appFolderInfo.GetConfigPath(), _appFolderInfo.GetUpdateBackupConfigFile(), true);
_diskProvider.CopyFile(_appFolderInfo.GetNzbDroneDatabase(), _appFolderInfo.GetUpdateBackupDatabase(),
true);
_diskTransferService.TransferFile(_appFolderInfo.GetConfigPath(), _appFolderInfo.GetUpdateBackupConfigFile(), TransferMode.Copy);
_diskTransferService.TransferFile(_appFolderInfo.GetNzbDroneDatabase(), _appFolderInfo.GetUpdateBackupDatabase(), TransferMode.Copy);
}
catch (Exception e)
{

View File

@ -16,6 +16,7 @@ public interface IInstallUpdateService
public class InstallUpdateService : IInstallUpdateService
{
private readonly IDiskProvider _diskProvider;
private readonly IDiskTransferService _diskTransferService;
private readonly IDetectApplicationType _detectApplicationType;
private readonly ITerminateNzbDrone _terminateNzbDrone;
private readonly IAppFolderInfo _appFolderInfo;
@ -26,6 +27,7 @@ public class InstallUpdateService : IInstallUpdateService
private readonly Logger _logger;
public InstallUpdateService(IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IDetectApplicationType detectApplicationType,
ITerminateNzbDrone terminateNzbDrone,
IAppFolderInfo appFolderInfo,
@ -36,6 +38,7 @@ public InstallUpdateService(IDiskProvider diskProvider,
Logger logger)
{
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_detectApplicationType = detectApplicationType;
_terminateNzbDrone = terminateNzbDrone;
_appFolderInfo = appFolderInfo;
@ -93,7 +96,7 @@ public void Start(string installationFolder, int processId)
_diskProvider.EmptyFolder(installationFolder);
_logger.Info("Copying new files to target folder");
_diskProvider.CopyFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder);
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder, TransferMode.Copy, false);
// Set executable flag on Sonarr app
if (OsInfo.IsOsx)