xref: /onnv-gate/usr/src/uts/common/io/vscan/vscan_drv.c (revision 5440:f84b7f8d106d)
1*5440Sjm199354 /*
2*5440Sjm199354  * CDDL HEADER START
3*5440Sjm199354  *
4*5440Sjm199354  * The contents of this file are subject to the terms of the
5*5440Sjm199354  * Common Development and Distribution License (the "License").
6*5440Sjm199354  * You may not use this file except in compliance with the License.
7*5440Sjm199354  *
8*5440Sjm199354  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5440Sjm199354  * or http://www.opensolaris.org/os/licensing.
10*5440Sjm199354  * See the License for the specific language governing permissions
11*5440Sjm199354  * and limitations under the License.
12*5440Sjm199354  *
13*5440Sjm199354  * When distributing Covered Code, include this CDDL HEADER in each
14*5440Sjm199354  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5440Sjm199354  * If applicable, add the following below this CDDL HEADER, with the
16*5440Sjm199354  * fields enclosed by brackets "[]" replaced with your own identifying
17*5440Sjm199354  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5440Sjm199354  *
19*5440Sjm199354  * CDDL HEADER END
20*5440Sjm199354  */
21*5440Sjm199354 
22*5440Sjm199354 /*
23*5440Sjm199354  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24*5440Sjm199354  * Use is subject to license terms.
25*5440Sjm199354  */
26*5440Sjm199354 
27*5440Sjm199354 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*5440Sjm199354 
29*5440Sjm199354 #include <sys/stat.h>
30*5440Sjm199354 #include <sys/ddi.h>
31*5440Sjm199354 #include <sys/sunddi.h>
32*5440Sjm199354 #include <sys/time.h>
33*5440Sjm199354 #include <sys/varargs.h>
34*5440Sjm199354 #include <sys/conf.h>
35*5440Sjm199354 #include <sys/modctl.h>
36*5440Sjm199354 #include <sys/vnode.h>
37*5440Sjm199354 #include <fs/fs_subr.h>
38*5440Sjm199354 #include <sys/types.h>
39*5440Sjm199354 #include <sys/file.h>
40*5440Sjm199354 #include <sys/disp.h>
41*5440Sjm199354 #include <sys/vscan.h>
42*5440Sjm199354 #include <sys/policy.h>
43*5440Sjm199354 #include <sys/sdt.h>
44*5440Sjm199354 
45*5440Sjm199354 #define	VS_DRV_NODENAME_LEN	16
46*5440Sjm199354 
47*5440Sjm199354 
48*5440Sjm199354 /*
49*5440Sjm199354  * Instance States: VS_INIT (initial state), VS_OPEN, VS_READING
50*5440Sjm199354  *
51*5440Sjm199354  * Instance 0 controls the state of the driver: vscan_drv_connected.
52*5440Sjm199354  *   vscan_drv_state[0] should NOT be used.
53*5440Sjm199354  * Actions:
54*5440Sjm199354  * open:	VS_INIT->VS_OPEN, otherwise ERROR
55*5440Sjm199354  * close:	any->VS_INIT
56*5440Sjm199354  * read:	VS_OPEN->VS_READING, otherwise ERROR
57*5440Sjm199354  */
58*5440Sjm199354 typedef enum {
59*5440Sjm199354 	VS_INIT,
60*5440Sjm199354 	VS_OPEN,
61*5440Sjm199354 	VS_READING
62*5440Sjm199354 } vscan_drv_state_t;
63*5440Sjm199354 
64*5440Sjm199354 static vscan_drv_state_t vscan_drv_state[VS_DRV_MAX_FILES + 1];
65*5440Sjm199354 static boolean_t vscan_drv_connected = B_FALSE; /* vscand daemon connected */
66*5440Sjm199354 
67*5440Sjm199354 static dev_info_t *vscan_drv_dip;
68*5440Sjm199354 static kmutex_t vscan_drv_mutex;
69*5440Sjm199354 
70*5440Sjm199354 /*
71*5440Sjm199354  * DDI entry points.
72*5440Sjm199354  */
73*5440Sjm199354 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
74*5440Sjm199354 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
75*5440Sjm199354 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
76*5440Sjm199354 static int vscan_drv_open(dev_t *, int, int, cred_t *);
77*5440Sjm199354 static int vscan_drv_close(dev_t, int, int, cred_t *);
78*5440Sjm199354 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
79*5440Sjm199354 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
80*5440Sjm199354 
81*5440Sjm199354 static boolean_t vscan_drv_in_use();
82*5440Sjm199354 
83*5440Sjm199354 
84*5440Sjm199354 /*
85*5440Sjm199354  * module linkage info for the kernel
86*5440Sjm199354  */
87*5440Sjm199354 
88*5440Sjm199354 static struct cb_ops cbops = {
89*5440Sjm199354 	vscan_drv_open,		/* cb_open */
90*5440Sjm199354 	vscan_drv_close,	/* cb_close */
91*5440Sjm199354 	nodev,			/* cb_strategy */
92*5440Sjm199354 	nodev,			/* cb_print */
93*5440Sjm199354 	nodev,			/* cb_dump */
94*5440Sjm199354 	vscan_drv_read,		/* cb_read */
95*5440Sjm199354 	nodev,			/* cb_write */
96*5440Sjm199354 	vscan_drv_ioctl,	/* cb_ioctl */
97*5440Sjm199354 	nodev,			/* cb_devmap */
98*5440Sjm199354 	nodev,			/* cb_mmap */
99*5440Sjm199354 	nodev,			/* cb_segmap */
100*5440Sjm199354 	nochpoll,		/* cb_chpoll */
101*5440Sjm199354 	ddi_prop_op,		/* cb_prop_op */
102*5440Sjm199354 	NULL,			/* cb_streamtab */
103*5440Sjm199354 	D_MP,			/* cb_flag */
104*5440Sjm199354 	CB_REV,			/* cb_rev */
105*5440Sjm199354 	nodev,			/* cb_aread */
106*5440Sjm199354 	nodev,			/* cb_awrite */
107*5440Sjm199354 };
108*5440Sjm199354 
109*5440Sjm199354 static struct dev_ops devops = {
110*5440Sjm199354 	DEVO_REV,		/* devo_rev */
111*5440Sjm199354 	0,			/* devo_refcnt */
112*5440Sjm199354 	vscan_drv_getinfo,	/* devo_getinfo */
113*5440Sjm199354 	nulldev,		/* devo_identify */
114*5440Sjm199354 	nulldev,		/* devo_probe */
115*5440Sjm199354 	vscan_drv_attach,	/* devo_attach */
116*5440Sjm199354 	vscan_drv_detach,	/* devo_detach */
117*5440Sjm199354 	nodev,			/* devo_reset */
118*5440Sjm199354 	&cbops,			/* devo_cb_ops */
119*5440Sjm199354 	NULL,			/* devo_bus_ops */
120*5440Sjm199354 	NULL,			/* devo_power */
121*5440Sjm199354 };
122*5440Sjm199354 
123*5440Sjm199354 static struct modldrv modldrv = {
124*5440Sjm199354 	&mod_driverops,		/* drv_modops */
125*5440Sjm199354 	"virus scanning",	/* drv_linkinfo */
126*5440Sjm199354 	&devops,
127*5440Sjm199354 };
128*5440Sjm199354 
129*5440Sjm199354 static struct modlinkage modlinkage = {
130*5440Sjm199354 
131*5440Sjm199354 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
132*5440Sjm199354 	&modldrv,	/* ptr to linkage structures			*/
133*5440Sjm199354 	NULL,
134*5440Sjm199354 };
135*5440Sjm199354 
136*5440Sjm199354 
137*5440Sjm199354 /*
138*5440Sjm199354  * _init
139*5440Sjm199354  */
140*5440Sjm199354 int
141*5440Sjm199354 _init(void)
142*5440Sjm199354 {
143*5440Sjm199354 	int rc;
144*5440Sjm199354 
145*5440Sjm199354 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
146*5440Sjm199354 
147*5440Sjm199354 	if (vscan_door_init() != 0) {
148*5440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
149*5440Sjm199354 		return (DDI_FAILURE);
150*5440Sjm199354 	}
151*5440Sjm199354 
152*5440Sjm199354 	if (vscan_svc_init() != 0) {
153*5440Sjm199354 		vscan_door_fini();
154*5440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
155*5440Sjm199354 		return (DDI_FAILURE);
156*5440Sjm199354 	}
157*5440Sjm199354 
158*5440Sjm199354 	(void) memset(&vscan_drv_state, 0, sizeof (vscan_drv_state));
159*5440Sjm199354 
160*5440Sjm199354 	if ((rc  = mod_install(&modlinkage)) != 0) {
161*5440Sjm199354 		vscan_door_fini();
162*5440Sjm199354 		vscan_svc_fini();
163*5440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
164*5440Sjm199354 	}
165*5440Sjm199354 
166*5440Sjm199354 	return (rc);
167*5440Sjm199354 }
168*5440Sjm199354 
169*5440Sjm199354 
170*5440Sjm199354 /*
171*5440Sjm199354  * _info
172*5440Sjm199354  */
173*5440Sjm199354 int
174*5440Sjm199354 _info(struct modinfo *modinfop)
175*5440Sjm199354 {
176*5440Sjm199354 	return (mod_info(&modlinkage, modinfop));
177*5440Sjm199354 }
178*5440Sjm199354 
179*5440Sjm199354 
180*5440Sjm199354 /*
181*5440Sjm199354  * _fini
182*5440Sjm199354  */
183*5440Sjm199354 int
184*5440Sjm199354 _fini(void)
185*5440Sjm199354 {
186*5440Sjm199354 	int rc;
187*5440Sjm199354 
188*5440Sjm199354 	if (vscan_drv_in_use())
189*5440Sjm199354 		return (EBUSY);
190*5440Sjm199354 
191*5440Sjm199354 	if ((rc = mod_remove(&modlinkage)) == 0) {
192*5440Sjm199354 		vscan_door_fini();
193*5440Sjm199354 		vscan_svc_fini();
194*5440Sjm199354 		mutex_destroy(&vscan_drv_mutex);
195*5440Sjm199354 	}
196*5440Sjm199354 
197*5440Sjm199354 	return (rc);
198*5440Sjm199354 }
199*5440Sjm199354 
200*5440Sjm199354 
201*5440Sjm199354 /*
202*5440Sjm199354  * DDI entry points.
203*5440Sjm199354  */
204*5440Sjm199354 
205*5440Sjm199354 /*
206*5440Sjm199354  * vscan_drv_getinfo
207*5440Sjm199354  */
208*5440Sjm199354 /* ARGSUSED */
209*5440Sjm199354 static int
210*5440Sjm199354 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
211*5440Sjm199354 {
212*5440Sjm199354 	ulong_t inst = getminor((dev_t)arg);
213*5440Sjm199354 
214*5440Sjm199354 	switch (cmd) {
215*5440Sjm199354 	case DDI_INFO_DEVT2DEVINFO:
216*5440Sjm199354 		*result = vscan_drv_dip;
217*5440Sjm199354 		return (DDI_SUCCESS);
218*5440Sjm199354 	case DDI_INFO_DEVT2INSTANCE:
219*5440Sjm199354 		*result = (void *)inst;
220*5440Sjm199354 		return (DDI_SUCCESS);
221*5440Sjm199354 	}
222*5440Sjm199354 	return (DDI_FAILURE);
223*5440Sjm199354 }
224*5440Sjm199354 
225*5440Sjm199354 
226*5440Sjm199354 /*
227*5440Sjm199354  * vscan_drv_attach
228*5440Sjm199354  */
229*5440Sjm199354 static int
230*5440Sjm199354 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
231*5440Sjm199354 {
232*5440Sjm199354 	int i;
233*5440Sjm199354 	char name[VS_DRV_NODENAME_LEN];
234*5440Sjm199354 
235*5440Sjm199354 	if (cmd != DDI_ATTACH)
236*5440Sjm199354 		return (DDI_FAILURE);
237*5440Sjm199354 
238*5440Sjm199354 	if (ddi_get_instance(dip) != 0)
239*5440Sjm199354 		return (DDI_FAILURE);
240*5440Sjm199354 
241*5440Sjm199354 	vscan_drv_dip = dip;
242*5440Sjm199354 
243*5440Sjm199354 	/* create the minor nodes */
244*5440Sjm199354 	for (i = 0; i <= VS_DRV_MAX_FILES; i++) {
245*5440Sjm199354 		(void) snprintf(name, VS_DRV_NODENAME_LEN, "vscan%d", i);
246*5440Sjm199354 		if (ddi_create_minor_node(dip, name, S_IFCHR, i,
247*5440Sjm199354 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
248*5440Sjm199354 			ddi_remove_minor_node(dip, NULL);
249*5440Sjm199354 			return (DDI_FAILURE);
250*5440Sjm199354 		}
251*5440Sjm199354 	}
252*5440Sjm199354 
253*5440Sjm199354 	return (DDI_SUCCESS);
254*5440Sjm199354 }
255*5440Sjm199354 
256*5440Sjm199354 
257*5440Sjm199354 /*
258*5440Sjm199354  * vscan_drv_detach
259*5440Sjm199354  */
260*5440Sjm199354 static int
261*5440Sjm199354 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
262*5440Sjm199354 {
263*5440Sjm199354 	if (cmd != DDI_DETACH)
264*5440Sjm199354 		return (DDI_FAILURE);
265*5440Sjm199354 
266*5440Sjm199354 	if (ddi_get_instance(dip) != 0)
267*5440Sjm199354 		return (DDI_FAILURE);
268*5440Sjm199354 
269*5440Sjm199354 	if (vscan_drv_in_use())
270*5440Sjm199354 		return (DDI_FAILURE);
271*5440Sjm199354 
272*5440Sjm199354 	vscan_drv_dip = NULL;
273*5440Sjm199354 	ddi_remove_minor_node(dip, NULL);
274*5440Sjm199354 
275*5440Sjm199354 	return (DDI_SUCCESS);
276*5440Sjm199354 }
277*5440Sjm199354 
278*5440Sjm199354 
279*5440Sjm199354 /*
280*5440Sjm199354  * vscan_drv_in_use
281*5440Sjm199354  */
282*5440Sjm199354 static boolean_t
283*5440Sjm199354 vscan_drv_in_use()
284*5440Sjm199354 {
285*5440Sjm199354 	if (vscan_drv_connected)
286*5440Sjm199354 		return (B_TRUE);
287*5440Sjm199354 	else
288*5440Sjm199354 		return (vscan_svc_in_use());
289*5440Sjm199354 }
290*5440Sjm199354 
291*5440Sjm199354 
292*5440Sjm199354 /*
293*5440Sjm199354  * vscan_drv_open
294*5440Sjm199354  * if inst == 0, this is vscand initializing.
295*5440Sjm199354  * Otherwise, open the file associated with inst.
296*5440Sjm199354  */
297*5440Sjm199354 /* ARGSUSED */
298*5440Sjm199354 static int
299*5440Sjm199354 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
300*5440Sjm199354 {
301*5440Sjm199354 	int rc;
302*5440Sjm199354 	int inst = getminor(*devp);
303*5440Sjm199354 
304*5440Sjm199354 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
305*5440Sjm199354 		return (EINVAL);
306*5440Sjm199354 
307*5440Sjm199354 	/* check if caller has privilege for virus scanning */
308*5440Sjm199354 	if ((rc = secpolicy_vscan(credp)) != 0) {
309*5440Sjm199354 		DTRACE_PROBE1(vscan__priv, int, rc);
310*5440Sjm199354 		return (EPERM);
311*5440Sjm199354 	}
312*5440Sjm199354 
313*5440Sjm199354 	mutex_enter(&vscan_drv_mutex);
314*5440Sjm199354 	if (inst == 0) {
315*5440Sjm199354 		if (vscan_drv_connected) {
316*5440Sjm199354 			mutex_exit(&vscan_drv_mutex);
317*5440Sjm199354 			return (EINVAL);
318*5440Sjm199354 		}
319*5440Sjm199354 		vscan_drv_connected = B_TRUE;
320*5440Sjm199354 	} else {
321*5440Sjm199354 		if ((!vscan_drv_connected) ||
322*5440Sjm199354 		    (vscan_drv_state[inst] != VS_INIT)) {
323*5440Sjm199354 				mutex_exit(&vscan_drv_mutex);
324*5440Sjm199354 				return (EINVAL);
325*5440Sjm199354 		}
326*5440Sjm199354 		vscan_drv_state[inst] = VS_OPEN;
327*5440Sjm199354 	}
328*5440Sjm199354 	mutex_exit(&vscan_drv_mutex);
329*5440Sjm199354 
330*5440Sjm199354 	return (0);
331*5440Sjm199354 }
332*5440Sjm199354 
333*5440Sjm199354 
334*5440Sjm199354 /*
335*5440Sjm199354  * vscan_drv_close
336*5440Sjm199354  * if inst == 0, this is vscand detaching
337*5440Sjm199354  * Otherwise close the file associated with inst
338*5440Sjm199354  */
339*5440Sjm199354 /* ARGSUSED */
340*5440Sjm199354 static int
341*5440Sjm199354 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
342*5440Sjm199354 {
343*5440Sjm199354 	int i, inst = getminor(dev);
344*5440Sjm199354 
345*5440Sjm199354 	if ((inst < 0) || (inst > VS_DRV_MAX_FILES))
346*5440Sjm199354 		return (EINVAL);
347*5440Sjm199354 
348*5440Sjm199354 	mutex_enter(&vscan_drv_mutex);
349*5440Sjm199354 	if (inst == 0) {
350*5440Sjm199354 		for (i = 1; i <= VS_DRV_MAX_FILES; i++)
351*5440Sjm199354 			vscan_drv_state[i] = VS_INIT;
352*5440Sjm199354 
353*5440Sjm199354 		vscan_drv_connected = B_FALSE;
354*5440Sjm199354 		vscan_svc_enable(B_FALSE);
355*5440Sjm199354 		vscan_door_close();
356*5440Sjm199354 	} else {
357*5440Sjm199354 		vscan_drv_state[inst] = VS_INIT;
358*5440Sjm199354 	}
359*5440Sjm199354 	mutex_exit(&vscan_drv_mutex);
360*5440Sjm199354 
361*5440Sjm199354 	return (0);
362*5440Sjm199354 }
363*5440Sjm199354 
364*5440Sjm199354 
365*5440Sjm199354 /*
366*5440Sjm199354  * vscan_drv_read
367*5440Sjm199354  */
368*5440Sjm199354 /* ARGSUSED */
369*5440Sjm199354 static int
370*5440Sjm199354 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
371*5440Sjm199354 {
372*5440Sjm199354 	int rc;
373*5440Sjm199354 	int inst = getminor(dev);
374*5440Sjm199354 	vnode_t *vp;
375*5440Sjm199354 
376*5440Sjm199354 	if ((inst <= 0) || (inst > VS_DRV_MAX_FILES))
377*5440Sjm199354 		return (EINVAL);
378*5440Sjm199354 
379*5440Sjm199354 	mutex_enter(&vscan_drv_mutex);
380*5440Sjm199354 	if ((!vscan_drv_connected) || (vscan_drv_state[inst] != VS_OPEN)) {
381*5440Sjm199354 		mutex_exit(&vscan_drv_mutex);
382*5440Sjm199354 		return (EINVAL);
383*5440Sjm199354 	}
384*5440Sjm199354 	vscan_drv_state[inst] = VS_READING;
385*5440Sjm199354 	mutex_exit(&vscan_drv_mutex);
386*5440Sjm199354 
387*5440Sjm199354 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
388*5440Sjm199354 		return (EINVAL);
389*5440Sjm199354 
390*5440Sjm199354 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
391*5440Sjm199354 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
392*5440Sjm199354 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
393*5440Sjm199354 
394*5440Sjm199354 	mutex_enter(&vscan_drv_mutex);
395*5440Sjm199354 	if (vscan_drv_state[inst] == VS_READING)
396*5440Sjm199354 		vscan_drv_state[inst] = VS_OPEN;
397*5440Sjm199354 	mutex_exit(&vscan_drv_mutex);
398*5440Sjm199354 
399*5440Sjm199354 	return (rc);
400*5440Sjm199354 }
401*5440Sjm199354 
402*5440Sjm199354 
403*5440Sjm199354 /*
404*5440Sjm199354  * vscan_drv_ioctl
405*5440Sjm199354  */
406*5440Sjm199354 /* ARGSUSED */
407*5440Sjm199354 static int
408*5440Sjm199354 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
409*5440Sjm199354 	cred_t *credp, int *rvalp)
410*5440Sjm199354 {
411*5440Sjm199354 	int inst = getminor(dev);
412*5440Sjm199354 	vs_config_t conf;
413*5440Sjm199354 
414*5440Sjm199354 	if (inst != 0)
415*5440Sjm199354 		return (EINVAL);
416*5440Sjm199354 
417*5440Sjm199354 	switch (cmd) {
418*5440Sjm199354 	case VS_DRV_IOCTL_ENABLE:
419*5440Sjm199354 		mutex_enter(&vscan_drv_mutex);
420*5440Sjm199354 		if ((!vscan_drv_connected) ||
421*5440Sjm199354 		    (vscan_door_open((int)arg) != 0)) {
422*5440Sjm199354 			mutex_exit(&vscan_drv_mutex);
423*5440Sjm199354 			return (EINVAL);
424*5440Sjm199354 		}
425*5440Sjm199354 		vscan_svc_enable(B_TRUE);
426*5440Sjm199354 		mutex_exit(&vscan_drv_mutex);
427*5440Sjm199354 		break;
428*5440Sjm199354 	case VS_DRV_IOCTL_DISABLE:
429*5440Sjm199354 		vscan_svc_enable(B_FALSE);
430*5440Sjm199354 		break;
431*5440Sjm199354 	case VS_DRV_IOCTL_CONFIG:
432*5440Sjm199354 		if (ddi_copyin((void *)arg, &conf,
433*5440Sjm199354 		    sizeof (vs_config_t), 0) == -1)
434*5440Sjm199354 			return (EFAULT);
435*5440Sjm199354 		if (vscan_svc_configure(&conf) == -1)
436*5440Sjm199354 			return (EINVAL);
437*5440Sjm199354 		break;
438*5440Sjm199354 	default:
439*5440Sjm199354 		return (ENOTTY);
440*5440Sjm199354 	}
441*5440Sjm199354 
442*5440Sjm199354 	return (0);
443*5440Sjm199354 }
444