Skip to content

fix(localfs): reject non-exact path aliases - #6053

Open
sponge225 wants to merge 1 commit into
volcengine:mainfrom
sponge225:fix/localfs-case-alias
Open

sponge225 wants to merge 1 commit into
volcengine:mainfrom
sponge225:fix/localfs-case-alias

Conversation

@sponge225

@sponge225 sponge225 commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Description

在大小写不敏感的文件系统上,localfs 可能把大小写不同的路径解析到同一个磁盘对象。上层权限系统按原始 URI 判断权限,而底层文件系统按自身规则解析文件名,两层语义不一致时可能造成越权读写。

例如,目录中已经存在 a.md,用户没有访问该 URI 的权限,但可以提交另一个 URI A.md。在默认 macOS 文件系统上:

  • 上层按 A.md 执行权限判断;
  • localfs 将 A.md 直接交给操作系统;
  • 操作系统实际打开已有的 a.md;
  • 最终可能读取或覆盖 a.md。

本 PR 在 localfs 的统一路径解析入口中增加精确名称检查:当操作系统认为请求路径命中了已有对象时,逐级读取父目录中的真实目录项,要求每个已有路径组件与请求名称完全一致;如果只通过大小写别名命中,则返回 InvalidPath,不再继续执行读取、写入、删除或移动等操作。

该检查依据实际文件系统行为生效。在大小写敏感的文件系统上,如果 A.md 不存在,仍然允许创建独立的 A.md,不会改变 a.md 与 A.md 可以同时存在的原有行为。

写操作会先通过 PathLock 读取锁文件。为了避免 PathLock 将底层明确的 InvalidPath 包装成普通 I/O 错误并返回 500 INTERNAL,本 PR 仅在读取锁 token 的路径上保留该错误类型,并在 Python binding 中映射为 AGFSInvalidPathError。因此别名读写现在都会返回 400 INVALID_URI;其他锁 I/O、无效 token 和内部错误仍保持原有的 500 分类。

Human Involvement

  • A human participated in the implementation or review loop
  • This PR was generated entirely by AI agents without human participation in the loop

Related Issue

无关联 Issue。

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test update

Changes Made

  • 在 LocalFileSystem::resolve_path() 中统一执行已有路径组件的精确名称检查,使所有经过该入口的 localfs 操作获得一致保护。
  • 使用 symlink_metadata() 判断请求名称是否被文件系统解析到已有对象,再通过 read_dir() 与真实目录项做精确比较;避免自行模拟不同文件系统的大小写规则。
  • 将 NotFound 和 NotADirectory 视为后续路径尚不存在,使新文件创建及 PathLock 内部探测路径保持原有行为。
  • 增加跨文件系统语义的回归测试:验证大小写不敏感系统拒绝别名读写,大小写敏感系统仍允许两个名称分别存在,并确认使用精确名称的正常写入不受影响。
  • 为 PathLock 增加 InvalidPath 错误类型,仅在读取锁 token 遇到底层 core::Error::InvalidPath 时保留该语义;其他错误仍按原逻辑处理。
  • 在 ragfs-python 中将 PathLock 的 InvalidPath 映射为 AGFSInvalidPathError,复用现有 HTTP 错误映射并返回 400 INVALID_URI。
  • 当父目录不可枚举时,使用平台原生路径查询能力核对真实名称,避免安全校验破坏原有的精确路径访问语义。

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested this on the following platforms:
    • Linux
    • macOS
    • Windows

执行结果:

cargo test -p ragfs
430 passed; 0 failed
Doc tests: 1 passed; 1 ignored

cargo test -p ragfs read_token --lib
3 passed; 0 failed

cargo check -p ragfs-python
passed

另外已通过:

rustfmt --edition 2021 --check crates/ragfs/src/plugins/localfs/mod.rs
git diff --check origin/main...HEAD

使用当前分支重新构建并安装 ragfs-python,启动本地 OpenViking Server 后验证完整 HTTP 链路:

  • 精确读取 a.md:返回 200,内容正确。
  • 别名读取 A.md:返回 400 INVALID_URI。
  • 别名写入 A.md:返回 400 INVALID_URI,磁盘中的 a.md 内容没有变化。
  • 精确写入 a.md:返回 200,磁盘内容正常更新。

Checklist

  • My code follows the project's coding style
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

文档未修改:本 PR 不增加或变更公共 API、配置项和使用方式,只修正 localfs 对已有路径的解析行为及对应的错误分类。

Screenshots (if applicable)

不适用。

Additional Notes

本 PR 采用最小修复,不引入挂载级全局锁,因此不会改变 localfs 原有的并发模型。它修复的是可稳定复现的大小写别名访问;检查与实际系统调用之间原本存在的通用文件系统竞态不在本次改动范围内,也没有因本 PR 扩大。

PathLock 的错误分类调整仅覆盖本问题实际触发的“读取锁 token 时底层返回 InvalidPath”场景。CAS、扫描、删除等其他 PathLock 路径未扩展处理范围。

目录权限兼容性:精确名称检查通常通过 read_dir(parent) 获取磁盘上的真实目录项名称;在 POSIX 系统上,若父目录仅允许路径遍历但禁止目录枚举(例如权限为 0111),localfs 不会直接跳过安全校验,而是通过平台能力获取实际名称(macOS getattrlist、Linux O_PATH + /proc/self/fd)。因此原本合法的精确路径读取保持可用,同时大小写别名仍会被拒绝;如果平台无法安全确认真实名称,则维持拒绝行为。

@sponge225
sponge225 force-pushed the fix/localfs-case-alias branch 2 times, most recently from 3f119ab to bf1725c Compare October 14, 2026 12:20
@sponge225
sponge225 force-pushed the fix/localfs-case-alias branch from bf1725c to 80ef6ca Compare October 14, 2026 12:24
@ZaynJarvis ZaynJarvis added the kernel Core server, runtime, storage, retrieval, SDK, and CLI. label Oct 15, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kernel Core server, runtime, storage, retrieval, SDK, and CLI.

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants