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