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