xref: /onnv-gate/usr/src/uts/common/fs/portfs/port_fop.c (revision 11899:b90a7691780e)
14863Spraks /*
24863Spraks  * CDDL HEADER START
34863Spraks  *
44863Spraks  * The contents of this file are subject to the terms of the
54863Spraks  * Common Development and Distribution License (the "License").
64863Spraks  * You may not use this file except in compliance with the License.
74863Spraks  *
84863Spraks  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94863Spraks  * or http://www.opensolaris.org/os/licensing.
104863Spraks  * See the License for the specific language governing permissions
114863Spraks  * and limitations under the License.
124863Spraks  *
134863Spraks  * When distributing Covered Code, include this CDDL HEADER in each
144863Spraks  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154863Spraks  * If applicable, add the following below this CDDL HEADER, with the
164863Spraks  * fields enclosed by brackets "[]" replaced with your own identifying
174863Spraks  * information: Portions Copyright [yyyy] [name of copyright owner]
184863Spraks  *
194863Spraks  * CDDL HEADER END
204863Spraks  */
214863Spraks /*
22*11899SPrakash.Sangappa@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
234863Spraks  * Use is subject to license terms.
244863Spraks  */
254863Spraks 
264863Spraks 
274863Spraks /*
284863Spraks  * File Events Notification
294863Spraks  * ------------------------
304863Spraks  *
314863Spraks  * The File Events Notification facility provides file and directory change
324863Spraks  * notification. It is implemented as an event source(PORT_SOURCE_FILE)
334863Spraks  * under the Event Ports framework. Therefore the API is an extension to
344863Spraks  * the Event Ports API.
354863Spraks  *
364863Spraks  * It uses the FEM (File Events Monitoring) framework to intercept
374863Spraks  * operations on the files & directories and generate appropriate events.
384863Spraks  *
394863Spraks  * It provides event notification in accordance with what an application
404863Spraks  * can find out by stat`ing the file and comparing time stamps. The various
414863Spraks  * system calls that update the file's access, modification, and change
424863Spraks  * time stamps are documented in the man page section 2.
434863Spraks  *
444863Spraks  * It is non intrusive. That is, having an active file event watch on a file
454863Spraks  * or directory will not prevent it from being removed or renamed or block an
464863Spraks  * unmount operation of the file system where the watched file or directory
474863Spraks  * resides.
484863Spraks  *
494863Spraks  *
504863Spraks  * Interface:
514863Spraks  * ----------
524863Spraks  *
534863Spraks  *   The object for this event source is of type 'struct file_obj *'
544863Spraks  *
554863Spraks  *   The file that needs to be monitored is specified in 'fo_name'.
564863Spraks  *   The time stamps collected by a stat(2) call are passed in fo_atime,
574863Spraks  *   fo_mtime, fo_ctime. At the time a file events watch is registered, the
584863Spraks  *   time stamps passed in are compared with the current time stamps of the
595331Samw  *   file. If it has changed, relevant events are sent immediately. If the time
604863Spraks  *   stamps are all '0', they will not be compared.
614863Spraks  *
624863Spraks  *
634863Spraks  * The events are delivered to an event port. A port is created using
644863Spraks  * port_create().
654863Spraks  *
664863Spraks  * To register a file events watch on a file or directory.
674863Spraks  *
684863Spraks  *   port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
694863Spraks  *
704863Spraks  *   'user' is the user pointer to be returned with the event.
714863Spraks  *
724863Spraks  * To de-register a file events watch,
734863Spraks  *
744863Spraks  *   port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
754863Spraks  *
764863Spraks  * The events are collected using the port_get()/port_getn() interface. The
774863Spraks  * event source will be PORT_SOURCE_FILE.
784863Spraks  *
794863Spraks  * After an event is delivered, the file events watch gets de-activated. To
804863Spraks  * receive the next event, the process will have to re-register the watch and
814863Spraks  * activate it by calling port_associate() again. This behavior is intentional
824863Spraks  * and supports proper multi threaded programming when using file events
834863Spraks  * notification API.
844863Spraks  *
854863Spraks  *
864863Spraks  * Implementation overview:
874863Spraks  * ------------------------
884863Spraks  *
894863Spraks  * Each file events watch is represented by 'portfop_t' in the kernel. A
904863Spraks  * cache(in portfop_cache_t) of these portfop_t's are maintained per event
914863Spraks  * port by this source. The object here is the pointer to the file_obj
924863Spraks  * structure. The portfop_t's are hashed in using the object pointer. Therefore
934863Spraks  * it is possible to have multiple file events watches on a file by the same
944863Spraks  * process by using different object structure(file_obj_t) and hence can
954863Spraks  * receive multiple event notification for a file. These watches can be for
964863Spraks  * different event types.
974863Spraks  *
984863Spraks  * The cached entries of these file objects are retained, even after delivering
994863Spraks  * an event, marking them inactive for performance reasons. The assumption
1004863Spraks  * is that the process would come back and re-register the file to receive
1014863Spraks  * further events. When there are more then 'port_fop_maxpfps' watches per file
1024863Spraks  * it will attempt to free the oldest inactive watches.
1034863Spraks  *
1044863Spraks  * In case the event that is being delivered is an exception event, the cached
1054863Spraks  * entries get removed. An exception event on a file or directory means its
1064863Spraks  * identity got changed(rename to/from, delete, mounted over, file system
1074863Spraks  * unmount).
1084863Spraks  *
1094863Spraks  * If the event port gets closed, all the associated file event watches will be
1104863Spraks  * removed and discarded.
1114863Spraks  *
1124863Spraks  *
1134863Spraks  * Data structures:
1144863Spraks  * ----------------
1154863Spraks  *
1164863Spraks  * The list of file event watches per file are managed by the data structure
1174863Spraks  * portfop_vp_t. The first time a file events watch is registered for a file,
1184863Spraks  * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
1194863Spraks  * removed and freed only when the vnode becomes inactive. The FEM hooks are
1204863Spraks  * also installed when the first watch is registered on a file. The FEM hooks
1214863Spraks  * get un-installed when all the watches are removed.
1224863Spraks  *
1234863Spraks  * Each file events watch is represented by the structure portfop_t. They
1244863Spraks  * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
1254863Spraks  * delivering an event, the portfop_t is marked inactive but retained. It is
1264863Spraks  * moved to the end of the list. All the active portfop_t's are maintained at
1274863Spraks  * the beginning. In case of exception events, the portfop_t will be removed
1284863Spraks  * and discarded.
1294863Spraks  *
1304863Spraks  * To intercept unmount operations, FSEM hooks are added to the file system
1314863Spraks  * under which files are being watched. A hash table('portfop_vfs_hash_t') of
1324863Spraks  * active file systems is maintained. Each file system that has active watches
1334863Spraks  * is represented by 'portfop_vfs_t' and is added to the hash table.
1344863Spraks  * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
1354863Spraks  * being watched on the portfop_vfs_t structure.
1364863Spraks  *
1374863Spraks  *
1384863Spraks  * File system support:
1394863Spraks  * -------------------
1404863Spraks  *
1414863Spraks  * The file system implementation has to provide vnode event notifications
1424863Spraks  * (vnevents) in order to support watching any files on that file system.
1434863Spraks  * The vnode events(vnevents) are notifications provided by the file system
1444863Spraks  * for name based file operations like rename, remove etc, which do not go
1454863Spraks  * thru the VOP_** interfaces. If the file system does not implement vnode
1464863Spraks  * notifications, watching for file events on such file systems is not
1474863Spraks  * supported. The vnode event notifications support is determined by the call
1484863Spraks  * vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
1494863Spraks  * has to implement.
1504863Spraks  *
1514863Spraks  *
1524863Spraks  * Locking order:
1534863Spraks  * --------------
1544863Spraks  *
1554863Spraks  * A file(vnode) can have file event watches registered by different processes.
1564863Spraks  * There is one portfop_t per watch registered. These are on the vnode's list
1574863Spraks  * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
1584863Spraks  * also on the per port cache. The cache is protected by the pfc_lock of
1594863Spraks  * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
1604863Spraks  *
1614863Spraks  */
1624863Spraks 
1634863Spraks #include <sys/types.h>
1644863Spraks #include <sys/systm.h>
1654863Spraks #include <sys/stat.h>
1664863Spraks #include <sys/errno.h>
1674863Spraks #include <sys/kmem.h>
1684863Spraks #include <sys/sysmacros.h>
1694863Spraks #include <sys/debug.h>
1704863Spraks #include <sys/vnode.h>
1714863Spraks #include <sys/poll_impl.h>
1724863Spraks #include <sys/port_impl.h>
1734863Spraks #include <sys/fem.h>
1744863Spraks #include <sys/vfs_opreg.h>
1754863Spraks #include <sys/atomic.h>
1767179Spraks #include <sys/mount.h>
1777179Spraks #include <sys/mntent.h>
1784863Spraks 
1794863Spraks /*
1804863Spraks  * For special case support of mnttab (/etc/mnttab).
1814863Spraks  */
1824863Spraks extern struct vnode *vfs_mntdummyvp;
1834863Spraks extern int mntfstype;
1844863Spraks 
1854863Spraks #define	PORTFOP_PVFSH(vfsp)	(&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
1864863Spraks portfop_vfs_hash_t	 portvfs_hash[PORTFOP_PVFSHASH_SZ];
1874863Spraks 
1886501Spraks #define	PORTFOP_NVP	20
1894863Spraks /*
1904863Spraks  * Inactive file event watches(portfop_t) are retained on the vnode's list
1914863Spraks  * for performance reason. If the applications re-registers the file, the
1924863Spraks  * inactive entry is made active and moved up the list.
1934863Spraks  *
1944863Spraks  * If there are greater then the following number of watches on a vnode,
1954863Spraks  * it will attempt to discard an oldest inactive watch(pfp) at the time
1966005Spraks  * a new watch is being registered and when events get delivered. We
1974863Spraks  * do this to avoid accumulating inactive watches on a file.
1984863Spraks  */
1994863Spraks int	port_fop_maxpfps = 20;
2004863Spraks 
2014863Spraks /* local functions */
2024863Spraks static int	port_fop_callback(void *, int *, pid_t, int, void *);
2034863Spraks 
2044863Spraks static void	port_pcache_insert(portfop_cache_t *, portfop_t *);
2054863Spraks static void	port_pcache_delete(portfop_cache_t *, portfop_t *);
2064863Spraks static void	port_close_fop(void *arg, int port, pid_t pid, int lastclose);
2074863Spraks 
2084863Spraks /*
2094863Spraks  * port fop functions that will be the fem hooks.
2104863Spraks  */
2115331Samw static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
2125331Samw     caller_context_t *);
2134863Spraks static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
2145331Samw     struct caller_context *ct);
2154863Spraks static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
2165331Samw     caller_context_t *ct);
2174863Spraks static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
2185331Samw     caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
2195331Samw     uint_t flags, cred_t *cr, caller_context_t *ct);
2204863Spraks static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
2215331Samw     caller_context_t *ct);
2224863Spraks static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
2235331Samw     vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
2245331Samw     caller_context_t *ct, vsecattr_t *vsecp);
2255331Samw static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
2265331Samw     caller_context_t *ct, int flags);
2275331Samw static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2285331Samw     caller_context_t *ct, int flags);
2294863Spraks static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
2305331Samw     cred_t *cr, caller_context_t *ct, int flags);
2314863Spraks static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
2325331Samw     vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
2335331Samw     vsecattr_t *vsecp);
2345331Samw static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2355331Samw     caller_context_t *ct, int flags);
2365331Samw static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2375331Samw     caller_context_t *ct, int flags);
2384863Spraks static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
2395331Samw     char *target, cred_t *cr, caller_context_t *ct, int flags);
2404863Spraks static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
2415331Samw     cred_t *cr, caller_context_t *ct);
2425331Samw 
2434863Spraks static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
2445331Samw     char *cname, caller_context_t *ct);
2454863Spraks 
2464863Spraks static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
2474863Spraks 
2484863Spraks 
2494863Spraks /*
2504863Spraks  * Fem hooks.
2514863Spraks  */
2524863Spraks const fs_operation_def_t	port_vnodesrc_template[] = {
2534863Spraks 	VOPNAME_OPEN,		{ .femop_open = port_fop_open },
2544863Spraks 	VOPNAME_READ,		{ .femop_read = port_fop_read },
2554863Spraks 	VOPNAME_WRITE,		{ .femop_write = port_fop_write },
2564863Spraks 	VOPNAME_MAP,		{ .femop_map = port_fop_map },
2574863Spraks 	VOPNAME_SETATTR, 	{ .femop_setattr = port_fop_setattr },
2584863Spraks 	VOPNAME_CREATE,		{ .femop_create = port_fop_create },
2594863Spraks 	VOPNAME_REMOVE,		{ .femop_remove = port_fop_remove },
2604863Spraks 	VOPNAME_LINK,		{ .femop_link = port_fop_link },
2614863Spraks 	VOPNAME_RENAME,		{ .femop_rename = port_fop_rename },
2624863Spraks 	VOPNAME_MKDIR,		{ .femop_mkdir = port_fop_mkdir },
2634863Spraks 	VOPNAME_RMDIR,		{ .femop_rmdir = port_fop_rmdir },
2644863Spraks 	VOPNAME_READDIR,	{ .femop_readdir = port_fop_readdir },
2654863Spraks 	VOPNAME_SYMLINK,	{ .femop_symlink = port_fop_symlink },
2664863Spraks 	VOPNAME_SETSECATTR, 	{ .femop_setsecattr = port_fop_setsecattr },
2674863Spraks 	VOPNAME_VNEVENT,	{ .femop_vnevent = port_fop_vnevent },
2684863Spraks 	NULL,	NULL
2694863Spraks };
2704863Spraks 
2714863Spraks /*
2724863Spraks  * Fsem - vfs ops hooks
2734863Spraks  */
2744863Spraks const fs_operation_def_t	port_vfssrc_template[] = {
2754863Spraks 	VFSNAME_UNMOUNT, 	{ .fsemop_unmount = port_fop_unmount },
2764863Spraks 	NULL,	NULL
2774863Spraks };
2784863Spraks 
2794863Spraks fem_t *fop_femop;
2804863Spraks fsem_t *fop_fsemop;
2814863Spraks 
2824863Spraks static fem_t *
port_fop_femop()2834863Spraks port_fop_femop()
2844863Spraks {
2854863Spraks 	fem_t *femp;
2864863Spraks 	if (fop_femop != NULL)
2874863Spraks 		return (fop_femop);
2884863Spraks 	if (fem_create("portfop_fem",
2894863Spraks 	    (const struct fs_operation_def *)port_vnodesrc_template,
2904863Spraks 	    (fem_t **)&femp)) {
2914863Spraks 		return (NULL);
2924863Spraks 	}
2934863Spraks 	if (casptr(&fop_femop, NULL, femp) != NULL) {
2944863Spraks 		/*
2954863Spraks 		 * some other thread beat us to it.
2964863Spraks 		 */
2974863Spraks 		fem_free(femp);
2984863Spraks 	}
2994863Spraks 	return (fop_femop);
3004863Spraks }
3014863Spraks 
3024863Spraks static fsem_t *
port_fop_fsemop()3034863Spraks port_fop_fsemop()
3044863Spraks {
3054863Spraks 	fsem_t *fsemp;
3064863Spraks 	if (fop_fsemop != NULL)
3074863Spraks 		return (fop_fsemop);
3084863Spraks 	if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
3094863Spraks 		return (NULL);
3104863Spraks 	}
3114863Spraks 	if (casptr(&fop_fsemop, NULL, fsemp) != NULL) {
3124863Spraks 		/*
3134863Spraks 		 * some other thread beat us to it.
3144863Spraks 		 */
3154863Spraks 		fsem_free(fsemp);
3164863Spraks 	}
3174863Spraks 	return (fop_fsemop);
3184863Spraks }
3194863Spraks 
3204863Spraks /*
3214863Spraks  * port_fop_callback()
3224863Spraks  * - PORT_CALLBACK_DEFAULT
3234863Spraks  *	The file event will be delivered to the application.
3244863Spraks  * - PORT_CALLBACK_DISSOCIATE
3254863Spraks  *	The object will be dissociated from  the port.
3264863Spraks  * - PORT_CALLBACK_CLOSE
3274863Spraks  *	The object will be dissociated from the port because the port
3284863Spraks  *	is being closed.
3294863Spraks  */
3304863Spraks /* ARGSUSED */
3314863Spraks static int
port_fop_callback(void * arg,int * events,pid_t pid,int flag,void * evp)3324863Spraks port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
3334863Spraks {
3344863Spraks 	portfop_t	*pfp = (portfop_t *)arg;
3354863Spraks 	port_kevent_t	*pkevp = (port_kevent_t *)evp;
3364863Spraks 	int		error = 0;
3374863Spraks 
3384863Spraks 	ASSERT((events != NULL));
3394863Spraks 	if (flag == PORT_CALLBACK_DEFAULT) {
3404863Spraks 		if (curproc->p_pid != pid) {
3414863Spraks 				return (EACCES); /* deny delivery of events */
3424863Spraks 		}
3434863Spraks 
3444863Spraks 		*events = pkevp->portkev_events;
3454863Spraks 		pkevp->portkev_events = 0;
3464863Spraks 		if (pfp != NULL) {
3474863Spraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
3484863Spraks 		}
3494863Spraks 	}
3504863Spraks 	return (error);
3514863Spraks }
3524863Spraks 
3534863Spraks /*
3544863Spraks  * Inserts a portfop_t into the port sources cache's.
3554863Spraks  */
3564863Spraks static void
port_pcache_insert(portfop_cache_t * pfcp,portfop_t * pfp)3574863Spraks port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
3584863Spraks {
3594863Spraks 	portfop_t	**bucket;
3604863Spraks 
3614863Spraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
3624863Spraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
3634863Spraks 	pfp->pfop_hashnext = *bucket;
3644863Spraks 	*bucket = pfp;
3654863Spraks 	pfcp->pfc_objcount++;
3664863Spraks }
3674863Spraks 
3684863Spraks /*
3694863Spraks  * Remove the pfp from the port source cache.
3704863Spraks  */
3714863Spraks static void
port_pcache_delete(portfop_cache_t * pfcp,portfop_t * pfp)3724863Spraks port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
3734863Spraks {
3744863Spraks 	portfop_t	*lpdp;
3754863Spraks 	portfop_t	*cpdp;
3764863Spraks 	portfop_t	**bucket;
3774863Spraks 
3784863Spraks 	bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
3794863Spraks 	cpdp = *bucket;
3804863Spraks 	if (pfp == cpdp) {
3814863Spraks 		*bucket = pfp->pfop_hashnext;
3824863Spraks 	} else {
3834863Spraks 		while (cpdp != NULL) {
3844863Spraks 			lpdp = cpdp;
3854863Spraks 			cpdp = cpdp->pfop_hashnext;
3864863Spraks 			if (cpdp == pfp) {
3874863Spraks 				/* portfop struct found */
3884863Spraks 				lpdp->pfop_hashnext = pfp->pfop_hashnext;
3894863Spraks 				break;
3904863Spraks 			}
3914863Spraks 		}
3924863Spraks 	}
3934863Spraks 	pfcp->pfc_objcount--;
3944863Spraks }
3954863Spraks 
3964863Spraks /*
3974863Spraks  * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
3984863Spraks  * when these routines are called.
3994863Spraks  *
4004863Spraks  * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
4014863Spraks  * It is used to discard the oldtest inactive pfp if the number of entries
4024863Spraks  * exceed the limit.
4034863Spraks  */
4044863Spraks static void
port_fop_listinsert(portfop_vp_t * pvp,portfop_t * pfp,int where)4054863Spraks port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
4064863Spraks {
4074863Spraks 	if (where == 1) {
4084863Spraks 		list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
4094863Spraks 	} else {
4104863Spraks 		list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
4114863Spraks 	}
4124863Spraks 	if (pvp->pvp_lpfop == NULL) {
4134863Spraks 		pvp->pvp_lpfop = pfp;
4144863Spraks 	}
4154863Spraks 	pvp->pvp_cnt++;
4164863Spraks }
4174863Spraks 
4184863Spraks static void
port_fop_listinsert_head(portfop_vp_t * pvp,portfop_t * pfp)4194863Spraks port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
4204863Spraks {
4214863Spraks 	port_fop_listinsert(pvp, pfp, 1);
4224863Spraks }
4234863Spraks 
4244863Spraks static void
port_fop_listinsert_tail(portfop_vp_t * pvp,portfop_t * pfp)4254863Spraks port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
4264863Spraks {
4274863Spraks 	/*
4284863Spraks 	 * We point lpfop to an inactive one, if it was initially pointing
4294863Spraks 	 * to an active one. Insert to the tail is done only when a pfp goes
4304863Spraks 	 * inactive.
4314863Spraks 	 */
4324863Spraks 	if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
4334863Spraks 		pvp->pvp_lpfop = pfp;
4344863Spraks 	}
4354863Spraks 	port_fop_listinsert(pvp, pfp, 0);
4364863Spraks }
4374863Spraks 
4384863Spraks static void
port_fop_listremove(portfop_vp_t * pvp,portfop_t * pfp)4394863Spraks port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
4404863Spraks {
4414863Spraks 	if (pvp->pvp_lpfop == pfp) {
4424863Spraks 		pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
4434863Spraks 	}
4444863Spraks 
4454863Spraks 	list_remove(&pvp->pvp_pfoplist, (void *)pfp);
4464863Spraks 
4474863Spraks 	pvp->pvp_cnt--;
4484863Spraks 	if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
4494863Spraks 		pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
4504863Spraks 	}
4514863Spraks }
4524863Spraks 
4534863Spraks static void
port_fop_listmove(portfop_vp_t * pvp,list_t * tlist)4544863Spraks port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
4554863Spraks {
4564863Spraks 	list_move_tail(tlist, &pvp->pvp_pfoplist);
4574863Spraks 	pvp->pvp_lpfop = NULL;
4584863Spraks 	pvp->pvp_cnt = 0;
4594863Spraks }
4604863Spraks 
4614863Spraks /*
4624863Spraks  * Remove a portfop_t from the port cache hash table and discard it.
4634863Spraks  * It is called only when pfp is not on the vnode's list. Otherwise,
4644863Spraks  * port_remove_fop() is called.
4654863Spraks  */
4664863Spraks void
port_pcache_remove_fop(portfop_cache_t * pfcp,portfop_t * pfp)4674863Spraks port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
4684863Spraks {
4694863Spraks 	port_kevent_t	*pkevp;
4704863Spraks 
4714863Spraks 
4724863Spraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
4734863Spraks 
4744863Spraks 	pkevp = pfp->pfop_pev;
4754863Spraks 	pfp->pfop_pev = NULL;
4764863Spraks 
4774863Spraks 	if (pkevp != NULL) {
4784863Spraks 		(void) port_remove_done_event(pkevp);
4794863Spraks 		port_free_event_local(pkevp, 0);
4804863Spraks 	}
4814863Spraks 
4824863Spraks 	port_pcache_delete(pfcp, pfp);
4834863Spraks 
4844863Spraks 	if (pfp->pfop_cname != NULL)
4854863Spraks 		kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
4864863Spraks 	kmem_free(pfp, sizeof (portfop_t));
4874863Spraks 	if (pfcp->pfc_objcount == 0)
4884863Spraks 		cv_signal(&pfcp->pfc_lclosecv);
4894863Spraks }
4904863Spraks 
4914863Spraks /*
4924863Spraks  * if we have too many watches on the vnode, attempt to discard an
4934863Spraks  * inactive one.
4944863Spraks  */
4954863Spraks static void
port_fop_trimpfplist(vnode_t * vp)4964863Spraks port_fop_trimpfplist(vnode_t *vp)
4974863Spraks {
4984863Spraks 	portfop_vp_t *pvp;
4994863Spraks 	portfop_t *pfp = NULL;
5004863Spraks 	portfop_cache_t *pfcp;
5016501Spraks 	vnode_t	*tdvp;
5024863Spraks 
5034863Spraks 	/*
5044863Spraks 	 * Due to a reference the vnode cannot disappear, v_fopdata should
5054863Spraks 	 * not change.
5064863Spraks 	 */
5074863Spraks 	if ((pvp = vp->v_fopdata) != NULL &&
5084863Spraks 	    pvp->pvp_cnt > port_fop_maxpfps) {
5094863Spraks 		mutex_enter(&pvp->pvp_mutex);
5104863Spraks 		pfp = pvp->pvp_lpfop;
5114863Spraks 		pfcp = pfp->pfop_pcache;
5124863Spraks 		/*
5134863Spraks 		 * only if we can get the cache lock, we need to
5144863Spraks 		 * do this due to reverse lock order and some thread
5154863Spraks 		 * that may be trying to reactivate this entry.
5164863Spraks 		 */
5174863Spraks 		if (mutex_tryenter(&pfcp->pfc_lock)) {
5184863Spraks 			if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
5194863Spraks 			    !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
5204863Spraks 				port_fop_listremove(pvp, pfp);
5214863Spraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
5224863Spraks 			} else {
5234863Spraks 				mutex_exit(&pfcp->pfc_lock);
5244863Spraks 				pfp = NULL;
5254863Spraks 			}
5264863Spraks 		} else {
5274863Spraks 			pfp = NULL;
5284863Spraks 		}
5294863Spraks 		mutex_exit(&pvp->pvp_mutex);
5304863Spraks 
5314863Spraks 		/*
5324863Spraks 		 * discard pfp if any.
5334863Spraks 		 */
5344863Spraks 		if (pfp != NULL) {
5356501Spraks 			tdvp = pfp->pfop_dvp;
5364863Spraks 			port_pcache_remove_fop(pfcp, pfp);
5374863Spraks 			mutex_exit(&pfcp->pfc_lock);
5386501Spraks 			if (tdvp != NULL)
5396501Spraks 				VN_RELE(tdvp);
5404863Spraks 		}
5414863Spraks 	}
5424863Spraks }
5434863Spraks 
5446501Spraks /*
5456501Spraks  * This routine returns 1, if the vnode can be rele'ed by the caller.
5466501Spraks  * The caller has to VN_RELE the vnode with out holding any
5476501Spraks  * locks.
5486501Spraks  */
5496501Spraks int
port_fop_femuninstall(vnode_t * vp)5504863Spraks port_fop_femuninstall(vnode_t *vp)
5514863Spraks {
5524863Spraks 	portfop_vp_t	*pvp;
5534863Spraks 	vfs_t		*vfsp;
5544863Spraks 	portfop_vfs_t *pvfsp;
5554863Spraks 	portfop_vfs_hash_t	*pvfsh;
5564863Spraks 	kmutex_t	*mtx;
5576501Spraks 	int	ret = 0;
5584863Spraks 
5594863Spraks 	/*
5604863Spraks 	 * if list is empty, uninstall fem.
5614863Spraks 	 */
5624863Spraks 	pvp = vp->v_fopdata;
5634863Spraks 	ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
5644863Spraks 
5654863Spraks 	/*
5664863Spraks 	 * make sure the list is empty.
5674863Spraks 	 */
5684863Spraks 	if (!list_head(&pvp->pvp_pfoplist)) {
5694863Spraks 
5704863Spraks 		/*
5714863Spraks 		 * we could possibly uninstall the fem hooks when
5724863Spraks 		 * the vnode becomes inactive and the v_fopdata is
5736005Spraks 		 * free. But the hooks get triggered unnecessarily
5744863Spraks 		 * even though there are no active watches. So, we
5754863Spraks 		 * uninstall it here.
5764863Spraks 		 */
5774863Spraks 		(void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
5784863Spraks 		pvp->pvp_femp = NULL;
5794863Spraks 		mutex_exit(&pvp->pvp_mutex);
5804863Spraks 
5814863Spraks 
5824863Spraks 		/*
5836005Spraks 		 * If we successfully uninstalled fem, no process is watching
5846005Spraks 		 * this vnode, Remove it from the vfs's list of watched vnodes.
5854863Spraks 		 */
5864863Spraks 		pvfsp = pvp->pvp_pvfsp;
5874863Spraks 		vfsp = vp->v_vfsp;
5884863Spraks 		pvfsh = PORTFOP_PVFSH(vfsp);
5894863Spraks 		mtx = &pvfsh->pvfshash_mutex;
5904863Spraks 		mutex_enter(mtx);
5914863Spraks 		/*
5924863Spraks 		 * If unmount is in progress, that thread will remove and
5934863Spraks 		 * release the vnode from the vfs's list, just leave.
5944863Spraks 		 */
5954863Spraks 		if (!pvfsp->pvfs_unmount) {
5964863Spraks 			list_remove(&pvfsp->pvfs_pvplist, pvp);
5974863Spraks 			mutex_exit(mtx);
5986501Spraks 			ret = 1;
5994863Spraks 		} else {
6004863Spraks 			mutex_exit(mtx);
6014863Spraks 		}
6024863Spraks 	} else {
6034863Spraks 		mutex_exit(&pvp->pvp_mutex);
6044863Spraks 	}
6056501Spraks 	return (ret);
6064863Spraks }
6074863Spraks 
6084863Spraks /*
6094863Spraks  * Remove pfp from the vnode's watch list and the cache and discard it.
6104863Spraks  * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
6116501Spraks  * Returns 1 if pfp removed successfully.
6124863Spraks  *
6134863Spraks  * The *active is set to indicate if the pfp was still active(no events had
6144863Spraks  * been posted, or the posted event had not been collected yet and it was
6154863Spraks  * able to remove it from the port's queue).
6166501Spraks  *
6176501Spraks  * vpp and dvpp will point to the vnode and directory vnode which the caller
6186501Spraks  * is required to VN_RELE without holding any locks.
6194863Spraks  */
6204863Spraks int
port_remove_fop(portfop_t * pfp,portfop_cache_t * pfcp,int cleanup,int * active,vnode_t ** vpp,vnode_t ** dvpp)6214863Spraks port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
6226501Spraks     int *active, vnode_t **vpp, vnode_t **dvpp)
6234863Spraks {
6244863Spraks 	vnode_t		*vp;
6254863Spraks 	portfop_vp_t	*pvp;
6264863Spraks 	int	tactive = 0;
6274863Spraks 
6284863Spraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
6294863Spraks 	vp = pfp->pfop_vp;
6304863Spraks 	pvp = vp->v_fopdata;
6314863Spraks 	mutex_enter(&pvp->pvp_mutex);
6324863Spraks 
6334863Spraks 	/*
6344863Spraks 	 * if not cleanup, remove it only if the pfp is still active and
6354863Spraks 	 * is not being removed by some other thread.
6364863Spraks 	 */
6374863Spraks 	if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
6384863Spraks 	    pfp->pfop_flags & PORT_FOP_REMOVING)) {
6394863Spraks 		mutex_exit(&pvp->pvp_mutex);
6404863Spraks 		return (0);
6414863Spraks 	}
6424863Spraks 
6434863Spraks 	/*
6444863Spraks 	 * mark it inactive.
6454863Spraks 	 */
6464863Spraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
6474863Spraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
6484863Spraks 		tactive = 1;
6494863Spraks 	}
6504863Spraks 
6514863Spraks 	/*
6524863Spraks 	 * Check if the pfp is still on the vnode's list. This can
6534863Spraks 	 * happen if port_fop_excep() is in the process of removing it.
6544863Spraks 	 * In case of cleanup, just mark this pfp as inactive so that no
6554863Spraks 	 * new events (VNEVENT) will be delivered, and remove it from the
6564863Spraks 	 * event queue if it was already queued. Since the cache lock is
6574863Spraks 	 * held, the pfp will not disappear, even though it is being
6584863Spraks 	 * removed.
6594863Spraks 	 */
6604863Spraks 	if (pfp->pfop_flags & PORT_FOP_REMOVING) {
6614863Spraks 		mutex_exit(&pvp->pvp_mutex);
6624863Spraks 		if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
6634863Spraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
6644863Spraks 			tactive = 1;
6654863Spraks 		}
6664863Spraks 		if (active) {
6674863Spraks 			*active = tactive;
6684863Spraks 		}
6694863Spraks 		return (1);
6704863Spraks 	}
6714863Spraks 
6724863Spraks 	/*
6734863Spraks 	 * if we find an event on the queue and removed it, then this
6744863Spraks 	 * association is considered active.
6754863Spraks 	 */
6764863Spraks 	if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
6774863Spraks 		pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
6784863Spraks 		tactive = 1;
6794863Spraks 	}
6804863Spraks 
6814863Spraks 	if (active) {
6824863Spraks 		*active = tactive;
6834863Spraks 	}
6844863Spraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
6854863Spraks 
6864863Spraks 	/*
6874863Spraks 	 * remove pfp from the vnode's list
6884863Spraks 	 */
6894863Spraks 	port_fop_listremove(pvp, pfp);
6904863Spraks 
6914863Spraks 	/*
6924863Spraks 	 * If no more associations on the vnode, uninstall fem hooks.
6934863Spraks 	 * The pvp mutex will be released in this routine.
6944863Spraks 	 */
6956501Spraks 	if (port_fop_femuninstall(vp))
6966501Spraks 		*vpp = vp;
6976501Spraks 	*dvpp = pfp->pfop_dvp;
6984863Spraks 	port_pcache_remove_fop(pfcp, pfp);
6994863Spraks 	return (1);
7004863Spraks }
7014863Spraks 
7024863Spraks /*
7034863Spraks  * This routine returns a pointer to a cached portfop entry, or NULL if it
7044863Spraks  * does not find it in the hash table. The object pointer is used as index.
7054863Spraks  * The entries are hashed by the object's address. We need to match the pid
7064863Spraks  * as the evet port can be shared between processes. The file events
7074863Spraks  * watches are per process only.
7084863Spraks  */
7094863Spraks portfop_t *
port_cache_lookup_fop(portfop_cache_t * pfcp,pid_t pid,uintptr_t obj)7104863Spraks port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
7114863Spraks {
7124863Spraks 	portfop_t	*pfp = NULL;
7134863Spraks 	portfop_t	**bucket;
7144863Spraks 
7154863Spraks 	ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
7164863Spraks 	bucket = PORT_FOP_BUCKET(pfcp, obj);
7174863Spraks 	pfp = *bucket;
7184863Spraks 	while (pfp != NULL) {
7194863Spraks 		if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
7204863Spraks 			break;
7214863Spraks 		pfp = pfp->pfop_hashnext;
7224863Spraks 	}
7234863Spraks 	return (pfp);
7244863Spraks }
7254863Spraks 
7264863Spraks /*
7274863Spraks  * Given the file name, get the vnode and also the directory vnode
7284863Spraks  * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
7294863Spraks  * the vnode(s).
7304863Spraks  */
7314863Spraks int
port_fop_getdvp(void * objptr,vnode_t ** vp,vnode_t ** dvp,char ** cname,int * len,int follow)7324863Spraks port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp,
7334863Spraks 	char **cname, int *len, int follow)
7344863Spraks {
7354863Spraks 	int error = 0;
7364863Spraks 	struct pathname pn;
7374863Spraks 	char *fname;
7384863Spraks 
7394863Spraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
7404863Spraks 		fname = ((file_obj_t *)objptr)->fo_name;
7414863Spraks #ifdef  _SYSCALL32_IMPL
7424863Spraks 	} else {
7434863Spraks 		fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
7444863Spraks #endif	/* _SYSCALL32_IMPL */
7454863Spraks 	}
7464863Spraks 
7474863Spraks 	/*
7484863Spraks 	 * lookuppn may fail with EINVAL, if dvp is  non-null(like when
7494863Spraks 	 * looking for "."). So call again with dvp = NULL.
7504863Spraks 	 */
7514863Spraks 	if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
7524863Spraks 		return (error);
7534863Spraks 	}
7544863Spraks 
7554863Spraks 	error = lookuppn(&pn, NULL, follow, dvp, vp);
7564863Spraks 	if (error == EINVAL) {
7574863Spraks 		pn_free(&pn);
7584863Spraks 		if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
7594863Spraks 			return (error);
7604863Spraks 		}
7614863Spraks 		error = lookuppn(&pn, NULL, follow, NULL, vp);
7624863Spraks 		if (dvp != NULL) {
7634863Spraks 			*dvp = NULL;
7644863Spraks 		}
7654863Spraks 	}
7664863Spraks 
7674863Spraks 	if (error == 0 && cname != NULL && len != NULL) {
7684863Spraks 		pn_setlast(&pn);
7694863Spraks 		*len = pn.pn_pathlen;
7704863Spraks 		*cname = kmem_alloc(*len + 1, KM_SLEEP);
7714863Spraks 		(void) strcpy(*cname, pn.pn_path);
7724863Spraks 	} else {
7734863Spraks 		if (cname != NULL && len != NULL) {
7744863Spraks 			*cname = NULL;
7754863Spraks 			*len = 0;
7764863Spraks 		}
7774863Spraks 	}
7784863Spraks 
7794863Spraks 	pn_free(&pn);
7804863Spraks 	return (error);
7814863Spraks }
7824863Spraks 
7834863Spraks port_source_t *
port_getsrc(port_t * pp,int source)7844863Spraks port_getsrc(port_t *pp, int source)
7854863Spraks {
7864863Spraks 	port_source_t *pse;
7874863Spraks 	int	lock = 0;
7884863Spraks 	/*
7894863Spraks 	 * get the port source structure.
7904863Spraks 	 */
7914863Spraks 	if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
7924863Spraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
7934863Spraks 		lock = 1;
7944863Spraks 	}
7954863Spraks 
7964863Spraks 	pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
7974863Spraks 	for (; pse != NULL; pse = pse->portsrc_next) {
7984863Spraks 		if (pse->portsrc_source == source)
7994863Spraks 			break;
8004863Spraks 	}
8014863Spraks 
8024863Spraks 	if (lock) {
8034863Spraks 		mutex_exit(&pp->port_queue.portq_source_mutex);
8044863Spraks 	}
8054863Spraks 	return (pse);
8064863Spraks }
8074863Spraks 
8084863Spraks 
8094863Spraks /*
8106005Spraks  * Compare time stamps and generate an event if it has changed.
8116005Spraks  * Note that the port cache pointer will be valid due to a reference
8126005Spraks  * to the port. We need to grab the port cache lock and verify that
8136005Spraks  * the pfp is still the same before proceeding to deliver an event.
8144863Spraks  */
8154863Spraks static void
port_check_timestamp(portfop_cache_t * pfcp,vnode_t * vp,vnode_t * dvp,portfop_t * pfp,void * objptr,uintptr_t object)8166005Spraks port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
8176005Spraks 	portfop_t *pfp, void *objptr, uintptr_t object)
8184863Spraks {
8194863Spraks 	vattr_t		vatt;
8204863Spraks 	portfop_vp_t	*pvp = vp->v_fopdata;
8214863Spraks 	int		events = 0;
8224863Spraks 	port_kevent_t	*pkevp;
8234863Spraks 	file_obj_t	*fobj;
8246005Spraks 	portfop_t	*tpfp;
8254863Spraks 
8264863Spraks 	/*
8276005Spraks 	 * If time stamps are specified, get attributes and compare.
8284863Spraks 	 */
8294863Spraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
8304863Spraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
8314863Spraks 		fobj = (file_obj_t *)objptr;
8324863Spraks 		if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
8334863Spraks 		    fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
8344863Spraks 		    fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
8355331Samw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
8364863Spraks 				return;
8374863Spraks 			}
8384863Spraks 		} else {
8394863Spraks 			/*
8404863Spraks 			 * timestamp not specified, all 0's,
8414863Spraks 			 */
8424863Spraks 			return;
8434863Spraks 		}
8444863Spraks #ifdef  _SYSCALL32_IMPL
8454863Spraks 	} else {
8464863Spraks 		file_obj32_t	*fobj32;
8474863Spraks 		fobj32 = (file_obj32_t *)objptr;
8484863Spraks 		if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
8494863Spraks 		    fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
8504863Spraks 		    fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
8515331Samw 			if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
8524863Spraks 				return;
8534863Spraks 			}
8544863Spraks 		} else {
8554863Spraks 			/*
8564863Spraks 			 * timestamp not specified, all 0.
8574863Spraks 			 */
8584863Spraks 			return;
8594863Spraks 		}
8604863Spraks #endif /* _SYSCALL32_IMPL */
8614863Spraks 	}
8624863Spraks 
8636005Spraks 	/*
8646005Spraks 	 * Now grab the cache lock and verify that we are still
8656005Spraks 	 * dealing with the same pfp and curthread is the one
8666005Spraks 	 * which registered it. We need to do this to avoid
8676005Spraks 	 * delivering redundant events.
8686005Spraks 	 */
8696005Spraks 	mutex_enter(&pfcp->pfc_lock);
8706005Spraks 	tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
8716005Spraks 
8726005Spraks 	if (tpfp == NULL || tpfp != pfp ||
8736005Spraks 	    pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
8746005Spraks 	    pfp->pfop_callrid != curthread ||
8756005Spraks 	    !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
8766005Spraks 		/*
8776005Spraks 		 * Some other event was delivered, the file
8786005Spraks 		 * watch was removed or reassociated. Just
8796005Spraks 		 * ignore it and leave
8806005Spraks 		 */
8816005Spraks 		mutex_exit(&pfcp->pfc_lock);
8826005Spraks 		return;
8836005Spraks 	}
8846005Spraks 
8854863Spraks 	mutex_enter(&pvp->pvp_mutex);
8864863Spraks 	/*
8876005Spraks 	 * The pfp cannot disappear as the port cache lock is held.
8884863Spraks 	 * While the pvp_mutex is held, no events will get delivered.
8894863Spraks 	 */
8904863Spraks 	if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
8914863Spraks 	    !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
8924863Spraks 		if (get_udatamodel() == DATAMODEL_NATIVE) {
8934863Spraks 			fobj = (file_obj_t *)objptr;
8944863Spraks 			if (pfp->pfop_events & FILE_ACCESS &&
8954863Spraks 			    (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
8964863Spraks 			    (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
8974863Spraks 			    vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
8984863Spraks 				events |= FILE_ACCESS;
8994863Spraks 
9004863Spraks 			if (pfp->pfop_events & FILE_MODIFIED &&
9014863Spraks 			    (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
9024863Spraks 			    (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
9034863Spraks 			    vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
9044863Spraks 				events |= FILE_MODIFIED;
9054863Spraks 
9064863Spraks 			if (pfp->pfop_events & FILE_ATTRIB &&
9074863Spraks 			    (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
9084863Spraks 			    (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
9094863Spraks 			    vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
9104863Spraks 				events |= FILE_ATTRIB;
9114863Spraks #ifdef  _SYSCALL32_IMPL
9124863Spraks 		} else {
9134863Spraks 			file_obj32_t	*fobj32;
9144863Spraks 			fobj32 = (file_obj32_t *)objptr;
9154863Spraks 			if (pfp->pfop_events & FILE_ACCESS &&
9164863Spraks 			    (fobj32->fo_atime.tv_sec ||
9174863Spraks 			    fobj32->fo_atime.tv_nsec) &&
9184863Spraks 			    (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
9194863Spraks 			    vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
9204863Spraks 				events |= FILE_ACCESS;
9214863Spraks 
9224863Spraks 			if (pfp->pfop_events & FILE_MODIFIED &&
9234863Spraks 			    (fobj32->fo_mtime.tv_sec ||
9244863Spraks 			    fobj32->fo_mtime.tv_nsec) &&
9254863Spraks 			    (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
9264863Spraks 			    vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
9274863Spraks 				events |= FILE_MODIFIED;
9284863Spraks 
9294863Spraks 			if (pfp->pfop_events & FILE_ATTRIB &&
9304863Spraks 			    (fobj32->fo_ctime.tv_sec ||
9314863Spraks 			    fobj32->fo_ctime.tv_nsec) &&
9324863Spraks 			    (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
9334863Spraks 			    vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
9344863Spraks 				events |= FILE_ATTRIB;
9354863Spraks #endif /* _SYSCALL32_IMPL */
9364863Spraks 		}
9374863Spraks 
9384863Spraks 		/*
9394863Spraks 		 * No events to deliver
9404863Spraks 		 */
9414863Spraks 		if (events == 0) {
9424863Spraks 			mutex_exit(&pvp->pvp_mutex);
9436005Spraks 			mutex_exit(&pfcp->pfc_lock);
9444863Spraks 			return;
9454863Spraks 		}
9464863Spraks 
9474863Spraks 		/*
9484863Spraks 		 * Deliver the event now.
9494863Spraks 		 */
9504863Spraks 		pkevp = pfp->pfop_pev;
9514863Spraks 		pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
9524863Spraks 		pkevp->portkev_events |= events;
9534863Spraks 		/*
9544863Spraks 		 * Move it to the tail as active once are in the
9556005Spraks 		 * beginning of the list.
9564863Spraks 		 */
9574863Spraks 		port_fop_listremove(pvp, pfp);
9584863Spraks 		port_fop_listinsert_tail(pvp, pfp);
9594863Spraks 		port_send_event(pkevp);
9604863Spraks 		pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
9614863Spraks 	}
9624863Spraks 	mutex_exit(&pvp->pvp_mutex);
9636005Spraks 	mutex_exit(&pfcp->pfc_lock);
9644863Spraks }
9654863Spraks 
9664863Spraks /*
9674863Spraks  * Add the event source to the port and return the port source cache pointer.
9684863Spraks  */
9694863Spraks int
port_fop_associate_source(portfop_cache_t ** pfcpp,port_t * pp,int source)9704863Spraks port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
9714863Spraks {
9724863Spraks 	portfop_cache_t *pfcp;
9734863Spraks 	port_source_t	*pse;
9744863Spraks 	int		error;
9754863Spraks 
9764863Spraks 	/*
9774863Spraks 	 * associate PORT_SOURCE_FILE source with the port, if it is
9784863Spraks 	 * not associated yet. Note the PORT_SOURCE_FILE source is
9794863Spraks 	 * associated once and will not be dissociated.
9804863Spraks 	 */
9814863Spraks 	if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
9824863Spraks 		if (error = port_associate_ksource(pp->port_fd, source,
9834863Spraks 		    &pse, port_close_fop, pp, NULL)) {
9844863Spraks 			*pfcpp = NULL;
9854863Spraks 			return (error);
9864863Spraks 		}
9874863Spraks 	}
9884863Spraks 
9894863Spraks 	/*
9904863Spraks 	 * Get the portfop cache pointer.
9914863Spraks 	 */
9924863Spraks 	if ((pfcp = pse->portsrc_data) == NULL) {
9934863Spraks 		/*
9944863Spraks 		 * This is the first time that a file is being associated,
9954863Spraks 		 * create the portfop cache.
9964863Spraks 		 */
9974863Spraks 		pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
9984863Spraks 		mutex_enter(&pp->port_queue.portq_source_mutex);
9994863Spraks 		if (pse->portsrc_data == NULL) {
10004863Spraks 			pse->portsrc_data = pfcp;
10014863Spraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
10024863Spraks 		} else {
10034863Spraks 			/*
10044863Spraks 			 * someone else created the port cache, free
10054863Spraks 			 * what we just now allocated.
10064863Spraks 			 */
10074863Spraks 			mutex_exit(&pp->port_queue.portq_source_mutex);
10084863Spraks 			kmem_free(pfcp, sizeof (portfop_cache_t));
10094863Spraks 			pfcp = pse->portsrc_data;
10104863Spraks 		}
10114863Spraks 	}
10124863Spraks 	*pfcpp = pfcp;
10134863Spraks 	return (0);
10144863Spraks }
10154863Spraks 
10164863Spraks /*
10174863Spraks  * Add the given pvp on the file system's list of vnodes watched.
10184863Spraks  */
10194863Spraks int
port_fop_pvfsadd(portfop_vp_t * pvp)10204863Spraks port_fop_pvfsadd(portfop_vp_t *pvp)
10214863Spraks {
10224863Spraks 	int error = 0;
10234863Spraks 	vnode_t	*vp = pvp->pvp_vp;
10244863Spraks 	portfop_vfs_hash_t *pvfsh;
10254863Spraks 	portfop_vfs_t	 *pvfsp;
10264863Spraks 	fsem_t		*fsemp;
10274863Spraks 
10284863Spraks 	pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
10294863Spraks 	mutex_enter(&pvfsh->pvfshash_mutex);
10304863Spraks 	for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
10314863Spraks 	    pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
10324863Spraks 		;
10334863Spraks 
10344863Spraks 	if (!pvfsp) {
10354863Spraks 		if ((fsemp = port_fop_fsemop()) != NULL) {
10364863Spraks 			if ((error = fsem_install(vp->v_vfsp, fsemp,
10374863Spraks 			    vp->v_vfsp, OPUNIQ, NULL, NULL))) {
10384863Spraks 				mutex_exit(&pvfsh->pvfshash_mutex);
10394863Spraks 				return (error);
10404863Spraks 			}
10414863Spraks 		} else {
10424863Spraks 			mutex_exit(&pvfsh->pvfshash_mutex);
10434863Spraks 			return (EINVAL);
10444863Spraks 		}
10454863Spraks 		pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
10464863Spraks 		pvfsp->pvfs = vp->v_vfsp;
10474863Spraks 		list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
10484863Spraks 		    offsetof(portfop_vp_t, pvp_pvfsnode));
10494863Spraks 		pvfsp->pvfs_fsemp = fsemp;
10504863Spraks 		pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
10514863Spraks 		pvfsh->pvfshash_pvfsp = pvfsp;
10524863Spraks 	}
10534863Spraks 
10544863Spraks 	/*
10554863Spraks 	 * check if an unmount is in progress.
10564863Spraks 	 */
10574863Spraks 	if (!pvfsp->pvfs_unmount) {
10584863Spraks 		/*
10594863Spraks 		 * insert the pvp on list.
10604863Spraks 		 */
10614863Spraks 		pvp->pvp_pvfsp = pvfsp;
10624863Spraks 		list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
10634863Spraks 	} else {
10644863Spraks 		error = EINVAL;
10654863Spraks 	}
10664863Spraks 	mutex_exit(&pvfsh->pvfshash_mutex);
10674863Spraks 	return (error);
10684863Spraks }
10694863Spraks 
10704863Spraks /*
10714863Spraks  * Installs the portfop_vp_t data structure on the
10724863Spraks  * vnode. The 'pvp_femp == NULL' indicates it is not
10734863Spraks  * active. The fem hooks have to be installed.
10744863Spraks  * The portfop_vp_t is only freed when the vnode gets freed.
10754863Spraks  */
10764863Spraks void
port_install_fopdata(vnode_t * vp)10774863Spraks port_install_fopdata(vnode_t *vp)
10784863Spraks {
10794863Spraks 	portfop_vp_t *npvp;
10804863Spraks 
10814863Spraks 	npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
10824863Spraks 	mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
10834863Spraks 	list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
10844863Spraks 	    offsetof(portfop_t, pfop_node));
10854863Spraks 	npvp->pvp_vp = vp;
10864863Spraks 	/*
10874863Spraks 	 * If v_fopdata is not null, some other thread beat us to it.
10884863Spraks 	 */
10894863Spraks 	if (casptr(&vp->v_fopdata, NULL, npvp) != NULL) {
10904863Spraks 		mutex_destroy(&npvp->pvp_mutex);
10914863Spraks 		list_destroy(&npvp->pvp_pfoplist);
10924863Spraks 		kmem_free(npvp, sizeof (*npvp));
10934863Spraks 	}
10944863Spraks }
10954863Spraks 
10964863Spraks 
10974863Spraks /*
10984863Spraks  * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
10994863Spraks  * to the vnode's list. The association is identified by the object pointer
11004863Spraks  * address and pid.
11014863Spraks  */
11024863Spraks 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)11034863Spraks port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
11044863Spraks 	uintptr_t object, int events, void *user, char *cname, int clen,
11054863Spraks 	vnode_t *dvp)
11064863Spraks {
11074863Spraks 	portfop_t	*pfp = NULL;
11084863Spraks 	port_kevent_t	*pkevp;
11094863Spraks 	fem_t		*femp;
11104863Spraks 	int		error = 0;
11114863Spraks 	portfop_vp_t	*pvp;
11124863Spraks 
11134863Spraks 
11144863Spraks 	/*
11154863Spraks 	 * The port cache mutex is held.
11164863Spraks 	 */
11174863Spraks 	*pfpp  = NULL;
11184863Spraks 
11194863Spraks 
11204863Spraks 	/*
11214863Spraks 	 * At this point the fem monitor is installed.
11224863Spraks 	 * Allocate a port event structure per vnode association.
11234863Spraks 	 */
11244863Spraks 	if (pfp == NULL) {
11254863Spraks 		if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
11264863Spraks 		    PORT_ALLOC_CACHED, &pkevp)) {
11274863Spraks 			return (error);
11284863Spraks 		}
11294863Spraks 		pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
11304863Spraks 		pfp->pfop_pev = pkevp;
11314863Spraks 	}
11324863Spraks 
11334863Spraks 	pfp->pfop_vp = vp;
11344863Spraks 	pfp->pfop_pid = curproc->p_pid;
11354863Spraks 	pfp->pfop_pcache = pfcp;
11364863Spraks 	pfp->pfop_pp = pp;
11374863Spraks 	pfp->pfop_flags |= PORT_FOP_ACTIVE;
11384863Spraks 	pfp->pfop_cname = cname;
11394863Spraks 	pfp->pfop_clen = clen;
11404863Spraks 	pfp->pfop_dvp = dvp;
11414863Spraks 	pfp->pfop_object = object;
11424863Spraks 
11434863Spraks 	pkevp->portkev_callback = port_fop_callback;
11444863Spraks 	pkevp->portkev_arg = pfp;
11454863Spraks 	pkevp->portkev_object = object;
11464863Spraks 	pkevp->portkev_user = user;
11474863Spraks 	pkevp->portkev_events = 0;
11484863Spraks 
11494863Spraks 	port_pcache_insert(pfcp, pfp);
11504863Spraks 
11514863Spraks 	/*
11524863Spraks 	 * Register a new file events monitor for this file(vnode), if not
11534863Spraks 	 * done already.
11544863Spraks 	 */
11554863Spraks 	if ((pvp = vp->v_fopdata) == NULL) {
11564863Spraks 		port_install_fopdata(vp);
11574863Spraks 		pvp = vp->v_fopdata;
11584863Spraks 	}
11594863Spraks 
11604863Spraks 	mutex_enter(&pvp->pvp_mutex);
11614863Spraks 	/*
11624863Spraks 	 * if the vnode does not have the file events hooks, install it.
11634863Spraks 	 */
11644863Spraks 	if (pvp->pvp_femp == NULL) {
11654863Spraks 		if ((femp = port_fop_femop()) != NULL) {
11664863Spraks 			if (!(error = fem_install(pfp->pfop_vp, femp,
11674863Spraks 			    (void *)vp, OPUNIQ, NULL, NULL))) {
11684863Spraks 				pvp->pvp_femp = femp;
11694863Spraks 				/*
11704863Spraks 				 * add fsem_t hooks to the vfsp and add pvp to
11714863Spraks 				 * the list of vnodes for this vfs.
11724863Spraks 				 */
11734863Spraks 				if (!(error = port_fop_pvfsadd(pvp))) {
11744863Spraks 					/*
11754863Spraks 					 * Hold a reference to the vnode since
11764863Spraks 					 * we successfully installed the hooks.
11774863Spraks 					 */
11784863Spraks 					VN_HOLD(vp);
11794863Spraks 				} else {
11804863Spraks 					(void) fem_uninstall(vp, femp, vp);
11814863Spraks 					pvp->pvp_femp = NULL;
11824863Spraks 				}
11834863Spraks 			}
11844863Spraks 		} else {
11854863Spraks 			error = EINVAL;
11864863Spraks 		}
11874863Spraks 	}
11884863Spraks 
11894863Spraks 	if (error) {
11904863Spraks 		/*
11914863Spraks 		 * pkevp will get freed here.
11924863Spraks 		 */
11936005Spraks 		pfp->pfop_cname = NULL;
11944863Spraks 		port_pcache_remove_fop(pfcp, pfp);
11954863Spraks 		mutex_exit(&pvp->pvp_mutex);
11964863Spraks 		return (error);
11974863Spraks 	}
11984863Spraks 
11994863Spraks 	/*
12004863Spraks 	 * insert the pfp on the vnode's list. After this
12014863Spraks 	 * events can get delivered.
12024863Spraks 	 */
12034863Spraks 	pfp->pfop_events = events;
12044863Spraks 	port_fop_listinsert_head(pvp, pfp);
12054863Spraks 
12064863Spraks 	mutex_exit(&pvp->pvp_mutex);
12076501Spraks 	/*
12086501Spraks 	 * Hold the directory vnode since we have a reference now.
12096501Spraks 	 */
12106501Spraks 	if (dvp != NULL)
12116501Spraks 		VN_HOLD(dvp);
12124863Spraks 	*pfpp = pfp;
12134863Spraks 	return (0);
12144863Spraks }
12154863Spraks 
12164863Spraks vnode_t *
port_resolve_vp(vnode_t * vp)12174863Spraks port_resolve_vp(vnode_t *vp)
12184863Spraks {
12194863Spraks 	vnode_t *rvp;
12204863Spraks 	/*
12214863Spraks 	 * special case /etc/mnttab(mntfs type). The mntfstype != 0
12224863Spraks 	 * if mntfs got mounted.
12234863Spraks 	 */
12244863Spraks 	if (vfs_mntdummyvp && mntfstype != 0 &&
12254863Spraks 	    vp->v_vfsp->vfs_fstype == mntfstype) {
12264863Spraks 		VN_RELE(vp);
12274863Spraks 		vp = vfs_mntdummyvp;
12284863Spraks 		VN_HOLD(vfs_mntdummyvp);
12294863Spraks 	}
12304863Spraks 
12314863Spraks 	/*
12324863Spraks 	 * This should take care of lofs mounted fs systems and nfs4
12334863Spraks 	 * hardlinks.
12344863Spraks 	 */
12355331Samw 	if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
12364863Spraks 		VN_HOLD(rvp);
12374863Spraks 		VN_RELE(vp);
12384863Spraks 		vp = rvp;
12394863Spraks 	}
12404863Spraks 	return (vp);
12414863Spraks }
12424863Spraks 
12434863Spraks /*
12444863Spraks  * Register a file events watch on the given file associated to the port *pp.
12454863Spraks  *
12464863Spraks  * The association is identified by the object pointer and the pid.
12474863Spraks  * The events argument contains the events to be monitored for.
12486501Spraks  *
12496501Spraks  * The vnode will have a VN_HOLD once the fem hooks are installed.
12506501Spraks  *
12516501Spraks  * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
12526501Spraks  * that the directory vnode pointer does not change.
12534863Spraks  */
12544863Spraks int
port_associate_fop(port_t * pp,int source,uintptr_t object,int events,void * user)12554863Spraks port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
12564863Spraks     void *user)
12574863Spraks {
12584863Spraks 	portfop_cache_t	*pfcp;
12596501Spraks 	vnode_t		*vp, *dvp, *oldvp = NULL, *olddvp = NULL;
12604863Spraks 	portfop_t	*pfp;
12614863Spraks 	int		error = 0;
12624863Spraks 	file_obj_t	fobj;
12634863Spraks 	void		*objptr;
12644863Spraks 	char		*cname;
12654863Spraks 	int		clen;
12664863Spraks 	int		follow;
12674863Spraks 
12684863Spraks 	/*
12694863Spraks 	 * check that events specified are valid.
12704863Spraks 	 */
12714863Spraks 	if ((events & ~FILE_EVENTS_MASK) != 0)
12724863Spraks 		return (EINVAL);
12734863Spraks 
12744863Spraks 	if (get_udatamodel() == DATAMODEL_NATIVE) {
12754863Spraks 		if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
12764863Spraks 			return (EFAULT);
12774863Spraks 		objptr = (void *)&fobj;
12784863Spraks #ifdef  _SYSCALL32_IMPL
12794863Spraks 	} else {
12804863Spraks 		file_obj32_t	fobj32;
12814863Spraks 		if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
12824863Spraks 			return (EFAULT);
12834863Spraks 		objptr = (void *)&fobj32;
12844863Spraks #endif  /* _SYSCALL32_IMPL */
12854863Spraks 	}
12864863Spraks 
12874863Spraks 	vp = dvp = NULL;
12884863Spraks 
12894863Spraks 	/*
12906005Spraks 	 * find out if we need to follow symbolic links.
12914863Spraks 	 */
12924863Spraks 	follow = !(events & FILE_NOFOLLOW);
12934863Spraks 	events = events & ~FILE_NOFOLLOW;
12944863Spraks 
12954863Spraks 	/*
12964863Spraks 	 * lookup and find the vnode and its directory vnode of the given
12974863Spraks 	 * file.
12984863Spraks 	 */
12994863Spraks 	if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
13004863Spraks 	    follow)) != 0) {
13014863Spraks 		return (error);
13024863Spraks 	}
13034863Spraks 
13044863Spraks 	if (dvp != NULL) {
13054863Spraks 		dvp = port_resolve_vp(dvp);
13064863Spraks 	}
13074863Spraks 
13084863Spraks 	/*
13094863Spraks 	 * Not found
13104863Spraks 	 */
13114863Spraks 	if (vp == NULL) {
13124863Spraks 		error = ENOENT;
13134863Spraks 		goto errout;
13144863Spraks 	}
13154863Spraks 
13164863Spraks 	vp = port_resolve_vp(vp);
13174863Spraks 
13184863Spraks 
13195331Samw 	if (vp != NULL && vnevent_support(vp, NULL)) {
13204863Spraks 		error = ENOTSUP;
13214863Spraks 		goto errout;
13224863Spraks 	}
13234863Spraks 
13244863Spraks 	/*
1325*11899SPrakash.Sangappa@Sun.COM 	 * If dvp belongs to a different filesystem just ignore it.
1326*11899SPrakash.Sangappa@Sun.COM 	 * Hardlinks cannot exist across filesystems.
1327*11899SPrakash.Sangappa@Sun.COM 	 */
1328*11899SPrakash.Sangappa@Sun.COM 	if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp) {
1329*11899SPrakash.Sangappa@Sun.COM 		VN_RELE(dvp);
1330*11899SPrakash.Sangappa@Sun.COM 		dvp = NULL;
1331*11899SPrakash.Sangappa@Sun.COM 	}
1332*11899SPrakash.Sangappa@Sun.COM 
1333*11899SPrakash.Sangappa@Sun.COM 	/*
13344863Spraks 	 * Associate this source to the port and get the per port
13354863Spraks 	 * fop cache pointer. If the source is already associated, it
13364863Spraks 	 * will just return the cache pointer.
13374863Spraks 	 */
13384863Spraks 	if (error = port_fop_associate_source(&pfcp, pp, source)) {
13394863Spraks 		goto errout;
13404863Spraks 	}
13414863Spraks 
13424863Spraks 	/*
13434863Spraks 	 * Check if there is an existing association of this file.
13444863Spraks 	 */
13454863Spraks 	mutex_enter(&pfcp->pfc_lock);
13464863Spraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
13474863Spraks 
13484863Spraks 	/*
13496501Spraks 	 * If it is not the same vnode, just discard it. VN_RELE needs to be
13506501Spraks 	 * called with no locks held, therefore save vnode pointers and
13516501Spraks 	 * vn_rele them later.
13524863Spraks 	 */
13534863Spraks 	if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
13546501Spraks 		(void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
13554863Spraks 		pfp = NULL;
13564863Spraks 	}
13574863Spraks 
13584863Spraks 	if (pfp == NULL) {
13596501Spraks 		vnode_t *tvp, *tdvp;
13606005Spraks 		portfop_t	*tpfp;
13616005Spraks 		int error;
13626005Spraks 
13634863Spraks 		/*
13644863Spraks 		 * Add a new association, save the file name and the
13654863Spraks 		 * directory vnode pointer.
13664863Spraks 		 */
13674863Spraks 		if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
13684863Spraks 		    events, user, cname, clen, dvp)) {
13694863Spraks 			mutex_exit(&pfcp->pfc_lock);
13704863Spraks 			goto errout;
13714863Spraks 		}
13724863Spraks 
13736005Spraks 		pfp->pfop_callrid = curthread;
13744863Spraks 		/*
13754863Spraks 		 * File name used, so make sure we don't free it.
13764863Spraks 		 */
13774863Spraks 		cname = NULL;
13784863Spraks 
13794863Spraks 		/*
13804863Spraks 		 * We need to check if the file was removed after the
13814863Spraks 		 * the lookup and before the fem hooks where added. If
13824863Spraks 		 * so, return error. The vnode will still exist as we have
13834863Spraks 		 * a hold on it.
13846005Spraks 		 *
13856005Spraks 		 * Drop the cache lock before calling port_fop_getdvp().
13866005Spraks 		 * port_fop_getdvp() may block either in the vfs layer
13876005Spraks 		 * or some filesystem.  Therefore there is potential
13886005Spraks 		 * for deadlock if cache lock is held and if some other
13896005Spraks 		 * thread is attempting to deliver file events which would
13906005Spraks 		 * require getting the cache lock, while it may be holding
13916005Spraks 		 * the filesystem or vfs layer locks.
13924863Spraks 		 */
13936005Spraks 		mutex_exit(&pfcp->pfc_lock);
13946005Spraks 		tvp = NULL;
13956005Spraks 		if ((error = port_fop_getdvp(objptr, &tvp, NULL,
13966005Spraks 		    NULL, NULL, follow)) == 0) {
13976005Spraks 			if (tvp != NULL) {
13986005Spraks 				tvp = port_resolve_vp(tvp);
13994863Spraks 				/*
14006005Spraks 				 * This vnode pointer is just used
14016005Spraks 				 * for comparison, so rele it
14024863Spraks 				 */
14034863Spraks 				VN_RELE(tvp);
14044863Spraks 			}
14054863Spraks 		}
14066005Spraks 
14076005Spraks 		if (error || tvp == NULL || tvp != vp) {
14086005Spraks 			/*
14096005Spraks 			 * Since we dropped the cache lock, make sure
14106005Spraks 			 * we are still dealing with the same pfp and this
14116005Spraks 			 * is the thread which registered it.
14126005Spraks 			 */
14136005Spraks 			mutex_enter(&pfcp->pfc_lock);
14146005Spraks 			tpfp = port_cache_lookup_fop(pfcp,
14156005Spraks 			    curproc->p_pid, object);
14166005Spraks 
14176005Spraks 			error = 0;
14186005Spraks 			if (tpfp == NULL || tpfp != pfp ||
14196005Spraks 			    pfp->pfop_vp != vp ||
14206005Spraks 			    pfp->pfop_dvp != dvp ||
14216005Spraks 			    pfp->pfop_callrid != curthread) {
14226005Spraks 				/*
14236005Spraks 				 * Some other event was delivered, the file
14246005Spraks 				 * watch was removed or reassociated, just
14256005Spraks 				 * ignore it and leave
14266005Spraks 				 */
14276005Spraks 				mutex_exit(&pfcp->pfc_lock);
14286005Spraks 				goto errout;
14296005Spraks 			}
14306005Spraks 
14316005Spraks 			/*
14326005Spraks 			 * remove the pfp and fem hooks, if pfp still
14336005Spraks 			 * active and it is not being removed from
14346005Spraks 			 * the vnode list. This is checked in
14356005Spraks 			 * port_remove_fop with the vnode lock held.
14366501Spraks 			 * The vnode returned is VN_RELE'ed after dropping
14376501Spraks 			 * the locks.
14386005Spraks 			 */
14396501Spraks 			tdvp = tvp = NULL;
14406501Spraks 			if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
14416005Spraks 				/*
14426005Spraks 				 * The pfp was removed, means no
14436005Spraks 				 * events where queued. Report the
14446005Spraks 				 * error now.
14456005Spraks 				 */
14466005Spraks 				error = EINVAL;
14476005Spraks 			}
14486005Spraks 			mutex_exit(&pfcp->pfc_lock);
14496501Spraks 			if (tvp != NULL)
14506501Spraks 				VN_RELE(tvp);
14516501Spraks 			if (tdvp != NULL)
14526501Spraks 				VN_RELE(tdvp);
14536005Spraks 			goto errout;
14546005Spraks 		}
14554863Spraks 	} else {
14564863Spraks 		portfop_vp_t	*pvp = vp->v_fopdata;
14574863Spraks 
14584863Spraks 		/*
14594863Spraks 		 * Re-association of the object.
14604863Spraks 		 */
14614863Spraks 		mutex_enter(&pvp->pvp_mutex);
14624863Spraks 
14634863Spraks 		/*
14644863Spraks 		 * remove any queued up event.
14654863Spraks 		 */
14664863Spraks 		if (port_remove_done_event(pfp->pfop_pev)) {
14674863Spraks 			pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
14684863Spraks 		}
14694863Spraks 
14704863Spraks 		/*
14714863Spraks 		 * set new events to watch.
14724863Spraks 		 */
14734863Spraks 		pfp->pfop_events = events;
14744863Spraks 
14754863Spraks 		/*
14764863Spraks 		 * If not active, mark it active even if it is being
14774863Spraks 		 * removed. Then it can send an exception event.
14784863Spraks 		 *
14794863Spraks 		 * Move it to the head, as the active ones are only
14806005Spraks 		 * in the beginning. If removing, the pfp will be on
14814863Spraks 		 * a temporary list, no need to move it to the front
14826005Spraks 		 * all the entries will be processed. Some exception
14836005Spraks 		 * events will be delivered in port_fop_excep();
14844863Spraks 		 */
14854863Spraks 		if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
14864863Spraks 			pfp->pfop_flags |= PORT_FOP_ACTIVE;
14876005Spraks 			if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
14884863Spraks 				pvp = (portfop_vp_t *)vp->v_fopdata;
14894863Spraks 				port_fop_listremove(pvp, pfp);
14904863Spraks 				port_fop_listinsert_head(pvp, pfp);
14914863Spraks 			}
14924863Spraks 		}
14936005Spraks 		pfp->pfop_callrid = curthread;
14944863Spraks 		mutex_exit(&pvp->pvp_mutex);
14956005Spraks 		mutex_exit(&pfcp->pfc_lock);
14964863Spraks 	}
14974863Spraks 
14984863Spraks 	/*
14996005Spraks 	 * Compare time stamps and deliver events.
15004863Spraks 	 */
15016005Spraks 	if (vp->v_type != VFIFO) {
15026005Spraks 		port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
15034863Spraks 	}
15044863Spraks 
15054863Spraks 	error = 0;
15064863Spraks 
15074863Spraks 	/*
15084863Spraks 	 *  If we have too many watches on the vnode, discard an
15094863Spraks 	 *  inactive watch.
15104863Spraks 	 */
15114863Spraks 	port_fop_trimpfplist(vp);
15124863Spraks 
15134863Spraks errout:
15144863Spraks 	/*
15154863Spraks 	 * Release the hold acquired due to the lookup operation.
15164863Spraks 	 */
15174863Spraks 	if (vp != NULL)
15184863Spraks 		VN_RELE(vp);
15196501Spraks 	if (dvp != NULL)
15206501Spraks 		VN_RELE(dvp);
15216501Spraks 
15226501Spraks 	if (oldvp != NULL)
15236501Spraks 		VN_RELE(oldvp);
15246501Spraks 	if (olddvp != NULL)
15256501Spraks 		VN_RELE(olddvp);
15264863Spraks 
15274863Spraks 	/*
15284863Spraks 	 * copied file name not used, free it.
15294863Spraks 	 */
15304863Spraks 	if (cname != NULL) {
15314863Spraks 		kmem_free(cname, clen + 1);
15324863Spraks 	}
15334863Spraks 	return (error);
15344863Spraks }
15354863Spraks 
15364863Spraks 
15374863Spraks /*
15384863Spraks  * The port_dissociate_fop() function dissociates the file object
15394863Spraks  * from the event port and removes any events that are already on the queue.
15404863Spraks  * Only the owner of the association is allowed to dissociate the file from
15414863Spraks  * the port. Returns  success (0) if it was found and removed. Otherwise
15424863Spraks  * ENOENT.
15434863Spraks  */
15444863Spraks int
port_dissociate_fop(port_t * pp,uintptr_t object)15454863Spraks port_dissociate_fop(port_t *pp, uintptr_t object)
15464863Spraks {
15474863Spraks 	portfop_cache_t	*pfcp;
15484863Spraks 	portfop_t	*pfp;
15494863Spraks 	port_source_t	*pse;
15504863Spraks 	int		active = 0;
15516501Spraks 	vnode_t		*tvp = NULL, *tdvp = NULL;
15524863Spraks 
15534863Spraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
15544863Spraks 
15554863Spraks 	/*
15564863Spraks 	 * if this source is not associated or if there is no
15574863Spraks 	 * cache, nothing to do just return.
15584863Spraks 	 */
15594863Spraks 	if (pse == NULL ||
15604863Spraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
15614863Spraks 		return (EINVAL);
15624863Spraks 
15634863Spraks 	/*
15644863Spraks 	 * Check if this object is on the cache. Only the owner pid
15654863Spraks 	 * is allowed to dissociate.
15664863Spraks 	 */
15674863Spraks 	mutex_enter(&pfcp->pfc_lock);
15684863Spraks 	pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
15694863Spraks 	if (pfp == NULL) {
15704863Spraks 		mutex_exit(&pfcp->pfc_lock);
15714863Spraks 		return (ENOENT);
15724863Spraks 	}
15734863Spraks 
15744863Spraks 	/*
15754863Spraks 	 * If this was the last association, it will release
15764863Spraks 	 * the hold on the vnode. There is a race condition where
15774863Spraks 	 * the the pfp is being removed due to an exception event
15784863Spraks 	 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
15794863Spraks 	 * Since port source cache lock is held, port_fop_excep() cannot
15806005Spraks 	 * complete. The vnode itself will not disappear as long its pfps
15814863Spraks 	 * have a reference.
15824863Spraks 	 */
15836501Spraks 	(void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
15844863Spraks 	mutex_exit(&pfcp->pfc_lock);
15856501Spraks 	if (tvp != NULL)
15866501Spraks 		VN_RELE(tvp);
15876501Spraks 	if (tdvp != NULL)
15886501Spraks 		VN_RELE(tdvp);
15894863Spraks 	return (active ? 0 : ENOENT);
15904863Spraks }
15914863Spraks 
15924863Spraks 
15934863Spraks /*
15944863Spraks  * port_close() calls this function to request the PORT_SOURCE_FILE source
15954863Spraks  * to remove/free all resources allocated and associated with the port.
15964863Spraks  */
15974863Spraks 
15984863Spraks /* ARGSUSED */
15994863Spraks static void
port_close_fop(void * arg,int port,pid_t pid,int lastclose)16004863Spraks port_close_fop(void *arg, int port, pid_t pid, int lastclose)
16014863Spraks {
16024863Spraks 	port_t		*pp = arg;
16034863Spraks 	portfop_cache_t	*pfcp;
16044863Spraks 	portfop_t	**hashtbl;
16054863Spraks 	portfop_t	*pfp;
16064863Spraks 	portfop_t	*pfpnext;
16076501Spraks 	int		index, i;
16084863Spraks 	port_source_t	*pse;
16096501Spraks 	vnode_t 	*tdvp = NULL;
16106501Spraks 	vnode_t		*vpl[PORTFOP_NVP];
16114863Spraks 
16124863Spraks 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
16134863Spraks 
16144863Spraks 	/*
16154863Spraks 	 * No source or no cache, nothing to do.
16164863Spraks 	 */
16174863Spraks 	if (pse == NULL ||
16184863Spraks 	    (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
16194863Spraks 		return;
16204863Spraks 	/*
16214863Spraks 	 * Scan the cache and free all allocated portfop_t and port_kevent_t
16226501Spraks 	 * structures of this pid. Note, no new association for this pid will
16236501Spraks 	 * be possible as the port is being closed.
16246501Spraks 	 *
16256501Spraks 	 * The common case is that the port is not shared and all the entries
16266501Spraks 	 * are of this pid and have to be freed. Since VN_RELE has to be
16276501Spraks 	 * called outside the lock, we do it in batches.
16284863Spraks 	 */
16296501Spraks 	hashtbl = (portfop_t **)pfcp->pfc_hash;
16306501Spraks 	index = i = 0;
16316501Spraks 	bzero(vpl, sizeof (vpl));
16324863Spraks 	mutex_enter(&pfcp->pfc_lock);
16336501Spraks 	while (index < PORTFOP_HASHSIZE) {
16346501Spraks 		pfp = hashtbl[index];
16356501Spraks 		while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
16364863Spraks 			pfpnext = pfp->pfop_hashnext;
16374863Spraks 			if (pid == pfp->pfop_pid) {
16386501Spraks 				(void) port_remove_fop(pfp, pfcp, 1, NULL,
16396501Spraks 				    &vpl[i], &tdvp);
16406501Spraks 				if (vpl[i] != NULL) {
16416501Spraks 					i++;
16426501Spraks 				}
16436501Spraks 				if (tdvp != NULL) {
16446501Spraks 					vpl[i++] = tdvp;
16456501Spraks 					tdvp = NULL;
16466501Spraks 				}
16474863Spraks 			}
16486501Spraks 			pfp = pfpnext;
16496501Spraks 		}
16506501Spraks 		if (pfp == NULL)
16516501Spraks 			index++;
16526501Spraks 		/*
16536501Spraks 		 * Now call VN_RELE if we have collected enough vnodes or
16546501Spraks 		 * we have reached the end of the hash table.
16556501Spraks 		 */
16566501Spraks 		if (i >= (PORTFOP_NVP - 1) ||
16576501Spraks 		    (i > 0 && index == PORTFOP_HASHSIZE)) {
16586501Spraks 			mutex_exit(&pfcp->pfc_lock);
16596501Spraks 			while (i > 0) {
16606501Spraks 				VN_RELE(vpl[--i]);
16616501Spraks 				vpl[i] = NULL;
16626501Spraks 			}
16636501Spraks 			mutex_enter(&pfcp->pfc_lock);
16644863Spraks 		}
16654863Spraks 	}
16664863Spraks 
16674863Spraks 	/*
16684863Spraks 	 * Due to a race between port_close_fop() and port_fop()
16694863Spraks 	 * trying to remove the pfp's from the port's cache, it is
16704863Spraks 	 * possible that some pfp's are still in the process of being
16714863Spraks 	 * freed so we wait.
16724863Spraks 	 */
16734863Spraks 	while (lastclose && pfcp->pfc_objcount) {
16744863Spraks 		(void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
16754863Spraks 	}
16764863Spraks 	mutex_exit(&pfcp->pfc_lock);
16774863Spraks 	/*
16784863Spraks 	 * last close, free the cache.
16794863Spraks 	 */
16804863Spraks 	if (lastclose) {
16814863Spraks 		ASSERT(pfcp->pfc_objcount == 0);
16824863Spraks 		pse->portsrc_data = NULL;
16834863Spraks 		kmem_free(pfcp, sizeof (portfop_cache_t));
16844863Spraks 	}
16854863Spraks }
16864863Spraks 
16874863Spraks /*
16884863Spraks  * Given the list of associations(watches), it will send exception events,
16894863Spraks  * if still active, and discard them. The exception events are handled
16905331Samw  * separately because, the pfp needs to be removed from the port cache and
16914863Spraks  * freed as the vnode's identity is changing or being removed. To remove
16924863Spraks  * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
16934863Spraks  * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
16944863Spraks  * the cache's lock cannot be acquired in port_fop_sendevent().
16954863Spraks  */
16964863Spraks static void
port_fop_excep(list_t * tlist,int op)16974863Spraks port_fop_excep(list_t *tlist, int op)
16984863Spraks {
16994863Spraks 	portfop_t	*pfp;
17004863Spraks 	portfop_cache_t *pfcp;
17014863Spraks 	port_t	*pp;
17024863Spraks 	port_kevent_t	*pkevp;
17036501Spraks 	vnode_t		*tdvp;
17044863Spraks 	int		error = 0;
17054863Spraks 
17064863Spraks 	while (pfp = (portfop_t *)list_head(tlist)) {
17074863Spraks 		int removed = 0;
17084863Spraks 		/*
17094863Spraks 		 * remove from the temp list. Since PORT_FOP_REMOVING is
17104863Spraks 		 * set, no other thread should attempt to perform a
17114863Spraks 		 * list_remove on this pfp.
17124863Spraks 		 */
17134863Spraks 		list_remove(tlist, pfp);
17144863Spraks 
17154863Spraks 		pfcp = pfp->pfop_pcache;
17164863Spraks 		mutex_enter(&pfcp->pfc_lock);
17174863Spraks 
17184863Spraks 		/*
17194863Spraks 		 * Remove the event from the port queue if it was queued up.
17204863Spraks 		 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
17214863Spraks 		 * no longer on the vnode's list.
17224863Spraks 		 */
17234863Spraks 		if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
17244863Spraks 			removed = port_remove_done_event(pfp->pfop_pev);
17254863Spraks 		}
17264863Spraks 
17274863Spraks 		/*
17284863Spraks 		 * If still active or the event was queued up and
17294863Spraks 		 * had not been collected yet, send an EXCEPTION event.
17304863Spraks 		 */
17314863Spraks 		if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
17324863Spraks 			pp = pfp->pfop_pp;
17334863Spraks 			/*
17344863Spraks 			 * Allocate a port_kevent_t non cached to send this
17354863Spraks 			 * event since we will be de-registering.
17364863Spraks 			 * The port_kevent_t cannot be pointing back to the
17374863Spraks 			 * pfp anymore.
17384863Spraks 			 */
17394863Spraks 			pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
17404863Spraks 			error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
17414863Spraks 			    PORT_ALLOC_DEFAULT, &pkevp);
17424863Spraks 			if (!error) {
17434863Spraks 
17444863Spraks 				pkevp->portkev_callback = port_fop_callback;
17454863Spraks 				pkevp->portkev_arg = NULL;
17464863Spraks 				pkevp->portkev_object =
17474863Spraks 				    pfp->pfop_pev->portkev_object;
17484863Spraks 				pkevp->portkev_user =
17494863Spraks 				    pfp->pfop_pev->portkev_user;
17504863Spraks 				/*
17514863Spraks 				 * Copy the pid of the watching process.
17524863Spraks 				 */
17534863Spraks 				pkevp->portkev_pid =
17544863Spraks 				    pfp->pfop_pev->portkev_pid;
17554863Spraks 				pkevp->portkev_events = op;
17564863Spraks 				port_send_event(pkevp);
17574863Spraks 			}
17584863Spraks 		}
17594863Spraks 		/*
17604863Spraks 		 * At this point the pfp has been removed from the vnode's
17614863Spraks 		 * list its cached port_kevent_t is not on the done queue.
17624863Spraks 		 * Remove the pfp and free it from the cache.
17634863Spraks 		 */
17646501Spraks 		tdvp = pfp->pfop_dvp;
17654863Spraks 		port_pcache_remove_fop(pfcp, pfp);
17664863Spraks 		mutex_exit(&pfcp->pfc_lock);
17676501Spraks 		if (tdvp != NULL)
17686501Spraks 			VN_RELE(tdvp);
17694863Spraks 	}
17704863Spraks }
17714863Spraks 
17724863Spraks /*
17734863Spraks  * Send the file events to all of the processes watching this
17744863Spraks  * vnode. In case of hard links, the directory vnode pointer and
17754863Spraks  * the file name are compared. If the names match, then the specified
17764863Spraks  * event is sent or else, the FILE_ATTRIB event is sent, This is the
17774863Spraks  * documented behavior.
17784863Spraks  */
17794863Spraks void
port_fop_sendevent(vnode_t * vp,int events,vnode_t * dvp,char * cname)17804863Spraks port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
17814863Spraks {
17824863Spraks 	port_kevent_t	*pkevp;
17834863Spraks 	portfop_t	*pfp, *npfp;
17844863Spraks 	portfop_vp_t	*pvp;
17854863Spraks 	list_t		tmplist;
17864863Spraks 	int		removeall = 0;
17874863Spraks 
17884863Spraks 	pvp = (portfop_vp_t *)vp->v_fopdata;
17894863Spraks 	mutex_enter(&pvp->pvp_mutex);
17904863Spraks 
17914863Spraks 	/*
17924863Spraks 	 * Check if the list is empty.
17934863Spraks 	 *
17944863Spraks 	 * All entries have been removed by some other thread.
17954863Spraks 	 * The vnode may be still active and we got called,
17964863Spraks 	 * but some other thread is in the process of removing the hooks.
17974863Spraks 	 */
17984863Spraks 	if (!list_head(&pvp->pvp_pfoplist)) {
17994863Spraks 		mutex_exit(&pvp->pvp_mutex);
18004863Spraks 		return;
18014863Spraks 	}
18024863Spraks 
18034863Spraks 	if ((events & (FILE_EXCEPTION))) {
18044863Spraks 		/*
18054863Spraks 		 * If it is an event for which we are going to remove
18064863Spraks 		 * the watches so just move it a temporary list and
18074863Spraks 		 * release this vnode.
18084863Spraks 		 */
18094863Spraks 		list_create(&tmplist, sizeof (portfop_t),
18104863Spraks 		    offsetof(portfop_t, pfop_node));
18114863Spraks 
18124863Spraks 		/*
18134863Spraks 		 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
18144863Spraks 		 * passed for an exception event, all associations need to be
18154863Spraks 		 * removed.
18164863Spraks 		 */
18174863Spraks 		if (dvp == NULL || cname == NULL) {
18184863Spraks 			removeall = 1;
18194863Spraks 		}
18204863Spraks 	}
18214863Spraks 
18224863Spraks 	if (!removeall) {
18234863Spraks 		/*
18246005Spraks 		 * All the active ones are in the beginning of the list.
18254863Spraks 		 */
18264863Spraks 		for (pfp = (portfop_t *)list_head(&pvp->pvp_pfoplist);
18274863Spraks 		    pfp && pfp->pfop_flags & PORT_FOP_ACTIVE; pfp = npfp) {
18284863Spraks 			int levents = events;
18294863Spraks 
18304863Spraks 			npfp = list_next(&pvp->pvp_pfoplist, pfp);
18314863Spraks 			/*
18324863Spraks 			 * Hard links case - If the file is being
18334863Spraks 			 * removed/renamed, and the name matches
18344863Spraks 			 * the watched file, then it is an EXCEPTION
18354863Spraks 			 * event or else it will be just a FILE_ATTRIB.
18364863Spraks 			 */
18374863Spraks 			if ((events & (FILE_EXCEPTION))) {
18384863Spraks 				ASSERT(dvp != NULL && cname != NULL);
18394863Spraks 				if (pfp->pfop_dvp == NULL ||
18404863Spraks 				    (pfp->pfop_dvp == dvp &&
18414863Spraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
18424863Spraks 					/*
18434863Spraks 					 * It is an exception event, move it
18444863Spraks 					 * to temp list and process it later.
18454863Spraks 					 * Note we don't set the pfp->pfop_vp
18464863Spraks 					 * to NULL even thought it has been
18474863Spraks 					 * removed from the vnode's list. This
18484863Spraks 					 * pointer is referenced in
18494863Spraks 					 * port_remove_fop(). The vnode it
18506005Spraks 					 * self cannot disappear until this
18514863Spraks 					 * pfp gets removed and freed.
18524863Spraks 					 */
18534863Spraks 					port_fop_listremove(pvp, pfp);
18544863Spraks 					list_insert_tail(&tmplist, (void *)pfp);
18554863Spraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
18564863Spraks 					continue;
18574863Spraks 				} else {
18584863Spraks 					levents = FILE_ATTRIB;
18594863Spraks 				}
18604863Spraks 
18614863Spraks 			}
18624863Spraks 
18634863Spraks 			if (pfp->pfop_events & levents) {
18644863Spraks 				/*
18654863Spraks 				 * deactivate and move it to the tail.
18664863Spraks 				 * If the pfp was active, it cannot be
18674863Spraks 				 * on the port's done queue.
18684863Spraks 				 */
18694863Spraks 				pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
18704863Spraks 				port_fop_listremove(pvp, pfp);
18714863Spraks 				port_fop_listinsert_tail(pvp, pfp);
18724863Spraks 
18734863Spraks 				pkevp = pfp->pfop_pev;
18744863Spraks 				pkevp->portkev_events |=
18754863Spraks 				    (levents & pfp->pfop_events);
18764863Spraks 				port_send_event(pkevp);
18774863Spraks 				pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
18784863Spraks 			}
18794863Spraks 		}
18804863Spraks 	}
18814863Spraks 
18824863Spraks 
18834863Spraks 	if ((events & (FILE_EXCEPTION))) {
18844863Spraks 		if (!removeall) {
18854863Spraks 			/*
18864863Spraks 			 * Check the inactive associations and remove them if
18874863Spraks 			 * the file name matches.
18884863Spraks 			 */
18894863Spraks 			for (; pfp; pfp = npfp) {
18904863Spraks 				npfp = list_next(&pvp->pvp_pfoplist, pfp);
18914863Spraks 				if (dvp == NULL || cname == NULL ||
18924863Spraks 				    pfp->pfop_dvp == NULL ||
18934863Spraks 				    (pfp->pfop_dvp == dvp &&
18944863Spraks 				    (strcmp(cname, pfp->pfop_cname) == 0))) {
18954863Spraks 					port_fop_listremove(pvp, pfp);
18964863Spraks 					list_insert_tail(&tmplist, (void *)pfp);
18974863Spraks 					pfp->pfop_flags  |= PORT_FOP_REMOVING;
18984863Spraks 				}
18994863Spraks 			}
19004863Spraks 		} else {
19014863Spraks 			/*
19024863Spraks 			 * Can be optimized to avoid two pass over this list
19034863Spraks 			 * by having a flag in the vnode's portfop_vp_t
19044863Spraks 			 * structure to indicate that it is going away,
19054863Spraks 			 * Or keep the list short by reusing inactive watches.
19064863Spraks 			 */
19074863Spraks 			port_fop_listmove(pvp, &tmplist);
19084863Spraks 			for (pfp = (portfop_t *)list_head(&tmplist);
19094863Spraks 			    pfp; pfp = list_next(&tmplist, pfp)) {
19104863Spraks 				pfp->pfop_flags |= PORT_FOP_REMOVING;
19114863Spraks 			}
19124863Spraks 		}
19134863Spraks 
19144863Spraks 		/*
19154863Spraks 		 * Uninstall the fem hooks if there are no more associations.
19164863Spraks 		 * This will release the pvp mutex.
19174863Spraks 		 *
19184863Spraks 		 * Even thought all entries may have been removed,
19194863Spraks 		 * the vnode itself cannot disappear as there will be a
19204863Spraks 		 * hold on it due to this call to port_fop_sendevent. This is
19214863Spraks 		 * important to syncronize with a port_dissociate_fop() call
19224863Spraks 		 * that may be attempting to remove an object from the vnode's.
19234863Spraks 		 */
19246501Spraks 		if (port_fop_femuninstall(vp))
19256501Spraks 			VN_RELE(vp);
19264863Spraks 
19274863Spraks 		/*
19284863Spraks 		 * Send exception events and discard the watch entries.
19294863Spraks 		 */
19304863Spraks 		port_fop_excep(&tmplist, events);
19314863Spraks 		list_destroy(&tmplist);
19324863Spraks 
19334863Spraks 	} else {
19344863Spraks 		mutex_exit(&pvp->pvp_mutex);
19354863Spraks 
19364863Spraks 		/*
19374863Spraks 		 * trim the list.
19384863Spraks 		 */
19394863Spraks 		port_fop_trimpfplist(vp);
19404863Spraks 	}
19414863Spraks }
19424863Spraks 
19434863Spraks /*
19444863Spraks  * Given the file operation, map it to the event types and send.
19454863Spraks  */
19464863Spraks void
port_fop(vnode_t * vp,int op,int retval)19474863Spraks port_fop(vnode_t *vp, int op, int retval)
19484863Spraks {
19494863Spraks 	int event = 0;
19504863Spraks 	/*
19514863Spraks 	 * deliver events only if the operation was successful.
19524863Spraks 	 */
19534863Spraks 	if (retval)
19544863Spraks 		return;
19554863Spraks 
19564863Spraks 	/*
19575331Samw 	 * These events occurring on the watched file.
19584863Spraks 	 */
19594863Spraks 	if (op & FOP_MODIFIED_MASK) {
19604863Spraks 		event  = FILE_MODIFIED;
19614863Spraks 	}
19624863Spraks 	if (op & FOP_ACCESS_MASK) {
19634863Spraks 		event  |= FILE_ACCESS;
19644863Spraks 	}
19654863Spraks 	if (op & FOP_ATTRIB_MASK) {
19664863Spraks 		event  |= FILE_ATTRIB;
19674863Spraks 	}
19684863Spraks 
19694863Spraks 	if (event) {
19704863Spraks 		port_fop_sendevent(vp, 	event, NULL, NULL);
19714863Spraks 	}
19724863Spraks }
19734863Spraks 
port_forceunmount(vfs_t * vfsp)19747179Spraks static int port_forceunmount(vfs_t *vfsp)
19757179Spraks {
19767179Spraks 	char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
19777179Spraks 
19787179Spraks 	if (fsname == NULL) {
19797179Spraks 		return (0);
19807179Spraks 	}
19817179Spraks 
19827179Spraks 	if (strcmp(fsname, MNTTYPE_NFS) == 0) {
19837179Spraks 		return (1);
19847179Spraks 	}
19857179Spraks 
19867179Spraks 	if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
19877179Spraks 		return (1);
19887179Spraks 	}
19897179Spraks 
19907179Spraks 	if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
19917179Spraks 		return (1);
19927179Spraks 	}
19937179Spraks 	return (0);
19947179Spraks }
19954863Spraks /*
19964863Spraks  * ----- the unmount filesystem op(fsem) hook.
19974863Spraks  */
19984863Spraks int
port_fop_unmount(fsemarg_t * vf,int flag,cred_t * cr)19994863Spraks port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
20004863Spraks {
20014863Spraks 	vfs_t	*vfsp = (vfs_t *)vf->fa_fnode->fn_available;
20024863Spraks 	kmutex_t	*mtx;
20034863Spraks 	portfop_vfs_t	*pvfsp, **ppvfsp;
20044863Spraks 	portfop_vp_t	*pvp;
20054863Spraks 	int error;
20067179Spraks 	int fmfs;
20077179Spraks 
20087179Spraks 	fmfs = port_forceunmount(vfsp);
20094863Spraks 
20104863Spraks 	mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
20114863Spraks 	ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
20124863Spraks 	pvfsp = NULL;
20134863Spraks 	mutex_enter(mtx);
20144863Spraks 	/*
20156501Spraks 	 * since this fsem hook is triggered, the vfsp has to be on
20164863Spraks 	 * the hash list.
20174863Spraks 	 */
20184863Spraks 	for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
20194863Spraks 	;
20204863Spraks 
20214863Spraks 	/*
20227179Spraks 	 * For some of the filesystems, allow unmounts to proceed only if
20237179Spraks 	 * there are no files being watched or it is a forced unmount.
20247179Spraks 	 */
20257179Spraks 	if (fmfs && !(flag & MS_FORCE) &&
20267179Spraks 	    !list_is_empty(&pvfsp->pvfs_pvplist)) {
20277179Spraks 		mutex_exit(mtx);
20287179Spraks 		return (EBUSY);
20297179Spraks 	}
20307179Spraks 
20317179Spraks 	/*
20324863Spraks 	 * Indicate that the unmount is in process. Don't remove it yet.
20334863Spraks 	 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
20344863Spraks 	 * flag on the vfs_t structure. But we call the filesystem unmount
20354863Spraks 	 * routine after removing all the file watches for this filesystem,
20364863Spraks 	 * otherwise the unmount will fail due to active vnodes.
20374863Spraks 	 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
20384863Spraks 	 * attempting to add a file watch.
20394863Spraks 	 */
20404863Spraks 	pvfsp->pvfs_unmount = 1;
20414863Spraks 	mutex_exit(mtx);
20424863Spraks 
20434863Spraks 	/*
20444863Spraks 	 * uninstall the fsem hooks.
20454863Spraks 	 */
20464863Spraks 	(void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
20474863Spraks 
20484863Spraks 	while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
20494863Spraks 		list_remove(&pvfsp->pvfs_pvplist, pvp);
20504863Spraks 		/*
20514863Spraks 		 * This should send an UNMOUNTED event to all the
20524863Spraks 		 * watched vnode of this filesystem and uninstall
20534863Spraks 		 * the fem hooks. We release the hold on the vnode here
20544863Spraks 		 * because port_fop_femuninstall() will not do it if
20554863Spraks 		 * unmount is in process.
20564863Spraks 		 */
20574863Spraks 		port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
20584863Spraks 		VN_RELE(pvp->pvp_vp);
20594863Spraks 	}
20604863Spraks 
20614863Spraks 	error = vfsnext_unmount(vf, flag, cr);
20624863Spraks 
20634863Spraks 	/*
20644863Spraks 	 * we free the pvfsp after the unmount has been completed.
20654863Spraks 	 */
20664863Spraks 	mutex_enter(mtx);
20674863Spraks 	for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
20684863Spraks 	    ppvfsp = &(*ppvfsp)->pvfs_next)
20694863Spraks 	;
20704863Spraks 
20714863Spraks 	/*
20724863Spraks 	 * remove and free it.
20734863Spraks 	 */
20744863Spraks 	ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
20754863Spraks 	if (*ppvfsp) {
20764863Spraks 		pvfsp = *ppvfsp;
20774863Spraks 		*ppvfsp = pvfsp->pvfs_next;
20784863Spraks 	}
20794863Spraks 	mutex_exit(mtx);
20804863Spraks 	kmem_free(pvfsp, sizeof (portfop_vfs_t));
20814863Spraks 	return (error);
20824863Spraks }
20834863Spraks 
20844863Spraks /*
20854863Spraks  * ------------------------------file op hooks--------------------------
20864863Spraks  * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
20874863Spraks  */
20884863Spraks static int
port_fop_open(femarg_t * vf,int mode,cred_t * cr,caller_context_t * ct)20895331Samw port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
20904863Spraks {
20914863Spraks 	int		retval;
20924863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
20934863Spraks 
20945331Samw 	retval = vnext_open(vf, mode, cr, ct);
20954863Spraks 	port_fop(vp, FOP_FILE_OPEN, retval);
20964863Spraks 	return (retval);
20974863Spraks }
20984863Spraks 
20994863Spraks static int
port_fop_write(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)21004863Spraks port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
21014863Spraks     caller_context_t *ct)
21024863Spraks {
21034863Spraks 	int		retval;
21044863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
21054863Spraks 
21064863Spraks 	retval =  vnext_write(vf, uiop, ioflag, cr, ct);
21074863Spraks 	port_fop(vp, FOP_FILE_WRITE, retval);
21084863Spraks 	return (retval);
21094863Spraks }
21104863Spraks 
21114863Spraks 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)21124863Spraks port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
21135331Samw     size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
21145331Samw     caller_context_t *ct)
21154863Spraks {
21164863Spraks 	int		retval;
21174863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
21184863Spraks 
21195331Samw 	retval =  vnext_map(vf, off, as, addrp, len, prot, maxport,
21205331Samw 	    flags, cr, ct);
21214863Spraks 	port_fop(vp, FOP_FILE_MAP, retval);
21224863Spraks 	return (retval);
21234863Spraks }
21244863Spraks 
21254863Spraks static int
port_fop_read(femarg_t * vf,struct uio * uiop,int ioflag,struct cred * cr,caller_context_t * ct)21264863Spraks port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
21274863Spraks     caller_context_t *ct)
21284863Spraks {
21294863Spraks 	int		retval;
21304863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
21314863Spraks 
21324863Spraks 	retval =  vnext_read(vf, uiop, ioflag, cr, ct);
21334863Spraks 	port_fop(vp, FOP_FILE_READ, retval);
21344863Spraks 	return (retval);
21354863Spraks }
21364863Spraks 
21374863Spraks 
21384863Spraks /*
21394863Spraks  * AT_SIZE - is for the open(O_TRUNC) case.
21404863Spraks  */
21414863Spraks int
port_fop_setattr(femarg_t * vf,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)21424863Spraks port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
21434863Spraks     caller_context_t *ct)
21444863Spraks {
21454863Spraks 	int		retval;
21464863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
21474863Spraks 	int		events = 0;
21484863Spraks 
21494863Spraks 	retval = vnext_setattr(vf, vap, flags, cr, ct);
21504863Spraks 	if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
21514863Spraks 		events |= FOP_FILE_SETATTR_MTIME;
21524863Spraks 	}
21534863Spraks 	if (vap->va_mask & AT_ATIME) {
21544863Spraks 		events |= FOP_FILE_SETATTR_ATIME;
21554863Spraks 	}
21564863Spraks 	events |= FOP_FILE_SETATTR_CTIME;
21574863Spraks 
21584863Spraks 	port_fop(vp, events, retval);
21594863Spraks 	return (retval);
21604863Spraks }
21614863Spraks 
21624863Spraks 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)21634863Spraks port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
21645331Samw     int mode, vnode_t **vpp, cred_t *cr, int flag,
21655331Samw     caller_context_t *ct, vsecattr_t *vsecp)
21664863Spraks {
21674863Spraks 	int		retval, got = 1;
21684863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
21694863Spraks 	vattr_t		vatt, vatt1;
21704863Spraks 
21714863Spraks 	/*
21724863Spraks 	 * If the file already exists, then there will be no change
21734863Spraks 	 * to the directory. Therefore, we need to compare the
21744863Spraks 	 * modification time of the directory to determine if the
21754863Spraks 	 * file was actually created.
21764863Spraks 	 */
21774863Spraks 	vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
21785331Samw 	if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
21794863Spraks 		got = 0;
21804863Spraks 	}
21815331Samw 	retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
21825331Samw 	    flag, ct, vsecp);
21834863Spraks 
21844863Spraks 	vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
21855331Samw 	if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
21864863Spraks 		if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
21874863Spraks 		    (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
21884863Spraks 		    vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
21894863Spraks 			/*
21904863Spraks 			 * File was created.
21914863Spraks 			 */
21924863Spraks 			port_fop(vp, FOP_FILE_CREATE, retval);
21934863Spraks 		}
21944863Spraks 	}
21954863Spraks 	return (retval);
21964863Spraks }
21974863Spraks 
21984863Spraks int
port_fop_remove(femarg_t * vf,char * nm,cred_t * cr,caller_context_t * ct,int flags)21995331Samw port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
22005331Samw     int flags)
22014863Spraks {
22024863Spraks 	int		retval;
22034863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22044863Spraks 
22055331Samw 	retval = vnext_remove(vf, nm, cr, ct, flags);
22064863Spraks 	port_fop(vp, FOP_FILE_REMOVE, retval);
22074863Spraks 	return (retval);
22084863Spraks }
22094863Spraks 
22104863Spraks int
port_fop_link(femarg_t * vf,vnode_t * svp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)22115331Samw port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
22125331Samw     caller_context_t *ct, int flags)
22134863Spraks {
22144863Spraks 	int		retval;
22154863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22164863Spraks 
22175331Samw 	retval = vnext_link(vf, svp, tnm, cr, ct, flags);
22184863Spraks 	port_fop(vp, FOP_FILE_LINK, retval);
22194863Spraks 	return (retval);
22204863Spraks }
22214863Spraks 
22224863Spraks /*
22234863Spraks  * Rename operation is allowed only when from and to directories are
22244863Spraks  * on the same filesystem. This is checked in vn_rename().
22254863Spraks  * The target directory is notified thru a VNEVENT by the filesystem
22264863Spraks  * if the source dir != target dir.
22274863Spraks  */
22284863Spraks int
port_fop_rename(femarg_t * vf,char * snm,vnode_t * tdvp,char * tnm,cred_t * cr,caller_context_t * ct,int flags)22295331Samw port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
22305331Samw     caller_context_t *ct, int flags)
22314863Spraks {
22324863Spraks 	int		retval;
22334863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22344863Spraks 
22355331Samw 	retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
22364863Spraks 	port_fop(vp, FOP_FILE_RENAMESRC, retval);
22374863Spraks 	return (retval);
22384863Spraks }
22394863Spraks 
22404863Spraks 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)22414863Spraks port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
22425331Samw     cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
22434863Spraks {
22444863Spraks 	int		retval;
22454863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22464863Spraks 
22475331Samw 	retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
22484863Spraks 	port_fop(vp, FOP_FILE_MKDIR, retval);
22494863Spraks 	return (retval);
22504863Spraks }
22514863Spraks 
22524863Spraks int
port_fop_rmdir(femarg_t * vf,char * nm,vnode_t * cdir,cred_t * cr,caller_context_t * ct,int flags)22535331Samw port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
22545331Samw     caller_context_t *ct, int flags)
22554863Spraks {
22564863Spraks 	int		retval;
22574863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22584863Spraks 
22595331Samw 	retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
22604863Spraks 	port_fop(vp, FOP_FILE_RMDIR, retval);
22614863Spraks 	return (retval);
22624863Spraks }
22634863Spraks 
22644863Spraks int
port_fop_readdir(femarg_t * vf,uio_t * uiop,cred_t * cr,int * eofp,caller_context_t * ct,int flags)22655331Samw port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
22665331Samw     caller_context_t *ct, int flags)
22674863Spraks {
22684863Spraks 	int		retval;
22694863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22704863Spraks 
22715331Samw 	retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
22724863Spraks 	port_fop(vp, FOP_FILE_READDIR, retval);
22734863Spraks 	return (retval);
22744863Spraks }
22754863Spraks 
22764863Spraks int
port_fop_symlink(femarg_t * vf,char * linkname,vattr_t * vap,char * target,cred_t * cr,caller_context_t * ct,int flags)22774863Spraks port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
22785331Samw     cred_t *cr, caller_context_t *ct, int flags)
22794863Spraks {
22804863Spraks 	int		retval;
22814863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22824863Spraks 
22835331Samw 	retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
22844863Spraks 	port_fop(vp, FOP_FILE_SYMLINK, retval);
22854863Spraks 	return (retval);
22864863Spraks }
22874863Spraks 
22884863Spraks /*
22894863Spraks  * acl, facl call this.
22904863Spraks  */
22914863Spraks int
port_fop_setsecattr(femarg_t * vf,vsecattr_t * vsap,int flags,cred_t * cr,caller_context_t * ct)22925331Samw port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
22935331Samw     caller_context_t *ct)
22944863Spraks {
22954863Spraks 	int	retval;
22964863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
22975331Samw 	retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
22984863Spraks 	port_fop(vp, FOP_FILE_SETSECATTR, retval);
22994863Spraks 	return (retval);
23004863Spraks }
23014863Spraks 
23024863Spraks /*
23034863Spraks  * these are events on the watched file/directory
23044863Spraks  */
23054863Spraks int
port_fop_vnevent(femarg_t * vf,vnevent_t vnevent,vnode_t * dvp,char * name,caller_context_t * ct)23065331Samw port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
23075331Samw     caller_context_t *ct)
23084863Spraks {
23094863Spraks 	vnode_t		*vp = (vnode_t *)vf->fa_fnode->fn_available;
23104863Spraks 
23114863Spraks 	switch (vnevent) {
23124863Spraks 	case	VE_RENAME_SRC:
23134863Spraks 			port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
23144863Spraks 		break;
23154863Spraks 	case	VE_RENAME_DEST:
23164863Spraks 			port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
23174863Spraks 		break;
23184863Spraks 	case	VE_REMOVE:
23194863Spraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
23204863Spraks 		break;
23214863Spraks 	case	VE_RMDIR:
23224863Spraks 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
23234863Spraks 		break;
23244863Spraks 	case	VE_CREATE:
23254863Spraks 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
23264863Spraks 			    NULL, NULL);
23274863Spraks 		break;
23284863Spraks 	case	VE_LINK:
23294863Spraks 			port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
23304863Spraks 		break;
23314863Spraks 
23324863Spraks 	case	VE_RENAME_DEST_DIR:
23334863Spraks 			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
23344863Spraks 			    NULL, NULL);
23354863Spraks 		break;
23364863Spraks 
23374863Spraks 	case	VE_MOUNTEDOVER:
23384863Spraks 			port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
23394863Spraks 		break;
23404863Spraks 	default:
23414863Spraks 		break;
23424863Spraks 	}
23435331Samw 	return (vnext_vnevent(vf, vnevent, dvp, name, ct));
23444863Spraks }
2345