changeset 5931:a37c3c1bff60

6622386 Modified bit is not set when a existing file is modified with an invalid scan engine configured 6634473 libvscan/Makefile.com contains unnecessary entries 6635736 SUNWvscanu packaging issues with snv_78. 6641932 vscan wasting system resources 6642770 when scan engine disabled its scanstamp still used in check if file's scanstamp is current 6642777 vscan kernel module unregistering from VFS immediately when daemon dies 6651761 Connection goes to CLOSE_WAIT when icap server closes connection
author jm199354
date Tue, 29 Jan 2008 13:27:28 -0800
parents ac8bbb374af7
children bd433e4e06c2
files usr/src/Targetdirs usr/src/cmd/vscan/vscand/vs_door.c usr/src/cmd/vscan/vscand/vs_eng.c usr/src/cmd/vscan/vscand/vs_icap.c usr/src/cmd/vscan/vscand/vs_incl.h usr/src/cmd/vscan/vscand/vs_svc.c usr/src/cmd/vscan/vscand/vscan.d usr/src/lib/libvscan/Makefile.com usr/src/pkgdefs/SUNWvscanu/depend usr/src/uts/common/io/vscan/vscan_drv.c usr/src/uts/common/io/vscan/vscan_svc.c usr/src/uts/common/sys/vscan.h
diffstat 12 files changed, 560 insertions(+), 321 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Targetdirs	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/Targetdirs	Tue Jan 29 13:27:28 2008 -0800
@@ -289,6 +289,7 @@
 	/usr/lib/security \
 	/usr/lib/smbsrv \
 	/usr/lib/term \
+	/usr/lib/vscan \
 	/usr/lib/zones \
 	/usr/old \
 	/usr/openwin \
--- a/usr/src/cmd/vscan/vscand/vs_door.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vs_door.c	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -111,31 +111,30 @@
 vs_door_scan_req(void *cookie, char *ptr, size_t size, door_desc_t *dp,
     uint_t n_desc)
 {
-	int flags = 0, access = VS_ACCESS_DENY;
+	int flags = 0;
 	vs_scan_req_t scan_rsp;
-	/* LINTED E_BAD_PTR_CAST_ALIGN - to be fixed with encoding */
-	vs_scan_req_t *scan_req = (vs_scan_req_t *)ptr;
-	char *fname = scan_req->vsr_path;
+	vs_scan_req_t *scan_req;
 	char devname[MAXPATHLEN];
-	uint64_t fsize = scan_req->vsr_size;
 	vs_attr_t fattr;
 
-	(void) snprintf(devname, MAXPATHLEN, "%s%d",
-	    VS_DRV_PATH, scan_req->vsr_id);
-	fattr.vsa_size = fsize;
-	fattr.vsa_modified = scan_req->vsr_modified;
-	fattr.vsa_quarantined = scan_req->vsr_quarantined;
-	(void) strlcpy(fattr.vsa_scanstamp, scan_req->vsr_scanstamp,
-	    sizeof (vs_scanstamp_t));
+	if (ptr == NULL) {
+		scan_rsp.vsr_result = VS_STATUS_ERROR;
+		scan_rsp.vsr_scanstamp[0] = '\0';
+	} else {
+		/* LINTED E_BAD_PTR_CAST_ALIGN - to be fixed with encoding */
+		scan_req = (vs_scan_req_t *)ptr;
+		(void) snprintf(devname, MAXPATHLEN, "%s%d",
+		    VS_DRV_PATH, scan_req->vsr_id);
 
-	access = vs_svc_scan_file(devname, fname, &fattr, flags);
+		fattr.vsa_size = scan_req->vsr_size;
+		fattr.vsa_modified = scan_req->vsr_modified;
+		fattr.vsa_quarantined = scan_req->vsr_quarantined;
+		(void) strlcpy(fattr.vsa_scanstamp, scan_req->vsr_scanstamp,
+		    sizeof (vs_scanstamp_t));
 
-	/* process result */
-	scan_rsp.vsr_access = access;
-	scan_rsp.vsr_modified = fattr.vsa_modified;
-	scan_rsp.vsr_quarantined = fattr.vsa_quarantined;
-	(void) strlcpy(scan_rsp.vsr_scanstamp, fattr.vsa_scanstamp,
-	    sizeof (vs_scanstamp_t));
+		scan_rsp.vsr_result = vs_svc_scan_file(devname,
+		    scan_req->vsr_path, &fattr, flags, &scan_rsp.vsr_scanstamp);
+	}
 
 	(void) door_return((char *)&scan_rsp, sizeof (vs_scan_req_t), NULL, 0);
 }
--- a/usr/src/cmd/vscan/vscand/vs_eng.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vs_eng.c	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -116,15 +116,11 @@
  *
  * Configure scan engine connections.
  *
- * The enable property is the single indicator that the engine
- * configuration is valid. Configuration is guaranteed (by the
- * library) to be consistent; if the host is invalid or unconfigured,
- * the enable setting will always be false.
+ * If a scan engine has been reconfigured (different host or port)
+ * the scan engine's error count is reset.
  *
- * If a scan engine has been reconfigured (different host or port)
- * the scan engine's error count and statitics are reset, and
  * vs_icap_config is invoked to reset engine-specific data stored
- * in vs_icap
+ * in vs_icap.
  *
  */
 void
@@ -132,6 +128,7 @@
 {
 	int i;
 	vs_props_se_t *cfg;
+	vs_engine_t *eng;
 
 	(void) pthread_mutex_lock(&vs_eng_mutex);
 
@@ -140,21 +137,20 @@
 
 	for (i = 0; i < VS_SE_MAX; i++) {
 		cfg = &config->va_se[i];
+		eng = &vs_engines[i];
 
-		if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0) {
-			vs_engines[i].vse_error = 0;
-			vs_icap_config(i, cfg->vep_host, cfg->vep_port);
-		}
+		if (vs_eng_compare(i, cfg->vep_host, cfg->vep_port) != 0)
+			eng->vse_error = 0;
 
 		if (cfg->vep_enable) {
-			vs_engines[i].vse_cfg = *cfg;
+			eng->vse_cfg = *cfg;
 			vs_eng_total_maxcon += cfg->vep_maxconn;
 			vs_eng_count++;
 		} else {
-			(void) memset(&vs_engines[i].vse_cfg, 0,
-			    sizeof (vs_props_se_t));
+			(void) memset(&eng->vse_cfg, 0, sizeof (vs_props_se_t));
 		}
 
+		vs_icap_config(i, eng->vse_cfg.vep_host, eng->vse_cfg.vep_port);
 	}
 
 	if ((vs_eng_total_maxcon <= 0) || (vs_eng_count == 0))
@@ -477,6 +473,8 @@
 	    &sock_opt, sizeof (sock_opt)) < 0) ||
 	    (setsockopt(conn->vsc_sockfd, SOL_SOCKET, SO_KEEPALIVE,
 	    &sock_opt, sizeof (sock_opt)) < 0)) {
+		syslog(LOG_WARNING, "Scan Engine - connection error (%s:%d) %s",
+		    conn->vsc_host, conn->vsc_port, strerror(errno));
 		(void) close(conn->vsc_sockfd);
 		conn->vsc_sockfd = -1;
 		return (-1);
@@ -553,10 +551,11 @@
 	if (scanstamp[0] == '\0')
 		return (0);
 
-	/* if scanstamp matches that of any engine with no errors */
+	/* if scanstamp matches that of any enabled engine with no errors */
 	(void) pthread_mutex_lock(&vs_eng_mutex);
 	for (i = 0; i < VS_SE_MAX; i++) {
-		if ((vs_engines[i].vse_error == 0) &&
+		if ((vs_engines[i].vse_cfg.vep_enable) &&
+		    (vs_engines[i].vse_error == 0) &&
 		    (vs_icap_compare_scanstamp(i, scanstamp) == 0))
 			break;
 	}
--- a/usr/src/cmd/vscan/vscand/vs_icap.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vs_icap.c	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -198,7 +198,7 @@
 /*
  * vs_icap_config
  *
- * When a new NAS AVA configuration is specified, this will be
+ * When a new VSCAN configuration is specified, this will be
  * called per scan engine. If the scan engine host or port has
  * changed delete the vs_options entry for that scan engine.
  */
@@ -242,7 +242,7 @@
 	int fd;
 
 	if ((fd = open(devname, O_RDONLY)) == -1) {
-		syslog(LOG_ERR, "Failed to open device %s\n", devname);
+		syslog(LOG_ERR, "Failed to open device %s", devname);
 		result->vsr_rc = VS_RESULT_ERROR;
 		return (result->vsr_rc);
 	}
@@ -1271,11 +1271,15 @@
 		errno = 0;
 		retval = recv(ctx->vsc_sockfd, &c, 1, 0);
 
-		if (retval < 0 && errno != EINTR)
-			return (-1);
+		if (retval < 0 && errno == EINTR)
+			continue;
 
-		if (retval <= 0)
-			continue;
+		if (retval <= 0) {
+			syslog(LOG_ERR, "Error receiving data from Scan Engine:"
+			    " %s", retval == 0 ? "Scan Engine disconnected"
+			    : strerror(errno));
+			return (-1);
+		}
 
 		buf[i++] = c;
 		if (c == '\n')
--- a/usr/src/cmd/vscan/vscand/vs_incl.h	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vs_incl.h	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -43,7 +43,7 @@
 #include <sys/vscan.h>
 #include <libvscan.h>
 
-/* vscan result code - "scan_ret_code" field of vs_result_t */
+/* vscan result code - "vsr_rc" field of vs_result_t */
 #define	VS_RESULT_SE_ERROR    	-2 /* scan engine i/f error */
 #define	VS_RESULT_ERROR    	-1
 #define	VS_RESULT_UNDEFINED	0
@@ -99,7 +99,7 @@
 	int vsc_sockfd;
 	struct vs_eng_conn *vsc_next;
 	struct vs_eng_conn *vsc_prev;
-}vs_eng_conn_t;
+} vs_eng_conn_t;
 
 
 /* file attributes used by virus scanning */
@@ -120,7 +120,7 @@
 
 void vs_svc_init(void);
 void vs_svc_fini(void);
-int vs_svc_scan_file(char *, char *, vs_attr_t *, int);
+int vs_svc_scan_file(char *, char *, vs_attr_t *, int, vs_scanstamp_t *);
 
 void vs_eng_init(void);
 void vs_eng_fini(void);
--- a/usr/src/cmd/vscan/vscand/vs_svc.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vs_svc.c	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -43,7 +43,6 @@
 #include "vs_incl.h"
 
 /* local functions */
-static int  vs_svc_process_scan_result(vs_attr_t *, vs_result_t *);
 static void vs_svc_vlog(char *, vs_result_t *);
 static void vs_svc_audit(char *, vs_result_t *);
 
@@ -74,25 +73,35 @@
  *  - updating scan statistics
  *  - logging virus information
  *
+ *
  * Returns:
- *  VS_ACCESS_ALLOW, VS_ACCESS_DENY
+ *  VS_STATUS_NO_SCAN - scan not reqd, or daemon shutting down
+ *  VS_STATUS_CLEAN - scan success. File clean.
+ *                    new scanstamp returned in scanstamp param.
+ *  VS_STATUS_INFECTED - scan success. File infected.
+ *  VS_STATUS_ERROR - scan failure either in vscand or scan engine.
  */
 int
-vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags)
+vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags,
+    vs_scanstamp_t *scanstamp)
 {
 	vs_eng_conn_t conn;
-	int access = VS_ACCESS_UNDEFINED;
-	int rc, retries;
+	int retries;
 	vs_result_t result;
 
-	/* deny access to quarantined files */
-	if (fattr->vsa_quarantined)
-		return (VS_ACCESS_DENY);
+	/* initialize response scanstamp to current scanstamp value */
+	(void) strlcpy(*scanstamp, fattr->vsa_scanstamp,
+	    sizeof (vs_scanstamp_t));
+
 
-	/* allow access if not modified & scanstamp current */
-	if ((fattr->vsa_modified  == 0) &&
+	/* No scan if file quarantined */
+	if (fattr->vsa_quarantined)
+		return (VS_STATUS_NO_SCAN);
+
+	/* No scan if file not modified AND scanstamp is current */
+	if ((fattr->vsa_modified == 0) &&
 	    vs_eng_scanstamp_current(fattr->vsa_scanstamp)) {
-		return (VS_ACCESS_ALLOW);
+		return (VS_STATUS_NO_SCAN);
 	}
 
 	(void) memset(&result, 0, sizeof (vs_result_t));
@@ -101,109 +110,67 @@
 	for (retries = 0; retries <= VS_MAX_RETRY; retries++) {
 		/* identify available engine connection */
 		if (vs_eng_get(&conn, retries) != 0) {
-			rc = VS_RESULT_ERROR;
+			result.vsr_rc = VS_RESULT_ERROR;
 			continue;
 		}
 
 		/* connect to engine and scan file */
-		if (vs_eng_connect(&conn) != 0)
-			rc = VS_RESULT_SE_ERROR;
-		else {
+		if (vs_eng_connect(&conn) != 0) {
+			result.vsr_rc = VS_RESULT_SE_ERROR;
+		} else {
 			if (vscand_get_state() == VS_STATE_SHUTDOWN) {
 				vs_eng_release(&conn);
-				return (VS_ACCESS_ALLOW);
+				return (VS_STATUS_NO_SCAN);
 			}
 
-			rc = vs_icap_scan_file(&conn, devname, fname,
+			(void) vs_icap_scan_file(&conn, devname, fname,
 			    fattr->vsa_size, flags, &result);
 		}
 
 		/* if no error, clear error state on engine and break */
-		if ((rc != VS_RESULT_SE_ERROR) && (rc != VS_RESULT_ERROR)) {
+		if ((result.vsr_rc != VS_RESULT_SE_ERROR) &&
+		    (result.vsr_rc != VS_RESULT_ERROR)) {
 			vs_eng_set_error(&conn, 0);
 			vs_eng_release(&conn);
 			break;
 		}
 
-		/* if scan failed due to shutdown, allow access */
+		/* treat error on shutdown as scan not required */
 		if (vscand_get_state() == VS_STATE_SHUTDOWN) {
 			vs_eng_release(&conn);
-			return (VS_ACCESS_ALLOW);
+			return (VS_STATUS_NO_SCAN);
 		}
 
 		/* set engine's error state and update engine stats */
-		if (rc == VS_RESULT_SE_ERROR) {
+		if (result.vsr_rc == VS_RESULT_SE_ERROR) {
 			vs_eng_set_error(&conn, 1);
 			vs_stats_eng_err(conn.vsc_engid);
 		}
 		vs_eng_release(&conn);
 	}
 
-	vs_stats_set(rc);
+	vs_stats_set(result.vsr_rc);
 
-	/* if file infected, update virus log and write audit record */
+	/*
+	 * VS_RESULT_CLEANED - file infected, cleaned data available
+	 * VS_RESULT_FORBIDDEN - file infected, no cleaned data
+	 * Log virus, write audit record and return INFECTED status
+	 */
 	if (result.vsr_rc == VS_RESULT_CLEANED ||
 	    result.vsr_rc == VS_RESULT_FORBIDDEN) {
 		vs_svc_vlog(fname, &result);
 		vs_svc_audit(fname, &result);
+		return (VS_STATUS_INFECTED);
 	}
 
-	access = vs_svc_process_scan_result(fattr, &result);
-
-	return (access);
-}
-
-
-/*
- * vs_svc_process_scan_result
- *
- * Translate the scan result into VS_ACCESS_ALLOW or VS_ACCESS_DENY.
- * If the scan failed (VS_RESULT_ERROR) deny access if the
- * scan was initiated because the file had been modified or
- * had never been scanned. Otherwise allow access.
- *
- *   If file has been modified or has never been scanned, it must
- *   be successfully scanned before access is allowed
- *
- *   If the file has previously been scanned and has not been
- *   modified, don't deny access if scan fail, only if the file
- *   is found to be infected.
- *
- * If the file is still infected set quarantine attribute,
- * otherwise clear modified attribute.
- *
- * Returns: VS_ACCESS_ALLOW, VS_ACCESS_DENY
- */
-static int
-vs_svc_process_scan_result(vs_attr_t *fattr, vs_result_t *result)
-{
-	int access = VS_ACCESS_DENY;
-
-	switch (result->vsr_rc) {
-	case VS_RESULT_CLEANED:
-	case VS_RESULT_FORBIDDEN:
-		fattr->vsa_scanstamp[0] = '\0';
-		fattr->vsa_quarantined = 1;
-		access = VS_ACCESS_DENY;
-		break;
-	case VS_RESULT_CLEAN:
-		(void) strlcpy(fattr->vsa_scanstamp, result->vsr_scanstamp,
+	/* VS_RESULT_CLEAN - Set the scanstamp and return CLEAN status */
+	if (result.vsr_rc == VS_RESULT_CLEAN) {
+		(void) strlcpy(*scanstamp, result.vsr_scanstamp,
 		    sizeof (vs_scanstamp_t));
-		fattr->vsa_modified = 0;
-		access = VS_ACCESS_ALLOW;
-		break;
-	case VS_RESULT_ERROR:
-	case VS_RESULT_SE_ERROR:
-	case VS_RESULT_UNDEFINED:
-	default:
-		if ((fattr->vsa_modified) || (fattr->vsa_scanstamp[0] == '\0'))
-			access = VS_ACCESS_DENY;
-		else
-			access = VS_ACCESS_ALLOW;
-		break;
+		return (VS_STATUS_CLEAN);
 	}
 
-	return (access);
+	return (VS_STATUS_ERROR);
 }
 
 
--- a/usr/src/cmd/vscan/vscand/vscan.d	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/cmd/vscan/vscand/vscan.d	Tue Jan 29 13:27:28 2008 -0800
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -62,8 +62,7 @@
 
 sdt:vscan::vscan-wait-slot
 {
-	printf("%s",
-		stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path));
+	printf("%s", stringof(arg0));
 }
 
 sdt:vscan::vscan-insert
@@ -76,7 +75,7 @@
 	printf("idx: %d - %s", arg1, stringof(arg0));
 }
 
-sdt:vscan::vscan-attr
+sdt:vscan::vscan-getattr
 {
 	printf("%s, m: %d, q: %d, scanstamp: %s",
 		stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path),
@@ -85,7 +84,47 @@
 		stringof(((vscan_file_t *)arg0)->vsf_scanstamp));
 }
 
+sdt:vscan::vscan-setattr
+{
+	/* XAT_AV_QUARANTINED */
+	printf("%s", (arg1 & 0x400) == 0 ? "" :
+	    ((vscan_file_t *)arg0)->vsf_quarantined ? "q: 1, " : "q: 0, ");
 
+	/* XAT_AV_MODIFIED */
+	printf("%s", (arg1 & 0x800) == 0 ? "" :
+	    ((vscan_file_t *)arg0)->vsf_modified ? "m: 1, " : "m: 0, ");
+
+	/* XAT_AV_SCANSTAMP */
+	printf("%s", (arg1 & 0x1000) == 0 ? "" : "scanstamp: ");
+	printf("%s", (arg1 & 0x1000) == 0 ? "" :
+	    stringof(((vscan_file_t *)arg0)->vsf_scanstamp));
+}
+
+
+sdt:vscan::vscan-mtime-changed
+{
+	printf("%s",
+		stringof(((vscan_file_t *)arg0)->vsf_req.vsr_vp->v_path));
+}
+
+
+sdt:vscan::vscan-result
+{
+	printf("VS_STATUS_%s - VS_ACCESS_%s",
+	    arg0 == 0 ? "UNDEFINED" :
+	    arg0 == 1 ? "NO_SCAN" :
+	    arg0 == 2 ? "ERROR" :
+	    arg0 == 3 ? "CLEAN" :
+	    arg0 == 4 ? "INFECTED" : "XXX unknown",
+	    arg1 == 0 ? "UNDEFINED" :
+	    arg1 == 1 ? "ALLOW" : "DENY");
+}
+
+
+fbt:vscan:vscan_svc_enable:entry,
+fbt:vscan:vscan_svc_enable:return,
+fbt:vscan:vscan_svc_disable:entry,
+fbt:vscan:vscan_svc_disable:return,
 fbt:vscan:vscan_svc_configure:entry,
 fbt:vscan:vscan_svc_configure:return,
 fbt:vscan:vscan_svc_exempt_filetype:entry,
@@ -114,10 +153,16 @@
 }
 fbt:vscan:vscan_door_scan_file:return
 {
+	printf("%s", args[1] == 0 ? "success" : "error");
 }
 
 /* vscan_drv.c */
 
+sdt:vscan::vscan-minor-node
+{
+	printf("vscan%d %s", arg0, arg1 != 0 ? "created" : "error");
+}
+
 /*
  * unprivileged vscan driver access attempt
  */
@@ -145,9 +190,36 @@
 fbt:vscan:vscan_drv_ioctl:entry
 / (int)args[0] == 0/
 {
-	printf("vscan daemon ioctl %d", args[1]);
+	printf("vscan daemon ioctl %d %s", args[1],
+		args[1] == 1 ? "ENABLE" :
+		args[1] == 2 ? "DISABLE" :
+		args[1] == 4 ? "CONFIG" : "unknown");
+}
+
+fbt:vscan:vscan_drv_delayed_disable:entry,
+fbt:vscan:vscan_drv_delayed_disable:return
+{
+}
+
+sdt:vscan::vscan-reconnect
+{
 }
 
+/*
+fbt:vscan:vscan_drv_attach:entry,
+fbt:vscan:vscan_drv_attach:return,
+fbt:vscan:vscan_drv_detach:entry,
+fbt:vscan:vscan_drv_detach:return
+{
+}
+
+fbt:vscan:vscan_drv_in_use:return,
+fbt:vscan:vscan_svc_in_use:return
+{
+	printf("%s", args[1] ? "in use" : "not in use");
+}
+*/
+
 
 /*
  * file access
@@ -169,8 +241,6 @@
 */
 
 
-
-
 /*
  *** vscan daemon - vscand ***
  */
@@ -184,10 +254,12 @@
 
 pid$target::vs_svc_scan_file:return
 {
-	printf("%s",
-	    arg1 == 0 ? "scan required" :
-	    arg1 == 1 ? "ALLOW" :
-	    arg1 == 2 ? "DENY" : "UNKNOWN");
+	printf("VS_STATUS_%s",
+	    arg1 == 0 ? "UNDEFINED" :
+	    arg1 == 1 ? "NO_SCAN" :
+	    arg1 == 2 ? "ERROR" :
+	    arg1 == 3 ? "CLEAN" :
+	    arg1 == 4 ? "INFECTED" : "XXX unknown");
 }
 
 pid$target::vs_eng_scanstamp_current:return
@@ -197,17 +269,18 @@
 
 pid$target::vs_icap_scan_file:return
 {
-	printf("%ld %s", arg1, arg1 == 0 ? "VSCAN_UNDEFINED" :
-	    arg1 == 1 ? "VSCAN_CLEAN" :
-	    arg1 == 2 ? "VSCAN_CLEANED" :
-	    arg1 == 3 ? "VSCAN_FORBIDDEN" : "VSCAN_(SE)_ERROR");
+	printf("%ld VS_RESULT_%s", arg1,
+	    arg1 == 0 ? "UNDEFINED" :
+	    arg1 == 1 ? "CLEAN" :
+	    arg1 == 2 ? "CLEANED" :
+	    arg1 == 3 ? "FORBIDDEN" : "(SE)_ERROR");
 }
 
 pid$target::vs_stats_set:entry
 {
 	printf("%s", (arg0 == 1) ? "CLEAN" :
 		(arg0 == 2) ? "CLEANED" :
-		(arg0 == 3) ? "QUARANTINE" : "SCAN ERROR");
+		(arg0 == 3) ? "QUARANTINE" : "ERROR");
 }
 
 pid$target::vs_stats_set:return
--- a/usr/src/lib/libvscan/Makefile.com	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/lib/libvscan/Makefile.com	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -28,46 +28,25 @@
 LIBRARY= libvscan.a
 VERS= .1
 
-OBJS_SHARED=
-OBJS_COMMON= libvscan.o
-
-OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
+OBJECTS= libvscan.o
 
 include ../../Makefile.lib
 
 LIBS=	$(DYNLIB) $(LINTLIB)
-
 SRCDIR =	../common
-SRCS=	$(OBJS_COMMON:%.o=$(SRCDIR)/%.c)	\
-	$(OBJS_SHARED:%.o=$(SRC)/common/vscan/%.c)
 $(LINTLIB) := SRCS=	$(SRCDIR)/$(LINTSRC)
 
 # Reset the Makefile.lib macro ROOTLIBDIR to refer to usr/lib/vscan
 ROOTLIBDIR = $(ROOT)/usr/lib/vscan
-ROOTHDRDIR = $(ROOT)/usr/include
-ROOTHDRS = $(HDRS:%=$(ROOTHDRDIR)/%)
 
-$(ROOTLIBDIR):
-	$(INS.dir)
-
-LDLIBS +=	-lc -lscf -lsecdb -lnsl -lm
-CFLAGS +=   $(CCVERBOSE)
+LDLIBS += -lc -lscf -lsecdb -lnsl -lm
+CFLAGS += $(CCVERBOSE)
 CPPFLAGS += -I$(SRCDIR)
-DYNFLAGS +=	-R/usr/lib/vscan
-LDLIBS32 +=	-L$(ROOT)/usr/lib/vscan
-
-#C99MODE=	-xc99=%all
-#C99LMODE=	-Xc99=%all
+DYNFLAGS += -R/usr/lib/vscan
 
 .KEEP_STATE:
 
-install: all $(ROOTLIBDIR) install_h
-
-install_h: $(ROOTHDRDIR) $(ROOTHDRS)
-
 all: $(LIBS)
-
 lint: lintcheck
 
 include ../../Makefile.targ
-
--- a/usr/src/pkgdefs/SUNWvscanu/depend	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/pkgdefs/SUNWvscanu/depend	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -51,4 +51,5 @@
 P SUNWcsu	Core Solaris, (Usr)
 P SUNWcsd	Core Solaris Devices
 P SUNWcsl	Core Solaris Libraries
+P SUNWlibmsr	Math & Microtasking Libraries (Root)
 P SUNWvscanr	Virus Scan Service (Root)
--- a/usr/src/uts/common/io/vscan/vscan_drv.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/uts/common/io/vscan/vscan_drv.c	Tue Jan 29 13:27:28 2008 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -62,10 +62,12 @@
 } vscan_drv_state_t;
 
 static vscan_drv_state_t vscan_drv_state[VS_DRV_MAX_FILES + 1];
+static boolean_t vscan_drv_nodes[VS_DRV_MAX_FILES + 1];
 static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */
 
 static dev_info_t *vscan_drv_dip;
 static kmutex_t vscan_drv_mutex;
+static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
 
 /*
  * DDI entry points.
@@ -78,7 +80,8 @@
 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
 
-static boolean_t vscan_drv_in_use();
+static boolean_t vscan_drv_in_use(void);
+static void vscan_drv_delayed_disable(void);
 
 
 /*
@@ -156,6 +159,7 @@
 	}
 
 	(void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state));
+	(void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes));
 
 	if ((rc  = mod_install(&modlinkage)) != 0) {
 		vscan_door_fini();
@@ -163,6 +167,7 @@
 		mutex_destroy(&vscan_drv_mutex);
 	}
 
+	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
 	return (rc);
 }
 
@@ -191,6 +196,7 @@
 	if ((rc = mod_remove(&modlinkage)) == 0) {
 		vscan_door_fini();
 		vscan_svc_fini();
+		cv_destroy(&vscan_drv_cv);
 		mutex_destroy(&vscan_drv_mutex);
 	}
 
@@ -229,9 +235,6 @@
 static int
 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 {
-	int i;
-	char name[VS_DRV_NODENAME_LEN];
-
 	if (cmd != DDI_ATTACH)
 		return (DDI_FAILURE);
 
@@ -240,15 +243,9 @@
 
 	vscan_drv_dip = dip;
 
-	/* create the minor nodes */
-	for (i = 0; i <= VS_DRV_MAX_FILES; i++) {
-		(void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", i);
-		if (ddi_create_minor_node(dip, name, S_IFCHR, i,
-		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
-			ddi_remove_minor_node(dip, NULL);
-			return (DDI_FAILURE);
-		}
-	}
+	/* create minor node 0 for daemon-driver synchronization */
+	if (vscan_drv_create_node(0) == B_FALSE)
+		return (DDI_FAILURE);
 
 	return (DDI_SUCCESS);
 }
@@ -269,8 +266,10 @@
 	if (vscan_drv_in_use())
 		return (DDI_FAILURE);
 
+	/* remove all minor nodes */
 	vscan_drv_dip = NULL;
 	ddi_remove_minor_node(dip, NULL);
+	(void) memset(&vscan_drv_nodes, 0, sizeof (vscan_drv_nodes));
 
 	return (DDI_SUCCESS);
 }
@@ -278,14 +277,29 @@
 
 /*
  * vscan_drv_in_use
+ *
+ * If vscand is connected (vscan_drv_connected == B_TRUE) the
+ * vscan driver is obviously in use. Otherwise invoke
+ * vscan_svc_in_use() to determine if the driver is in use,
+ * even though the daemon has disconnected.
+ * For example, there may be requests not yet complete, or
+ * the driver may still be enabled waiting for the daemon to
+ * reconnect.
+ * Used to determine whether the driver can be unloaded.
  */
 static boolean_t
 vscan_drv_in_use()
 {
-	if (vscan_drv_connected)
-		return (B_TRUE);
-	else
-		return (vscan_svc_in_use());
+	boolean_t in_use;
+
+	mutex_enter(&vscan_drv_mutex);
+	in_use = vscan_drv_connected;
+	mutex_exit(&vscan_drv_mutex);
+
+	if (in_use == B_FALSE)
+		in_use = vscan_svc_in_use();
+
+	return (in_use);
 }
 
 
@@ -317,6 +331,8 @@
 			return (EINVAL);
 		}
 		vscan_drv_connected = B_TRUE;
