[PATCH v10 00/11] Data in direntry (dirdata) feature
Artem Blagodarenko <[email protected]> Fri, 31 Jul 2026 11:37:34 -0400
| Newsgroups | org.kernel.vger.linux-ext4 |
|---|---|
| Message-ID | <[email protected]> |
EXT4 currently stores an 8-byte hash in the directory entry immediately after the file name to support simultaneous fscrypt and casefold functionality. The dirdata feature provides a general mechanism to store multiple metadata records in each directory entry after the NUL filename terminator. The unused high 4 bits of 'file_type' indicate which records are present; each record starts with a 1-byte length field, providing forward compatibility. The first user of this feature is LUFID (Locally Unique File ID), an identifier that must survive rename and be accessible from directory readdir without an inode lookup. e2fsprogs support is provided in a separate patch series. Thanks to Sashiko AI review for identifying several correctness and safety issues in earlier versions of this series. Changes in v10: Reorder patches 4 and 5: "dirdata format definitions and access helpers" now precedes "refactor dx_root to support variable dirent sizes". The refactor patch uses ext4_dir_entry_len(), which in turn depends on types and functions from the format definitions patch; placing format definitions first makes the dependency explicit. Patch 04 (dirdata format definitions): restore ext4_dirdata_next() helper. v9 removed it as unused dead code; it is restored as a public accessor for external consumers of the dirdata format (such as the Lustre filesystem module) that traverse extension records without going through ext4_dirent_get_data_len(). Patch 05 (refactor dx_root): fix OOB write in dx_get_dx_info(). The bounds check ensured only sizeof(struct dx_root_info) (8 bytes) fits after the '.' and '..' entries, but callers in make_indexed_dir() immediately write the full sizeof(struct dx_entry) (8 bytes) that follows. A crafted image with '.' and '..' lengths summing to (blocksize - 8) passes the old check, places dx_info at (base + blocksize - 8), and causes all three dx_set_*() writes to land past the end of the block buffer. Fix by adding sizeof(struct dx_entry) to the guard. Reported-by: sashiko-bot <[email protected]> Closes: https://sashiko.dev/#/patchset/[email protected]?part=4 Patch 09 (LUFID rename support): fix two duplicate ext4_fc_mark_ineligible(EXT4_FC_REASON_DIRDATA) calls. - __ext4_link() called ext4_fc_mark_ineligible() before invoking __ext4_add_entry(), which calls it again when a LUFID payload is present. Remove the redundant outer call; __ext4_add_entry() is the authoritative site. - ext4_rename() called ext4_fc_mark_ineligible() unconditionally at the top of the dirdata block for both the !new.bh path (which goes through __ext4_add_entry() and marks ineligible there) and the new.bh path (which calls ext4_setent() and needs the mark). Move the call into the new.bh branch, after ext4_setent() succeeds. Reported-by: sashiko-bot <[email protected]> Closes: https://sashiko.dev/#/patchset/[email protected]?part=8 Patch 10 (dirdata set/get helpers): fix OOB read in ext4_search_dir(). ext4_match() calls ext4_dirdata_get() for casefolded+encrypted+dirdata directories, which decodes de->rec_len and uses it as the upper bound for all pointer arithmetic. ext4_search_dir() previously validated de_len only after ext4_match() returned. A crafted block with de->rec_len encoding a value larger than the block size passes the loop guard and reaches ext4_match(), allowing ext4_dirdata_get() to read past the end of the block buffer. Fix by decoding and bounding de_len at the top of the loop before ext4_match() is called. Reported-by: sashiko-bot <[email protected]> Closes: https://sashiko.dev/#/patchset/[email protected]?part=10 Patch 11 (SET_LUFID ioctl): three fixes to ext4_dirdata_set_lufid(). - Enforce IS_APPEND(dir) on the parent directory. The ioctl calls ext4_delete_entry() directly, bypassing may_delete(), which is the only VFS path that enforces append-only semantics for directories. inode_permission() blocks IS_IMMUTABLE but not IS_APPEND. Add an explicit IS_APPEND(dir) check consistent with may_delete(). - Restore the original LUFID on rollback. If ext4_add_entry() fails after ext4_delete_entry() succeeds, the rollback path previously re-added the entry with no LUFID, silently orphaning any FID->path mapping the entry carried. Fix by snapshotting the on-disk LUFID before deletion with ext4_lufid_snapshot() and attaching the snapshot to the rollback dentry's d_fsdata, so ext4_add_entry() restores the original LUFID. - Preserve on-disk filename case for re-add. For non-encrypted casefolded directories, ext4_find_entry() matches case-insensitively, so de->name may differ from the caller-supplied filename. Using the caller's form for the re-add silently renames the entry. Fix by copying de->name into a stack buffer immediately after the phase-2 lookup and using those bytes for both the new entry and the rollback entry. For encrypted directories (with or without casefold), de->name holds raw ciphertext; use the plaintext d_name instead and let ext4_fname_setup_filename() re-derive the ciphertext, avoiding double-encryption. Reported-by: sashiko-bot <[email protected]> Closes: https://sashiko.dev/#/patchset/[email protected]?part=11 Artem Blagodarenko (11): ext4: validate count against limit in ext4_dx_csum_verify/_set ext4: replace ext4_dir_entry with ext4_dir_entry_2 ext4: add ext4_dir_entry_is_tail() ext4: add dirdata format definitions and access helpers ext4: refactor dx_root to support variable dirent sizes ext4: add ext4_dir_entry_len() ext4: rename ext4_dir_rec_len() and clarify dirdata usage ext4: dirdata feature ext4: add dirdata LUFID support for directory entry rename ext4: add dirdata set/get helpers ext4: Add EXT4_IOC_SET_LUFID ioctl for setting LUFID on directory entries fs/ext4/dir.c | 49 +- fs/ext4/ext4.h | 237 ++++++- fs/ext4/fast_commit.c | 1 + fs/ext4/fast_commit.h | 1 + fs/ext4/inline.c | 67 +- fs/ext4/ioctl.c | 85 +++ fs/ext4/namei.c | 1264 +++++++++++++++++++++++++++++++------ fs/ext4/sysfs.c | 2 + include/uapi/linux/ext4.h | 14 + 9 files changed, 1470 insertions(+), 250 deletions(-) -- 2.43.7