Merge commit '2cedd8f0ecbd2b29bf0aac72bb8b7413b0326938' into merges
[unleashed.git] / kernel / fs / portfs / port_fop.c
blobbd944b21162ade7fb59e6947258c0ff5210d62ad
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
31 * File Events Notification
32 * ------------------------
34 * The File Events Notification facility provides file and directory change
35 * notification. It is implemented as an event source(PORT_SOURCE_FILE)
36 * under the Event Ports framework. Therefore the API is an extension to
37 * the Event Ports API.
39 * It uses the FEM (File Events Monitoring) framework to intercept
40 * operations on the files & directories and generate appropriate events.
42 * It provides event notification in accordance with what an application
43 * can find out by stat`ing the file and comparing time stamps. The various
44 * system calls that update the file's access, modification, and change
45 * time stamps are documented in the man page section 2.
47 * It is non intrusive. That is, having an active file event watch on a file
48 * or directory will not prevent it from being removed or renamed or block an
49 * unmount operation of the file system where the watched file or directory
50 * resides.
53 * Interface:
54 * ----------
56 * The object for this event source is of type 'struct file_obj *'
58 * The file that needs to be monitored is specified in 'fo_name'.
59 * The time stamps collected by a stat(2) call are passed in fo_atime,
60 * fo_mtime, fo_ctime. At the time a file events watch is registered, the
61 * time stamps passed in are compared with the current time stamps of the
62 * file. If it has changed, relevant events are sent immediately. If the time
63 * stamps are all '0', they will not be compared.
66 * The events are delivered to an event port. A port is created using
67 * port_create().
69 * To register a file events watch on a file or directory.
71 * port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
73 * 'user' is the user pointer to be returned with the event.
75 * To de-register a file events watch,
77 * port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
79 * The events are collected using the port_get()/port_getn() interface. The
80 * event source will be PORT_SOURCE_FILE.
82 * After an event is delivered, the file events watch gets de-activated. To
83 * receive the next event, the process will have to re-register the watch and
84 * activate it by calling port_associate() again. This behavior is intentional
85 * and supports proper multi threaded programming when using file events
86 * notification API.
89 * Implementation overview:
90 * ------------------------
92 * Each file events watch is represented by 'portfop_t' in the kernel. A
93 * cache(in portfop_cache_t) of these portfop_t's are maintained per event
94 * port by this source. The object here is the pointer to the file_obj
95 * structure. The portfop_t's are hashed in using the object pointer. Therefore
96 * it is possible to have multiple file events watches on a file by the same
97 * process by using different object structure(file_obj_t) and hence can
98 * receive multiple event notification for a file. These watches can be for
99 * different event types.
101 * The cached entries of these file objects are retained, even after delivering
102 * an event, marking them inactive for performance reasons. The assumption
103 * is that the process would come back and re-register the file to receive
104 * further events. When there are more then 'port_fop_maxpfps' watches per file
105 * it will attempt to free the oldest inactive watches.
107 * In case the event that is being delivered is an exception event, the cached
108 * entries get removed. An exception event on a file or directory means its
109 * identity got changed(rename to/from, delete, mounted over, file system
110 * unmount).
112 * If the event port gets closed, all the associated file event watches will be
113 * removed and discarded.
116 * Data structures:
117 * ----------------
119 * The list of file event watches per file are managed by the data structure
120 * portfop_vp_t. The first time a file events watch is registered for a file,
121 * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
122 * removed and freed only when the vnode becomes inactive. The FEM hooks are
123 * also installed when the first watch is registered on a file. The FEM hooks
124 * get un-installed when all the watches are removed.
126 * Each file events watch is represented by the structure portfop_t. They
127 * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
128 * delivering an event, the portfop_t is marked inactive but retained. It is
129 * moved to the end of the list. All the active portfop_t's are maintained at
130 * the beginning. In case of exception events, the portfop_t will be removed
131 * and discarded.
133 * To intercept unmount operations, FSEM hooks are added to the file system
134 * under which files are being watched. A hash table('portfop_vfs_hash_t') of
135 * active file systems is maintained. Each file system that has active watches
136 * is represented by 'portfop_vfs_t' and is added to the hash table.
137 * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
138 * being watched on the portfop_vfs_t structure.
141 * File system support:
142 * -------------------
144 * The file system implementation has to provide vnode event notifications
145 * (vnevents) in order to support watching any files on that file system.
146 * The vnode events(vnevents) are notifications provided by the file system
147 * for name based file operations like rename, remove etc, which do not go
148 * thru the VOP_** interfaces. If the file system does not implement vnode
149 * notifications, watching for file events on such file systems is not
150 * supported. The vnode event notifications support is determined by the call
151 * vnevent_support(vp) (fop_vnevent(vp, VE_SUPPORT)), which the file system
152 * has to implement.
155 * Locking order:
156 * --------------
158 * A file(vnode) can have file event watches registered by different processes.
159 * There is one portfop_t per watch registered. These are on the vnode's list
160 * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
161 * also on the per port cache. The cache is protected by the pfc_lock of
162 * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
166 #include <sys/types.h>
167 #include <sys/systm.h>
168 #include <sys/stat.h>
169 #include <sys/errno.h>
170 #include <sys/kmem.h>
171 #include <sys/sysmacros.h>
172 #include <sys/debug.h>
173 #include <sys/vnode.h>
174 #include <sys/poll_impl.h>
175 #include <sys/port_impl.h>
176 #include <sys/fem.h>
177 #include <sys/vfs.h>
178 #include <sys/atomic.h>
179 #include <sys/mount.h>
180 #include <sys/mntent.h>
183 * For special case support of mnttab (/etc/mnttab).
185 extern struct vnode *vfs_mntdummyvp;
186 extern int mntfstype;
188 #define PORTFOP_PVFSH(vfsp) (&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
189 portfop_vfs_hash_t portvfs_hash[PORTFOP_PVFSHASH_SZ];
191 #define PORTFOP_NVP 20
193 * Inactive file event watches(portfop_t) are retained on the vnode's list
194 * for performance reason. If the applications re-registers the file, the
195 * inactive entry is made active and moved up the list.
197 * If there are greater then the following number of watches on a vnode,
198 * it will attempt to discard an oldest inactive watch(pfp) at the time
199 * a new watch is being registered and when events get delivered. We
200 * do this to avoid accumulating inactive watches on a file.
202 int port_fop_maxpfps = 20;
204 /* local functions */
205 static int port_fop_callback(void *, int *, pid_t, int, void *);
207 static void port_pcache_insert(portfop_cache_t *, portfop_t *);
208 static void port_pcache_delete(portfop_cache_t *, portfop_t *);
209 static void port_close_fop(void *arg, int port, pid_t pid, int lastclose);
212 * port fop functions that will be the fem hooks.
214 static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
215 caller_context_t *);
216 static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
217 struct caller_context *ct);
218 static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
219 caller_context_t *ct);
220 static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
221 caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
222 uint_t flags, cred_t *cr, caller_context_t *ct);
223 static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
224 caller_context_t *ct);
225 static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
226 vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
227 caller_context_t *ct, vsecattr_t *vsecp);
228 static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
229 caller_context_t *ct, int flags);
230 static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
231 caller_context_t *ct, int flags);
232 static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
233 cred_t *cr, caller_context_t *ct, int flags);
234 static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
235 vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
236 vsecattr_t *vsecp);
237 static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
238 caller_context_t *ct, int flags);
239 static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
240 caller_context_t *ct, int flags);
241 static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
242 char *target, cred_t *cr, caller_context_t *ct, int flags);
243 static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
244 cred_t *cr, caller_context_t *ct);
246 static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
247 char *cname, caller_context_t *ct);
249 static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
253 * Fem hooks.
255 static fem_t fop_femop = {
256 .name = "portfop_fem",
257 .femop_open = port_fop_open,
258 .femop_read = port_fop_read,
259 .femop_write = port_fop_write,
260 .femop_map = port_fop_map,
261 .femop_setattr = port_fop_setattr,
262 .femop_create = port_fop_create,
263 .femop_remove = port_fop_remove,
264 .femop_link = port_fop_link,
265 .femop_rename = port_fop_rename,
266 .femop_mkdir = port_fop_mkdir,
267 .femop_rmdir = port_fop_rmdir,
268 .femop_readdir = port_fop_readdir,
269 .femop_symlink = port_fop_symlink,
270 .femop_setsecattr = port_fop_setsecattr,
271 .femop_vnevent = port_fop_vnevent,
275 * Fsem - vfs ops hooks
278 static fsem_t fop_fsemop = {
279 .name = "portfop_fsem",
280 .fsemop_unmount = port_fop_unmount,
284 * port_fop_callback()
285 * - PORT_CALLBACK_DEFAULT
286 * The file event will be delivered to the application.
287 * - PORT_CALLBACK_DISSOCIATE
288 * The object will be dissociated from the port.
289 * - PORT_CALLBACK_CLOSE
290 * The object will be dissociated from the port because the port
291 * is being closed.
293 /* ARGSUSED */
294 static int
295 port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
297 portfop_t *pfp = (portfop_t *)arg;
298 port_kevent_t *pkevp = (port_kevent_t *)evp;
299 int error = 0;
301 ASSERT((events != NULL));
302 if (flag == PORT_CALLBACK_DEFAULT) {
303 if (curproc->p_pid != pid) {
304 return (EACCES); /* deny delivery of events */
307 *events = pkevp->portkev_events;
308 pkevp->portkev_events = 0;
309 if (pfp != NULL) {
310 pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
313 return (error);
317 * Inserts a portfop_t into the port sources cache's.
319 static void
320 port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
322 portfop_t **bucket;
324 ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
325 bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
326 pfp->pfop_hashnext = *bucket;
327 *bucket = pfp;
328 pfcp->pfc_objcount++;
332 * Remove the pfp from the port source cache.
334 static void
335 port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
337 portfop_t *lpdp;
338 portfop_t *cpdp;
339 portfop_t **bucket;
341 bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
342 cpdp = *bucket;
343 if (pfp == cpdp) {
344 *bucket = pfp->pfop_hashnext;
345 } else {
346 while (cpdp != NULL) {
347 lpdp = cpdp;
348 cpdp = cpdp->pfop_hashnext;
349 if (cpdp == pfp) {
350 /* portfop struct found */
351 lpdp->pfop_hashnext = pfp->pfop_hashnext;
352 break;
356 pfcp->pfc_objcount--;
360 * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
361 * when these routines are called.
363 * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
364 * It is used to discard the oldtest inactive pfp if the number of entries
365 * exceed the limit.
367 static void
368 port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
370 if (where == 1) {
371 list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
372 } else {
373 list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
375 if (pvp->pvp_lpfop == NULL) {
376 pvp->pvp_lpfop = pfp;
378 pvp->pvp_cnt++;
381 static void
382 port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
384 port_fop_listinsert(pvp, pfp, 1);
387 static void
388 port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
391 * We point lpfop to an inactive one, if it was initially pointing
392 * to an active one. Insert to the tail is done only when a pfp goes
393 * inactive.
395 if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
396 pvp->pvp_lpfop = pfp;
398 port_fop_listinsert(pvp, pfp, 0);
401 static void
402 port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
404 if (pvp->pvp_lpfop == pfp) {
405 pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
408 list_remove(&pvp->pvp_pfoplist, (void *)pfp);
410 pvp->pvp_cnt--;
411 if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
412 pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
416 static void
417 port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
419 list_move_tail(tlist, &pvp->pvp_pfoplist);
420 pvp->pvp_lpfop = NULL;
421 pvp->pvp_cnt = 0;
425 * Remove a portfop_t from the port cache hash table and discard it.
426 * It is called only when pfp is not on the vnode's list. Otherwise,
427 * port_remove_fop() is called.
429 void
430 port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
432 port_kevent_t *pkevp;
435 ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
437 pkevp = pfp->pfop_pev;
438 pfp->pfop_pev = NULL;
440 if (pkevp != NULL) {
441 (void) port_remove_done_event(pkevp);
442 port_free_event_local(pkevp, 0);
445 port_pcache_delete(pfcp, pfp);
447 if (pfp->pfop_cname != NULL)
448 kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
449 kmem_free(pfp, sizeof (portfop_t));
450 if (pfcp->pfc_objcount == 0)
451 cv_signal(&pfcp->pfc_lclosecv);
455 * if we have too many watches on the vnode, attempt to discard an
456 * inactive one.
458 static void
459 port_fop_trimpfplist(vnode_t *vp)
461 portfop_vp_t *pvp;
462 portfop_t *pfp = NULL;
463 portfop_cache_t *pfcp;
464 vnode_t *tdvp;
467 * Due to a reference the vnode cannot disappear, v_fopdata should
468 * not change.
470 if ((pvp = vp->v_fopdata) != NULL &&
471 pvp->pvp_cnt > port_fop_maxpfps) {
472 mutex_enter(&pvp->pvp_mutex);
473 pfp = pvp->pvp_lpfop;
474 pfcp = pfp->pfop_pcache;
476 * only if we can get the cache lock, we need to
477 * do this due to reverse lock order and some thread
478 * that may be trying to reactivate this entry.
480 if (mutex_tryenter(&pfcp->pfc_lock)) {
481 if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
482 !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
483 port_fop_listremove(pvp, pfp);
484 pfp->pfop_flags |= PORT_FOP_REMOVING;
485 } else {
486 mutex_exit(&pfcp->pfc_lock);
487 pfp = NULL;
489 } else {
490 pfp = NULL;
492 mutex_exit(&pvp->pvp_mutex);
495 * discard pfp if any.
497 if (pfp != NULL) {
498 tdvp = pfp->pfop_dvp;
499 port_pcache_remove_fop(pfcp, pfp);
500 mutex_exit(&pfcp->pfc_lock);
501 if (tdvp != NULL)
502 VN_RELE(tdvp);
508 * This routine returns 1, if the vnode can be rele'ed by the caller.
509 * The caller has to VN_RELE the vnode with out holding any
510 * locks.
513 port_fop_femuninstall(vnode_t *vp)
515 portfop_vp_t *pvp;
516 vfs_t *vfsp;
517 portfop_vfs_t *pvfsp;
518 portfop_vfs_hash_t *pvfsh;
519 kmutex_t *mtx;
520 int ret = 0;
523 * if list is empty, uninstall fem.
525 pvp = vp->v_fopdata;
526 ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
529 * make sure the list is empty.
531 if (!list_head(&pvp->pvp_pfoplist)) {
534 * we could possibly uninstall the fem hooks when
535 * the vnode becomes inactive and the v_fopdata is
536 * free. But the hooks get triggered unnecessarily
537 * even though there are no active watches. So, we
538 * uninstall it here.
540 (void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
541 pvp->pvp_femp = NULL;
545 * If we successfully uninstalled fem, no process is watching
546 * this vnode, Remove it from the vfs's list of watched vnodes.
548 pvfsp = pvp->pvp_pvfsp;
549 vfsp = vp->v_vfsp;
550 pvfsh = PORTFOP_PVFSH(vfsp);
551 mtx = &pvfsh->pvfshash_mutex;
552 mutex_enter(mtx);
554 * If unmount is in progress, that thread will remove and
555 * release the vnode from the vfs's list, just leave.
557 if (!pvfsp->pvfs_unmount) {
558 list_remove(&pvfsp->pvfs_pvplist, pvp);
559 mutex_exit(mtx);
560 ret = 1;
561 } else {
562 mutex_exit(mtx);
565 mutex_exit(&pvp->pvp_mutex);
566 return (ret);
570 * Remove pfp from the vnode's watch list and the cache and discard it.
571 * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
572 * Returns 1 if pfp removed successfully.
574 * The *active is set to indicate if the pfp was still active(no events had
575 * been posted, or the posted event had not been collected yet and it was
576 * able to remove it from the port's queue).
578 * vpp and dvpp will point to the vnode and directory vnode which the caller
579 * is required to VN_RELE without holding any locks.
582 port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
583 int *active, vnode_t **vpp, vnode_t **dvpp)
585 vnode_t *vp;
586 portfop_vp_t *pvp;
587 int tactive = 0;
589 ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
590 vp = pfp->pfop_vp;
591 pvp = vp->v_fopdata;
592 mutex_enter(&pvp->pvp_mutex);
595 * if not cleanup, remove it only if the pfp is still active and
596 * is not being removed by some other thread.
598 if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
599 pfp->pfop_flags & PORT_FOP_REMOVING)) {
600 mutex_exit(&pvp->pvp_mutex);
601 return (0);
605 * mark it inactive.
607 if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
608 pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
609 tactive = 1;
613 * Check if the pfp is still on the vnode's list. This can
614 * happen if port_fop_excep() is in the process of removing it.
615 * In case of cleanup, just mark this pfp as inactive so that no
616 * new events (VNEVENT) will be delivered, and remove it from the
617 * event queue if it was already queued. Since the cache lock is
618 * held, the pfp will not disappear, even though it is being
619 * removed.
621 if (pfp->pfop_flags & PORT_FOP_REMOVING) {
622 mutex_exit(&pvp->pvp_mutex);
623 if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
624 pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
625 tactive = 1;
627 if (active) {
628 *active = tactive;
630 return (1);
634 * if we find an event on the queue and removed it, then this
635 * association is considered active.
637 if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
638 pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
639 tactive = 1;
642 if (active) {
643 *active = tactive;
645 pvp = (portfop_vp_t *)vp->v_fopdata;
648 * remove pfp from the vnode's list
650 port_fop_listremove(pvp, pfp);
653 * If no more associations on the vnode, uninstall fem hooks.
654 * The pvp mutex will be released in this routine.
656 if (port_fop_femuninstall(vp))
657 *vpp = vp;
658 *dvpp = pfp->pfop_dvp;
659 port_pcache_remove_fop(pfcp, pfp);
660 return (1);
664 * This routine returns a pointer to a cached portfop entry, or NULL if it
665 * does not find it in the hash table. The object pointer is used as index.
666 * The entries are hashed by the object's address. We need to match the pid
667 * as the evet port can be shared between processes. The file events
668 * watches are per process only.
670 portfop_t *
671 port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
673 portfop_t *pfp = NULL;
674 portfop_t **bucket;
676 ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
677 bucket = PORT_FOP_BUCKET(pfcp, obj);
678 pfp = *bucket;
679 while (pfp != NULL) {
680 if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
681 break;
682 pfp = pfp->pfop_hashnext;
684 return (pfp);
688 * Given the file name, get the vnode and also the directory vnode
689 * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
690 * the vnode(s).
693 port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp,
694 char **cname, int *len, int follow)
696 int error = 0;
697 struct pathname pn;
698 char *fname;
700 if (get_udatamodel() == DATAMODEL_NATIVE) {
701 fname = ((file_obj_t *)objptr)->fo_name;
702 #ifdef _SYSCALL32_IMPL
703 } else {
704 fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
705 #endif /* _SYSCALL32_IMPL */
709 * lookuppn may fail with EINVAL, if dvp is non-null(like when
710 * looking for "."). So call again with dvp = NULL.
712 if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
713 return (error);
716 error = lookuppn(&pn, NULL, follow, dvp, vp);
717 if (error == EINVAL) {
718 pn_free(&pn);
719 if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
720 return (error);
722 error = lookuppn(&pn, NULL, follow, NULL, vp);
723 if (dvp != NULL) {
724 *dvp = NULL;
728 if (error == 0 && cname != NULL && len != NULL) {
729 pn_setlast(&pn);
730 *len = pn.pn_pathlen;
731 *cname = kmem_alloc(*len + 1, KM_SLEEP);
732 (void) strcpy(*cname, pn.pn_path);
733 } else {
734 if (cname != NULL && len != NULL) {
735 *cname = NULL;
736 *len = 0;
740 pn_free(&pn);
741 return (error);
744 port_source_t *
745 port_getsrc(port_t *pp, int source)
747 port_source_t *pse;
748 int lock = 0;
750 * get the port source structure.
752 if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
753 mutex_enter(&pp->port_queue.portq_source_mutex);
754 lock = 1;
757 pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
758 for (; pse != NULL; pse = pse->portsrc_next) {
759 if (pse->portsrc_source == source)
760 break;
763 if (lock) {
764 mutex_exit(&pp->port_queue.portq_source_mutex);
766 return (pse);
771 * Compare time stamps and generate an event if it has changed.
772 * Note that the port cache pointer will be valid due to a reference
773 * to the port. We need to grab the port cache lock and verify that
774 * the pfp is still the same before proceeding to deliver an event.
776 static void
777 port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
778 portfop_t *pfp, void *objptr, uintptr_t object)
780 vattr_t vatt;
781 portfop_vp_t *pvp = vp->v_fopdata;
782 int events = 0;
783 port_kevent_t *pkevp;
784 file_obj_t *fobj;
785 portfop_t *tpfp;
788 * If time stamps are specified, get attributes and compare.
790 vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
791 if (get_udatamodel() == DATAMODEL_NATIVE) {
792 fobj = (file_obj_t *)objptr;
793 if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
794 fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
795 fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
796 if (fop_getattr(vp, &vatt, 0, CRED(), NULL)) {
797 return;
799 } else {
801 * timestamp not specified, all 0's,
803 return;
805 #ifdef _SYSCALL32_IMPL
806 } else {
807 file_obj32_t *fobj32;
808 fobj32 = (file_obj32_t *)objptr;
809 if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
810 fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
811 fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
812 if (fop_getattr(vp, &vatt, 0, CRED(), NULL)) {
813 return;
815 } else {
817 * timestamp not specified, all 0.
819 return;
821 #endif /* _SYSCALL32_IMPL */
825 * Now grab the cache lock and verify that we are still
826 * dealing with the same pfp and curthread is the one
827 * which registered it. We need to do this to avoid
828 * delivering redundant events.
830 mutex_enter(&pfcp->pfc_lock);
831 tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
833 if (tpfp == NULL || tpfp != pfp ||
834 pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
835 pfp->pfop_callrid != curthread ||
836 !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
838 * Some other event was delivered, the file
839 * watch was removed or reassociated. Just
840 * ignore it and leave
842 mutex_exit(&pfcp->pfc_lock);
843 return;
846 mutex_enter(&pvp->pvp_mutex);
848 * The pfp cannot disappear as the port cache lock is held.
849 * While the pvp_mutex is held, no events will get delivered.
851 if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
852 !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
853 if (get_udatamodel() == DATAMODEL_NATIVE) {
854 fobj = (file_obj_t *)objptr;
855 if (pfp->pfop_events & FILE_ACCESS &&
856 (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
857 (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
858 vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
859 events |= FILE_ACCESS;
861 if (pfp->pfop_events & FILE_MODIFIED &&
862 (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
863 (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
864 vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
865 events |= FILE_MODIFIED;
867 if (pfp->pfop_events & FILE_ATTRIB &&
868 (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
869 (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
870 vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
871 events |= FILE_ATTRIB;
872 #ifdef _SYSCALL32_IMPL
873 } else {
874 file_obj32_t *fobj32;
875 fobj32 = (file_obj32_t *)objptr;
876 if (pfp->pfop_events & FILE_ACCESS &&
877 (fobj32->fo_atime.tv_sec ||
878 fobj32->fo_atime.tv_nsec) &&
879 (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
880 vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
881 events |= FILE_ACCESS;
883 if (pfp->pfop_events & FILE_MODIFIED &&
884 (fobj32->fo_mtime.tv_sec ||
885 fobj32->fo_mtime.tv_nsec) &&
886 (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
887 vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
888 events |= FILE_MODIFIED;
890 if (pfp->pfop_events & FILE_ATTRIB &&
891 (fobj32->fo_ctime.tv_sec ||
892 fobj32->fo_ctime.tv_nsec) &&
893 (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
894 vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
895 events |= FILE_ATTRIB;
896 #endif /* _SYSCALL32_IMPL */
900 * No events to deliver
902 if (events == 0) {
903 mutex_exit(&pvp->pvp_mutex);
904 mutex_exit(&pfcp->pfc_lock);
905 return;
909 * Deliver the event now.
911 pkevp = pfp->pfop_pev;
912 pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
913 pkevp->portkev_events |= events;
915 * Move it to the tail as active once are in the
916 * beginning of the list.
918 port_fop_listremove(pvp, pfp);
919 port_fop_listinsert_tail(pvp, pfp);
920 port_send_event(pkevp);
921 pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
923 mutex_exit(&pvp->pvp_mutex);
924 mutex_exit(&pfcp->pfc_lock);
928 * Add the event source to the port and return the port source cache pointer.
931 port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
933 portfop_cache_t *pfcp;
934 port_source_t *pse;
935 int error;
938 * associate PORT_SOURCE_FILE source with the port, if it is
939 * not associated yet. Note the PORT_SOURCE_FILE source is
940 * associated once and will not be dissociated.
942 if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
943 if (error = port_associate_ksource(pp->port_fd, source,
944 &pse, port_close_fop, pp, NULL)) {
945 *pfcpp = NULL;
946 return (error);
951 * Get the portfop cache pointer.
953 if ((pfcp = pse->portsrc_data) == NULL) {
955 * This is the first time that a file is being associated,
956 * create the portfop cache.
958 pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
959 mutex_enter(&pp->port_queue.portq_source_mutex);
960 if (pse->portsrc_data == NULL) {
961 pse->portsrc_data = pfcp;
962 mutex_exit(&pp->port_queue.portq_source_mutex);
963 } else {
965 * someone else created the port cache, free
966 * what we just now allocated.
968 mutex_exit(&pp->port_queue.portq_source_mutex);
969 kmem_free(pfcp, sizeof (portfop_cache_t));
970 pfcp = pse->portsrc_data;
973 *pfcpp = pfcp;
974 return (0);
978 * Add the given pvp on the file system's list of vnodes watched.
981 port_fop_pvfsadd(portfop_vp_t *pvp)
983 int error = 0;
984 vnode_t *vp = pvp->pvp_vp;
985 portfop_vfs_hash_t *pvfsh;
986 portfop_vfs_t *pvfsp;
988 pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
989 mutex_enter(&pvfsh->pvfshash_mutex);
990 for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
991 pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
994 if (!pvfsp) {
995 if ((error = fsem_install(vp->v_vfsp, &fop_fsemop,
996 vp->v_vfsp, OPUNIQ, NULL, NULL))) {
997 mutex_exit(&pvfsh->pvfshash_mutex);
998 return (error);
1001 pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
1002 pvfsp->pvfs = vp->v_vfsp;
1003 list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
1004 offsetof(portfop_vp_t, pvp_pvfsnode));
1005 pvfsp->pvfs_fsemp = &fop_fsemop;
1006 pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
1007 pvfsh->pvfshash_pvfsp = pvfsp;
1011 * check if an unmount is in progress.
1013 if (!pvfsp->pvfs_unmount) {
1015 * insert the pvp on list.
1017 pvp->pvp_pvfsp = pvfsp;
1018 list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
1019 } else {
1020 error = EINVAL;
1022 mutex_exit(&pvfsh->pvfshash_mutex);
1023 return (error);
1027 * Installs the portfop_vp_t data structure on the
1028 * vnode. The 'pvp_femp == NULL' indicates it is not
1029 * active. The fem hooks have to be installed.
1030 * The portfop_vp_t is only freed when the vnode gets freed.
1032 void
1033 port_install_fopdata(vnode_t *vp)
1035 portfop_vp_t *npvp;
1037 npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
1038 mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
1039 list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
1040 offsetof(portfop_t, pfop_node));
1041 npvp->pvp_vp = vp;
1043 * If v_fopdata is not null, some other thread beat us to it.
1045 if (atomic_cas_ptr(&vp->v_fopdata, NULL, npvp) != NULL) {
1046 mutex_destroy(&npvp->pvp_mutex);
1047 list_destroy(&npvp->pvp_pfoplist);
1048 kmem_free(npvp, sizeof (*npvp));
1054 * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
1055 * to the vnode's list. The association is identified by the object pointer
1056 * address and pid.
1059 port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
1060 uintptr_t object, int events, void *user, char *cname, int clen,
1061 vnode_t *dvp)
1063 portfop_t *pfp = NULL;
1064 port_kevent_t *pkevp;
1065 int error = 0;
1066 portfop_vp_t *pvp;
1070 * The port cache mutex is held.
1072 *pfpp = NULL;
1076 * At this point the fem monitor is installed.
1077 * Allocate a port event structure per vnode association.
1079 if (pfp == NULL) {
1080 if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1081 PORT_ALLOC_CACHED, &pkevp)) {
1082 return (error);
1084 pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
1085 pfp->pfop_pev = pkevp;
1088 pfp->pfop_vp = vp;
1089 pfp->pfop_pid = curproc->p_pid;
1090 pfp->pfop_pcache = pfcp;
1091 pfp->pfop_pp = pp;
1092 pfp->pfop_flags |= PORT_FOP_ACTIVE;
1093 pfp->pfop_cname = cname;
1094 pfp->pfop_clen = clen;
1095 pfp->pfop_dvp = dvp;
1096 pfp->pfop_object = object;
1098 pkevp->portkev_callback = port_fop_callback;
1099 pkevp->portkev_arg = pfp;
1100 pkevp->portkev_object = object;
1101 pkevp->portkev_user = user;
1102 pkevp->portkev_events = 0;
1104 port_pcache_insert(pfcp, pfp);
1107 * Register a new file events monitor for this file(vnode), if not
1108 * done already.
1110 if ((pvp = vp->v_fopdata) == NULL) {
1111 port_install_fopdata(vp);
1112 pvp = vp->v_fopdata;
1115 mutex_enter(&pvp->pvp_mutex);
1117 * if the vnode does not have the file events hooks, install it.
1119 if (pvp->pvp_femp == NULL) {
1120 if (!(error = fem_install(pfp->pfop_vp, &fop_femop, vp, OPUNIQ,
1121 NULL, NULL))) {
1122 pvp->pvp_femp = &fop_femop;
1124 * add fsem_t hooks to the vfsp and add pvp to
1125 * the list of vnodes for this vfs.
1127 if (!(error = port_fop_pvfsadd(pvp))) {
1129 * Hold a reference to the vnode since
1130 * we successfully installed the hooks.
1132 VN_HOLD(vp);
1133 } else {
1134 (void) fem_uninstall(vp, &fop_femop, vp);
1135 pvp->pvp_femp = NULL;
1140 if (error) {
1142 * pkevp will get freed here.
1144 pfp->pfop_cname = NULL;
1145 port_pcache_remove_fop(pfcp, pfp);
1146 mutex_exit(&pvp->pvp_mutex);
1147 return (error);
1151 * insert the pfp on the vnode's list. After this
1152 * events can get delivered.
1154 pfp->pfop_events = events;
1155 port_fop_listinsert_head(pvp, pfp);
1157 mutex_exit(&pvp->pvp_mutex);
1159 * Hold the directory vnode since we have a reference now.
1161 if (dvp != NULL)
1162 VN_HOLD(dvp);
1163 *pfpp = pfp;
1164 return (0);
1167 vnode_t *
1168 port_resolve_vp(vnode_t *vp)
1170 vnode_t *rvp;
1172 * special case /etc/mnttab(mntfs type). The mntfstype != 0
1173 * if mntfs got mounted.
1175 if (vfs_mntdummyvp && mntfstype != 0 &&
1176 vp->v_vfsp->vfs_fstype == mntfstype) {
1177 VN_RELE(vp);
1178 vp = vfs_mntdummyvp;
1179 VN_HOLD(vfs_mntdummyvp);
1183 * This should take care of lofs mounted fs systems and nfs4
1184 * hardlinks.
1186 if ((fop_realvp(vp, &rvp, NULL) == 0) && vp != rvp) {
1187 VN_HOLD(rvp);
1188 VN_RELE(vp);
1189 vp = rvp;
1191 return (vp);
1195 * Register a file events watch on the given file associated to the port *pp.
1197 * The association is identified by the object pointer and the pid.
1198 * The events argument contains the events to be monitored for.
1200 * The vnode will have a VN_HOLD once the fem hooks are installed.
1202 * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
1203 * that the directory vnode pointer does not change.
1206 port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
1207 void *user)
1209 portfop_cache_t *pfcp;
1210 vnode_t *vp, *dvp, *oldvp = NULL, *olddvp = NULL, *orig;
1211 portfop_t *pfp;
1212 int error = 0;
1213 file_obj_t fobj;
1214 void *objptr;
1215 char *cname;
1216 int clen;
1217 int follow;
1220 * check that events specified are valid.
1222 if ((events & ~FILE_EVENTS_MASK) != 0)
1223 return (EINVAL);
1225 if (get_udatamodel() == DATAMODEL_NATIVE) {
1226 if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
1227 return (EFAULT);
1228 objptr = (void *)&fobj;
1229 #ifdef _SYSCALL32_IMPL
1230 } else {
1231 file_obj32_t fobj32;
1232 if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
1233 return (EFAULT);
1234 objptr = (void *)&fobj32;
1235 #endif /* _SYSCALL32_IMPL */
1238 vp = dvp = NULL;
1241 * find out if we need to follow symbolic links.
1243 follow = !(events & FILE_NOFOLLOW);
1244 events = events & ~FILE_NOFOLLOW;
1247 * lookup and find the vnode and its directory vnode of the given
1248 * file.
1250 if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
1251 follow)) != 0) {
1252 return (error);
1255 if (dvp != NULL) {
1256 dvp = port_resolve_vp(dvp);
1260 * Not found
1262 if (vp == NULL) {
1263 error = ENOENT;
1264 goto errout;
1267 vp = port_resolve_vp(orig = vp);
1269 if (vp != NULL && vnevent_support(vp, NULL)) {
1270 error = ENOTSUP;
1271 goto errout;
1275 * If dvp belongs to a different filesystem just ignore it, as hard
1276 * links cannot exist across filesystems. We make an exception for
1277 * procfs, however, the magic of which we treat semantically as a hard
1278 * link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE
1279 * and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events.
1281 if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp &&
1282 !(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) {
1283 VN_RELE(dvp);
1284 dvp = NULL;
1288 * Associate this source to the port and get the per port
1289 * fop cache pointer. If the source is already associated, it
1290 * will just return the cache pointer.
1292 if (error = port_fop_associate_source(&pfcp, pp, source)) {
1293 goto errout;
1297 * Check if there is an existing association of this file.
1299 mutex_enter(&pfcp->pfc_lock);
1300 pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1303 * If it is not the same vnode, just discard it. VN_RELE needs to be
1304 * called with no locks held, therefore save vnode pointers and
1305 * vn_rele them later.
1307 if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
1308 (void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
1309 pfp = NULL;
1312 if (pfp == NULL) {
1313 vnode_t *tvp, *tdvp;
1314 portfop_t *tpfp;
1315 int error;
1318 * Add a new association, save the file name and the
1319 * directory vnode pointer.
1321 if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
1322 events, user, cname, clen, dvp)) {
1323 mutex_exit(&pfcp->pfc_lock);
1324 goto errout;
1327 pfp->pfop_callrid = curthread;
1329 * File name used, so make sure we don't free it.
1331 cname = NULL;
1334 * We need to check if the file was removed after the
1335 * the lookup and before the fem hooks where added. If
1336 * so, return error. The vnode will still exist as we have
1337 * a hold on it.
1339 * Drop the cache lock before calling port_fop_getdvp().
1340 * port_fop_getdvp() may block either in the vfs layer
1341 * or some filesystem. Therefore there is potential
1342 * for deadlock if cache lock is held and if some other
1343 * thread is attempting to deliver file events which would
1344 * require getting the cache lock, while it may be holding
1345 * the filesystem or vfs layer locks.
1347 mutex_exit(&pfcp->pfc_lock);
1348 tvp = NULL;
1349 if ((error = port_fop_getdvp(objptr, &tvp, NULL,
1350 NULL, NULL, follow)) == 0) {
1351 if (tvp != NULL) {
1352 tvp = port_resolve_vp(tvp);
1354 * This vnode pointer is just used
1355 * for comparison, so rele it
1357 VN_RELE(tvp);
1361 if (error || tvp == NULL || tvp != vp) {
1363 * Since we dropped the cache lock, make sure
1364 * we are still dealing with the same pfp and this
1365 * is the thread which registered it.
1367 mutex_enter(&pfcp->pfc_lock);
1368 tpfp = port_cache_lookup_fop(pfcp,
1369 curproc->p_pid, object);
1371 error = 0;
1372 if (tpfp == NULL || tpfp != pfp ||
1373 pfp->pfop_vp != vp ||
1374 pfp->pfop_dvp != dvp ||
1375 pfp->pfop_callrid != curthread) {
1377 * Some other event was delivered, the file
1378 * watch was removed or reassociated, just
1379 * ignore it and leave
1381 mutex_exit(&pfcp->pfc_lock);
1382 goto errout;
1386 * remove the pfp and fem hooks, if pfp still
1387 * active and it is not being removed from
1388 * the vnode list. This is checked in
1389 * port_remove_fop with the vnode lock held.
1390 * The vnode returned is VN_RELE'ed after dropping
1391 * the locks.
1393 tdvp = tvp = NULL;
1394 if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
1396 * The pfp was removed, means no
1397 * events where queued. Report the
1398 * error now.
1400 error = EINVAL;
1402 mutex_exit(&pfcp->pfc_lock);
1403 if (tvp != NULL)
1404 VN_RELE(tvp);
1405 if (tdvp != NULL)
1406 VN_RELE(tdvp);
1407 goto errout;
1409 } else {
1410 portfop_vp_t *pvp = vp->v_fopdata;
1413 * Re-association of the object.
1415 mutex_enter(&pvp->pvp_mutex);
1418 * remove any queued up event.
1420 if (port_remove_done_event(pfp->pfop_pev)) {
1421 pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
1425 * set new events to watch.
1427 pfp->pfop_events = events;
1430 * If not active, mark it active even if it is being
1431 * removed. Then it can send an exception event.
1433 * Move it to the head, as the active ones are only
1434 * in the beginning. If removing, the pfp will be on
1435 * a temporary list, no need to move it to the front
1436 * all the entries will be processed. Some exception
1437 * events will be delivered in port_fop_excep();
1439 if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
1440 pfp->pfop_flags |= PORT_FOP_ACTIVE;
1441 if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
1442 pvp = (portfop_vp_t *)vp->v_fopdata;
1443 port_fop_listremove(pvp, pfp);
1444 port_fop_listinsert_head(pvp, pfp);
1447 pfp->pfop_callrid = curthread;
1448 mutex_exit(&pvp->pvp_mutex);
1449 mutex_exit(&pfcp->pfc_lock);
1453 * Compare time stamps and deliver events.
1455 if (vp->v_type != VFIFO) {
1456 port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
1459 error = 0;
1462 * If we have too many watches on the vnode, discard an
1463 * inactive watch.
1465 port_fop_trimpfplist(vp);
1467 errout:
1469 * Release the hold acquired due to the lookup operation.
1471 if (vp != NULL)
1472 VN_RELE(vp);
1473 if (dvp != NULL)
1474 VN_RELE(dvp);
1476 if (oldvp != NULL)
1477 VN_RELE(oldvp);
1478 if (olddvp != NULL)
1479 VN_RELE(olddvp);
1482 * copied file name not used, free it.
1484 if (cname != NULL) {
1485 kmem_free(cname, clen + 1);
1487 return (error);
1492 * The port_dissociate_fop() function dissociates the file object
1493 * from the event port and removes any events that are already on the queue.
1494 * Only the owner of the association is allowed to dissociate the file from
1495 * the port. Returns success (0) if it was found and removed. Otherwise
1496 * ENOENT.
1499 port_dissociate_fop(port_t *pp, uintptr_t object)
1501 portfop_cache_t *pfcp;
1502 portfop_t *pfp;
1503 port_source_t *pse;
1504 int active = 0;
1505 vnode_t *tvp = NULL, *tdvp = NULL;
1507 pse = port_getsrc(pp, PORT_SOURCE_FILE);
1510 * if this source is not associated or if there is no
1511 * cache, nothing to do just return.
1513 if (pse == NULL ||
1514 (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1515 return (EINVAL);
1518 * Check if this object is on the cache. Only the owner pid
1519 * is allowed to dissociate.
1521 mutex_enter(&pfcp->pfc_lock);
1522 pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1523 if (pfp == NULL) {
1524 mutex_exit(&pfcp->pfc_lock);
1525 return (ENOENT);
1529 * If this was the last association, it will release
1530 * the hold on the vnode. There is a race condition where
1531 * the the pfp is being removed due to an exception event
1532 * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
1533 * Since port source cache lock is held, port_fop_excep() cannot
1534 * complete. The vnode itself will not disappear as long its pfps
1535 * have a reference.
1537 (void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
1538 mutex_exit(&pfcp->pfc_lock);
1539 if (tvp != NULL)
1540 VN_RELE(tvp);
1541 if (tdvp != NULL)
1542 VN_RELE(tdvp);
1543 return (active ? 0 : ENOENT);
1548 * port_close() calls this function to request the PORT_SOURCE_FILE source
1549 * to remove/free all resources allocated and associated with the port.
1552 /* ARGSUSED */
1553 static void
1554 port_close_fop(void *arg, int port, pid_t pid, int lastclose)
1556 port_t *pp = arg;
1557 portfop_cache_t *pfcp;
1558 portfop_t **hashtbl;
1559 portfop_t *pfp;
1560 portfop_t *pfpnext;
1561 int index, i;
1562 port_source_t *pse;
1563 vnode_t *tdvp = NULL;
1564 vnode_t *vpl[PORTFOP_NVP];
1566 pse = port_getsrc(pp, PORT_SOURCE_FILE);
1569 * No source or no cache, nothing to do.
1571 if (pse == NULL ||
1572 (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1573 return;
1575 * Scan the cache and free all allocated portfop_t and port_kevent_t
1576 * structures of this pid. Note, no new association for this pid will
1577 * be possible as the port is being closed.
1579 * The common case is that the port is not shared and all the entries
1580 * are of this pid and have to be freed. Since VN_RELE has to be
1581 * called outside the lock, we do it in batches.
1583 hashtbl = (portfop_t **)pfcp->pfc_hash;
1584 index = i = 0;
1585 bzero(vpl, sizeof (vpl));
1586 mutex_enter(&pfcp->pfc_lock);
1587 while (index < PORTFOP_HASHSIZE) {
1588 pfp = hashtbl[index];
1589 while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
1590 pfpnext = pfp->pfop_hashnext;
1591 if (pid == pfp->pfop_pid) {
1592 (void) port_remove_fop(pfp, pfcp, 1, NULL,
1593 &vpl[i], &tdvp);
1594 if (vpl[i] != NULL) {
1595 i++;
1597 if (tdvp != NULL) {
1598 vpl[i++] = tdvp;
1599 tdvp = NULL;
1602 pfp = pfpnext;
1604 if (pfp == NULL)
1605 index++;
1607 * Now call VN_RELE if we have collected enough vnodes or
1608 * we have reached the end of the hash table.
1610 if (i >= (PORTFOP_NVP - 1) ||
1611 (i > 0 && index == PORTFOP_HASHSIZE)) {
1612 mutex_exit(&pfcp->pfc_lock);
1613 while (i > 0) {
1614 VN_RELE(vpl[--i]);
1615 vpl[i] = NULL;
1617 mutex_enter(&pfcp->pfc_lock);
1622 * Due to a race between port_close_fop() and port_fop()
1623 * trying to remove the pfp's from the port's cache, it is
1624 * possible that some pfp's are still in the process of being
1625 * freed so we wait.
1627 while (lastclose && pfcp->pfc_objcount) {
1628 (void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
1630 mutex_exit(&pfcp->pfc_lock);
1632 * last close, free the cache.
1634 if (lastclose) {
1635 ASSERT(pfcp->pfc_objcount == 0);
1636 pse->portsrc_data = NULL;
1637 kmem_free(pfcp, sizeof (portfop_cache_t));
1642 * Given the list of associations(watches), it will send exception events,
1643 * if still active, and discard them. The exception events are handled
1644 * separately because, the pfp needs to be removed from the port cache and
1645 * freed as the vnode's identity is changing or being removed. To remove
1646 * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
1647 * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
1648 * the cache's lock cannot be acquired in port_fop_sendevent().
1650 static void
1651 port_fop_excep(list_t *tlist, int op)
1653 portfop_t *pfp;
1654 portfop_cache_t *pfcp;
1655 port_t *pp;
1656 port_kevent_t *pkevp;
1657 vnode_t *tdvp;
1658 int error = 0;
1660 while (pfp = (portfop_t *)list_head(tlist)) {
1661 int removed = 0;
1663 * remove from the temp list. Since PORT_FOP_REMOVING is
1664 * set, no other thread should attempt to perform a
1665 * list_remove on this pfp.
1667 list_remove(tlist, pfp);
1669 pfcp = pfp->pfop_pcache;
1670 mutex_enter(&pfcp->pfc_lock);
1673 * Remove the event from the port queue if it was queued up.
1674 * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
1675 * no longer on the vnode's list.
1677 if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
1678 removed = port_remove_done_event(pfp->pfop_pev);
1682 * If still active or the event was queued up and
1683 * had not been collected yet, send an EXCEPTION event.
1685 if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
1686 pp = pfp->pfop_pp;
1688 * Allocate a port_kevent_t non cached to send this
1689 * event since we will be de-registering.
1690 * The port_kevent_t cannot be pointing back to the
1691 * pfp anymore.
1693 pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1694 error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1695 PORT_ALLOC_DEFAULT, &pkevp);
1696 if (!error) {
1698 pkevp->portkev_callback = port_fop_callback;
1699 pkevp->portkev_arg = NULL;
1700 pkevp->portkev_object =
1701 pfp->pfop_pev->portkev_object;
1702 pkevp->portkev_user =
1703 pfp->pfop_pev->portkev_user;
1705 * Copy the pid of the watching process.
1707 pkevp->portkev_pid =
1708 pfp->pfop_pev->portkev_pid;
1709 pkevp->portkev_events = op;
1710 port_send_event(pkevp);
1714 * At this point the pfp has been removed from the vnode's
1715 * list its cached port_kevent_t is not on the done queue.
1716 * Remove the pfp and free it from the cache.
1718 tdvp = pfp->pfop_dvp;
1719 port_pcache_remove_fop(pfcp, pfp);
1720 mutex_exit(&pfcp->pfc_lock);
1721 if (tdvp != NULL)
1722 VN_RELE(tdvp);
1727 * Send the file events to all of the processes watching this
1728 * vnode. In case of hard links, the directory vnode pointer and
1729 * the file name are compared. If the names match, then the specified
1730 * event is sent or else, the FILE_ATTRIB event is sent, This is the
1731 * documented behavior.
1733 void
1734 port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
1736 port_kevent_t *pkevp;
1737 portfop_t *pfp, *npfp;
1738 portfop_vp_t *pvp;
1739 list_t tmplist;
1740 int removeall = 0;
1742 pvp = (portfop_vp_t *)vp->v_fopdata;
1743 mutex_enter(&pvp->pvp_mutex);
1746 * Check if the list is empty.
1748 * All entries have been removed by some other thread.
1749 * The vnode may be still active and we got called,
1750 * but some other thread is in the process of removing the hooks.
1752 if (!list_head(&pvp->pvp_pfoplist)) {
1753 mutex_exit(&pvp->pvp_mutex);
1754 return;
1757 if ((events & (FILE_EXCEPTION))) {
1759 * If it is an event for which we are going to remove
1760 * the watches so just move it a temporary list and
1761 * release this vnode.
1763 list_create(&tmplist, sizeof (portfop_t),
1764 offsetof(portfop_t, pfop_node));
1767 * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
1768 * passed for an exception event, all associations need to be
1769 * removed.
1771 if (dvp == NULL || cname == NULL) {
1772 removeall = 1;
1776 if (!removeall) {
1778 * All the active ones are in the beginning of the list.
1779 * Note that we process this list in reverse order to assure
1780 * that events are delivered in the order that they were
1781 * associated.
1783 for (pfp = (portfop_t *)list_tail(&pvp->pvp_pfoplist);
1784 pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE); pfp = npfp) {
1785 npfp = list_prev(&pvp->pvp_pfoplist, pfp);
1788 for (; pfp != NULL; pfp = npfp) {
1789 int levents = events;
1791 npfp = list_prev(&pvp->pvp_pfoplist, pfp);
1793 * Hard links case - If the file is being
1794 * removed/renamed, and the name matches
1795 * the watched file, then it is an EXCEPTION
1796 * event or else it will be just a FILE_ATTRIB.
1798 if ((events & (FILE_EXCEPTION))) {
1799 ASSERT(dvp != NULL && cname != NULL);
1800 if (pfp->pfop_dvp == NULL ||
1801 (pfp->pfop_dvp == dvp &&
1802 (strcmp(cname, pfp->pfop_cname) == 0))) {
1804 * It is an exception event, move it
1805 * to temp list and process it later.
1806 * Note we don't set the pfp->pfop_vp
1807 * to NULL even thought it has been
1808 * removed from the vnode's list. This
1809 * pointer is referenced in
1810 * port_remove_fop(). The vnode it
1811 * self cannot disappear until this
1812 * pfp gets removed and freed.
1814 port_fop_listremove(pvp, pfp);
1815 list_insert_tail(&tmplist, (void *)pfp);
1816 pfp->pfop_flags |= PORT_FOP_REMOVING;
1817 continue;
1818 } else {
1819 levents = FILE_ATTRIB;
1824 if (pfp->pfop_events & levents) {
1826 * deactivate and move it to the tail.
1827 * If the pfp was active, it cannot be
1828 * on the port's done queue.
1830 pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1831 port_fop_listremove(pvp, pfp);
1832 port_fop_listinsert_tail(pvp, pfp);
1834 pkevp = pfp->pfop_pev;
1835 pkevp->portkev_events |=
1836 (levents & pfp->pfop_events);
1837 port_send_event(pkevp);
1838 pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
1844 if ((events & (FILE_EXCEPTION))) {
1845 if (!removeall) {
1847 * Check the inactive associations and remove them if
1848 * the file name matches.
1850 for (; pfp; pfp = npfp) {
1851 npfp = list_next(&pvp->pvp_pfoplist, pfp);
1852 if (dvp == NULL || cname == NULL ||
1853 pfp->pfop_dvp == NULL ||
1854 (pfp->pfop_dvp == dvp &&
1855 (strcmp(cname, pfp->pfop_cname) == 0))) {
1856 port_fop_listremove(pvp, pfp);
1857 list_insert_tail(&tmplist, (void *)pfp);
1858 pfp->pfop_flags |= PORT_FOP_REMOVING;
1861 } else {
1863 * Can be optimized to avoid two pass over this list
1864 * by having a flag in the vnode's portfop_vp_t
1865 * structure to indicate that it is going away,
1866 * Or keep the list short by reusing inactive watches.
1868 port_fop_listmove(pvp, &tmplist);
1869 for (pfp = (portfop_t *)list_head(&tmplist);
1870 pfp; pfp = list_next(&tmplist, pfp)) {
1871 pfp->pfop_flags |= PORT_FOP_REMOVING;
1876 * Uninstall the fem hooks if there are no more associations.
1877 * This will release the pvp mutex.
1879 * Even thought all entries may have been removed,
1880 * the vnode itself cannot disappear as there will be a
1881 * hold on it due to this call to port_fop_sendevent. This is
1882 * important to syncronize with a port_dissociate_fop() call
1883 * that may be attempting to remove an object from the vnode's.
1885 if (port_fop_femuninstall(vp))
1886 VN_RELE(vp);
1889 * Send exception events and discard the watch entries.
1891 port_fop_excep(&tmplist, events);
1892 list_destroy(&tmplist);
1894 } else {
1895 mutex_exit(&pvp->pvp_mutex);
1898 * trim the list.
1900 port_fop_trimpfplist(vp);
1905 * Given the file operation, map it to the event types and send.
1907 void
1908 port_fop(vnode_t *vp, int op, int retval)
1910 int event = 0;
1912 * deliver events only if the operation was successful.
1914 if (retval)
1915 return;
1918 * These events occurring on the watched file.
1920 if (op & FOP_MODIFIED_MASK) {
1921 event = FILE_MODIFIED;
1923 if (op & FOP_ACCESS_MASK) {
1924 event |= FILE_ACCESS;
1926 if (op & FOP_ATTRIB_MASK) {
1927 event |= FILE_ATTRIB;
1929 if (op & FOP_TRUNC_MASK) {
1930 event |= FILE_TRUNC;
1932 if (event) {
1933 port_fop_sendevent(vp, event, NULL, NULL);
1937 static int port_forceunmount(vfs_t *vfsp)
1939 char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
1941 if (fsname == NULL) {
1942 return (0);
1945 if (strcmp(fsname, MNTTYPE_NFS) == 0) {
1946 return (1);
1949 if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
1950 return (1);
1953 if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
1954 return (1);
1956 return (0);
1959 * ----- the unmount filesystem op(fsem) hook.
1962 port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
1964 vfs_t *vfsp = (vfs_t *)vf->fa_fnode->fn_available;
1965 kmutex_t *mtx;
1966 portfop_vfs_t *pvfsp, **ppvfsp;
1967 portfop_vp_t *pvp;
1968 int error;
1969 int fmfs;
1971 fmfs = port_forceunmount(vfsp);
1973 mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
1974 ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
1975 pvfsp = NULL;
1976 mutex_enter(mtx);
1978 * since this fsem hook is triggered, the vfsp has to be on
1979 * the hash list.
1981 for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
1985 * For some of the filesystems, allow unmounts to proceed only if
1986 * there are no files being watched or it is a forced unmount.
1988 if (fmfs && !(flag & MS_FORCE) &&
1989 !list_is_empty(&pvfsp->pvfs_pvplist)) {
1990 mutex_exit(mtx);
1991 return (EBUSY);
1995 * Indicate that the unmount is in process. Don't remove it yet.
1996 * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
1997 * flag on the vfs_t structure. But we call the filesystem unmount
1998 * routine after removing all the file watches for this filesystem,
1999 * otherwise the unmount will fail due to active vnodes.
2000 * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
2001 * attempting to add a file watch.
2003 pvfsp->pvfs_unmount = 1;
2004 mutex_exit(mtx);
2007 * uninstall the fsem hooks.
2009 (void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
2011 while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
2012 list_remove(&pvfsp->pvfs_pvplist, pvp);
2014 * This should send an UNMOUNTED event to all the
2015 * watched vnode of this filesystem and uninstall
2016 * the fem hooks. We release the hold on the vnode here
2017 * because port_fop_femuninstall() will not do it if
2018 * unmount is in process.
2020 port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
2021 VN_RELE(pvp->pvp_vp);
2024 error = vfsnext_unmount(vf, flag, cr);
2027 * we free the pvfsp after the unmount has been completed.
2029 mutex_enter(mtx);
2030 for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
2031 ppvfsp = &(*ppvfsp)->pvfs_next)
2035 * remove and free it.
2037 ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
2038 if (*ppvfsp) {
2039 pvfsp = *ppvfsp;
2040 *ppvfsp = pvfsp->pvfs_next;
2042 mutex_exit(mtx);
2043 kmem_free(pvfsp, sizeof (portfop_vfs_t));
2044 return (error);
2048 * ------------------------------file op hooks--------------------------
2049 * The O_TRUNC operation is caught with the fop_setattr(AT_SIZE) call.
2051 static int
2052 port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
2054 int retval;
2055 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2057 retval = vnext_open(vf, mode, cr, ct);
2058 port_fop(vp, FOP_FILE_OPEN, retval);
2059 return (retval);
2062 static int
2063 port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2064 caller_context_t *ct)
2066 int retval;
2067 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2069 retval = vnext_write(vf, uiop, ioflag, cr, ct);
2070 port_fop(vp, FOP_FILE_WRITE, retval);
2071 return (retval);
2074 static int
2075 port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
2076 size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
2077 caller_context_t *ct)
2079 int retval;
2080 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2082 retval = vnext_map(vf, off, as, addrp, len, prot, maxport,
2083 flags, cr, ct);
2084 port_fop(vp, FOP_FILE_MAP, retval);
2085 return (retval);
2088 static int
2089 port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2090 caller_context_t *ct)
2092 int retval;
2093 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2095 retval = vnext_read(vf, uiop, ioflag, cr, ct);
2096 port_fop(vp, FOP_FILE_READ, retval);
2097 return (retval);
2102 * AT_SIZE - is for the open(O_TRUNC) case.
2105 port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
2106 caller_context_t *ct)
2108 int retval;
2109 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2110 int events = 0;
2112 retval = vnext_setattr(vf, vap, flags, cr, ct);
2113 if (vap->va_mask & AT_SIZE) {
2114 events |= FOP_FILE_TRUNC;
2116 if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
2117 events |= FOP_FILE_SETATTR_MTIME;
2119 if (vap->va_mask & AT_ATIME) {
2120 events |= FOP_FILE_SETATTR_ATIME;
2122 events |= FOP_FILE_SETATTR_CTIME;
2124 port_fop(vp, events, retval);
2125 return (retval);
2129 port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
2130 int mode, vnode_t **vpp, cred_t *cr, int flag,
2131 caller_context_t *ct, vsecattr_t *vsecp)
2133 int retval, got = 1;
2134 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2135 vattr_t vatt, vatt1;
2138 * If the file already exists, then there will be no change
2139 * to the directory. Therefore, we need to compare the
2140 * modification time of the directory to determine if the
2141 * file was actually created.
2143 vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2144 if (fop_getattr(vp, &vatt, 0, CRED(), ct)) {
2145 got = 0;
2147 retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
2148 flag, ct, vsecp);
2150 vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2151 if (got && !fop_getattr(vp, &vatt1, 0, CRED(), ct)) {
2152 if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
2153 (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
2154 vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
2156 * File was created.
2158 port_fop(vp, FOP_FILE_CREATE, retval);
2161 return (retval);
2165 port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
2166 int flags)
2168 int retval;
2169 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2171 retval = vnext_remove(vf, nm, cr, ct, flags);
2172 port_fop(vp, FOP_FILE_REMOVE, retval);
2173 return (retval);
2177 port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2178 caller_context_t *ct, int flags)
2180 int retval;
2181 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2183 retval = vnext_link(vf, svp, tnm, cr, ct, flags);
2184 port_fop(vp, FOP_FILE_LINK, retval);
2185 return (retval);
2189 * Rename operation is allowed only when from and to directories are
2190 * on the same filesystem. This is checked in vn_rename().
2191 * The target directory is notified thru a VNEVENT by the filesystem
2192 * if the source dir != target dir.
2195 port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
2196 caller_context_t *ct, int flags)
2198 int retval;
2199 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2201 retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
2202 port_fop(vp, FOP_FILE_RENAMESRC, retval);
2203 return (retval);
2207 port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
2208 cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
2210 int retval;
2211 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2213 retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
2214 port_fop(vp, FOP_FILE_MKDIR, retval);
2215 return (retval);
2219 port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2220 caller_context_t *ct, int flags)
2222 int retval;
2223 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2225 retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
2226 port_fop(vp, FOP_FILE_RMDIR, retval);
2227 return (retval);
2231 port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2232 caller_context_t *ct, int flags)
2234 int retval;
2235 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2237 retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
2238 port_fop(vp, FOP_FILE_READDIR, retval);
2239 return (retval);
2243 port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
2244 cred_t *cr, caller_context_t *ct, int flags)
2246 int retval;
2247 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2249 retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
2250 port_fop(vp, FOP_FILE_SYMLINK, retval);
2251 return (retval);
2255 * acl, facl call this.
2258 port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
2259 caller_context_t *ct)
2261 int retval;
2262 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2263 retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
2264 port_fop(vp, FOP_FILE_SETSECATTR, retval);
2265 return (retval);
2269 * these are events on the watched file/directory
2272 port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
2273 caller_context_t *ct)
2275 vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
2277 switch (vnevent) {
2278 case VE_RENAME_SRC:
2279 port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
2280 break;
2281 case VE_RENAME_DEST:
2282 port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
2283 break;
2284 case VE_REMOVE:
2285 port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2286 break;
2287 case VE_RMDIR:
2288 port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2289 break;
2290 case VE_CREATE:
2291 port_fop_sendevent(vp,
2292 FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
2293 break;
2294 case VE_LINK:
2295 port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
2296 break;
2298 case VE_RENAME_DEST_DIR:
2299 port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2300 NULL, NULL);
2301 break;
2303 case VE_MOUNTEDOVER:
2304 port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
2305 break;
2306 case VE_TRUNCATE:
2307 port_fop_sendevent(vp, FILE_TRUNC, NULL, NULL);
2308 break;
2309 default:
2310 break;
2312 return (vnext_vnevent(vf, vnevent, dvp, name, ct));