#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/time.h>
#include <sys/varargs.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/vnode.h>
#include <fs/fs_subr.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/disp.h>
#include <sys/sdt.h>
#include <sys/cred.h>
#include <sys/list.h>
#include <sys/vscan.h>
#include <sys/sysmacros.h>
#define VS_REQ_MAGIC 0x52515354
#define VS_REQS_DEFAULT 20000
#define VS_NODES_DEFAULT 128
#define VS_WORKERS_DEFAULT 32
#define VS_SCANWAIT_DEFAULT 15*60
#define VS_REQL_HANDLER_TIMEOUT 30
#define VS_EXT_RECURSE_DEPTH 8
#define VS_ACCESS_UNDEFINED 0
#define VS_ACCESS_ALLOW 1
#define VS_ACCESS_DENY 2
#define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
uint32_t vs_reqs_max = VS_REQS_DEFAULT;
uint32_t vs_nodes_max = VS_NODES_DEFAULT;
uint32_t vs_workers = VS_WORKERS_DEFAULT;
uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT;
typedef enum {
VS_SVC_UNCONFIG,
VS_SVC_IDLE,
VS_SVC_ENABLED,
VS_SVC_DISABLED
} vscan_svc_state_t;
static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
typedef enum {
VS_SVC_REQ_INIT,
VS_SVC_REQ_QUEUED,
VS_SVC_REQ_IN_PROGRESS,
VS_SVC_REQ_SCANNING,
VS_SVC_REQ_ASYNC_COMPLETE,
VS_SVC_REQ_COMPLETE
} vscan_svc_req_state_t;
typedef struct vscan_req {
uint32_t vsr_magic;
list_node_t vsr_lnode;
vnode_t *vsr_vp;
uint32_t vsr_idx;
uint32_t vsr_seqnum;
uint32_t vsr_refcnt;
kcondvar_t vsr_cv;
vscan_svc_req_state_t vsr_state;
} vscan_req_t;
static list_t vscan_svc_reql;
typedef struct vscan_svc_node {
vscan_req_t *vsn_req;
uint8_t vsn_quarantined;
uint8_t vsn_modified;
uint64_t vsn_size;
timestruc_t vsn_mtime;
vs_scanstamp_t vsn_scanstamp;
uint32_t vsn_result;
uint32_t vsn_access;
} vscan_svc_node_t;
static vscan_svc_node_t *vscan_svc_nodes;
static int vscan_svc_nodes_sz;
static taskq_t *vscan_svc_taskq = NULL;
typedef struct {
uint32_t vsc_reql;
uint32_t vsc_node;
uint32_t vsc_tq;
} vscan_svc_counts_t;
static vscan_svc_counts_t vscan_svc_counts;
static kmutex_t vscan_svc_mutex;
static uint32_t vscan_svc_seqnum = 0;
static kmutex_t vscan_svc_cfg_mutex;
static vs_config_t vscan_svc_config;
static char *vscan_svc_types[VS_TYPES_MAX];
static kthread_t *vscan_svc_reql_thread;
static kcondvar_t vscan_svc_reql_cv;
static vscan_req_t *vscan_svc_reql_next;
int vscan_svc_scan_file(vnode_t *, cred_t *, int);
static void vscan_svc_taskq_callback(void *);
static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
static int vscan_svc_exempt_filetype(char *);
static int vscan_svc_match_ext(char *, char *, int);
static void vscan_svc_do_scan(vscan_req_t *);
static vs_scan_req_t *vscan_svc_populate_req(int);
static void vscan_svc_process_scan_result(int);
static void vscan_svc_scan_complete(vscan_req_t *);
static void vscan_svc_delete_req(vscan_req_t *);
static int vscan_svc_insert_req(vscan_req_t *);
static void vscan_svc_remove_req(int);
static vscan_req_t *vscan_svc_reql_find(vnode_t *);
static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
static void vscan_svc_reql_remove(vscan_req_t *);
static int vscan_svc_getattr(int);
static int vscan_svc_setattr(int, int);
static void vscan_svc_reql_handler(void);
int
vscan_svc_init()
{
if (vscan_svc_state != VS_SVC_UNCONFIG) {
DTRACE_PROBE1(vscan__svc__state__violation,
int, vscan_svc_state);
return (-1);
}
mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
vscan_svc_counts.vsc_reql = 0;
vscan_svc_counts.vsc_node = 0;
vscan_svc_counts.vsc_tq = 0;
vscan_svc_state = VS_SVC_IDLE;
return (0);
}
void
vscan_svc_fini()
{
if (vscan_svc_state != VS_SVC_IDLE) {
DTRACE_PROBE1(vscan__svc__state__violation,
int, vscan_svc_state);
return;
}
kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
cv_destroy(&vscan_svc_reql_cv);
mutex_destroy(&vscan_svc_mutex);
mutex_destroy(&vscan_svc_cfg_mutex);
vscan_svc_state = VS_SVC_UNCONFIG;
}
int
vscan_svc_enable(void)
{
mutex_enter(&vscan_svc_mutex);
switch (vscan_svc_state) {
case VS_SVC_ENABLED:
break;
case VS_SVC_IDLE:
list_create(&vscan_svc_reql, sizeof (vscan_req_t),
offsetof(vscan_req_t, vsr_lnode));
vscan_svc_reql_next = list_head(&vscan_svc_reql);
vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
ASSERT(vscan_svc_taskq != NULL);
vscan_svc_reql_thread = thread_create(NULL, 0,
vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
ASSERT(vscan_svc_reql_thread != NULL);
vscan_svc_state = VS_SVC_ENABLED;
fs_vscan_register(vscan_svc_scan_file);
break;
default:
DTRACE_PROBE1(vscan__svc__state__violation,
int, vscan_svc_state);
return (-1);
}
mutex_exit(&vscan_svc_mutex);
return (0);
}
void
vscan_svc_disable(void)
{
mutex_enter(&vscan_svc_mutex);
switch (vscan_svc_state) {
case VS_SVC_ENABLED:
fs_vscan_register(NULL);
vscan_svc_state = VS_SVC_DISABLED;
cv_signal(&vscan_svc_reql_cv);
break;
default:
DTRACE_PROBE1(vscan__svc__state__violation, int,
vscan_svc_state);
}
mutex_exit(&vscan_svc_mutex);
}
boolean_t
vscan_svc_in_use()
{
boolean_t in_use;
mutex_enter(&vscan_svc_mutex);
switch (vscan_svc_state) {
case VS_SVC_IDLE:
case VS_SVC_UNCONFIG:
in_use = B_FALSE;
break;
default:
in_use = B_TRUE;
break;
}
mutex_exit(&vscan_svc_mutex);
return (in_use);
}
vnode_t *
vscan_svc_get_vnode(int idx)
{
vnode_t *vp = NULL;
ASSERT(idx > 0);
ASSERT(idx <= vs_nodes_max);
mutex_enter(&vscan_svc_mutex);
if (vscan_svc_nodes[idx].vsn_req)
vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
mutex_exit(&vscan_svc_mutex);
return (vp);
}
int
vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
{
int access;
vscan_req_t *req;
boolean_t allow;
clock_t timeout, time_left;
if ((vp == NULL) || (vp->v_path == vn_vpath_empty) || cr == NULL)
return (0);
DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
if (vscan_svc_exempt_file(vp, &allow)) {
if ((allow == B_TRUE) || (async != 0))
return (0);
return (EACCES);
}
mutex_enter(&vscan_svc_mutex);
if (vscan_svc_state != VS_SVC_ENABLED) {
DTRACE_PROBE1(vscan__svc__state__violation,
int, vscan_svc_state);
mutex_exit(&vscan_svc_mutex);
return (0);
}
if ((req = vscan_svc_reql_insert(vp)) == NULL) {
mutex_exit(&vscan_svc_mutex);
cmn_err(CE_WARN, "Virus scan request list full");
return ((async != 0) ? 0 : EACCES);
}
if (async) {
mutex_exit(&vscan_svc_mutex);
return (0);
}
++(req->vsr_refcnt);
time_left = SEC_TO_TICK(vs_scan_wait);
while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
timeout = time_left;
time_left = cv_reltimedwait_sig(&(req->vsr_cv),
&vscan_svc_mutex, timeout, TR_CLOCK_TICK);
}
if (time_left == -1) {
cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
vp->v_path, req->vsr_seqnum);
DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
}
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
if (vscan_svc_state == VS_SVC_DISABLED)
access = VS_ACCESS_ALLOW;
else if (req->vsr_idx == 0)
access = VS_ACCESS_DENY;
else
access = vscan_svc_nodes[req->vsr_idx].vsn_access;
if ((--req->vsr_refcnt) == 0)
vscan_svc_delete_req(req);
mutex_exit(&vscan_svc_mutex);
return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
}
static void
vscan_svc_reql_handler(void)
{
vscan_req_t *req, *next;
for (;;) {
mutex_enter(&vscan_svc_mutex);
if ((vscan_svc_state == VS_SVC_DISABLED) &&
(vscan_svc_counts.vsc_reql == 0)) {
taskq_destroy(vscan_svc_taskq);
vscan_svc_taskq = NULL;
list_destroy(&vscan_svc_reql);
vscan_svc_state = VS_SVC_IDLE;
mutex_exit(&vscan_svc_mutex);
return;
}
req = vscan_svc_reql_next;
while (req != NULL) {
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
next = list_next(&vscan_svc_reql, req);
if (vscan_svc_state == VS_SVC_DISABLED) {
vscan_svc_scan_complete(req);
} else {
if (vscan_svc_insert_req(req) == -1)
break;
(void) taskq_dispatch(vscan_svc_taskq,
vscan_svc_taskq_callback,
(void *)req, TQ_SLEEP);
++(vscan_svc_counts.vsc_tq);
req->vsr_state = VS_SVC_REQ_QUEUED;
}
req = next;
}
vscan_svc_reql_next = req;
DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
vscan_svc_counts_t *, &vscan_svc_counts);
(void) cv_reltimedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT), TR_CLOCK_TICK);
DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
vscan_svc_counts_t *, &vscan_svc_counts);
mutex_exit(&vscan_svc_mutex);
}
}
static void
vscan_svc_taskq_callback(void *data)
{
vscan_req_t *req;
mutex_enter(&vscan_svc_mutex);
req = (vscan_req_t *)data;
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
vscan_svc_do_scan(req);
if (req->vsr_state != VS_SVC_REQ_SCANNING)
vscan_svc_scan_complete(req);
--(vscan_svc_counts.vsc_tq);
mutex_exit(&vscan_svc_mutex);
}
static void
vscan_svc_do_scan(vscan_req_t *req)
{
int idx, result;
vscan_svc_node_t *node;
vs_scan_req_t *door_req;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
idx = req->vsr_idx;
node = &vscan_svc_nodes[idx];
req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
if (vscan_svc_state != VS_SVC_ENABLED) {
node->vsn_access = VS_ACCESS_ALLOW;
return;
}
if (vscan_svc_getattr(idx) != 0) {
cmn_err(CE_WARN, "Can't access xattr for %s\n",
req->vsr_vp->v_path);
node->vsn_access = VS_ACCESS_DENY;
return;
}
door_req = vscan_svc_populate_req(idx);
mutex_exit(&vscan_svc_mutex);
if (vscan_drv_create_node(idx) != B_TRUE)
result = VS_STATUS_ERROR;
else
result = vscan_door_scan_file(door_req);
kmem_free(door_req, sizeof (vs_scan_req_t));
mutex_enter(&vscan_svc_mutex);
if (result != VS_STATUS_SCANNING) {
vscan_svc_nodes[idx].vsn_result = result;
vscan_svc_process_scan_result(idx);
} else {
if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
req->vsr_state = VS_SVC_REQ_SCANNING;
}
}
static vs_scan_req_t *
vscan_svc_populate_req(int idx)
{
vs_scan_req_t *scan_req;
vscan_req_t *req;
vscan_svc_node_t *node;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
node = &vscan_svc_nodes[idx];
req = node->vsn_req;
scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
scan_req->vsr_idx = idx;
scan_req->vsr_seqnum = req->vsr_seqnum;
(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
scan_req->vsr_size = node->vsn_size;
scan_req->vsr_modified = node->vsn_modified;
scan_req->vsr_quarantined = node->vsn_quarantined;
scan_req->vsr_flags = 0;
(void) strncpy(scan_req->vsr_scanstamp,
node->vsn_scanstamp, sizeof (vs_scanstamp_t));
return (scan_req);
}
static void
vscan_svc_scan_complete(vscan_req_t *req)
{
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
ASSERT(req != NULL);
req->vsr_state = VS_SVC_REQ_COMPLETE;
if ((--req->vsr_refcnt) == 0)
vscan_svc_delete_req(req);
else
cv_broadcast(&(req->vsr_cv));
}
static void
vscan_svc_delete_req(vscan_req_t *req)
{
int idx;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
ASSERT(req != NULL);
ASSERT(req->vsr_refcnt == 0);
ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
if ((idx = req->vsr_idx) != 0)
vscan_svc_remove_req(idx);
vscan_svc_reql_remove(req);
cv_signal(&vscan_svc_reql_cv);
}
void
vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
{
vscan_req_t *req;
vscan_svc_node_t *node;
mutex_enter(&vscan_svc_mutex);
node = &vscan_svc_nodes[scan_rsp->vsr_idx];
if ((req = node->vsn_req) == NULL) {
mutex_exit(&vscan_svc_mutex);
return;
}
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
mutex_exit(&vscan_svc_mutex);
return;
}
node->vsn_result = scan_rsp->vsr_result;
(void) strncpy(node->vsn_scanstamp,
scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
vscan_svc_process_scan_result(scan_rsp->vsr_idx);
if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
vscan_svc_scan_complete(node->vsn_req);
else
node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
mutex_exit(&vscan_svc_mutex);
}
void
vscan_svc_scan_abort()
{
int idx;
vscan_req_t *req;
mutex_enter(&vscan_svc_mutex);
for (idx = 1; idx <= vs_nodes_max; idx++) {
if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
continue;
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
if (req->vsr_state == VS_SVC_REQ_SCANNING) {
DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
vscan_svc_process_scan_result(idx);
vscan_svc_scan_complete(req);
}
}
mutex_exit(&vscan_svc_mutex);
}
static void
vscan_svc_process_scan_result(int idx)
{
struct vattr attr;
vnode_t *vp;
timestruc_t *mtime;
vscan_svc_node_t *node;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
node = &vscan_svc_nodes[idx];
switch (node->vsn_result) {
case VS_STATUS_INFECTED:
node->vsn_access = VS_ACCESS_DENY;
node->vsn_quarantined = 1;
node->vsn_scanstamp[0] = '\0';
(void) vscan_svc_setattr(idx,
XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
break;
case VS_STATUS_CLEAN:
node->vsn_access = VS_ACCESS_ALLOW;
vp = node->vsn_req->vsr_vp;
mtime = &(node->vsn_mtime);
attr.va_mask = AT_MTIME;
if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
(mtime->tv_sec != attr.va_mtime.tv_sec) ||
(mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
node);
(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
break;
}
node->vsn_modified = 0;
(void) vscan_svc_setattr(idx,
XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
break;
case VS_STATUS_NO_SCAN:
if (node->vsn_quarantined)
node->vsn_access = VS_ACCESS_DENY;
else
node->vsn_access = VS_ACCESS_ALLOW;
break;
case VS_STATUS_ERROR:
case VS_STATUS_UNDEFINED:
default:
if ((node->vsn_quarantined) ||
(node->vsn_modified) ||
(node->vsn_scanstamp[0] == '\0'))
node->vsn_access = VS_ACCESS_DENY;
else
node->vsn_access = VS_ACCESS_ALLOW;
break;
}
DTRACE_PROBE4(vscan__result,
int, idx, int, node->vsn_req->vsr_seqnum,
int, node->vsn_result, int, node->vsn_access);
}
static int
vscan_svc_getattr(int idx)
{
xvattr_t xvattr;
xoptattr_t *xoap = NULL;
vnode_t *vp;
vscan_svc_node_t *node;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
node = &vscan_svc_nodes[idx];
if ((vp = node->vsn_req->vsr_vp) == NULL)
return (-1);
xva_init(&xvattr);
xvattr.xva_vattr.va_mask |= AT_SIZE;
xvattr.xva_vattr.va_mask |= AT_MTIME;
XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
return (-1);
if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
cmn_err(CE_NOTE, "Virus scan request failed; "
"file system does not support virus scanning");
return (-1);
}
node->vsn_size = xvattr.xva_vattr.va_size;
node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
return (-1);
node->vsn_modified = xoap->xoa_av_modified;
if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
return (-1);
node->vsn_quarantined = xoap->xoa_av_quarantined;
if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
(void) memcpy(node->vsn_scanstamp,
xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
}
DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
return (0);
}
static int
vscan_svc_setattr(int idx, int which)
{
xvattr_t xvattr;
xoptattr_t *xoap = NULL;
vnode_t *vp;
int len;
vscan_svc_node_t *node;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
node = &vscan_svc_nodes[idx];
if ((vp = node->vsn_req->vsr_vp) == NULL)
return (-1);
xva_init(&xvattr);
if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
return (-1);
if (which & XAT_AV_MODIFIED) {
XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
xoap->xoa_av_modified = node->vsn_modified;
}
if (which & XAT_AV_QUARANTINED) {
XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
xoap->xoa_av_quarantined = node->vsn_quarantined;
}
if (which & XAT_AV_SCANSTAMP) {
XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
len = strlen(node->vsn_scanstamp);
(void) memcpy(xoap->xoa_av_scanstamp,
node->vsn_scanstamp, len);
}
if (node->vsn_access != VS_ACCESS_ALLOW) {
xvattr.xva_vattr.va_mask |= AT_MTIME;
gethrestime(&xvattr.xva_vattr.va_mtime);
}
if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
return (-1);
DTRACE_PROBE2(vscan__setattr,
vscan_svc_node_t *, node, int, which);
return (0);
}
int
vscan_svc_configure(vs_config_t *conf)
{
int count = 0;
char *p, *beg, *end;
mutex_enter(&vscan_svc_cfg_mutex);
vscan_svc_config = *conf;
(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
beg = vscan_svc_config.vsc_types;
end = beg + vscan_svc_config.vsc_types_len;
for (p = beg; p < end; p += strlen(p) + 1) {
if (count >= VS_TYPES_MAX) {
mutex_exit(&vscan_svc_mutex);
return (-1);
}
vscan_svc_types[count] = p;
++count;
}
mutex_exit(&vscan_svc_cfg_mutex);
return (0);
}
static int
vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
{
struct vattr attr;
ASSERT(vp != NULL);
attr.va_mask = AT_SIZE;
if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
*allow = B_FALSE;
return (0);
}
mutex_enter(&vscan_svc_cfg_mutex);
if (attr.va_size > vscan_svc_config.vsc_max_size) {
DTRACE_PROBE2(vscan__exempt__filesize, char *,
vp->v_path, int, *allow);
*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
mutex_exit(&vscan_svc_cfg_mutex);
return (1);
}
if (vscan_svc_exempt_filetype(vp->v_path)) {
DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
*allow = B_TRUE;
mutex_exit(&vscan_svc_cfg_mutex);
return (1);
}
mutex_exit(&vscan_svc_cfg_mutex);
return (0);
}
static int
vscan_svc_exempt_filetype(char *filepath)
{
int i, rc, exempt = 0;
char *filename, *ext;
ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
if ((filename = strrchr(filepath, '/')) == 0)
filename = filepath;
else
filename++;
if ((ext = strrchr(filename, '.')) == NULL)
ext = "";
else
ext++;
for (i = 0; i < VS_TYPES_MAX; i ++) {
if (vscan_svc_types[i] == 0)
break;
rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
if (rc == -1)
break;
if (rc > 0) {
DTRACE_PROBE2(vscan__type__match, char *, ext,
char *, vscan_svc_types[i]);
exempt = (vscan_svc_types[i][0] == '-');
break;
}
}
return (exempt);
}
static int
vscan_svc_match_ext(char *patn, char *str, int depth)
{
int c1, c2;
if (depth > VS_EXT_RECURSE_DEPTH)
return (-1);
for (;;) {
switch (*patn) {
case 0:
return (*str == 0);
case '?':
if (*str != 0) {
str++;
patn++;
continue;
}
return (0);
case '*':
patn++;
if (*patn == 0)
return (1);
while (*str) {
if (vscan_svc_match_ext(patn, str, depth + 1))
return (1);
str++;
}
return (0);
default:
if (*str != *patn) {
c1 = *str;
c2 = *patn;
c1 = tolower(c1);
c2 = tolower(c2);
if (c1 != c2)
return (0);
}
str++;
patn++;
continue;
}
}
}
static int
vscan_svc_insert_req(vscan_req_t *req)
{
int idx;
vscan_svc_node_t *node;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
if (vscan_svc_counts.vsc_node == vs_nodes_max)
return (-1);
for (idx = 1; idx <= vs_nodes_max; idx++) {
if (vscan_svc_nodes[idx].vsn_req == NULL) {
req->vsr_idx = idx;
node = &vscan_svc_nodes[idx];
(void) memset(node, 0, sizeof (vscan_svc_node_t));
node->vsn_req = req;
node->vsn_modified = 1;
node->vsn_result = VS_STATUS_UNDEFINED;
node->vsn_access = VS_ACCESS_UNDEFINED;
++(vscan_svc_counts.vsc_node);
return (idx);
}
}
return (-1);
}
static void
vscan_svc_remove_req(int idx)
{
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
if (idx != 0) {
(void) memset(&vscan_svc_nodes[idx], 0,
sizeof (vscan_svc_node_t));
--(vscan_svc_counts.vsc_node);
}
}
static vscan_req_t *
vscan_svc_reql_find(vnode_t *vp)
{
vscan_req_t *req;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
req = list_head(&vscan_svc_reql);
while (req != NULL) {
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
if ((req->vsr_vp == vp) &&
(req->vsr_state != VS_SVC_REQ_COMPLETE))
break;
req = list_next(&vscan_svc_reql, req);
}
return (req);
}
static vscan_req_t *
vscan_svc_reql_insert(vnode_t *vp)
{
vscan_req_t *req;
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
if ((req = vscan_svc_reql_find(vp)) != NULL)
return (req);
if (vscan_svc_counts.vsc_reql == vs_reqs_max)
return (NULL);
VN_HOLD(vp);
req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
req->vsr_magic = VS_REQ_MAGIC;
if (vscan_svc_seqnum == UINT32_MAX)
vscan_svc_seqnum = 0;
req->vsr_seqnum = ++vscan_svc_seqnum;
req->vsr_vp = vp;
req->vsr_refcnt = 1;
req->vsr_state = VS_SVC_REQ_INIT;
cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
list_insert_tail(&vscan_svc_reql, req);
if (vscan_svc_reql_next == NULL)
vscan_svc_reql_next = req;
++(vscan_svc_counts.vsc_reql);
cv_signal(&vscan_svc_reql_cv);
return (req);
}
static void
vscan_svc_reql_remove(vscan_req_t *req)
{
ASSERT(MUTEX_HELD(&vscan_svc_mutex));
ASSERT(req->vsr_magic == VS_REQ_MAGIC);
if (vscan_svc_reql_next == req)
vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
list_remove(&vscan_svc_reql, req);
cv_destroy(&(req->vsr_cv));
VN_RELE(req->vsr_vp);
kmem_free(req, sizeof (vscan_req_t));
--(vscan_svc_counts.vsc_reql);
}