如何确定两个文件是否硬链接到相同的数据?
How to determine if two file are hard-linked to the same data?
我已经为 System.IO.FileInfo
class 编写了一个扩展方法来创建硬 link,它是这样的:
[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 linked 到相同的数据?
我想出了一些方法来测试它:
- 检查数据是否在整个更改过程中保持一致由于创建硬link副本只是为文件赋予第二个名称,因此对第一个实例所做的任何修改将反映在第二个上,反之亦然。如果两个文件之间的数据在修改后保持一致,则可以安全地假设这些文件都被硬linked 到相同的数据。
- 断言硬 link 的创建不会影响父文件夹的大小。 因为硬 link 副本不会复制磁盘上的任何数据,父目录不应该变得更重。如果在调用该方法时创建了一个与原始文件具有相同内容的新文件,并且父文件夹的大小没有改变(或者比普通副本所做的更少),则新文件必须是硬文件link复制。
但是,这些方法有味道。 OS 中的某处必须至少有一种内置方法来检查两个文件是否指向磁盘上的相同数据!
有人可以分享线索吗?
按照 Bennett Yeo 的建议,我发现了以下内容:
没有直接的方法来检查两个文件是否链接到相同的数据,但我们可以通过比较文件的唯一 ID(或基于 UNIX 的系统中的 inode)来创建我们自己的方法。据我了解,此值用作磁盘上实际内容的索引。
Bennett 还链接了 This thread,这给了我两种获取文件唯一 ID 的方法:
- 链接的答案建议从
kernel32.dll
调用 GetFileInformationByHandle
。正如该方法的名称所暗示的那样,我必须首先获取文件的句柄,但每当我尝试获取一个句柄时,都会抛出一个异常,指出目标文件已被另一个进程使用。
- 最后,使用命令
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",它就应该可以工作。
仍然欢迎任何更可靠的解决方案。
我已经为 System.IO.FileInfo
class 编写了一个扩展方法来创建硬 link,它是这样的:
[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 linked 到相同的数据?
我想出了一些方法来测试它:
- 检查数据是否在整个更改过程中保持一致由于创建硬link副本只是为文件赋予第二个名称,因此对第一个实例所做的任何修改将反映在第二个上,反之亦然。如果两个文件之间的数据在修改后保持一致,则可以安全地假设这些文件都被硬linked 到相同的数据。
- 断言硬 link 的创建不会影响父文件夹的大小。 因为硬 link 副本不会复制磁盘上的任何数据,父目录不应该变得更重。如果在调用该方法时创建了一个与原始文件具有相同内容的新文件,并且父文件夹的大小没有改变(或者比普通副本所做的更少),则新文件必须是硬文件link复制。
但是,这些方法有味道。 OS 中的某处必须至少有一种内置方法来检查两个文件是否指向磁盘上的相同数据!
有人可以分享线索吗?
按照 Bennett Yeo 的建议,我发现了以下内容:
没有直接的方法来检查两个文件是否链接到相同的数据,但我们可以通过比较文件的唯一 ID(或基于 UNIX 的系统中的 inode)来创建我们自己的方法。据我了解,此值用作磁盘上实际内容的索引。
Bennett 还链接了 This thread,这给了我两种获取文件唯一 ID 的方法:
- 链接的答案建议从
kernel32.dll
调用GetFileInformationByHandle
。正如该方法的名称所暗示的那样,我必须首先获取文件的句柄,但每当我尝试获取一个句柄时,都会抛出一个异常,指出目标文件已被另一个进程使用。 - 最后,使用命令
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",它就应该可以工作。
仍然欢迎任何更可靠的解决方案。