15440Sjm199354 /*
25440Sjm199354 * CDDL HEADER START
35440Sjm199354 *
45440Sjm199354 * The contents of this file are subject to the terms of the
55440Sjm199354 * Common Development and Distribution License (the "License").
65440Sjm199354 * You may not use this file except in compliance with the License.
75440Sjm199354 *
85440Sjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95440Sjm199354 * or http://www.opensolaris.org/os/licensing.
105440Sjm199354 * See the License for the specific language governing permissions
115440Sjm199354 * and limitations under the License.
125440Sjm199354 *
135440Sjm199354 * When distributing Covered Code, include this CDDL HEADER in each
145440Sjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155440Sjm199354 * If applicable, add the following below this CDDL HEADER, with the
165440Sjm199354 * fields enclosed by brackets "[]" replaced with your own identifying
175440Sjm199354 * information: Portions Copyright [yyyy] [name of copyright owner]
185440Sjm199354 *
195440Sjm199354 * CDDL HEADER END
205440Sjm199354 */
215440Sjm199354
225440Sjm199354 /*
23*11066Srafael.vanoni@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
245440Sjm199354 * Use is subject to license terms.
255440Sjm199354 */
265440Sjm199354
275440Sjm199354 #include <sys/stat.h>
285440Sjm199354 #include <sys/ddi.h>
295440Sjm199354 #include <sys/sunddi.h>
305440Sjm199354 #include <sys/time.h>
315440Sjm199354 #include <sys/varargs.h>
325440Sjm199354 #include <sys/conf.h>
335440Sjm199354 #include <sys/modctl.h>
345440Sjm199354 #include <sys/cmn_err.h>
355440Sjm199354 #include <sys/vnode.h>
365440Sjm199354 #include <fs/fs_subr.h>
375440Sjm199354 #include <sys/types.h>
385440Sjm199354 #include <sys/file.h>
395440Sjm199354 #include <sys/disp.h>
405440Sjm199354 #include <sys/sdt.h>
415440Sjm199354 #include <sys/cred.h>
426407Sjm199354 #include <sys/list.h>
435440Sjm199354 #include <sys/vscan.h>
445440Sjm199354
456407Sjm199354 #define VS_REQ_MAGIC 0x52515354 /* 'RQST' */
466407Sjm199354
476407Sjm199354 #define VS_REQS_DEFAULT 20000 /* pending scan requests - reql */
486407Sjm199354 #define VS_NODES_DEFAULT 128 /* concurrent file scans */
496407Sjm199354 #define VS_WORKERS_DEFAULT 32 /* worker threads */
506407Sjm199354 #define VS_SCANWAIT_DEFAULT 15*60 /* seconds to wait for scan result */
516407Sjm199354 #define VS_REQL_HANDLER_TIMEOUT 30
525440Sjm199354 #define VS_EXT_RECURSE_DEPTH 8
536407Sjm199354
546407Sjm199354 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
556407Sjm199354 #define VS_ACCESS_UNDEFINED 0
566407Sjm199354 #define VS_ACCESS_ALLOW 1 /* return 0 */
576407Sjm199354 #define VS_ACCESS_DENY 2 /* return EACCES */
586407Sjm199354
595440Sjm199354 #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
606407Sjm199354 #define offsetof(s, m) (size_t)(&(((s *)0)->m))
616407Sjm199354
626407Sjm199354 /* global variables - tunable via /etc/system */
636407Sjm199354 uint32_t vs_reqs_max = VS_REQS_DEFAULT; /* max scan requests */
646407Sjm199354 uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
656407Sjm199354 uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
666407Sjm199354 uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
676407Sjm199354
685440Sjm199354
696407Sjm199354 /*
706407Sjm199354 * vscan_svc_state
716407Sjm199354 *
726407Sjm199354 * +-----------------+
736407Sjm199354 * | VS_SVC_UNCONFIG |
746407Sjm199354 * +-----------------+
756407Sjm199354 * | ^
766407Sjm199354 * | svc_init | svc_fini
776407Sjm199354 * v |
786407Sjm199354 * +-----------------+
796407Sjm199354 * | VS_SVC_IDLE |<----|
806407Sjm199354 * +-----------------+ |
816407Sjm199354 * | |
826407Sjm199354 * | svc_enable |
836407Sjm199354 * |<----------------| |
846407Sjm199354 * v | |
856407Sjm199354 * +-----------------+ | |
866407Sjm199354 * | VS_SVC_ENABLED |--| |
876407Sjm199354 * +-----------------+ |
886407Sjm199354 * | |
896407Sjm199354 * | svc_disable | handler thread exit,
906407Sjm199354 * v | all requests complete
916407Sjm199354 * +-----------------+ |
926407Sjm199354 * | VS_SVC_DISABLED |-----|
936407Sjm199354 * +-----------------+
946407Sjm199354 *
956407Sjm199354 * svc_enable may occur when we are already in the ENABLED
966407Sjm199354 * state if vscand has exited without clean shutdown and
976407Sjm199354 * then reconnected within the delayed disable time period
986407Sjm199354 * (vs_reconnect_timeout) - see vscan_drv
996407Sjm199354 */
1006407Sjm199354
1016407Sjm199354 typedef enum {
1026407Sjm199354 VS_SVC_UNCONFIG,
1036407Sjm199354 VS_SVC_IDLE,
1046407Sjm199354 VS_SVC_ENABLED, /* service enabled and registered */
1056407Sjm199354 VS_SVC_DISABLED /* service disabled and nunregistered */
1066407Sjm199354 } vscan_svc_state_t;
1076407Sjm199354 static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
1086407Sjm199354
1095440Sjm199354
1105440Sjm199354 /*
1116407Sjm199354 * vscan_svc_req_state
1126407Sjm199354 *
1136407Sjm199354 * When a scan request is received from the file system it is
1146407Sjm199354 * identified in or inserted into the vscan_svc_reql (INIT).
1156407Sjm199354 * If the request is asynchronous 0 is then returned to the caller.
1166407Sjm199354 * If the request is synchronous the req's refcnt is incremented
1176407Sjm199354 * and the caller waits for the request to complete.
1186407Sjm199354 * The refcnt is also incremented when the request is inserted
1196407Sjm199354 * in vscan_svc_nodes, and decremented on scan_complete.
1206407Sjm199354 *
1216407Sjm199354 * vscan_svc_handler processes requests from the request list,
1226407Sjm199354 * inserting them into vscan_svc_nodes and the task queue (QUEUED).
1236407Sjm199354 * When the task queue call back (vscan_svc_do_scan) is invoked
1246407Sjm199354 * the request transitions to IN_PROGRESS state. If the request
1256407Sjm199354 * is sucessfully sent to vscand (door_call) and the door response
1266407Sjm199354 * is SCANNING then the scan result will be received asynchronously.
1276407Sjm199354 * Although unusual, it is possible that the async response is
1286407Sjm199354 * received before the door call returns (hence the ASYNC_COMPLETE
1296407Sjm199354 * state).
1306407Sjm199354 * When the result has been determined / received,
1316407Sjm199354 * vscan_svc_scan_complete is invoked to transition the request to
1326407Sjm199354 * COMPLETE state, decrement refcnt and signal all waiting callers.
1336407Sjm199354 * When the last waiting caller has processed the result (refcnt == 0)
1346407Sjm199354 * the request is removed from vscan_svc_reql and vscan_svc_nodes
1356407Sjm199354 * and deleted.
1366407Sjm199354 *
1376407Sjm199354 * | ^
1386407Sjm199354 * | reql_insert | refcnt == 0
1396407Sjm199354 * v | (delete)
1406407Sjm199354 * +------------------------+ +---------------------+
1416407Sjm199354 * | VS_SVC_REQ_INIT | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
1426407Sjm199354 * +------------------------+ +---------------------+
1436407Sjm199354 * | ^
1446407Sjm199354 * | insert_req, tq_dispatch |
1456407Sjm199354 * v |
1466407Sjm199354 * +------------------------+ |
1476407Sjm199354 * | VS_SVC_REQ_QUEUED | scan_complete
1486407Sjm199354 * +------------------------+ |
1496407Sjm199354 * | |
1506407Sjm199354 * | tq_callback (do_scan) |
1516407Sjm199354 * | |
1526407Sjm199354 * v scan not req'd, error, |
1536407Sjm199354 * +------------------------+ or door_result != SCANNING |
1546407Sjm199354 * | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
1556407Sjm199354 * +------------------------+ |
1566407Sjm199354 * | | |
1576407Sjm199354 * | | door_result == SCANNING |
1586407Sjm199354 * | v |
1596407Sjm199354 * | +---------------------------+ async result |
1606407Sjm199354 * | | VS_SVC_REQ_SCANNING |-------->---------|
1616407Sjm199354 * | +---------------------------+ |
1626407Sjm199354 * | |
1636407Sjm199354 * | async result |
1646407Sjm199354 * v |
1656407Sjm199354 * +---------------------------+ door_result = SCANNING |
1666407Sjm199354 * | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
1676407Sjm199354 * +---------------------------+
1686407Sjm199354 */
1696407Sjm199354 typedef enum {
1706407Sjm199354 VS_SVC_REQ_INIT,
1716407Sjm199354 VS_SVC_REQ_QUEUED,
1726407Sjm199354 VS_SVC_REQ_IN_PROGRESS,
1736407Sjm199354 VS_SVC_REQ_SCANNING,
1746407Sjm199354 VS_SVC_REQ_ASYNC_COMPLETE,
1756407Sjm199354 VS_SVC_REQ_COMPLETE
1766407Sjm199354 } vscan_svc_req_state_t;
1776407Sjm199354
1786407Sjm199354
1796407Sjm199354 /*
1806407Sjm199354 * vscan_svc_reql - the list of pending and in-progress scan requests
1816407Sjm199354 */
1826407Sjm199354 typedef struct vscan_req {
1836407Sjm199354 uint32_t vsr_magic; /* VS_REQ_MAGIC */
1846407Sjm199354 list_node_t vsr_lnode;
1856407Sjm199354 vnode_t *vsr_vp;
1866407Sjm199354 uint32_t vsr_idx; /* vscan_svc_nodes index */
1876407Sjm199354 uint32_t vsr_seqnum; /* unigue request id */
1886407Sjm199354 uint32_t vsr_refcnt;
1896407Sjm199354 kcondvar_t vsr_cv;
1906407Sjm199354 vscan_svc_req_state_t vsr_state;
1916407Sjm199354 } vscan_req_t;
1926407Sjm199354
1936407Sjm199354 static list_t vscan_svc_reql;
1946407Sjm199354
1956407Sjm199354
1966407Sjm199354 /*
1976407Sjm199354 * vscan_svc_nodes - table of files being scanned
1985440Sjm199354 *
1995440Sjm199354 * The index into this table is passed in the door call to
2005440Sjm199354 * vscand. vscand uses the idx to determine which minor node
2015440Sjm199354 * to open to read the file data. Within the kernel driver
2025440Sjm199354 * the minor device number can thus be used to identify the
2035440Sjm199354 * table index to get the appropriate vnode.
2045440Sjm199354 *
2055440Sjm199354 * Instance 0 is reserved for the daemon/driver control
2065440Sjm199354 * interface: enable/configure/disable
2075440Sjm199354 */
2086407Sjm199354 typedef struct vscan_svc_node {
2096407Sjm199354 vscan_req_t *vsn_req;
2106407Sjm199354 uint8_t vsn_quarantined;
2116407Sjm199354 uint8_t vsn_modified;
2126407Sjm199354 uint64_t vsn_size;
2136407Sjm199354 timestruc_t vsn_mtime;
2146407Sjm199354 vs_scanstamp_t vsn_scanstamp;
2156407Sjm199354 uint32_t vsn_result;
2166407Sjm199354 uint32_t vsn_access;
2176407Sjm199354 } vscan_svc_node_t;
2185440Sjm199354
2196407Sjm199354 static vscan_svc_node_t *vscan_svc_nodes;
2206407Sjm199354 static int vscan_svc_nodes_sz;
2216407Sjm199354
2226407Sjm199354
2236407Sjm199354 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
2246407Sjm199354 static taskq_t *vscan_svc_taskq = NULL;
2255440Sjm199354
2266407Sjm199354 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
2276407Sjm199354 typedef struct {
2286407Sjm199354 uint32_t vsc_reql;
2296407Sjm199354 uint32_t vsc_node;
2306407Sjm199354 uint32_t vsc_tq;
2316407Sjm199354 } vscan_svc_counts_t;
2326407Sjm199354 static vscan_svc_counts_t vscan_svc_counts;
2335440Sjm199354
2345440Sjm199354 /*
2355440Sjm199354 * vscan_svc_mutex protects the data pertaining to scan requests:
2366407Sjm199354 * request list - vscan_svc_reql
2376407Sjm199354 * node table - vscan_svc_nodes
2385440Sjm199354 */
2395440Sjm199354 static kmutex_t vscan_svc_mutex;
2405440Sjm199354
2416407Sjm199354 /* unique request id for vscand request/response correlation */
2426407Sjm199354 static uint32_t vscan_svc_seqnum = 0;
2436407Sjm199354
2445440Sjm199354 /*
2455440Sjm199354 * vscan_svc_cfg_mutex protects the configuration data:
2465440Sjm199354 * vscan_svc_config, vscan_svc_types
2475440Sjm199354 */
2485440Sjm199354 static kmutex_t vscan_svc_cfg_mutex;
2495440Sjm199354
2505440Sjm199354 /* configuration data - for virus scan exemption */
2515440Sjm199354 static vs_config_t vscan_svc_config;
2525440Sjm199354 static char *vscan_svc_types[VS_TYPES_MAX];
2535440Sjm199354
2546407Sjm199354 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
2556407Sjm199354 static kthread_t *vscan_svc_reql_thread;
2566407Sjm199354 static kcondvar_t vscan_svc_reql_cv;
2576407Sjm199354 static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
2586407Sjm199354
2595440Sjm199354 /* local functions */
2605440Sjm199354 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
2616407Sjm199354 static void vscan_svc_taskq_callback(void *);
2625440Sjm199354 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
2635440Sjm199354 static int vscan_svc_exempt_filetype(char *);
2645440Sjm199354 static int vscan_svc_match_ext(char *, char *, int);
2656407Sjm199354 static void vscan_svc_do_scan(vscan_req_t *);
2666407Sjm199354 static vs_scan_req_t *vscan_svc_populate_req(int);
2675931Sjm199354 static void vscan_svc_process_scan_result(int);
2686407Sjm199354 static void vscan_svc_scan_complete(vscan_req_t *);
2696407Sjm199354 static void vscan_svc_delete_req(vscan_req_t *);
2706407Sjm199354 static int vscan_svc_insert_req(vscan_req_t *);
2716407Sjm199354 static void vscan_svc_remove_req(int);
2726407Sjm199354 static vscan_req_t *vscan_svc_reql_find(vnode_t *);
2736407Sjm199354 static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
2746407Sjm199354 static void vscan_svc_reql_remove(vscan_req_t *);
2756407Sjm199354
2765440Sjm199354 static int vscan_svc_getattr(int);
2775931Sjm199354 static int vscan_svc_setattr(int, int);
2785440Sjm199354
2796407Sjm199354 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
2806407Sjm199354 static void vscan_svc_reql_handler(void);
2815440Sjm199354
2825440Sjm199354
2835440Sjm199354 /*
2845440Sjm199354 * vscan_svc_init
2855440Sjm199354 */
2865440Sjm199354 int
vscan_svc_init()2875440Sjm199354 vscan_svc_init()
2885440Sjm199354 {
2896407Sjm199354 if (vscan_svc_state != VS_SVC_UNCONFIG) {
2906407Sjm199354 DTRACE_PROBE1(vscan__svc__state__violation,
2916407Sjm199354 int, vscan_svc_state);
2926407Sjm199354 return (-1);
2936407Sjm199354 }
2946407Sjm199354
2956407Sjm199354 mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
2966407Sjm199354 mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
2976407Sjm199354 cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
2986407Sjm199354
2996407Sjm199354 vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
3006407Sjm199354 vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
3016407Sjm199354
3026407Sjm199354 vscan_svc_counts.vsc_reql = 0;
3036407Sjm199354 vscan_svc_counts.vsc_node = 0;
3046407Sjm199354 vscan_svc_counts.vsc_tq = 0;
3056407Sjm199354
3066407Sjm199354 vscan_svc_state = VS_SVC_IDLE;
3075440Sjm199354
3085440Sjm199354 return (0);
3095440Sjm199354 }
3105440Sjm199354
3116407Sjm199354
3125440Sjm199354 /*
3135440Sjm199354 * vscan_svc_fini
3145440Sjm199354 */
3155440Sjm199354 void
vscan_svc_fini()3165440Sjm199354 vscan_svc_fini()
3175440Sjm199354 {
3186407Sjm199354 if (vscan_svc_state != VS_SVC_IDLE) {
3196407Sjm199354 DTRACE_PROBE1(vscan__svc__state__violation,
3206407Sjm199354 int, vscan_svc_state);
3216407Sjm199354 return;
3226407Sjm199354 }
3235440Sjm199354
3246407Sjm199354 kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
3256407Sjm199354
3266407Sjm199354 cv_destroy(&vscan_svc_reql_cv);
3275440Sjm199354 mutex_destroy(&vscan_svc_mutex);
3285440Sjm199354 mutex_destroy(&vscan_svc_cfg_mutex);
3296407Sjm199354 vscan_svc_state = VS_SVC_UNCONFIG;
3305440Sjm199354 }
3315440Sjm199354
3326407Sjm199354
3335440Sjm199354 /*
3345440Sjm199354 * vscan_svc_enable
3355440Sjm199354 */
3366407Sjm199354 int
vscan_svc_enable(void)3375931Sjm199354 vscan_svc_enable(void)
3385440Sjm199354 {
3395931Sjm199354 mutex_enter(&vscan_svc_mutex);
3406407Sjm199354
3416407Sjm199354 switch (vscan_svc_state) {
3426407Sjm199354 case VS_SVC_ENABLED:
3436407Sjm199354 /*
3446407Sjm199354 * it's possible (and okay) for vscan_svc_enable to be
3456407Sjm199354 * called when already enabled if vscand reconnects
3466407Sjm199354 * during a delayed disable
3476407Sjm199354 */
3486407Sjm199354 break;
3496407Sjm199354 case VS_SVC_IDLE:
3506407Sjm199354 list_create(&vscan_svc_reql, sizeof (vscan_req_t),
3516407Sjm199354 offsetof(vscan_req_t, vsr_lnode));
3526407Sjm199354 vscan_svc_reql_next = list_head(&vscan_svc_reql);
3535931Sjm199354
3546407Sjm199354 vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
3556407Sjm199354 MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
3566407Sjm199354 ASSERT(vscan_svc_taskq != NULL);
3576407Sjm199354
3586407Sjm199354 vscan_svc_reql_thread = thread_create(NULL, 0,
3596407Sjm199354 vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
3606407Sjm199354 ASSERT(vscan_svc_reql_thread != NULL);
3616407Sjm199354
3626407Sjm199354 /* ready to start processing requests */
3636407Sjm199354 vscan_svc_state = VS_SVC_ENABLED;
3646407Sjm199354 fs_vscan_register(vscan_svc_scan_file);
3656407Sjm199354 break;
3666407Sjm199354 default:
3676407Sjm199354 DTRACE_PROBE1(vscan__svc__state__violation,
3686407Sjm199354 int, vscan_svc_state);
3696407Sjm199354 return (-1);
3705931Sjm199354 }
3715931Sjm199354
3725931Sjm199354 mutex_exit(&vscan_svc_mutex);
3736407Sjm199354 return (0);
3745931Sjm199354 }
3755931Sjm199354
3765440Sjm199354
3775931Sjm199354 /*
3785931Sjm199354 * vscan_svc_disable
3796407Sjm199354 *
3806407Sjm199354 * Resources allocated during vscan_svc_enable are free'd by
3816407Sjm199354 * the handler thread immediately prior to exiting
3825931Sjm199354 */
3835931Sjm199354 void
vscan_svc_disable(void)3845931Sjm199354 vscan_svc_disable(void)
3855931Sjm199354 {
3865931Sjm199354 mutex_enter(&vscan_svc_mutex);
3875440Sjm199354
3886407Sjm199354 switch (vscan_svc_state) {
3896407Sjm199354 case VS_SVC_ENABLED:
3906407Sjm199354 fs_vscan_register(NULL);
3916407Sjm199354 vscan_svc_state = VS_SVC_DISABLED;
3926407Sjm199354 cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
3936407Sjm199354 break;
3946407Sjm199354 default:
3956407Sjm199354 DTRACE_PROBE1(vscan__svc__state__violation, int,
3966407Sjm199354 vscan_svc_state);
3976407Sjm199354 }
3985931Sjm199354
3996407Sjm199354 mutex_exit(&vscan_svc_mutex);
4005931Sjm199354 }
4015931Sjm199354
4025931Sjm199354
4035440Sjm199354 /*
4045440Sjm199354 * vscan_svc_in_use
4055440Sjm199354 */
4065440Sjm199354 boolean_t
vscan_svc_in_use()4075440Sjm199354 vscan_svc_in_use()
4085440Sjm199354 {
4096407Sjm199354 boolean_t in_use;
4105440Sjm199354
4115440Sjm199354 mutex_enter(&vscan_svc_mutex);
4125440Sjm199354
4136407Sjm199354 switch (vscan_svc_state) {
4146407Sjm199354 case VS_SVC_IDLE:
4156407Sjm199354 case VS_SVC_UNCONFIG:
4166407Sjm199354 in_use = B_FALSE;
4176407Sjm199354 break;
4186407Sjm199354 default:
4196407Sjm199354 in_use = B_TRUE;
4206407Sjm199354 break;
4216407Sjm199354 }
4226407Sjm199354
4236407Sjm199354 mutex_exit(&vscan_svc_mutex);
4246407Sjm199354 return (in_use);
4255440Sjm199354 }
4265440Sjm199354
4276407Sjm199354
4285440Sjm199354 /*
4295440Sjm199354 * vscan_svc_get_vnode
4305440Sjm199354 *
4315440Sjm199354 * Get the file vnode indexed by idx.
4325440Sjm199354 */
4335440Sjm199354 vnode_t *
vscan_svc_get_vnode(int idx)4345440Sjm199354 vscan_svc_get_vnode(int idx)
4355440Sjm199354 {
4366407Sjm199354 vnode_t *vp = NULL;
4376407Sjm199354
4385440Sjm199354 ASSERT(idx > 0);
4396407Sjm199354 ASSERT(idx <= vs_nodes_max);
4405440Sjm199354
4416407Sjm199354 mutex_enter(&vscan_svc_mutex);
4426407Sjm199354 if (vscan_svc_nodes[idx].vsn_req)
4436407Sjm199354 vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
4446407Sjm199354 mutex_exit(&vscan_svc_mutex);
4456407Sjm199354
4466407Sjm199354 return (vp);
4475440Sjm199354 }
4485440Sjm199354
4495440Sjm199354
4505440Sjm199354 /*
4515440Sjm199354 * vscan_svc_scan_file
4525440Sjm199354 *
4535440Sjm199354 * This function is the entry point for the file system to
4545440Sjm199354 * request that a file be virus scanned.
4555440Sjm199354 */
4565440Sjm199354 int
vscan_svc_scan_file(vnode_t * vp,cred_t * cr,int async)4575440Sjm199354 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
4585440Sjm199354 {
4596407Sjm199354 int access;
4606407Sjm199354 vscan_req_t *req;
4615440Sjm199354 boolean_t allow;
4626407Sjm199354 clock_t timeout, time_left;
4635440Sjm199354
4646407Sjm199354 if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
4655440Sjm199354 return (0);
4665440Sjm199354
4675440Sjm199354 DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
4685440Sjm199354
4695440Sjm199354 /* check if size or type exempts file from scanning */
4705440Sjm199354 if (vscan_svc_exempt_file(vp, &allow)) {
4715440Sjm199354 if ((allow == B_TRUE) || (async != 0))
4725440Sjm199354 return (0);
4735440Sjm199354
4745440Sjm199354 return (EACCES);
4755440Sjm199354 }
4765440Sjm199354
4776407Sjm199354 mutex_enter(&vscan_svc_mutex);
4786407Sjm199354
4796407Sjm199354 if (vscan_svc_state != VS_SVC_ENABLED) {
4806407Sjm199354 DTRACE_PROBE1(vscan__svc__state__violation,
4816407Sjm199354 int, vscan_svc_state);
4826407Sjm199354 mutex_exit(&vscan_svc_mutex);
4836407Sjm199354 return (0);
4846407Sjm199354 }
4855440Sjm199354
4866407Sjm199354 /* insert (or find) request in list */
4876407Sjm199354 if ((req = vscan_svc_reql_insert(vp)) == NULL) {
4886407Sjm199354 mutex_exit(&vscan_svc_mutex);
4896407Sjm199354 cmn_err(CE_WARN, "Virus scan request list full");
4906407Sjm199354 return ((async != 0) ? 0 : EACCES);
4916407Sjm199354 }
4925440Sjm199354
4936407Sjm199354 /* asynchronous request: return 0 */
4945440Sjm199354 if (async) {
4956407Sjm199354 mutex_exit(&vscan_svc_mutex);
4966407Sjm199354 return (0);
4975440Sjm199354 }
4985440Sjm199354
4996407Sjm199354 /* synchronous scan request: wait for result */
5006407Sjm199354 ++(req->vsr_refcnt);
5016407Sjm199354 time_left = SEC_TO_TICK(vs_scan_wait);
5026407Sjm199354 while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
503*11066Srafael.vanoni@sun.com timeout = time_left;
504*11066Srafael.vanoni@sun.com time_left = cv_reltimedwait_sig(&(req->vsr_cv),
505*11066Srafael.vanoni@sun.com &vscan_svc_mutex, timeout, TR_CLOCK_TICK);
5066407Sjm199354 }
5076407Sjm199354
5086407Sjm199354 if (time_left == -1) {
5096407Sjm199354 cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
5106407Sjm199354 vp->v_path, req->vsr_seqnum);
5116407Sjm199354 DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
5126407Sjm199354 }
5136407Sjm199354
5146407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
5156407Sjm199354 if (vscan_svc_state == VS_SVC_DISABLED)
5166407Sjm199354 access = VS_ACCESS_ALLOW;
5176407Sjm199354 else if (req->vsr_idx == 0)
5186407Sjm199354 access = VS_ACCESS_DENY;
5196407Sjm199354 else
5206407Sjm199354 access = vscan_svc_nodes[req->vsr_idx].vsn_access;
5216407Sjm199354
5226407Sjm199354 if ((--req->vsr_refcnt) == 0)
5236407Sjm199354 vscan_svc_delete_req(req);
5246407Sjm199354
5255440Sjm199354 mutex_exit(&vscan_svc_mutex);
5266407Sjm199354 return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
5275440Sjm199354 }
5285440Sjm199354
5295440Sjm199354
5305440Sjm199354 /*
5316407Sjm199354 * vscan_svc_reql_handler
5325440Sjm199354 *
5336407Sjm199354 * inserts scan requests (from vscan_svc_reql) into
5346407Sjm199354 * vscan_svc_nodes and vscan_svc_taskq
5355440Sjm199354 */
5366407Sjm199354 static void
vscan_svc_reql_handler(void)5376407Sjm199354 vscan_svc_reql_handler(void)
5386407Sjm199354 {
5396407Sjm199354 vscan_req_t *req, *next;
5406407Sjm199354
5416407Sjm199354 for (;;) {
5426407Sjm199354 mutex_enter(&vscan_svc_mutex);
5436407Sjm199354
5446407Sjm199354 if ((vscan_svc_state == VS_SVC_DISABLED) &&
5456407Sjm199354 (vscan_svc_counts.vsc_reql == 0)) {
5466407Sjm199354 /* free resources allocated durining enable */
5476407Sjm199354 taskq_destroy(vscan_svc_taskq);
5486407Sjm199354 vscan_svc_taskq = NULL;
5496407Sjm199354 list_destroy(&vscan_svc_reql);
5506407Sjm199354 vscan_svc_state = VS_SVC_IDLE;
5516407Sjm199354 mutex_exit(&vscan_svc_mutex);
5526407Sjm199354 return;
5536407Sjm199354 }
5546407Sjm199354
5556407Sjm199354 /*
5566407Sjm199354 * If disabled, scan_complete any pending requests.
5576407Sjm199354 * Otherwise insert pending requests into vscan_svc_nodes
5586407Sjm199354 * and vscan_svc_taskq. If no slots are available in
5596407Sjm199354 * vscan_svc_nodes break loop and wait for one
5606407Sjm199354 */
5616407Sjm199354 req = vscan_svc_reql_next;
5626407Sjm199354
5636407Sjm199354 while (req != NULL) {
5646407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
5656407Sjm199354 next = list_next(&vscan_svc_reql, req);
5666407Sjm199354
5676407Sjm199354 if (vscan_svc_state == VS_SVC_DISABLED) {
5686407Sjm199354 vscan_svc_scan_complete(req);
5696407Sjm199354 } else {
5706407Sjm199354 /* insert request into vscan_svc_nodes */
5716407Sjm199354 if (vscan_svc_insert_req(req) == -1)
5726407Sjm199354 break;
5736407Sjm199354
5746407Sjm199354 /* add the scan request into the taskq */
5756407Sjm199354 (void) taskq_dispatch(vscan_svc_taskq,
5766407Sjm199354 vscan_svc_taskq_callback,
5776407Sjm199354 (void *)req, TQ_SLEEP);
5786407Sjm199354 ++(vscan_svc_counts.vsc_tq);
5796407Sjm199354
5806407Sjm199354 req->vsr_state = VS_SVC_REQ_QUEUED;
5816407Sjm199354 }
5826407Sjm199354 req = next;
5836407Sjm199354 }
5846407Sjm199354
5856407Sjm199354 vscan_svc_reql_next = req;
5866407Sjm199354
5876407Sjm199354 DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
5886407Sjm199354 vscan_svc_counts_t *, &vscan_svc_counts);
5896407Sjm199354
590*11066Srafael.vanoni@sun.com (void) cv_reltimedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
591*11066Srafael.vanoni@sun.com SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT), TR_CLOCK_TICK);
5926407Sjm199354
5936407Sjm199354 DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
5946407Sjm199354 vscan_svc_counts_t *, &vscan_svc_counts);
5956407Sjm199354
5966407Sjm199354 mutex_exit(&vscan_svc_mutex);
5976407Sjm199354 }
5986407Sjm199354 }
5996407Sjm199354
6006407Sjm199354
6016407Sjm199354 static void
vscan_svc_taskq_callback(void * data)6025440Sjm199354 vscan_svc_taskq_callback(void *data)
6035440Sjm199354 {
6046407Sjm199354 vscan_req_t *req;
6055440Sjm199354
6065440Sjm199354 mutex_enter(&vscan_svc_mutex);
6076407Sjm199354
6086407Sjm199354 req = (vscan_req_t *)data;
6096407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
6106407Sjm199354 vscan_svc_do_scan(req);
6116407Sjm199354 if (req->vsr_state != VS_SVC_REQ_SCANNING)
6126407Sjm199354 vscan_svc_scan_complete(req);
6136407Sjm199354
6146407Sjm199354 --(vscan_svc_counts.vsc_tq);
6155440Sjm199354 mutex_exit(&vscan_svc_mutex);
6165440Sjm199354 }
6175440Sjm199354
6185440Sjm199354
6195440Sjm199354 /*
6205440Sjm199354 * vscan_svc_do_scan
6215440Sjm199354 *
6226407Sjm199354 * Note: To avoid potential deadlock it is important that
6236407Sjm199354 * vscan_svc_mutex is not held during the call to
6246407Sjm199354 * vscan_drv_create_note. vscan_drv_create_note enters
6256407Sjm199354 * the vscan_drv_mutex and it is possible that a thread
6266407Sjm199354 * holding that mutex could be waiting for vscan_svc_mutex.
6276407Sjm199354 */
6286407Sjm199354 static void
vscan_svc_do_scan(vscan_req_t * req)6296407Sjm199354 vscan_svc_do_scan(vscan_req_t *req)
6306407Sjm199354 {
6316407Sjm199354 int idx, result;
6326407Sjm199354 vscan_svc_node_t *node;
6336407Sjm199354 vs_scan_req_t *door_req;
6346407Sjm199354
6356407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
6366407Sjm199354
6376407Sjm199354 idx = req->vsr_idx;
6386407Sjm199354 node = &vscan_svc_nodes[idx];
6396407Sjm199354
6406407Sjm199354 req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
6416407Sjm199354
6426407Sjm199354 /* if vscan not enabled (shutting down), allow ACCESS */
6436407Sjm199354 if (vscan_svc_state != VS_SVC_ENABLED) {
6446407Sjm199354 node->vsn_access = VS_ACCESS_ALLOW;
6456407Sjm199354 return;
6466407Sjm199354 }
6476407Sjm199354
6486407Sjm199354 if (vscan_svc_getattr(idx) != 0) {
6496407Sjm199354 cmn_err(CE_WARN, "Can't access xattr for %s\n",
6506407Sjm199354 req->vsr_vp->v_path);
6516407Sjm199354 node->vsn_access = VS_ACCESS_DENY;
6526407Sjm199354 return;
6536407Sjm199354 }
6546407Sjm199354
6556407Sjm199354 /* valid scan_req ptr guaranteed */
6566407Sjm199354 door_req = vscan_svc_populate_req(idx);
6576407Sjm199354
6586407Sjm199354 /* free up mutex around create node and door call */
6596407Sjm199354 mutex_exit(&vscan_svc_mutex);
6606407Sjm199354 if (vscan_drv_create_node(idx) != B_TRUE)
6616407Sjm199354 result = VS_STATUS_ERROR;
6626407Sjm199354 else
6636407Sjm199354 result = vscan_door_scan_file(door_req);
6646407Sjm199354 kmem_free(door_req, sizeof (vs_scan_req_t));
6656407Sjm199354 mutex_enter(&vscan_svc_mutex);
6666407Sjm199354
6676407Sjm199354 if (result != VS_STATUS_SCANNING) {
6686407Sjm199354 vscan_svc_nodes[idx].vsn_result = result;
6696407Sjm199354 vscan_svc_process_scan_result(idx);
6706407Sjm199354 } else { /* async response */
6716407Sjm199354 if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
6726407Sjm199354 req->vsr_state = VS_SVC_REQ_SCANNING;
6736407Sjm199354 }
6746407Sjm199354 }
6756407Sjm199354
6766407Sjm199354
6776407Sjm199354 /*
6786407Sjm199354 * vscan_svc_populate_req
6796407Sjm199354 *
6806407Sjm199354 * Allocate a scan request to be sent to vscand, populating it
6816407Sjm199354 * from the data in vscan_svc_nodes[idx].
6826407Sjm199354 *
6836407Sjm199354 * Returns: scan request object
6845440Sjm199354 */
6856407Sjm199354 static vs_scan_req_t *
vscan_svc_populate_req(int idx)6866407Sjm199354 vscan_svc_populate_req(int idx)
6876407Sjm199354 {
6886407Sjm199354 vs_scan_req_t *scan_req;
6896407Sjm199354 vscan_req_t *req;
6906407Sjm199354 vscan_svc_node_t *node;
6916407Sjm199354
6926407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
6936407Sjm199354
6946407Sjm199354 node = &vscan_svc_nodes[idx];
6956407Sjm199354 req = node->vsn_req;
6966407Sjm199354 scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
6976407Sjm199354
6986407Sjm199354 scan_req->vsr_idx = idx;
6996407Sjm199354 scan_req->vsr_seqnum = req->vsr_seqnum;
7006407Sjm199354 (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
7016407Sjm199354 scan_req->vsr_size = node->vsn_size;
7026407Sjm199354 scan_req->vsr_modified = node->vsn_modified;
7036407Sjm199354 scan_req->vsr_quarantined = node->vsn_quarantined;
7046407Sjm199354 scan_req->vsr_flags = 0;
7056407Sjm199354 (void) strncpy(scan_req->vsr_scanstamp,
7066407Sjm199354 node->vsn_scanstamp, sizeof (vs_scanstamp_t));
7076407Sjm199354
7086407Sjm199354 return (scan_req);
7096407Sjm199354 }
7106407Sjm199354
7116407Sjm199354
7126407Sjm199354 /*
7136407Sjm199354 * vscan_svc_scan_complete
7146407Sjm199354 */
7156407Sjm199354 static void
vscan_svc_scan_complete(vscan_req_t * req)7166407Sjm199354 vscan_svc_scan_complete(vscan_req_t *req)
7175440Sjm199354 {
7186407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
7196407Sjm199354 ASSERT(req != NULL);
7206407Sjm199354
7216407Sjm199354 req->vsr_state = VS_SVC_REQ_COMPLETE;
7226407Sjm199354
7236407Sjm199354 if ((--req->vsr_refcnt) == 0)
7246407Sjm199354 vscan_svc_delete_req(req);
7256407Sjm199354 else
7266407Sjm199354 cv_broadcast(&(req->vsr_cv));
7276407Sjm199354 }
7286407Sjm199354
7296407Sjm199354
7306407Sjm199354 /*
7316407Sjm199354 * vscan_svc_delete_req
7326407Sjm199354 */
7336407Sjm199354 static void
vscan_svc_delete_req(vscan_req_t * req)7346407Sjm199354 vscan_svc_delete_req(vscan_req_t *req)
7356407Sjm199354 {
7366407Sjm199354 int idx;
7376407Sjm199354
7386407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
7396407Sjm199354 ASSERT(req != NULL);
7406407Sjm199354 ASSERT(req->vsr_refcnt == 0);
7416407Sjm199354 ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
7426407Sjm199354
7436407Sjm199354 if ((idx = req->vsr_idx) != 0)
7446407Sjm199354 vscan_svc_remove_req(idx);
7456407Sjm199354
7466407Sjm199354 vscan_svc_reql_remove(req);
7476407Sjm199354
7486407Sjm199354 cv_signal(&vscan_svc_reql_cv);
7496407Sjm199354 }
7506407Sjm199354
7516407Sjm199354
7526407Sjm199354 /*
7536407Sjm199354 * vscan_svc_scan_result
7546407Sjm199354 *
7556407Sjm199354 * Invoked from vscan_drv.c on receipt of an ioctl containing
7566407Sjm199354 * an async scan result (VS_DRV_IOCTL_RESULT)
7576407Sjm199354 * If the vsr_seqnum in the response does not match that in the
7586407Sjm199354 * vscan_svc_nodes entry the result is discarded.
7596407Sjm199354 */
7606407Sjm199354 void
vscan_svc_scan_result(vs_scan_rsp_t * scan_rsp)7616407Sjm199354 vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
7626407Sjm199354 {
7636407Sjm199354 vscan_req_t *req;
7646407Sjm199354 vscan_svc_node_t *node;
7655440Sjm199354
7665440Sjm199354 mutex_enter(&vscan_svc_mutex);
7675440Sjm199354
7686407Sjm199354 node = &vscan_svc_nodes[scan_rsp->vsr_idx];
7696407Sjm199354
7706407Sjm199354 if ((req = node->vsn_req) == NULL) {
7716407Sjm199354 mutex_exit(&vscan_svc_mutex);
7726407Sjm199354 return;
7736407Sjm199354 }
7746407Sjm199354
7756407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
7766407Sjm199354
7776407Sjm199354 if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
7786407Sjm199354 mutex_exit(&vscan_svc_mutex);
7796407Sjm199354 return;
7806407Sjm199354 }
7816407Sjm199354
7826407Sjm199354 node->vsn_result = scan_rsp->vsr_result;
7836407Sjm199354 (void) strncpy(node->vsn_scanstamp,
7846407Sjm199354 scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
7856407Sjm199354
7866407Sjm199354 vscan_svc_process_scan_result(scan_rsp->vsr_idx);
7876407Sjm199354
7886407Sjm199354 if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
7896407Sjm199354 vscan_svc_scan_complete(node->vsn_req);
7906407Sjm199354 else
7916407Sjm199354 node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
7925440Sjm199354
7936407Sjm199354 mutex_exit(&vscan_svc_mutex);
7946407Sjm199354 }
7956407Sjm199354
7965931Sjm199354
7976407Sjm199354 /*
7986407Sjm199354 * vscan_svc_scan_abort
7996407Sjm199354 *
8006407Sjm199354 * Abort in-progress scan requests.
8016407Sjm199354 */
8026407Sjm199354 void
vscan_svc_scan_abort()8036407Sjm199354 vscan_svc_scan_abort()
8046407Sjm199354 {
8056407Sjm199354 int idx;
8066407Sjm199354 vscan_req_t *req;
8076407Sjm199354
8086407Sjm199354 mutex_enter(&vscan_svc_mutex);
8096407Sjm199354
8106407Sjm199354 for (idx = 1; idx <= vs_nodes_max; idx++) {
8116407Sjm199354 if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
8126407Sjm199354 continue;
8136407Sjm199354
8146407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
8156407Sjm199354
8166407Sjm199354 if (req->vsr_state == VS_SVC_REQ_SCANNING) {
8176407Sjm199354 DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
8186407Sjm199354 vscan_svc_process_scan_result(idx);
8196407Sjm199354 vscan_svc_scan_complete(req);
8205440Sjm199354 }
8215440Sjm199354 }
8225440Sjm199354
8235440Sjm199354 mutex_exit(&vscan_svc_mutex);
8245440Sjm199354 }
8255440Sjm199354
8265931Sjm199354
8275931Sjm199354 /*
8285931Sjm199354 * vscan_svc_process_scan_result
8295931Sjm199354 *
8306407Sjm199354 * Sets vsn_access and updates file attributes based on vsn_result,
8315931Sjm199354 * as follows:
8325931Sjm199354 *
8335931Sjm199354 * VS_STATUS_INFECTED
8345931Sjm199354 * deny access, set quarantine attribute, clear scanstamp
8355931Sjm199354 * VS_STATUS_CLEAN
8365931Sjm199354 * allow access, set scanstamp,
8375931Sjm199354 * if file not modified since scan initiated, clear modified attribute
8385931Sjm199354 * VS_STATUS_NO_SCAN
8395931Sjm199354 * deny access if file quarantined, otherwise allow access
8405931Sjm199354 * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
8415931Sjm199354 * deny access if file quarantined, modified or no scanstamp
8425931Sjm199354 * otherwise, allow access
8435931Sjm199354 */
8445931Sjm199354 static void
vscan_svc_process_scan_result(int idx)8455931Sjm199354 vscan_svc_process_scan_result(int idx)
8465931Sjm199354 {
8475931Sjm199354 struct vattr attr;
8485931Sjm199354 vnode_t *vp;
8495931Sjm199354 timestruc_t *mtime;
8506407Sjm199354 vscan_svc_node_t *node;
8515931Sjm199354
8525931Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
8535931Sjm199354
8546407Sjm199354 node = &vscan_svc_nodes[idx];
8555931Sjm199354
8566407Sjm199354 switch (node->vsn_result) {
8575931Sjm199354 case VS_STATUS_INFECTED:
8586407Sjm199354 node->vsn_access = VS_ACCESS_DENY;
8596407Sjm199354 node->vsn_quarantined = 1;
8606407Sjm199354 node->vsn_scanstamp[0] = '\0';
8615931Sjm199354 (void) vscan_svc_setattr(idx,
8625931Sjm199354 XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
8636407Sjm199354 break;
8645931Sjm199354
8655931Sjm199354 case VS_STATUS_CLEAN:
8666407Sjm199354 node->vsn_access = VS_ACCESS_ALLOW;
8675931Sjm199354
8685931Sjm199354 /* if mtime has changed, don't clear the modified attribute */
8696407Sjm199354 vp = node->vsn_req->vsr_vp;
8706407Sjm199354 mtime = &(node->vsn_mtime);
8715931Sjm199354 attr.va_mask = AT_MTIME;
8725931Sjm199354 if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
8735931Sjm199354 (mtime->tv_sec != attr.va_mtime.tv_sec) ||
8745931Sjm199354 (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
8756407Sjm199354 DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
8766407Sjm199354 node);
8775931Sjm199354 (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
8786407Sjm199354 break;
8795931Sjm199354 }
8805931Sjm199354
8816407Sjm199354 node->vsn_modified = 0;
8825931Sjm199354 (void) vscan_svc_setattr(idx,
8835931Sjm199354 XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
8846407Sjm199354 break;
8855931Sjm199354
8865931Sjm199354 case VS_STATUS_NO_SCAN:
8876407Sjm199354 if (node->vsn_quarantined)
8886407Sjm199354 node->vsn_access = VS_ACCESS_DENY;
8895931Sjm199354 else
8906407Sjm199354 node->vsn_access = VS_ACCESS_ALLOW;
8916407Sjm199354 break;
8925931Sjm199354
8935931Sjm199354 case VS_STATUS_ERROR:
8945931Sjm199354 case VS_STATUS_UNDEFINED:
8955931Sjm199354 default:
8966407Sjm199354 if ((node->vsn_quarantined) ||
8976407Sjm199354 (node->vsn_modified) ||
8986407Sjm199354 (node->vsn_scanstamp[0] == '\0'))
8996407Sjm199354 node->vsn_access = VS_ACCESS_DENY;
9005931Sjm199354 else
9016407Sjm199354 node->vsn_access = VS_ACCESS_ALLOW;
9026407Sjm199354 break;
9035440Sjm199354 }
9045440Sjm199354
9056407Sjm199354 DTRACE_PROBE4(vscan__result,
9066407Sjm199354 int, idx, int, node->vsn_req->vsr_seqnum,
9076407Sjm199354 int, node->vsn_result, int, node->vsn_access);
9085440Sjm199354 }
9095440Sjm199354
9105440Sjm199354
9115440Sjm199354 /*
9125440Sjm199354 * vscan_svc_getattr
9135440Sjm199354 *
9145931Sjm199354 * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
9155440Sjm199354 */
9165440Sjm199354 static int
vscan_svc_getattr(int idx)9175440Sjm199354 vscan_svc_getattr(int idx)
9185440Sjm199354 {
9195440Sjm199354 xvattr_t xvattr;
9205440Sjm199354 xoptattr_t *xoap = NULL;
9215440Sjm199354 vnode_t *vp;
9226407Sjm199354 vscan_svc_node_t *node;
9235440Sjm199354
9245440Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
9255440Sjm199354
9266407Sjm199354 node = &vscan_svc_nodes[idx];
9276407Sjm199354 if ((vp = node->vsn_req->vsr_vp) == NULL)
9285440Sjm199354 return (-1);
9295440Sjm199354
9305440Sjm199354 /* get the attributes */
9315440Sjm199354 xva_init(&xvattr); /* sets AT_XVATTR */
9325440Sjm199354
9335440Sjm199354 xvattr.xva_vattr.va_mask |= AT_SIZE;
9345931Sjm199354 xvattr.xva_vattr.va_mask |= AT_MTIME;
9355440Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
9365440Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
9375440Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
9385440Sjm199354
9395440Sjm199354 if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
9405440Sjm199354 return (-1);
9415440Sjm199354
9425440Sjm199354 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
9435440Sjm199354 cmn_err(CE_NOTE, "Virus scan request failed; "
9445440Sjm199354 "file system does not support virus scanning");
9455440Sjm199354 return (-1);
9465440Sjm199354 }
9475440Sjm199354
9486407Sjm199354 node->vsn_size = xvattr.xva_vattr.va_size;
9496407Sjm199354 node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
9506407Sjm199354 node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
9515440Sjm199354
9525440Sjm199354 if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
9535440Sjm199354 return (-1);
9546407Sjm199354 node->vsn_modified = xoap->xoa_av_modified;
9555440Sjm199354
9565440Sjm199354 if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
9575440Sjm199354 return (-1);
9586407Sjm199354 node->vsn_quarantined = xoap->xoa_av_quarantined;
9595440Sjm199354
9605440Sjm199354 if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
9616407Sjm199354 (void) memcpy(node->vsn_scanstamp,
9625440Sjm199354 xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
9635440Sjm199354 }
9645440Sjm199354
9656407Sjm199354 DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
9665440Sjm199354 return (0);
9675440Sjm199354 }
9685440Sjm199354
9695440Sjm199354
9705440Sjm199354 /*
9715440Sjm199354 * vscan_svc_setattr
9725440Sjm199354 *
9735440Sjm199354 * Set the vscan related system attributes.
9745440Sjm199354 */
9755440Sjm199354 static int
vscan_svc_setattr(int idx,int which)9765931Sjm199354 vscan_svc_setattr(int idx, int which)
9775440Sjm199354 {
9785440Sjm199354 xvattr_t xvattr;
9795440Sjm199354 xoptattr_t *xoap = NULL;
9805440Sjm199354 vnode_t *vp;
9815440Sjm199354 int len;
9826407Sjm199354 vscan_svc_node_t *node;
9835440Sjm199354
9845440Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
9855440Sjm199354
9866407Sjm199354 node = &vscan_svc_nodes[idx];
9876407Sjm199354 if ((vp = node->vsn_req->vsr_vp) == NULL)
9885440Sjm199354 return (-1);
9895440Sjm199354
9905440Sjm199354 /* update the attributes */
9915440Sjm199354 xva_init(&xvattr); /* sets AT_XVATTR */
9925440Sjm199354 if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
9935440Sjm199354 return (-1);
9945440Sjm199354
9955931Sjm199354 if (which & XAT_AV_MODIFIED) {
9965931Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
9976407Sjm199354 xoap->xoa_av_modified = node->vsn_modified;
9985931Sjm199354 }
9995440Sjm199354
10005931Sjm199354 if (which & XAT_AV_QUARANTINED) {
10015931Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
10026407Sjm199354 xoap->xoa_av_quarantined = node->vsn_quarantined;
10035931Sjm199354 }
10045440Sjm199354
10055931Sjm199354 if (which & XAT_AV_SCANSTAMP) {
10065931Sjm199354 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
10076407Sjm199354 len = strlen(node->vsn_scanstamp);
10085931Sjm199354 (void) memcpy(xoap->xoa_av_scanstamp,
10096407Sjm199354 node->vsn_scanstamp, len);
10105931Sjm199354 }
10115440Sjm199354
10125440Sjm199354 /* if access is denied, set mtime to invalidate client cache */
10136407Sjm199354 if (node->vsn_access != VS_ACCESS_ALLOW) {
10145440Sjm199354 xvattr.xva_vattr.va_mask |= AT_MTIME;
10155440Sjm199354 gethrestime(&xvattr.xva_vattr.va_mtime);
10165440Sjm199354 }
10175440Sjm199354
10185440Sjm199354 if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
10195440Sjm199354 return (-1);
10205440Sjm199354
10215931Sjm199354 DTRACE_PROBE2(vscan__setattr,
10226407Sjm199354 vscan_svc_node_t *, node, int, which);
10235931Sjm199354
10245440Sjm199354 return (0);
10255440Sjm199354 }
10265440Sjm199354
10275440Sjm199354
10285440Sjm199354 /*
10295440Sjm199354 * vscan_svc_configure
10305440Sjm199354 *
10315440Sjm199354 * store configuration in vscan_svc_config
10325440Sjm199354 * set up vscan_svc_types array of pointers into
10335440Sjm199354 * vscan_svc_config.vsc_types for efficient searching
10345440Sjm199354 */
10355440Sjm199354 int
vscan_svc_configure(vs_config_t * conf)10365440Sjm199354 vscan_svc_configure(vs_config_t *conf)
10375440Sjm199354 {
10385440Sjm199354 int count = 0;
10395440Sjm199354 char *p, *beg, *end;
10405440Sjm199354
10415440Sjm199354 mutex_enter(&vscan_svc_cfg_mutex);
10425440Sjm199354
10435440Sjm199354 vscan_svc_config = *conf;
10445440Sjm199354
10455440Sjm199354 (void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
10465440Sjm199354
10475440Sjm199354 beg = vscan_svc_config.vsc_types;
10485440Sjm199354 end = beg + vscan_svc_config.vsc_types_len;
10495440Sjm199354
10505440Sjm199354 for (p = beg; p < end; p += strlen(p) + 1) {
10515440Sjm199354 if (count >= VS_TYPES_MAX) {
10525440Sjm199354 mutex_exit(&vscan_svc_mutex);
10535440Sjm199354 return (-1);
10545440Sjm199354 }
10555440Sjm199354
10565440Sjm199354 vscan_svc_types[count] = p;
10575440Sjm199354 ++count;
10585440Sjm199354 }
10595440Sjm199354
10605440Sjm199354 mutex_exit(&vscan_svc_cfg_mutex);
10615440Sjm199354 return (0);
10625440Sjm199354 }
10635440Sjm199354
10645440Sjm199354
10655440Sjm199354 /*
10665440Sjm199354 * vscan_svc_exempt_file
10675440Sjm199354 *
10685440Sjm199354 * check if a file's size or type exempts it from virus scanning
10695440Sjm199354 *
10705440Sjm199354 * If the file is exempt from virus scanning, allow will be set
10715440Sjm199354 * to define whether files access should be allowed (B_TRUE) or
10725440Sjm199354 * denied (B_FALSE)
10735440Sjm199354 *
10745440Sjm199354 * Returns: 1 exempt
10755440Sjm199354 * 0 scan required
10765440Sjm199354 */
10775440Sjm199354 static int
vscan_svc_exempt_file(vnode_t * vp,boolean_t * allow)10785440Sjm199354 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
10795440Sjm199354 {
10805440Sjm199354 struct vattr attr;
10815440Sjm199354
10825440Sjm199354 ASSERT(vp != NULL);
10835440Sjm199354 ASSERT(vp->v_path != NULL);
10845440Sjm199354
10855440Sjm199354 attr.va_mask = AT_SIZE;
10865440Sjm199354
10875440Sjm199354 if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
10885440Sjm199354 *allow = B_FALSE;
10895440Sjm199354 return (0);
10905440Sjm199354 }
10915440Sjm199354
10925440Sjm199354 mutex_enter(&vscan_svc_cfg_mutex);
10935440Sjm199354
10945440Sjm199354 if (attr.va_size > vscan_svc_config.vsc_max_size) {
10955440Sjm199354 DTRACE_PROBE2(vscan__exempt__filesize, char *,
10965440Sjm199354 vp->v_path, int, *allow);
10975440Sjm199354
10985440Sjm199354 *allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
10995440Sjm199354 mutex_exit(&vscan_svc_cfg_mutex);
11005440Sjm199354 return (1);
11015440Sjm199354 }
11025440Sjm199354
11035440Sjm199354 if (vscan_svc_exempt_filetype(vp->v_path)) {
11045440Sjm199354 DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
11055440Sjm199354 *allow = B_TRUE;
11065440Sjm199354 mutex_exit(&vscan_svc_cfg_mutex);
11075440Sjm199354 return (1);
11085440Sjm199354 }
11095440Sjm199354
11105440Sjm199354 mutex_exit(&vscan_svc_cfg_mutex);
11115440Sjm199354 return (0);
11125440Sjm199354 }
11135440Sjm199354
11145440Sjm199354
11155440Sjm199354 /*
11165440Sjm199354 * vscan_svc_exempt_filetype
11175440Sjm199354 *
11185440Sjm199354 * Each entry in vscan_svc_types includes a rule indicator (+,-)
11195440Sjm199354 * followed by the match string for file types to which the rule
11205440Sjm199354 * applies. Look for first match of file type in vscan_svc_types
11215440Sjm199354 * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
11225440Sjm199354 * if the indicator is '+'.
11235440Sjm199354 * If vscan_svc_match_ext fails, or no match is found, return 0
11245440Sjm199354 * (not exempt)
11255440Sjm199354 *
11265440Sjm199354 * Returns 1: exempt, 0: not exempt
11275440Sjm199354 */
11285440Sjm199354 static int
vscan_svc_exempt_filetype(char * filepath)11295440Sjm199354 vscan_svc_exempt_filetype(char *filepath)
11305440Sjm199354 {
11315440Sjm199354 int i, rc, exempt = 0;
11325440Sjm199354 char *filename, *ext;
11335440Sjm199354
11345440Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
11355440Sjm199354
11365440Sjm199354 if ((filename = strrchr(filepath, '/')) == 0)
11375440Sjm199354 filename = filepath;
11385440Sjm199354 else
11395440Sjm199354 filename++;
11405440Sjm199354
11415440Sjm199354 if ((ext = strrchr(filename, '.')) == NULL)
11425440Sjm199354 ext = "";
11435440Sjm199354 else
11445440Sjm199354 ext++;
11455440Sjm199354
11465440Sjm199354 for (i = 0; i < VS_TYPES_MAX; i ++) {
11475440Sjm199354 if (vscan_svc_types[i] == 0)
11485440Sjm199354 break;
11495440Sjm199354
11505440Sjm199354 rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
11515440Sjm199354 if (rc == -1)
11525440Sjm199354 break;
11535440Sjm199354 if (rc > 0) {
11545440Sjm199354 DTRACE_PROBE2(vscan__type__match, char *, ext,
11555440Sjm199354 char *, vscan_svc_types[i]);
11565440Sjm199354 exempt = (vscan_svc_types[i][0] == '-');
11575440Sjm199354 break;
11585440Sjm199354 }
11595440Sjm199354 }
11605440Sjm199354
11615440Sjm199354 return (exempt);
11625440Sjm199354 }
11635440Sjm199354
11645440Sjm199354
11655440Sjm199354 /*
11665440Sjm199354 * vscan_svc_match_ext
11675440Sjm199354 *
11685440Sjm199354 * Performs a case-insensitive match for two strings. The first string
11695440Sjm199354 * argument can contain the wildcard characters '?' and '*'
11705440Sjm199354 *
11715440Sjm199354 * Returns: 0 no match
11725440Sjm199354 * 1 match
11735440Sjm199354 * -1 recursion error
11745440Sjm199354 */
11755440Sjm199354 static int
vscan_svc_match_ext(char * patn,char * str,int depth)11765440Sjm199354 vscan_svc_match_ext(char *patn, char *str, int depth)
11775440Sjm199354 {
11785440Sjm199354 int c1, c2;
11795440Sjm199354 if (depth > VS_EXT_RECURSE_DEPTH)
11805440Sjm199354 return (-1);
11815440Sjm199354
11825440Sjm199354 for (;;) {
11835440Sjm199354 switch (*patn) {
11845440Sjm199354 case 0:
11855440Sjm199354 return (*str == 0);
11865440Sjm199354
11875440Sjm199354 case '?':
11885440Sjm199354 if (*str != 0) {
11895440Sjm199354 str++;
11905440Sjm199354 patn++;
11915440Sjm199354 continue;
11925440Sjm199354 }
11935440Sjm199354 return (0);
11945440Sjm199354
11955440Sjm199354 case '*':
11965440Sjm199354 patn++;
11975440Sjm199354 if (*patn == 0)
11985440Sjm199354 return (1);
11995440Sjm199354
12005440Sjm199354 while (*str) {
12015440Sjm199354 if (vscan_svc_match_ext(patn, str, depth + 1))
12025440Sjm199354 return (1);
12035440Sjm199354 str++;
12045440Sjm199354 }
12055440Sjm199354 return (0);
12065440Sjm199354
12075440Sjm199354 default:
12085440Sjm199354 if (*str != *patn) {
12095440Sjm199354 c1 = *str;
12105440Sjm199354 c2 = *patn;
12115440Sjm199354
12125440Sjm199354 c1 = tolower(c1);
12135440Sjm199354 c2 = tolower(c2);
12145440Sjm199354 if (c1 != c2)
12155440Sjm199354 return (0);
12165440Sjm199354 }
12175440Sjm199354 str++;
12185440Sjm199354 patn++;
12195440Sjm199354 continue;
12205440Sjm199354 }
12215440Sjm199354 }
12225440Sjm199354 /* NOT REACHED */
12235440Sjm199354 }
12246407Sjm199354
12256407Sjm199354
12266407Sjm199354 /*
12276407Sjm199354 * vscan_svc_insert_req
12286407Sjm199354 *
12296407Sjm199354 * Insert request in next available available slot in vscan_svc_nodes
12306407Sjm199354 *
12316407Sjm199354 * Returns: idx of slot, or -1 if no slot available
12326407Sjm199354 */
12336407Sjm199354 static int
vscan_svc_insert_req(vscan_req_t * req)12346407Sjm199354 vscan_svc_insert_req(vscan_req_t *req)
12356407Sjm199354 {
12366407Sjm199354 int idx;
12376407Sjm199354 vscan_svc_node_t *node;
12386407Sjm199354
12396407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
12406407Sjm199354
12416407Sjm199354 if (vscan_svc_counts.vsc_node == vs_nodes_max)
12426407Sjm199354 return (-1);
12436407Sjm199354
12446407Sjm199354 for (idx = 1; idx <= vs_nodes_max; idx++) {
12456407Sjm199354 if (vscan_svc_nodes[idx].vsn_req == NULL) {
12466407Sjm199354 req->vsr_idx = idx;
12476407Sjm199354
12486407Sjm199354 node = &vscan_svc_nodes[idx];
12496407Sjm199354 (void) memset(node, 0, sizeof (vscan_svc_node_t));
12506407Sjm199354 node->vsn_req = req;
12516407Sjm199354 node->vsn_modified = 1;
12526407Sjm199354 node->vsn_result = VS_STATUS_UNDEFINED;
12536407Sjm199354 node->vsn_access = VS_ACCESS_UNDEFINED;
12546407Sjm199354
12556407Sjm199354 ++(vscan_svc_counts.vsc_node);
12566407Sjm199354 return (idx);
12576407Sjm199354 }
12586407Sjm199354 }
12596407Sjm199354
12606407Sjm199354 return (-1);
12616407Sjm199354 }
12626407Sjm199354
12636407Sjm199354
12646407Sjm199354 /*
12656407Sjm199354 * vscan_svc_remove_req
12666407Sjm199354 */
12676407Sjm199354 static void
vscan_svc_remove_req(int idx)12686407Sjm199354 vscan_svc_remove_req(int idx)
12696407Sjm199354 {
12706407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
12716407Sjm199354
12726407Sjm199354 if (idx != 0) {
12736407Sjm199354 (void) memset(&vscan_svc_nodes[idx], 0,
12746407Sjm199354 sizeof (vscan_svc_node_t));
12756407Sjm199354 --(vscan_svc_counts.vsc_node);
12766407Sjm199354 }
12776407Sjm199354 }
12786407Sjm199354
12796407Sjm199354
12806407Sjm199354 /*
12816407Sjm199354 * vscan_svc_reql_find
12826407Sjm199354 */
12836407Sjm199354 static vscan_req_t *
vscan_svc_reql_find(vnode_t * vp)12846407Sjm199354 vscan_svc_reql_find(vnode_t *vp)
12856407Sjm199354 {
12866407Sjm199354 vscan_req_t *req;
12876407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
12886407Sjm199354
12896407Sjm199354 req = list_head(&vscan_svc_reql);
12906407Sjm199354
12916407Sjm199354 while (req != NULL) {
12926407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
12936407Sjm199354 if ((req->vsr_vp == vp) &&
12946407Sjm199354 (req->vsr_state != VS_SVC_REQ_COMPLETE))
12956407Sjm199354 break;
12966407Sjm199354
12976407Sjm199354 req = list_next(&vscan_svc_reql, req);
12986407Sjm199354 }
12996407Sjm199354
13006407Sjm199354 return (req);
13016407Sjm199354 }
13026407Sjm199354
13036407Sjm199354
13046407Sjm199354 /*
13056407Sjm199354 * vscan_svc_reql_insert
13066407Sjm199354 */
13076407Sjm199354 static vscan_req_t *
vscan_svc_reql_insert(vnode_t * vp)13086407Sjm199354 vscan_svc_reql_insert(vnode_t *vp)
13096407Sjm199354 {
13106407Sjm199354 vscan_req_t *req;
13116407Sjm199354
13126407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
13136407Sjm199354
13146407Sjm199354 /* if request already in list then return it */
13156407Sjm199354 if ((req = vscan_svc_reql_find(vp)) != NULL)
13166407Sjm199354 return (req);
13176407Sjm199354
13186407Sjm199354 /* if list is full return NULL */
13196407Sjm199354 if (vscan_svc_counts.vsc_reql == vs_reqs_max)
13206407Sjm199354 return (NULL);
13216407Sjm199354
13226407Sjm199354 /* create a new request and insert into list */
13236407Sjm199354 VN_HOLD(vp);
13246407Sjm199354
13256407Sjm199354 req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
13266407Sjm199354
13276407Sjm199354 req->vsr_magic = VS_REQ_MAGIC;
13286407Sjm199354 if (vscan_svc_seqnum == UINT32_MAX)
13296407Sjm199354 vscan_svc_seqnum = 0;
13306407Sjm199354 req->vsr_seqnum = ++vscan_svc_seqnum;
13316407Sjm199354 req->vsr_vp = vp;
13326407Sjm199354 req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
13336407Sjm199354 req->vsr_state = VS_SVC_REQ_INIT;
13346407Sjm199354 cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
13356407Sjm199354
13366407Sjm199354 list_insert_tail(&vscan_svc_reql, req);
13376407Sjm199354 if (vscan_svc_reql_next == NULL)
13386407Sjm199354 vscan_svc_reql_next = req;
13396407Sjm199354
13406407Sjm199354 ++(vscan_svc_counts.vsc_reql);
13416407Sjm199354
13426407Sjm199354 /* wake reql handler thread */
13436407Sjm199354 cv_signal(&vscan_svc_reql_cv);
13446407Sjm199354
13456407Sjm199354 return (req);
13466407Sjm199354 }
13476407Sjm199354
13486407Sjm199354
13496407Sjm199354 /*
13506407Sjm199354 * vscan_svc_reql_remove
13516407Sjm199354 */
13526407Sjm199354 static void
vscan_svc_reql_remove(vscan_req_t * req)13536407Sjm199354 vscan_svc_reql_remove(vscan_req_t *req)
13546407Sjm199354 {
13556407Sjm199354 ASSERT(MUTEX_HELD(&vscan_svc_mutex));
13566407Sjm199354 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
13576407Sjm199354
13586407Sjm199354 if (vscan_svc_reql_next == req)
13596407Sjm199354 vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
13606407Sjm199354
13616407Sjm199354 list_remove(&vscan_svc_reql, req);
13626407Sjm199354 cv_destroy(&(req->vsr_cv));
13636407Sjm199354 VN_RELE(req->vsr_vp);
13646407Sjm199354
13656407Sjm199354 kmem_free(req, sizeof (vscan_req_t));
13666407Sjm199354 --(vscan_svc_counts.vsc_reql);
13676407Sjm199354 }
1368