文件: core/io/file_reader.rs:23-42 分类: 并发安全
core/io/file_reader.rs:23-42
问题: FileReader::open() 先 mmap 文件,再通过 BufReader 读取文件构建行索引。如果文件在两步之间被修改,line_index 描述的字节范围可能与 mmap 数据不一致。
FileReader::open()
line_index
mmap
let mmap = unsafe { memmap2::Mmap::map(&file) }?; // 快照 A let line_index = LineIndex::from_reader(&mut reader)?; // 快照 B(可能已变)
影响: get_line() 可能返回错误行或 panic。
get_line()
建议修复: 从 mmap 字节直接构建索引,确保数据和索引来自同一快照。
根因: FileReader::open() 和 reload() 中,mmap 映射(快照 A)和 LineIndex::from_reader(BufReader) (快照 B)是两个独立的文件读取操作。如果文件在两步之间被外部修改,索引偏移量与 mmap 字节数据不一致,导致 get_line() 返回错误内容或越界 panic。
reload()
LineIndex::from_reader(BufReader)
修复: 将 LineIndex::from_reader(BufReader<&File>) 替换为 LineIndex::from_bytes(&mmap),让行索引直接从 mmap 内存快照构建,确保数据和索引来自同一个不可变的快照。
LineIndex::from_reader(BufReader<&File>)
LineIndex::from_bytes(&mmap)
变更文件: crates/core/src/io/file_reader.rs
crates/core/src/io/file_reader.rs
open()
from_reader
from_bytes(&mmap)
update_for_append()
新增测试 (4 个):
test_open_from_bytes_matches_from_reader
test_reload_after_external_modify_returns_correct_content
test_reload_after_truncate_then_rewrite_no_stale_data
test_open_reload_idempotent_cross_block
提交: 24fe97a
24fe97a
No dependencies set.
The note is not visible to the blocked user.
文件:
core/io/file_reader.rs:23-42分类: 并发安全
问题:
FileReader::open()先 mmap 文件,再通过 BufReader 读取文件构建行索引。如果文件在两步之间被修改,line_index描述的字节范围可能与mmap数据不一致。影响:
get_line()可能返回错误行或 panic。建议修复: 从 mmap 字节直接构建索引,确保数据和索引来自同一快照。
修复方案
根因:
FileReader::open()和reload()中,mmap 映射(快照 A)和LineIndex::from_reader(BufReader)(快照 B)是两个独立的文件读取操作。如果文件在两步之间被外部修改,索引偏移量与 mmap 字节数据不一致,导致get_line()返回错误内容或越界 panic。修复: 将
LineIndex::from_reader(BufReader<&File>)替换为LineIndex::from_bytes(&mmap),让行索引直接从 mmap 内存快照构建,确保数据和索引来自同一个不可变的快照。变更文件:
crates/core/src/io/file_reader.rsopen():from_reader→from_bytes(&mmap)reload():from_reader→from_bytes(&mmap)update_for_append(): 无需改动(已从 mmap 切片构建)新增测试 (4 个):
test_open_from_bytes_matches_from_reader: 验证 from_bytes 与 from_reader 产出完全一致的索引(空文件/单行/跨块 256/300/512 行)test_reload_after_external_modify_returns_correct_content: 外部覆盖文件后 reload 返回新内容test_reload_after_truncate_then_rewrite_no_stale_data: 截断后写入更短内容,旧行不可访问test_open_reload_idempotent_cross_block: 600 行跨块场景 open 与 reload 逐行一致提交:
24fe97a