1911106dfSjm /*
2911106dfSjm  * CDDL HEADER START
3911106dfSjm  *
4911106dfSjm  * The contents of this file are subject to the terms of the
5911106dfSjm  * Common Development and Distribution License (the "License").
6911106dfSjm  * You may not use this file except in compliance with the License.
7911106dfSjm  *
8911106dfSjm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9911106dfSjm  * or http://www.opensolaris.org/os/licensing.
10911106dfSjm  * See the License for the specific language governing permissions
11911106dfSjm  * and limitations under the License.
12911106dfSjm  *
13911106dfSjm  * When distributing Covered Code, include this CDDL HEADER in each
14911106dfSjm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15911106dfSjm  * If applicable, add the following below this CDDL HEADER, with the
16911106dfSjm  * fields enclosed by brackets "[]" replaced with your own identifying
17911106dfSjm  * information: Portions Copyright [yyyy] [name of copyright owner]
18911106dfSjm  *
19911106dfSjm  * CDDL HEADER END
20911106dfSjm  */
21911106dfSjm 
22911106dfSjm /*
23d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24911106dfSjm  * Use is subject to license terms.
25*e2fc3408SPatrick Mooney  * Copyright (c) 2015, Joyent, Inc.
26911106dfSjm  */
27911106dfSjm 
28911106dfSjm #include <sys/stat.h>
29911106dfSjm #include <sys/ddi.h>
30911106dfSjm #include <sys/sunddi.h>
31911106dfSjm #include <sys/time.h>
32911106dfSjm #include <sys/varargs.h>
33911106dfSjm #include <sys/conf.h>
34911106dfSjm #include <sys/modctl.h>
35911106dfSjm #include <sys/cmn_err.h>
36911106dfSjm #include <sys/vnode.h>
37911106dfSjm #include <fs/fs_subr.h>
38911106dfSjm #include <sys/types.h>
39911106dfSjm #include <sys/file.h>
40911106dfSjm #include <sys/disp.h>
41911106dfSjm #include <sys/sdt.h>
42911106dfSjm #include <sys/cred.h>
43bfc848c6Sjm #include <sys/list.h>
44911106dfSjm #include <sys/vscan.h>
455c5f1371SRichard Lowe #include <sys/sysmacros.h>
46911106dfSjm 
47bfc848c6Sjm #define	VS_REQ_MAGIC		0x52515354 /* 'RQST' */
48bfc848c6Sjm 
49bfc848c6Sjm #define	VS_REQS_DEFAULT		20000	/* pending scan requests - reql */
50bfc848c6Sjm #define	VS_NODES_DEFAULT	128	/* concurrent file scans */
51bfc848c6Sjm #define	VS_WORKERS_DEFAULT	32	/* worker threads */
52bfc848c6Sjm #define	VS_SCANWAIT_DEFAULT	15*60	/* seconds to wait for scan result */
53bfc848c6Sjm #define	VS_REQL_HANDLER_TIMEOUT	30
54911106dfSjm #define	VS_EXT_RECURSE_DEPTH	8
55bfc848c6Sjm 
56bfc848c6Sjm /* access derived from scan result (VS_STATUS_XXX) and file attributes */
57bfc848c6Sjm #define	VS_ACCESS_UNDEFINED	0
58bfc848c6Sjm #define	VS_ACCESS_ALLOW		1	/* return 0 */
59bfc848c6Sjm #define	VS_ACCESS_DENY		2	/* return EACCES */
60bfc848c6Sjm 
61911106dfSjm #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
62bfc848c6Sjm 
63bfc848c6Sjm /* global variables - tunable via /etc/system */
64bfc848c6Sjm uint32_t vs_reqs_max = VS_REQS_DEFAULT;	/* max scan requests */
65bfc848c6Sjm uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
66bfc848c6Sjm uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
67bfc848c6Sjm uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
68bfc848c6Sjm 
69bfc848c6Sjm 
70bfc848c6Sjm /*
71bfc848c6Sjm  * vscan_svc_state
72bfc848c6Sjm  *
73bfc848c6Sjm  *   +-----------------+
74bfc848c6Sjm  *   | VS_SVC_UNCONFIG |
75bfc848c6Sjm  *   +-----------------+
76bfc848c6Sjm  *      |           ^
77bfc848c6Sjm  *      | svc_init  | svc_fini
78bfc848c6Sjm  *      v           |
79bfc848c6Sjm  *   +-----------------+
80bfc848c6Sjm  *   | VS_SVC_IDLE     |<----|
81bfc848c6Sjm  *   +-----------------+	 |
82bfc848c6Sjm  *      |                    |
83bfc848c6Sjm  *      | svc_enable         |
84bfc848c6Sjm  *      |<----------------|  |
85bfc848c6Sjm  *      v                 |  |
86bfc848c6Sjm  *   +-----------------+  |  |
87bfc848c6Sjm  *   | VS_SVC_ENABLED  |--|  |
88bfc848c6Sjm  *   +-----------------+     |
89bfc848c6Sjm  *      |                    |
90bfc848c6Sjm  *      | svc_disable        | handler thread exit,
91bfc848c6Sjm  *      v                    | all requests complete
92bfc848c6Sjm  *   +-----------------+	 |
93bfc848c6Sjm  *   | VS_SVC_DISABLED |-----|
94bfc848c6Sjm  *   +-----------------+
95bfc848c6Sjm  *
96bfc848c6Sjm  * svc_enable may occur when we are already in the ENABLED
97bfc848c6Sjm  * state if vscand has exited without clean shutdown and
98bfc848c6Sjm  * then reconnected within the delayed disable time period
99bfc848c6Sjm  * (vs_reconnect_timeout) - see vscan_drv
100bfc848c6Sjm  */
101bfc848c6Sjm 
102bfc848c6Sjm typedef enum {
103bfc848c6Sjm 	VS_SVC_UNCONFIG,
104bfc848c6Sjm 	VS_SVC_IDLE,
105bfc848c6Sjm 	VS_SVC_ENABLED, /* service enabled and registered */
106bfc848c6Sjm 	VS_SVC_DISABLED /* service disabled and nunregistered */
107bfc848c6Sjm } vscan_svc_state_t;
108bfc848c6Sjm static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
109bfc848c6Sjm 
110911106dfSjm 
111bfc848c6Sjm /*
112bfc848c6Sjm  * vscan_svc_req_state
113bfc848c6Sjm  *
114bfc848c6Sjm  * When a scan request is received from the file system it is
115bfc848c6Sjm  * identified in or inserted into the vscan_svc_reql (INIT).
116bfc848c6Sjm  * If the request is asynchronous 0 is then returned to the caller.
117bfc848c6Sjm  * If the request is synchronous the req's refcnt is incremented
118bfc848c6Sjm  * and the caller waits for the request to complete.
119bfc848c6Sjm  * The refcnt is also incremented when the request is inserted
120bfc848c6Sjm  * in vscan_svc_nodes, and decremented on scan_complete.
121bfc848c6Sjm  *
122bfc848c6Sjm  * vscan_svc_handler processes requests from the request list,
123bfc848c6Sjm  * inserting them into vscan_svc_nodes and the task queue (QUEUED).
124bfc848c6Sjm  * When the task queue call back (vscan_svc_do_scan) is invoked
125bfc848c6Sjm  * the request transitions to IN_PROGRESS state. If the request
126bfc848c6Sjm  * is sucessfully sent to vscand (door_call) and the door response
127bfc848c6Sjm  * is SCANNING then the scan result will be received asynchronously.
128bfc848c6Sjm  * Although unusual, it is possible that the async response is
129bfc848c6Sjm  * received before the door call returns (hence the ASYNC_COMPLETE
130bfc848c6Sjm  * state).
131bfc848c6Sjm  * When the result has been determined / received,
132bfc848c6Sjm  * vscan_svc_scan_complete is invoked to transition the request to
133bfc848c6Sjm  * COMPLETE state, decrement refcnt and signal all waiting callers.
134bfc848c6Sjm  * When the last waiting caller has processed the result (refcnt == 0)
135bfc848c6Sjm  * the request is removed from vscan_svc_reql and vscan_svc_nodes
136bfc848c6Sjm  * and deleted.
137bfc848c6Sjm  *
138bfc848c6Sjm  *      |                                                     ^
139bfc848c6Sjm  *      | reql_insert                                         | refcnt == 0
140bfc848c6Sjm  *      v                                                     | (delete)
141bfc848c6Sjm  *   +------------------------+	                  +---------------------+
142bfc848c6Sjm  *   | VS_SVC_REQ_INIT        | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
143bfc848c6Sjm  *   +------------------------+	                  +---------------------+
144bfc848c6Sjm  *      |                                                     ^
145bfc848c6Sjm  *      | insert_req, tq_dispatch                             |
146bfc848c6Sjm  *      v                                                     |
147bfc848c6Sjm  *   +------------------------+	                              |
148bfc848c6Sjm  *   | VS_SVC_REQ_QUEUED      |                           scan_complete
149bfc848c6Sjm  *   +------------------------+	                              |
150bfc848c6Sjm  *      |                                                     |
151bfc848c6Sjm  *      | tq_callback (do_scan)                               |
152bfc848c6Sjm  *      |                                                     |
153bfc848c6Sjm  *      v                        scan not req'd, error,       |
154bfc848c6Sjm  *   +------------------------+  or door_result != SCANNING   |
155bfc848c6Sjm  *   | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
156bfc848c6Sjm  *   +------------------------+	                              |
157bfc848c6Sjm  *       |         |                                          |
158bfc848c6Sjm  *       |         | door_result == SCANNING                  |
159bfc848c6Sjm  *       |         v                                          |
160bfc848c6Sjm  *       |     +---------------------------+	async result  |
161bfc848c6Sjm  *       |     | VS_SVC_REQ_SCANNING       |-------->---------|
162bfc848c6Sjm  *       |     +---------------------------+	              |
163bfc848c6Sjm  *       |                                                    |
164bfc848c6Sjm  *       | async result                                       |
165bfc848c6Sjm  *       v                                                    |
166bfc848c6Sjm  *    +---------------------------+	 door_result = SCANNING   |
167bfc848c6Sjm  *    | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
168bfc848c6Sjm  *    +---------------------------+
169bfc848c6Sjm  */
170bfc848c6Sjm typedef enum {
171bfc848c6Sjm 	VS_SVC_REQ_INIT,
172bfc848c6Sjm 	VS_SVC_REQ_QUEUED,
173bfc848c6Sjm 	VS_SVC_REQ_IN_PROGRESS,
174bfc848c6Sjm 	VS_SVC_REQ_SCANNING,
175bfc848c6Sjm 	VS_SVC_REQ_ASYNC_COMPLETE,
176bfc848c6Sjm 	VS_SVC_REQ_COMPLETE
177bfc848c6Sjm } vscan_svc_req_state_t;
178bfc848c6Sjm 
179bfc848c6Sjm 
180bfc848c6Sjm /*
181bfc848c6Sjm  * vscan_svc_reql - the list of pending and in-progress scan requests
182bfc848c6Sjm  */
183bfc848c6Sjm typedef struct vscan_req {
184bfc848c6Sjm 	uint32_t vsr_magic;	/* VS_REQ_MAGIC */
185bfc848c6Sjm 	list_node_t vsr_lnode;
186911106dfSjm 	vnode_t *vsr_vp;
187bfc848c6Sjm 	uint32_t vsr_idx;	/* vscan_svc_nodes index */
188bfc848c6Sjm 	uint32_t vsr_seqnum;	/* unigue request id */
189bfc848c6Sjm 	uint32_t vsr_refcnt;
190bfc848c6Sjm 	kcondvar_t vsr_cv;
191bfc848c6Sjm 	vscan_svc_req_state_t vsr_state;
192bfc848c6Sjm } vscan_req_t;
193bfc848c6Sjm 
194bfc848c6Sjm static list_t vscan_svc_reql;
195bfc848c6Sjm 
196911106dfSjm 
197911106dfSjm /*
198bfc848c6Sjm  * vscan_svc_nodes - table of files being scanned
199911106dfSjm  *
200911106dfSjm  * The index into this table is passed in the door call to
201911106dfSjm  * vscand. vscand uses the idx to determine which minor node
202911106dfSjm  * to open to read the file data. Within the kernel driver
203911106dfSjm  * the minor device number can thus be used to identify the
204911106dfSjm  * table index to get the appropriate vnode.
205911106dfSjm  *
206911106dfSjm  * Instance 0 is reserved for the daemon/driver control
207911106dfSjm  * interface: enable/configure/disable
208911106dfSjm  */
209bfc848c6Sjm typedef struct vscan_svc_node {
210bfc848c6Sjm 	vscan_req_t *vsn_req;
211bfc848c6Sjm 	uint8_t vsn_quarantined;
212bfc848c6Sjm 	uint8_t vsn_modified;
213bfc848c6Sjm 	uint64_t vsn_size;
214bfc848c6Sjm 	timestruc_t vsn_mtime;
215bfc848c6Sjm 	vs_scanstamp_t vsn_scanstamp;
216bfc848c6Sjm 	uint32_t vsn_result;
217bfc848c6Sjm 	uint32_t vsn_access;
218bfc848c6Sjm } vscan_svc_node_t;
219bfc848c6Sjm 
220bfc848c6Sjm static vscan_svc_node_t *vscan_svc_nodes;
221bfc848c6Sjm static int vscan_svc_nodes_sz;
222bfc848c6Sjm 
223bfc848c6Sjm 
224bfc848c6Sjm /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
225911106dfSjm static taskq_t *vscan_svc_taskq = NULL;
226bfc848c6Sjm 
227bfc848c6Sjm /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
228bfc848c6Sjm typedef struct {
229bfc848c6Sjm 	uint32_t vsc_reql;
230bfc848c6Sjm 	uint32_t vsc_node;
231bfc848c6Sjm 	uint32_t vsc_tq;
232bfc848c6Sjm } vscan_svc_counts_t;
233bfc848c6Sjm static vscan_svc_counts_t vscan_svc_counts;
234911106dfSjm 
235911106dfSjm /*
236911106dfSjm  * vscan_svc_mutex protects the data pertaining to scan requests:
237bfc848c6Sjm  * request list - vscan_svc_reql
238bfc848c6Sjm  * node table - vscan_svc_nodes
239911106dfSjm  */
240911106dfSjm static kmutex_t vscan_svc_mutex;
241911106dfSjm 
242bfc848c6Sjm /* unique request id for vscand request/response correlation */
243bfc848c6Sjm static uint32_t vscan_svc_seqnum = 0;
244bfc848c6Sjm 
245911106dfSjm /*
246911106dfSjm  * vscan_svc_cfg_mutex protects the configuration data:
247911106dfSjm  * vscan_svc_config, vscan_svc_types
248911106dfSjm  */
249911106dfSjm static kmutex_t vscan_svc_cfg_mutex;
250911106dfSjm 
251911106dfSjm /* configuration data - for virus scan exemption */
252911106dfSjm static vs_config_t vscan_svc_config;
253911106dfSjm static char *vscan_svc_types[VS_TYPES_MAX];
254911106dfSjm 
255bfc848c6Sjm /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
256bfc848c6Sjm static kthread_t *vscan_svc_reql_thread;
257bfc848c6Sjm static kcondvar_t vscan_svc_reql_cv;
258bfc848c6Sjm static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
259bfc848c6Sjm 
260911106dfSjm /* local functions */
261911106dfSjm int vscan_svc_scan_file(vnode_t *, cred_t *, int);
262bfc848c6Sjm static void vscan_svc_taskq_callback(void *);
263911106dfSjm static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
264911106dfSjm static int vscan_svc_exempt_filetype(char *);
265911106dfSjm static int vscan_svc_match_ext(char *, char *, int);
266bfc848c6Sjm static void vscan_svc_do_scan(vscan_req_t *);
267bfc848c6Sjm static vs_scan_req_t *vscan_svc_populate_req(int);
26853c11029Sjm static void vscan_svc_process_scan_result(int);
269bfc848c6Sjm static void vscan_svc_scan_complete(vscan_req_t *);
270bfc848c6Sjm static void vscan_svc_delete_req(vscan_req_t *);
271bfc848c6Sjm static int vscan_svc_insert_req(vscan_req_t *);
272bfc848c6Sjm static void vscan_svc_remove_req(int);
273bfc848c6Sjm static vscan_req_t *vscan_svc_reql_find(vnode_t *);
274bfc848c6Sjm static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
275bfc848c6Sjm static void vscan_svc_reql_remove(vscan_req_t *);
276bfc848c6Sjm 
277911106dfSjm static int vscan_svc_getattr(int);
27853c11029Sjm static int vscan_svc_setattr(int, int);
279911106dfSjm 
280bfc848c6Sjm /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
281bfc848c6Sjm static void vscan_svc_reql_handler(void);
282911106dfSjm 
283911106dfSjm 
284911106dfSjm /*
285911106dfSjm  * vscan_svc_init
286911106dfSjm  */
287911106dfSjm int
vscan_svc_init()288911106dfSjm vscan_svc_init()
289911106dfSjm {
290bfc848c6Sjm 	if (vscan_svc_state != VS_SVC_UNCONFIG) {
291bfc848c6Sjm 		DTRACE_PROBE1(vscan__svc__state__violation,
292bfc848c6Sjm 		    int, vscan_svc_state);
293bfc848c6Sjm 		return (-1);
294bfc848c6Sjm 	}
295bfc848c6Sjm 
296bfc848c6Sjm 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
297bfc848c6Sjm 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
298bfc848c6Sjm 	cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
299bfc848c6Sjm 
300bfc848c6Sjm 	vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
301bfc848c6Sjm 	vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
302bfc848c6Sjm 
303bfc848c6Sjm 	vscan_svc_counts.vsc_reql = 0;
304bfc848c6Sjm 	vscan_svc_counts.vsc_node = 0;
305bfc848c6Sjm 	vscan_svc_counts.vsc_tq = 0;
306bfc848c6Sjm 
307bfc848c6Sjm 	vscan_svc_state = VS_SVC_IDLE;
308911106dfSjm 
309911106dfSjm 	return (0);
310911106dfSjm }
311911106dfSjm 
312bfc848c6Sjm 
313911106dfSjm /*
314911106dfSjm  * vscan_svc_fini
315911106dfSjm  */
316911106dfSjm void
vscan_svc_fini()317911106dfSjm vscan_svc_fini()
318911106dfSjm {
319bfc848c6Sjm 	if (vscan_svc_state != VS_SVC_IDLE) {
320bfc848c6Sjm 		DTRACE_PROBE1(vscan__svc__state__violation,
321bfc848c6Sjm 		    int, vscan_svc_state);
322bfc848c6Sjm 		return;
323bfc848c6Sjm 	}
324911106dfSjm 
325bfc848c6Sjm 	kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
326bfc848c6Sjm 
327bfc848c6Sjm 	cv_destroy(&vscan_svc_reql_cv);
328911106dfSjm 	mutex_destroy(&vscan_svc_mutex);
329911106dfSjm 	mutex_destroy(&vscan_svc_cfg_mutex);
330bfc848c6Sjm 	vscan_svc_state = VS_SVC_UNCONFIG;
331911106dfSjm }
332911106dfSjm 
333bfc848c6Sjm 
334911106dfSjm /*
335911106dfSjm  * vscan_svc_enable
336911106dfSjm  */
337bfc848c6Sjm int
vscan_svc_enable(void)33853c11029Sjm vscan_svc_enable(void)
339911106dfSjm {
34053c11029Sjm 	mutex_enter(&vscan_svc_mutex);
341bfc848c6Sjm 
342bfc848c6Sjm 	switch (vscan_svc_state) {
343bfc848c6Sjm 	case VS_SVC_ENABLED:
344bfc848c6Sjm 		/*
345bfc848c6Sjm 		 * it's possible (and okay) for vscan_svc_enable to be
346bfc848c6Sjm 		 * called when already enabled if vscand reconnects
347bfc848c6Sjm 		 * during a delayed disable
348bfc848c6Sjm 		 */
349bfc848c6Sjm 		break;
350bfc848c6Sjm 	case VS_SVC_IDLE:
351bfc848c6Sjm 		list_create(&vscan_svc_reql, sizeof (vscan_req_t),
352bfc848c6Sjm 		    offsetof(vscan_req_t, vsr_lnode));
353bfc848c6Sjm 		vscan_svc_reql_next = list_head(&vscan_svc_reql);
354bfc848c6Sjm 
355bfc848c6Sjm 		vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
356bfc848c6Sjm 		    MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
357bfc848c6Sjm 		ASSERT(vscan_svc_taskq != NULL);
358bfc848c6Sjm 
359bfc848c6Sjm 		vscan_svc_reql_thread = thread_create(NULL, 0,
360bfc848c6Sjm 		    vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
361bfc848c6Sjm 		ASSERT(vscan_svc_reql_thread != NULL);
362bfc848c6Sjm 
363bfc848c6Sjm 		/* ready to start processing requests */
364bfc848c6Sjm 		vscan_svc_state = VS_SVC_ENABLED;
365bfc848c6Sjm 		fs_vscan_register(vscan_svc_scan_file);
366bfc848c6Sjm 		break;
367bfc848c6Sjm 	default:
368bfc848c6Sjm 		DTRACE_PROBE1(vscan__svc__state__violation,
369bfc848c6Sjm 		    int, vscan_svc_state);
370bfc848c6Sjm 		return (-1);
37153c11029Sjm 	}
372911106dfSjm 
37353c11029Sjm 	mutex_exit(&vscan_svc_mutex);
374bfc848c6Sjm 	return (0);
375911106dfSjm }
376911106dfSjm 
37753c11029Sjm 
37853c11029Sjm /*
37953c11029Sjm  * vscan_svc_disable
380bfc848c6Sjm  *
381bfc848c6Sjm  * Resources allocated during vscan_svc_enable are free'd by
382bfc848c6Sjm  * the handler thread immediately prior to exiting
38353c11029Sjm  */
38453c11029Sjm void
vscan_svc_disable(void)38553c11029Sjm vscan_svc_disable(void)
38653c11029Sjm {
38753c11029Sjm 	mutex_enter(&vscan_svc_mutex);
38853c11029Sjm 
389bfc848c6Sjm 	switch (vscan_svc_state) {
390bfc848c6Sjm 	case VS_SVC_ENABLED:
391bfc848c6Sjm 		fs_vscan_register(NULL);
392bfc848c6Sjm 		vscan_svc_state = VS_SVC_DISABLED;
393bfc848c6Sjm 		cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
394bfc848c6Sjm 		break;
395bfc848c6Sjm 	default:
396bfc848c6Sjm 		DTRACE_PROBE1(vscan__svc__state__violation, int,
397bfc848c6Sjm 		    vscan_svc_state);
39853c11029Sjm 	}
39953c11029Sjm 
400bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
40153c11029Sjm }
40253c11029Sjm 
40353c11029Sjm 
404911106dfSjm /*
405911106dfSjm  * vscan_svc_in_use
406911106dfSjm  */
407911106dfSjm boolean_t
vscan_svc_in_use()408911106dfSjm vscan_svc_in_use()
409911106dfSjm {
410bfc848c6Sjm 	boolean_t in_use;
411911106dfSjm 
412911106dfSjm 	mutex_enter(&vscan_svc_mutex);
413911106dfSjm 
414bfc848c6Sjm 	switch (vscan_svc_state) {
415bfc848c6Sjm 	case VS_SVC_IDLE:
416bfc848c6Sjm 	case VS_SVC_UNCONFIG:
417bfc848c6Sjm 		in_use = B_FALSE;
418bfc848c6Sjm 		break;
419bfc848c6Sjm 	default:
420bfc848c6Sjm 		in_use = B_TRUE;
421bfc848c6Sjm 		break;
422bfc848c6Sjm 	}
423bfc848c6Sjm 
424bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
425bfc848c6Sjm 	return (in_use);
426911106dfSjm }
427911106dfSjm 
428bfc848c6Sjm 
429911106dfSjm /*
430911106dfSjm  * vscan_svc_get_vnode
431911106dfSjm  *
432911106dfSjm  * Get the file vnode indexed by idx.
433911106dfSjm  */
434911106dfSjm vnode_t *
vscan_svc_get_vnode(int idx)435911106dfSjm vscan_svc_get_vnode(int idx)
436911106dfSjm {
437bfc848c6Sjm 	vnode_t *vp = NULL;
438bfc848c6Sjm 
439911106dfSjm 	ASSERT(idx > 0);
440bfc848c6Sjm 	ASSERT(idx <= vs_nodes_max);
441911106dfSjm 
442bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
443bfc848c6Sjm 	if (vscan_svc_nodes[idx].vsn_req)
444bfc848c6Sjm 		vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
445bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
446bfc848c6Sjm 
447bfc848c6Sjm 	return (vp);
448911106dfSjm }
449911106dfSjm 
450911106dfSjm 
451911106dfSjm /*
452911106dfSjm  * vscan_svc_scan_file
453911106dfSjm  *
454911106dfSjm  * This function is the entry point for the file system to
455911106dfSjm  * request that a file be virus scanned.
456911106dfSjm  */
457911106dfSjm int
vscan_svc_scan_file(vnode_t * vp,cred_t * cr,int async)458911106dfSjm vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
459911106dfSjm {
460bfc848c6Sjm 	int access;
461bfc848c6Sjm 	vscan_req_t *req;
462911106dfSjm 	boolean_t allow;
463bfc848c6Sjm 	clock_t timeout, time_left;
464911106dfSjm 
465*e2fc3408SPatrick Mooney 	if ((vp == NULL) || (vp->v_path == vn_vpath_empty) || cr == NULL)
466911106dfSjm 		return (0);
467911106dfSjm 
468911106dfSjm 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
469911106dfSjm 
470911106dfSjm 	/* check if size or type exempts file from scanning */
471911106dfSjm 	if (vscan_svc_exempt_file(vp, &allow)) {
472911106dfSjm 		if ((allow == B_TRUE) || (async != 0))
473911106dfSjm 			return (0);
474911106dfSjm 
475911106dfSjm 		return (EACCES);
476911106dfSjm 	}
477911106dfSjm 
478bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
479911106dfSjm 
480bfc848c6Sjm 	if (vscan_svc_state != VS_SVC_ENABLED) {
481bfc848c6Sjm 		DTRACE_PROBE1(vscan__svc__state__violation,
482bfc848c6Sjm 		    int, vscan_svc_state);
483bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
484bfc848c6Sjm 		return (0);
485bfc848c6Sjm 	}
486911106dfSjm 
487bfc848c6Sjm 	/* insert (or find) request in list */
488bfc848c6Sjm 	if ((req = vscan_svc_reql_insert(vp)) == NULL) {
489bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
490bfc848c6Sjm 		cmn_err(CE_WARN, "Virus scan request list full");
491bfc848c6Sjm 		return ((async != 0) ? 0 : EACCES);
492911106dfSjm 	}
493911106dfSjm 
494bfc848c6Sjm 	/* asynchronous request: return 0 */
495bfc848c6Sjm 	if (async) {
496bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
497bfc848c6Sjm 		return (0);
498bfc848c6Sjm 	}
499911106dfSjm 
500bfc848c6Sjm 	/* synchronous scan request: wait for result */
501bfc848c6Sjm 	++(req->vsr_refcnt);
502bfc848c6Sjm 	time_left = SEC_TO_TICK(vs_scan_wait);
503bfc848c6Sjm 	while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
504d3d50737SRafael Vanoni 		timeout = time_left;
505d3d50737SRafael Vanoni 		time_left = cv_reltimedwait_sig(&(req->vsr_cv),
506d3d50737SRafael Vanoni 		    &vscan_svc_mutex, timeout, TR_CLOCK_TICK);
507bfc848c6Sjm 	}
508911106dfSjm 
509bfc848c6Sjm 	if (time_left == -1) {
510bfc848c6Sjm 		cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
511bfc848c6Sjm 		    vp->v_path, req->vsr_seqnum);
512bfc848c6Sjm 		DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
513bfc848c6Sjm 	}
514911106dfSjm 
515bfc848c6Sjm 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
516bfc848c6Sjm 	if (vscan_svc_state == VS_SVC_DISABLED)
517bfc848c6Sjm 		access = VS_ACCESS_ALLOW;
518bfc848c6Sjm 	else if (req->vsr_idx == 0)
519bfc848c6Sjm 		access = VS_ACCESS_DENY;
520bfc848c6Sjm 	else
521bfc848c6Sjm 		access = vscan_svc_nodes[req->vsr_idx].vsn_access;
522911106dfSjm 
523bfc848c6Sjm 	if ((--req->vsr_refcnt) == 0)
524bfc848c6Sjm 		vscan_svc_delete_req(req);
525911106dfSjm 
526911106dfSjm 	mutex_exit(&vscan_svc_mutex);
527bfc848c6Sjm 	return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
528911106dfSjm }
529911106dfSjm 
530911106dfSjm 
531911106dfSjm /*
532bfc848c6Sjm  * vscan_svc_reql_handler
533911106dfSjm  *
534bfc848c6Sjm  * inserts scan requests (from vscan_svc_reql) into
535bfc848c6Sjm  * vscan_svc_nodes and vscan_svc_taskq
536911106dfSjm  */
537bfc848c6Sjm static void
vscan_svc_reql_handler(void)538bfc848c6Sjm vscan_svc_reql_handler(void)
539911106dfSjm {
540bfc848c6Sjm 	vscan_req_t *req, *next;
541911106dfSjm 
542bfc848c6Sjm 	for (;;) {
543bfc848c6Sjm 		mutex_enter(&vscan_svc_mutex);
544bfc848c6Sjm 
545bfc848c6Sjm 		if ((vscan_svc_state == VS_SVC_DISABLED) &&
546bfc848c6Sjm 		    (vscan_svc_counts.vsc_reql == 0)) {
547bfc848c6Sjm 			/* free resources allocated durining enable */
548bfc848c6Sjm 			taskq_destroy(vscan_svc_taskq);
549bfc848c6Sjm 			vscan_svc_taskq = NULL;
550bfc848c6Sjm 			list_destroy(&vscan_svc_reql);
551bfc848c6Sjm 			vscan_svc_state = VS_SVC_IDLE;
552bfc848c6Sjm 			mutex_exit(&vscan_svc_mutex);
553bfc848c6Sjm 			return;
554bfc848c6Sjm 		}
555911106dfSjm 
556bfc848c6Sjm 		/*
557bfc848c6Sjm 		 * If disabled, scan_complete any pending requests.
558bfc848c6Sjm 		 * Otherwise insert pending requests into vscan_svc_nodes
559bfc848c6Sjm 		 * and vscan_svc_taskq. If no slots are available in
560bfc848c6Sjm 		 * vscan_svc_nodes break loop and wait for one
561bfc848c6Sjm 		 */
562bfc848c6Sjm 		req = vscan_svc_reql_next;
563bfc848c6Sjm 
564bfc848c6Sjm 		while (req != NULL) {
565bfc848c6Sjm 			ASSERT(req->vsr_magic == VS_REQ_MAGIC);
566bfc848c6Sjm 			next = list_next(&vscan_svc_reql, req);
567bfc848c6Sjm 
568bfc848c6Sjm 			if (vscan_svc_state == VS_SVC_DISABLED) {
569bfc848c6Sjm 				vscan_svc_scan_complete(req);
570911106dfSjm 			} else {
571bfc848c6Sjm 				/* insert request into vscan_svc_nodes */
572bfc848c6Sjm 				if (vscan_svc_insert_req(req) == -1)
573bfc848c6Sjm 					break;
574bfc848c6Sjm 
575bfc848c6Sjm 				/* add the scan request into the taskq */
576bfc848c6Sjm 				(void) taskq_dispatch(vscan_svc_taskq,
577bfc848c6Sjm 				    vscan_svc_taskq_callback,
578bfc848c6Sjm 				    (void *)req, TQ_SLEEP);
579bfc848c6Sjm 				++(vscan_svc_counts.vsc_tq);
580bfc848c6Sjm 
581bfc848c6Sjm 				req->vsr_state = VS_SVC_REQ_QUEUED;
582911106dfSjm 			}
583bfc848c6Sjm 			req = next;
584911106dfSjm 		}
585bfc848c6Sjm 
586bfc848c6Sjm 		vscan_svc_reql_next = req;
587bfc848c6Sjm 
588bfc848c6Sjm 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
589bfc848c6Sjm 		    vscan_svc_counts_t *, &vscan_svc_counts);
590bfc848c6Sjm 
591d3d50737SRafael Vanoni 		(void) cv_reltimedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
592d3d50737SRafael Vanoni 		    SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT), TR_CLOCK_TICK);
593bfc848c6Sjm 
594bfc848c6Sjm 		DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
595bfc848c6Sjm 		    vscan_svc_counts_t *, &vscan_svc_counts);
596bfc848c6Sjm 
597bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
598911106dfSjm 	}
599bfc848c6Sjm }
600911106dfSjm 
601911106dfSjm 
602bfc848c6Sjm static void
vscan_svc_taskq_callback(void * data)603bfc848c6Sjm vscan_svc_taskq_callback(void *data)
604bfc848c6Sjm {
605bfc848c6Sjm 	vscan_req_t *req;
606911106dfSjm 
607bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
608911106dfSjm 
609bfc848c6Sjm 	req = (vscan_req_t *)data;
610bfc848c6Sjm 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
611bfc848c6Sjm 	vscan_svc_do_scan(req);
612bfc848c6Sjm 	if (req->vsr_state != VS_SVC_REQ_SCANNING)
613bfc848c6Sjm 		vscan_svc_scan_complete(req);
614911106dfSjm 
615bfc848c6Sjm 	--(vscan_svc_counts.vsc_tq);
616bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
617911106dfSjm }
618911106dfSjm 
61953c11029Sjm 
62053c11029Sjm /*
621bfc848c6Sjm  * vscan_svc_do_scan
62253c11029Sjm  *
623bfc848c6Sjm  * Note: To avoid potential deadlock it is important that
624bfc848c6Sjm  * vscan_svc_mutex is not held during the call to
625bfc848c6Sjm  * vscan_drv_create_note. vscan_drv_create_note enters
626bfc848c6Sjm  * the vscan_drv_mutex and it is possible that a thread
627bfc848c6Sjm  * holding that mutex could be waiting for vscan_svc_mutex.
62853c11029Sjm  */
62953c11029Sjm static void
vscan_svc_do_scan(vscan_req_t * req)630bfc848c6Sjm vscan_svc_do_scan(vscan_req_t *req)
63153c11029Sjm {
632bfc848c6Sjm 	int idx, result;
633bfc848c6Sjm 	vscan_svc_node_t *node;
634bfc848c6Sjm 	vs_scan_req_t *door_req;
63553c11029Sjm 
63653c11029Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
63753c11029Sjm 
638bfc848c6Sjm 	idx = req->vsr_idx;
639bfc848c6Sjm 	node = &vscan_svc_nodes[idx];
64053c11029Sjm 
641bfc848c6Sjm 	req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
64253c11029Sjm 
643bfc848c6Sjm 	/* if vscan not enabled (shutting down), allow ACCESS */
644bfc848c6Sjm 	if (vscan_svc_state != VS_SVC_ENABLED) {
645bfc848c6Sjm 		node->vsn_access = VS_ACCESS_ALLOW;
64653c11029Sjm 		return;
647bfc848c6Sjm 	}
64853c11029Sjm 
649bfc848c6Sjm 	if (vscan_svc_getattr(idx) != 0) {
650bfc848c6Sjm 		cmn_err(CE_WARN, "Can't access xattr for %s\n",
651bfc848c6Sjm 		    req->vsr_vp->v_path);
652bfc848c6Sjm 		node->vsn_access = VS_ACCESS_DENY;
65353c11029Sjm 		return;
654bfc848c6Sjm 	}
65553c11029Sjm 
656bfc848c6Sjm 	/* valid scan_req ptr guaranteed */
657bfc848c6Sjm 	door_req = vscan_svc_populate_req(idx);
658bfc848c6Sjm 
659bfc848c6Sjm 	/* free up mutex around create node and door call */
660bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
661bfc848c6Sjm 	if (vscan_drv_create_node(idx) != B_TRUE)
662bfc848c6Sjm 		result = VS_STATUS_ERROR;
663bfc848c6Sjm 	else
664bfc848c6Sjm 		result = vscan_door_scan_file(door_req);
665bfc848c6Sjm 	kmem_free(door_req, sizeof (vs_scan_req_t));
666bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
667bfc848c6Sjm 
668bfc848c6Sjm 	if (result != VS_STATUS_SCANNING) {
669bfc848c6Sjm 		vscan_svc_nodes[idx].vsn_result = result;
670bfc848c6Sjm 		vscan_svc_process_scan_result(idx);
671bfc848c6Sjm 	} else { /* async response */
672bfc848c6Sjm 		if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
673bfc848c6Sjm 			req->vsr_state = VS_SVC_REQ_SCANNING;
67453c11029Sjm 	}
67553c11029Sjm }
67653c11029Sjm 
67753c11029Sjm 
678911106dfSjm /*
679bfc848c6Sjm  * vscan_svc_populate_req
680911106dfSjm  *
681bfc848c6Sjm  * Allocate a scan request to be sent to vscand, populating it
682bfc848c6Sjm  * from the data in vscan_svc_nodes[idx].
683911106dfSjm  *
684bfc848c6Sjm  * Returns: scan request object
685911106dfSjm  */
686bfc848c6Sjm static vs_scan_req_t *
vscan_svc_populate_req(int idx)687bfc848c6Sjm vscan_svc_populate_req(int idx)
688911106dfSjm {
689bfc848c6Sjm 	vs_scan_req_t *scan_req;
690bfc848c6Sjm 	vscan_req_t *req;
691bfc848c6Sjm 	vscan_svc_node_t *node;
692911106dfSjm 
693911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
694911106dfSjm 
695bfc848c6Sjm 	node = &vscan_svc_nodes[idx];
696bfc848c6Sjm 	req = node->vsn_req;
697bfc848c6Sjm 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
698911106dfSjm 
699bfc848c6Sjm 	scan_req->vsr_idx = idx;
700bfc848c6Sjm 	scan_req->vsr_seqnum = req->vsr_seqnum;
701bfc848c6Sjm 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
702bfc848c6Sjm 	scan_req->vsr_size = node->vsn_size;
703bfc848c6Sjm 	scan_req->vsr_modified = node->vsn_modified;
704bfc848c6Sjm 	scan_req->vsr_quarantined = node->vsn_quarantined;
705bfc848c6Sjm 	scan_req->vsr_flags = 0;
706bfc848c6Sjm 	(void) strncpy(scan_req->vsr_scanstamp,
707bfc848c6Sjm 	    node->vsn_scanstamp, sizeof (vs_scanstamp_t));
708911106dfSjm 
709bfc848c6Sjm 	return (scan_req);
710911106dfSjm }
711911106dfSjm 
712911106dfSjm 
713911106dfSjm /*
714bfc848c6Sjm  * vscan_svc_scan_complete
715911106dfSjm  */
716bfc848c6Sjm static void
vscan_svc_scan_complete(vscan_req_t * req)717bfc848c6Sjm vscan_svc_scan_complete(vscan_req_t *req)
718911106dfSjm {
719911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
720bfc848c6Sjm 	ASSERT(req != NULL);
721911106dfSjm 
722bfc848c6Sjm 	req->vsr_state = VS_SVC_REQ_COMPLETE;
723bfc848c6Sjm 
724bfc848c6Sjm 	if ((--req->vsr_refcnt) == 0)
725bfc848c6Sjm 		vscan_svc_delete_req(req);
726bfc848c6Sjm 	else
727bfc848c6Sjm 		cv_broadcast(&(req->vsr_cv));
728911106dfSjm }
729911106dfSjm 
730911106dfSjm 
731911106dfSjm /*
732bfc848c6Sjm  * vscan_svc_delete_req
733911106dfSjm  */
734bfc848c6Sjm static void
vscan_svc_delete_req(vscan_req_t * req)735bfc848c6Sjm vscan_svc_delete_req(vscan_req_t *req)
736911106dfSjm {
737911106dfSjm 	int idx;
738911106dfSjm 
739911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
740bfc848c6Sjm 	ASSERT(req != NULL);
741bfc848c6Sjm 	ASSERT(req->vsr_refcnt == 0);
742bfc848c6Sjm 	ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
743911106dfSjm 
744bfc848c6Sjm 	if ((idx = req->vsr_idx) != 0)
745bfc848c6Sjm 		vscan_svc_remove_req(idx);
74653c11029Sjm 
747bfc848c6Sjm 	vscan_svc_reql_remove(req);
748911106dfSjm 
749bfc848c6Sjm 	cv_signal(&vscan_svc_reql_cv);
750911106dfSjm }
751911106dfSjm 
752911106dfSjm 
753911106dfSjm /*
754bfc848c6Sjm  * vscan_svc_scan_result
755911106dfSjm  *
756bfc848c6Sjm  * Invoked from vscan_drv.c on receipt of an ioctl containing
757bfc848c6Sjm  * an async scan result (VS_DRV_IOCTL_RESULT)
758bfc848c6Sjm  * If the vsr_seqnum in the response does not match that in the
759bfc848c6Sjm  * vscan_svc_nodes entry the result is discarded.
760911106dfSjm  */
761bfc848c6Sjm void
vscan_svc_scan_result(vs_scan_rsp_t * scan_rsp)762bfc848c6Sjm vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
763911106dfSjm {
764bfc848c6Sjm 	vscan_req_t *req;
765bfc848c6Sjm 	vscan_svc_node_t *node;
766911106dfSjm 
767bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
768bfc848c6Sjm 
769bfc848c6Sjm 	node = &vscan_svc_nodes[scan_rsp->vsr_idx];
770bfc848c6Sjm 
771bfc848c6Sjm 	if ((req = node->vsn_req) == NULL) {
772bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
773bfc848c6Sjm 		return;
774bfc848c6Sjm 	}
775911106dfSjm 
776bfc848c6Sjm 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
777bfc848c6Sjm 
778bfc848c6Sjm 	if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
779bfc848c6Sjm 		mutex_exit(&vscan_svc_mutex);
780911106dfSjm 		return;
781bfc848c6Sjm 	}
782911106dfSjm 
783bfc848c6Sjm 	node->vsn_result = scan_rsp->vsr_result;
784bfc848c6Sjm 	(void) strncpy(node->vsn_scanstamp,
785bfc848c6Sjm 	    scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
78653c11029Sjm 
787bfc848c6Sjm 	vscan_svc_process_scan_result(scan_rsp->vsr_idx);
788bfc848c6Sjm 
789bfc848c6Sjm 	if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
790bfc848c6Sjm 		vscan_svc_scan_complete(node->vsn_req);
791bfc848c6Sjm 	else
792bfc848c6Sjm 		node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
793bfc848c6Sjm 
794bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
795911106dfSjm }
796911106dfSjm 
797911106dfSjm 
798911106dfSjm /*
799bfc848c6Sjm  * vscan_svc_scan_abort
800911106dfSjm  *
801bfc848c6Sjm  * Abort in-progress scan requests.
802911106dfSjm  */
803bfc848c6Sjm void
vscan_svc_scan_abort()804bfc848c6Sjm vscan_svc_scan_abort()
805911106dfSjm {
806bfc848c6Sjm 	int idx;
807bfc848c6Sjm 	vscan_req_t *req;
808911106dfSjm 
809bfc848c6Sjm 	mutex_enter(&vscan_svc_mutex);
810911106dfSjm 
811bfc848c6Sjm 	for (idx = 1; idx <= vs_nodes_max; idx++) {
812bfc848c6Sjm 		if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
813bfc848c6Sjm 			continue;
814911106dfSjm 
815bfc848c6Sjm 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
816911106dfSjm 
817bfc848c6Sjm 		if (req->vsr_state == VS_SVC_REQ_SCANNING) {
818bfc848c6Sjm 			DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
819bfc848c6Sjm 			vscan_svc_process_scan_result(idx);
820bfc848c6Sjm 			vscan_svc_scan_complete(req);
821bfc848c6Sjm 		}
822bfc848c6Sjm 	}
823bfc848c6Sjm 
824bfc848c6Sjm 	mutex_exit(&vscan_svc_mutex);
825911106dfSjm }
826911106dfSjm 
827911106dfSjm 
828911106dfSjm /*
829bfc848c6Sjm  * vscan_svc_process_scan_result
830bfc848c6Sjm  *
831bfc848c6Sjm  * Sets vsn_access and updates file attributes based on vsn_result,
832bfc848c6Sjm  * as follows:
833911106dfSjm  *
834bfc848c6Sjm  * VS_STATUS_INFECTED
835bfc848c6Sjm  *  deny access, set quarantine attribute, clear scanstamp
836bfc848c6Sjm  * VS_STATUS_CLEAN
837bfc848c6Sjm  *  allow access, set scanstamp,
838bfc848c6Sjm  *  if file not modified since scan initiated, clear modified attribute
839bfc848c6Sjm  * VS_STATUS_NO_SCAN
840bfc848c6Sjm  *  deny access if file quarantined, otherwise allow access
841bfc848c6Sjm  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
842bfc848c6Sjm  *  deny access if file quarantined, modified or no scanstamp
843bfc848c6Sjm  *  otherwise, allow access
844911106dfSjm  */
845911106dfSjm static void
vscan_svc_process_scan_result(int idx)846bfc848c6Sjm vscan_svc_process_scan_result(int idx)
847911106dfSjm {
848bfc848c6Sjm 	struct vattr attr;
849bfc848c6Sjm 	vnode_t *vp;
850bfc848c6Sjm 	timestruc_t *mtime;
851bfc848c6Sjm 	vscan_svc_node_t *node;
85253c11029Sjm 
853911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
854911106dfSjm 
855bfc848c6Sjm 	node = &vscan_svc_nodes[idx];
856911106dfSjm 
857bfc848c6Sjm 	switch (node->vsn_result) {
858bfc848c6Sjm 	case VS_STATUS_INFECTED:
859bfc848c6Sjm 		node->vsn_access = VS_ACCESS_DENY;
860bfc848c6Sjm 		node->vsn_quarantined = 1;
861bfc848c6Sjm 		node->vsn_scanstamp[0] = '\0';
862bfc848c6Sjm 		(void) vscan_svc_setattr(idx,
863bfc848c6Sjm 		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
864bfc848c6Sjm 		break;
865911106dfSjm 
866bfc848c6Sjm 	case VS_STATUS_CLEAN:
867bfc848c6Sjm 		node->vsn_access = VS_ACCESS_ALLOW;
86853c11029Sjm 
869bfc848c6Sjm 		/* if mtime has changed, don't clear the modified attribute */
870bfc848c6Sjm 		vp = node->vsn_req->vsr_vp;
871bfc848c6Sjm 		mtime = &(node->vsn_mtime);
872bfc848c6Sjm 		attr.va_mask = AT_MTIME;
873bfc848c6Sjm 		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
874bfc848c6Sjm 		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
875bfc848c6Sjm 		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
876bfc848c6Sjm 			DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
877bfc848c6Sjm 			    node);
878bfc848c6Sjm 			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
879bfc848c6Sjm 			break;
880bfc848c6Sjm 		}
881bfc848c6Sjm 
882bfc848c6Sjm 		node->vsn_modified = 0;
883bfc848c6Sjm 		(void) vscan_svc_setattr(idx,
884bfc848c6Sjm 		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
885bfc848c6Sjm 		break;
886911106dfSjm 
887bfc848c6Sjm 	case VS_STATUS_NO_SCAN:
888bfc848c6Sjm 		if (node->vsn_quarantined)
889bfc848c6Sjm 			node->vsn_access = VS_ACCESS_DENY;
890bfc848c6Sjm 		else
891bfc848c6Sjm 			node->vsn_access = VS_ACCESS_ALLOW;
892bfc848c6Sjm 		break;
89353c11029Sjm 
894bfc848c6Sjm 	case VS_STATUS_ERROR:
895bfc848c6Sjm 	case VS_STATUS_UNDEFINED:
896bfc848c6Sjm 	default:
897bfc848c6Sjm 		if ((node->vsn_quarantined) ||
898bfc848c6Sjm 		    (node->vsn_modified) ||
899bfc848c6Sjm 		    (node->vsn_scanstamp[0] == '\0'))
900bfc848c6Sjm 			node->vsn_access = VS_ACCESS_DENY;
901bfc848c6Sjm 		else
902bfc848c6Sjm 			node->vsn_access = VS_ACCESS_ALLOW;
903bfc848c6Sjm 		break;
904bfc848c6Sjm 	}
905911106dfSjm 
906bfc848c6Sjm 	DTRACE_PROBE4(vscan__result,
907bfc848c6Sjm 	    int, idx, int, node->vsn_req->vsr_seqnum,
908bfc848c6Sjm 	    int, node->vsn_result, int, node->vsn_access);
909911106dfSjm }
910911106dfSjm 
911911106dfSjm 
912911106dfSjm /*
913911106dfSjm  * vscan_svc_getattr
914911106dfSjm  *
91553c11029Sjm  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
916911106dfSjm  */
917911106dfSjm static int
vscan_svc_getattr(int idx)918911106dfSjm vscan_svc_getattr(int idx)
919911106dfSjm {
920911106dfSjm 	xvattr_t xvattr;
921911106dfSjm 	xoptattr_t *xoap = NULL;
922911106dfSjm 	vnode_t *vp;
923bfc848c6Sjm 	vscan_svc_node_t *node;
924911106dfSjm 
925911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
926911106dfSjm 
927bfc848c6Sjm 	node = &vscan_svc_nodes[idx];
928bfc848c6Sjm 	if ((vp = node->vsn_req->vsr_vp) == NULL)
929911106dfSjm 		return (-1);
930911106dfSjm 
931911106dfSjm 	/* get the attributes */
932911106dfSjm 	xva_init(&xvattr); /* sets AT_XVATTR */
933911106dfSjm 
934911106dfSjm 	xvattr.xva_vattr.va_mask |= AT_SIZE;
93553c11029Sjm 	xvattr.xva_vattr.va_mask |= AT_MTIME;
936911106dfSjm 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
937911106dfSjm 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
938911106dfSjm 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
939911106dfSjm 
940911106dfSjm 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
941911106dfSjm 		return (-1);
942911106dfSjm 
943911106dfSjm 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
944911106dfSjm 		cmn_err(CE_NOTE, "Virus scan request failed; "
945911106dfSjm 		    "file system does not support virus scanning");
946911106dfSjm 		return (-1);
947911106dfSjm 	}
948911106dfSjm 
949bfc848c6Sjm 	node->vsn_size = xvattr.xva_vattr.va_size;
950bfc848c6Sjm 	node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
951bfc848c6Sjm 	node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
952911106dfSjm 
953911106dfSjm 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
954911106dfSjm 		return (-1);
955bfc848c6Sjm 	node->vsn_modified = xoap->xoa_av_modified;
956911106dfSjm 
957911106dfSjm 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
958911106dfSjm 		return (-1);
959bfc848c6Sjm 	node->vsn_quarantined = xoap->xoa_av_quarantined;
960911106dfSjm 
961911106dfSjm 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
962bfc848c6Sjm 		(void) memcpy(node->vsn_scanstamp,
963911106dfSjm 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
964911106dfSjm 	}
965911106dfSjm 
966bfc848c6Sjm 	DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
967911106dfSjm 	return (0);
968911106dfSjm }
969911106dfSjm 
970911106dfSjm 
971911106dfSjm /*
972911106dfSjm  * vscan_svc_setattr
973911106dfSjm  *
974911106dfSjm  * Set the vscan related system attributes.
975911106dfSjm  */
976911106dfSjm static int
vscan_svc_setattr(int idx,int which)97753c11029Sjm vscan_svc_setattr(int idx, int which)
978911106dfSjm {
979911106dfSjm 	xvattr_t xvattr;
980911106dfSjm 	xoptattr_t *xoap = NULL;
981911106dfSjm 	vnode_t *vp;
982911106dfSjm 	int len;
983bfc848c6Sjm 	vscan_svc_node_t *node;
984911106dfSjm 
985911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
986911106dfSjm 
987bfc848c6Sjm 	node = &vscan_svc_nodes[idx];
988bfc848c6Sjm 	if ((vp = node->vsn_req->vsr_vp) == NULL)
989911106dfSjm 		return (-1);
990911106dfSjm 
991911106dfSjm 	/* update the attributes */
992911106dfSjm 	xva_init(&xvattr); /* sets AT_XVATTR */
993911106dfSjm 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
994911106dfSjm 		return (-1);
995911106dfSjm 
99653c11029Sjm 	if (which & XAT_AV_MODIFIED) {
99753c11029Sjm 		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
998bfc848c6Sjm 		xoap->xoa_av_modified = node->vsn_modified;
99953c11029Sjm 	}
1000911106dfSjm 
100153c11029Sjm 	if (which & XAT_AV_QUARANTINED) {
100253c11029Sjm 		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
1003bfc848c6Sjm 		xoap->xoa_av_quarantined = node->vsn_quarantined;
100453c11029Sjm 	}
1005911106dfSjm 
100653c11029Sjm 	if (which & XAT_AV_SCANSTAMP) {
100753c11029Sjm 		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
1008bfc848c6Sjm 		len = strlen(node->vsn_scanstamp);
100953c11029Sjm 		(void) memcpy(xoap->xoa_av_scanstamp,
1010bfc848c6Sjm 		    node->vsn_scanstamp, len);
101153c11029Sjm 	}
1012911106dfSjm 
1013911106dfSjm 	/* if access is denied, set mtime to invalidate client cache */
1014bfc848c6Sjm 	if (node->vsn_access != VS_ACCESS_ALLOW) {
1015911106dfSjm 		xvattr.xva_vattr.va_mask |= AT_MTIME;
1016911106dfSjm 		gethrestime(&xvattr.xva_vattr.va_mtime);
1017911106dfSjm 	}
1018911106dfSjm 
1019911106dfSjm 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
1020911106dfSjm 		return (-1);
1021911106dfSjm 
102253c11029Sjm 	DTRACE_PROBE2(vscan__setattr,
1023bfc848c6Sjm 	    vscan_svc_node_t *, node, int, which);
102453c11029Sjm 
1025911106dfSjm 	return (0);
1026911106dfSjm }
1027911106dfSjm 
1028911106dfSjm 
1029911106dfSjm /*
1030911106dfSjm  * vscan_svc_configure
1031911106dfSjm  *
1032911106dfSjm  * store configuration in vscan_svc_config
1033911106dfSjm  * set up vscan_svc_types array of pointers into
1034911106dfSjm  * vscan_svc_config.vsc_types for efficient searching
1035911106dfSjm  */
1036911106dfSjm int
vscan_svc_configure(vs_config_t * conf)1037911106dfSjm vscan_svc_configure(vs_config_t *conf)
1038911106dfSjm {
1039911106dfSjm 	int count = 0;
1040911106dfSjm 	char *p, *beg, *end;
1041911106dfSjm 
1042911106dfSjm 	mutex_enter(&vscan_svc_cfg_mutex);
1043911106dfSjm 
1044911106dfSjm 	vscan_svc_config = *conf;
1045911106dfSjm 
1046911106dfSjm 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
1047911106dfSjm 
1048911106dfSjm 	beg = vscan_svc_config.vsc_types;
1049911106dfSjm 	end = beg + vscan_svc_config.vsc_types_len;
1050911106dfSjm 
1051911106dfSjm 	for (p = beg; p < end; p += strlen(p) + 1) {
1052911106dfSjm 		if (count >= VS_TYPES_MAX) {
1053911106dfSjm 			mutex_exit(&vscan_svc_mutex);
1054911106dfSjm 			return (-1);
1055911106dfSjm 		}
1056911106dfSjm 
1057911106dfSjm 		vscan_svc_types[count] = p;
1058911106dfSjm 		++count;
1059911106dfSjm 	}
1060911106dfSjm 
1061911106dfSjm 	mutex_exit(&vscan_svc_cfg_mutex);
1062911106dfSjm 	return (0);
1063911106dfSjm }
1064911106dfSjm 
1065911106dfSjm 
1066911106dfSjm /*
1067911106dfSjm  * vscan_svc_exempt_file
1068911106dfSjm  *
1069911106dfSjm  * check if a file's size or type exempts it from virus scanning
1070911106dfSjm  *
1071911106dfSjm  * If the file is exempt from virus scanning, allow will be set
1072911106dfSjm  * to define whether files access should be allowed (B_TRUE) or
1073911106dfSjm  * denied (B_FALSE)
1074911106dfSjm  *
1075911106dfSjm  * Returns: 1 exempt
1076911106dfSjm  *          0 scan required
1077911106dfSjm  */
1078911106dfSjm static int
vscan_svc_exempt_file(vnode_t * vp,boolean_t * allow)1079911106dfSjm vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
1080911106dfSjm {
1081911106dfSjm 	struct vattr attr;
1082911106dfSjm 
1083911106dfSjm 	ASSERT(vp != NULL);
1084911106dfSjm 
1085911106dfSjm 	attr.va_mask = AT_SIZE;
1086911106dfSjm 
1087911106dfSjm 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
1088911106dfSjm 		*allow = B_FALSE;
1089911106dfSjm 		return (0);
1090911106dfSjm 	}
1091911106dfSjm 
1092911106dfSjm 	mutex_enter(&vscan_svc_cfg_mutex);
1093911106dfSjm 
1094911106dfSjm 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
1095911106dfSjm 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
1096911106dfSjm 		    vp->v_path, int, *allow);
1097911106dfSjm 
1098911106dfSjm 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
1099911106dfSjm 		mutex_exit(&vscan_svc_cfg_mutex);
1100911106dfSjm 		return (1);
1101911106dfSjm 	}
1102911106dfSjm 
1103911106dfSjm 	if (vscan_svc_exempt_filetype(vp->v_path)) {
1104911106dfSjm 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
1105911106dfSjm 		*allow = B_TRUE;
1106911106dfSjm 		mutex_exit(&vscan_svc_cfg_mutex);
1107911106dfSjm 		return (1);
1108911106dfSjm 	}
1109911106dfSjm 
1110911106dfSjm 	mutex_exit(&vscan_svc_cfg_mutex);
1111911106dfSjm 	return (0);
1112911106dfSjm }
1113911106dfSjm 
1114911106dfSjm 
1115911106dfSjm /*
1116911106dfSjm  * vscan_svc_exempt_filetype
1117911106dfSjm  *
1118911106dfSjm  * Each entry in vscan_svc_types includes a rule indicator (+,-)
1119911106dfSjm  * followed by the match string for file types to which the rule
1120911106dfSjm  * applies. Look for first match of file type in vscan_svc_types
1121911106dfSjm  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
1122911106dfSjm  * if the indicator is '+'.
1123911106dfSjm  * If vscan_svc_match_ext fails, or no match is found, return 0
1124911106dfSjm  * (not exempt)
1125911106dfSjm  *
1126911106dfSjm  * Returns 1: exempt, 0: not exempt
1127911106dfSjm  */
1128911106dfSjm static int
vscan_svc_exempt_filetype(char * filepath)1129911106dfSjm vscan_svc_exempt_filetype(char *filepath)
1130911106dfSjm {
1131911106dfSjm 	int i, rc, exempt = 0;
1132911106dfSjm 	char *filename, *ext;
1133911106dfSjm 
1134911106dfSjm 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
1135911106dfSjm 
1136911106dfSjm 	if ((filename = strrchr(filepath, '/')) == 0)
1137911106dfSjm 		filename = filepath;
1138911106dfSjm 	else
1139911106dfSjm 		filename++;
1140911106dfSjm 
1141911106dfSjm 	if ((ext = strrchr(filename, '.')) == NULL)
1142911106dfSjm 		ext = "";
1143911106dfSjm 	else
1144911106dfSjm 		ext++;
1145911106dfSjm 
1146911106dfSjm 	for (i = 0; i < VS_TYPES_MAX; i ++) {
1147911106dfSjm 		if (vscan_svc_types[i] == 0)
1148911106dfSjm 			break;
1149911106dfSjm 
1150911106dfSjm 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
1151911106dfSjm 		if (rc == -1)
1152911106dfSjm 			break;
1153911106dfSjm 		if (rc > 0) {
1154911106dfSjm 			DTRACE_PROBE2(vscan__type__match, char *, ext,
1155911106dfSjm 			    char *, vscan_svc_types[i]);
1156911106dfSjm 			exempt = (vscan_svc_types[i][0] == '-');
1157911106dfSjm 			break;
1158911106dfSjm 		}
1159911106dfSjm 	}
1160911106dfSjm 
1161911106dfSjm 	return (exempt);
1162911106dfSjm }
1163911106dfSjm 
1164911106dfSjm 
1165911106dfSjm /*
1166911106dfSjm  *  vscan_svc_match_ext
1167911106dfSjm  *
1168911106dfSjm  * Performs a case-insensitive match for two strings.  The first string
1169911106dfSjm  * argument can contain the wildcard characters '?' and '*'
1170911106dfSjm  *
1171911106dfSjm  * Returns: 0 no match
1172911106dfSjm  *          1 match
1173911106dfSjm  *         -1 recursion error
1174911106dfSjm  */
1175911106dfSjm static int
vscan_svc_match_ext(char * patn,char * str,int depth)1176911106dfSjm vscan_svc_match_ext(char *patn, char *str, int depth)
1177911106dfSjm {
1178911106dfSjm 	int c1, c2;
1179911106dfSjm 	if (depth > VS_EXT_RECURSE_DEPTH)
1180911106dfSjm 		return (-1);
1181911106dfSjm 
1182911106dfSjm 	for (;;) {
1183911106dfSjm 		switch (*patn) {
1184911106dfSjm 		case 0:
1185911106dfSjm 			return (*str == 0);
1186911106dfSjm 
1187911106dfSjm 		case '?':
1188911106dfSjm 			if (*str != 0) {
1189911106dfSjm 				str++;
1190911106dfSjm 				patn++;
1191911106dfSjm 				continue;
1192911106dfSjm 			}
1193911106dfSjm 			return (0);
1194911106dfSjm 
1195911106dfSjm 		case '*':
1196911106dfSjm 			patn++;
1197911106dfSjm 			if (*patn == 0)
1198911106dfSjm 				return (1);
1199911106dfSjm 
1200911106dfSjm 			while (*str) {
1201911106dfSjm 				if (vscan_svc_match_ext(patn, str, depth + 1))
1202911106dfSjm 					return (1);
1203911106dfSjm 				str++;
1204911106dfSjm 			}
1205911106dfSjm 			return (0);
1206911106dfSjm 
1207911106dfSjm 		default:
1208911106dfSjm 			if (*str != *patn) {
1209911106dfSjm 				c1 = *str;
1210911106dfSjm 				c2 = *patn;
1211911106dfSjm 
1212911106dfSjm 				c1 = tolower(c1);
1213911106dfSjm 				c2 = tolower(c2);
1214911106dfSjm 				if (c1 != c2)
1215911106dfSjm 					return (0);
1216911106dfSjm 			}
1217911106dfSjm 			str++;
1218911106dfSjm 			patn++;
1219911106dfSjm 			continue;
1220911106dfSjm 		}
1221911106dfSjm 	}
1222911106dfSjm 	/* NOT REACHED */
1223911106dfSjm }
1224bfc848c6Sjm 
1225bfc848c6Sjm 
1226bfc848c6Sjm /*
1227bfc848c6Sjm  * vscan_svc_insert_req
1228bfc848c6Sjm  *
1229bfc848c6Sjm  * Insert request in next available available slot in vscan_svc_nodes
1230bfc848c6Sjm  *
1231bfc848c6Sjm  * Returns: idx of slot, or -1 if no slot available
1232bfc848c6Sjm  */
1233bfc848c6Sjm static int
vscan_svc_insert_req(vscan_req_t * req)1234bfc848c6Sjm vscan_svc_insert_req(vscan_req_t *req)
1235bfc848c6Sjm {
1236bfc848c6Sjm 	int idx;
1237bfc848c6Sjm 	vscan_svc_node_t *node;
1238bfc848c6Sjm 
1239bfc848c6Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1240bfc848c6Sjm 
1241bfc848c6Sjm 	if (vscan_svc_counts.vsc_node == vs_nodes_max)
1242bfc848c6Sjm 		return (-1);
1243bfc848c6Sjm 
1244bfc848c6Sjm 	for (idx = 1; idx <= vs_nodes_max; idx++) {
1245bfc848c6Sjm 		if (vscan_svc_nodes[idx].vsn_req == NULL) {
1246bfc848c6Sjm 			req->vsr_idx = idx;
1247bfc848c6Sjm 
1248bfc848c6Sjm 			node = &vscan_svc_nodes[idx];
1249bfc848c6Sjm 			(void) memset(node, 0, sizeof (vscan_svc_node_t));
1250bfc848c6Sjm 			node->vsn_req = req;
1251bfc848c6Sjm 			node->vsn_modified = 1;
1252bfc848c6Sjm 			node->vsn_result = VS_STATUS_UNDEFINED;
1253bfc848c6Sjm 			node->vsn_access = VS_ACCESS_UNDEFINED;
1254bfc848c6Sjm 
1255bfc848c6Sjm 			++(vscan_svc_counts.vsc_node);
1256bfc848c6Sjm 			return (idx);
1257bfc848c6Sjm 		}
1258bfc848c6Sjm 	}
1259bfc848c6Sjm 
1260bfc848c6Sjm 	return (-1);
1261bfc848c6Sjm }
1262bfc848c6Sjm 
1263bfc848c6Sjm 
1264bfc848c6Sjm /*
1265bfc848c6Sjm  * vscan_svc_remove_req
1266bfc848c6Sjm  */
1267bfc848c6Sjm static void
vscan_svc_remove_req(int idx)1268bfc848c6Sjm vscan_svc_remove_req(int idx)
1269bfc848c6Sjm {
1270bfc848c6Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1271bfc848c6Sjm 
1272bfc848c6Sjm 	if (idx != 0) {
1273bfc848c6Sjm 		(void) memset(&vscan_svc_nodes[idx], 0,
1274bfc848c6Sjm 		    sizeof (vscan_svc_node_t));
1275bfc848c6Sjm 		--(vscan_svc_counts.vsc_node);
1276bfc848c6Sjm 	}
1277bfc848c6Sjm }
1278bfc848c6Sjm 
1279bfc848c6Sjm 
1280bfc848c6Sjm /*
1281bfc848c6Sjm  * vscan_svc_reql_find
1282bfc848c6Sjm  */
1283bfc848c6Sjm static vscan_req_t *
vscan_svc_reql_find(vnode_t * vp)1284bfc848c6Sjm vscan_svc_reql_find(vnode_t *vp)
1285bfc848c6Sjm {
1286bfc848c6Sjm 	vscan_req_t *req;
1287bfc848c6Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1288bfc848c6Sjm 
1289bfc848c6Sjm 	req = list_head(&vscan_svc_reql);
1290bfc848c6Sjm 
1291bfc848c6Sjm 	while (req != NULL) {
1292bfc848c6Sjm 		ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1293bfc848c6Sjm 		if ((req->vsr_vp == vp) &&
1294bfc848c6Sjm 		    (req->vsr_state != VS_SVC_REQ_COMPLETE))
1295bfc848c6Sjm 			break;
1296bfc848c6Sjm 
1297bfc848c6Sjm 		req = list_next(&vscan_svc_reql, req);
1298bfc848c6Sjm 	}
1299bfc848c6Sjm 
1300bfc848c6Sjm 	return (req);
1301bfc848c6Sjm }
1302bfc848c6Sjm 
1303bfc848c6Sjm 
1304bfc848c6Sjm /*
1305bfc848c6Sjm  * vscan_svc_reql_insert
1306bfc848c6Sjm  */
1307bfc848c6Sjm static vscan_req_t *
vscan_svc_reql_insert(vnode_t * vp)1308bfc848c6Sjm vscan_svc_reql_insert(vnode_t *vp)
1309bfc848c6Sjm {
1310bfc848c6Sjm 	vscan_req_t *req;
1311bfc848c6Sjm 
1312bfc848c6Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1313bfc848c6Sjm 
1314bfc848c6Sjm 	/* if request already in list then return it */
1315bfc848c6Sjm 	if ((req = vscan_svc_reql_find(vp)) != NULL)
1316bfc848c6Sjm 		return (req);
1317bfc848c6Sjm 
1318bfc848c6Sjm 	/* if list is full return NULL */
1319bfc848c6Sjm 	if (vscan_svc_counts.vsc_reql == vs_reqs_max)
1320bfc848c6Sjm 		return (NULL);
1321bfc848c6Sjm 
1322bfc848c6Sjm 	/* create a new request and insert into list */
1323bfc848c6Sjm 	VN_HOLD(vp);
1324bfc848c6Sjm 
1325bfc848c6Sjm 	req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
1326bfc848c6Sjm 
1327bfc848c6Sjm 	req->vsr_magic = VS_REQ_MAGIC;
1328bfc848c6Sjm 	if (vscan_svc_seqnum == UINT32_MAX)
1329bfc848c6Sjm 		vscan_svc_seqnum = 0;
1330bfc848c6Sjm 	req->vsr_seqnum = ++vscan_svc_seqnum;
1331bfc848c6Sjm 	req->vsr_vp = vp;
1332bfc848c6Sjm 	req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
1333bfc848c6Sjm 	req->vsr_state = VS_SVC_REQ_INIT;
1334bfc848c6Sjm 	cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
1335bfc848c6Sjm 
1336bfc848c6Sjm 	list_insert_tail(&vscan_svc_reql, req);
1337bfc848c6Sjm 	if (vscan_svc_reql_next == NULL)
1338bfc848c6Sjm 		vscan_svc_reql_next = req;
1339bfc848c6Sjm 
1340bfc848c6Sjm 	++(vscan_svc_counts.vsc_reql);
1341bfc848c6Sjm 
1342bfc848c6Sjm 	/* wake reql handler thread */
1343bfc848c6Sjm 	cv_signal(&vscan_svc_reql_cv);
1344bfc848c6Sjm 
1345bfc848c6Sjm 	return (req);
1346bfc848c6Sjm }
1347bfc848c6Sjm 
1348bfc848c6Sjm 
1349bfc848c6Sjm /*
1350bfc848c6Sjm  * vscan_svc_reql_remove
1351bfc848c6Sjm  */
1352bfc848c6Sjm static void
vscan_svc_reql_remove(vscan_req_t * req)1353bfc848c6Sjm vscan_svc_reql_remove(vscan_req_t *req)
1354bfc848c6Sjm {
1355bfc848c6Sjm 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1356bfc848c6Sjm 	ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1357bfc848c6Sjm 
1358bfc848c6Sjm 	if (vscan_svc_reql_next == req)
1359bfc848c6Sjm 		vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
1360bfc848c6Sjm 
1361bfc848c6Sjm 	list_remove(&vscan_svc_reql, req);
1362bfc848c6Sjm 	cv_destroy(&(req->vsr_cv));
1363bfc848c6Sjm 	VN_RELE(req->vsr_vp);
1364bfc848c6Sjm 
1365bfc848c6Sjm 	kmem_free(req, sizeof (vscan_req_t));
1366bfc848c6Sjm 	--(vscan_svc_counts.vsc_reql);
1367bfc848c6Sjm }
1368