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