precizer/precizer
GitHub: precizer/precizer
precizer 是一个用纯 C 编写的高性能文件完整性验证工具,通过 SQLite 数据库持久化 SHA512 校验和,支持断点续扫和跨机器比较,专门解决超大规模文件树的逐字节同步验证问题。
Stars: 21 | Forks: 1
[
链接至俄语 README 页面](README.ru.md)
# Precizer:适用于任何规模文件系统的数据完整性验证工具
一个小巧、高性能的文件完整性与比较工具
“真正伟大的应用程序总是能装进一张软盘里。希望外面还有人记得那是什么东西……但这与软盘无关,而是与优秀的软件有关!”© :-D

[](https://github.com/precizer/precizer/actions/workflows/precizer.yml)
[](https://www.bestpractices.dev/projects/12159)
[](https://scorecard.dev/viewer/?uri=github.com/precizer/precizer)
## TL;DR
### 概述
**precizer** 是一个用纯 C 语言编写的轻量级、高性能 CLI 工具。它专为文件完整性验证和比较而设计,在验证同步结果方面尤为有效。该程序会遍历目录树,并构建一个包含文件及其校验和的数据库,以实现快速、可重复的比较。
**precizer** 专为嵌入式系统和大规模集群环境而构建,通过比较各个源中的文件和校验和来检测同步偏移。它还可以通过比较在不同时间点从同一源捕获的数据库来分析历史更改。
### 基础示例
考虑这样一个场景:两台机器分别在 `/mnt1` 和 `/mnt2` 处挂载了包含相同数据的大型卷。目标是通过逐字节的比较来验证内容是否完全一致,或者是否存在差异。
1. 在第一台机器(例如,主机名为 `host1`)上运行 **precizer**:
```
precizer --progress /mnt1
```
此命令遍历 `/mnt1` 下的目录树,在当前目录中创建一个数据库文件 `host1.db`。`--progress` 标志提供实时进度更新,显示已遍历的总空间和已处理的文件数。
2. 在第二台机器(例如,主机名为 `host2`)上运行 **precizer**:
```
precizer --progress /mnt2
```
这将在当前目录中生成一个数据库文件 `host2.db`。
3. 将 `host1.db` 和 `host2.db` 复制到其中一台机器上,并运行以下命令进行比较:
```
precizer --compare host1.db host2.db
```
输出将显示:
- 在 `host1` 上存在但在 `host2` 上缺失的文件,反之亦然。
- 两台主机上都存在但**校验和不同**的文件。
### 用于一致比较的相对路径
**precizer** 在其数据库中仅存储相对文件路径。例如,位于以下位置的文件:
```
/mnt1/abc/def/aaa.txt
```
将被存储为:
```
abc/def/aaa.txt
```
不包含 `/mnt1` 前缀。类似地,`/mnt2` 上的对应文件:
```
/mnt2/abc/def/aaa.txt
```
也将被存储为:
```
abc/def/aaa.txt
```
这确保了即使文件位于不同的挂载点或源中,它们仍然可以在相同的相对路径及其各自的校验和下进行准确比较。
## [下载](https://github.com/precizer/precizer/releases/latest/)
下载 [https://github.com/precizer/precizer/releases/latest/](https://github.com/precizer/precizer/releases/latest/) 的可执行文件适用于:
* Linux x86_64 [precizer_linux_x86_64_portable.zip](https://github.com/precizer/precizer/releases/latest/download/precizer_linux_x86_64_portable.zip)
* Linux arm aarch64 [precizer_linux_aarch64_portable.zip](https://github.com/precizer/precizer/releases/latest/download/precizer_linux_aarch64_portable.zip)
* macOS arm64 [precizer_macos_arm64.zip](https://github.com/precizer/precizer/releases/latest/download/precizer_macos_arm64.zip)
发布包中包含 zip 压缩包格式的可移植可执行文件。
### 下载、解压并运行
自动化升级到更新版本的通用方法
```
# 下载和解压新版本的自动化
# 下载
wget -O precizer.zip -q "https://github.com/precizer/precizer/releases/latest/download/precizer_$(uname -s | tr '[:upper:]' '[:lower:]' | sed 's/darwin/macos/')_$(uname -m | sed 's/amd64/x86_64/')$( [ "$(uname -s)" = "Linux" ] && echo '_portable' ).zip"
# 解压归档文件
unzip -jqo precizer.zip '*/precizer' -d ./
# 运行
./precizer --version
```
### 便携版构建的技术细节
* Linux 构建是一个单一的可执行文件,是静态链接的 ELF 二进制文件,不依赖于任何特定的发行版。它几乎可以在任何 Linux 发行版上立即运行,并且不需要外部共享库。
* 该二进制文件由 GitHub CI/CD 生成,然后使用 [UPX (the executable packer)](https://upx.github.io) 进行压缩。自解压的压缩二进制文件随后被放入 ZIP 压缩包中以便于下载。该文件可以从压缩包中提取并直接运行。
* macOS 不支持静态链接,因此运行下载的应用程序需要系统上提供以下库:sqlite3、pcre2、argp 和 fts。
## 变更日志
按版本划分的更改列表可在单独的文件中找到:[CHANGELOG](CHANGELOG.md)
## 技术细节
考虑这样一个场景:一个主存储系统拥有一个备份副本。例如,这可以是数据中心存储及其*容灾 (Disaster Recovery)* 副本。
从主存储到备份的同步是定期进行的,但由于*海量数据*,同步很可能不是逐字节进行的,而是通过检测文件系统内的*元数据更改*来进行。在这种情况下,会考虑*文件大小*和*修改时间*,但不会*逐字节验证实际内容*。
这种方法是合理的,因为主数据中心和*容灾*站点通常拥有*高速通信通道*,但完整的逐字节同步将花费*不合理的漫长时间*。
像 `rsync` 这样的工具允许进行两种类型的同步——*基于元数据的*和*逐字节的*——但它们有一个*主要缺点*:*会话之间不保留状态*。
以下场景说明了这个问题:
* 给定:服务器“A”和服务器“B”(主数据中心和容灾系统)
* 服务器“A”上的某些文件已被修改。
* `rsync` 算法根据大小和修改时间的变化检测到这些文件,并将它们同步到服务器“B”。
* 在主数据中心和容灾站点之间的同步过程中发生了多次连接故障。
* 为了验证数据完整性(即确保“A”和“B”上的文件逐字节一致),通常使用 `rsync` 进行逐字节比较。其过程如下:
* 在服务器“A”上以 `--checksum` 模式启动 `rsync`,尝试在单个会话中按顺序计算“A”和“B”上的校验和。
* 对于大规模存储系统,此过程需要极长的时间。
* 由于 `rsync` 不会在会话之间保存计算出的校验和,因此它带来了一些技术挑战:
* 如果连接中断,`rsync` 将终止会话,并且在下次运行时,所有内容都必须从头开始!考虑到庞大的数据量,执行逐字节验证以确保完整数据完整性成为一项不可能完成的任务。
* 存储子系统的故障也可能导致二进制不一致。在这种情况下,文件系统元数据无法可靠地确定“A”和“B”上的文件内容是否真正一致。
* 随着时间的推移,错误不断累积,增加了在系统“B”上维护系统“A”的容灾副本不一致的风险,使得整个容灾工作变得毫无意义。标准实用程序无法检测到这些不一致,并且技术人员可能完全不知道容灾存储中的数据完整性问题。
* 为了克服这些限制,开发了 precizer。该程序能准确识别“A”和“B”之间哪些文件存在差异,从而可以通过必要的更正重新同步它们。该工具以最高速度运行(将硬件性能推向极限),因为它是用纯 C 语言编写的,并使用了经过优化的高性能算法。该程序旨在处理从小型文件到 PB 级的数据量,没有上限*。
* precizer 这个名字来源于 precision(精确)这个词,暗示着某种能提高准确性的东西。
* 该程序精确地分析目录内容(包括子目录),计算遇到的每个文件的校验和,同时将元数据存储在 SQLite 数据库(一个常规的二进制文件)中。
* precizer 具有容错能力,可以从断点处恢复执行。例如,如果在分析 PB 级文件时通过 Ctrl+C 终止了程序,它将不会从头开始重新扫描,而是利用数据库中先前记录的数据准确地从上次停止的地方继续。这极大地节省了系统管理员的资源、时间和精力。
* 可以使用任何方法随时中断程序,这对于已扫描的数据和由 precizer 创建的数据库来说是完全安全的。
* 如果程序被有意或无意地停止,则无需担心进度丢失。所有结果都会被完整保留,并可在后续运行中使用。
* 校验和计算依赖于加密的 SHA512 哈希算法,该算法可靠、快速,并提供极强的实际抗碰撞性。如果两个大文件即使有一个字节的差异,SHA512 极大概率会产生不同的校验和;与 CRC32 和现已过时的 SHA1 不同,它是专为稳健的数据完整性验证而设计的
* precizer 中的算法旨在使数据库轻松保持最新状态,而无需从头开始重新计算所有内容。只需使用 `--update` 参数运行程序,新文件将被添加到数据库中,而已删除文件的记录将被移除。如果文件被修改并且其大小发生了变化,其 SHA512 校验和将被重新计算并在数据库中更新。
* 在 `--update` 期间,缺失文件的条目会被移除,但不可访问文件(权限被拒绝)的记录默认会被保留。这种保护机制的存在是因为权限可能会发生临时变化(所有权、ACL、临时的挂载问题),在这种状态下删除记录会悄无声息地抹除有效的数据库历史记录。仅在必须删除那些数据库记录时,才应将 `--db-drop-inaccessible` 与 `--update` 结合使用。
* 当启用 `--progress` 时,在会话期间收集的警告和错误将在退出前作为一个整体打印出来,这样重要的消息(例如,文件访问问题)就不会在常规的日志中丢失。
* `--quiet-ignored` 选项会抑制由 `--ignore` 和 `--include` 过滤的路径的单文件日志行。这有助于在忽略正则表达式已调整并在使用中稳定后,保持程序日志中没有多余的消息;其他警告和错误仍然可见。
* 有一个选项可以考虑在更新数据库时不仅考虑文件大小,还要考虑文件的创建或修改时间戳。这意味着文件元数据的任何更改都将触发 SHA512 校验和的重新计算并在数据库中进行更新。例如,如果文件的 ctime 发生变化但其大小保持不变,如果仅使用 `--update` 参数,则校验和将不会被重新计算。要强制重新计算此类文件的校验和,应添加 `--watch-timestamps`。此选项默认处于禁用状态,因为 ctime(如 mtime)可能会由于诸如 `chmod` 或 `chown` 等命令而频繁更改,即使文件内容保持不变。
* precizer 可用作安全监控工具,检测在元数据未变的情况下内容可能已被更改的未经授权的文件修改。
* 安全性:
* 程序永远不会修改、删除、移动或复制它处理的任何文件或目录。
* 程序枚举文件,计算 SHA512 校验和,并更新本地数据库;所有更改都严格限制在数据库内。
* 数据库不存储文件内容。它存储相对路径、校验和以及大小和时间戳(ctime/mtime)等元数据。
* 程序不打开网络套接字。
* 程序不传输数据。
* 程序不需要特权执行,也不使用 SUID 位或其他不安全的权限位。
* 未提供任何用于权限提升或其他安全违规的功能。
* 性能主要受限于磁盘子系统的速度。每个文件都会被逐字节读取,并计算其 SHA512 校验和。
* 得益于 SQLite 和 FTS 库,程序运行速度非常快 ([man 3 fts](https://man7.org/linux/man-pages/man3/fts.3.html))。
* 命令行参数解析通过 ARGP 库处理。
* 正则表达式支持由 PCRE2 提供。
* 该程序可以安全地用于极其庞大的文件、目录和深度嵌套的子目录。由于 FTS 库避免了递归,即使在极端嵌套级别下也能防止堆栈溢出。
* 由于其紧凑且可移植的代码库,该程序甚至可以在 NAS 系统、嵌入式平台或 IoT 设备等专业设备上使用。
* 使用 [DB Browser for SQLite](https://sqlitebrowser.org) 可以浏览由 **precizer** 创建的数据库内容。
## 问题与错误报告
* `--help` 选项的设计尽可能详细,专门用于帮助可能不具备高级技术知识的用户。
* 作者联系方式:
* [GitHub Discussions](https://github.com/precizer/precizer/discussions)。
* [错误报告与功能需求](https://github.com/precizer/precizer/issues/new)。
## 贡献
欢迎各种贡献。请从 [CONTRIBUTING](CONTRIBUTING.md) 开始了解工作流程、依赖项、验证步骤和 PR 预期。对于未解决的请求,请查看 [Issues](https://github.com/precizer/precizer/issues) 列表,并选择与您的兴趣和参与程度相匹配的任务。
## 构建与安装
### 为发行版打包
* 作者已建立了一个使用 GitHub Workflows 的自动化构建系统,并将继续维护新版本。
* 作者**不**打算亲自为_所有_现有的操作系统发行版打包和维护 **precizer**。
* 如果为特定发行版打包在调整代码时遇到重大挑战,可以帮助支持该计划并为目标发行版或包管理器优化程序。联系方式详见[“问题与错误报告”](#questions--bug-reports)部分。
### 使用 Docker 构建
已经支持通过 Docker 构建程序。准备了几个经过调优的平台,可以作为构建发行版进行选择。已成功测试的发行版:
* Almalinux
* Alpine
* Arch
* Debian
* Gentoo
* Rocky
* Ubuntu
配置细节和已安装的库列在 `.docker/` 目录下的相应 Dockerfiles 中。
构建目标使用 `docker--` 的形式(例如 `debian` 和 `dynamic-production`)。
```
make docker-gentoo-production
```
这使用 Gentoo Docker 容器构建了一个生产环境二进制文件。
```
make docker-ubuntu-production
```
这使用 Ubuntu 构建了相同的 `production` 目标。
构建完成后,项目目录中会出现一个可执行文件 `precizer`(在容器内构建)。使用 Docker 的主要好处是主机系统上不需要完整的构建工具链、库及其依赖项;运行 Docker 即可生成二进制文件。下一步是选择二进制文件的变体。如有疑问,`make portable` 是一个不错的起点。所有可用的构建变体如下所述。
### 手动构建
#### 准备工作
```
git clone --depth=1 https://github.com/precizer/precizer.git
cd precizer
```
#### 便携版二进制文件
```
make portable
```
结果是一个单一的静态链接、自解压的压缩 UPX ELF 文件,没有动态依赖项。它包含了整个程序,几乎可以在任何现代 Linux 发行版上运行。该文件可以复制到相同架构(x64/arm 等)的任何平台上。
该程序针对**最大的可移植性**进行了优化。
编译和链接标志:`-static -O2 -mtune=generic`
Docker 替代方案:
```
make docker-ubuntu-portable
```
或将 `-ubuntu-` 替换为上述列表中的任何发行版。
#### 针对本地 CPU 优化的单一二进制文件
```
make production
```
结果是一个静态链接、自解压的压缩 UPX ELF 文件,针对本地 CPU 进行了调优。它包含了整个程序,可以在本地机器上运行,并将使用最大可用的 CPU 特性。
该程序针对**本地硬件上的最大可能性能**进行了优化。
编译和链接标志:`-static -O3 -march=native`
Docker 替代方案:
```
make docker-ubuntu-production
```
或将 `-ubuntu-` 替换为上述列表中的任何发行版。
#### 针对本地 CPU 优化的动态链接二进制文件
```
make dynamic-production
```
结果是一个大约 **50 KB** 的 ELF 可执行文件。它针对本地 CPU 进行了调优,并与系统上安装的库进行动态链接;它也是自解压并经过 UPX 压缩的。如果安装了 sqlite3、pcre2、argp 和 fts 等库,它可以在本地机器上构建并运行。
该二进制文件针对**最大的性能和最小的体积**进行了优化。
编译标志:`-O3 -march=native`
Docker 替代方案:
```
make docker-ubuntu-dynamic-production
```
或将 `-ubuntu-` 替换为上述列表中的任何发行版。
#### 测试
`tests/fixtures/` 目录中的测试集可用于评估程序的功能。
测试执行:
```
git clone https://github.com/precizer/precizer.git
cd precizer
make tests
```
#### 安装
只需将生成的 **precizer** 可执行文件复制到 `$PATH` 环境变量中列出的任何位置,即可快速调用。
#### 特定操作系统的构建依赖项
在 Linux 上安装构建和编译工具
#### Arch Linux
```
sudo pacman -S --noconfirm base-devel gcc-libs sqlite pcre2 upx
```
#### Ubuntu/Debian Linux
```
sudo apt -y install gcc make libpcre2-dev libsqlite3-dev upx-ucl
```
#### Alpine Linux
```
sudo apk add --update build-base pcre2-dev pcre2-static fts-dev argp-standalone sqlite-dev upx
```
#### Almalinux/Rocky Linux
```
sudo dnf -y install gcc make sqlite sqlite-devel glibc-devel pcre2 pcre2-devel upx pcre2-static glibc-static
```
#### Gentoo Linux
```
echo "dev-libs/libpcre2 static-libs" >> /etc/portage/package.use/libpcre2;
emerge dev-libs/libpcre2 app-arch/upx
```
#### 清理
##### 移除所有构建产物
```
make purge
```
## 使用示例
### 示例 1
将文件添加到两个数据库中并进行比较:
```
precizer --progress --database=database1.db tests/fixtures/diffs/diff1
precizer --progress --database=database2.db tests/fixtures/diffs/diff2
precizer --compare database1.db database2.db
```
database1.db 和 database2.db 数据库的比较正在开始…
开始数据库文件 database1.db 完整性检查…
数据库 database1.db 已通过验证且状况良好
开始数据库文件 database2.db 完整性检查…
数据库 database2.db 已通过验证且状况良好
**这些文件不再存在于 database1.db 中,但在 database2.db 中仍然存在**
path1/AAA/BCB/CCC/b.txt
**这些文件不再存在于 database2.db 中,但在 database1.db 中仍然存在**
path2/AAA/ZAW/D/e/f/b_file.txt
**这些文件的 SHA512 校验和在 database1.db 和 database2.db 之间不匹配**
2/AAA/BBB/CZC/a.txt
3/AAA/BBB/CCC/a.txt
4/AAA/BBB/CCC/a.txt
path1/AAA/ZAW/D/e/f/b_file.txt
path2/AAA/BCB/CCC/a.txt
database1.db 和 database2.db 数据库的比较已完成
precizer 成功完成其执行,没有任何问题
在 `--compare` 模式下,`--ignore` 和 `--include` 限制了用于生成比较报告的相对路径范围。如果过滤器隐藏了部分差异,则相等性消息仅适用于剩余的过滤范围。使用 `--include` 重新引入的任何路径将再次参与差异列表和最终摘要
### 示例 2
数据库更新
再次运行前一个示例。第一次尝试。警告消息。
```
precizer --progress --database=database1.db tests/fixtures/diffs/diff1
```
数据库 database1.db 先前已创建,并且已包含带有文件及其校验和的数据。仅当确定需要更新数据库并且应将文件信息(包括更改、删除和添加)与数据库同步时,才使用 `--update` 选项。
ERROR: precizer 进程由于错误意外终止
必须包含 **--update** 参数。此参数是保护数据库免受意外执行导致的数据丢失所必需的。
```
precizer --update --progress --database=database1.db tests/fixtures/diffs/diff1
```
主数据库文件名:database1.db
开始数据库文件 database1.db 完整性检查…
数据库 database1.db 已通过验证且状况良好
启动文件系统遍历以计算文件数量和存储使用量
总大小:45B,总项目数:58,目录:46,文件:12,符号链接:0
**自程序启动以来,数据库文件 database1.db 尚未被修改**
precizer 成功完成其执行,没有任何问题
进行以下调整:
```
# 修改文件
echo -n " " >> tests/fixtures/diffs/diff1/1/AAA/BCB/CCC/a.txt
# 添加新文件
touch tests/fixtures/diffs/diff1/1/AAA/BCB/CCC/c.txt
# 移除文件
rm tests/fixtures/diffs/diff1/path2/AAA/ZAW/D/e/f/b_file.txt
```
使用 `--update` 参数再次运行 **precizer**:
```
precizer --update --progress --database=database1.db tests/fixtures/diffs/diff1
```
主数据库文件名:database1.db
开始数据库文件 database1.db 完整性检查…
数据库 database1.db 已通过验证且状况良好
启动文件系统遍历以计算文件数量和存储使用量
总大小:43B,总项目数:58,目录:46,文件:12,符号链接:0
已使用 **--update** 选项,因此有关文件的信息将针对数据库 database1.db 进行更新
文件遍历已开始
**这些文件已被添加或更改,这些更改将反映在数据库 database1.db 中:**
1/AAA/BCB/CCC/a.txt 更改了 lsize & ctime & mtime 并重新计算了哈希
1/AAA/BCB/CCC/c.txt 已添加
文件遍历完成
总大小:43B,总项目数:58,目录:46,文件:12,符号链接:0
**这些文件不再存在或被忽略,将在数据库 database1.db 中被删除:**
path2/AAA/ZAW/D/e/f/b_file.txt
开始清理主数据库…
主数据库已被清理
**自程序启动以来,数据库文件 database1.db 已被修改**
precizer 成功完成其执行,没有任何问题
输出中的更改标签含义:
- `lsize` — 以字节为单位的逻辑文件大小 (`st_size`)
- `asize` — 以字节为单位的磁盘分配大小 (`st_blocks * 512`)
- `ctime` — 元数据/状态更改时间
- `mtime` — 文件内容修改时间
每次 **precizer** 运行时,它都会遍历文件系统,然后检查数据库中是否已存在特定文件的记录。换言之,程序将磁盘上文件系统的当前状态置于优先地位。
**precizer** 中的目录遍历工作方式类似于 `rsync`,因为它使用了类似的算法。
需要注意的是,只要文件的元数据(如大小和最后访问时间,即 **atime**)未发生改变,**precizer** 就不会重新计算数据库中已记录文件的 SHA512 校验和。如果指定了 `--watch-timestamps` 参数,除了文件大小之外,程序还将考虑创建时间 (**ctime**) 和修改时间 (**mtime**)。
在应用程序运行期间检测到的任何新增、删除或修改的文件都将得到相应处理。如果指定了 `--update` 参数,所有更改都将反映在数据库中。
### 示例 3
使用 `--silent` 模式。启用此模式后,程序不会在屏幕上产生任何输出。这在脚本中使用 **precizer** 时非常有用。
例外是 `--compare`:在 `--silent` 模式下,只有比较结果仍然可见。存在差异的路径会被直接打印出来,并且仅当激活了多个比较类别时才会保留类别标题。
将 **--silent** 参数添加到上一个示例中:
```
precizer --silent --update --progress --database=database1.db tests/fixtures/diffs/diff1
```
因此,屏幕上不会显示任何内容。
### 示例 4
`--verbose` 模式下的附加信息。此模式对于调试很有用。
将 **--verbose** 参数添加到上一个示例中:
```
precizer --verbose --update --progress --database=database1.db tests/fixtures/diffs/diff1
```
2025-01-25 09:55:59:820 src/parse_arguments.c:442:parse_arguments:配置: rational_logger_mode=VERBOSE
paths=tests/fixtures/diffs/diff1; database=database1.db; db_file_name=database1.db; verbose=yes; maxdepth=-1; silent=no; force=no; update=yes; watch-timestamps=no; progress=yes; compare=no, db-drop-ignored=no, dry-run=no, check-level=FULL, rational_logger_mode=VERBOSE
2025-01-25 09:55:59:820 src/parse_arguments.c:558:parse_arguments:参数已解析
2025-01-25 09:55:59:820 src/detect_paths.c:025:detect_paths:正在检查作为参数提供的目录路径
2025-01-25 09:55:59:820 src/file_availability.c:034:file_availability:验证路径 tests/fixtures/diffs/diff1 是否存在
2025-01-25 09:55:59:820 src/file_availability.c:053:file_availability:路径 tests/fixtures/diffs/diff1 存在且是一个目录
2025-01-25 09:55:59:821 src/detect_paths.c:036:detect_paths:路径已检测到
2025-01-25 09:55:59:821 src/init_signals.c:034:init_signals:设置信号 SIGUSR2 OK:pid:604770
2025-01-25 09:55:59:821 src/init_signals.c:043:init_signals:设置信号 SIGINT OK:pid:604770
2025-01-25 09:55:59:821 src/init_signals.c:052:init_signals:设置信号 SIGTERM OK:pid:604770
2025-01-25 09:55:59:821 src/init_signals.c:055:init_signals:信号已初始化
2025-01-25 09:55:59:821 src/determine_running_dir.c:018:determine_running_dir:当前目录: /tmp
2025-01-25 09:55:59:821 src/db_determine_name.c:099:db_determine_name:主数据库文件名: database1.db
2025-01-25 09:55:59:821 src/db_determine_name.c:105:db_determine_name:主数据库文件路径: database1.db
2025-01-25 09:55:59:821 src/db_determine_name.c:109:db_determine_name:数据库名称已确定
2025-01-25 09:55:59:821 src/file_availability.c:034:file_availability:验证路径 . 是否存在
2025-01-25 09:55:59:821 src/file_availability.c:053:file_availability:路径 . 存在且是一个目录
2025-01-25 09:55:59:821 src/file_availability.c:034:file_availability:验证路径 database1.db 是否存在
2025-01-25 09:55:59:821 src/file_availability.c:044:file_availability:路径 database1.db 存在且是一个文件
2025-01-25 09:55:59:821 src/db_determine_mode.c:128:db_determine_mode:config->sqlite_open_flag 的最终值: SQLITE_OPEN_READWRITE
2025-01-25 09:55:59:821 src/db_determine_mode.c:129:db_determine_mode:config->db_initialize_tables 的最终值: false
2025-01-25 :55:59:821 src/db_determine_mode.c:131:db_determine_mode:数据库模式已确定
2025-01-25 09:55:59:821 src/db_test.c:061:db_test:开始数据库文件 database1.db 完整性检查…
2025-01-25 09:55:59:821 src/db_test.c:082:db_test:数据库验证级别已设置为 FULL
2025-01-25 09:55:59:821 src/db_test.c:126:db_test:数据库 database1.db 已通过验证且状况良好
2025-01-25 09:55:59:822 src/db_get_version.c:087:db_get_version:在数据库中找到版本号 1
2025-01-25 09:55:59:822 src/db_check_version.c:032:db_check_version:database1.db 数据库文件是版本 1
2025-01-25 09:55:59:822 src/db_check_version.c:061:db_check_version:数据库 database1.db 处于版本 1,不需要任何升级
2025-01-25 09:55:59:822 src/db_init.c:030:db_init:成功打开数据库 database1.db
2025-01-25 09:55:59:822 src/db_init.c:118:db_init:主数据库和表尚未被初始化
2025-01-25 09:55:59:822 src/db_init.c:150:db_init:名为 database1.db 的主数据库已准备好进行操作
2025-01-25 09:55:59:822 src/db_init.c:167:db_init:内存中的 runtime_paths_id 数据库已成功附加到主数据库 database1.db
2025-01-25 09:55:59:822 src/db_init.c:174:db_init:数据库初始化过程已完成
2025-01-25 09:55:59:822 src/db_compare.c:136:db_compare:数据库比较模式未启用。跳过比较
2025-01-25 09:55:59:822 src/db_contains_data.c:086:db_contains_data:数据库 database1.db 此前已被创建
2025-01-25 09:55:59:822 src/db_validate_paths.c:192:db_validate_paths:针对数据库写入的路径和作为参数传递的路径完全相同
2025-01-25 09:55:59:822 src/file_list.c:143:file_list:启动文件系统遍历以计算文件数量和存储使用量
2025-01-25 09:55:59:823 src/file_list.c:038:show_status:总大小: 43B,总项目数: 58,目录: 46,文件: 12,符号链接: 0
2025-01-25 09:55:59:825 src/db_get_version.c:087:db_get_version:在数据库中找到版本号 1
2025-01-25 09:55:59:825 src/db_consider_vacuum_primary.c:025:db_consider_vacuum_primary:未进行任何更改。主数据库不需要清理
2025-01-25 09:55:59:825 src/status_of_changes.c:049:status_of_changes:**自程序启动以来,数据库文件 database1.db 尚未被修改**
2025-01-25 09:55:59:825 src/exit_status.c:027:exit_status:precizer 成功完成其执行,没有任何问题
### 示例 5
使用 `--maxdepth` 参数进行非递归遍历
```
tree tests/fixtures/4
tests/fixtures/4
├── AAA
│ ├── BBB
│ │ ├── CCC
│ │ │ └── a.txt
│ │ └── uuu.txt
│ └── tttt.txt
└── sss.txt
3 directories, 4 files
```
`--maxdepth=0` 参数完全禁用递归。
```
precizer --maxdepth=0 tests/fixtures/4
```
主数据库文件名:myhost.db
路径 myhost.db 不存在或者它不是一个文件
主数据库文件尚不存在。将创建全新的数据库
递归深度限制为:0
文件遍历已开始
**这些文件将被添加到 myhost.db 数据库中:**
sss.txt
文件遍历完成
总大小:2B,总项目数:5,目录:4,文件:1,符号链接:0
开始清理主数据库…
主数据库已被清理
**自上次检查以来,数据库 myhost.db 已被修改(文件已被添加、移除或更新)**
precizer 成功完成其执行,没有任何问题
### 示例 6
忽略路径的示例。要指定用于忽略文件或目录的模式,可以使用 PCRE2 正则表达式。**注意:**正则表达式中的所有路径必须指定为**相对路径**。
可以使用 https://regex101.com 对 PCRE2 正则表达式进行测试和验证。
为了说明相对路径的外观,在不使用 `--ignore` 选项的情况下运行目录遍历,并检查终端如何显示记录在数据库中的相对路径:
```
% tree -L 3 tests/fixtures/diffs
tests/fixtures/diffs
├── diff1
│ ├── 1
│ │ └── AAA
│ ├── 2
│ │ └── AAA
│ ├── 3
│ │ └── AAA
│ ├── 4
│ │ └── AAA
│ ├── path1
│ │ └── AAA
│ └── path2
│ └── AAA
└── diff2
├── 1
│ └── AAA
├── 2
│ └── AAA
├── 3
│ └── AAA
├── 4
│ └── AAA
├── path1
│ └── AAA
└── path2
└── AAA
26 directories, 0 files
```
```
precizer --ignore="^diff1/1/.*" tests/fixtures/diffs
```
在此示例中,初始遍历路径是 `./tests/fixtures/diffs`,生成的忽略路径是 `./tests/fixtures/diffs/diff1/1/` 及其所有子目录(`/*`)。
主数据库文件名:myhost.db
路径 myhost.db 不存在或者它不是一个文件
主数据库文件尚不存在。将创建全新的数据库
文件遍历已开始
**这些文件将被添加到 myhost.db 数据库中:**
diff1/1/AAA/BCB/CCC/a.txt **已忽略 & 未添加**
diff1/1/AAA/ZAW/A/b/c/a_file.txt **已忽略 & 未添加**
diff1/1/AAA/ZAW/D/e/f/b_file.txt **已忽略 & 未添加**
diff1/2/AAA/BBB/CZC/a.txt
diff1/3/AAA/BBB/CCC/a.txt
diff1/4/AAA/BBB/CCC/a.txt
diff1/path1/AAA/BCB/CCC/a.txt
diff1/path1/AAA/ZAW/A/b/c/a_file.txt
diff1/path1/AAA/ZAW/D/e/f/b_file.txt
diff1/path2/AAA/BCB/CCC/a.txt
diff1/path2/AAA/ZAW/A/b/c/a_file.txt
diff1/path2/AAA/ZAW/D/e/f/b_file.txt
diff2/1/AAA/BCB/CCC/a.txt
diff2/1/AAA/ZAW/A/b/c/a_file.txt
diff2/1/AAA/ZAW/D/e/f/b_file.txt
diff2/2/AAA/BBB/CZC/a.txt
diff2/3/AAA/BBB/CCC/a.txt
diff2/4/AAA/BBB/CCC/a.txt
diff2/path1/AAA/BCB/CCC/a.txt
diff2/path1/AAA/BCB/CCC/b.txt
diff2/path1/AAA/ZAW/A/b/c/a_file.txt
diff2/path1/AAA/ZAW/D/e/f/b_file.txt
diff2/path2/AAA/BCB/CCC/a.txt
diff2/path2/AAA/ZAW/A/b/c/a_file.txt
文件遍历完成
总大小:97B,总项目数:114,目录:90,文件:24,符号链接:0
开始清理主数据库…
主数据库已被清理
**自上次检查以来,数据库 myhost.db 已被修改(文件已被添加、移除或更新)**
precizer 成功完成其执行,没有任何问题
享受生活吧!
重复相同的示例,但这次不使用 `--ignore` 选项,以包含先前忽略的三个文件:
```
precizer --update tests/fixtures/diffs
```
主数据库文件名:myhost.db
开始数据库文件 myhost.db 完整性检查…
数据库 myhost.db 已通过验证且状况良好
已使用 **--update** 选项,因此有关文件的信息将针对数据库 myhost.db 进行更新
文件遍历已开始
**这些文件已被添加或更改,这些更改将反映在数据库 myhost.db 中:**
diff1/1/AAA/BCB/CCC/a.txt 已添加
diff1/1/AAA/ZAW/A/b/c/a_file.txt 已添加
diff1/1/AAA/ZAW/D/e/f/b_file.txt 已添加
文件遍历完成
总大小:97B,总项目数:114,目录:90,文件:24,符号链接:0
开始清理主数据库…
主数据库已被清理
**自程序启动以来,数据库文件 myhost.db 已被修改**
precizer 成功完成其执行,没有任何问题
### 示例 7
上一个示例 [示例 6](#example-6) 的延续。
可以通过重复 `--ignore` 选项来同时指定多个用于忽略文件的正则表达式。
数据库将被清除与通过 `--ignore` 参数提供的正则表达式匹配的文件引用:`"diff1/1/.*"` 和 `"diff2/1/.*"`。
必须显式指定 `--db-drop-ignored` 参数,以移除与通过 `--ignore` 选项传递的模式相匹配的文件的数据库条目。
未对文件系统进行任何更改,但被忽略的文件将从数据库中移除。
```
# 通过移除被标记为忽略的文件条目来更新数据库:
precizer \
--update \
--db-drop-ignored \
--ignore="^diff1/1/.*" \
--ignore="^diff2/1/.*" \
tests/fixtures/diffs
```
主数据库文件名:myhost.db
开始数据库文件 myhost.db 完整性检查…
数据库 myhost.db 已通过验证且状况良好
已使用 **--update** 选项,因此有关文件的信息将在数据库 myhost.db 中被删除
**这些文件不再存在或被忽略,将在数据库 myhost.db 中被删除:**
diff1/1/AAA/BCB/CCC/a.txt **清理忽略项**
diff1/1/AAA/ZAW/A/b/c/a_file.txt **清理忽略项**
diff1/1/AAA/ZAW/D/e/f/b_file.txt **清理忽略项**
diff2/1/AAA/BCB/CCC/a.txt **清理忽略项**
diff2/1/AAA/ZAW/A/b/c/a_file.txt **清理忽略项**
diff2/1/AAA/ZAW/D/e/f/b_file.txt **清理忽略项**
开始清理主数据库…
主数据库已被清理
**自程序启动以来,数据库文件 myhost.db 已被修改**
precizer 成功完成其执行,没有任何问题
### 示例 8
结合使用 `--ignore` 和 `--include`
```
# 移除旧数据库并创建一个新数据库,然后用数据填充它:
rm -i "${HOST}.db"
precizer tests/fixtures/diffs
```
此变体使用正则表达式。
用于需要包含的相对路径的 PCRE2 正则表达式。指定的相对路径将被包含在内,即使它们被一个或多个 `--ignore` 参数排除。可以使用 `--include` 指定多个正则表达式。
可以使用 https://regex101.com 检查和测试 PCRE2 正则表达式
数据库将被清除与 `--ignore` 参数中提供的正则表达式匹配的文件引用:`"^.*/path2/.*"` 和 `"diff2/.*"`,但与 `--include` 中的模式匹配的路径将保留在数据库中。
必须额外指定 `--db-drop-ignored` 参数,以便从数据库中移除与通过 `--ignore` 选项传递的正则表达式匹配的文件引用。
```
# 更新数据库,移除对被标记为忽略的文件的引用,
# 除了匹配 --include 模式的路径。
precizer --update \
--progress \
--ignore="^.*/path2/.*" \
--ignore="^diff2/.*" \
--include="^diff2/1/AAA/ZAW/A/b/c/.*" \
--include="^diff2/path1/AAA/ZAW/.*" \
--include="^diff1/path2/AAA/ZAW/A/b/c/a_file\..*" \
--db-drop-ignored \
tests/fixtures/diffs
```
主数据库文件名:myhost.db
开始数据库文件 myhost.db 完整性检查…
数据库 myhost.db 已通过验证且状况良好
已使用 **--update** 选项,因此有关文件的信息将在数据库 myhost.db 中被删除
**这些文件不再存在或被忽略,将在数据库 myhost.db 中被删除:**
diff1/path2/AAA/BCB/CCC/a.txt 清理忽略项
diff1/path2/AAA/ZAW/A/b/c/a_file.txt 清理忽略项
diff1/path2/AAA/ZAW/D/e/f/b_file.txt 清理忽略项
diff2/1/AAA/BCB/CCC/a.txt 清理忽略项
diff2/1/AAA/ZAW/D/e/f/b_file.txt 清理忽略项
diff2/2/AAA/BBB/CZC/a.txt 清理忽略项
diff2/3/AAA/BBB/CCC/a.txt 清理忽略项
diff2/4/AAA/BBB/CCC/a.txt 清理忽略项
diff2/path1/AAA/BCB/CCC/a.txt 清理忽略项
diff2/path1/AAA/BCB/CCC/b.txt 清理忽略项
diff2/path2/AAA/BCB/CCC/a.txt 清理忽略项
diff2/path2/AAA/ZAW/A/b/c/a_file.txt 清理忽略项
开始清理主数据库…
主数据库已被清理
**自程序启动以来,数据库文件 myhost.db 已被修改**
precizer 成功完成其执行,没有任何问题
#### `--compare` 中的相同过滤器
相同的 `--ignore` 和 `--include` 组合也适用于数据库与数据库之间的比较。这些过滤器不仅仅是隐藏屏幕上的单行记录:它们决定了哪些相对路径被保留在比较报告内。因此,最终的摘要和相等性消息仅针对该过滤范围进行评估
```
# 继续示例 1,从隐藏的差异组中仅恢复一个路径
precizer --compare \
--ignore="^(?:2|3|4)/.*" \
--ignore="^path1/.*" \
--ignore="^path2/.*" \
--include="^2/AAA/BBB/CZC/a\.txt$" \
database1.db database2.db
```
database1.db 和 database2.db 数据库的比较正在开始…
开始数据库文件 database1.db 完整性检查…
database1.db 已通过验证且状况良好
开始数据库文件 database2.db 完整性检查…
数据库 database2.db 已通过验证且状况良好
**这些文件的 SHA512 校验和在 database1.db 和 database2.db 之间不匹配**
2/AAA/BBB/CZC/a.txt
database1.db 和 database2.db 数据库的比较已完成
precizer 成功完成其执行,没有任何问题
在此示例中,`2/`、`3/`、`4/`、`path1/` 和 `path2/` 下的每一个差异首先被排除在比较报告之外,然后 `--include` 仅恢复 `2/AAA/BBB/CZC/a.txt`。这使得报告集中于那个单一恢复的路径,而其他隐藏的差异则被排除在最终列表和摘要之外
### 示例 9
使用 `--lock-checksum` 保护不可变归档
对于内容绝不能被改写的归档文件夹,请使用 `--lock-checksum`。它接受用于**相对**路径的 PCRE2 正则表达式(格式与 `--ignore` 相同)。匹配任何锁定模式的路径会被一次性写入数据库。此后,即使使用 `--update`,它们的校验和也不会被重新计算。随后在大小上的任何更改,或者在启用 `--watch-timestamps` 时在时间戳上的任何更改,都会被视为数据损坏并加以报告,而不是更新记录。您可以通过重复该选项来提供多个模式。
```
precizer \
--lock-checksum="^archive/2024/.*" \
--lock-checksum="^snapshots/monthly/.*" \
/mnt/storage
```
在随后的运行中,必须在刷新数据库的同时保留相同的锁定模式:
```
precizer \
--update \
--lock-checksum="^archive/2024/.*" \
--lock-checksum="^snapshots/monthly/.*" \
/mnt/storage
```
锁定模式之外的文件遵循正常的更新规则。对于通过 `--lock-checksum` 锁定的条目,任何偏移都会立即可见,并且 `precizer` 会以非零状态退出,这可用于脚本中。
### 示例 10
使用 `--rehash-locked` 对锁定的数据进行深度验证
`--rehash-locked` 选项只能与 `--lock-checksum` 结合使用。启用后,匹配锁定模式并且已存在于数据库中的每个文件都会被重新读取,重新计算其 SHA512 校验和,并将结果与存储的校验和进行比较。这以额外的磁盘 I/O 为代价,为不可变归档提供了明确的完整性扫描。该选项会忽略是否启用了 `--watch-timestamps`。如果重新计算的校验和记录的大小相匹配,则认为文件是一致的;如果其在磁盘上的时间戳与数据库中的不同,则数据库中的 ctime/mtime 字段将使用新值进行更新。
```
precizer --update \
--lock-checksum="^archive/2024/.*" \
--rehash-locked \
/mnt/storage
```
以下案例说明了 `--lock-checksum`、`--watch-timestamps` 和 `--rehash-locked` 是如何交互的:
1. **文件大小不匹配。** 如果数据库中存储的大小与磁盘上的大小不同,无论是否使用 `--watch-timestamps` 和 `--rehash-locked`,该文件都会被标记为“锁定校验和违规”。对大小不同的文件重新计算哈希是毫无意义的,因为无论如何校验和都不可能匹配。
2. **文件大小匹配;既未使用 `--watch-timestamps` 也未使用 `--rehash-locked`。** 不考虑其他值,例如 SHA512 和时间戳;该文件被视为完全一致,并且 `precizer` 以 `SUCCESS` 状态完成。
3. **大小和时间戳均匹配;启用了 `--watch-timestamps` 但省略了 `--rehash-locked`。** 该文件被视为完全一致,不会出现在输出中,并且 `precizer` 以 `SUCCESS` 状态完成。
4. **大小匹配,时间戳不同;启用了 `--watch-timestamps` 但省略了 `--rehash-locked`。** 该文件仅因时间戳偏移而被标记为“锁定校验和违规”,并且 `precizer` 以 `WARNING` 状态完成。
5. **大小匹配;启用了 `--rehash-locked`。** 只有数据库中存储的校验和与大小起作用。如果两者都匹配,则认为文件是一致的。如果磁盘上的时间戳发生了变化,无论是否使用了 `--watch-timestamps`,新的 ctime/mtime 值都将被保存到数据库中。
一个实际的实用工作流是,在没有 `--rehash-locked` 的情况下(如果时间戳偏移是可以接受的,甚至可以不使用 `--watch-timestamps`)运行快速的每日扫描以保持数据库同步,然后计划进行频率较低的深度审计,使用 `--rehash-locked` 对冻结的数据集强制进行校验和级别的验证。
### 示例 11
使用 `--db-drop-inaccessible` 丢弃不可访问的记录
默认情况下,当文件由于权限错误而不可访问时,其数据库记录会在 `--update` 期间被保留,以防止意外的数据丢失。删除此类记录需要使用 `--db-drop-inaccessible`:
```
precizer --update --db-drop-inaccessible /mnt/storage
```
由于不可访问而删除 archive/secret.bin
注意:此示例仅适用于在数据库中有记录但由于某种原因在磁盘上确实不可访问的文件。这可能是由于不正确的 `chmod`/`chown` 权限或挂载不正确的卷造成的。警告:如果文件(甚至其路径)实际上已被删除,而不仅仅是暂时不可访问,那么使用 `--update` 更新数据库将无条件移除其记录——不需要额外的选项。
## 故障排除
### 文件遍历慢、校验和计算慢、数据库写入慢(“一切都很慢”)
要找出瓶颈,请尝试在 `--dry-run` 或 `--dry-run=with-checksums` 模式下运行 `precizer`。
`--dry-run` 会递归遍历**文件系统**。在此模式下,除了目录树遍历之外不会发生任何事情。您可以添加 `--progress` 来同时统计总字节数和文件数,但不会进行任何数据库写入。此模式有助于验证文件系统的可访问性,并在一定程度上验证底层硬件。如果即使使用 `--dry-run` 也很慢,根本原因不太可能是 `precizer` 本身。
`--dry-run=with-checksums` 与 `--dry-run` 的区别仅在于遇到的每个文件都会被完整读取(逐字节)并计算校验和。这会消耗明显更多的资源,并且接近程序的真实工作负载,但它仍然不会写入数据库。如果启用了 `--progress`,`precizer` 还会打印已计算哈希的字节数以及以 B/s 为单位的平均哈希吞吐量。可以将该数值与第三方基准测试进行比较,以帮助发现瓶颈。
也有可能 `--dry-run=with-checksums` 速度很快,但实际运行(非试运行)明显变慢,尤其是在添加或更改许多记录时。在这种情况下,请检查存储数据库文件的文件系统——问题可能出在那里。
`precizer` 使用有利于保护已写入数据并尽可能抵抗数据库损坏的设置来打开 SQLite 数据库。代价是对底层块设备进行更多的磁盘 I/O 和更多的文件系统同步。在实践中,SQLite 非常快,通常不是最薄弱的环节。然而,压缩的、网络化的或纯粹缓慢的文件系统会实质性地影响整体性能。
例如,在大量插入期间,`precizer` 会等待文件记录被写入并且事务被提交,然后再继续处理下一个文件。作为一个快速测试,尝试将 `.db` 文件临时放置在快速介质上(例如,`tmpfs`)——这既能提高整体性能,又能帮助确认瓶颈在哪里。
### 如果在所有模式下一切仍然很慢,请检查:
#### 文件系统完整性
对正在扫描的卷(捕获校验和的位置)以及存储和更新 `.db` 文件的卷运行文件系统检查。性能下降可能是由逻辑文件系统问题引起的。
#### 硬件
##### 磁盘吞吐量
文件被逐字节完整读取。磁盘子系统的吞吐量直接反映在 `precizer` 的速度上。该程序在文件系统级别(更高的抽象层)工作,而不是直接与原始块设备工作,因此文件系统的健康状况很重要。
性能可能因文件系统的不同而有很大差异。例如,从 NFS 挂载读取文件可能受限于网络吞吐量,其行为与禁用了 `atime`(`atime` 是访问时间戳)的本地 NVMe 文件系统读取非常不同。请使用第三方工具对存储子系统进行基准测试。
##### CPU 性能
校验和计算是纯粹的数学运算,可能会占用大量 CPU 资源。现代 CPU 通常可以轻松应对,但值得确保在容器化或虚拟化环境中性能不会因共享的 vCPU 资源而降低。请使用监控和基准测试来验证 CPU 性能。
### 瓶颈分诊矩阵
| 步骤 | 模式/命令 | 测量内容 | 如果在此处缓慢 | 后续步骤 |
| ---- | --------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| 1 | precizer --dry-run | 文件系统可访问性、目录遍历速度、基线 I/O | 最可能在 `precizer` 之外:文件系统/磁盘/网络/系统负载 | 检查存储子系统、挂载状态和整体系统负载 |
| 2 | precizer --dry-run=with-checksums | 真实读取 + 校验和计算速度(无数据库写入) | 瓶颈:数据读取(磁盘/网络/文件系统)或 CPU(较少见) | 检查磁盘/网络吞吐量、文件系统设置和 CPU 资源限制(虚拟机/容器) |
| 3 | 正常运行(非试运行) | SQLite 写入和事务的影响 | 通常是托管 `.db` 的文件系统存在问题(缓慢/网络/压缩) | 检查 `.db` 文件系统;尝试将 `.db` 临时移动到更快的介质或 `tmpfs` |
## 替代方案
具有开放架构和源代码的 **precizer** 替代方案。更多替代方案(保持最新):[www.alternativeto.net](https://alternativeto.net/software/precizer-verify-file-checksums-at-scale/)
* **AIDE** — [aide.github.io](https://aide.github.io/)
* 平台/架构:Linux/*BSD/macOS(x86_64、arm64 等)
* 编写语言:C,积极维护中
* 支持在规则级别选择/组合不同的哈希算法并控制更多非哈希属性(ACL/xattr/SELinux 等)。
* 快照/数据库使用文本格式(可选 gzip),而非 SQLite:不太适合对数据库进行复杂的查询/分析。
* 不支持在大型文件内恢复中断的哈希计算:重新扫描通常从头开始。
* **Samhain** — [www.la-samhna.de/samhain](https://www.la-samhna.de/samhain/)
* 平台/架构:Linux/Unix/POSIX、Windows(x86_64、arm64 等)
* 编写语言:C,积极维护中
* 内置代理 + 中央服务器模型(集中收集/控制),超出了“一个本地数据库”的范畴。
* 注重防篡改(对某些制品的签名/加密保护)。
* 如果您只需要通过 SQLite 快速比较两棵目录树,其部署/维护(代理/服务器/密钥)要复杂得多。
* **OSSEC** — [www.ossec.net](https://www.ossec.net/)
* 平台/架构:Linux、Windows、macOS、*BSD(x86_64、arm64 等)
* 编写语言:C,积极维护中
* 事件驱动/基于代理的 HIDS 平台:实时警报、规则、关联、响应——与“快照 + 差异比较”属于不同类别的任务。
* 开箱即用的集中式架构。
* 不是专为在 SQLite 中专门存储文件快照或在大型文件内恢复中断的校验和计算而设计的。
* **Open Source Tripwire** — [github.com/Tripwire/tripwire-open-source](https://github.com/Tripwire/tripwire-open-source)
* 平台/架构:类 POSIX 操作系统(Linux/macOS/*BSD/Solaris 等),通过 Cygwin 支持 Windows(x86_64、arm64 等)
* 编写语言:C++,最近一次更改在 8 年前
* 高级策略语言以及签署策略/配置/数据库的做法(围绕基线的信任链)。
* 没有以 SQLite 数据库作为主要数据格式,不支持在大型文件内恢复中断的哈希计算。
* **integrit** — [github.com/integrit/integrit](https://github.com/integrit/integrit)
* 平台/架构:Linux/*BSD(x86_64、arm64 等)
* 编写语言:C,最近一次更改在 2 年前
* 极简主义和低外部库依赖(如果“小型实用程序”比 SQL 数据模型更重要)。
* 不使用 SQLite,因此不太适合对快照进行繁重的查询/比较/分析。
* **mtree (NetBSD mtree)** — [man.netbsd.org/mtree.8](https://man.netbsd.org/mtree.8)
* 平台/架构:*BSD、Linux(x86_64、arm64 等)
* 编写语言:C,最近一次更改在 18 年前
* 树规范(spec)在需要声明式格式的“规范布局/权限/所有权”场景中非常方便。
* 非常陈旧的代码库(实际上已冻结)。
* 基于规范文件格式/工作流,而非 SQLite 快照 + 快速 SQL 差异比较;在大型文件内恢复中断的哈希计算。
* **hashdeep (md5deep/sha*deep)** — [github.com/jessek/hashdeep](https://github.com/jessek/hashdeep)
* 平台/架构:Linux、Windows、macOS(x86_64、arm64 等)
* 编写语言:C++/C,最近一次更改在 9 年前
* 哈希族/输出格式的选择。当输出必须符合外部要求/标准时非常方便。
* 没有 SQLite 快照作为产物:结果主要是报告文件/列表,而不是用于快速比较的数据库。
* **hashit** — [github.com/boyter/hashit](https://github.com/boyter/hashit)
* 平台/架构:Linux、Windows、macOS(x86_64、arm64 等)
* 编写语言:Go,积极维护中
* 可以在单次通过中计算一个文件的多个不同哈希值。
* 没有 SQLite 快照和数据库更新机制。
* **RHash** — [github.com/rhash/RHash](https://github.com/rhash/RHash)
* 平台/架构:Linux、Windows、macOS、*BSD(x86_64、arm64 等)
* 编写语言:C,积极维护中
* 当需要与外部生态系统兼容时,提供非常广泛的算法/输出格式(包括磁力链接等)。
* 不维护目录树的 SQLite 快照作为主要实体(更多是“计算/验证哈希”而不是“维护快照数据库”)。
* **rsync** — [rsync.samba.org](https://rsync.samba.org/)
* 平台/架构:Linux、*BSD、macOS(x86_64、arm64 等),通过 Cygwin/MSYS2 支持 Windows
* 编写语言:C,积极维护中
* 将传输/同步与验证结合:可以立即纠正差异,而不仅仅是检测。
* 校验和不会在运行之间持久保存:在中断或重新检查之后,计算会从头开始。
* **rclone** — [rclone.org](https://rclone.org/)
* 平台/架构:Linux、Windows、macOS、*BSD(amd64/arm/arm64 等)
* 编写语言:Go,积极维护中
* 面向远程/云存储:S3/Drive 等。
* 不维护用于快速离线 A↔B 比较的本地目录树 SQLite 快照。
* 完整性检查通常取决于特定后端的功能(它暴露哪些哈希/元数据)。
* **QuickHash GUI** — [www.quickhash-gui.org](https://www.quickhash-gui.org/)
* 平台/架构:Linux、Windows、macOS(x86_64;macOS arm64)
* 编写语言:FreePascal (Lazarus),最近一次更改在 2 年前
* 仅限 GUI 的工具,面向各种媒体/制品(例如取证场景),在这些场景中 CLI 快照并不总是方便的。
* 不使用 SQLite 目录树快照作为其工作流的核心。
* **restic** — [restic.net](https://restic.net/)
* 平台/架构:Linux、Windows、macOS、*BSD(x86_64、arm64 等)
* 编写语言:Go,积极维护中
* 带有快照、去重和加密的备份存储库——如果目标是“存储历史记录并传输它”,这比简单地比较两棵树更强大。
* 不同的方法:分块/去重模型而不是“SQLite 快照 + 差异比较”;对于纯粹的 A↔B 比较任务来说可能大材小用。
* 不提供通过单个 SQL 数据库比较两个本地目录树的直接模型。
## 作者
软件开发者:[Dennis V. Razumovsky](https://github.com/dennisrazumovsky)
## 版权声明
本程序基于 GNU 通用公共许可证 v3.0 (GPLv3) 分发,如顶级 `COPYING` 文件中所提供。作者不对源代码或整个程序的任何使用负责。任何人使用代码或程序需自担风险和责任
### 在被俄西斯恐怖主义政权占领、权力已被独裁专制窃取的领土内的使用限制
- 允许:私人 strictly 个人、非商业用途。
- 禁止:任何直接或间接导致向该司法管辖区的公共预算缴纳税款、费用、捐款或其他强制性付款(包括增值税、企业所得税、个人所得税代扣、社会保险费、关税等)的用途。
- 同样禁止:被误称为政府机构、国有企业、预算资助机构及其附属组织的实体的使用。
- 如果在该领土内或为其居民进行商业开发、付费分发、付费支持和集成,并导致需要支付强制性费用,则予以禁止。
- 该限制适用于程序本身及其源代码(全部或部分)。
- 目的:防止直接和间接地为乌克兰战争提供资金。
标签:DevOps工具, 哈希校验, 字节级比对, 存储一致性检查, 客户端加密, 嵌入式系统, 数据一致性, 数据完整性校验, 数据库状态持久化, 文件同步, 文件树验证, 文件漂移分析, 文件系统, 断点续传, 校验和比对, 目录树比对, 集群文件检测, 高并发性能