校验和 VFS 垫片

、概述

校验和 VFS 扩展是一个VFS 垫片,它向 SQLite 数据库中每个页面的末尾添加一个 8 字节的校验和。校验和在写入每一页时添加,并在读取每一页时进行验证。校验和旨在帮助检测由大容量存储设备中的随机位翻转引起的数据库损坏。

校验和 VFS 扩展需要 SQLite 版本 3.32.0 (2020-05-22) 或更高版本。它不适用于早期版本的 SQLite。

2.编译

校验和 VFS 模块是一个可加载的扩展它不包括在合并中。它必须在编译时或运行时添加到 SQLite。校验和 VFS 模块的源代码 位于SQLite 源代码树中的ext/misc/cksumvfs.c 源文件中

要将校验和 VFS 模块构建到运行时可加载扩展中,请使用类似于以下的命令:

  • (linux) → gcc -fPIC -shared cksumvfs.c -o cksumvfs.so
  • (mac) → clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib
  • (windows) → cl cksumvfs.c -link -dll -out:cksumvfs.dll

当然,根据项目的需要,您可能希望添加额外的编译器选项。

要将此扩展与您的产品静态链接,像任何其他 C 语言模块一样编译它,但添加“-DSQLITE_CKSUMVFS_STATIC”选项,以便此模块知道它是静态链接而不是动态链接。

3.加载

要将此扩展加载为共享库,您首先必须启动一个虚拟 SQLite 数据库连接以用作sqlite3_load_extension() API 调用的参数。然后调用 sqlite3_load_extension() API 并关闭虚拟数据库连接。所有后续打开的数据库连接都将包含此扩展名。例如:

sqlite3 *db;
sqlite3_open(":memory:", &db);
sqlite3_load_extension(db, "./cksumvfs");
sqlite3_close(db);

如果此扩展使用 -DSQLITE_CKSUMVFS_STATIC 编译并静态链接到应用程序,则使用单个 API 调用对其进行初始化,如下所示:

sqlite3_cksumvfs_init();

Cksumvfs 是一个VFS 垫片加载时,“cksmvfs”成为新的默认 VFS,它使用先前的默认 VFS 作为堆栈中的下一个 VFS。这通常是您想要的。但是,在加载多个 VFS shim 的复杂情况下,确保以正确的顺序加载 cksumvfs 可能很重要,这样它才能以正确的顺序将自身排序到默认的 VFS Shim 堆栈中。

4.用法

照常使用sqlite3_open()sqlite3_open_v2()接口打开数据库连接。普通数据库文件(没有校验和)将正常运行。如果遇到包含无效校验和的页面,具有校验和的数据库将返回 SQLITE_IOERR_DATA 错误。

校验和仅适用于保留字节 值恰好为 8 的数据库。保留字节的默认值为 0。因此,默认情况下,新创建的数据库文件将忽略校验和。要创建包含校验和的数据库,请通过运行类似于以下的代码将保留字节值更改为 8:

int n = 8;
sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);

如果您在创建新的数据库文件后立即执行此操作,那么在将任何其他内容写入文件之前,那么这可能就是您需要做的全部。否则,上面的 API 调用后面应该跟:

sqlite3_exec(db, "VACUUM", 0, 0, 0);

运行 VACUUM 永远不会有坏处,即使您不需要它。如果数据库处于 WAL 模式,您应该在继续之前关闭并重新打开所有数据库连接。

在 CLI 中,使用“.filectrl reserve_bytes 8”命令,然后使用“VACUUM;”。

请注意,SQLite 允许增加但不能减少保留字节数。因此,如果数据库文件的 reserve-bytes 值已经大于 8,则除了转储和恢复数据库文件外,没有办法在该数据库上激活校验和。另请注意,其他扩展也可能使用保留字节。校验和将与那些其他扩展不兼容。

5.校验和的验证

如果任何校验和不正确,“PRAGMA quick_check”命令将找到它。要验证校验和实际上已启用并正在运行,请使用如下 SQL:

SELECT count(*), verify_checksum(data)
  FROM sqlite_dbpage
 GROUP BY 2;

verify_checksum() 函数有三种可能的输出:1、0 和 NULL。如果校验和正确则返回 1。如果校验和不正确,则返回 0。如果页面不可读,则返回 NULL。如果启用校验和,如果校验和错误,读取将失败,因此 verify_checksum() 在错误校验和上的通常结果为 NULL。

如果一切正常,上面的查询应该返回第二列为 1 的单行。任何其他结果表明存在校验和错误,或者校验和验证被禁用。

6.控制校验和验证

cksumvfs 扩展实现了一个新的 PRAGMA 语句,可用于禁用、重新启用或查询校验和验证的状态:

PRAGMA checksum_verification;          -- query status
PRAGMA checksum_verification=OFF;      -- disable verification
PRAGMA checksum_verification=ON;       -- re-enable verification

如果启用或禁用校验和验证,“checksum_verification”pragma 将分别返回“1”(真)或“0”(假)。此上下文中的“验证”是指如果在读取时检测到校验和不匹配会导致 SQLITE_IOERR_DATA 错误的功能。只要数据库的 保留字节值为 8,无论此 pragma 的设置如何,校验和始终保持最新。可以禁用校验和验证(例如)以对先前报告校验和错误的数据库进行取证分析。

如果数据库文件的保留字节值不为 8,“checksum_verification”pragma 将始终以“0”响应。如果未加载 cksumvfs 扩展,pragma 将完全不返回任何行。