Move hard link helper functions to their own class

This commit is contained in:
Dean Herbert 2022-12-13 19:53:12 +09:00
parent 1d4230993d
commit bbf931c746
3 changed files with 71 additions and 63 deletions

View File

@ -72,7 +72,7 @@ public bool CheckHardLinkAvailability()
string testExistingPath = stableStorage.GetFullPath(stableStorage.GetFiles(string.Empty).First());
string testDestinationPath = desktopGameHost.Storage.GetFullPath(test_filename);
return RealmFileStore.CreateHardLink(testDestinationPath, testExistingPath, IntPtr.Zero);
return HardLinkHelper.CreateHardLink(testDestinationPath, testExistingPath, IntPtr.Zero);
}
public virtual async Task<int> GetImportCount(StableContent content, CancellationToken cancellationToken)

View File

@ -4,9 +4,6 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Win32.SafeHandles;
using osu.Framework;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
@ -14,6 +11,7 @@
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Models;
using Realms;
@ -68,7 +66,7 @@ private void copyToStore(RealmFile file, Stream data, bool preferHardLinks)
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows && data is FileStream fs && preferHardLinks)
{
// attempt to do a fast hard link rather than copy.
if (CreateHardLink(Storage.GetFullPath(file.GetStoragePath()), fs.Name, IntPtr.Zero))
if (HardLinkHelper.CreateHardLink(Storage.GetFullPath(file.GetStoragePath()), fs.Name, IntPtr.Zero))
return;
}
@ -128,63 +126,5 @@ public void Cleanup()
Logger.Log($@"Finished realm file store cleanup ({removedFiles} of {totalFiles} deleted)");
}
#region Windows hard link support
// For future use (to detect if a file is a hard link with other references existing on disk).
public static int GetFileLinkCount(string filePath)
{
int result = 0;
SafeFileHandle handle = CreateFile(filePath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
ByHandleFileInformation fileInfo;
if (GetFileInformationByHandle(handle, out fileInfo))
result = (int)fileInfo.NumberOfLinks;
CloseHandle(handle);
return result;
}
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
[StructLayout(LayoutKind.Sequential)]
private struct ByHandleFileInformation
{
public readonly uint FileAttributes;
public readonly FILETIME CreationTime;
public readonly FILETIME LastAccessTime;
public readonly FILETIME LastWriteTime;
public readonly uint VolumeSerialNumber;
public readonly uint FileSizeHigh;
public readonly uint FileSizeLow;
public readonly uint NumberOfLinks;
public readonly uint FileIndexHigh;
public readonly uint FileIndexLow;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandle(SafeFileHandle handle, out ByHandleFileInformation lpFileInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(SafeHandle hObject);
#endregion
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Win32.SafeHandles;
namespace osu.Game.IO
{
internal static class HardLinkHelper
{
// For future use (to detect if a file is a hard link with other references existing on disk).
public static int GetFileLinkCount(string filePath)
{
int result = 0;
SafeFileHandle handle = CreateFile(filePath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
ByHandleFileInformation fileInfo;
if (GetFileInformationByHandle(handle, out fileInfo))
result = (int)fileInfo.NumberOfLinks;
CloseHandle(handle);
return result;
}
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
public static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
[StructLayout(LayoutKind.Sequential)]
private struct ByHandleFileInformation
{
public readonly uint FileAttributes;
public readonly FILETIME CreationTime;
public readonly FILETIME LastAccessTime;
public readonly FILETIME LastWriteTime;
public readonly uint VolumeSerialNumber;
public readonly uint FileSizeHigh;
public readonly uint FileSizeLow;
public readonly uint NumberOfLinks;
public readonly uint FileIndexHigh;
public readonly uint FileIndexLow;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandle(SafeFileHandle handle, out ByHandleFileInformation lpFileInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(SafeHandle hObject);
}
}