xref: /onnv-gate/usr/src/uts/common/io/vscan/vscan_svc.c (revision 5931:a37c3c1bff60)
15440Sjm199354 /*
25440Sjm199354  * CDDL HEADER START
35440Sjm199354  *
45440Sjm199354  * The contents of this file are subject to the terms of the
55440Sjm199354  * Common Development and Distribution License (the "License").
65440Sjm199354  * You may not use this file except in compliance with the License.
75440Sjm199354  *
85440Sjm199354  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95440Sjm199354  * or http://www.opensolaris.org/os/licensing.
105440Sjm199354  * See the License for the specific language governing permissions
115440Sjm199354  * and limitations under the License.
125440Sjm199354  *
135440Sjm199354  * When distributing Covered Code, include this CDDL HEADER in each
145440Sjm199354  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155440Sjm199354  * If applicable, add the following below this CDDL HEADER, with the
165440Sjm199354  * fields enclosed by brackets "[]" replaced with your own identifying
175440Sjm199354  * information: Portions Copyright [yyyy] [name of copyright owner]
185440Sjm199354  *
195440Sjm199354  * CDDL HEADER END
205440Sjm199354  */
215440Sjm199354 
225440Sjm199354 /*
23*5931Sjm199354  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245440Sjm199354  * Use is subject to license terms.
255440Sjm199354  */
265440Sjm199354 
275440Sjm199354 #pragma ident	"%Z%%M%	%I%	%E% SMI"
285440Sjm199354 
295440Sjm199354 #include <sys/stat.h>
305440Sjm199354 #include <sys/ddi.h>
315440Sjm199354 #include <sys/sunddi.h>
325440Sjm199354 #include <sys/time.h>
335440Sjm199354 #include <sys/varargs.h>
345440Sjm199354 #include <sys/conf.h>
355440Sjm199354 #include <sys/modctl.h>
365440Sjm199354 #include <sys/cmn_err.h>
375440Sjm199354 #include <sys/vnode.h>
385440Sjm199354 #include <fs/fs_subr.h>
395440Sjm199354 #include <sys/types.h>
405440Sjm199354 #include <sys/file.h>
415440Sjm199354 #include <sys/disp.h>
425440Sjm199354 #include <sys/sdt.h>
435440Sjm199354 #include <sys/cred.h>
445440Sjm199354 #include <sys/vscan.h>
455440Sjm199354 
465440Sjm199354 #define	VS_TASKQ_NUM_THREADS	VS_DRV_MAX_FILES
475440Sjm199354 #define	VS_EXT_RECURSE_DEPTH	8
485440Sjm199354 #define	tolower(C)	(((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
495440Sjm199354 
505440Sjm199354 /* represents request received from filesystem - currently only use vp */
515440Sjm199354 typedef struct vscan_fs_req {
525440Sjm199354 	vnode_t *vsr_vp;
535440Sjm199354 } vscan_fs_req_t;
545440Sjm199354 
555440Sjm199354 /*
565440Sjm199354  * vscan_svc_files - table of files being scanned
575440Sjm199354  *
585440Sjm199354  * The index into this table is passed in the door call to
595440Sjm199354  * vscand. vscand uses the idx to determine which minor node
605440Sjm199354  * to open to read the file data. Within the kernel driver
615440Sjm199354  * the minor device number can thus be used to identify the
625440Sjm199354  * table index to get the appropriate vnode.
635440Sjm199354  *
645440Sjm199354  * Instance 0 is reserved for the daemon/driver control
655440Sjm199354  * interface: enable/configure/disable
665440Sjm199354  */
675440Sjm199354 typedef struct vscan_file {
685440Sjm199354 	vscan_fs_req_t vsf_req;
695440Sjm199354 	uint32_t vsf_wait_count;
70*5931Sjm199354 	kcondvar_t vsf_cv; /* wait for in progress scan */
715440Sjm199354 	uint8_t vsf_quarantined;
725440Sjm199354 	uint8_t vsf_modified;
735440Sjm199354 	uint64_t vsf_size;
74*5931Sjm199354 	timestruc_t vsf_mtime;
755440Sjm199354 	vs_scanstamp_t vsf_scanstamp;
76*5931Sjm199354 	uint32_t vsf_result;
775440Sjm199354 	uint32_t vsf_access;
785440Sjm199354 } vscan_file_t;
795440Sjm199354 
805440Sjm199354 static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1];
815440Sjm199354 static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */
825440Sjm199354 static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */
835440Sjm199354 static int vscan_svc_req_count = 0; /* # scan requests */
845440Sjm199354 
855440Sjm199354 static taskq_t *vscan_svc_taskq = NULL;
865440Sjm199354 static boolean_t vscan_svc_enabled = B_FALSE;
875440Sjm199354 
885440Sjm199354 /*
895440Sjm199354  * vscan_svc_mutex protects the data pertaining to scan requests:
905440Sjm199354  * file table - vscan_svc_files
915440Sjm199354  * counts - vscan_svc_wait_count, vscan_svc_req_count
925440Sjm199354  */
935440Sjm199354 static kmutex_t vscan_svc_mutex;
945440Sjm199354 
955440Sjm199354 /*
965440Sjm199354  * vscan_svc_cfg_mutex protects the configuration data:
975440Sjm199354  * vscan_svc_config, vscan_svc_types
985440Sjm199354  */
995440Sjm199354 static kmutex_t vscan_svc_cfg_mutex;
1005440Sjm199354 
1015440Sjm199354 /* configuration data - for virus scan exemption */
1025440Sjm199354 static vs_config_t vscan_svc_config;
1035440Sjm199354 static char *vscan_svc_types[VS_TYPES_MAX];
1045440Sjm199354 
1055440Sjm199354 /* local functions */
1065440Sjm199354 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
1075440Sjm199354 void vscan_svc_taskq_callback(void *);
1085440Sjm199354 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
1095440Sjm199354 static int vscan_svc_exempt_filetype(char *);
1105440Sjm199354 static int vscan_svc_match_ext(char *, char *, int);
1115440Sjm199354 static int vscan_svc_do_scan(vscan_fs_req_t *);
1125440Sjm199354 static int vscan_svc_wait_for_scan(vnode_t *);
1135440Sjm199354 static int vscan_svc_insert_file(vscan_fs_req_t *);
1145440Sjm199354 static void vscan_svc_release_file(int);
1155440Sjm199354 static int vscan_svc_find_slot(void);
116*5931Sjm199354 static void vscan_svc_process_scan_result(int);
1175440Sjm199354 static void vscan_svc_notify_scan_complete(int);
1185440Sjm199354 static int vscan_svc_getattr(int);
119*5931Sjm199354 static int vscan_svc_setattr(int, int);
1205440Sjm199354 
1215440Sjm199354 static vs_scan_req_t *vscan_svc_populate_req(int);
1225440Sjm199354 static void vscan_svc_parse_rsp(int, vs_scan_req_t *);
1235440Sjm199354 
1245440Sjm199354 
1255440Sjm199354 /*
1265440Sjm199354  * vscan_svc_init
1275440Sjm199354  */
1285440Sjm199354 int
1295440Sjm199354 vscan_svc_init()
1305440Sjm199354 {
1315440Sjm199354 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL);
1325440Sjm199354 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL);
1335440Sjm199354 	(void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files));
1345440Sjm199354 	cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL);
1355440Sjm199354 
1365440Sjm199354 	return (0);
1375440Sjm199354 }
1385440Sjm199354 
1395440Sjm199354 /*
1405440Sjm199354  * vscan_svc_fini
1415440Sjm199354  */
1425440Sjm199354 void
1435440Sjm199354 vscan_svc_fini()
1445440Sjm199354 {
1455440Sjm199354 	ASSERT(vscan_svc_enabled == B_FALSE);
1465440Sjm199354 	ASSERT(vscan_svc_in_use() == B_FALSE);
1475440Sjm199354 
1485440Sjm199354 	cv_destroy(&vscan_svc_cv);
1495440Sjm199354 	mutex_destroy(&vscan_svc_mutex);
1505440Sjm199354 	mutex_destroy(&vscan_svc_cfg_mutex);
1515440Sjm199354 }
1525440Sjm199354 
1535440Sjm199354 /*
1545440Sjm199354  * vscan_svc_enable
1555440Sjm199354  */
1565440Sjm199354 void
157*5931Sjm199354 vscan_svc_enable(void)
1585440Sjm199354 {
159*5931Sjm199354 	mutex_enter(&vscan_svc_mutex);
160*5931Sjm199354 	vscan_svc_enabled = B_TRUE;
161*5931Sjm199354 
162*5931Sjm199354 	if (vscan_svc_taskq == NULL) {
163*5931Sjm199354 		if ((vscan_svc_taskq = taskq_create("vscan",
164*5931Sjm199354 		    VS_TASKQ_NUM_THREADS, MINCLSYSPRI, 1,
165*5931Sjm199354 		    INT_MAX, TASKQ_DYNAMIC)) == NULL) {
166*5931Sjm199354 			cmn_err(CE_WARN, "All scan requests "
167*5931Sjm199354 			    "will be processed synchronously");
168*5931Sjm199354 		}
169*5931Sjm199354 	}
170*5931Sjm199354 
171*5931Sjm199354 	fs_vscan_register(vscan_svc_scan_file);
172*5931Sjm199354 	mutex_exit(&vscan_svc_mutex);
173*5931Sjm199354 }
174*5931Sjm199354 
1755440Sjm199354 
176*5931Sjm199354 /*
177*5931Sjm199354  * vscan_svc_disable
178*5931Sjm199354  */
179*5931Sjm199354 void
180*5931Sjm199354 vscan_svc_disable(void)
181*5931Sjm199354 {
182*5931Sjm199354 	mutex_enter(&vscan_svc_mutex);
183*5931Sjm199354 	vscan_svc_enabled = B_FALSE;
184*5931Sjm199354 	fs_vscan_register(NULL);
185*5931Sjm199354 
186*5931Sjm199354 	if (vscan_svc_taskq) {
187*5931Sjm199354 		taskq_destroy(vscan_svc_taskq);
188*5931Sjm199354 		vscan_svc_taskq = NULL;
189*5931Sjm199354 	}
190*5931Sjm199354 	mutex_exit(&vscan_svc_mutex);
1915440Sjm199354 }
1925440Sjm199354 
193*5931Sjm199354 
194*5931Sjm199354 
195*5931Sjm199354 /*
196*5931Sjm199354  * vscan_svc_is_enabled
197*5931Sjm199354  */
198*5931Sjm199354 boolean_t
199*5931Sjm199354 vscan_svc_is_enabled()
200*5931Sjm199354 {
201*5931Sjm199354 	return (vscan_svc_enabled);
202*5931Sjm199354 }
203*5931Sjm199354 
204*5931Sjm199354 
2055440Sjm199354 /*
2065440Sjm199354  * vscan_svc_in_use
207*5931Sjm199354  *
208*5931Sjm199354  * The vscan driver is considered to be in use if it is
209*5931Sjm199354  * enabled or if there are in-progress scan requests.
210*5931Sjm199354  * Used to determine whether the driver can be unloaded.
2115440Sjm199354  */
2125440Sjm199354 boolean_t
2135440Sjm199354 vscan_svc_in_use()
2145440Sjm199354 {
2155440Sjm199354 	boolean_t rc;
2165440Sjm199354 
2175440Sjm199354 	mutex_enter(&vscan_svc_mutex);
218*5931Sjm199354 	rc = (vscan_svc_enabled == B_TRUE) || (vscan_svc_req_count > 0);
2195440Sjm199354 	mutex_exit(&vscan_svc_mutex);
2205440Sjm199354 
2215440Sjm199354 	return (rc);
2225440Sjm199354 }
2235440Sjm199354 
2245440Sjm199354 /*
2255440Sjm199354  * vscan_svc_get_vnode
2265440Sjm199354  *
2275440Sjm199354  * Get the file vnode indexed by idx.
2285440Sjm199354  * Returns NULL if idx not valid.
2295440Sjm199354  */
2305440Sjm199354 vnode_t *
2315440Sjm199354 vscan_svc_get_vnode(int idx)
2325440Sjm199354 {
2335440Sjm199354 	ASSERT(idx > 0);
2345440Sjm199354 	ASSERT(idx <= VS_DRV_MAX_FILES);
2355440Sjm199354 
2365440Sjm199354 	if ((idx <= 0) || (idx > VS_DRV_MAX_FILES))
2375440Sjm199354 		return (NULL);
2385440Sjm199354 	else
2395440Sjm199354 		return (vscan_svc_files[idx].vsf_req.vsr_vp);
2405440Sjm199354 }
2415440Sjm199354 
2425440Sjm199354 
2435440Sjm199354 /*
2445440Sjm199354  * vscan_svc_scan_file
2455440Sjm199354  *
2465440Sjm199354  * This function is the entry point for the file system to
2475440Sjm199354  * request that a file be virus scanned.
2485440Sjm199354  *
2495440Sjm199354  * Asynchronous requests:
2505440Sjm199354  * If an async scan request cannot be queued it is discarded.
2515440Sjm199354  *   By definition the caller of an async request is not dependent
2525440Sjm199354  *   on the outcome of the result. Although the file will thus
2535440Sjm199354  *   not be scanned at this time, it will be scanned
2545440Sjm199354  *   (synchronously) on subsequent access.
2555440Sjm199354  *   This scenario should not occur during normal operation.
2565440Sjm199354  *
2575440Sjm199354  * Before queuing an async request do VN_HOLD(vp). VN_RELE(vp)
2585440Sjm199354  *   will be done when the scan completes or if the request
2595440Sjm199354  *   couldn't be queued.
2605440Sjm199354  *
2615440Sjm199354  * The vscan_fs_req_t, allocated to hold the request information
2625440Sjm199354  * passed from the fs, will be free'd when the scan completes.
2635440Sjm199354  */
2645440Sjm199354 int
2655440Sjm199354 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
2665440Sjm199354 {
2675440Sjm199354 	int rc = 0;
2685440Sjm199354 	vscan_fs_req_t *req;
2695440Sjm199354 	boolean_t allow;
2705440Sjm199354 
2715440Sjm199354 	mutex_enter(&vscan_svc_mutex);
2725440Sjm199354 
2735440Sjm199354 	if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL) {
2745440Sjm199354 		mutex_exit(&vscan_svc_mutex);
2755440Sjm199354 		return (0);
2765440Sjm199354 	}
2775440Sjm199354 
2785440Sjm199354 	DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
2795440Sjm199354 
2805440Sjm199354 	/* check if size or type exempts file from scanning */
2815440Sjm199354 	if (vscan_svc_exempt_file(vp, &allow)) {
2825440Sjm199354 		mutex_exit(&vscan_svc_mutex);
2835440Sjm199354 		if ((allow == B_TRUE) || (async != 0))
2845440Sjm199354 			return (0);
2855440Sjm199354 
2865440Sjm199354 		return (EACCES);
2875440Sjm199354 	}
2885440Sjm199354 
2895440Sjm199354 	vscan_svc_req_count++;
2905440Sjm199354 	mutex_exit(&vscan_svc_mutex);
2915440Sjm199354 
2925440Sjm199354 	req = kmem_zalloc(sizeof (vscan_fs_req_t), KM_SLEEP);
2935440Sjm199354 	req->vsr_vp = vp;
2945440Sjm199354 
2955440Sjm199354 	if (async) {
2965440Sjm199354 		VN_HOLD(vp);
2975440Sjm199354 		if (vscan_svc_taskq &&
2985440Sjm199354 		    taskq_dispatch(vscan_svc_taskq, vscan_svc_taskq_callback,
2995440Sjm199354 		    (void *)req, TQ_SLEEP)) {
3005440Sjm199354 			return (0);
3015440Sjm199354 		} else {
3025440Sjm199354 			VN_RELE(vp);
3035440Sjm199354 			kmem_free(req, sizeof (vscan_fs_req_t));
3045440Sjm199354 		}
3055440Sjm199354 	} else {
3065440Sjm199354 		rc = vscan_svc_do_scan(req);
3075440Sjm199354 		kmem_free(req, sizeof (vscan_fs_req_t));
3085440Sjm199354 	}
3095440Sjm199354 
3105440Sjm199354 	mutex_enter(&vscan_svc_mutex);
3115440Sjm199354 	vscan_svc_req_count--;
3125440Sjm199354 	mutex_exit(&vscan_svc_mutex);
3135440Sjm199354 
3145440Sjm199354 	return (rc);
3155440Sjm199354 }
3165440Sjm199354 
3175440Sjm199354 
3185440Sjm199354 /*
3195440Sjm199354  * vscan_svc_taskq_callback
3205440Sjm199354  *
3215440Sjm199354  * Callback function for async scan requests
3225440Sjm199354  */
3235440Sjm199354 void
3245440Sjm199354 vscan_svc_taskq_callback(void *data)
3255440Sjm199354 {
3265440Sjm199354 	vscan_fs_req_t *req = (vscan_fs_req_t *)data;
3275440Sjm199354 
3285440Sjm199354 	(void) vscan_svc_do_scan(req);
3295440Sjm199354 	VN_RELE(req->vsr_vp); /* VN_HOLD done before request queued */
3305440Sjm199354 	kmem_free(req, sizeof (vscan_fs_req_t));
3315440Sjm199354 
3325440Sjm199354 	mutex_enter(&vscan_svc_mutex);
3335440Sjm199354 	vscan_svc_req_count--;
3345440Sjm199354 	mutex_exit(&vscan_svc_mutex);
3355440Sjm199354 }
3365440Sjm199354 
3375440Sjm199354 
3385440Sjm199354 /*
3395440Sjm199354  * vscan_svc_do_scan
3405440Sjm199354  *
3415440Sjm199354  * Should never be called directly. Invoke via vscan_svc_scan_file()
3425440Sjm199354  * If scan is in progress wait for it to complete, otherwise
3435440Sjm199354  * initiate door call to scan the file.
3445440Sjm199354  */
3455440Sjm199354 static int
3465440Sjm199354 vscan_svc_do_scan(vscan_fs_req_t *req)
3475440Sjm199354 {
348*5931Sjm199354 	int rc = -1, idx;
3495440Sjm199354 	vs_scan_req_t *scan_req;
350*5931Sjm199354 	vscan_file_t *svc_file;
3515440Sjm199354 
3525440Sjm199354 	mutex_enter(&vscan_svc_mutex);
3535440Sjm199354 
3545440Sjm199354 	/*
3555440Sjm199354 	 * if a scan is in progress on the files vscan_svc_wait_for_scan will
3565440Sjm199354 	 * wait for it to complete and return the idx of the scan request.
3575440Sjm199354 	 * Otherwise it will return -1 and we will initiate a scan here.
3585440Sjm199354 	 */
359*5931Sjm199354 	if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) != -1) {
360*5931Sjm199354 		svc_file = &vscan_svc_files[idx];
361*5931Sjm199354 	} else {
3625440Sjm199354 		/* insert the scan request into vscan_svc_files */
3635440Sjm199354 		idx = vscan_svc_insert_file(req);
364*5931Sjm199354 		svc_file = &vscan_svc_files[idx];
3655440Sjm199354 
3665440Sjm199354 		if (vscan_svc_enabled) {
3675440Sjm199354 			if (vscan_svc_getattr(idx) == 0) {
3685440Sjm199354 				/* valid scan_req ptr guaranteed */
3695440Sjm199354 				scan_req = vscan_svc_populate_req(idx);
3705440Sjm199354 				mutex_exit(&vscan_svc_mutex);
371*5931Sjm199354 				if (vscan_drv_create_node(idx) == B_TRUE)
372*5931Sjm199354 					rc = vscan_door_scan_file(scan_req);
3735440Sjm199354 				mutex_enter(&vscan_svc_mutex);
374*5931Sjm199354 				if (rc == 0)
3755440Sjm199354 					vscan_svc_parse_rsp(idx, scan_req);
3765440Sjm199354 				kmem_free(scan_req, sizeof (vs_scan_req_t));
377*5931Sjm199354 
378*5931Sjm199354 				/* process scan result */
379*5931Sjm199354 				vscan_svc_process_scan_result(idx);
380*5931Sjm199354 				DTRACE_PROBE2(vscan__result, int,
381*5931Sjm199354 				    svc_file->vsf_result, int,
382*5931Sjm199354 				    svc_file->vsf_access);
3835440Sjm199354 			} else {
384*5931Sjm199354 				/* if getattr fails: log error, deny access */
3855440Sjm199354 				cmn_err(CE_WARN, "Can't access xattr for %s\n",
386*5931Sjm199354 				    svc_file->vsf_req.vsr_vp->v_path);
387*5931Sjm199354 				svc_file->vsf_access = VS_ACCESS_DENY;
3885440Sjm199354 			}
3895440Sjm199354 		} else {
3905440Sjm199354 			/* if vscan not enabled (shutting down), allow ACCESS */
391*5931Sjm199354 			svc_file->vsf_access = VS_ACCESS_ALLOW;
3925440Sjm199354 		}
3935440Sjm199354 	}
3945440Sjm199354 
3955440Sjm199354 	/* When a scan completes the result is saved in vscan_svc_files */
396*5931Sjm199354 	rc = (svc_file->vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES;
3975440Sjm199354 
3985440Sjm199354 	/* wake threads waiting for result, or for a slot in vscan_svc_files */
3995440Sjm199354 	vscan_svc_notify_scan_complete(idx);
4005440Sjm199354 
4015440Sjm199354 	/* remove the entry from vscan_svc_files if nobody else is waiting */
4025440Sjm199354 	vscan_svc_release_file(idx);
4035440Sjm199354 
4045440Sjm199354 	mutex_exit(&vscan_svc_mutex);
4055440Sjm199354 
4065440Sjm199354 	return (rc);
4075440Sjm199354 }
4085440Sjm199354 
409*5931Sjm199354 
410*5931Sjm199354 /*
411*5931Sjm199354  * vscan_svc_process_scan_result
412*5931Sjm199354  *
413*5931Sjm199354  * Sets vsf_access and updates file attributes based on vsf_result,
414*5931Sjm199354  * as follows:
415*5931Sjm199354  *
416*5931Sjm199354  * VS_STATUS_INFECTED
417*5931Sjm199354  *  deny access, set quarantine attribute, clear scanstamp
418*5931Sjm199354  * VS_STATUS_CLEAN
419*5931Sjm199354  *  allow access, set scanstamp,
420*5931Sjm199354  *  if file not modified since scan initiated, clear modified attribute
421*5931Sjm199354  * VS_STATUS_NO_SCAN
422*5931Sjm199354  *  deny access if file quarantined, otherwise allow access
423*5931Sjm199354  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
424*5931Sjm199354  *  deny access if file quarantined, modified or no scanstamp
425*5931Sjm199354  *  otherwise, allow access
426*5931Sjm199354  */
427*5931Sjm199354 static void
428*5931Sjm199354 vscan_svc_process_scan_result(int idx)
429*5931Sjm199354 {
430*5931Sjm199354 	struct vattr attr;
431*5931Sjm199354 	vnode_t *vp;
432*5931Sjm199354 	timestruc_t *mtime;
433*5931Sjm199354 	vscan_file_t *svc_file;
434*5931Sjm199354 
435*5931Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
436*5931Sjm199354 
437*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
438*5931Sjm199354 
439*5931Sjm199354 	switch (svc_file->vsf_result) {
440*5931Sjm199354 	case VS_STATUS_INFECTED:
441*5931Sjm199354 		svc_file->vsf_access = VS_ACCESS_DENY;
442*5931Sjm199354 		svc_file->vsf_quarantined = 1;
443*5931Sjm199354 		svc_file->vsf_scanstamp[0] = '\0';
444*5931Sjm199354 		(void) vscan_svc_setattr(idx,
445*5931Sjm199354 		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
446*5931Sjm199354 		return;
447*5931Sjm199354 
448*5931Sjm199354 	case VS_STATUS_CLEAN:
449*5931Sjm199354 		svc_file->vsf_access = VS_ACCESS_ALLOW;
450*5931Sjm199354 
451*5931Sjm199354 		/* if mtime has changed, don't clear the modified attribute */
452*5931Sjm199354 		vp = svc_file->vsf_req.vsr_vp;
453*5931Sjm199354 		mtime = &(svc_file->vsf_mtime);
454*5931Sjm199354 		attr.va_mask = AT_MTIME;
455*5931Sjm199354 		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
456*5931Sjm199354 		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
457*5931Sjm199354 		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
458*5931Sjm199354 			DTRACE_PROBE1(vscan__mtime__changed, vscan_file_t *,
459*5931Sjm199354 			    svc_file);
460*5931Sjm199354 			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
461*5931Sjm199354 			return;
462*5931Sjm199354 		}
463*5931Sjm199354 
464*5931Sjm199354 		svc_file->vsf_modified = 0;
465*5931Sjm199354 		(void) vscan_svc_setattr(idx,
466*5931Sjm199354 		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
467*5931Sjm199354 		return;
468*5931Sjm199354 
469*5931Sjm199354 	case VS_STATUS_NO_SCAN:
470*5931Sjm199354 		if (svc_file->vsf_quarantined)
471*5931Sjm199354 			svc_file->vsf_access = VS_ACCESS_DENY;
472*5931Sjm199354 		else
473*5931Sjm199354 			svc_file->vsf_access = VS_ACCESS_ALLOW;
474*5931Sjm199354 		return;
475*5931Sjm199354 
476*5931Sjm199354 	case VS_STATUS_ERROR:
477*5931Sjm199354 	case VS_STATUS_UNDEFINED:
478*5931Sjm199354 	default:
479*5931Sjm199354 		if ((svc_file->vsf_quarantined) ||
480*5931Sjm199354 		    (svc_file->vsf_modified) ||
481*5931Sjm199354 		    (svc_file->vsf_scanstamp[0] == '\0'))
482*5931Sjm199354 			svc_file->vsf_access = VS_ACCESS_DENY;
483*5931Sjm199354 		else
484*5931Sjm199354 			svc_file->vsf_access = VS_ACCESS_ALLOW;
485*5931Sjm199354 		return;
486*5931Sjm199354 	}
487*5931Sjm199354 }
488*5931Sjm199354 
489*5931Sjm199354 
4905440Sjm199354 /*
4915440Sjm199354  * vscan_svc_wait_for_scan
4925440Sjm199354  *
4935440Sjm199354  * Search for vp in vscan_svc_files. If vp already exists in
4945440Sjm199354  * vscan_svc_files scan is already in progress on file so wait
4955440Sjm199354  * for the inprogress scan to complete.
4965440Sjm199354  *
4975440Sjm199354  * Returns: idx of file waited for
4985440Sjm199354  *          -1 if file not already scanning
4995440Sjm199354  */
5005440Sjm199354 static int
5015440Sjm199354 vscan_svc_wait_for_scan(vnode_t *vp)
5025440Sjm199354 {
5035440Sjm199354 	int idx;
504*5931Sjm199354 	vscan_file_t *svc_file;
5055440Sjm199354 
5065440Sjm199354 	ASSERT(vp);
5075440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
5085440Sjm199354 
5095440Sjm199354 	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
5105440Sjm199354 		if (vscan_svc_files[idx].vsf_req.vsr_vp == vp)
5115440Sjm199354 			break;
5125440Sjm199354 	}
5135440Sjm199354 
5145440Sjm199354 	/* file not found in table thus not currently being scanned */
5155440Sjm199354 	if (idx > VS_DRV_MAX_FILES)
5165440Sjm199354 		return (-1);
5175440Sjm199354 
5185440Sjm199354 	/* file found - wait for scan to complete */
519*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
520*5931Sjm199354 	svc_file->vsf_wait_count++;
5215440Sjm199354 
522*5931Sjm199354 	DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *, svc_file, int, idx);
5235440Sjm199354 
524*5931Sjm199354 	while (svc_file->vsf_access == VS_ACCESS_UNDEFINED)
525*5931Sjm199354 		cv_wait(&(svc_file->vsf_cv), &vscan_svc_mutex);
5265440Sjm199354 
527*5931Sjm199354 	svc_file->vsf_wait_count--;
5285440Sjm199354 
5295440Sjm199354 	return (idx);
5305440Sjm199354 }
5315440Sjm199354 
5325440Sjm199354 
5335440Sjm199354 /*
5345440Sjm199354  * vscan_svc_find_slot
5355440Sjm199354  *
5365440Sjm199354  * Find empty slot in vscan_svc_files table.
5375440Sjm199354  *
5385440Sjm199354  * Returns idx of slot, or -1 if not found
5395440Sjm199354  */
5405440Sjm199354 static int
5415440Sjm199354 vscan_svc_find_slot(void)
5425440Sjm199354 {
543*5931Sjm199354 	int idx;
5445440Sjm199354 
5455440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
546*5931Sjm199354 	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
547*5931Sjm199354 		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL)
5485440Sjm199354 			return (idx);
5495440Sjm199354 	}
5505440Sjm199354 
5515440Sjm199354 	return (-1);
5525440Sjm199354 }
5535440Sjm199354 
5545440Sjm199354 
5555440Sjm199354 /*
5565440Sjm199354  * vscan_svc_insert_file
5575440Sjm199354  *
5585440Sjm199354  * Find the next available flot in vscan_svc_files and
5595440Sjm199354  * initialize it for the scan request. If no slot is
5605440Sjm199354  * available, vscan_svc_find_slot will wait for one.
5615440Sjm199354  *
5625440Sjm199354  * Returns: idx of scan request in vscan_svc_files table
5635440Sjm199354  */
5645440Sjm199354 static int
5655440Sjm199354 vscan_svc_insert_file(vscan_fs_req_t *req)
5665440Sjm199354 {
5675440Sjm199354 	int idx;
568*5931Sjm199354 	vscan_file_t *svc_file;
5695440Sjm199354 
5705440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
5715440Sjm199354 
5725440Sjm199354 	while ((idx = vscan_svc_find_slot()) == -1) {
573*5931Sjm199354 		DTRACE_PROBE1(vscan__wait__slot, char *, req->vsr_vp->v_path);
5745440Sjm199354 		vscan_svc_wait_count++;
5755440Sjm199354 		cv_wait(&(vscan_svc_cv), &vscan_svc_mutex);
5765440Sjm199354 		vscan_svc_wait_count--;
5775440Sjm199354 	}
5785440Sjm199354 
579*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
580*5931Sjm199354 
581*5931Sjm199354 	(void) memset(svc_file, 0, sizeof (vscan_file_t));
582*5931Sjm199354 	svc_file->vsf_req = *req;
583*5931Sjm199354 	svc_file->vsf_modified = 1;
584*5931Sjm199354 	svc_file->vsf_result = VS_STATUS_UNDEFINED;
585*5931Sjm199354 	svc_file->vsf_access = VS_ACCESS_UNDEFINED;
586*5931Sjm199354 	cv_init(&(svc_file->vsf_cv), NULL, CV_DEFAULT, NULL);
5875440Sjm199354 
5885440Sjm199354 	DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx);
5895440Sjm199354 	return (idx);
5905440Sjm199354 }
5915440Sjm199354 
5925440Sjm199354 
5935440Sjm199354 /*
5945440Sjm199354  * vscan_svc_release_file
5955440Sjm199354  *
5965440Sjm199354  * Release the file (free the slot in vscan_svc_files)
5975440Sjm199354  * if no thread is waiting on it.
5985440Sjm199354  */
5995440Sjm199354 static void
6005440Sjm199354 vscan_svc_release_file(int idx)
6015440Sjm199354 {
602*5931Sjm199354 	vscan_file_t *svc_file;
6035440Sjm199354 
6045440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
605*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
6065440Sjm199354 
607*5931Sjm199354 	if (svc_file->vsf_wait_count != 0)
6085440Sjm199354 		return;
6095440Sjm199354 
610*5931Sjm199354 	DTRACE_PROBE2(vscan__release, char *,
611*5931Sjm199354 	    svc_file->vsf_req.vsr_vp->v_path, int, idx);
612*5931Sjm199354 
613*5931Sjm199354 	cv_destroy(&(svc_file->vsf_cv));
614*5931Sjm199354 	(void) memset(svc_file, 0, sizeof (vscan_file_t));
6155440Sjm199354 }
6165440Sjm199354 
6175440Sjm199354 
6185440Sjm199354 /*
6195440Sjm199354  * vscan_svc_populate_req
6205440Sjm199354  *
6215440Sjm199354  * Allocate a scan request to be sent to vscand, populating it
6225440Sjm199354  * from the data in vscan_svc_files[idx].
6235440Sjm199354  *
6245440Sjm199354  * Returns: scan request object
6255440Sjm199354  */
6265440Sjm199354 static vs_scan_req_t *
6275440Sjm199354 vscan_svc_populate_req(int idx)
6285440Sjm199354 {
6295440Sjm199354 	vs_scan_req_t *scan_req;
6305440Sjm199354 	vscan_fs_req_t *req;
631*5931Sjm199354 	vscan_file_t *svc_file;
6325440Sjm199354 
6335440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
6345440Sjm199354 
635*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
636*5931Sjm199354 	req = &(svc_file->vsf_req);
6375440Sjm199354 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
6385440Sjm199354 
6395440Sjm199354 	scan_req->vsr_id = idx;
6405440Sjm199354 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
641*5931Sjm199354 	scan_req->vsr_size = svc_file->vsf_size;
642*5931Sjm199354 	scan_req->vsr_modified = svc_file->vsf_modified;
643*5931Sjm199354 	scan_req->vsr_quarantined = svc_file->vsf_quarantined;
6445440Sjm199354 	scan_req->vsr_flags = 0;
6455440Sjm199354 	(void) strncpy(scan_req->vsr_scanstamp,
646*5931Sjm199354 	    svc_file->vsf_scanstamp, sizeof (vs_scanstamp_t));
6475440Sjm199354 
6485440Sjm199354 	return (scan_req);
6495440Sjm199354 }
6505440Sjm199354 
6515440Sjm199354 
6525440Sjm199354 /*
6535440Sjm199354  * vscan_svc_parse_rsp
6545440Sjm199354  *
6555440Sjm199354  * Parse scan response data and save in vscan_svc_files[idx]
6565440Sjm199354  */
6575440Sjm199354 static void
6585440Sjm199354 vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req)
6595440Sjm199354 {
660*5931Sjm199354 	vscan_file_t *svc_file;
661*5931Sjm199354 
6625440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
6635440Sjm199354 
664*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
665*5931Sjm199354 	svc_file->vsf_result = scan_req->vsr_result;
666*5931Sjm199354 	(void) strncpy(svc_file->vsf_scanstamp,
6675440Sjm199354 	    scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t));
6685440Sjm199354 }
6695440Sjm199354 
6705440Sjm199354 
6715440Sjm199354 /*
6725440Sjm199354  * vscan_svc_notify_scan_complete
6735440Sjm199354  *
674*5931Sjm199354  * signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake
675*5931Sjm199354  * threads waiting for the scan result for the specified
676*5931Sjm199354  * file (vscan_svc_files[idx].vsf_cv) or for a slot in
677*5931Sjm199354  * vscan_svc_files table (vscan_svc_cv)
6785440Sjm199354  */
6795440Sjm199354 static void
6805440Sjm199354 vscan_svc_notify_scan_complete(int idx)
6815440Sjm199354 {
682*5931Sjm199354 	vscan_file_t *svc_file;
683*5931Sjm199354 
6845440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
6855440Sjm199354 
686*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
687*5931Sjm199354 
6885440Sjm199354 	/* if someone waiting for result, cv_signal */
689*5931Sjm199354 	if (svc_file->vsf_wait_count > 0)
690*5931Sjm199354 		cv_signal(&(svc_file->vsf_cv));
6915440Sjm199354 
6925440Sjm199354 	/* signal vscan_svc_cv if any threads waiting for a slot */
6935440Sjm199354 	if (vscan_svc_wait_count > 0)
6945440Sjm199354 		cv_signal(&vscan_svc_cv);
6955440Sjm199354 }
6965440Sjm199354 
6975440Sjm199354 
6985440Sjm199354 /*
6995440Sjm199354  * vscan_svc_getattr
7005440Sjm199354  *
701*5931Sjm199354  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
7025440Sjm199354  */
7035440Sjm199354 static int
7045440Sjm199354 vscan_svc_getattr(int idx)
7055440Sjm199354 {
7065440Sjm199354 	xvattr_t xvattr;
7075440Sjm199354 	xoptattr_t *xoap = NULL;
7085440Sjm199354 	vnode_t *vp;
709*5931Sjm199354 	vscan_file_t *svc_file;
7105440Sjm199354 
7115440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
7125440Sjm199354 
713*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
714*5931Sjm199354 	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
7155440Sjm199354 		return (-1);
7165440Sjm199354 
7175440Sjm199354 	/* get the attributes */
7185440Sjm199354 	xva_init(&xvattr); /* sets AT_XVATTR */
7195440Sjm199354 
7205440Sjm199354 	xvattr.xva_vattr.va_mask |= AT_SIZE;
721*5931Sjm199354 	xvattr.xva_vattr.va_mask |= AT_MTIME;
7225440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
7235440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
7245440Sjm199354 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
7255440Sjm199354 
7265440Sjm199354 	if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
7275440Sjm199354 		return (-1);
7285440Sjm199354 
7295440Sjm199354 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
7305440Sjm199354 		cmn_err(CE_NOTE, "Virus scan request failed; "
7315440Sjm199354 		    "file system does not support virus scanning");
7325440Sjm199354 		return (-1);
7335440Sjm199354 	}
7345440Sjm199354 
735*5931Sjm199354 	svc_file->vsf_size = xvattr.xva_vattr.va_size;
736*5931Sjm199354 	svc_file->vsf_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
737*5931Sjm199354 	svc_file->vsf_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
7385440Sjm199354 
7395440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
7405440Sjm199354 		return (-1);
741*5931Sjm199354 	svc_file->vsf_modified = xoap->xoa_av_modified;
7425440Sjm199354 
7435440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
7445440Sjm199354 		return (-1);
745*5931Sjm199354 	svc_file->vsf_quarantined = xoap->xoa_av_quarantined;
7465440Sjm199354 
7475440Sjm199354 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
748*5931Sjm199354 		(void) memcpy(svc_file->vsf_scanstamp,
7495440Sjm199354 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
7505440Sjm199354 	}
7515440Sjm199354 
752*5931Sjm199354 	DTRACE_PROBE1(vscan__getattr, vscan_file_t *, svc_file);
7535440Sjm199354 	return (0);
7545440Sjm199354 }
7555440Sjm199354 
7565440Sjm199354 
7575440Sjm199354 /*
7585440Sjm199354  * vscan_svc_setattr
7595440Sjm199354  *
7605440Sjm199354  * Set the vscan related system attributes.
7615440Sjm199354  */
7625440Sjm199354 static int
763*5931Sjm199354 vscan_svc_setattr(int idx, int which)
7645440Sjm199354 {
7655440Sjm199354 	xvattr_t xvattr;
7665440Sjm199354 	xoptattr_t *xoap = NULL;
7675440Sjm199354 	vnode_t *vp;
7685440Sjm199354 	int len;
769*5931Sjm199354 	vscan_file_t *svc_file;
7705440Sjm199354 
7715440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
7725440Sjm199354 
773*5931Sjm199354 	svc_file = &vscan_svc_files[idx];
774*5931Sjm199354 	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
7755440Sjm199354 		return (-1);
7765440Sjm199354 
7775440Sjm199354 	/* update the attributes */
7785440Sjm199354 	xva_init(&xvattr); /* sets AT_XVATTR */
7795440Sjm199354 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
7805440Sjm199354 		return (-1);
7815440Sjm199354 
782*5931Sjm199354 	if (which & XAT_AV_MODIFIED) {
783*5931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
784*5931Sjm199354 		xoap->xoa_av_modified = svc_file->vsf_modified;
785*5931Sjm199354 	}
7865440Sjm199354 
787*5931Sjm199354 	if (which & XAT_AV_QUARANTINED) {
788*5931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
789*5931Sjm199354 		xoap->xoa_av_quarantined = svc_file->vsf_quarantined;
790*5931Sjm199354 	}
7915440Sjm199354 
792*5931Sjm199354 	if (which & XAT_AV_SCANSTAMP) {
793*5931Sjm199354 		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
794*5931Sjm199354 		len = strlen(svc_file->vsf_scanstamp);
795*5931Sjm199354 		(void) memcpy(xoap->xoa_av_scanstamp,
796*5931Sjm199354 		    svc_file->vsf_scanstamp, len);
797*5931Sjm199354 	}
7985440Sjm199354 
7995440Sjm199354 	/* if access is denied, set mtime to invalidate client cache */
800*5931Sjm199354 	if (svc_file->vsf_access != VS_ACCESS_ALLOW) {
8015440Sjm199354 		xvattr.xva_vattr.va_mask |= AT_MTIME;
8025440Sjm199354 		gethrestime(&xvattr.xva_vattr.va_mtime);
8035440Sjm199354 	}
8045440Sjm199354 
8055440Sjm199354 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
8065440Sjm199354 		return (-1);
8075440Sjm199354 
808*5931Sjm199354 	DTRACE_PROBE2(vscan__setattr,
809*5931Sjm199354 	    vscan_file_t *, svc_file, int, which);
810*5931Sjm199354 
8115440Sjm199354 	return (0);
8125440Sjm199354 }
8135440Sjm199354 
8145440Sjm199354 
8155440Sjm199354 /*
8165440Sjm199354  * vscan_svc_configure
8175440Sjm199354  *
8185440Sjm199354  * store configuration in vscan_svc_config
8195440Sjm199354  * set up vscan_svc_types array of pointers into
8205440Sjm199354  * vscan_svc_config.vsc_types for efficient searching
8215440Sjm199354  */
8225440Sjm199354 int
8235440Sjm199354 vscan_svc_configure(vs_config_t *conf)
8245440Sjm199354 {
8255440Sjm199354 	int count = 0;
8265440Sjm199354 	char *p, *beg, *end;
8275440Sjm199354 
8285440Sjm199354 	mutex_enter(&vscan_svc_cfg_mutex);
8295440Sjm199354 
8305440Sjm199354 	vscan_svc_config = *conf;
8315440Sjm199354 
8325440Sjm199354 	(void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
8335440Sjm199354 
8345440Sjm199354 	beg = vscan_svc_config.vsc_types;
8355440Sjm199354 	end = beg + vscan_svc_config.vsc_types_len;
8365440Sjm199354 
8375440Sjm199354 	for (p = beg; p < end; p += strlen(p) + 1) {
8385440Sjm199354 		if (count >= VS_TYPES_MAX) {
8395440Sjm199354 			mutex_exit(&vscan_svc_mutex);
8405440Sjm199354 			return (-1);
8415440Sjm199354 		}
8425440Sjm199354 
8435440Sjm199354 		vscan_svc_types[count] = p;
8445440Sjm199354 		++count;
8455440Sjm199354 	}
8465440Sjm199354 
8475440Sjm199354 	mutex_exit(&vscan_svc_cfg_mutex);
8485440Sjm199354 	return (0);
8495440Sjm199354 }
8505440Sjm199354 
8515440Sjm199354 
8525440Sjm199354 /*
8535440Sjm199354  * vscan_svc_exempt_file
8545440Sjm199354  *
8555440Sjm199354  * check if a file's size or type exempts it from virus scanning
8565440Sjm199354  *
8575440Sjm199354  * If the file is exempt from virus scanning, allow will be set
8585440Sjm199354  * to define whether files access should be allowed (B_TRUE) or
8595440Sjm199354  * denied (B_FALSE)
8605440Sjm199354  *
8615440Sjm199354  * Returns: 1 exempt
8625440Sjm199354  *          0 scan required
8635440Sjm199354  */
8645440Sjm199354 static int
8655440Sjm199354 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
8665440Sjm199354 {
8675440Sjm199354 	struct vattr attr;
8685440Sjm199354 
8695440Sjm199354 	ASSERT(vp != NULL);
8705440Sjm199354 	ASSERT(vp->v_path != NULL);
8715440Sjm199354 
8725440Sjm199354 	attr.va_mask = AT_SIZE;
8735440Sjm199354 
8745440Sjm199354 	if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
8755440Sjm199354 		*allow = B_FALSE;
8765440Sjm199354 		return (0);
8775440Sjm199354 	}
8785440Sjm199354 
8795440Sjm199354 	mutex_enter(&vscan_svc_cfg_mutex);
8805440Sjm199354 
8815440Sjm199354 	if (attr.va_size > vscan_svc_config.vsc_max_size) {
8825440Sjm199354 		DTRACE_PROBE2(vscan__exempt__filesize, char *,
8835440Sjm199354 		    vp->v_path, int, *allow);
8845440Sjm199354 
8855440Sjm199354 		*allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
8865440Sjm199354 		mutex_exit(&vscan_svc_cfg_mutex);
8875440Sjm199354 		return (1);
8885440Sjm199354 	}
8895440Sjm199354 
8905440Sjm199354 	if (vscan_svc_exempt_filetype(vp->v_path)) {
8915440Sjm199354 		DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
8925440Sjm199354 		*allow = B_TRUE;
8935440Sjm199354 		mutex_exit(&vscan_svc_cfg_mutex);
8945440Sjm199354 		return (1);
8955440Sjm199354 	}
8965440Sjm199354 
8975440Sjm199354 	mutex_exit(&vscan_svc_cfg_mutex);
8985440Sjm199354 	return (0);
8995440Sjm199354 }
9005440Sjm199354 
9015440Sjm199354 
9025440Sjm199354 /*
9035440Sjm199354  * vscan_svc_exempt_filetype
9045440Sjm199354  *
9055440Sjm199354  * Each entry in vscan_svc_types includes a rule indicator (+,-)
9065440Sjm199354  * followed by the match string for file types to which the rule
9075440Sjm199354  * applies. Look for first match of file type in vscan_svc_types
9085440Sjm199354  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
9095440Sjm199354  * if the indicator is '+'.
9105440Sjm199354  * If vscan_svc_match_ext fails, or no match is found, return 0
9115440Sjm199354  * (not exempt)
9125440Sjm199354  *
9135440Sjm199354  * Returns 1: exempt, 0: not exempt
9145440Sjm199354  */
9155440Sjm199354 static int
9165440Sjm199354 vscan_svc_exempt_filetype(char *filepath)
9175440Sjm199354 {
9185440Sjm199354 	int i, rc, exempt = 0;
9195440Sjm199354 	char *filename, *ext;
9205440Sjm199354 
9215440Sjm199354 	ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
9225440Sjm199354 
9235440Sjm199354 	if ((filename = strrchr(filepath, '/')) == 0)
9245440Sjm199354 		filename = filepath;
9255440Sjm199354 	else
9265440Sjm199354 		filename++;
9275440Sjm199354 
9285440Sjm199354 	if ((ext = strrchr(filename, '.')) == NULL)
9295440Sjm199354 		ext = "";
9305440Sjm199354 	else
9315440Sjm199354 		ext++;
9325440Sjm199354 
9335440Sjm199354 
9345440Sjm199354 	for (i = 0; i < VS_TYPES_MAX; i ++) {
9355440Sjm199354 		if (vscan_svc_types[i] == 0)
9365440Sjm199354 			break;
9375440Sjm199354 
9385440Sjm199354 		rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
9395440Sjm199354 		if (rc == -1)
9405440Sjm199354 			break;
9415440Sjm199354 		if (rc > 0) {
9425440Sjm199354 			DTRACE_PROBE2(vscan__type__match, char *, ext,
9435440Sjm199354 			    char *, vscan_svc_types[i]);
9445440Sjm199354 			exempt = (vscan_svc_types[i][0] == '-');
9455440Sjm199354 			break;
9465440Sjm199354 		}
9475440Sjm199354 	}
9485440Sjm199354 
9495440Sjm199354 	return (exempt);
9505440Sjm199354 }
9515440Sjm199354 
9525440Sjm199354 
9535440Sjm199354 /*
9545440Sjm199354  *  vscan_svc_match_ext
9555440Sjm199354  *
9565440Sjm199354  * Performs a case-insensitive match for two strings.  The first string
9575440Sjm199354  * argument can contain the wildcard characters '?' and '*'
9585440Sjm199354  *
9595440Sjm199354  * Returns: 0 no match
9605440Sjm199354  *          1 match
9615440Sjm199354  *         -1 recursion error
9625440Sjm199354  */
9635440Sjm199354 static int
9645440Sjm199354 vscan_svc_match_ext(char *patn, char *str, int depth)
9655440Sjm199354 {
9665440Sjm199354 	int c1, c2;
9675440Sjm199354 	if (depth > VS_EXT_RECURSE_DEPTH)
9685440Sjm199354 		return (-1);
9695440Sjm199354 
9705440Sjm199354 	for (;;) {
9715440Sjm199354 		switch (*patn) {
9725440Sjm199354 		case 0:
9735440Sjm199354 			return (*str == 0);
9745440Sjm199354 
9755440Sjm199354 		case '?':
9765440Sjm199354 			if (*str != 0) {
9775440Sjm199354 				str++;
9785440Sjm199354 				patn++;
9795440Sjm199354 				continue;
9805440Sjm199354 			}
9815440Sjm199354 			return (0);
9825440Sjm199354 
9835440Sjm199354 		case '*':
9845440Sjm199354 			patn++;
9855440Sjm199354 			if (*patn == 0)
9865440Sjm199354 				return (1);
9875440Sjm199354 
9885440Sjm199354 			while (*str) {
9895440Sjm199354 				if (vscan_svc_match_ext(patn, str, depth + 1))
9905440Sjm199354 					return (1);
9915440Sjm199354 				str++;
9925440Sjm199354 			}
9935440Sjm199354 			return (0);
9945440Sjm199354 
9955440Sjm199354 		default:
9965440Sjm199354 			if (*str != *patn) {
9975440Sjm199354 				c1 = *str;
9985440Sjm199354 				c2 = *patn;
9995440Sjm199354 
10005440Sjm199354 				c1 = tolower(c1);
10015440Sjm199354 				c2 = tolower(c2);
10025440Sjm199354 				if (c1 != c2)
10035440Sjm199354 					return (0);
10045440Sjm199354 			}
10055440Sjm199354 			str++;
10065440Sjm199354 			patn++;
10075440Sjm199354 			continue;
10085440Sjm199354 		}
10095440Sjm199354 	}
10105440Sjm199354 	/* NOT REACHED */
10115440Sjm199354 }
1012