/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2025 Klara, Inc. */ #ifndef _INOTIFY_H_ #define _INOTIFY_H_ #include /* Flags for inotify_init1(). */ #define IN_NONBLOCK 0x00000004 /* O_NONBLOCK */ #define IN_CLOEXEC 0x00100000 /* O_CLOEXEC */ struct inotify_event { int wd; __uint32_t mask; __uint32_t cookie; __uint32_t len; char name[0]; }; /* Events, set in the mask field. */ #define IN_ACCESS 0x00000001 #define IN_MODIFY 0x00000002 #define IN_ATTRIB 0x00000004 #define IN_CLOSE_WRITE 0x00000008 #define IN_CLOSE_NOWRITE 0x00000010 #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) #define IN_OPEN 0x00000020 #define IN_MOVED_FROM 0x00000040 #define IN_MOVED_TO 0x00000080 #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) #define IN_CREATE 0x00000100 #define IN_DELETE 0x00000200 #define IN_DELETE_SELF 0x00000400 #define IN_MOVE_SELF 0x00000800 #define IN_ALL_EVENTS 0x00000fff /* Events report only for entries in a watched dir, not the dir itself. */ #define _IN_DIR_EVENTS (IN_CLOSE_WRITE | IN_DELETE | IN_MODIFY | \ IN_MOVED_FROM | IN_MOVED_TO) #ifdef _KERNEL /* * An unlink that's done as part of a rename only records IN_DELETE if the * unlinked vnode itself is watched, and not when the containing directory is * watched. */ #define _IN_MOVE_DELETE 0x40000000 /* * Inode link count changes only trigger IN_ATTRIB events if the inode itself is * watched, and not when the containing directory is watched. */ #define _IN_ATTRIB_LINKCOUNT 0x80000000 #endif /* Flags, set in the mask field. */ #define IN_ONLYDIR 0x01000000 #define IN_DONT_FOLLOW 0x02000000 #define IN_EXCL_UNLINK 0x04000000 #define IN_MASK_CREATE 0x10000000 #define IN_MASK_ADD 0x20000000 #define IN_ONESHOT 0x80000000 #define _IN_ALL_FLAGS (IN_ONLYDIR | IN_DONT_FOLLOW | \ IN_EXCL_UNLINK | IN_MASK_CREATE | \ IN_MASK_ADD | IN_ONESHOT) /* Flags returned by the kernel. */ #define IN_UNMOUNT 0x00002000 #define IN_Q_OVERFLOW 0x00004000 #define IN_IGNORED 0x00008000 #define IN_ISDIR 0x40000000 #define _IN_ALL_RETFLAGS (IN_Q_OVERFLOW | IN_UNMOUNT | IN_IGNORED | \ IN_ISDIR) #define _IN_ALIGN _Alignof(struct inotify_event) #define _IN_NAMESIZE(namelen) \ ((namelen) == 0 ? 0 : __align_up((namelen) + 1, _IN_ALIGN)) #ifdef _KERNEL struct componentname; struct file; struct inotify_softc; struct thread; struct vnode; int inotify_create_file(struct thread *, struct file *, int, int *); void inotify_log(struct vnode *, const char *, size_t, int, __uint32_t); int kern_inotify_rm_watch(int, uint32_t, struct thread *); int kern_inotify_add_watch(int, int, const char *, uint32_t, struct thread *); void vn_inotify(struct vnode *, struct vnode *, struct componentname *, int, uint32_t); int vn_inotify_add_watch(struct vnode *, struct inotify_softc *, __uint32_t, __uint32_t *, struct thread *); void vn_inotify_revoke(struct vnode *); /* Log an inotify event. */ #define INOTIFY(vp, ev) do { \ if (__predict_false((vn_irflag_read(vp) & (VIRF_INOTIFY | \ VIRF_INOTIFY_PARENT)) != 0)) \ VOP_INOTIFY((vp), NULL, NULL, (ev), 0); \ } while (0) /* Log an inotify event using a specific name for the vnode. */ #define INOTIFY_NAME_LOCK(vp, dvp, cnp, ev, lock) do { \ if (__predict_false((vn_irflag_read(vp) & VIRF_INOTIFY) != 0 || \ (vn_irflag_read(dvp) & VIRF_INOTIFY) != 0)) { \ if (lock) \ vn_lock((vp), LK_SHARED | LK_RETRY); \ VOP_INOTIFY((vp), (dvp), (cnp), (ev), 0); \ if (lock) \ VOP_UNLOCK(vp); \ } \ } while (0) #define INOTIFY_NAME(vp, dvp, cnp, ev) \ INOTIFY_NAME_LOCK((vp), (dvp), (cnp), (ev), false) extern __uint32_t inotify_rename_cookie; #define INOTIFY_MOVE(vp, fdvp, fcnp, tvp, tdvp, tcnp) do { \ if (__predict_false((vn_irflag_read(fdvp) & VIRF_INOTIFY) != 0 || \ (vn_irflag_read(tdvp) & VIRF_INOTIFY) != 0 || \ (vn_irflag_read(vp) & VIRF_INOTIFY) != 0)) { \ __uint32_t cookie; \ \ cookie = atomic_fetchadd_32(&inotify_rename_cookie, 1); \ VOP_INOTIFY((vp), (fdvp), (fcnp), IN_MOVED_FROM, cookie); \ VOP_INOTIFY((vp), (tdvp), (tcnp), IN_MOVED_TO, cookie); \ } \ if ((tvp) != NULL) \ INOTIFY_NAME_LOCK((tvp), (tdvp), (tcnp), \ _IN_MOVE_DELETE, true); \ } while (0) #define INOTIFY_REVOKE(vp) do { \ if (__predict_false((vn_irflag_read(vp) & VIRF_INOTIFY) != 0)) \ vn_inotify_revoke((vp)); \ } while (0) #else #include __BEGIN_DECLS int inotify_init(void); int inotify_init1(int flags); int inotify_add_watch(int fd, const char *pathname, __uint32_t mask); int inotify_add_watch_at(int fd, int dfd, const char *pathname, __uint32_t mask); int inotify_rm_watch(int fd, int wd); __END_DECLS #endif /* !_KERNEL */ #endif /* !_INOTIFY_H_ */