4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
31 * File Events Notification
32 * ------------------------
34 * The File Events Notification facility provides file and directory change
35 * notification. It is implemented as an event source(PORT_SOURCE_FILE)
36 * under the Event Ports framework. Therefore the API is an extension to
37 * the Event Ports API.
39 * It uses the FEM (File Events Monitoring) framework to intercept
40 * operations on the files & directories and generate appropriate events.
42 * It provides event notification in accordance with what an application
43 * can find out by stat`ing the file and comparing time stamps. The various
44 * system calls that update the file's access, modification, and change
45 * time stamps are documented in the man page section 2.
47 * It is non intrusive. That is, having an active file event watch on a file
48 * or directory will not prevent it from being removed or renamed or block an
49 * unmount operation of the file system where the watched file or directory
56 * The object for this event source is of type 'struct file_obj *'
58 * The file that needs to be monitored is specified in 'fo_name'.
59 * The time stamps collected by a stat(2) call are passed in fo_atime,
60 * fo_mtime, fo_ctime. At the time a file events watch is registered, the
61 * time stamps passed in are compared with the current time stamps of the
62 * file. If it has changed, relevant events are sent immediately. If the time
63 * stamps are all '0', they will not be compared.
66 * The events are delivered to an event port. A port is created using
69 * To register a file events watch on a file or directory.
71 * port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
73 * 'user' is the user pointer to be returned with the event.
75 * To de-register a file events watch,
77 * port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
79 * The events are collected using the port_get()/port_getn() interface. The
80 * event source will be PORT_SOURCE_FILE.
82 * After an event is delivered, the file events watch gets de-activated. To
83 * receive the next event, the process will have to re-register the watch and
84 * activate it by calling port_associate() again. This behavior is intentional
85 * and supports proper multi threaded programming when using file events
89 * Implementation overview:
90 * ------------------------
92 * Each file events watch is represented by 'portfop_t' in the kernel. A
93 * cache(in portfop_cache_t) of these portfop_t's are maintained per event
94 * port by this source. The object here is the pointer to the file_obj
95 * structure. The portfop_t's are hashed in using the object pointer. Therefore
96 * it is possible to have multiple file events watches on a file by the same
97 * process by using different object structure(file_obj_t) and hence can
98 * receive multiple event notification for a file. These watches can be for
99 * different event types.
101 * The cached entries of these file objects are retained, even after delivering
102 * an event, marking them inactive for performance reasons. The assumption
103 * is that the process would come back and re-register the file to receive
104 * further events. When there are more then 'port_fop_maxpfps' watches per file
105 * it will attempt to free the oldest inactive watches.
107 * In case the event that is being delivered is an exception event, the cached
108 * entries get removed. An exception event on a file or directory means its
109 * identity got changed(rename to/from, delete, mounted over, file system
112 * If the event port gets closed, all the associated file event watches will be
113 * removed and discarded.
119 * The list of file event watches per file are managed by the data structure
120 * portfop_vp_t. The first time a file events watch is registered for a file,
121 * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
122 * removed and freed only when the vnode becomes inactive. The FEM hooks are
123 * also installed when the first watch is registered on a file. The FEM hooks
124 * get un-installed when all the watches are removed.
126 * Each file events watch is represented by the structure portfop_t. They
127 * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
128 * delivering an event, the portfop_t is marked inactive but retained. It is
129 * moved to the end of the list. All the active portfop_t's are maintained at
130 * the beginning. In case of exception events, the portfop_t will be removed
133 * To intercept unmount operations, FSEM hooks are added to the file system
134 * under which files are being watched. A hash table('portfop_vfs_hash_t') of
135 * active file systems is maintained. Each file system that has active watches
136 * is represented by 'portfop_vfs_t' and is added to the hash table.
137 * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
138 * being watched on the portfop_vfs_t structure.
141 * File system support:
142 * -------------------
144 * The file system implementation has to provide vnode event notifications
145 * (vnevents) in order to support watching any files on that file system.
146 * The vnode events(vnevents) are notifications provided by the file system
147 * for name based file operations like rename, remove etc, which do not go
148 * thru the VOP_** interfaces. If the file system does not implement vnode
149 * notifications, watching for file events on such file systems is not
150 * supported. The vnode event notifications support is determined by the call
151 * vnevent_support(vp) (fop_vnevent(vp, VE_SUPPORT)), which the file system
158 * A file(vnode) can have file event watches registered by different processes.
159 * There is one portfop_t per watch registered. These are on the vnode's list
160 * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
161 * also on the per port cache. The cache is protected by the pfc_lock of
162 * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
166 #include <sys/types.h>
167 #include <sys/systm.h>
168 #include <sys/stat.h>
169 #include <sys/errno.h>
170 #include <sys/kmem.h>
171 #include <sys/sysmacros.h>
172 #include <sys/debug.h>
173 #include <sys/vnode.h>
174 #include <sys/poll_impl.h>
175 #include <sys/port_impl.h>
178 #include <sys/atomic.h>
179 #include <sys/mount.h>
180 #include <sys/mntent.h>
183 * For special case support of mnttab (/etc/mnttab).
185 extern struct vnode
*vfs_mntdummyvp
;
186 extern int mntfstype
;
188 #define PORTFOP_PVFSH(vfsp) (&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
189 portfop_vfs_hash_t portvfs_hash
[PORTFOP_PVFSHASH_SZ
];
191 #define PORTFOP_NVP 20
193 * Inactive file event watches(portfop_t) are retained on the vnode's list
194 * for performance reason. If the applications re-registers the file, the
195 * inactive entry is made active and moved up the list.
197 * If there are greater then the following number of watches on a vnode,
198 * it will attempt to discard an oldest inactive watch(pfp) at the time
199 * a new watch is being registered and when events get delivered. We
200 * do this to avoid accumulating inactive watches on a file.
202 int port_fop_maxpfps
= 20;
204 /* local functions */
205 static int port_fop_callback(void *, int *, pid_t
, int, void *);
207 static void port_pcache_insert(portfop_cache_t
*, portfop_t
*);
208 static void port_pcache_delete(portfop_cache_t
*, portfop_t
*);
209 static void port_close_fop(void *arg
, int port
, pid_t pid
, int lastclose
);
212 * port fop functions that will be the fem hooks.
214 static int port_fop_open(femarg_t
*vf
, int mode
, cred_t
*cr
,
216 static int port_fop_read(femarg_t
*vf
, uio_t
*uiop
, int ioflag
, cred_t
*cr
,
217 struct caller_context
*ct
);
218 static int port_fop_write(femarg_t
*vf
, uio_t
*uiop
, int ioflag
, cred_t
*cr
,
219 caller_context_t
*ct
);
220 static int port_fop_map(femarg_t
*vf
, offset_t off
, struct as
*as
,
221 caddr_t
*addrp
, size_t len
, uchar_t prot
, uchar_t maxport
,
222 uint_t flags
, cred_t
*cr
, caller_context_t
*ct
);
223 static int port_fop_setattr(femarg_t
*vf
, vattr_t
*vap
, int flags
, cred_t
*cr
,
224 caller_context_t
*ct
);
225 static int port_fop_create(femarg_t
*vf
, char *name
, vattr_t
*vap
,
226 vcexcl_t excl
, int mode
, vnode_t
**vpp
, cred_t
*cr
, int flag
,
227 caller_context_t
*ct
, vsecattr_t
*vsecp
);
228 static int port_fop_remove(femarg_t
*vf
, char *nm
, cred_t
*cr
,
229 caller_context_t
*ct
, int flags
);
230 static int port_fop_link(femarg_t
*vf
, vnode_t
*svp
, char *tnm
, cred_t
*cr
,
231 caller_context_t
*ct
, int flags
);
232 static int port_fop_rename(femarg_t
*vf
, char *snm
, vnode_t
*tdvp
, char *tnm
,
233 cred_t
*cr
, caller_context_t
*ct
, int flags
);
234 static int port_fop_mkdir(femarg_t
*vf
, char *dirname
, vattr_t
*vap
,
235 vnode_t
**vpp
, cred_t
*cr
, caller_context_t
*ct
, int flags
,
237 static int port_fop_rmdir(femarg_t
*vf
, char *nm
, vnode_t
*cdir
, cred_t
*cr
,
238 caller_context_t
*ct
, int flags
);
239 static int port_fop_readdir(femarg_t
*vf
, uio_t
*uiop
, cred_t
*cr
, int *eofp
,
240 caller_context_t
*ct
, int flags
);
241 static int port_fop_symlink(femarg_t
*vf
, char *linkname
, vattr_t
*vap
,
242 char *target
, cred_t
*cr
, caller_context_t
*ct
, int flags
);
243 static int port_fop_setsecattr(femarg_t
*vf
, vsecattr_t
*vsap
, int flag
,
244 cred_t
*cr
, caller_context_t
*ct
);
246 static int port_fop_vnevent(femarg_t
*vf
, vnevent_t vnevent
, vnode_t
*dvp
,
247 char *cname
, caller_context_t
*ct
);
249 static int port_fop_unmount(fsemarg_t
*vf
, int flag
, cred_t
*cr
);
255 static fem_t fop_femop
= {
256 .name
= "portfop_fem",
257 .femop_open
= port_fop_open
,
258 .femop_read
= port_fop_read
,
259 .femop_write
= port_fop_write
,
260 .femop_map
= port_fop_map
,
261 .femop_setattr
= port_fop_setattr
,
262 .femop_create
= port_fop_create
,
263 .femop_remove
= port_fop_remove
,
264 .femop_link
= port_fop_link
,
265 .femop_rename
= port_fop_rename
,
266 .femop_mkdir
= port_fop_mkdir
,
267 .femop_rmdir
= port_fop_rmdir
,
268 .femop_readdir
= port_fop_readdir
,
269 .femop_symlink
= port_fop_symlink
,
270 .femop_setsecattr
= port_fop_setsecattr
,
271 .femop_vnevent
= port_fop_vnevent
,
275 * Fsem - vfs ops hooks
278 static fsem_t fop_fsemop
= {
279 .name
= "portfop_fsem",
280 .fsemop_unmount
= port_fop_unmount
,
284 * port_fop_callback()
285 * - PORT_CALLBACK_DEFAULT
286 * The file event will be delivered to the application.
287 * - PORT_CALLBACK_DISSOCIATE
288 * The object will be dissociated from the port.
289 * - PORT_CALLBACK_CLOSE
290 * The object will be dissociated from the port because the port
295 port_fop_callback(void *arg
, int *events
, pid_t pid
, int flag
, void *evp
)
297 portfop_t
*pfp
= (portfop_t
*)arg
;
298 port_kevent_t
*pkevp
= (port_kevent_t
*)evp
;
301 ASSERT((events
!= NULL
));
302 if (flag
== PORT_CALLBACK_DEFAULT
) {
303 if (curproc
->p_pid
!= pid
) {
304 return (EACCES
); /* deny delivery of events */
307 *events
= pkevp
->portkev_events
;
308 pkevp
->portkev_events
= 0;
310 pfp
->pfop_flags
&= ~PORT_FOP_KEV_ONQ
;
317 * Inserts a portfop_t into the port sources cache's.
320 port_pcache_insert(portfop_cache_t
*pfcp
, portfop_t
*pfp
)
324 ASSERT(MUTEX_HELD(&pfcp
->pfc_lock
));
325 bucket
= PORT_FOP_BUCKET(pfcp
, pfp
->pfop_object
);
326 pfp
->pfop_hashnext
= *bucket
;
328 pfcp
->pfc_objcount
++;
332 * Remove the pfp from the port source cache.
335 port_pcache_delete(portfop_cache_t
*pfcp
, portfop_t
*pfp
)
341 bucket
= PORT_FOP_BUCKET(pfcp
, pfp
->pfop_object
);
344 *bucket
= pfp
->pfop_hashnext
;
346 while (cpdp
!= NULL
) {
348 cpdp
= cpdp
->pfop_hashnext
;
350 /* portfop struct found */
351 lpdp
->pfop_hashnext
= pfp
->pfop_hashnext
;
356 pfcp
->pfc_objcount
--;
360 * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
361 * when these routines are called.
363 * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
364 * It is used to discard the oldtest inactive pfp if the number of entries
368 port_fop_listinsert(portfop_vp_t
*pvp
, portfop_t
*pfp
, int where
)
371 list_insert_head(&pvp
->pvp_pfoplist
, (void *)pfp
);
373 list_insert_tail(&pvp
->pvp_pfoplist
, (void *)pfp
);
375 if (pvp
->pvp_lpfop
== NULL
) {
376 pvp
->pvp_lpfop
= pfp
;
382 port_fop_listinsert_head(portfop_vp_t
*pvp
, portfop_t
*pfp
)
384 port_fop_listinsert(pvp
, pfp
, 1);
388 port_fop_listinsert_tail(portfop_vp_t
*pvp
, portfop_t
*pfp
)
391 * We point lpfop to an inactive one, if it was initially pointing
392 * to an active one. Insert to the tail is done only when a pfp goes
395 if (pvp
->pvp_lpfop
&& pvp
->pvp_lpfop
->pfop_flags
& PORT_FOP_ACTIVE
) {
396 pvp
->pvp_lpfop
= pfp
;
398 port_fop_listinsert(pvp
, pfp
, 0);
402 port_fop_listremove(portfop_vp_t
*pvp
, portfop_t
*pfp
)
404 if (pvp
->pvp_lpfop
== pfp
) {
405 pvp
->pvp_lpfop
= list_next(&pvp
->pvp_pfoplist
, (void *)pfp
);
408 list_remove(&pvp
->pvp_pfoplist
, (void *)pfp
);
411 if (pvp
->pvp_cnt
&& pvp
->pvp_lpfop
== NULL
) {
412 pvp
->pvp_lpfop
= list_head(&pvp
->pvp_pfoplist
);
417 port_fop_listmove(portfop_vp_t
*pvp
, list_t
*tlist
)
419 list_move_tail(tlist
, &pvp
->pvp_pfoplist
);
420 pvp
->pvp_lpfop
= NULL
;
425 * Remove a portfop_t from the port cache hash table and discard it.
426 * It is called only when pfp is not on the vnode's list. Otherwise,
427 * port_remove_fop() is called.
430 port_pcache_remove_fop(portfop_cache_t
*pfcp
, portfop_t
*pfp
)
432 port_kevent_t
*pkevp
;
435 ASSERT(MUTEX_HELD(&pfcp
->pfc_lock
));
437 pkevp
= pfp
->pfop_pev
;
438 pfp
->pfop_pev
= NULL
;
441 (void) port_remove_done_event(pkevp
);
442 port_free_event_local(pkevp
, 0);
445 port_pcache_delete(pfcp
, pfp
);
447 if (pfp
->pfop_cname
!= NULL
)
448 kmem_free(pfp
->pfop_cname
, pfp
->pfop_clen
+ 1);
449 kmem_free(pfp
, sizeof (portfop_t
));
450 if (pfcp
->pfc_objcount
== 0)
451 cv_signal(&pfcp
->pfc_lclosecv
);
455 * if we have too many watches on the vnode, attempt to discard an
459 port_fop_trimpfplist(vnode_t
*vp
)
462 portfop_t
*pfp
= NULL
;
463 portfop_cache_t
*pfcp
;
467 * Due to a reference the vnode cannot disappear, v_fopdata should
470 if ((pvp
= vp
->v_fopdata
) != NULL
&&
471 pvp
->pvp_cnt
> port_fop_maxpfps
) {
472 mutex_enter(&pvp
->pvp_mutex
);
473 pfp
= pvp
->pvp_lpfop
;
474 pfcp
= pfp
->pfop_pcache
;
476 * only if we can get the cache lock, we need to
477 * do this due to reverse lock order and some thread
478 * that may be trying to reactivate this entry.
480 if (mutex_tryenter(&pfcp
->pfc_lock
)) {
481 if (pfp
&& !(pfp
->pfop_flags
& PORT_FOP_ACTIVE
) &&
482 !(pfp
->pfop_flags
& PORT_FOP_KEV_ONQ
)) {
483 port_fop_listremove(pvp
, pfp
);
484 pfp
->pfop_flags
|= PORT_FOP_REMOVING
;
486 mutex_exit(&pfcp
->pfc_lock
);
492 mutex_exit(&pvp
->pvp_mutex
);
495 * discard pfp if any.
498 tdvp
= pfp
->pfop_dvp
;
499 port_pcache_remove_fop(pfcp
, pfp
);
500 mutex_exit(&pfcp
->pfc_lock
);
508 * This routine returns 1, if the vnode can be rele'ed by the caller.
509 * The caller has to VN_RELE the vnode with out holding any
513 port_fop_femuninstall(vnode_t
*vp
)
517 portfop_vfs_t
*pvfsp
;
518 portfop_vfs_hash_t
*pvfsh
;
523 * if list is empty, uninstall fem.
526 ASSERT(MUTEX_HELD(&pvp
->pvp_mutex
));
529 * make sure the list is empty.
531 if (!list_head(&pvp
->pvp_pfoplist
)) {
534 * we could possibly uninstall the fem hooks when
535 * the vnode becomes inactive and the v_fopdata is
536 * free. But the hooks get triggered unnecessarily
537 * even though there are no active watches. So, we
540 (void) fem_uninstall(vp
, (fem_t
*)pvp
->pvp_femp
, vp
);
541 pvp
->pvp_femp
= NULL
;
545 * If we successfully uninstalled fem, no process is watching
546 * this vnode, Remove it from the vfs's list of watched vnodes.
548 pvfsp
= pvp
->pvp_pvfsp
;
550 pvfsh
= PORTFOP_PVFSH(vfsp
);
551 mtx
= &pvfsh
->pvfshash_mutex
;
554 * If unmount is in progress, that thread will remove and
555 * release the vnode from the vfs's list, just leave.
557 if (!pvfsp
->pvfs_unmount
) {
558 list_remove(&pvfsp
->pvfs_pvplist
, pvp
);
565 mutex_exit(&pvp
->pvp_mutex
);
570 * Remove pfp from the vnode's watch list and the cache and discard it.
571 * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
572 * Returns 1 if pfp removed successfully.
574 * The *active is set to indicate if the pfp was still active(no events had
575 * been posted, or the posted event had not been collected yet and it was
576 * able to remove it from the port's queue).
578 * vpp and dvpp will point to the vnode and directory vnode which the caller
579 * is required to VN_RELE without holding any locks.
582 port_remove_fop(portfop_t
*pfp
, portfop_cache_t
*pfcp
, int cleanup
,
583 int *active
, vnode_t
**vpp
, vnode_t
**dvpp
)
589 ASSERT(MUTEX_HELD(&pfcp
->pfc_lock
));
592 mutex_enter(&pvp
->pvp_mutex
);
595 * if not cleanup, remove it only if the pfp is still active and
596 * is not being removed by some other thread.
598 if (!cleanup
&& (!(pfp
->pfop_flags
& PORT_FOP_ACTIVE
) ||
599 pfp
->pfop_flags
& PORT_FOP_REMOVING
)) {
600 mutex_exit(&pvp
->pvp_mutex
);
607 if (pfp
->pfop_flags
& PORT_FOP_ACTIVE
) {
608 pfp
->pfop_flags
&= ~PORT_FOP_ACTIVE
;
613 * Check if the pfp is still on the vnode's list. This can
614 * happen if port_fop_excep() is in the process of removing it.
615 * In case of cleanup, just mark this pfp as inactive so that no
616 * new events (VNEVENT) will be delivered, and remove it from the
617 * event queue if it was already queued. Since the cache lock is
618 * held, the pfp will not disappear, even though it is being
621 if (pfp
->pfop_flags
& PORT_FOP_REMOVING
) {
622 mutex_exit(&pvp
->pvp_mutex
);
623 if (!tactive
&& port_remove_done_event(pfp
->pfop_pev
)) {
624 pfp
->pfop_flags
&= ~PORT_FOP_KEV_ONQ
;
634 * if we find an event on the queue and removed it, then this
635 * association is considered active.
637 if (!tactive
&& port_remove_done_event(pfp
->pfop_pev
)) {
638 pfp
->pfop_flags
&= ~PORT_FOP_KEV_ONQ
;
645 pvp
= (portfop_vp_t
*)vp
->v_fopdata
;
648 * remove pfp from the vnode's list
650 port_fop_listremove(pvp
, pfp
);
653 * If no more associations on the vnode, uninstall fem hooks.
654 * The pvp mutex will be released in this routine.
656 if (port_fop_femuninstall(vp
))
658 *dvpp
= pfp
->pfop_dvp
;
659 port_pcache_remove_fop(pfcp
, pfp
);
664 * This routine returns a pointer to a cached portfop entry, or NULL if it
665 * does not find it in the hash table. The object pointer is used as index.
666 * The entries are hashed by the object's address. We need to match the pid
667 * as the evet port can be shared between processes. The file events
668 * watches are per process only.
671 port_cache_lookup_fop(portfop_cache_t
*pfcp
, pid_t pid
, uintptr_t obj
)
673 portfop_t
*pfp
= NULL
;
676 ASSERT(MUTEX_HELD(&pfcp
->pfc_lock
));
677 bucket
= PORT_FOP_BUCKET(pfcp
, obj
);
679 while (pfp
!= NULL
) {
680 if (pfp
->pfop_object
== obj
&& pfp
->pfop_pid
== pid
)
682 pfp
= pfp
->pfop_hashnext
;
688 * Given the file name, get the vnode and also the directory vnode
689 * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
693 port_fop_getdvp(void *objptr
, vnode_t
**vp
, vnode_t
**dvp
,
694 char **cname
, int *len
, int follow
)
700 if (get_udatamodel() == DATAMODEL_NATIVE
) {
701 fname
= ((file_obj_t
*)objptr
)->fo_name
;
702 #ifdef _SYSCALL32_IMPL
704 fname
= (caddr_t
)(uintptr_t)((file_obj32_t
*)objptr
)->fo_name
;
705 #endif /* _SYSCALL32_IMPL */
709 * lookuppn may fail with EINVAL, if dvp is non-null(like when
710 * looking for "."). So call again with dvp = NULL.
712 if ((error
= pn_get(fname
, UIO_USERSPACE
, &pn
)) != 0) {
716 error
= lookuppn(&pn
, NULL
, follow
, dvp
, vp
);
717 if (error
== EINVAL
) {
719 if ((error
= pn_get(fname
, UIO_USERSPACE
, &pn
)) != 0) {
722 error
= lookuppn(&pn
, NULL
, follow
, NULL
, vp
);
728 if (error
== 0 && cname
!= NULL
&& len
!= NULL
) {
730 *len
= pn
.pn_pathlen
;
731 *cname
= kmem_alloc(*len
+ 1, KM_SLEEP
);
732 (void) strcpy(*cname
, pn
.pn_path
);
734 if (cname
!= NULL
&& len
!= NULL
) {
745 port_getsrc(port_t
*pp
, int source
)
750 * get the port source structure.
752 if (!MUTEX_HELD(&pp
->port_queue
.portq_source_mutex
)) {
753 mutex_enter(&pp
->port_queue
.portq_source_mutex
);
757 pse
= pp
->port_queue
.portq_scache
[PORT_SHASH(source
)];
758 for (; pse
!= NULL
; pse
= pse
->portsrc_next
) {
759 if (pse
->portsrc_source
== source
)
764 mutex_exit(&pp
->port_queue
.portq_source_mutex
);
771 * Compare time stamps and generate an event if it has changed.
772 * Note that the port cache pointer will be valid due to a reference
773 * to the port. We need to grab the port cache lock and verify that
774 * the pfp is still the same before proceeding to deliver an event.
777 port_check_timestamp(portfop_cache_t
*pfcp
, vnode_t
*vp
, vnode_t
*dvp
,
778 portfop_t
*pfp
, void *objptr
, uintptr_t object
)
781 portfop_vp_t
*pvp
= vp
->v_fopdata
;
783 port_kevent_t
*pkevp
;
788 * If time stamps are specified, get attributes and compare.
790 vatt
.va_mask
= AT_ATIME
|AT_MTIME
|AT_CTIME
;
791 if (get_udatamodel() == DATAMODEL_NATIVE
) {
792 fobj
= (file_obj_t
*)objptr
;
793 if (fobj
->fo_atime
.tv_sec
|| fobj
->fo_atime
.tv_nsec
||
794 fobj
->fo_mtime
.tv_sec
|| fobj
->fo_mtime
.tv_nsec
||
795 fobj
->fo_ctime
.tv_sec
|| fobj
->fo_ctime
.tv_nsec
) {
796 if (fop_getattr(vp
, &vatt
, 0, CRED(), NULL
)) {
801 * timestamp not specified, all 0's,
805 #ifdef _SYSCALL32_IMPL
807 file_obj32_t
*fobj32
;
808 fobj32
= (file_obj32_t
*)objptr
;
809 if (fobj32
->fo_atime
.tv_sec
|| fobj32
->fo_atime
.tv_nsec
||
810 fobj32
->fo_mtime
.tv_sec
|| fobj32
->fo_mtime
.tv_nsec
||
811 fobj32
->fo_ctime
.tv_sec
|| fobj32
->fo_ctime
.tv_nsec
) {
812 if (fop_getattr(vp
, &vatt
, 0, CRED(), NULL
)) {
817 * timestamp not specified, all 0.
821 #endif /* _SYSCALL32_IMPL */
825 * Now grab the cache lock and verify that we are still
826 * dealing with the same pfp and curthread is the one
827 * which registered it. We need to do this to avoid
828 * delivering redundant events.
830 mutex_enter(&pfcp
->pfc_lock
);
831 tpfp
= port_cache_lookup_fop(pfcp
, curproc
->p_pid
, object
);
833 if (tpfp
== NULL
|| tpfp
!= pfp
||
834 pfp
->pfop_vp
!= vp
|| pfp
->pfop_dvp
!= dvp
||
835 pfp
->pfop_callrid
!= curthread
||
836 !(pfp
->pfop_flags
& PORT_FOP_ACTIVE
)) {
838 * Some other event was delivered, the file
839 * watch was removed or reassociated. Just
840 * ignore it and leave
842 mutex_exit(&pfcp
->pfc_lock
);
846 mutex_enter(&pvp
->pvp_mutex
);
848 * The pfp cannot disappear as the port cache lock is held.
849 * While the pvp_mutex is held, no events will get delivered.
851 if (pfp
->pfop_flags
& PORT_FOP_ACTIVE
&&
852 !(pfp
->pfop_flags
& PORT_FOP_REMOVING
)) {
853 if (get_udatamodel() == DATAMODEL_NATIVE
) {
854 fobj
= (file_obj_t
*)objptr
;
855 if (pfp
->pfop_events
& FILE_ACCESS
&&
856 (fobj
->fo_atime
.tv_sec
|| fobj
->fo_atime
.tv_nsec
) &&
857 (vatt
.va_atime
.tv_sec
!= fobj
->fo_atime
.tv_sec
||
858 vatt
.va_atime
.tv_nsec
!= fobj
->fo_atime
.tv_nsec
))
859 events
|= FILE_ACCESS
;
861 if (pfp
->pfop_events
& FILE_MODIFIED
&&
862 (fobj
->fo_mtime
.tv_sec
|| fobj
->fo_mtime
.tv_nsec
) &&
863 (vatt
.va_mtime
.tv_sec
!= fobj
->fo_mtime
.tv_sec
||
864 vatt
.va_mtime
.tv_nsec
!= fobj
->fo_mtime
.tv_nsec
))
865 events
|= FILE_MODIFIED
;
867 if (pfp
->pfop_events
& FILE_ATTRIB
&&
868 (fobj
->fo_ctime
.tv_sec
|| fobj
->fo_ctime
.tv_nsec
) &&
869 (vatt
.va_ctime
.tv_sec
!= fobj
->fo_ctime
.tv_sec
||
870 vatt
.va_ctime
.tv_nsec
!= fobj
->fo_ctime
.tv_nsec
))
871 events
|= FILE_ATTRIB
;
872 #ifdef _SYSCALL32_IMPL
874 file_obj32_t
*fobj32
;
875 fobj32
= (file_obj32_t
*)objptr
;
876 if (pfp
->pfop_events
& FILE_ACCESS
&&
877 (fobj32
->fo_atime
.tv_sec
||
878 fobj32
->fo_atime
.tv_nsec
) &&
879 (vatt
.va_atime
.tv_sec
!= fobj32
->fo_atime
.tv_sec
||
880 vatt
.va_atime
.tv_nsec
!= fobj32
->fo_atime
.tv_nsec
))
881 events
|= FILE_ACCESS
;
883 if (pfp
->pfop_events
& FILE_MODIFIED
&&
884 (fobj32
->fo_mtime
.tv_sec
||
885 fobj32
->fo_mtime
.tv_nsec
) &&
886 (vatt
.va_mtime
.tv_sec
!= fobj32
->fo_mtime
.tv_sec
||
887 vatt
.va_mtime
.tv_nsec
!= fobj32
->fo_mtime
.tv_nsec
))
888 events
|= FILE_MODIFIED
;
890 if (pfp
->pfop_events
& FILE_ATTRIB
&&
891 (fobj32
->fo_ctime
.tv_sec
||
892 fobj32
->fo_ctime
.tv_nsec
) &&
893 (vatt
.va_ctime
.tv_sec
!= fobj32
->fo_ctime
.tv_sec
||
894 vatt
.va_ctime
.tv_nsec
!= fobj32
->fo_ctime
.tv_nsec
))
895 events
|= FILE_ATTRIB
;
896 #endif /* _SYSCALL32_IMPL */
900 * No events to deliver
903 mutex_exit(&pvp
->pvp_mutex
);
904 mutex_exit(&pfcp
->pfc_lock
);
909 * Deliver the event now.
911 pkevp
= pfp
->pfop_pev
;
912 pfp
->pfop_flags
&= ~PORT_FOP_ACTIVE
;
913 pkevp
->portkev_events
|= events
;
915 * Move it to the tail as active once are in the
916 * beginning of the list.
918 port_fop_listremove(pvp
, pfp
);
919 port_fop_listinsert_tail(pvp
, pfp
);
920 port_send_event(pkevp
);
921 pfp
->pfop_flags
|= PORT_FOP_KEV_ONQ
;
923 mutex_exit(&pvp
->pvp_mutex
);
924 mutex_exit(&pfcp
->pfc_lock
);
928 * Add the event source to the port and return the port source cache pointer.
931 port_fop_associate_source(portfop_cache_t
**pfcpp
, port_t
*pp
, int source
)
933 portfop_cache_t
*pfcp
;
938 * associate PORT_SOURCE_FILE source with the port, if it is
939 * not associated yet. Note the PORT_SOURCE_FILE source is
940 * associated once and will not be dissociated.
942 if ((pse
= port_getsrc(pp
, PORT_SOURCE_FILE
)) == NULL
) {
943 if (error
= port_associate_ksource(pp
->port_fd
, source
,
944 &pse
, port_close_fop
, pp
, NULL
)) {
951 * Get the portfop cache pointer.
953 if ((pfcp
= pse
->portsrc_data
) == NULL
) {
955 * This is the first time that a file is being associated,
956 * create the portfop cache.
958 pfcp
= kmem_zalloc(sizeof (portfop_cache_t
), KM_SLEEP
);
959 mutex_enter(&pp
->port_queue
.portq_source_mutex
);
960 if (pse
->portsrc_data
== NULL
) {
961 pse
->portsrc_data
= pfcp
;
962 mutex_exit(&pp
->port_queue
.portq_source_mutex
);
965 * someone else created the port cache, free
966 * what we just now allocated.
968 mutex_exit(&pp
->port_queue
.portq_source_mutex
);
969 kmem_free(pfcp
, sizeof (portfop_cache_t
));
970 pfcp
= pse
->portsrc_data
;
978 * Add the given pvp on the file system's list of vnodes watched.
981 port_fop_pvfsadd(portfop_vp_t
*pvp
)
984 vnode_t
*vp
= pvp
->pvp_vp
;
985 portfop_vfs_hash_t
*pvfsh
;
986 portfop_vfs_t
*pvfsp
;
988 pvfsh
= PORTFOP_PVFSH(vp
->v_vfsp
);
989 mutex_enter(&pvfsh
->pvfshash_mutex
);
990 for (pvfsp
= pvfsh
->pvfshash_pvfsp
; pvfsp
&&
991 pvfsp
->pvfs
!= vp
->v_vfsp
; pvfsp
= pvfsp
->pvfs_next
)
995 if ((error
= fsem_install(vp
->v_vfsp
, &fop_fsemop
,
996 vp
->v_vfsp
, OPUNIQ
, NULL
, NULL
))) {
997 mutex_exit(&pvfsh
->pvfshash_mutex
);
1001 pvfsp
= kmem_zalloc(sizeof (portfop_vfs_t
), KM_SLEEP
);
1002 pvfsp
->pvfs
= vp
->v_vfsp
;
1003 list_create(&(pvfsp
->pvfs_pvplist
), sizeof (portfop_vp_t
),
1004 offsetof(portfop_vp_t
, pvp_pvfsnode
));
1005 pvfsp
->pvfs_fsemp
= &fop_fsemop
;
1006 pvfsp
->pvfs_next
= pvfsh
->pvfshash_pvfsp
;
1007 pvfsh
->pvfshash_pvfsp
= pvfsp
;
1011 * check if an unmount is in progress.
1013 if (!pvfsp
->pvfs_unmount
) {
1015 * insert the pvp on list.
1017 pvp
->pvp_pvfsp
= pvfsp
;
1018 list_insert_head(&pvfsp
->pvfs_pvplist
, (void *)pvp
);
1022 mutex_exit(&pvfsh
->pvfshash_mutex
);
1027 * Installs the portfop_vp_t data structure on the
1028 * vnode. The 'pvp_femp == NULL' indicates it is not
1029 * active. The fem hooks have to be installed.
1030 * The portfop_vp_t is only freed when the vnode gets freed.
1033 port_install_fopdata(vnode_t
*vp
)
1037 npvp
= kmem_zalloc(sizeof (*npvp
), KM_SLEEP
);
1038 mutex_init(&npvp
->pvp_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
1039 list_create(&npvp
->pvp_pfoplist
, sizeof (portfop_t
),
1040 offsetof(portfop_t
, pfop_node
));
1043 * If v_fopdata is not null, some other thread beat us to it.
1045 if (atomic_cas_ptr(&vp
->v_fopdata
, NULL
, npvp
) != NULL
) {
1046 mutex_destroy(&npvp
->pvp_mutex
);
1047 list_destroy(&npvp
->pvp_pfoplist
);
1048 kmem_free(npvp
, sizeof (*npvp
));
1054 * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
1055 * to the vnode's list. The association is identified by the object pointer
1059 port_pfp_setup(portfop_t
**pfpp
, port_t
*pp
, vnode_t
*vp
, portfop_cache_t
*pfcp
,
1060 uintptr_t object
, int events
, void *user
, char *cname
, int clen
,
1063 portfop_t
*pfp
= NULL
;
1064 port_kevent_t
*pkevp
;
1070 * The port cache mutex is held.
1076 * At this point the fem monitor is installed.
1077 * Allocate a port event structure per vnode association.
1080 if (error
= port_alloc_event_local(pp
, PORT_SOURCE_FILE
,
1081 PORT_ALLOC_CACHED
, &pkevp
)) {
1084 pfp
= kmem_zalloc(sizeof (portfop_t
), KM_SLEEP
);
1085 pfp
->pfop_pev
= pkevp
;
1089 pfp
->pfop_pid
= curproc
->p_pid
;
1090 pfp
->pfop_pcache
= pfcp
;
1092 pfp
->pfop_flags
|= PORT_FOP_ACTIVE
;
1093 pfp
->pfop_cname
= cname
;
1094 pfp
->pfop_clen
= clen
;
1095 pfp
->pfop_dvp
= dvp
;
1096 pfp
->pfop_object
= object
;
1098 pkevp
->portkev_callback
= port_fop_callback
;
1099 pkevp
->portkev_arg
= pfp
;
1100 pkevp
->portkev_object
= object
;
1101 pkevp
->portkev_user
= user
;
1102 pkevp
->portkev_events
= 0;
1104 port_pcache_insert(pfcp
, pfp
);
1107 * Register a new file events monitor for this file(vnode), if not
1110 if ((pvp
= vp
->v_fopdata
) == NULL
) {
1111 port_install_fopdata(vp
);
1112 pvp
= vp
->v_fopdata
;
1115 mutex_enter(&pvp
->pvp_mutex
);
1117 * if the vnode does not have the file events hooks, install it.
1119 if (pvp
->pvp_femp
== NULL
) {
1120 if (!(error
= fem_install(pfp
->pfop_vp
, &fop_femop
, vp
, OPUNIQ
,
1122 pvp
->pvp_femp
= &fop_femop
;
1124 * add fsem_t hooks to the vfsp and add pvp to
1125 * the list of vnodes for this vfs.
1127 if (!(error
= port_fop_pvfsadd(pvp
))) {
1129 * Hold a reference to the vnode since
1130 * we successfully installed the hooks.
1134 (void) fem_uninstall(vp
, &fop_femop
, vp
);
1135 pvp
->pvp_femp
= NULL
;
1142 * pkevp will get freed here.
1144 pfp
->pfop_cname
= NULL
;
1145 port_pcache_remove_fop(pfcp
, pfp
);
1146 mutex_exit(&pvp
->pvp_mutex
);
1151 * insert the pfp on the vnode's list. After this
1152 * events can get delivered.
1154 pfp
->pfop_events
= events
;
1155 port_fop_listinsert_head(pvp
, pfp
);
1157 mutex_exit(&pvp
->pvp_mutex
);
1159 * Hold the directory vnode since we have a reference now.
1168 port_resolve_vp(vnode_t
*vp
)
1172 * special case /etc/mnttab(mntfs type). The mntfstype != 0
1173 * if mntfs got mounted.
1175 if (vfs_mntdummyvp
&& mntfstype
!= 0 &&
1176 vp
->v_vfsp
->vfs_fstype
== mntfstype
) {
1178 vp
= vfs_mntdummyvp
;
1179 VN_HOLD(vfs_mntdummyvp
);
1183 * This should take care of lofs mounted fs systems and nfs4
1186 if ((fop_realvp(vp
, &rvp
, NULL
) == 0) && vp
!= rvp
) {
1195 * Register a file events watch on the given file associated to the port *pp.
1197 * The association is identified by the object pointer and the pid.
1198 * The events argument contains the events to be monitored for.
1200 * The vnode will have a VN_HOLD once the fem hooks are installed.
1202 * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
1203 * that the directory vnode pointer does not change.
1206 port_associate_fop(port_t
*pp
, int source
, uintptr_t object
, int events
,
1209 portfop_cache_t
*pfcp
;
1210 vnode_t
*vp
, *dvp
, *oldvp
= NULL
, *olddvp
= NULL
, *orig
;
1220 * check that events specified are valid.
1222 if ((events
& ~FILE_EVENTS_MASK
) != 0)
1225 if (get_udatamodel() == DATAMODEL_NATIVE
) {
1226 if (copyin((void *)object
, &fobj
, sizeof (file_obj_t
)))
1228 objptr
= (void *)&fobj
;
1229 #ifdef _SYSCALL32_IMPL
1231 file_obj32_t fobj32
;
1232 if (copyin((void *)object
, &fobj32
, sizeof (file_obj32_t
)))
1234 objptr
= (void *)&fobj32
;
1235 #endif /* _SYSCALL32_IMPL */
1241 * find out if we need to follow symbolic links.
1243 follow
= !(events
& FILE_NOFOLLOW
);
1244 events
= events
& ~FILE_NOFOLLOW
;
1247 * lookup and find the vnode and its directory vnode of the given
1250 if ((error
= port_fop_getdvp(objptr
, &vp
, &dvp
, &cname
, &clen
,
1256 dvp
= port_resolve_vp(dvp
);
1267 vp
= port_resolve_vp(orig
= vp
);
1269 if (vp
!= NULL
&& vnevent_support(vp
, NULL
)) {
1275 * If dvp belongs to a different filesystem just ignore it, as hard
1276 * links cannot exist across filesystems. We make an exception for
1277 * procfs, however, the magic of which we treat semantically as a hard
1278 * link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE
1279 * and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events.
1281 if (dvp
!= NULL
&& dvp
->v_vfsp
!= vp
->v_vfsp
&&
1282 !(orig
->v_type
== VPROC
&& vp
!= NULL
&& vp
->v_type
!= VPROC
)) {
1288 * Associate this source to the port and get the per port
1289 * fop cache pointer. If the source is already associated, it
1290 * will just return the cache pointer.
1292 if (error
= port_fop_associate_source(&pfcp
, pp
, source
)) {
1297 * Check if there is an existing association of this file.
1299 mutex_enter(&pfcp
->pfc_lock
);
1300 pfp
= port_cache_lookup_fop(pfcp
, curproc
->p_pid
, object
);
1303 * If it is not the same vnode, just discard it. VN_RELE needs to be
1304 * called with no locks held, therefore save vnode pointers and
1305 * vn_rele them later.
1307 if (pfp
!= NULL
&& (pfp
->pfop_vp
!= vp
|| pfp
->pfop_dvp
!= dvp
)) {
1308 (void) port_remove_fop(pfp
, pfcp
, 1, NULL
, &oldvp
, &olddvp
);
1313 vnode_t
*tvp
, *tdvp
;
1318 * Add a new association, save the file name and the
1319 * directory vnode pointer.
1321 if (error
= port_pfp_setup(&pfp
, pp
, vp
, pfcp
, object
,
1322 events
, user
, cname
, clen
, dvp
)) {
1323 mutex_exit(&pfcp
->pfc_lock
);
1327 pfp
->pfop_callrid
= curthread
;
1329 * File name used, so make sure we don't free it.
1334 * We need to check if the file was removed after the
1335 * the lookup and before the fem hooks where added. If
1336 * so, return error. The vnode will still exist as we have
1339 * Drop the cache lock before calling port_fop_getdvp().
1340 * port_fop_getdvp() may block either in the vfs layer
1341 * or some filesystem. Therefore there is potential
1342 * for deadlock if cache lock is held and if some other
1343 * thread is attempting to deliver file events which would
1344 * require getting the cache lock, while it may be holding
1345 * the filesystem or vfs layer locks.
1347 mutex_exit(&pfcp
->pfc_lock
);
1349 if ((error
= port_fop_getdvp(objptr
, &tvp
, NULL
,
1350 NULL
, NULL
, follow
)) == 0) {
1352 tvp
= port_resolve_vp(tvp
);
1354 * This vnode pointer is just used
1355 * for comparison, so rele it
1361 if (error
|| tvp
== NULL
|| tvp
!= vp
) {
1363 * Since we dropped the cache lock, make sure
1364 * we are still dealing with the same pfp and this
1365 * is the thread which registered it.
1367 mutex_enter(&pfcp
->pfc_lock
);
1368 tpfp
= port_cache_lookup_fop(pfcp
,
1369 curproc
->p_pid
, object
);
1372 if (tpfp
== NULL
|| tpfp
!= pfp
||
1373 pfp
->pfop_vp
!= vp
||
1374 pfp
->pfop_dvp
!= dvp
||
1375 pfp
->pfop_callrid
!= curthread
) {
1377 * Some other event was delivered, the file
1378 * watch was removed or reassociated, just
1379 * ignore it and leave
1381 mutex_exit(&pfcp
->pfc_lock
);
1386 * remove the pfp and fem hooks, if pfp still
1387 * active and it is not being removed from
1388 * the vnode list. This is checked in
1389 * port_remove_fop with the vnode lock held.
1390 * The vnode returned is VN_RELE'ed after dropping
1394 if (port_remove_fop(pfp
, pfcp
, 0, NULL
, &tvp
, &tdvp
)) {
1396 * The pfp was removed, means no
1397 * events where queued. Report the
1402 mutex_exit(&pfcp
->pfc_lock
);
1410 portfop_vp_t
*pvp
= vp
->v_fopdata
;
1413 * Re-association of the object.
1415 mutex_enter(&pvp
->pvp_mutex
);
1418 * remove any queued up event.
1420 if (port_remove_done_event(pfp
->pfop_pev
)) {
1421 pfp
->pfop_flags
&= ~PORT_FOP_KEV_ONQ
;
1425 * set new events to watch.
1427 pfp
->pfop_events
= events
;
1430 * If not active, mark it active even if it is being
1431 * removed. Then it can send an exception event.
1433 * Move it to the head, as the active ones are only
1434 * in the beginning. If removing, the pfp will be on
1435 * a temporary list, no need to move it to the front
1436 * all the entries will be processed. Some exception
1437 * events will be delivered in port_fop_excep();
1439 if (!(pfp
->pfop_flags
& PORT_FOP_ACTIVE
)) {
1440 pfp
->pfop_flags
|= PORT_FOP_ACTIVE
;
1441 if (!(pfp
->pfop_flags
& PORT_FOP_REMOVING
)) {
1442 pvp
= (portfop_vp_t
*)vp
->v_fopdata
;
1443 port_fop_listremove(pvp
, pfp
);
1444 port_fop_listinsert_head(pvp
, pfp
);
1447 pfp
->pfop_callrid
= curthread
;
1448 mutex_exit(&pvp
->pvp_mutex
);
1449 mutex_exit(&pfcp
->pfc_lock
);
1453 * Compare time stamps and deliver events.
1455 if (vp
->v_type
!= VFIFO
) {
1456 port_check_timestamp(pfcp
, vp
, dvp
, pfp
, objptr
, object
);
1462 * If we have too many watches on the vnode, discard an
1465 port_fop_trimpfplist(vp
);
1469 * Release the hold acquired due to the lookup operation.
1482 * copied file name not used, free it.
1484 if (cname
!= NULL
) {
1485 kmem_free(cname
, clen
+ 1);
1492 * The port_dissociate_fop() function dissociates the file object
1493 * from the event port and removes any events that are already on the queue.
1494 * Only the owner of the association is allowed to dissociate the file from
1495 * the port. Returns success (0) if it was found and removed. Otherwise
1499 port_dissociate_fop(port_t
*pp
, uintptr_t object
)
1501 portfop_cache_t
*pfcp
;
1505 vnode_t
*tvp
= NULL
, *tdvp
= NULL
;
1507 pse
= port_getsrc(pp
, PORT_SOURCE_FILE
);
1510 * if this source is not associated or if there is no
1511 * cache, nothing to do just return.
1514 (pfcp
= (portfop_cache_t
*)pse
->portsrc_data
) == NULL
)
1518 * Check if this object is on the cache. Only the owner pid
1519 * is allowed to dissociate.
1521 mutex_enter(&pfcp
->pfc_lock
);
1522 pfp
= port_cache_lookup_fop(pfcp
, curproc
->p_pid
, object
);
1524 mutex_exit(&pfcp
->pfc_lock
);
1529 * If this was the last association, it will release
1530 * the hold on the vnode. There is a race condition where
1531 * the the pfp is being removed due to an exception event
1532 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
1533 * Since port source cache lock is held, port_fop_excep() cannot
1534 * complete. The vnode itself will not disappear as long its pfps
1537 (void) port_remove_fop(pfp
, pfcp
, 1, &active
, &tvp
, &tdvp
);
1538 mutex_exit(&pfcp
->pfc_lock
);
1543 return (active
? 0 : ENOENT
);
1548 * port_close() calls this function to request the PORT_SOURCE_FILE source
1549 * to remove/free all resources allocated and associated with the port.
1554 port_close_fop(void *arg
, int port
, pid_t pid
, int lastclose
)
1557 portfop_cache_t
*pfcp
;
1558 portfop_t
**hashtbl
;
1563 vnode_t
*tdvp
= NULL
;
1564 vnode_t
*vpl
[PORTFOP_NVP
];
1566 pse
= port_getsrc(pp
, PORT_SOURCE_FILE
);
1569 * No source or no cache, nothing to do.
1572 (pfcp
= (portfop_cache_t
*)pse
->portsrc_data
) == NULL
)
1575 * Scan the cache and free all allocated portfop_t and port_kevent_t
1576 * structures of this pid. Note, no new association for this pid will
1577 * be possible as the port is being closed.
1579 * The common case is that the port is not shared and all the entries
1580 * are of this pid and have to be freed. Since VN_RELE has to be
1581 * called outside the lock, we do it in batches.
1583 hashtbl
= (portfop_t
**)pfcp
->pfc_hash
;
1585 bzero(vpl
, sizeof (vpl
));
1586 mutex_enter(&pfcp
->pfc_lock
);
1587 while (index
< PORTFOP_HASHSIZE
) {
1588 pfp
= hashtbl
[index
];
1589 while (pfp
!= NULL
&& i
< (PORTFOP_NVP
- 1)) {
1590 pfpnext
= pfp
->pfop_hashnext
;
1591 if (pid
== pfp
->pfop_pid
) {
1592 (void) port_remove_fop(pfp
, pfcp
, 1, NULL
,
1594 if (vpl
[i
] != NULL
) {
1607 * Now call VN_RELE if we have collected enough vnodes or
1608 * we have reached the end of the hash table.
1610 if (i
>= (PORTFOP_NVP
- 1) ||
1611 (i
> 0 && index
== PORTFOP_HASHSIZE
)) {
1612 mutex_exit(&pfcp
->pfc_lock
);
1617 mutex_enter(&pfcp
->pfc_lock
);
1622 * Due to a race between port_close_fop() and port_fop()
1623 * trying to remove the pfp's from the port's cache, it is
1624 * possible that some pfp's are still in the process of being
1627 while (lastclose
&& pfcp
->pfc_objcount
) {
1628 (void) cv_wait_sig(&pfcp
->pfc_lclosecv
, &pfcp
->pfc_lock
);
1630 mutex_exit(&pfcp
->pfc_lock
);
1632 * last close, free the cache.
1635 ASSERT(pfcp
->pfc_objcount
== 0);
1636 pse
->portsrc_data
= NULL
;
1637 kmem_free(pfcp
, sizeof (portfop_cache_t
));
1642 * Given the list of associations(watches), it will send exception events,
1643 * if still active, and discard them. The exception events are handled
1644 * separately because, the pfp needs to be removed from the port cache and
1645 * freed as the vnode's identity is changing or being removed. To remove
1646 * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
1647 * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
1648 * the cache's lock cannot be acquired in port_fop_sendevent().
1651 port_fop_excep(list_t
*tlist
, int op
)
1654 portfop_cache_t
*pfcp
;
1656 port_kevent_t
*pkevp
;
1660 while (pfp
= (portfop_t
*)list_head(tlist
)) {
1663 * remove from the temp list. Since PORT_FOP_REMOVING is
1664 * set, no other thread should attempt to perform a
1665 * list_remove on this pfp.
1667 list_remove(tlist
, pfp
);
1669 pfcp
= pfp
->pfop_pcache
;
1670 mutex_enter(&pfcp
->pfc_lock
);
1673 * Remove the event from the port queue if it was queued up.
1674 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
1675 * no longer on the vnode's list.
1677 if ((pfp
->pfop_flags
& PORT_FOP_KEV_ONQ
)) {
1678 removed
= port_remove_done_event(pfp
->pfop_pev
);
1682 * If still active or the event was queued up and
1683 * had not been collected yet, send an EXCEPTION event.
1685 if (pfp
->pfop_flags
& (PORT_FOP_ACTIVE
) || removed
) {
1688 * Allocate a port_kevent_t non cached to send this
1689 * event since we will be de-registering.
1690 * The port_kevent_t cannot be pointing back to the
1693 pfp
->pfop_flags
&= ~PORT_FOP_ACTIVE
;
1694 error
= port_alloc_event_local(pp
, PORT_SOURCE_FILE
,
1695 PORT_ALLOC_DEFAULT
, &pkevp
);
1698 pkevp
->portkev_callback
= port_fop_callback
;
1699 pkevp
->portkev_arg
= NULL
;
1700 pkevp
->portkev_object
=
1701 pfp
->pfop_pev
->portkev_object
;
1702 pkevp
->portkev_user
=
1703 pfp
->pfop_pev
->portkev_user
;
1705 * Copy the pid of the watching process.
1707 pkevp
->portkev_pid
=
1708 pfp
->pfop_pev
->portkev_pid
;
1709 pkevp
->portkev_events
= op
;
1710 port_send_event(pkevp
);
1714 * At this point the pfp has been removed from the vnode's
1715 * list its cached port_kevent_t is not on the done queue.
1716 * Remove the pfp and free it from the cache.
1718 tdvp
= pfp
->pfop_dvp
;
1719 port_pcache_remove_fop(pfcp
, pfp
);
1720 mutex_exit(&pfcp
->pfc_lock
);
1727 * Send the file events to all of the processes watching this
1728 * vnode. In case of hard links, the directory vnode pointer and
1729 * the file name are compared. If the names match, then the specified
1730 * event is sent or else, the FILE_ATTRIB event is sent, This is the
1731 * documented behavior.
1734 port_fop_sendevent(vnode_t
*vp
, int events
, vnode_t
*dvp
, char *cname
)
1736 port_kevent_t
*pkevp
;
1737 portfop_t
*pfp
, *npfp
;
1742 pvp
= (portfop_vp_t
*)vp
->v_fopdata
;
1743 mutex_enter(&pvp
->pvp_mutex
);
1746 * Check if the list is empty.
1748 * All entries have been removed by some other thread.
1749 * The vnode may be still active and we got called,
1750 * but some other thread is in the process of removing the hooks.
1752 if (!list_head(&pvp
->pvp_pfoplist
)) {
1753 mutex_exit(&pvp
->pvp_mutex
);
1757 if ((events
& (FILE_EXCEPTION
))) {
1759 * If it is an event for which we are going to remove
1760 * the watches so just move it a temporary list and
1761 * release this vnode.
1763 list_create(&tmplist
, sizeof (portfop_t
),
1764 offsetof(portfop_t
, pfop_node
));
1767 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
1768 * passed for an exception event, all associations need to be
1771 if (dvp
== NULL
|| cname
== NULL
) {
1778 * All the active ones are in the beginning of the list.
1779 * Note that we process this list in reverse order to assure
1780 * that events are delivered in the order that they were
1783 for (pfp
= (portfop_t
*)list_tail(&pvp
->pvp_pfoplist
);
1784 pfp
&& !(pfp
->pfop_flags
& PORT_FOP_ACTIVE
); pfp
= npfp
) {
1785 npfp
= list_prev(&pvp
->pvp_pfoplist
, pfp
);
1788 for (; pfp
!= NULL
; pfp
= npfp
) {
1789 int levents
= events
;
1791 npfp
= list_prev(&pvp
->pvp_pfoplist
, pfp
);
1793 * Hard links case - If the file is being
1794 * removed/renamed, and the name matches
1795 * the watched file, then it is an EXCEPTION
1796 * event or else it will be just a FILE_ATTRIB.
1798 if ((events
& (FILE_EXCEPTION
))) {
1799 ASSERT(dvp
!= NULL
&& cname
!= NULL
);
1800 if (pfp
->pfop_dvp
== NULL
||
1801 (pfp
->pfop_dvp
== dvp
&&
1802 (strcmp(cname
, pfp
->pfop_cname
) == 0))) {
1804 * It is an exception event, move it
1805 * to temp list and process it later.
1806 * Note we don't set the pfp->pfop_vp
1807 * to NULL even thought it has been
1808 * removed from the vnode's list. This
1809 * pointer is referenced in
1810 * port_remove_fop(). The vnode it
1811 * self cannot disappear until this
1812 * pfp gets removed and freed.
1814 port_fop_listremove(pvp
, pfp
);
1815 list_insert_tail(&tmplist
, (void *)pfp
);
1816 pfp
->pfop_flags
|= PORT_FOP_REMOVING
;
1819 levents
= FILE_ATTRIB
;
1824 if (pfp
->pfop_events
& levents
) {
1826 * deactivate and move it to the tail.
1827 * If the pfp was active, it cannot be
1828 * on the port's done queue.
1830 pfp
->pfop_flags
&= ~PORT_FOP_ACTIVE
;
1831 port_fop_listremove(pvp
, pfp
);
1832 port_fop_listinsert_tail(pvp
, pfp
);
1834 pkevp
= pfp
->pfop_pev
;
1835 pkevp
->portkev_events
|=
1836 (levents
& pfp
->pfop_events
);
1837 port_send_event(pkevp
);
1838 pfp
->pfop_flags
|= PORT_FOP_KEV_ONQ
;
1844 if ((events
& (FILE_EXCEPTION
))) {
1847 * Check the inactive associations and remove them if
1848 * the file name matches.
1850 for (; pfp
; pfp
= npfp
) {
1851 npfp
= list_next(&pvp
->pvp_pfoplist
, pfp
);
1852 if (dvp
== NULL
|| cname
== NULL
||
1853 pfp
->pfop_dvp
== NULL
||
1854 (pfp
->pfop_dvp
== dvp
&&
1855 (strcmp(cname
, pfp
->pfop_cname
) == 0))) {
1856 port_fop_listremove(pvp
, pfp
);
1857 list_insert_tail(&tmplist
, (void *)pfp
);
1858 pfp
->pfop_flags
|= PORT_FOP_REMOVING
;
1863 * Can be optimized to avoid two pass over this list
1864 * by having a flag in the vnode's portfop_vp_t
1865 * structure to indicate that it is going away,
1866 * Or keep the list short by reusing inactive watches.
1868 port_fop_listmove(pvp
, &tmplist
);
1869 for (pfp
= (portfop_t
*)list_head(&tmplist
);
1870 pfp
; pfp
= list_next(&tmplist
, pfp
)) {
1871 pfp
->pfop_flags
|= PORT_FOP_REMOVING
;
1876 * Uninstall the fem hooks if there are no more associations.
1877 * This will release the pvp mutex.
1879 * Even thought all entries may have been removed,
1880 * the vnode itself cannot disappear as there will be a
1881 * hold on it due to this call to port_fop_sendevent. This is
1882 * important to syncronize with a port_dissociate_fop() call
1883 * that may be attempting to remove an object from the vnode's.
1885 if (port_fop_femuninstall(vp
))
1889 * Send exception events and discard the watch entries.
1891 port_fop_excep(&tmplist
, events
);
1892 list_destroy(&tmplist
);
1895 mutex_exit(&pvp
->pvp_mutex
);
1900 port_fop_trimpfplist(vp
);
1905 * Given the file operation, map it to the event types and send.
1908 port_fop(vnode_t
*vp
, int op
, int retval
)
1912 * deliver events only if the operation was successful.
1918 * These events occurring on the watched file.
1920 if (op
& FOP_MODIFIED_MASK
) {
1921 event
= FILE_MODIFIED
;
1923 if (op
& FOP_ACCESS_MASK
) {
1924 event
|= FILE_ACCESS
;
1926 if (op
& FOP_ATTRIB_MASK
) {
1927 event
|= FILE_ATTRIB
;
1929 if (op
& FOP_TRUNC_MASK
) {
1930 event
|= FILE_TRUNC
;
1933 port_fop_sendevent(vp
, event
, NULL
, NULL
);
1937 static int port_forceunmount(vfs_t
*vfsp
)
1939 char *fsname
= vfssw
[vfsp
->vfs_fstype
].vsw_name
;
1941 if (fsname
== NULL
) {
1945 if (strcmp(fsname
, MNTTYPE_NFS
) == 0) {
1949 if (strcmp(fsname
, MNTTYPE_NFS3
) == 0) {
1953 if (strcmp(fsname
, MNTTYPE_NFS4
) == 0) {
1959 * ----- the unmount filesystem op(fsem) hook.
1962 port_fop_unmount(fsemarg_t
*vf
, int flag
, cred_t
*cr
)
1964 vfs_t
*vfsp
= (vfs_t
*)vf
->fa_fnode
->fn_available
;
1966 portfop_vfs_t
*pvfsp
, **ppvfsp
;
1971 fmfs
= port_forceunmount(vfsp
);
1973 mtx
= &(portvfs_hash
[PORTFOP_PVFSHASH(vfsp
)].pvfshash_mutex
);
1974 ppvfsp
= &(portvfs_hash
[PORTFOP_PVFSHASH(vfsp
)].pvfshash_pvfsp
);
1978 * since this fsem hook is triggered, the vfsp has to be on
1981 for (pvfsp
= *ppvfsp
; pvfsp
->pvfs
!= vfsp
; pvfsp
= pvfsp
->pvfs_next
)
1985 * For some of the filesystems, allow unmounts to proceed only if
1986 * there are no files being watched or it is a forced unmount.
1988 if (fmfs
&& !(flag
& MS_FORCE
) &&
1989 !list_is_empty(&pvfsp
->pvfs_pvplist
)) {
1995 * Indicate that the unmount is in process. Don't remove it yet.
1996 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
1997 * flag on the vfs_t structure. But we call the filesystem unmount
1998 * routine after removing all the file watches for this filesystem,
1999 * otherwise the unmount will fail due to active vnodes.
2000 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
2001 * attempting to add a file watch.
2003 pvfsp
->pvfs_unmount
= 1;
2007 * uninstall the fsem hooks.
2009 (void) fsem_uninstall(vfsp
, (fsem_t
*)pvfsp
->pvfs_fsemp
, vfsp
);
2011 while (pvp
= list_head(&pvfsp
->pvfs_pvplist
)) {
2012 list_remove(&pvfsp
->pvfs_pvplist
, pvp
);
2014 * This should send an UNMOUNTED event to all the
2015 * watched vnode of this filesystem and uninstall
2016 * the fem hooks. We release the hold on the vnode here
2017 * because port_fop_femuninstall() will not do it if
2018 * unmount is in process.
2020 port_fop_sendevent(pvp
->pvp_vp
, UNMOUNTED
, NULL
, NULL
);
2021 VN_RELE(pvp
->pvp_vp
);
2024 error
= vfsnext_unmount(vf
, flag
, cr
);
2027 * we free the pvfsp after the unmount has been completed.
2030 for (; *ppvfsp
&& (*ppvfsp
)->pvfs
!= vfsp
;
2031 ppvfsp
= &(*ppvfsp
)->pvfs_next
)
2035 * remove and free it.
2037 ASSERT(list_head(&pvfsp
->pvfs_pvplist
) == NULL
);
2040 *ppvfsp
= pvfsp
->pvfs_next
;
2043 kmem_free(pvfsp
, sizeof (portfop_vfs_t
));
2048 * ------------------------------file op hooks--------------------------
2049 * The O_TRUNC operation is caught with the fop_setattr(AT_SIZE) call.
2052 port_fop_open(femarg_t
*vf
, int mode
, cred_t
*cr
, caller_context_t
*ct
)
2055 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2057 retval
= vnext_open(vf
, mode
, cr
, ct
);
2058 port_fop(vp
, FOP_FILE_OPEN
, retval
);
2063 port_fop_write(femarg_t
*vf
, struct uio
*uiop
, int ioflag
, struct cred
*cr
,
2064 caller_context_t
*ct
)
2067 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2069 retval
= vnext_write(vf
, uiop
, ioflag
, cr
, ct
);
2070 port_fop(vp
, FOP_FILE_WRITE
, retval
);
2075 port_fop_map(femarg_t
*vf
, offset_t off
, struct as
*as
, caddr_t
*addrp
,
2076 size_t len
, uchar_t prot
, uchar_t maxport
, uint_t flags
, cred_t
*cr
,
2077 caller_context_t
*ct
)
2080 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2082 retval
= vnext_map(vf
, off
, as
, addrp
, len
, prot
, maxport
,
2084 port_fop(vp
, FOP_FILE_MAP
, retval
);
2089 port_fop_read(femarg_t
*vf
, struct uio
*uiop
, int ioflag
, struct cred
*cr
,
2090 caller_context_t
*ct
)
2093 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2095 retval
= vnext_read(vf
, uiop
, ioflag
, cr
, ct
);
2096 port_fop(vp
, FOP_FILE_READ
, retval
);
2102 * AT_SIZE - is for the open(O_TRUNC) case.
2105 port_fop_setattr(femarg_t
*vf
, vattr_t
*vap
, int flags
, cred_t
*cr
,
2106 caller_context_t
*ct
)
2109 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2112 retval
= vnext_setattr(vf
, vap
, flags
, cr
, ct
);
2113 if (vap
->va_mask
& AT_SIZE
) {
2114 events
|= FOP_FILE_TRUNC
;
2116 if (vap
->va_mask
& (AT_SIZE
|AT_MTIME
)) {
2117 events
|= FOP_FILE_SETATTR_MTIME
;
2119 if (vap
->va_mask
& AT_ATIME
) {
2120 events
|= FOP_FILE_SETATTR_ATIME
;
2122 events
|= FOP_FILE_SETATTR_CTIME
;
2124 port_fop(vp
, events
, retval
);
2129 port_fop_create(femarg_t
*vf
, char *name
, vattr_t
*vap
, vcexcl_t excl
,
2130 int mode
, vnode_t
**vpp
, cred_t
*cr
, int flag
,
2131 caller_context_t
*ct
, vsecattr_t
*vsecp
)
2133 int retval
, got
= 1;
2134 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2135 vattr_t vatt
, vatt1
;
2138 * If the file already exists, then there will be no change
2139 * to the directory. Therefore, we need to compare the
2140 * modification time of the directory to determine if the
2141 * file was actually created.
2143 vatt
.va_mask
= AT_ATIME
|AT_MTIME
|AT_CTIME
;
2144 if (fop_getattr(vp
, &vatt
, 0, CRED(), ct
)) {
2147 retval
= vnext_create(vf
, name
, vap
, excl
, mode
, vpp
, cr
,
2150 vatt1
.va_mask
= AT_ATIME
|AT_MTIME
|AT_CTIME
;
2151 if (got
&& !fop_getattr(vp
, &vatt1
, 0, CRED(), ct
)) {
2152 if ((vatt1
.va_mtime
.tv_sec
> vatt
.va_mtime
.tv_sec
||
2153 (vatt1
.va_mtime
.tv_sec
= vatt
.va_mtime
.tv_sec
&&
2154 vatt1
.va_mtime
.tv_nsec
> vatt
.va_mtime
.tv_nsec
))) {
2158 port_fop(vp
, FOP_FILE_CREATE
, retval
);
2165 port_fop_remove(femarg_t
*vf
, char *nm
, cred_t
*cr
, caller_context_t
*ct
,
2169 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2171 retval
= vnext_remove(vf
, nm
, cr
, ct
, flags
);
2172 port_fop(vp
, FOP_FILE_REMOVE
, retval
);
2177 port_fop_link(femarg_t
*vf
, vnode_t
*svp
, char *tnm
, cred_t
*cr
,
2178 caller_context_t
*ct
, int flags
)
2181 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2183 retval
= vnext_link(vf
, svp
, tnm
, cr
, ct
, flags
);
2184 port_fop(vp
, FOP_FILE_LINK
, retval
);
2189 * Rename operation is allowed only when from and to directories are
2190 * on the same filesystem. This is checked in vn_rename().
2191 * The target directory is notified thru a VNEVENT by the filesystem
2192 * if the source dir != target dir.
2195 port_fop_rename(femarg_t
*vf
, char *snm
, vnode_t
*tdvp
, char *tnm
, cred_t
*cr
,
2196 caller_context_t
*ct
, int flags
)
2199 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2201 retval
= vnext_rename(vf
, snm
, tdvp
, tnm
, cr
, ct
, flags
);
2202 port_fop(vp
, FOP_FILE_RENAMESRC
, retval
);
2207 port_fop_mkdir(femarg_t
*vf
, char *dirname
, vattr_t
*vap
, vnode_t
**vpp
,
2208 cred_t
*cr
, caller_context_t
*ct
, int flags
, vsecattr_t
*vsecp
)
2211 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2213 retval
= vnext_mkdir(vf
, dirname
, vap
, vpp
, cr
, ct
, flags
, vsecp
);
2214 port_fop(vp
, FOP_FILE_MKDIR
, retval
);
2219 port_fop_rmdir(femarg_t
*vf
, char *nm
, vnode_t
*cdir
, cred_t
*cr
,
2220 caller_context_t
*ct
, int flags
)
2223 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2225 retval
= vnext_rmdir(vf
, nm
, cdir
, cr
, ct
, flags
);
2226 port_fop(vp
, FOP_FILE_RMDIR
, retval
);
2231 port_fop_readdir(femarg_t
*vf
, uio_t
*uiop
, cred_t
*cr
, int *eofp
,
2232 caller_context_t
*ct
, int flags
)
2235 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2237 retval
= vnext_readdir(vf
, uiop
, cr
, eofp
, ct
, flags
);
2238 port_fop(vp
, FOP_FILE_READDIR
, retval
);
2243 port_fop_symlink(femarg_t
*vf
, char *linkname
, vattr_t
*vap
, char *target
,
2244 cred_t
*cr
, caller_context_t
*ct
, int flags
)
2247 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2249 retval
= vnext_symlink(vf
, linkname
, vap
, target
, cr
, ct
, flags
);
2250 port_fop(vp
, FOP_FILE_SYMLINK
, retval
);
2255 * acl, facl call this.
2258 port_fop_setsecattr(femarg_t
*vf
, vsecattr_t
*vsap
, int flags
, cred_t
*cr
,
2259 caller_context_t
*ct
)
2262 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2263 retval
= vnext_setsecattr(vf
, vsap
, flags
, cr
, ct
);
2264 port_fop(vp
, FOP_FILE_SETSECATTR
, retval
);
2269 * these are events on the watched file/directory
2272 port_fop_vnevent(femarg_t
*vf
, vnevent_t vnevent
, vnode_t
*dvp
, char *name
,
2273 caller_context_t
*ct
)
2275 vnode_t
*vp
= (vnode_t
*)vf
->fa_fnode
->fn_available
;
2279 port_fop_sendevent(vp
, FILE_RENAME_FROM
, dvp
, name
);
2281 case VE_RENAME_DEST
:
2282 port_fop_sendevent(vp
, FILE_RENAME_TO
, dvp
, name
);
2285 port_fop_sendevent(vp
, FILE_DELETE
, dvp
, name
);
2288 port_fop_sendevent(vp
, FILE_DELETE
, dvp
, name
);
2291 port_fop_sendevent(vp
,
2292 FILE_MODIFIED
|FILE_ATTRIB
|FILE_TRUNC
, NULL
, NULL
);
2295 port_fop_sendevent(vp
, FILE_ATTRIB
, NULL
, NULL
);
2298 case VE_RENAME_DEST_DIR
:
2299 port_fop_sendevent(vp
, FILE_MODIFIED
|FILE_ATTRIB
,
2303 case VE_MOUNTEDOVER
:
2304 port_fop_sendevent(vp
, MOUNTEDOVER
, NULL
, NULL
);
2307 port_fop_sendevent(vp
, FILE_TRUNC
, NULL
, NULL
);
2312 return (vnext_vnevent(vf
, vnevent
, dvp
, name
, ct
));