xref: /onnv-gate/usr/src/uts/common/io/vscan/vscan_svc.c (revision 6407:71e85e2b3164)
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 /*
235931Sjm199354  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245440Sjm199354  * Use is subject to license terms.
255440Sjm199354  */
265440Sjm199354 
275440Sjm199354 #pragma ident	"%Z%%M%	%I%	%E% SMI"
285440Sjm199354 
295440Sjm199354 #include <sys/stat.h>
305440Sjm199354 #include <sys/ddi.h>
315440Sjm199354 #include <sys/sunddi.h>
325440Sjm199354 #include <sys/time.h>
335440Sjm199354 #include <sys/varargs.h>
345440Sjm199354 #include <sys/conf.h>
355440Sjm199354 #include <sys/modctl.h>
365440Sjm199354 #include <sys/cmn_err.h>
375440Sjm199354 #include <sys/vnode.h>
385440Sjm199354 #include <fs/fs_subr.h>
395440Sjm199354 #include <sys/types.h>
405440Sjm199354 #include <sys/file.h>
415440Sjm199354 #include <sys/disp.h>
425440Sjm199354 #include <sys/sdt.h>
435440Sjm199354 #include <sys/cred.h>
44*6407Sjm199354 #include <sys/list.h>
455440Sjm199354 #include <sys/vscan.h>
465440Sjm199354 
47*6407Sjm199354 #define	VS_REQ_MAGIC		0x52515354 /* 'RQST' */
48*6407Sjm199354 
49*6407Sjm199354 #define	VS_REQS_DEFAULT		20000	/* pending scan requests - reql */
50*6407Sjm199354 #define	VS_NODES_DEFAULT	128	/* concurrent file scans */
51*6407Sjm199354 #define	VS_WORKERS_DEFAULT	32	/* worker threads */
52*6407Sjm199354 #define	VS_SCANWAIT_DEFAULT	15*60	/* seconds to wait for scan result */
53*6407Sjm199354 #define	VS_REQL_HANDLER_TIMEOUT	30
545440Sjm199354 #define	VS_EXT_RECURSE_DEPTH	8
55*6407Sjm199354 
56*6407Sjm199354 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
57*6407Sjm199354 #define	VS_ACCESS_UNDEFINED	0
58*6407Sjm199354 #define	VS_ACCESS_ALLOW		1	/* return 0 */
59*6407Sjm199354 #define	VS_ACCESS_DENY		2	/* return EACCES */
60*6407Sjm199354 
615440Sjm199354 #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
62*6407Sjm199354 #define	offsetof(s, m)	(size_t)(&(((s *)0)->m))
63*6407Sjm199354 
64*6407Sjm199354 /* global variables - tunable via /etc/system */
65*6407Sjm199354 uint32_t vs_reqs_max = VS_REQS_DEFAULT;	/* max scan requests */
66*6407Sjm199354 uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
67*6407Sjm199354 uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
68*6407Sjm199354 uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
69*6407Sjm199354 
705440Sjm199354 
71*6407Sjm199354 /*
72*6407Sjm199354  * vscan_svc_state
73*6407Sjm199354  *
74*6407Sjm199354  *   +-----------------+
75*6407Sjm199354  *   | VS_SVC_UNCONFIG |
76*6407Sjm199354  *   +-----------------+
77*6407Sjm199354  *      |           ^
78*6407Sjm199354  *      | svc_init  | svc_fini
79*6407Sjm199354  *      v           |
80*6407Sjm199354  *   +-----------------+
81*6407Sjm199354  *   | VS_SVC_IDLE     |<----|
82*6407Sjm199354  *   +-----------------+	 |
83*6407Sjm199354  *      |                    |
84*6407Sjm199354  *      | svc_enable         |
85*6407Sjm199354  *      |<----------------|  |
86*6407Sjm199354  *      v                 |  |
87*6407Sjm199354  *   +-----------------+  |  |
88*6407Sjm199354  *   | VS_SVC_ENABLED  |--|  |
89*6407Sjm199354  *   +-----------------+     |
90*6407Sjm199354  *      |                    |
91*6407Sjm199354  *      | svc_disable        | handler thread exit,
92*6407Sjm199354  *      v                    | all requests complete
93*6407Sjm199354  *   +-----------------+	 |
94*6407Sjm199354  *   | VS_SVC_DISABLED |-----|
95*6407Sjm199354  *   +-----------------+
96*6407Sjm199354  *
97*6407Sjm199354  * svc_enable may occur when we are already in the ENABLED
98*6407Sjm199354  * state if vscand has exited without clean shutdown and
99*6407Sjm199354  * then reconnected within the delayed disable time period
100*6407Sjm199354  * (vs_reconnect_timeout) - see vscan_drv
101*6407Sjm199354  */
102*6407Sjm199354 
103*6407Sjm199354 typedef enum {
104*6407Sjm199354 	VS_SVC_UNCONFIG,
105*6407Sjm199354 	VS_SVC_IDLE,
106*6407Sjm199354 	VS_SVC_ENABLED, /* service enabled and registered */
107*6407Sjm199354 	VS_SVC_DISABLED /* service disabled and nunregistered */
108*6407Sjm199354 } vscan_svc_state_t;
109*6407Sjm199354 static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
110*6407Sjm199354 
1115440Sjm199354 
1125440Sjm199354 /*
113*6407Sjm199354  * vscan_svc_req_state
114*6407Sjm199354  *
115*6407Sjm199354  * When a scan request is received from the file system it is
116*6407Sjm199354  * identified in or inserted into the vscan_svc_reql (INIT).
117*6407Sjm199354  * If the request is asynchronous 0 is then returned to the caller.
118*6407Sjm199354  * If the request is synchronous the req's refcnt is incremented
119*6407Sjm199354  * and the caller waits for the request to complete.
120*6407Sjm199354  * The refcnt is also incremented when the request is inserted
121*6407Sjm199354  * in vscan_svc_nodes, and decremented on scan_complete.
122*6407Sjm199354  *
123*6407Sjm199354  * vscan_svc_handler processes requests from the request list,
124*6407Sjm199354  * inserting them into vscan_svc_nodes and the task queue (QUEUED).
125*6407Sjm199354  * When the task queue call back (vscan_svc_do_scan) is invoked
126*6407Sjm199354  * the request transitions to IN_PROGRESS state. If the request
127*6407Sjm199354  * is sucessfully sent to vscand (door_call) and the door response
128*6407Sjm199354  * is SCANNING then the scan result will be received asynchronously.
129*6407Sjm199354  * Although unusual, it is possible that the async response is
130*6407Sjm199354  * received before the door call returns (hence the ASYNC_COMPLETE
131*6407Sjm199354  * state).
132*6407Sjm199354  * When the result has been determined / received,
133*6407Sjm199354  * vscan_svc_scan_complete is invoked to transition the request to
134*6407Sjm199354  * COMPLETE state, decrement refcnt and signal all waiting callers.
135*6407Sjm199354  * When the last waiting caller has processed the result (refcnt == 0)
136*6407Sjm199354  * the request is removed from vscan_svc_reql and vscan_svc_nodes
137*6407Sjm199354  * and deleted.
138*6407Sjm199354  *
139*6407Sjm199354  *      |                                                     ^
140*6407Sjm199354  *      | reql_insert                                         | refcnt == 0
141*6407Sjm199354  *      v                                                     | (delete)
142*6407Sjm199354  *   +------------------------+	                  +---------------------+
143*6407Sjm199354  *   | VS_SVC_REQ_INIT        | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
144*6407Sjm199354  *   +------------------------+	                  +---------------------+
145*6407Sjm199354  *      |                                                     ^
146*6407Sjm199354  *      | insert_req, tq_dispatch                             |
147*6407Sjm199354  *      v                                                     |
148*6407Sjm199354  *   +------------------------+	                              |
149*6407Sjm199354  *   | VS_SVC_REQ_QUEUED      |                           scan_complete
150*6407Sjm199354  *   +------------------------+	                              |
151*6407Sjm199354  *      |                                                     |
152*6407Sjm199354  *      | tq_callback (do_scan)                               |
153*6407Sjm199354  *      |                                                     |
154*6407Sjm199354  *      v                        scan not req'd, error,       |
155*6407Sjm199354  *   +------------------------+  or door_result != SCANNING   |
156*6407Sjm199354  *   | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
157*6407Sjm199354  *   +------------------------+	                              |
158*6407Sjm199354  *       |         |                                          |
159*6407Sjm199354  *       |         | door_result == SCANNING                  |
160*6407Sjm199354  *       |         v                                          |
161*6407Sjm199354  *       |     +---------------------------+	async result  |
162*6407Sjm199354  *       |     | VS_SVC_REQ_SCANNING       |-------->---------|
163*6407Sjm199354  *       |     +---------------------------+	              |
164*6407Sjm199354  *       |                                                    |
165*6407Sjm199354  *       | async result                                       |
166*6407Sjm199354  *       v                                                    |
167*6407Sjm199354  *    +---------------------------+	 door_result = SCANNING   |
168*6407Sjm199354  *    | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
169*6407Sjm199354  *    +---------------------------+
170*6407Sjm199354  */
171*6407Sjm199354 typedef enum {
172*6407Sjm199354 	VS_SVC_REQ_INIT,
173*6407Sjm199354 	VS_SVC_REQ_QUEUED,
174*6407Sjm199354 	VS_SVC_REQ_IN_PROGRESS,
175*6407Sjm199354 	VS_SVC_REQ_SCANNING,
176*6407Sjm199354 	VS_SVC_REQ_ASYNC_COMPLETE,
177*6407Sjm199354 	VS_SVC_REQ_COMPLETE
178*6407Sjm199354 } vscan_svc_req_state_t;
179*6407Sjm199354 
180*6407Sjm199354 
181*6407Sjm199354 /*
182*6407Sjm199354  * vscan_svc_reql - the list of pending and in-progress scan requests
183*6407Sjm199354  */
184*6407Sjm199354 typedef struct vscan_req {
185*6407Sjm199354 	uint32_t vsr_magic;	/* VS_REQ_MAGIC */
186*6407Sjm199354 	list_node_t vsr_lnode;
187*6407Sjm199354 	vnode_t *vsr_vp;
188*6407Sjm199354 	uint32_t vsr_idx;	/* vscan_svc_nodes index */
189*6407Sjm199354 	uint32_t vsr_seqnum;	/* unigue request id */
190*6407Sjm199354 	uint32_t vsr_refcnt;
191*6407Sjm199354 	kcondvar_t vsr_cv;
192*6407Sjm199354 	vscan_svc_req_state_t vsr_state;
193*6407Sjm199354 } vscan_req_t;
194*6407Sjm199354 
195*6407Sjm199354 static list_t vscan_svc_reql;
196*6407Sjm199354 
197*6407Sjm199354 
198*6407Sjm199354 /*
199*6407Sjm199354  * vscan_svc_nodes - table of files being scanned
2005440Sjm199354  *
2015440Sjm199354  * The index into this table is passed in the door call to
2025440Sjm199354  * vscand. vscand uses the idx to determine which minor node
2035440Sjm199354  * to open to read the file data. Within the kernel driver
2045440Sjm199354  * the minor device number can thus be used to identify the
2055440Sjm199354  * table index to get the appropriate vnode.
2065440Sjm199354  *
2075440Sjm199354  * Instance 0 is reserved for the daemon/driver control
2085440Sjm199354  * interface: enable/configure/disable
2095440Sjm199354  */
210*6407Sjm199354 typedef struct vscan_svc_node {
211*6407Sjm199354 	vscan_req_t *vsn_req;
212*6407Sjm199354 	uint8_t vsn_quarantined;
213*6407Sjm199354 	uint8_t vsn_modified;
214*6407Sjm199354 	uint64_t vsn_size;
215*6407Sjm199354 	timestruc_t vsn_mtime;
216*6407Sjm199354 	vs_scanstamp_t vsn_scanstamp;
217*6407Sjm199354 	uint32_t vsn_result;
218*6407Sjm199354 	uint32_t vsn_access;
219*6407Sjm199354 } vscan_svc_node_t;
2205440Sjm199354 
221*6407Sjm199354 static vscan_svc_node_t *vscan_svc_nodes;
222*6407Sjm199354 static int vscan_svc_nodes_sz;
223*6407Sjm199354 
224*6407Sjm199354 
225*6407Sjm199354 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
226*6407Sjm199354 static taskq_t *vscan_svc_taskq = NULL;
2275440Sjm199354 
228*6407Sjm199354 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
229*6407Sjm199354 typedef struct {
230*6407Sjm199354 	uint32_t vsc_reql;
231*6407Sjm199354 	uint32_t vsc_node;
232*6407Sjm199354 	uint32_t vsc_tq;
233*6407Sjm199354 } vscan_svc_counts_t;
234*6407Sjm199354 static vscan_svc_counts_t vscan_svc_counts;
2355440Sjm199354 
2365440Sjm199354 /*
2375440Sjm199354  * vscan_svc_mutex protects the data pertaining to scan requests:
238*6407Sjm199354  * request list - vscan_svc_reql
239*6407Sjm199354  * node table - vscan_svc_nodes
2405440Sjm199354  */
2415440Sjm199354 static kmutex_t vscan_svc_mutex;
2425440Sjm199354 
243*6407Sjm199354 /* unique request id for vscand request/response correlation */
244*6407Sjm199354 static uint32_t vscan_svc_seqnum = 0;
245*6407Sjm199354 
2465440Sjm199354 /*
2475440Sjm199354  * vscan_svc_cfg_mutex protects the configuration data:
2485440Sjm199354  * vscan_svc_config, vscan_svc_types
2495440Sjm199354  */
2505440Sjm199354 static kmutex_t vscan_svc_cfg_mutex;
2515440Sjm199354 
2525440Sjm199354 /* configuration data - for virus scan exemption */
2535440Sjm199354 static vs_config_t vscan_svc_config;
2545440Sjm199354 static char *vscan_svc_types[VS_TYPES_MAX];
2555440Sjm199354 
256*6407Sjm199354 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
257*6407Sjm199354 static kthread_t *vscan_svc_reql_thread;
258*6407Sjm199354 static kcondvar_t vscan_svc_reql_cv;
259*6407Sjm199354 static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
260*6407Sjm199354 
2615440Sjm199354 /* local functions */
2625440Sjm199354 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
263*6407Sjm199354 static void vscan_svc_taskq_callback(void *);
2645440Sjm199354 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
2655440Sjm199354 static int vscan_svc_exempt_filetype(char *);
2665440Sjm199354 static int vscan_svc_match_ext(char *, char *, int);
267*6407Sjm199354 static void vscan_svc_do_scan(vscan_req_t *);
268*6407Sjm199354 static vs_scan_req_t *vscan_svc_populate_req(int);
2695931Sjm199354 static void vscan_svc_process_scan_result(int);
270*6407Sjm199354 static void vscan_svc_scan_complete(vscan_req_t *);
271*6407Sjm199354 static void vscan_svc_delete_req(vscan_req_t *);
272*6407Sjm199354 static int vscan_svc_insert_req(vscan_req_t *);
273*6407Sjm199354 static void vscan_svc_remove_req(int);
274*6407Sjm199354 static vscan_req_t *vscan_svc_reql_find(vnode_t *);
275*6407Sjm199354 static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
276*6407Sjm199354 static void vscan_svc_reql_remove(vscan_req_t *);
277*6407Sjm199354 
2785440Sjm199354 static int vscan_svc_getattr(int);
2795931Sjm199354 static int vscan_svc_setattr(int, int);
2805440Sjm199354 
281*6407Sjm199354 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
282*6407Sjm199354 static void vscan_svc_reql_handler(void);
2835440Sjm199354 
2845440Sjm199354 
2855440Sjm199354 /*
2865440Sjm199354  * vscan_svc_init
2875440Sjm199354  */
2885440Sjm199354 int
2895440Sjm199354 vscan_svc_init()
2905440Sjm199354 {
291*6407Sjm199354 	if (vscan_svc_state != VS_SVC_UNCONFIG) {
292*6407Sjm199354 		DTRACE_PROBE1(vscan__svc__state__violation,
293*6407Sjm199354 		    int, vscan_svc_state);
294*6407Sjm199354 		return (-1);
295*6407Sjm199354 	}
296*6407Sjm199354 
297*6407Sjm199354 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
298*6407Sjm199354 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
299*6407Sjm199354 	cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
300*6407Sjm199354 
301*6407Sjm199354 	vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
302*6407Sjm199354 	vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
303*6407Sjm199354 
304*6407Sjm199354 	vscan_svc_counts.vsc_reql = 0;
305*6407Sjm199354 	vscan_svc_counts.vsc_node = 0;
306*6407Sjm199354 	vscan_svc_counts.vsc_tq = 0;
307*6407Sjm199354 
308*6407Sjm199354 	vscan_svc_state = VS_SVC_IDLE;
3095440Sjm199354 
3105440Sjm199354 	return (0);
3115440Sjm199354 }
3125440Sjm199354 
313*6407Sjm199354 
3145440Sjm199354 /*
3155440Sjm199354  * vscan_svc_fini
3165440Sjm199354  */
3175440Sjm199354 void
3185440Sjm199354 vscan_svc_fini()
3195440Sjm199354 {
320*6407Sjm199354 	if (vscan_svc_state != VS_SVC_IDLE) {
321*6407Sjm199354 		DTRACE_PROBE1(vscan__svc__state__violation,
322*6407Sjm199354 		    int, vscan_svc_state);
323*6407Sjm199354 		return;
324*6407Sjm199354 	}
3255440Sjm199354 
326*6407Sjm199354 	kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
327*6407Sjm199354 
328*6407Sjm199354 	cv_destroy(&vscan_svc_reql_cv);
3295440Sjm199354 	mutex_destroy(&vscan_svc_mutex);
3305440Sjm199354 	mutex_destroy(&vscan_svc_cfg_mutex);
331*6407Sjm199354 	vscan_svc_state = VS_SVC_UNCONFIG;
3325440Sjm199354 }
3335440Sjm199354 
334*6407Sjm199354 
3355440Sjm199354 /*
3365440Sjm199354  * vscan_svc_enable
3375440Sjm199354  */
338*6407Sjm199354 int
3395931Sjm199354 vscan_svc_enable(void)
3405440Sjm199354 {
3415931Sjm199354 	mutex_enter(&vscan_svc_mutex);
342*6407Sjm199354 
343*6407Sjm199354 	switch (vscan_svc_state) {
344*6407Sjm199354 	case VS_SVC_ENABLED:
345*6407Sjm199354 		/*
346*6407Sjm199354 		 * it's possible (and okay) for vscan_svc_enable to be
347*6407Sjm199354 		 * called when already enabled if vscand reconnects
348*6407Sjm199354 		 * during a delayed disable
349*6407Sjm199354 		 */
350*6407Sjm199354 		break;
351*6407Sjm199354 	case VS_SVC_IDLE:
352*6407Sjm199354 		list_create(&vscan_svc_reql, sizeof (vscan_req_t),
353*6407Sjm199354 		    offsetof(vscan_req_t, vsr_lnode));
354*6407Sjm199354 		vscan_svc_reql_next = list_head(&vscan_svc_reql);
3555931Sjm199354 
356*6407Sjm199354 		vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
357*6407Sjm199354 		    MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
358*6407Sjm199354 		ASSERT(vscan_svc_taskq != NULL);
359*6407Sjm199354 
360*6407Sjm199354 		vscan_svc_reql_thread = thread_create(NULL, 0,
361*6407Sjm199354 		    vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
362*6407Sjm199354 		ASSERT(vscan_svc_reql_thread != NULL);
363*6407Sjm199354 
364*6407Sjm199354 		/* ready to start processing requests */
365*6407Sjm199354 		vscan_svc_state = VS_SVC_ENABLED;
366*6407Sjm199354 		fs_vscan_register(vscan_svc_scan_file);
367*6407Sjm199354 		break;
368*6407Sjm199354 	default:
369*6407Sjm199354 		DTRACE_PROBE1(vscan__svc__state__violation,
370*6407Sjm199354 		    int, vscan_svc_state);
371*6407Sjm199354 		return (-1);
3725931Sjm199354 	}
3735931Sjm199354 
3745931Sjm199354 	mutex_exit(&vscan_svc_mutex);
375*6407Sjm199354 	return (0);
3765931Sjm199354 }
3775931Sjm199354 
3785440Sjm199354 
3795931Sjm199354 /*
3805931Sjm199354  * vscan_svc_disable
381*6407Sjm199354  *
382*6407Sjm199354  * Resources allocated during vscan_svc_enable are free'd by
383*6407Sjm199354  * the handler thread immediately prior to exiting
3845931Sjm199354  */
3855931Sjm199354 void
3865931Sjm199354 vscan_svc_disable(void)
3875931Sjm199354 {
3885931Sjm199354 	mutex_enter(&vscan_svc_mutex);
3895440Sjm199354 
390*6407Sjm199354 	switch (vscan_svc_state) {
391*6407Sjm199354 	case VS_SVC_ENABLED:
392*6407Sjm199354 		fs_vscan_register(NULL);
393*6407Sjm199354 		vscan_svc_state = VS_SVC_DISABLED;
394*6407Sjm199354 		cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
395*6407Sjm199354 		break;
396*6407Sjm199354 	default:
397*6407Sjm199354 		DTRACE_PROBE1(vscan__svc__state__violation, int,
398*6407Sjm199354 		    vscan_svc_state);
399*6407Sjm199354 	}
4005931Sjm199354 
401*6407Sjm199354 	mutex_exit(&vscan_svc_mutex);
4025931Sjm199354 }
4035931Sjm199354 
4045931Sjm199354 
4055440Sjm199354 /*
4065440Sjm199354  * vscan_svc_in_use
4075440Sjm199354  */
4085440Sjm199354 boolean_t
4095440Sjm199354 vscan_svc_in_use()
4105440Sjm199354 {
411*6407Sjm199354 	boolean_t in_use;
4125440Sjm199354 
4135440Sjm199354 	mutex_enter(&vscan_svc_mutex);
4145440Sjm199354 
415*6407Sjm199354 	switch (vscan_svc_state) {
416*6407Sjm199354 	case VS_SVC_IDLE:
417*6407Sjm199354 	case VS_SVC_UNCONFIG:
418*6407Sjm199354 		in_use = B_FALSE;
419*6407Sjm199354 		break;
420*6407Sjm199354 	default:
421*6407Sjm199354 		in_use = B_TRUE;
422*6407Sjm199354 		break;
423*6407Sjm199354 	}
424*6407Sjm199354 
425*6407Sjm199354 	mutex_exit(&vscan_svc_mutex);
426*6407Sjm199354 	return (in_use);
4275440Sjm199354 }
4285440Sjm199354 
429*6407Sjm199354 
4305440Sjm199354 /*
4315440Sjm199354  * vscan_svc_get_vnode
4325440Sjm199354  *
4335440Sjm199354  * Get the file vnode indexed by idx.
4345440Sjm199354  */
4355440Sjm199354 vnode_t *
4365440Sjm199354 vscan_svc_get_vnode(int idx)
4375440Sjm199354 {
438*6407Sjm199354 	vnode_t *vp = NULL;
439*6407Sjm199354 
4405440Sjm199354 	ASSERT(idx > 0);
441*6407Sjm199354 	ASSERT(idx <= vs_nodes_max);
4425440Sjm199354 
443*6407Sjm199354 	mutex_enter(&vscan_svc_mutex);
444*6407Sjm199354 	if (vscan_svc_nodes[idx].vsn_req)
445*6407Sjm199354 		vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
446*6407Sjm199354 	mutex_exit(&vscan_svc_mutex);
447*6407Sjm199354 
448*6407Sjm199354 	return (vp);
4495440Sjm199354 }
4505440Sjm199354 
4515440Sjm199354 
4525440Sjm199354 /*
4535440Sjm199354  * vscan_svc_scan_file
4545440Sjm199354  *
4555440Sjm199354  * This function is the entry point for the file system to
4565440Sjm199354  * request that a file be virus scanned.
4575440Sjm199354  */
4585440Sjm199354 int
4595440Sjm199354 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
4605440Sjm199354 {
461*6407Sjm199354 	int access;
462*6407Sjm199354 	vscan_req_t *req;
4635440Sjm199354 	boolean_t allow;
464*6407Sjm199354 	clock_t timeout, time_left;
4655440Sjm199354 
466*6407Sjm199354 	if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
4675440Sjm199354 		return (0);
4685440Sjm199354 
4695440Sjm199354 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
4705440Sjm199354 
4715440Sjm199354 	/* check if size or type exempts file from scanning */
4725440Sjm199354 	if (vscan_svc_exempt_file(vp, &allow)) {
4735440Sjm199354 		if ((allow == B_TRUE) || (async != 0))
4745440Sjm199354 			return (0);
4755440Sjm199354 
4765440Sjm199354 		return (EACCES);
4775440Sjm199354 	}
4785440Sjm199354 
479*6407Sjm199354 	mutex_enter(&vscan_svc_mutex);
480*6407Sjm199354 
481*6407Sjm199354 	if (vscan_svc_state != VS_SVC_ENABLED) {
482*6407Sjm199354 		DTRACE_PROBE1(vscan__svc__state__violation,
483*6407Sjm199354 		    int, vscan_svc_state);
484*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
485*6407Sjm199354 		return (0);
486*6407Sjm199354 	}
4875440Sjm199354 
488*6407Sjm199354 	/* insert (or find) request in list */
489*6407Sjm199354 	if ((req = vscan_svc_reql_insert(vp)) == NULL) {
490*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
491*6407Sjm199354 		cmn_err(CE_WARN, "Virus scan request list full");
492*6407Sjm199354 		return ((async != 0) ? 0 : EACCES);
493*6407Sjm199354 	}
4945440Sjm199354 
495*6407Sjm199354 	/* asynchronous request: return 0 */
4965440Sjm199354 	if (async) {
497*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
498*6407Sjm199354 		return (0);
4995440Sjm199354 	}
5005440Sjm199354 
501*6407Sjm199354 	/* synchronous scan request: wait for result */
502*6407Sjm199354 	++(req->vsr_refcnt);
503*6407Sjm199354 	time_left = SEC_TO_TICK(vs_scan_wait);
504*6407Sjm199354 	while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
505*6407Sjm199354 		timeout = lbolt + time_left;
506*6407Sjm199354 		time_left = cv_timedwait_sig(&(req->vsr_cv),
507*6407Sjm199354 		    &vscan_svc_mutex, timeout);
508*6407Sjm199354 	}
509*6407Sjm199354 
510*6407Sjm199354 	if (time_left == -1) {
511*6407Sjm199354 		cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
512*6407Sjm199354 		    vp->v_path, req->vsr_seqnum);
513*6407Sjm199354 		DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
514*6407Sjm199354 	}
515*6407Sjm199354 
516*6407Sjm199354 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
517*6407Sjm199354 	if (vscan_svc_state == VS_SVC_DISABLED)
518*6407Sjm199354 		access = VS_ACCESS_ALLOW;
519*6407Sjm199354 	else if (req->vsr_idx == 0)
520*6407Sjm199354 		access = VS_ACCESS_DENY;
521*6407Sjm199354 	else
522*6407Sjm199354 		access = vscan_svc_nodes[req->vsr_idx].vsn_access;
523*6407Sjm199354 
524*6407Sjm199354 	if ((--req->vsr_refcnt) == 0)
525*6407Sjm199354 		vscan_svc_delete_req(req);
526*6407Sjm199354 
5275440Sjm199354 	mutex_exit(&vscan_svc_mutex);
528*6407Sjm199354 	return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
5295440Sjm199354 }
5305440Sjm199354 
5315440Sjm199354 
5325440Sjm199354 /*
533*6407Sjm199354  * vscan_svc_reql_handler
5345440Sjm199354  *
535*6407Sjm199354  * inserts scan requests (from vscan_svc_reql) into
536*6407Sjm199354  * vscan_svc_nodes and vscan_svc_taskq
5375440Sjm199354  */
538*6407Sjm199354 static void
539*6407Sjm199354 vscan_svc_reql_handler(void)
540*6407Sjm199354 {
541*6407Sjm199354 	vscan_req_t *req, *next;
542*6407Sjm199354 
543*6407Sjm199354 	for (;;) {
544*6407Sjm199354 		mutex_enter(&vscan_svc_mutex);
545*6407Sjm199354 
546*6407Sjm199354 		if ((vscan_svc_state == VS_SVC_DISABLED) &&
547*6407Sjm199354 		    (vscan_svc_counts.vsc_reql == 0)) {
548*6407Sjm199354 			/* free resources allocated durining enable */
549*6407Sjm199354 			taskq_destroy(vscan_svc_taskq);
550*6407Sjm199354 			vscan_svc_taskq = NULL;
551*6407Sjm199354 			list_destroy(&vscan_svc_reql);
552*6407Sjm199354 			vscan_svc_state = VS_SVC_IDLE;
553*6407Sjm199354 			mutex_exit(&vscan_svc_mutex);
554*6407Sjm199354 			return;
555*6407Sjm199354 		}
556*6407Sjm199354 
557*6407Sjm199354 		/*
558*6407Sjm199354 		 * If disabled, scan_complete any pending requests.
559*6407Sjm199354 		 * Otherwise insert pending requests into vscan_svc_nodes
560*6407Sjm199354 		 * and vscan_svc_taskq. If no slots are available in
561*6407Sjm199354 		 * vscan_svc_nodes break loop and wait for one
562*6407Sjm199354 		 */
563*6407Sjm199354 		req = vscan_svc_reql_next;
564*6407Sjm199354 
565*6407Sjm199354 		while (req != NULL) {
566*6407Sjm199354 			ASSERT(req->vsr_magic == VS_REQ_MAGIC);
567*6407Sjm199354 			next = list_next(&vscan_svc_reql, req);
568*6407Sjm199354 
569*6407Sjm199354 			if (vscan_svc_state == VS_SVC_DISABLED) {
570*6407Sjm199354 				vscan_svc_scan_complete(req);
571*6407Sjm199354 			} else {
572*6407Sjm199354 				/* insert request into vscan_svc_nodes */
573*6407Sjm199354 				if (vscan_svc_insert_req(req) == -1)
574*6407Sjm199354 					break;
575*6407Sjm199354 
576*6407Sjm199354 				/* add the scan request into the taskq */
577*6407Sjm199354 				(void) taskq_dispatch(vscan_svc_taskq,
578*6407Sjm199354 				    vscan_svc_taskq_callback,
579*6407Sjm199354 				    (void *)req, TQ_SLEEP);
580*6407Sjm199354 				++(vscan_svc_counts.vsc_tq);
581*6407Sjm199354 
582*6407Sjm199354 				req->vsr_state = VS_SVC_REQ_QUEUED;
583*6407Sjm199354 			}
584*6407Sjm199354 			req = next;
585*6407Sjm199354 		}
586*6407Sjm199354 
587*6407Sjm199354 		vscan_svc_reql_next = req;
588*6407Sjm199354 
589*6407Sjm199354 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
590*6407Sjm199354 		    vscan_svc_counts_t *, &vscan_svc_counts);
591*6407Sjm199354 
592*6407Sjm199354 		(void) cv_timedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
593*6407Sjm199354 		    lbolt + SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT));
594*6407Sjm199354 
595*6407Sjm199354 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
596*6407Sjm199354 		    vscan_svc_counts_t *, &vscan_svc_counts);
597*6407Sjm199354 
598*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
599*6407Sjm199354 	}
600*6407Sjm199354 }
601*6407Sjm199354 
602*6407Sjm199354 
603*6407Sjm199354 static void
6045440Sjm199354 vscan_svc_taskq_callback(void *data)
6055440Sjm199354 {
606*6407Sjm199354 	vscan_req_t *req;
6075440Sjm199354 
6085440Sjm199354 	mutex_enter(&vscan_svc_mutex);
609*6407Sjm199354 
610*6407Sjm199354 	req = (vscan_req_t *)data;
611*6407Sjm199354 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
612*6407Sjm199354 	vscan_svc_do_scan(req);
613*6407Sjm199354 	if (req->vsr_state != VS_SVC_REQ_SCANNING)
614*6407Sjm199354 		vscan_svc_scan_complete(req);
615*6407Sjm199354 
616*6407Sjm199354 	--(vscan_svc_counts.vsc_tq);
6175440Sjm199354 	mutex_exit(&vscan_svc_mutex);
6185440Sjm199354 }
6195440Sjm199354 
6205440Sjm199354 
6215440Sjm199354 /*
6225440Sjm199354  * vscan_svc_do_scan
6235440Sjm199354  *
624*6407Sjm199354  * Note: To avoid potential deadlock it is important that
625*6407Sjm199354  * vscan_svc_mutex is not held during the call to
626*6407Sjm199354  * vscan_drv_create_note. vscan_drv_create_note enters
627*6407Sjm199354  * the vscan_drv_mutex and it is possible that a thread
628*6407Sjm199354  * holding that mutex could be waiting for vscan_svc_mutex.
629*6407Sjm199354  */
630*6407Sjm199354 static void
631*6407Sjm199354 vscan_svc_do_scan(vscan_req_t *req)
632*6407Sjm199354 {
633*6407Sjm199354 	int idx, result;
634*6407Sjm199354 	vscan_svc_node_t *node;
635*6407Sjm199354 	vs_scan_req_t *door_req;
636*6407Sjm199354 
637*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
638*6407Sjm199354 
639*6407Sjm199354 	idx = req->vsr_idx;
640*6407Sjm199354 	node = &vscan_svc_nodes[idx];
641*6407Sjm199354 
642*6407Sjm199354 	req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
643*6407Sjm199354 
644*6407Sjm199354 	/* if vscan not enabled (shutting down), allow ACCESS */
645*6407Sjm199354 	if (vscan_svc_state != VS_SVC_ENABLED) {
646*6407Sjm199354 		node->vsn_access = VS_ACCESS_ALLOW;
647*6407Sjm199354 		return;
648*6407Sjm199354 	}
649*6407Sjm199354 
650*6407Sjm199354 	if (vscan_svc_getattr(idx) != 0) {
651*6407Sjm199354 		cmn_err(CE_WARN, "Can't access xattr for %s\n",
652*6407Sjm199354 		    req->vsr_vp->v_path);
653*6407Sjm199354 		node->vsn_access = VS_ACCESS_DENY;
654*6407Sjm199354 		return;
655*6407Sjm199354 	}
656*6407Sjm199354 
657*6407Sjm199354 	/* valid scan_req ptr guaranteed */
658*6407Sjm199354 	door_req = vscan_svc_populate_req(idx);
659*6407Sjm199354 
660*6407Sjm199354 	/* free up mutex around create node and door call */
661*6407Sjm199354 	mutex_exit(&vscan_svc_mutex);
662*6407Sjm199354 	if (vscan_drv_create_node(idx) != B_TRUE)
663*6407Sjm199354 		result = VS_STATUS_ERROR;
664*6407Sjm199354 	else
665*6407Sjm199354 		result = vscan_door_scan_file(door_req);
666*6407Sjm199354 	kmem_free(door_req, sizeof (vs_scan_req_t));
667*6407Sjm199354 	mutex_enter(&vscan_svc_mutex);
668*6407Sjm199354 
669*6407Sjm199354 	if (result != VS_STATUS_SCANNING) {
670*6407Sjm199354 		vscan_svc_nodes[idx].vsn_result = result;
671*6407Sjm199354 		vscan_svc_process_scan_result(idx);
672*6407Sjm199354 	} else { /* async response */
673*6407Sjm199354 		if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
674*6407Sjm199354 			req->vsr_state = VS_SVC_REQ_SCANNING;
675*6407Sjm199354 	}
676*6407Sjm199354 }
677*6407Sjm199354 
678*6407Sjm199354 
679*6407Sjm199354 /*
680*6407Sjm199354  * vscan_svc_populate_req
681*6407Sjm199354  *
682*6407Sjm199354  * Allocate a scan request to be sent to vscand, populating it
683*6407Sjm199354  * from the data in vscan_svc_nodes[idx].
684*6407Sjm199354  *
685*6407Sjm199354  * Returns: scan request object
6865440Sjm199354  */
687*6407Sjm199354 static vs_scan_req_t *
688*6407Sjm199354 vscan_svc_populate_req(int idx)
689*6407Sjm199354 {
690*6407Sjm199354 	vs_scan_req_t *scan_req;
691*6407Sjm199354 	vscan_req_t *req;
692*6407Sjm199354 	vscan_svc_node_t *node;
693*6407Sjm199354 
694*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
695*6407Sjm199354 
696*6407Sjm199354 	node = &vscan_svc_nodes[idx];
697*6407Sjm199354 	req = node->vsn_req;
698*6407Sjm199354 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
699*6407Sjm199354 
700*6407Sjm199354 	scan_req->vsr_idx = idx;
701*6407Sjm199354 	scan_req->vsr_seqnum = req->vsr_seqnum;
702*6407Sjm199354 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
703*6407Sjm199354 	scan_req->vsr_size = node->vsn_size;
704*6407Sjm199354 	scan_req->vsr_modified = node->vsn_modified;
705*6407Sjm199354 	scan_req->vsr_quarantined = node->vsn_quarantined;
706*6407Sjm199354 	scan_req->vsr_flags = 0;
707*6407Sjm199354 	(void) strncpy(scan_req->vsr_scanstamp,
708*6407Sjm199354 	    node->vsn_scanstamp, sizeof (vs_scanstamp_t));
709*6407Sjm199354 
710*6407Sjm199354 	return (scan_req);
711*6407Sjm199354 }
712*6407Sjm199354 
713*6407Sjm199354 
714*6407Sjm199354 /*
715*6407Sjm199354  * vscan_svc_scan_complete
716*6407Sjm199354  */
717*6407Sjm199354 static void
718*6407Sjm199354 vscan_svc_scan_complete(vscan_req_t *req)
7195440Sjm199354 {
720*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
721*6407Sjm199354 	ASSERT(req != NULL);
722*6407Sjm199354 
723*6407Sjm199354 	req->vsr_state = VS_SVC_REQ_COMPLETE;
724*6407Sjm199354 
725*6407Sjm199354 	if ((--req->vsr_refcnt) == 0)
726*6407Sjm199354 		vscan_svc_delete_req(req);
727*6407Sjm199354 	else
728*6407Sjm199354 		cv_broadcast(&(req->vsr_cv));
729*6407Sjm199354 }
730*6407Sjm199354 
731*6407Sjm199354 
732*6407Sjm199354 /*
733*6407Sjm199354  * vscan_svc_delete_req
734*6407Sjm199354  */
735*6407Sjm199354 static void
736*6407Sjm199354 vscan_svc_delete_req(vscan_req_t *req)
737*6407Sjm199354 {
738*6407Sjm199354 	int idx;
739*6407Sjm199354 
740*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
741*6407Sjm199354 	ASSERT(req != NULL);
742*6407Sjm199354 	ASSERT(req->vsr_refcnt == 0);
743*6407Sjm199354 	ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
744*6407Sjm199354 
745*6407Sjm199354 	if ((idx = req->vsr_idx) != 0)
746*6407Sjm199354 		vscan_svc_remove_req(idx);
747*6407Sjm199354 
748*6407Sjm199354 	vscan_svc_reql_remove(req);
749*6407Sjm199354 
750*6407Sjm199354 	cv_signal(&vscan_svc_reql_cv);
751*6407Sjm199354 }
752*6407Sjm199354 
753*6407Sjm199354 
754*6407Sjm199354 /*
755*6407Sjm199354  * vscan_svc_scan_result
756*6407Sjm199354  *
757*6407Sjm199354  * Invoked from vscan_drv.c on receipt of an ioctl containing
758*6407Sjm199354  * an async scan result (VS_DRV_IOCTL_RESULT)
759*6407Sjm199354  * If the vsr_seqnum in the response does not match that in the
760*6407Sjm199354  * vscan_svc_nodes entry the result is discarded.
761*6407Sjm199354  */
762*6407Sjm199354 void
763*6407Sjm199354 vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
764*6407Sjm199354 {
765*6407Sjm199354 	vscan_req_t *req;
766*6407Sjm199354 	vscan_svc_node_t *node;
7675440Sjm199354 
7685440Sjm199354 	mutex_enter(&vscan_svc_mutex);
7695440Sjm199354 
770*6407Sjm199354 	node = &vscan_svc_nodes[scan_rsp->vsr_idx];
771*6407Sjm199354 
772*6407Sjm199354 	if ((req = node->vsn_req) == NULL) {
773*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
774*6407Sjm199354 		return;
775*6407Sjm199354 	}
776*6407Sjm199354 
777*6407Sjm199354 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
778*6407Sjm199354 
779*6407Sjm199354 	if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
780*6407Sjm199354 		mutex_exit(&vscan_svc_mutex);
781*6407Sjm199354 		return;
782*6407Sjm199354 	}
783*6407Sjm199354 
784*6407Sjm199354 	node->vsn_result = scan_rsp->vsr_result;
785*6407Sjm199354 	(void) strncpy(node->vsn_scanstamp,
786*6407Sjm199354 	    scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
787*6407Sjm199354 
788*6407Sjm199354 	vscan_svc_process_scan_result(scan_rsp->vsr_idx);
789*6407Sjm199354 
790*6407Sjm199354 	if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
791*6407Sjm199354 		vscan_svc_scan_complete(node->vsn_req);
792*6407Sjm199354 	else
793*6407Sjm199354 		node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
7945440Sjm199354 
795*6407Sjm199354 	mutex_exit(&vscan_svc_mutex);
796*6407Sjm199354 }
797*6407Sjm199354 
7985931Sjm199354 
799*6407Sjm199354 /*
800*6407Sjm199354  * vscan_svc_scan_abort
801*6407Sjm199354  *
802*6407Sjm199354  * Abort in-progress scan requests.
803*6407Sjm199354  */
804*6407Sjm199354 void
805*6407Sjm199354 vscan_svc_scan_abort()
806*6407Sjm199354 {
807*6407Sjm199354 	int idx;
808*6407Sjm199354 	vscan_req_t *req;
809*6407Sjm199354 
810*6407Sjm199354 	mutex_enter(&vscan_svc_mutex);
811*6407Sjm199354 
812*6407Sjm199354 	for (idx = 1; idx <= vs_nodes_max; idx++) {
813*6407Sjm199354 		if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
814*6407Sjm199354 			continue;
815*6407Sjm199354 
816*6407Sjm199354 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
817*6407Sjm199354 
818*6407Sjm199354 		if (req->vsr_state == VS_SVC_REQ_SCANNING) {
819*6407Sjm199354 			DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
820*6407Sjm199354 			vscan_svc_process_scan_result(idx);
821*6407Sjm199354 			vscan_svc_scan_complete(req);
8225440Sjm199354 		}
8235440Sjm199354 	}
8245440Sjm199354 
8255440Sjm199354 	mutex_exit(&vscan_svc_mutex);
8265440Sjm199354 }
8275440Sjm199354 
8285931Sjm199354 
8295931Sjm199354 /*
8305931Sjm199354  * vscan_svc_process_scan_result
8315931Sjm199354  *
832*6407Sjm199354  * Sets vsn_access and updates file attributes based on vsn_result,
8335931Sjm199354  * as follows:
8345931Sjm199354  *
8355931Sjm199354  * VS_STATUS_INFECTED
8365931Sjm199354  *  deny access, set quarantine attribute, clear scanstamp
8375931Sjm199354  * VS_STATUS_CLEAN
8385931Sjm199354  *  allow access, set scanstamp,
8395931Sjm199354  *  if file not modified since scan initiated, clear modified attribute
8405931Sjm199354  * VS_STATUS_NO_SCAN
8415931Sjm199354  *  deny access if file quarantined, otherwise allow access
8425931Sjm199354  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
8435931Sjm199354  *  deny access if file quarantined, modified or no scanstamp
8445931Sjm199354  *  otherwise, allow access
8455931Sjm199354  */
8465931Sjm199354 static void
8475931Sjm199354 vscan_svc_process_scan_result(int idx)
8485931Sjm199354 {
8495931Sjm199354 	struct vattr attr;
8505931Sjm199354 	vnode_t *vp;
8515931Sjm199354 	timestruc_t *mtime;
852*6407Sjm199354 	vscan_svc_node_t *node;
8535931Sjm199354 
8545931Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
8555931Sjm199354 
856*6407Sjm199354 	node = &vscan_svc_nodes[idx];
8575931Sjm199354 
858*6407Sjm199354 	switch (node->vsn_result) {
8595931Sjm199354 	case VS_STATUS_INFECTED:
860*6407Sjm199354 		node->vsn_access = VS_ACCESS_DENY;
861*6407Sjm199354 		node->vsn_quarantined = 1;
862*6407Sjm199354 		node->vsn_scanstamp[0] = '\0';
8635931Sjm199354 		(void) vscan_svc_setattr(idx,
8645931Sjm199354 		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
865*6407Sjm199354 		break;
8665931Sjm199354 
8675931Sjm199354 	case VS_STATUS_CLEAN:
868*6407Sjm199354 		node->vsn_access = VS_ACCESS_ALLOW;
8695931Sjm199354 
8705931Sjm199354 		/* if mtime has changed, don't clear the modified attribute */
871*6407Sjm199354 		vp = node->vsn_req->vsr_vp;
872*6407Sjm199354 		mtime = &(node->vsn_mtime);
8735931Sjm199354 		attr.va_mask = AT_MTIME;
8745931Sjm199354 		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
8755931Sjm199354 		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
8765931Sjm199354 		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
877*6407Sjm199354 			DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
878*6407Sjm199354 			    node);
8795931Sjm199354 			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
880*6407Sjm199354 			break;
8815931Sjm199354 		}
8825931Sjm199354 
883*6407Sjm199354 		node->vsn_modified = 0;
8845931Sjm199354 		(void) vscan_svc_setattr(idx,
8855931Sjm199354 		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
886*6407Sjm199354 		break;
8875931Sjm199354 
8885931Sjm199354 	case VS_STATUS_NO_SCAN:
889*6407Sjm199354 		if (node->vsn_quarantined)
890*6407Sjm199354 			node->vsn_access = VS_ACCESS_DENY;
8915931Sjm199354 		else
892*6407Sjm199354 			node->vsn_access = VS_ACCESS_ALLOW;
893*6407Sjm199354 		break;
8945931Sjm199354 
8955931Sjm199354 	case VS_STATUS_ERROR:
8965931Sjm199354 	case VS_STATUS_UNDEFINED:
8975931Sjm199354 	default:
898*6407Sjm199354 		if ((node->vsn_quarantined) ||
899*6407Sjm199354 		    (node->vsn_modified) ||
900*6407Sjm199354 		    (node->vsn_scanstamp[0] == '\0'))
901*6407Sjm199354 			node->vsn_access = VS_ACCESS_DENY;
9025931Sjm199354 		else
903*6407Sjm199354 			node->vsn_access = VS_ACCESS_ALLOW;
904*6407Sjm199354 		break;
9055440Sjm199354 	}
9065440Sjm199354 
907*6407Sjm199354 	DTRACE_PROBE4(vscan__result,
908*6407Sjm199354 	    int, idx, int, node->vsn_req->vsr_seqnum,
909*6407Sjm199354 	    int, node->vsn_result, int, node->vsn_access);
9105440Sjm199354 }
9115440Sjm199354 
9125440Sjm199354 
9135440Sjm199354 /*
9145440Sjm199354  * vscan_svc_getattr
9155440Sjm199354  *
9165931Sjm199354  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
9175440Sjm199354  */
9185440Sjm199354 static int
9195440Sjm199354 vscan_svc_getattr(int idx)
9205440Sjm199354 {
9215440Sjm199354 	xvattr_t xvattr;
9225440Sjm199354 	xoptattr_t *xoap = NULL;
9235440Sjm199354 	vnode_t *vp;
924*6407Sjm199354 	vscan_svc_node_t *node;
9255440Sjm199354 
9265440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
9275440Sjm199354 
928*6407Sjm199354 	node = &vscan_svc_nodes[idx];
929*6407Sjm199354 	if ((vp = node->vsn_req->vsr_vp) == NULL)
9305440Sjm199354 		return (-1);
9315440Sjm199354 
9325440Sjm199354 	/* get the attributes */
9335440Sjm199354 	xva_init(&xvattr); /* sets AT_XVATTR */
9345440Sjm199354 
9355440Sjm199354 	xvattr.xva_vattr.va_mask |= AT_SIZE;
9365931Sjm199354 	xvattr.xva_vattr.va_mask |= AT_MTIME;
9375440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
9385440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
9395440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
9405440Sjm199354 
9415440Sjm199354 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
9425440Sjm199354 		return (-1);
9435440Sjm199354 
9445440Sjm199354 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
9455440Sjm199354 		cmn_err(CE_NOTE, "Virus scan request failed; "
9465440Sjm199354 		    "file system does not support virus scanning");
9475440Sjm199354 		return (-1);
9485440Sjm199354 	}
9495440Sjm199354 
950*6407Sjm199354 	node->vsn_size = xvattr.xva_vattr.va_size;
951*6407Sjm199354 	node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
952*6407Sjm199354 	node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
9535440Sjm199354 
9545440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
9555440Sjm199354 		return (-1);
956*6407Sjm199354 	node->vsn_modified = xoap->xoa_av_modified;
9575440Sjm199354 
9585440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
9595440Sjm199354 		return (-1);
960*6407Sjm199354 	node->vsn_quarantined = xoap->xoa_av_quarantined;
9615440Sjm199354 
9625440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
963*6407Sjm199354 		(void) memcpy(node->vsn_scanstamp,
9645440Sjm199354 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
9655440Sjm199354 	}
9665440Sjm199354 
967*6407Sjm199354 	DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
9685440Sjm199354 	return (0);
9695440Sjm199354 }
9705440Sjm199354 
9715440Sjm199354 
9725440Sjm199354 /*
9735440Sjm199354  * vscan_svc_setattr
9745440Sjm199354  *
9755440Sjm199354  * Set the vscan related system attributes.
9765440Sjm199354  */
9775440Sjm199354 static int
9785931Sjm199354 vscan_svc_setattr(int idx, int which)
9795440Sjm199354 {
9805440Sjm199354 	xvattr_t xvattr;
9815440Sjm199354 	xoptattr_t *xoap = NULL;
9825440Sjm199354 	vnode_t *vp;
9835440Sjm199354 	int len;
984*6407Sjm199354 	vscan_svc_node_t *node;
9855440Sjm199354 
9865440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
9875440Sjm199354 
988*6407Sjm199354 	node = &vscan_svc_nodes[idx];
989*6407Sjm199354 	if ((vp = node->vsn_req->vsr_vp) == NULL)
9905440Sjm199354 		return (-1);
9915440Sjm199354 
9925440Sjm199354 	/* update the attributes */
9935440Sjm199354 	xva_init(&xvattr); /* sets AT_XVATTR */
9945440Sjm199354 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
9955440Sjm199354 		return (-1);
9965440Sjm199354 
9975931Sjm199354 	if (which & XAT_AV_MODIFIED) {
9985931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
999*6407Sjm199354 		xoap->xoa_av_modified = node->vsn_modified;
10005931Sjm199354 	}
10015440Sjm199354 
10025931Sjm199354 	if (which & XAT_AV_QUARANTINED) {
10035931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
1004*6407Sjm199354 		xoap->xoa_av_quarantined = node->vsn_quarantined;
10055931Sjm199354 	}
10065440Sjm199354 
10075931Sjm199354 	if (which & XAT_AV_SCANSTAMP) {
10085931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
1009*6407Sjm199354 		len = strlen(node->vsn_scanstamp);
10105931Sjm199354 		(void) memcpy(xoap->xoa_av_scanstamp,
1011*6407Sjm199354 		    node->vsn_scanstamp, len);
10125931Sjm199354 	}
10135440Sjm199354 
10145440Sjm199354 	/* if access is denied, set mtime to invalidate client cache */
1015*6407Sjm199354 	if (node->vsn_access != VS_ACCESS_ALLOW) {
10165440Sjm199354 		xvattr.xva_vattr.va_mask |= AT_MTIME;
10175440Sjm199354 		gethrestime(&xvattr.xva_vattr.va_mtime);
10185440Sjm199354 	}
10195440Sjm199354 
10205440Sjm199354 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
10215440Sjm199354 		return (-1);
10225440Sjm199354 
10235931Sjm199354 	DTRACE_PROBE2(vscan__setattr,
1024*6407Sjm199354 	    vscan_svc_node_t *, node, int, which);
10255931Sjm199354 
10265440Sjm199354 	return (0);
10275440Sjm199354 }
10285440Sjm199354 
10295440Sjm199354 
10305440Sjm199354 /*
10315440Sjm199354  * vscan_svc_configure
10325440Sjm199354  *
10335440Sjm199354  * store configuration in vscan_svc_config
10345440Sjm199354  * set up vscan_svc_types array of pointers into
10355440Sjm199354  * vscan_svc_config.vsc_types for efficient searching
10365440Sjm199354  */
10375440Sjm199354 int
10385440Sjm199354 vscan_svc_configure(vs_config_t *conf)
10395440Sjm199354 {
10405440Sjm199354 	int count = 0;
10415440Sjm199354 	char *p, *beg, *end;
10425440Sjm199354 
10435440Sjm199354 	mutex_enter(&vscan_svc_cfg_mutex);
10445440Sjm199354 
10455440Sjm199354 	vscan_svc_config = *conf;
10465440Sjm199354 
10475440Sjm199354 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
10485440Sjm199354 
10495440Sjm199354 	beg = vscan_svc_config.vsc_types;
10505440Sjm199354 	end = beg + vscan_svc_config.vsc_types_len;
10515440Sjm199354 
10525440Sjm199354 	for (p = beg; p < end; p += strlen(p) + 1) {
10535440Sjm199354 		if (count >= VS_TYPES_MAX) {
10545440Sjm199354 			mutex_exit(&vscan_svc_mutex);
10555440Sjm199354 			return (-1);
10565440Sjm199354 		}
10575440Sjm199354 
10585440Sjm199354 		vscan_svc_types[count] = p;
10595440Sjm199354 		++count;
10605440Sjm199354 	}
10615440Sjm199354 
10625440Sjm199354 	mutex_exit(&vscan_svc_cfg_mutex);
10635440Sjm199354 	return (0);
10645440Sjm199354 }
10655440Sjm199354 
10665440Sjm199354 
10675440Sjm199354 /*
10685440Sjm199354  * vscan_svc_exempt_file
10695440Sjm199354  *
10705440Sjm199354  * check if a file's size or type exempts it from virus scanning
10715440Sjm199354  *
10725440Sjm199354  * If the file is exempt from virus scanning, allow will be set
10735440Sjm199354  * to define whether files access should be allowed (B_TRUE) or
10745440Sjm199354  * denied (B_FALSE)
10755440Sjm199354  *
10765440Sjm199354  * Returns: 1 exempt
10775440Sjm199354  *          0 scan required
10785440Sjm199354  */
10795440Sjm199354 static int
10805440Sjm199354 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
10815440Sjm199354 {
10825440Sjm199354 	struct vattr attr;
10835440Sjm199354 
10845440Sjm199354 	ASSERT(vp != NULL);
10855440Sjm199354 	ASSERT(vp->v_path != NULL);
10865440Sjm199354 
10875440Sjm199354 	attr.va_mask = AT_SIZE;
10885440Sjm199354 
10895440Sjm199354 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
10905440Sjm199354 		*allow = B_FALSE;
10915440Sjm199354 		return (0);
10925440Sjm199354 	}
10935440Sjm199354 
10945440Sjm199354 	mutex_enter(&vscan_svc_cfg_mutex);
10955440Sjm199354 
10965440Sjm199354 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
10975440Sjm199354 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
10985440Sjm199354 		    vp->v_path, int, *allow);
10995440Sjm199354 
11005440Sjm199354 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
11015440Sjm199354 		mutex_exit(&vscan_svc_cfg_mutex);
11025440Sjm199354 		return (1);
11035440Sjm199354 	}
11045440Sjm199354 
11055440Sjm199354 	if (vscan_svc_exempt_filetype(vp->v_path)) {
11065440Sjm199354 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
11075440Sjm199354 		*allow = B_TRUE;
11085440Sjm199354 		mutex_exit(&vscan_svc_cfg_mutex);
11095440Sjm199354 		return (1);
11105440Sjm199354 	}
11115440Sjm199354 
11125440Sjm199354 	mutex_exit(&vscan_svc_cfg_mutex);
11135440Sjm199354 	return (0);
11145440Sjm199354 }
11155440Sjm199354 
11165440Sjm199354 
11175440Sjm199354 /*
11185440Sjm199354  * vscan_svc_exempt_filetype
11195440Sjm199354  *
11205440Sjm199354  * Each entry in vscan_svc_types includes a rule indicator (+,-)
11215440Sjm199354  * followed by the match string for file types to which the rule
11225440Sjm199354  * applies. Look for first match of file type in vscan_svc_types
11235440Sjm199354  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
11245440Sjm199354  * if the indicator is '+'.
11255440Sjm199354  * If vscan_svc_match_ext fails, or no match is found, return 0
11265440Sjm199354  * (not exempt)
11275440Sjm199354  *
11285440Sjm199354  * Returns 1: exempt, 0: not exempt
11295440Sjm199354  */
11305440Sjm199354 static int
11315440Sjm199354 vscan_svc_exempt_filetype(char *filepath)
11325440Sjm199354 {
11335440Sjm199354 	int i, rc, exempt = 0;
11345440Sjm199354 	char *filename, *ext;
11355440Sjm199354 
11365440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
11375440Sjm199354 
11385440Sjm199354 	if ((filename = strrchr(filepath, '/')) == 0)
11395440Sjm199354 		filename = filepath;
11405440Sjm199354 	else
11415440Sjm199354 		filename++;
11425440Sjm199354 
11435440Sjm199354 	if ((ext = strrchr(filename, '.')) == NULL)
11445440Sjm199354 		ext = "";
11455440Sjm199354 	else
11465440Sjm199354 		ext++;
11475440Sjm199354 
11485440Sjm199354 	for (i = 0; i < VS_TYPES_MAX; i ++) {
11495440Sjm199354 		if (vscan_svc_types[i] == 0)
11505440Sjm199354 			break;
11515440Sjm199354 
11525440Sjm199354 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
11535440Sjm199354 		if (rc == -1)
11545440Sjm199354 			break;
11555440Sjm199354 		if (rc > 0) {
11565440Sjm199354 			DTRACE_PROBE2(vscan__type__match, char *, ext,
11575440Sjm199354 			    char *, vscan_svc_types[i]);
11585440Sjm199354 			exempt = (vscan_svc_types[i][0] == '-');
11595440Sjm199354 			break;
11605440Sjm199354 		}
11615440Sjm199354 	}
11625440Sjm199354 
11635440Sjm199354 	return (exempt);
11645440Sjm199354 }
11655440Sjm199354 
11665440Sjm199354 
11675440Sjm199354 /*
11685440Sjm199354  *  vscan_svc_match_ext
11695440Sjm199354  *
11705440Sjm199354  * Performs a case-insensitive match for two strings.  The first string
11715440Sjm199354  * argument can contain the wildcard characters '?' and '*'
11725440Sjm199354  *
11735440Sjm199354  * Returns: 0 no match
11745440Sjm199354  *          1 match
11755440Sjm199354  *         -1 recursion error
11765440Sjm199354  */
11775440Sjm199354 static int
11785440Sjm199354 vscan_svc_match_ext(char *patn, char *str, int depth)
11795440Sjm199354 {
11805440Sjm199354 	int c1, c2;
11815440Sjm199354 	if (depth > VS_EXT_RECURSE_DEPTH)
11825440Sjm199354 		return (-1);
11835440Sjm199354 
11845440Sjm199354 	for (;;) {
11855440Sjm199354 		switch (*patn) {
11865440Sjm199354 		case 0:
11875440Sjm199354 			return (*str == 0);
11885440Sjm199354 
11895440Sjm199354 		case '?':
11905440Sjm199354 			if (*str != 0) {
11915440Sjm199354 				str++;
11925440Sjm199354 				patn++;
11935440Sjm199354 				continue;
11945440Sjm199354 			}
11955440Sjm199354 			return (0);
11965440Sjm199354 
11975440Sjm199354 		case '*':
11985440Sjm199354 			patn++;
11995440Sjm199354 			if (*patn == 0)
12005440Sjm199354 				return (1);
12015440Sjm199354 
12025440Sjm199354 			while (*str) {
12035440Sjm199354 				if (vscan_svc_match_ext(patn, str, depth + 1))
12045440Sjm199354 					return (1);
12055440Sjm199354 				str++;
12065440Sjm199354 			}
12075440Sjm199354 			return (0);
12085440Sjm199354 
12095440Sjm199354 		default:
12105440Sjm199354 			if (*str != *patn) {
12115440Sjm199354 				c1 = *str;
12125440Sjm199354 				c2 = *patn;
12135440Sjm199354 
12145440Sjm199354 				c1 = tolower(c1);
12155440Sjm199354 				c2 = tolower(c2);
12165440Sjm199354 				if (c1 != c2)
12175440Sjm199354 					return (0);
12185440Sjm199354 			}
12195440Sjm199354 			str++;
12205440Sjm199354 			patn++;
12215440Sjm199354 			continue;
12225440Sjm199354 		}
12235440Sjm199354 	}
12245440Sjm199354 	/* NOT REACHED */
12255440Sjm199354 }
1226*6407Sjm199354 
1227*6407Sjm199354 
1228*6407Sjm199354 /*
1229*6407Sjm199354  * vscan_svc_insert_req
1230*6407Sjm199354  *
1231*6407Sjm199354  * Insert request in next available available slot in vscan_svc_nodes
1232*6407Sjm199354  *
1233*6407Sjm199354  * Returns: idx of slot, or -1 if no slot available
1234*6407Sjm199354  */
1235*6407Sjm199354 static int
1236*6407Sjm199354 vscan_svc_insert_req(vscan_req_t *req)
1237*6407Sjm199354 {
1238*6407Sjm199354 	int idx;
1239*6407Sjm199354 	vscan_svc_node_t *node;
1240*6407Sjm199354 
1241*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1242*6407Sjm199354 
1243*6407Sjm199354 	if (vscan_svc_counts.vsc_node == vs_nodes_max)
1244*6407Sjm199354 		return (-1);
1245*6407Sjm199354 
1246*6407Sjm199354 	for (idx = 1; idx <= vs_nodes_max; idx++) {
1247*6407Sjm199354 		if (vscan_svc_nodes[idx].vsn_req == NULL) {
1248*6407Sjm199354 			req->vsr_idx = idx;
1249*6407Sjm199354 
1250*6407Sjm199354 			node = &vscan_svc_nodes[idx];
1251*6407Sjm199354 			(void) memset(node, 0, sizeof (vscan_svc_node_t));
1252*6407Sjm199354 			node->vsn_req = req;
1253*6407Sjm199354 			node->vsn_modified = 1;
1254*6407Sjm199354 			node->vsn_result = VS_STATUS_UNDEFINED;
1255*6407Sjm199354 			node->vsn_access = VS_ACCESS_UNDEFINED;
1256*6407Sjm199354 
1257*6407Sjm199354 			++(vscan_svc_counts.vsc_node);
1258*6407Sjm199354 			return (idx);
1259*6407Sjm199354 		}
1260*6407Sjm199354 	}
1261*6407Sjm199354 
1262*6407Sjm199354 	return (-1);
1263*6407Sjm199354 }
1264*6407Sjm199354 
1265*6407Sjm199354 
1266*6407Sjm199354 /*
1267*6407Sjm199354  * vscan_svc_remove_req
1268*6407Sjm199354  */
1269*6407Sjm199354 static void
1270*6407Sjm199354 vscan_svc_remove_req(int idx)
1271*6407Sjm199354 {
1272*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1273*6407Sjm199354 
1274*6407Sjm199354 	if (idx != 0) {
1275*6407Sjm199354 		(void) memset(&vscan_svc_nodes[idx], 0,
1276*6407Sjm199354 		    sizeof (vscan_svc_node_t));
1277*6407Sjm199354 		--(vscan_svc_counts.vsc_node);
1278*6407Sjm199354 	}
1279*6407Sjm199354 }
1280*6407Sjm199354 
1281*6407Sjm199354 
1282*6407Sjm199354 /*
1283*6407Sjm199354  * vscan_svc_reql_find
1284*6407Sjm199354  */
1285*6407Sjm199354 static vscan_req_t *
1286*6407Sjm199354 vscan_svc_reql_find(vnode_t *vp)
1287*6407Sjm199354 {
1288*6407Sjm199354 	vscan_req_t *req;
1289*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1290*6407Sjm199354 
1291*6407Sjm199354 	req = list_head(&vscan_svc_reql);
1292*6407Sjm199354 
1293*6407Sjm199354 	while (req != NULL) {
1294*6407Sjm199354 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1295*6407Sjm199354 		if ((req->vsr_vp == vp) &&
1296*6407Sjm199354 		    (req->vsr_state != VS_SVC_REQ_COMPLETE))
1297*6407Sjm199354 			break;
1298*6407Sjm199354 
1299*6407Sjm199354 		req = list_next(&vscan_svc_reql, req);
1300*6407Sjm199354 	}
1301*6407Sjm199354 
1302*6407Sjm199354 	return (req);
1303*6407Sjm199354 }
1304*6407Sjm199354 
1305*6407Sjm199354 
1306*6407Sjm199354 /*
1307*6407Sjm199354  * vscan_svc_reql_insert
1308*6407Sjm199354  */
1309*6407Sjm199354 static vscan_req_t *
1310*6407Sjm199354 vscan_svc_reql_insert(vnode_t *vp)
1311*6407Sjm199354 {
1312*6407Sjm199354 	vscan_req_t *req;
1313*6407Sjm199354 
1314*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1315*6407Sjm199354 
1316*6407Sjm199354 	/* if request already in list then return it */
1317*6407Sjm199354 	if ((req = vscan_svc_reql_find(vp)) != NULL)
1318*6407Sjm199354 		return (req);
1319*6407Sjm199354 
1320*6407Sjm199354 	/* if list is full return NULL */
1321*6407Sjm199354 	if (vscan_svc_counts.vsc_reql == vs_reqs_max)
1322*6407Sjm199354 		return (NULL);
1323*6407Sjm199354 
1324*6407Sjm199354 	/* create a new request and insert into list */
1325*6407Sjm199354 	VN_HOLD(vp);
1326*6407Sjm199354 
1327*6407Sjm199354 	req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
1328*6407Sjm199354 
1329*6407Sjm199354 	req->vsr_magic = VS_REQ_MAGIC;
1330*6407Sjm199354 	if (vscan_svc_seqnum == UINT32_MAX)
1331*6407Sjm199354 		vscan_svc_seqnum = 0;
1332*6407Sjm199354 	req->vsr_seqnum = ++vscan_svc_seqnum;
1333*6407Sjm199354 	req->vsr_vp = vp;
1334*6407Sjm199354 	req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
1335*6407Sjm199354 	req->vsr_state = VS_SVC_REQ_INIT;
1336*6407Sjm199354 	cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
1337*6407Sjm199354 
1338*6407Sjm199354 	list_insert_tail(&vscan_svc_reql, req);
1339*6407Sjm199354 	if (vscan_svc_reql_next == NULL)
1340*6407Sjm199354 		vscan_svc_reql_next = req;
1341*6407Sjm199354 
1342*6407Sjm199354 	++(vscan_svc_counts.vsc_reql);
1343*6407Sjm199354 
1344*6407Sjm199354 	/* wake reql handler thread */
1345*6407Sjm199354 	cv_signal(&vscan_svc_reql_cv);
1346*6407Sjm199354 
1347*6407Sjm199354 	return (req);
1348*6407Sjm199354 }
1349*6407Sjm199354 
1350*6407Sjm199354 
1351*6407Sjm199354 /*
1352*6407Sjm199354  * vscan_svc_reql_remove
1353*6407Sjm199354  */
1354*6407Sjm199354 static void
1355*6407Sjm199354 vscan_svc_reql_remove(vscan_req_t *req)
1356*6407Sjm199354 {
1357*6407Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1358*6407Sjm199354 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1359*6407Sjm199354 
1360*6407Sjm199354 	if (vscan_svc_reql_next == req)
1361*6407Sjm199354 		vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
1362*6407Sjm199354 
1363*6407Sjm199354 	list_remove(&vscan_svc_reql, req);
1364*6407Sjm199354 	cv_destroy(&(req->vsr_cv));
1365*6407Sjm199354 	VN_RELE(req->vsr_vp);
1366*6407Sjm199354 
1367*6407Sjm199354 	kmem_free(req, sizeof (vscan_req_t));
1368*6407Sjm199354 	--(vscan_svc_counts.vsc_reql);
1369*6407Sjm199354 }
1370