131ceb98bSpraks /*
231ceb98bSpraks  * CDDL HEADER START
331ceb98bSpraks  *
431ceb98bSpraks  * The contents of this file are subject to the terms of the
531ceb98bSpraks  * Common Development and Distribution License (the "License").
631ceb98bSpraks  * You may not use this file except in compliance with the License.
731ceb98bSpraks  *
831ceb98bSpraks  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
931ceb98bSpraks  * or http://www.opensolaris.org/os/licensing.
1031ceb98bSpraks  * See the License for the specific language governing permissions
1131ceb98bSpraks  * and limitations under the License.
1231ceb98bSpraks  *
1331ceb98bSpraks  * When distributing Covered Code, include this CDDL HEADER in each
1431ceb98bSpraks  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1531ceb98bSpraks  * If applicable, add the following below this CDDL HEADER, with the
1631ceb98bSpraks  * fields enclosed by brackets "[]" replaced with your own identifying
1731ceb98bSpraks  * information: Portions Copyright [yyyy] [name of copyright owner]
1831ceb98bSpraks  *
1931ceb98bSpraks  * CDDL HEADER END
2031ceb98bSpraks  */
2131ceb98bSpraks /*
22efaadbbfSPrakash Sangappa  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2331ceb98bSpraks  * Use is subject to license terms.
2431ceb98bSpraks  */
2531ceb98bSpraks 
2672102e74SBryan Cantrill /*
2715c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
28*abb88ab1SRobert Mustacchi  * Copyright 2022 Oxide Computer Company
2972102e74SBryan Cantrill  */
3031ceb98bSpraks 
3131ceb98bSpraks /*
3231ceb98bSpraks  * File Events Notification
3331ceb98bSpraks  * ------------------------
3431ceb98bSpraks  *
3531ceb98bSpraks  * The File Events Notification facility provides file and directory change
3631ceb98bSpraks  * notification. It is implemented as an event source(PORT_SOURCE_FILE)
3731ceb98bSpraks  * under the Event Ports framework. Therefore the API is an extension to
3831ceb98bSpraks  * the Event Ports API.
3931ceb98bSpraks  *
4031ceb98bSpraks  * It uses the FEM (File Events Monitoring) framework to intercept
4131ceb98bSpraks  * operations on the files & directories and generate appropriate events.
4231ceb98bSpraks  *
4331ceb98bSpraks  * It provides event notification in accordance with what an application
4431ceb98bSpraks  * can find out by stat`ing the file and comparing time stamps. The various
4531ceb98bSpraks  * system calls that update the file's access, modification, and change
4631ceb98bSpraks  * time stamps are documented in the man page section 2.
4731ceb98bSpraks  *
4831ceb98bSpraks  * It is non intrusive. That is, having an active file event watch on a file
4931ceb98bSpraks  * or directory will not prevent it from being removed or renamed or block an
5031ceb98bSpraks  * unmount operation of the file system where the watched file or directory
5131ceb98bSpraks  * resides.
5231ceb98bSpraks  *
5331ceb98bSpraks  *
5431ceb98bSpraks  * Interface:
5531ceb98bSpraks  * ----------
5631ceb98bSpraks  *
5731ceb98bSpraks  *   The object for this event source is of type 'struct file_obj *'
5831ceb98bSpraks  *
5931ceb98bSpraks  *   The file that needs to be monitored is specified in 'fo_name'.
6031ceb98bSpraks  *   The time stamps collected by a stat(2) call are passed in fo_atime,
6131ceb98bSpraks  *   fo_mtime, fo_ctime. At the time a file events watch is registered, the
6231ceb98bSpraks  *   time stamps passed in are compared with the current time stamps of the
63da6c28aaSamw  *   file. If it has changed, relevant events are sent immediately. If the time
6431ceb98bSpraks  *   stamps are all '0', they will not be compared.
6531ceb98bSpraks  *
6631ceb98bSpraks  *
6731ceb98bSpraks  * The events are delivered to an event port. A port is created using
6831ceb98bSpraks  * port_create().
6931ceb98bSpraks  *
7031ceb98bSpraks  * To register a file events watch on a file or directory.
7131ceb98bSpraks  *
7231ceb98bSpraks  *   port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
7331ceb98bSpraks  *
7431ceb98bSpraks  *   'user' is the user pointer to be returned with the event.
7531ceb98bSpraks  *
7631ceb98bSpraks  * To de-register a file events watch,
7731ceb98bSpraks  *
7831ceb98bSpraks  *   port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
7931ceb98bSpraks  *
8031ceb98bSpraks  * The events are collected using the port_get()/port_getn() interface. The
8131ceb98bSpraks  * event source will be PORT_SOURCE_FILE.
8231ceb98bSpraks  *
8331ceb98bSpraks  * After an event is delivered, the file events watch gets de-activated. To
8431ceb98bSpraks  * receive the next event, the process will have to re-register the watch and
8531ceb98bSpraks  * activate it by calling port_associate() again. This behavior is intentional
86df2381bfSpraks  * and supports proper multi threaded programming when using file events
8731ceb98bSpraks  * notification API.
8831ceb98bSpraks  *
8931ceb98bSpraks  *
9031ceb98bSpraks  * Implementation overview:
9131ceb98bSpraks  * ------------------------
9231ceb98bSpraks  *
9331ceb98bSpraks  * Each file events watch is represented by 'portfop_t' in the kernel. A
94df2381bfSpraks  * cache(in portfop_cache_t) of these portfop_t's are maintained per event
9531ceb98bSpraks  * port by this source. The object here is the pointer to the file_obj
9631ceb98bSpraks  * structure. The portfop_t's are hashed in using the object pointer. Therefore
97df2381bfSpraks  * it is possible to have multiple file events watches on a file by the same
9831ceb98bSpraks  * process by using different object structure(file_obj_t) and hence can
9931ceb98bSpraks  * receive multiple event notification for a file. These watches can be for
10031ceb98bSpraks  * different event types.
10131ceb98bSpraks  *
10231ceb98bSpraks  * The cached entries of these file objects are retained, even after delivering
103df2381bfSpraks  * an event, marking them inactive for performance reasons. The assumption
10431ceb98bSpraks  * is that the process would come back and re-register the file to receive
10531ceb98bSpraks  * further events. When there are more then 'port_fop_maxpfps' watches per file
106df2381bfSpraks  * it will attempt to free the oldest inactive watches.
10731ceb98bSpraks  *
10831ceb98bSpraks  * In case the event that is being delivered is an exception event, the cached
10931ceb98bSpraks  * entries get removed. An exception event on a file or directory means its
11031ceb98bSpraks  * identity got changed(rename to/from, delete, mounted over, file system
11131ceb98bSpraks  * unmount).
11231ceb98bSpraks  *
11331ceb98bSpraks  * If the event port gets closed, all the associated file event watches will be
11431ceb98bSpraks  * removed and discarded.
11531ceb98bSpraks  *
11631ceb98bSpraks  *
11731ceb98bSpraks  * Data structures:
11831ceb98bSpraks  * ----------------
11931ceb98bSpraks  *
12031ceb98bSpraks  * The list of file event watches per file are managed by the data structure
12131ceb98bSpraks  * portfop_vp_t. The first time a file events watch is registered for a file,
122df2381bfSpraks  * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
12331ceb98bSpraks  * removed and freed only when the vnode becomes inactive. The FEM hooks are
12431ceb98bSpraks  * also installed when the first watch is registered on a file. The FEM hooks
12531ceb98bSpraks  * get un-installed when all the watches are removed.
12631ceb98bSpraks  *
12731ceb98bSpraks  * Each file events watch is represented by the structure portfop_t. They
12831ceb98bSpraks  * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
12931ceb98bSpraks  * delivering an event, the portfop_t is marked inactive but retained. It is
13031ceb98bSpraks  * moved to the end of the list. All the active portfop_t's are maintained at
13131ceb98bSpraks  * the beginning. In case of exception events, the portfop_t will be removed
13231ceb98bSpraks  * and discarded.
13331ceb98bSpraks  *
13431ceb98bSpraks  * To intercept unmount operations, FSEM hooks are added to the file system
13531ceb98bSpraks  * under which files are being watched. A hash table('portfop_vfs_hash_t') of
13631ceb98bSpraks  * active file systems is maintained. Each file system that has active watches
13731ceb98bSpraks  * is represented by 'portfop_vfs_t' and is added to the hash table.
13831ceb98bSpraks  * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
13931ceb98bSpraks  * being watched on the portfop_vfs_t structure.
14031ceb98bSpraks  *
14131ceb98bSpraks  *
14231ceb98bSpraks  * File system support:
14331ceb98bSpraks  * -------------------
14431ceb98bSpraks  *
145df2381bfSpraks  * The file system implementation has to provide vnode event notifications
14631ceb98bSpraks  * (vnevents) in order to support watching any files on that file system.
14731ceb98bSpraks  * The vnode events(vnevents) are notifications provided by the file system
14831ceb98bSpraks  * for name based file operations like rename, remove etc, which do not go
14931ceb98bSpraks  * thru the VOP_** interfaces. If the file system does not implement vnode
15031ceb98bSpraks  * notifications, watching for file events on such file systems is not
15131ceb98bSpraks  * supported. The vnode event notifications support is determined by the call
15231ceb98bSpraks  * vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
15331ceb98bSpraks  * has to implement.
15431ceb98bSpraks  *
15531ceb98bSpraks  *
15631ceb98bSpraks  * Locking order:
15731ceb98bSpraks  * --------------
15831ceb98bSpraks  *
15931ceb98bSpraks  * A file(vnode) can have file event watches registered by different processes.
16031ceb98bSpraks  * There is one portfop_t per watch registered. These are on the vnode's list
16131ceb98bSpraks  * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
16231ceb98bSpraks  * also on the per port cache. The cache is protected by the pfc_lock of
16331ceb98bSpraks  * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
16431ceb98bSpraks  *
16531ceb98bSpraks  */
16631ceb98bSpraks 
16731ceb98bSpraks #include <sys/types.h>
16831ceb98bSpraks #include <sys/systm.h>
16931ceb98bSpraks #include <sys/stat.h>
17031ceb98bSpraks #include <sys/errno.h>
17131ceb98bSpraks #include <sys/kmem.h>
17231ceb98bSpraks #include <sys/sysmacros.h>
17331ceb98bSpraks #include <sys/debug.h>
17431ceb98bSpraks #include <sys/vnode.h>
17531ceb98bSpraks #include <sys/poll_impl.h>
17631ceb98bSpraks #include <sys/port_impl.h>
17731ceb98bSpraks #include <sys/fem.h>
17831ceb98bSpraks #include <sys/vfs_opreg.h>
17931ceb98bSpraks #include <sys/atomic.h>
18084c5ce69Spraks #include <sys/mount.h>
18184c5ce69Spraks #include <sys/mntent.h>
18231ceb98bSpraks 
18331ceb98bSpraks /*
184df2381bfSpraks  * For special case support of mnttab (/etc/mnttab).
18531ceb98bSpraks  */
186df2381bfSpraks extern struct vnode *vfs_mntdummyvp;
18731ceb98bSpraks extern int mntfstype;
18831ceb98bSpraks 
18931ceb98bSpraks #define	PORTFOP_PVFSH(vfsp)	(&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
19031ceb98bSpraks portfop_vfs_hash_t	 portvfs_hash[PORTFOP_PVFSHASH_SZ];
19131ceb98bSpraks 
1926b5ad791Spraks #define	PORTFOP_NVP	20
19331ceb98bSpraks /*
19431ceb98bSpraks  * Inactive file event watches(portfop_t) are retained on the vnode's list
19531ceb98bSpraks  * for performance reason. If the applications re-registers the file, the
19631ceb98bSpraks  * inactive entry is made active and moved up the list.
19731ceb98bSpraks  *
19831ceb98bSpraks  * If there are greater then the following number of watches on a vnode,
19931ceb98bSpraks  * it will attempt to discard an oldest inactive watch(pfp) at the time
200073af7d9Spraks  * a new watch is being registered and when events get delivered. We
20131ceb98bSpraks  * do this to avoid accumulating inactive watches on a file.
20231ceb98bSpraks  */
20331ceb98bSpraks int	port_fop_maxpfps = 20;
20431ceb98bSpraks 
20531ceb98bSpraks /* local functions */
20631ceb98bSpraks static int	port_fop_callback(void *, int *, pid_t, int, void *);
20731ceb98bSpraks 
20831ceb98bSpraks static void	port_pcache_insert(portfop_cache_t *, portfop_t *);
20931ceb98bSpraks static void	port_pcache_delete(portfop_cache_t *, portfop_t *);
21031ceb98bSpraks static void	port_close_fop(void *arg, int port, pid_t pid, int lastclose);
21131ceb98bSpraks 
21231ceb98bSpraks /*
21331ceb98bSpraks  * port fop functions that will be the fem hooks.
21431ceb98bSpraks  */
215da6c28aaSamw static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
216da6c28aaSamw     caller_context_t *);
21731ceb98bSpraks static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
218da6c28aaSamw     struct caller_context *ct);
21931ceb98bSpraks static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
220da6c28aaSamw     caller_context_t *ct);
22131ceb98bSpraks static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
222da6c28aaSamw     caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
223da6c28aaSamw     uint_t flags, cred_t *cr, caller_context_t *ct);
22431ceb98bSpraks static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
225da6c28aaSamw     caller_context_t *ct);
22631ceb98bSpraks static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
227da6c28aaSamw     vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
228da6c28aaSamw     caller_context_t *ct, vsecattr_t *vsecp);
229da6c28aaSamw static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
230da6c28aaSamw     caller_context_t *ct, int flags);
231da6c28aaSamw static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
232da6c28aaSamw     caller_context_t *ct, int flags);
23331ceb98bSpraks static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
234da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags);
23531ceb98bSpraks static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
236da6c28aaSamw     vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
237da6c28aaSamw     vsecattr_t *vsecp);
238da6c28aaSamw static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
239da6c28aaSamw     caller_context_t *ct, int flags);
240da6c28aaSamw static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
241da6c28aaSamw     caller_context_t *ct, int flags);
24231ceb98bSpraks static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
243da6c28aaSamw     char *target, cred_t *cr, caller_context_t *ct, int flags);
24431ceb98bSpraks static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
245da6c28aaSamw     cred_t *cr, caller_context_t *ct);
246da6c28aaSamw 
24731ceb98bSpraks static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
248da6c28aaSamw     char *cname, caller_context_t *ct);
24931ceb98bSpraks 
25031ceb98bSpraks static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
25131ceb98bSpraks 
25231ceb98bSpraks 
25331ceb98bSpraks /*
25431ceb98bSpraks  * Fem hooks.
25531ceb98bSpraks  */
25631ceb98bSpraks const fs_operation_def_t	port_vnodesrc_template[] = {
25731ceb98bSpraks 	VOPNAME_OPEN,		{ .femop_open = port_fop_open },
25831ceb98bSpraks 	VOPNAME_READ,		{ .femop_read = port_fop_read },
25931ceb98bSpraks 	VOPNAME_WRITE,		{ .femop_write = port_fop_write },
26031ceb98bSpraks 	VOPNAME_MAP,		{ .femop_map = port_fop_map },
261*abb88ab1SRobert Mustacchi 	VOPNAME_SETATTR,	{ .femop_setattr = port_fop_setattr },
26231ceb98bSpraks 	VOPNAME_CREATE,		{ .femop_create = port_fop_create },
26331ceb98bSpraks 	VOPNAME_REMOVE,		{ .femop_remove = port_fop_remove },
26431ceb98bSpraks 	VOPNAME_LINK,		{ .femop_link = port_fop_link },
26531ceb98bSpraks 	VOPNAME_RENAME,		{ .femop_rename = port_fop_rename },
26631ceb98bSpraks 	VOPNAME_MKDIR,		{ .femop_mkdir = port_fop_mkdir },
26731ceb98bSpraks 	VOPNAME_RMDIR,		{ .femop_rmdir = port_fop_rmdir },
26831ceb98bSpraks 	VOPNAME_READDIR,	{ .femop_readdir = port_fop_readdir },
26931ceb98bSpraks 	VOPNAME_SYMLINK,	{ .femop_symlink = port_fop_symlink },
270*abb88ab1SRobert Mustacchi 	VOPNAME_SETSECATTR,	{ .femop_setsecattr = port_fop_setsecattr },
27131ceb98bSpraks 	VOPNAME_VNEVENT,	{ .femop_vnevent = port_fop_vnevent },
27231ceb98bSpraks 	NULL,	NULL
27331ceb98bSpraks };
27431ceb98bSpraks 
27531ceb98bSpraks /*
27631ceb98bSpraks  * Fsem - vfs ops hooks
27731ceb98bSpraks  */
27831ceb98bSpraks const fs_operation_def_t	port_vfssrc_template[] = {
279*abb88ab1SRobert Mustacchi 	VFSNAME_UNMOUNT,	{ .fsemop_unmount = port_fop_unmount },
28031ceb98bSpraks 	NULL,	NULL
28131ceb98bSpraks };
28231ceb98bSpraks 
28331ceb98bSpraks fem_t *fop_femop;
28431ceb98bSpraks fsem_t *fop_fsemop;
28531ceb98bSpraks 
28631ceb98bSpraks static fem_t *
port_fop_femop()28731ceb98bSpraks port_fop_femop()
28831ceb98bSpraks {
28931ceb98bSpraks 	fem_t *femp;
29031ceb98bSpraks 	if (fop_femop != NULL)
29131ceb98bSpraks 		return (fop_femop);
29231ceb98bSpraks 	if (fem_create("portfop_fem",
29331ceb98bSpraks 	    (const struct fs_operation_def *)port_vnodesrc_template,
29431ceb98bSpraks 	    (fem_t **)&femp)) {
29531ceb98bSpraks 		return (NULL);
29631ceb98bSpraks 	}
29775d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&fop_femop, NULL, femp) != NULL) {
29831ceb98bSpraks 		/*
29931ceb98bSpraks 		 * some other thread beat us to it.
30031ceb98bSpraks 		 */
30131ceb98bSpraks 		fem_free(femp);
30231ceb98bSpraks 	}
30331ceb98bSpraks 	return (fop_femop);
30431ceb98bSpraks }
30531ceb98bSpraks 
30631ceb98bSpraks static fsem_t *
port_fop_fsemop()30731ceb98bSpraks port_fop_fsemop()
30831ceb98bSpraks {
30931ceb98bSpraks 	fsem_t *fsemp;
31031ceb98bSpraks 	if (fop_fsemop != NULL)
31131ceb98bSpraks 		return (fop_fsemop);
31231ceb98bSpraks 	if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
31331ceb98bSpraks 		return (NULL);
31431ceb98bSpraks 	}
31575d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&fop_fsemop, NULL, fsemp) != NULL) {
31631ceb98bSpraks 		/*
31731ceb98bSpraks 		 * some other thread beat us to it.
31831ceb98bSpraks 		 */
31931ceb98bSpraks 		fsem_free(fsemp);
32031ceb98bSpraks 	}
32131ceb98bSpraks 	return (fop_fsemop);
32231ceb98bSpraks }
32331ceb98bSpraks 
32431ceb98bSpraks /*
32531ceb98bSpraks  * port_fop_callback()
32631ceb98bSpraks  * - PORT_CALLBACK_DEFAULT
32731ceb98bSpraks  *	The file event will be delivered to the application.
32831ceb98bSpraks  * - PORT_CALLBACK_DISSOCIATE
32931ceb98bSpraks  *	The object will be dissociated from  the port.
33031ceb98bSpraks  * - PORT_CALLBACK_CLOSE
33131ceb98bSpraks  *	The object will be dissociated from the port because the port
33231ceb98bSpraks  *	is being closed.
33331ceb98bSpraks  */
33431ceb98bSpraks /* ARGSUSED */
33531ceb98bSpraks static int
port_fop_callback(void * arg,int * events,pid_t pid,int flag,void * evp)33631ceb98bSpraks port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
33731ceb98bSpraks {
33831ceb98bSpraks 	portfop_t	*pfp = (portfop_t *)arg;
33931ceb98bSpraks 	port_kevent_t	*pkevp = (port_kevent_t *)evp;
34031ceb98bSpraks 	int		error = 0;
34131ceb98bSpraks 
34231ceb98bSpraks 	ASSERT((events != NULL));
34331ceb98bSpraks 	if (flag == PORT_CALLBACK_DEFAULT) {
34431ceb98bSpraks 		if (curproc->p_pid != pid) {
34531ceb98bSpraks 				return (EACCES); /* deny delivery of events */
34631ceb98bSpraks 		}
34731ceb98bSpraks 
34831ceb98bSpraks 		*events = pkevp->portkev_events;
34931ceb98bSpraks 		pkevp->portkev_events = 0;
35031ceb98bSpraks 		if (pfp != NULL) {
35131ceb98bSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
35231ceb98bSpraks 		}
35331ceb98bSpraks 	}
35431ceb98bSpraks 	return (error);
35531ceb98bSpraks }
35631ceb98bSpraks 
35731ceb98bSpraks /*
35831ceb98bSpraks  * Inserts a portfop_t into the port sources cache's.
35931ceb98bSpraks  */
36031ceb98bSpraks static void
port_pcache_insert(portfop_cache_t * pfcp,portfop_t * pfp)36131ceb98bSpraks port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
36231ceb98bSpraks {
36331ceb98bSpraks 	portfop_t	**bucket;
36431ceb98bSpraks 
36531ceb98bSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
36631ceb98bSpraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
36731ceb98bSpraks 	pfp->pfop_hashnext = *bucket;
36831ceb98bSpraks 	*bucket = pfp;
36931ceb98bSpraks 	pfcp->pfc_objcount++;
37031ceb98bSpraks }
37131ceb98bSpraks 
37231ceb98bSpraks /*
37331ceb98bSpraks  * Remove the pfp from the port source cache.
37431ceb98bSpraks  */
37531ceb98bSpraks static void
port_pcache_delete(portfop_cache_t * pfcp,portfop_t * pfp)37631ceb98bSpraks port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
37731ceb98bSpraks {
37831ceb98bSpraks 	portfop_t	*lpdp;
37931ceb98bSpraks 	portfop_t	*cpdp;
38031ceb98bSpraks 	portfop_t	**bucket;
38131ceb98bSpraks 
38231ceb98bSpraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
38331ceb98bSpraks 	cpdp = *bucket;
38431ceb98bSpraks 	if (pfp == cpdp) {
38531ceb98bSpraks 		*bucket = pfp->pfop_hashnext;
38631ceb98bSpraks 	} else {
38731ceb98bSpraks 		while (cpdp != NULL) {
38831ceb98bSpraks 			lpdp = cpdp;
38931ceb98bSpraks 			cpdp = cpdp->pfop_hashnext;
39031ceb98bSpraks 			if (cpdp == pfp) {
39131ceb98bSpraks 				/* portfop struct found */
39231ceb98bSpraks 				lpdp->pfop_hashnext = pfp->pfop_hashnext;
39331ceb98bSpraks 				break;
39431ceb98bSpraks 			}
39531ceb98bSpraks 		}
39631ceb98bSpraks 	}
39731ceb98bSpraks 	pfcp->pfc_objcount--;
39831ceb98bSpraks }
39931ceb98bSpraks 
40031ceb98bSpraks /*
40131ceb98bSpraks  * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
40231ceb98bSpraks  * when these routines are called.
40331ceb98bSpraks  *
40431ceb98bSpraks  * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
40531ceb98bSpraks  * It is used to discard the oldtest inactive pfp if the number of entries
40631ceb98bSpraks  * exceed the limit.
40731ceb98bSpraks  */
40831ceb98bSpraks static void
port_fop_listinsert(portfop_vp_t * pvp,portfop_t * pfp,int where)40931ceb98bSpraks port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
41031ceb98bSpraks {
41131ceb98bSpraks 	if (where == 1) {
41231ceb98bSpraks 		list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
41331ceb98bSpraks 	} else {
41431ceb98bSpraks 		list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
41531ceb98bSpraks 	}
41631ceb98bSpraks 	if (pvp->pvp_lpfop == NULL) {
41731ceb98bSpraks 		pvp->pvp_lpfop = pfp;
41831ceb98bSpraks 	}
41931ceb98bSpraks 	pvp->pvp_cnt++;
42031ceb98bSpraks }
42131ceb98bSpraks 
42231ceb98bSpraks static void
port_fop_listinsert_head(portfop_vp_t * pvp,portfop_t * pfp)42331ceb98bSpraks port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
42431ceb98bSpraks {
42531ceb98bSpraks 	port_fop_listinsert(pvp, pfp, 1);
42631ceb98bSpraks }
42731ceb98bSpraks 
42831ceb98bSpraks static void
port_fop_listinsert_tail(portfop_vp_t * pvp,portfop_t * pfp)42931ceb98bSpraks port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
43031ceb98bSpraks {
43131ceb98bSpraks 	/*
43231ceb98bSpraks 	 * We point lpfop to an inactive one, if it was initially pointing
43331ceb98bSpraks 	 * to an active one. Insert to the tail is done only when a pfp goes
43431ceb98bSpraks 	 * inactive.
43531ceb98bSpraks 	 */
43631ceb98bSpraks 	if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
43731ceb98bSpraks 		pvp->pvp_lpfop = pfp;
43831ceb98bSpraks 	}
43931ceb98bSpraks 	port_fop_listinsert(pvp, pfp, 0);
44031ceb98bSpraks }
44131ceb98bSpraks 
44231ceb98bSpraks static void
port_fop_listremove(portfop_vp_t * pvp,portfop_t * pfp)44331ceb98bSpraks port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
44431ceb98bSpraks {
44531ceb98bSpraks 	if (pvp->pvp_lpfop == pfp) {
44631ceb98bSpraks 		pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
44731ceb98bSpraks 	}
44831ceb98bSpraks 
44931ceb98bSpraks 	list_remove(&pvp->pvp_pfoplist, (void *)pfp);
45031ceb98bSpraks 
45131ceb98bSpraks 	pvp->pvp_cnt--;
45231ceb98bSpraks 	if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
45331ceb98bSpraks 		pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
45431ceb98bSpraks 	}
45531ceb98bSpraks }
45631ceb98bSpraks 
45731ceb98bSpraks static void
port_fop_listmove(portfop_vp_t * pvp,list_t * tlist)45831ceb98bSpraks port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
45931ceb98bSpraks {
46031ceb98bSpraks 	list_move_tail(tlist, &pvp->pvp_pfoplist);
46131ceb98bSpraks 	pvp->pvp_lpfop = NULL;
46231ceb98bSpraks 	pvp->pvp_cnt = 0;
46331ceb98bSpraks }
46431ceb98bSpraks 
46531ceb98bSpraks /*
46631ceb98bSpraks  * Remove a portfop_t from the port cache hash table and discard it.
46731ceb98bSpraks  * It is called only when pfp is not on the vnode's list. Otherwise,
46831ceb98bSpraks  * port_remove_fop() is called.
46931ceb98bSpraks  */
47031ceb98bSpraks void
port_pcache_remove_fop(portfop_cache_t * pfcp,portfop_t * pfp)47131ceb98bSpraks port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
47231ceb98bSpraks {
47331ceb98bSpraks 	port_kevent_t	*pkevp;
47431ceb98bSpraks 
47531ceb98bSpraks 
47631ceb98bSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
47731ceb98bSpraks 
47831ceb98bSpraks 	pkevp = pfp->pfop_pev;
47931ceb98bSpraks 	pfp->pfop_pev = NULL;
48031ceb98bSpraks 
48131ceb98bSpraks 	if (pkevp != NULL) {
48231ceb98bSpraks 		(void) port_remove_done_event(pkevp);
48331ceb98bSpraks 		port_free_event_local(pkevp, 0);
48431ceb98bSpraks 	}
48531ceb98bSpraks 
48631ceb98bSpraks 	port_pcache_delete(pfcp, pfp);
48731ceb98bSpraks 
48831ceb98bSpraks 	if (pfp->pfop_cname != NULL)
48931ceb98bSpraks 		kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
49031ceb98bSpraks 	kmem_free(pfp, sizeof (portfop_t));
49131ceb98bSpraks 	if (pfcp->pfc_objcount == 0)
49231ceb98bSpraks 		cv_signal(&pfcp->pfc_lclosecv);
49331ceb98bSpraks }
49431ceb98bSpraks 
49531ceb98bSpraks /*
49631ceb98bSpraks  * if we have too many watches on the vnode, attempt to discard an
49731ceb98bSpraks  * inactive one.
49831ceb98bSpraks  */
49931ceb98bSpraks static void
port_fop_trimpfplist(vnode_t * vp)50031ceb98bSpraks port_fop_trimpfplist(vnode_t *vp)
50131ceb98bSpraks {
50231ceb98bSpraks 	portfop_vp_t *pvp;
50331ceb98bSpraks 	portfop_t *pfp = NULL;
50431ceb98bSpraks 	portfop_cache_t *pfcp;
5056b5ad791Spraks 	vnode_t	*tdvp;
50631ceb98bSpraks 
50731ceb98bSpraks 	/*
50831ceb98bSpraks 	 * Due to a reference the vnode cannot disappear, v_fopdata should
50931ceb98bSpraks 	 * not change.
51031ceb98bSpraks 	 */
51131ceb98bSpraks 	if ((pvp = vp->v_fopdata) != NULL &&
51231ceb98bSpraks 	    pvp->pvp_cnt > port_fop_maxpfps) {
51331ceb98bSpraks 		mutex_enter(&pvp->pvp_mutex);
51431ceb98bSpraks 		pfp = pvp->pvp_lpfop;
51531ceb98bSpraks 		pfcp = pfp->pfop_pcache;
51631ceb98bSpraks 		/*
51731ceb98bSpraks 		 * only if we can get the cache lock, we need to
51831ceb98bSpraks 		 * do this due to reverse lock order and some thread
51931ceb98bSpraks 		 * that may be trying to reactivate this entry.
52031ceb98bSpraks 		 */
52131ceb98bSpraks 		if (mutex_tryenter(&pfcp->pfc_lock)) {
52231ceb98bSpraks 			if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
52331ceb98bSpraks 			    !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
52431ceb98bSpraks 				port_fop_listremove(pvp, pfp);
52531ceb98bSpraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
52631ceb98bSpraks 			} else {
52731ceb98bSpraks 				mutex_exit(&pfcp->pfc_lock);
52831ceb98bSpraks 				pfp = NULL;
52931ceb98bSpraks 			}
53031ceb98bSpraks 		} else {
53131ceb98bSpraks 			pfp = NULL;
53231ceb98bSpraks 		}
53331ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
53431ceb98bSpraks 
53531ceb98bSpraks 		/*
53631ceb98bSpraks 		 * discard pfp if any.
53731ceb98bSpraks 		 */
53831ceb98bSpraks 		if (pfp != NULL) {
5396b5ad791Spraks 			tdvp = pfp->pfop_dvp;
54031ceb98bSpraks 			port_pcache_remove_fop(pfcp, pfp);
54131ceb98bSpraks 			mutex_exit(&pfcp->pfc_lock);
5426b5ad791Spraks 			if (tdvp != NULL)
5436b5ad791Spraks 				VN_RELE(tdvp);
54431ceb98bSpraks 		}
54531ceb98bSpraks 	}
54631ceb98bSpraks }
54731ceb98bSpraks 
5486b5ad791Spraks /*
5496b5ad791Spraks  * This routine returns 1, if the vnode can be rele'ed by the caller.
5506b5ad791Spraks  * The caller has to VN_RELE the vnode with out holding any
5516b5ad791Spraks  * locks.
5526b5ad791Spraks  */
5536b5ad791Spraks int
port_fop_femuninstall(vnode_t * vp)55431ceb98bSpraks port_fop_femuninstall(vnode_t *vp)
55531ceb98bSpraks {
55631ceb98bSpraks 	portfop_vp_t	*pvp;
55731ceb98bSpraks 	vfs_t		*vfsp;
55831ceb98bSpraks 	portfop_vfs_t *pvfsp;
55931ceb98bSpraks 	portfop_vfs_hash_t	*pvfsh;
56031ceb98bSpraks 	kmutex_t	*mtx;
5616b5ad791Spraks 	int	ret = 0;
56231ceb98bSpraks 
56331ceb98bSpraks 	/*
56431ceb98bSpraks 	 * if list is empty, uninstall fem.
56531ceb98bSpraks 	 */
56631ceb98bSpraks 	pvp = vp->v_fopdata;
56731ceb98bSpraks 	ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
56831ceb98bSpraks 
56931ceb98bSpraks 	/*
57031ceb98bSpraks 	 * make sure the list is empty.
57131ceb98bSpraks 	 */
57231ceb98bSpraks 	if (!list_head(&pvp->pvp_pfoplist)) {
57331ceb98bSpraks 
57431ceb98bSpraks 		/*
57531ceb98bSpraks 		 * we could possibly uninstall the fem hooks when
57631ceb98bSpraks 		 * the vnode becomes inactive and the v_fopdata is
577073af7d9Spraks 		 * free. But the hooks get triggered unnecessarily
57831ceb98bSpraks 		 * even though there are no active watches. So, we
57931ceb98bSpraks 		 * uninstall it here.
58031ceb98bSpraks 		 */
58131ceb98bSpraks 		(void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
58231ceb98bSpraks 		pvp->pvp_femp = NULL;
58331ceb98bSpraks 
58431ceb98bSpraks 
58531ceb98bSpraks 		/*
586073af7d9Spraks 		 * If we successfully uninstalled fem, no process is watching
587073af7d9Spraks 		 * this vnode, Remove it from the vfs's list of watched vnodes.
58831ceb98bSpraks 		 */
58931ceb98bSpraks 		pvfsp = pvp->pvp_pvfsp;
59031ceb98bSpraks 		vfsp = vp->v_vfsp;
59131ceb98bSpraks 		pvfsh = PORTFOP_PVFSH(vfsp);
59231ceb98bSpraks 		mtx = &pvfsh->pvfshash_mutex;
59331ceb98bSpraks 		mutex_enter(mtx);
59431ceb98bSpraks 		/*
59531ceb98bSpraks 		 * If unmount is in progress, that thread will remove and
59631ceb98bSpraks 		 * release the vnode from the vfs's list, just leave.
59731ceb98bSpraks 		 */
59831ceb98bSpraks 		if (!pvfsp->pvfs_unmount) {
59931ceb98bSpraks 			list_remove(&pvfsp->pvfs_pvplist, pvp);
60031ceb98bSpraks 			mutex_exit(mtx);
6016b5ad791Spraks 			ret = 1;
60231ceb98bSpraks 		} else {
60331ceb98bSpraks 			mutex_exit(mtx);
60431ceb98bSpraks 		}
60531ceb98bSpraks 	}
606e1054916SRobert Mustacchi 	mutex_exit(&pvp->pvp_mutex);
6076b5ad791Spraks 	return (ret);
60831ceb98bSpraks }
60931ceb98bSpraks 
61031ceb98bSpraks /*
61131ceb98bSpraks  * Remove pfp from the vnode's watch list and the cache and discard it.
61231ceb98bSpraks  * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
6136b5ad791Spraks  * Returns 1 if pfp removed successfully.
61431ceb98bSpraks  *
61531ceb98bSpraks  * The *active is set to indicate if the pfp was still active(no events had
61631ceb98bSpraks  * been posted, or the posted event had not been collected yet and it was
61731ceb98bSpraks  * able to remove it from the port's queue).
6186b5ad791Spraks  *
6196b5ad791Spraks  * vpp and dvpp will point to the vnode and directory vnode which the caller
6206b5ad791Spraks  * is required to VN_RELE without holding any locks.
62131ceb98bSpraks  */
62231ceb98bSpraks int
port_remove_fop(portfop_t * pfp,portfop_cache_t * pfcp,int cleanup,int * active,vnode_t ** vpp,vnode_t ** dvpp)62331ceb98bSpraks port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
6246b5ad791Spraks     int *active, vnode_t **vpp, vnode_t **dvpp)
62531ceb98bSpraks {
62631ceb98bSpraks 	vnode_t		*vp;
62731ceb98bSpraks 	portfop_vp_t	*pvp;
62831ceb98bSpraks 	int	tactive = 0;
62931ceb98bSpraks 
63031ceb98bSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
63131ceb98bSpraks 	vp = pfp->pfop_vp;
63231ceb98bSpraks 	pvp = vp->v_fopdata;
63331ceb98bSpraks 	mutex_enter(&pvp->pvp_mutex);
63431ceb98bSpraks 
63531ceb98bSpraks 	/*
63631ceb98bSpraks 	 * if not cleanup, remove it only if the pfp is still active and
63731ceb98bSpraks 	 * is not being removed by some other thread.
63831ceb98bSpraks 	 */
63931ceb98bSpraks 	if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
64031ceb98bSpraks 	    pfp->pfop_flags & PORT_FOP_REMOVING)) {
64131ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
64231ceb98bSpraks 		return (0);
64331ceb98bSpraks 	}
64431ceb98bSpraks 
64531ceb98bSpraks 	/*
64631ceb98bSpraks 	 * mark it inactive.
64731ceb98bSpraks 	 */
64831ceb98bSpraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
64931ceb98bSpraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
65031ceb98bSpraks 		tactive = 1;
65131ceb98bSpraks 	}
65231ceb98bSpraks 
65331ceb98bSpraks 	/*
65431ceb98bSpraks 	 * Check if the pfp is still on the vnode's list. This can
65531ceb98bSpraks 	 * happen if port_fop_excep() is in the process of removing it.
65631ceb98bSpraks 	 * In case of cleanup, just mark this pfp as inactive so that no
65731ceb98bSpraks 	 * new events (VNEVENT) will be delivered, and remove it from the
65831ceb98bSpraks 	 * event queue if it was already queued. Since the cache lock is
65931ceb98bSpraks 	 * held, the pfp will not disappear, even though it is being
66031ceb98bSpraks 	 * removed.
66131ceb98bSpraks 	 */
66231ceb98bSpraks 	if (pfp->pfop_flags & PORT_FOP_REMOVING) {
66331ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
66431ceb98bSpraks 		if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
66531ceb98bSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
66631ceb98bSpraks 			tactive = 1;
66731ceb98bSpraks 		}
66831ceb98bSpraks 		if (active) {
66931ceb98bSpraks 			*active = tactive;
67031ceb98bSpraks 		}
67131ceb98bSpraks 		return (1);
67231ceb98bSpraks 	}
67331ceb98bSpraks 
67431ceb98bSpraks 	/*
67531ceb98bSpraks 	 * if we find an event on the queue and removed it, then this
67631ceb98bSpraks 	 * association is considered active.
67731ceb98bSpraks 	 */
67831ceb98bSpraks 	if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
67931ceb98bSpraks 		pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
68031ceb98bSpraks 		tactive = 1;
68131ceb98bSpraks 	}
68231ceb98bSpraks 
68331ceb98bSpraks 	if (active) {
68431ceb98bSpraks 		*active = tactive;
68531ceb98bSpraks 	}
68631ceb98bSpraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
68731ceb98bSpraks 
68831ceb98bSpraks 	/*
68931ceb98bSpraks 	 * remove pfp from the vnode's list
69031ceb98bSpraks 	 */
69131ceb98bSpraks 	port_fop_listremove(pvp, pfp);
69231ceb98bSpraks 
69331ceb98bSpraks 	/*
69431ceb98bSpraks 	 * If no more associations on the vnode, uninstall fem hooks.
69531ceb98bSpraks 	 * The pvp mutex will be released in this routine.
69631ceb98bSpraks 	 */
6976b5ad791Spraks 	if (port_fop_femuninstall(vp))
6986b5ad791Spraks 		*vpp = vp;
6996b5ad791Spraks 	*dvpp = pfp->pfop_dvp;
70031ceb98bSpraks 	port_pcache_remove_fop(pfcp, pfp);
70131ceb98bSpraks 	return (1);
70231ceb98bSpraks }
70331ceb98bSpraks 
70431ceb98bSpraks /*
70531ceb98bSpraks  * This routine returns a pointer to a cached portfop entry, or NULL if it
70631ceb98bSpraks  * does not find it in the hash table. The object pointer is used as index.
70731ceb98bSpraks  * The entries are hashed by the object's address. We need to match the pid
70831ceb98bSpraks  * as the evet port can be shared between processes. The file events
70931ceb98bSpraks  * watches are per process only.
71031ceb98bSpraks  */
71131ceb98bSpraks portfop_t *
port_cache_lookup_fop(portfop_cache_t * pfcp,pid_t pid,uintptr_t obj)71231ceb98bSpraks port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
71331ceb98bSpraks {
71431ceb98bSpraks 	portfop_t	*pfp = NULL;
71531ceb98bSpraks 	portfop_t	**bucket;
71631ceb98bSpraks 
71731ceb98bSpraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
71831ceb98bSpraks 	bucket = PORT_FOP_BUCKET(pfcp, obj);
71931ceb98bSpraks 	pfp = *bucket;
72031ceb98bSpraks 	while (pfp != NULL) {
72131ceb98bSpraks 		if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
72231ceb98bSpraks 			break;
72331ceb98bSpraks 		pfp = pfp->pfop_hashnext;
72431ceb98bSpraks 	}
72531ceb98bSpraks 	return (pfp);
72631ceb98bSpraks }
72731ceb98bSpraks 
72831ceb98bSpraks /*
72931ceb98bSpraks  * Given the file name, get the vnode and also the directory vnode
73031ceb98bSpraks  * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
73131ceb98bSpraks  * the vnode(s).
73231ceb98bSpraks  */
73331ceb98bSpraks int
port_fop_getdvp(void * objptr,vnode_t ** vp,vnode_t ** dvp,char ** cname,int * len,int follow)734*abb88ab1SRobert Mustacchi port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
735*abb88ab1SRobert Mustacchi     int *len, int follow)
73631ceb98bSpraks {
73731ceb98bSpraks 	int error = 0;
73831ceb98bSpraks 	struct pathname pn;
73931ceb98bSpraks 	char *fname;
74031ceb98bSpraks 
74131ceb98bSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
74231ceb98bSpraks 		fname = ((file_obj_t *)objptr)->fo_name;
74331ceb98bSpraks #ifdef  _SYSCALL32_IMPL
74431ceb98bSpraks 	} else {
74531ceb98bSpraks 		fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
74631ceb98bSpraks #endif	/* _SYSCALL32_IMPL */
74731ceb98bSpraks 	}
74831ceb98bSpraks 
74931ceb98bSpraks 	/*
75031ceb98bSpraks 	 * lookuppn may fail with EINVAL, if dvp is  non-null(like when
75131ceb98bSpraks 	 * looking for "."). So call again with dvp = NULL.
75231ceb98bSpraks 	 */
75331ceb98bSpraks 	if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
75431ceb98bSpraks 		return (error);
75531ceb98bSpraks 	}
75631ceb98bSpraks 
75731ceb98bSpraks 	error = lookuppn(&pn, NULL, follow, dvp, vp);
75831ceb98bSpraks 	if (error == EINVAL) {
75931ceb98bSpraks 		pn_free(&pn);
76031ceb98bSpraks 		if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
76131ceb98bSpraks 			return (error);
76231ceb98bSpraks 		}
76331ceb98bSpraks 		error = lookuppn(&pn, NULL, follow, NULL, vp);
76431ceb98bSpraks 		if (dvp != NULL) {
76531ceb98bSpraks 			*dvp = NULL;
76631ceb98bSpraks 		}
76731ceb98bSpraks 	}
76831ceb98bSpraks 
76931ceb98bSpraks 	if (error == 0 && cname != NULL && len != NULL) {
77031ceb98bSpraks 		pn_setlast(&pn);
77131ceb98bSpraks 		*len = pn.pn_pathlen;
77231ceb98bSpraks 		*cname = kmem_alloc(*len + 1, KM_SLEEP);
77331ceb98bSpraks 		(void) strcpy(*cname, pn.pn_path);
77431ceb98bSpraks 	} else {
77531ceb98bSpraks 		if (cname != NULL && len != NULL) {
77631ceb98bSpraks 			*cname = NULL;
77731ceb98bSpraks 			*len = 0;
77831ceb98bSpraks 		}
77931ceb98bSpraks 	}
78031ceb98bSpraks 
78131ceb98bSpraks 	pn_free(&pn);
78231ceb98bSpraks 	return (error);
78331ceb98bSpraks }
78431ceb98bSpraks 
78531ceb98bSpraks port_source_t *
port_getsrc(port_t * pp,int source)78631ceb98bSpraks port_getsrc(port_t *pp, int source)
78731ceb98bSpraks {
78831ceb98bSpraks 	port_source_t *pse;
78931ceb98bSpraks 	int	lock = 0;
79031ceb98bSpraks 	/*
79131ceb98bSpraks 	 * get the port source structure.
79231ceb98bSpraks 	 */
79331ceb98bSpraks 	if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
79431ceb98bSpraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
79531ceb98bSpraks 		lock = 1;
79631ceb98bSpraks 	}
79731ceb98bSpraks 
79831ceb98bSpraks 	pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
79931ceb98bSpraks 	for (; pse != NULL; pse = pse->portsrc_next) {
80031ceb98bSpraks 		if (pse->portsrc_source == source)
80131ceb98bSpraks 			break;
80231ceb98bSpraks 	}
80331ceb98bSpraks 
80431ceb98bSpraks 	if (lock) {
80531ceb98bSpraks 		mutex_exit(&pp->port_queue.portq_source_mutex);
80631ceb98bSpraks 	}
80731ceb98bSpraks 	return (pse);
80831ceb98bSpraks }
80931ceb98bSpraks 
81031ceb98bSpraks 
81131ceb98bSpraks /*
812073af7d9Spraks  * Compare time stamps and generate an event if it has changed.
813073af7d9Spraks  * Note that the port cache pointer will be valid due to a reference
814073af7d9Spraks  * to the port. We need to grab the port cache lock and verify that
815073af7d9Spraks  * the pfp is still the same before proceeding to deliver an event.
81631ceb98bSpraks  */
81731ceb98bSpraks static void
port_check_timestamp(portfop_cache_t * pfcp,vnode_t * vp,vnode_t * dvp,portfop_t * pfp,void * objptr,uintptr_t object)818073af7d9Spraks port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
819*abb88ab1SRobert Mustacchi     portfop_t *pfp, void *objptr, uintptr_t object)
82031ceb98bSpraks {
82131ceb98bSpraks 	vattr_t		vatt;
82231ceb98bSpraks 	portfop_vp_t	*pvp = vp->v_fopdata;
82331ceb98bSpraks 	int		events = 0;
82431ceb98bSpraks 	port_kevent_t	*pkevp;
82531ceb98bSpraks 	file_obj_t	*fobj;
826073af7d9Spraks 	portfop_t	*tpfp;
82731ceb98bSpraks 
82831ceb98bSpraks 	/*
829073af7d9Spraks 	 * If time stamps are specified, get attributes and compare.
83031ceb98bSpraks 	 */
83131ceb98bSpraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
83231ceb98bSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
83331ceb98bSpraks 		fobj = (file_obj_t *)objptr;
83431ceb98bSpraks 		if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
83531ceb98bSpraks 		    fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
83631ceb98bSpraks 		    fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
837da6c28aaSamw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
83831ceb98bSpraks 				return;
83931ceb98bSpraks 			}
84031ceb98bSpraks 		} else {
84131ceb98bSpraks 			/*
84231ceb98bSpraks 			 * timestamp not specified, all 0's,
84331ceb98bSpraks 			 */
84431ceb98bSpraks 			return;
84531ceb98bSpraks 		}
84631ceb98bSpraks #ifdef  _SYSCALL32_IMPL
84731ceb98bSpraks 	} else {
84831ceb98bSpraks 		file_obj32_t	*fobj32;
84931ceb98bSpraks 		fobj32 = (file_obj32_t *)objptr;
85031ceb98bSpraks 		if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
85131ceb98bSpraks 		    fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
85231ceb98bSpraks 		    fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
853da6c28aaSamw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
85431ceb98bSpraks 				return;
85531ceb98bSpraks 			}
85631ceb98bSpraks 		} else {
85731ceb98bSpraks 			/*
85831ceb98bSpraks 			 * timestamp not specified, all 0.
85931ceb98bSpraks 			 */
86031ceb98bSpraks 			return;
86131ceb98bSpraks 		}
86231ceb98bSpraks #endif /* _SYSCALL32_IMPL */
86331ceb98bSpraks 	}
86431ceb98bSpraks 
865073af7d9Spraks 	/*
866073af7d9Spraks 	 * Now grab the cache lock and verify that we are still
867073af7d9Spraks 	 * dealing with the same pfp and curthread is the one
868073af7d9Spraks 	 * which registered it. We need to do this to avoid
869073af7d9Spraks 	 * delivering redundant events.
870073af7d9Spraks 	 */
871073af7d9Spraks 	mutex_enter(&pfcp->pfc_lock);
872073af7d9Spraks 	tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
873073af7d9Spraks 
874073af7d9Spraks 	if (tpfp == NULL || tpfp != pfp ||
875073af7d9Spraks 	    pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
876073af7d9Spraks 	    pfp->pfop_callrid != curthread ||
877073af7d9Spraks 	    !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
878073af7d9Spraks 		/*
879073af7d9Spraks 		 * Some other event was delivered, the file
880073af7d9Spraks 		 * watch was removed or reassociated. Just
881073af7d9Spraks 		 * ignore it and leave
882073af7d9Spraks 		 */
883073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
884073af7d9Spraks 		return;
885073af7d9Spraks 	}
886073af7d9Spraks 
88731ceb98bSpraks 	mutex_enter(&pvp->pvp_mutex);
88831ceb98bSpraks 	/*
889073af7d9Spraks 	 * The pfp cannot disappear as the port cache lock is held.
89031ceb98bSpraks 	 * While the pvp_mutex is held, no events will get delivered.
89131ceb98bSpraks 	 */
89231ceb98bSpraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
89331ceb98bSpraks 	    !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
89431ceb98bSpraks 		if (get_udatamodel() == DATAMODEL_NATIVE) {
89531ceb98bSpraks 			fobj = (file_obj_t *)objptr;
89631ceb98bSpraks 			if (pfp->pfop_events & FILE_ACCESS &&
89731ceb98bSpraks 			    (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
89831ceb98bSpraks 			    (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
89931ceb98bSpraks 			    vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
90031ceb98bSpraks 				events |= FILE_ACCESS;
90131ceb98bSpraks 
90231ceb98bSpraks 			if (pfp->pfop_events & FILE_MODIFIED &&
90331ceb98bSpraks 			    (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
90431ceb98bSpraks 			    (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
90531ceb98bSpraks 			    vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
90631ceb98bSpraks 				events |= FILE_MODIFIED;
90731ceb98bSpraks 
90831ceb98bSpraks 			if (pfp->pfop_events & FILE_ATTRIB &&
90931ceb98bSpraks 			    (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
91031ceb98bSpraks 			    (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
91131ceb98bSpraks 			    vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
91231ceb98bSpraks 				events |= FILE_ATTRIB;
91331ceb98bSpraks #ifdef  _SYSCALL32_IMPL
91431ceb98bSpraks 		} else {
91531ceb98bSpraks 			file_obj32_t	*fobj32;
91631ceb98bSpraks 			fobj32 = (file_obj32_t *)objptr;
91731ceb98bSpraks 			if (pfp->pfop_events & FILE_ACCESS &&
91831ceb98bSpraks 			    (fobj32->fo_atime.tv_sec ||
91931ceb98bSpraks 			    fobj32->fo_atime.tv_nsec) &&
92031ceb98bSpraks 			    (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
92131ceb98bSpraks 			    vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
92231ceb98bSpraks 				events |= FILE_ACCESS;
92331ceb98bSpraks 
92431ceb98bSpraks 			if (pfp->pfop_events & FILE_MODIFIED &&
92531ceb98bSpraks 			    (fobj32->fo_mtime.tv_sec ||
92631ceb98bSpraks 			    fobj32->fo_mtime.tv_nsec) &&
92731ceb98bSpraks 			    (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
92831ceb98bSpraks 			    vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
92931ceb98bSpraks 				events |= FILE_MODIFIED;
93031ceb98bSpraks 
93131ceb98bSpraks 			if (pfp->pfop_events & FILE_ATTRIB &&
93231ceb98bSpraks 			    (fobj32->fo_ctime.tv_sec ||
93331ceb98bSpraks 			    fobj32->fo_ctime.tv_nsec) &&
93431ceb98bSpraks 			    (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
93531ceb98bSpraks 			    vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
93631ceb98bSpraks 				events |= FILE_ATTRIB;
93731ceb98bSpraks #endif /* _SYSCALL32_IMPL */
93831ceb98bSpraks 		}
93931ceb98bSpraks 
94031ceb98bSpraks 		/*
94131ceb98bSpraks 		 * No events to deliver
94231ceb98bSpraks 		 */
94331ceb98bSpraks 		if (events == 0) {
94431ceb98bSpraks 			mutex_exit(&pvp->pvp_mutex);
945073af7d9Spraks 			mutex_exit(&pfcp->pfc_lock);
94631ceb98bSpraks 			return;
94731ceb98bSpraks 		}
94831ceb98bSpraks 
94931ceb98bSpraks 		/*
95031ceb98bSpraks 		 * Deliver the event now.
95131ceb98bSpraks 		 */
95231ceb98bSpraks 		pkevp = pfp->pfop_pev;
95331ceb98bSpraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
95431ceb98bSpraks 		pkevp->portkev_events |= events;
95531ceb98bSpraks 		/*
95631ceb98bSpraks 		 * Move it to the tail as active once are in the
957073af7d9Spraks 		 * beginning of the list.
95831ceb98bSpraks 		 */
95931ceb98bSpraks 		port_fop_listremove(pvp, pfp);
96031ceb98bSpraks 		port_fop_listinsert_tail(pvp, pfp);
96131ceb98bSpraks 		port_send_event(pkevp);
96231ceb98bSpraks 		pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
96331ceb98bSpraks 	}
96431ceb98bSpraks 	mutex_exit(&pvp->pvp_mutex);
965073af7d9Spraks 	mutex_exit(&pfcp->pfc_lock);
96631ceb98bSpraks }
96731ceb98bSpraks 
96831ceb98bSpraks /*
96931ceb98bSpraks  * Add the event source to the port and return the port source cache pointer.
97031ceb98bSpraks  */
97131ceb98bSpraks int
port_fop_associate_source(portfop_cache_t ** pfcpp,port_t * pp,int source)97231ceb98bSpraks port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
97331ceb98bSpraks {
97431ceb98bSpraks 	portfop_cache_t *pfcp;
97531ceb98bSpraks 	port_source_t	*pse;
97631ceb98bSpraks 	int		error;
97731ceb98bSpraks 
97831ceb98bSpraks 	/*
97931ceb98bSpraks 	 * associate PORT_SOURCE_FILE source with the port, if it is
98031ceb98bSpraks 	 * not associated yet. Note the PORT_SOURCE_FILE source is
98131ceb98bSpraks 	 * associated once and will not be dissociated.
98231ceb98bSpraks 	 */
98331ceb98bSpraks 	if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
98431ceb98bSpraks 		if (error = port_associate_ksource(pp->port_fd, source,
98531ceb98bSpraks 		    &pse, port_close_fop, pp, NULL)) {
98631ceb98bSpraks 			*pfcpp = NULL;
98731ceb98bSpraks 			return (error);
98831ceb98bSpraks 		}
98931ceb98bSpraks 	}
99031ceb98bSpraks 
99131ceb98bSpraks 	/*
99231ceb98bSpraks 	 * Get the portfop cache pointer.
99331ceb98bSpraks 	 */
99431ceb98bSpraks 	if ((pfcp = pse->portsrc_data) == NULL) {
99531ceb98bSpraks 		/*
99631ceb98bSpraks 		 * This is the first time that a file is being associated,
99731ceb98bSpraks 		 * create the portfop cache.
99831ceb98bSpraks 		 */
99931ceb98bSpraks 		pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
100031ceb98bSpraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
100131ceb98bSpraks 		if (pse->portsrc_data == NULL) {
100231ceb98bSpraks 			pse->portsrc_data = pfcp;
100331ceb98bSpraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
100431ceb98bSpraks 		} else {
100531ceb98bSpraks 			/*
100631ceb98bSpraks 			 * someone else created the port cache, free
100731ceb98bSpraks 			 * what we just now allocated.
100831ceb98bSpraks 			 */
100931ceb98bSpraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
101031ceb98bSpraks 			kmem_free(pfcp, sizeof (portfop_cache_t));
101131ceb98bSpraks 			pfcp = pse->portsrc_data;
101231ceb98bSpraks 		}
101331ceb98bSpraks 	}
101431ceb98bSpraks 	*pfcpp = pfcp;
101531ceb98bSpraks 	return (0);
101631ceb98bSpraks }
101731ceb98bSpraks 
101831ceb98bSpraks /*
101931ceb98bSpraks  * Add the given pvp on the file system's list of vnodes watched.
102031ceb98bSpraks  */
102131ceb98bSpraks int
port_fop_pvfsadd(portfop_vp_t * pvp)102231ceb98bSpraks port_fop_pvfsadd(portfop_vp_t *pvp)
102331ceb98bSpraks {
102431ceb98bSpraks 	int error = 0;
102531ceb98bSpraks 	vnode_t	*vp = pvp->pvp_vp;
102631ceb98bSpraks 	portfop_vfs_hash_t *pvfsh;
102731ceb98bSpraks 	portfop_vfs_t	 *pvfsp;
102831ceb98bSpraks 	fsem_t		*fsemp;
102931ceb98bSpraks 
103031ceb98bSpraks 	pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
103131ceb98bSpraks 	mutex_enter(&pvfsh->pvfshash_mutex);
103231ceb98bSpraks 	for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
103331ceb98bSpraks 	    pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
103431ceb98bSpraks 		;
103531ceb98bSpraks 
103631ceb98bSpraks 	if (!pvfsp) {
103731ceb98bSpraks 		if ((fsemp = port_fop_fsemop()) != NULL) {
103831ceb98bSpraks 			if ((error = fsem_install(vp->v_vfsp, fsemp,
103931ceb98bSpraks 			    vp->v_vfsp, OPUNIQ, NULL, NULL))) {
104031ceb98bSpraks 				mutex_exit(&pvfsh->pvfshash_mutex);
104131ceb98bSpraks 				return (error);
104231ceb98bSpraks 			}
104331ceb98bSpraks 		} else {
104431ceb98bSpraks 			mutex_exit(&pvfsh->pvfshash_mutex);
104531ceb98bSpraks 			return (EINVAL);
104631ceb98bSpraks 		}
104731ceb98bSpraks 		pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
104831ceb98bSpraks 		pvfsp->pvfs = vp->v_vfsp;
104931ceb98bSpraks 		list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
105031ceb98bSpraks 		    offsetof(portfop_vp_t, pvp_pvfsnode));
105131ceb98bSpraks 		pvfsp->pvfs_fsemp = fsemp;
105231ceb98bSpraks 		pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
105331ceb98bSpraks 		pvfsh->pvfshash_pvfsp = pvfsp;
105431ceb98bSpraks 	}
105531ceb98bSpraks 
105631ceb98bSpraks 	/*
105731ceb98bSpraks 	 * check if an unmount is in progress.
105831ceb98bSpraks 	 */
105931ceb98bSpraks 	if (!pvfsp->pvfs_unmount) {
106031ceb98bSpraks 		/*
106131ceb98bSpraks 		 * insert the pvp on list.
106231ceb98bSpraks 		 */
106331ceb98bSpraks 		pvp->pvp_pvfsp = pvfsp;
106431ceb98bSpraks 		list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
106531ceb98bSpraks 	} else {
106631ceb98bSpraks 		error = EINVAL;
106731ceb98bSpraks 	}
106831ceb98bSpraks 	mutex_exit(&pvfsh->pvfshash_mutex);
106931ceb98bSpraks 	return (error);
107031ceb98bSpraks }
107131ceb98bSpraks 
107231ceb98bSpraks /*
107331ceb98bSpraks  * Installs the portfop_vp_t data structure on the
107431ceb98bSpraks  * vnode. The 'pvp_femp == NULL' indicates it is not
107531ceb98bSpraks  * active. The fem hooks have to be installed.
107631ceb98bSpraks  * The portfop_vp_t is only freed when the vnode gets freed.
107731ceb98bSpraks  */
107831ceb98bSpraks void
port_install_fopdata(vnode_t * vp)107931ceb98bSpraks port_install_fopdata(vnode_t *vp)
108031ceb98bSpraks {
108131ceb98bSpraks 	portfop_vp_t *npvp;
108231ceb98bSpraks 
108331ceb98bSpraks 	npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
108431ceb98bSpraks 	mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
108531ceb98bSpraks 	list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
108631ceb98bSpraks 	    offsetof(portfop_t, pfop_node));
108731ceb98bSpraks 	npvp->pvp_vp = vp;
108831ceb98bSpraks 	/*
108931ceb98bSpraks 	 * If v_fopdata is not null, some other thread beat us to it.
109031ceb98bSpraks 	 */
109175d94465SJosef 'Jeff' Sipek 	if (atomic_cas_ptr(&vp->v_fopdata, NULL, npvp) != NULL) {
109231ceb98bSpraks 		mutex_destroy(&npvp->pvp_mutex);
109331ceb98bSpraks 		list_destroy(&npvp->pvp_pfoplist);
109431ceb98bSpraks 		kmem_free(npvp, sizeof (*npvp));
109531ceb98bSpraks 	}
109631ceb98bSpraks }
109731ceb98bSpraks 
109831ceb98bSpraks 
109931ceb98bSpraks /*
110031ceb98bSpraks  * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
110131ceb98bSpraks  * to the vnode's list. The association is identified by the object pointer
110231ceb98bSpraks  * address and pid.
110331ceb98bSpraks  */
110431ceb98bSpraks int
port_pfp_setup(portfop_t ** pfpp,port_t * pp,vnode_t * vp,portfop_cache_t * pfcp,uintptr_t object,int events,void * user,char * cname,int clen,vnode_t * dvp)110531ceb98bSpraks port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
1106*abb88ab1SRobert Mustacchi     uintptr_t object, int events, void *user, char *cname, int clen,
1107*abb88ab1SRobert Mustacchi     vnode_t *dvp)
110831ceb98bSpraks {
110931ceb98bSpraks 	portfop_t	*pfp = NULL;
111031ceb98bSpraks 	port_kevent_t	*pkevp;
111131ceb98bSpraks 	fem_t		*femp;
111231ceb98bSpraks 	int		error = 0;
111331ceb98bSpraks 	portfop_vp_t	*pvp;
111431ceb98bSpraks 
111531ceb98bSpraks 
111631ceb98bSpraks 	/*
111731ceb98bSpraks 	 * The port cache mutex is held.
111831ceb98bSpraks 	 */
111931ceb98bSpraks 	*pfpp  = NULL;
112031ceb98bSpraks 
112131ceb98bSpraks 
112231ceb98bSpraks 	/*
112331ceb98bSpraks 	 * At this point the fem monitor is installed.
112431ceb98bSpraks 	 * Allocate a port event structure per vnode association.
112531ceb98bSpraks 	 */
112631ceb98bSpraks 	if (pfp == NULL) {
112731ceb98bSpraks 		if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
112831ceb98bSpraks 		    PORT_ALLOC_CACHED, &pkevp)) {
112931ceb98bSpraks 			return (error);
113031ceb98bSpraks 		}
113131ceb98bSpraks 		pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
113231ceb98bSpraks 		pfp->pfop_pev = pkevp;
113331ceb98bSpraks 	}
113431ceb98bSpraks 
113531ceb98bSpraks 	pfp->pfop_vp = vp;
113631ceb98bSpraks 	pfp->pfop_pid = curproc->p_pid;
113731ceb98bSpraks 	pfp->pfop_pcache = pfcp;
113831ceb98bSpraks 	pfp->pfop_pp = pp;
113931ceb98bSpraks 	pfp->pfop_flags |= PORT_FOP_ACTIVE;
114031ceb98bSpraks 	pfp->pfop_cname = cname;
114131ceb98bSpraks 	pfp->pfop_clen = clen;
114231ceb98bSpraks 	pfp->pfop_dvp = dvp;
114331ceb98bSpraks 	pfp->pfop_object = object;
114431ceb98bSpraks 
114531ceb98bSpraks 	pkevp->portkev_callback = port_fop_callback;
114631ceb98bSpraks 	pkevp->portkev_arg = pfp;
114731ceb98bSpraks 	pkevp->portkev_object = object;
114831ceb98bSpraks 	pkevp->portkev_user = user;
114931ceb98bSpraks 	pkevp->portkev_events = 0;
115031ceb98bSpraks 
115131ceb98bSpraks 	port_pcache_insert(pfcp, pfp);
115231ceb98bSpraks 
115331ceb98bSpraks 	/*
115431ceb98bSpraks 	 * Register a new file events monitor for this file(vnode), if not
115531ceb98bSpraks 	 * done already.
115631ceb98bSpraks 	 */
115731ceb98bSpraks 	if ((pvp = vp->v_fopdata) == NULL) {
115831ceb98bSpraks 		port_install_fopdata(vp);
115931ceb98bSpraks 		pvp = vp->v_fopdata;
116031ceb98bSpraks 	}
116131ceb98bSpraks 
116231ceb98bSpraks 	mutex_enter(&pvp->pvp_mutex);
116331ceb98bSpraks 	/*
116431ceb98bSpraks 	 * if the vnode does not have the file events hooks, install it.
116531ceb98bSpraks 	 */
116631ceb98bSpraks 	if (pvp->pvp_femp == NULL) {
116731ceb98bSpraks 		if ((femp = port_fop_femop()) != NULL) {
116831ceb98bSpraks 			if (!(error = fem_install(pfp->pfop_vp, femp,
116931ceb98bSpraks 			    (void *)vp, OPUNIQ, NULL, NULL))) {
117031ceb98bSpraks 				pvp->pvp_femp = femp;
117131ceb98bSpraks 				/*
117231ceb98bSpraks 				 * add fsem_t hooks to the vfsp and add pvp to
117331ceb98bSpraks 				 * the list of vnodes for this vfs.
117431ceb98bSpraks 				 */
117531ceb98bSpraks 				if (!(error = port_fop_pvfsadd(pvp))) {
117631ceb98bSpraks 					/*
117731ceb98bSpraks 					 * Hold a reference to the vnode since
117831ceb98bSpraks 					 * we successfully installed the hooks.
117931ceb98bSpraks 					 */
118031ceb98bSpraks 					VN_HOLD(vp);
118131ceb98bSpraks 				} else {
118231ceb98bSpraks 					(void) fem_uninstall(vp, femp, vp);
118331ceb98bSpraks 					pvp->pvp_femp = NULL;
118431ceb98bSpraks 				}
118531ceb98bSpraks 			}
118631ceb98bSpraks 		} else {
118731ceb98bSpraks 			error = EINVAL;
118831ceb98bSpraks 		}
118931ceb98bSpraks 	}
119031ceb98bSpraks 
119131ceb98bSpraks 	if (error) {
119231ceb98bSpraks 		/*
119331ceb98bSpraks 		 * pkevp will get freed here.
119431ceb98bSpraks 		 */
1195073af7d9Spraks 		pfp->pfop_cname = NULL;
119631ceb98bSpraks 		port_pcache_remove_fop(pfcp, pfp);
119731ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
119831ceb98bSpraks 		return (error);
119931ceb98bSpraks 	}
120031ceb98bSpraks 
120131ceb98bSpraks 	/*
120231ceb98bSpraks 	 * insert the pfp on the vnode's list. After this
120331ceb98bSpraks 	 * events can get delivered.
120431ceb98bSpraks 	 */
120531ceb98bSpraks 	pfp->pfop_events = events;
120631ceb98bSpraks 	port_fop_listinsert_head(pvp, pfp);
120731ceb98bSpraks 
120831ceb98bSpraks 	mutex_exit(&pvp->pvp_mutex);
12096b5ad791Spraks 	/*
12106b5ad791Spraks 	 * Hold the directory vnode since we have a reference now.
12116b5ad791Spraks 	 */
12126b5ad791Spraks 	if (dvp != NULL)
12136b5ad791Spraks 		VN_HOLD(dvp);
121431ceb98bSpraks 	*pfpp = pfp;
121531ceb98bSpraks 	return (0);
121631ceb98bSpraks }
121731ceb98bSpraks 
121831ceb98bSpraks vnode_t *
port_resolve_vp(vnode_t * vp)121931ceb98bSpraks port_resolve_vp(vnode_t *vp)
122031ceb98bSpraks {
122131ceb98bSpraks 	vnode_t *rvp;
122231ceb98bSpraks 	/*
1223df2381bfSpraks 	 * special case /etc/mnttab(mntfs type). The mntfstype != 0
1224df2381bfSpraks 	 * if mntfs got mounted.
122531ceb98bSpraks 	 */
1226df2381bfSpraks 	if (vfs_mntdummyvp && mntfstype != 0 &&
1227df2381bfSpraks 	    vp->v_vfsp->vfs_fstype == mntfstype) {
122831ceb98bSpraks 		VN_RELE(vp);
1229df2381bfSpraks 		vp = vfs_mntdummyvp;
1230df2381bfSpraks 		VN_HOLD(vfs_mntdummyvp);
123131ceb98bSpraks 	}
123231ceb98bSpraks 
123331ceb98bSpraks 	/*
123431ceb98bSpraks 	 * This should take care of lofs mounted fs systems and nfs4
123531ceb98bSpraks 	 * hardlinks.
123631ceb98bSpraks 	 */
1237da6c28aaSamw 	if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
123831ceb98bSpraks 		VN_HOLD(rvp);
123931ceb98bSpraks 		VN_RELE(vp);
124031ceb98bSpraks 		vp = rvp;
124131ceb98bSpraks 	}
124231ceb98bSpraks 	return (vp);
124331ceb98bSpraks }
124431ceb98bSpraks 
124531ceb98bSpraks /*
124631ceb98bSpraks  * Register a file events watch on the given file associated to the port *pp.
124731ceb98bSpraks  *
124831ceb98bSpraks  * The association is identified by the object pointer and the pid.
124931ceb98bSpraks  * The events argument contains the events to be monitored for.
12506b5ad791Spraks  *
12516b5ad791Spraks  * The vnode will have a VN_HOLD once the fem hooks are installed.
12526b5ad791Spraks  *
12536b5ad791Spraks  * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
12546b5ad791Spraks  * that the directory vnode pointer does not change.
125531ceb98bSpraks  */
125631ceb98bSpraks int
port_associate_fop(port_t * pp,int source,uintptr_t object,int events,void * user)125731ceb98bSpraks port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
125831ceb98bSpraks     void *user)
125931ceb98bSpraks {
126031ceb98bSpraks 	portfop_cache_t	*pfcp;
126172102e74SBryan Cantrill 	vnode_t		*vp, *dvp, *oldvp = NULL, *olddvp = NULL, *orig;
126231ceb98bSpraks 	portfop_t	*pfp;
126331ceb98bSpraks 	int		error = 0;
126431ceb98bSpraks 	file_obj_t	fobj;
126531ceb98bSpraks 	void		*objptr;
126631ceb98bSpraks 	char		*cname;
126731ceb98bSpraks 	int		clen;
126831ceb98bSpraks 	int		follow;
126931ceb98bSpraks 
127031ceb98bSpraks 	/*
127131ceb98bSpraks 	 * check that events specified are valid.
127231ceb98bSpraks 	 */
127331ceb98bSpraks 	if ((events & ~FILE_EVENTS_MASK) != 0)
127431ceb98bSpraks 		return (EINVAL);
127531ceb98bSpraks 
127631ceb98bSpraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
127731ceb98bSpraks 		if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
127831ceb98bSpraks 			return (EFAULT);
127931ceb98bSpraks 		objptr = (void *)&fobj;
128031ceb98bSpraks #ifdef  _SYSCALL32_IMPL
128131ceb98bSpraks 	} else {
128231ceb98bSpraks 		file_obj32_t	fobj32;
128331ceb98bSpraks 		if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
128431ceb98bSpraks 			return (EFAULT);
128531ceb98bSpraks 		objptr = (void *)&fobj32;
128631ceb98bSpraks #endif  /* _SYSCALL32_IMPL */
128731ceb98bSpraks 	}
128831ceb98bSpraks 
128931ceb98bSpraks 	vp = dvp = NULL;
129031ceb98bSpraks 
129131ceb98bSpraks 	/*
1292073af7d9Spraks 	 * find out if we need to follow symbolic links.
129331ceb98bSpraks 	 */
129431ceb98bSpraks 	follow = !(events & FILE_NOFOLLOW);
129531ceb98bSpraks 	events = events & ~FILE_NOFOLLOW;
129631ceb98bSpraks 
129731ceb98bSpraks 	/*
129831ceb98bSpraks 	 * lookup and find the vnode and its directory vnode of the given
129931ceb98bSpraks 	 * file.
130031ceb98bSpraks 	 */
130131ceb98bSpraks 	if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
130231ceb98bSpraks 	    follow)) != 0) {
130331ceb98bSpraks 		return (error);
130431ceb98bSpraks 	}
130531ceb98bSpraks 
130631ceb98bSpraks 	if (dvp != NULL) {
130731ceb98bSpraks 		dvp = port_resolve_vp(dvp);
130831ceb98bSpraks 	}
130931ceb98bSpraks 
131031ceb98bSpraks 	/*
131131ceb98bSpraks 	 * Not found
131231ceb98bSpraks 	 */
131331ceb98bSpraks 	if (vp == NULL) {
131431ceb98bSpraks 		error = ENOENT;
131531ceb98bSpraks 		goto errout;
131631ceb98bSpraks 	}
131731ceb98bSpraks 
131872102e74SBryan Cantrill 	vp = port_resolve_vp(orig = vp);
131931ceb98bSpraks 
1320da6c28aaSamw 	if (vp != NULL && vnevent_support(vp, NULL)) {
132131ceb98bSpraks 		error = ENOTSUP;
132231ceb98bSpraks 		goto errout;
132331ceb98bSpraks 	}
132431ceb98bSpraks 
1325efaadbbfSPrakash Sangappa 	/*
132672102e74SBryan Cantrill 	 * If dvp belongs to a different filesystem just ignore it, as hard
132772102e74SBryan Cantrill 	 * links cannot exist across filesystems.  We make an exception for
132872102e74SBryan Cantrill 	 * procfs, however, the magic of which we treat semantically as a hard
132972102e74SBryan Cantrill 	 * link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE
133072102e74SBryan Cantrill 	 * and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events.
1331efaadbbfSPrakash Sangappa 	 */
133272102e74SBryan Cantrill 	if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp &&
133372102e74SBryan Cantrill 	    !(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) {
1334efaadbbfSPrakash Sangappa 		VN_RELE(dvp);
1335efaadbbfSPrakash Sangappa 		dvp = NULL;
1336efaadbbfSPrakash Sangappa 	}
1337efaadbbfSPrakash Sangappa 
133831ceb98bSpraks 	/*
133931ceb98bSpraks 	 * Associate this source to the port and get the per port
134031ceb98bSpraks 	 * fop cache pointer. If the source is already associated, it
134131ceb98bSpraks 	 * will just return the cache pointer.
134231ceb98bSpraks 	 */
134331ceb98bSpraks 	if (error = port_fop_associate_source(&pfcp, pp, source)) {
134431ceb98bSpraks 		goto errout;
134531ceb98bSpraks 	}
134631ceb98bSpraks 
134731ceb98bSpraks 	/*
134831ceb98bSpraks 	 * Check if there is an existing association of this file.
134931ceb98bSpraks 	 */
135031ceb98bSpraks 	mutex_enter(&pfcp->pfc_lock);
135131ceb98bSpraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
135231ceb98bSpraks 
135331ceb98bSpraks 	/*
13546b5ad791Spraks 	 * If it is not the same vnode, just discard it. VN_RELE needs to be
13556b5ad791Spraks 	 * called with no locks held, therefore save vnode pointers and
13566b5ad791Spraks 	 * vn_rele them later.
135731ceb98bSpraks 	 */
135831ceb98bSpraks 	if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
13596b5ad791Spraks 		(void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
136031ceb98bSpraks 		pfp = NULL;
136131ceb98bSpraks 	}
136231ceb98bSpraks 
136331ceb98bSpraks 	if (pfp == NULL) {
13646b5ad791Spraks 		vnode_t *tvp, *tdvp;
1365073af7d9Spraks 		portfop_t	*tpfp;
1366073af7d9Spraks 		int error;
1367073af7d9Spraks 
136831ceb98bSpraks 		/*
136931ceb98bSpraks 		 * Add a new association, save the file name and the
137031ceb98bSpraks 		 * directory vnode pointer.
137131ceb98bSpraks 		 */
137231ceb98bSpraks 		if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
137331ceb98bSpraks 		    events, user, cname, clen, dvp)) {
137431ceb98bSpraks 			mutex_exit(&pfcp->pfc_lock);
137531ceb98bSpraks 			goto errout;
137631ceb98bSpraks 		}
137731ceb98bSpraks 
1378073af7d9Spraks 		pfp->pfop_callrid = curthread;
137931ceb98bSpraks 		/*
138031ceb98bSpraks 		 * File name used, so make sure we don't free it.
138131ceb98bSpraks 		 */
138231ceb98bSpraks 		cname = NULL;
138331ceb98bSpraks 
138431ceb98bSpraks 		/*
138531ceb98bSpraks 		 * We need to check if the file was removed after the
138631ceb98bSpraks 		 * the lookup and before the fem hooks where added. If
138731ceb98bSpraks 		 * so, return error. The vnode will still exist as we have
138831ceb98bSpraks 		 * a hold on it.
1389073af7d9Spraks 		 *
1390073af7d9Spraks 		 * Drop the cache lock before calling port_fop_getdvp().
1391073af7d9Spraks 		 * port_fop_getdvp() may block either in the vfs layer
1392073af7d9Spraks 		 * or some filesystem.  Therefore there is potential
1393073af7d9Spraks 		 * for deadlock if cache lock is held and if some other
1394073af7d9Spraks 		 * thread is attempting to deliver file events which would
1395073af7d9Spraks 		 * require getting the cache lock, while it may be holding
1396073af7d9Spraks 		 * the filesystem or vfs layer locks.
139731ceb98bSpraks 		 */
1398073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
1399073af7d9Spraks 		tvp = NULL;
1400073af7d9Spraks 		if ((error = port_fop_getdvp(objptr, &tvp, NULL,
1401073af7d9Spraks 		    NULL, NULL, follow)) == 0) {
1402073af7d9Spraks 			if (tvp != NULL) {
1403073af7d9Spraks 				tvp = port_resolve_vp(tvp);
1404073af7d9Spraks 				/*
1405073af7d9Spraks 				 * This vnode pointer is just used
1406073af7d9Spraks 				 * for comparison, so rele it
1407073af7d9Spraks 				 */
1408073af7d9Spraks 				VN_RELE(tvp);
140931ceb98bSpraks 			}
1410073af7d9Spraks 		}
141131ceb98bSpraks 
1412073af7d9Spraks 		if (error || tvp == NULL || tvp != vp) {
1413073af7d9Spraks 			/*
1414073af7d9Spraks 			 * Since we dropped the cache lock, make sure
1415073af7d9Spraks 			 * we are still dealing with the same pfp and this
1416073af7d9Spraks 			 * is the thread which registered it.
1417073af7d9Spraks 			 */
1418073af7d9Spraks 			mutex_enter(&pfcp->pfc_lock);
1419073af7d9Spraks 			tpfp = port_cache_lookup_fop(pfcp,
1420073af7d9Spraks 			    curproc->p_pid, object);
1421073af7d9Spraks 
1422073af7d9Spraks 			error = 0;
1423073af7d9Spraks 			if (tpfp == NULL || tpfp != pfp ||
1424073af7d9Spraks 			    pfp->pfop_vp != vp ||
1425073af7d9Spraks 			    pfp->pfop_dvp != dvp ||
1426073af7d9Spraks 			    pfp->pfop_callrid != curthread) {
142731ceb98bSpraks 				/*
1428073af7d9Spraks 				 * Some other event was delivered, the file
1429073af7d9Spraks 				 * watch was removed or reassociated, just
1430073af7d9Spraks 				 * ignore it and leave
143131ceb98bSpraks 				 */
1432073af7d9Spraks 				mutex_exit(&pfcp->pfc_lock);
1433073af7d9Spraks 				goto errout;
1434073af7d9Spraks 			}
1435073af7d9Spraks 
1436073af7d9Spraks 			/*
1437073af7d9Spraks 			 * remove the pfp and fem hooks, if pfp still
1438073af7d9Spraks 			 * active and it is not being removed from
1439073af7d9Spraks 			 * the vnode list. This is checked in
1440073af7d9Spraks 			 * port_remove_fop with the vnode lock held.
14416b5ad791Spraks 			 * The vnode returned is VN_RELE'ed after dropping
14426b5ad791Spraks 			 * the locks.
1443073af7d9Spraks 			 */
14446b5ad791Spraks 			tdvp = tvp = NULL;
14456b5ad791Spraks 			if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
1446073af7d9Spraks 				/*
1447073af7d9Spraks 				 * The pfp was removed, means no
1448073af7d9Spraks 				 * events where queued. Report the
1449073af7d9Spraks 				 * error now.
1450073af7d9Spraks 				 */
1451073af7d9Spraks 				error = EINVAL;
145231ceb98bSpraks 			}
1453073af7d9Spraks 			mutex_exit(&pfcp->pfc_lock);
14546b5ad791Spraks 			if (tvp != NULL)
14556b5ad791Spraks 				VN_RELE(tvp);
14566b5ad791Spraks 			if (tdvp != NULL)
14576b5ad791Spraks 				VN_RELE(tdvp);
1458073af7d9Spraks 			goto errout;
145931ceb98bSpraks 		}
146031ceb98bSpraks 	} else {
146131ceb98bSpraks 		portfop_vp_t	*pvp = vp->v_fopdata;
146231ceb98bSpraks 
146331ceb98bSpraks 		/*
146431ceb98bSpraks 		 * Re-association of the object.
146531ceb98bSpraks 		 */
146631ceb98bSpraks 		mutex_enter(&pvp->pvp_mutex);
146731ceb98bSpraks 
146831ceb98bSpraks 		/*
1469*abb88ab1SRobert Mustacchi 		 * Remove any queued up event.
147031ceb98bSpraks 		 */
147131ceb98bSpraks 		if (port_remove_done_event(pfp->pfop_pev)) {
147231ceb98bSpraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
147331ceb98bSpraks 		}
147431ceb98bSpraks 
147531ceb98bSpraks 		/*
1476*abb88ab1SRobert Mustacchi 		 * Set new events to watch and update the user pointer.
147731ceb98bSpraks 		 */
147831ceb98bSpraks 		pfp->pfop_events = events;
1479*abb88ab1SRobert Mustacchi 		pfp->pfop_pev->portkev_user = user;
148031ceb98bSpraks 
148131ceb98bSpraks 		/*
148231ceb98bSpraks 		 * If not active, mark it active even if it is being
148331ceb98bSpraks 		 * removed. Then it can send an exception event.
148431ceb98bSpraks 		 *
148531ceb98bSpraks 		 * Move it to the head, as the active ones are only
1486073af7d9Spraks 		 * in the beginning. If removing, the pfp will be on
148731ceb98bSpraks 		 * a temporary list, no need to move it to the front
1488073af7d9Spraks 		 * all the entries will be processed. Some exception
1489073af7d9Spraks 		 * events will be delivered in port_fop_excep();
149031ceb98bSpraks 		 */
149131ceb98bSpraks 		if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
149231ceb98bSpraks 			pfp->pfop_flags |= PORT_FOP_ACTIVE;
1493073af7d9Spraks 			if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
149431ceb98bSpraks 				pvp = (portfop_vp_t *)vp->v_fopdata;
149531ceb98bSpraks 				port_fop_listremove(pvp, pfp);
149631ceb98bSpraks 				port_fop_listinsert_head(pvp, pfp);
149731ceb98bSpraks 			}
149831ceb98bSpraks 		}
1499073af7d9Spraks 		pfp->pfop_callrid = curthread;
150031ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
1501073af7d9Spraks 		mutex_exit(&pfcp->pfc_lock);
150231ceb98bSpraks 	}
150331ceb98bSpraks 
150431ceb98bSpraks 	/*
1505073af7d9Spraks 	 * Compare time stamps and deliver events.
150631ceb98bSpraks 	 */
1507073af7d9Spraks 	if (vp->v_type != VFIFO) {
1508073af7d9Spraks 		port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
150931ceb98bSpraks 	}
151031ceb98bSpraks 
151131ceb98bSpraks 	error = 0;
151231ceb98bSpraks 
151331ceb98bSpraks 	/*
151431ceb98bSpraks 	 *  If we have too many watches on the vnode, discard an
151531ceb98bSpraks 	 *  inactive watch.
151631ceb98bSpraks 	 */
151731ceb98bSpraks 	port_fop_trimpfplist(vp);
151831ceb98bSpraks 
151931ceb98bSpraks errout:
152031ceb98bSpraks 	/*
152131ceb98bSpraks 	 * Release the hold acquired due to the lookup operation.
152231ceb98bSpraks 	 */
152331ceb98bSpraks 	if (vp != NULL)
152431ceb98bSpraks 		VN_RELE(vp);
15256b5ad791Spraks 	if (dvp != NULL)
15266b5ad791Spraks 		VN_RELE(dvp);
15276b5ad791Spraks 
15286b5ad791Spraks 	if (oldvp != NULL)
15296b5ad791Spraks 		VN_RELE(oldvp);
15306b5ad791Spraks 	if (olddvp != NULL)
15316b5ad791Spraks 		VN_RELE(olddvp);
153231ceb98bSpraks 
153331ceb98bSpraks 	/*
153431ceb98bSpraks 	 * copied file name not used, free it.
153531ceb98bSpraks 	 */
153631ceb98bSpraks 	if (cname != NULL) {
153731ceb98bSpraks 		kmem_free(cname, clen + 1);
153831ceb98bSpraks 	}
153931ceb98bSpraks 	return (error);
154031ceb98bSpraks }
154131ceb98bSpraks 
154231ceb98bSpraks 
154331ceb98bSpraks /*
154431ceb98bSpraks  * The port_dissociate_fop() function dissociates the file object
154531ceb98bSpraks  * from the event port and removes any events that are already on the queue.
154631ceb98bSpraks  * Only the owner of the association is allowed to dissociate the file from
154731ceb98bSpraks  * the port. Returns  success (0) if it was found and removed. Otherwise
154831ceb98bSpraks  * ENOENT.
154931ceb98bSpraks  */
155031ceb98bSpraks int
port_dissociate_fop(port_t * pp,uintptr_t object)155131ceb98bSpraks port_dissociate_fop(port_t *pp, uintptr_t object)
155231ceb98bSpraks {
155331ceb98bSpraks 	portfop_cache_t	*pfcp;
155431ceb98bSpraks 	portfop_t	*pfp;
155531ceb98bSpraks 	port_source_t	*pse;
155631ceb98bSpraks 	int		active = 0;
15576b5ad791Spraks 	vnode_t		*tvp = NULL, *tdvp = NULL;
155831ceb98bSpraks 
155931ceb98bSpraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
156031ceb98bSpraks 
156131ceb98bSpraks 	/*
156231ceb98bSpraks 	 * if this source is not associated or if there is no
156331ceb98bSpraks 	 * cache, nothing to do just return.
156431ceb98bSpraks 	 */
156531ceb98bSpraks 	if (pse == NULL ||
156631ceb98bSpraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
156731ceb98bSpraks 		return (EINVAL);
156831ceb98bSpraks 
156931ceb98bSpraks 	/*
157031ceb98bSpraks 	 * Check if this object is on the cache. Only the owner pid
157131ceb98bSpraks 	 * is allowed to dissociate.
157231ceb98bSpraks 	 */
157331ceb98bSpraks 	mutex_enter(&pfcp->pfc_lock);
157431ceb98bSpraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
157531ceb98bSpraks 	if (pfp == NULL) {
157631ceb98bSpraks 		mutex_exit(&pfcp->pfc_lock);
157731ceb98bSpraks 		return (ENOENT);
157831ceb98bSpraks 	}
157931ceb98bSpraks 
158031ceb98bSpraks 	/*
158131ceb98bSpraks 	 * If this was the last association, it will release
158231ceb98bSpraks 	 * the hold on the vnode. There is a race condition where
158331ceb98bSpraks 	 * the the pfp is being removed due to an exception event
158431ceb98bSpraks 	 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
158531ceb98bSpraks 	 * Since port source cache lock is held, port_fop_excep() cannot
1586073af7d9Spraks 	 * complete. The vnode itself will not disappear as long its pfps
158731ceb98bSpraks 	 * have a reference.
158831ceb98bSpraks 	 */
15896b5ad791Spraks 	(void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
159031ceb98bSpraks 	mutex_exit(&pfcp->pfc_lock);
15916b5ad791Spraks 	if (tvp != NULL)
15926b5ad791Spraks 		VN_RELE(tvp);
15936b5ad791Spraks 	if (tdvp != NULL)
15946b5ad791Spraks 		VN_RELE(tdvp);
159531ceb98bSpraks 	return (active ? 0 : ENOENT);
159631ceb98bSpraks }
159731ceb98bSpraks 
159831ceb98bSpraks 
159931ceb98bSpraks /*
160031ceb98bSpraks  * port_close() calls this function to request the PORT_SOURCE_FILE source
160131ceb98bSpraks  * to remove/free all resources allocated and associated with the port.
160231ceb98bSpraks  */
160331ceb98bSpraks 
160431ceb98bSpraks /* ARGSUSED */
160531ceb98bSpraks static void
port_close_fop(void * arg,int port,pid_t pid,int lastclose)160631ceb98bSpraks port_close_fop(void *arg, int port, pid_t pid, int lastclose)
160731ceb98bSpraks {
160831ceb98bSpraks 	port_t		*pp = arg;
160931ceb98bSpraks 	portfop_cache_t	*pfcp;
161031ceb98bSpraks 	portfop_t	**hashtbl;
161131ceb98bSpraks 	portfop_t	*pfp;
161231ceb98bSpraks 	portfop_t	*pfpnext;
16136b5ad791Spraks 	int		index, i;
161431ceb98bSpraks 	port_source_t	*pse;
1615*abb88ab1SRobert Mustacchi 	vnode_t		*tdvp = NULL;
16166b5ad791Spraks 	vnode_t		*vpl[PORTFOP_NVP];
161731ceb98bSpraks 
161831ceb98bSpraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
161931ceb98bSpraks 
162031ceb98bSpraks 	/*
162131ceb98bSpraks 	 * No source or no cache, nothing to do.
162231ceb98bSpraks 	 */
162331ceb98bSpraks 	if (pse == NULL ||
162431ceb98bSpraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
162531ceb98bSpraks 		return;
162631ceb98bSpraks 	/*
162731ceb98bSpraks 	 * Scan the cache and free all allocated portfop_t and port_kevent_t
16286b5ad791Spraks 	 * structures of this pid. Note, no new association for this pid will
16296b5ad791Spraks 	 * be possible as the port is being closed.
16306b5ad791Spraks 	 *
16316b5ad791Spraks 	 * The common case is that the port is not shared and all the entries
16326b5ad791Spraks 	 * are of this pid and have to be freed. Since VN_RELE has to be
16336b5ad791Spraks 	 * called outside the lock, we do it in batches.
163431ceb98bSpraks 	 */
163531ceb98bSpraks 	hashtbl = (portfop_t **)pfcp->pfc_hash;
16366b5ad791Spraks 	index = i = 0;
16376b5ad791Spraks 	bzero(vpl, sizeof (vpl));
16386b5ad791Spraks 	mutex_enter(&pfcp->pfc_lock);
16396b5ad791Spraks 	while (index < PORTFOP_HASHSIZE) {
16406b5ad791Spraks 		pfp = hashtbl[index];
16416b5ad791Spraks 		while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
164231ceb98bSpraks 			pfpnext = pfp->pfop_hashnext;
164331ceb98bSpraks 			if (pid == pfp->pfop_pid) {
16446b5ad791Spraks 				(void) port_remove_fop(pfp, pfcp, 1, NULL,
16456b5ad791Spraks 				    &vpl[i], &tdvp);
16466b5ad791Spraks 				if (vpl[i] != NULL) {
16476b5ad791Spraks 					i++;
16486b5ad791Spraks 				}
16496b5ad791Spraks 				if (tdvp != NULL) {
16506b5ad791Spraks 					vpl[i++] = tdvp;
16516b5ad791Spraks 					tdvp = NULL;
16526b5ad791Spraks 				}
165331ceb98bSpraks 			}
16546b5ad791Spraks 			pfp = pfpnext;
16556b5ad791Spraks 		}
16566b5ad791Spraks 		if (pfp == NULL)
16576b5ad791Spraks 			index++;
16586b5ad791Spraks 		/*
16596b5ad791Spraks 		 * Now call VN_RELE if we have collected enough vnodes or
16606b5ad791Spraks 		 * we have reached the end of the hash table.
16616b5ad791Spraks 		 */
16626b5ad791Spraks 		if (i >= (PORTFOP_NVP - 1) ||
16636b5ad791Spraks 		    (i > 0 && index == PORTFOP_HASHSIZE)) {
16646b5ad791Spraks 			mutex_exit(&pfcp->pfc_lock);
16656b5ad791Spraks 			while (i > 0) {
16666b5ad791Spraks 				VN_RELE(vpl[--i]);
16676b5ad791Spraks 				vpl[i] = NULL;
16686b5ad791Spraks 			}
16696b5ad791Spraks 			mutex_enter(&pfcp->pfc_lock);
167031ceb98bSpraks 		}
167131ceb98bSpraks 	}
167231ceb98bSpraks 
167331ceb98bSpraks 	/*
167431ceb98bSpraks 	 * Due to a race between port_close_fop() and port_fop()
167531ceb98bSpraks 	 * trying to remove the pfp's from the port's cache, it is
167631ceb98bSpraks 	 * possible that some pfp's are still in the process of being
167731ceb98bSpraks 	 * freed so we wait.
167831ceb98bSpraks 	 */
167931ceb98bSpraks 	while (lastclose && pfcp->pfc_objcount) {
168031ceb98bSpraks 		(void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
168131ceb98bSpraks 	}
168231ceb98bSpraks 	mutex_exit(&pfcp->pfc_lock);
168331ceb98bSpraks 	/*
168431ceb98bSpraks 	 * last close, free the cache.
168531ceb98bSpraks 	 */
168631ceb98bSpraks 	if (lastclose) {
168731ceb98bSpraks 		ASSERT(pfcp->pfc_objcount == 0);
168831ceb98bSpraks 		pse->portsrc_data = NULL;
168931ceb98bSpraks 		kmem_free(pfcp, sizeof (portfop_cache_t));
169031ceb98bSpraks 	}
169131ceb98bSpraks }
169231ceb98bSpraks 
169331ceb98bSpraks /*
169431ceb98bSpraks  * Given the list of associations(watches), it will send exception events,
169531ceb98bSpraks  * if still active, and discard them. The exception events are handled
1696da6c28aaSamw  * separately because, the pfp needs to be removed from the port cache and
169731ceb98bSpraks  * freed as the vnode's identity is changing or being removed. To remove
169831ceb98bSpraks  * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
169931ceb98bSpraks  * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
170031ceb98bSpraks  * the cache's lock cannot be acquired in port_fop_sendevent().
170131ceb98bSpraks  */
170231ceb98bSpraks static void
port_fop_excep(list_t * tlist,int op)170331ceb98bSpraks port_fop_excep(list_t *tlist, int op)
170431ceb98bSpraks {
170531ceb98bSpraks 	portfop_t	*pfp;
170631ceb98bSpraks 	portfop_cache_t *pfcp;
170731ceb98bSpraks 	port_t	*pp;
170831ceb98bSpraks 	port_kevent_t	*pkevp;
17096b5ad791Spraks 	vnode_t		*tdvp;
171031ceb98bSpraks 	int		error = 0;
171131ceb98bSpraks 
171231ceb98bSpraks 	while (pfp = (portfop_t *)list_head(tlist)) {
171331ceb98bSpraks 		int removed = 0;
171431ceb98bSpraks 		/*
171531ceb98bSpraks 		 * remove from the temp list. Since PORT_FOP_REMOVING is
171631ceb98bSpraks 		 * set, no other thread should attempt to perform a
171731ceb98bSpraks 		 * list_remove on this pfp.
171831ceb98bSpraks 		 */
171931ceb98bSpraks 		list_remove(tlist, pfp);
172031ceb98bSpraks 
172131ceb98bSpraks 		pfcp = pfp->pfop_pcache;
172231ceb98bSpraks 		mutex_enter(&pfcp->pfc_lock);
172331ceb98bSpraks 
172431ceb98bSpraks 		/*
172531ceb98bSpraks 		 * Remove the event from the port queue if it was queued up.
172631ceb98bSpraks 		 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
172731ceb98bSpraks 		 * no longer on the vnode's list.
172831ceb98bSpraks 		 */
172931ceb98bSpraks 		if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
173031ceb98bSpraks 			removed = port_remove_done_event(pfp->pfop_pev);
173131ceb98bSpraks 		}
173231ceb98bSpraks 
173331ceb98bSpraks 		/*
173431ceb98bSpraks 		 * If still active or the event was queued up and
173531ceb98bSpraks 		 * had not been collected yet, send an EXCEPTION event.
173631ceb98bSpraks 		 */
173731ceb98bSpraks 		if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
173831ceb98bSpraks 			pp = pfp->pfop_pp;
173931ceb98bSpraks 			/*
174031ceb98bSpraks 			 * Allocate a port_kevent_t non cached to send this
174131ceb98bSpraks 			 * event since we will be de-registering.
174231ceb98bSpraks 			 * The port_kevent_t cannot be pointing back to the
174331ceb98bSpraks 			 * pfp anymore.
174431ceb98bSpraks 			 */
174531ceb98bSpraks 			pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
174631ceb98bSpraks 			error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
174731ceb98bSpraks 			    PORT_ALLOC_DEFAULT, &pkevp);
174831ceb98bSpraks 			if (!error) {
174931ceb98bSpraks 
175031ceb98bSpraks 				pkevp->portkev_callback = port_fop_callback;
175131ceb98bSpraks 				pkevp->portkev_arg = NULL;
175231ceb98bSpraks 				pkevp->portkev_object =
175331ceb98bSpraks 				    pfp->pfop_pev->portkev_object;
175431ceb98bSpraks 				pkevp->portkev_user =
175531ceb98bSpraks 				    pfp->pfop_pev->portkev_user;
175631ceb98bSpraks 				/*
175731ceb98bSpraks 				 * Copy the pid of the watching process.
175831ceb98bSpraks 				 */
175931ceb98bSpraks 				pkevp->portkev_pid =
176031ceb98bSpraks 				    pfp->pfop_pev->portkev_pid;
176131ceb98bSpraks 				pkevp->portkev_events = op;
176231ceb98bSpraks 				port_send_event(pkevp);
176331ceb98bSpraks 			}
176431ceb98bSpraks 		}
176531ceb98bSpraks 		/*
176631ceb98bSpraks 		 * At this point the pfp has been removed from the vnode's
176731ceb98bSpraks 		 * list its cached port_kevent_t is not on the done queue.
176831ceb98bSpraks 		 * Remove the pfp and free it from the cache.
176931ceb98bSpraks 		 */
17706b5ad791Spraks 		tdvp = pfp->pfop_dvp;
177131ceb98bSpraks 		port_pcache_remove_fop(pfcp, pfp);
177231ceb98bSpraks 		mutex_exit(&pfcp->pfc_lock);
17736b5ad791Spraks 		if (tdvp != NULL)
17746b5ad791Spraks 			VN_RELE(tdvp);
177531ceb98bSpraks 	}
177631ceb98bSpraks }
177731ceb98bSpraks 
177831ceb98bSpraks /*
177931ceb98bSpraks  * Send the file events to all of the processes watching this
178031ceb98bSpraks  * vnode. In case of hard links, the directory vnode pointer and
178131ceb98bSpraks  * the file name are compared. If the names match, then the specified
178231ceb98bSpraks  * event is sent or else, the FILE_ATTRIB event is sent, This is the
178331ceb98bSpraks  * documented behavior.
178431ceb98bSpraks  */
178531ceb98bSpraks void
port_fop_sendevent(vnode_t * vp,int events,vnode_t * dvp,char * cname)178631ceb98bSpraks port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
178731ceb98bSpraks {
178831ceb98bSpraks 	port_kevent_t	*pkevp;
178931ceb98bSpraks 	portfop_t	*pfp, *npfp;
179031ceb98bSpraks 	portfop_vp_t	*pvp;
179131ceb98bSpraks 	list_t		tmplist;
179231ceb98bSpraks 	int		removeall = 0;
179331ceb98bSpraks 
179431ceb98bSpraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
179531ceb98bSpraks 	mutex_enter(&pvp->pvp_mutex);
179631ceb98bSpraks 
179731ceb98bSpraks 	/*
179831ceb98bSpraks 	 * Check if the list is empty.
179931ceb98bSpraks 	 *
180031ceb98bSpraks 	 * All entries have been removed by some other thread.
180131ceb98bSpraks 	 * The vnode may be still active and we got called,
180231ceb98bSpraks 	 * but some other thread is in the process of removing the hooks.
180331ceb98bSpraks 	 */
180431ceb98bSpraks 	if (!list_head(&pvp->pvp_pfoplist)) {
180531ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
180631ceb98bSpraks 		return;
180731ceb98bSpraks 	}
180831ceb98bSpraks 
180931ceb98bSpraks 	if ((events & (FILE_EXCEPTION))) {
181031ceb98bSpraks 		/*
181131ceb98bSpraks 		 * If it is an event for which we are going to remove
181231ceb98bSpraks 		 * the watches so just move it a temporary list and
181331ceb98bSpraks 		 * release this vnode.
181431ceb98bSpraks 		 */
181531ceb98bSpraks 		list_create(&tmplist, sizeof (portfop_t),
181631ceb98bSpraks 		    offsetof(portfop_t, pfop_node));
181731ceb98bSpraks 
181831ceb98bSpraks 		/*
181931ceb98bSpraks 		 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
182031ceb98bSpraks 		 * passed for an exception event, all associations need to be
182131ceb98bSpraks 		 * removed.
182231ceb98bSpraks 		 */
182331ceb98bSpraks 		if (dvp == NULL || cname == NULL) {
182431ceb98bSpraks 			removeall = 1;
182531ceb98bSpraks 		}
182631ceb98bSpraks 	}
182731ceb98bSpraks 
182831ceb98bSpraks 	if (!removeall) {
182931ceb98bSpraks 		/*
1830073af7d9Spraks 		 * All the active ones are in the beginning of the list.
183172102e74SBryan Cantrill 		 * Note that we process this list in reverse order to assure
183272102e74SBryan Cantrill 		 * that events are delivered in the order that they were
183372102e74SBryan Cantrill 		 * associated.
183431ceb98bSpraks 		 */
183572102e74SBryan Cantrill 		for (pfp = (portfop_t *)list_tail(&pvp->pvp_pfoplist);
183672102e74SBryan Cantrill 		    pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE); pfp = npfp) {
183772102e74SBryan Cantrill 			npfp = list_prev(&pvp->pvp_pfoplist, pfp);
183872102e74SBryan Cantrill 		}
183972102e74SBryan Cantrill 
184072102e74SBryan Cantrill 		for (; pfp != NULL; pfp = npfp) {
184131ceb98bSpraks 			int levents = events;
184231ceb98bSpraks 
184372102e74SBryan Cantrill 			npfp = list_prev(&pvp->pvp_pfoplist, pfp);
184431ceb98bSpraks 			/*
184531ceb98bSpraks 			 * Hard links case - If the file is being
184631ceb98bSpraks 			 * removed/renamed, and the name matches
184731ceb98bSpraks 			 * the watched file, then it is an EXCEPTION
184831ceb98bSpraks 			 * event or else it will be just a FILE_ATTRIB.
184931ceb98bSpraks 			 */
185031ceb98bSpraks 			if ((events & (FILE_EXCEPTION))) {
185131ceb98bSpraks 				ASSERT(dvp != NULL && cname != NULL);
185231ceb98bSpraks 				if (pfp->pfop_dvp == NULL ||
185331ceb98bSpraks 				    (pfp->pfop_dvp == dvp &&
185431ceb98bSpraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
185531ceb98bSpraks 					/*
185631ceb98bSpraks 					 * It is an exception event, move it
185731ceb98bSpraks 					 * to temp list and process it later.
185831ceb98bSpraks 					 * Note we don't set the pfp->pfop_vp
185931ceb98bSpraks 					 * to NULL even thought it has been
186031ceb98bSpraks 					 * removed from the vnode's list. This
186131ceb98bSpraks 					 * pointer is referenced in
186231ceb98bSpraks 					 * port_remove_fop(). The vnode it
1863073af7d9Spraks 					 * self cannot disappear until this
186431ceb98bSpraks 					 * pfp gets removed and freed.
186531ceb98bSpraks 					 */
186631ceb98bSpraks 					port_fop_listremove(pvp, pfp);
186731ceb98bSpraks 					list_insert_tail(&tmplist, (void *)pfp);
186831ceb98bSpraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
186931ceb98bSpraks 					continue;
187031ceb98bSpraks 				} else {
187131ceb98bSpraks 					levents = FILE_ATTRIB;
187231ceb98bSpraks 				}
187331ceb98bSpraks 
187431ceb98bSpraks 			}
187531ceb98bSpraks 
187631ceb98bSpraks 			if (pfp->pfop_events & levents) {
187731ceb98bSpraks 				/*
187831ceb98bSpraks 				 * deactivate and move it to the tail.
187931ceb98bSpraks 				 * If the pfp was active, it cannot be
188031ceb98bSpraks 				 * on the port's done queue.
188131ceb98bSpraks 				 */
188231ceb98bSpraks 				pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
188331ceb98bSpraks 				port_fop_listremove(pvp, pfp);
188431ceb98bSpraks 				port_fop_listinsert_tail(pvp, pfp);
188531ceb98bSpraks 
188631ceb98bSpraks 				pkevp = pfp->pfop_pev;
188731ceb98bSpraks 				pkevp->portkev_events |=
188831ceb98bSpraks 				    (levents & pfp->pfop_events);
188931ceb98bSpraks 				port_send_event(pkevp);
189031ceb98bSpraks 				pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
189131ceb98bSpraks 			}
189231ceb98bSpraks 		}
189331ceb98bSpraks 	}
189431ceb98bSpraks 
189531ceb98bSpraks 
189631ceb98bSpraks 	if ((events & (FILE_EXCEPTION))) {
189731ceb98bSpraks 		if (!removeall) {
189831ceb98bSpraks 			/*
189931ceb98bSpraks 			 * Check the inactive associations and remove them if
190031ceb98bSpraks 			 * the file name matches.
190131ceb98bSpraks 			 */
190231ceb98bSpraks 			for (; pfp; pfp = npfp) {
190331ceb98bSpraks 				npfp = list_next(&pvp->pvp_pfoplist, pfp);
190431ceb98bSpraks 				if (dvp == NULL || cname == NULL ||
190531ceb98bSpraks 				    pfp->pfop_dvp == NULL ||
190631ceb98bSpraks 				    (pfp->pfop_dvp == dvp &&
190731ceb98bSpraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
190831ceb98bSpraks 					port_fop_listremove(pvp, pfp);
190931ceb98bSpraks 					list_insert_tail(&tmplist, (void *)pfp);
191031ceb98bSpraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
191131ceb98bSpraks 				}
191231ceb98bSpraks 			}
191331ceb98bSpraks 		} else {
191431ceb98bSpraks 			/*
191531ceb98bSpraks 			 * Can be optimized to avoid two pass over this list
191631ceb98bSpraks 			 * by having a flag in the vnode's portfop_vp_t
191731ceb98bSpraks 			 * structure to indicate that it is going away,
191831ceb98bSpraks 			 * Or keep the list short by reusing inactive watches.
191931ceb98bSpraks 			 */
192031ceb98bSpraks 			port_fop_listmove(pvp, &tmplist);
192131ceb98bSpraks 			for (pfp = (portfop_t *)list_head(&tmplist);
192231ceb98bSpraks 			    pfp; pfp = list_next(&tmplist, pfp)) {
192331ceb98bSpraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
192431ceb98bSpraks 			}
192531ceb98bSpraks 		}
192631ceb98bSpraks 
192731ceb98bSpraks 		/*
192831ceb98bSpraks 		 * Uninstall the fem hooks if there are no more associations.
192931ceb98bSpraks 		 * This will release the pvp mutex.
193031ceb98bSpraks 		 *
193131ceb98bSpraks 		 * Even thought all entries may have been removed,
193231ceb98bSpraks 		 * the vnode itself cannot disappear as there will be a
193331ceb98bSpraks 		 * hold on it due to this call to port_fop_sendevent. This is
193431ceb98bSpraks 		 * important to syncronize with a port_dissociate_fop() call
193531ceb98bSpraks 		 * that may be attempting to remove an object from the vnode's.
193631ceb98bSpraks 		 */
19376b5ad791Spraks 		if (port_fop_femuninstall(vp))
19386b5ad791Spraks 			VN_RELE(vp);
193931ceb98bSpraks 
194031ceb98bSpraks 		/*
194131ceb98bSpraks 		 * Send exception events and discard the watch entries.
194231ceb98bSpraks 		 */
194331ceb98bSpraks 		port_fop_excep(&tmplist, events);
194431ceb98bSpraks 		list_destroy(&tmplist);
194531ceb98bSpraks 
194631ceb98bSpraks 	} else {
194731ceb98bSpraks 		mutex_exit(&pvp->pvp_mutex);
194831ceb98bSpraks 
194931ceb98bSpraks 		/*
1950df2381bfSpraks 		 * trim the list.
195131ceb98bSpraks 		 */
195231ceb98bSpraks 		port_fop_trimpfplist(vp);
195331ceb98bSpraks 	}
195431ceb98bSpraks }
195531ceb98bSpraks 
195631ceb98bSpraks /*
1957df2381bfSpraks  * Given the file operation, map it to the event types and send.
195831ceb98bSpraks  */
195931ceb98bSpraks void
port_fop(vnode_t * vp,int op,int retval)196031ceb98bSpraks port_fop(vnode_t *vp, int op, int retval)
196131ceb98bSpraks {
196231ceb98bSpraks 	int event = 0;
196331ceb98bSpraks 	/*
196431ceb98bSpraks 	 * deliver events only if the operation was successful.
196531ceb98bSpraks 	 */
196631ceb98bSpraks 	if (retval)
196731ceb98bSpraks 		return;
196831ceb98bSpraks 
196931ceb98bSpraks 	/*
1970da6c28aaSamw 	 * These events occurring on the watched file.
197131ceb98bSpraks 	 */
197231ceb98bSpraks 	if (op & FOP_MODIFIED_MASK) {
197331ceb98bSpraks 		event  = FILE_MODIFIED;
197431ceb98bSpraks 	}
197531ceb98bSpraks 	if (op & FOP_ACCESS_MASK) {
197631ceb98bSpraks 		event  |= FILE_ACCESS;
197731ceb98bSpraks 	}
197831ceb98bSpraks 	if (op & FOP_ATTRIB_MASK) {
197931ceb98bSpraks 		event  |= FILE_ATTRIB;
198031ceb98bSpraks 	}
198172102e74SBryan Cantrill 	if (op & FOP_TRUNC_MASK) {
198272102e74SBryan Cantrill 		event  |= FILE_TRUNC;
198372102e74SBryan Cantrill 	}
198431ceb98bSpraks 	if (event) {
1985*abb88ab1SRobert Mustacchi 		port_fop_sendevent(vp, event, NULL, NULL);
198631ceb98bSpraks 	}
198731ceb98bSpraks }
198831ceb98bSpraks 
port_forceunmount(vfs_t * vfsp)198984c5ce69Spraks static int port_forceunmount(vfs_t *vfsp)
199084c5ce69Spraks {
199184c5ce69Spraks 	char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
199284c5ce69Spraks 
199384c5ce69Spraks 	if (fsname == NULL) {
199484c5ce69Spraks 		return (0);
199584c5ce69Spraks 	}
199684c5ce69Spraks 
199784c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS) == 0) {
199884c5ce69Spraks 		return (1);
199984c5ce69Spraks 	}
200084c5ce69Spraks 
200184c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
200284c5ce69Spraks 		return (1);
200384c5ce69Spraks 	}
200484c5ce69Spraks 
200584c5ce69Spraks 	if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
200684c5ce69Spraks 		return (1);
200784c5ce69Spraks 	}
200884c5ce69Spraks 	return (0);
200984c5ce69Spraks }
201031ceb98bSpraks /*
201131ceb98bSpraks  * ----- the unmount filesystem op(fsem) hook.
201231ceb98bSpraks  */
201331ceb98bSpraks int
port_fop_unmount(fsemarg_t * vf,int flag,cred_t * cr)201431ceb98bSpraks port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
201531ceb98bSpraks {
201631ceb98bSpraks 	vfs_t	*vfsp = (vfs_t *)vf->fa_fnode->fn_available;
201731ceb98bSpraks 	kmutex_t	*mtx;
201831ceb98bSpraks 	portfop_vfs_t	*pvfsp, **ppvfsp;
201931ceb98bSpraks 	portfop_vp_t	*pvp;
202031ceb98bSpraks 	int error;
202184c5ce69Spraks 	int fmfs;
202284c5ce69Spraks 
202384c5ce69Spraks 	fmfs = port_forceunmount(vfsp);
202431ceb98bSpraks 
202531ceb98bSpraks 	mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
202631ceb98bSpraks 	ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
202731ceb98bSpraks 	pvfsp = NULL;
202831ceb98bSpraks 	mutex_enter(mtx);
202931ceb98bSpraks 	/*
20306b5ad791Spraks 	 * since this fsem hook is triggered, the vfsp has to be on
203131ceb98bSpraks 	 * the hash list.
203231ceb98bSpraks 	 */
203331ceb98bSpraks 	for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
203415c07adcSJohn Levon 		;
203531ceb98bSpraks 
203684c5ce69Spraks 	/*
203784c5ce69Spraks 	 * For some of the filesystems, allow unmounts to proceed only if
203884c5ce69Spraks 	 * there are no files being watched or it is a forced unmount.
203984c5ce69Spraks 	 */
204084c5ce69Spraks 	if (fmfs && !(flag & MS_FORCE) &&
204184c5ce69Spraks 	    !list_is_empty(&pvfsp->pvfs_pvplist)) {
204284c5ce69Spraks 		mutex_exit(mtx);
204384c5ce69Spraks 		return (EBUSY);
204484c5ce69Spraks 	}
204584c5ce69Spraks 
204631ceb98bSpraks 	/*
204731ceb98bSpraks 	 * Indicate that the unmount is in process. Don't remove it yet.
204831ceb98bSpraks 	 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
204931ceb98bSpraks 	 * flag on the vfs_t structure. But we call the filesystem unmount
205031ceb98bSpraks 	 * routine after removing all the file watches for this filesystem,
205131ceb98bSpraks 	 * otherwise the unmount will fail due to active vnodes.
205231ceb98bSpraks 	 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
205331ceb98bSpraks 	 * attempting to add a file watch.
205431ceb98bSpraks 	 */
205531ceb98bSpraks 	pvfsp->pvfs_unmount = 1;
205631ceb98bSpraks 	mutex_exit(mtx);
205731ceb98bSpraks 
205831ceb98bSpraks 	/*
205931ceb98bSpraks 	 * uninstall the fsem hooks.
206031ceb98bSpraks 	 */
206131ceb98bSpraks 	(void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
206231ceb98bSpraks 
206331ceb98bSpraks 	while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
206431ceb98bSpraks 		list_remove(&pvfsp->pvfs_pvplist, pvp);
206531ceb98bSpraks 		/*
206631ceb98bSpraks 		 * This should send an UNMOUNTED event to all the
206731ceb98bSpraks 		 * watched vnode of this filesystem and uninstall
206831ceb98bSpraks 		 * the fem hooks. We release the hold on the vnode here
206931ceb98bSpraks 		 * because port_fop_femuninstall() will not do it if
207031ceb98bSpraks 		 * unmount is in process.
207131ceb98bSpraks 		 */
207231ceb98bSpraks 		port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
207331ceb98bSpraks 		VN_RELE(pvp->pvp_vp);
207431ceb98bSpraks 	}
207531ceb98bSpraks 
207631ceb98bSpraks 	error = vfsnext_unmount(vf, flag, cr);
207731ceb98bSpraks 
207831ceb98bSpraks 	/*
207931ceb98bSpraks 	 * we free the pvfsp after the unmount has been completed.
208031ceb98bSpraks 	 */
208131ceb98bSpraks 	mutex_enter(mtx);
208231ceb98bSpraks 	for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
208331ceb98bSpraks 	    ppvfsp = &(*ppvfsp)->pvfs_next)
208415c07adcSJohn Levon 		;
208531ceb98bSpraks 
208631ceb98bSpraks 	/*
208731ceb98bSpraks 	 * remove and free it.
208831ceb98bSpraks 	 */
208931ceb98bSpraks 	ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
209031ceb98bSpraks 	if (*ppvfsp) {
209131ceb98bSpraks 		pvfsp = *ppvfsp;
209231ceb98bSpraks 		*ppvfsp = pvfsp->pvfs_next;
209331ceb98bSpraks 	}
209431ceb98bSpraks 	mutex_exit(mtx);
209531ceb98bSpraks 	kmem_free(pvfsp, sizeof (portfop_vfs_t));
209631ceb98bSpraks 	return (error);
209731ceb98bSpraks }
209831ceb98bSpraks 
209931ceb98bSpraks /*
210031ceb98bSpraks  * ------------------------------file op hooks--------------------------
210131ceb98bSpraks  * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
210231ceb98bSpraks  */
210331ceb98bSpraks static int
port_fop_open(femarg_t * vf,int mode,cred_t * cr,caller_context_t * ct)2104da6c28aaSamw port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
210531ceb98bSpraks {
210631ceb98bSpraks 	int		retval;
210731ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
210831ceb98bSpraks 
2109da6c28aaSamw 	retval = vnext_open(vf, mode, cr, ct);
211031ceb98bSpraks 	port_fop(vp, FOP_FILE_OPEN, retval);
211131ceb98bSpraks 	return (retval);
211231ceb98bSpraks }
211331ceb98bSpraks 
211431ceb98bSpraks static int
port_fop_write(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)211531ceb98bSpraks port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
211631ceb98bSpraks     caller_context_t *ct)
211731ceb98bSpraks {
211831ceb98bSpraks 	int		retval;
211931ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
212031ceb98bSpraks 
212131ceb98bSpraks 	retval =  vnext_write(vf, uiop, ioflag, cr, ct);
212231ceb98bSpraks 	port_fop(vp, FOP_FILE_WRITE, retval);
212331ceb98bSpraks 	return (retval);
212431ceb98bSpraks }
212531ceb98bSpraks 
212631ceb98bSpraks static int
port_fop_map(femarg_t * vf,offset_t off,struct as * as,caddr_t * addrp,size_t len,uchar_t prot,uchar_t maxport,uint_t flags,cred_t * cr,caller_context_t * ct)212731ceb98bSpraks port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
2128da6c28aaSamw     size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
2129da6c28aaSamw     caller_context_t *ct)
213031ceb98bSpraks {
213131ceb98bSpraks 	int		retval;
213231ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
213331ceb98bSpraks 
2134da6c28aaSamw 	retval =  vnext_map(vf, off, as, addrp, len, prot, maxport,
2135da6c28aaSamw 	    flags, cr, ct);
213631ceb98bSpraks 	port_fop(vp, FOP_FILE_MAP, retval);
213731ceb98bSpraks 	return (retval);
213831ceb98bSpraks }
213931ceb98bSpraks 
214031ceb98bSpraks static int
port_fop_read(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)214131ceb98bSpraks port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
214231ceb98bSpraks     caller_context_t *ct)
214331ceb98bSpraks {
214431ceb98bSpraks 	int		retval;
214531ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
214631ceb98bSpraks 
214731ceb98bSpraks 	retval =  vnext_read(vf, uiop, ioflag, cr, ct);
214831ceb98bSpraks 	port_fop(vp, FOP_FILE_READ, retval);
214931ceb98bSpraks 	return (retval);
215031ceb98bSpraks }
215131ceb98bSpraks 
215231ceb98bSpraks 
215331ceb98bSpraks /*
215431ceb98bSpraks  * AT_SIZE - is for the open(O_TRUNC) case.
215531ceb98bSpraks  */
215631ceb98bSpraks int
port_fop_setattr(femarg_t * vf,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)215731ceb98bSpraks port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
215831ceb98bSpraks     caller_context_t *ct)
215931ceb98bSpraks {
216031ceb98bSpraks 	int		retval;
216131ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
216231ceb98bSpraks 	int		events = 0;
216331ceb98bSpraks 
216431ceb98bSpraks 	retval = vnext_setattr(vf, vap, flags, cr, ct);
216572102e74SBryan Cantrill 	if (vap->va_mask & AT_SIZE) {
216672102e74SBryan Cantrill 		events |= FOP_FILE_TRUNC;
216772102e74SBryan Cantrill 	}
216831ceb98bSpraks 	if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
216931ceb98bSpraks 		events |= FOP_FILE_SETATTR_MTIME;
217031ceb98bSpraks 	}
217131ceb98bSpraks 	if (vap->va_mask & AT_ATIME) {
217231ceb98bSpraks 		events |= FOP_FILE_SETATTR_ATIME;
217331ceb98bSpraks 	}
2174df2381bfSpraks 	events |= FOP_FILE_SETATTR_CTIME;
217531ceb98bSpraks 
217631ceb98bSpraks 	port_fop(vp, events, retval);
217731ceb98bSpraks 	return (retval);
217831ceb98bSpraks }
217931ceb98bSpraks 
218031ceb98bSpraks int
port_fop_create(femarg_t * vf,char * name,vattr_t * vap,vcexcl_t excl,int mode,vnode_t ** vpp,cred_t * cr,int flag,caller_context_t * ct,vsecattr_t * vsecp)218131ceb98bSpraks port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
2182da6c28aaSamw     int mode, vnode_t **vpp, cred_t *cr, int flag,
2183da6c28aaSamw     caller_context_t *ct, vsecattr_t *vsecp)
218431ceb98bSpraks {
218531ceb98bSpraks 	int		retval, got = 1;
218631ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
218731ceb98bSpraks 	vattr_t		vatt, vatt1;
218831ceb98bSpraks 
218931ceb98bSpraks 	/*
219031ceb98bSpraks 	 * If the file already exists, then there will be no change
219131ceb98bSpraks 	 * to the directory. Therefore, we need to compare the
219231ceb98bSpraks 	 * modification time of the directory to determine if the
219331ceb98bSpraks 	 * file was actually created.
219431ceb98bSpraks 	 */
2195df2381bfSpraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2196da6c28aaSamw 	if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
219731ceb98bSpraks 		got = 0;
219831ceb98bSpraks 	}
2199da6c28aaSamw 	retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
2200da6c28aaSamw 	    flag, ct, vsecp);
220131ceb98bSpraks 
2202df2381bfSpraks 	vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2203da6c28aaSamw 	if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
220431ceb98bSpraks 		if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
220531ceb98bSpraks 		    (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
220631ceb98bSpraks 		    vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
220731ceb98bSpraks 			/*
220831ceb98bSpraks 			 * File was created.
220931ceb98bSpraks 			 */
221031ceb98bSpraks 			port_fop(vp, FOP_FILE_CREATE, retval);
221131ceb98bSpraks 		}
221231ceb98bSpraks 	}
221331ceb98bSpraks 	return (retval);
221431ceb98bSpraks }
221531ceb98bSpraks 
221631ceb98bSpraks int
port_fop_remove(femarg_t * vf,char * nm,cred_t * cr,caller_context_t * ct,int flags)2217da6c28aaSamw port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
2218da6c28aaSamw     int flags)
221931ceb98bSpraks {
222031ceb98bSpraks 	int		retval;
222131ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
222231ceb98bSpraks 
2223da6c28aaSamw 	retval = vnext_remove(vf, nm, cr, ct, flags);
222431ceb98bSpraks 	port_fop(vp, FOP_FILE_REMOVE, retval);
222531ceb98bSpraks 	return (retval);
222631ceb98bSpraks }
222731ceb98bSpraks 
222831ceb98bSpraks int
port_fop_link(femarg_t * vf,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)2229da6c28aaSamw port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2230da6c28aaSamw     caller_context_t *ct, int flags)
223131ceb98bSpraks {
223231ceb98bSpraks 	int		retval;
223331ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
223431ceb98bSpraks 
2235da6c28aaSamw 	retval = vnext_link(vf, svp, tnm, cr, ct, flags);
223631ceb98bSpraks 	port_fop(vp, FOP_FILE_LINK, retval);
223731ceb98bSpraks 	return (retval);
223831ceb98bSpraks }
223931ceb98bSpraks 
224031ceb98bSpraks /*
224131ceb98bSpraks  * Rename operation is allowed only when from and to directories are
224231ceb98bSpraks  * on the same filesystem. This is checked in vn_rename().
224331ceb98bSpraks  * The target directory is notified thru a VNEVENT by the filesystem
224431ceb98bSpraks  * if the source dir != target dir.
224531ceb98bSpraks  */
224631ceb98bSpraks int
port_fop_rename(femarg_t * vf,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)2247da6c28aaSamw port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
2248da6c28aaSamw     caller_context_t *ct, int flags)
224931ceb98bSpraks {
225031ceb98bSpraks 	int		retval;
225131ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
225231ceb98bSpraks 
2253da6c28aaSamw 	retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
225431ceb98bSpraks 	port_fop(vp, FOP_FILE_RENAMESRC, retval);
225531ceb98bSpraks 	return (retval);
225631ceb98bSpraks }
225731ceb98bSpraks 
225831ceb98bSpraks int
port_fop_mkdir(femarg_t * vf,char * dirname,vattr_t * vap,vnode_t ** vpp,cred_t * cr,caller_context_t * ct,int flags,vsecattr_t * vsecp)225931ceb98bSpraks port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
2260da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
226131ceb98bSpraks {
226231ceb98bSpraks 	int		retval;
226331ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
226431ceb98bSpraks 
2265da6c28aaSamw 	retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
226631ceb98bSpraks 	port_fop(vp, FOP_FILE_MKDIR, retval);
226731ceb98bSpraks 	return (retval);
226831ceb98bSpraks }
226931ceb98bSpraks 
227031ceb98bSpraks int
port_fop_rmdir(femarg_t * vf,char * nm,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)2271da6c28aaSamw port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2272da6c28aaSamw     caller_context_t *ct, int flags)
227331ceb98bSpraks {
227431ceb98bSpraks 	int		retval;
227531ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
227631ceb98bSpraks 
2277da6c28aaSamw 	retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
227831ceb98bSpraks 	port_fop(vp, FOP_FILE_RMDIR, retval);
227931ceb98bSpraks 	return (retval);
228031ceb98bSpraks }
228131ceb98bSpraks 
228231ceb98bSpraks int
port_fop_readdir(femarg_t * vf,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)2283da6c28aaSamw port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2284da6c28aaSamw     caller_context_t *ct, int flags)
228531ceb98bSpraks {
228631ceb98bSpraks 	int		retval;
228731ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
228831ceb98bSpraks 
2289da6c28aaSamw 	retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
229031ceb98bSpraks 	port_fop(vp, FOP_FILE_READDIR, retval);
229131ceb98bSpraks 	return (retval);
229231ceb98bSpraks }
229331ceb98bSpraks 
229431ceb98bSpraks int
port_fop_symlink(femarg_t * vf,char * linkname,vattr_t * vap,char * target,cred_t * cr,caller_context_t * ct,int flags)229531ceb98bSpraks port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
2296da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags)
229731ceb98bSpraks {
229831ceb98bSpraks 	int		retval;
229931ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
230031ceb98bSpraks 
2301da6c28aaSamw 	retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
230231ceb98bSpraks 	port_fop(vp, FOP_FILE_SYMLINK, retval);
230331ceb98bSpraks 	return (retval);
230431ceb98bSpraks }
230531ceb98bSpraks 
230631ceb98bSpraks /*
230731ceb98bSpraks  * acl, facl call this.
230831ceb98bSpraks  */
230931ceb98bSpraks int
port_fop_setsecattr(femarg_t * vf,vsecattr_t * vsap,int flags,cred_t * cr,caller_context_t * ct)2310da6c28aaSamw port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
2311da6c28aaSamw     caller_context_t *ct)
231231ceb98bSpraks {
231331ceb98bSpraks 	int	retval;
231431ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
2315da6c28aaSamw 	retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
231631ceb98bSpraks 	port_fop(vp, FOP_FILE_SETSECATTR, retval);
231731ceb98bSpraks 	return (retval);
231831ceb98bSpraks }
231931ceb98bSpraks 
232031ceb98bSpraks /*
232131ceb98bSpraks  * these are events on the watched file/directory
232231ceb98bSpraks  */
232331ceb98bSpraks int
port_fop_vnevent(femarg_t * vf,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)2324da6c28aaSamw port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
2325da6c28aaSamw     caller_context_t *ct)
232631ceb98bSpraks {
232731ceb98bSpraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
232831ceb98bSpraks 
232931ceb98bSpraks 	switch (vnevent) {
233031ceb98bSpraks 	case	VE_RENAME_SRC:
233131ceb98bSpraks 			port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
233231ceb98bSpraks 		break;
233331ceb98bSpraks 	case	VE_RENAME_DEST:
233431ceb98bSpraks 			port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
233531ceb98bSpraks 		break;
233631ceb98bSpraks 	case	VE_REMOVE:
233731ceb98bSpraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
233831ceb98bSpraks 		break;
233931ceb98bSpraks 	case	VE_RMDIR:
234031ceb98bSpraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
234131ceb98bSpraks 		break;
234231ceb98bSpraks 	case	VE_CREATE:
234372102e74SBryan Cantrill 			port_fop_sendevent(vp,
234472102e74SBryan Cantrill 			    FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
234531ceb98bSpraks 		break;
234631ceb98bSpraks 	case	VE_LINK:
234731ceb98bSpraks 			port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
234831ceb98bSpraks 		break;
234931ceb98bSpraks 
235031ceb98bSpraks 	case	VE_RENAME_DEST_DIR:
235131ceb98bSpraks 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
235231ceb98bSpraks 			    NULL, NULL);
235331ceb98bSpraks 		break;
235431ceb98bSpraks 
235531ceb98bSpraks 	case	VE_MOUNTEDOVER:
235631ceb98bSpraks 			port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
235731ceb98bSpraks 		break;
235872102e74SBryan Cantrill 	case	VE_TRUNCATE:
235972102e74SBryan Cantrill 			port_fop_sendevent(vp, FILE_TRUNC, NULL, NULL);
236072102e74SBryan Cantrill 		break;
236131ceb98bSpraks 	default:
236231ceb98bSpraks 		break;
236331ceb98bSpraks 	}
2364da6c28aaSamw 	return (vnext_vnevent(vf, vnevent, dvp, name, ct));
236531ceb98bSpraks }
2366