【问题标题】:Algorithm for determining a file's identity确定文件身份的算法
【发布时间】:2009-01-19 00:05:25
【问题描述】:

对于一个开源项目,我正在文件系统之上编写一个抽象层。

此层允许我将元数据和关系附加到每个文件。

如果文件被重命名/移动或复制,我希望该层能够优雅地处理文件重命名并维护元数据。

为此,我需要一种计算文件身份的机制。显而易见的解决方案是为每个文件计算一个 SHA1 哈希,然后针对该哈希分配元数据。但是……那真的很贵,尤其是电影。

所以,我一直在考虑一种算法,虽然不是 100% 正确,但绝大多数时间都是正确的,而且很便宜。

一种这样的算法可能是使用文件大小和该文件的字节样本来计算散列。

我应该为样本选择哪些字节?如何保持计算便宜且相当准确?我知道这里需要权衡,但性能至关重要。并且用户将能够处理系统出错的情况。

我需要这个算法来处理非常大的文件(1GB+ 和 5K 的小文件)

编辑

我需要此算法在 NTFS 和所有 SMB 共享(基于 Linux 或 Windows)上工作,我希望它支持将文件从一个位置复制到另一个位置的情况(存在 2 个物理副本被视为一个身份) .我什至可能会考虑希望它在 MP3 被重新标记的情况下工作(物理文件已更改,因此我可能对每种文件类型都有一个身份提供者)。

编辑 2

相关问题:Algorithm for determining a file’s identity (Optimisation)

