vfs: Initial revision of inotify

Add an implementation of inotify_init(), inotify_add_watch(),
inotify_rm_watch(), source-compatible with Linux.  This provides
functionality similar to kevent(2)'s EVFILT_VNODE, i.e., it lets
applications monitor filesystem files for accesses.  Compared to
inotify, however, EVFILT_VNODE has the limitation of requiring the
application to open the file to be monitored.  This means that activity
on a newly created file cannot be monitored reliably, and that a file
descriptor per file in the hierarchy is required.

inotify on the other hand allows a directory and its entries to be
monitored at once.  It introduces a new file descriptor type to which
"watches" can be attached; a watch is a pseudo-file descriptor
associated with a file or directory and a set of events to watch for.
When a watched vnode is accessed, a description of the event is queued
to the inotify descriptor, readable with read(2).  Events for files in a
watched directory include the file name.

A watched vnode has its usecount bumped, so name cache entries
originating from a watched directory are not evicted.  Name cache
entries are used to populate inotify events for files with a link in a
watched directory.  In particular, if a file is accessed with, say,
read(2), an IN_ACCESS event will be generated for any watched hard link
of the file.

The inotify_add_watch_at() variant is included so that this
functionality is available in capability mode; plain inotify_add_watch()
is disallowed in capability mode.

When a file in a nullfs mount is watched, the watch is attached to the
lower vnode, such that accesses via either layer generate inotify
events.

Many thanks to Gleb Popov for testing this patch and finding lots of
bugs.

