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