Skip to content

fix(rsext4): use physical byte offset in readdir to fix rm -rf skipping entries#1001

Merged
ZR233 merged 4 commits into
rcore-os:devfrom
zyc107109102:pr/pip-tests
May 28, 2026
Merged

fix(rsext4): use physical byte offset in readdir to fix rm -rf skipping entries#1001
ZR233 merged 4 commits into
rcore-os:devfrom
zyc107109102:pr/pip-tests

Conversation

@zyc107109102

Copy link
Copy Markdown
Contributor

问题

在 StarryOS 中执行 rm -rf 删除包含多个文件的目录时,需要多次执行才能完全删除。例如 rm -rf venv(Python 虚拟环境):

  1. 第一次:提示 rich/ 文件夹非空(ENOTEMPTY)
  2. 第二次:提示 rich/__pycache__/ 非空(ENOTEMPTY)
  3. 第三次:才成功删除

根因是 rsext4 的 read_dir 使用逻辑索引(只计未删除条目)作为 getdents64 的 offset。当 rm -rf 在两次 getdents64 调用之间删除条目后,逻辑索引偏移导致后续条目被跳过。

修复

1. remove_dentry_in_dir_block:删除时不再合并 rec_len

原逻辑会将被删条目的 rec_len 合并到前一个条目,改变目录块字节布局。修复后只清零 inode,保持 rec_len 不变(与 Linux ext4 一致),确保字节偏移稳定。

2. read_dir:改用物理字节偏移

原逻辑使用逻辑索引 idx(只对未删除条目计数)。修复后手动遍历目录块原始数据,对所有条目(包括 inode==0 的已删除条目)累加 rec_len 作为字节偏移。

不能使用 DirEntryIterator,因为它内部跳过 inode==0 条目,导致这些条目的 rec_len 不被计入偏移。

测试

bug-ext4-dir-ops 中新增两个测试用例:

  • test_readdir_offset_after_delete:极小 buffer 强制单条目读取,读取后立即删除,验证全部 30 个文件可见
  • test_rm_rf_pattern:模拟 rm -rf 批量读取-删除模式,验证 rmdir 最终成功
平台 结果
Linux(验证测试正确性) 151 pass, 0 fail
Starry QEMU aarch64 151 pass, 0 fail

关联

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 审查:fix(rsext4): use physical byte offset in readdir to fix rm -rf skipping entries

变更概述

本 PR 修复了 rsext4 中 read_dir 使用逻辑索引(只计未删除条目)作为 getdents64 offset 的问题,改为使用物理字节偏移,与 Linux ext4 保持一致。修改涉及 4 个文件:+206/-36。

实现逻辑分析

1. remove_dentry_in_dir_block(delete.rs)

原逻辑在删除目录条目时,会将被删条目的 rec_len 合并到前一个条目。这改变了目录块的字节布局,导致后续条目偏移不稳定。

修复后只清零 inode 字段,保持 rec_len 不变。这与 Linux ext4 的删除行为一致——Linux 不会在删除时合并 rec_len,从而保证字节偏移的稳定性。代码正确简化了删除逻辑,移除了 prev_offprev_rec_len 的跟踪。

2. read_dir(inode.rs)

原逻辑使用 DirEntryIterator 遍历(内部跳过 inode==0 条目),用逻辑索引 idx 作为 offset。修复后手动遍历目录块原始数据,对所有条目(包括已删除的 inode==0)累加 rec_len 作为字节偏移。

