#include <sys/types.h>
#include <sys/systm.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/vnode.h>
#include <sys/poll_impl.h>
#include <sys/port_impl.h>
#include <sys/fem.h>
#include <sys/vfs_opreg.h>
#include <sys/atomic.h>
#include <sys/mount.h>
#include <sys/mntent.h>
extern struct vnode *vfs_mntdummyvp;
extern int mntfstype;
#define PORTFOP_PVFSH(vfsp) (&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
portfop_vfs_hash_t portvfs_hash[PORTFOP_PVFSHASH_SZ];
#define PORTFOP_NVP 20
int port_fop_maxpfps = 20;
static int port_fop_callback(void *, int *, pid_t, int, void *);
static void port_pcache_insert(portfop_cache_t *, portfop_t *);
static void port_pcache_delete(portfop_cache_t *, portfop_t *);
static void port_close_fop(void *arg, int port, pid_t pid, int lastclose);
static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
caller_context_t *);
static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
struct caller_context *ct);
static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
caller_context_t *ct);
static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
uint_t flags, cred_t *cr, caller_context_t *ct);
static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct);
static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
caller_context_t *ct, vsecattr_t *vsecp);
static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
caller_context_t *ct, int flags);
static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
caller_context_t *ct, int flags);
static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
cred_t *cr, caller_context_t *ct, int flags);
static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
vsecattr_t *vsecp);
static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
caller_context_t *ct, int flags);
static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
caller_context_t *ct, int flags);
static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
char *target, cred_t *cr, caller_context_t *ct, int flags);
static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
cred_t *cr, caller_context_t *ct);
static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
char *cname, caller_context_t *ct);
static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
const fs_operation_def_t port_vnodesrc_template[] = {
VOPNAME_OPEN, { .femop_open = port_fop_open },
VOPNAME_READ, { .femop_read = port_fop_read },
VOPNAME_WRITE, { .femop_write = port_fop_write },
VOPNAME_MAP, { .femop_map = port_fop_map },
VOPNAME_SETATTR, { .femop_setattr = port_fop_setattr },
VOPNAME_CREATE, { .femop_create = port_fop_create },
VOPNAME_REMOVE, { .femop_remove = port_fop_remove },
VOPNAME_LINK, { .femop_link = port_fop_link },
VOPNAME_RENAME, { .femop_rename = port_fop_rename },
VOPNAME_MKDIR, { .femop_mkdir = port_fop_mkdir },
VOPNAME_RMDIR, { .femop_rmdir = port_fop_rmdir },
VOPNAME_READDIR, { .femop_readdir = port_fop_readdir },
VOPNAME_SYMLINK, { .femop_symlink = port_fop_symlink },
VOPNAME_SETSECATTR, { .femop_setsecattr = port_fop_setsecattr },
VOPNAME_VNEVENT, { .femop_vnevent = port_fop_vnevent },
NULL, NULL
};
const fs_operation_def_t port_vfssrc_template[] = {
VFSNAME_UNMOUNT, { .fsemop_unmount = port_fop_unmount },
NULL, NULL
};
fem_t *fop_femop;
fsem_t *fop_fsemop;
static fem_t *
port_fop_femop()
{
fem_t *femp;
if (fop_femop != NULL)
return (fop_femop);
if (fem_create("portfop_fem",
(const struct fs_operation_def *)port_vnodesrc_template,
(fem_t **)&femp)) {
return (NULL);
}
if (atomic_cas_ptr(&fop_femop, NULL, femp) != NULL) {
fem_free(femp);
}
return (fop_femop);
}
static fsem_t *
port_fop_fsemop()
{
fsem_t *fsemp;
if (fop_fsemop != NULL)
return (fop_fsemop);
if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
return (NULL);
}
if (atomic_cas_ptr(&fop_fsemop, NULL, fsemp) != NULL) {
fsem_free(fsemp);
}
return (fop_fsemop);
}
static int
port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
{
portfop_t *pfp = (portfop_t *)arg;
port_kevent_t *pkevp = (port_kevent_t *)evp;
int error = 0;
ASSERT((events != NULL));
if (flag == PORT_CALLBACK_DEFAULT) {
if (curproc->p_pid != pid) {
return (EACCES);
}
*events = pkevp->portkev_events;
pkevp->portkev_events = 0;
if (pfp != NULL) {
pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
}
}
return (error);
}
static void
port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
{
portfop_t **bucket;
ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
pfp->pfop_hashnext = *bucket;
*bucket = pfp;
pfcp->pfc_objcount++;
}
static void
port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
{
portfop_t *lpdp;
portfop_t *cpdp;
portfop_t **bucket;
bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
cpdp = *bucket;
if (pfp == cpdp) {
*bucket = pfp->pfop_hashnext;
} else {
while (cpdp != NULL) {
lpdp = cpdp;
cpdp = cpdp->pfop_hashnext;
if (cpdp == pfp) {
lpdp->pfop_hashnext = pfp->pfop_hashnext;
break;
}
}
}
pfcp->pfc_objcount--;
}
static void
port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
{
if (where == 1) {
list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
} else {
list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
}
if (pvp->pvp_lpfop == NULL) {
pvp->pvp_lpfop = pfp;
}
pvp->pvp_cnt++;
}
static void
port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
{
port_fop_listinsert(pvp, pfp, 1);
}
static void
port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
{
if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
pvp->pvp_lpfop = pfp;
}
port_fop_listinsert(pvp, pfp, 0);
}
static void
port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
{
if (pvp->pvp_lpfop == pfp) {
pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
}
list_remove(&pvp->pvp_pfoplist, (void *)pfp);
pvp->pvp_cnt--;
if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
}
}
static void
port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
{
list_move_tail(tlist, &pvp->pvp_pfoplist);
pvp->pvp_lpfop = NULL;
pvp->pvp_cnt = 0;
}
void
port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
{
port_kevent_t *pkevp;
ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
pkevp = pfp->pfop_pev;
pfp->pfop_pev = NULL;
if (pkevp != NULL) {
(void) port_remove_done_event(pkevp);
port_free_event_local(pkevp, 0);
}
port_pcache_delete(pfcp, pfp);
if (pfp->pfop_cname != NULL)
kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
kmem_free(pfp, sizeof (portfop_t));
if (pfcp->pfc_objcount == 0)
cv_signal(&pfcp->pfc_lclosecv);
}
static void
port_fop_trimpfplist(vnode_t *vp)
{
portfop_vp_t *pvp;
portfop_t *pfp = NULL;
portfop_cache_t *pfcp;
vnode_t *tdvp;
if ((pvp = vp->v_fopdata) != NULL &&
pvp->pvp_cnt > port_fop_maxpfps) {
mutex_enter(&pvp->pvp_mutex);
pfp = pvp->pvp_lpfop;
pfcp = pfp->pfop_pcache;
if (mutex_tryenter(&pfcp->pfc_lock)) {
if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
!(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
port_fop_listremove(pvp, pfp);
pfp->pfop_flags |= PORT_FOP_REMOVING;
} else {
mutex_exit(&pfcp->pfc_lock);
pfp = NULL;
}
} else {
pfp = NULL;
}
mutex_exit(&pvp->pvp_mutex);
if (pfp != NULL) {
tdvp = pfp->pfop_dvp;
port_pcache_remove_fop(pfcp, pfp);
mutex_exit(&pfcp->pfc_lock);
if (tdvp != NULL)
VN_RELE(tdvp);
}
}
}
int
port_fop_femuninstall(vnode_t *vp)
{
portfop_vp_t *pvp;
vfs_t *vfsp;
portfop_vfs_t *pvfsp;
portfop_vfs_hash_t *pvfsh;
kmutex_t *mtx;
int ret = 0;
pvp = vp->v_fopdata;
ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
if (!list_head(&pvp->pvp_pfoplist)) {
(void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
pvp->pvp_femp = NULL;
pvfsp = pvp->pvp_pvfsp;
vfsp = vp->v_vfsp;
pvfsh = PORTFOP_PVFSH(vfsp);
mtx = &pvfsh->pvfshash_mutex;
mutex_enter(mtx);
if (!pvfsp->pvfs_unmount) {
list_remove(&pvfsp->pvfs_pvplist, pvp);
mutex_exit(mtx);
ret = 1;
} else {
mutex_exit(mtx);
}
}
mutex_exit(&pvp->pvp_mutex);
return (ret);
}
int
port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
int *active, vnode_t **vpp, vnode_t **dvpp)
{
vnode_t *vp;
portfop_vp_t *pvp;
int tactive = 0;
ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
vp = pfp->pfop_vp;
pvp = vp->v_fopdata;
mutex_enter(&pvp->pvp_mutex);
if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
pfp->pfop_flags & PORT_FOP_REMOVING)) {
mutex_exit(&pvp->pvp_mutex);
return (0);
}
if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
tactive = 1;
}
if (pfp->pfop_flags & PORT_FOP_REMOVING) {
mutex_exit(&pvp->pvp_mutex);
if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
tactive = 1;
}
if (active) {
*active = tactive;
}
return (1);
}
if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
tactive = 1;
}
if (active) {
*active = tactive;
}
pvp = (portfop_vp_t *)vp->v_fopdata;
port_fop_listremove(pvp, pfp);
if (port_fop_femuninstall(vp))
*vpp = vp;
*dvpp = pfp->pfop_dvp;
port_pcache_remove_fop(pfcp, pfp);
return (1);
}
portfop_t *
port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
{
portfop_t *pfp = NULL;
portfop_t **bucket;
ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
bucket = PORT_FOP_BUCKET(pfcp, obj);
pfp = *bucket;
while (pfp != NULL) {
if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
break;
pfp = pfp->pfop_hashnext;
}
return (pfp);
}
int
port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
int *len, int follow)
{
int error = 0;
struct pathname pn;
char *fname;
if (get_udatamodel() == DATAMODEL_NATIVE) {
fname = ((file_obj_t *)objptr)->fo_name;
#ifdef _SYSCALL32_IMPL
} else {
fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
#endif
}
if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
return (error);
}
error = lookuppn(&pn, NULL, follow, dvp, vp);
if (error == EINVAL) {
pn_free(&pn);
if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
return (error);
}
error = lookuppn(&pn, NULL, follow, NULL, vp);
if (dvp != NULL) {
*dvp = NULL;
}
}
if (error == 0 && cname != NULL && len != NULL) {
pn_setlast(&pn);
*len = pn.pn_pathlen;
*cname = kmem_alloc(*len + 1, KM_SLEEP);
(void) strcpy(*cname, pn.pn_path);
} else {
if (cname != NULL && len != NULL) {
*cname = NULL;
*len = 0;
}
}
pn_free(&pn);
return (error);
}
port_source_t *
port_getsrc(port_t *pp, int source)
{
port_source_t *pse;
int lock = 0;
if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
mutex_enter(&pp->port_queue.portq_source_mutex);
lock = 1;
}
pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
for (; pse != NULL; pse = pse->portsrc_next) {
if (pse->portsrc_source == source)
break;
}
if (lock) {
mutex_exit(&pp->port_queue.portq_source_mutex);
}
return (pse);
}
static void
port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
portfop_t *pfp, void *objptr, uintptr_t object)
{
vattr_t vatt;
portfop_vp_t *pvp = vp->v_fopdata;
int events = 0;
port_kevent_t *pkevp;
file_obj_t *fobj;
portfop_t *tpfp;
vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
if (get_udatamodel() == DATAMODEL_NATIVE) {
fobj = (file_obj_t *)objptr;
if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
return;
}
} else {
return;
}
#ifdef _SYSCALL32_IMPL
} else {
file_obj32_t *fobj32;
fobj32 = (file_obj32_t *)objptr;
if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
return;
}
} else {
return;
}
#endif
}
mutex_enter(&pfcp->pfc_lock);
tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
if (tpfp == NULL || tpfp != pfp ||
pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
pfp->pfop_callrid != curthread ||
!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
mutex_exit(&pfcp->pfc_lock);
return;
}
mutex_enter(&pvp->pvp_mutex);
if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
if (get_udatamodel() == DATAMODEL_NATIVE) {
fobj = (file_obj_t *)objptr;
if (pfp->pfop_events & FILE_ACCESS &&
(fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
(vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
events |= FILE_ACCESS;
if (pfp->pfop_events & FILE_MODIFIED &&
(fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
(vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
events |= FILE_MODIFIED;
if (pfp->pfop_events & FILE_ATTRIB &&
(fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
(vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
events |= FILE_ATTRIB;
#ifdef _SYSCALL32_IMPL
} else {
file_obj32_t *fobj32;
fobj32 = (file_obj32_t *)objptr;
if (pfp->pfop_events & FILE_ACCESS &&
(fobj32->fo_atime.tv_sec ||
fobj32->fo_atime.tv_nsec) &&
(vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
events |= FILE_ACCESS;
if (pfp->pfop_events & FILE_MODIFIED &&
(fobj32->fo_mtime.tv_sec ||
fobj32->fo_mtime.tv_nsec) &&
(vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
events |= FILE_MODIFIED;
if (pfp->pfop_events & FILE_ATTRIB &&
(fobj32->fo_ctime.tv_sec ||
fobj32->fo_ctime.tv_nsec) &&
(vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
events |= FILE_ATTRIB;
#endif
}
if (events == 0) {
mutex_exit(&pvp->pvp_mutex);
mutex_exit(&pfcp->pfc_lock);
return;
}
pkevp = pfp->pfop_pev;
pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
pkevp->portkev_events |= events;
port_fop_listremove(pvp, pfp);
port_fop_listinsert_tail(pvp, pfp);
port_send_event(pkevp);
pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
}
mutex_exit(&pvp->pvp_mutex);
mutex_exit(&pfcp->pfc_lock);
}
int
port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
{
portfop_cache_t *pfcp;
port_source_t *pse;
int error;
if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
if (error = port_associate_ksource(pp->port_fd, source,
&pse, port_close_fop, pp, NULL)) {
*pfcpp = NULL;
return (error);
}
}
if ((pfcp = pse->portsrc_data) == NULL) {
pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
mutex_enter(&pp->port_queue.portq_source_mutex);
if (pse->portsrc_data == NULL) {
pse->portsrc_data = pfcp;
mutex_exit(&pp->port_queue.portq_source_mutex);
} else {
mutex_exit(&pp->port_queue.portq_source_mutex);
kmem_free(pfcp, sizeof (portfop_cache_t));
pfcp = pse->portsrc_data;
}
}
*pfcpp = pfcp;
return (0);
}
int
port_fop_pvfsadd(portfop_vp_t *pvp)
{
int error = 0;
vnode_t *vp = pvp->pvp_vp;
portfop_vfs_hash_t *pvfsh;
portfop_vfs_t *pvfsp;
fsem_t *fsemp;
pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
mutex_enter(&pvfsh->pvfshash_mutex);
for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
;
if (!pvfsp) {
if ((fsemp = port_fop_fsemop()) != NULL) {
if ((error = fsem_install(vp->v_vfsp, fsemp,
vp->v_vfsp, OPUNIQ, NULL, NULL))) {
mutex_exit(&pvfsh->pvfshash_mutex);
return (error);
}
} else {
mutex_exit(&pvfsh->pvfshash_mutex);
return (EINVAL);
}
pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
pvfsp->pvfs = vp->v_vfsp;
list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
offsetof(portfop_vp_t, pvp_pvfsnode));
pvfsp->pvfs_fsemp = fsemp;
pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
pvfsh->pvfshash_pvfsp = pvfsp;
}
if (!pvfsp->pvfs_unmount) {
pvp->pvp_pvfsp = pvfsp;
list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
} else {
error = EINVAL;
}
mutex_exit(&pvfsh->pvfshash_mutex);
return (error);
}
void
port_install_fopdata(vnode_t *vp)
{
portfop_vp_t *npvp;
npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
offsetof(portfop_t, pfop_node));
npvp->pvp_vp = vp;
if (atomic_cas_ptr(&vp->v_fopdata, NULL, npvp) != NULL) {
mutex_destroy(&npvp->pvp_mutex);
list_destroy(&npvp->pvp_pfoplist);
kmem_free(npvp, sizeof (*npvp));
}
}
int
port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
uintptr_t object, int events, void *user, char *cname, int clen,
vnode_t *dvp)
{
portfop_t *pfp = NULL;
port_kevent_t *pkevp;
fem_t *femp;
int error = 0;
portfop_vp_t *pvp;
*pfpp = NULL;
if (pfp == NULL) {
if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
PORT_ALLOC_CACHED, &pkevp)) {
return (error);
}
pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
pfp->pfop_pev = pkevp;
}
pfp->pfop_vp = vp;
pfp->pfop_pid = curproc->p_pid;
pfp->pfop_pcache = pfcp;
pfp->pfop_pp = pp;
pfp->pfop_flags |= PORT_FOP_ACTIVE;
pfp->pfop_cname = cname;
pfp->pfop_clen = clen;
pfp->pfop_dvp = dvp;
pfp->pfop_object = object;
pkevp->portkev_callback = port_fop_callback;
pkevp->portkev_arg = pfp;
pkevp->portkev_object = object;
pkevp->portkev_user = user;
pkevp->portkev_events = 0;
port_pcache_insert(pfcp, pfp);
if ((pvp = vp->v_fopdata) == NULL) {
port_install_fopdata(vp);
pvp = vp->v_fopdata;
}
mutex_enter(&pvp->pvp_mutex);
if (pvp->pvp_femp == NULL) {
if ((femp = port_fop_femop()) != NULL) {
if (!(error = fem_install(pfp->pfop_vp, femp,
(void *)vp, OPUNIQ, NULL, NULL))) {
pvp->pvp_femp = femp;
if (!(error = port_fop_pvfsadd(pvp))) {
VN_HOLD(vp);
} else {
(void) fem_uninstall(vp, femp, vp);
pvp->pvp_femp = NULL;
}
}
} else {
error = EINVAL;
}
}
if (error) {
pfp->pfop_cname = NULL;
port_pcache_remove_fop(pfcp, pfp);
mutex_exit(&pvp->pvp_mutex);
return (error);
}
pfp->pfop_events = events;
port_fop_listinsert_head(pvp, pfp);
mutex_exit(&pvp->pvp_mutex);
if (dvp != NULL)
VN_HOLD(dvp);
*pfpp = pfp;
return (0);
}
vnode_t *
port_resolve_vp(vnode_t *vp)
{
vnode_t *rvp;
if (vfs_mntdummyvp && mntfstype != 0 &&
vp->v_vfsp->vfs_fstype == mntfstype) {
VN_RELE(vp);
vp = vfs_mntdummyvp;
VN_HOLD(vfs_mntdummyvp);
}
if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
VN_HOLD(rvp);
VN_RELE(vp);
vp = rvp;
}
return (vp);
}
int
port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
void *user)
{
portfop_cache_t *pfcp;
vnode_t *vp, *dvp, *oldvp = NULL, *olddvp = NULL, *orig;
portfop_t *pfp;
int error = 0;
file_obj_t fobj;
void *objptr;
char *cname;
int clen;
int follow;
if ((events & ~FILE_EVENTS_MASK) != 0)
return (EINVAL);
if (get_udatamodel() == DATAMODEL_NATIVE) {
if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
return (EFAULT);
objptr = (void *)&fobj;
#ifdef _SYSCALL32_IMPL
} else {
file_obj32_t fobj32;
if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
return (EFAULT);
objptr = (void *)&fobj32;
#endif
}
vp = dvp = NULL;
follow = !(events & FILE_NOFOLLOW);
events = events & ~FILE_NOFOLLOW;
if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
follow)) != 0) {
return (error);
}
if (dvp != NULL) {
dvp = port_resolve_vp(dvp);
}
if (vp == NULL) {
error = ENOENT;
goto errout;
}
vp = port_resolve_vp(orig = vp);
if (vp != NULL && vnevent_support(vp, NULL)) {
error = ENOTSUP;
goto errout;
}
if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp &&
!(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) {
VN_RELE(dvp);
dvp = NULL;
}
if (error = port_fop_associate_source(&pfcp, pp, source)) {
goto errout;
}
mutex_enter(&pfcp->pfc_lock);
pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
(void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
pfp = NULL;
}
if (pfp == NULL) {
vnode_t *tvp, *tdvp;
portfop_t *tpfp;
int error;
if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
events, user, cname, clen, dvp)) {
mutex_exit(&pfcp->pfc_lock);
goto errout;
}
pfp->pfop_callrid = curthread;
cname = NULL;
mutex_exit(&pfcp->pfc_lock);
tvp = NULL;
if ((error = port_fop_getdvp(objptr, &tvp, NULL,
NULL, NULL, follow)) == 0) {
if (tvp != NULL) {
tvp = port_resolve_vp(tvp);
VN_RELE(tvp);
}
}
if (error || tvp == NULL || tvp != vp) {
mutex_enter(&pfcp->pfc_lock);
tpfp = port_cache_lookup_fop(pfcp,
curproc->p_pid, object);
error = 0;
if (tpfp == NULL || tpfp != pfp ||
pfp->pfop_vp != vp ||
pfp->pfop_dvp != dvp ||
pfp->pfop_callrid != curthread) {
mutex_exit(&pfcp->pfc_lock);
goto errout;
}
tdvp = tvp = NULL;
if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
error = EINVAL;
}
mutex_exit(&pfcp->pfc_lock);
if (tvp != NULL)
VN_RELE(tvp);
if (tdvp != NULL)
VN_RELE(tdvp);
goto errout;
}
} else {
portfop_vp_t *pvp = vp->v_fopdata;
mutex_enter(&pvp->pvp_mutex);
if (port_remove_done_event(pfp->pfop_pev)) {
pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
}
pfp->pfop_events = events;
pfp->pfop_pev->portkev_user = user;
if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
pfp->pfop_flags |= PORT_FOP_ACTIVE;
if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
pvp = (portfop_vp_t *)vp->v_fopdata;
port_fop_listremove(pvp, pfp);
port_fop_listinsert_head(pvp, pfp);
}
}
pfp->pfop_callrid = curthread;
mutex_exit(&pvp->pvp_mutex);
mutex_exit(&pfcp->pfc_lock);
}
if (vp->v_type != VFIFO) {
port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
}
error = 0;
port_fop_trimpfplist(vp);
errout:
if (vp != NULL)
VN_RELE(vp);
if (dvp != NULL)
VN_RELE(dvp);
if (oldvp != NULL)
VN_RELE(oldvp);
if (olddvp != NULL)
VN_RELE(olddvp);
if (cname != NULL) {
kmem_free(cname, clen + 1);
}
return (error);
}
int
port_dissociate_fop(port_t *pp, uintptr_t object)
{
portfop_cache_t *pfcp;
portfop_t *pfp;
port_source_t *pse;
int active = 0;
vnode_t *tvp = NULL, *tdvp = NULL;
pse = port_getsrc(pp, PORT_SOURCE_FILE);
if (pse == NULL ||
(pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
return (EINVAL);
mutex_enter(&pfcp->pfc_lock);
pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
if (pfp == NULL) {
mutex_exit(&pfcp->pfc_lock);
return (ENOENT);
}
(void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
mutex_exit(&pfcp->pfc_lock);
if (tvp != NULL)
VN_RELE(tvp);
if (tdvp != NULL)
VN_RELE(tdvp);
return (active ? 0 : ENOENT);
}
static void
port_close_fop(void *arg, int port, pid_t pid, int lastclose)
{
port_t *pp = arg;
portfop_cache_t *pfcp;
portfop_t **hashtbl;
portfop_t *pfp;
portfop_t *pfpnext;
int index, i;
port_source_t *pse;
vnode_t *tdvp = NULL;
vnode_t *vpl[PORTFOP_NVP];
pse = port_getsrc(pp, PORT_SOURCE_FILE);
if (pse == NULL ||
(pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
return;
hashtbl = (portfop_t **)pfcp->pfc_hash;
index = i = 0;
bzero(vpl, sizeof (vpl));
mutex_enter(&pfcp->pfc_lock);
while (index < PORTFOP_HASHSIZE) {
pfp = hashtbl[index];
while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
pfpnext = pfp->pfop_hashnext;
if (pid == pfp->pfop_pid) {
(void) port_remove_fop(pfp, pfcp, 1, NULL,
&vpl[i], &tdvp);
if (vpl[i] != NULL) {
i++;
}
if (tdvp != NULL) {
vpl[i++] = tdvp;
tdvp = NULL;
}
}
pfp = pfpnext;
}
if (pfp == NULL)
index++;
if (i >= (PORTFOP_NVP - 1) ||
(i > 0 && index == PORTFOP_HASHSIZE)) {
mutex_exit(&pfcp->pfc_lock);
while (i > 0) {
VN_RELE(vpl[--i]);
vpl[i] = NULL;
}
mutex_enter(&pfcp->pfc_lock);
}
}
while (lastclose && pfcp->pfc_objcount) {
(void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
}
mutex_exit(&pfcp->pfc_lock);
if (lastclose) {
ASSERT(pfcp->pfc_objcount == 0);
pse->portsrc_data = NULL;
kmem_free(pfcp, sizeof (portfop_cache_t));
}
}
static void
port_fop_excep(list_t *tlist, int op)
{
portfop_t *pfp;
portfop_cache_t *pfcp;
port_t *pp;
port_kevent_t *pkevp;
vnode_t *tdvp;
int error = 0;
while (pfp = (portfop_t *)list_head(tlist)) {
int removed = 0;
list_remove(tlist, pfp);
pfcp = pfp->pfop_pcache;
mutex_enter(&pfcp->pfc_lock);
if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
removed = port_remove_done_event(pfp->pfop_pev);
}
if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
pp = pfp->pfop_pp;
pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
PORT_ALLOC_DEFAULT, &pkevp);
if (!error) {
pkevp->portkev_callback = port_fop_callback;
pkevp->portkev_arg = NULL;
pkevp->portkev_object =
pfp->pfop_pev->portkev_object;
pkevp->portkev_user =
pfp->pfop_pev->portkev_user;
pkevp->portkev_pid =
pfp->pfop_pev->portkev_pid;
pkevp->portkev_events = op;
port_send_event(pkevp);
}
}
tdvp = pfp->pfop_dvp;
port_pcache_remove_fop(pfcp, pfp);
mutex_exit(&pfcp->pfc_lock);
if (tdvp != NULL)
VN_RELE(tdvp);
}
}
void
port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
{
port_kevent_t *pkevp;
portfop_t *pfp, *npfp;
portfop_vp_t *pvp;
list_t tmplist;
int removeall = 0;
pvp = (portfop_vp_t *)vp->v_fopdata;
mutex_enter(&pvp->pvp_mutex);
if (!list_head(&pvp->pvp_pfoplist)) {
mutex_exit(&pvp->pvp_mutex);
return;
}
if ((events & (FILE_EXCEPTION))) {
list_create(&tmplist, sizeof (portfop_t),
offsetof(portfop_t, pfop_node));
if (dvp == NULL || cname == NULL) {
removeall = 1;
}
}
if (!removeall) {
for (pfp = (portfop_t *)list_tail(&pvp->pvp_pfoplist);
pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE); pfp = npfp) {
npfp = list_prev(&pvp->pvp_pfoplist, pfp);
}
for (; pfp != NULL; pfp = npfp) {
int levents = events;
npfp = list_prev(&pvp->pvp_pfoplist, pfp);
if ((events & (FILE_EXCEPTION))) {
ASSERT(dvp != NULL && cname != NULL);
if (pfp->pfop_dvp == NULL ||
(pfp->pfop_dvp == dvp &&
(strcmp(cname, pfp->pfop_cname) == 0))) {
port_fop_listremove(pvp, pfp);
list_insert_tail(&tmplist, (void *)pfp);
pfp->pfop_flags |= PORT_FOP_REMOVING;
continue;
} else {
levents = FILE_ATTRIB;
}
}
if (pfp->pfop_events & levents) {
pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
port_fop_listremove(pvp, pfp);
port_fop_listinsert_tail(pvp, pfp);
pkevp = pfp->pfop_pev;
pkevp->portkev_events |=
(levents & pfp->pfop_events);
port_send_event(pkevp);
pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
}
}
}
if ((events & (FILE_EXCEPTION))) {
if (!removeall) {
for (; pfp; pfp = npfp) {
npfp = list_next(&pvp->pvp_pfoplist, pfp);
if (dvp == NULL || cname == NULL ||
pfp->pfop_dvp == NULL ||
(pfp->pfop_dvp == dvp &&
(strcmp(cname, pfp->pfop_cname) == 0))) {
port_fop_listremove(pvp, pfp);
list_insert_tail(&tmplist, (void *)pfp);
pfp->pfop_flags |= PORT_FOP_REMOVING;
}
}
} else {
port_fop_listmove(pvp, &tmplist);
for (pfp = (portfop_t *)list_head(&tmplist);
pfp; pfp = list_next(&tmplist, pfp)) {
pfp->pfop_flags |= PORT_FOP_REMOVING;
}
}
if (port_fop_femuninstall(vp))
VN_RELE(vp);
port_fop_excep(&tmplist, events);
list_destroy(&tmplist);
} else {
mutex_exit(&pvp->pvp_mutex);
port_fop_trimpfplist(vp);
}
}
void
port_fop(vnode_t *vp, int op, int retval)
{
int event = 0;
if (retval)
return;
if (op & FOP_MODIFIED_MASK) {
event = FILE_MODIFIED;
}
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) {
port_fop_sendevent(vp, event, NULL, NULL);
}
}
static int port_forceunmount(vfs_t *vfsp)
{
char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
if (fsname == NULL) {
return (0);
}
if (strcmp(fsname, MNTTYPE_NFS) == 0) {
return (1);
}
if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
return (1);
}
if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
return (1);
}
return (0);
}
int
port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
{
vfs_t *vfsp = (vfs_t *)vf->fa_fnode->fn_available;
kmutex_t *mtx;
portfop_vfs_t *pvfsp, **ppvfsp;
portfop_vp_t *pvp;
int error;
int fmfs;
fmfs = port_forceunmount(vfsp);
mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
pvfsp = NULL;
mutex_enter(mtx);
for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
;
if (fmfs && !(flag & MS_FORCE) &&
!list_is_empty(&pvfsp->pvfs_pvplist)) {
mutex_exit(mtx);
return (EBUSY);
}
pvfsp->pvfs_unmount = 1;
mutex_exit(mtx);
(void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
list_remove(&pvfsp->pvfs_pvplist, pvp);
port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
VN_RELE(pvp->pvp_vp);
}
error = vfsnext_unmount(vf, flag, cr);
mutex_enter(mtx);
for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
ppvfsp = &(*ppvfsp)->pvfs_next)
;
ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
if (*ppvfsp) {
pvfsp = *ppvfsp;
*ppvfsp = pvfsp->pvfs_next;
}
mutex_exit(mtx);
kmem_free(pvfsp, sizeof (portfop_vfs_t));
return (error);
}
static int
port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_open(vf, mode, cr, ct);
port_fop(vp, FOP_FILE_OPEN, retval);
return (retval);
}
static int
port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_write(vf, uiop, ioflag, cr, ct);
port_fop(vp, FOP_FILE_WRITE, retval);
return (retval);
}
static int
port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_map(vf, off, as, addrp, len, prot, maxport,
flags, cr, ct);
port_fop(vp, FOP_FILE_MAP, retval);
return (retval);
}
static int
port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_read(vf, uiop, ioflag, cr, ct);
port_fop(vp, FOP_FILE_READ, retval);
return (retval);
}
int
port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
int events = 0;
retval = vnext_setattr(vf, vap, flags, cr, ct);
if (vap->va_mask & AT_SIZE) {
events |= FOP_FILE_TRUNC;
}
if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
events |= FOP_FILE_SETATTR_MTIME;
}
if (vap->va_mask & AT_ATIME) {
events |= FOP_FILE_SETATTR_ATIME;
}
events |= FOP_FILE_SETATTR_CTIME;
port_fop(vp, events, retval);
return (retval);
}
int
port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
int mode, vnode_t **vpp, cred_t *cr, int flag,
caller_context_t *ct, vsecattr_t *vsecp)
{
int retval, got = 1;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
vattr_t vatt, vatt1;
vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
got = 0;
}
retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
flag, ct, vsecp);
vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
(vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
port_fop(vp, FOP_FILE_CREATE, retval);
}
}
return (retval);
}
int
port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_remove(vf, nm, cr, ct, flags);
port_fop(vp, FOP_FILE_REMOVE, retval);
return (retval);
}
int
port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
caller_context_t *ct, int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_link(vf, svp, tnm, cr, ct, flags);
port_fop(vp, FOP_FILE_LINK, retval);
return (retval);
}
int
port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
caller_context_t *ct, int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
port_fop(vp, FOP_FILE_RENAMESRC, retval);
return (retval);
}
int
port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
port_fop(vp, FOP_FILE_MKDIR, retval);
return (retval);
}
int
port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
caller_context_t *ct, int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
port_fop(vp, FOP_FILE_RMDIR, retval);
return (retval);
}
int
port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
caller_context_t *ct, int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
port_fop(vp, FOP_FILE_READDIR, retval);
return (retval);
}
int
port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
cred_t *cr, caller_context_t *ct, int flags)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
port_fop(vp, FOP_FILE_SYMLINK, retval);
return (retval);
}
int
port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
caller_context_t *ct)
{
int retval;
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
port_fop(vp, FOP_FILE_SETSECATTR, retval);
return (retval);
}
int
port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
caller_context_t *ct)
{
vnode_t *vp = (vnode_t *)vf->fa_fnode->fn_available;
switch (vnevent) {
case VE_RENAME_SRC:
port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
break;
case VE_RENAME_DEST:
port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
break;
case VE_REMOVE:
port_fop_sendevent(vp, FILE_DELETE, dvp, name);
break;
case VE_RMDIR:
port_fop_sendevent(vp, FILE_DELETE, dvp, name);
break;
case VE_CREATE:
port_fop_sendevent(vp,
FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
break;
case VE_LINK:
port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
break;
case VE_RENAME_DEST_DIR:
port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
NULL, NULL);
break;
case VE_MOUNTEDOVER:
port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
break;
case VE_TRUNCATE:
port_fop_sendevent(vp, FILE_TRUNC, NULL, NULL);
break;
default:
break;
}
return (vnext_vnevent(vf, vnevent, dvp, name, ct));
}