由于对一个完整的二进制文件进行散列太重,无法在相当快的时间内进行计算:
哪些文件信息足以对文件进行哈希处理下面列出了生成的哈希理想情况下应该具有的属性:
- 与目录中的其他文件无冲突
- 快速
- 捕获所有文件更改
根据经验,我们可以用来创建足够熵的信息越少越好。由于特定信息的检索速度可能在很大程度上取决于给定的环境(操作系统、语言的文件IO、所用库的IO等),因此这里应该忽略它。
(这是我第一次尝试使用社区维基。我之所以将其作为一个社区维基,是因为这里询问的信息非常通用,但(希望)信息丰富。)。我还希望将这个问题标记为社区wiki,以便在合适的地方进行改进。)
概述
我们的目标是跟踪两个文件状态之间的差异,同时不使用冗余数据。因此,每个信息源必须是文件状态信息的不相交子集。
以下项目表示有关文件的信息来源:
- 文件名
- 相对于指定文档根的目录路径(也称为来自文档根的绝对路径)
- 文件权限
- 文件所有者(用户/组)
- 上次更改时间
- 文件的大小
- 文件所在计算机的主机名
- 实际保存的二进制数据
每项注意事项
文件名称
文件名是其绝对文件系统路径(最后一位)的一部分,正如@La comadreja所说,它的独特之处在于,一个系统上没有两个文件可以具有相同的绝对路径。强烈建议将文件名与其绝对路径的其余部分结合使用(有关更多信息,请参阅目录路径),以避免与其他文件发生哈希冲突。
目录路径
虽然文件的绝对路径是完全唯一的,但应该注意的是,在某些情况下,散列绝对路径可能是不合适的。例如,当两个文件在两台机器上的绝对路径不相同时,比较不同机器上两个文件的哈希很可能会失败。这在具有不同操作系统和/或体系结构的机器上变得更加困难。因此,我们鼓励指定一个文档根并从中解析一个绝对路径。
权限
如果你想跟踪对文件权限的更改,下面的测试表明,你需要将它们直接合并到你的哈希中,因为它们不会更改有关文件的任何其他信息(尤其是时间戳)。然而,请注意,在不同的机器上,权限的处理方式非常不同,因此这里必须小心(例如,使用规范的权限转换方案)。
所有权
所有权,就像权限一样,在体系结构和文件系统之间的处理方式非常不同。所有权的变更不会改变其他信息(如以下测试所示)。
时间戳
文件的时间戳也不是在所有(或至少是最常见的)系统中统一实现的。首先,我们可以查看不同文件系统上的不同时间戳:创建日期、修改日期、访问日期等。就我们的目的而言,修改日期是最合适的,因为它受到大多数可用文件系统[1]的支持,并包含我们所需的确切信息:文件的最后一次更改。然而,比较不同操作系统中的文件可能会带来问题,因为Windows和Unix(通常)处理时间戳的方式不同(有关该问题的详细文章,请参阅此处[2])。请注意,每当编辑文件时,文件的修改日期都会发生变化(忽略边缘情况),因此时间戳表示文件大小的变化(请注意,情况并非如此,请参阅文件大小)。
文件大小
以字节为单位的文件大小非常好地指示文件是否已被编辑(权限、所有权和名称更改除外),因为每次编辑都会更改文件内容,从而更改其大小。然而,如果添加到文件中的内容与删除内容一样大,则这种情况就不成立。因此,文件时间戳可以是更好的指示符。此外,计算文件二进制大小可能需要大量计算。
主机名
如果要比较多个主机上的文件,并将不同主机上的相同文件视为不同的文件,则应在哈希中包括机器的主机名(或主机的另一个合适的唯一标识符)。
二进制数据
当然,文件的二进制数据具有检查文件是否已更改的所有必要信息。然而,它也过于资源密集,不具有任何实用性。我非常不鼓励使用这些信息。
建议
以下源应用于比较文件:
- 文件名
- 目录路径
- 时间戳(有关问题,请参阅上文)
以下额外来源可用于跟踪更多信息:
- 权限(见上文)
- 所有权(见上文)
- 主机名(在不同机器之间进行比较时)
应忽略以下信息来源:
- 文件大小
- 二进制数据
测试
我在Debian上做了一些测试,检查更改一个信息是否会更改另一个信息。最有趣的是,重命名、权限更改和所有者更改间戳更改或文件大小更改。(注意,这些测试目前只在Debian Linux上进行测试。其他操作系统可能会有不同的表现。)
$ ls -l
-rw-r--r-- 1 alex alex 30 Apr 26 11:04 bar
-rw-r--r-- 1 alex alex 0 Apr 26 11:03 baz
-rw-r--r-- 1 alex alex 14 Apr 26 11:04 foo
$ mv baz baz2
$ ls -l
-rw-r--r-- 1 alex alex 30 Apr 26 11:04 bar
-rw-r--r-- 1 alex alex 0 Apr 26 11:03 baz2
-rw-r--r-- 1 alex alex 14 Apr 26 11:04 foo
$ chmod 777 foo
$ ls -l
-rw-r--r-- 1 alex alex 30 Apr 26 11:04 bar
-rw-r--r-- 1 alex alex 0 Apr 26 11:03 baz2
-rwxrwxrwx 1 alex alex 14 Apr 26 11:04 foo
$ mv baz2 baz
$ echo "Another string" >> bar
$ ls -l
-rw-r--r-- 1 alex alex 45 Apr 26 11:17 bar
-rw-r--r-- 1 alex alex 0 Apr 26 11:03 baz
-rwxrwxrwx 1 alex alex 14 Apr 26 11:04 foo
$ sudo chown root baz
$ ls -l
-rw-r--r-- 1 alex alex 45 Apr 26 11:17 bar
-rw-r--r-- 1 root alex 0 Apr 26 11:03 baz
-rwxrwxrwx 1 alex alex 14 Apr 26 11:04 foo
假设所有文件都在同一台机器上,目录路径和文件名应该产生唯一的组合,因为同一目录中的两个文件不能具有相同的名称。上次更改的目录路径、文件名和时间戳应捕获每个更改。
如果文件位于不同的计算机上,则应将计算机名称包含在目录路径中。