关键实现细节正确:

  • rec_len < 8 时终止,防止畸形条目
  • pos + rec_usize > data.len() 边界检查
  • name_len > rec_usize - 8 防止名称越界
  • byte_offset 跨块累积,传递给 sink.accept 的是当前条目结束后的偏移(与 Linux getdents64d_off 语义一致)
  • 不使用 DirEntryIterator(它会跳过已删除条目的 rec_len

测试覆盖

新增两个回归测试,位于正确的 bugfix/bug-ext4-dir-ops 分组:

  • test_readdir_offset_after_delete:极小 buffer(32 字节)强制单条目读取,每读一条立即删除,验证全部 30 个文件可见。精确触发旧行为下偏移跳变的 bug。
  • test_rm_rf_pattern:模拟 rm -rf 批量读取-删除模式(80 字节 buffer),验证 rmdir 最终成功。

两个测试都使用 SYS_getdents64 系统调用直接调用,避免 libc 包装层掩盖返回值或 errno。测试位于已有 grouped case /usr/bin/bug-ext4-dir-ops,已在 qemu-aarch64.tomlqemu-x86_64.toml 中注册。

本地验证

  • cargo fmt --check:✅ 通过
  • cargo clippy -p rsext4:✅ 无 warning
  • git diff --check:✅ 通过

CI 状态

所有 GitHub Actions check runs 均为 skipped(fork PR 可能受限于 CI 触发策略,非测试失败)。本地验证覆盖了代码质量和格式检查。

重复/重叠分析

  • base 分支:dev 分支当前的 remove_dentry_in_dir_block 仍使用旧的 rec_len 合并逻辑,read_dir 仍使用逻辑索引。本 PR 不与已有代码重复。
  • 已关闭关联 PR#854(rmdir ENOTEMPTY + rename 类型交叉检查)和 #938(rename 时跳过 children cache 转移)已合入 dev,解决的是不同问题。
  • 其他 open PR#990(syscall harness)和 #976(block driver 重构)不修改 readdir 或 delete 代码。#625(非 512 字节块设备支持)触及同一文件但修改不同路径。无冲突或重复风险。

结论

修复正确、完整,符合 Linux ext4 语义,测试覆盖充分,代码质量良好。推荐合入。

Powered by mimo-v2.5-pro

@mai-team-app
mai-team-app Bot requested review from luodeb and yfblock May 27, 2026 17:52

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 审查:fix(rsext4): use physical byte offset in readdir

变更概述

本 PR 修复了 rsext4 中 read_dir 使用逻辑索引(只计未删除条目)作为 getdents64 offset 的问题,改为使用物理字节偏移,与 Linux ext4 保持一致。修改涉及 4 个文件:+207/-37。

实现逻辑分析

1. remove_dentry_in_dir_block(delete.rs)

原逻辑在删除目录条目时,会将被删条目的 rec_len 合并到前一个条目,改变目录块字节布局。修复后只清零 inode 字段,保持 rec_len 不变。这与 Linux ext4 的删除行为一致,保证字节偏移的稳定性。

2. read_dir(inode.rs)

原逻辑使用 DirEntryIterator 遍历(内部跳过 inode==0 条目),用逻辑索引 idx 作为 offset。修复后手动遍历目录块原始数据,对所有条目(包括已删除的 inode==0)累加 rec_len 作为字节偏移。

关键实现细节正确:

  • rec_len < 8 时终止,防止畸形条目
  • pos + rec_usize > data.len() 边界检查
  • name_len > rec_usize - 8 防止名称越界
  • byte_offset 跨块累积,传递给 sink.accept 的是当前条目结束后的偏移(与 DirEntrySink accept 的 offset of the next entry 语义一致)

本地验证

  • cargo fmt --check:通过
  • cargo clippy -p rsext4:无 warning
  • git diff --check:通过

测试覆盖

新增两个回归测试,位于已注册的 bug-ext4-dir-ops 分组(已注册于 4 个 qemu 平台 toml):

test_readdir_offset_after_delete:极小 buffer(32 字节)强制单条目读取,每读一条立即删除,验证全部 30 个文件可见。
test_rm_rf_pattern:模拟 rm -rf 批量读取-删除模式(80 字节 buffer),验证 rmdir 最终成功。

两个测试都使用 SYS_getdents64 系统调用直接调用,避免 libc 包装层掩盖返回值。

CI 状态

所有 GitHub Actions check runs 均为 skipped(fork PR 受限于 CI 触发策略,非测试失败)。本地验证覆盖了代码质量和格式检查。

重复/重叠分析

  • base 分支:dev 分支当前的 remove_dentry_in_dir_block 仍使用旧的 rec_len 合并逻辑,read_dir 仍使用逻辑索引。本 PR 不与已有代码重复。
  • 已关闭关联 PR:#854(rmdir ENOTEMPTY)和 #938(rename children cache)已合入 dev,解决不同问题。
  • 其他 open PR:#1000(curl test clean)不涉及 readdir 或 delete 代码。无冲突或重复风险。

结论

修复正确、完整,符合 Linux ext4 语义,测试覆盖充分,代码质量良好。推荐合入。

Powered by mimo-v2.5-pro

zyc107109102 added 2 commits May 27, 2026 18:02

@mai-team-app mai-team-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 审查:fix(rsext4): use physical byte offset in readdir to fix rm -rf skipping entries

变更概述

本 PR 修复了 rsext4 中 read_dir 使用逻辑索引(只计未删除条目)作为 getdents64 offset 的问题,改为使用物理字节偏移,与 Linux ext4 保持一致。修改涉及 4 个文件:+207/-37。

实现逻辑分析

1. remove_dentry_in_dir_block(delete.rs)

原逻辑在删除目录条目时,会将被删条目的 rec_len 合并到前一个条目,改变目录块字节布局。修复后只清零 inode 字段,保持 rec_len 不变。这与 Linux ext4 的删除行为一致——Linux 在 ext4_delete_entry() 中也只清零 inode 并标记删除,不会合并 rec_len。代码正确简化了删除逻辑,移除了 prev_offprev_rec_len 的跟踪。

2. read_dir(inode.rs)

原逻辑使用 DirEntryIterator 遍历(内部跳过 inode==0 条目),用逻辑索引 idx 作为 offset。修复后手动遍历目录块原始数据,对所有条目(包括已删除的 inode==0)累加 rec_len 作为字节偏移。

关键实现细节验证通过:

  • rec_len < 8 时终止,防止畸形条目
  • pos + rec_usize > data.len() 边界检查,防止越界
  • name_len > rec_usize - 8 防止名称字段越界
  • byte_offset 跨块累积,传递给 sink.accept 的是当前条目结束后的偏移,与 DirEntrySink 文档中 "offset of the next entry" 语义一致
  • 不使用 DirEntryIterator(它会跳过已删除条目的 rec_len),正确做法
  • 已删除条目(inode==0)先推进偏移再跳过,确保后续条目字节位置正确

本地验证

  • cargo fmt --check:✅ 通过
  • cargo clippy --manifest-path components/rsext4/Cargo.toml --all-features -- -D warnings:✅ 无 warning
  • git diff --check:✅ 通过

测试覆盖

新增两个回归测试,位于已有的 bugfix/bug-ext4-dir-ops 分组:

  • test_readdir_offset_after_delete:极小 buffer(32 字节)强制单条目读取,每读一条立即删除,验证全部 30 个文件可见。精确触发旧行为下偏移跳变的 bug。
  • test_rm_rf_pattern:模拟 rm -rf 批量读取-删除模式(80 字节 buffer),验证 rmdir 最终成功。

两个测试都使用 SYS_getdents64 系统调用直接调用,避免 libc 包装层掩盖返回值或 errno,符合项目 bugfix 测试规范。测试注册在 main() 中,会在 grouped case 运行中被执行。

CI 状态

所有 GitHub Actions check runs 均为 skipped(fork PR 受限于 CI 触发策略,非测试失败)。mergeable_state=unstable。本地验证覆盖了代码质量和格式检查。已在 Linux 和 Starry QEMU aarch64 上手动验证 151 pass, 0 fail。

重复/重叠分析

  • base 分支:dev 分支当前的 remove_dentry_in_dir_block 仍使用旧的 rec_len 合并逻辑,read_dir 仍使用逻辑索引。本 PR 不与已有代码重复。
  • 已关闭关联 PR#854(rmdir ENOTEMPTY + rename 类型交叉检查,已合入)和 #938(rename 时跳过 children cache 转移,已合入)解决的是不同问题。
  • 其他 open PR#1000(curl test clean)、#997#990 等不修改 readdir 或 delete 代码,无冲突或重复风险。

结论

修复正确、完整,符合 Linux ext4 语义,测试覆盖充分,代码质量良好。推荐合入。

Powered by mimo-v2.5-pro

@ZR233
ZR233 merged commit a30ce64 into rcore-os:dev May 28, 2026
47 checks passed
@github-actions github-actions Bot mentioned this pull request May 28, 2026
54dK3n pushed a commit to 54dK3n/tgoskits that referenced this pull request May 31, 2026
…ng entries (rcore-os#1001)

* fix(rsext4): use physical byte offset in readdir to fix rm -rf skipping entries

* test ci

* test ci

* test ci

---------

Co-authored-by: zyc107109102 <zhouyucong2019@163.com>
This was referenced Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants