rclone 已知 Bug 与限制全解析:目录时间戳、海量文件与 Bucket 远端空目录问题
rclone 自诩 "rsync for cloud storage",试图为几十种云端与本地存储提供统一接口,但各存储系统底层能力差异决定了它存在若干官方记载的已知限制与 Bug。本文以仓库文档 docs/content/bugs.md 为核心骨架,结合 docs/content/overview.md 的特性说明与 fs/sync/sync.go、fs/features.go 等源码实现,逐一剖析"目录时间戳丢失""单目录海量文件导致内存暴涨""Bucket 类远端空目录消失"三大限制的成因、现状与应对思路,帮助你在真实同步场景中提前规避这些坑。
目录时间戳无法在所有后端保留
现状:v1.66 起支持目录级 modtime 同步
在较早版本中,rclone 主要同步文件及其内容级元数据,目录自身的修改时间(modtime)在很多后端上无法被保留。自 v1.66 开始,只要后端能力允许,rclone 就支持同步目录的修改时间(当前仓库源码版本为 v1.76.0,见 VERSION)。
但并非所有后端都支持这一能力——哪些支持、支持到什么程度,以 docs/content/overview.md 提供的完整清单为准。该文档定义了 ModTime 能力的分级标记:
| 标记 | 含义 |
|---|---|
- |
不支持 ModTime,对象上的时间多为上传时间或其它时间 |
R |
文件级 ModTime 只读:保留上传时的修改时间,但不能只改时间而不重传 |
R/W |
文件级 ModTime 可完整读写 |
DR |
修饰符 D 表示上述规则同样适用于目录(目录级时间戳只读) |
DR/W |
文件和目录级 ModTime 均可完整读写 |
也就是说,只有当目标后端在总表中标为带 D 的级别(DR / DR/W)时,目录时间戳才能真正被同步。
对实际操作的影响
- 对于只读级
R的存储:copy、sync等命令会自动探测SetModTime支持情况,必要时选择重传以保持修改时间一致;而touch命令在已有文件上会直接失败;mount场景下仅修改时间戳的操作会被静默忽略。 - 空目录默认不会被同步(详见下文说明),即使开启目录时间戳同步也只在目录存在的前提下生效。
源码侧如何实现目录 modtime 同步
目录时间戳的同步能力并非对所有远端一律开启,而是由目标后端的特性位(Features)动态决定。在 fs/sync/sync.go 中可以找到关键的门控逻辑:
setDirModTime: (!ci.NoUpdateDirModTime && fsrc.Features().CanHaveEmptyDirectories) &&
(fdst.Features().WriteDirSetModTime || fdst.Features().MkdirMetadata != nil || fdst.Features().DirSetModTime != nil),
setDirModTimeAfter: !ci.NoUpdateDirModTime &&
(!copyEmptySrcDirs || fsrc.Features().CanHaveEmptyDirectories && fdst.Features().DirModTimeUpdatesOnWrite),
也就是说,只有当源端支持空目录(CanHaveEmptyDirectories),并且目标端具备 WriteDirSetModTime、MkdirMetadata、DirSetModTime 三者之一时,目录时间戳同步才会启用。这些特性位在 fs/features.go 中统一定义,并由各后端实现时自行填充。
实际写入路径位于 fs/operations/operations.go(MkdirModTime)与 fs/sync/sync.go(copyDirMetadata)。同步引擎在比对源/目标目录时间戳不相等后,会调用 operations.SetDirModTime(fs/operations/operations.go)或 operations.MkdirModTime 完成写入。由于创建目录后再写入其内部文件会改变目录时间戳,对于 DirModTimeUpdatesOnWrite=false 的后端,rclone 还实现了"延迟设置目录时间戳"(setDelayedDirModTimes,见 fs/sync/sync.go)机制:先创建目录与文件,最后再按层级从深到浅并行回填目录时间戳。
空目录默认不参与同步,可用标志显式开启
文档特别强调:空目录默认不会被同步。若需要保留源端的空目录结构,必须显式加上 --create-empty-src-dirs 标志。该标志适用于多个命令,例如:
rclone copy source:path dest:path --create-empty-src-dirs
rclone sync source:path dest:path --create-empty-src-dirs
从源码看,sync(cmd/sync/sync.go)、copy(cmd/copy/copy.go)、move(cmd/move/move.go)、convmv(cmd/convmv/convmv.go)都注册了该布尔标志,最终传入同步引擎的 copyEmptySrcDirs 字段,驱动空目录的创建(fs/sync/sync.go)。bisync 同样支持该选项(见 cmd/bisync/cmd.go),并提供了相应的集成测试场景(见 cmd/bisync/testdata/test_createemptysrcdirs/scenario.txt)。
注意:即便开启了 --create-empty-src-dirs,如果目标后端本身不支持空目录(见下文"Bucket-based remotes"一节),空目录依然无法保留——因为空目录的创建依赖 CanHaveEmptyDirectories 特性。相关测试也印证了这一点:在 fs/sync/sync_test.go 中,若远端不支持 CanHaveEmptyDirectories,测试会直接跳过空目录相关断言。
单个目录/桶中存放数百万文件时 rclone 会吃力
成因:目录/桶被整体载入内存
这是 rclone 一个结构性的限制:rclone 会在使用一个目录或桶之前,把它整体读入内存。由于每个 rclone 内部对象大约占用 0.5k–1k 的内存,当某个目录/桶中包含数百万个文件时,列举过程会花费非常长的时间,并消耗大量内存。
从源码结构看,这一设计贯穿同步与列举流程:同步引擎通过目录遍历器逐目录收集条目(如 fs/sync/sync.go 中的 srcFilesChan 等管道与 dstFiles 去重映射),桶级远端在递归列举(ListR)时会一次性吐出海量对象供上层过滤与比对。
高发场景:Bucket 类远端
文档明确指出,百万级文件的目录往往出现在 Bucket 类远端(例如 S3 桶)。原因在于:
这类远端没有把桶内的"子目录"作为独立实体隔离存放——桶内所有对象都处于同一个扁平命名空间中,即便带了
dir/前缀,它们仍同属"一个目录/桶",需要整体列举。
因此在 S3 / GCS 这类存储上,如果你的"逻辑目录"前缀下堆积了海量对象,rclone 就不得不一次处理全部条目。
应对思路
- 规划阶段尽量避免在单个桶或单个顶层"目录"下无限堆叠文件;对可分区的前缀(prefix)或桶做合理拆分,让单次列举的规模可控。
- 关注后端是否支持递归快速列举
ListR(对应--fast-list),可部分缓解多次往返带来的延迟,但无法解决单目录对象总量巨大本身的内存占用问题——因为内存压力来自对象条目的绝对数量,而非往返次数。后端是否支持ListR可参见 docs/content/overview.md 的 Optional Features 清单。
Bucket 类远端没有"目录"概念,空目录会消失
成因
S3、GCS、Swift、B2 这类 Bucket 类远端本质上没有目录,只有扁平的 key(对象名)。rclone 因而无法真正在这些远端上"创建目录"——目录只是对象 key 中 / 前缀的视觉呈现。其直接后果是:
在 Bucket 类远端上,空目录往往会消失:因为没有任何对象承载"该目录存在"这一信息。
这正是 docs/content/overview.md 中 EmptyDir 可选特性标注"大多数对象/桶类远端不支持空目录"的原因,也是上文 --create-empty-src-dirs 无法在这些后端完整生效的根源。
rclone 不创建目录标记对象的原因
部分软件会通过创建以 / 结尾的空 key(例如 dir/)作为"目录标记对象"(directory marker),从而让空目录在桶中可被枚举。文档明确说明 rclone 目前刻意不做这件事,理由是:
- 会额外产生更多对象,从而增加存储与 API 计费成本;
- 该能力未来可能以 flag/option 的形式加入(目前尚不可用)。
也就是说,从当前仓库的实现看,rclone 不通过空 key 标记目录——这与部分以对象存储为底层、又需要保目录结构的同步工具存在行为差异。
实用的规避思路
在官方目录标记能力落地之前,若你的工作流确实依赖"目录存在"(例如下游扫描器、WebDAV 客户端或人类浏览习惯),常见的工程化做法包括:
- 放入占位文件:在每个需要保留的目录内放一个
.keep之类的占位对象,使目录"非空",从而能在桶中以 key 前缀形式保留下来; - 自行维护清单:把目录结构清单存为元数据文件,由消费端按清单重建空目录;
- 若只是空目录在源与目标之间往返消失,需在迁移方案中显式设计"重建空目录"的补偿步骤。
这些均为基于上述限制推导出的工程实践,并非 rclone 内置能力。
Bug 的登记与追踪
与很多开源项目一样,rclone 的 Bug 统一登记在其 GitHub 项目的 issue 中,主要分为两类入口:
- 已上报 Bug(Reported bugs):状态为 open 且带有
bug标签的 issue; - 已知问题(Known issues):归属
Known Problem里程碑的 issue,多为长期存在、暂难根治的设计性限制。
本文所述的三条限制(目录时间戳、海量文件内存占用、Bucket 空目录)即属于文档明确列出的 Limitations 范畴,与"已知问题"有部分重叠——它们不是突发性缺陷,而是后端能力边界与 rclone 架构取舍的结果。用户侧遇到问题时,建议先对照上述清单确认是否为已知问题,避免重复上报。
总结
rclone 通过统一的抽象层服务几十种异构存储,docs/content/bugs.md 记录的这些限制本质上都源于"后端能力差异"与"架构取舍":
| 限制 | 本质原因 | 关键应对 |
|---|---|---|
| 部分后端目录时间戳无法保留 | 后端不支持目录级 ModTime(能力分级见 docs/content/overview.md) | 确认远端能力分级;空目录需加 --create-empty-src-dirs |
| 数百万文件导致内存/时间开销大 | 目录或桶被整体载入内存,单对象约占用 0.5k–1k | 拆分桶与前缀,控制单目录规模;按需使用 --fast-list |
| Bucket 类远端空目录消失 | 桶无目录概念,rclone 不创建 / 结尾的目录标记对象 |
占位文件、自行维护目录清单 |
理解这些限制的底层机制(特性位如何门控能力、空目录如何创建)能帮助你正确选择后端、设置合理同步参数,并在数据规划阶段避开容量与语义上的深坑,让 rclone 的同步体验更接近它宣传的"云存储的 rsync"。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00