xref: /titanic_50/usr/src/uts/common/io/vscan/vscan_svc.c (revision e79c98e6c943cb3032f272714ff4ce6137d40394)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/stat.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/time.h>
33 #include <sys/varargs.h>
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/cmn_err.h>
37 #include <sys/vnode.h>
38 #include <fs/fs_subr.h>
39 #include <sys/types.h>
40 #include <sys/file.h>
41 #include <sys/disp.h>
42 #include <sys/sdt.h>
43 #include <sys/cred.h>
44 #include <sys/vscan.h>
45 
46 #define	VS_TASKQ_NUM_THREADS	VS_DRV_MAX_FILES
47 #define	VS_EXT_RECURSE_DEPTH	8
48 #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
49 
50 /* represents request received from filesystem - currently only use vp */
51 typedef struct vscan_fs_req {
52 	vnode_t *vsr_vp;
53 } vscan_fs_req_t;
54 
55 /*
56  * vscan_svc_files - table of files being scanned
57  *
58  * The index into this table is passed in the door call to
59  * vscand. vscand uses the idx to determine which minor node
60  * to open to read the file data. Within the kernel driver
61  * the minor device number can thus be used to identify the
62  * table index to get the appropriate vnode.
63  *
64  * Instance 0 is reserved for the daemon/driver control
65  * interface: enable/configure/disable
66  */
67 typedef struct vscan_file {
68 	vscan_fs_req_t vsf_req;
69 	uint32_t vsf_wait_count;
70 	uint8_t vsf_quarantined;
71 	uint8_t vsf_modified;
72 	uint64_t vsf_size;
73 	vs_scanstamp_t vsf_scanstamp;
74 	uint32_t vsf_access;
75 } vscan_file_t;
76 
77 static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1];
78 static int vscan_svc_files_idx = 0; /* idx of most recently allocated slot */
79 static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */
80 static kcondvar_t vscan_svc_file_cv[VS_DRV_MAX_FILES + 1]; /* wait for scan */
81 static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */
82 static int vscan_svc_req_count = 0; /* # scan requests */
83 
84 static taskq_t *vscan_svc_taskq = NULL;
85 static boolean_t vscan_svc_enabled = B_FALSE;
86 
87 /*
88  * vscan_svc_mutex protects the data pertaining to scan requests:
89  * file table - vscan_svc_files
90  * counts - vscan_svc_wait_count, vscan_svc_req_count
91  */
92 static kmutex_t vscan_svc_mutex;
93 
94 /*
95  * vscan_svc_cfg_mutex protects the configuration data:
96  * vscan_svc_config, vscan_svc_types
97  */
98 static kmutex_t vscan_svc_cfg_mutex;
99 
100 /* configuration data - for virus scan exemption */
101 static vs_config_t vscan_svc_config;
102 static char *vscan_svc_types[VS_TYPES_MAX];
103 
104 /* local functions */
105 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
106 void vscan_svc_taskq_callback(void *);
107 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
108 static int vscan_svc_exempt_filetype(char *);
109 static int vscan_svc_match_ext(char *, char *, int);
110 static int vscan_svc_do_scan(vscan_fs_req_t *);
111 static int vscan_svc_wait_for_scan(vnode_t *);
112 static int vscan_svc_insert_file(vscan_fs_req_t *);
113 static void vscan_svc_release_file(int);
114 static int vscan_svc_find_slot(void);
115 static void vscan_svc_notify_scan_complete(int);
116 static int vscan_svc_getattr(int);
117 static int vscan_svc_setattr(int);
118 
119 static vs_scan_req_t *vscan_svc_populate_req(int);
120 static void vscan_svc_parse_rsp(int, vs_scan_req_t *);
121 
122 
123 /*
124  * vscan_svc_init
125  */
126 int
127 vscan_svc_init()
128 {
129 	int i;
130 
131 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL);
132 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL);
133 
134 	/* create task queue for async requests */
135 	if ((vscan_svc_taskq = taskq_create("vscan", VS_TASKQ_NUM_THREADS,
136 	    MINCLSYSPRI, 1, INT_MAX, 0)) == NULL) {
137 		cmn_err(CE_WARN, "All scan requests will be "
138 		    "processed synchronously");
139 	}
140 
141 	/* initialize vscan_svc_files table */
142 	(void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files));
143 
144 	/* initialize condition variables */
145 	cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL);
146 
147 	for (i = 0; i <= VS_DRV_MAX_FILES; i++)
148 		cv_init(&vscan_svc_file_cv[i], NULL, CV_DEFAULT, NULL);
149 
150 	return (0);
151 }
152 
153 /*
154  * vscan_svc_fini
155  */
156 void
157 vscan_svc_fini()
158 {
159 	int i;
160 
161 	ASSERT(vscan_svc_enabled == B_FALSE);
162 	ASSERT(vscan_svc_in_use() == B_FALSE);
163 
164 	if (vscan_svc_taskq)
165 		taskq_destroy(vscan_svc_taskq);
166 
167 	cv_destroy(&vscan_svc_cv);
168 
169 	for (i = 0; i <= VS_DRV_MAX_FILES; i++)
170 		cv_destroy(&vscan_svc_file_cv[i]);
171 
172 	mutex_destroy(&vscan_svc_mutex);
173 	mutex_destroy(&vscan_svc_cfg_mutex);
174 }
175 
176 /*
177  * vscan_svc_enable
178  */
179 void
180 vscan_svc_enable(boolean_t enable)
181 {
182 	vscan_svc_enabled = enable;
183 
184 	if (enable)
185 		fs_vscan_register(vscan_svc_scan_file);
186 	else
187 		fs_vscan_register(NULL);
188 }
189 
190 /*
191  * vscan_svc_in_use
192  */
193 boolean_t
194 vscan_svc_in_use()
195 {
196 	boolean_t rc;
197 
198 	mutex_enter(&vscan_svc_mutex);
199 	rc = (vscan_svc_req_count > 0) ? B_TRUE : B_FALSE;
200 	mutex_exit(&vscan_svc_mutex);
201 
202 	return (rc);
203 }
204 
205 /*
206  * vscan_svc_get_vnode
207  *
208  * Get the file vnode indexed by idx.
209  * Returns NULL if idx not valid.
210  */
211 vnode_t *
212 vscan_svc_get_vnode(int idx)
213 {
214 	ASSERT(idx > 0);
215 	ASSERT(idx <= VS_DRV_MAX_FILES);
216 
217 	if ((idx <= 0) || (idx > VS_DRV_MAX_FILES))
218 		return (NULL);
219 	else
220 		return (vscan_svc_files[idx].vsf_req.vsr_vp);
221 }
222 
223 
224 /*
225  * vscan_svc_scan_file
226  *
227  * This function is the entry point for the file system to
228  * request that a file be virus scanned.
229  *
230  * Asynchronous requests:
231  * If an async scan request cannot be queued it is discarded.
232  *   By definition the caller of an async request is not dependent
233  *   on the outcome of the result. Although the file will thus
234  *   not be scanned at this time, it will be scanned
235  *   (synchronously) on subsequent access.
236  *   This scenario should not occur during normal operation.
237  *
238  * Before queuing an async request do VN_HOLD(vp). VN_RELE(vp)
239  *   will be done when the scan completes or if the request
240  *   couldn't be queued.
241  *
242  * The vscan_fs_req_t, allocated to hold the request information
243  * passed from the fs, will be free'd when the scan completes.
244  */
245 int
246 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
247 {
248 	int rc = 0;
249 	vscan_fs_req_t *req;
250 	boolean_t allow;
251 
252 	mutex_enter(&vscan_svc_mutex);
253 
254 	if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) {
255 		mutex_exit(&vscan_svc_mutex);
256 		return (0);
257 	}
258 
259 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
260 
261 	/* check if size or type exempts file from scanning */
262 	if (vscan_svc_exempt_file(vp, &allow)) {
263 		mutex_exit(&vscan_svc_mutex);
264 		if ((allow == B_TRUE) || (async != 0))
265 			return (0);
266 
267 		return (EACCES);
268 	}
269 
270 	vscan_svc_req_count++;
271 	mutex_exit(&vscan_svc_mutex);
272 
273 	req = kmem_zalloc(sizeof (vscan_fs_req_t), KM_SLEEP);
274 	req->vsr_vp = vp;
275 
276 	if (async) {
277 		VN_HOLD(vp);
278 		if (vscan_svc_taskq &&
279 		    taskq_dispatch(vscan_svc_taskq, vscan_svc_taskq_callback,
280 		    (void *)req, TQ_SLEEP)) {
281 			return (0);
282 		} else {
283 			VN_RELE(vp);
284 			kmem_free(req, sizeof (vscan_fs_req_t));
285 		}
286 	} else {
287 		rc = vscan_svc_do_scan(req);
288 		kmem_free(req, sizeof (vscan_fs_req_t));
289 	}
290 
291 	mutex_enter(&vscan_svc_mutex);
292 	vscan_svc_req_count--;
293 	mutex_exit(&vscan_svc_mutex);
294 
295 	return (rc);
296 }
297 
298 
299 /*
300  * vscan_svc_taskq_callback
301  *
302  * Callback function for async scan requests
303  */
304 void
305 vscan_svc_taskq_callback(void *data)
306 {
307 	vscan_fs_req_t *req = (vscan_fs_req_t *)data;
308 
309 	(void) vscan_svc_do_scan(req);
310 	VN_RELE(req->vsr_vp); /* VN_HOLD done before request queued */
311 	kmem_free(req, sizeof (vscan_fs_req_t));
312 
313 	mutex_enter(&vscan_svc_mutex);
314 	vscan_svc_req_count--;
315 	mutex_exit(&vscan_svc_mutex);
316 }
317 
318 
319 /*
320  * vscan_svc_do_scan
321  *
322  * Should never be called directly. Invoke via vscan_svc_scan_file()
323  * If scan is in progress wait for it to complete, otherwise
324  * initiate door call to scan the file.
325  *
326  * Currently scanstamps cannot be created on files that existed
327  * prior to scanstamp being a system attribute. Thus an attempt
328  * to access the scanstamp may fail. For this reason if vscan_getattr
329  * or vscan_setattr fails, it is retried excluding scanstamp.
330  */
331 static int
332 vscan_svc_do_scan(vscan_fs_req_t *req)
333 {
334 	int rc = 0, idx;
335 	vs_scan_req_t *scan_req;
336 
337 	mutex_enter(&vscan_svc_mutex);
338 
339 	/*
340 	 * if a scan is in progress on the files vscan_svc_wait_for_scan will
341 	 * wait for it to complete and return the idx of the scan request.
342 	 * Otherwise it will return -1 and we will initiate a scan here.
343 	 */
344 	if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) == -1) {
345 		/* insert the scan request into vscan_svc_files */
346 		idx = vscan_svc_insert_file(req);
347 
348 		if (vscan_svc_enabled) {
349 			if (vscan_svc_getattr(idx) == 0) {
350 				/* valid scan_req ptr guaranteed */
351 				scan_req = vscan_svc_populate_req(idx);
352 				mutex_exit(&vscan_svc_mutex);
353 				rc = vscan_door_scan_file(scan_req);
354 				mutex_enter(&vscan_svc_mutex);
355 
356 				if (rc == 0) {
357 					vscan_svc_parse_rsp(idx, scan_req);
358 					(void) vscan_svc_setattr(idx);
359 				}
360 				kmem_free(scan_req, sizeof (vs_scan_req_t));
361 			} else {
362 				cmn_err(CE_WARN, "Can't access xattr for %s\n",
363 				    vscan_svc_files[idx].vsf_req.
364 				    vsr_vp->v_path);
365 			}
366 		} else {
367 			/* if vscan not enabled (shutting down), allow ACCESS */
368 			vscan_svc_files[idx].vsf_access = VS_ACCESS_ALLOW;
369 		}
370 	}
371 
372 	/* When a scan completes the result is saved in vscan_svc_files */
373 	rc = (vscan_svc_files[idx].vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES;
374 
375 	/* wake threads waiting for result, or for a slot in vscan_svc_files */
376 	vscan_svc_notify_scan_complete(idx);
377 
378 	/* remove the entry from vscan_svc_files if nobody else is waiting */
379 	vscan_svc_release_file(idx);
380 
381 	mutex_exit(&vscan_svc_mutex);
382 
383 	return (rc);
384 }
385 
386 /*
387  * vscan_svc_wait_for_scan
388  *
389  * Search for vp in vscan_svc_files. If vp already exists in
390  * vscan_svc_files scan is already in progress on file so wait
391  * for the inprogress scan to complete.
392  *
393  * Returns: idx of file waited for
394  *          -1 if file not already scanning
395  */
396 static int
397 vscan_svc_wait_for_scan(vnode_t *vp)
398 {
399 	int idx;
400 
401 	ASSERT(vp);
402 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
403 
404 	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
405 		if (vscan_svc_files[idx].vsf_req.vsr_vp == vp)
406 			break;
407 	}
408 
409 	/* file not found in table thus not currently being scanned */
410 	if (idx > VS_DRV_MAX_FILES)
411 		return (-1);
412 
413 	/* file found - wait for scan to complete */
414 	vscan_svc_files[idx].vsf_wait_count++;
415 
416 	DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *,
417 	    &(vscan_svc_files[idx]), int, idx);
418 
419 	while (vscan_svc_files[idx].vsf_access == VS_ACCESS_UNDEFINED)
420 		cv_wait(&(vscan_svc_file_cv[idx]), &vscan_svc_mutex);
421 
422 	vscan_svc_files[idx].vsf_wait_count--;
423 
424 	return (idx);
425 }
426 
427 
428 /*
429  * vscan_svc_find_slot
430  *
431  * Find empty slot in vscan_svc_files table.
432  *
433  * vscan_svc_files_idx is the most recently allocated slot,
434  * start search at next slot.
435  * slot 0 is reserved for control interface
436  *
437  * Returns idx of slot, or -1 if not found
438  */
439 static int
440 vscan_svc_find_slot(void)
441 {
442 	int idx, start;
443 
444 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
445 
446 	if ((start = vscan_svc_files_idx + 1) > VS_DRV_MAX_FILES)
447 		start = 1;
448 
449 	for (idx = start; idx <= VS_DRV_MAX_FILES; idx++) {
450 		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) {
451 			vscan_svc_files_idx = idx;
452 			return (idx);
453 		}
454 	}
455 
456 	for (idx = 1; idx < start; idx++) {
457 		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) {
458 			vscan_svc_files_idx = idx;
459 			return (idx);
460 		}
461 	}
462 
463 	return (-1);
464 }
465 
466 
467 /*
468  * vscan_svc_insert_file
469  *
470  * Find the next available flot in vscan_svc_files and
471  * initialize it for the scan request. If no slot is
472  * available, vscan_svc_find_slot will wait for one.
473  *
474  * Returns: idx of scan request in vscan_svc_files table
475  */
476 static int
477 vscan_svc_insert_file(vscan_fs_req_t *req)
478 {
479 	int idx;
480 
481 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
482 
483 	while ((idx = vscan_svc_find_slot()) == -1) {
484 		DTRACE_PROBE1(vscan__wait__slot, vscan_file_t *,
485 		    &(vscan_svc_files[idx]));
486 		vscan_svc_wait_count++;
487 		cv_wait(&(vscan_svc_cv), &vscan_svc_mutex);
488 		vscan_svc_wait_count--;
489 	}
490 
491 	(void) memset(&vscan_svc_files[idx], 0, sizeof (vscan_file_t));
492 	vscan_svc_files[idx].vsf_req = *req;
493 	vscan_svc_files[idx].vsf_modified = 1;
494 	vscan_svc_files[idx].vsf_access = VS_ACCESS_UNDEFINED;
495 
496 	DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx);
497 	return (idx);
498 }
499 
500 
501 /*
502  * vscan_svc_release_file
503  *
504  * Release the file (free the slot in vscan_svc_files)
505  * if no thread is waiting on it.
506  */
507 static void
508 vscan_svc_release_file(int idx)
509 {
510 	vscan_file_t *slot;
511 
512 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
513 
514 	if (vscan_svc_files[idx].vsf_wait_count != 0)
515 		return;
516 
517 	slot = &vscan_svc_files[idx];
518 	DTRACE_PROBE2(vscan__release, char *, slot->vsf_req.vsr_vp->v_path,
519 	    int, idx);
520 	(void) memset(slot, 0, sizeof (vscan_file_t));
521 }
522 
523 
524 /*
525  * vscan_svc_populate_req
526  *
527  * Allocate a scan request to be sent to vscand, populating it
528  * from the data in vscan_svc_files[idx].
529  *
530  * Returns: scan request object
531  */
532 static vs_scan_req_t *
533 vscan_svc_populate_req(int idx)
534 {
535 	vs_scan_req_t *scan_req;
536 	vscan_fs_req_t *req;
537 
538 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
539 
540 	req = &vscan_svc_files[idx].vsf_req;
541 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
542 
543 	scan_req->vsr_id = idx;
544 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
545 	scan_req->vsr_size = vscan_svc_files[idx].vsf_size;
546 	scan_req->vsr_modified = vscan_svc_files[idx].vsf_modified;
547 	scan_req->vsr_quarantined = vscan_svc_files[idx].vsf_quarantined;
548 	scan_req->vsr_flags = 0;
549 	(void) strncpy(scan_req->vsr_scanstamp,
550 	    vscan_svc_files[idx].vsf_scanstamp, sizeof (vs_scanstamp_t));
551 
552 	return (scan_req);
553 }
554 
555 
556 /*
557  * vscan_svc_parse_rsp
558  *
559  * Parse scan response data and save in vscan_svc_files[idx]
560  */
561 static void
562 vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req)
563 {
564 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
565 
566 	vscan_svc_files[idx].vsf_access = scan_req->vsr_access;
567 	vscan_svc_files[idx].vsf_modified = scan_req->vsr_modified;
568 	vscan_svc_files[idx].vsf_quarantined = scan_req->vsr_quarantined;
569 	(void) strncpy(vscan_svc_files[idx].vsf_scanstamp,
570 	    scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t));
571 }
572 
573 
574 /*
575  * vscan_svc_notify_scan_complete
576  *
577  * signal vscan_svc_file_cv and vscan_svc_cv to wake threads waiting
578  * for the scan result for the specified file (vscan_svc_file_cv)
579  * or for a slot in vscan_svc_files table (vscan_svc_cv)
580  */
581 static void
582 vscan_svc_notify_scan_complete(int idx)
583 {
584 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
585 
586 	/* if someone waiting for result, cv_signal */
587 	if (vscan_svc_files[idx].vsf_wait_count > 0)
588 		cv_signal(&vscan_svc_file_cv[idx]);
589 
590 	/* signal vscan_svc_cv if any threads waiting for a slot */
591 	if (vscan_svc_wait_count > 0)
592 		cv_signal(&vscan_svc_cv);
593 }
594 
595 
596 /*
597  * vscan_svc_getattr
598  *
599  * Get the vscan related system attributes and AT_SIZE.
600  */
601 static int
602 vscan_svc_getattr(int idx)
603 {
604 	xvattr_t xvattr;
605 	xoptattr_t *xoap = NULL;
606 	vnode_t *vp;
607 
608 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
609 
610 	if ((vp = vscan_svc_files[idx].vsf_req.vsr_vp) == NULL)
611 		return (-1);
612 
613 	/* get the attributes */
614 	xva_init(&xvattr); /* sets AT_XVATTR */
615 
616 	xvattr.xva_vattr.va_mask |= AT_SIZE;
617 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
618 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
619 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
620 
621 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
622 		return (-1);
623 
624 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
625 		cmn_err(CE_NOTE, "Virus scan request failed; "
626 		    "file system does not support virus scanning");
627 		return (-1);
628 	}
629 
630 	vscan_svc_files[idx].vsf_size = xvattr.xva_vattr.va_size;
631 
632 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
633 		return (-1);
634 	vscan_svc_files[idx].vsf_modified = xoap->xoa_av_modified;
635 
636 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
637 		return (-1);
638 	vscan_svc_files[idx].vsf_quarantined = xoap->xoa_av_quarantined;
639 
640 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
641 		(void) memcpy(vscan_svc_files[idx].vsf_scanstamp,
642 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
643 	}
644 
645 	DTRACE_PROBE1(vscan__attr, vscan_file_t *, &(vscan_svc_files[idx]));
646 	return (0);
647 }
648 
649 
650 /*
651  * vscan_svc_setattr
652  *
653  * Set the vscan related system attributes.
654  *
655  * Caller must already have vscan_svc_mutex
656  */
657 static int
658 vscan_svc_setattr(int idx)
659 {
660 	xvattr_t xvattr;
661 	xoptattr_t *xoap = NULL;
662 	vnode_t *vp;
663 	int len;
664 
665 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
666 
667 	if ((vp = vscan_svc_files[idx].vsf_req.vsr_vp) == NULL)
668 		return (-1);
669 
670 	/* update the attributes */
671 	xva_init(&xvattr); /* sets AT_XVATTR */
672 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
673 		return (-1);
674 
675 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
676 	xoap->xoa_av_modified = vscan_svc_files[idx].vsf_modified;
677 
678 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
679 	xoap->xoa_av_quarantined = vscan_svc_files[idx].vsf_quarantined;
680 
681 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
682 	len = strlen(vscan_svc_files[idx].vsf_scanstamp);
683 	(void) memcpy(xoap->xoa_av_scanstamp,
684 	    vscan_svc_files[idx].vsf_scanstamp, len);
685 
686 	/* if access is denied, set mtime to invalidate client cache */
687 	if (vscan_svc_files[idx].vsf_access != VS_ACCESS_ALLOW) {
688 		xvattr.xva_vattr.va_mask |= AT_MTIME;
689 		gethrestime(&xvattr.xva_vattr.va_mtime);
690 	}
691 
692 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
693 		return (-1);
694 
695 	DTRACE_PROBE1(vscan__attr, vscan_file_t *, &(vscan_svc_files[idx]));
696 	return (0);
697 }
698 
699 
700 /*
701  * vscan_svc_configure
702  *
703  * store configuration in vscan_svc_config
704  * set up vscan_svc_types array of pointers into
705  * vscan_svc_config.vsc_types for efficient searching
706  */
707 int
708 vscan_svc_configure(vs_config_t *conf)
709 {
710 	int count = 0;
711 	char *p, *beg, *end;
712 
713 	mutex_enter(&vscan_svc_cfg_mutex);
714 
715 	vscan_svc_config = *conf;
716 
717 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
718 
719 	beg = vscan_svc_config.vsc_types;
720 	end = beg + vscan_svc_config.vsc_types_len;
721 
722 	for (p = beg; p < end; p += strlen(p) + 1) {
723 		if (count >= VS_TYPES_MAX) {
724 			mutex_exit(&vscan_svc_mutex);
725 			return (-1);
726 		}
727 
728 		vscan_svc_types[count] = p;
729 		++count;
730 	}
731 
732 	mutex_exit(&vscan_svc_cfg_mutex);
733 	return (0);
734 }
735 
736 
737 /*
738  * vscan_svc_exempt_file
739  *
740  * check if a file's size or type exempts it from virus scanning
741  *
742  * If the file is exempt from virus scanning, allow will be set
743  * to define whether files access should be allowed (B_TRUE) or
744  * denied (B_FALSE)
745  *
746  * Returns: 1 exempt
747  *          0 scan required
748  */
749 static int
750 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
751 {
752 	struct vattr attr;
753 
754 	ASSERT(vp != NULL);
755 	ASSERT(vp->v_path != NULL);
756 
757 	attr.va_mask = AT_SIZE;
758 
759 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
760 		*allow = B_FALSE;
761 		return (0);
762 	}
763 
764 	mutex_enter(&vscan_svc_cfg_mutex);
765 
766 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
767 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
768 		    vp->v_path, int, *allow);
769 
770 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
771 		mutex_exit(&vscan_svc_cfg_mutex);
772 		return (1);
773 	}
774 
775 	if (vscan_svc_exempt_filetype(vp->v_path)) {
776 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
777 		*allow = B_TRUE;
778 		mutex_exit(&vscan_svc_cfg_mutex);
779 		return (1);
780 	}
781 
782 	mutex_exit(&vscan_svc_cfg_mutex);
783 	return (0);
784 }
785 
786 
787 /*
788  * vscan_svc_exempt_filetype
789  *
790  * Each entry in vscan_svc_types includes a rule indicator (+,-)
791  * followed by the match string for file types to which the rule
792  * applies. Look for first match of file type in vscan_svc_types
793  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
794  * if the indicator is '+'.
795  * If vscan_svc_match_ext fails, or no match is found, return 0
796  * (not exempt)
797  *
798  * Returns 1: exempt, 0: not exempt
799  */
800 static int
801 vscan_svc_exempt_filetype(char *filepath)
802 {
803 	int i, rc, exempt = 0;
804 	char *filename, *ext;
805 
806 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
807 
808 	if ((filename = strrchr(filepath, '/')) == 0)
809 		filename = filepath;
810 	else
811 		filename++;
812 
813 	if ((ext = strrchr(filename, '.')) == NULL)
814 		ext = "";
815 	else
816 		ext++;
817 
818 
819 	for (i = 0; i < VS_TYPES_MAX; i ++) {
820 		if (vscan_svc_types[i] == 0)
821 			break;
822 
823 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
824 		if (rc == -1)
825 			break;
826 		if (rc > 0) {
827 			DTRACE_PROBE2(vscan__type__match, char *, ext,
828 			    char *, vscan_svc_types[i]);
829 			exempt = (vscan_svc_types[i][0] == '-');
830 			break;
831 		}
832 	}
833 
834 	return (exempt);
835 }
836 
837 
838 /*
839  *  vscan_svc_match_ext
840  *
841  * Performs a case-insensitive match for two strings.  The first string
842  * argument can contain the wildcard characters '?' and '*'
843  *
844  * Returns: 0 no match
845  *          1 match
846  *         -1 recursion error
847  */
848 static int
849 vscan_svc_match_ext(char *patn, char *str, int depth)
850 {
851 	int c1, c2;
852 	if (depth > VS_EXT_RECURSE_DEPTH)
853 		return (-1);
854 
855 	for (;;) {
856 		switch (*patn) {
857 		case 0:
858 			return (*str == 0);
859 
860 		case '?':
861 			if (*str != 0) {
862 				str++;
863 				patn++;
864 				continue;
865 			}
866 			return (0);
867 
868 		case '*':
869 			patn++;
870 			if (*patn == 0)
871 				return (1);
872 
873 			while (*str) {
874 				if (vscan_svc_match_ext(patn, str, depth + 1))
875 					return (1);
876 				str++;
877 			}
878 			return (0);
879 
880 		default:
881 			if (*str != *patn) {
882 				c1 = *str;
883 				c2 = *patn;
884 
885 				c1 = tolower(c1);
886 				c2 = tolower(c2);
887 				if (c1 != c2)
888 					return (0);
889 			}
890 			str++;
891 			patn++;
892 			continue;
893 		}
894 	}
895 	/* NOT REACHED */
896 }
897