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.

pull/3113/head
Taloth Saldono 10 years ago
parent aa4fca7177
commit 5effca92b8

@ -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 @@ namespace NzbDrone.Common.Test.DiskTests
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 @@ namespace NzbDrone.Common.Test.DiskTests
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 @@ namespace NzbDrone.Common.Test.DiskTests
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 @@ namespace NzbDrone.Common.Test.DiskTests
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");
Directory.CreateDirectory(sourceDir);
File.WriteAllText(source, "SourceFile");
Subject.TryCreateHardLink(source, destination).Should().BeTrue();
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();
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
File.AppendAllText(source, "Test");
File.ReadAllText(rename).Should().Be("SourceFileTest");
}
private void VerifyMove(string source, string from, string destination)
[Test]
public void should_be_able_to_rename_open_hardlinks_with_fileshare_delete()
{
Directory.Exists(from).Should().BeFalse();
DoHardLinkRename(FileShare.Delete);
}
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();
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_none()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.None));
}
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
[Test]
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_write()
{
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.Read));
}
}
}

@ -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());
}
}
}

@ -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" />

@ -168,62 +168,6 @@ namespace NzbDrone.Common.Disk
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 @@ namespace NzbDrone.Common.Disk
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();
public void MoveFile(string source, string destination, bool overwrite = false)
{
TransferFile(source, destination, TransferMode.Move, overwrite);
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 TransferMode TransferFile(string source, string destination, TransferMode mode, bool overwrite)
public void MoveFile(string source, string destination, bool overwrite = false)
{
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 @@ namespace NzbDrone.Common.Disk
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;
RemoveReadOnly(source);
File.Move(source, destination);
}
public abstract bool TryCreateHardLink(string source, string destination);

@ -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;
}
}
}

@ -25,13 +25,9 @@ namespace NzbDrone.Common.Disk
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);

@ -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" />

@ -45,7 +45,8 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
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]
@ -68,7 +69,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
var path = @"C:\Test\TV\30 Rock".AsOsAgnostic();
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(@"C:\Test\Recycle Bin\30 Rock".AsOsAgnostic(), SearchOption.AllDirectories))
.Returns(new[] { "File1", "File2", "File3" });
.Returns(new[] { "File1", "File2", "File3" });
Mocker.Resolve<RecycleBinProvider>().DeleteFolder(path);

@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
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 @@ namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
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]

@ -39,8 +39,8 @@ namespace NzbDrone.Core.Test.TvTests
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>();
}

@ -135,7 +135,8 @@ namespace NzbDrone.Core.Test.UpdateTests
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]

@ -25,6 +25,7 @@ namespace NzbDrone.Core.Backup
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 @@ namespace NzbDrone.Core.Backup
private static readonly Regex BackupFileRegex = new Regex(@"nzbdrone_backup_[._0-9]+\.zip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public BackupService(IMainDatabase maindDb,
IDiskProvider diskProvider,
IAppFolderInfo appFolderInfo,
IArchiveService archiveService,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IAppFolderInfo appFolderInfo,
IArchiveService archiveService,
Logger logger)
{
_maindDb = maindDb;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_appFolderInfo = appFolderInfo;
_archiveService = archiveService;
@ -115,7 +118,7 @@ namespace NzbDrone.Core.Backup
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 @@ namespace NzbDrone.Core.Backup
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)

@ -27,6 +27,7 @@ namespace NzbDrone.Core.MediaFiles
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 @@ namespace NzbDrone.Core.MediaFiles
public EpisodeFileMovingService(IEpisodeService episodeService,
IUpdateEpisodeFileService updateEpisodeFileService,
IBuildFileNames buildFileNames,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IMediaFileAttributeService mediaFileAttributeService,
IEventAggregator eventAggregator,
@ -45,6 +47,7 @@ namespace NzbDrone.Core.MediaFiles
_episodeService = episodeService;
_updateEpisodeFileService = updateEpisodeFileService;
_buildFileNames = buildFileNames;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_mediaFileAttributeService = mediaFileAttributeService;
_eventAggregator = eventAggregator;
@ -112,8 +115,7 @@ namespace NzbDrone.Core.MediaFiles
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);

@ -22,13 +22,18 @@ namespace NzbDrone.Core.MediaFiles
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 @@ namespace NzbDrone.Core.MediaFiles
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 @@ namespace NzbDrone.Core.MediaFiles
}
_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)

@ -27,6 +27,7 @@ namespace NzbDrone.Core.Metadata
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 @@ namespace NzbDrone.Core.Metadata
ICleanMetadataService cleanMetadataService,
IMediaFileService mediaFileService,
IEpisodeService episodeService,
IDiskTransferService diskTransferService,
IDiskProvider diskProvider,
IHttpClient httpClient,
IMediaFileAttributeService mediaFileAttributeService,
@ -49,6 +51,7 @@ namespace NzbDrone.Core.Metadata
_cleanMetadataService = cleanMetadataService;
_mediaFileService = mediaFileService;
_episodeService = episodeService;
_diskTransferService = diskTransferService;
_diskProvider = diskProvider;
_httpClient = httpClient;
_mediaFileAttributeService = mediaFileAttributeService;
@ -218,7 +221,7 @@ namespace NzbDrone.Core.Metadata
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 @@ namespace NzbDrone.Core.Metadata
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 };

@ -16,19 +16,19 @@ namespace NzbDrone.Core.Tv
{
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 @@ namespace NzbDrone.Core.Tv
//TODO: Move to transactional disk operations
try
{
_diskProvider.MoveFolder(source, destination);
_diskTransferService.TransferFolder(source, destination, TransferMode.Move);
}
catch (IOException ex)
{

@ -24,6 +24,7 @@ namespace NzbDrone.Core.Update
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 @@ namespace NzbDrone.Core.Update
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 @@ namespace NzbDrone.Core.Update
_checkUpdateService = checkUpdateService;
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_httpClient = httpClient;
_archiveService = archiveService;
_processProvider = processProvider;
@ -113,8 +119,7 @@ namespace NzbDrone.Core.Update
}
_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.");

@ -13,13 +13,13 @@ namespace NzbDrone.Update.UpdateEngine
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 @@ namespace NzbDrone.Update.UpdateEngine
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);
}
}
}

@ -14,13 +14,18 @@ namespace NzbDrone.Update.UpdateEngine
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 @@ namespace NzbDrone.Update.UpdateEngine
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)
{

@ -16,6 +16,7 @@ namespace NzbDrone.Update.UpdateEngine
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 @@ namespace NzbDrone.Update.UpdateEngine
private readonly Logger _logger;
public InstallUpdateService(IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IDetectApplicationType detectApplicationType,
ITerminateNzbDrone terminateNzbDrone,
IAppFolderInfo appFolderInfo,
@ -36,6 +38,7 @@ namespace NzbDrone.Update.UpdateEngine
Logger logger)
{
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_detectApplicationType = detectApplicationType;
_terminateNzbDrone = terminateNzbDrone;
_appFolderInfo = appFolderInfo;
@ -93,7 +96,7 @@ namespace NzbDrone.Update.UpdateEngine
_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)

Loading…
Cancel
Save