【问题讨论】:

    标签: algorithm filesystems virtualfilesystem


    【解决方案1】:

    分桶,多层比较应该是最快的,并且可以在您讨论的文件范围内进行扩展。

    第一级索引只是文件的长度。

    第二级是哈希。低于一定大小,它是整个文件的散列。除此之外,是的,我同意您对采样算法的想法。我认为可能影响采样速度的问题:

    1. 为避免命中可能高度相似或相同的规则间隔的标题,您需要输入一个不一致的数字,例如:素数或连续素数的倍数。
    2. 避免可能最终遇到常规记录标头的步骤,因此,如果您从样本字节中获得相同的值,尽管位置不同,请尝试通过另一个素数调整该步骤。
    3. 处理具有大量相同值的异常文件,因为它们是未编码的图像,或者只是填充了空值。

    【讨论】:

    • 所有优点。我还要说你需要做大量的实证分析来解决像 2 和 3 中提到的问题。
    • 错误,素数没有非平凡因子。
    • 确实是“素数的因数”措辞不当(脸红)我想我的意思是逐个连续的素数或奇数素数的倍数。我的数学比那更好(只是)。
    【解决方案2】:

    做第一个 128k,在 1mb 标记处再做 128k,在 10mb 标记处再做 128k,在 100mb 标记处再做 128k,在 1000mb 标记处再做 128k,等等。随着文件大小变大,它变得更有可能你将能够仅根据它们的大小来区分两个文件,你散列的数据越来越小。 128k以下的东西都处理好了。

    【讨论】:

    • +1。文件系统读取的局部性对于合理的性能很重要——读取几个大块将比读取分散在整个文件中的相同数量的字节快得多。
    • 从经验测试中得出结论,地方性有很大的不同。
    【解决方案3】:

    信不信由你,我使用刻度来表示文件的最后一次写入时间。它尽可能便宜,但我仍然看到不同文件之间的冲突。

    【讨论】:

    • 问题是它不支持重命名......而且它看起来非常脆弱。
    • 重命名不应更改文件的创建时间。无论如何,我用它来检测一个文件是否被另一个(同名)覆盖。我还存储了文件的哈希值,但如果它仍然是同一个文件,刻度会告诉我。
    • 我明白了.. 计算一个完整的文件哈希对于我的目的来说会很昂贵
    • 另外...您自己说过“便宜”、“不是 100% 正确”和“关键性能”这些词。如果要精确,则必须计算哈希。
    • 试一试,让我知道这些限制,这对我来说也是有用的信息。
    【解决方案4】:

    如果您可以放弃 Linux 共享要求并将自己限制在 NTFS,那么 NTFS 备用数据流将是一个完美的解决方案:

    • 不需要任何类型的散列;
    • 保留重命名;和
    • 可以承受移动(即使在不同的 NTFS 卷之间)。

    您可以阅读更多关于它的信息here。基本上,您只需为您的流添加一个冒号和一个名称(例如“:meta”),然后在其中写入任何您喜欢的内容。因此,如果您有一个目录“D:\Movies\Terminator”,请使用普通文件 I/O 将元数据写入“D:\Movies\Terminator:meta”。如果要保存特定文件(而不是整个文件夹)的元数据,也可以这样做。

    如果您希望将元数据存储在其他位置并且只能够检测同一 NTFS 卷上的移动/重命名,则可以使用 GetFileInformationByHandle API 调用(请参阅 MSDN /en-us/library/aa364952(VS. 85).aspx) 获取文件夹的唯一 ID(结合 VolumeSerialNumber 和 FileIndex 成员)。如果文件/文件夹在同一卷上移动/重命名,此 ID 不会更改。

    【讨论】:

    • 对奇怪的 MSDN 链接感到抱歉 - 因为我是新用户,所以不允许我发布 2 个超链接。
    • GetFileInformationByHandle 的文档说:“nFileIndexLow:与文件关联的唯一标识符的低位部分。此值仅在文件被至少一个进程打开时才有用。如果没有进程将其打开,索引可能会在下次打开文件时更改。”
    【解决方案5】:

    如何存储一些随机整数 ri,然后查找字节 (ri mod n),其中 n 是文件的大小?对于带有头的文件,可以先忽略它们,然后对剩余的字节进行此处理。

    如果您的文件实际上非常不同(不仅仅是某处单个字节的差异,而是说至少有 1% 的差异),那么随机选择的字节会注意到这一点。例如,在字节数相差 1% 的情况下,100 个随机字节将无法注意到的概率为 1/e ~ 37%;增加您查看的字节数会使此概率呈指数级下降。

    使用随机字节背后的想法是,它们基本上可以保证(嗯,从概率上讲)与任何其他字节序列一样好,除了它们容易受到某些问题的影响与其他序列(例如,碰巧查看文件格式的每个第 256 个字节,其中该字节必须为 0 或其他内容)。

    更多建议:

    • 不要抓取字节,而是抓取更大的块来证明寻找成本是合理的。
    • 我建议始终查看文件的第一个块左右。由此,您可以确定文件类型等。 (例如,您可以使用file 程序。)
    • 至少权衡整个文件的 CRC 之类的成本/收益。它不像真正的加密哈希函数那样昂贵,但仍然需要读取整个文件。好处是它注意到单字节差异。

    【讨论】:

    • 太棒了,我认为这些方面的一些东西可以通过一些改变来工作,事情是尽快寻求最大的成本,所以你可能希望在每个随机点读取比一个更多的字节,因为你是已经有了
    【解决方案6】:

    嗯,首先您需要更深入地了解文件系统的工作原理。您将使用哪些文件系统?大多数文件系统都支持硬链接和软链接,因此“文件名”信息不一定存储在文件本身的元数据中。

    实际上,这就是可堆叠分层文件系统的全部意义所在,您可以通过各种方式对其进行扩展,例如支持压缩或加密。这就是“vnodes”的全部意义所在。您实际上可以通过多种方式做到这一点。其中一些非常依赖于您正在查看的平台。这在使用 VFS 概念的 UNIX/Linux 系统上要简单得多。例如,您可以在 ext3 之上实现自己的层,或者您拥有什么。

    ** 阅读您的编辑后,还有更多内容。如前所述,文件系统已经使用 inode 之类的东西来做到这一点。散列可能不是一个好主意,不仅因为它很昂贵,而且因为两个或多个原像可以共享同一个图像;也就是说,两个完全不同的文件可以具有相同的哈希值。我认为您真正想做的是利用文件系统已经公开的元数据。当然,这在开源系统上会更简单。 :)

    【讨论】:

    • 我将使用 NTFS .. 请参阅我的扩展问题。我还想支持存在于 2 个独立文件系统上的文件。
    • 回复:“两个不同的文件共享相同的哈希”,这在我的情况下是完全可取的......如果我的文件系统中存在两次电影,我想为两个文件获得相同的身份.
    • 嗯,我的意思是,您可能拥有电影和数据库日志文件或共享相同哈希的任何内容。除非您将身份定义为“具有相同的哈希值”,否则我认为这没有帮助。
    • 好的,是的,我将身份定义为字节集合的 SHA1 哈希 .... 并寻找一种可以对字节进行采样(而不是读取所有字节)并产生 SHA1 哈希的算法对于(mp3/avi/ogg/doc/xml 等...文件)来说,绝大多数时间都是正确的
    【解决方案7】:

    我应该为样本选择哪些字节?

    我想我会尝试使用一些算术级数,比如斐波那契数。这些很容易计算,并且它们的密度越来越小。小文件的采样率会比大文件高,而且样本仍然会超过整个文件中的点。

    【讨论】:

    • 使用斐波那契数之类的东西的一个问题是,它们对于某些文件大小可能表现不佳(例如,斐波那契数 mod 144 或 6765 的周期很小)。对于某些文件类型,算术级数 (a+n*b) 可能表现不佳。但是“足够随机”的序列应该可以工作。
    【解决方案8】:

    这项工作听起来可以在文件系统级别更有效地实现,或者使用一些松散的版本控制系统近似(两者?)。

    要解决最初的问题,您可以为每个文件保留一个(文件大小、哈希字节数、哈希值)的数据库,并尝试最小化每个文件大小的哈希字节数。每当您检测到冲突时,您要么拥有相同的文件,要么增加哈希长度以超过第一个差异。

    毫无疑问,需要进行优化并在 CPU 与 I/O 之间进行权衡,但对于不会出现误报的事情来说,这是一个好的开始。

    【讨论】:

      猜你喜欢
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 2012-05-19
      • 2011-04-25
      • 1970-01-01
      • 2016-05-26
      • 1970-01-01
      • 2013-03-25
      相关资源
      最近更新 更多