1df2381bfSpraks /*
2df2381bfSpraks  * CDDL HEADER START
3df2381bfSpraks  *
4df2381bfSpraks  * The contents of this file are subject to the terms of the
5df2381bfSpraks  * Common Development and Distribution License (the "License").
6df2381bfSpraks  * You may not use this file except in compliance with the License.
7df2381bfSpraks  *
8df2381bfSpraks  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9df2381bfSpraks  * or http://www.opensolaris.org/os/licensing.
10df2381bfSpraks  * See the License for the specific language governing permissions
11df2381bfSpraks  * and limitations under the License.
12df2381bfSpraks  *
13df2381bfSpraks  * When distributing Covered Code, include this CDDL HEADER in each
14df2381bfSpraks  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15df2381bfSpraks  * If applicable, add the following below this CDDL HEADER, with the
16df2381bfSpraks  * fields enclosed by brackets "[]" replaced with your own identifying
17df2381bfSpraks  * information: Portions Copyright [yyyy] [name of copyright owner]
18df2381bfSpraks  *
19df2381bfSpraks  * CDDL HEADER END
20df2381bfSpraks  */
21df2381bfSpraks /*
22efaadbbfSPrakash Sangappa  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23df2381bfSpraks  * Use is subject to license terms.
24df2381bfSpraks  */
25df2381bfSpraks 
2672102e74SBryan Cantrill /*
2715c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
28*abb88ab1SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
2972102e74SBryan Cantrill  */
30df2381bfSpraks 
31df2381bfSpraks /*
32df2381bfSpraks  * File Events Notification
33df2381bfSpraks  * ------------------------
34df2381bfSpraks  *
35df2381bfSpraks  * The File Events Notification facility provides file and directory change
36df2381bfSpraks  * notification. It is implemented as an event source(PORT_SOURCE_FILE)
37df2381bfSpraks  * under the Event Ports framework. Therefore the API is an extension to
38df2381bfSpraks  * the Event Ports API.
39df2381bfSpraks  *
40df2381bfSpraks  * It uses the FEM (File Events Monitoring) framework to intercept
41df2381bfSpraks  * operations on the files & directories and generate appropriate events.
42df2381bfSpraks  *
43df2381bfSpraks  * It provides event notification in accordance with what an application
44df2381bfSpraks  * can find out by stat`ing the file and comparing time stamps. The various
45df2381bfSpraks  * system calls that update the file's access, modification, and change
46df2381bfSpraks  * time stamps are documented in the man page section 2.
47df2381bfSpraks  *
48df2381bfSpraks  * It is non intrusive. That is, having an active file event watch on a file
49df2381bfSpraks  * or directory will not prevent it from being removed or renamed or block an
50df2381bfSpraks  * unmount operation of the file system where the watched file or directory
51df2381bfSpraks  * resides.
52df2381bfSpraks  *
53df2381bfSpraks  *
54df2381bfSpraks  * Interface:
55df2381bfSpraks  * ----------
56df2381bfSpraks  *
57df2381bfSpraks  *   The object for this event source is of type 'struct file_obj *'
58df2381bfSpraks  *
59df2381bfSpraks  *   The file that needs to be monitored is specified in 'fo_name'.
60df2381bfSpraks  *   The time stamps collected by a stat(2) call are passed in fo_atime,
61df2381bfSpraks  *   fo_mtime, fo_ctime. At the time a file events watch is registered, the
62df2381bfSpraks  *   time stamps passed in are compared with the current time stamps of the
63da6c28aaSamw  *   file. If it has changed, relevant events are sent immediately. If the time
64df2381bfSpraks  *   stamps are all '0', they will not be compared.
65df2381bfSpraks  *
66df2381bfSpraks  *
67df2381bfSpraks  * The events are delivered to an event port. A port is created using
68df2381bfSpraks  * port_create().
69df2381bfSpraks  *
70df2381bfSpraks  * To register a file events watch on a file or directory.
71df2381bfSpraks  *
72df2381bfSpraks  *   port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
73df2381bfSpraks  *
74df2381bfSpraks  *   'user' is the user pointer to be returned with the event.
75df2381bfSpraks  *
76df2381bfSpraks  * To de-register a file events watch,
77df2381bfSpraks  *
78df2381bfSpraks  *   port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
79df2381bfSpraks  *
80df2381bfSpraks  * The events are collected using the port_get()/port_getn() interface. The
81df2381bfSpraks  * event source will be PORT_SOURCE_FILE.
82df2381bfSpraks  *
83df2381bfSpraks  * After an event is delivered, the file events watch gets de-activated. To
84df2381bfSpraks  * receive the next event, the process will have to re-register the watch and
85df2381bfSpraks  * activate it by calling port_associate() again. This behavior is intentional
86df2381bfSpraks  * and supports proper multi threaded programming when using file events
87df2381bfSpraks  * notification API.
88df2381bfSpraks  *
89df2381bfSpraks  *
90df2381bfSpraks  * Implementation overview:
91df2381bfSpraks  * ------------------------
92df2381bfSpraks  *
93df2381bfSpraks  * Each file events watch is represented by 'portfop_t' in the kernel. A
94df2381bfSpraks  * cache(in portfop_cache_t) of these portfop_t's are maintained per event
95df2381bfSpraks  * port by this source. The object here is the pointer to the file_obj
96df2381bfSpraks  * structure. The portfop_t's are hashed in using the object pointer. Therefore
97df2381bfSpraks  * it is possible to have multiple file events watches on a file by the same
98df2381bfSpraks  * process by using different object structure(file_obj_t) and hence can
99df2381bfSpraks  * receive multiple event notification for a file. These watches can be for
100df2381bfSpraks  * different event types.
101df2381bfSpraks  *
102df2381bfSpraks  * The cached entries of these file objects are retained, even after delivering
103df2381bfSpraks  * an event, marking them inactive for performance reasons. The assumption
104df2381bfSpraks  * is that the process would come back and re-register the file to receive
105df2381bfSpraks  * further events. When there are more then 'port_fop_maxpfps' watches per file
106df2381bfSpraks  * it will attempt to free the oldest inactive watches.
107df2381bfSpraks  *
108df2381bfSpraks  * In case the event that is being delivered is an exception event, the cached
109df2381bfSpraks  * entries get removed. An exception event on a file or directory means its
110df2381bfSpraks  * identity got changed(rename to/from, delete, mounted over, file system
111df2381bfSpraks  * unmount).
112df2381bfSpraks  *
113df2381bfSpraks  * If the event port gets closed, all the associated file event watches will be
114df2381bfSpraks  * removed and discarded.
115df2381bfSpraks  *
116df2381bfSpraks  *
117df2381bfSpraks  * Data structures:
118df2381bfSpraks  * ----------------
119df2381bfSpraks  *
120df2381bfSpraks  * The list of file event watches per file are managed by the data structure
121df2381bfSpraks  * portfop_vp_t. The first time a file events watch is registered for a file,
122df2381bfSpraks  * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
123df2381bfSpraks  * removed and freed only when the vnode becomes inactive. The FEM hooks are
124df2381bfSpraks  * also installed when the first watch is registered on a file. The FEM hooks
125df2381bfSpraks  * get un-installed when all the watches are removed.
126df2381bfSpraks  *
127df2381bfSpraks  * Each file events watch is represented by the structure portfop_t. They
128df2381bfSpraks  * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
129df2381bfSpraks  * delivering an event, the portfop_t is marked inactive but retained. It is
130df2381bfSpraks  * moved to the end of the list. All the active portfop_t's are maintained at
131df2381bfSpraks  * the beginning. In case of exception events, the portfop_t will be removed
132df2381bfSpraks  * and discarded.
133df2381bfSpraks  *
134df2381bfSpraks  * To intercept unmount operations, FSEM hooks are added to the file system
135df2381bfSpraks  * under which files are being watched. A hash table('portfop_vfs_hash_t') of
136df2381bfSpraks  * active file systems is maintained. Each file system that has active watches
137df2381bfSpraks  * is represented by 'portfop_vfs_t' and is added to the hash table.
138df2381bfSpraks  * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
139df2381bfSpraks  * being watched on the portfop_vfs_t structure.
140df2381bfSpraks  *
141df2381bfSpraks  *
142df2381bfSpraks  * File system support:
143df2381bfSpraks  * -------------------
144df2381bfSpraks  *
145df2381bfSpraks  * The file system implementation has to provide vnode event notifications
146df2381bfSpraks  * (vnevents) in order to support watching any files on that file system.
147df2381bfSpraks  * The vnode events(vnevents) are notifications provided by the file system
148df2381bfSpraks  * for name based file operations like rename, remove etc, which do not go
149df2381bfSpraks  * thru the VOP_** interfaces. If the file system does not implement vnode
150df2381bfSpraks  * notifications, watching for file events on such file systems is not
151df2381bfSpraks  * supported. The vnode event notifications support is determined by the call
152df2381bfSpraks  * vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
153df2381bfSpraks  * has to implement.
154df2381bfSpraks  *
155df2381bfSpraks  *
156df2381bfSpraks  * Locking order:
157df2381bfSpraks  * --------------
158df2381bfSpraks  *
159df2381bfSpraks  * A file(vnode) can have file event watches registered by different processes.
160df2381bfSpraks  * There is one portfop_t per watch registered. These are on the vnode's list
161df2381bfSpraks  * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
162df2381bfSpraks  * also on the per port cache. The cache is protected by the pfc_lock of
163df2381bfSpraks  * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
164df2381bfSpraks  *
165df2381bfSpraks  */
166df2381bfSpraks 
167df2381bfSpraks #include <sys/types.h>
168df2381bfSpraks #include <sys/systm.h>
169df2381bfSpraks #include <sys/stat.h>
170df2381bfSpraks #include <sys/errno.h>
171df2381bfSpraks #include <sys/kmem.h>
172df2381bfSpraks #include <sys/sysmacros.h>
173df2381bfSpraks #include <sys/debug.h>
174df2381bfSpraks #include <sys/vnode.h>
175df2381bfSpraks #include <sys/poll_impl.h>
176df2381bfSpraks #include <sys/port_impl.h>
177df2381bfSpraks #include <sys/fem.h>
178df2381bfSpraks #include <sys/vfs_opreg.h>
179df2381bfSpraks #include <sys/atomic.h>
18084c5ce69Spraks #include <sys/mount.h>
18184c5ce69Spraks #include <sys/mntent.h>
182df2381bfSpraks 
183df2381bfSpraks /*
184df2381bfSpraks  * For special case support of mnttab (/etc/mnttab).
185df2381bfSpraks  */
186df2381bfSpraks extern struct vnode *vfs_mntdummyvp;
187df2381bfSpraks extern int mntfstype;
188df2381bfSpraks 
189df2381bfSpraks #define	PORTFOP_PVFSH(vfsp)	(&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
190df2381bfSpraks portfop_vfs_hash_t	 portvfs_hash[PORTFOP_PVFSHASH_SZ];
191df2381bfSpraks 
1926b5ad791Spraks #define	PORTFOP_NVP	20
193df2381bfSpraks /*
194df2381bfSpraks  * Inactive file event watches(portfop_t) are retained on the vnode's list
195df2381bfSpraks  * for performance reason. If the applications re-registers the file, the
196df2381bfSpraks  * inactive entry is made active and moved up the list.
197df2381bfSpraks  *
198df2381bfSpraks  * If there are greater then the following number of watches on a vnode,
199df2381bfSpraks  * it will attempt to discard an oldest inactive watch(pfp) at the time
200073af7d9Spraks  * a new watch is being registered and when events get delivered. We
201df2381bfSpraks  * do this to avoid accumulating inactive watches on a file.
202df2381bfSpraks  */
203df2381bfSpraks int	port_fop_maxpfps = 20;
204df2381bfSpraks 
205df2381bfSpraks /* local functions */
206df2381bfSpraks static int	port_fop_callback(void *, int *, pid_t, int, void *);
207df2381bfSpraks 
208df2381bfSpraks static void	port_pcache_insert(portfop_cache_t *, portfop_t *);
209df2381bfSpraks static void	port_pcache_delete(portfop_cache_t *, portfop_t *);
210df2381bfSpraks static void	port_close_fop(void *arg, int port, pid_t pid, int lastclose);
211df2381bfSpraks 
212df2381bfSpraks /*
213df2381bfSpraks  * port fop functions that will be the fem hooks.
214df2381bfSpraks  */
215da6c28aaSamw static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
216da6c28aaSamw     caller_context_t *);
217df2381bfSpraks static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
218df2381bfSpraks     struct caller_context *ct);
219df2381bfSpraks static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
220df2381bfSpraks     caller_context_t *ct);
221df2381bfSpraks static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
222df2381bfSpraks     caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
223da6c28aaSamw     uint_t flags, cred_t *cr, caller_context_t *ct);
224df2381bfSpraks static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
225df2381bfSpraks     caller_context_t *ct);
226df2381bfSpraks static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
227da6c28aaSamw     vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
228da6c28aaSamw     caller_context_t *ct, vsecattr_t *vsecp);
229da6c28aaSamw static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
230da6c28aaSamw     caller_context_t *ct, int flags);
231da6c28aaSamw static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
232da6c28aaSamw     caller_context_t *ct, int flags);
233df2381bfSpraks static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
234da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags);
235df2381bfSpraks static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
236da6c28aaSamw     vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
237da6c28aaSamw     vsecattr_t *vsecp);
238da6c28aaSamw static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
239da6c28aaSamw     caller_context_t *ct, int flags);
240da6c28aaSamw static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
241da6c28aaSamw     caller_context_t *ct, int flags);
242df2381bfSpraks static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
243da6c28aaSamw     char *target, cred_t *cr, caller_context_t *ct, int flags);
244df2381bfSpraks static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
245da6c28aaSamw     cred_t *cr, caller_context_t *ct);
246da6c28aaSamw 
247df2381bfSpraks static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
248da6c28aaSamw     char *cname, caller_context_t *ct);
249df2381bfSpraks 
250df2381bfSpraks static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
251df2381bfSpraks 
252df2381bfSpraks 
253df2381bfSpraks /*
254df2381bfSpraks  * Fem hooks.
255df2381bfSpraks  */
256df2381bfSpraks const fs_operation_def_t	port_vnodesrc_template[] = {
257df2381bfSpraks 	VOPNAME_OPEN,		{ .femop_open = port_fop_open },
258df2381bfSpraks 	VOPNAME_READ,		{ .femop_read = port_fop_read },
259df2381bfSpraks 	VOPNAME_WRITE,		{ .femop_write = port_fop_write },
260df2381bfSpraks 	VOPNAME_MAP,		{ .femop_map = port_fop_map },
261df2381bfSpraks 	VOPNAME_SETATTR,	{ .femop_setattr = port_fop_setattr },
262df2381bfSpraks 	VOPNAME_CREATE,		{ .femop_create = port_fop_create },
263df2381bfSpraks 	VOPNAME_REMOVE,		{ .femop_remove = port_fop_remove },
264df2381bfSpraks 	VOPNAME_LINK,		{ .femop_link = port_fop_link },
265df2381bfSpraks 	VOPNAME_RENAME,		{ .femop_rename = port_fop_rename },
266df2381bfSpraks 	VOPNAME_MKDIR,		{ .femop_mkdir = port_fop_mkdir },
267df2381bfSpraks 	VOPNAME_RMDIR,		{ .femop_rmdir = port_fop_rmdir },
268df2381bfSpraks 	VOPNAME_READDIR,	{ .femop_readdir = port_fop_readdir },
269df2381bfSpraks 	VOPNAME_SYMLINK,	{ .femop_symlink = port_fop_symlink },
270df2381bfSpraks 	VOPNAME_SETSECATTR,	{ .femop_setsecattr = port_fop_setsecattr },
271df2381bfSpraks 	VOPNAME_VNEVENT,	{ .femop_vnevent = port_fop_vnevent },
272df2381bfSpraks 	NULL,	NULL
273df2381bfSpraks };
274df2381bfSpraks 
275df2381bfSpraks /*
276df2381bfSpraks  * Fsem - vfs ops hooks
277df2381bfSpraks  */
278df2381bfSpraks const fs_operation_def_t	port_vfssrc_template[] = {
279df2381bfSpraks 	VFSNAME_UNMOUNT,	{ .fsemop_unmount = port_fop_unmount },
280df2381bfSpraks 	NULL,	NULL
281df2381bfSpraks };
282df2381bfSpraks 
283df2381bfSpraks fem_t *fop_femop;
284df2381bfSpraks fsem_t *fop_fsemop;
285df2381bfSpraks 
286df2381bfSpraks static fem_t *
port_fop_femop()287df2381bfSpraks port_fop_femop()
288df2381bfSpraks {
289df2381bfSpraks 	fem_t *femp;
290df2381bfSpraks 	if (fop_femop != NULL)
291df2381bfSpraks 		return (fop_femop);
292df2381bfSpraks 	if (fem_create("portfop_fem",
293df2381bfSpraks 	    (const struct fs_operation_def *)port_vnodesrc_template,
294df2381bfSpraks 	    (fem_t **)&femp)) {
295df2381bfSpraks 		return (NULL);
296df2381bfSpraks 	}
29775d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&fop_femop, NULL, femp) != NULL) {
298df2381bfSpraks 		/*
299df2381bfSpraks 		 * some other thread beat us to it.
300df2381bfSpraks 		 */
301df2381bfSpraks 		fem_free(femp);
302df2381bfSpraks 	}
303df2381bfSpraks 	return (fop_femop);
304df2381bfSpraks }
305df2381bfSpraks 
306df2381bfSpraks static fsem_t *
port_fop_fsemop()307df2381bfSpraks port_fop_fsemop()
308df2381bfSpraks {
309df2381bfSpraks 	fsem_t *fsemp;
310df2381bfSpraks 	if (fop_fsemop != NULL)
311df2381bfSpraks 		return (fop_fsemop);
312df2381bfSpraks 	if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
313df2381bfSpraks 		return (NULL);
314df2381bfSpraks 	}
31575d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&fop_fsemop, NULL, fsemp) != NULL) {
316df2381bfSpraks 		/*
317df2381bfSpraks 		 * some other thread beat us to it.
318df2381bfSpraks 		 */
319df2381bfSpraks 		fsem_free(fsemp);
320df2381bfSpraks 	}
321df2381bfSpraks 	return (fop_fsemop);
322df2381bfSpraks }
323df2381bfSpraks 
324df2381bfSpraks /*
325df2381bfSpraks  * port_fop_callback()
326df2381bfSpraks  * - PORT_CALLBACK_DEFAULT
327df2381bfSpraks  *	The file event will be delivered to the application.
328df2381bfSpraks  * - PORT_CALLBACK_DISSOCIATE
329df2381bfSpraks  *	The object will be dissociated from  the port.
330df2381bfSpraks  * - PORT_CALLBACK_CLOSE
331df2381bfSpraks  *	The object will be dissociated from the port because the port
332df2381bfSpraks  *	is being closed.
333df2381bfSpraks  */
334df2381bfSpraks /* ARGSUSED */
335df2381bfSpraks static int
port_fop_callback(void * arg,int * events,pid_t pid,int flag,void * evp)336df2381bfSpraks port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
337df2381bfSpraks {
338df2381bfSpraks 	portfop_t	*pfp = (portfop_t *)arg;
339df2381bfSpraks 	port_kevent_t	*pkevp = (port_kevent_t *)evp;
340df2381bfSpraks 	int		error = 0;
341df2381bfSpraks 
342df2381bfSpraks 	ASSERT((events != NULL));
343df2381bfSpraks 	if (flag == PORT_CALLBACK_DEFAULT) {
344df2381bfSpraks 		if (curproc->p_pid != pid) {
345df2381bfSpraks 				return (EACCES); /* deny delivery of events */
346df2381bfSpraks 		}
347df2381bfSpraks 
348df2381bfSpraks 		*events = pkevp->portkev_events;
349df2381bfSpraks 		pkevp->portkev_events = 0;
350df2381bfSpraks 		if (pfp != NULL) {
351df2381bfSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
352df2381bfSpraks 		}
353df2381bfSpraks 	}
354df2381bfSpraks 	return (error);
355df2381bfSpraks }
356df2381bfSpraks 
357df2381bfSpraks /*
358df2381bfSpraks  * Inserts a portfop_t into the port sources cache's.
359df2381bfSpraks  */
360df2381bfSpraks static void
port_pcache_insert(portfop_cache_t * pfcp,portfop_t * pfp)361df2381bfSpraks port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
362df2381bfSpraks {
363df2381bfSpraks 	portfop_t	**bucket;
364df2381bfSpraks 
365df2381bfSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
366df2381bfSpraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
367df2381bfSpraks 	pfp->pfop_hashnext = *bucket;
368df2381bfSpraks 	*bucket = pfp;
369df2381bfSpraks 	pfcp->pfc_objcount++;
370df2381bfSpraks }
371df2381bfSpraks 
372df2381bfSpraks /*
373df2381bfSpraks  * Remove the pfp from the port source cache.
374df2381bfSpraks  */
375df2381bfSpraks static void
port_pcache_delete(portfop_cache_t * pfcp,portfop_t * pfp)376df2381bfSpraks port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
377df2381bfSpraks {
378df2381bfSpraks 	portfop_t	*lpdp;
379df2381bfSpraks 	portfop_t	*cpdp;
380df2381bfSpraks 	portfop_t	**bucket;
381df2381bfSpraks 
382df2381bfSpraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
383df2381bfSpraks 	cpdp = *bucket;
384df2381bfSpraks 	if (pfp == cpdp) {
385df2381bfSpraks 		*bucket = pfp->pfop_hashnext;
386df2381bfSpraks 	} else {
387df2381bfSpraks 		while (cpdp != NULL) {
388df2381bfSpraks 			lpdp = cpdp;
389df2381bfSpraks 			cpdp = cpdp->pfop_hashnext;
390df2381bfSpraks 			if (cpdp == pfp) {
391df2381bfSpraks 				/* portfop struct found */
392df2381bfSpraks 				lpdp->pfop_hashnext = pfp->pfop_hashnext;
393df2381bfSpraks 				break;
394df2381bfSpraks 			}
395df2381bfSpraks 		}
396df2381bfSpraks 	}
397df2381bfSpraks 	pfcp->pfc_objcount--;
398df2381bfSpraks }
399df2381bfSpraks 
400df2381bfSpraks /*
401df2381bfSpraks  * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
402df2381bfSpraks  * when these routines are called.
403df2381bfSpraks  *
404df2381bfSpraks  * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
405df2381bfSpraks  * It is used to discard the oldtest inactive pfp if the number of entries
406df2381bfSpraks  * exceed the limit.
407df2381bfSpraks  */
408df2381bfSpraks static void
port_fop_listinsert(portfop_vp_t * pvp,portfop_t * pfp,int where)409df2381bfSpraks port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
410df2381bfSpraks {
411df2381bfSpraks 	if (where == 1) {
412df2381bfSpraks 		list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
413df2381bfSpraks 	} else {
414df2381bfSpraks 		list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
415df2381bfSpraks 	}
416df2381bfSpraks 	if (pvp->pvp_lpfop == NULL) {
417df2381bfSpraks 		pvp->pvp_lpfop = pfp;
418df2381bfSpraks 	}
419df2381bfSpraks 	pvp->pvp_cnt++;
420df2381bfSpraks }
421df2381bfSpraks 
422df2381bfSpraks static void
port_fop_listinsert_head(portfop_vp_t * pvp,portfop_t * pfp)423df2381bfSpraks port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
424df2381bfSpraks {
425df2381bfSpraks 	port_fop_listinsert(pvp, pfp, 1);
426df2381bfSpraks }
427df2381bfSpraks 
428df2381bfSpraks static void
port_fop_listinsert_tail(portfop_vp_t * pvp,portfop_t * pfp)429df2381bfSpraks port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
430df2381bfSpraks {
431df2381bfSpraks 	/*
432df2381bfSpraks 	 * We point lpfop to an inactive one, if it was initially pointing
433df2381bfSpraks 	 * to an active one. Insert to the tail is done only when a pfp goes
434df2381bfSpraks 	 * inactive.
435df2381bfSpraks 	 */
436df2381bfSpraks 	if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
437df2381bfSpraks 		pvp->pvp_lpfop = pfp;
438df2381bfSpraks 	}
439df2381bfSpraks 	port_fop_listinsert(pvp, pfp, 0);
440df2381bfSpraks }
441df2381bfSpraks 
442df2381bfSpraks static void
port_fop_listremove(portfop_vp_t * pvp,portfop_t * pfp)443df2381bfSpraks port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
444df2381bfSpraks {
445df2381bfSpraks 	if (pvp->pvp_lpfop == pfp) {
446df2381bfSpraks 		pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
447df2381bfSpraks 	}
448df2381bfSpraks 
449df2381bfSpraks 	list_remove(&pvp->pvp_pfoplist, (void *)pfp);
450df2381bfSpraks 
451df2381bfSpraks 	pvp->pvp_cnt--;
452df2381bfSpraks 	if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
453df2381bfSpraks 		pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
454df2381bfSpraks 	}
455df2381bfSpraks }
456df2381bfSpraks 
457df2381bfSpraks static void
port_fop_listmove(portfop_vp_t * pvp,list_t * tlist)458df2381bfSpraks port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
459df2381bfSpraks {
460df2381bfSpraks 	list_move_tail(tlist, &pvp->pvp_pfoplist);
461df2381bfSpraks 	pvp->pvp_lpfop = NULL;
462df2381bfSpraks 	pvp->pvp_cnt = 0;
463df2381bfSpraks }
464df2381bfSpraks 
465df2381bfSpraks /*
466df2381bfSpraks  * Remove a portfop_t from the port cache hash table and discard it.
467df2381bfSpraks  * It is called only when pfp is not on the vnode's list. Otherwise,
468df2381bfSpraks  * port_remove_fop() is called.
469df2381bfSpraks  */
470df2381bfSpraks void
port_pcache_remove_fop(portfop_cache_t * pfcp,portfop_t * pfp)471df2381bfSpraks port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
472df2381bfSpraks {
473df2381bfSpraks 	port_kevent_t	*pkevp;
474df2381bfSpraks 
475df2381bfSpraks 
476df2381bfSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
477df2381bfSpraks 
478df2381bfSpraks 	pkevp = pfp->pfop_pev;
479df2381bfSpraks 	pfp->pfop_pev = NULL;
480df2381bfSpraks 
481df2381bfSpraks 	if (pkevp != NULL) {
482df2381bfSpraks 		(void) port_remove_done_event(pkevp);
483df2381bfSpraks 		port_free_event_local(pkevp, 0);
484df2381bfSpraks 	}
485df2381bfSpraks 
486df2381bfSpraks 	port_pcache_delete(pfcp, pfp);
487df2381bfSpraks 
488df2381bfSpraks 	if (pfp->pfop_cname != NULL)
489df2381bfSpraks 		kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
490df2381bfSpraks 	kmem_free(pfp, sizeof (portfop_t));
491df2381bfSpraks 	if (pfcp->pfc_objcount == 0)
492df2381bfSpraks 		cv_signal(&pfcp->pfc_lclosecv);
493df2381bfSpraks }
494df2381bfSpraks 
495df2381bfSpraks /*
496df2381bfSpraks  * if we have too many watches on the vnode, attempt to discard an
497df2381bfSpraks  * inactive one.
498df2381bfSpraks  */
499df2381bfSpraks static void
port_fop_trimpfplist(vnode_t * vp)500df2381bfSpraks port_fop_trimpfplist(vnode_t *vp)
501df2381bfSpraks {
502df2381bfSpraks 	portfop_vp_t *pvp;
503df2381bfSpraks 	portfop_t *pfp = NULL;
504df2381bfSpraks 	portfop_cache_t *pfcp;
5056b5ad791Spraks 	vnode_t	*tdvp;
506df2381bfSpraks 
507df2381bfSpraks 	/*
508df2381bfSpraks 	 * Due to a reference the vnode cannot disappear, v_fopdata should
509df2381bfSpraks 	 * not change.
510df2381bfSpraks 	 */
511df2381bfSpraks 	if ((pvp = vp->v_fopdata) != NULL &&
512df2381bfSpraks 	    pvp->pvp_cnt > port_fop_maxpfps) {
513df2381bfSpraks 		mutex_enter(&pvp->pvp_mutex);
514df2381bfSpraks 		pfp = pvp->pvp_lpfop;
515df2381bfSpraks 		pfcp = pfp->pfop_pcache;
516df2381bfSpraks 		/*
517df2381bfSpraks 		 * only if we can get the cache lock, we need to
518df2381bfSpraks 		 * do this due to reverse lock order and some thread
519df2381bfSpraks 		 * that may be trying to reactivate this entry.
520df2381bfSpraks 		 */
521df2381bfSpraks 		if (mutex_tryenter(&pfcp->pfc_lock)) {
522df2381bfSpraks 			if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
523df2381bfSpraks 			    !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
524df2381bfSpraks 				port_fop_listremove(pvp, pfp);
525df2381bfSpraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
526df2381bfSpraks 			} else {
527df2381bfSpraks 				mutex_exit(&pfcp->pfc_lock);
528df2381bfSpraks 				pfp = NULL;
529df2381bfSpraks 			}
530df2381bfSpraks 		} else {
531df2381bfSpraks 			pfp = NULL;
532df2381bfSpraks 		}
533df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
534df2381bfSpraks 
535df2381bfSpraks 		/*
536df2381bfSpraks 		 * discard pfp if any.
537df2381bfSpraks 		 */
538df2381bfSpraks 		if (pfp != NULL) {
5396b5ad791Spraks 			tdvp = pfp->pfop_dvp;
540df2381bfSpraks 			port_pcache_remove_fop(pfcp, pfp);
541df2381bfSpraks 			mutex_exit(&pfcp->pfc_lock);
5426b5ad791Spraks 			if (tdvp != NULL)
5436b5ad791Spraks 				VN_RELE(tdvp);
544df2381bfSpraks 		}
545df2381bfSpraks 	}
546df2381bfSpraks }
547df2381bfSpraks 
5486b5ad791Spraks /*
5496b5ad791Spraks  * This routine returns 1, if the vnode can be rele'ed by the caller.
5506b5ad791Spraks  * The caller has to VN_RELE the vnode with out holding any
5516b5ad791Spraks  * locks.
5526b5ad791Spraks  */
5536b5ad791Spraks int
port_fop_femuninstall(vnode_t * vp)554df2381bfSpraks port_fop_femuninstall(vnode_t *vp)
555df2381bfSpraks {
556df2381bfSpraks 	portfop_vp_t	*pvp;
557df2381bfSpraks 	vfs_t		*vfsp;
558df2381bfSpraks 	portfop_vfs_t *pvfsp;
559df2381bfSpraks 	portfop_vfs_hash_t	*pvfsh;
560df2381bfSpraks 	kmutex_t	*mtx;
5616b5ad791Spraks 	int	ret = 0;
562df2381bfSpraks 
563df2381bfSpraks 	/*
564df2381bfSpraks 	 * if list is empty, uninstall fem.
565df2381bfSpraks 	 */
566df2381bfSpraks 	pvp = vp->v_fopdata;
567df2381bfSpraks 	ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
568df2381bfSpraks 
569df2381bfSpraks 	/*
570df2381bfSpraks 	 * make sure the list is empty.
571df2381bfSpraks 	 */
572df2381bfSpraks 	if (!list_head(&pvp->pvp_pfoplist)) {
573df2381bfSpraks 
574df2381bfSpraks 		/*
575df2381bfSpraks 		 * we could possibly uninstall the fem hooks when
576df2381bfSpraks 		 * the vnode becomes inactive and the v_fopdata is
577073af7d9Spraks 		 * free. But the hooks get triggered unnecessarily
578df2381bfSpraks 		 * even though there are no active watches. So, we
579df2381bfSpraks 		 * uninstall it here.
580df2381bfSpraks 		 */
581df2381bfSpraks 		(void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
582df2381bfSpraks 		pvp->pvp_femp = NULL;
583df2381bfSpraks 
584df2381bfSpraks 
585df2381bfSpraks 		/*
586073af7d9Spraks 		 * If we successfully uninstalled fem, no process is watching
587073af7d9Spraks 		 * this vnode, Remove it from the vfs's list of watched vnodes.
588df2381bfSpraks 		 */
589df2381bfSpraks 		pvfsp = pvp->pvp_pvfsp;
590df2381bfSpraks 		vfsp = vp->v_vfsp;
591df2381bfSpraks 		pvfsh = PORTFOP_PVFSH(vfsp);
592df2381bfSpraks 		mtx = &pvfsh->pvfshash_mutex;
593df2381bfSpraks 		mutex_enter(mtx);
594df2381bfSpraks 		/*
595df2381bfSpraks 		 * If unmount is in progress, that thread will remove and
596df2381bfSpraks 		 * release the vnode from the vfs's list, just leave.
597df2381bfSpraks 		 */
598df2381bfSpraks 		if (!pvfsp->pvfs_unmount) {
599df2381bfSpraks 			list_remove(&pvfsp->pvfs_pvplist, pvp);
600df2381bfSpraks 			mutex_exit(mtx);
6016b5ad791Spraks 			ret = 1;
602df2381bfSpraks 		} else {
603df2381bfSpraks 			mutex_exit(mtx);
604df2381bfSpraks 		}
605df2381bfSpraks 	}
606e1054916SRobert Mustacchi 	mutex_exit(&pvp->pvp_mutex);
6076b5ad791Spraks 	return (ret);
608df2381bfSpraks }
609df2381bfSpraks 
610df2381bfSpraks /*
611df2381bfSpraks  * Remove pfp from the vnode's watch list and the cache and discard it.
612df2381bfSpraks  * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
6136b5ad791Spraks  * Returns 1 if pfp removed successfully.
614df2381bfSpraks  *
615df2381bfSpraks  * The *active is set to indicate if the pfp was still active(no events had
616df2381bfSpraks  * been posted, or the posted event had not been collected yet and it was
617df2381bfSpraks  * able to remove it from the port's queue).
6186b5ad791Spraks  *
6196b5ad791Spraks  * vpp and dvpp will point to the vnode and directory vnode which the caller
6206b5ad791Spraks  * is required to VN_RELE without holding any locks.
621df2381bfSpraks  */
622df2381bfSpraks int
port_remove_fop(portfop_t * pfp,portfop_cache_t * pfcp,int cleanup,int * active,vnode_t ** vpp,vnode_t ** dvpp)623df2381bfSpraks port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
6246b5ad791Spraks     int *active, vnode_t **vpp, vnode_t **dvpp)
625df2381bfSpraks {
626df2381bfSpraks 	vnode_t		*vp;
627df2381bfSpraks 	portfop_vp_t	*pvp;
628df2381bfSpraks 	int	tactive = 0;
629df2381bfSpraks 
630df2381bfSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
631df2381bfSpraks 	vp = pfp->pfop_vp;
632df2381bfSpraks 	pvp = vp->v_fopdata;
633df2381bfSpraks 	mutex_enter(&pvp->pvp_mutex);
634df2381bfSpraks 
635df2381bfSpraks 	/*
636df2381bfSpraks 	 * if not cleanup, remove it only if the pfp is still active and
637df2381bfSpraks 	 * is not being removed by some other thread.
638df2381bfSpraks 	 */
639df2381bfSpraks 	if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
640df2381bfSpraks 	    pfp->pfop_flags & PORT_FOP_REMOVING)) {
641df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
642df2381bfSpraks 		return (0);
643df2381bfSpraks 	}
644df2381bfSpraks 
645df2381bfSpraks 	/*
646df2381bfSpraks 	 * mark it inactive.
647df2381bfSpraks 	 */
648df2381bfSpraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
649df2381bfSpraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
650df2381bfSpraks 		tactive = 1;
651df2381bfSpraks 	}
652df2381bfSpraks 
653df2381bfSpraks 	/*
654df2381bfSpraks 	 * Check if the pfp is still on the vnode's list. This can
655df2381bfSpraks 	 * happen if port_fop_excep() is in the process of removing it.
656df2381bfSpraks 	 * In case of cleanup, just mark this pfp as inactive so that no
657df2381bfSpraks 	 * new events (VNEVENT) will be delivered, and remove it from the
658df2381bfSpraks 	 * event queue if it was already queued. Since the cache lock is
659df2381bfSpraks 	 * held, the pfp will not disappear, even though it is being
660df2381bfSpraks 	 * removed.
661df2381bfSpraks 	 */
662df2381bfSpraks 	if (pfp->pfop_flags & PORT_FOP_REMOVING) {
663df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
664df2381bfSpraks 		if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
665df2381bfSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
666df2381bfSpraks 			tactive = 1;
667df2381bfSpraks 		}
668df2381bfSpraks 		if (active) {
669df2381bfSpraks 			*active = tactive;
670df2381bfSpraks 		}
671df2381bfSpraks 		return (1);
672df2381bfSpraks 	}
673df2381bfSpraks 
674df2381bfSpraks 	/*
675df2381bfSpraks 	 * if we find an event on the queue and removed it, then this
676df2381bfSpraks 	 * association is considered active.
677df2381bfSpraks 	 */
678df2381bfSpraks 	if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
679df2381bfSpraks 		pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
680df2381bfSpraks 		tactive = 1;
681df2381bfSpraks 	}
682df2381bfSpraks 
683df2381bfSpraks 	if (active) {
684df2381bfSpraks 		*active = tactive;
685df2381bfSpraks 	}
686df2381bfSpraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
687df2381bfSpraks 
688df2381bfSpraks 	/*
689df2381bfSpraks 	 * remove pfp from the vnode's list
690df2381bfSpraks 	 */
691df2381bfSpraks 	port_fop_listremove(pvp, pfp);
692df2381bfSpraks 
693df2381bfSpraks 	/*
694df2381bfSpraks 	 * If no more associations on the vnode, uninstall fem hooks.
695df2381bfSpraks 	 * The pvp mutex will be released in this routine.
696df2381bfSpraks 	 */
6976b5ad791Spraks 	if (port_fop_femuninstall(vp))
6986b5ad791Spraks 		*vpp = vp;
6996b5ad791Spraks 	*dvpp = pfp->pfop_dvp;
700df2381bfSpraks 	port_pcache_remove_fop(pfcp, pfp);
701df2381bfSpraks 	return (1);
702df2381bfSpraks }
703df2381bfSpraks 
704df2381bfSpraks /*
705df2381bfSpraks  * This routine returns a pointer to a cached portfop entry, or NULL if it
706df2381bfSpraks  * does not find it in the hash table. The object pointer is used as index.
707df2381bfSpraks  * The entries are hashed by the object's address. We need to match the pid
708df2381bfSpraks  * as the evet port can be shared between processes. The file events
709df2381bfSpraks  * watches are per process only.
710df2381bfSpraks  */
711df2381bfSpraks portfop_t *
port_cache_lookup_fop(portfop_cache_t * pfcp,pid_t pid,uintptr_t obj)712df2381bfSpraks port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
713df2381bfSpraks {
714df2381bfSpraks 	portfop_t	*pfp = NULL;
715df2381bfSpraks 	portfop_t	**bucket;
716df2381bfSpraks 
717df2381bfSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
718df2381bfSpraks 	bucket = PORT_FOP_BUCKET(pfcp, obj);
719df2381bfSpraks 	pfp = *bucket;
720df2381bfSpraks 	while (pfp != NULL) {
721df2381bfSpraks 		if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
722df2381bfSpraks 			break;
723df2381bfSpraks 		pfp = pfp->pfop_hashnext;
724df2381bfSpraks 	}
725df2381bfSpraks 	return (pfp);
726df2381bfSpraks }
727df2381bfSpraks 
728df2381bfSpraks /*
729df2381bfSpraks  * Given the file name, get the vnode and also the directory vnode
730df2381bfSpraks  * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
731df2381bfSpraks  * the vnode(s).
732df2381bfSpraks  */
733df2381bfSpraks int
port_fop_getdvp(void * objptr,vnode_t ** vp,vnode_t ** dvp,char ** cname,int * len,int follow)734*abb88ab1SRobert Mustacchi port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
735*abb88ab1SRobert Mustacchi     int *len, int follow)
736df2381bfSpraks {
737df2381bfSpraks 	int error = 0;
738df2381bfSpraks 	struct pathname pn;
739df2381bfSpraks 	char *fname;
740df2381bfSpraks 
741df2381bfSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
742df2381bfSpraks 		fname = ((file_obj_t *)objptr)->fo_name;
743df2381bfSpraks #ifdef  _SYSCALL32_IMPL
744df2381bfSpraks 	} else {
745df2381bfSpraks 		fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
746df2381bfSpraks #endif	/* _SYSCALL32_IMPL */
747df2381bfSpraks 	}
748df2381bfSpraks 
749df2381bfSpraks 	/*
750df2381bfSpraks 	 * lookuppn may fail with EINVAL, if dvp is  non-null(like when
751df2381bfSpraks 	 * looking for "."). So call again with dvp = NULL.
752df2381bfSpraks 	 */
753df2381bfSpraks 	if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
754df2381bfSpraks 		return (error);
755df2381bfSpraks 	}
756df2381bfSpraks 
757df2381bfSpraks 	error = lookuppn(&pn, NULL, follow, dvp, vp);
758df2381bfSpraks 	if (error == EINVAL) {
759df2381bfSpraks 		pn_free(&pn);
760df2381bfSpraks 		if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
761df2381bfSpraks 			return (error);
762df2381bfSpraks 		}
763df2381bfSpraks 		error = lookuppn(&pn, NULL, follow, NULL, vp);
764df2381bfSpraks 		if (dvp != NULL) {
765df2381bfSpraks 			*dvp = NULL;
766df2381bfSpraks 		}
767df2381bfSpraks 	}
768df2381bfSpraks 
769df2381bfSpraks 	if (error == 0 && cname != NULL && len != NULL) {
770df2381bfSpraks 		pn_setlast(&pn);
771df2381bfSpraks 		*len = pn.pn_pathlen;
772df2381bfSpraks 		*cname = kmem_alloc(*len + 1, KM_SLEEP);
773df2381bfSpraks 		(void) strcpy(*cname, pn.pn_path);
774df2381bfSpraks 	} else {
775df2381bfSpraks 		if (cname != NULL && len != NULL) {
776df2381bfSpraks 			*cname = NULL;
777df2381bfSpraks 			*len = 0;
778df2381bfSpraks 		}
779df2381bfSpraks 	}
780df2381bfSpraks 
781df2381bfSpraks 	pn_free(&pn);
782df2381bfSpraks 	return (error);
783df2381bfSpraks }
784df2381bfSpraks 
785df2381bfSpraks port_source_t *
port_getsrc(port_t * pp,int source)786df2381bfSpraks port_getsrc(port_t *pp, int source)
787df2381bfSpraks {
788df2381bfSpraks 	port_source_t *pse;
789df2381bfSpraks 	int	lock = 0;
790df2381bfSpraks 	/*
791df2381bfSpraks 	 * get the port source structure.
792df2381bfSpraks 	 */
793df2381bfSpraks 	if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
794df2381bfSpraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
795df2381bfSpraks 		lock = 1;
796df2381bfSpraks 	}
797df2381bfSpraks 
798df2381bfSpraks 	pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
799df2381bfSpraks 	for (; pse != NULL; pse = pse->portsrc_next) {
800df2381bfSpraks 		if (pse->portsrc_source == source)
801df2381bfSpraks 			break;
802df2381bfSpraks 	}
803df2381bfSpraks 
804df2381bfSpraks 	if (lock) {
805df2381bfSpraks 		mutex_exit(&pp->port_queue.portq_source_mutex);
806df2381bfSpraks 	}
807df2381bfSpraks 	return (pse);
808df2381bfSpraks }
809df2381bfSpraks 
810df2381bfSpraks 
811df2381bfSpraks /*
812073af7d9Spraks  * Compare time stamps and generate an event if it has changed.
813073af7d9Spraks  * Note that the port cache pointer will be valid due to a reference
814073af7d9Spraks  * to the port. We need to grab the port cache lock and verify that
815073af7d9Spraks  * the pfp is still the same before proceeding to deliver an event.
816df2381bfSpraks  */
817df2381bfSpraks static void
port_check_timestamp(portfop_cache_t * pfcp,vnode_t * vp,vnode_t * dvp,portfop_t * pfp,void * objptr,uintptr_t object)818073af7d9Spraks port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
819073af7d9Spraks     portfop_t *pfp, void *objptr, uintptr_t object)
820df2381bfSpraks {
821df2381bfSpraks 	vattr_t		vatt;
822df2381bfSpraks 	portfop_vp_t	*pvp = vp->v_fopdata;
823df2381bfSpraks 	int		events = 0;
824df2381bfSpraks 	port_kevent_t	*pkevp;
825df2381bfSpraks 	file_obj_t	*fobj;
826073af7d9Spraks 	portfop_t	*tpfp;
827df2381bfSpraks 
828df2381bfSpraks 	/*
829073af7d9Spraks 	 * If time stamps are specified, get attributes and compare.
830df2381bfSpraks 	 */
831df2381bfSpraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
832df2381bfSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
833df2381bfSpraks 		fobj = (file_obj_t *)objptr;
834df2381bfSpraks 		if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
835df2381bfSpraks 		    fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
836df2381bfSpraks 		    fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
837da6c28aaSamw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
838df2381bfSpraks 				return;
839df2381bfSpraks 			}
840df2381bfSpraks 		} else {
841df2381bfSpraks 			/*
842df2381bfSpraks 			 * timestamp not specified, all 0's,
843df2381bfSpraks 			 */
844df2381bfSpraks 			return;
845df2381bfSpraks 		}
846df2381bfSpraks #ifdef  _SYSCALL32_IMPL
847df2381bfSpraks 	} else {
848df2381bfSpraks 		file_obj32_t	*fobj32;
849df2381bfSpraks 		fobj32 = (file_obj32_t *)objptr;
850df2381bfSpraks 		if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
851df2381bfSpraks 		    fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
852df2381bfSpraks 		    fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
853da6c28aaSamw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
854df2381bfSpraks 				return;
855df2381bfSpraks 			}
856df2381bfSpraks 		} else {
857df2381bfSpraks 			/*
858df2381bfSpraks 			 * timestamp not specified, all 0.
859df2381bfSpraks 			 */
860df2381bfSpraks 			return;
861df2381bfSpraks 		}
862df2381bfSpraks #endif /* _SYSCALL32_IMPL */
863df2381bfSpraks 	}
864df2381bfSpraks 
865073af7d9Spraks 	/*
866073af7d9Spraks 	 * Now grab the cache lock and verify that we are still
867073af7d9Spraks 	 * dealing with the same pfp and curthread is the one
868073af7d9Spraks 	 * which registered it. We need to do this to avoid
869073af7d9Spraks 	 * delivering redundant events.
870073af7d9Spraks 	 */
871073af7d9Spraks 	mutex_enter(&pfcp->pfc_lock);
872073af7d9Spraks 	tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
873073af7d9Spraks 
874073af7d9Spraks 	if (tpfp == NULL || tpfp != pfp ||
875073af7d9Spraks 	    pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
876073af7d9Spraks 	    pfp->pfop_callrid != curthread ||
877073af7d9Spraks 	    !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
878073af7d9Spraks 		/*
879073af7d9Spraks 		 * Some other event was delivered, the file
880073af7d9Spraks 		 * watch was removed or reassociated. Just
881073af7d9Spraks 		 * ignore it and leave
882073af7d9Spraks 		 */
883073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
884073af7d9Spraks 		return;
885073af7d9Spraks 	}
886073af7d9Spraks 
887df2381bfSpraks 	mutex_enter(&pvp->pvp_mutex);
888df2381bfSpraks 	/*
889073af7d9Spraks 	 * The pfp cannot disappear as the port cache lock is held.
890df2381bfSpraks 	 * While the pvp_mutex is held, no events will get delivered.
891df2381bfSpraks 	 */
892df2381bfSpraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
893df2381bfSpraks 	    !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
894df2381bfSpraks 		if (get_udatamodel() == DATAMODEL_NATIVE) {
895df2381bfSpraks 			fobj = (file_obj_t *)objptr;
896df2381bfSpraks 			if (pfp->pfop_events & FILE_ACCESS &&
897df2381bfSpraks 			    (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
898df2381bfSpraks 			    (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
899df2381bfSpraks 			    vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
900df2381bfSpraks 				events |= FILE_ACCESS;
901df2381bfSpraks 
902df2381bfSpraks 			if (pfp->pfop_events & FILE_MODIFIED &&
903df2381bfSpraks 			    (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
904df2381bfSpraks 			    (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
905df2381bfSpraks 			    vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
906df2381bfSpraks 				events |= FILE_MODIFIED;
907df2381bfSpraks 
908df2381bfSpraks 			if (pfp->pfop_events & FILE_ATTRIB &&
909df2381bfSpraks 			    (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
910df2381bfSpraks 			    (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
911df2381bfSpraks 			    vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
912df2381bfSpraks 				events |= FILE_ATTRIB;
913df2381bfSpraks #ifdef  _SYSCALL32_IMPL
914df2381bfSpraks 		} else {
915df2381bfSpraks 			file_obj32_t	*fobj32;
916df2381bfSpraks 			fobj32 = (file_obj32_t *)objptr;
917df2381bfSpraks 			if (pfp->pfop_events & FILE_ACCESS &&
918df2381bfSpraks 			    (fobj32->fo_atime.tv_sec ||
919df2381bfSpraks 			    fobj32->fo_atime.tv_nsec) &&
920df2381bfSpraks 			    (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
921df2381bfSpraks 			    vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
922df2381bfSpraks 				events |= FILE_ACCESS;
923df2381bfSpraks 
924df2381bfSpraks 			if (pfp->pfop_events & FILE_MODIFIED &&
925df2381bfSpraks 			    (fobj32->fo_mtime.tv_sec ||
926df2381bfSpraks 			    fobj32->fo_mtime.tv_nsec) &&
927df2381bfSpraks 			    (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
928df2381bfSpraks 			    vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
929df2381bfSpraks 				events |= FILE_MODIFIED;
930df2381bfSpraks 
931df2381bfSpraks 			if (pfp->pfop_events & FILE_ATTRIB &&
932df2381bfSpraks 			    (fobj32->fo_ctime.tv_sec ||
933df2381bfSpraks 			    fobj32->fo_ctime.tv_nsec) &&
934df2381bfSpraks 			    (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
935df2381bfSpraks 			    vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
936df2381bfSpraks 				events |= FILE_ATTRIB;
937df2381bfSpraks #endif /* _SYSCALL32_IMPL */
938df2381bfSpraks 		}
939df2381bfSpraks 
940df2381bfSpraks 		/*
941df2381bfSpraks 		 * No events to deliver
942df2381bfSpraks 		 */
943df2381bfSpraks 		if (events == 0) {
944df2381bfSpraks 			mutex_exit(&pvp->pvp_mutex);
945073af7d9Spraks 			mutex_exit(&pfcp->pfc_lock);
946df2381bfSpraks 			return;
947df2381bfSpraks 		}
948df2381bfSpraks 
949df2381bfSpraks 		/*
950df2381bfSpraks 		 * Deliver the event now.
951df2381bfSpraks 		 */
952df2381bfSpraks 		pkevp = pfp->pfop_pev;
953df2381bfSpraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
954df2381bfSpraks 		pkevp->portkev_events |= events;
955df2381bfSpraks 		/*
956df2381bfSpraks 		 * Move it to the tail as active once are in the
957073af7d9Spraks 		 * beginning of the list.
958df2381bfSpraks 		 */
959df2381bfSpraks 		port_fop_listremove(pvp, pfp);
960df2381bfSpraks 		port_fop_listinsert_tail(pvp, pfp);
961df2381bfSpraks 		port_send_event(pkevp);
962df2381bfSpraks 		pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
963df2381bfSpraks 	}
964df2381bfSpraks 	mutex_exit(&pvp->pvp_mutex);
965073af7d9Spraks 	mutex_exit(&pfcp->pfc_lock);
966df2381bfSpraks }
967df2381bfSpraks 
968df2381bfSpraks /*
969df2381bfSpraks  * Add the event source to the port and return the port source cache pointer.
970df2381bfSpraks  */
971df2381bfSpraks int
port_fop_associate_source(portfop_cache_t ** pfcpp,port_t * pp,int source)972df2381bfSpraks port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
973df2381bfSpraks {
974df2381bfSpraks 	portfop_cache_t *pfcp;
975df2381bfSpraks 	port_source_t	*pse;
976df2381bfSpraks 	int		error;
977df2381bfSpraks 
978df2381bfSpraks 	/*
979df2381bfSpraks 	 * associate PORT_SOURCE_FILE source with the port, if it is
980df2381bfSpraks 	 * not associated yet. Note the PORT_SOURCE_FILE source is
981df2381bfSpraks 	 * associated once and will not be dissociated.
982df2381bfSpraks 	 */
983df2381bfSpraks 	if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
984df2381bfSpraks 		if (error = port_associate_ksource(pp->port_fd, source,
985df2381bfSpraks 		    &pse, port_close_fop, pp, NULL)) {
986df2381bfSpraks 			*pfcpp = NULL;
987df2381bfSpraks 			return (error);
988df2381bfSpraks 		}
989df2381bfSpraks 	}
990df2381bfSpraks 
991df2381bfSpraks 	/*
992df2381bfSpraks 	 * Get the portfop cache pointer.
993df2381bfSpraks 	 */
994df2381bfSpraks 	if ((pfcp = pse->portsrc_data) == NULL) {
995df2381bfSpraks 		/*
996df2381bfSpraks 		 * This is the first time that a file is being associated,
997df2381bfSpraks 		 * create the portfop cache.
998df2381bfSpraks 		 */
999df2381bfSpraks 		pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
1000df2381bfSpraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
1001df2381bfSpraks 		if (pse->portsrc_data == NULL) {
1002df2381bfSpraks 			pse->portsrc_data = pfcp;
1003df2381bfSpraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
1004df2381bfSpraks 		} else {
1005df2381bfSpraks 			/*
1006df2381bfSpraks 			 * someone else created the port cache, free
1007df2381bfSpraks 			 * what we just now allocated.
1008df2381bfSpraks 			 */
1009df2381bfSpraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
1010df2381bfSpraks 			kmem_free(pfcp, sizeof (portfop_cache_t));
1011df2381bfSpraks 			pfcp = pse->portsrc_data;
1012df2381bfSpraks 		}
1013df2381bfSpraks 	}
1014df2381bfSpraks 	*pfcpp = pfcp;
1015df2381bfSpraks 	return (0);
1016df2381bfSpraks }
1017df2381bfSpraks 
1018df2381bfSpraks /*
1019df2381bfSpraks  * Add the given pvp on the file system's list of vnodes watched.
1020df2381bfSpraks  */
1021df2381bfSpraks int
port_fop_pvfsadd(portfop_vp_t * pvp)1022df2381bfSpraks port_fop_pvfsadd(portfop_vp_t *pvp)
1023df2381bfSpraks {
1024df2381bfSpraks 	int error = 0;
1025df2381bfSpraks 	vnode_t	*vp = pvp->pvp_vp;
1026df2381bfSpraks 	portfop_vfs_hash_t *pvfsh;
1027df2381bfSpraks 	portfop_vfs_t	 *pvfsp;
1028df2381bfSpraks 	fsem_t		*fsemp;
1029df2381bfSpraks 
1030df2381bfSpraks 	pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
1031df2381bfSpraks 	mutex_enter(&pvfsh->pvfshash_mutex);
1032df2381bfSpraks 	for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
1033df2381bfSpraks 	    pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
1034df2381bfSpraks 		;
1035df2381bfSpraks 
1036df2381bfSpraks 	if (!pvfsp) {
1037df2381bfSpraks 		if ((fsemp = port_fop_fsemop()) != NULL) {
1038df2381bfSpraks 			if ((error = fsem_install(vp->v_vfsp, fsemp,
1039df2381bfSpraks 			    vp->v_vfsp, OPUNIQ, NULL, NULL))) {
1040df2381bfSpraks 				mutex_exit(&pvfsh->pvfshash_mutex);
1041df2381bfSpraks 				return (error);
1042df2381bfSpraks 			}
1043df2381bfSpraks 		} else {
1044df2381bfSpraks 			mutex_exit(&pvfsh->pvfshash_mutex);
1045df2381bfSpraks 			return (EINVAL);
1046df2381bfSpraks 		}
1047df2381bfSpraks 		pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
1048df2381bfSpraks 		pvfsp->pvfs = vp->v_vfsp;
1049df2381bfSpraks 		list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
1050df2381bfSpraks 		    offsetof(portfop_vp_t, pvp_pvfsnode));
1051df2381bfSpraks 		pvfsp->pvfs_fsemp = fsemp;
1052df2381bfSpraks 		pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
1053df2381bfSpraks 		pvfsh->pvfshash_pvfsp = pvfsp;
1054df2381bfSpraks 	}
1055df2381bfSpraks 
1056df2381bfSpraks 	/*
1057df2381bfSpraks 	 * check if an unmount is in progress.
1058df2381bfSpraks 	 */
1059df2381bfSpraks 	if (!pvfsp->pvfs_unmount) {
1060df2381bfSpraks 		/*
1061df2381bfSpraks 		 * insert the pvp on list.
1062df2381bfSpraks 		 */
1063df2381bfSpraks 		pvp->pvp_pvfsp = pvfsp;
1064df2381bfSpraks 		list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
1065df2381bfSpraks 	} else {
1066df2381bfSpraks 		error = EINVAL;
1067df2381bfSpraks 	}
1068df2381bfSpraks 	mutex_exit(&pvfsh->pvfshash_mutex);
1069df2381bfSpraks 	return (error);
1070df2381bfSpraks }
1071df2381bfSpraks 
1072df2381bfSpraks /*
1073df2381bfSpraks  * Installs the portfop_vp_t data structure on the
1074df2381bfSpraks  * vnode. The 'pvp_femp == NULL' indicates it is not
1075df2381bfSpraks  * active. The fem hooks have to be installed.
1076df2381bfSpraks  * The portfop_vp_t is only freed when the vnode gets freed.
1077df2381bfSpraks  */
1078df2381bfSpraks void
port_install_fopdata(vnode_t * vp)1079df2381bfSpraks port_install_fopdata(vnode_t *vp)
1080df2381bfSpraks {
1081df2381bfSpraks 	portfop_vp_t *npvp;
1082df2381bfSpraks 
1083df2381bfSpraks 	npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
1084df2381bfSpraks 	mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
1085df2381bfSpraks 	list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
1086df2381bfSpraks 	    offsetof(portfop_t, pfop_node));
1087df2381bfSpraks 	npvp->pvp_vp = vp;
1088df2381bfSpraks 	/*
1089df2381bfSpraks 	 * If v_fopdata is not null, some other thread beat us to it.
1090df2381bfSpraks 	 */
109175d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&vp->v_fopdata, NULL, npvp) != NULL) {
1092df2381bfSpraks 		mutex_destroy(&npvp->pvp_mutex);
1093df2381bfSpraks 		list_destroy(&npvp->pvp_pfoplist);
1094df2381bfSpraks 		kmem_free(npvp, sizeof (*npvp));
1095df2381bfSpraks 	}
1096df2381bfSpraks }
1097df2381bfSpraks 
1098df2381bfSpraks 
1099df2381bfSpraks /*
1100df2381bfSpraks  * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
1101df2381bfSpraks  * to the vnode's list. The association is identified by the object pointer
1102df2381bfSpraks  * address and pid.
1103df2381bfSpraks  */
1104df2381bfSpraks int
port_pfp_setup(portfop_t ** pfpp,port_t * pp,vnode_t * vp,portfop_cache_t * pfcp,uintptr_t object,int events,void * user,char * cname,int clen,vnode_t * dvp)1105df2381bfSpraks port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
1106df2381bfSpraks     uintptr_t object, int events, void *user, char *cname, int clen,
1107df2381bfSpraks     vnode_t *dvp)
1108df2381bfSpraks {
1109df2381bfSpraks 	portfop_t	*pfp = NULL;
1110df2381bfSpraks 	port_kevent_t	*pkevp;
1111df2381bfSpraks 	fem_t		*femp;
1112df2381bfSpraks 	int		error = 0;
1113df2381bfSpraks 	portfop_vp_t	*pvp;
1114df2381bfSpraks 
1115df2381bfSpraks 
1116df2381bfSpraks 	/*
1117df2381bfSpraks 	 * The port cache mutex is held.
1118df2381bfSpraks 	 */
1119df2381bfSpraks 	*pfpp  = NULL;
1120df2381bfSpraks 
1121df2381bfSpraks 
1122df2381bfSpraks 	/*
1123df2381bfSpraks 	 * At this point the fem monitor is installed.
1124df2381bfSpraks 	 * Allocate a port event structure per vnode association.
1125df2381bfSpraks 	 */
1126df2381bfSpraks 	if (pfp == NULL) {
1127df2381bfSpraks 		if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1128df2381bfSpraks 		    PORT_ALLOC_CACHED, &pkevp)) {
1129df2381bfSpraks 			return (error);
1130df2381bfSpraks 		}
1131df2381bfSpraks 		pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
1132df2381bfSpraks 		pfp->pfop_pev = pkevp;
1133df2381bfSpraks 	}
1134df2381bfSpraks 
1135df2381bfSpraks 	pfp->pfop_vp = vp;
1136df2381bfSpraks 	pfp->pfop_pid = curproc->p_pid;
1137df2381bfSpraks 	pfp->pfop_pcache = pfcp;
1138df2381bfSpraks 	pfp->pfop_pp = pp;
1139df2381bfSpraks 	pfp->pfop_flags |= PORT_FOP_ACTIVE;
1140df2381bfSpraks 	pfp->pfop_cname = cname;
1141df2381bfSpraks 	pfp->pfop_clen = clen;
1142df2381bfSpraks 	pfp->pfop_dvp = dvp;
1143df2381bfSpraks 	pfp->pfop_object = object;
1144df2381bfSpraks 
1145df2381bfSpraks 	pkevp->portkev_callback = port_fop_callback;
1146df2381bfSpraks 	pkevp->portkev_arg = pfp;
1147df2381bfSpraks 	pkevp->portkev_object = object;
1148df2381bfSpraks 	pkevp->portkev_user = user;
1149df2381bfSpraks 	pkevp->portkev_events = 0;
1150df2381bfSpraks 
1151df2381bfSpraks 	port_pcache_insert(pfcp, pfp);
1152df2381bfSpraks 
1153df2381bfSpraks 	/*
1154df2381bfSpraks 	 * Register a new file events monitor for this file(vnode), if not
1155df2381bfSpraks 	 * done already.
1156df2381bfSpraks 	 */
1157df2381bfSpraks 	if ((pvp = vp->v_fopdata) == NULL) {
1158df2381bfSpraks 		port_install_fopdata(vp);
1159df2381bfSpraks 		pvp = vp->v_fopdata;
1160df2381bfSpraks 	}
1161df2381bfSpraks 
1162df2381bfSpraks 	mutex_enter(&pvp->pvp_mutex);
1163df2381bfSpraks 	/*
1164df2381bfSpraks 	 * if the vnode does not have the file events hooks, install it.
1165df2381bfSpraks 	 */
1166df2381bfSpraks 	if (pvp->pvp_femp == NULL) {
1167df2381bfSpraks 		if ((femp = port_fop_femop()) != NULL) {
1168df2381bfSpraks 			if (!(error = fem_install(pfp->pfop_vp, femp,
1169df2381bfSpraks 			    (void *)vp, OPUNIQ, NULL, NULL))) {
1170df2381bfSpraks 				pvp->pvp_femp = femp;
1171df2381bfSpraks 				/*
1172df2381bfSpraks 				 * add fsem_t hooks to the vfsp and add pvp to
1173df2381bfSpraks 				 * the list of vnodes for this vfs.
1174df2381bfSpraks 				 */
1175df2381bfSpraks 				if (!(error = port_fop_pvfsadd(pvp))) {
1176df2381bfSpraks 					/*
1177df2381bfSpraks 					 * Hold a reference to the vnode since
1178df2381bfSpraks 					 * we successfully installed the hooks.
1179df2381bfSpraks 					 */
1180df2381bfSpraks 					VN_HOLD(vp);
1181df2381bfSpraks 				} else {
1182df2381bfSpraks 					(void) fem_uninstall(vp, femp, vp);
1183df2381bfSpraks 					pvp->pvp_femp = NULL;
1184df2381bfSpraks 				}
1185df2381bfSpraks 			}
1186df2381bfSpraks 		} else {
1187df2381bfSpraks 			error = EINVAL;
1188df2381bfSpraks 		}
1189df2381bfSpraks 	}
1190df2381bfSpraks 
1191df2381bfSpraks 	if (error) {
1192df2381bfSpraks 		/*
1193df2381bfSpraks 		 * pkevp will get freed here.
1194df2381bfSpraks 		 */
1195073af7d9Spraks 		pfp->pfop_cname = NULL;
1196df2381bfSpraks 		port_pcache_remove_fop(pfcp, pfp);
1197df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
1198df2381bfSpraks 		return (error);
1199df2381bfSpraks 	}
1200df2381bfSpraks 
1201df2381bfSpraks 	/*
1202df2381bfSpraks 	 * insert the pfp on the vnode's list. After this
1203df2381bfSpraks 	 * events can get delivered.
1204df2381bfSpraks 	 */
1205df2381bfSpraks 	pfp->pfop_events = events;
1206df2381bfSpraks 	port_fop_listinsert_head(pvp, pfp);
1207df2381bfSpraks 
1208df2381bfSpraks 	mutex_exit(&pvp->pvp_mutex);
12096b5ad791Spraks 	/*
12106b5ad791Spraks 	 * Hold the directory vnode since we have a reference now.
12116b5ad791Spraks 	 */
12126b5ad791Spraks 	if (dvp != NULL)
12136b5ad791Spraks 		VN_HOLD(dvp);
1214df2381bfSpraks 	*pfpp = pfp;
1215df2381bfSpraks 	return (0);
1216df2381bfSpraks }
1217df2381bfSpraks 
1218df2381bfSpraks vnode_t *
port_resolve_vp(vnode_t * vp)1219df2381bfSpraks port_resolve_vp(vnode_t *vp)
1220df2381bfSpraks {
1221df2381bfSpraks 	vnode_t *rvp;
1222df2381bfSpraks 	/*
1223df2381bfSpraks 	 * special case /etc/mnttab(mntfs type). The mntfstype != 0
1224df2381bfSpraks 	 * if mntfs got mounted.
1225df2381bfSpraks 	 */
1226df2381bfSpraks 	if (vfs_mntdummyvp && mntfstype != 0 &&
1227df2381bfSpraks 	    vp->v_vfsp->vfs_fstype == mntfstype) {
1228df2381bfSpraks 		VN_RELE(vp);
1229df2381bfSpraks 		vp = vfs_mntdummyvp;
1230df2381bfSpraks 		VN_HOLD(vfs_mntdummyvp);
1231df2381bfSpraks 	}
1232df2381bfSpraks 
1233df2381bfSpraks 	/*
1234df2381bfSpraks 	 * This should take care of lofs mounted fs systems and nfs4
1235df2381bfSpraks 	 * hardlinks.
1236df2381bfSpraks 	 */
1237da6c28aaSamw 	if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
1238df2381bfSpraks 		VN_HOLD(rvp);
1239df2381bfSpraks 		VN_RELE(vp);
1240df2381bfSpraks 		vp = rvp;
1241df2381bfSpraks 	}
1242df2381bfSpraks 	return (vp);
1243df2381bfSpraks }
1244df2381bfSpraks 
1245df2381bfSpraks /*
1246df2381bfSpraks  * Register a file events watch on the given file associated to the port *pp.
1247df2381bfSpraks  *
1248df2381bfSpraks  * The association is identified by the object pointer and the pid.
1249df2381bfSpraks  * The events argument contains the events to be monitored for.
12506b5ad791Spraks  *
12516b5ad791Spraks  * The vnode will have a VN_HOLD once the fem hooks are installed.
12526b5ad791Spraks  *
12536b5ad791Spraks  * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
12546b5ad791Spraks  * that the directory vnode pointer does not change.
1255df2381bfSpraks  */
1256df2381bfSpraks int
port_associate_fop(port_t * pp,int source,uintptr_t object,int events,void * user)1257df2381bfSpraks port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
1258df2381bfSpraks     void *user)
1259df2381bfSpraks {
1260df2381bfSpraks 	portfop_cache_t	*pfcp;
126172102e74SBryan Cantrill 	vnode_t		*vp, *dvp, *oldvp = NULL, *olddvp = NULL, *orig;
1262df2381bfSpraks 	portfop_t	*pfp;
1263df2381bfSpraks 	int		error = 0;
1264df2381bfSpraks 	file_obj_t	fobj;
1265df2381bfSpraks 	void		*objptr;
1266df2381bfSpraks 	char		*cname;
1267df2381bfSpraks 	int		clen;
1268df2381bfSpraks 	int		follow;
1269df2381bfSpraks 
1270df2381bfSpraks 	/*
1271df2381bfSpraks 	 * check that events specified are valid.
1272df2381bfSpraks 	 */
1273df2381bfSpraks 	if ((events & ~FILE_EVENTS_MASK) != 0)
1274df2381bfSpraks 		return (EINVAL);
1275df2381bfSpraks 
1276df2381bfSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
1277df2381bfSpraks 		if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
1278df2381bfSpraks 			return (EFAULT);
1279df2381bfSpraks 		objptr = (void *)&fobj;
1280df2381bfSpraks #ifdef  _SYSCALL32_IMPL
1281df2381bfSpraks 	} else {
1282df2381bfSpraks 		file_obj32_t	fobj32;
1283df2381bfSpraks 		if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
1284df2381bfSpraks 			return (EFAULT);
1285df2381bfSpraks 		objptr = (void *)&fobj32;
1286df2381bfSpraks #endif  /* _SYSCALL32_IMPL */
1287df2381bfSpraks 	}
1288df2381bfSpraks 
1289df2381bfSpraks 	vp = dvp = NULL;
1290df2381bfSpraks 
1291df2381bfSpraks 	/*
1292df2381bfSpraks 	 * find out if we need to follow symbolic links.
1293df2381bfSpraks 	 */
1294df2381bfSpraks 	follow = !(events & FILE_NOFOLLOW);
1295df2381bfSpraks 	events = events & ~FILE_NOFOLLOW;
1296df2381bfSpraks 
1297df2381bfSpraks 	/*
1298df2381bfSpraks 	 * lookup and find the vnode and its directory vnode of the given
1299df2381bfSpraks 	 * file.
1300df2381bfSpraks 	 */
1301df2381bfSpraks 	if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
1302df2381bfSpraks 	    follow)) != 0) {
1303df2381bfSpraks 		return (error);
1304df2381bfSpraks 	}
1305df2381bfSpraks 
1306df2381bfSpraks 	if (dvp != NULL) {
1307df2381bfSpraks 		dvp = port_resolve_vp(dvp);
1308df2381bfSpraks 	}
1309df2381bfSpraks 
1310df2381bfSpraks 	/*
1311df2381bfSpraks 	 * Not found
1312df2381bfSpraks 	 */
1313df2381bfSpraks 	if (vp == NULL) {
1314df2381bfSpraks 		error = ENOENT;
1315df2381bfSpraks 		goto errout;
1316df2381bfSpraks 	}
1317df2381bfSpraks 
131872102e74SBryan Cantrill 	vp = port_resolve_vp(orig = vp);
1319df2381bfSpraks 
1320da6c28aaSamw 	if (vp != NULL && vnevent_support(vp, NULL)) {
1321df2381bfSpraks 		error = ENOTSUP;
1322df2381bfSpraks 		goto errout;
1323df2381bfSpraks 	}
1324df2381bfSpraks 
1325df2381bfSpraks 	/*
132672102e74SBryan Cantrill 	 * If dvp belongs to a different filesystem just ignore it, as hard
132772102e74SBryan Cantrill 	 * links cannot exist across filesystems.  We make an exception for
132872102e74SBryan Cantrill 	 * procfs, however, the magic of which we treat semantically as a hard
132972102e74SBryan Cantrill 	 * link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE
133072102e74SBryan Cantrill 	 * and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events.
1331efaadbbfSPrakash Sangappa 	 */
133272102e74SBryan Cantrill 	if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp &&
133372102e74SBryan Cantrill 	    !(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) {
1334efaadbbfSPrakash Sangappa 		VN_RELE(dvp);
1335efaadbbfSPrakash Sangappa 		dvp = NULL;
1336efaadbbfSPrakash Sangappa 	}
1337efaadbbfSPrakash Sangappa 
1338efaadbbfSPrakash Sangappa 	/*
1339df2381bfSpraks 	 * Associate this source to the port and get the per port
1340df2381bfSpraks 	 * fop cache pointer. If the source is already associated, it
1341df2381bfSpraks 	 * will just return the cache pointer.
1342df2381bfSpraks 	 */
1343df2381bfSpraks 	if (error = port_fop_associate_source(&pfcp, pp, source)) {
1344df2381bfSpraks 		goto errout;
1345df2381bfSpraks 	}
1346df2381bfSpraks 
1347df2381bfSpraks 	/*
1348df2381bfSpraks 	 * Check if there is an existing association of this file.
1349df2381bfSpraks 	 */
1350df2381bfSpraks 	mutex_enter(&pfcp->pfc_lock);
1351df2381bfSpraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1352df2381bfSpraks 
1353df2381bfSpraks 	/*
13546b5ad791Spraks 	 * If it is not the same vnode, just discard it. VN_RELE needs to be
13556b5ad791Spraks 	 * called with no locks held, therefore save vnode pointers and
13566b5ad791Spraks 	 * vn_rele them later.
1357df2381bfSpraks 	 */
1358df2381bfSpraks 	if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
13596b5ad791Spraks 		(void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
1360df2381bfSpraks 		pfp = NULL;
1361df2381bfSpraks 	}
1362df2381bfSpraks 
1363df2381bfSpraks 	if (pfp == NULL) {
13646b5ad791Spraks 		vnode_t *tvp, *tdvp;
1365073af7d9Spraks 		portfop_t	*tpfp;
1366073af7d9Spraks 		int error;
1367073af7d9Spraks 
1368df2381bfSpraks 		/*
1369df2381bfSpraks 		 * Add a new association, save the file name and the
1370df2381bfSpraks 		 * directory vnode pointer.
1371df2381bfSpraks 		 */
1372df2381bfSpraks 		if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
1373df2381bfSpraks 		    events, user, cname, clen, dvp)) {
1374df2381bfSpraks 			mutex_exit(&pfcp->pfc_lock);
1375df2381bfSpraks 			goto errout;
1376df2381bfSpraks 		}
1377df2381bfSpraks 
1378073af7d9Spraks 		pfp->pfop_callrid = curthread;
1379df2381bfSpraks 		/*
1380df2381bfSpraks 		 * File name used, so make sure we don't free it.
1381df2381bfSpraks 		 */
1382df2381bfSpraks 		cname = NULL;
1383df2381bfSpraks 
1384df2381bfSpraks 		/*
1385df2381bfSpraks 		 * We need to check if the file was removed after the
1386df2381bfSpraks 		 * the lookup and before the fem hooks where added. If
1387df2381bfSpraks 		 * so, return error. The vnode will still exist as we have
1388df2381bfSpraks 		 * a hold on it.
1389073af7d9Spraks 		 *
1390073af7d9Spraks 		 * Drop the cache lock before calling port_fop_getdvp().
1391073af7d9Spraks 		 * port_fop_getdvp() may block either in the vfs layer
1392073af7d9Spraks 		 * or some filesystem.  Therefore there is potential
1393073af7d9Spraks 		 * for deadlock if cache lock is held and if some other
1394073af7d9Spraks 		 * thread is attempting to deliver file events which would
1395073af7d9Spraks 		 * require getting the cache lock, while it may be holding
1396073af7d9Spraks 		 * the filesystem or vfs layer locks.
1397df2381bfSpraks 		 */
1398073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
1399df2381bfSpraks 		tvp = NULL;
1400df2381bfSpraks 		if ((error = port_fop_getdvp(objptr, &tvp, NULL,
1401df2381bfSpraks 		    NULL, NULL, follow)) == 0) {
1402df2381bfSpraks 			if (tvp != NULL) {
1403df2381bfSpraks 				tvp = port_resolve_vp(tvp);
1404073af7d9Spraks 				/*
1405073af7d9Spraks 				 * This vnode pointer is just used
1406073af7d9Spraks 				 * for comparison, so rele it
1407073af7d9Spraks 				 */
1408073af7d9Spraks 				VN_RELE(tvp);
1409df2381bfSpraks 			}
1410df2381bfSpraks 		}
1411073af7d9Spraks 
1412df2381bfSpraks 		if (error || tvp == NULL || tvp != vp) {
1413073af7d9Spraks 			/*
1414073af7d9Spraks 			 * Since we dropped the cache lock, make sure
1415073af7d9Spraks 			 * we are still dealing with the same pfp and this
1416073af7d9Spraks 			 * is the thread which registered it.
1417073af7d9Spraks 			 */
1418073af7d9Spraks 			mutex_enter(&pfcp->pfc_lock);
1419073af7d9Spraks 			tpfp = port_cache_lookup_fop(pfcp,
1420073af7d9Spraks 			    curproc->p_pid, object);
1421073af7d9Spraks 
1422073af7d9Spraks 			error = 0;
1423073af7d9Spraks 			if (tpfp == NULL || tpfp != pfp ||
1424073af7d9Spraks 			    pfp->pfop_vp != vp ||
1425073af7d9Spraks 			    pfp->pfop_dvp != dvp ||
1426073af7d9Spraks 			    pfp->pfop_callrid != curthread) {
1427073af7d9Spraks 				/*
1428073af7d9Spraks 				 * Some other event was delivered, the file
1429073af7d9Spraks 				 * watch was removed or reassociated, just
1430073af7d9Spraks 				 * ignore it and leave
1431073af7d9Spraks 				 */
1432073af7d9Spraks 				mutex_exit(&pfcp->pfc_lock);
1433073af7d9Spraks 				goto errout;
1434073af7d9Spraks 			}
1435df2381bfSpraks 
1436df2381bfSpraks 			/*
1437df2381bfSpraks 			 * remove the pfp and fem hooks, if pfp still
1438df2381bfSpraks 			 * active and it is not being removed from
1439df2381bfSpraks 			 * the vnode list. This is checked in
1440df2381bfSpraks 			 * port_remove_fop with the vnode lock held.
14416b5ad791Spraks 			 * The vnode returned is VN_RELE'ed after dropping
14426b5ad791Spraks 			 * the locks.
1443df2381bfSpraks 			 */
14446b5ad791Spraks 			tdvp = tvp = NULL;
14456b5ad791Spraks 			if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
1446df2381bfSpraks 				/*
1447073af7d9Spraks 				 * The pfp was removed, means no
1448df2381bfSpraks 				 * events where queued. Report the
1449df2381bfSpraks 				 * error now.
1450df2381bfSpraks 				 */
1451df2381bfSpraks 				error = EINVAL;
1452073af7d9Spraks 			}
1453df2381bfSpraks 			mutex_exit(&pfcp->pfc_lock);
14546b5ad791Spraks 			if (tvp != NULL)
14556b5ad791Spraks 				VN_RELE(tvp);
14566b5ad791Spraks 			if (tdvp != NULL)
14576b5ad791Spraks 				VN_RELE(tdvp);
1458df2381bfSpraks 			goto errout;
1459df2381bfSpraks 		}
1460df2381bfSpraks 	} else {
1461df2381bfSpraks 		portfop_vp_t	*pvp = vp->v_fopdata;
1462df2381bfSpraks 
1463df2381bfSpraks 		/*
1464df2381bfSpraks 		 * Re-association of the object.
1465df2381bfSpraks 		 */
1466df2381bfSpraks 		mutex_enter(&pvp->pvp_mutex);
1467df2381bfSpraks 
1468df2381bfSpraks 		/*
1469*abb88ab1SRobert Mustacchi 		 * Remove any queued up event.
1470df2381bfSpraks 		 */
1471df2381bfSpraks 		if (port_remove_done_event(pfp->pfop_pev)) {
1472df2381bfSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
1473df2381bfSpraks 		}
1474df2381bfSpraks 
1475df2381bfSpraks 		/*
1476*abb88ab1SRobert Mustacchi 		 * Set new events to watch and update the user pointer.
1477df2381bfSpraks 		 */
1478df2381bfSpraks 		pfp->pfop_events = events;
1479*abb88ab1SRobert Mustacchi 		pfp->pfop_pev->portkev_user = user;
1480df2381bfSpraks 
1481df2381bfSpraks 		/*
1482df2381bfSpraks 		 * If not active, mark it active even if it is being
1483df2381bfSpraks 		 * removed. Then it can send an exception event.
1484df2381bfSpraks 		 *
1485df2381bfSpraks 		 * Move it to the head, as the active ones are only
1486073af7d9Spraks 		 * in the beginning. If removing, the pfp will be on
1487df2381bfSpraks 		 * a temporary list, no need to move it to the front
1488073af7d9Spraks 		 * all the entries will be processed. Some exception
1489073af7d9Spraks 		 * events will be delivered in port_fop_excep();
1490df2381bfSpraks 		 */
1491df2381bfSpraks 		if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
1492df2381bfSpraks 			pfp->pfop_flags |= PORT_FOP_ACTIVE;
1493073af7d9Spraks 			if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
1494df2381bfSpraks 				pvp = (portfop_vp_t *)vp->v_fopdata;
1495df2381bfSpraks 				port_fop_listremove(pvp, pfp);
1496df2381bfSpraks 				port_fop_listinsert_head(pvp, pfp);
1497df2381bfSpraks 			}
1498df2381bfSpraks 		}
1499073af7d9Spraks 		pfp->pfop_callrid = curthread;
1500df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
1501073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
1502df2381bfSpraks 	}
1503df2381bfSpraks 
1504df2381bfSpraks 	/*
1505073af7d9Spraks 	 * Compare time stamps and deliver events.
1506df2381bfSpraks 	 */
1507073af7d9Spraks 	if (vp->v_type != VFIFO) {
1508073af7d9Spraks 		port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
1509df2381bfSpraks 	}
1510df2381bfSpraks 
1511df2381bfSpraks 	error = 0;
1512df2381bfSpraks 
1513df2381bfSpraks 	/*
1514df2381bfSpraks 	 *  If we have too many watches on the vnode, discard an
1515df2381bfSpraks 	 *  inactive watch.
1516df2381bfSpraks 	 */
1517df2381bfSpraks 	port_fop_trimpfplist(vp);
1518df2381bfSpraks 
1519df2381bfSpraks errout:
1520df2381bfSpraks 	/*
1521df2381bfSpraks 	 * Release the hold acquired due to the lookup operation.
1522df2381bfSpraks 	 */
1523df2381bfSpraks 	if (vp != NULL)
1524df2381bfSpraks 		VN_RELE(vp);
15256b5ad791Spraks 	if (dvp != NULL)
15266b5ad791Spraks 		VN_RELE(dvp);
15276b5ad791Spraks 
15286b5ad791Spraks 	if (oldvp != NULL)
15296b5ad791Spraks 		VN_RELE(oldvp);
15306b5ad791Spraks 	if (olddvp != NULL)
15316b5ad791Spraks 		VN_RELE(olddvp);
1532df2381bfSpraks 
1533df2381bfSpraks 	/*
1534df2381bfSpraks 	 * copied file name not used, free it.
1535df2381bfSpraks 	 */
1536df2381bfSpraks 	if (cname != NULL) {
1537df2381bfSpraks 		kmem_free(cname, clen + 1);
1538df2381bfSpraks 	}
1539df2381bfSpraks 	return (error);
1540df2381bfSpraks }
1541df2381bfSpraks 
1542df2381bfSpraks 
1543df2381bfSpraks /*
1544df2381bfSpraks  * The port_dissociate_fop() function dissociates the file object
1545df2381bfSpraks  * from the event port and removes any events that are already on the queue.
1546df2381bfSpraks  * Only the owner of the association is allowed to dissociate the file from
1547df2381bfSpraks  * the port. Returns  success (0) if it was found and removed. Otherwise
1548df2381bfSpraks  * ENOENT.
1549df2381bfSpraks  */
1550df2381bfSpraks int
port_dissociate_fop(port_t * pp,uintptr_t object)1551df2381bfSpraks port_dissociate_fop(port_t *pp, uintptr_t object)
1552df2381bfSpraks {
1553df2381bfSpraks 	portfop_cache_t	*pfcp;
1554df2381bfSpraks 	portfop_t	*pfp;
1555df2381bfSpraks 	port_source_t	*pse;
1556df2381bfSpraks 	int		active = 0;
15576b5ad791Spraks 	vnode_t		*tvp = NULL, *tdvp = NULL;
1558df2381bfSpraks 
1559df2381bfSpraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
1560df2381bfSpraks 
1561df2381bfSpraks 	/*
1562df2381bfSpraks 	 * if this source is not associated or if there is no
1563df2381bfSpraks 	 * cache, nothing to do just return.
1564df2381bfSpraks 	 */
1565df2381bfSpraks 	if (pse == NULL ||
1566df2381bfSpraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1567df2381bfSpraks 		return (EINVAL);
1568df2381bfSpraks 
1569df2381bfSpraks 	/*
1570df2381bfSpraks 	 * Check if this object is on the cache. Only the owner pid
1571df2381bfSpraks 	 * is allowed to dissociate.
1572df2381bfSpraks 	 */
1573df2381bfSpraks 	mutex_enter(&pfcp->pfc_lock);
1574df2381bfSpraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1575df2381bfSpraks 	if (pfp == NULL) {
1576df2381bfSpraks 		mutex_exit(&pfcp->pfc_lock);
1577df2381bfSpraks 		return (ENOENT);
1578df2381bfSpraks 	}
1579df2381bfSpraks 
1580df2381bfSpraks 	/*
1581df2381bfSpraks 	 * If this was the last association, it will release
1582df2381bfSpraks 	 * the hold on the vnode. There is a race condition where
1583df2381bfSpraks 	 * the the pfp is being removed due to an exception event
1584df2381bfSpraks 	 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
1585df2381bfSpraks 	 * Since port source cache lock is held, port_fop_excep() cannot
1586073af7d9Spraks 	 * complete. The vnode itself will not disappear as long its pfps
1587df2381bfSpraks 	 * have a reference.
1588df2381bfSpraks 	 */
15896b5ad791Spraks 	(void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
1590df2381bfSpraks 	mutex_exit(&pfcp->pfc_lock);
15916b5ad791Spraks 	if (tvp != NULL)
15926b5ad791Spraks 		VN_RELE(tvp);
15936b5ad791Spraks 	if (tdvp != NULL)
15946b5ad791Spraks 		VN_RELE(tdvp);
1595df2381bfSpraks 	return (active ? 0 : ENOENT);
1596df2381bfSpraks }
1597df2381bfSpraks 
1598df2381bfSpraks 
1599df2381bfSpraks /*
1600df2381bfSpraks  * port_close() calls this function to request the PORT_SOURCE_FILE source
1601df2381bfSpraks  * to remove/free all resources allocated and associated with the port.
1602df2381bfSpraks  */
1603df2381bfSpraks 
1604df2381bfSpraks /* ARGSUSED */
1605df2381bfSpraks static void
port_close_fop(void * arg,int port,pid_t pid,int lastclose)1606df2381bfSpraks port_close_fop(void *arg, int port, pid_t pid, int lastclose)
1607df2381bfSpraks {
1608df2381bfSpraks 	port_t		*pp = arg;
1609df2381bfSpraks 	portfop_cache_t	*pfcp;
1610df2381bfSpraks 	portfop_t	**hashtbl;
1611df2381bfSpraks 	portfop_t	*pfp;
1612df2381bfSpraks 	portfop_t	*pfpnext;
16136b5ad791Spraks 	int		index, i;
1614df2381bfSpraks 	port_source_t	*pse;
16156b5ad791Spraks 	vnode_t		*tdvp = NULL;
16166b5ad791Spraks 	vnode_t		*vpl[PORTFOP_NVP];
1617df2381bfSpraks 
1618df2381bfSpraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
1619df2381bfSpraks 
1620df2381bfSpraks 	/*
1621df2381bfSpraks 	 * No source or no cache, nothing to do.
1622df2381bfSpraks 	 */
1623df2381bfSpraks 	if (pse == NULL ||
1624df2381bfSpraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1625df2381bfSpraks 		return;
1626df2381bfSpraks 	/*
1627df2381bfSpraks 	 * Scan the cache and free all allocated portfop_t and port_kevent_t
16286b5ad791Spraks 	 * structures of this pid. Note, no new association for this pid will
16296b5ad791Spraks 	 * be possible as the port is being closed.
16306b5ad791Spraks 	 *
16316b5ad791Spraks 	 * The common case is that the port is not shared and all the entries
16326b5ad791Spraks 	 * are of this pid and have to be freed. Since VN_RELE has to be
16336b5ad791Spraks 	 * called outside the lock, we do it in batches.
1634df2381bfSpraks 	 */
1635df2381bfSpraks 	hashtbl = (portfop_t **)pfcp->pfc_hash;
16366b5ad791Spraks 	index = i = 0;
16376b5ad791Spraks 	bzero(vpl, sizeof (vpl));
16386b5ad791Spraks 	mutex_enter(&pfcp->pfc_lock);
16396b5ad791Spraks 	while (index < PORTFOP_HASHSIZE) {
16406b5ad791Spraks 		pfp = hashtbl[index];
16416b5ad791Spraks 		while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
1642df2381bfSpraks 			pfpnext = pfp->pfop_hashnext;
1643df2381bfSpraks 			if (pid == pfp->pfop_pid) {
16446b5ad791Spraks 				(void) port_remove_fop(pfp, pfcp, 1, NULL,
16456b5ad791Spraks 				    &vpl[i], &tdvp);
16466b5ad791Spraks 				if (vpl[i] != NULL) {
16476b5ad791Spraks 					i++;
1648df2381bfSpraks 				}
16496b5ad791Spraks 				if (tdvp != NULL) {
16506b5ad791Spraks 					vpl[i++] = tdvp;
16516b5ad791Spraks 					tdvp = NULL;
16526b5ad791Spraks 				}
16536b5ad791Spraks 			}
16546b5ad791Spraks 			pfp = pfpnext;
16556b5ad791Spraks 		}
16566b5ad791Spraks 		if (pfp == NULL)
16576b5ad791Spraks 			index++;
16586b5ad791Spraks 		/*
16596b5ad791Spraks 		 * Now call VN_RELE if we have collected enough vnodes or
16606b5ad791Spraks 		 * we have reached the end of the hash table.
16616b5ad791Spraks 		 */
16626b5ad791Spraks 		if (i >= (PORTFOP_NVP - 1) ||
16636b5ad791Spraks 		    (i > 0 && index == PORTFOP_HASHSIZE)) {
16646b5ad791Spraks 			mutex_exit(&pfcp->pfc_lock);
16656b5ad791Spraks 			while (i > 0) {
16666b5ad791Spraks 				VN_RELE(vpl[--i]);
16676b5ad791Spraks 				vpl[i] = NULL;
16686b5ad791Spraks 			}
16696b5ad791Spraks 			mutex_enter(&pfcp->pfc_lock);
1670df2381bfSpraks 		}
1671df2381bfSpraks 	}
1672df2381bfSpraks 
1673df2381bfSpraks 	/*
1674df2381bfSpraks 	 * Due to a race between port_close_fop() and port_fop()
1675df2381bfSpraks 	 * trying to remove the pfp's from the port's cache, it is
1676df2381bfSpraks 	 * possible that some pfp's are still in the process of being
1677df2381bfSpraks 	 * freed so we wait.
1678df2381bfSpraks 	 */
1679df2381bfSpraks 	while (lastclose && pfcp->pfc_objcount) {
1680df2381bfSpraks 		(void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
1681df2381bfSpraks 	}
1682df2381bfSpraks 	mutex_exit(&pfcp->pfc_lock);
1683df2381bfSpraks 	/*
1684df2381bfSpraks 	 * last close, free the cache.
1685df2381bfSpraks 	 */
1686df2381bfSpraks 	if (lastclose) {
1687df2381bfSpraks 		ASSERT(pfcp->pfc_objcount == 0);
1688df2381bfSpraks 		pse->portsrc_data = NULL;
1689df2381bfSpraks 		kmem_free(pfcp, sizeof (portfop_cache_t));
1690df2381bfSpraks 	}
1691df2381bfSpraks }
1692df2381bfSpraks 
1693df2381bfSpraks /*
1694df2381bfSpraks  * Given the list of associations(watches), it will send exception events,
1695df2381bfSpraks  * if still active, and discard them. The exception events are handled
1696da6c28aaSamw  * separately because, the pfp needs to be removed from the port cache and
1697df2381bfSpraks  * freed as the vnode's identity is changing or being removed. To remove
1698df2381bfSpraks  * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
1699df2381bfSpraks  * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
1700df2381bfSpraks  * the cache's lock cannot be acquired in port_fop_sendevent().
1701df2381bfSpraks  */
1702df2381bfSpraks static void
port_fop_excep(list_t * tlist,int op)1703df2381bfSpraks port_fop_excep(list_t *tlist, int op)
1704df2381bfSpraks {
1705df2381bfSpraks 	portfop_t	*pfp;
1706df2381bfSpraks 	portfop_cache_t *pfcp;
1707df2381bfSpraks 	port_t	*pp;
1708df2381bfSpraks 	port_kevent_t	*pkevp;
17096b5ad791Spraks 	vnode_t		*tdvp;
1710df2381bfSpraks 	int		error = 0;
1711df2381bfSpraks 
1712df2381bfSpraks 	while (pfp = (portfop_t *)list_head(tlist)) {
1713df2381bfSpraks 		int removed = 0;
1714df2381bfSpraks 		/*
1715df2381bfSpraks 		 * remove from the temp list. Since PORT_FOP_REMOVING is
1716df2381bfSpraks 		 * set, no other thread should attempt to perform a
1717df2381bfSpraks 		 * list_remove on this pfp.
1718df2381bfSpraks 		 */
1719df2381bfSpraks 		list_remove(tlist, pfp);
1720df2381bfSpraks 
1721df2381bfSpraks 		pfcp = pfp->pfop_pcache;
1722df2381bfSpraks 		mutex_enter(&pfcp->pfc_lock);
1723df2381bfSpraks 
1724df2381bfSpraks 		/*
1725df2381bfSpraks 		 * Remove the event from the port queue if it was queued up.
1726df2381bfSpraks 		 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
1727df2381bfSpraks 		 * no longer on the vnode's list.
1728df2381bfSpraks 		 */
1729df2381bfSpraks 		if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
1730df2381bfSpraks 			removed = port_remove_done_event(pfp->pfop_pev);
1731df2381bfSpraks 		}
1732df2381bfSpraks 
1733df2381bfSpraks 		/*
1734df2381bfSpraks 		 * If still active or the event was queued up and
1735df2381bfSpraks 		 * had not been collected yet, send an EXCEPTION event.
1736df2381bfSpraks 		 */
1737df2381bfSpraks 		if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
1738df2381bfSpraks 			pp = pfp->pfop_pp;
1739df2381bfSpraks 			/*
1740df2381bfSpraks 			 * Allocate a port_kevent_t non cached to send this
1741df2381bfSpraks 			 * event since we will be de-registering.
1742df2381bfSpraks 			 * The port_kevent_t cannot be pointing back to the
1743df2381bfSpraks 			 * pfp anymore.
1744df2381bfSpraks 			 */
1745df2381bfSpraks 			pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1746df2381bfSpraks 			error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1747df2381bfSpraks 			    PORT_ALLOC_DEFAULT, &pkevp);
1748df2381bfSpraks 			if (!error) {
1749df2381bfSpraks 
1750df2381bfSpraks 				pkevp->portkev_callback = port_fop_callback;
1751df2381bfSpraks 				pkevp->portkev_arg = NULL;
1752df2381bfSpraks 				pkevp->portkev_object =
1753df2381bfSpraks 				    pfp->pfop_pev->portkev_object;
1754df2381bfSpraks 				pkevp->portkev_user =
1755df2381bfSpraks 				    pfp->pfop_pev->portkev_user;
1756df2381bfSpraks 				/*
1757df2381bfSpraks 				 * Copy the pid of the watching process.
1758df2381bfSpraks 				 */
1759df2381bfSpraks 				pkevp->portkev_pid =
1760df2381bfSpraks 				    pfp->pfop_pev->portkev_pid;
1761df2381bfSpraks 				pkevp->portkev_events = op;
1762df2381bfSpraks 				port_send_event(pkevp);
1763df2381bfSpraks 			}
1764df2381bfSpraks 		}
1765df2381bfSpraks 		/*
1766df2381bfSpraks 		 * At this point the pfp has been removed from the vnode's
1767df2381bfSpraks 		 * list its cached port_kevent_t is not on the done queue.
1768df2381bfSpraks 		 * Remove the pfp and free it from the cache.
1769df2381bfSpraks 		 */
17706b5ad791Spraks 		tdvp = pfp->pfop_dvp;
1771df2381bfSpraks 		port_pcache_remove_fop(pfcp, pfp);
1772df2381bfSpraks 		mutex_exit(&pfcp->pfc_lock);
17736b5ad791Spraks 		if (tdvp != NULL)
17746b5ad791Spraks 			VN_RELE(tdvp);
1775df2381bfSpraks 	}
1776df2381bfSpraks }
1777df2381bfSpraks 
1778df2381bfSpraks /*
1779df2381bfSpraks  * Send the file events to all of the processes watching this
1780df2381bfSpraks  * vnode. In case of hard links, the directory vnode pointer and
1781df2381bfSpraks  * the file name are compared. If the names match, then the specified
1782df2381bfSpraks  * event is sent or else, the FILE_ATTRIB event is sent, This is the
1783df2381bfSpraks  * documented behavior.
1784df2381bfSpraks  */
1785df2381bfSpraks void
port_fop_sendevent(vnode_t * vp,int events,vnode_t * dvp,char * cname)1786df2381bfSpraks port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
1787df2381bfSpraks {
1788df2381bfSpraks 	port_kevent_t	*pkevp;
1789df2381bfSpraks 	portfop_t	*pfp, *npfp;
1790df2381bfSpraks 	portfop_vp_t	*pvp;
1791df2381bfSpraks 	list_t		tmplist;
1792df2381bfSpraks 	int		removeall = 0;
1793df2381bfSpraks 
1794df2381bfSpraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
1795df2381bfSpraks 	mutex_enter(&pvp->pvp_mutex);
1796df2381bfSpraks 
1797df2381bfSpraks 	/*
1798df2381bfSpraks 	 * Check if the list is empty.
1799df2381bfSpraks 	 *
1800df2381bfSpraks 	 * All entries have been removed by some other thread.
1801df2381bfSpraks 	 * The vnode may be still active and we got called,
1802df2381bfSpraks 	 * but some other thread is in the process of removing the hooks.
1803df2381bfSpraks 	 */
1804df2381bfSpraks 	if (!list_head(&pvp->pvp_pfoplist)) {
1805df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
1806df2381bfSpraks 		return;
1807df2381bfSpraks 	}
1808df2381bfSpraks 
1809df2381bfSpraks 	if ((events & (FILE_EXCEPTION))) {
1810df2381bfSpraks 		/*
1811df2381bfSpraks 		 * If it is an event for which we are going to remove
1812df2381bfSpraks 		 * the watches so just move it a temporary list and
1813df2381bfSpraks 		 * release this vnode.
1814df2381bfSpraks 		 */
1815df2381bfSpraks 		list_create(&tmplist, sizeof (portfop_t),
1816df2381bfSpraks 		    offsetof(portfop_t, pfop_node));
1817df2381bfSpraks 
1818df2381bfSpraks 		/*
1819df2381bfSpraks 		 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
1820df2381bfSpraks 		 * passed for an exception event, all associations need to be
1821df2381bfSpraks 		 * removed.
1822df2381bfSpraks 		 */
1823df2381bfSpraks 		if (dvp == NULL || cname == NULL) {
1824df2381bfSpraks 			removeall = 1;
1825df2381bfSpraks 		}
1826df2381bfSpraks 	}
1827df2381bfSpraks 
1828df2381bfSpraks 	if (!removeall) {
1829df2381bfSpraks 		/*
1830073af7d9Spraks 		 * All the active ones are in the beginning of the list.
183172102e74SBryan Cantrill 		 * Note that we process this list in reverse order to assure
183272102e74SBryan Cantrill 		 * that events are delivered in the order that they were
183372102e74SBryan Cantrill 		 * associated.
1834df2381bfSpraks 		 */
183572102e74SBryan Cantrill 		for (pfp = (portfop_t *)list_tail(&pvp->pvp_pfoplist);
183672102e74SBryan Cantrill 		    pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE); pfp = npfp) {
183772102e74SBryan Cantrill 			npfp = list_prev(&pvp->pvp_pfoplist, pfp);
183872102e74SBryan Cantrill 		}
183972102e74SBryan Cantrill 
184072102e74SBryan Cantrill 		for (; pfp != NULL; pfp = npfp) {
1841df2381bfSpraks 			int levents = events;
1842df2381bfSpraks 
184372102e74SBryan Cantrill 			npfp = list_prev(&pvp->pvp_pfoplist, pfp);
1844df2381bfSpraks 			/*
1845df2381bfSpraks 			 * Hard links case - If the file is being
1846df2381bfSpraks 			 * removed/renamed, and the name matches
1847df2381bfSpraks 			 * the watched file, then it is an EXCEPTION
1848df2381bfSpraks 			 * event or else it will be just a FILE_ATTRIB.
1849df2381bfSpraks 			 */
1850df2381bfSpraks 			if ((events & (FILE_EXCEPTION))) {
1851df2381bfSpraks 				ASSERT(dvp != NULL && cname != NULL);
1852df2381bfSpraks 				if (pfp->pfop_dvp == NULL ||
1853df2381bfSpraks 				    (pfp->pfop_dvp == dvp &&
1854df2381bfSpraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
1855df2381bfSpraks 					/*
1856df2381bfSpraks 					 * It is an exception event, move it
1857df2381bfSpraks 					 * to temp list and process it later.
1858df2381bfSpraks 					 * Note we don't set the pfp->pfop_vp
1859df2381bfSpraks 					 * to NULL even thought it has been
1860df2381bfSpraks 					 * removed from the vnode's list. This
1861df2381bfSpraks 					 * pointer is referenced in
1862df2381bfSpraks 					 * port_remove_fop(). The vnode it
1863073af7d9Spraks 					 * self cannot disappear until this
1864df2381bfSpraks 					 * pfp gets removed and freed.
1865df2381bfSpraks 					 */
1866df2381bfSpraks 					port_fop_listremove(pvp, pfp);
1867df2381bfSpraks 					list_insert_tail(&tmplist, (void *)pfp);
1868df2381bfSpraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
1869df2381bfSpraks 					continue;
1870df2381bfSpraks 				} else {
1871df2381bfSpraks 					levents = FILE_ATTRIB;
1872df2381bfSpraks 				}
1873df2381bfSpraks 
1874df2381bfSpraks 			}
1875df2381bfSpraks 
1876df2381bfSpraks 			if (pfp->pfop_events & levents) {
1877df2381bfSpraks 				/*
1878df2381bfSpraks 				 * deactivate and move it to the tail.
1879df2381bfSpraks 				 * If the pfp was active, it cannot be
1880df2381bfSpraks 				 * on the port's done queue.
1881df2381bfSpraks 				 */
1882df2381bfSpraks 				pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1883df2381bfSpraks 				port_fop_listremove(pvp, pfp);
1884df2381bfSpraks 				port_fop_listinsert_tail(pvp, pfp);
1885df2381bfSpraks 
1886df2381bfSpraks 				pkevp = pfp->pfop_pev;
1887df2381bfSpraks 				pkevp->portkev_events |=
1888df2381bfSpraks 				    (levents & pfp->pfop_events);
1889df2381bfSpraks 				port_send_event(pkevp);
1890df2381bfSpraks 				pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
1891df2381bfSpraks 			}
1892df2381bfSpraks 		}
1893df2381bfSpraks 	}
1894df2381bfSpraks 
1895df2381bfSpraks 
1896df2381bfSpraks 	if ((events & (FILE_EXCEPTION))) {
1897df2381bfSpraks 		if (!removeall) {
1898df2381bfSpraks 			/*
1899df2381bfSpraks 			 * Check the inactive associations and remove them if
1900df2381bfSpraks 			 * the file name matches.
1901df2381bfSpraks 			 */
1902df2381bfSpraks 			for (; pfp; pfp = npfp) {
1903df2381bfSpraks 				npfp = list_next(&pvp->pvp_pfoplist, pfp);
1904df2381bfSpraks 				if (dvp == NULL || cname == NULL ||
1905df2381bfSpraks 				    pfp->pfop_dvp == NULL ||
1906df2381bfSpraks 				    (pfp->pfop_dvp == dvp &&
1907df2381bfSpraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
1908df2381bfSpraks 					port_fop_listremove(pvp, pfp);
1909df2381bfSpraks 					list_insert_tail(&tmplist, (void *)pfp);
1910df2381bfSpraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
1911df2381bfSpraks 				}
1912df2381bfSpraks 			}
1913df2381bfSpraks 		} else {
1914df2381bfSpraks 			/*
1915df2381bfSpraks 			 * Can be optimized to avoid two pass over this list
1916df2381bfSpraks 			 * by having a flag in the vnode's portfop_vp_t
1917df2381bfSpraks 			 * structure to indicate that it is going away,
1918df2381bfSpraks 			 * Or keep the list short by reusing inactive watches.
1919df2381bfSpraks 			 */
1920df2381bfSpraks 			port_fop_listmove(pvp, &tmplist);
1921df2381bfSpraks 			for (pfp = (portfop_t *)list_head(&tmplist);
1922df2381bfSpraks 			    pfp; pfp = list_next(&tmplist, pfp)) {
1923df2381bfSpraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
1924df2381bfSpraks 			}
1925df2381bfSpraks 		}
1926df2381bfSpraks 
1927df2381bfSpraks 		/*
1928df2381bfSpraks 		 * Uninstall the fem hooks if there are no more associations.
1929df2381bfSpraks 		 * This will release the pvp mutex.
1930df2381bfSpraks 		 *
1931df2381bfSpraks 		 * Even thought all entries may have been removed,
1932df2381bfSpraks 		 * the vnode itself cannot disappear as there will be a
1933df2381bfSpraks 		 * hold on it due to this call to port_fop_sendevent. This is
1934df2381bfSpraks 		 * important to syncronize with a port_dissociate_fop() call
1935df2381bfSpraks 		 * that may be attempting to remove an object from the vnode's.
1936df2381bfSpraks 		 */
19376b5ad791Spraks 		if (port_fop_femuninstall(vp))
19386b5ad791Spraks 			VN_RELE(vp);
1939df2381bfSpraks 
1940df2381bfSpraks 		/*
1941df2381bfSpraks 		 * Send exception events and discard the watch entries.
1942df2381bfSpraks 		 */
1943df2381bfSpraks 		port_fop_excep(&tmplist, events);
1944df2381bfSpraks 		list_destroy(&tmplist);
1945df2381bfSpraks 
1946df2381bfSpraks 	} else {
1947df2381bfSpraks 		mutex_exit(&pvp->pvp_mutex);
1948df2381bfSpraks 
1949df2381bfSpraks 		/*
1950df2381bfSpraks 		 * trim the list.
1951df2381bfSpraks 		 */
1952df2381bfSpraks 		port_fop_trimpfplist(vp);
1953df2381bfSpraks 	}
1954df2381bfSpraks }
1955df2381bfSpraks 
1956df2381bfSpraks /*
1957df2381bfSpraks  * Given the file operation, map it to the event types and send.
1958df2381bfSpraks  */
1959df2381bfSpraks void
port_fop(vnode_t * vp,int op,int retval)1960df2381bfSpraks port_fop(vnode_t *vp, int op, int retval)
1961df2381bfSpraks {
1962df2381bfSpraks 	int event = 0;
1963df2381bfSpraks 	/*
1964df2381bfSpraks 	 * deliver events only if the operation was successful.
1965df2381bfSpraks 	 */
1966df2381bfSpraks 	if (retval)
1967df2381bfSpraks 		return;
1968df2381bfSpraks 
1969df2381bfSpraks 	/*
1970da6c28aaSamw 	 * These events occurring on the watched file.
1971df2381bfSpraks 	 */
1972df2381bfSpraks 	if (op & FOP_MODIFIED_MASK) {
1973df2381bfSpraks 		event  = FILE_MODIFIED;
1974df2381bfSpraks 	}
1975df2381bfSpraks 	if (op & FOP_ACCESS_MASK) {
1976df2381bfSpraks 		event  |= FILE_ACCESS;
1977df2381bfSpraks 	}
1978df2381bfSpraks 	if (op & FOP_ATTRIB_MASK) {
1979df2381bfSpraks 		event  |= FILE_ATTRIB;
1980df2381bfSpraks 	}
198172102e74SBryan Cantrill 	if (op & FOP_TRUNC_MASK) {
198272102e74SBryan Cantrill 		event  |= FILE_TRUNC;
198372102e74SBryan Cantrill 	}
1984df2381bfSpraks 	if (event) {
1985df2381bfSpraks 		port_fop_sendevent(vp, event, NULL, NULL);
1986df2381bfSpraks 	}
1987df2381bfSpraks }
1988df2381bfSpraks 
port_forceunmount(vfs_t * vfsp)198984c5ce69Spraks static int port_forceunmount(vfs_t *vfsp)
199084c5ce69Spraks {
199184c5ce69Spraks 	char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
199284c5ce69Spraks 
199384c5ce69Spraks 	if (fsname == NULL) {
199484c5ce69Spraks 		return (0);
199584c5ce69Spraks 	}
199684c5ce69Spraks 
199784c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS) == 0) {
199884c5ce69Spraks 		return (1);
199984c5ce69Spraks 	}
200084c5ce69Spraks 
200184c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
200284c5ce69Spraks 		return (1);
200384c5ce69Spraks 	}
200484c5ce69Spraks 
200584c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
200684c5ce69Spraks 		return (1);
200784c5ce69Spraks 	}
200884c5ce69Spraks 	return (0);
200984c5ce69Spraks }
2010df2381bfSpraks /*
2011df2381bfSpraks  * ----- the unmount filesystem op(fsem) hook.
2012df2381bfSpraks  */
2013df2381bfSpraks int
port_fop_unmount(fsemarg_t * vf,int flag,cred_t * cr)2014df2381bfSpraks port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
2015df2381bfSpraks {
2016df2381bfSpraks 	vfs_t	*vfsp = (vfs_t *)vf->fa_fnode->fn_available;
2017df2381bfSpraks 	kmutex_t	*mtx;
2018df2381bfSpraks 	portfop_vfs_t	*pvfsp, **ppvfsp;
2019df2381bfSpraks 	portfop_vp_t	*pvp;
2020df2381bfSpraks 	int error;
202184c5ce69Spraks 	int fmfs;
202284c5ce69Spraks 
202384c5ce69Spraks 	fmfs = port_forceunmount(vfsp);
2024df2381bfSpraks 
2025df2381bfSpraks 	mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
2026df2381bfSpraks 	ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
2027df2381bfSpraks 	pvfsp = NULL;
2028df2381bfSpraks 	mutex_enter(mtx);
2029df2381bfSpraks 	/*
20306b5ad791Spraks 	 * since this fsem hook is triggered, the vfsp has to be on
2031df2381bfSpraks 	 * the hash list.
2032df2381bfSpraks 	 */
2033df2381bfSpraks 	for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
2034df2381bfSpraks 		;
2035df2381bfSpraks 
2036df2381bfSpraks 	/*
203784c5ce69Spraks 	 * For some of the filesystems, allow unmounts to proceed only if
203884c5ce69Spraks 	 * there are no files being watched or it is a forced unmount.
203984c5ce69Spraks 	 */
204084c5ce69Spraks 	if (fmfs && !(flag & MS_FORCE) &&
204184c5ce69Spraks 	    !list_is_empty(&pvfsp->pvfs_pvplist)) {
204284c5ce69Spraks 		mutex_exit(mtx);
204384c5ce69Spraks 		return (EBUSY);
204484c5ce69Spraks 	}
204584c5ce69Spraks 
204684c5ce69Spraks 	/*
2047df2381bfSpraks 	 * Indicate that the unmount is in process. Don't remove it yet.
2048df2381bfSpraks 	 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
2049df2381bfSpraks 	 * flag on the vfs_t structure. But we call the filesystem unmount
2050df2381bfSpraks 	 * routine after removing all the file watches for this filesystem,
2051df2381bfSpraks 	 * otherwise the unmount will fail due to active vnodes.
2052df2381bfSpraks 	 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
2053df2381bfSpraks 	 * attempting to add a file watch.
2054df2381bfSpraks 	 */
2055df2381bfSpraks 	pvfsp->pvfs_unmount = 1;
2056df2381bfSpraks 	mutex_exit(mtx);
2057df2381bfSpraks 
2058df2381bfSpraks 	/*
2059df2381bfSpraks 	 * uninstall the fsem hooks.
2060df2381bfSpraks 	 */
2061df2381bfSpraks 	(void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
2062df2381bfSpraks 
2063df2381bfSpraks 	while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
2064df2381bfSpraks 		list_remove(&pvfsp->pvfs_pvplist, pvp);
2065df2381bfSpraks 		/*
2066df2381bfSpraks 		 * This should send an UNMOUNTED event to all the
2067df2381bfSpraks 		 * watched vnode of this filesystem and uninstall
2068df2381bfSpraks 		 * the fem hooks. We release the hold on the vnode here
2069df2381bfSpraks 		 * because port_fop_femuninstall() will not do it if
2070df2381bfSpraks 		 * unmount is in process.
2071df2381bfSpraks 		 */
2072df2381bfSpraks 		port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
2073df2381bfSpraks 		VN_RELE(pvp->pvp_vp);
2074df2381bfSpraks 	}
2075df2381bfSpraks 
2076df2381bfSpraks 	error = vfsnext_unmount(vf, flag, cr);
2077df2381bfSpraks 
2078df2381bfSpraks 	/*
2079df2381bfSpraks 	 * we free the pvfsp after the unmount has been completed.
2080df2381bfSpraks 	 */
2081df2381bfSpraks 	mutex_enter(mtx);
2082df2381bfSpraks 	for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
2083df2381bfSpraks 	    ppvfsp = &(*ppvfsp)->pvfs_next)
2084df2381bfSpraks 		;
2085df2381bfSpraks 
2086df2381bfSpraks 	/*
2087df2381bfSpraks 	 * remove and free it.
2088df2381bfSpraks 	 */
2089df2381bfSpraks 	ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
2090df2381bfSpraks 	if (*ppvfsp) {
2091df2381bfSpraks 		pvfsp = *ppvfsp;
2092df2381bfSpraks 		*ppvfsp = pvfsp->pvfs_next;
2093df2381bfSpraks 	}
2094df2381bfSpraks 	mutex_exit(mtx);
2095df2381bfSpraks 	kmem_free(pvfsp, sizeof (portfop_vfs_t));
2096df2381bfSpraks 	return (error);
2097df2381bfSpraks }
2098df2381bfSpraks 
2099df2381bfSpraks /*
2100df2381bfSpraks  * ------------------------------file op hooks--------------------------
2101df2381bfSpraks  * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
2102df2381bfSpraks  */
2103df2381bfSpraks static int
port_fop_open(femarg_t * vf,int mode,cred_t * cr,caller_context_t * ct)2104da6c28aaSamw port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
2105df2381bfSpraks {
2106df2381bfSpraks 	int		retval;
2107df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2108df2381bfSpraks 
2109da6c28aaSamw 	retval = vnext_open(vf, mode, cr, ct);
2110df2381bfSpraks 	port_fop(vp, FOP_FILE_OPEN, retval);
2111df2381bfSpraks 	return (retval);
2112df2381bfSpraks }
2113df2381bfSpraks 
2114df2381bfSpraks static int
port_fop_write(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)2115df2381bfSpraks port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2116df2381bfSpraks     caller_context_t *ct)
2117df2381bfSpraks {
2118df2381bfSpraks 	int		retval;
2119df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2120df2381bfSpraks 
2121df2381bfSpraks 	retval =  vnext_write(vf, uiop, ioflag, cr, ct);
2122df2381bfSpraks 	port_fop(vp, FOP_FILE_WRITE, retval);
2123df2381bfSpraks 	return (retval);
2124df2381bfSpraks }
2125df2381bfSpraks 
2126df2381bfSpraks static int
port_fop_map(femarg_t * vf,offset_t off,struct as * as,caddr_t * addrp,size_t len,uchar_t prot,uchar_t maxport,uint_t flags,cred_t * cr,caller_context_t * ct)2127df2381bfSpraks port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
2128da6c28aaSamw     size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
2129da6c28aaSamw     caller_context_t *ct)
2130df2381bfSpraks {
2131df2381bfSpraks 	int		retval;
2132df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2133df2381bfSpraks 
2134da6c28aaSamw 	retval =  vnext_map(vf, off, as, addrp, len, prot, maxport,
2135da6c28aaSamw 	    flags, cr, ct);
2136df2381bfSpraks 	port_fop(vp, FOP_FILE_MAP, retval);
2137df2381bfSpraks 	return (retval);
2138df2381bfSpraks }
2139df2381bfSpraks 
2140df2381bfSpraks static int
port_fop_read(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)2141df2381bfSpraks port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2142df2381bfSpraks     caller_context_t *ct)
2143df2381bfSpraks {
2144df2381bfSpraks 	int		retval;
2145df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2146df2381bfSpraks 
2147df2381bfSpraks 	retval =  vnext_read(vf, uiop, ioflag, cr, ct);
2148df2381bfSpraks 	port_fop(vp, FOP_FILE_READ, retval);
2149df2381bfSpraks 	return (retval);
2150df2381bfSpraks }
2151df2381bfSpraks 
2152df2381bfSpraks 
2153df2381bfSpraks /*
2154df2381bfSpraks  * AT_SIZE - is for the open(O_TRUNC) case.
2155df2381bfSpraks  */
2156df2381bfSpraks int
port_fop_setattr(femarg_t * vf,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)2157df2381bfSpraks port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
2158df2381bfSpraks     caller_context_t *ct)
2159df2381bfSpraks {
2160df2381bfSpraks 	int		retval;
2161df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2162df2381bfSpraks 	int		events = 0;
2163df2381bfSpraks 
2164df2381bfSpraks 	retval = vnext_setattr(vf, vap, flags, cr, ct);
216572102e74SBryan Cantrill 	if (vap->va_mask & AT_SIZE) {
216672102e74SBryan Cantrill 		events |= FOP_FILE_TRUNC;
216772102e74SBryan Cantrill 	}
2168df2381bfSpraks 	if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
2169df2381bfSpraks 		events |= FOP_FILE_SETATTR_MTIME;
2170df2381bfSpraks 	}
2171df2381bfSpraks 	if (vap->va_mask & AT_ATIME) {
2172df2381bfSpraks 		events |= FOP_FILE_SETATTR_ATIME;
2173df2381bfSpraks 	}
2174df2381bfSpraks 	events |= FOP_FILE_SETATTR_CTIME;
2175df2381bfSpraks 
2176df2381bfSpraks 	port_fop(vp, events, retval);
2177df2381bfSpraks 	return (retval);
2178df2381bfSpraks }
2179df2381bfSpraks 
2180df2381bfSpraks int
port_fop_create(femarg_t * vf,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)2181df2381bfSpraks port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
2182da6c28aaSamw     int mode, vnode_t **vpp, cred_t *cr, int flag,
2183da6c28aaSamw     caller_context_t *ct, vsecattr_t *vsecp)
2184df2381bfSpraks {
2185df2381bfSpraks 	int		retval, got = 1;
2186df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2187df2381bfSpraks 	vattr_t		vatt, vatt1;
2188df2381bfSpraks 
2189df2381bfSpraks 	/*
2190df2381bfSpraks 	 * If the file already exists, then there will be no change
2191df2381bfSpraks 	 * to the directory. Therefore, we need to compare the
2192df2381bfSpraks 	 * modification time of the directory to determine if the
2193df2381bfSpraks 	 * file was actually created.
2194df2381bfSpraks 	 */
2195df2381bfSpraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2196da6c28aaSamw 	if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
2197df2381bfSpraks 		got = 0;
2198df2381bfSpraks 	}
2199da6c28aaSamw 	retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
2200da6c28aaSamw 	    flag, ct, vsecp);
2201df2381bfSpraks 
2202df2381bfSpraks 	vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2203da6c28aaSamw 	if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
2204df2381bfSpraks 		if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
2205df2381bfSpraks 		    (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
2206df2381bfSpraks 		    vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
2207df2381bfSpraks 			/*
2208df2381bfSpraks 			 * File was created.
2209df2381bfSpraks 			 */
2210df2381bfSpraks 			port_fop(vp, FOP_FILE_CREATE, retval);
2211df2381bfSpraks 		}
2212df2381bfSpraks 	}
2213df2381bfSpraks 	return (retval);
2214df2381bfSpraks }
2215df2381bfSpraks 
2216df2381bfSpraks int
port_fop_remove(femarg_t * vf,char * nm,cred_t * cr,caller_context_t * ct,int flags)2217da6c28aaSamw port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
2218da6c28aaSamw     int flags)
2219df2381bfSpraks {
2220df2381bfSpraks 	int		retval;
2221df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2222df2381bfSpraks 
2223da6c28aaSamw 	retval = vnext_remove(vf, nm, cr, ct, flags);
2224df2381bfSpraks 	port_fop(vp, FOP_FILE_REMOVE, retval);
2225df2381bfSpraks 	return (retval);
2226df2381bfSpraks }
2227df2381bfSpraks 
2228df2381bfSpraks int
port_fop_link(femarg_t * vf,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)2229da6c28aaSamw port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2230da6c28aaSamw     caller_context_t *ct, int flags)
2231df2381bfSpraks {
2232df2381bfSpraks 	int		retval;
2233df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2234df2381bfSpraks 
2235da6c28aaSamw 	retval = vnext_link(vf, svp, tnm, cr, ct, flags);
2236df2381bfSpraks 	port_fop(vp, FOP_FILE_LINK, retval);
2237df2381bfSpraks 	return (retval);
2238df2381bfSpraks }
2239df2381bfSpraks 
2240df2381bfSpraks /*
2241df2381bfSpraks  * Rename operation is allowed only when from and to directories are
2242df2381bfSpraks  * on the same filesystem. This is checked in vn_rename().
2243df2381bfSpraks  * The target directory is notified thru a VNEVENT by the filesystem
2244df2381bfSpraks  * if the source dir != target dir.
2245df2381bfSpraks  */
2246df2381bfSpraks int
port_fop_rename(femarg_t * vf,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)2247da6c28aaSamw port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
2248da6c28aaSamw     caller_context_t *ct, int flags)
2249df2381bfSpraks {
2250df2381bfSpraks 	int		retval;
2251df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2252df2381bfSpraks 
2253da6c28aaSamw 	retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
2254df2381bfSpraks 	port_fop(vp, FOP_FILE_RENAMESRC, retval);
2255df2381bfSpraks 	return (retval);
2256df2381bfSpraks }
2257df2381bfSpraks 
2258df2381bfSpraks int
port_fop_mkdir(femarg_t * vf,char * dirname,vattr_t * vap,vnode_t ** vpp,cred_t * cr,caller_context_t * ct,int flags,vsecattr_t * vsecp)2259df2381bfSpraks port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
2260da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
2261df2381bfSpraks {
2262df2381bfSpraks 	int		retval;
2263df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2264df2381bfSpraks 
2265da6c28aaSamw 	retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
2266df2381bfSpraks 	port_fop(vp, FOP_FILE_MKDIR, retval);
2267df2381bfSpraks 	return (retval);
2268df2381bfSpraks }
2269df2381bfSpraks 
2270df2381bfSpraks int
port_fop_rmdir(femarg_t * vf,char * nm,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)2271da6c28aaSamw port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2272da6c28aaSamw     caller_context_t *ct, int flags)
2273df2381bfSpraks {
2274df2381bfSpraks 	int		retval;
2275df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2276df2381bfSpraks 
2277da6c28aaSamw 	retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
2278df2381bfSpraks 	port_fop(vp, FOP_FILE_RMDIR, retval);
2279df2381bfSpraks 	return (retval);
2280df2381bfSpraks }
2281df2381bfSpraks 
2282df2381bfSpraks int
port_fop_readdir(femarg_t * vf,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)2283da6c28aaSamw port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2284da6c28aaSamw     caller_context_t *ct, int flags)
2285df2381bfSpraks {
2286df2381bfSpraks 	int		retval;
2287df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2288df2381bfSpraks 
2289da6c28aaSamw 	retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
2290df2381bfSpraks 	port_fop(vp, FOP_FILE_READDIR, retval);
2291df2381bfSpraks 	return (retval);
2292df2381bfSpraks }
2293df2381bfSpraks 
2294df2381bfSpraks int
port_fop_symlink(femarg_t * vf,char * linkname,vattr_t * vap,char * target,cred_t * cr,caller_context_t * ct,int flags)2295df2381bfSpraks port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
2296da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags)
2297df2381bfSpraks {
2298df2381bfSpraks 	int		retval;
2299df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2300df2381bfSpraks 
2301da6c28aaSamw 	retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
2302df2381bfSpraks 	port_fop(vp, FOP_FILE_SYMLINK, retval);
2303df2381bfSpraks 	return (retval);
2304df2381bfSpraks }
2305df2381bfSpraks 
2306df2381bfSpraks /*
2307df2381bfSpraks  * acl, facl call this.
2308df2381bfSpraks  */
2309df2381bfSpraks int
port_fop_setsecattr(femarg_t * vf,vsecattr_t * vsap,int flags,cred_t * cr,caller_context_t * ct)2310da6c28aaSamw port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
2311da6c28aaSamw     caller_context_t *ct)
2312df2381bfSpraks {
2313df2381bfSpraks 	int	retval;
2314df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2315da6c28aaSamw 	retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
2316df2381bfSpraks 	port_fop(vp, FOP_FILE_SETSECATTR, retval);
2317df2381bfSpraks 	return (retval);
2318df2381bfSpraks }
2319df2381bfSpraks 
2320df2381bfSpraks /*
2321df2381bfSpraks  * these are events on the watched file/directory
2322df2381bfSpraks  */
2323df2381bfSpraks int
port_fop_vnevent(femarg_t * vf,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)2324da6c28aaSamw port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
2325da6c28aaSamw     caller_context_t *ct)
2326df2381bfSpraks {
2327df2381bfSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2328df2381bfSpraks 
2329df2381bfSpraks 	switch (vnevent) {
2330df2381bfSpraks 	case	VE_RENAME_SRC:
2331df2381bfSpraks 			port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
2332df2381bfSpraks 		break;
2333df2381bfSpraks 	case	VE_RENAME_DEST:
2334df2381bfSpraks 			port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
2335df2381bfSpraks 		break;
2336df2381bfSpraks 	case	VE_REMOVE:
2337df2381bfSpraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2338df2381bfSpraks 		break;
2339df2381bfSpraks 	case	VE_RMDIR:
2340df2381bfSpraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2341df2381bfSpraks 		break;
2342df2381bfSpraks 	case	VE_CREATE:
234372102e74SBryan Cantrill 			port_fop_sendevent(vp,
234472102e74SBryan Cantrill 			    FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
2345df2381bfSpraks 		break;
2346df2381bfSpraks 	case	VE_LINK:
2347df2381bfSpraks 			port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
2348df2381bfSpraks 		break;
2349df2381bfSpraks 
2350df2381bfSpraks 	case	VE_RENAME_DEST_DIR:
2351df2381bfSpraks 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2352df2381bfSpraks 			    NULL, NULL);
2353df2381bfSpraks 		break;
2354df2381bfSpraks 
2355df2381bfSpraks 	case	VE_MOUNTEDOVER:
2356df2381bfSpraks 			port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
2357df2381bfSpraks 		break;
235872102e74SBryan Cantrill 	case	VE_TRUNCATE:
235972102e74SBryan Cantrill 			port_fop_sendevent(vp, FILE_TRUNC, NULL, NULL);
236072102e74SBryan Cantrill 		break;
2361df2381bfSpraks 	default:
2362df2381bfSpraks 		break;
2363df2381bfSpraks 	}
2364da6c28aaSamw 	return (vnext_vnevent(vf, vnevent, dvp, name, ct));
2365df2381bfSpraks }
2366