+		/* wake any pending delayed disable */
+		cv_signal(&vscan_drv_cv);
 	} else {
 		if ((!vscan_drv_connected) ||
 		    (vscan_drv_state[inst] != VS_INIT)) {
@@ -351,7 +367,12 @@
 			vscan_drv_state[i] = VS_INIT;
 
 		vscan_drv_connected = B_FALSE;
-		vscan_svc_enable(B_FALSE);
+		if (vscan_svc_is_enabled()) {
+			if (thread_create(NULL, 0, vscan_drv_delayed_disable,
+			    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
+				vscan_svc_enable();
+			}
+		}
 		vscan_door_close();
 	} else {
 		vscan_drv_state[inst] = VS_INIT;
@@ -363,6 +384,32 @@
 
 
 /*
+ * vscan_drv_delayed_disable
+ *
+ * Invoked from vscan_drv_close if the daemon disconnects
+ * without first sending disable (e.g. daemon crashed).
+ * Delays for VS_DAEMON_WAIT_SEC before disabling, to allow
+ * the daemon to reconnect. During this time, scan requests
+ * will be processed locally (see vscan_svc.c)
+ */
+static void
+vscan_drv_delayed_disable(void)
+{
+	clock_t timeout = lbolt + SEC_TO_TICK(VS_DAEMON_WAIT_SEC);
+
+	mutex_enter(&vscan_drv_mutex);
+	(void) cv_timedwait(&vscan_drv_cv, &vscan_drv_mutex, timeout);
+
+	if (vscan_drv_connected) {
+		DTRACE_PROBE(vscan__reconnect);
+	} else {
+		vscan_svc_disable();
+	}
+	mutex_exit(&vscan_drv_mutex);
+}
+
+
+/*
  * vscan_drv_read
  */
 /* ARGSUSED */
@@ -422,11 +469,11 @@
 			mutex_exit(&vscan_drv_mutex);
 			return (EINVAL);
 		}
-		vscan_svc_enable(B_TRUE);
+		vscan_svc_enable();
 		mutex_exit(&vscan_drv_mutex);
 		break;
 	case VS_DRV_IOCTL_DISABLE:
-		vscan_svc_enable(B_FALSE);
+		vscan_svc_disable();
 		break;
 	case VS_DRV_IOCTL_CONFIG:
 		if (ddi_copyin((void *)arg, &conf,
@@ -441,3 +488,38 @@
 
 	return (0);
 }
+
+
+/*
+ * vscan_drv_create_node
+ *
+ * Create minor node with which vscan daemon will communicate
+ * to access a file. Invoked from vscan_svc before scan request
+ * sent up to daemon.
+ * Minor node 0 is reserved for daemon-driver synchronization
+ * and is created during attach.
+ * All minor nodes are removed during detach.
+ */
+boolean_t
+vscan_drv_create_node(int idx)
+{
+	char name[VS_DRV_NODENAME_LEN];
+	boolean_t *pnode, rc;
+
+	mutex_enter(&vscan_drv_mutex);
+
+	pnode = &vscan_drv_nodes[idx];
+	if (*pnode == B_FALSE) {
+		(void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", idx);
+		if (ddi_create_minor_node(vscan_drv_dip, name,
+		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
+			*pnode = B_TRUE;
+		}
+		DTRACE_PROBE2(vscan__minor__node, int, idx, int, *pnode);
+	}
+
+	rc = *pnode;
+	mutex_exit(&vscan_drv_mutex);
+
+	return (rc);
+}
--- a/usr/src/uts/common/io/vscan/vscan_svc.c	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/uts/common/io/vscan/vscan_svc.c	Tue Jan 29 13:27:28 2008 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -67,17 +67,18 @@
 typedef struct vscan_file {
 	vscan_fs_req_t vsf_req;
 	uint32_t vsf_wait_count;
+	kcondvar_t vsf_cv; /* wait for in progress scan */
 	uint8_t vsf_quarantined;
 	uint8_t vsf_modified;
 	uint64_t vsf_size;
+	timestruc_t vsf_mtime;
 	vs_scanstamp_t vsf_scanstamp;
+	uint32_t vsf_result;
 	uint32_t vsf_access;
 } vscan_file_t;
 
 static vscan_file_t vscan_svc_files[VS_DRV_MAX_FILES + 1];
-static int vscan_svc_files_idx = 0; /* idx of most recently allocated slot */
 static kcondvar_t vscan_svc_cv; /* wait for slot in vscan_svc_files */
-static kcondvar_t vscan_svc_file_cv[VS_DRV_MAX_FILES + 1]; /* wait for scan */
 static int vscan_svc_wait_count = 0; /* # waiting for slot in vscan_svc_files */
 static int vscan_svc_req_count = 0; /* # scan requests */
 
@@ -112,9 +113,10 @@
 static int vscan_svc_insert_file(vscan_fs_req_t *);
 static void vscan_svc_release_file(int);
 static int vscan_svc_find_slot(void);
+static void vscan_svc_process_scan_result(int);
 static void vscan_svc_notify_scan_complete(int);
 static int vscan_svc_getattr(int);
-static int vscan_svc_setattr(int);
+static int vscan_svc_setattr(int, int);
 
 static vs_scan_req_t *vscan_svc_populate_req(int);
 static void vscan_svc_parse_rsp(int, vs_scan_req_t *);
@@ -126,27 +128,11 @@
 int
 vscan_svc_init()
 {
-	int i;
-
 	mutex_init(&vscan_svc_mutex, NULL, MUTEX_DRIVER, NULL);
 	mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DRIVER, NULL);
-
-	/* create task queue for async requests */
-	if ((vscan_svc_taskq = taskq_create("vscan", VS_TASKQ_NUM_THREADS,
-	    MINCLSYSPRI, 1, INT_MAX, 0)) == NULL) {
-		cmn_err(CE_WARN, "All scan requests will be "
-		    "processed synchronously");
-	}
-
-	/* initialize vscan_svc_files table */
 	(void) memset(&vscan_svc_files, 0, sizeof (vscan_svc_files));
-
-	/* initialize condition variables */
 	cv_init(&vscan_svc_cv, NULL, CV_DEFAULT, NULL);
 
-	for (i = 0; i <= VS_DRV_MAX_FILES; i++)
-		cv_init(&vscan_svc_file_cv[i], NULL, CV_DEFAULT, NULL);
-
 	return (0);
 }
 
@@ -156,19 +142,10 @@
 void
 vscan_svc_fini()
 {
-	int i;
-
 	ASSERT(vscan_svc_enabled == B_FALSE);
 	ASSERT(vscan_svc_in_use() == B_FALSE);
 
-	if (vscan_svc_taskq)
-		taskq_destroy(vscan_svc_taskq);
-
 	cv_destroy(&vscan_svc_cv);
-
-	for (i = 0; i <= VS_DRV_MAX_FILES; i++)
-		cv_destroy(&vscan_svc_file_cv[i]);
-
 	mutex_destroy(&vscan_svc_mutex);
 	mutex_destroy(&vscan_svc_cfg_mutex);
 }
@@ -177,18 +154,60 @@
  * vscan_svc_enable
  */
 void
-vscan_svc_enable(boolean_t enable)
+vscan_svc_enable(void)
 {
-	vscan_svc_enabled = enable;
+	mutex_enter(&vscan_svc_mutex);
+	vscan_svc_enabled = B_TRUE;
+
+	if (vscan_svc_taskq == NULL) {
+		if ((vscan_svc_taskq = taskq_create("vscan",
+		    VS_TASKQ_NUM_THREADS, MINCLSYSPRI, 1,
+		    INT_MAX, TASKQ_DYNAMIC)) == NULL) {
+			cmn_err(CE_WARN, "All scan requests "
+			    "will be processed synchronously");
+		}
+	}
+
+	fs_vscan_register(vscan_svc_scan_file);
+	mutex_exit(&vscan_svc_mutex);
+}
+
 
-	if (enable)
-		fs_vscan_register(vscan_svc_scan_file);
-	else
-		fs_vscan_register(NULL);
+/*
+ * vscan_svc_disable
+ */
+void
+vscan_svc_disable(void)
+{
+	mutex_enter(&vscan_svc_mutex);
+	vscan_svc_enabled = B_FALSE;
+	fs_vscan_register(NULL);
+
+	if (vscan_svc_taskq) {
+		taskq_destroy(vscan_svc_taskq);
+		vscan_svc_taskq = NULL;
+	}
+	mutex_exit(&vscan_svc_mutex);
 }
 
+
+
+/*
+ * vscan_svc_is_enabled
+ */
+boolean_t
+vscan_svc_is_enabled()
+{
+	return (vscan_svc_enabled);
+}
+
+
 /*
  * vscan_svc_in_use
+ *
+ * The vscan driver is considered to be in use if it is
+ * enabled or if there are in-progress scan requests.
+ * Used to determine whether the driver can be unloaded.
  */
 boolean_t
 vscan_svc_in_use()
@@ -196,7 +215,7 @@
 	boolean_t rc;
 
 	mutex_enter(&vscan_svc_mutex);
-	rc = (vscan_svc_req_count > 0) ? B_TRUE : B_FALSE;
+	rc = (vscan_svc_enabled == B_TRUE) || (vscan_svc_req_count > 0);
 	mutex_exit(&vscan_svc_mutex);
 
 	return (rc);
@@ -322,17 +341,13 @@
  * Should never be called directly. Invoke via vscan_svc_scan_file()
  * If scan is in progress wait for it to complete, otherwise
  * initiate door call to scan the file.
- *
- * Currently scanstamps cannot be created on files that existed
- * prior to scanstamp being a system attribute. Thus an attempt
- * to access the scanstamp may fail. For this reason if vscan_getattr
- * or vscan_setattr fails, it is retried excluding scanstamp.
  */
 static int
 vscan_svc_do_scan(vscan_fs_req_t *req)
 {
-	int rc = 0, idx;
+	int rc = -1, idx;
 	vs_scan_req_t *scan_req;
+	vscan_file_t *svc_file;
 
 	mutex_enter(&vscan_svc_mutex);
 
@@ -341,36 +356,44 @@
 	 * wait for it to complete and return the idx of the scan request.
 	 * Otherwise it will return -1 and we will initiate a scan here.
 	 */
-	if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) == -1) {
+	if ((idx = vscan_svc_wait_for_scan(req->vsr_vp)) != -1) {
+		svc_file = &vscan_svc_files[idx];
+	} else {
 		/* insert the scan request into vscan_svc_files */
 		idx = vscan_svc_insert_file(req);
+		svc_file = &vscan_svc_files[idx];
 
 		if (vscan_svc_enabled) {
 			if (vscan_svc_getattr(idx) == 0) {
 				/* valid scan_req ptr guaranteed */
 				scan_req = vscan_svc_populate_req(idx);
 				mutex_exit(&vscan_svc_mutex);
-				rc = vscan_door_scan_file(scan_req);
+				if (vscan_drv_create_node(idx) == B_TRUE)
+					rc = vscan_door_scan_file(scan_req);
 				mutex_enter(&vscan_svc_mutex);
-
-				if (rc == 0) {
+				if (rc == 0)
 					vscan_svc_parse_rsp(idx, scan_req);
-					(void) vscan_svc_setattr(idx);
-				}
 				kmem_free(scan_req, sizeof (vs_scan_req_t));
+
+				/* process scan result */
+				vscan_svc_process_scan_result(idx);
+				DTRACE_PROBE2(vscan__result, int,
+				    svc_file->vsf_result, int,
+				    svc_file->vsf_access);
 			} else {
+				/* if getattr fails: log error, deny access */
 				cmn_err(CE_WARN, "Can't access xattr for %s\n",
-				    vscan_svc_files[idx].vsf_req.
-				    vsr_vp->v_path);
+				    svc_file->vsf_req.vsr_vp->v_path);
+				svc_file->vsf_access = VS_ACCESS_DENY;
 			}
 		} else {
 			/* if vscan not enabled (shutting down), allow ACCESS */
-			vscan_svc_files[idx].vsf_access = VS_ACCESS_ALLOW;
+			svc_file->vsf_access = VS_ACCESS_ALLOW;
 		}
 	}
 
 	/* When a scan completes the result is saved in vscan_svc_files */
-	rc = (vscan_svc_files[idx].vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES;
+	rc = (svc_file->vsf_access == VS_ACCESS_ALLOW) ? 0 : EACCES;
 
 	/* wake threads waiting for result, or for a slot in vscan_svc_files */
 	vscan_svc_notify_scan_complete(idx);
@@ -383,6 +406,87 @@
 	return (rc);
 }
 
+
+/*
+ * vscan_svc_process_scan_result
+ *
+ * Sets vsf_access and updates file attributes based on vsf_result,
+ * as follows:
+ *
+ * VS_STATUS_INFECTED
+ *  deny access, set quarantine attribute, clear scanstamp
+ * VS_STATUS_CLEAN
+ *  allow access, set scanstamp,
+ *  if file not modified since scan initiated, clear modified attribute
+ * VS_STATUS_NO_SCAN
+ *  deny access if file quarantined, otherwise allow access
+ * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
+ *  deny access if file quarantined, modified or no scanstamp
+ *  otherwise, allow access
+ */
+static void
+vscan_svc_process_scan_result(int idx)
+{
+	struct vattr attr;
+	vnode_t *vp;
+	timestruc_t *mtime;
+	vscan_file_t *svc_file;
+
+	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
+
+	svc_file = &vscan_svc_files[idx];
+
+	switch (svc_file->vsf_result) {
+	case VS_STATUS_INFECTED:
+		svc_file->vsf_access = VS_ACCESS_DENY;
+		svc_file->vsf_quarantined = 1;
+		svc_file->vsf_scanstamp[0] = '\0';
+		(void) vscan_svc_setattr(idx,
+		    XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
+		return;
+
+	case VS_STATUS_CLEAN:
+		svc_file->vsf_access = VS_ACCESS_ALLOW;
+
+		/* if mtime has changed, don't clear the modified attribute */
+		vp = svc_file->vsf_req.vsr_vp;
+		mtime = &(svc_file->vsf_mtime);
+		attr.va_mask = AT_MTIME;
+		if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
+		    (mtime->tv_sec != attr.va_mtime.tv_sec) ||
+		    (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
+			DTRACE_PROBE1(vscan__mtime__changed, vscan_file_t *,
+			    svc_file);
+			(void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
+			return;
+		}
+
+		svc_file->vsf_modified = 0;
+		(void) vscan_svc_setattr(idx,
+		    XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
+		return;
+
+	case VS_STATUS_NO_SCAN:
+		if (svc_file->vsf_quarantined)
+			svc_file->vsf_access = VS_ACCESS_DENY;
+		else
+			svc_file->vsf_access = VS_ACCESS_ALLOW;
+		return;
+
+	case VS_STATUS_ERROR:
+	case VS_STATUS_UNDEFINED:
+	default:
+		if ((svc_file->vsf_quarantined) ||
+		    (svc_file->vsf_modified) ||
+		    (svc_file->vsf_scanstamp[0] == '\0'))
+			svc_file->vsf_access = VS_ACCESS_DENY;
+		else
+			svc_file->vsf_access = VS_ACCESS_ALLOW;
+		return;
+	}
+}
+
+
 /*
  * vscan_svc_wait_for_scan
  *
@@ -397,6 +501,7 @@
 vscan_svc_wait_for_scan(vnode_t *vp)
 {
 	int idx;
+	vscan_file_t *svc_file;
 
 	ASSERT(vp);
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
@@ -411,15 +516,15 @@
 		return (-1);
 
 	/* file found - wait for scan to complete */
-	vscan_svc_files[idx].vsf_wait_count++;
+	svc_file = &vscan_svc_files[idx];
+	svc_file->vsf_wait_count++;
 
-	DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *,
-	    &(vscan_svc_files[idx]), int, idx);
+	DTRACE_PROBE2(vscan__wait__scan, vscan_file_t *, svc_file, int, idx);
 
-	while (vscan_svc_files[idx].vsf_access == VS_ACCESS_UNDEFINED)
-		cv_wait(&(vscan_svc_file_cv[idx]), &vscan_svc_mutex);
+	while (svc_file->vsf_access == VS_ACCESS_UNDEFINED)
+		cv_wait(&(svc_file->vsf_cv), &vscan_svc_mutex);
 
-	vscan_svc_files[idx].vsf_wait_count--;
+	svc_file->vsf_wait_count--;
 
 	return (idx);
 }
@@ -430,34 +535,17 @@
  *
  * Find empty slot in vscan_svc_files table.
  *
- * vscan_svc_files_idx is the most recently allocated slot,
- * start search at next slot.
- * slot 0 is reserved for control interface
- *
  * Returns idx of slot, or -1 if not found
  */
 static int
 vscan_svc_find_slot(void)
 {
-	int idx, start;
+	int idx;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
-
-	if ((start = vscan_svc_files_idx + 1) > VS_DRV_MAX_FILES)
-		start = 1;
-
-	for (idx = start; idx <= VS_DRV_MAX_FILES; idx++) {
-		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) {
-			vscan_svc_files_idx = idx;
+	for (idx = 1; idx <= VS_DRV_MAX_FILES; idx++) {
+		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL)
 			return (idx);
-		}
-	}
-
-	for (idx = 1; idx < start; idx++) {
-		if (vscan_svc_files[idx].vsf_req.vsr_vp == NULL) {
-			vscan_svc_files_idx = idx;
-			return (idx);
-		}
 	}
 
 	return (-1);
@@ -477,21 +565,25 @@
 vscan_svc_insert_file(vscan_fs_req_t *req)
 {
 	int idx;
+	vscan_file_t *svc_file;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
 	while ((idx = vscan_svc_find_slot()) == -1) {
-		DTRACE_PROBE1(vscan__wait__slot, vscan_file_t *,
-		    &(vscan_svc_files[idx]));
+		DTRACE_PROBE1(vscan__wait__slot, char *, req->vsr_vp->v_path);
 		vscan_svc_wait_count++;
 		cv_wait(&(vscan_svc_cv), &vscan_svc_mutex);
 		vscan_svc_wait_count--;
 	}
 
-	(void) memset(&vscan_svc_files[idx], 0, sizeof (vscan_file_t));
-	vscan_svc_files[idx].vsf_req = *req;
-	vscan_svc_files[idx].vsf_modified = 1;
-	vscan_svc_files[idx].vsf_access = VS_ACCESS_UNDEFINED;
+	svc_file = &vscan_svc_files[idx];
+
+	(void) memset(svc_file, 0, sizeof (vscan_file_t));
+	svc_file->vsf_req = *req;
+	svc_file->vsf_modified = 1;
+	svc_file->vsf_result = VS_STATUS_UNDEFINED;
+	svc_file->vsf_access = VS_ACCESS_UNDEFINED;
+	cv_init(&(svc_file->vsf_cv), NULL, CV_DEFAULT, NULL);
 
 	DTRACE_PROBE2(vscan__insert, char *, req->vsr_vp->v_path, int, idx);
 	return (idx);
@@ -507,17 +599,19 @@
 static void
 vscan_svc_release_file(int idx)
 {
-	vscan_file_t *slot;
+	vscan_file_t *svc_file;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
+	svc_file = &vscan_svc_files[idx];
 
-	if (vscan_svc_files[idx].vsf_wait_count != 0)
+	if (svc_file->vsf_wait_count != 0)
 		return;
 
-	slot = &vscan_svc_files[idx];
-	DTRACE_PROBE2(vscan__release, char *, slot->vsf_req.vsr_vp->v_path,
-	    int, idx);
-	(void) memset(slot, 0, sizeof (vscan_file_t));
+	DTRACE_PROBE2(vscan__release, char *,
+	    svc_file->vsf_req.vsr_vp->v_path, int, idx);
+
+	cv_destroy(&(svc_file->vsf_cv));
+	(void) memset(svc_file, 0, sizeof (vscan_file_t));
 }
 
 
@@ -534,20 +628,22 @@
 {
 	vs_scan_req_t *scan_req;
 	vscan_fs_req_t *req;
+	vscan_file_t *svc_file;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
-	req = &vscan_svc_files[idx].vsf_req;
+	svc_file = &vscan_svc_files[idx];
+	req = &(svc_file->vsf_req);
 	scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
 
 	scan_req->vsr_id = idx;
 	(void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
-	scan_req->vsr_size = vscan_svc_files[idx].vsf_size;
-	scan_req->vsr_modified = vscan_svc_files[idx].vsf_modified;
-	scan_req->vsr_quarantined = vscan_svc_files[idx].vsf_quarantined;
+	scan_req->vsr_size = svc_file->vsf_size;
+	scan_req->vsr_modified = svc_file->vsf_modified;
+	scan_req->vsr_quarantined = svc_file->vsf_quarantined;
 	scan_req->vsr_flags = 0;
 	(void) strncpy(scan_req->vsr_scanstamp,
-	    vscan_svc_files[idx].vsf_scanstamp, sizeof (vs_scanstamp_t));
+	    svc_file->vsf_scanstamp, sizeof (vs_scanstamp_t));
 
 	return (scan_req);
 }
@@ -561,12 +657,13 @@
 static void
 vscan_svc_parse_rsp(int idx, vs_scan_req_t *scan_req)
 {
+	vscan_file_t *svc_file;
+
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
-	vscan_svc_files[idx].vsf_access = scan_req->vsr_access;
-	vscan_svc_files[idx].vsf_modified = scan_req->vsr_modified;
-	vscan_svc_files[idx].vsf_quarantined = scan_req->vsr_quarantined;
-	(void) strncpy(vscan_svc_files[idx].vsf_scanstamp,
+	svc_file = &vscan_svc_files[idx];
+	svc_file->vsf_result = scan_req->vsr_result;
+	(void) strncpy(svc_file->vsf_scanstamp,
 	    scan_req->vsr_scanstamp, sizeof (vs_scanstamp_t));
 }
 
@@ -574,18 +671,23 @@
 /*
  * vscan_svc_notify_scan_complete
  *
- * signal vscan_svc_file_cv and vscan_svc_cv to wake threads waiting
- * for the scan result for the specified file (vscan_svc_file_cv)
- * or for a slot in vscan_svc_files table (vscan_svc_cv)
+ * signal vscan_svc_files.vsf_cv and vscan_svc_cv to wake
+ * threads waiting for the scan result for the specified
+ * file (vscan_svc_files[idx].vsf_cv) or for a slot in
+ * vscan_svc_files table (vscan_svc_cv)
  */
 static void
 vscan_svc_notify_scan_complete(int idx)
 {
+	vscan_file_t *svc_file;
+
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
+	svc_file = &vscan_svc_files[idx];
+
 	/* if someone waiting for result, cv_signal */
-	if (vscan_svc_files[idx].vsf_wait_count > 0)
-		cv_signal(&vscan_svc_file_cv[idx]);
+	if (svc_file->vsf_wait_count > 0)
+		cv_signal(&(svc_file->vsf_cv));
 
 	/* signal vscan_svc_cv if any threads waiting for a slot */
 	if (vscan_svc_wait_count > 0)
@@ -596,7 +698,7 @@
 /*
  * vscan_svc_getattr
  *
- * Get the vscan related system attributes and AT_SIZE.
+ * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
  */
 static int
 vscan_svc_getattr(int idx)
@@ -604,16 +706,19 @@
 	xvattr_t xvattr;
 	xoptattr_t *xoap = NULL;
 	vnode_t *vp;
+	vscan_file_t *svc_file;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
-	if ((vp = vscan_svc_files[idx].vsf_req.vsr_vp) == NULL)
+	svc_file = &vscan_svc_files[idx];
+	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
 		return (-1);
 
 	/* get the attributes */
 	xva_init(&xvattr); /* sets AT_XVATTR */
 
 	xvattr.xva_vattr.va_mask |= AT_SIZE;
+	xvattr.xva_vattr.va_mask |= AT_MTIME;
 	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
 	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
 	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
@@ -627,22 +732,24 @@
 		return (-1);
 	}
 
-	vscan_svc_files[idx].vsf_size = xvattr.xva_vattr.va_size;
+	svc_file->vsf_size = xvattr.xva_vattr.va_size;
+	svc_file->vsf_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
+	svc_file->vsf_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
 
 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
 		return (-1);
-	vscan_svc_files[idx].vsf_modified = xoap->xoa_av_modified;
+	svc_file->vsf_modified = xoap->xoa_av_modified;
 
 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
 		return (-1);
-	vscan_svc_files[idx].vsf_quarantined = xoap->xoa_av_quarantined;
+	svc_file->vsf_quarantined = xoap->xoa_av_quarantined;
 
 	if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
-		(void) memcpy(vscan_svc_files[idx].vsf_scanstamp,
+		(void) memcpy(svc_file->vsf_scanstamp,
 		    xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
 	}
 
-	DTRACE_PROBE1(vscan__attr, vscan_file_t *, &(vscan_svc_files[idx]));
+	DTRACE_PROBE1(vscan__getattr, vscan_file_t *, svc_file);
 	return (0);
 }
 
@@ -651,20 +758,20 @@
  * vscan_svc_setattr
  *
  * Set the vscan related system attributes.
- *
- * Caller must already have vscan_svc_mutex
  */
 static int
-vscan_svc_setattr(int idx)
+vscan_svc_setattr(int idx, int which)
 {
 	xvattr_t xvattr;
 	xoptattr_t *xoap = NULL;
 	vnode_t *vp;
 	int len;
+	vscan_file_t *svc_file;
 
 	ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 
-	if ((vp = vscan_svc_files[idx].vsf_req.vsr_vp) == NULL)
+	svc_file = &vscan_svc_files[idx];
+	if ((vp = svc_file->vsf_req.vsr_vp) == NULL)
 		return (-1);
 
 	/* update the attributes */
@@ -672,19 +779,25 @@
 	if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
 		return (-1);
 
-	XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
-	xoap->xoa_av_modified = vscan_svc_files[idx].vsf_modified;
+	if (which & XAT_AV_MODIFIED) {
+		XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
+		xoap->xoa_av_modified = svc_file->vsf_modified;
+	}
 
-	XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
-	xoap->xoa_av_quarantined = vscan_svc_files[idx].vsf_quarantined;
+	if (which & XAT_AV_QUARANTINED) {
+		XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
+		xoap->xoa_av_quarantined = svc_file->vsf_quarantined;
+	}
 
-	XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
-	len = strlen(vscan_svc_files[idx].vsf_scanstamp);
-	(void) memcpy(xoap->xoa_av_scanstamp,
-	    vscan_svc_files[idx].vsf_scanstamp, len);
+	if (which & XAT_AV_SCANSTAMP) {
+		XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
+		len = strlen(svc_file->vsf_scanstamp);
+		(void) memcpy(xoap->xoa_av_scanstamp,
+		    svc_file->vsf_scanstamp, len);
+	}
 
 	/* if access is denied, set mtime to invalidate client cache */
-	if (vscan_svc_files[idx].vsf_access != VS_ACCESS_ALLOW) {
+	if (svc_file->vsf_access != VS_ACCESS_ALLOW) {
 		xvattr.xva_vattr.va_mask |= AT_MTIME;
 		gethrestime(&xvattr.xva_vattr.va_mtime);
 	}
@@ -692,7 +805,9 @@
 	if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
 		return (-1);
 
-	DTRACE_PROBE1(vscan__attr, vscan_file_t *, &(vscan_svc_files[idx]));
+	DTRACE_PROBE2(vscan__setattr,
+	    vscan_file_t *, svc_file, int, which);
+
 	return (0);
 }
 
--- a/usr/src/uts/common/sys/vscan.h	Tue Jan 29 10:19:16 2008 -0800
+++ b/usr/src/uts/common/sys/vscan.h	Tue Jan 29 13:27:28 2008 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -45,10 +45,12 @@
 #define	VS_DRV_IOCTL_DISABLE	0x0002	/* vscand shutting down */
 #define	VS_DRV_IOCTL_CONFIG	0x0004	/* vscand config data update */
 
-/* vsr_access */
-#define	VS_ACCESS_UNDEFINED	0
-#define	VS_ACCESS_ALLOW		1
-#define	VS_ACCESS_DENY		2
+/* Scan Result - vsr_result */
+#define	VS_STATUS_UNDEFINED	0
+#define	VS_STATUS_NO_SCAN	1 /* scan not required */
+#define	VS_STATUS_ERROR		2 /* scan failed */
+#define	VS_STATUS_CLEAN		3 /* scan successful, file clean */
+#define	VS_STATUS_INFECTED	4 /* scan successful, file infected */
 
 #define	VS_TYPES_LEN		4096	/* vs_config_t - types buffer */
 
@@ -68,7 +70,7 @@
 	uint8_t vsr_quarantined;
 	char vsr_path[MAXPATHLEN];
 	vs_scanstamp_t vsr_scanstamp;
-	uint32_t vsr_access; /* VS_ACCESS_ALLOW, VS_ACCESS_DENY */
+	uint32_t vsr_result;
 } vs_scan_req_t;
 
 
@@ -89,10 +91,25 @@
  */
 #define	VS_TYPES_MAX		VS_TYPES_LEN / 2
 
+/*
+ * seconds to wait for daemon to reconnect before unregistering from VFS
+ * during this time, the kernel will:
+ * - allow access to files that have not been modified since last scanned
+ * - deny access to files which have been modified since last scanned
+ */
+#define	VS_DAEMON_WAIT_SEC	60
+
+/* access derived from scan result (VS_STATUS_XXX) and file attributes */
+#define	VS_ACCESS_UNDEFINED	0
+#define	VS_ACCESS_ALLOW		1
+#define	VS_ACCESS_DENY		2
+
 int vscan_svc_init(void);
 void vscan_svc_fini(void);
-void vscan_svc_enable(boolean_t);
+void vscan_svc_enable(void);
+void vscan_svc_disable(void);
 int vscan_svc_configure(vs_config_t *);
+boolean_t vscan_svc_is_enabled(void);
 boolean_t vscan_svc_in_use(void);
 vnode_t *vscan_svc_get_vnode(int);
 
@@ -102,6 +119,8 @@
 void vscan_door_close(void);
 int vscan_door_scan_file(vs_scan_req_t *);
 
+boolean_t vscan_drv_create_node(int);
+
 #endif /* _KERNEL */
 
 #ifdef __cplusplus