xref: /illumos-gate/usr/src/uts/common/io/vscan/vscan_svc.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/stat.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/time.h>
33 #include <sys/varargs.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/cmn_err.h>
37 #include <sys/vnode.h>
38 #include <fs/fs_subr.h>
39 #include <sys/types.h>
40 #include <sys/file.h>
41 #include <sys/disp.h>
42 #include <sys/sdt.h>
43 #include <sys/cred.h>
44 #include <sys/list.h>
45 #include <sys/vscan.h>
46 
47 #define	VS_REQ_MAGIC		0x52515354 /* 'RQST' */
48 
49 #define	VS_REQS_DEFAULT		20000	/* pending scan requests - reql */
50 #define	VS_NODES_DEFAULT	128	/* concurrent file scans */
51 #define	VS_WORKERS_DEFAULT	32	/* worker threads */
52 #define	VS_SCANWAIT_DEFAULT	15*60	/* seconds to wait for scan result */
53 #define	VS_REQL_HANDLER_TIMEOUT	30
54 #define	VS_EXT_RECURSE_DEPTH	8
55 
56 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
57 #define	VS_ACCESS_UNDEFINED	0
58 #define	VS_ACCESS_ALLOW		1	/* return 0 */
59 #define	VS_ACCESS_DENY		2	/* return EACCES */
60 
61 #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
62 #define	offsetof(s, m)	(size_t)(&(((s *)0)->m))
63 
64 /* global variables - tunable via /etc/system */
65 uint32_t vs_reqs_max = VS_REQS_DEFAULT;	/* max scan requests */
66 uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
67 uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
68 uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
69 
70 
71 /*
72  * vscan_svc_state
73  *
74  *   +-----------------+
75  *   | VS_SVC_UNCONFIG |
76  *   +-----------------+
77  *      |           ^
78  *      | svc_init  | svc_fini
79  *      v           |
80  *   +-----------------+
81  *   | VS_SVC_IDLE     |<----|
82  *   +-----------------+	 |
83  *      |                    |
84  *      | svc_enable         |
85  *      |<----------------|  |
86  *      v                 |  |
87  *   +-----------------+  |  |
88  *   | VS_SVC_ENABLED  |--|  |
89  *   +-----------------+     |
90  *      |                    |
91  *      | svc_disable        | handler thread exit,
92  *      v                    | all requests complete
93  *   +-----------------+	 |
94  *   | VS_SVC_DISABLED |-----|
95  *   +-----------------+
96  *
97  * svc_enable may occur when we are already in the ENABLED
98  * state if vscand has exited without clean shutdown and
99  * then reconnected within the delayed disable time period
100  * (vs_reconnect_timeout) - see vscan_drv
101  */
102 
103 typedef enum {
104 	VS_SVC_UNCONFIG,
105 	VS_SVC_IDLE,
106 	VS_SVC_ENABLED, /* service enabled and registered */
107 	VS_SVC_DISABLED /* service disabled and nunregistered */
108 } vscan_svc_state_t;
109 static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
110 
111 
112 /*
113  * vscan_svc_req_state
114  *
115  * When a scan request is received from the file system it is
116  * identified in or inserted into the vscan_svc_reql (INIT).
117  * If the request is asynchronous 0 is then returned to the caller.
118  * If the request is synchronous the req's refcnt is incremented
119  * and the caller waits for the request to complete.
120  * The refcnt is also incremented when the request is inserted
121  * in vscan_svc_nodes, and decremented on scan_complete.
122  *
123  * vscan_svc_handler processes requests from the request list,
124  * inserting them into vscan_svc_nodes and the task queue (QUEUED).
125  * When the task queue call back (vscan_svc_do_scan) is invoked
126  * the request transitions to IN_PROGRESS state. If the request
127  * is sucessfully sent to vscand (door_call) and the door response
128  * is SCANNING then the scan result will be received asynchronously.
129  * Although unusual, it is possible that the async response is
130  * received before the door call returns (hence the ASYNC_COMPLETE
131  * state).
132  * When the result has been determined / received,
133  * vscan_svc_scan_complete is invoked to transition the request to
134  * COMPLETE state, decrement refcnt and signal all waiting callers.
135  * When the last waiting caller has processed the result (refcnt == 0)
136  * the request is removed from vscan_svc_reql and vscan_svc_nodes
137  * and deleted.
138  *
139  *      |                                                     ^
140  *      | reql_insert                                         | refcnt == 0
141  *      v                                                     | (delete)
142  *   +------------------------+	                  +---------------------+
143  *   | VS_SVC_REQ_INIT        | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
144  *   +------------------------+	                  +---------------------+
145  *      |                                                     ^
146  *      | insert_req, tq_dispatch                             |
147  *      v                                                     |
148  *   +------------------------+	                              |
149  *   | VS_SVC_REQ_QUEUED      |                           scan_complete
150  *   +------------------------+	                              |
151  *      |                                                     |
152  *      | tq_callback (do_scan)                               |
153  *      |                                                     |
154  *      v                        scan not req'd, error,       |
155  *   +------------------------+  or door_result != SCANNING   |
156  *   | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
157  *   +------------------------+	                              |
158  *       |         |                                          |
159  *       |         | door_result == SCANNING                  |
160  *       |         v                                          |
161  *       |     +---------------------------+	async result  |
162  *       |     | VS_SVC_REQ_SCANNING       |-------->---------|
163  *       |     +---------------------------+	              |
164  *       |                                                    |
165  *       | async result                                       |
166  *       v                                                    |
167  *    +---------------------------+	 door_result = SCANNING   |
168  *    | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
169  *    +---------------------------+
170  */
171 typedef enum {
172 	VS_SVC_REQ_INIT,
173 	VS_SVC_REQ_QUEUED,
174 	VS_SVC_REQ_IN_PROGRESS,
175 	VS_SVC_REQ_SCANNING,
176 	VS_SVC_REQ_ASYNC_COMPLETE,
177 	VS_SVC_REQ_COMPLETE
178 } vscan_svc_req_state_t;
179 
180 
181 /*
182  * vscan_svc_reql - the list of pending and in-progress scan requests
183  */
184 typedef struct vscan_req {
185 	uint32_t vsr_magic;	/* VS_REQ_MAGIC */
186 	list_node_t vsr_lnode;
187 	vnode_t *vsr_vp;
188 	uint32_t vsr_idx;	/* vscan_svc_nodes index */
189 	uint32_t vsr_seqnum;	/* unigue request id */
190 	uint32_t vsr_refcnt;
191 	kcondvar_t vsr_cv;
192 	vscan_svc_req_state_t vsr_state;
193 } vscan_req_t;
194 
195 static list_t vscan_svc_reql;
196 
197 
198 /*
199  * vscan_svc_nodes - table of files being scanned
200  *
201  * The index into this table is passed in the door call to
202  * vscand. vscand uses the idx to determine which minor node
203  * to open to read the file data. Within the kernel driver
204  * the minor device number can thus be used to identify the
205  * table index to get the appropriate vnode.
206  *
207  * Instance 0 is reserved for the daemon/driver control
208  * interface: enable/configure/disable
209  */
210 typedef struct vscan_svc_node {
211 	vscan_req_t *vsn_req;
212 	uint8_t vsn_quarantined;
213 	uint8_t vsn_modified;
214 	uint64_t vsn_size;
215 	timestruc_t vsn_mtime;
216 	vs_scanstamp_t vsn_scanstamp;
217 	uint32_t vsn_result;
218 	uint32_t vsn_access;
219 } vscan_svc_node_t;
220 
221 static vscan_svc_node_t *vscan_svc_nodes;
222 static int vscan_svc_nodes_sz;
223 
224 
225 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
226 static taskq_t *vscan_svc_taskq = NULL;
227 
228 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
229 typedef struct {
230 	uint32_t vsc_reql;
231 	uint32_t vsc_node;
232 	uint32_t vsc_tq;
233 } vscan_svc_counts_t;
234 static vscan_svc_counts_t vscan_svc_counts;
235 
236 /*
237  * vscan_svc_mutex protects the data pertaining to scan requests:
238  * request list - vscan_svc_reql
239  * node table - vscan_svc_nodes
240  */
241 static kmutex_t vscan_svc_mutex;
242 
243 /* unique request id for vscand request/response correlation */
244 static uint32_t vscan_svc_seqnum = 0;
245 
246 /*
247  * vscan_svc_cfg_mutex protects the configuration data:
248  * vscan_svc_config, vscan_svc_types
249  */
250 static kmutex_t vscan_svc_cfg_mutex;
251 
252 /* configuration data - for virus scan exemption */
253 static vs_config_t vscan_svc_config;
254 static char *vscan_svc_types[VS_TYPES_MAX];
255 
256 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
257 static kthread_t *vscan_svc_reql_thread;
258 static kcondvar_t vscan_svc_reql_cv;
259 static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
260 
261 /* local functions */
262 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
263 static void vscan_svc_taskq_callback(void *);
264 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
265 static int vscan_svc_exempt_filetype(char *);
266 static int vscan_svc_match_ext(char *, char *, int);
267 static void vscan_svc_do_scan(vscan_req_t *);
268 static vs_scan_req_t *vscan_svc_populate_req(int);
269 static void vscan_svc_process_scan_result(int);
270 static void vscan_svc_scan_complete(vscan_req_t *);
271 static void vscan_svc_delete_req(vscan_req_t *);
272 static int vscan_svc_insert_req(vscan_req_t *);
273 static void vscan_svc_remove_req(int);
274 static vscan_req_t *vscan_svc_reql_find(vnode_t *);
275 static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
276 static void vscan_svc_reql_remove(vscan_req_t *);
277 
278 static int vscan_svc_getattr(int);
279 static int vscan_svc_setattr(int, int);
280 
281 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
282 static void vscan_svc_reql_handler(void);
283 
284 
285 /*
286  * vscan_svc_init
287  */
288 int
289 vscan_svc_init()
290 {
291 	if (vscan_svc_state != VS_SVC_UNCONFIG) {
292 		DTRACE_PROBE1(vscan__svc__state__violation,
293 		    int, vscan_svc_state);
294 		return (-1);
295 	}
296 
297 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
298 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
299 	cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
300 
301 	vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
302 	vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
303 
304 	vscan_svc_counts.vsc_reql = 0;
305 	vscan_svc_counts.vsc_node = 0;
306 	vscan_svc_counts.vsc_tq = 0;
307 
308 	vscan_svc_state = VS_SVC_IDLE;
309 
310 	return (0);
311 }
312 
313 
314 /*
315  * vscan_svc_fini
316  */
317 void
318 vscan_svc_fini()
319 {
320 	if (vscan_svc_state != VS_SVC_IDLE) {
321 		DTRACE_PROBE1(vscan__svc__state__violation,
322 		    int, vscan_svc_state);
323 		return;
324 	}
325 
326 	kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
327 
328 	cv_destroy(&vscan_svc_reql_cv);
329 	mutex_destroy(&vscan_svc_mutex);
330 	mutex_destroy(&vscan_svc_cfg_mutex);
331 	vscan_svc_state = VS_SVC_UNCONFIG;
332 }
333 
334 
335 /*
336  * vscan_svc_enable
337  */
338 int
339 vscan_svc_enable(void)
340 {
341 	mutex_enter(&vscan_svc_mutex);
342 
343 	switch (vscan_svc_state) {
344 	case VS_SVC_ENABLED:
345 		/*
346 		 * it's possible (and okay) for vscan_svc_enable to be
347 		 * called when already enabled if vscand reconnects
348 		 * during a delayed disable
349 		 */
350 		break;
351 	case VS_SVC_IDLE:
352 		list_create(&vscan_svc_reql, sizeof (vscan_req_t),
353 		    offsetof(vscan_req_t, vsr_lnode));
354 		vscan_svc_reql_next = list_head(&vscan_svc_reql);
355 
356 		vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
357 		    MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
358 		ASSERT(vscan_svc_taskq != NULL);
359 
360 		vscan_svc_reql_thread = thread_create(NULL, 0,
361 		    vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
362 		ASSERT(vscan_svc_reql_thread != NULL);
363 
364 		/* ready to start processing requests */
365 		vscan_svc_state = VS_SVC_ENABLED;
366 		fs_vscan_register(vscan_svc_scan_file);
367 		break;
368 	default:
369 		DTRACE_PROBE1(vscan__svc__state__violation,
370 		    int, vscan_svc_state);
371 		return (-1);
372 	}
373 
374 	mutex_exit(&vscan_svc_mutex);
375 	return (0);
376 }
377 
378 
379 /*
380  * vscan_svc_disable
381  *
382  * Resources allocated during vscan_svc_enable are free'd by
383  * the handler thread immediately prior to exiting
384  */
385 void
386 vscan_svc_disable(void)
387 {
388 	mutex_enter(&vscan_svc_mutex);
389 
390 	switch (vscan_svc_state) {
391 	case VS_SVC_ENABLED:
392 		fs_vscan_register(NULL);
393 		vscan_svc_state = VS_SVC_DISABLED;
394 		cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
395 		break;
396 	default:
397 		DTRACE_PROBE1(vscan__svc__state__violation, int,
398 		    vscan_svc_state);
399 	}
400 
401 	mutex_exit(&vscan_svc_mutex);
402 }
403 
404 
405 /*
406  * vscan_svc_in_use
407  */
408 boolean_t
409 vscan_svc_in_use()
410 {
411 	boolean_t in_use;
412 
413 	mutex_enter(&vscan_svc_mutex);
414 
415 	switch (vscan_svc_state) {
416 	case VS_SVC_IDLE:
417 	case VS_SVC_UNCONFIG:
418 		in_use = B_FALSE;
419 		break;
420 	default:
421 		in_use = B_TRUE;
422 		break;
423 	}
424 
425 	mutex_exit(&vscan_svc_mutex);
426 	return (in_use);
427 }
428 
429 
430 /*
431  * vscan_svc_get_vnode
432  *
433  * Get the file vnode indexed by idx.
434  */
435 vnode_t *
436 vscan_svc_get_vnode(int idx)
437 {
438 	vnode_t *vp = NULL;
439 
440 	ASSERT(idx > 0);
441 	ASSERT(idx <= vs_nodes_max);
442 
443 	mutex_enter(&vscan_svc_mutex);
444 	if (vscan_svc_nodes[idx].vsn_req)
445 		vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
446 	mutex_exit(&vscan_svc_mutex);
447 
448 	return (vp);
449 }
450 
451 
452 /*
453  * vscan_svc_scan_file
454  *
455  * This function is the entry point for the file system to
456  * request that a file be virus scanned.
457  */
458 int
459 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
460 {
461 	int access;
462 	vscan_req_t *req;
463 	boolean_t allow;
464 	clock_t timeout, time_left;
465 
466 	if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
467 		return (0);
468 
469 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
470 
471 	/* check if size or type exempts file from scanning */
472 	if (vscan_svc_exempt_file(vp, &allow)) {
473 		if ((allow == B_TRUE) || (async != 0))
474 			return (0);
475 
476 		return (EACCES);
477 	}
478 
479 	mutex_enter(&vscan_svc_mutex);
480 
481 	if (vscan_svc_state != VS_SVC_ENABLED) {
482 		DTRACE_PROBE1(vscan__svc__state__violation,
483 		    int, vscan_svc_state);
484 		mutex_exit(&vscan_svc_mutex);
485 		return (0);
486 	}
487 
488 	/* insert (or find) request in list */
489 	if ((req = vscan_svc_reql_insert(vp)) == NULL) {
490 		mutex_exit(&vscan_svc_mutex);
491 		cmn_err(CE_WARN, "Virus scan request list full");
492 		return ((async != 0) ? 0 : EACCES);
493 	}
494 
495 	/* asynchronous request: return 0 */
496 	if (async) {
497 		mutex_exit(&vscan_svc_mutex);
498 		return (0);
499 	}
500 
501 	/* synchronous scan request: wait for result */
502 	++(req->vsr_refcnt);
503 	time_left = SEC_TO_TICK(vs_scan_wait);
504 	while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
505 		timeout = lbolt + time_left;
506 		time_left = cv_timedwait_sig(&(req->vsr_cv),
507 		    &vscan_svc_mutex, timeout);
508 	}
509 
510 	if (time_left == -1) {
511 		cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
512 		    vp->v_path, req->vsr_seqnum);
513 		DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
514 	}
515 
516 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
517 	if (vscan_svc_state == VS_SVC_DISABLED)
518 		access = VS_ACCESS_ALLOW;
519 	else if (req->vsr_idx == 0)
520 		access = VS_ACCESS_DENY;
521 	else
522 		access = vscan_svc_nodes[req->vsr_idx].vsn_access;
523 
524 	if ((--req->vsr_refcnt) == 0)
525 		vscan_svc_delete_req(req);
526 
527 	mutex_exit(&vscan_svc_mutex);
528 	return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
529 }
530 
531 
532 /*
533  * vscan_svc_reql_handler
534  *
535  * inserts scan requests (from vscan_svc_reql) into
536  * vscan_svc_nodes and vscan_svc_taskq
537  */
538 static void
539 vscan_svc_reql_handler(void)
540 {
541 	vscan_req_t *req, *next;
542 
543 	for (;;) {
544 		mutex_enter(&vscan_svc_mutex);
545 
546 		if ((vscan_svc_state == VS_SVC_DISABLED) &&
547 		    (vscan_svc_counts.vsc_reql == 0)) {
548 			/* free resources allocated durining enable */
549 			taskq_destroy(vscan_svc_taskq);
550 			vscan_svc_taskq = NULL;
551 			list_destroy(&vscan_svc_reql);
552 			vscan_svc_state = VS_SVC_IDLE;
553 			mutex_exit(&vscan_svc_mutex);
554 			return;
555 		}
556 
557 		/*
558 		 * If disabled, scan_complete any pending requests.
559 		 * Otherwise insert pending requests into vscan_svc_nodes
560 		 * and vscan_svc_taskq. If no slots are available in
561 		 * vscan_svc_nodes break loop and wait for one
562 		 */
563 		req = vscan_svc_reql_next;
564 
565 		while (req != NULL) {
566 			ASSERT(req->vsr_magic == VS_REQ_MAGIC);
567 			next = list_next(&vscan_svc_reql, req);
568 
569 			if (vscan_svc_state == VS_SVC_DISABLED) {
570 				vscan_svc_scan_complete(req);
571 			} else {
572 				/* insert request into vscan_svc_nodes */
573 				if (vscan_svc_insert_req(req) == -1)
574 					break;
575 
576 				/* add the scan request into the taskq */
577 				(void) taskq_dispatch(vscan_svc_taskq,
578 				    vscan_svc_taskq_callback,
579 				    (void *)req, TQ_SLEEP);
580 				++(vscan_svc_counts.vsc_tq);
581 
582 				req->vsr_state = VS_SVC_REQ_QUEUED;
583 			}
584 			req = next;
585 		}
586 
587 		vscan_svc_reql_next = req;
588 
589 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
590 		    vscan_svc_counts_t *, &vscan_svc_counts);
591 
592 		(void) cv_timedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
593 		    lbolt + SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT));
594 
595 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
596 		    vscan_svc_counts_t *, &vscan_svc_counts);
597 
598 		mutex_exit(&vscan_svc_mutex);
599 	}
600 }
601 
602 
603 static void
604 vscan_svc_taskq_callback(void *data)
605 {
606 	vscan_req_t *req;
607 
608 	mutex_enter(&vscan_svc_mutex);
609 
610 	req = (vscan_req_t *)data;
611 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
612 	vscan_svc_do_scan(req);
613 	if (req->vsr_state != VS_SVC_REQ_SCANNING)
614 		vscan_svc_scan_complete(req);
615 
616 	--(vscan_svc_counts.vsc_tq);
617 	mutex_exit(&vscan_svc_mutex);
618 }
619 
620 
621 /*
622  * vscan_svc_do_scan
623  *
624  * Note: To avoid potential deadlock it is important that
625  * vscan_svc_mutex is not held during the call to
626  * vscan_drv_create_note. vscan_drv_create_note enters
627  * the vscan_drv_mutex and it is possible that a thread
628  * holding that mutex could be waiting for vscan_svc_mutex.
629  */
630 static void
631 vscan_svc_do_scan(vscan_req_t *req)
632 {
633 	int idx, result;
634 	vscan_svc_node_t *node;
635 	vs_scan_req_t *door_req;
636 
637 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
638 
639 	idx = req->vsr_idx;
640 	node = &vscan_svc_nodes[idx];
641 
642 	req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
643 
644 	/* if vscan not enabled (shutting down), allow ACCESS */
645 	if (vscan_svc_state != VS_SVC_ENABLED) {
646 		node->vsn_access = VS_ACCESS_ALLOW;
647 		return;
648 	}
649 
650 	if (vscan_svc_getattr(idx) != 0) {
651 		cmn_err(CE_WARN, "Can't access xattr for %s\n",
652 		    req->vsr_vp->v_path);
653 		node->vsn_access = VS_ACCESS_DENY;
654 		return;
655 	}
656 
657 	/* valid scan_req ptr guaranteed */
658 	door_req = vscan_svc_populate_req(idx);
659 
660 	/* free up mutex around create node and door call */
661 	mutex_exit(&vscan_svc_mutex);
662 	if (vscan_drv_create_node(idx) != B_TRUE)
663 		result = VS_STATUS_ERROR;
664 	else
665 		result = vscan_door_scan_file(door_req);
666 	kmem_free(door_req, sizeof (vs_scan_req_t));
667 	mutex_enter(&vscan_svc_mutex);
668 
669 	if (result != VS_STATUS_SCANNING) {
670 		vscan_svc_nodes[idx].vsn_result = result;
671 		vscan_svc_process_scan_result(idx);
672 	} else { /* async response */
673 		if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
674 			req->vsr_state = VS_SVC_REQ_SCANNING;
675 	}
676 }
677 
678 
679 /*
680  * vscan_svc_populate_req
681  *
682  * Allocate a scan request to be sent to vscand, populating it
683  * from the data in vscan_svc_nodes[idx].
684  *
685  * Returns: scan request object
686  */
687 static vs_scan_req_t *
688 vscan_svc_populate_req(int idx)
689 {
690 	vs_scan_req_t *scan_req;
691 	vscan_req_t *req;
692 	vscan_svc_node_t *node;
693 
694 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
695 
696 	node = &vscan_svc_nodes[idx];
697 	req = node->vsn_req;
698 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
699 
700 	scan_req->vsr_idx = idx;
701 	scan_req->vsr_seqnum = req->vsr_seqnum;
702 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
703 	scan_req->vsr_size = node->vsn_size;
704 	scan_req->vsr_modified = node->vsn_modified;
705 	scan_req->vsr_quarantined = node->vsn_quarantined;
706 	scan_req->vsr_flags = 0;
707 	(void) strncpy(scan_req->vsr_scanstamp,
708 	    node->vsn_scanstamp, sizeof (vs_scanstamp_t));
709 
710 	return (scan_req);
711 }
712 
713 
714 /*
715  * vscan_svc_scan_complete
716  */
717 static void
718 vscan_svc_scan_complete(vscan_req_t *req)
719 {
720 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
721 	ASSERT(req != NULL);
722 
723 	req->vsr_state = VS_SVC_REQ_COMPLETE;
724 
725 	if ((--req->vsr_refcnt) == 0)
726 		vscan_svc_delete_req(req);
727 	else
728 		cv_broadcast(&(req->vsr_cv));
729 }
730 
731 
732 /*
733  * vscan_svc_delete_req
734  */
735 static void
736 vscan_svc_delete_req(vscan_req_t *req)
737 {
738 	int idx;
739 
740 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
741 	ASSERT(req != NULL);
742 	ASSERT(req->vsr_refcnt == 0);
743 	ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
744 
745 	if ((idx = req->vsr_idx) != 0)
746 		vscan_svc_remove_req(idx);
747 
748 	vscan_svc_reql_remove(req);
749 
750 	cv_signal(&vscan_svc_reql_cv);
751 }
752 
753 
754 /*
755  * vscan_svc_scan_result
756  *
757  * Invoked from vscan_drv.c on receipt of an ioctl containing
758  * an async scan result (VS_DRV_IOCTL_RESULT)
759  * If the vsr_seqnum in the response does not match that in the
760  * vscan_svc_nodes entry the result is discarded.
761  */
762 void
763 vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
764 {
765 	vscan_req_t *req;
766 	vscan_svc_node_t *node;
767 
768 	mutex_enter(&vscan_svc_mutex);
769 
770 	node = &vscan_svc_nodes[scan_rsp->vsr_idx];
771 
772 	if ((req = node->vsn_req) == NULL) {
773 		mutex_exit(&vscan_svc_mutex);
774 		return;
775 	}
776 
777 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
778 
779 	if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
780 		mutex_exit(&vscan_svc_mutex);
781 		return;
782 	}
783 
784 	node->vsn_result = scan_rsp->vsr_result;
785 	(void) strncpy(node->vsn_scanstamp,
786 	    scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
787 
788 	vscan_svc_process_scan_result(scan_rsp->vsr_idx);
789 
790 	if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
791 		vscan_svc_scan_complete(node->vsn_req);
792 	else
793 		node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
794 
795 	mutex_exit(&vscan_svc_mutex);
796 }
797 
798 
799 /*
800  * vscan_svc_scan_abort
801  *
802  * Abort in-progress scan requests.
803  */
804 void
805 vscan_svc_scan_abort()
806 {
807 	int idx;
808 	vscan_req_t *req;
809 
810 	mutex_enter(&vscan_svc_mutex);
811 
812 	for (idx = 1; idx <= vs_nodes_max; idx++) {
813 		if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
814 			continue;
815 
816 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
817 
818 		if (req->vsr_state == VS_SVC_REQ_SCANNING) {
819 			DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
820 			vscan_svc_process_scan_result(idx);
821 			vscan_svc_scan_complete(req);
822 		}
823 	}
824 
825 	mutex_exit(&vscan_svc_mutex);
826 }
827 
828 
829 /*
830  * vscan_svc_process_scan_result
831  *
832  * Sets vsn_access and updates file attributes based on vsn_result,
833  * as follows:
834  *
835  * VS_STATUS_INFECTED
836  *  deny access, set quarantine attribute, clear scanstamp
837  * VS_STATUS_CLEAN
838  *  allow access, set scanstamp,
839  *  if file not modified since scan initiated, clear modified attribute
840  * VS_STATUS_NO_SCAN
841  *  deny access if file quarantined, otherwise allow access
842  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
843  *  deny access if file quarantined, modified or no scanstamp
844  *  otherwise, allow access
845  */
846 static void
847 vscan_svc_process_scan_result(int idx)
848 {
849 	struct vattr attr;
850 	vnode_t *vp;
851 	timestruc_t *mtime;
852 	vscan_svc_node_t *node;
853 
854 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
855 
856 	node = &vscan_svc_nodes[idx];
857 
858 	switch (node->vsn_result) {
859 	case VS_STATUS_INFECTED:
860 		node->vsn_access = VS_ACCESS_DENY;
861 		node->vsn_quarantined = 1;
862 		node->vsn_scanstamp[0] = '\0';
863 		(void) vscan_svc_setattr(idx,
864 		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
865 		break;
866 
867 	case VS_STATUS_CLEAN:
868 		node->vsn_access = VS_ACCESS_ALLOW;
869 
870 		/* if mtime has changed, don't clear the modified attribute */
871 		vp = node->vsn_req->vsr_vp;
872 		mtime = &(node->vsn_mtime);
873 		attr.va_mask = AT_MTIME;
874 		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
875 		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
876 		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
877 			DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
878 			    node);
879 			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
880 			break;
881 		}
882 
883 		node->vsn_modified = 0;
884 		(void) vscan_svc_setattr(idx,
885 		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
886 		break;
887 
888 	case VS_STATUS_NO_SCAN:
889 		if (node->vsn_quarantined)
890 			node->vsn_access = VS_ACCESS_DENY;
891 		else
892 			node->vsn_access = VS_ACCESS_ALLOW;
893 		break;
894 
895 	case VS_STATUS_ERROR:
896 	case VS_STATUS_UNDEFINED:
897 	default:
898 		if ((node->vsn_quarantined) ||
899 		    (node->vsn_modified) ||
900 		    (node->vsn_scanstamp[0] == '\0'))
901 			node->vsn_access = VS_ACCESS_DENY;
902 		else
903 			node->vsn_access = VS_ACCESS_ALLOW;
904 		break;
905 	}
906 
907 	DTRACE_PROBE4(vscan__result,
908 	    int, idx, int, node->vsn_req->vsr_seqnum,
909 	    int, node->vsn_result, int, node->vsn_access);
910 }
911 
912 
913 /*
914  * vscan_svc_getattr
915  *
916  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
917  */
918 static int
919 vscan_svc_getattr(int idx)
920 {
921 	xvattr_t xvattr;
922 	xoptattr_t *xoap = NULL;
923 	vnode_t *vp;
924 	vscan_svc_node_t *node;
925 
926 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
927 
928 	node = &vscan_svc_nodes[idx];
929 	if ((vp = node->vsn_req->vsr_vp) == NULL)
930 		return (-1);
931 
932 	/* get the attributes */
933 	xva_init(&xvattr); /* sets AT_XVATTR */
934 
935 	xvattr.xva_vattr.va_mask |= AT_SIZE;
936 	xvattr.xva_vattr.va_mask |= AT_MTIME;
937 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
938 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
939 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
940 
941 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
942 		return (-1);
943 
944 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
945 		cmn_err(CE_NOTE, "Virus scan request failed; "
946 		    "file system does not support virus scanning");
947 		return (-1);
948 	}
949 
950 	node->vsn_size = xvattr.xva_vattr.va_size;
951 	node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
952 	node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
953 
954 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
955 		return (-1);
956 	node->vsn_modified = xoap->xoa_av_modified;
957 
958 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
959 		return (-1);
960 	node->vsn_quarantined = xoap->xoa_av_quarantined;
961 
962 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
963 		(void) memcpy(node->vsn_scanstamp,
964 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
965 	}
966 
967 	DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
968 	return (0);
969 }
970 
971 
972 /*
973  * vscan_svc_setattr
974  *
975  * Set the vscan related system attributes.
976  */
977 static int
978 vscan_svc_setattr(int idx, int which)
979 {
980 	xvattr_t xvattr;
981 	xoptattr_t *xoap = NULL;
982 	vnode_t *vp;
983 	int len;
984 	vscan_svc_node_t *node;
985 
986 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
987 
988 	node = &vscan_svc_nodes[idx];
989 	if ((vp = node->vsn_req->vsr_vp) == NULL)
990 		return (-1);
991 
992 	/* update the attributes */
993 	xva_init(&xvattr); /* sets AT_XVATTR */
994 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
995 		return (-1);
996 
997 	if (which & XAT_AV_MODIFIED) {
998 		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
999 		xoap->xoa_av_modified = node->vsn_modified;
1000 	}
1001 
1002 	if (which & XAT_AV_QUARANTINED) {
1003 		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
1004 		xoap->xoa_av_quarantined = node->vsn_quarantined;
1005 	}
1006 
1007 	if (which & XAT_AV_SCANSTAMP) {
1008 		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
1009 		len = strlen(node->vsn_scanstamp);
1010 		(void) memcpy(xoap->xoa_av_scanstamp,
1011 		    node->vsn_scanstamp, len);
1012 	}
1013 
1014 	/* if access is denied, set mtime to invalidate client cache */
1015 	if (node->vsn_access != VS_ACCESS_ALLOW) {
1016 		xvattr.xva_vattr.va_mask |= AT_MTIME;
1017 		gethrestime(&xvattr.xva_vattr.va_mtime);
1018 	}
1019 
1020 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
1021 		return (-1);
1022 
1023 	DTRACE_PROBE2(vscan__setattr,
1024 	    vscan_svc_node_t *, node, int, which);
1025 
1026 	return (0);
1027 }
1028 
1029 
1030 /*
1031  * vscan_svc_configure
1032  *
1033  * store configuration in vscan_svc_config
1034  * set up vscan_svc_types array of pointers into
1035  * vscan_svc_config.vsc_types for efficient searching
1036  */
1037 int
1038 vscan_svc_configure(vs_config_t *conf)
1039 {
1040 	int count = 0;
1041 	char *p, *beg, *end;
1042 
1043 	mutex_enter(&vscan_svc_cfg_mutex);
1044 
1045 	vscan_svc_config = *conf;
1046 
1047 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
1048 
1049 	beg = vscan_svc_config.vsc_types;
1050 	end = beg + vscan_svc_config.vsc_types_len;
1051 
1052 	for (p = beg; p < end; p += strlen(p) + 1) {
1053 		if (count >= VS_TYPES_MAX) {
1054 			mutex_exit(&vscan_svc_mutex);
1055 			return (-1);
1056 		}
1057 
1058 		vscan_svc_types[count] = p;
1059 		++count;
1060 	}
1061 
1062 	mutex_exit(&vscan_svc_cfg_mutex);
1063 	return (0);
1064 }
1065 
1066 
1067 /*
1068  * vscan_svc_exempt_file
1069  *
1070  * check if a file's size or type exempts it from virus scanning
1071  *
1072  * If the file is exempt from virus scanning, allow will be set
1073  * to define whether files access should be allowed (B_TRUE) or
1074  * denied (B_FALSE)
1075  *
1076  * Returns: 1 exempt
1077  *          0 scan required
1078  */
1079 static int
1080 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
1081 {
1082 	struct vattr attr;
1083 
1084 	ASSERT(vp != NULL);
1085 	ASSERT(vp->v_path != NULL);
1086 
1087 	attr.va_mask = AT_SIZE;
1088 
1089 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
1090 		*allow = B_FALSE;
1091 		return (0);
1092 	}
1093 
1094 	mutex_enter(&vscan_svc_cfg_mutex);
1095 
1096 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
1097 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
1098 		    vp->v_path, int, *allow);
1099 
1100 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
1101 		mutex_exit(&vscan_svc_cfg_mutex);
1102 		return (1);
1103 	}
1104 
1105 	if (vscan_svc_exempt_filetype(vp->v_path)) {
1106 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
1107 		*allow = B_TRUE;
1108 		mutex_exit(&vscan_svc_cfg_mutex);
1109 		return (1);
1110 	}
1111 
1112 	mutex_exit(&vscan_svc_cfg_mutex);
1113 	return (0);
1114 }
1115 
1116 
1117 /*
1118  * vscan_svc_exempt_filetype
1119  *
1120  * Each entry in vscan_svc_types includes a rule indicator (+,-)
1121  * followed by the match string for file types to which the rule
1122  * applies. Look for first match of file type in vscan_svc_types
1123  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
1124  * if the indicator is '+'.
1125  * If vscan_svc_match_ext fails, or no match is found, return 0
1126  * (not exempt)
1127  *
1128  * Returns 1: exempt, 0: not exempt
1129  */
1130 static int
1131 vscan_svc_exempt_filetype(char *filepath)
1132 {
1133 	int i, rc, exempt = 0;
1134 	char *filename, *ext;
1135 
1136 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
1137 
1138 	if ((filename = strrchr(filepath, '/')) == 0)
1139 		filename = filepath;
1140 	else
1141 		filename++;
1142 
1143 	if ((ext = strrchr(filename, '.')) == NULL)
1144 		ext = "";
1145 	else
1146 		ext++;
1147 
1148 	for (i = 0; i < VS_TYPES_MAX; i ++) {
1149 		if (vscan_svc_types[i] == 0)
1150 			break;
1151 
1152 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
1153 		if (rc == -1)
1154 			break;
1155 		if (rc > 0) {
1156 			DTRACE_PROBE2(vscan__type__match, char *, ext,
1157 			    char *, vscan_svc_types[i]);
1158 			exempt = (vscan_svc_types[i][0] == '-');
1159 			break;
1160 		}
1161 	}
1162 
1163 	return (exempt);
1164 }
1165 
1166 
1167 /*
1168  *  vscan_svc_match_ext
1169  *
1170  * Performs a case-insensitive match for two strings.  The first string
1171  * argument can contain the wildcard characters '?' and '*'
1172  *
1173  * Returns: 0 no match
1174  *          1 match
1175  *         -1 recursion error
1176  */
1177 static int
1178 vscan_svc_match_ext(char *patn, char *str, int depth)
1179 {
1180 	int c1, c2;
1181 	if (depth > VS_EXT_RECURSE_DEPTH)
1182 		return (-1);
1183 
1184 	for (;;) {
1185 		switch (*patn) {
1186 		case 0:
1187 			return (*str == 0);
1188 
1189 		case '?':
1190 			if (*str != 0) {
1191 				str++;
1192 				patn++;
1193 				continue;
1194 			}
1195 			return (0);
1196 
1197 		case '*':
1198 			patn++;
1199 			if (*patn == 0)
1200 				return (1);
1201 
1202 			while (*str) {
1203 				if (vscan_svc_match_ext(patn, str, depth + 1))
1204 					return (1);
1205 				str++;
1206 			}
1207 			return (0);
1208 
1209 		default:
1210 			if (*str != *patn) {
1211 				c1 = *str;
1212 				c2 = *patn;
1213 
1214 				c1 = tolower(c1);
1215 				c2 = tolower(c2);
1216 				if (c1 != c2)
1217 					return (0);
1218 			}
1219 			str++;
1220 			patn++;
1221 			continue;
1222 		}
1223 	}
1224 	/* NOT REACHED */
1225 }
1226 
1227 
1228 /*
1229  * vscan_svc_insert_req
1230  *
1231  * Insert request in next available available slot in vscan_svc_nodes
1232  *
1233  * Returns: idx of slot, or -1 if no slot available
1234  */
1235 static int
1236 vscan_svc_insert_req(vscan_req_t *req)
1237 {
1238 	int idx;
1239 	vscan_svc_node_t *node;
1240 
1241 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1242 
1243 	if (vscan_svc_counts.vsc_node == vs_nodes_max)
1244 		return (-1);
1245 
1246 	for (idx = 1; idx <= vs_nodes_max; idx++) {
1247 		if (vscan_svc_nodes[idx].vsn_req == NULL) {
1248 			req->vsr_idx = idx;
1249 
1250 			node = &vscan_svc_nodes[idx];
1251 			(void) memset(node, 0, sizeof (vscan_svc_node_t));
1252 			node->vsn_req = req;
1253 			node->vsn_modified = 1;
1254 			node->vsn_result = VS_STATUS_UNDEFINED;
1255 			node->vsn_access = VS_ACCESS_UNDEFINED;
1256 
1257 			++(vscan_svc_counts.vsc_node);
1258 			return (idx);
1259 		}
1260 	}
1261 
1262 	return (-1);
1263 }
1264 
1265 
1266 /*
1267  * vscan_svc_remove_req
1268  */
1269 static void
1270 vscan_svc_remove_req(int idx)
1271 {
1272 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1273 
1274 	if (idx != 0) {
1275 		(void) memset(&vscan_svc_nodes[idx], 0,
1276 		    sizeof (vscan_svc_node_t));
1277 		--(vscan_svc_counts.vsc_node);
1278 	}
1279 }
1280 
1281 
1282 /*
1283  * vscan_svc_reql_find
1284  */
1285 static vscan_req_t *
1286 vscan_svc_reql_find(vnode_t *vp)
1287 {
1288 	vscan_req_t *req;
1289 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1290 
1291 	req = list_head(&vscan_svc_reql);
1292 
1293 	while (req != NULL) {
1294 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1295 		if ((req->vsr_vp == vp) &&
1296 		    (req->vsr_state != VS_SVC_REQ_COMPLETE))
1297 			break;
1298 
1299 		req = list_next(&vscan_svc_reql, req);
1300 	}
1301 
1302 	return (req);
1303 }
1304 
1305 
1306 /*
1307  * vscan_svc_reql_insert
1308  */
1309 static vscan_req_t *
1310 vscan_svc_reql_insert(vnode_t *vp)
1311 {
1312 	vscan_req_t *req;
1313 
1314 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1315 
1316 	/* if request already in list then return it */
1317 	if ((req = vscan_svc_reql_find(vp)) != NULL)
1318 		return (req);
1319 
1320 	/* if list is full return NULL */
1321 	if (vscan_svc_counts.vsc_reql == vs_reqs_max)
1322 		return (NULL);
1323 
1324 	/* create a new request and insert into list */
1325 	VN_HOLD(vp);
1326 
1327 	req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
1328 
1329 	req->vsr_magic = VS_REQ_MAGIC;
1330 	if (vscan_svc_seqnum == UINT32_MAX)
1331 		vscan_svc_seqnum = 0;
1332 	req->vsr_seqnum = ++vscan_svc_seqnum;
1333 	req->vsr_vp = vp;
1334 	req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
1335 	req->vsr_state = VS_SVC_REQ_INIT;
1336 	cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
1337 
1338 	list_insert_tail(&vscan_svc_reql, req);
1339 	if (vscan_svc_reql_next == NULL)
1340 		vscan_svc_reql_next = req;
1341 
1342 	++(vscan_svc_counts.vsc_reql);
1343 
1344 	/* wake reql handler thread */
1345 	cv_signal(&vscan_svc_reql_cv);
1346 
1347 	return (req);
1348 }
1349 
1350 
1351 /*
1352  * vscan_svc_reql_remove
1353  */
1354 static void
1355 vscan_svc_reql_remove(vscan_req_t *req)
1356 {
1357 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1358 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1359 
1360 	if (vscan_svc_reql_next == req)
1361 		vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
1362 
1363 	list_remove(&vscan_svc_reql, req);
1364 	cv_destroy(&(req->vsr_cv));
1365 	VN_RELE(req->vsr_vp);
1366 
1367 	kmem_free(req, sizeof (vscan_req_t));
1368 	--(vscan_svc_counts.vsc_reql);
1369 }
1370