parent
4f220d9532
commit
9eb5b335f3
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using Mono.Unix;
|
||||
using Mono.Unix.Native;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Mono.Interop;
|
||||
|
||||
namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
public interface IRefLinkCreator
|
||||
{
|
||||
bool TryCreateRefLink(string srcPath, string linkPath);
|
||||
}
|
||||
|
||||
public class RefLinkCreator : IRefLinkCreator
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly bool _supported;
|
||||
|
||||
public RefLinkCreator(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
// Only support x86_64 because we know the FICLONE value is valid for it
|
||||
_supported = OsInfo.IsLinux && (Syscall.uname(out var results) == 0 && results.machine == "x86_64");
|
||||
}
|
||||
|
||||
public bool TryCreateRefLink(string srcPath, string linkPath)
|
||||
{
|
||||
if (!_supported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var srcHandle = NativeMethods.open(srcPath, OpenFlags.O_RDONLY))
|
||||
{
|
||||
if (srcHandle.IsInvalid)
|
||||
{
|
||||
_logger.Trace("Failed to create reflink at '{0}' to '{1}': Couldn't open source file", linkPath, srcPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var linkHandle = NativeMethods.open(linkPath, OpenFlags.O_WRONLY | OpenFlags.O_CREAT | OpenFlags.O_TRUNC))
|
||||
{
|
||||
if (linkHandle.IsInvalid)
|
||||
{
|
||||
_logger.Trace("Failed to create reflink at '{0}' to '{1}': Couldn't create new link file", linkPath, srcPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NativeMethods.clone_file(linkHandle, srcHandle) == -1)
|
||||
{
|
||||
var error = new UnixIOException();
|
||||
linkHandle.Dispose();
|
||||
Syscall.unlink(linkPath);
|
||||
_logger.Trace("Failed to create reflink at '{0}' to '{1}': {2}", linkPath, srcPath, error.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Trace("Created reflink at '{0}' to '{1}'", linkPath, srcPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Syscall.unlink(linkPath);
|
||||
_logger.Trace(ex, "Failed to create reflink at '{0}' to '{1}'", linkPath, srcPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Mono.Unix.Native;
|
||||
|
||||
namespace NzbDrone.Mono.Interop
|
||||
{
|
||||
internal enum IoctlRequest : uint
|
||||
{
|
||||
// Hardcoded ioctl for FICLONE on a typical linux system
|
||||
// #define FICLONE _IOW(0x94, 9, int)
|
||||
FICLONE = 0x40049409
|
||||
}
|
||||
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
|
||||
private static extern int Ioctl(SafeUnixHandle dst_fd, IoctlRequest request, SafeUnixHandle src_fd);
|
||||
|
||||
public static SafeUnixHandle open(string pathname, OpenFlags flags)
|
||||
{
|
||||
return new SafeUnixHandle(Syscall.open(pathname, flags));
|
||||
}
|
||||
|
||||
internal static int clone_file(SafeUnixHandle link_fd, SafeUnixHandle src_fd)
|
||||
{
|
||||
return Ioctl(link_fd, IoctlRequest.FICLONE, src_fd);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Permissions;
|
||||
using Mono.Unix.Native;
|
||||
|
||||
namespace NzbDrone.Mono.Interop
|
||||
{
|
||||
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
|
||||
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||||
internal sealed class SafeUnixHandle : SafeHandle
|
||||
{
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
private SafeUnixHandle()
|
||||
: base(new IntPtr(-1), true)
|
||||
{
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public SafeUnixHandle(int fd)
|
||||
: base(new IntPtr(-1), true)
|
||||
{
|
||||
handle = new IntPtr(fd);
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
get { return handle == new IntPtr(-1); }
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return Syscall.close(handle.ToInt32()) != -1;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue