Skip to content

fix(fsnotify): harden inotify lifecycle and semantics - #2204

Closed
fslongjin wants to merge 2 commits into
DragonOS-Community:masterfrom
fslongjin:feat/inotify
Closed

fix(fsnotify): harden inotify lifecycle and semantics#2204
fslongjin wants to merge 2 commits into
DragonOS-Community:masterfrom
fslongjin:feat/inotify

Conversation

@fslongjin

Copy link
Copy Markdown
Member

Summary

  • build on feat(fs): implement inotify filesystem event notification #2164 and fix the fsnotify/inotify lifecycle and concurrency issues found during review
  • linearize mark publication, dispatch, removal, inode deletion, and final unmount using stable object identities
  • make queue overflow, allocation rollback, quotas, and dentry snapshots safe under concurrency and memory pressure
  • align VFS event routing with Linux behavior for open/close, links, rename, attributes, unlinked paths, and deletion ordering
  • add focused dunitest coverage for namespace events, lifecycle cases, O_PATH, hard links, rename aliases, multiple instances, and poll readiness

Validation

  • make kernel
  • 12/12 inotify tests passed on host Linux
  • 12/12 inotify tests passed in a DragonOS QEMU guest
  • final kernel booted to the DragonOS userspace console without panic

sparkzky and others added 2 commits August 16, 2026 18:00
Implements inotify (issue DragonOS-Community#2151): the fsnotify core notification layer,
the inotify pseudo-device, 4 syscalls (init/init1/add_watch/rm_watch),
and VFS write-path hooks for all standard events (create/delete/move/
modify/access/close/attrib/self events).

Architecture:
- fsnotify/ unified dispatch layer: global (inode_id, dev_id) index,
  TOTAL_WATCHES atomic fast-path (zero cost when no watches), lock-family
  separation (global index lock / events lock / wd lock never nested).
- inotify.rs device: InotifyInode implements IndexNode + PollableInode,
  epoll-integrated via LockedEPItemLinkedList, exact inotify_event layout
  (name field aligned to sizeof(inotify_event)=16, matching Linux ABI).
- VFS hooks placed in syscall-core layer (vcore/open/rename_utils/...),
  NOT per-filesystem: single anchor covers ext4/tmpfs/overlayfs/fuse.
  Hooks fire only after success and never alter syscall return values.

Review fixes incorporated:
- Directory watches receive child content events (issue B): IN_MODIFY/
  ACCESS/OPEN/CLOSE delivered to parent dir watch with child name.
  MountFSInode::as_any_ref() returns the inner inode's Any, so use
  downcast_arc instead of downcast_ref for parent resolution.
- Guard DELETE_SELF on hardlink unlink/rename-over: only emit when
  i_nlink reaches 0, matching Linux fsnotify_link_count() semantics.
- Composite mark index key (inode_id, dev_id): prevents FUSE cross-mount
  event leakage when multiple mounts reuse same inode number.
- Remove has_any_watch() gate on File::inotify_parent resolution so
  watches added after open receive content events.
- Tolerate metadata failure after unlink/rename-over (FUSE GETATTR can
  return ENOENT); cache nlinks before the namespace operation.
- SYS_INOTIFY_INIT only registered on x86_64 (generic syscall ABI uses
  inotify_init1); riscv64/loongarch64 lack the legacy init syscall.
- Skip MOVED events on no-op rename; EOF reads no longer deliver
  IN_ACCESS; reject mask==0 and IN_MASK_ADD|IN_MASK_CREATE.

Test: dunitest inotify_dir_watch (2 tests) + inotify_events (6 tests)
covering content/namespace/self events, multi-instance, and poll.

Design doc: docs/kernel/filesystem/inotify.md

Signed-off-by: sparkzky <sparkhhhhhhhhhh@outlook.com>
Linearize mark publication, dispatch, and removal around stable object identities so watch updates, one-shot delivery, inode deletion, and final unmount cannot race.

Make queue overflow, read consumption, allocation rollback, user quotas, and dentry snapshots safe under concurrency and memory pressure. Align VFS event routing with Linux semantics for open, close, links, rename, attributes, exclusion of unlinked paths, and deletion ordering.

Add dunitest coverage for namespace events, inode lifecycle, O_PATH, hard links, rename aliases, multi-instance delivery, and poll readiness.

Validated with make kernel and 12 passing inotify tests on both host Linux and a DragonOS QEMU guest.

Signed-off-by: longjin <longjin@dragonos.org>
@github-actions github-actions Bot added the Bug fix A bug is fixed in this pull request label Aug 20, 2026
@fslongjin

Copy link
Copy Markdown
Member Author

@codex review

@fslongjin fslongjin closed this Aug 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8ef2adf4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +512 to +513
// fsnotify:打开成功 → 被打开 inode 得 IN_OPEN。
if r.is_ok() && !how.o_flags.contains(FileFlags::O_PATH) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 为 exec 打开路径投递 IN_OPEN

当文件通过 execve/execveat 打开时,实际执行的是 do_open_execat_with_flags(),该函数在 open.rs:608 直接创建 File,不会经过这里仅属于 do_sys_openat2() 的通知逻辑;后续 ELF 读取和 File::drop 仍会产生 IN_ACCESSIN_CLOSE_NOWRITE,导致监听可执行文件的审计程序收到关闭事件却完全漏掉对应的 IN_OPEN。应让普通打开与 exec 打开共用 OPEN 通知入口。

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Comment on lines +595 to +596
let r = current_inode.mkdir(name, final_mode);
if let Ok(new_inode) = &r {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 把 mknod 创建纳入 IN_CREATE 投递

这里开始按具体 syscall 在创建成功后投递 IN_CREATE,但 sys_mknod.rs:82sys_mknodat.rs:77 仍直接调用 IndexNode::mknod 后返回,MountFSInode::mknod 也不投递通知。因此通过 mkfifomknod 创建 FIFO、设备节点或普通文件时,父目录 watch 会永久漏掉 Linux 应产生的带节点名 IN_CREATE;需要在两个 mknod 入口成功后执行同样的通知。

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Comment on lines +810 to +812
if r.is_ok() {
fsnotify::fsnotify_inode(FsEvent::MODIFY, &inode);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 在 O_TRUNC 的 MODIFY 之前发布 IN_OPEN

当调用 open(path, O_TRUNC) 时,do_sys_openat2()open.rs:484-485 先调用这里的 truncate 路径并立即排入 IN_MODIFY,直到 open.rs:512 才排入 IN_OPEN,所以用户可见顺序变成 IN_MODIFY → IN_OPEN;Linux 6.6 的打开流程先发布 OPEN,再执行截断通知。依赖 OPEN 划分文件操作边界的消费者会把修改归入错误的一次打开,应在该打开流程中把截断的 MODIFY 延迟到 IN_OPEN 之后。

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Comment on lines +550 to +553
let consumes_oneshot = matches!(
enqueue_result,
EnqueueResult::Queued | EnqueueResult::Merged | EnqueueResult::DroppedQueueFull
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 在分配失败后同样撤销 IN_ONESHOT watch

当首个匹配的 one-shot 事件因名称或队列分配失败返回 AllocationFailed 时,后端已经用溢出记录表示该事件丢失,但这个匹配列表排除了该结果,watch 因而不会撤销;待溢出被读走后,它还能消费后续事件,违反 IN_ONESHOT 在首个匹配事件后自动移除的语义,也延迟了 IN_IGNORED。应将 AllocationFailed 也视为已经触发 one-shot。

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix A bug is fixed in this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants