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