首页
/ rclone cryptcheck 命令详解:验证加密远端数据完整性的原理、参数与实战

rclone cryptcheck 命令详解:验证加密远端数据完整性的原理、参数与实战

2026-09-07 20:34:50作者:翟萌耘Ralph

rclone cryptcheck 是 rclone 为 crypt 加密远端(encrypted remote)专门提供的完整性校验命令,相当于能够校验密文校验和的 rclone check。本文以 cryptcheck 命令参考文档 为主体,结合 cmd/cryptcheck/cryptcheck.gobackend/crypt/crypt.gofs/operations/check.go 的源码实现,讲解它的核心原理、两种调用形态、全部校验选项与结果解读,帮助你在“明文数据 ↔ 加密远端”之间做出不下载全量数据的高效完整性校验。

为什么普通 rclone check 校验不了加密远端

rclone crypt 是一种“覆盖型后端”(overlay backend),它本身不直接访问存储,而是把加解密逻辑叠加在任意其他后端之上。文件在存储端落盘的是密文,文件名和文件内容都已发生变换。

普通 rclone check 的比较逻辑是:在源与目标之间取公共支持的哈希做比对。而 crypt 后端对外暴露的 Hashes() 返回的是空集合(见 backend/crypt/crypt.go#L579-L582),因为加密后的密文哈希与用户掌握的明文哈希根本对不上——云端存储的是密文,它的校验和自然也是针对密文计算的。

因此,rclone 在 crypt 远端之外专门实现了 cryptcheck 命令,其定位就是“等价于 rclone check,但能够校验加密远端的校验和”。该命令自 v1.36 引入,命令源文件为 cmd/cryptcheck/cryptcheck.go

工作原理:读取 nonce → 原地重加密 → 比对密文哈希

cryptcheck 之所以能工作,依赖于一个巧妙的设计:加密文件的头部保存了本次加密所使用的 nonce(随机数)。只要拿到这个 nonce,就能用同一把密钥、同一个 nonce,把本地明文重新加密一遍,从而在本地“复现”云端密文的字节流,再计算它的哈希与云端存储的哈希比对。

完整校验逻辑位于 cmd/cryptcheck/cryptcheck.go#L66-L116cryptCheck 函数,步骤可以拆解如下:

1. 校验目标确实是 crypt 远端,并选定哈希算法

fcrypt, ok := fdst.(*crypt.Fs)
if !ok {
    return fmt.Errorf("%s:%s is not a crypt remote", fdst.Name(), fdst.Root())
}
funderlying := fcrypt.UnWrap()
hashType := funderlying.Hashes().GetOne()
if hashType == hash.None {
    return fmt.Errorf("%s:%s does not support any hashes", ...)
}
fs.Infof(nil, "Using %v for hash comparisons", hashType)
  • 第二个参数 encryptedremote:path 必须是 crypt 类型远端,否则直接报错 is not a crypt remote
  • 通过 UnWrap() 取得被加密包装的下层后端,并要求下层后端至少支持一种校验和(GetOne() 会从支持列表中选一种),否则报 does not support any hashes
  • 命令启动时会打印 Using MD5 for hash comparisons 之类的日志,告知本次采用哪种哈希。

2. 从加密文件头部读出 nonce

在校验函数中,目标对象被断言为 *crypt.Object 并解包成底层密文对象,取其存储的哈希作为“云端真实值”;同时调用 fcrypt.ComputeHash(...) 计算“本地重加密值”。关键实现在 backend/crypt/crypt.go#L811-L852

  • 使用 RangeOption{Start: 0, End: int64(fileHeaderSize) - 1} 对密文对象做有限范围的读取,只下载文件头部区域;
  • 通过解密器读取该头部中的 nonce(注释中明确写道:“opening the file is sufficient to read the nonce in / use a limited read so we only read the header”);
  • 若配置了 NoDataEncryption(仅加密文件名、不加密内容),则直接返回源文件的哈希,无需重加密。

这意味着在绝大多数文件上,cryptcheck 并不需要把加密远端上的文件整体下载下来——只读头部那几十字节即可取得 nonce。

3. 用该 nonce 重加密本地明文并即时算哈希

得到 nonce 后,computeHashWithNoncebackend/crypt/crypt.go#L780-L809)打开明文源文件,用 cipher.newEncrypter(in, &nonce) 把数据边加密边灌入多哈希器,最终得到本地“复现密文”的哈希值。

源码注释坦诚地写道:“Note that we break lots of encapsulation in this function.”——为了在外部对象上重放加密流程,这个函数特意打破了大量封装,是理解 cryptcheck 底层原理时最值得研读的一段代码。

4. 两值比对并汇入统一报表框架

本地重加密哈希与云端密文哈希不一致时,返回差异;一致则计为匹配。整个流程交由通用框架 operations.CheckFnfs/operations/check.go#L212-L238)驱动:它对源与目标做“march”式目录对拍,逐对对象先比较大小(比较的是逻辑明文层的尺寸),再调用 cryptcheck 注入的自定义哈希比对闭包,最后统一汇总统计。

一句话概括整个调用链:

rclone cryptcheckcryptCheck(类型断言 + 选哈希)→ operations.CheckFn(march 对拍调度)→ 注入式 Check 闭包(UnWrap + 读头部 nonce + ComputeHash 重加密比对)。

基本用法:两种调用形态与适用场景

形态一:本地目录 vs 加密远端(推荐)

rclone cryptcheck /path/to/files encryptedremote:path

源侧是本地明文目录,目标侧是加密远端。此时校验只读取加密远端每个文件的头部 nonce,再对本地文件做一次流式重加密,源文件由本地磁盘读取,因此传输量最小、性能最好。

形态二:远端明文 vs 加密远端

rclone cryptcheck remote:path encryptedremote:path

源侧也可以是另一个远端上的明文数据。正如官方文档所强调,这种用法需要把 remote:path 下的所有文件先下载到本地才能完成重加密,因此会产生全量下载流量。只有当明文侧确实存放在远端时才应选用。

参数位置约定

命令固定接收两个参数:rclone cryptcheck source:path cryptedremote:path。从 cmd/cryptcheck/cryptcheck.go#L57-L63Run 实现可看到,cmd.CheckArgs(2, 2, ...) 强制要求恰好两个参数,并且第二个参数必须是 crypt 远端(见上文第 1 步的类型断言)。

运行结束后,cryptcheck 会汇总并记录 encryptedremote: 的校验状态,例如匹配文件数、发现的差异数、缺失文件数等。

差异报告输出选项:把结果写到文件或 stdout

cryptcheck 继承自通用 check 命令的一批报表选项由 cmd/check/check.go#L42-L50AddFlags 注册。这些选项接收一个文件名作为值;若值为 -,则输出到 stdout。对应实现见 cmd/check/check.go#L79-L135GetCheckOpt:文件名为空时跳过,为 - 时绑定 os.Stdout,否则 os.Create 创建报告文件并在结束时统一关闭。

选项 作用
--differ string 把所有“源与目标都存在但内容不同”的文件路径写入该文件
--missing-on-dst string 把“目标中缺失(只在源中)”的文件路径写入该文件
--missing-on-src string 把“源中缺失(只在目标中)”的文件路径写入该文件
--match string 把所有匹配一致的文件路径写入该文件
--error string 把所有“读取或哈希出错”的文件路径写入该文件
--combined string 生成一个包含全部文件路径及状态符号的组合报告
--one-way 仅单向校验:只检查源文件在目标中是否存在且一致

--combined:diff 风格的符号化报告

--combined 生成的文件格式类似 diff 输出,每行由一个符号、一个空格和文件路径组成,符号含义如下:

符号 含义
= path 该路径在源和目标中都存在,且完全一致
- path 该路径在源中缺失(只存在于目标中)
+ path 该路径在目标中缺失(只存在于源中)
* path 该路径在源和目标中都存在,但内容不同
! path 读取或哈希该路径的源/目标时发生错误

生成逻辑集中在 fs/operations/check.go#L64-L76report 方法:每个状态既写入对应的单项报告文件(如 --differ--missing-on-src),也会同步写入 --combined 指定的输出。因此这些选项可以任意组合使用,一份符号报告 + 若干份明细清单即可完整呈现校验结果。

--one-way:单向校验

默认情况下 check 系命令做的是“双向”校验:源中多出的文件(missing-on-dst)与目标中多出的文件(missing-on-src)都会被视为差异。这对“两边本应完全镜像”的场景是合适的。

但如果你只是想确认“源里每个文件在目标里都完好”,可以加 --one-way 只做单向检查:只检查源文件在目标中是否匹配,目标中额外多出的文件不会被报告。在 fs/operations/check.go#L78-L101 的实现中,DstOnly 分支遇到 OneWay 为真时直接返回不再递归,这正是“忽略目标多余文件”行为的来源。

全部选项参考

cryptcheck 的帮助信息由三组选项组成。cryptcheck 专有选项之外,其余为与其它命令共享的选项,全局选项见 flags 全局帮助

cryptcheck 专有选项

      --combined string         Make a combined report of changes to this file
      --differ string           Report all non-matching files to this file
      --error string            Report all files with errors (hashing or reading) to this file
  -h, --help                    help for cryptcheck
      --match string            Report all matching files to this file
      --missing-on-dst string   Report all files missing from the destination to this file
      --missing-on-src string   Report all files missing from the source to this file
      --one-way                 Check one way only, source files must exist on remote

注意:与 rclone check 不同,cryptcheck 没有 --download(下载全量内容比对)和 --checkfile 等选项,因为它的校验方式本身就是“重加密哈希”,不需要也不应该走全量下载比对。

Check Options

      --max-backlog int   Maximum number of objects in sync or check backlog (default 10000)

该选项控制同步或校验过程中待处理对象队列的最大长度(默认 10000),当源目录中对象数量巨大、而 --checkers 并行处理跟不上时,用于防止内存中的待办对象无限增长。

Filter Options

这些过滤选项用于裁剪参与校验的文件集,例如只校验特定子集、排除某种模式或按文件年龄筛选:

      --delete-excluded                     Delete files on dest excluded from sync
      --exclude stringArray                 Exclude files matching pattern
      --exclude-from stringArray            Read file exclude patterns from file (use - to read from stdin)
      --exclude-if-present stringArray      Exclude directories if filename is present
      --files-from stringArray              Read list of source-file names from file (use - to read from stdin)
      --files-from-raw stringArray          Read list of source-file names from file without any processing of lines (use - to read from stdin)
      --files-from0 stringArray             Read list of source-file names from file using NUL as separator (use - to read from stdin)
  -f, --filter stringArray                  Add a file filtering rule
      --filter-from stringArray             Read file filtering patterns from a file (use - to read from stdin)
      --hash-filter string                  Partition filenames by hash k/n or randomly @/n
      --ignore-case                         Ignore case in filters (case insensitive)
      --include stringArray                 Include files matching pattern
      --include-from stringArray            Read file include patterns from file (use - to read from stdin)
      --max-age Duration                    Only transfer files younger than this in s or suffix ms|s|m|h|d|w|M|y (default off)
      --max-depth int                       If set limits the recursion depth to this (default -1)
      --max-size SizeSuffix                 Only transfer files smaller than this in KiB or suffix B|K|M|G|T|P (default off)
      --metadata-exclude stringArray        Exclude metadatas matching pattern
      --metadata-exclude-from stringArray   Read metadata exclude patterns from file (use - to read from stdin)
      --metadata-filter stringArray         Add a metadata filtering rule
      --metadata-filter-from stringArray    Read metadata filtering patterns from file (use - to read from stdin)
      --metadata-include stringArray        Include metadatas matching pattern
      --metadata-include-from stringArray   Read metadata include patterns from file (use - to read from stdin)
      --min-age Duration                    Only transfer files older than this in s or suffix ms|s|m|h|d|w|M|y (default off)
      --min-size SizeSuffix                 Only transfer files bigger than this in KiB or suffix B|K|M|G|T|P (default off)

Listing Options

      --default-time Time   Time to show if modtime is unknown for files and directories (default 2000-01-01T00:00:00Z)
      --fast-list           Use recursive list if available; uses more memory but fewer transactions

其中 --fast-list 适合对象存储后端,通过一次性递归列举来换取更少的 API 事务数;--default-time 则用于当目标系统无法提供修改时间时展示的兜底时间值。

并行模型与性能特征(--checkers)

校验是按文件并行的,默认并行度是 8,可通过全局 --checkers 选项调整(对应参数说明见 --checkers 帮助)。从源码看,这个默认并行度通过 ci.Checkers 决定并发令牌桶大小(fs/operations/check.go#L217-L221),每个配对对象的哈希比对以 goroutine 形式执行,令牌桶限制同时进行的比对数量。

性能上值得注意的三点:

  1. 加密远端侧开销极小:对绝大多数文件只读取头部 fileHeaderSize 字节以取得 nonce,随后 Hash() 读取的是云端已存储的校验和,不下载文件本体;
  2. 明文源侧是主要开销:本地源文件需要被完整读取一遍并流式重加密;若源也在远端,则会先发生全量下载(形态二);
  3. 校验粒度:同一对文件仅比对哈希,不做逐字节内容下载比对,因此整体比 --download 式全量校验轻量得多。

从代码可以推断:目标后端原生支持校验和(如 MD5、SHA1、XXHASH 等其一)是 cryptcheck 生效的硬前提,云端校验和一般由服务端直接计算,无需把密文拉回本地。

结果状态日志与退出码

运行结束时,fs/operations/check.go#L240-L271reportResults 会以 Logf 汇总如下信息,直接说明校验结果:

  • N files missing(源或目标缺失的文件数);
  • N differences found(发现差异的文件数);
  • N errors while checking(校验中出错数);
  • N hashes could not be checked(因底层不支持等原因无法比对哈希的文件数);
  • N matching files(匹配一致的文件数)。

更值得关注的是退出码语义:当差异数大于 0 时,reportResults 会返回一个已计数的 FsError(内容形如 N differences found),命令以非零状态退出。这一点对脚本化非常友好——你可以直接根据退出码判断加密备份是否完好,例如在 CI 或定时任务里以非零退出触发告警。

在 bisync 中的实际复用

cryptcheck 的价值不只体现在命令行直接使用上,rclone 的 bisync 命令在目标侧检测到 crypt 远端时,会自动选用与 cryptcheck 等价的校验逻辑,而不是退化为仅比对大小。相关实现见 cmd/bisync/checkfn.go#L25-L53

Crypt detected! Using cryptcheck instead of check. (Use --size-only or --ignore-checksum to disable)

其选型策略是:先看源与目标是否有公共哈希;没有公共哈希且目标为 crypt 时,解包取底层后端支持的哈希并调用基于 nonce 重加密的 CryptCheckFn;该函数注释明确指出它“比普通 Check 更健壮、更准确”,因为它会回退到 CryptCheck 或 DownloadCheck,而不是默默降级成 --size-only。这从另一个侧面印证了:凡是涉及 crypt 远端的完整性校验,重加密哈希比对都是 rclone 生态中的首选方案。

相关命令与延伸阅读

一句话实践建议:定期用 rclone cryptcheck /本地/明文目录 encryptedremote:备份目录 校验加密备份,配好 --differ--combined 报告路径,并把它接入退出码判断——这是在不信任的云端存储上维护“密文完好、可解密还原”低成本高可信度的校验方案。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.79 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
390