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