陶泥儿产物读取改用 no-follow 打开

- read_package_bytes 用 OpenOptions + O_NOFOLLOW / FILE_FLAG_OPEN_REPARSE_POINT,关掉 symlink_metadata 检查后到打开之间被换成符号链接的 TOCTOU 窗口
This commit is contained in:
2026-10-07 14:02:21 +08:00
parent a3cf980000
commit ba9d70a5b6
@@ -262,7 +262,23 @@ pub(crate) fn read_artifact_package(root: &Path) -> Result<TaonierArtifactPackag
/// 调用方已经确认过 `expected_bytes` 在上限内,按它预分配容量即可,避免接近 200 MiB 的包在
/// 反复扩容时多复制约一倍数据。
fn read_package_bytes(path: &Path, expected_bytes: u64) -> Result<(Vec<u8>, String), String> {
let mut file = std::fs::File::open(path)
// 不跟随符号链接打开:`read_artifact_package` 先做了 symlink_metadata 检查,这里用
// no-follow 打开关掉「检查后、打开前被换成同长度链接」的 TOCTOU 窗口。
let mut options = std::fs::OpenOptions::new();
options.read(true);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.custom_flags(libc::O_CLOEXEC | libc::O_NOFOLLOW);
}
#[cfg(windows)]
{
use std::os::windows::fs::OpenOptionsExt;
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
}
let mut file = options
.open(path)
.map_err(|error| format!("打开陶泥儿产物失败:{}:{error}", path.display()))?;
let mut hasher = Sha256::new();
let mut bytes = Vec::with_capacity(expected_bytes as usize);