【问题标题】:How to determine if two file are hard-linked to the same data?如何确定两个文件是否硬链接到相同的数据?
【发布时间】:2019-12-30 13:57:21
【问题描述】:

我为System.IO.FileInfo 类编写了一个扩展方法来创建硬链接,它是这样的:

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);

public static void CreateHardLink(this FileInfo file, string destination) {
    CreateHardLink(destination, file.FullName, IntPtr.Zero);
}

// Usage:
fileInfo.CreateHardLink(@".\hardLinkCopy.txt");

该方法工作正常,但我想做一些单元测试只是为了它。那么如何断言一个文件 x 和另一个文件 y 链接到相同的数据呢?

我想出了一些方法来测试它:

  • 检查数据在整个更改过程中是否一致 由于创建硬链接副本只是为文件提供第二个名称,因此对第一个实例所做的任何修改都将反映在第二个实例上,反之亦然.如果尽管修改了两个文件之间的数据保持一致,则可以安全地假设这些文件都硬链接到相同的数据。
  • 断言硬链接的创建不会影响父文件夹的大小。 由于硬链接副本不会复制磁盘上的任何数据,因此父目录不应变得更重。如果在调用该方法时创建了一个与原始文件具有相同内容的新文件,并且父文件夹的大小没有改变(或增益小于正常副本的大小),则新文件必须是硬文件链接副本。

但是,这些方法有异味。操作系统中必须至少有一个内置方法来检查两个文件是否指向磁盘上的相同数据!

任何人都可以分享线索吗?

【问题讨论】:

标签: c# unit-testing hardlink


【解决方案1】:

按照 Bennett Yeo 的建议,我发现了以下内容:

没有直接的方法来检查两个文件是否链接到相同的数据,但我们可以通过比较文件的唯一 ID(或基于 UNIX 的系统中的 inode)来创建自己的方法。在我的理解中,这个值是磁盘上实际内容的索引。

Bennett 还链接了 This thread,这给了我两种获取文件唯一 ID 的方法:

  1. 链接的答案建议从kernel32.dll 调用GetFileInformationByHandle。正如该方法的名称所暗示的,我必须首先获取文件的句柄,但每当我尝试获取一个句柄时,就会抛出一个异常,指出目标文件已被另一个进程使用。
  2. 最后,使用命令fsutil file queryfileid <filename> (Credit to this answer)。

第二种方法对我有用,所以我写了以下代码:

private static string InvokeShellAndGetOutput(string fileName, string arguments) {
    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.FileName = fileName;
    p.StartInfo.Arguments = arguments;
    p.Start();
    string output = p.StandardOutput.ReadToEnd();
    p.WaitForExit();
    return output;
}

public static long GetFileId(this FileInfo fileInfo) {
    // Call "fsutil" to get the unique file id
    string output = InvokeShellAndGetOutput("fsutil", $"file queryfileid {fileInfo.FullName}");

    // Remove the following characters: "File ID is " and the EOL at the end. The remaining string is an hex string with the "0x" prefix.
    string parsedOutput = output.Remove(0, 11).Trim();
    return Convert.ToInt64(parsedOutput, 16); ;
}

public static bool IsHardlinkedToSameData(this FileInfo fileInfo, FileInfo otherFileInfo) {
    return fileInfo.GetFileId() == otherFileInfo.GetFileId();
}

虽然不完整,但我觉得它已经比我之前的想法更可靠了。只要运行测试的主机安装了“fsutil”,它就可以工作。

仍然欢迎任何更可靠的解决方案。

【讨论】:

    猜你喜欢
    • 2012-11-16
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多