PR:		258010, 215011
Reviewed by:	kib
Tested by:	arrowd
MFC after:	3 months
Sponsored by:	Klara, Inc.
Differential Revision:	https://reviews.freebsd.org/D50315
This commit is contained in:
Mark Johnston
2025-07-03 20:07:45 +00:00
parent 4695e3aa7c
commit f1f230439f
25 changed files with 1405 additions and 15 deletions
+9 -1
View File
@@ -30,7 +30,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd May 1, 2024
.Dd May 22, 2025
.Dt RIGHTS 4
.Os
.Sh NAME
@@ -319,6 +319,14 @@ Permit
.It Dv CAP_GETSOCKOPT
Permit
.Xr getsockopt 2 .
.It Dv CAP_INOTIFY_ADD
Permit
.Xr inotify_add_watch 2
and
.Xr inotify_add_watch_at 2 .
.It Dv CAP_INOTIFY_RM
Permit
.Xr inotify_rm_watch 2 .
.It Dv CAP_IOCTL
Permit
.Xr ioctl 2 .
+1
View File
@@ -663,6 +663,7 @@
#define AUE_FSPACECTL 43269 /* FreeBSD-specific. */
#define AUE_TIMERFD 43270 /* FreeBSD/Linux. */
#define AUE_SETCRED 43271 /* FreeBSD-specific. */
#define AUE_INOTIFY 43272 /* FreeBSD/Linux. */
/*
* Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the
+1
View File
@@ -3992,6 +3992,7 @@ kern/vfs_export.c standard
kern/vfs_extattr.c standard
kern/vfs_hash.c standard
kern/vfs_init.c standard
kern/vfs_inotify.c standard
kern/vfs_lookup.c standard
kern/vfs_mount.c standard
kern/vfs_mountroot.c standard
+4
View File
@@ -245,6 +245,10 @@ null_nodeget(struct mount *mp, struct vnode *lowervp, struct vnode **vpp)
vp->v_object = lowervp->v_object;
vn_irflag_set(vp, VIRF_PGREAD);
}
if ((vn_irflag_read(lowervp) & VIRF_INOTIFY) != 0)
vn_irflag_set(vp, VIRF_INOTIFY);
if ((vn_irflag_read(lowervp) & VIRF_INOTIFY_PARENT) != 0)
vn_irflag_set(vp, VIRF_INOTIFY_PARENT);
if (lowervp == MOUNTTONULLMOUNT(mp)->nullm_lowerrootvp)
vp->v_vflag |= VV_ROOT;
+28 -1
View File
@@ -189,6 +189,26 @@ static int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
SYSCTL_INT(_debug, OID_AUTO, nullfs_bug_bypass, CTLFLAG_RW,
&null_bug_bypass, 0, "");
/*
* Synchronize inotify flags with the lower vnode:
* - If the upper vnode has the flag set and the lower does not, then the lower
* vnode is unwatched and the upper vnode does not need to go through
* VOP_INOTIFY.
* - If the lower vnode is watched, then the upper vnode should go through
* VOP_INOTIFY, so copy the flag up.
*/
static void
null_copy_inotify(struct vnode *vp, struct vnode *lvp, short flag)
{
if ((vn_irflag_read(vp) & flag) != 0) {
if (__predict_false((vn_irflag_read(lvp) & flag) == 0))
vn_irflag_unset(vp, flag);
} else if ((vn_irflag_read(lvp) & flag) != 0) {
if (__predict_false((vn_irflag_read(vp) & flag) == 0))
vn_irflag_set(vp, flag);
}
}
/*
* This is the 10-Apr-92 bypass routine.
* This version has been optimized for speed, throwing away some
@@ -305,7 +325,10 @@ null_bypass(struct vop_generic_args *ap)
lvp = *(vps_p[i]);
/*
* Get rid of the transient hold on lvp.
* Get rid of the transient hold on lvp. Copy inotify
* flags up in case something is watching the lower
* layer.
*
* If lowervp was unlocked during VOP
* operation, nullfs upper vnode could have
* been reclaimed, which changes its v_vnlock
@@ -314,6 +337,10 @@ null_bypass(struct vop_generic_args *ap)
* upper (reclaimed) vnode.
*/
if (lvp != NULLVP) {
null_copy_inotify(old_vps[i], lvp,
VIRF_INOTIFY);
null_copy_inotify(old_vps[i], lvp,
VIRF_INOTIFY_PARENT);
if (VOP_ISLOCKED(lvp) == LK_EXCLUSIVE &&
old_vps[i]->v_vnlock != lvp->v_vnlock) {
VOP_UNLOCK(lvp);
+21
View File
@@ -1637,6 +1637,12 @@ uifree(struct uidinfo *uip)
if (uip->ui_pipecnt != 0)
printf("freeing uidinfo: uid = %d, pipecnt = %ld\n",
uip->ui_uid, uip->ui_pipecnt);
if (uip->ui_inotifycnt != 0)
printf("freeing uidinfo: uid = %d, inotifycnt = %ld\n",
uip->ui_uid, uip->ui_inotifycnt);
if (uip->ui_inotifywatchcnt != 0)
printf("freeing uidinfo: uid = %d, inotifywatchcnt = %ld\n",
uip->ui_uid, uip->ui_inotifywatchcnt);
free(uip, M_UIDINFO);
}
@@ -1742,6 +1748,21 @@ chgpipecnt(struct uidinfo *uip, int diff, rlim_t max)
return (chglimit(uip, &uip->ui_pipecnt, diff, max, "pipecnt"));
}
int
chginotifycnt(struct uidinfo *uip, int diff, rlim_t max)
{
return (chglimit(uip, &uip->ui_inotifycnt, diff, max, "inotifycnt"));
}
int
chginotifywatchcnt(struct uidinfo *uip, int diff, rlim_t max)
{
return (chglimit(uip, &uip->ui_inotifywatchcnt, diff, max,
"inotifywatchcnt"));
}
static int
sysctl_kern_proc_rlimit_usage(SYSCTL_HANDLER_ARGS)
{
+4
View File
@@ -74,6 +74,10 @@ const cap_rights_t cap_getsockopt_rights =
CAP_RIGHTS_INITIALIZER(CAP_GETSOCKOPT);
const cap_rights_t cap_getsockname_rights =
CAP_RIGHTS_INITIALIZER(CAP_GETSOCKNAME);
const cap_rights_t cap_inotify_add_rights =
CAP_RIGHTS_INITIALIZER(CAP_INOTIFY_ADD);
const cap_rights_t cap_inotify_rm_rights =
CAP_RIGHTS_INITIALIZER(CAP_INOTIFY_RM);
const cap_rights_t cap_ioctl_rights = CAP_RIGHTS_INITIALIZER(CAP_IOCTL);
const cap_rights_t cap_listen_rights = CAP_RIGHTS_INITIALIZER(CAP_LISTEN);
const cap_rights_t cap_linkat_source_rights =
+30 -5
View File
@@ -42,11 +42,12 @@
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/capsicum.h>
#include <sys/exterrvar.h>
#include <sys/filedesc.h>
#include <sys/filio.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/exterrvar.h>
#include <sys/inotify.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
@@ -939,7 +940,6 @@ int
kern_specialfd(struct thread *td, int type, void *arg)
{
struct file *fp;
struct specialfd_eventfd *ae;
int error, fd, fflags;
fflags = 0;
@@ -948,12 +948,22 @@ kern_specialfd(struct thread *td, int type, void *arg)
return (error);
switch (type) {
case SPECIALFD_EVENTFD:
case SPECIALFD_EVENTFD: {
struct specialfd_eventfd *ae;
ae = arg;
if ((ae->flags & EFD_CLOEXEC) != 0)
fflags |= O_CLOEXEC;
error = eventfd_create_file(td, fp, ae->initval, ae->flags);
break;
}
case SPECIALFD_INOTIFY: {
struct specialfd_inotify *si;
si = arg;
error = inotify_create_file(td, fp, si->flags, &fflags);
break;
}
default:
error = EINVAL;
break;
@@ -970,11 +980,12 @@ kern_specialfd(struct thread *td, int type, void *arg)
int
sys___specialfd(struct thread *td, struct __specialfd_args *args)
{
struct specialfd_eventfd ae;
int error;
switch (args->type) {
case SPECIALFD_EVENTFD:
case SPECIALFD_EVENTFD: {
struct specialfd_eventfd ae;
if (args->len != sizeof(struct specialfd_eventfd)) {
error = EINVAL;
break;
@@ -989,6 +1000,20 @@ sys___specialfd(struct thread *td, struct __specialfd_args *args)
}
error = kern_specialfd(td, args->type, &ae);
break;
}
case SPECIALFD_INOTIFY: {
struct specialfd_inotify si;
if (args->len != sizeof(si)) {
error = EINVAL;
break;
}
error = copyin(args->req, &si, sizeof(si));
if (error != 0)
break;
error = kern_specialfd(td, args->type, &si);
break;
}
default:
error = EINVAL;
break;
+15
View File
@@ -3356,4 +3356,19 @@
_In_reads_bytes_(4) void *ptr
);
}
593 AUE_INOTIFY STD|CAPENABLED {
int inotify_add_watch_at(
int fd,
int dfd,
_In_z_ const char *path,
uint32_t mask
);
}
594 AUE_INOTIFY STD|CAPENABLED {
int inotify_rm_watch(
int fd,
int wd
);
}
; vim: syntax=off
+59
View File
@@ -41,6 +41,7 @@
#include <sys/counter.h>
#include <sys/filedesc.h>
#include <sys/fnv_hash.h>
#include <sys/inotify.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/lock.h>
@@ -2628,6 +2629,14 @@ cache_enter_time(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
atomic_thread_fence_rel();
atomic_store_ptr(&dvp->v_cache_dd, ncp);
} else if (vp != NULL) {
/*
* Take the slow path in INOTIFY(). This flag will be lazily
* cleared by cache_vop_inotify() once all directories referring
* to vp are unwatched.
*/
if (__predict_false((vn_irflag_read(dvp) & VIRF_INOTIFY) != 0))
vn_irflag_set_cond(vp, VIRF_INOTIFY_PARENT);
/*
* For this case, the cache entry maps both the
* directory name in it and the name ".." for the
@@ -4008,6 +4017,56 @@ vn_path_to_global_path_hardlink(struct thread *td, struct vnode *vp,
return (error);
}
void
cache_vop_inotify(struct vnode *vp, int event, uint32_t cookie)
{
struct mtx *vlp;
struct namecache *ncp;
int isdir;
bool logged, self;
isdir = vp->v_type == VDIR ? IN_ISDIR : 0;
self = (vn_irflag_read(vp) & VIRF_INOTIFY) != 0 &&
(vp->v_type != VDIR || (event & ~_IN_DIR_EVENTS) != 0);
if (self) {
int selfevent;
if (event == _IN_ATTRIB_LINKCOUNT)
selfevent = IN_ATTRIB;
else
selfevent = event;
inotify_log(vp, NULL, 0, selfevent | isdir, cookie);
}
if ((event & IN_ALL_EVENTS) == 0)
return;
logged = false;
vlp = VP2VNODELOCK(vp);
mtx_lock(vlp);
TAILQ_FOREACH(ncp, &vp->v_cache_dst, nc_dst) {
if ((ncp->nc_flag & NCF_ISDOTDOT) != 0)
continue;
if ((vn_irflag_read(ncp->nc_dvp) & VIRF_INOTIFY) != 0) {
/*
* XXX-MJ if the vnode has two links in the same
* dir, we'll log the same event twice.
*/
inotify_log(ncp->nc_dvp, ncp->nc_name, ncp->nc_nlen,
event | isdir, cookie);
logged = true;
}
}
if (!logged && (vn_irflag_read(vp) & VIRF_INOTIFY_PARENT) != 0) {
/*
* We didn't find a watched directory that contains this vnode,
* so stop calling VOP_INOTIFY for operations on the vnode.
*/
vn_irflag_unset(vp, VIRF_INOTIFY_PARENT);
}
mtx_unlock(vlp);
}
#ifdef DDB
static void
db_print_vpath(struct vnode *vp)
+17
View File
@@ -39,6 +39,7 @@
#include <sys/conf.h>
#include <sys/event.h>
#include <sys/filio.h>
#include <sys/inotify.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
@@ -119,6 +120,8 @@ struct vop_vector default_vnodeops = {
.vop_getwritemount = vop_stdgetwritemount,
.vop_inactive = VOP_NULL,
.vop_need_inactive = vop_stdneed_inactive,
.vop_inotify = vop_stdinotify,
.vop_inotify_add_watch = vop_stdinotify_add_watch,
.vop_ioctl = vop_stdioctl,
.vop_kqfilter = vop_stdkqfilter,
.vop_islocked = vop_stdislocked,
@@ -1305,6 +1308,20 @@ vop_stdneed_inactive(struct vop_need_inactive_args *ap)
return (1);
}
int
vop_stdinotify(struct vop_inotify_args *ap)
{
vn_inotify(ap->a_vp, ap->a_dvp, ap->a_cnp, ap->a_event, ap->a_cookie);
return (0);
}
int
vop_stdinotify_add_watch(struct vop_inotify_add_watch_args *ap)
{
return (vn_inotify_add_watch(ap->a_vp, ap->a_sc, ap->a_mask,
ap->a_wdp, ap->a_td));
}
int
vop_stdioctl(struct vop_ioctl_args *ap)
{
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -38,7 +38,6 @@
* External virtual filesystem routines
*/
#include <sys/cdefs.h>
#include "opt_ddb.h"
#include "opt_watchdog.h"
@@ -5246,7 +5245,8 @@ destroy_vpollinfo_free(struct vpollinfo *vi)
static void
destroy_vpollinfo(struct vpollinfo *vi)
{
KASSERT(TAILQ_EMPTY(&vi->vpi_inotify),
("%s: pollinfo %p has lingering watches", __func__, vi));
knlist_clear(&vi->vpi_selinfo.si_note, 1);
seldrain(&vi->vpi_selinfo);
destroy_vpollinfo_free(vi);
@@ -5260,12 +5260,13 @@ v_addpollinfo(struct vnode *vp)
{
struct vpollinfo *vi;
if (vp->v_pollinfo != NULL)
if (atomic_load_ptr(&vp->v_pollinfo) != NULL)
return;
vi = malloc(sizeof(*vi), M_VNODEPOLL, M_WAITOK | M_ZERO);
mtx_init(&vi->vpi_lock, "vnode pollinfo", NULL, MTX_DEF);
knlist_init(&vi->vpi_selinfo.si_note, vp, vfs_knllock,
vfs_knlunlock, vfs_knl_assert_lock);
TAILQ_INIT(&vi->vpi_inotify);
VI_LOCK(vp);
if (vp->v_pollinfo != NULL) {
VI_UNLOCK(vp);
+2 -1
View File
@@ -308,7 +308,8 @@ vn_open_cred(struct nameidata *ndp, int *flagp, int cmode, u_int vn_open_flags,
NDREINIT(ndp);
goto restart;
}
if ((vn_open_flags & VN_OPEN_NAMECACHE) != 0)
if ((vn_open_flags & VN_OPEN_NAMECACHE) != 0 ||
(vn_irflag_read(ndp->ni_dvp) & VIRF_INOTIFY) != 0)
ndp->ni_cnd.cn_flags |= MAKEENTRY;
#ifdef MAC
error = mac_vnode_check_create(cred, ndp->ni_dvp,
+21
View File
@@ -821,6 +821,27 @@ vop_deallocate {
};
%% inotify vp - - -
vop_inotify {
IN struct vnode *vp;
IN struct vnode *dvp;
IN struct componentname *cnp;
IN int event;
IN uint32_t cookie;
};
%% inotify_add_watch vp L L L
vop_inotify_add_watch {
IN struct vnode *vp;
IN struct inotify_softc *sc;
IN uint32_t mask;
OUT uint32_t *wdp;
IN struct thread *td;
};
# The VOPs below are spares at the end of the table to allow new VOPs to be
# added in stable branches without breaking the KBI. New VOPs in HEAD should
# be added above these spares. When merging a new VOP to a stable branch,
+2
View File
@@ -79,6 +79,8 @@ extern const cap_rights_t cap_futimes_rights;
extern const cap_rights_t cap_getpeername_rights;
extern const cap_rights_t cap_getsockopt_rights;
extern const cap_rights_t cap_getsockname_rights;
extern const cap_rights_t cap_inotify_add_rights;
extern const cap_rights_t cap_inotify_rm_rights;
extern const cap_rights_t cap_ioctl_rights;
extern const cap_rights_t cap_linkat_source_rights;
extern const cap_rights_t cap_linkat_target_rights;
+6 -2
View File
@@ -279,11 +279,15 @@
#define CAP_KQUEUE (CAP_KQUEUE_EVENT | CAP_KQUEUE_CHANGE)
/* Allows operations on inotify descriptors. */
#define CAP_INOTIFY_ADD CAPRIGHT(1, 0x0000000000200000ULL)
#define CAP_INOTIFY_RM CAPRIGHT(1, 0x0000000000400000ULL)
/* All used bits for index 1. */
#define CAP_ALL1 CAPRIGHT(1, 0x00000000001FFFFFULL)
#define CAP_ALL1 CAPRIGHT(1, 0x00000000007FFFFFULL)
/* Available bits for index 1. */
#define CAP_UNUSED1_22 CAPRIGHT(1, 0x0000000000200000ULL)
#define CAP_UNUSED1_22 CAPRIGHT(1, 0x0000000000800000ULL)
/* ... */
#define CAP_UNUSED1_57 CAPRIGHT(1, 0x0100000000000000ULL)
+1
View File
@@ -16,6 +16,7 @@
#define EXTERR_KTRACE 3 /* To allow inclusion of this
file into kern_ktrace.c */
#define EXTERR_CAT_FUSE 4
#define EXTERR_CAT_INOTIFY 5
#endif
+1
View File
@@ -71,6 +71,7 @@ struct nameidata;
#define DTYPE_PROCDESC 12 /* process descriptor */
#define DTYPE_EVENTFD 13 /* eventfd */
#define DTYPE_TIMERFD 14 /* timerfd */
#define DTYPE_INOTIFY 15 /* inotify descriptor */
#ifdef _KERNEL
+146
View File
@@ -0,0 +1,146 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Klara, Inc.
*/
#ifndef _INOTIFY_H_
#define _INOTIFY_H_
#include <sys/_types.h>
/* 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);
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(vp, dvp, cnp, ev) do { \
if (__predict_false((vn_irflag_read(vp) & VIRF_INOTIFY) != 0 || \
(vn_irflag_read(dvp) & VIRF_INOTIFY) != 0)) \
VOP_INOTIFY((vp), (dvp), (cnp), (ev), 0); \
} while (0)
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((tvp), (tdvp), (tcnp), _IN_MOVE_DELETE); \
} 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 <sys/cdefs.h>
__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_ */
+4
View File
@@ -122,6 +122,8 @@ struct uidinfo {
long ui_kqcnt; /* (b) number of kqueues */
long ui_umtxcnt; /* (b) number of shared umtxs */
long ui_pipecnt; /* (b) consumption of pipe buffers */
long ui_inotifycnt; /* (b) number of inotify descriptors */
long ui_inotifywatchcnt; /* (b) number of inotify watches */
uid_t ui_uid; /* (a) uid */
u_int ui_ref; /* (b) reference count */
#ifdef RACCT
@@ -144,6 +146,8 @@ int chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to,
int chgptscnt(struct uidinfo *uip, int diff, rlim_t maxval);
int chgumtxcnt(struct uidinfo *uip, int diff, rlim_t maxval);
int chgpipecnt(struct uidinfo *uip, int diff, rlim_t max);
int chginotifycnt(struct uidinfo *uip, int diff, rlim_t maxval);
int chginotifywatchcnt(struct uidinfo *uip, int diff, rlim_t maxval);
int kern_proc_setrlimit(struct thread *td, struct proc *p, u_int which,
struct rlimit *limp);
struct plimit
+5
View File
@@ -30,6 +30,7 @@
enum specialfd_type {
SPECIALFD_EVENTFD = 1,
SPECIALFD_INOTIFY = 2,
};
struct specialfd_eventfd {
@@ -37,4 +38,8 @@ struct specialfd_eventfd {
int flags;
};
struct specialfd_inotify {
int flags;
};
#endif /* !_SYS_SPECIALFD_H_ */
+5
View File
@@ -265,6 +265,7 @@ struct user {
#define KF_TYPE_DEV 12
#define KF_TYPE_EVENTFD 13
#define KF_TYPE_TIMERFD 14
#define KF_TYPE_INOTIFY 15
#define KF_TYPE_UNKNOWN 255
#define KF_VTYPE_VNON 0
@@ -456,6 +457,10 @@ struct kinfo_file {
int32_t kf_kqueue_count;
int32_t kf_kqueue_state;
} kf_kqueue;
struct {
uint64_t kf_inotify_npending;
uint64_t kf_inotify_nbpending;
} kf_inotify;
} kf_un;
};
uint16_t kf_status; /* Status flags. */
+10 -2
View File
@@ -86,11 +86,13 @@ enum vgetstate {
* it from v_data. If non-null, this area is freed in getnewvnode().
*/
struct namecache;
struct cache_fpl;
struct inotify_watch;
struct namecache;
struct vpollinfo {
struct mtx vpi_lock; /* lock to protect below */
TAILQ_HEAD(, inotify_watch) vpi_inotify; /* list of inotify watchers */
struct selinfo vpi_selinfo; /* identity of poller(s) */
short vpi_events; /* what they are looking for */
short vpi_revents; /* what has happened */
@@ -248,6 +250,9 @@ _Static_assert(sizeof(struct vnode) <= 448, "vnode size crosses 448 bytes");
#define VIRF_CROSSMP 0x0010 /* Cross-mp vnode, no locking */
#define VIRF_NAMEDDIR 0x0020 /* Named attribute directory */
#define VIRF_NAMEDATTR 0x0040 /* Named attribute */
#define VIRF_INOTIFY 0x0080 /* This vnode is being watched */
#define VIRF_INOTIFY_PARENT 0x0100 /* A parent of this vnode may be being
watched */
#define VI_UNUSED0 0x0001 /* unused */
#define VI_MOUNT 0x0002 /* Mount in progress */
@@ -667,6 +672,7 @@ char *cache_symlink_alloc(size_t size, int flags);
void cache_symlink_free(char *string, size_t size);
int cache_symlink_resolve(struct cache_fpl *fpl, const char *string,
size_t len);
void cache_vop_inotify(struct vnode *vp, int event, uint32_t cookie);
void cache_vop_rename(struct vnode *fdvp, struct vnode *fvp, struct vnode *tdvp,
struct vnode *tvp, struct componentname *fcnp, struct componentname *tcnp);
void cache_vop_rmdir(struct vnode *dvp, struct vnode *vp);
@@ -869,8 +875,10 @@ int vop_stdfsync(struct vop_fsync_args *);
int vop_stdgetwritemount(struct vop_getwritemount_args *);
int vop_stdgetpages(struct vop_getpages_args *);
int vop_stdinactive(struct vop_inactive_args *);
int vop_stdioctl(struct vop_ioctl_args *);
int vop_stdneed_inactive(struct vop_need_inactive_args *);
int vop_stdinotify(struct vop_inotify_args *);
int vop_stdinotify_add_watch(struct vop_inotify_add_watch_args *);
int vop_stdioctl(struct vop_ioctl_args *);
int vop_stdkqfilter(struct vop_kqfilter_args *);
int vop_stdlock(struct vop_lock1_args *);
int vop_stdunlock(struct vop_unlock_args *);
+1
View File
@@ -193,6 +193,7 @@ if (cfile) {
printc(common_head \
"#include <sys/param.h>\n" \
"#include <sys/event.h>\n" \
"#include <sys/inotify.h>\n" \
"#include <sys/kernel.h>\n" \
"#include <sys/mount.h>\n" \
"#include <sys/sdt.h>\n" \