xref: /illumos-gate/usr/src/uts/common/fs/portfs/port_fop.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * File Events Notification
30  * ------------------------
31  *
32  * The File Events Notification facility provides file and directory change
33  * notification. It is implemented as an event source(PORT_SOURCE_FILE)
34  * under the Event Ports framework. Therefore the API is an extension to
35  * the Event Ports API.
36  *
37  * It uses the FEM (File Events Monitoring) framework to intercept
38  * operations on the files & directories and generate appropriate events.
39  *
40  * It provides event notification in accordance with what an application
41  * can find out by stat`ing the file and comparing time stamps. The various
42  * system calls that update the file's access, modification, and change
43  * time stamps are documented in the man page section 2.
44  *
45  * It is non intrusive. That is, having an active file event watch on a file
46  * or directory will not prevent it from being removed or renamed or block an
47  * unmount operation of the file system where the watched file or directory
48  * resides.
49  *
50  *
51  * Interface:
52  * ----------
53  *
54  *   The object for this event source is of type 'struct file_obj *'
55  *
56  *   The file that needs to be monitored is specified in 'fo_name'.
57  *   The time stamps collected by a stat(2) call are passed in fo_atime,
58  *   fo_mtime, fo_ctime. At the time a file events watch is registered, the
59  *   time stamps passed in are compared with the current time stamps of the
60  *   file. If it has changed, relevant events are sent immediately. If the time
61  *   stamps are all '0', they will not be compared.
62  *
63  *
64  * The events are delivered to an event port. A port is created using
65  * port_create().
66  *
67  * To register a file events watch on a file or directory.
68  *
69  *   port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
70  *
71  *   'user' is the user pointer to be returned with the event.
72  *
73  * To de-register a file events watch,
74  *
75  *   port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
76  *
77  * The events are collected using the port_get()/port_getn() interface. The
78  * event source will be PORT_SOURCE_FILE.
79  *
80  * After an event is delivered, the file events watch gets de-activated. To
81  * receive the next event, the process will have to re-register the watch and
82  * activate it by calling port_associate() again. This behavior is intentional
83  * and supports proper multi threaded programming when using file events
84  * notification API.
85  *
86  *
87  * Implementation overview:
88  * ------------------------
89  *
90  * Each file events watch is represented by 'portfop_t' in the kernel. A
91  * cache(in portfop_cache_t) of these portfop_t's are maintained per event
92  * port by this source. The object here is the pointer to the file_obj
93  * structure. The portfop_t's are hashed in using the object pointer. Therefore
94  * it is possible to have multiple file events watches on a file by the same
95  * process by using different object structure(file_obj_t) and hence can
96  * receive multiple event notification for a file. These watches can be for
97  * different event types.
98  *
99  * The cached entries of these file objects are retained, even after delivering
100  * an event, marking them inactive for performance reasons. The assumption
101  * is that the process would come back and re-register the file to receive
102  * further events. When there are more then 'port_fop_maxpfps' watches per file
103  * it will attempt to free the oldest inactive watches.
104  *
105  * In case the event that is being delivered is an exception event, the cached
106  * entries get removed. An exception event on a file or directory means its
107  * identity got changed(rename to/from, delete, mounted over, file system
108  * unmount).
109  *
110  * If the event port gets closed, all the associated file event watches will be
111  * removed and discarded.
112  *
113  *
114  * Data structures:
115  * ----------------
116  *
117  * The list of file event watches per file are managed by the data structure
118  * portfop_vp_t. The first time a file events watch is registered for a file,
119  * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
120  * removed and freed only when the vnode becomes inactive. The FEM hooks are
121  * also installed when the first watch is registered on a file. The FEM hooks
122  * get un-installed when all the watches are removed.
123  *
124  * Each file events watch is represented by the structure portfop_t. They
125  * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
126  * delivering an event, the portfop_t is marked inactive but retained. It is
127  * moved to the end of the list. All the active portfop_t's are maintained at
128  * the beginning. In case of exception events, the portfop_t will be removed
129  * and discarded.
130  *
131  * To intercept unmount operations, FSEM hooks are added to the file system
132  * under which files are being watched. A hash table('portfop_vfs_hash_t') of
133  * active file systems is maintained. Each file system that has active watches
134  * is represented by 'portfop_vfs_t' and is added to the hash table.
135  * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
136  * being watched on the portfop_vfs_t structure.
137  *
138  *
139  * File system support:
140  * -------------------
141  *
142  * The file system implementation has to provide vnode event notifications
143  * (vnevents) in order to support watching any files on that file system.
144  * The vnode events(vnevents) are notifications provided by the file system
145  * for name based file operations like rename, remove etc, which do not go
146  * thru the VOP_** interfaces. If the file system does not implement vnode
147  * notifications, watching for file events on such file systems is not
148  * supported. The vnode event notifications support is determined by the call
149  * vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
150  * has to implement.
151  *
152  *
153  * Locking order:
154  * --------------
155  *
156  * A file(vnode) can have file event watches registered by different processes.
157  * There is one portfop_t per watch registered. These are on the vnode's list
158  * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
159  * also on the per port cache. The cache is protected by the pfc_lock of
160  * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
161  *
162  */
163 
164 #include <sys/types.h>
165 #include <sys/systm.h>
166 #include <sys/stat.h>
167 #include <sys/errno.h>
168 #include <sys/kmem.h>
169 #include <sys/sysmacros.h>
170 #include <sys/debug.h>
171 #include <sys/vnode.h>
172 #include <sys/poll_impl.h>
173 #include <sys/port_impl.h>
174 #include <sys/fem.h>
175 #include <sys/vfs_opreg.h>
176 #include <sys/atomic.h>
177 
178 /*
179  * For special case support of mnttab (/etc/mnttab).
180  */
181 extern struct vnode *vfs_mntdummyvp;
182 extern int mntfstype;
183 
184 #define	PORTFOP_PVFSH(vfsp)	(&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
185 portfop_vfs_hash_t	 portvfs_hash[PORTFOP_PVFSHASH_SZ];
186 
187 /*
188  * Inactive file event watches(portfop_t) are retained on the vnode's list
189  * for performance reason. If the applications re-registers the file, the
190  * inactive entry is made active and moved up the list.
191  *
192  * If there are greater then the following number of watches on a vnode,
193  * it will attempt to discard an oldest inactive watch(pfp) at the time
194  * a new watch is being registered and when events get delivered. We
195  * do this to avoid accumulating inactive watches on a file.
196  */
197 int	port_fop_maxpfps = 20;
198 
199 /* local functions */
200 static int	port_fop_callback(void *, int *, pid_t, int, void *);
201 
202 static void	port_pcache_insert(portfop_cache_t *, portfop_t *);
203 static void	port_pcache_delete(portfop_cache_t *, portfop_t *);
204 static void	port_close_fop(void *arg, int port, pid_t pid, int lastclose);
205 
206 /*
207  * port fop functions that will be the fem hooks.
208  */
209 static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
210     caller_context_t *);
211 static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
212     struct caller_context *ct);
213 static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
214     caller_context_t *ct);
215 static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
216     caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
217     uint_t flags, cred_t *cr, caller_context_t *ct);
218 static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
219     caller_context_t *ct);
220 static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
221     vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
222     caller_context_t *ct, vsecattr_t *vsecp);
223 static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
224     caller_context_t *ct, int flags);
225 static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
226     caller_context_t *ct, int flags);
227 static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
228     cred_t *cr, caller_context_t *ct, int flags);
229 static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
230     vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
231     vsecattr_t *vsecp);
232 static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
233     caller_context_t *ct, int flags);
234 static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
235     caller_context_t *ct, int flags);
236 static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
237     char *target, cred_t *cr, caller_context_t *ct, int flags);
238 static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
239     cred_t *cr, caller_context_t *ct);
240 
241 static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
242     char *cname, caller_context_t *ct);
243 
244 static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
245 
246 
247 /*
248  * Fem hooks.
249  */
250 const fs_operation_def_t	port_vnodesrc_template[] = {
251 	VOPNAME_OPEN,		{ .femop_open = port_fop_open },
252 	VOPNAME_READ,		{ .femop_read = port_fop_read },
253 	VOPNAME_WRITE,		{ .femop_write = port_fop_write },
254 	VOPNAME_MAP,		{ .femop_map = port_fop_map },
255 	VOPNAME_SETATTR, 	{ .femop_setattr = port_fop_setattr },
256 	VOPNAME_CREATE,		{ .femop_create = port_fop_create },
257 	VOPNAME_REMOVE,		{ .femop_remove = port_fop_remove },
258 	VOPNAME_LINK,		{ .femop_link = port_fop_link },
259 	VOPNAME_RENAME,		{ .femop_rename = port_fop_rename },
260 	VOPNAME_MKDIR,		{ .femop_mkdir = port_fop_mkdir },
261 	VOPNAME_RMDIR,		{ .femop_rmdir = port_fop_rmdir },
262 	VOPNAME_READDIR,	{ .femop_readdir = port_fop_readdir },
263 	VOPNAME_SYMLINK,	{ .femop_symlink = port_fop_symlink },
264 	VOPNAME_SETSECATTR, 	{ .femop_setsecattr = port_fop_setsecattr },
265 	VOPNAME_VNEVENT,	{ .femop_vnevent = port_fop_vnevent },
266 	NULL,	NULL
267 };
268 
269 /*
270  * Fsem - vfs ops hooks
271  */
272 const fs_operation_def_t	port_vfssrc_template[] = {
273 	VFSNAME_UNMOUNT, 	{ .fsemop_unmount = port_fop_unmount },
274 	NULL,	NULL
275 };
276 
277 fem_t *fop_femop;
278 fsem_t *fop_fsemop;
279 
280 static fem_t *
281 port_fop_femop()
282 {
283 	fem_t *femp;
284 	if (fop_femop != NULL)
285 		return (fop_femop);
286 	if (fem_create("portfop_fem",
287 	    (const struct fs_operation_def *)port_vnodesrc_template,
288 	    (fem_t **)&femp)) {
289 		return (NULL);
290 	}
291 	if (casptr(&fop_femop, NULL, femp) != NULL) {
292 		/*
293 		 * some other thread beat us to it.
294 		 */
295 		fem_free(femp);
296 	}
297 	return (fop_femop);
298 }
299 
300 static fsem_t *
301 port_fop_fsemop()
302 {
303 	fsem_t *fsemp;
304 	if (fop_fsemop != NULL)
305 		return (fop_fsemop);
306 	if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
307 		return (NULL);
308 	}
309 	if (casptr(&fop_fsemop, NULL, fsemp) != NULL) {
310 		/*
311 		 * some other thread beat us to it.
312 		 */
313 		fsem_free(fsemp);
314 	}
315 	return (fop_fsemop);
316 }
317 
318 /*
319  * port_fop_callback()
320  * - PORT_CALLBACK_DEFAULT
321  *	The file event will be delivered to the application.
322  * - PORT_CALLBACK_DISSOCIATE
323  *	The object will be dissociated from  the port.
324  * - PORT_CALLBACK_CLOSE
325  *	The object will be dissociated from the port because the port
326  *	is being closed.
327  */
328 /* ARGSUSED */
329 static int
330 port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
331 {
332 	portfop_t	*pfp = (portfop_t *)arg;
333 	port_kevent_t	*pkevp = (port_kevent_t *)evp;
334 	int		error = 0;
335 
336 	ASSERT((events != NULL));
337 	if (flag == PORT_CALLBACK_DEFAULT) {
338 		if (curproc->p_pid != pid) {
339 				return (EACCES); /* deny delivery of events */
340 		}
341 
342 		*events = pkevp->portkev_events;
343 		pkevp->portkev_events = 0;
344 		if (pfp != NULL) {
345 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
346 		}
347 	}
348 	return (error);
349 }
350 
351 /*
352  * Inserts a portfop_t into the port sources cache's.
353  */
354 static void
355 port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
356 {
357 	portfop_t	**bucket;
358 
359 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
360 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
361 	pfp->pfop_hashnext = *bucket;
362 	*bucket = pfp;
363 	pfcp->pfc_objcount++;
364 }
365 
366 /*
367  * Remove the pfp from the port source cache.
368  */
369 static void
370 port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
371 {
372 	portfop_t	*lpdp;
373 	portfop_t	*cpdp;
374 	portfop_t	**bucket;
375 
376 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
377 	cpdp = *bucket;
378 	if (pfp == cpdp) {
379 		*bucket = pfp->pfop_hashnext;
380 	} else {
381 		while (cpdp != NULL) {
382 			lpdp = cpdp;
383 			cpdp = cpdp->pfop_hashnext;
384 			if (cpdp == pfp) {
385 				/* portfop struct found */
386 				lpdp->pfop_hashnext = pfp->pfop_hashnext;
387 				break;
388 			}
389 		}
390 	}
391 	pfcp->pfc_objcount--;
392 }
393 
394 /*
395  * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
396  * when these routines are called.
397  *
398  * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
399  * It is used to discard the oldtest inactive pfp if the number of entries
400  * exceed the limit.
401  */
402 static void
403 port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
404 {
405 	if (where == 1) {
406 		list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
407 	} else {
408 		list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
409 	}
410 	if (pvp->pvp_lpfop == NULL) {
411 		pvp->pvp_lpfop = pfp;
412 	}
413 	pvp->pvp_cnt++;
414 }
415 
416 static void
417 port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
418 {
419 	port_fop_listinsert(pvp, pfp, 1);
420 }
421 
422 static void
423 port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
424 {
425 	/*
426 	 * We point lpfop to an inactive one, if it was initially pointing
427 	 * to an active one. Insert to the tail is done only when a pfp goes
428 	 * inactive.
429 	 */
430 	if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
431 		pvp->pvp_lpfop = pfp;
432 	}
433 	port_fop_listinsert(pvp, pfp, 0);
434 }
435 
436 static void
437 port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
438 {
439 	if (pvp->pvp_lpfop == pfp) {
440 		pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
441 	}
442 
443 	list_remove(&pvp->pvp_pfoplist, (void *)pfp);
444 
445 	pvp->pvp_cnt--;
446 	if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
447 		pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
448 	}
449 }
450 
451 static void
452 port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
453 {
454 	list_move_tail(tlist, &pvp->pvp_pfoplist);
455 	pvp->pvp_lpfop = NULL;
456 	pvp->pvp_cnt = 0;
457 }
458 
459 /*
460  * Remove a portfop_t from the port cache hash table and discard it.
461  * It is called only when pfp is not on the vnode's list. Otherwise,
462  * port_remove_fop() is called.
463  */
464 void
465 port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
466 {
467 	port_kevent_t	*pkevp;
468 
469 
470 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
471 
472 	pkevp = pfp->pfop_pev;
473 	pfp->pfop_pev = NULL;
474 
475 	if (pkevp != NULL) {
476 		(void) port_remove_done_event(pkevp);
477 		port_free_event_local(pkevp, 0);
478 	}
479 
480 	port_pcache_delete(pfcp, pfp);
481 
482 	if (pfp->pfop_cname != NULL)
483 		kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
484 	kmem_free(pfp, sizeof (portfop_t));
485 	if (pfcp->pfc_objcount == 0)
486 		cv_signal(&pfcp->pfc_lclosecv);
487 }
488 
489 /*
490  * if we have too many watches on the vnode, attempt to discard an
491  * inactive one.
492  */
493 static void
494 port_fop_trimpfplist(vnode_t *vp)
495 {
496 	portfop_vp_t *pvp;
497 	portfop_t *pfp = NULL;
498 	portfop_cache_t *pfcp;
499 
500 	/*
501 	 * Due to a reference the vnode cannot disappear, v_fopdata should
502 	 * not change.
503 	 */
504 	if ((pvp = vp->v_fopdata) != NULL &&
505 	    pvp->pvp_cnt > port_fop_maxpfps) {
506 		mutex_enter(&pvp->pvp_mutex);
507 		pfp = pvp->pvp_lpfop;
508 		pfcp = pfp->pfop_pcache;
509 		/*
510 		 * only if we can get the cache lock, we need to
511 		 * do this due to reverse lock order and some thread
512 		 * that may be trying to reactivate this entry.
513 		 */
514 		if (mutex_tryenter(&pfcp->pfc_lock)) {
515 			if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
516 			    !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
517 				port_fop_listremove(pvp, pfp);
518 				pfp->pfop_flags |= PORT_FOP_REMOVING;
519 			} else {
520 				mutex_exit(&pfcp->pfc_lock);
521 				pfp = NULL;
522 			}
523 		} else {
524 			pfp = NULL;
525 		}
526 		mutex_exit(&pvp->pvp_mutex);
527 
528 		/*
529 		 * discard pfp if any.
530 		 */
531 		if (pfp != NULL) {
532 			port_pcache_remove_fop(pfcp, pfp);
533 			mutex_exit(&pfcp->pfc_lock);
534 		}
535 	}
536 }
537 
538 void
539 port_fop_femuninstall(vnode_t *vp)
540 {
541 	portfop_vp_t	*pvp;
542 	vfs_t		*vfsp;
543 	portfop_vfs_t *pvfsp;
544 	portfop_vfs_hash_t	*pvfsh;
545 	kmutex_t	*mtx;
546 
547 	/*
548 	 * if list is empty, uninstall fem.
549 	 */
550 	pvp = vp->v_fopdata;
551 	ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
552 
553 	/*
554 	 * make sure the list is empty.
555 	 */
556 	if (!list_head(&pvp->pvp_pfoplist)) {
557 
558 		/*
559 		 * we could possibly uninstall the fem hooks when
560 		 * the vnode becomes inactive and the v_fopdata is
561 		 * free. But the hooks get triggered unnecessarily
562 		 * even though there are no active watches. So, we
563 		 * uninstall it here.
564 		 */
565 		(void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
566 		pvp->pvp_femp = NULL;
567 		mutex_exit(&pvp->pvp_mutex);
568 
569 
570 		/*
571 		 * If we successfully uninstalled fem, no process is watching
572 		 * this vnode, Remove it from the vfs's list of watched vnodes.
573 		 */
574 		pvfsp = pvp->pvp_pvfsp;
575 		vfsp = vp->v_vfsp;
576 		pvfsh = PORTFOP_PVFSH(vfsp);
577 		mtx = &pvfsh->pvfshash_mutex;
578 		mutex_enter(mtx);
579 		/*
580 		 * If unmount is in progress, that thread will remove and
581 		 * release the vnode from the vfs's list, just leave.
582 		 */
583 		if (!pvfsp->pvfs_unmount) {
584 			list_remove(&pvfsp->pvfs_pvplist, pvp);
585 			mutex_exit(mtx);
586 			VN_RELE(vp);
587 		} else {
588 			mutex_exit(mtx);
589 		}
590 	} else {
591 		mutex_exit(&pvp->pvp_mutex);
592 	}
593 }
594 
595 /*
596  * Remove pfp from the vnode's watch list and the cache and discard it.
597  * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
598  * Returns 1 if removed successfully.
599  *
600  * The *active is set to indicate if the pfp was still active(no events had
601  * been posted, or the posted event had not been collected yet and it was
602  * able to remove it from the port's queue).
603  */
604 int
605 port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
606     int *active)
607 {
608 	vnode_t		*vp;
609 	portfop_vp_t	*pvp;
610 	int	tactive = 0;
611 
612 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
613 	vp = pfp->pfop_vp;
614 	pvp = vp->v_fopdata;
615 	mutex_enter(&pvp->pvp_mutex);
616 
617 	/*
618 	 * if not cleanup, remove it only if the pfp is still active and
619 	 * is not being removed by some other thread.
620 	 */
621 	if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
622 	    pfp->pfop_flags & PORT_FOP_REMOVING)) {
623 		mutex_exit(&pvp->pvp_mutex);
624 		return (0);
625 	}
626 
627 	/*
628 	 * mark it inactive.
629 	 */
630 	if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
631 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
632 		tactive = 1;
633 	}
634 
635 	/*
636 	 * Check if the pfp is still on the vnode's list. This can
637 	 * happen if port_fop_excep() is in the process of removing it.
638 	 * In case of cleanup, just mark this pfp as inactive so that no
639 	 * new events (VNEVENT) will be delivered, and remove it from the
640 	 * event queue if it was already queued. Since the cache lock is
641 	 * held, the pfp will not disappear, even though it is being
642 	 * removed.
643 	 */
644 	if (pfp->pfop_flags & PORT_FOP_REMOVING) {
645 		mutex_exit(&pvp->pvp_mutex);
646 		if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
647 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
648 			tactive = 1;
649 		}
650 		if (active) {
651 			*active = tactive;
652 		}
653 		return (1);
654 	}
655 
656 	/*
657 	 * if we find an event on the queue and removed it, then this
658 	 * association is considered active.
659 	 */
660 	if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
661 		pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
662 		tactive = 1;
663 	}
664 
665 	if (active) {
666 		*active = tactive;
667 	}
668 	pvp = (portfop_vp_t *)vp->v_fopdata;
669 
670 	/*
671 	 * remove pfp from the vnode's list
672 	 */
673 	port_fop_listremove(pvp, pfp);
674 
675 	/*
676 	 * If no more associations on the vnode, uninstall fem hooks.
677 	 * The pvp mutex will be released in this routine.
678 	 */
679 	port_fop_femuninstall(vp);
680 	port_pcache_remove_fop(pfcp, pfp);
681 	return (1);
682 }
683 
684 /*
685  * This routine returns a pointer to a cached portfop entry, or NULL if it
686  * does not find it in the hash table. The object pointer is used as index.
687  * The entries are hashed by the object's address. We need to match the pid
688  * as the evet port can be shared between processes. The file events
689  * watches are per process only.
690  */
691 portfop_t *
692 port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
693 {
694 	portfop_t	*pfp = NULL;
695 	portfop_t	**bucket;
696 
697 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
698 	bucket = PORT_FOP_BUCKET(pfcp, obj);
699 	pfp = *bucket;
700 	while (pfp != NULL) {
701 		if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
702 			break;
703 		pfp = pfp->pfop_hashnext;
704 	}
705 	return (pfp);
706 }
707 
708 /*
709  * Given the file name, get the vnode and also the directory vnode
710  * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
711  * the vnode(s).
712  */
713 int
714 port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp,
715 	char **cname, int *len, int follow)
716 {
717 	int error = 0;
718 	struct pathname pn;
719 	char *fname;
720 
721 	if (get_udatamodel() == DATAMODEL_NATIVE) {
722 		fname = ((file_obj_t *)objptr)->fo_name;
723 #ifdef  _SYSCALL32_IMPL
724 	} else {
725 		fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
726 #endif	/* _SYSCALL32_IMPL */
727 	}
728 
729 	/*
730 	 * lookuppn may fail with EINVAL, if dvp is  non-null(like when
731 	 * looking for "."). So call again with dvp = NULL.
732 	 */
733 	if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
734 		return (error);
735 	}
736 
737 	error = lookuppn(&pn, NULL, follow, dvp, vp);
738 	if (error == EINVAL) {
739 		pn_free(&pn);
740 		if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
741 			return (error);
742 		}
743 		error = lookuppn(&pn, NULL, follow, NULL, vp);
744 		if (dvp != NULL) {
745 			*dvp = NULL;
746 		}
747 	}
748 
749 	if (error == 0 && cname != NULL && len != NULL) {
750 		pn_setlast(&pn);
751 		*len = pn.pn_pathlen;
752 		*cname = kmem_alloc(*len + 1, KM_SLEEP);
753 		(void) strcpy(*cname, pn.pn_path);
754 	} else {
755 		if (cname != NULL && len != NULL) {
756 			*cname = NULL;
757 			*len = 0;
758 		}
759 	}
760 
761 	pn_free(&pn);
762 	return (error);
763 }
764 
765 port_source_t *
766 port_getsrc(port_t *pp, int source)
767 {
768 	port_source_t *pse;
769 	int	lock = 0;
770 	/*
771 	 * get the port source structure.
772 	 */
773 	if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
774 		mutex_enter(&pp->port_queue.portq_source_mutex);
775 		lock = 1;
776 	}
777 
778 	pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
779 	for (; pse != NULL; pse = pse->portsrc_next) {
780 		if (pse->portsrc_source == source)
781 			break;
782 	}
783 
784 	if (lock) {
785 		mutex_exit(&pp->port_queue.portq_source_mutex);
786 	}
787 	return (pse);
788 }
789 
790 
791 /*
792  * Compare time stamps and generate an event if it has changed.
793  * Note that the port cache pointer will be valid due to a reference
794  * to the port. We need to grab the port cache lock and verify that
795  * the pfp is still the same before proceeding to deliver an event.
796  */
797 static void
798 port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
799 	portfop_t *pfp, void *objptr, uintptr_t object)
800 {
801 	vattr_t		vatt;
802 	portfop_vp_t	*pvp = vp->v_fopdata;
803 	int		events = 0;
804 	port_kevent_t	*pkevp;
805 	file_obj_t	*fobj;
806 	portfop_t	*tpfp;
807 
808 	/*
809 	 * If time stamps are specified, get attributes and compare.
810 	 */
811 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
812 	if (get_udatamodel() == DATAMODEL_NATIVE) {
813 		fobj = (file_obj_t *)objptr;
814 		if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
815 		    fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
816 		    fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
817 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
818 				return;
819 			}
820 		} else {
821 			/*
822 			 * timestamp not specified, all 0's,
823 			 */
824 			return;
825 		}
826 #ifdef  _SYSCALL32_IMPL
827 	} else {
828 		file_obj32_t	*fobj32;
829 		fobj32 = (file_obj32_t *)objptr;
830 		if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
831 		    fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
832 		    fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
833 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
834 				return;
835 			}
836 		} else {
837 			/*
838 			 * timestamp not specified, all 0.
839 			 */
840 			return;
841 		}
842 #endif /* _SYSCALL32_IMPL */
843 	}
844 
845 	/*
846 	 * Now grab the cache lock and verify that we are still
847 	 * dealing with the same pfp and curthread is the one
848 	 * which registered it. We need to do this to avoid
849 	 * delivering redundant events.
850 	 */
851 	mutex_enter(&pfcp->pfc_lock);
852 	tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
853 
854 	if (tpfp == NULL || tpfp != pfp ||
855 	    pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
856 	    pfp->pfop_callrid != curthread ||
857 	    !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
858 		/*
859 		 * Some other event was delivered, the file
860 		 * watch was removed or reassociated. Just
861 		 * ignore it and leave
862 		 */
863 		mutex_exit(&pfcp->pfc_lock);
864 		return;
865 	}
866 
867 	mutex_enter(&pvp->pvp_mutex);
868 	/*
869 	 * The pfp cannot disappear as the port cache lock is held.
870 	 * While the pvp_mutex is held, no events will get delivered.
871 	 */
872 	if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
873 	    !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
874 		if (get_udatamodel() == DATAMODEL_NATIVE) {
875 			fobj = (file_obj_t *)objptr;
876 			if (pfp->pfop_events & FILE_ACCESS &&
877 			    (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
878 			    (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
879 			    vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
880 				events |= FILE_ACCESS;
881 
882 			if (pfp->pfop_events & FILE_MODIFIED &&
883 			    (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
884 			    (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
885 			    vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
886 				events |= FILE_MODIFIED;
887 
888 			if (pfp->pfop_events & FILE_ATTRIB &&
889 			    (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
890 			    (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
891 			    vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
892 				events |= FILE_ATTRIB;
893 #ifdef  _SYSCALL32_IMPL
894 		} else {
895 			file_obj32_t	*fobj32;
896 			fobj32 = (file_obj32_t *)objptr;
897 			if (pfp->pfop_events & FILE_ACCESS &&
898 			    (fobj32->fo_atime.tv_sec ||
899 			    fobj32->fo_atime.tv_nsec) &&
900 			    (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
901 			    vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
902 				events |= FILE_ACCESS;
903 
904 			if (pfp->pfop_events & FILE_MODIFIED &&
905 			    (fobj32->fo_mtime.tv_sec ||
906 			    fobj32->fo_mtime.tv_nsec) &&
907 			    (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
908 			    vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
909 				events |= FILE_MODIFIED;
910 
911 			if (pfp->pfop_events & FILE_ATTRIB &&
912 			    (fobj32->fo_ctime.tv_sec ||
913 			    fobj32->fo_ctime.tv_nsec) &&
914 			    (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
915 			    vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
916 				events |= FILE_ATTRIB;
917 #endif /* _SYSCALL32_IMPL */
918 		}
919 
920 		/*
921 		 * No events to deliver
922 		 */
923 		if (events == 0) {
924 			mutex_exit(&pvp->pvp_mutex);
925 			mutex_exit(&pfcp->pfc_lock);
926 			return;
927 		}
928 
929 		/*
930 		 * Deliver the event now.
931 		 */
932 		pkevp = pfp->pfop_pev;
933 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
934 		pkevp->portkev_events |= events;
935 		/*
936 		 * Move it to the tail as active once are in the
937 		 * beginning of the list.
938 		 */
939 		port_fop_listremove(pvp, pfp);
940 		port_fop_listinsert_tail(pvp, pfp);
941 		port_send_event(pkevp);
942 		pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
943 	}
944 	mutex_exit(&pvp->pvp_mutex);
945 	mutex_exit(&pfcp->pfc_lock);
946 }
947 
948 /*
949  * Add the event source to the port and return the port source cache pointer.
950  */
951 int
952 port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
953 {
954 	portfop_cache_t *pfcp;
955 	port_source_t	*pse;
956 	int		error;
957 
958 	/*
959 	 * associate PORT_SOURCE_FILE source with the port, if it is
960 	 * not associated yet. Note the PORT_SOURCE_FILE source is
961 	 * associated once and will not be dissociated.
962 	 */
963 	if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
964 		if (error = port_associate_ksource(pp->port_fd, source,
965 		    &pse, port_close_fop, pp, NULL)) {
966 			*pfcpp = NULL;
967 			return (error);
968 		}
969 	}
970 
971 	/*
972 	 * Get the portfop cache pointer.
973 	 */
974 	if ((pfcp = pse->portsrc_data) == NULL) {
975 		/*
976 		 * This is the first time that a file is being associated,
977 		 * create the portfop cache.
978 		 */
979 		pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
980 		mutex_enter(&pp->port_queue.portq_source_mutex);
981 		if (pse->portsrc_data == NULL) {
982 			pse->portsrc_data = pfcp;
983 			mutex_exit(&pp->port_queue.portq_source_mutex);
984 		} else {
985 			/*
986 			 * someone else created the port cache, free
987 			 * what we just now allocated.
988 			 */
989 			mutex_exit(&pp->port_queue.portq_source_mutex);
990 			kmem_free(pfcp, sizeof (portfop_cache_t));
991 			pfcp = pse->portsrc_data;
992 		}
993 	}
994 	*pfcpp = pfcp;
995 	return (0);
996 }
997 
998 /*
999  * Add the given pvp on the file system's list of vnodes watched.
1000  */
1001 int
1002 port_fop_pvfsadd(portfop_vp_t *pvp)
1003 {
1004 	int error = 0;
1005 	vnode_t	*vp = pvp->pvp_vp;
1006 	portfop_vfs_hash_t *pvfsh;
1007 	portfop_vfs_t	 *pvfsp;
1008 	fsem_t		*fsemp;
1009 
1010 	pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
1011 	mutex_enter(&pvfsh->pvfshash_mutex);
1012 	for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
1013 	    pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
1014 		;
1015 
1016 	if (!pvfsp) {
1017 		if ((fsemp = port_fop_fsemop()) != NULL) {
1018 			if ((error = fsem_install(vp->v_vfsp, fsemp,
1019 			    vp->v_vfsp, OPUNIQ, NULL, NULL))) {
1020 				mutex_exit(&pvfsh->pvfshash_mutex);
1021 				return (error);
1022 			}
1023 		} else {
1024 			mutex_exit(&pvfsh->pvfshash_mutex);
1025 			return (EINVAL);
1026 		}
1027 		pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
1028 		pvfsp->pvfs = vp->v_vfsp;
1029 		list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
1030 		    offsetof(portfop_vp_t, pvp_pvfsnode));
1031 		pvfsp->pvfs_fsemp = fsemp;
1032 		pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
1033 		pvfsh->pvfshash_pvfsp = pvfsp;
1034 	}
1035 
1036 	/*
1037 	 * check if an unmount is in progress.
1038 	 */
1039 	if (!pvfsp->pvfs_unmount) {
1040 		/*
1041 		 * insert the pvp on list.
1042 		 */
1043 		pvp->pvp_pvfsp = pvfsp;
1044 		list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
1045 	} else {
1046 		error = EINVAL;
1047 	}
1048 	mutex_exit(&pvfsh->pvfshash_mutex);
1049 	return (error);
1050 }
1051 
1052 /*
1053  * Installs the portfop_vp_t data structure on the
1054  * vnode. The 'pvp_femp == NULL' indicates it is not
1055  * active. The fem hooks have to be installed.
1056  * The portfop_vp_t is only freed when the vnode gets freed.
1057  */
1058 void
1059 port_install_fopdata(vnode_t *vp)
1060 {
1061 	portfop_vp_t *npvp;
1062 
1063 	npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
1064 	mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
1065 	list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
1066 	    offsetof(portfop_t, pfop_node));
1067 	npvp->pvp_vp = vp;
1068 	/*
1069 	 * If v_fopdata is not null, some other thread beat us to it.
1070 	 */
1071 	if (casptr(&vp->v_fopdata, NULL, npvp) != NULL) {
1072 		mutex_destroy(&npvp->pvp_mutex);
1073 		list_destroy(&npvp->pvp_pfoplist);
1074 		kmem_free(npvp, sizeof (*npvp));
1075 	}
1076 }
1077 
1078 
1079 /*
1080  * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
1081  * to the vnode's list. The association is identified by the object pointer
1082  * address and pid.
1083  */
1084 int
1085 port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
1086 	uintptr_t object, int events, void *user, char *cname, int clen,
1087 	vnode_t *dvp)
1088 {
1089 	portfop_t	*pfp = NULL;
1090 	port_kevent_t	*pkevp;
1091 	fem_t		*femp;
1092 	int		error = 0;
1093 	portfop_vp_t	*pvp;
1094 
1095 
1096 	/*
1097 	 * The port cache mutex is held.
1098 	 */
1099 	*pfpp  = NULL;
1100 
1101 
1102 	/*
1103 	 * At this point the fem monitor is installed.
1104 	 * Allocate a port event structure per vnode association.
1105 	 */
1106 	if (pfp == NULL) {
1107 		if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1108 		    PORT_ALLOC_CACHED, &pkevp)) {
1109 			return (error);
1110 		}
1111 		pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
1112 		pfp->pfop_pev = pkevp;
1113 	}
1114 
1115 	pfp->pfop_vp = vp;
1116 	pfp->pfop_pid = curproc->p_pid;
1117 	pfp->pfop_pcache = pfcp;
1118 	pfp->pfop_pp = pp;
1119 	pfp->pfop_flags |= PORT_FOP_ACTIVE;
1120 	pfp->pfop_cname = cname;
1121 	pfp->pfop_clen = clen;
1122 	pfp->pfop_dvp = dvp;
1123 	pfp->pfop_object = object;
1124 
1125 	pkevp->portkev_callback = port_fop_callback;
1126 	pkevp->portkev_arg = pfp;
1127 	pkevp->portkev_object = object;
1128 	pkevp->portkev_user = user;
1129 	pkevp->portkev_events = 0;
1130 
1131 	port_pcache_insert(pfcp, pfp);
1132 
1133 	/*
1134 	 * Register a new file events monitor for this file(vnode), if not
1135 	 * done already.
1136 	 */
1137 	if ((pvp = vp->v_fopdata) == NULL) {
1138 		port_install_fopdata(vp);
1139 		pvp = vp->v_fopdata;
1140 	}
1141 
1142 	mutex_enter(&pvp->pvp_mutex);
1143 	/*
1144 	 * if the vnode does not have the file events hooks, install it.
1145 	 */
1146 	if (pvp->pvp_femp == NULL) {
1147 		if ((femp = port_fop_femop()) != NULL) {
1148 			if (!(error = fem_install(pfp->pfop_vp, femp,
1149 			    (void *)vp, OPUNIQ, NULL, NULL))) {
1150 				pvp->pvp_femp = femp;
1151 				/*
1152 				 * add fsem_t hooks to the vfsp and add pvp to
1153 				 * the list of vnodes for this vfs.
1154 				 */
1155 				if (!(error = port_fop_pvfsadd(pvp))) {
1156 					/*
1157 					 * Hold a reference to the vnode since
1158 					 * we successfully installed the hooks.
1159 					 */
1160 					VN_HOLD(vp);
1161 				} else {
1162 					(void) fem_uninstall(vp, femp, vp);
1163 					pvp->pvp_femp = NULL;
1164 				}
1165 			}
1166 		} else {
1167 			error = EINVAL;
1168 		}
1169 	}
1170 
1171 	if (error) {
1172 		/*
1173 		 * pkevp will get freed here.
1174 		 */
1175 		pfp->pfop_cname = NULL;
1176 		port_pcache_remove_fop(pfcp, pfp);
1177 		mutex_exit(&pvp->pvp_mutex);
1178 		return (error);
1179 	}
1180 
1181 	/*
1182 	 * insert the pfp on the vnode's list. After this
1183 	 * events can get delivered.
1184 	 */
1185 	pfp->pfop_events = events;
1186 	port_fop_listinsert_head(pvp, pfp);
1187 
1188 	mutex_exit(&pvp->pvp_mutex);
1189 	*pfpp = pfp;
1190 	return (0);
1191 }
1192 
1193 vnode_t *
1194 port_resolve_vp(vnode_t *vp)
1195 {
1196 	vnode_t *rvp;
1197 	/*
1198 	 * special case /etc/mnttab(mntfs type). The mntfstype != 0
1199 	 * if mntfs got mounted.
1200 	 */
1201 	if (vfs_mntdummyvp && mntfstype != 0 &&
1202 	    vp->v_vfsp->vfs_fstype == mntfstype) {
1203 		VN_RELE(vp);
1204 		vp = vfs_mntdummyvp;
1205 		VN_HOLD(vfs_mntdummyvp);
1206 	}
1207 
1208 	/*
1209 	 * This should take care of lofs mounted fs systems and nfs4
1210 	 * hardlinks.
1211 	 */
1212 	if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
1213 		VN_HOLD(rvp);
1214 		VN_RELE(vp);
1215 		vp = rvp;
1216 	}
1217 	return (vp);
1218 }
1219 
1220 /*
1221  * Register a file events watch on the given file associated to the port *pp.
1222  *
1223  * The association is identified by the object pointer and the pid.
1224  * The events argument contains the events to be monitored for.
1225  */
1226 int
1227 port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
1228     void *user)
1229 {
1230 	portfop_cache_t	*pfcp;
1231 	vnode_t		*vp, *dvp;
1232 	portfop_t	*pfp;
1233 	int		error = 0;
1234 	file_obj_t	fobj;
1235 	void		*objptr;
1236 	char		*cname;
1237 	int		clen;
1238 	int		follow;
1239 
1240 	/*
1241 	 * check that events specified are valid.
1242 	 */
1243 	if ((events & ~FILE_EVENTS_MASK) != 0)
1244 		return (EINVAL);
1245 
1246 	if (get_udatamodel() == DATAMODEL_NATIVE) {
1247 		if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
1248 			return (EFAULT);
1249 		objptr = (void *)&fobj;
1250 #ifdef  _SYSCALL32_IMPL
1251 	} else {
1252 		file_obj32_t	fobj32;
1253 		if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
1254 			return (EFAULT);
1255 		objptr = (void *)&fobj32;
1256 #endif  /* _SYSCALL32_IMPL */
1257 	}
1258 
1259 	vp = dvp = NULL;
1260 
1261 	/*
1262 	 * find out if we need to follow symbolic links.
1263 	 */
1264 	follow = !(events & FILE_NOFOLLOW);
1265 	events = events & ~FILE_NOFOLLOW;
1266 
1267 	/*
1268 	 * lookup and find the vnode and its directory vnode of the given
1269 	 * file.
1270 	 */
1271 	if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
1272 	    follow)) != 0) {
1273 		return (error);
1274 	}
1275 
1276 	if (dvp != NULL) {
1277 		dvp = port_resolve_vp(dvp);
1278 		VN_RELE(dvp);
1279 	}
1280 
1281 	/*
1282 	 * Not found
1283 	 */
1284 	if (vp == NULL) {
1285 		error = ENOENT;
1286 		goto errout;
1287 	}
1288 
1289 	vp = port_resolve_vp(vp);
1290 
1291 
1292 	if (vp != NULL && vnevent_support(vp, NULL)) {
1293 		error = ENOTSUP;
1294 		goto errout;
1295 	}
1296 
1297 	/*
1298 	 * Associate this source to the port and get the per port
1299 	 * fop cache pointer. If the source is already associated, it
1300 	 * will just return the cache pointer.
1301 	 */
1302 	if (error = port_fop_associate_source(&pfcp, pp, source)) {
1303 		goto errout;
1304 	}
1305 
1306 	/*
1307 	 * Check if there is an existing association of this file.
1308 	 */
1309 	mutex_enter(&pfcp->pfc_lock);
1310 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1311 
1312 	/*
1313 	 * if it is not the same vnode, just discard it.
1314 	 */
1315 	if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
1316 		(void) port_remove_fop(pfp, pfcp, 1, NULL);
1317 		pfp = NULL;
1318 	}
1319 
1320 	if (pfp == NULL) {
1321 		vnode_t *tvp;
1322 		portfop_t	*tpfp;
1323 		int error;
1324 
1325 		/*
1326 		 * Add a new association, save the file name and the
1327 		 * directory vnode pointer.
1328 		 */
1329 		if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
1330 		    events, user, cname, clen, dvp)) {
1331 			mutex_exit(&pfcp->pfc_lock);
1332 			goto errout;
1333 		}
1334 
1335 		pfp->pfop_callrid = curthread;
1336 		/*
1337 		 * File name used, so make sure we don't free it.
1338 		 */
1339 		cname = NULL;
1340 
1341 		/*
1342 		 * We need to check if the file was removed after the
1343 		 * the lookup and before the fem hooks where added. If
1344 		 * so, return error. The vnode will still exist as we have
1345 		 * a hold on it.
1346 		 *
1347 		 * Drop the cache lock before calling port_fop_getdvp().
1348 		 * port_fop_getdvp() may block either in the vfs layer
1349 		 * or some filesystem.  Therefore there is potential
1350 		 * for deadlock if cache lock is held and if some other
1351 		 * thread is attempting to deliver file events which would
1352 		 * require getting the cache lock, while it may be holding
1353 		 * the filesystem or vfs layer locks.
1354 		 */
1355 		mutex_exit(&pfcp->pfc_lock);
1356 		tvp = NULL;
1357 		if ((error = port_fop_getdvp(objptr, &tvp, NULL,
1358 		    NULL, NULL, follow)) == 0) {
1359 			if (tvp != NULL) {
1360 				tvp = port_resolve_vp(tvp);
1361 				/*
1362 				 * This vnode pointer is just used
1363 				 * for comparison, so rele it
1364 				 */
1365 				VN_RELE(tvp);
1366 			}
1367 		}
1368 
1369 		if (error || tvp == NULL || tvp != vp) {
1370 			/*
1371 			 * Since we dropped the cache lock, make sure
1372 			 * we are still dealing with the same pfp and this
1373 			 * is the thread which registered it.
1374 			 */
1375 			mutex_enter(&pfcp->pfc_lock);
1376 			tpfp = port_cache_lookup_fop(pfcp,
1377 			    curproc->p_pid, object);
1378 
1379 			error = 0;
1380 			if (tpfp == NULL || tpfp != pfp ||
1381 			    pfp->pfop_vp != vp ||
1382 			    pfp->pfop_dvp != dvp ||
1383 			    pfp->pfop_callrid != curthread) {
1384 				/*
1385 				 * Some other event was delivered, the file
1386 				 * watch was removed or reassociated, just
1387 				 * ignore it and leave
1388 				 */
1389 				mutex_exit(&pfcp->pfc_lock);
1390 				goto errout;
1391 			}
1392 
1393 			/*
1394 			 * remove the pfp and fem hooks, if pfp still
1395 			 * active and it is not being removed from
1396 			 * the vnode list. This is checked in
1397 			 * port_remove_fop with the vnode lock held.
1398 			 */
1399 			if (port_remove_fop(pfp, pfcp, 0, NULL)) {
1400 				/*
1401 				 * The pfp was removed, means no
1402 				 * events where queued. Report the
1403 				 * error now.
1404 				 */
1405 				error = EINVAL;
1406 			}
1407 			mutex_exit(&pfcp->pfc_lock);
1408 			goto errout;
1409 		}
1410 	} else {
1411 		portfop_vp_t	*pvp = vp->v_fopdata;
1412 
1413 		/*
1414 		 * Re-association of the object.
1415 		 */
1416 		mutex_enter(&pvp->pvp_mutex);
1417 
1418 		/*
1419 		 * remove any queued up event.
1420 		 */
1421 		if (port_remove_done_event(pfp->pfop_pev)) {
1422 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
1423 		}
1424 
1425 		/*
1426 		 * set new events to watch.
1427 		 */
1428 		pfp->pfop_events = events;
1429 
1430 		/*
1431 		 * If not active, mark it active even if it is being
1432 		 * removed. Then it can send an exception event.
1433 		 *
1434 		 * Move it to the head, as the active ones are only
1435 		 * in the beginning. If removing, the pfp will be on
1436 		 * a temporary list, no need to move it to the front
1437 		 * all the entries will be processed. Some exception
1438 		 * events will be delivered in port_fop_excep();
1439 		 */
1440 		if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
1441 			pfp->pfop_flags |= PORT_FOP_ACTIVE;
1442 			if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
1443 				pvp = (portfop_vp_t *)vp->v_fopdata;
1444 				port_fop_listremove(pvp, pfp);
1445 				port_fop_listinsert_head(pvp, pfp);
1446 			}
1447 		}
1448 		pfp->pfop_callrid = curthread;
1449 		mutex_exit(&pvp->pvp_mutex);
1450 		mutex_exit(&pfcp->pfc_lock);
1451 	}
1452 
1453 	/*
1454 	 * Compare time stamps and deliver events.
1455 	 */
1456 	if (vp->v_type != VFIFO) {
1457 		port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
1458 	}
1459 
1460 	error = 0;
1461 
1462 	/*
1463 	 *  If we have too many watches on the vnode, discard an
1464 	 *  inactive watch.
1465 	 */
1466 	port_fop_trimpfplist(vp);
1467 
1468 errout:
1469 	/*
1470 	 * Release the hold acquired due to the lookup operation.
1471 	 */
1472 	if (vp != NULL)
1473 		VN_RELE(vp);
1474 
1475 	/*
1476 	 * copied file name not used, free it.
1477 	 */
1478 	if (cname != NULL) {
1479 		kmem_free(cname, clen + 1);
1480 	}
1481 	return (error);
1482 }
1483 
1484 
1485 /*
1486  * The port_dissociate_fop() function dissociates the file object
1487  * from the event port and removes any events that are already on the queue.
1488  * Only the owner of the association is allowed to dissociate the file from
1489  * the port. Returns  success (0) if it was found and removed. Otherwise
1490  * ENOENT.
1491  */
1492 int
1493 port_dissociate_fop(port_t *pp, uintptr_t object)
1494 {
1495 	portfop_cache_t	*pfcp;
1496 	portfop_t	*pfp;
1497 	port_source_t	*pse;
1498 	int		active = 0;
1499 
1500 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
1501 
1502 	/*
1503 	 * if this source is not associated or if there is no
1504 	 * cache, nothing to do just return.
1505 	 */
1506 	if (pse == NULL ||
1507 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1508 		return (EINVAL);
1509 
1510 	/*
1511 	 * Check if this object is on the cache. Only the owner pid
1512 	 * is allowed to dissociate.
1513 	 */
1514 	mutex_enter(&pfcp->pfc_lock);
1515 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1516 	if (pfp == NULL) {
1517 		mutex_exit(&pfcp->pfc_lock);
1518 		return (ENOENT);
1519 	}
1520 
1521 	/*
1522 	 * If this was the last association, it will release
1523 	 * the hold on the vnode. There is a race condition where
1524 	 * the the pfp is being removed due to an exception event
1525 	 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
1526 	 * Since port source cache lock is held, port_fop_excep() cannot
1527 	 * complete. The vnode itself will not disappear as long its pfps
1528 	 * have a reference.
1529 	 */
1530 	(void) port_remove_fop(pfp, pfcp, 1, &active);
1531 	mutex_exit(&pfcp->pfc_lock);
1532 	return (active ? 0 : ENOENT);
1533 }
1534 
1535 
1536 /*
1537  * port_close() calls this function to request the PORT_SOURCE_FILE source
1538  * to remove/free all resources allocated and associated with the port.
1539  */
1540 
1541 /* ARGSUSED */
1542 static void
1543 port_close_fop(void *arg, int port, pid_t pid, int lastclose)
1544 {
1545 	port_t		*pp = arg;
1546 	portfop_cache_t	*pfcp;
1547 	portfop_t	**hashtbl;
1548 	portfop_t	*pfp;
1549 	portfop_t	*pfpnext;
1550 	int		index;
1551 	port_source_t	*pse;
1552 
1553 
1554 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
1555 
1556 	/*
1557 	 * No source or no cache, nothing to do.
1558 	 */
1559 	if (pse == NULL ||
1560 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1561 		return;
1562 	/*
1563 	 * Scan the cache and free all allocated portfop_t and port_kevent_t
1564 	 * structures of this pid.
1565 	 */
1566 	mutex_enter(&pfcp->pfc_lock);
1567 	hashtbl = (portfop_t **)pfcp->pfc_hash;
1568 	for (index = 0; index < PORTFOP_HASHSIZE; index++) {
1569 		for (pfp = hashtbl[index]; pfp != NULL; pfp = pfpnext) {
1570 			pfpnext = pfp->pfop_hashnext;
1571 			if (pid == pfp->pfop_pid) {
1572 				(void) port_remove_fop(pfp, pfcp, 1, NULL);
1573 			}
1574 		}
1575 	}
1576 
1577 	/*
1578 	 * Due to a race between port_close_fop() and port_fop()
1579 	 * trying to remove the pfp's from the port's cache, it is
1580 	 * possible that some pfp's are still in the process of being
1581 	 * freed so we wait.
1582 	 */
1583 	while (lastclose && pfcp->pfc_objcount) {
1584 		(void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
1585 	}
1586 	mutex_exit(&pfcp->pfc_lock);
1587 	/*
1588 	 * last close, free the cache.
1589 	 */
1590 	if (lastclose) {
1591 		ASSERT(pfcp->pfc_objcount == 0);
1592 		pse->portsrc_data = NULL;
1593 		kmem_free(pfcp, sizeof (portfop_cache_t));
1594 	}
1595 }
1596 
1597 /*
1598  * Given the list of associations(watches), it will send exception events,
1599  * if still active, and discard them. The exception events are handled
1600  * separately because, the pfp needs to be removed from the port cache and
1601  * freed as the vnode's identity is changing or being removed. To remove
1602  * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
1603  * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
1604  * the cache's lock cannot be acquired in port_fop_sendevent().
1605  */
1606 static void
1607 port_fop_excep(list_t *tlist, int op)
1608 {
1609 	portfop_t	*pfp;
1610 	portfop_cache_t *pfcp;
1611 	port_t	*pp;
1612 	port_kevent_t	*pkevp;
1613 	int		error = 0;
1614 
1615 	while (pfp = (portfop_t *)list_head(tlist)) {
1616 		int removed = 0;
1617 		/*
1618 		 * remove from the temp list. Since PORT_FOP_REMOVING is
1619 		 * set, no other thread should attempt to perform a
1620 		 * list_remove on this pfp.
1621 		 */
1622 		list_remove(tlist, pfp);
1623 
1624 		pfcp = pfp->pfop_pcache;
1625 		mutex_enter(&pfcp->pfc_lock);
1626 
1627 		/*
1628 		 * Remove the event from the port queue if it was queued up.
1629 		 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
1630 		 * no longer on the vnode's list.
1631 		 */
1632 		if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
1633 			removed = port_remove_done_event(pfp->pfop_pev);
1634 		}
1635 
1636 		/*
1637 		 * If still active or the event was queued up and
1638 		 * had not been collected yet, send an EXCEPTION event.
1639 		 */
1640 		if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
1641 			pp = pfp->pfop_pp;
1642 			/*
1643 			 * Allocate a port_kevent_t non cached to send this
1644 			 * event since we will be de-registering.
1645 			 * The port_kevent_t cannot be pointing back to the
1646 			 * pfp anymore.
1647 			 */
1648 			pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1649 			error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1650 			    PORT_ALLOC_DEFAULT, &pkevp);
1651 			if (!error) {
1652 
1653 				pkevp->portkev_callback = port_fop_callback;
1654 				pkevp->portkev_arg = NULL;
1655 				pkevp->portkev_object =
1656 				    pfp->pfop_pev->portkev_object;
1657 				pkevp->portkev_user =
1658 				    pfp->pfop_pev->portkev_user;
1659 				/*
1660 				 * Copy the pid of the watching process.
1661 				 */
1662 				pkevp->portkev_pid =
1663 				    pfp->pfop_pev->portkev_pid;
1664 				pkevp->portkev_events = op;
1665 				port_send_event(pkevp);
1666 			}
1667 		}
1668 		/*
1669 		 * At this point the pfp has been removed from the vnode's
1670 		 * list its cached port_kevent_t is not on the done queue.
1671 		 * Remove the pfp and free it from the cache.
1672 		 */
1673 		port_pcache_remove_fop(pfcp, pfp);
1674 		mutex_exit(&pfcp->pfc_lock);
1675 	}
1676 }
1677 
1678 /*
1679  * Send the file events to all of the processes watching this
1680  * vnode. In case of hard links, the directory vnode pointer and
1681  * the file name are compared. If the names match, then the specified
1682  * event is sent or else, the FILE_ATTRIB event is sent, This is the
1683  * documented behavior.
1684  */
1685 void
1686 port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
1687 {
1688 	port_kevent_t	*pkevp;
1689 	portfop_t	*pfp, *npfp;
1690 	portfop_vp_t	*pvp;
1691 	list_t		tmplist;
1692 	int		removeall = 0;
1693 
1694 	pvp = (portfop_vp_t *)vp->v_fopdata;
1695 	mutex_enter(&pvp->pvp_mutex);
1696 
1697 	/*
1698 	 * Check if the list is empty.
1699 	 *
1700 	 * All entries have been removed by some other thread.
1701 	 * The vnode may be still active and we got called,
1702 	 * but some other thread is in the process of removing the hooks.
1703 	 */
1704 	if (!list_head(&pvp->pvp_pfoplist)) {
1705 		mutex_exit(&pvp->pvp_mutex);
1706 		return;
1707 	}
1708 
1709 	if ((events & (FILE_EXCEPTION))) {
1710 		/*
1711 		 * If it is an event for which we are going to remove
1712 		 * the watches so just move it a temporary list and
1713 		 * release this vnode.
1714 		 */
1715 		list_create(&tmplist, sizeof (portfop_t),
1716 		    offsetof(portfop_t, pfop_node));
1717 
1718 		/*
1719 		 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
1720 		 * passed for an exception event, all associations need to be
1721 		 * removed.
1722 		 */
1723 		if (dvp == NULL || cname == NULL) {
1724 			removeall = 1;
1725 		}
1726 	}
1727 
1728 	if (!removeall) {
1729 		/*
1730 		 * All the active ones are in the beginning of the list.
1731 		 */
1732 		for (pfp = (portfop_t *)list_head(&pvp->pvp_pfoplist);
1733 		    pfp && pfp->pfop_flags & PORT_FOP_ACTIVE; pfp = npfp) {
1734 			int levents = events;
1735 
1736 			npfp = list_next(&pvp->pvp_pfoplist, pfp);
1737 			/*
1738 			 * Hard links case - If the file is being
1739 			 * removed/renamed, and the name matches
1740 			 * the watched file, then it is an EXCEPTION
1741 			 * event or else it will be just a FILE_ATTRIB.
1742 			 */
1743 			if ((events & (FILE_EXCEPTION))) {
1744 				ASSERT(dvp != NULL && cname != NULL);
1745 				if (pfp->pfop_dvp == NULL ||
1746 				    (pfp->pfop_dvp == dvp &&
1747 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
1748 					/*
1749 					 * It is an exception event, move it
1750 					 * to temp list and process it later.
1751 					 * Note we don't set the pfp->pfop_vp
1752 					 * to NULL even thought it has been
1753 					 * removed from the vnode's list. This
1754 					 * pointer is referenced in
1755 					 * port_remove_fop(). The vnode it
1756 					 * self cannot disappear until this
1757 					 * pfp gets removed and freed.
1758 					 */
1759 					port_fop_listremove(pvp, pfp);
1760 					list_insert_tail(&tmplist, (void *)pfp);
1761 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
1762 					continue;
1763 				} else {
1764 					levents = FILE_ATTRIB;
1765 				}
1766 
1767 			}
1768 
1769 			if (pfp->pfop_events & levents) {
1770 				/*
1771 				 * deactivate and move it to the tail.
1772 				 * If the pfp was active, it cannot be
1773 				 * on the port's done queue.
1774 				 */
1775 				pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1776 				port_fop_listremove(pvp, pfp);
1777 				port_fop_listinsert_tail(pvp, pfp);
1778 
1779 				pkevp = pfp->pfop_pev;
1780 				pkevp->portkev_events |=
1781 				    (levents & pfp->pfop_events);
1782 				port_send_event(pkevp);
1783 				pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
1784 			}
1785 		}
1786 	}
1787 
1788 
1789 	if ((events & (FILE_EXCEPTION))) {
1790 		if (!removeall) {
1791 			/*
1792 			 * Check the inactive associations and remove them if
1793 			 * the file name matches.
1794 			 */
1795 			for (; pfp; pfp = npfp) {
1796 				npfp = list_next(&pvp->pvp_pfoplist, pfp);
1797 				if (dvp == NULL || cname == NULL ||
1798 				    pfp->pfop_dvp == NULL ||
1799 				    (pfp->pfop_dvp == dvp &&
1800 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
1801 					port_fop_listremove(pvp, pfp);
1802 					list_insert_tail(&tmplist, (void *)pfp);
1803 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
1804 				}
1805 			}
1806 		} else {
1807 			/*
1808 			 * Can be optimized to avoid two pass over this list
1809 			 * by having a flag in the vnode's portfop_vp_t
1810 			 * structure to indicate that it is going away,
1811 			 * Or keep the list short by reusing inactive watches.
1812 			 */
1813 			port_fop_listmove(pvp, &tmplist);
1814 			for (pfp = (portfop_t *)list_head(&tmplist);
1815 			    pfp; pfp = list_next(&tmplist, pfp)) {
1816 				pfp->pfop_flags |= PORT_FOP_REMOVING;
1817 			}
1818 		}
1819 
1820 		/*
1821 		 * Uninstall the fem hooks if there are no more associations.
1822 		 * This will release the pvp mutex.
1823 		 *
1824 		 * Even thought all entries may have been removed,
1825 		 * the vnode itself cannot disappear as there will be a
1826 		 * hold on it due to this call to port_fop_sendevent. This is
1827 		 * important to syncronize with a port_dissociate_fop() call
1828 		 * that may be attempting to remove an object from the vnode's.
1829 		 */
1830 		port_fop_femuninstall(vp);
1831 
1832 		/*
1833 		 * Send exception events and discard the watch entries.
1834 		 */
1835 		port_fop_excep(&tmplist, events);
1836 		list_destroy(&tmplist);
1837 
1838 	} else {
1839 		mutex_exit(&pvp->pvp_mutex);
1840 
1841 		/*
1842 		 * trim the list.
1843 		 */
1844 		port_fop_trimpfplist(vp);
1845 	}
1846 }
1847 
1848 /*
1849  * Given the file operation, map it to the event types and send.
1850  */
1851 void
1852 port_fop(vnode_t *vp, int op, int retval)
1853 {
1854 	int event = 0;
1855 	/*
1856 	 * deliver events only if the operation was successful.
1857 	 */
1858 	if (retval)
1859 		return;
1860 
1861 	/*
1862 	 * These events occurring on the watched file.
1863 	 */
1864 	if (op & FOP_MODIFIED_MASK) {
1865 		event  = FILE_MODIFIED;
1866 	}
1867 	if (op & FOP_ACCESS_MASK) {
1868 		event  |= FILE_ACCESS;
1869 	}
1870 	if (op & FOP_ATTRIB_MASK) {
1871 		event  |= FILE_ATTRIB;
1872 	}
1873 
1874 	if (event) {
1875 		port_fop_sendevent(vp, 	event, NULL, NULL);
1876 	}
1877 }
1878 
1879 /*
1880  * ----- the unmount filesystem op(fsem) hook.
1881  */
1882 int
1883 port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
1884 {
1885 	vfs_t	*vfsp = (vfs_t *)vf->fa_fnode->fn_available;
1886 	kmutex_t	*mtx;
1887 	portfop_vfs_t	*pvfsp, **ppvfsp;
1888 	portfop_vp_t	*pvp;
1889 	int error;
1890 
1891 	mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
1892 	ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
1893 	pvfsp = NULL;
1894 	mutex_enter(mtx);
1895 	/*
1896 	 * since this fsem hook is triggered, tit has to be on
1897 	 * the hash list.
1898 	 */
1899 	for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
1900 	;
1901 
1902 	/*
1903 	 * Indicate that the unmount is in process. Don't remove it yet.
1904 	 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
1905 	 * flag on the vfs_t structure. But we call the filesystem unmount
1906 	 * routine after removing all the file watches for this filesystem,
1907 	 * otherwise the unmount will fail due to active vnodes.
1908 	 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
1909 	 * attempting to add a file watch.
1910 	 */
1911 	pvfsp->pvfs_unmount = 1;
1912 	mutex_exit(mtx);
1913 
1914 	/*
1915 	 * uninstall the fsem hooks.
1916 	 */
1917 	(void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
1918 
1919 	while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
1920 		list_remove(&pvfsp->pvfs_pvplist, pvp);
1921 		/*
1922 		 * This should send an UNMOUNTED event to all the
1923 		 * watched vnode of this filesystem and uninstall
1924 		 * the fem hooks. We release the hold on the vnode here
1925 		 * because port_fop_femuninstall() will not do it if
1926 		 * unmount is in process.
1927 		 */
1928 		port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
1929 		VN_RELE(pvp->pvp_vp);
1930 	}
1931 
1932 	error = vfsnext_unmount(vf, flag, cr);
1933 
1934 	/*
1935 	 * we free the pvfsp after the unmount has been completed.
1936 	 */
1937 	mutex_enter(mtx);
1938 	for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
1939 	    ppvfsp = &(*ppvfsp)->pvfs_next)
1940 	;
1941 
1942 	/*
1943 	 * remove and free it.
1944 	 */
1945 	ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
1946 	if (*ppvfsp) {
1947 		pvfsp = *ppvfsp;
1948 		*ppvfsp = pvfsp->pvfs_next;
1949 	}
1950 	mutex_exit(mtx);
1951 	kmem_free(pvfsp, sizeof (portfop_vfs_t));
1952 	return (error);
1953 }
1954 
1955 /*
1956  * ------------------------------file op hooks--------------------------
1957  * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
1958  */
1959 static int
1960 port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
1961 {
1962 	int		retval;
1963 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
1964 
1965 	retval = vnext_open(vf, mode, cr, ct);
1966 	port_fop(vp, FOP_FILE_OPEN, retval);
1967 	return (retval);
1968 }
1969 
1970 static int
1971 port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
1972     caller_context_t *ct)
1973 {
1974 	int		retval;
1975 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
1976 
1977 	retval =  vnext_write(vf, uiop, ioflag, cr, ct);
1978 	port_fop(vp, FOP_FILE_WRITE, retval);
1979 	return (retval);
1980 }
1981 
1982 static int
1983 port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
1984     size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
1985     caller_context_t *ct)
1986 {
1987 	int		retval;
1988 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
1989 
1990 	retval =  vnext_map(vf, off, as, addrp, len, prot, maxport,
1991 	    flags, cr, ct);
1992 	port_fop(vp, FOP_FILE_MAP, retval);
1993 	return (retval);
1994 }
1995 
1996 static int
1997 port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
1998     caller_context_t *ct)
1999 {
2000 	int		retval;
2001 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2002 
2003 	retval =  vnext_read(vf, uiop, ioflag, cr, ct);
2004 	port_fop(vp, FOP_FILE_READ, retval);
2005 	return (retval);
2006 }
2007 
2008 
2009 /*
2010  * AT_SIZE - is for the open(O_TRUNC) case.
2011  */
2012 int
2013 port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
2014     caller_context_t *ct)
2015 {
2016 	int		retval;
2017 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2018 	int		events = 0;
2019 
2020 	retval = vnext_setattr(vf, vap, flags, cr, ct);
2021 	if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
2022 		events |= FOP_FILE_SETATTR_MTIME;
2023 	}
2024 	if (vap->va_mask & AT_ATIME) {
2025 		events |= FOP_FILE_SETATTR_ATIME;
2026 	}
2027 	events |= FOP_FILE_SETATTR_CTIME;
2028 
2029 	port_fop(vp, events, retval);
2030 	return (retval);
2031 }
2032 
2033 int
2034 port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
2035     int mode, vnode_t **vpp, cred_t *cr, int flag,
2036     caller_context_t *ct, vsecattr_t *vsecp)
2037 {
2038 	int		retval, got = 1;
2039 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2040 	vattr_t		vatt, vatt1;
2041 
2042 	/*
2043 	 * If the file already exists, then there will be no change
2044 	 * to the directory. Therefore, we need to compare the
2045 	 * modification time of the directory to determine if the
2046 	 * file was actually created.
2047 	 */
2048 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2049 	if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
2050 		got = 0;
2051 	}
2052 	retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
2053 	    flag, ct, vsecp);
2054 
2055 	vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2056 	if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
2057 		if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
2058 		    (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
2059 		    vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
2060 			/*
2061 			 * File was created.
2062 			 */
2063 			port_fop(vp, FOP_FILE_CREATE, retval);
2064 		}
2065 	}
2066 	return (retval);
2067 }
2068 
2069 int
2070 port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
2071     int flags)
2072 {
2073 	int		retval;
2074 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2075 
2076 	retval = vnext_remove(vf, nm, cr, ct, flags);
2077 	port_fop(vp, FOP_FILE_REMOVE, retval);
2078 	return (retval);
2079 }
2080 
2081 int
2082 port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2083     caller_context_t *ct, int flags)
2084 {
2085 	int		retval;
2086 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2087 
2088 	retval = vnext_link(vf, svp, tnm, cr, ct, flags);
2089 	port_fop(vp, FOP_FILE_LINK, retval);
2090 	return (retval);
2091 }
2092 
2093 /*
2094  * Rename operation is allowed only when from and to directories are
2095  * on the same filesystem. This is checked in vn_rename().
2096  * The target directory is notified thru a VNEVENT by the filesystem
2097  * if the source dir != target dir.
2098  */
2099 int
2100 port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
2101     caller_context_t *ct, int flags)
2102 {
2103 	int		retval;
2104 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2105 
2106 	retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
2107 	port_fop(vp, FOP_FILE_RENAMESRC, retval);
2108 	return (retval);
2109 }
2110 
2111 int
2112 port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
2113     cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
2114 {
2115 	int		retval;
2116 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2117 
2118 	retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
2119 	port_fop(vp, FOP_FILE_MKDIR, retval);
2120 	return (retval);
2121 }
2122 
2123 int
2124 port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2125     caller_context_t *ct, int flags)
2126 {
2127 	int		retval;
2128 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2129 
2130 	retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
2131 	port_fop(vp, FOP_FILE_RMDIR, retval);
2132 	return (retval);
2133 }
2134 
2135 int
2136 port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2137     caller_context_t *ct, int flags)
2138 {
2139 	int		retval;
2140 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2141 
2142 	retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
2143 	port_fop(vp, FOP_FILE_READDIR, retval);
2144 	return (retval);
2145 }
2146 
2147 int
2148 port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
2149     cred_t *cr, caller_context_t *ct, int flags)
2150 {
2151 	int		retval;
2152 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2153 
2154 	retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
2155 	port_fop(vp, FOP_FILE_SYMLINK, retval);
2156 	return (retval);
2157 }
2158 
2159 /*
2160  * acl, facl call this.
2161  */
2162 int
2163 port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
2164     caller_context_t *ct)
2165 {
2166 	int	retval;
2167 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2168 	retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
2169 	port_fop(vp, FOP_FILE_SETSECATTR, retval);
2170 	return (retval);
2171 }
2172 
2173 /*
2174  * these are events on the watched file/directory
2175  */
2176 int
2177 port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
2178     caller_context_t *ct)
2179 {
2180 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2181 
2182 	switch (vnevent) {
2183 	case	VE_RENAME_SRC:
2184 			port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
2185 		break;
2186 	case	VE_RENAME_DEST:
2187 			port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
2188 		break;
2189 	case	VE_REMOVE:
2190 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2191 		break;
2192 	case	VE_RMDIR:
2193 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2194 		break;
2195 	case	VE_CREATE:
2196 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2197 			    NULL, NULL);
2198 		break;
2199 	case	VE_LINK:
2200 			port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
2201 		break;
2202 
2203 	case	VE_RENAME_DEST_DIR:
2204 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2205 			    NULL, NULL);
2206 		break;
2207 
2208 	case	VE_MOUNTEDOVER:
2209 			port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
2210 		break;
2211 	default:
2212 		break;
2213 	}
2214 	return (vnext_vnevent(vf, vnevent, dvp, name, ct));
2215 }
2216