mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-01-19 10:54:05 +02:00
Added support for Hardlinking instead of Copy.
This commit is contained in:
parent
a8bea777d7
commit
ffa814f387
@ -19,5 +19,6 @@ namespace NzbDrone.Api.Config
|
|||||||
public String ChownGroup { get; set; }
|
public String ChownGroup { get; set; }
|
||||||
|
|
||||||
public Boolean SkipFreeSpaceCheckWhenImporting { get; set; }
|
public Boolean SkipFreeSpaceCheckWhenImporting { get; set; }
|
||||||
|
public Boolean CopyUsingHardlinks { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,72 @@ namespace NzbDrone.Common.Test.DiskProviderTests
|
|||||||
Directory.Exists(sourceDir).Should().BeFalse();
|
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]
|
[Test]
|
||||||
public void empty_folder_should_return_folder_modified_date()
|
public void empty_folder_should_return_folder_modified_date()
|
||||||
{
|
{
|
||||||
|
@ -13,12 +13,6 @@ namespace NzbDrone.Common.Disk
|
|||||||
{
|
{
|
||||||
public abstract class DiskProviderBase : IDiskProvider
|
public abstract class DiskProviderBase : IDiskProvider
|
||||||
{
|
{
|
||||||
enum TransferAction
|
|
||||||
{
|
|
||||||
Copy,
|
|
||||||
Move
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
|
||||||
|
|
||||||
public abstract long? GetAvailableSpace(string path);
|
public abstract long? GetAvailableSpace(string path);
|
||||||
@ -152,7 +146,7 @@ namespace NzbDrone.Common.Disk
|
|||||||
Ensure.That(source, () => source).IsValidPath();
|
Ensure.That(source, () => source).IsValidPath();
|
||||||
Ensure.That(destination, () => destination).IsValidPath();
|
Ensure.That(destination, () => destination).IsValidPath();
|
||||||
|
|
||||||
TransferFolder(source, destination, TransferAction.Copy);
|
TransferFolder(source, destination, TransferMode.Copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveFolder(string source, string destination)
|
public void MoveFolder(string source, string destination)
|
||||||
@ -162,7 +156,7 @@ namespace NzbDrone.Common.Disk
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TransferFolder(source, destination, TransferAction.Move);
|
TransferFolder(source, destination, TransferMode.Move);
|
||||||
DeleteFolder(source, true);
|
DeleteFolder(source, true);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -173,15 +167,15 @@ namespace NzbDrone.Common.Disk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TransferFolder(string source, string target, TransferAction transferAction)
|
public void TransferFolder(string source, string destination, TransferMode mode)
|
||||||
{
|
{
|
||||||
Ensure.That(source, () => source).IsValidPath();
|
Ensure.That(source, () => source).IsValidPath();
|
||||||
Ensure.That(target, () => target).IsValidPath();
|
Ensure.That(destination, () => destination).IsValidPath();
|
||||||
|
|
||||||
Logger.ProgressDebug("{0} {1} -> {2}", transferAction, source, target);
|
Logger.ProgressDebug("{0} {1} -> {2}", mode, source, destination);
|
||||||
|
|
||||||
var sourceFolder = new DirectoryInfo(source);
|
var sourceFolder = new DirectoryInfo(source);
|
||||||
var targetFolder = new DirectoryInfo(target);
|
var targetFolder = new DirectoryInfo(destination);
|
||||||
|
|
||||||
if (!targetFolder.Exists)
|
if (!targetFolder.Exists)
|
||||||
{
|
{
|
||||||
@ -190,28 +184,16 @@ namespace NzbDrone.Common.Disk
|
|||||||
|
|
||||||
foreach (var subDir in sourceFolder.GetDirectories())
|
foreach (var subDir in sourceFolder.GetDirectories())
|
||||||
{
|
{
|
||||||
TransferFolder(subDir.FullName, Path.Combine(target, subDir.Name), transferAction);
|
TransferFolder(subDir.FullName, Path.Combine(destination, subDir.Name), mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var sourceFile in sourceFolder.GetFiles("*.*", SearchOption.TopDirectoryOnly))
|
foreach (var sourceFile in sourceFolder.GetFiles("*.*", SearchOption.TopDirectoryOnly))
|
||||||
{
|
{
|
||||||
var destFile = Path.Combine(target, sourceFile.Name);
|
var destFile = Path.Combine(destination, sourceFile.Name);
|
||||||
|
|
||||||
Logger.ProgressDebug("{0} {1} -> {2}", transferAction, sourceFile, destFile);
|
Logger.ProgressDebug("{0} {1} -> {2}", mode, sourceFile, destFile);
|
||||||
|
|
||||||
switch (transferAction)
|
TransferFile(sourceFile.FullName, destFile, mode, true);
|
||||||
{
|
|
||||||
case TransferAction.Copy:
|
|
||||||
{
|
|
||||||
sourceFile.CopyTo(destFile, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TransferAction.Move:
|
|
||||||
{
|
|
||||||
MoveFile(sourceFile.FullName, destFile, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,19 +209,15 @@ namespace NzbDrone.Common.Disk
|
|||||||
|
|
||||||
public void CopyFile(string source, string destination, bool overwrite = false)
|
public void CopyFile(string source, string destination, bool overwrite = false)
|
||||||
{
|
{
|
||||||
Ensure.That(source, () => source).IsValidPath();
|
TransferFile(source, destination, TransferMode.Copy, overwrite);
|
||||||
Ensure.That(destination, () => destination).IsValidPath();
|
|
||||||
|
|
||||||
if (source.PathEquals(destination))
|
|
||||||
{
|
|
||||||
Logger.Warn("Source and destination can't be the same {0}", source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File.Copy(source, destination, overwrite);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveFile(string source, string destination, bool overwrite = false)
|
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(source, () => source).IsValidPath();
|
||||||
Ensure.That(destination, () => destination).IsValidPath();
|
Ensure.That(destination, () => destination).IsValidPath();
|
||||||
@ -247,7 +225,7 @@ namespace NzbDrone.Common.Disk
|
|||||||
if (source.PathEquals(destination))
|
if (source.PathEquals(destination))
|
||||||
{
|
{
|
||||||
Logger.Warn("Source and destination can't be the same {0}", source);
|
Logger.Warn("Source and destination can't be the same {0}", source);
|
||||||
return;
|
return TransferMode.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileExists(destination) && overwrite)
|
if (FileExists(destination) && overwrite)
|
||||||
@ -255,10 +233,37 @@ namespace NzbDrone.Common.Disk
|
|||||||
DeleteFile(destination);
|
DeleteFile(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode.HasFlag(TransferMode.HardLink))
|
||||||
|
{
|
||||||
|
bool createdHardlink = TryCreateHardLink(source, destination);
|
||||||
|
if (createdHardlink)
|
||||||
|
{
|
||||||
|
return TransferMode.HardLink;
|
||||||
|
}
|
||||||
|
else 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);
|
RemoveReadOnly(source);
|
||||||
File.Move(source, destination);
|
File.Move(source, destination);
|
||||||
|
return TransferMode.Move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TransferMode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract bool TryCreateHardLink(string source, string destination);
|
||||||
|
|
||||||
public void DeleteFolder(string path, bool recursive)
|
public void DeleteFolder(string path, bool recursive)
|
||||||
{
|
{
|
||||||
Ensure.That(path, () => path).IsValidPath();
|
Ensure.That(path, () => path).IsValidPath();
|
||||||
|
@ -25,9 +25,12 @@ namespace NzbDrone.Common.Disk
|
|||||||
void CreateFolder(string path);
|
void CreateFolder(string path);
|
||||||
void CopyFolder(string source, string destination);
|
void CopyFolder(string source, string destination);
|
||||||
void MoveFolder(string source, string destination);
|
void MoveFolder(string source, string destination);
|
||||||
|
void TransferFolder(string source, string destination, TransferMode transferMode);
|
||||||
void DeleteFile(string path);
|
void DeleteFile(string path);
|
||||||
void CopyFile(string source, string destination, bool overwrite = false);
|
void CopyFile(string source, string destination, bool overwrite = false);
|
||||||
void MoveFile(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);
|
void DeleteFolder(string path, bool recursive);
|
||||||
string ReadAllText(string filePath);
|
string ReadAllText(string filePath);
|
||||||
void WriteAllText(string filename, string contents);
|
void WriteAllText(string filename, string contents);
|
||||||
|
19
src/NzbDrone.Common/Disk/TransferMode.cs
Normal file
19
src/NzbDrone.Common/Disk/TransferMode.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Disk
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum TransferMode
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Move = 1,
|
||||||
|
Copy = 2,
|
||||||
|
HardLink = 4,
|
||||||
|
|
||||||
|
HardLinkOrCopy = Copy | HardLink
|
||||||
|
}
|
||||||
|
}
|
@ -73,6 +73,7 @@
|
|||||||
<Compile Include="DictionaryExtensions.cs" />
|
<Compile Include="DictionaryExtensions.cs" />
|
||||||
<Compile Include="Disk\DiskProviderBase.cs" />
|
<Compile Include="Disk\DiskProviderBase.cs" />
|
||||||
<Compile Include="Disk\IDiskProvider.cs" />
|
<Compile Include="Disk\IDiskProvider.cs" />
|
||||||
|
<Compile Include="Disk\TransferMode.cs" />
|
||||||
<Compile Include="EnsureThat\Ensure.cs" />
|
<Compile Include="EnsureThat\Ensure.cs" />
|
||||||
<Compile Include="EnsureThat\EnsureBoolExtensions.cs" />
|
<Compile Include="EnsureThat\EnsureBoolExtensions.cs" />
|
||||||
<Compile Include="EnsureThat\EnsureCollectionExtensions.cs" />
|
<Compile Include="EnsureThat\EnsureCollectionExtensions.cs" />
|
||||||
|
@ -212,6 +212,13 @@ namespace NzbDrone.Core.Configuration
|
|||||||
set { SetValue("SkipFreeSpaceCheckWhenImporting", value); }
|
set { SetValue("SkipFreeSpaceCheckWhenImporting", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean CopyUsingHardlinks
|
||||||
|
{
|
||||||
|
get { return GetValueBoolean("CopyUsingHardlinks", true); }
|
||||||
|
|
||||||
|
set { SetValue("CopyUsingHardlinks", value); }
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean SetPermissionsLinux
|
public Boolean SetPermissionsLinux
|
||||||
{
|
{
|
||||||
get { return GetValueBoolean("SetPermissionsLinux", false); }
|
get { return GetValueBoolean("SetPermissionsLinux", false); }
|
||||||
|
@ -36,6 +36,7 @@ namespace NzbDrone.Core.Configuration
|
|||||||
Boolean CreateEmptySeriesFolders { get; set; }
|
Boolean CreateEmptySeriesFolders { get; set; }
|
||||||
FileDateType FileDate { get; set; }
|
FileDateType FileDate { get; set; }
|
||||||
Boolean SkipFreeSpaceCheckWhenImporting { get; set; }
|
Boolean SkipFreeSpaceCheckWhenImporting { get; set; }
|
||||||
|
Boolean CopyUsingHardlinks { get; set; }
|
||||||
|
|
||||||
//Permissions (Media Management)
|
//Permissions (Media Management)
|
||||||
Boolean SetPermissionsLinux { get; set; }
|
Boolean SetPermissionsLinux { get; set; }
|
||||||
|
@ -28,6 +28,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
private readonly IBuildFileNames _buildFileNames;
|
private readonly IBuildFileNames _buildFileNames;
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
||||||
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public EpisodeFileMovingService(IEpisodeService episodeService,
|
public EpisodeFileMovingService(IEpisodeService episodeService,
|
||||||
@ -35,6 +36,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
IBuildFileNames buildFileNames,
|
IBuildFileNames buildFileNames,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IMediaFileAttributeService mediaFileAttributeService,
|
IMediaFileAttributeService mediaFileAttributeService,
|
||||||
|
IConfigService configService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
@ -42,6 +44,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
_buildFileNames = buildFileNames;
|
_buildFileNames = buildFileNames;
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_mediaFileAttributeService = mediaFileAttributeService;
|
_mediaFileAttributeService = mediaFileAttributeService;
|
||||||
|
_configService = configService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
|
|
||||||
_logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
|
_logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
|
||||||
|
|
||||||
return TransferFile(episodeFile, series, episodes, filePath, false);
|
return TransferFile(episodeFile, series, episodes, filePath, TransferMode.Move);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
||||||
@ -63,7 +66,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
|
|
||||||
_logger.Debug("Moving episode file: {0} to {1}", episodeFile, filePath);
|
_logger.Debug("Moving episode file: {0} to {1}", episodeFile, filePath);
|
||||||
|
|
||||||
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, false);
|
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
||||||
@ -73,10 +76,17 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
|
|
||||||
_logger.Debug("Copying episode file: {0} to {1}", episodeFile, filePath);
|
_logger.Debug("Copying episode file: {0} to {1}", episodeFile, filePath);
|
||||||
|
|
||||||
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, true);
|
if (_configService.CopyUsingHardlinks)
|
||||||
|
{
|
||||||
|
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.HardLinkOrCopy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Copy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, String destinationFilename, Boolean copyOnly)
|
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, string destinationFilename, TransferMode mode)
|
||||||
{
|
{
|
||||||
Ensure.That(episodeFile, () => episodeFile).IsNotNull();
|
Ensure.That(episodeFile, () => episodeFile).IsNotNull();
|
||||||
Ensure.That(series,() => series).IsNotNull();
|
Ensure.That(series,() => series).IsNotNull();
|
||||||
@ -115,16 +125,8 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copyOnly)
|
_logger.Debug("{0} [{1}] > [{2}]", mode, episodeFilePath, destinationFilename);
|
||||||
{
|
_diskProvider.TransferFile(episodeFilePath, destinationFilename, mode);
|
||||||
_logger.Debug("Copying [{0}] > [{1}]", episodeFilePath, destinationFilename);
|
|
||||||
_diskProvider.CopyFile(episodeFilePath, destinationFilename);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Moving [{0}] > [{1}]", episodeFilePath, destinationFilename);
|
|
||||||
_diskProvider.MoveFile(episodeFilePath, destinationFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename);
|
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilename);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using NzbDrone.Common;
|
|||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
using Mono.Unix;
|
||||||
|
|
||||||
namespace NzbDrone.Mono
|
namespace NzbDrone.Mono
|
||||||
{
|
{
|
||||||
@ -151,5 +152,18 @@ namespace NzbDrone.Mono
|
|||||||
.OrderByDescending(drive => drive.Name.Length)
|
.OrderByDescending(drive => drive.Name.Length)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool TryCreateHardLink(string source, string destination)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UnixFileSystemInfo.GetFileSystemEntry(source).CreateLink(destination);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ namespace NzbDrone.Windows
|
|||||||
out ulong lpTotalNumberOfBytes,
|
out ulong lpTotalNumberOfBytes,
|
||||||
out ulong lpTotalNumberOfFreeBytes);
|
out ulong lpTotalNumberOfFreeBytes);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
|
||||||
|
|
||||||
public override long? GetAvailableSpace(string path)
|
public override long? GetAvailableSpace(string path)
|
||||||
{
|
{
|
||||||
Ensure.That(path, () => path).IsValidPath();
|
Ensure.That(path, () => path).IsValidPath();
|
||||||
@ -98,5 +102,18 @@ namespace NzbDrone.Windows
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool TryCreateHardLink(string source, string destination)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return CreateHardLink(destination, source, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
{{#if_mono}}
|
|
||||||
<fieldset class="advanced-setting">
|
<fieldset class="advanced-setting">
|
||||||
<legend>Importing</legend>
|
<legend>Importing</legend>
|
||||||
|
|
||||||
|
{{#if_mono}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Skip Free Space Check</label>
|
<label class="col-sm-3 control-label">Skip Free Space Check</label>
|
||||||
|
|
||||||
@ -51,5 +51,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
{{/if_mono}}
|
{{/if_mono}}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Use Hardlinks instead of Copy</label>
|
||||||
|
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="checkbox toggle well">
|
||||||
|
<input type="checkbox" name="copyUsingHardlinks"/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span>Yes</span>
|
||||||
|
<span>No</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<span class="help-inline-checkbox">
|
||||||
|
<i class="icon-nd-form-info" title="Use Hardlinks when trying to copy files from seeding torrents"/>
|
||||||
|
<i class="icon-nd-form-warn" title="Occassionally, file locks may prevent renaming files that are currently seeding. Temporarily disable seeding while using the Rename UI to rename existing episodes to work around it."/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user