Как определить, связаны ли два файла с одними и теми же данными?

Я написал метод расширения для класса 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 связаны с одними и теми же данными?

Я придумал несколько способов проверить это:

  • Проверьте, согласуются ли данные во всех изменениях. Поскольку создание копии с жесткой ссылкой — это просто присвоение второму имени файлу, любые изменения, внесенные в первый экземпляр, будут отражены во втором, и наоборот. . Если данные между двумя файлами остаются согласованными, несмотря на изменения, можно с уверенностью предположить, что эти файлы жестко связаны с одними и теми же данными.
  • Утверждайте, что создание жесткой ссылки не влияет на размер родительской папки. Поскольку копия с жесткой ссылкой не копирует данные на диск, родительский каталог не должен становиться тяжелее. Если при вызове метода создается новый файл с тем же содержимым, что и исходный файл, а родительская папка не изменилась в размере (или увеличилась меньше, чем обычная копия), новый файл должен быть жестко копия ссылки.

Однако эти методы пахнут. Где-то в ОС должен быть хотя бы один встроенный метод, чтобы проверить, указывают ли два файла на одни и те же данные на диске!

Кто-нибудь может поделиться наводкой?


person 3Nd_R1m    schedule 30.12.2019    source источник
comment
Возможно, вы сможете получить идентификатор файла, подобный индексному узлу, и проверить, эквивалентен ли он. См. ответ asveikau на Есть ли у Windows номер Inode. По-видимому, это также можно проверить с помощью командная строка.   -  person Bennett Yeo    schedule 30.12.2019


Ответы (1)


Следуя предложению Беннета Йео, я обнаружил следующее:

Нет прямого способа проверить, связаны ли два файла с одними и теми же данными, но мы можем создать свои собственные методы, сравнивая уникальный идентификатор файла (или индексный дескриптор в системе на основе UNIX). Насколько я понимаю, это значение служит индексом фактического содержимого на диске.

Беннетт также связался с этой веткой, что дало мне два способа получить уникальный идентификатор файла:

  1. Связанный ответ предлагал позвонить GetFileInformationByHandle из kernel32.dll. Как следует из названия метода, я должен сначала получить дескриптор файла, но всякий раз, когда я пытаюсь его получить, возникает исключение, сообщающее, что целевой файл используется другим процессом.
  2. И, наконец, с помощью команды fsutil file queryfileid <filename> (за этот ответ).

Второй метод работает для меня, поэтому я написал следующий код:

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», он должен работать.

Любые более надежные решения по-прежнему приветствуются.

person 3Nd_R1m    schedule 30.12.2019