| 1 | /*- |
| 2 | * SPDX-License-Identifier: BSD-2-Clause |
| 3 | * |
| 4 | * Copyright (c) 2025 Klara, Inc. |
| 5 | */ |
| 6 | |
| 7 | #ifndef _INOTIFY_H_ |
| 8 | #define _INOTIFY_H_ |
| 9 | |
| 10 | #include <sys/_types.h> |
| 11 | |
| 12 | /* Flags for inotify_init1(). */ |
| 13 | #define	IN_NONBLOCK	0x00000004	/* O_NONBLOCK */ |
| 14 | #define	IN_CLOEXEC	0x00100000	/* O_CLOEXEC */ |
| 15 | |
| 16 | struct inotify_event { |
| 17 | 	int		wd; |
| 18 | 	__uint32_t	mask; |
| 19 | 	__uint32_t	cookie; |
| 20 | 	__uint32_t	len; |
| 21 | 	char		name[0]; |
| 22 | }; |
| 23 | |
| 24 | /* Events, set in the mask field. */ |
| 25 | #define	IN_ACCESS		0x00000001 |
| 26 | #define	IN_MODIFY		0x00000002 |
| 27 | #define	IN_ATTRIB		0x00000004 |
| 28 | #define	IN_CLOSE_WRITE		0x00000008 |
| 29 | #define	IN_CLOSE_NOWRITE	0x00000010 |
| 30 | #define	IN_CLOSE		(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) |
| 31 | #define	IN_OPEN			0x00000020 |
| 32 | #define	IN_MOVED_FROM		0x00000040 |
| 33 | #define	IN_MOVED_TO		0x00000080 |
| 34 | #define	IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO) |
| 35 | #define	IN_CREATE		0x00000100 |
| 36 | #define	IN_DELETE		0x00000200 |
| 37 | #define	IN_DELETE_SELF		0x00000400 |
| 38 | #define	IN_MOVE_SELF		0x00000800 |
| 39 | #define	IN_ALL_EVENTS		0x00000fff |
| 40 | |
| 41 | /* Events report only for entries in a watched dir, not the dir itself. */ |
| 42 | #define	_IN_DIR_EVENTS		(IN_CLOSE_WRITE | IN_DELETE | IN_MODIFY | \ |
| 43 | 				 IN_MOVED_FROM | IN_MOVED_TO) |
| 44 | |
| 45 | #ifdef _KERNEL |
| 46 | /* |
| 47 | * An unlink that's done as part of a rename only records IN_DELETE if the |
| 48 | * unlinked vnode itself is watched, and not when the containing directory is |
| 49 | * watched. |
| 50 | */ |
| 51 | #define	_IN_MOVE_DELETE		0x40000000 |
| 52 | /* |
| 53 | * Inode link count changes only trigger IN_ATTRIB events if the inode itself is |
| 54 | * watched, and not when the containing directory is watched. |
| 55 | */ |
| 56 | #define	_IN_ATTRIB_LINKCOUNT	0x80000000 |
| 57 | #endif |
| 58 | |
| 59 | /* Flags, set in the mask field. */ |
| 60 | #define	IN_ONLYDIR		0x01000000 |
| 61 | #define	IN_DONT_FOLLOW		0x02000000 |
| 62 | #define	IN_EXCL_UNLINK		0x04000000 |
| 63 | #define	IN_MASK_CREATE		0x10000000 |
| 64 | #define	IN_MASK_ADD		0x20000000 |
| 65 | #define	IN_ONESHOT		0x80000000 |
| 66 | #define	_IN_ALL_FLAGS		(IN_ONLYDIR | IN_DONT_FOLLOW |		\ |
| 67 | 				 IN_EXCL_UNLINK | IN_MASK_CREATE |	\ |
| 68 | 				 IN_MASK_ADD | IN_ONESHOT) |
| 69 | |
| 70 | /* Flags returned by the kernel. */ |
| 71 | #define	IN_UNMOUNT		0x00002000 |
| 72 | #define	IN_Q_OVERFLOW		0x00004000 |
| 73 | #define	IN_IGNORED		0x00008000 |
| 74 | #define	IN_ISDIR		0x40000000 |
| 75 | #define	_IN_ALL_RETFLAGS	(IN_Q_OVERFLOW | IN_UNMOUNT | IN_IGNORED | \ |
| 76 | 				 IN_ISDIR) |
| 77 | |
| 78 | #define	_IN_ALIGN		_Alignof(struct inotify_event) |
| 79 | #define	_IN_NAMESIZE(namelen)	\ |
| 80 | 	((namelen) == 0 ? 0 : __align_up((namelen) + 1, _IN_ALIGN)) |
| 81 | |
| 82 | #ifdef _KERNEL |
| 83 | struct componentname; |
| 84 | struct file; |
| 85 | struct inotify_softc; |
| 86 | struct thread; |
| 87 | struct vnode; |
| 88 | |
| 89 | int	inotify_create_file(struct thread *, struct file *, int, int *); |
| 90 | void	inotify_log(struct vnode *, const char *, size_t, int, __uint32_t); |
| 91 | |
| 92 | int	kern_inotify_rm_watch(int, uint32_t, struct thread *); |
| 93 | int	kern_inotify_add_watch(int, int, const char *, uint32_t, |
| 94 | 	 struct thread *); |
| 95 | |
| 96 | void	vn_inotify(struct vnode *, struct vnode *, struct componentname *, int, |
| 97 | 	 uint32_t); |
| 98 | int	vn_inotify_add_watch(struct vnode *, struct inotify_softc *, |
| 99 | 	 __uint32_t, __uint32_t *, struct thread *); |
| 100 | void	vn_inotify_revoke(struct vnode *); |
| 101 | |
| 102 | /* Log an inotify event. */ |
| 103 | #define	INOTIFY(vp, ev) do {						\ |
| 104 | 	if (__predict_false((vn_irflag_read(vp) & (VIRF_INOTIFY |	\ |
| 105 | 	 VIRF_INOTIFY_PARENT)) != 0))				\ |
| 106 | 		VOP_INOTIFY((vp), NULL, NULL, (ev), 0);			\ |
| 107 | } while (0) |
| 108 | |
| 109 | /* Log an inotify event using a specific name for the vnode. */ |
| 110 | #define	INOTIFY_NAME_LOCK(vp, dvp, cnp, ev, lock) do {			\ |
| 111 | 	if (__predict_false((vn_irflag_read(vp) & VIRF_INOTIFY) != 0 ||	\ |
| 112 | 	 (vn_irflag_read(dvp) & VIRF_INOTIFY) != 0)) {		\ |
| 113 | 		if (lock)						\ |
| 114 | 			vn_lock((vp), LK_SHARED | LK_RETRY);		\ |
| 115 | 		VOP_INOTIFY((vp), (dvp), (cnp), (ev), 0);		\ |
| 116 | 		if (lock)						\ |
| 117 | 			VOP_UNLOCK(vp);					\ |
| 118 | 	}								\ |
| 119 | } while (0) |
| 120 | #define	INOTIFY_NAME(vp, dvp, cnp, ev)					\ |
| 121 | 	INOTIFY_NAME_LOCK((vp), (dvp), (cnp), (ev), false) |
| 122 | |
| 123 | extern __uint32_t inotify_rename_cookie; |
| 124 | |
| 125 | #define	INOTIFY_MOVE(vp, fdvp, fcnp, tvp, tdvp, tcnp) do {		\ |
| 126 | 	if (__predict_false((vn_irflag_read(fdvp) & VIRF_INOTIFY) != 0 || \ |
| 127 | 	 (vn_irflag_read(tdvp) & VIRF_INOTIFY) != 0 ||		\ |
| 128 | 	 (vn_irflag_read(vp) & VIRF_INOTIFY) != 0)) {		\ |
| 129 | 		__uint32_t cookie;					\ |
| 130 | 									\ |
| 131 | 		cookie = atomic_fetchadd_32(&inotify_rename_cookie, 1);	\ |
| 132 | 		VOP_INOTIFY((vp), (fdvp), (fcnp), IN_MOVED_FROM, cookie); \ |
| 133 | 		VOP_INOTIFY((vp), (tdvp), (tcnp), IN_MOVED_TO, cookie);	\ |
| 134 | 	}								\ |
| 135 | 	if ((tvp) != NULL)						\ |
| 136 | 		INOTIFY_NAME_LOCK((tvp), (tdvp), (tcnp),		\ |
| 137 | 		 _IN_MOVE_DELETE, true);				\ |
| 138 | } while (0) |
| 139 | |
| 140 | #define	INOTIFY_REVOKE(vp) do {						\ |
| 141 | 	if (__predict_false((vn_irflag_read(vp) & VIRF_INOTIFY) != 0))	\ |
| 142 | 		vn_inotify_revoke((vp));				\ |
| 143 | } while (0) |
| 144 | |
| 145 | #else |
| 146 | #include <sys/cdefs.h> |
| 147 | |
| 148 | __BEGIN_DECLS |
| 149 | int	inotify_init(void); |
| 150 | int	inotify_init1(int flags); |
| 151 | int	inotify_add_watch(int fd, const char *pathname, __uint32_t mask); |
| 152 | int	inotify_add_watch_at(int fd, int dfd, const char *pathname, |
| 153 | 	 __uint32_t mask); |
| 154 | int	inotify_rm_watch(int fd, int wd); |
| 155 | __END_DECLS |
| 156 | #endif /* !_KERNEL */ |
| 157 | |
| 158 | #endif /* !_INOTIFY_H_ */ |