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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2015, Joyent, Inc.
30 #include <sys/sunddi.h>
32 #include <sys/varargs.h>
34 #include <sys/modctl.h>
35 #include <sys/cmn_err.h>
36 #include <sys/vnode.h>
37 #include <sys/fs_subr.h>
38 #include <sys/types.h>
44 #include <sys/vscan.h>
45 #include <sys/sysmacros.h>
47 #define VS_REQ_MAGIC 0x52515354 /* 'RQST' */
49 #define VS_REQS_DEFAULT 20000 /* pending scan requests - reql */
50 #define VS_NODES_DEFAULT 128 /* concurrent file scans */
51 #define VS_WORKERS_DEFAULT 32 /* worker threads */
52 #define VS_SCANWAIT_DEFAULT 15*60 /* seconds to wait for scan result */
53 #define VS_REQL_HANDLER_TIMEOUT 30
54 #define VS_EXT_RECURSE_DEPTH 8
56 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
57 #define VS_ACCESS_UNDEFINED 0
58 #define VS_ACCESS_ALLOW 1 /* return 0 */
59 #define VS_ACCESS_DENY 2 /* return EACCES */
61 #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
63 /* global variables - tunable via /etc/system */
64 uint32_t vs_reqs_max
= VS_REQS_DEFAULT
; /* max scan requests */
65 uint32_t vs_nodes_max
= VS_NODES_DEFAULT
; /* max in-progress scan requests */
66 uint32_t vs_workers
= VS_WORKERS_DEFAULT
; /* max workers send reqs to vscand */
67 uint32_t vs_scan_wait
= VS_SCANWAIT_DEFAULT
; /* secs to wait for scan result */
77 * | svc_init | svc_fini
80 * | VS_SVC_IDLE |<----|
81 * +-----------------+ |
84 * |<----------------| |
86 * +-----------------+ | |
87 * | VS_SVC_ENABLED |--| |
88 * +-----------------+ |
90 * | svc_disable | handler thread exit,
91 * v | all requests complete
92 * +-----------------+ |
93 * | VS_SVC_DISABLED |-----|
96 * svc_enable may occur when we are already in the ENABLED
97 * state if vscand has exited without clean shutdown and
98 * then reconnected within the delayed disable time period
99 * (vs_reconnect_timeout) - see vscan_drv
105 VS_SVC_ENABLED
, /* service enabled and registered */
106 VS_SVC_DISABLED
/* service disabled and nunregistered */
108 static vscan_svc_state_t vscan_svc_state
= VS_SVC_UNCONFIG
;
112 * vscan_svc_req_state
114 * When a scan request is received from the file system it is
115 * identified in or inserted into the vscan_svc_reql (INIT).
116 * If the request is asynchronous 0 is then returned to the caller.
117 * If the request is synchronous the req's refcnt is incremented
118 * and the caller waits for the request to complete.
119 * The refcnt is also incremented when the request is inserted
120 * in vscan_svc_nodes, and decremented on scan_complete.
122 * vscan_svc_handler processes requests from the request list,
123 * inserting them into vscan_svc_nodes and the task queue (QUEUED).
124 * When the task queue call back (vscan_svc_do_scan) is invoked
125 * the request transitions to IN_PROGRESS state. If the request
126 * is sucessfully sent to vscand (door_call) and the door response
127 * is SCANNING then the scan result will be received asynchronously.
128 * Although unusual, it is possible that the async response is
129 * received before the door call returns (hence the ASYNC_COMPLETE
131 * When the result has been determined / received,
132 * vscan_svc_scan_complete is invoked to transition the request to
133 * COMPLETE state, decrement refcnt and signal all waiting callers.
134 * When the last waiting caller has processed the result (refcnt == 0)
135 * the request is removed from vscan_svc_reql and vscan_svc_nodes
139 * | reql_insert | refcnt == 0
141 * +------------------------+ +---------------------+
142 * | VS_SVC_REQ_INIT | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
143 * +------------------------+ +---------------------+
145 * | insert_req, tq_dispatch |
147 * +------------------------+ |
148 * | VS_SVC_REQ_QUEUED | scan_complete
149 * +------------------------+ |
151 * | tq_callback (do_scan) |
153 * v scan not req'd, error, |
154 * +------------------------+ or door_result != SCANNING |
155 * | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
156 * +------------------------+ |
158 * | | door_result == SCANNING |
160 * | +---------------------------+ async result |
161 * | | VS_SVC_REQ_SCANNING |-------->---------|
162 * | +---------------------------+ |
166 * +---------------------------+ door_result = SCANNING |
167 * | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
168 * +---------------------------+
173 VS_SVC_REQ_IN_PROGRESS
,
175 VS_SVC_REQ_ASYNC_COMPLETE
,
177 } vscan_svc_req_state_t
;
181 * vscan_svc_reql - the list of pending and in-progress scan requests
183 typedef struct vscan_req
{
184 uint32_t vsr_magic
; /* VS_REQ_MAGIC */
185 list_node_t vsr_lnode
;
187 uint32_t vsr_idx
; /* vscan_svc_nodes index */
188 uint32_t vsr_seqnum
; /* unigue request id */
191 vscan_svc_req_state_t vsr_state
;
194 static list_t vscan_svc_reql
;
198 * vscan_svc_nodes - table of files being scanned
200 * The index into this table is passed in the door call to
201 * vscand. vscand uses the idx to determine which minor node
202 * to open to read the file data. Within the kernel driver
203 * the minor device number can thus be used to identify the
204 * table index to get the appropriate vnode.
206 * Instance 0 is reserved for the daemon/driver control
207 * interface: enable/configure/disable
209 typedef struct vscan_svc_node
{
210 vscan_req_t
*vsn_req
;
211 uint8_t vsn_quarantined
;
212 uint8_t vsn_modified
;
214 timestruc_t vsn_mtime
;
215 vs_scanstamp_t vsn_scanstamp
;
220 static vscan_svc_node_t
*vscan_svc_nodes
;
221 static int vscan_svc_nodes_sz
;
224 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
225 static taskq_t
*vscan_svc_taskq
= NULL
;
227 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
232 } vscan_svc_counts_t
;
233 static vscan_svc_counts_t vscan_svc_counts
;
236 * vscan_svc_mutex protects the data pertaining to scan requests:
237 * request list - vscan_svc_reql
238 * node table - vscan_svc_nodes
240 static kmutex_t vscan_svc_mutex
;
242 /* unique request id for vscand request/response correlation */
243 static uint32_t vscan_svc_seqnum
= 0;
246 * vscan_svc_cfg_mutex protects the configuration data:
247 * vscan_svc_config, vscan_svc_types
249 static kmutex_t vscan_svc_cfg_mutex
;
251 /* configuration data - for virus scan exemption */
252 static vs_config_t vscan_svc_config
;
253 static char *vscan_svc_types
[VS_TYPES_MAX
];
255 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
256 static kthread_t
*vscan_svc_reql_thread
;
257 static kcondvar_t vscan_svc_reql_cv
;
258 static vscan_req_t
*vscan_svc_reql_next
; /* next pending scan request */
260 /* local functions */
261 int vscan_svc_scan_file(vnode_t
*, cred_t
*, int);
262 static void vscan_svc_taskq_callback(void *);
263 static int vscan_svc_exempt_file(vnode_t
*, boolean_t
*);
264 static int vscan_svc_exempt_filetype(char *);
265 static int vscan_svc_match_ext(char *, char *, int);
266 static void vscan_svc_do_scan(vscan_req_t
*);
267 static vs_scan_req_t
*vscan_svc_populate_req(int);
268 static void vscan_svc_process_scan_result(int);
269 static void vscan_svc_scan_complete(vscan_req_t
*);
270 static void vscan_svc_delete_req(vscan_req_t
*);
271 static int vscan_svc_insert_req(vscan_req_t
*);
272 static void vscan_svc_remove_req(int);
273 static vscan_req_t
*vscan_svc_reql_find(vnode_t
*);
274 static vscan_req_t
*vscan_svc_reql_insert(vnode_t
*);
275 static void vscan_svc_reql_remove(vscan_req_t
*);
277 static int vscan_svc_getattr(int);
278 static int vscan_svc_setattr(int, int);
280 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
281 static void vscan_svc_reql_handler(void);
290 if (vscan_svc_state
!= VS_SVC_UNCONFIG
) {
291 DTRACE_PROBE1(vscan__svc__state__violation
,
292 int, vscan_svc_state
);
296 mutex_init(&vscan_svc_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
297 mutex_init(&vscan_svc_cfg_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
298 cv_init(&vscan_svc_reql_cv
, NULL
, CV_DEFAULT
, NULL
);
300 vscan_svc_nodes_sz
= sizeof (vscan_svc_node_t
) * (vs_nodes_max
+ 1);
301 vscan_svc_nodes
= kmem_zalloc(vscan_svc_nodes_sz
, KM_SLEEP
);
303 vscan_svc_counts
.vsc_reql
= 0;
304 vscan_svc_counts
.vsc_node
= 0;
305 vscan_svc_counts
.vsc_tq
= 0;
307 vscan_svc_state
= VS_SVC_IDLE
;
319 if (vscan_svc_state
!= VS_SVC_IDLE
) {
320 DTRACE_PROBE1(vscan__svc__state__violation
,
321 int, vscan_svc_state
);
325 kmem_free(vscan_svc_nodes
, vscan_svc_nodes_sz
);
327 cv_destroy(&vscan_svc_reql_cv
);
328 mutex_destroy(&vscan_svc_mutex
);
329 mutex_destroy(&vscan_svc_cfg_mutex
);
330 vscan_svc_state
= VS_SVC_UNCONFIG
;
338 vscan_svc_enable(void)
340 mutex_enter(&vscan_svc_mutex
);
342 switch (vscan_svc_state
) {
345 * it's possible (and okay) for vscan_svc_enable to be
346 * called when already enabled if vscand reconnects
347 * during a delayed disable
351 list_create(&vscan_svc_reql
, sizeof (vscan_req_t
),
352 offsetof(vscan_req_t
, vsr_lnode
));
353 vscan_svc_reql_next
= list_head(&vscan_svc_reql
);
355 vscan_svc_taskq
= taskq_create("vscan_taskq", vs_workers
,
356 MINCLSYSPRI
, 1, INT_MAX
, TASKQ_DYNAMIC
);
357 ASSERT(vscan_svc_taskq
!= NULL
);
359 vscan_svc_reql_thread
= thread_create(NULL
, 0,
360 vscan_svc_reql_handler
, 0, 0, &p0
, TS_RUN
, MINCLSYSPRI
);
361 ASSERT(vscan_svc_reql_thread
!= NULL
);
363 /* ready to start processing requests */
364 vscan_svc_state
= VS_SVC_ENABLED
;
365 fs_vscan_register(vscan_svc_scan_file
);
368 DTRACE_PROBE1(vscan__svc__state__violation
,
369 int, vscan_svc_state
);
373 mutex_exit(&vscan_svc_mutex
);
381 * Resources allocated during vscan_svc_enable are free'd by
382 * the handler thread immediately prior to exiting
385 vscan_svc_disable(void)
387 mutex_enter(&vscan_svc_mutex
);
389 switch (vscan_svc_state
) {
391 fs_vscan_register(NULL
);
392 vscan_svc_state
= VS_SVC_DISABLED
;
393 cv_signal(&vscan_svc_reql_cv
); /* wake handler thread */
396 DTRACE_PROBE1(vscan__svc__state__violation
, int,
400 mutex_exit(&vscan_svc_mutex
);
412 mutex_enter(&vscan_svc_mutex
);
414 switch (vscan_svc_state
) {
416 case VS_SVC_UNCONFIG
:
424 mutex_exit(&vscan_svc_mutex
);
430 * vscan_svc_get_vnode
432 * Get the file vnode indexed by idx.
435 vscan_svc_get_vnode(int idx
)
440 ASSERT(idx
<= vs_nodes_max
);
442 mutex_enter(&vscan_svc_mutex
);
443 if (vscan_svc_nodes
[idx
].vsn_req
)
444 vp
= vscan_svc_nodes
[idx
].vsn_req
->vsr_vp
;
445 mutex_exit(&vscan_svc_mutex
);
452 * vscan_svc_scan_file
454 * This function is the entry point for the file system to
455 * request that a file be virus scanned.
458 vscan_svc_scan_file(vnode_t
*vp
, cred_t
*cr
, int async
)
463 clock_t timeout
, time_left
;
465 if ((vp
== NULL
) || (vp
->v_path
== vn_vpath_empty
) || cr
== NULL
)
468 DTRACE_PROBE2(vscan__scan__file
, char *, vp
->v_path
, int, async
);
470 /* check if size or type exempts file from scanning */
471 if (vscan_svc_exempt_file(vp
, &allow
)) {
472 if ((allow
== B_TRUE
) || (async
!= 0))
478 mutex_enter(&vscan_svc_mutex
);
480 if (vscan_svc_state
!= VS_SVC_ENABLED
) {
481 DTRACE_PROBE1(vscan__svc__state__violation
,
482 int, vscan_svc_state
);
483 mutex_exit(&vscan_svc_mutex
);
487 /* insert (or find) request in list */
488 if ((req
= vscan_svc_reql_insert(vp
)) == NULL
) {
489 mutex_exit(&vscan_svc_mutex
);
490 cmn_err(CE_WARN
, "Virus scan request list full");
491 return ((async
!= 0) ? 0 : EACCES
);
494 /* asynchronous request: return 0 */
496 mutex_exit(&vscan_svc_mutex
);
500 /* synchronous scan request: wait for result */
502 time_left
= SEC_TO_TICK(vs_scan_wait
);
503 while ((time_left
> 0) && (req
->vsr_state
!= VS_SVC_REQ_COMPLETE
)) {
505 time_left
= cv_reltimedwait_sig(&(req
->vsr_cv
),
506 &vscan_svc_mutex
, timeout
, TR_CLOCK_TICK
);
509 if (time_left
== -1) {
510 cmn_err(CE_WARN
, "Virus scan request timeout %s (%d) \n",
511 vp
->v_path
, req
->vsr_seqnum
);
512 DTRACE_PROBE1(vscan__scan__timeout
, vscan_req_t
*, req
);
515 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
516 if (vscan_svc_state
== VS_SVC_DISABLED
)
517 access
= VS_ACCESS_ALLOW
;
518 else if (req
->vsr_idx
== 0)
519 access
= VS_ACCESS_DENY
;
521 access
= vscan_svc_nodes
[req
->vsr_idx
].vsn_access
;
523 if ((--req
->vsr_refcnt
) == 0)
524 vscan_svc_delete_req(req
);
526 mutex_exit(&vscan_svc_mutex
);
527 return ((access
== VS_ACCESS_ALLOW
) ? 0 : EACCES
);
532 * vscan_svc_reql_handler
534 * inserts scan requests (from vscan_svc_reql) into
535 * vscan_svc_nodes and vscan_svc_taskq
538 vscan_svc_reql_handler(void)
540 vscan_req_t
*req
, *next
;
543 mutex_enter(&vscan_svc_mutex
);
545 if ((vscan_svc_state
== VS_SVC_DISABLED
) &&
546 (vscan_svc_counts
.vsc_reql
== 0)) {
547 /* free resources allocated durining enable */
548 taskq_destroy(vscan_svc_taskq
);
549 vscan_svc_taskq
= NULL
;
550 list_destroy(&vscan_svc_reql
);
551 vscan_svc_state
= VS_SVC_IDLE
;
552 mutex_exit(&vscan_svc_mutex
);
557 * If disabled, scan_complete any pending requests.
558 * Otherwise insert pending requests into vscan_svc_nodes
559 * and vscan_svc_taskq. If no slots are available in
560 * vscan_svc_nodes break loop and wait for one
562 req
= vscan_svc_reql_next
;
564 while (req
!= NULL
) {
565 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
566 next
= list_next(&vscan_svc_reql
, req
);
568 if (vscan_svc_state
== VS_SVC_DISABLED
) {
569 vscan_svc_scan_complete(req
);
571 /* insert request into vscan_svc_nodes */
572 if (vscan_svc_insert_req(req
) == -1)
575 /* add the scan request into the taskq */
576 (void) taskq_dispatch(vscan_svc_taskq
,
577 vscan_svc_taskq_callback
,
578 (void *)req
, TQ_SLEEP
);
579 ++(vscan_svc_counts
.vsc_tq
);
581 req
->vsr_state
= VS_SVC_REQ_QUEUED
;
586 vscan_svc_reql_next
= req
;
588 DTRACE_PROBE2(vscan__req__counts
, char *, "handler wait",
589 vscan_svc_counts_t
*, &vscan_svc_counts
);
591 (void) cv_reltimedwait(&vscan_svc_reql_cv
, &vscan_svc_mutex
,
592 SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT
), TR_CLOCK_TICK
);
594 DTRACE_PROBE2(vscan__req__counts
, char *, "handler wake",
595 vscan_svc_counts_t
*, &vscan_svc_counts
);
597 mutex_exit(&vscan_svc_mutex
);
603 vscan_svc_taskq_callback(void *data
)
607 mutex_enter(&vscan_svc_mutex
);
609 req
= (vscan_req_t
*)data
;
610 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
611 vscan_svc_do_scan(req
);
612 if (req
->vsr_state
!= VS_SVC_REQ_SCANNING
)
613 vscan_svc_scan_complete(req
);
615 --(vscan_svc_counts
.vsc_tq
);
616 mutex_exit(&vscan_svc_mutex
);
623 * Note: To avoid potential deadlock it is important that
624 * vscan_svc_mutex is not held during the call to
625 * vscan_drv_create_note. vscan_drv_create_note enters
626 * the vscan_drv_mutex and it is possible that a thread
627 * holding that mutex could be waiting for vscan_svc_mutex.
630 vscan_svc_do_scan(vscan_req_t
*req
)
633 vscan_svc_node_t
*node
;
634 vs_scan_req_t
*door_req
;
636 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
639 node
= &vscan_svc_nodes
[idx
];
641 req
->vsr_state
= VS_SVC_REQ_IN_PROGRESS
;
643 /* if vscan not enabled (shutting down), allow ACCESS */
644 if (vscan_svc_state
!= VS_SVC_ENABLED
) {
645 node
->vsn_access
= VS_ACCESS_ALLOW
;
649 if (vscan_svc_getattr(idx
) != 0) {
650 cmn_err(CE_WARN
, "Can't access xattr for %s\n",
651 req
->vsr_vp
->v_path
);
652 node
->vsn_access
= VS_ACCESS_DENY
;
656 /* valid scan_req ptr guaranteed */
657 door_req
= vscan_svc_populate_req(idx
);
659 /* free up mutex around create node and door call */
660 mutex_exit(&vscan_svc_mutex
);
661 if (vscan_drv_create_node(idx
) != B_TRUE
)
662 result
= VS_STATUS_ERROR
;
664 result
= vscan_door_scan_file(door_req
);
665 kmem_free(door_req
, sizeof (vs_scan_req_t
));
666 mutex_enter(&vscan_svc_mutex
);
668 if (result
!= VS_STATUS_SCANNING
) {
669 vscan_svc_nodes
[idx
].vsn_result
= result
;
670 vscan_svc_process_scan_result(idx
);
671 } else { /* async response */
672 if (req
->vsr_state
== VS_SVC_REQ_IN_PROGRESS
)
673 req
->vsr_state
= VS_SVC_REQ_SCANNING
;
679 * vscan_svc_populate_req
681 * Allocate a scan request to be sent to vscand, populating it
682 * from the data in vscan_svc_nodes[idx].
684 * Returns: scan request object
686 static vs_scan_req_t
*
687 vscan_svc_populate_req(int idx
)
689 vs_scan_req_t
*scan_req
;
691 vscan_svc_node_t
*node
;
693 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
695 node
= &vscan_svc_nodes
[idx
];
697 scan_req
= kmem_zalloc(sizeof (vs_scan_req_t
), KM_SLEEP
);
699 scan_req
->vsr_idx
= idx
;
700 scan_req
->vsr_seqnum
= req
->vsr_seqnum
;
701 (void) strncpy(scan_req
->vsr_path
, req
->vsr_vp
->v_path
, MAXPATHLEN
);
702 scan_req
->vsr_size
= node
->vsn_size
;
703 scan_req
->vsr_modified
= node
->vsn_modified
;
704 scan_req
->vsr_quarantined
= node
->vsn_quarantined
;
705 scan_req
->vsr_flags
= 0;
706 (void) strncpy(scan_req
->vsr_scanstamp
,
707 node
->vsn_scanstamp
, sizeof (vs_scanstamp_t
));
714 * vscan_svc_scan_complete
717 vscan_svc_scan_complete(vscan_req_t
*req
)
719 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
722 req
->vsr_state
= VS_SVC_REQ_COMPLETE
;
724 if ((--req
->vsr_refcnt
) == 0)
725 vscan_svc_delete_req(req
);
727 cv_broadcast(&(req
->vsr_cv
));
732 * vscan_svc_delete_req
735 vscan_svc_delete_req(vscan_req_t
*req
)
739 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
741 ASSERT(req
->vsr_refcnt
== 0);
742 ASSERT(req
->vsr_state
== VS_SVC_REQ_COMPLETE
);
744 if ((idx
= req
->vsr_idx
) != 0)
745 vscan_svc_remove_req(idx
);
747 vscan_svc_reql_remove(req
);
749 cv_signal(&vscan_svc_reql_cv
);
754 * vscan_svc_scan_result
756 * Invoked from vscan_drv.c on receipt of an ioctl containing
757 * an async scan result (VS_DRV_IOCTL_RESULT)
758 * If the vsr_seqnum in the response does not match that in the
759 * vscan_svc_nodes entry the result is discarded.
762 vscan_svc_scan_result(vs_scan_rsp_t
*scan_rsp
)
765 vscan_svc_node_t
*node
;
767 mutex_enter(&vscan_svc_mutex
);
769 node
= &vscan_svc_nodes
[scan_rsp
->vsr_idx
];
771 if ((req
= node
->vsn_req
) == NULL
) {
772 mutex_exit(&vscan_svc_mutex
);
776 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
778 if (scan_rsp
->vsr_seqnum
!= req
->vsr_seqnum
) {
779 mutex_exit(&vscan_svc_mutex
);
783 node
->vsn_result
= scan_rsp
->vsr_result
;
784 (void) strncpy(node
->vsn_scanstamp
,
785 scan_rsp
->vsr_scanstamp
, sizeof (vs_scanstamp_t
));
787 vscan_svc_process_scan_result(scan_rsp
->vsr_idx
);
789 if (node
->vsn_req
->vsr_state
== VS_SVC_REQ_SCANNING
)
790 vscan_svc_scan_complete(node
->vsn_req
);
792 node
->vsn_req
->vsr_state
= VS_SVC_REQ_ASYNC_COMPLETE
;
794 mutex_exit(&vscan_svc_mutex
);
799 * vscan_svc_scan_abort
801 * Abort in-progress scan requests.
804 vscan_svc_scan_abort()
809 mutex_enter(&vscan_svc_mutex
);
811 for (idx
= 1; idx
<= vs_nodes_max
; idx
++) {
812 if ((req
= vscan_svc_nodes
[idx
].vsn_req
) == NULL
)
815 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
817 if (req
->vsr_state
== VS_SVC_REQ_SCANNING
) {
818 DTRACE_PROBE1(vscan__abort
, vscan_req_t
*, req
);
819 vscan_svc_process_scan_result(idx
);
820 vscan_svc_scan_complete(req
);
824 mutex_exit(&vscan_svc_mutex
);
829 * vscan_svc_process_scan_result
831 * Sets vsn_access and updates file attributes based on vsn_result,
835 * deny access, set quarantine attribute, clear scanstamp
837 * allow access, set scanstamp,
838 * if file not modified since scan initiated, clear modified attribute
840 * deny access if file quarantined, otherwise allow access
841 * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
842 * deny access if file quarantined, modified or no scanstamp
843 * otherwise, allow access
846 vscan_svc_process_scan_result(int idx
)
851 vscan_svc_node_t
*node
;
853 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
855 node
= &vscan_svc_nodes
[idx
];
857 switch (node
->vsn_result
) {
858 case VS_STATUS_INFECTED
:
859 node
->vsn_access
= VS_ACCESS_DENY
;
860 node
->vsn_quarantined
= 1;
861 node
->vsn_scanstamp
[0] = '\0';
862 (void) vscan_svc_setattr(idx
,
863 XAT_AV_QUARANTINED
| XAT_AV_SCANSTAMP
);
866 case VS_STATUS_CLEAN
:
867 node
->vsn_access
= VS_ACCESS_ALLOW
;
869 /* if mtime has changed, don't clear the modified attribute */
870 vp
= node
->vsn_req
->vsr_vp
;
871 mtime
= &(node
->vsn_mtime
);
872 attr
.va_mask
= VATTR_MTIME
;
873 if ((fop_getattr(vp
, &attr
, 0, kcred
, NULL
) != 0) ||
874 (mtime
->tv_sec
!= attr
.va_mtime
.tv_sec
) ||
875 (mtime
->tv_nsec
!= attr
.va_mtime
.tv_nsec
)) {
876 DTRACE_PROBE1(vscan__mtime__changed
, vscan_svc_node_t
*,
878 (void) vscan_svc_setattr(idx
, XAT_AV_SCANSTAMP
);
882 node
->vsn_modified
= 0;
883 (void) vscan_svc_setattr(idx
,
884 XAT_AV_SCANSTAMP
| XAT_AV_MODIFIED
);
887 case VS_STATUS_NO_SCAN
:
888 if (node
->vsn_quarantined
)
889 node
->vsn_access
= VS_ACCESS_DENY
;
891 node
->vsn_access
= VS_ACCESS_ALLOW
;
894 case VS_STATUS_ERROR
:
895 case VS_STATUS_UNDEFINED
:
897 if ((node
->vsn_quarantined
) ||
898 (node
->vsn_modified
) ||
899 (node
->vsn_scanstamp
[0] == '\0'))
900 node
->vsn_access
= VS_ACCESS_DENY
;
902 node
->vsn_access
= VS_ACCESS_ALLOW
;
906 DTRACE_PROBE4(vscan__result
,
907 int, idx
, int, node
->vsn_req
->vsr_seqnum
,
908 int, node
->vsn_result
, int, node
->vsn_access
);
915 * Get the vscan related system attributes, VATTR_SIZE & VATTR_MTIME.
918 vscan_svc_getattr(int idx
)
921 xoptattr_t
*xoap
= NULL
;
923 vscan_svc_node_t
*node
;
925 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
927 node
= &vscan_svc_nodes
[idx
];
928 if ((vp
= node
->vsn_req
->vsr_vp
) == NULL
)
931 /* get the attributes */
932 xva_init(&xvattr
); /* sets VATTR_XVATTR */
934 xvattr
.xva_vattr
.va_mask
|= VATTR_SIZE
;
935 xvattr
.xva_vattr
.va_mask
|= VATTR_MTIME
;
936 XVA_SET_REQ(&xvattr
, XAT_AV_MODIFIED
);
937 XVA_SET_REQ(&xvattr
, XAT_AV_QUARANTINED
);
938 XVA_SET_REQ(&xvattr
, XAT_AV_SCANSTAMP
);
940 if (fop_getattr(vp
, (vattr_t
*)&xvattr
, 0, kcred
, NULL
) != 0)
943 if ((xoap
= xva_getxoptattr(&xvattr
)) == NULL
) {
944 cmn_err(CE_NOTE
, "Virus scan request failed; "
945 "file system does not support virus scanning");
949 node
->vsn_size
= xvattr
.xva_vattr
.va_size
;
950 node
->vsn_mtime
.tv_sec
= xvattr
.xva_vattr
.va_mtime
.tv_sec
;
951 node
->vsn_mtime
.tv_nsec
= xvattr
.xva_vattr
.va_mtime
.tv_nsec
;
953 if (XVA_ISSET_RTN(&xvattr
, XAT_AV_MODIFIED
) == 0)
955 node
->vsn_modified
= xoap
->xoa_av_modified
;
957 if (XVA_ISSET_RTN(&xvattr
, XAT_AV_QUARANTINED
) == 0)
959 node
->vsn_quarantined
= xoap
->xoa_av_quarantined
;
961 if (XVA_ISSET_RTN(&xvattr
, XAT_AV_SCANSTAMP
) != 0) {
962 (void) memcpy(node
->vsn_scanstamp
,
963 xoap
->xoa_av_scanstamp
, AV_SCANSTAMP_SZ
);
966 DTRACE_PROBE1(vscan__getattr
, vscan_svc_node_t
*, node
);
974 * Set the vscan related system attributes.
977 vscan_svc_setattr(int idx
, int which
)
980 xoptattr_t
*xoap
= NULL
;
983 vscan_svc_node_t
*node
;
985 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
987 node
= &vscan_svc_nodes
[idx
];
988 if ((vp
= node
->vsn_req
->vsr_vp
) == NULL
)
991 /* update the attributes */
992 xva_init(&xvattr
); /* sets VATTR_XVATTR */
993 if ((xoap
= xva_getxoptattr(&xvattr
)) == NULL
)
996 if (which
& XAT_AV_MODIFIED
) {
997 XVA_SET_REQ(&xvattr
, XAT_AV_MODIFIED
);
998 xoap
->xoa_av_modified
= node
->vsn_modified
;
1001 if (which
& XAT_AV_QUARANTINED
) {
1002 XVA_SET_REQ(&xvattr
, XAT_AV_QUARANTINED
);
1003 xoap
->xoa_av_quarantined
= node
->vsn_quarantined
;
1006 if (which
& XAT_AV_SCANSTAMP
) {
1007 XVA_SET_REQ(&xvattr
, XAT_AV_SCANSTAMP
);
1008 len
= strlen(node
->vsn_scanstamp
);
1009 (void) memcpy(xoap
->xoa_av_scanstamp
,
1010 node
->vsn_scanstamp
, len
);
1013 /* if access is denied, set mtime to invalidate client cache */
1014 if (node
->vsn_access
!= VS_ACCESS_ALLOW
) {
1015 xvattr
.xva_vattr
.va_mask
|= VATTR_MTIME
;
1016 gethrestime(&xvattr
.xva_vattr
.va_mtime
);
1019 if (fop_setattr(vp
, (vattr_t
*)&xvattr
, 0, kcred
, NULL
) != 0)
1022 DTRACE_PROBE2(vscan__setattr
,
1023 vscan_svc_node_t
*, node
, int, which
);
1030 * vscan_svc_configure
1032 * store configuration in vscan_svc_config
1033 * set up vscan_svc_types array of pointers into
1034 * vscan_svc_config.vsc_types for efficient searching
1037 vscan_svc_configure(vs_config_t
*conf
)
1040 char *p
, *beg
, *end
;
1042 mutex_enter(&vscan_svc_cfg_mutex
);
1044 vscan_svc_config
= *conf
;
1046 (void) memset(vscan_svc_types
, 0, sizeof (vscan_svc_types
));
1048 beg
= vscan_svc_config
.vsc_types
;
1049 end
= beg
+ vscan_svc_config
.vsc_types_len
;
1051 for (p
= beg
; p
< end
; p
+= strlen(p
) + 1) {
1052 if (count
>= VS_TYPES_MAX
) {
1053 mutex_exit(&vscan_svc_mutex
);
1057 vscan_svc_types
[count
] = p
;
1061 mutex_exit(&vscan_svc_cfg_mutex
);
1067 * vscan_svc_exempt_file
1069 * check if a file's size or type exempts it from virus scanning
1071 * If the file is exempt from virus scanning, allow will be set
1072 * to define whether files access should be allowed (B_TRUE) or
1079 vscan_svc_exempt_file(vnode_t
*vp
, boolean_t
*allow
)
1085 attr
.va_mask
= VATTR_SIZE
;
1087 if (fop_getattr(vp
, &attr
, 0, kcred
, NULL
) != 0) {
1092 mutex_enter(&vscan_svc_cfg_mutex
);
1094 if (attr
.va_size
> vscan_svc_config
.vsc_max_size
) {
1095 DTRACE_PROBE2(vscan__exempt__filesize
, char *,
1096 vp
->v_path
, int, *allow
);
1098 *allow
= (vscan_svc_config
.vsc_allow
) ? B_TRUE
: B_FALSE
;
1099 mutex_exit(&vscan_svc_cfg_mutex
);
1103 if (vscan_svc_exempt_filetype(vp
->v_path
)) {
1104 DTRACE_PROBE1(vscan__exempt__filetype
, char *, vp
->v_path
);
1106 mutex_exit(&vscan_svc_cfg_mutex
);
1110 mutex_exit(&vscan_svc_cfg_mutex
);
1116 * vscan_svc_exempt_filetype
1118 * Each entry in vscan_svc_types includes a rule indicator (+,-)
1119 * followed by the match string for file types to which the rule
1120 * applies. Look for first match of file type in vscan_svc_types
1121 * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
1122 * if the indicator is '+'.
1123 * If vscan_svc_match_ext fails, or no match is found, return 0
1126 * Returns 1: exempt, 0: not exempt
1129 vscan_svc_exempt_filetype(char *filepath
)
1131 int i
, rc
, exempt
= 0;
1132 char *filename
, *ext
;
1134 ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex
));
1136 if ((filename
= strrchr(filepath
, '/')) == 0)
1137 filename
= filepath
;
1141 if ((ext
= strrchr(filename
, '.')) == NULL
)
1146 for (i
= 0; i
< VS_TYPES_MAX
; i
++) {
1147 if (vscan_svc_types
[i
] == 0)
1150 rc
= vscan_svc_match_ext(vscan_svc_types
[i
] + 1, ext
, 1);
1154 DTRACE_PROBE2(vscan__type__match
, char *, ext
,
1155 char *, vscan_svc_types
[i
]);
1156 exempt
= (vscan_svc_types
[i
][0] == '-');
1166 * vscan_svc_match_ext
1168 * Performs a case-insensitive match for two strings. The first string
1169 * argument can contain the wildcard characters '?' and '*'
1171 * Returns: 0 no match
1173 * -1 recursion error
1176 vscan_svc_match_ext(char *patn
, char *str
, int depth
)
1179 if (depth
> VS_EXT_RECURSE_DEPTH
)
1201 if (vscan_svc_match_ext(patn
, str
, depth
+ 1))
1208 if (*str
!= *patn
) {
1227 * vscan_svc_insert_req
1229 * Insert request in next available available slot in vscan_svc_nodes
1231 * Returns: idx of slot, or -1 if no slot available
1234 vscan_svc_insert_req(vscan_req_t
*req
)
1237 vscan_svc_node_t
*node
;
1239 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
1241 if (vscan_svc_counts
.vsc_node
== vs_nodes_max
)
1244 for (idx
= 1; idx
<= vs_nodes_max
; idx
++) {
1245 if (vscan_svc_nodes
[idx
].vsn_req
== NULL
) {
1248 node
= &vscan_svc_nodes
[idx
];
1249 (void) memset(node
, 0, sizeof (vscan_svc_node_t
));
1250 node
->vsn_req
= req
;
1251 node
->vsn_modified
= 1;
1252 node
->vsn_result
= VS_STATUS_UNDEFINED
;
1253 node
->vsn_access
= VS_ACCESS_UNDEFINED
;
1255 ++(vscan_svc_counts
.vsc_node
);
1265 * vscan_svc_remove_req
1268 vscan_svc_remove_req(int idx
)
1270 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
1273 (void) memset(&vscan_svc_nodes
[idx
], 0,
1274 sizeof (vscan_svc_node_t
));
1275 --(vscan_svc_counts
.vsc_node
);
1281 * vscan_svc_reql_find
1283 static vscan_req_t
*
1284 vscan_svc_reql_find(vnode_t
*vp
)
1287 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
1289 req
= list_head(&vscan_svc_reql
);
1291 while (req
!= NULL
) {
1292 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
1293 if ((req
->vsr_vp
== vp
) &&
1294 (req
->vsr_state
!= VS_SVC_REQ_COMPLETE
))
1297 req
= list_next(&vscan_svc_reql
, req
);
1305 * vscan_svc_reql_insert
1307 static vscan_req_t
*
1308 vscan_svc_reql_insert(vnode_t
*vp
)
1312 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
1314 /* if request already in list then return it */
1315 if ((req
= vscan_svc_reql_find(vp
)) != NULL
)
1318 /* if list is full return NULL */
1319 if (vscan_svc_counts
.vsc_reql
== vs_reqs_max
)
1322 /* create a new request and insert into list */
1325 req
= kmem_zalloc(sizeof (vscan_req_t
), KM_SLEEP
);
1327 req
->vsr_magic
= VS_REQ_MAGIC
;
1328 if (vscan_svc_seqnum
== UINT32_MAX
)
1329 vscan_svc_seqnum
= 0;
1330 req
->vsr_seqnum
= ++vscan_svc_seqnum
;
1332 req
->vsr_refcnt
= 1; /* decremented in vscan_svc_scan_complete */
1333 req
->vsr_state
= VS_SVC_REQ_INIT
;
1334 cv_init(&(req
->vsr_cv
), NULL
, CV_DEFAULT
, NULL
);
1336 list_insert_tail(&vscan_svc_reql
, req
);
1337 if (vscan_svc_reql_next
== NULL
)
1338 vscan_svc_reql_next
= req
;
1340 ++(vscan_svc_counts
.vsc_reql
);
1342 /* wake reql handler thread */
1343 cv_signal(&vscan_svc_reql_cv
);
1350 * vscan_svc_reql_remove
1353 vscan_svc_reql_remove(vscan_req_t
*req
)
1355 ASSERT(MUTEX_HELD(&vscan_svc_mutex
));
1356 ASSERT(req
->vsr_magic
== VS_REQ_MAGIC
);
1358 if (vscan_svc_reql_next
== req
)
1359 vscan_svc_reql_next
= list_next(&vscan_svc_reql
, req
);
1361 list_remove(&vscan_svc_reql
, req
);
1362 cv_destroy(&(req
->vsr_cv
));
1363 VN_RELE(req
->vsr_vp
);
1365 kmem_free(req
, sizeof (vscan_req_t
));
1366 --(vscan_svc_counts
.vsc_reql
);