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