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