RK3588 kernel-6.1 fs/ext4/namei.c 路径解析与目录元数据操作分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/namei.c(目录 inode 的 VFSinode_operations:lookup/create/link/unlink/symlink/mkdir/rmdir/mknod/tmpfile/rename,以及 目录块读写、htree 增删分裂、目录项校验和 等底层支撑)- 与
dir.c(readdir、线性/dx读路径)、inline.c(内联目录项增删转普通目录)、hash.c(ext4_htree_fill_tree等 遍历填充)、xattr.c/acl.c(listxattr/get_acl等透传)的协作
分工提示:
dir.c文档已覆盖 大目录按 hash 遍历 与f_pos编码;本文重点在 「写名字」:如何在磁盘目录结构里 查找、插入、删除、改名,以及 htree 随写入而分裂、升层。
2. 代码作用(一句话)
namei.c 实现 ext4 目录层名空间操作:通过 ext4_read_dirblock 按 索引块 / 叶子块 预期做 一致性校验与元数据 CRC;ext4_lookup/ext4_get_parent** 解析 目录项 → ext4_iget;ext4_add_entry 统一 内联目录 → htree → 线性目录,必要时 make_indexed_dir 把单块目录 提升为索引目录;ext4_dx_add_entry/do_split 在叶子满或索引满时 分裂叶子或分裂/升高索引;ext4_delete_entry/__ext4_unlink 合并 rec_len 回收槽位;ext4_rename/ext4_cross_rename/ext4_rename2 在 jbd2 事务 内完成 换名、目录 .. 维护、白化(whiteout)、fast commit 跟踪或置为不合格;并向 VFS 导出 ext4_dir_inode_operations。
3. 解决了什么问题
| 问题 | namei.c 中的处理方式 |
|---|---|
| 按路径打开/创建需要「在目录里找名字」 | ext4_lookup_entry/ext4_find_entry(含 htree dx_probe 与线性扫描);ext4_lookup 校验 inum、加密上下文(目录与目标子项)、d_splice_alias |
.. 与父 dentry 解析 |
ext4_get_parent:查找 .. 项并 ext4_iget |
| 超大目录 O(n) 插入 | 索引目录:ext4_dx_add_entry 按 hash 落到叶子;叶子满 do_split;索引项满则 分裂索引块 或 indirect_levels++ 升层(受 ext4_dir_htree_level 限制) |
| 单块目录长高后首次建索引 | make_indexed_dir:把原块改为 dx root,分配新叶子等(与 ext4_add_entry 中「仅一块且无 dx 回退」路径衔接) |
| htree 损坏时能否继续用 | ext4_dx_add_entry 失败且返回 ERR_BAD_DX_DIR:无 metadata_csum 时可 ext4_clear_inode_flag(EXT4_INODE_INDEX) 回退线性(有风险);有 checksum 则 EFSCORRUPTED,避免 silent 损坏 |
| 目录块读错类型 / 洞 / 越界 | __ext4_read_dirblock:block 与 i_size、首块非洞、期望 INDEX 却读到叶子 等 ext4_error_inode;索引块 CRC(ext4_dx_csum_verify);叶子 目录尾 CRC(与 ext4_dirblock_csum_* 协作) |
| 目录无限膨胀 | ext4_append:可选 s_max_dir_size_kb 限制 i_size |
| fscrypt / casefold 文件名 | fscrypt_is_nokey_name、fscrypt_prepare_*;ext4_fname_setup_*、ext4_ci_compare;casefold 下 negative dentry 与 lookup 缓存 策略(d_invalidate / 查找失败 不缓存) |
| 删除目录项后空间碎片 | ext4_generic_delete_entry:与前项 合并 rec_len 或 清空 inode/name |
重命名与目录链接计数、.. |
ext4_rename_dir_prepare/ext4_rename_dir_finish、ext4_update_dx_flag;跨目录移动目录时 ext4_inc_count/ext4_dec_count |
| overlay 等需要的 RENAME_WHITEOUT | ext4_whiteout_for_rename:ext4_setent 把原项改成 whiteout inode,失败 ext4_resetent |
| RENAME_EXCHANGE | ext4_cross_rename:双方向 ext4_setent |
| rename 目录与 fast commit | ext4_fc_mark_ineligible(EXT4_FC_REASON_RENAME_DIR)(回放尚不能正确处理 .. 改写);非目录 rename 则 __ext4_fc_track_link/unlink/create |
| 内联目录与普通目录形态切换 | ext4_try_add_inline_entry、ext4_delete_inline_entry;rename 时 inline → block 需 force_reread 避免删项时还按旧形态解释缓冲 |
| sticky 位与「目录项 inode 与 dentry 不一致」 | 注释说明:须用 de->inode 与 d_inode 比对,防 cwd 被删后同名占位 等竞态 |
4. 核心流程摘要
4.1 目录块读取与校验类型
dirblock_type_t:EITHER/INDEX/DIRENT/DIRENT_HTREE,告知__ext4_read_dirblock期望值,用于 区分索引块与叶子、选择性做强校验(避免空叶子被误判为索引块时误验索引 CRC)。ext4_read_dirblock为带 调用点 的封装,便于ext4_error_inode(..., func, line, ...)定位。
4.2 添加目录项:ext4_add_entry
ext4_fname_setup_filename(加密名、casefold 等)。- 若
ext4_has_inline_data(dir):ext4_try_add_inline_entry;成功则返回。 - 若
is_dx(dir):ext4_dx_add_entry;失败则按是否 metadata_csum 决定 降级或报错。 - 否则 块号 0…blocks-1 上
add_dirent_to_buf;-ENOSPC且 单块 +dir_indexfeature:make_indexed_dir。 - 仍满则
ext4_append新目录块再add_dirent_to_buf。 - 成功则
ext4_set_inode_state(..., EXT4_STATE_NEWENTRY)。
4.3 htree 写路径:ext4_dx_add_entry
dx_probe定位帧栈;读 叶子DIRENT_HTREE。- 叶子
add_dirent_to_buf若ENOSPC:索引项满 则 分裂索引 或 增加indirect_levels;否则do_split分裂叶子(dx_make_map/dx_sort_map均分条目),再在目标叶add_dirent_to_buf。 restart时 重新dx_probe,因索引路径已变。
4.4 删除与 unlink
ext4_delete_entry:先尝试 inline 路径,再ext4_journal_get_write_access+ext4_generic_delete_entry+ext4_dirblock_csum_set+ext4_handle_dirty_dirblock。__ext4_unlink:ext4_find_entry与 inode 校验;事务内删项;drop_nlink;i_nlink==0ext4_orphan_add;fast commitext4_fc_track_unlink(带 dentry 时)。
4.5 创建类操作(节选)
ext4_create/ext4_mknod/ext4_tmpfile:ext4_new_inode_start_handle+ext4_add_entry(或 tmpfile 不建目录项),设置ext4_file_inode_operations或ext4_special_inode_operations。ext4_mkdir:ext4_new_inode_start_handle+ext4_init_new_dir(./..)+ext4_add_entry,ext4_dir_inode_operations。ext4_symlink:fscrypt_prepare_symlink;短链可走 fast symlink;长链ext4_init_symlink_block写数据块;加密则ext4_encrypted_symlink_inode_operations。
4.6 ext4_rename2 → ext4_rename / ext4_cross_rename
fscrypt_prepare_rename。RENAME_EXCHANGE→ext4_cross_rename。- 否则
ext4_rename:quota、ext4_find_entry、journal_start、目录则empty_dir/EMLINK检查、ext4_setent/ext4_add_entry、删旧项、更新 ctime/nlink/orphan、whiteout 回滚路径。
5. 与 ext4_dir_inode_operations 的对应关系
1 | const struct inode_operations ext4_dir_inode_operations = { |
目录 inode 的 setattr/getattr/xattr/acl 等在其它文件中实现,经此表挂到 目录 上。
6. 小结
namei.c 是 ext4 「名字 ↔ inode」 的枢纽:读目录块时防呆与校验、写目录时维护线性与 htree 两种形态(含 分裂与索引升高),并把 POSIX 语义(link 计数、目录 ..、加密与白化、journaling/fast commit)落到 事务化的目录项更新 上。深入 htree 读侧与 readdir 请结合 dir.c/hash.c 文档;内联目录 细节见 inline 相关源码与 rk3588-kernel-6.1-ext4-inline内联数据与内联目录分析.md。
正在加载留言…