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.
29 #include <sys/sunddi.h>
31 #include <sys/varargs.h>
33 #include <sys/modctl.h>
34 #include <sys/vnode.h>
35 #include <sys/fs_subr.h>
36 #include <sys/types.h>
39 #include <sys/vscan.h>
40 #include <sys/policy.h>
44 /* seconds to wait for daemon to reconnect before disabling */
45 #define VS_DAEMON_WAIT_SEC 60
47 /* length of minor node name - vscan%d */
48 #define VS_NODENAME_LEN 16
50 /* global variables - tunable via /etc/system */
51 uint32_t vs_reconnect_timeout
= VS_DAEMON_WAIT_SEC
;
52 extern uint32_t vs_nodes_max
; /* max in-progress scan requests */
57 * Operations on instance 0 represent vscand initiated state
59 * open(0) - vscand connect
60 * close(0) - vscan disconnect
61 * enable(0) - vscand enable (ready to hand requests)
62 * disable(0) - vscand disable (shutting down)
64 * +------------------------+
66 * +------------------------+
70 * +------------------------+
71 * | VS_DRV_IDLE |<------|
72 * +------------------------+ |
74 * | open(0) | close(0) |
76 * +------------------------+ |
77 * | VS_DRV_CONNECTED |<-| |
78 * +------------------------+ | |
80 * | enable(0) | disable(0) | |
82 * +------------------------+ | |
83 * | VS_DRV_ENABLED | | |
84 * +------------------------+ | |
86 * | close(0) open(0) |
88 * +------------------------+ | | timeout
89 * | VS_DRV_DELAYED_DISABLE | -- |
90 * +------------------------+ ------|
98 VS_DRV_DELAYED_DISABLE
100 static vscan_drv_state_t vscan_drv_state
= VS_DRV_UNCONFIG
;
104 * vscan_drv_inst_state
106 * Instance 0 controls the state of the driver: vscan_drv_state.
107 * vscan_drv_inst_state[0] should NOT be used.
109 * vscan_drv_inst_state[n] represents the state of driver
110 * instance n, used by vscand to access file data for the
111 * scan request with index n in vscan_svc_reqs.
112 * Minor nodes are created as required then all are destroyed
113 * during driver detach.
115 * +------------------------+
116 * | VS_DRV_INST_UNCONFIG |
117 * +------------------------+
119 * | create_node(n) | detach
121 * +------------------------+
122 * | VS_DRV_INST_INIT |<-|
123 * +------------------------+ |
127 * +------------------------+ |
128 * | VS_DRV_INST_OPEN |--|
129 * +------------------------+ |
133 * +------------------------+ |
134 * | VS_DRV_INST_READING |--|
135 * +------------------------+
138 VS_DRV_INST_UNCONFIG
= 0, /* minor node not created */
142 } vscan_drv_inst_state_t
;
144 static vscan_drv_inst_state_t
*vscan_drv_inst_state
;
145 static int vscan_drv_inst_state_sz
;
147 static dev_info_t
*vscan_drv_dip
;
148 static kmutex_t vscan_drv_mutex
;
149 static kcondvar_t vscan_drv_cv
; /* wait for daemon reconnect */
154 static int vscan_drv_attach(dev_info_t
*, ddi_attach_cmd_t
);
155 static int vscan_drv_detach(dev_info_t
*, ddi_detach_cmd_t
);
156 static int vscan_drv_getinfo(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
157 static int vscan_drv_open(dev_t
*, int, int, cred_t
*);
158 static int vscan_drv_close(dev_t
, int, int, cred_t
*);
159 static int vscan_drv_read(dev_t
, struct uio
*, cred_t
*);
160 static int vscan_drv_ioctl(dev_t
, int, intptr_t, int, cred_t
*, int *);
162 static boolean_t
vscan_drv_in_use(void);
163 static void vscan_drv_delayed_disable(void);
167 * module linkage info for the kernel
169 static struct cb_ops cbops
= {
170 vscan_drv_open
, /* cb_open */
171 vscan_drv_close
, /* cb_close */
172 nodev
, /* cb_strategy */
173 nodev
, /* cb_print */
175 vscan_drv_read
, /* cb_read */
176 nodev
, /* cb_write */
177 vscan_drv_ioctl
, /* cb_ioctl */
178 nodev
, /* cb_devmap */
180 nodev
, /* cb_segmap */
181 nochpoll
, /* cb_chpoll */
182 ddi_prop_op
, /* cb_prop_op */
183 NULL
, /* cb_streamtab */
186 nodev
, /* cb_aread */
187 nodev
, /* cb_awrite */
190 static struct dev_ops devops
= {
191 DEVO_REV
, /* devo_rev */
193 vscan_drv_getinfo
, /* devo_getinfo */
194 nulldev
, /* devo_identify */
195 nulldev
, /* devo_probe */
196 vscan_drv_attach
, /* devo_attach */
197 vscan_drv_detach
, /* devo_detach */
198 nodev
, /* devo_reset */
199 &cbops
, /* devo_cb_ops */
200 NULL
, /* devo_bus_ops */
201 NULL
, /* devo_power */
202 ddi_quiesce_not_needed
, /* devo_quiesce */
205 static struct modldrv modldrv
= {
206 &mod_driverops
, /* drv_modops */
207 "virus scanning", /* drv_linkinfo */
211 static struct modlinkage modlinkage
= {
213 MODREV_1
, /* revision of the module, must be: MODREV_1 */
214 &modldrv
, /* ptr to linkage structures */
227 vscan_drv_inst_state_sz
=
228 sizeof (vscan_drv_inst_state_t
) * (vs_nodes_max
+ 1);
230 if (vscan_door_init() != 0)
231 return (DDI_FAILURE
);
233 if (vscan_svc_init() != 0) {
235 return (DDI_FAILURE
);
238 mutex_init(&vscan_drv_mutex
, NULL
, MUTEX_DRIVER
, NULL
);
239 vscan_drv_inst_state
= kmem_zalloc(vscan_drv_inst_state_sz
, KM_SLEEP
);
241 cv_init(&vscan_drv_cv
, NULL
, CV_DEFAULT
, NULL
);
243 if ((rc
= mod_install(&modlinkage
)) != 0) {
246 kmem_free(vscan_drv_inst_state
, vscan_drv_inst_state_sz
);
247 cv_destroy(&vscan_drv_cv
);
248 mutex_destroy(&vscan_drv_mutex
);
259 _info(struct modinfo
*modinfop
)
261 return (mod_info(&modlinkage
, modinfop
));
273 if (vscan_drv_in_use())
276 if ((rc
= mod_remove(&modlinkage
)) == 0) {
279 kmem_free(vscan_drv_inst_state
, vscan_drv_inst_state_sz
);
280 cv_destroy(&vscan_drv_cv
);
281 mutex_destroy(&vscan_drv_mutex
);
297 vscan_drv_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **result
)
299 ulong_t inst
= getminor((dev_t
)arg
);
302 case DDI_INFO_DEVT2DEVINFO
:
303 *result
= vscan_drv_dip
;
304 return (DDI_SUCCESS
);
305 case DDI_INFO_DEVT2INSTANCE
:
306 *result
= (void *)inst
;
307 return (DDI_SUCCESS
);
309 return (DDI_FAILURE
);
317 vscan_drv_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
319 if (cmd
!= DDI_ATTACH
)
320 return (DDI_FAILURE
);
322 if (ddi_get_instance(dip
) != 0)
323 return (DDI_FAILURE
);
327 /* create minor node 0 for daemon-driver synchronization */
328 if (vscan_drv_create_node(0) == B_FALSE
)
329 return (DDI_FAILURE
);
331 vscan_drv_state
= VS_DRV_IDLE
;
332 return (DDI_SUCCESS
);
340 vscan_drv_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
344 if (cmd
!= DDI_DETACH
)
345 return (DDI_FAILURE
);
347 if (ddi_get_instance(dip
) != 0)
348 return (DDI_FAILURE
);
350 if (vscan_drv_in_use())
351 return (DDI_FAILURE
);
353 /* remove all minor nodes */
354 vscan_drv_dip
= NULL
;
355 ddi_remove_minor_node(dip
, NULL
);
356 for (i
= 0; i
<= vs_nodes_max
; i
++)
357 vscan_drv_inst_state
[i
] = VS_DRV_INST_UNCONFIG
;
359 vscan_drv_state
= VS_DRV_UNCONFIG
;
360 return (DDI_SUCCESS
);
367 * If the driver state is not IDLE or UNCONFIG then the
368 * driver is in use. Otherwise, check the service interface
369 * (vscan_svc) to see if it is still in use - for example
370 * there there may be requests still in progress.
375 boolean_t in_use
= B_FALSE
;
377 mutex_enter(&vscan_drv_mutex
);
378 if ((vscan_drv_state
!= VS_DRV_IDLE
) &&
379 (vscan_drv_state
!= VS_DRV_UNCONFIG
)) {
382 mutex_exit(&vscan_drv_mutex
);
387 return (vscan_svc_in_use());
394 * If inst == 0, this is vscand initializing.
395 * If the driver is in DELAYED_DISABLE, ie vscand previously
396 * disconnected without a clean shutdown and the driver is
397 * waiting for a period to allow vscand to reconnect, signal
398 * vscan_drv_cv to cancel the delayed disable.
400 * If inst != 0, open the file associated with inst.
404 vscan_drv_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*credp
)
407 int inst
= getminor(*devp
);
409 if ((inst
< 0) || (inst
> vs_nodes_max
))
412 /* check if caller has privilege for virus scanning */
413 if ((rc
= secpolicy_vscan(credp
)) != 0) {
414 DTRACE_PROBE1(vscan__priv
, int, rc
);
418 mutex_enter(&vscan_drv_mutex
);
420 switch (vscan_drv_state
) {
422 vscan_drv_state
= VS_DRV_CONNECTED
;
424 case VS_DRV_DELAYED_DISABLE
:
425 cv_signal(&vscan_drv_cv
);
426 vscan_drv_state
= VS_DRV_CONNECTED
;
429 DTRACE_PROBE1(vscan__drv__state__violation
,
430 int, vscan_drv_state
);
431 mutex_exit(&vscan_drv_mutex
);
435 if ((vscan_drv_state
!= VS_DRV_ENABLED
) ||
436 (vscan_drv_inst_state
[inst
] != VS_DRV_INST_INIT
)) {
437 mutex_exit(&vscan_drv_mutex
);
440 vscan_drv_inst_state
[inst
] = VS_DRV_INST_OPEN
;
442 mutex_exit(&vscan_drv_mutex
);
451 * If inst == 0, this is vscand detaching.
452 * If the driver is in ENABLED state vscand has terminated without
453 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
454 * state and initiate a delayed disable to allow vscand time to
457 * If inst != 0, close the file associated with inst
461 vscan_drv_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
)
463 int i
, inst
= getminor(dev
);
465 if ((inst
< 0) || (inst
> vs_nodes_max
))
468 mutex_enter(&vscan_drv_mutex
);
470 vscan_drv_inst_state
[inst
] = VS_DRV_INST_INIT
;
471 mutex_exit(&vscan_drv_mutex
);
475 /* instance 0 - daemon disconnect */
476 if ((vscan_drv_state
!= VS_DRV_CONNECTED
) &&
477 (vscan_drv_state
!= VS_DRV_ENABLED
)) {
478 DTRACE_PROBE1(vscan__drv__state__violation
,
479 int, vscan_drv_state
);
480 mutex_exit(&vscan_drv_mutex
);
484 for (i
= 1; i
<= vs_nodes_max
; i
++) {
485 if (vscan_drv_inst_state
[i
] != VS_DRV_INST_UNCONFIG
)
486 vscan_drv_inst_state
[i
] = VS_DRV_INST_INIT
;
489 if (vscan_drv_state
== VS_DRV_CONNECTED
) {
490 vscan_drv_state
= VS_DRV_IDLE
;
491 } else { /* VS_DRV_ENABLED */
492 cmn_err(CE_WARN
, "Detected vscand exit without clean shutdown");
493 if (thread_create(NULL
, 0, vscan_drv_delayed_disable
,
494 0, 0, &p0
, TS_RUN
, minclsyspri
) == NULL
) {
496 vscan_drv_state
= VS_DRV_IDLE
;
498 vscan_drv_state
= VS_DRV_DELAYED_DISABLE
;
501 mutex_exit(&vscan_drv_mutex
);
503 vscan_svc_scan_abort();
510 * vscan_drv_delayed_disable
512 * Invoked from vscan_drv_close if the daemon disconnects
513 * without first sending disable (e.g. daemon crashed).
514 * Delays for vs_reconnect_timeout before disabling, to allow
515 * the daemon to reconnect. During this time, scan requests
516 * will be processed locally (see vscan_svc.c)
519 vscan_drv_delayed_disable(void)
521 mutex_enter(&vscan_drv_mutex
);
522 (void) cv_reltimedwait(&vscan_drv_cv
, &vscan_drv_mutex
,
523 SEC_TO_TICK(vs_reconnect_timeout
), TR_CLOCK_TICK
);
525 if (vscan_drv_state
== VS_DRV_DELAYED_DISABLE
) {
527 vscan_drv_state
= VS_DRV_IDLE
;
529 DTRACE_PROBE(vscan__reconnect
);
531 mutex_exit(&vscan_drv_mutex
);
540 vscan_drv_read(dev_t dev
, struct uio
*uiop
, cred_t
*credp
)
543 int inst
= getminor(dev
);
546 if ((inst
<= 0) || (inst
> vs_nodes_max
))
549 mutex_enter(&vscan_drv_mutex
);
550 if ((vscan_drv_state
!= VS_DRV_ENABLED
) ||
551 (vscan_drv_inst_state
[inst
] != VS_DRV_INST_OPEN
)) {
552 mutex_exit(&vscan_drv_mutex
);
555 vscan_drv_inst_state
[inst
] = VS_DRV_INST_READING
;
556 mutex_exit(&vscan_drv_mutex
);
558 if ((vp
= vscan_svc_get_vnode(inst
)) == NULL
)
561 (void) fop_rwlock(vp
, V_WRITELOCK_FALSE
, NULL
);
562 rc
= fop_read(vp
, uiop
, 0, kcred
, NULL
);
563 fop_rwunlock(vp
, V_WRITELOCK_FALSE
, NULL
);
565 mutex_enter(&vscan_drv_mutex
);
566 if (vscan_drv_inst_state
[inst
] == VS_DRV_INST_READING
)
567 vscan_drv_inst_state
[inst
] = VS_DRV_INST_OPEN
;
568 mutex_exit(&vscan_drv_mutex
);
577 * Process ioctls from vscand:
578 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
579 * enable VFS interface.
580 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
581 * VS_IOCTL_RESULT - scan response data
582 * VS_IOCTL_CONFIG - configuration data from vscand
583 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
584 * to allow vscand to set appropriate resource allocation limits
588 vscan_drv_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
,
589 cred_t
*credp
, int *rvalp
)
591 int inst
= getminor(dev
);
599 case VS_IOCTL_ENABLE
:
600 mutex_enter(&vscan_drv_mutex
);
601 if (vscan_drv_state
!= VS_DRV_CONNECTED
) {
602 DTRACE_PROBE1(vscan__drv__state__violation
,
603 int, vscan_drv_state
);
604 mutex_exit(&vscan_drv_mutex
);
607 if ((vscan_door_open((int)arg
) != 0) ||
608 (vscan_svc_enable() != 0)) {
609 mutex_exit(&vscan_drv_mutex
);
612 vscan_drv_state
= VS_DRV_ENABLED
;
613 mutex_exit(&vscan_drv_mutex
);
616 case VS_IOCTL_DISABLE
:
617 mutex_enter(&vscan_drv_mutex
);
618 if (vscan_drv_state
!= VS_DRV_ENABLED
) {
619 DTRACE_PROBE1(vscan__drv__state__violation
,
620 int, vscan_drv_state
);
621 mutex_exit(&vscan_drv_mutex
);
625 vscan_drv_state
= VS_DRV_CONNECTED
;
626 mutex_exit(&vscan_drv_mutex
);
629 case VS_IOCTL_RESULT
:
630 if (ddi_copyin((void *)arg
, &rsp
,
631 sizeof (vs_scan_rsp_t
), 0) == -1)
634 vscan_svc_scan_result(&rsp
);
637 case VS_IOCTL_CONFIG
:
638 if (ddi_copyin((void *)arg
, &conf
,
639 sizeof (vs_config_t
), 0) == -1)
641 if (vscan_svc_configure(&conf
) == -1)
645 case VS_IOCTL_MAX_REQ
:
646 if (ddi_copyout(&vs_nodes_max
, (void *)arg
,
647 sizeof (uint32_t), 0) == -1)
660 * vscan_drv_create_node
662 * Create minor node with which vscan daemon will communicate
663 * to access a file. Invoked from vscan_svc before scan request
665 * Minor node 0 is reserved for daemon-driver synchronization
666 * and is created during attach.
667 * All minor nodes are removed during detach.
670 vscan_drv_create_node(int idx
)
672 char name
[VS_NODENAME_LEN
];
673 boolean_t rc
= B_TRUE
;
675 mutex_enter(&vscan_drv_mutex
);
677 if (vscan_drv_inst_state
[idx
] == VS_DRV_INST_UNCONFIG
) {
678 (void) snprintf(name
, VS_NODENAME_LEN
, "vscan%d", idx
);
679 if (ddi_create_minor_node(vscan_drv_dip
, name
,
680 S_IFCHR
, idx
, DDI_PSEUDO
, 0) == DDI_SUCCESS
) {
681 vscan_drv_inst_state
[idx
] = VS_DRV_INST_INIT
;
685 DTRACE_PROBE2(vscan__minor__node
, int, idx
, int, rc
);
688 mutex_exit(&vscan_drv_mutex
);