/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
/*
* File Events Notification
* ------------------------
*
* The File Events Notification facility provides file and directory change
* notification. It is implemented as an event source(PORT_SOURCE_FILE)
* under the Event Ports framework. Therefore the API is an extension to
* the Event Ports API.
*
* It uses the FEM (File Events Monitoring) framework to intercept
* operations on the files & directories and generate appropriate events.
*
* It provides event notification in accordance with what an application
* can find out by stat`ing the file and comparing time stamps. The various
* system calls that update the file's access, modification, and change
* time stamps are documented in the man page section 2.
*
* It is non intrusive. That is, having an active file event watch on a file
* or directory will not prevent it from being removed or renamed or block an
* unmount operation of the file system where the watched file or directory
* resides.
*
*
* Interface:
* ----------
*
* The object for this event source is of type 'struct file_obj *'
*
* The file that needs to be monitored is specified in 'fo_name'.
* The time stamps collected by a stat(2) call are passed in fo_atime,
* fo_mtime, fo_ctime. At the time a file events watch is registered, the
* time stamps passed in are compared with the current time stamps of the
* file. If it has changed, relevant events are sent immediately. If the time
* stamps are all '0', they will not be compared.
*
*
* The events are delivered to an event port. A port is created using
* port_create().
*
* To register a file events watch on a file or directory.
*
* port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
*
* 'user' is the user pointer to be returned with the event.
*
* To de-register a file events watch,
*
* port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
*
* The events are collected using the port_get()/port_getn() interface. The
* event source will be PORT_SOURCE_FILE.
*
* After an event is delivered, the file events watch gets de-activated. To
* receive the next event, the process will have to re-register the watch and
* activate it by calling port_associate() again. This behavior is intentional
* and supports proper multi threaded programming when using file events
* notification API.
*
*
* Implementation overview:
* ------------------------
*
* Each file events watch is represented by 'portfop_t' in the kernel. A
* cache(in portfop_cache_t) of these portfop_t's are maintained per event
* port by this source. The object here is the pointer to the file_obj
* structure. The portfop_t's are hashed in using the object pointer. Therefore
* it is possible to have multiple file events watches on a file by the same
* process by using different object structure(file_obj_t) and hence can
* receive multiple event notification for a file. These watches can be for
* different event types.
*
* The cached entries of these file objects are retained, even after delivering
* an event, marking them inactive for performance reasons. The assumption
* is that the process would come back and re-register the file to receive
* further events. When there are more then 'port_fop_maxpfps' watches per file
* it will attempt to free the oldest inactive watches.
*
* In case the event that is being delivered is an exception event, the cached
* entries get removed. An exception event on a file or directory means its
* unmount).
*
* If the event port gets closed, all the associated file event watches will be
* removed and discarded.
*
*
* Data structures:
* ----------------
*
* The list of file event watches per file are managed by the data structure
* portfop_vp_t. The first time a file events watch is registered for a file,
* a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
* removed and freed only when the vnode becomes inactive. The FEM hooks are
* also installed when the first watch is registered on a file. The FEM hooks
* get un-installed when all the watches are removed.
*
* Each file events watch is represented by the structure portfop_t. They
* get added to a list of portfop_t's on the vnode(portfop_vp_t). After
* delivering an event, the portfop_t is marked inactive but retained. It is
* moved to the end of the list. All the active portfop_t's are maintained at
* the beginning. In case of exception events, the portfop_t will be removed
* and discarded.
*
* To intercept unmount operations, FSEM hooks are added to the file system
* under which files are being watched. A hash table('portfop_vfs_hash_t') of
* active file systems is maintained. Each file system that has active watches
* is represented by 'portfop_vfs_t' and is added to the hash table.
* The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
* being watched on the portfop_vfs_t structure.
*
*
* File system support:
* -------------------
*
* The file system implementation has to provide vnode event notifications
* (vnevents) in order to support watching any files on that file system.
* The vnode events(vnevents) are notifications provided by the file system
* for name based file operations like rename, remove etc, which do not go
* thru the VOP_** interfaces. If the file system does not implement vnode
* notifications, watching for file events on such file systems is not
* supported. The vnode event notifications support is determined by the call
* vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
* has to implement.
*
*
* Locking order:
* --------------
*
* A file(vnode) can have file event watches registered by different processes.
* There is one portfop_t per watch registered. These are on the vnode's list
* protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
* also on the per port cache. The cache is protected by the pfc_lock of
* portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
*
*/
#include <sys/sysmacros.h>
#include <sys/poll_impl.h>
#include <sys/port_impl.h>
#include <sys/vfs_opreg.h>
/*
*/
extern struct vnode *vfs_mntdummyvp;
extern int mntfstype;
/*
* Inactive file event watches(portfop_t) are retained on the vnode's list
* for performance reason. If the applications re-registers the file, the
* inactive entry is made active and moved up the list.
*
* If there are greater then the following number of watches on a vnode,
* it will attempt to discard an oldest inactive watch(pfp) at the time
* a new watch is being registered and when events get delivered. We
* do this to avoid accumulating inactive watches on a file.
*/
/* local functions */
static int port_fop_callback(void *, int *, pid_t, int, void *);
/*
* port fop functions that will be the fem hooks.
*/
caller_context_t *);
struct caller_context *ct);
vsecattr_t *vsecp);
/*
* Fem hooks.
*/
};
/*
* Fsem - vfs ops hooks
*/
};
static fem_t *
{
return (fop_femop);
if (fem_create("portfop_fem",
(const struct fs_operation_def *)port_vnodesrc_template,
return (NULL);
}
/*
* some other thread beat us to it.
*/
}
return (fop_femop);
}
static fsem_t *
{
if (fop_fsemop != NULL)
return (fop_fsemop);
return (NULL);
}
/*
* some other thread beat us to it.
*/
}
return (fop_fsemop);
}
/*
* port_fop_callback()
* - PORT_CALLBACK_DEFAULT
* The file event will be delivered to the application.
* - PORT_CALLBACK_DISSOCIATE
* The object will be dissociated from the port.
* - PORT_CALLBACK_CLOSE
* The object will be dissociated from the port because the port
* is being closed.
*/
/* ARGSUSED */
static int
{
int error = 0;
if (flag == PORT_CALLBACK_DEFAULT) {
return (EACCES); /* deny delivery of events */
}
pkevp->portkev_events = 0;
}
}
return (error);
}
/*
* Inserts a portfop_t into the port sources cache's.
*/
static void
{
pfcp->pfc_objcount++;
}
/*
* Remove the pfp from the port source cache.
*/
static void
{
} else {
/* portfop struct found */
break;
}
}
}
pfcp->pfc_objcount--;
}
/*
* The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
* when these routines are called.
*
* The 'pvp_lpfop' member points to the oldest inactive entry on the list.
* It is used to discard the oldtest inactive pfp if the number of entries
* exceed the limit.
*/
static void
{
if (where == 1) {
} else {
}
}
}
static void
{
}
static void
{
/*
* We point lpfop to an inactive one, if it was initially pointing
* to an active one. Insert to the tail is done only when a pfp goes
* inactive.
*/
}
}
static void
{
}
}
}
static void
{
}
/*
* Remove a portfop_t from the port cache hash table and discard it.
* It is called only when pfp is not on the vnode's list. Otherwise,
* port_remove_fop() is called.
*/
void
{
(void) port_remove_done_event(pkevp);
}
if (pfcp->pfc_objcount == 0)
}
/*
* if we have too many watches on the vnode, attempt to discard an
* inactive one.
*/
static void
{
/*
* Due to a reference the vnode cannot disappear, v_fopdata should
* not change.
*/
/*
* only if we can get the cache lock, we need to
* do this due to reverse lock order and some thread
* that may be trying to reactivate this entry.
*/
} else {
}
} else {
}
/*
* discard pfp if any.
*/
}
}
}
/*
* This routine returns 1, if the vnode can be rele'ed by the caller.
* The caller has to VN_RELE the vnode with out holding any
* locks.
*/
int
{
int ret = 0;
/*
* if list is empty, uninstall fem.
*/
/*
* make sure the list is empty.
*/
/*
* we could possibly uninstall the fem hooks when
* the vnode becomes inactive and the v_fopdata is
* free. But the hooks get triggered unnecessarily
* even though there are no active watches. So, we
* uninstall it here.
*/
/*
* If we successfully uninstalled fem, no process is watching
* this vnode, Remove it from the vfs's list of watched vnodes.
*/
/*
* If unmount is in progress, that thread will remove and
* release the vnode from the vfs's list, just leave.
*/
if (!pvfsp->pvfs_unmount) {
ret = 1;
} else {
}
}
return (ret);
}
/*
* Remove pfp from the vnode's watch list and the cache and discard it.
* If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
* Returns 1 if pfp removed successfully.
*
* The *active is set to indicate if the pfp was still active(no events had
* been posted, or the posted event had not been collected yet and it was
* able to remove it from the port's queue).
*
* vpp and dvpp will point to the vnode and directory vnode which the caller
* is required to VN_RELE without holding any locks.
*/
int
{
int tactive = 0;
/*
* if not cleanup, remove it only if the pfp is still active and
* is not being removed by some other thread.
*/
return (0);
}
/*
* mark it inactive.
*/
tactive = 1;
}
/*
* Check if the pfp is still on the vnode's list. This can
* happen if port_fop_excep() is in the process of removing it.
* In case of cleanup, just mark this pfp as inactive so that no
* new events (VNEVENT) will be delivered, and remove it from the
* event queue if it was already queued. Since the cache lock is
* held, the pfp will not disappear, even though it is being
* removed.
*/
tactive = 1;
}
if (active) {
}
return (1);
}
/*
* if we find an event on the queue and removed it, then this
* association is considered active.
*/
tactive = 1;
}
if (active) {
}
/*
* remove pfp from the vnode's list
*/
/*
* If no more associations on the vnode, uninstall fem hooks.
* The pvp mutex will be released in this routine.
*/
if (port_fop_femuninstall(vp))
return (1);
}
/*
* This routine returns a pointer to a cached portfop entry, or NULL if it
* does not find it in the hash table. The object pointer is used as index.
* The entries are hashed by the object's address. We need to match the pid
* as the evet port can be shared between processes. The file events
* watches are per process only.
*/
{
break;
}
return (pfp);
}
/*
* Given the file name, get the vnode and also the directory vnode
* On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
* the vnode(s).
*/
int
{
int error = 0;
char *fname;
if (get_udatamodel() == DATAMODEL_NATIVE) {
#ifdef _SYSCALL32_IMPL
} else {
#endif /* _SYSCALL32_IMPL */
}
/*
* lookuppn may fail with EINVAL, if dvp is non-null(like when
* looking for "."). So call again with dvp = NULL.
*/
return (error);
}
return (error);
}
}
}
pn_setlast(&pn);
} else {
*len = 0;
}
}
return (error);
}
{
int lock = 0;
/*
* get the port source structure.
*/
lock = 1;
}
break;
}
if (lock) {
}
return (pse);
}
/*
* Compare time stamps and generate an event if it has changed.
* Note that the port cache pointer will be valid due to a reference
* to the port. We need to grab the port cache lock and verify that
* the pfp is still the same before proceeding to deliver an event.
*/
static void
{
int events = 0;
/*
* If time stamps are specified, get attributes and compare.
*/
if (get_udatamodel() == DATAMODEL_NATIVE) {
return;
}
} else {
/*
* timestamp not specified, all 0's,
*/
return;
}
#ifdef _SYSCALL32_IMPL
} else {
return;
}
} else {
/*
* timestamp not specified, all 0.
*/
return;
}
#endif /* _SYSCALL32_IMPL */
}
/*
* Now grab the cache lock and verify that we are still
* dealing with the same pfp and curthread is the one
* which registered it. We need to do this to avoid
* delivering redundant events.
*/
/*
* Some other event was delivered, the file
* watch was removed or reassociated. Just
* ignore it and leave
*/
return;
}
/*
* The pfp cannot disappear as the port cache lock is held.
* While the pvp_mutex is held, no events will get delivered.
*/
if (get_udatamodel() == DATAMODEL_NATIVE) {
events |= FILE_ACCESS;
events |= FILE_MODIFIED;
events |= FILE_ATTRIB;
#ifdef _SYSCALL32_IMPL
} else {
events |= FILE_ACCESS;
events |= FILE_MODIFIED;
events |= FILE_ATTRIB;
#endif /* _SYSCALL32_IMPL */
}
/*
* No events to deliver
*/
if (events == 0) {
return;
}
/*
* Deliver the event now.
*/
/*
* Move it to the tail as active once are in the
* beginning of the list.
*/
}
}
/*
* Add the event source to the port and return the port source cache pointer.
*/
int
{
int error;
/*
* associate PORT_SOURCE_FILE source with the port, if it is
* not associated yet. Note the PORT_SOURCE_FILE source is
* associated once and will not be dissociated.
*/
return (error);
}
}
/*
* Get the portfop cache pointer.
*/
/*
* This is the first time that a file is being associated,
* create the portfop cache.
*/
} else {
/*
* someone else created the port cache, free
* what we just now allocated.
*/
}
}
return (0);
}
/*
* Add the given pvp on the file system's list of vnodes watched.
*/
int
{
int error = 0;
;
if (!pvfsp) {
return (error);
}
} else {
return (EINVAL);
}
}
/*
* check if an unmount is in progress.
*/
if (!pvfsp->pvfs_unmount) {
/*
* insert the pvp on list.
*/
} else {
}
return (error);
}
/*
* Installs the portfop_vp_t data structure on the
* vnode. The 'pvp_femp == NULL' indicates it is not
* active. The fem hooks have to be installed.
* The portfop_vp_t is only freed when the vnode gets freed.
*/
void
{
/*
* If v_fopdata is not null, some other thread beat us to it.
*/
}
}
/*
* Allocate and add a portfop_t to the per port cache. Also add the portfop_t
* to the vnode's list. The association is identified by the object pointer
* address and pid.
*/
int
{
int error = 0;
/*
* The port cache mutex is held.
*/
/*
* At this point the fem monitor is installed.
* Allocate a port event structure per vnode association.
*/
PORT_ALLOC_CACHED, &pkevp)) {
return (error);
}
}
pkevp->portkev_events = 0;
/*
* Register a new file events monitor for this file(vnode), if not
* done already.
*/
}
/*
* if the vnode does not have the file events hooks, install it.
*/
/*
* add fsem_t hooks to the vfsp and add pvp to
* the list of vnodes for this vfs.
*/
/*
* Hold a reference to the vnode since
* we successfully installed the hooks.
*/
} else {
}
}
} else {
}
}
if (error) {
/*
* pkevp will get freed here.
*/
return (error);
}
/*
* insert the pfp on the vnode's list. After this
* events can get delivered.
*/
/*
* Hold the directory vnode since we have a reference now.
*/
return (0);
}
vnode_t *
{
/*
* if mntfs got mounted.
*/
if (vfs_mntdummyvp && mntfstype != 0 &&
vp = vfs_mntdummyvp;
}
/*
* This should take care of lofs mounted fs systems and nfs4
* hardlinks.
*/
}
return (vp);
}
/*
* Register a file events watch on the given file associated to the port *pp.
*
* The association is identified by the object pointer and the pid.
* The events argument contains the events to be monitored for.
*
* The vnode will have a VN_HOLD once the fem hooks are installed.
*
* Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
* that the directory vnode pointer does not change.
*/
int
void *user)
{
int error = 0;
void *objptr;
char *cname;
int clen;
int follow;
/*
* check that events specified are valid.
*/
if ((events & ~FILE_EVENTS_MASK) != 0)
return (EINVAL);
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
#ifdef _SYSCALL32_IMPL
} else {
return (EFAULT);
#endif /* _SYSCALL32_IMPL */
}
/*
* find out if we need to follow symbolic links.
*/
/*
* lookup and find the vnode and its directory vnode of the given
* file.
*/
follow)) != 0) {
return (error);
}
}
/*
* Not found
*/
goto errout;
}
goto errout;
}
/*
* If dvp belongs to a different filesystem just ignore it, as hard
* links cannot exist across filesystems. We make an exception for
* procfs, however, the magic of which we treat semantically as a hard
* link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE
* and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events.
*/
}
/*
* Associate this source to the port and get the per port
* fop cache pointer. If the source is already associated, it
* will just return the cache pointer.
*/
goto errout;
}
/*
* Check if there is an existing association of this file.
*/
/*
* If it is not the same vnode, just discard it. VN_RELE needs to be
* called with no locks held, therefore save vnode pointers and
* vn_rele them later.
*/
}
int error;
/*
* Add a new association, save the file name and the
* directory vnode pointer.
*/
goto errout;
}
/*
* File name used, so make sure we don't free it.
*/
/*
* We need to check if the file was removed after the
* the lookup and before the fem hooks where added. If
* so, return error. The vnode will still exist as we have
* a hold on it.
*
* Drop the cache lock before calling port_fop_getdvp().
* port_fop_getdvp() may block either in the vfs layer
* or some filesystem. Therefore there is potential
* for deadlock if cache lock is held and if some other
* thread is attempting to deliver file events which would
* require getting the cache lock, while it may be holding
* the filesystem or vfs layer locks.
*/
/*
* This vnode pointer is just used
* for comparison, so rele it
*/
}
}
/*
* Since we dropped the cache lock, make sure
* we are still dealing with the same pfp and this
* is the thread which registered it.
*/
error = 0;
/*
* Some other event was delivered, the file
* watch was removed or reassociated, just
* ignore it and leave
*/
goto errout;
}
/*
* remove the pfp and fem hooks, if pfp still
* active and it is not being removed from
* the vnode list. This is checked in
* port_remove_fop with the vnode lock held.
* The vnode returned is VN_RELE'ed after dropping
* the locks.
*/
/*
* The pfp was removed, means no
* events where queued. Report the
* error now.
*/
}
goto errout;
}
} else {
/*
* Re-association of the object.
*/
/*
* remove any queued up event.
*/
}
/*
* set new events to watch.
*/
/*
* If not active, mark it active even if it is being
* removed. Then it can send an exception event.
*
* Move it to the head, as the active ones are only
* in the beginning. If removing, the pfp will be on
* a temporary list, no need to move it to the front
* all the entries will be processed. Some exception
* events will be delivered in port_fop_excep();
*/
}
}
}
/*
* Compare time stamps and deliver events.
*/
}
error = 0;
/*
* If we have too many watches on the vnode, discard an
* inactive watch.
*/
/*
* Release the hold acquired due to the lookup operation.
*/
/*
* copied file name not used, free it.
*/
}
return (error);
}
/*
* The port_dissociate_fop() function dissociates the file object
* from the event port and removes any events that are already on the queue.
* Only the owner of the association is allowed to dissociate the file from
* the port. Returns success (0) if it was found and removed. Otherwise
* ENOENT.
*/
int
{
int active = 0;
/*
* if this source is not associated or if there is no
* cache, nothing to do just return.
*/
return (EINVAL);
/*
* Check if this object is on the cache. Only the owner pid
* is allowed to dissociate.
*/
return (ENOENT);
}
/*
* If this was the last association, it will release
* the hold on the vnode. There is a race condition where
* the the pfp is being removed due to an exception event
* in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
* Since port source cache lock is held, port_fop_excep() cannot
* complete. The vnode itself will not disappear as long its pfps
* have a reference.
*/
}
/*
* port_close() calls this function to request the PORT_SOURCE_FILE source
*/
/* ARGSUSED */
static void
{
int index, i;
/*
* No source or no cache, nothing to do.
*/
return;
/*
* Scan the cache and free all allocated portfop_t and port_kevent_t
* structures of this pid. Note, no new association for this pid will
* be possible as the port is being closed.
*
* The common case is that the port is not shared and all the entries
* are of this pid and have to be freed. Since VN_RELE has to be
* called outside the lock, we do it in batches.
*/
index = i = 0;
while (index < PORTFOP_HASHSIZE) {
i++;
}
}
}
}
index++;
/*
* Now call VN_RELE if we have collected enough vnodes or
* we have reached the end of the hash table.
*/
if (i >= (PORTFOP_NVP - 1) ||
(i > 0 && index == PORTFOP_HASHSIZE)) {
while (i > 0) {
}
}
}
/*
* Due to a race between port_close_fop() and port_fop()
* trying to remove the pfp's from the port's cache, it is
* possible that some pfp's are still in the process of being
* freed so we wait.
*/
}
/*
* last close, free the cache.
*/
if (lastclose) {
}
}
/*
* Given the list of associations(watches), it will send exception events,
* if still active, and discard them. The exception events are handled
* separately because, the pfp needs to be removed from the port cache and
* freed as the vnode's identity is changing or being removed. To remove
* the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
* The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
* the cache's lock cannot be acquired in port_fop_sendevent().
*/
static void
{
int error = 0;
int removed = 0;
/*
* remove from the temp list. Since PORT_FOP_REMOVING is
* set, no other thread should attempt to perform a
* list_remove on this pfp.
*/
/*
* Remove the event from the port queue if it was queued up.
* No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
* no longer on the vnode's list.
*/
}
/*
* If still active or the event was queued up and
* had not been collected yet, send an EXCEPTION event.
*/
/*
* Allocate a port_kevent_t non cached to send this
* event since we will be de-registering.
* The port_kevent_t cannot be pointing back to the
* pfp anymore.
*/
if (!error) {
/*
* Copy the pid of the watching process.
*/
pkevp->portkev_pid =
}
}
/*
* At this point the pfp has been removed from the vnode's
* list its cached port_kevent_t is not on the done queue.
* Remove the pfp and free it from the cache.
*/
}
}
/*
* Send the file events to all of the processes watching this
* vnode. In case of hard links, the directory vnode pointer and
* the file name are compared. If the names match, then the specified
* event is sent or else, the FILE_ATTRIB event is sent, This is the
* documented behavior.
*/
void
{
int removeall = 0;
/*
* Check if the list is empty.
*
* All entries have been removed by some other thread.
* The vnode may be still active and we got called,
* but some other thread is in the process of removing the hooks.
*/
return;
}
if ((events & (FILE_EXCEPTION))) {
/*
* If it is an event for which we are going to remove
* the watches so just move it a temporary list and
* release this vnode.
*/
/*
* If it is an UNMOUNT, MOUNTEDOVER or no file name has been
* passed for an exception event, all associations need to be
* removed.
*/
removeall = 1;
}
}
if (!removeall) {
/*
* All the active ones are in the beginning of the list.
* Note that we process this list in reverse order to assure
* that events are delivered in the order that they were
* associated.
*/
}
/*
* Hard links case - If the file is being
* the watched file, then it is an EXCEPTION
* event or else it will be just a FILE_ATTRIB.
*/
if ((events & (FILE_EXCEPTION))) {
/*
* It is an exception event, move it
* to temp list and process it later.
* Note we don't set the pfp->pfop_vp
* to NULL even thought it has been
* removed from the vnode's list. This
* pointer is referenced in
* port_remove_fop(). The vnode it
* self cannot disappear until this
* pfp gets removed and freed.
*/
continue;
} else {
}
}
/*
* deactivate and move it to the tail.
* If the pfp was active, it cannot be
* on the port's done queue.
*/
pkevp->portkev_events |=
}
}
}
if ((events & (FILE_EXCEPTION))) {
if (!removeall) {
/*
* Check the inactive associations and remove them if
* the file name matches.
*/
}
}
} else {
/*
* Can be optimized to avoid two pass over this list
* by having a flag in the vnode's portfop_vp_t
* structure to indicate that it is going away,
* Or keep the list short by reusing inactive watches.
*/
}
}
/*
* Uninstall the fem hooks if there are no more associations.
* This will release the pvp mutex.
*
* Even thought all entries may have been removed,
* the vnode itself cannot disappear as there will be a
* hold on it due to this call to port_fop_sendevent. This is
* important to syncronize with a port_dissociate_fop() call
* that may be attempting to remove an object from the vnode's.
*/
if (port_fop_femuninstall(vp))
/*
* Send exception events and discard the watch entries.
*/
} else {
/*
* trim the list.
*/
}
}
/*
* Given the file operation, map it to the event types and send.
*/
void
{
int event = 0;
/*
* deliver events only if the operation was successful.
*/
if (retval)
return;
/*
* These events occurring on the watched file.
*/
if (op & FOP_MODIFIED_MASK) {
}
if (op & FOP_ACCESS_MASK) {
event |= FILE_ACCESS;
}
if (op & FOP_ATTRIB_MASK) {
event |= FILE_ATTRIB;
}
if (op & FOP_TRUNC_MASK) {
event |= FILE_TRUNC;
}
if (event) {
}
}
{
return (0);
}
return (1);
}
return (1);
}
return (1);
}
return (0);
}
/*
* ----- the unmount filesystem op(fsem) hook.
*/
int
{
int error;
int fmfs;
/*
* since this fsem hook is triggered, the vfsp has to be on
* the hash list.
*/
;
/*
* For some of the filesystems, allow unmounts to proceed only if
* there are no files being watched or it is a forced unmount.
*/
return (EBUSY);
}
/*
* Indicate that the unmount is in process. Don't remove it yet.
* The underlying filesystem unmount routine sets the VFS_UNMOUNTED
* flag on the vfs_t structure. But we call the filesystem unmount
* routine after removing all the file watches for this filesystem,
* otherwise the unmount will fail due to active vnodes.
* Meanwhile setting pvfsp->unmount = 1 will prevent any thread
* attempting to add a file watch.
*/
/*
* uninstall the fsem hooks.
*/
/*
* This should send an UNMOUNTED event to all the
* watched vnode of this filesystem and uninstall
* the fem hooks. We release the hold on the vnode here
* because port_fop_femuninstall() will not do it if
* unmount is in process.
*/
}
/*
* we free the pvfsp after the unmount has been completed.
*/
;
/*
* remove and free it.
*/
if (*ppvfsp) {
}
return (error);
}
/*
* ------------------------------file op hooks--------------------------
* The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
*/
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
static int
{
int retval;
return (retval);
}
/*
* AT_SIZE - is for the open(O_TRUNC) case.
*/
int
{
int retval;
int events = 0;
events |= FOP_FILE_TRUNC;
}
}
}
return (retval);
}
int
{
/*
* If the file already exists, then there will be no change
* to the directory. Therefore, we need to compare the
* modification time of the directory to determine if the
* file was actually created.
*/
got = 0;
}
/*
* File was created.
*/
}
}
return (retval);
}
int
int flags)
{
int retval;
return (retval);
}
int
{
int retval;
return (retval);
}
/*
* Rename operation is allowed only when from and to directories are
* on the same filesystem. This is checked in vn_rename().
* The target directory is notified thru a VNEVENT by the filesystem
* if the source dir != target dir.
*/
int
{
int retval;
return (retval);
}
int
{
int retval;
return (retval);
}
int
{
int retval;
return (retval);
}
int
{
int retval;
return (retval);
}
int
{
int retval;
return (retval);
}
/*
* acl, facl call this.
*/
int
{
int retval;
return (retval);
}
/*
*/
int
{
switch (vnevent) {
case VE_RENAME_SRC:
break;
case VE_RENAME_DEST:
break;
case VE_REMOVE:
break;
case VE_RMDIR:
break;
case VE_CREATE:
break;
case VE_LINK:
break;
case VE_RENAME_DEST_DIR:
break;
case VE_MOUNTEDOVER:
break;
case VE_TRUNCATE:
break;
default:
break;
}
}