1911106dfSjm199354 /* 2911106dfSjm199354 * CDDL HEADER START 3911106dfSjm199354 * 4911106dfSjm199354 * The contents of this file are subject to the terms of the 5911106dfSjm199354 * Common Development and Distribution License (the "License"). 6911106dfSjm199354 * You may not use this file except in compliance with the License. 7911106dfSjm199354 * 8911106dfSjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9911106dfSjm199354 * or http://www.opensolaris.org/os/licensing. 10911106dfSjm199354 * See the License for the specific language governing permissions 11911106dfSjm199354 * and limitations under the License. 12911106dfSjm199354 * 13911106dfSjm199354 * When distributing Covered Code, include this CDDL HEADER in each 14911106dfSjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15911106dfSjm199354 * If applicable, add the following below this CDDL HEADER, with the 16911106dfSjm199354 * fields enclosed by brackets "[]" replaced with your own identifying 17911106dfSjm199354 * information: Portions Copyright [yyyy] [name of copyright owner] 18911106dfSjm199354 * 19911106dfSjm199354 * CDDL HEADER END 20911106dfSjm199354 */ 21911106dfSjm199354 22911106dfSjm199354 /* 23*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24911106dfSjm199354 * Use is subject to license terms. 25911106dfSjm199354 */ 26911106dfSjm199354 27911106dfSjm199354 #include <sys/stat.h> 28911106dfSjm199354 #include <sys/ddi.h> 29911106dfSjm199354 #include <sys/sunddi.h> 30911106dfSjm199354 #include <sys/time.h> 31911106dfSjm199354 #include <sys/varargs.h> 32911106dfSjm199354 #include <sys/conf.h> 33911106dfSjm199354 #include <sys/modctl.h> 34911106dfSjm199354 #include <sys/vnode.h> 35911106dfSjm199354 #include <fs/fs_subr.h> 36911106dfSjm199354 #include <sys/types.h> 37911106dfSjm199354 #include <sys/file.h> 38911106dfSjm199354 #include <sys/disp.h> 39911106dfSjm199354 #include <sys/vscan.h> 40911106dfSjm199354 #include <sys/policy.h> 41911106dfSjm199354 #include <sys/sdt.h> 42911106dfSjm199354 43bfc848c6Sjm199354 44bfc848c6Sjm199354 /* seconds to wait for daemon to reconnect before disabling */ 45bfc848c6Sjm199354 #define VS_DAEMON_WAIT_SEC 60 46bfc848c6Sjm199354 47bfc848c6Sjm199354 /* length of minor node name - vscan%d */ 48bfc848c6Sjm199354 #define VS_NODENAME_LEN 16 49bfc848c6Sjm199354 50bfc848c6Sjm199354 /* global variables - tunable via /etc/system */ 51bfc848c6Sjm199354 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC; 52bfc848c6Sjm199354 extern uint32_t vs_nodes_max; /* max in-progress scan requests */ 53bfc848c6Sjm199354 54bfc848c6Sjm199354 /* 55bfc848c6Sjm199354 * vscan_drv_state 56bfc848c6Sjm199354 * 57bfc848c6Sjm199354 * Operations on instance 0 represent vscand initiated state 58bfc848c6Sjm199354 * transition events: 59bfc848c6Sjm199354 * open(0) - vscand connect 60bfc848c6Sjm199354 * close(0) - vscan disconnect 61bfc848c6Sjm199354 * enable(0) - vscand enable (ready to hand requests) 62bfc848c6Sjm199354 * disable(0) - vscand disable (shutting down) 63bfc848c6Sjm199354 * 64bfc848c6Sjm199354 * +------------------------+ 65bfc848c6Sjm199354 * | VS_DRV_UNCONFIG | 66bfc848c6Sjm199354 * +------------------------+ 67bfc848c6Sjm199354 * | ^ 68bfc848c6Sjm199354 * | attach | detach 69bfc848c6Sjm199354 * v | 70bfc848c6Sjm199354 * +------------------------+ 71bfc848c6Sjm199354 * | VS_DRV_IDLE |<------| 72bfc848c6Sjm199354 * +------------------------+ | 73bfc848c6Sjm199354 * | ^ | 74bfc848c6Sjm199354 * | open(0) | close(0) | 75bfc848c6Sjm199354 * v | | 76bfc848c6Sjm199354 * +------------------------+ | 77bfc848c6Sjm199354 * | VS_DRV_CONNECTED |<-| | 78bfc848c6Sjm199354 * +------------------------+ | | 79bfc848c6Sjm199354 * | ^ | | 80bfc848c6Sjm199354 * | enable(0) | disable(0) | | 81bfc848c6Sjm199354 * v | | | 82bfc848c6Sjm199354 * +------------------------+ | | 83bfc848c6Sjm199354 * | VS_DRV_ENABLED | | | 84bfc848c6Sjm199354 * +------------------------+ | | 85bfc848c6Sjm199354 * | | | 86bfc848c6Sjm199354 * | close(0) open(0) | 87bfc848c6Sjm199354 * v | | 88bfc848c6Sjm199354 * +------------------------+ | | timeout 89bfc848c6Sjm199354 * | VS_DRV_DELAYED_DISABLE | -- | 90bfc848c6Sjm199354 * +------------------------+ ------| 91bfc848c6Sjm199354 * 92bfc848c6Sjm199354 */ 93bfc848c6Sjm199354 typedef enum { 94bfc848c6Sjm199354 VS_DRV_UNCONFIG, 95bfc848c6Sjm199354 VS_DRV_IDLE, 96bfc848c6Sjm199354 VS_DRV_CONNECTED, 97bfc848c6Sjm199354 VS_DRV_ENABLED, 98bfc848c6Sjm199354 VS_DRV_DELAYED_DISABLE 99bfc848c6Sjm199354 } vscan_drv_state_t; 100bfc848c6Sjm199354 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG; 101911106dfSjm199354 102911106dfSjm199354 103911106dfSjm199354 /* 104bfc848c6Sjm199354 * vscan_drv_inst_state 105911106dfSjm199354 * 106bfc848c6Sjm199354 * Instance 0 controls the state of the driver: vscan_drv_state. 107bfc848c6Sjm199354 * vscan_drv_inst_state[0] should NOT be used. 108bfc848c6Sjm199354 * 109bfc848c6Sjm199354 * vscan_drv_inst_state[n] represents the state of driver 110bfc848c6Sjm199354 * instance n, used by vscand to access file data for the 111bfc848c6Sjm199354 * scan request with index n in vscan_svc_reqs. 112bfc848c6Sjm199354 * Minor nodes are created as required then all are destroyed 113bfc848c6Sjm199354 * during driver detach. 114bfc848c6Sjm199354 * 115bfc848c6Sjm199354 * +------------------------+ 116bfc848c6Sjm199354 * | VS_DRV_INST_UNCONFIG | 117bfc848c6Sjm199354 * +------------------------+ 118bfc848c6Sjm199354 * | ^ 119bfc848c6Sjm199354 * | create_node(n) | detach 120bfc848c6Sjm199354 * v | 121bfc848c6Sjm199354 * +------------------------+ 122bfc848c6Sjm199354 * | VS_DRV_INST_INIT |<-| 123bfc848c6Sjm199354 * +------------------------+ | 124bfc848c6Sjm199354 * | | 125bfc848c6Sjm199354 * | open(n) | 126bfc848c6Sjm199354 * v | 127bfc848c6Sjm199354 * +------------------------+ | 128bfc848c6Sjm199354 * | VS_DRV_INST_OPEN |--| 129bfc848c6Sjm199354 * +------------------------+ | 130bfc848c6Sjm199354 * | | 131bfc848c6Sjm199354 * | read(n) | 132bfc848c6Sjm199354 * v | close(n) 133bfc848c6Sjm199354 * +------------------------+ | 134bfc848c6Sjm199354 * | VS_DRV_INST_READING |--| 135bfc848c6Sjm199354 * +------------------------+ 136911106dfSjm199354 */ 137911106dfSjm199354 typedef enum { 138bfc848c6Sjm199354 VS_DRV_INST_UNCONFIG = 0, /* minor node not created */ 139bfc848c6Sjm199354 VS_DRV_INST_INIT, 140bfc848c6Sjm199354 VS_DRV_INST_OPEN, 141bfc848c6Sjm199354 VS_DRV_INST_READING 142bfc848c6Sjm199354 } vscan_drv_inst_state_t; 143911106dfSjm199354 144bfc848c6Sjm199354 static vscan_drv_inst_state_t *vscan_drv_inst_state; 145bfc848c6Sjm199354 static int vscan_drv_inst_state_sz; 146911106dfSjm199354 147911106dfSjm199354 static dev_info_t *vscan_drv_dip; 148911106dfSjm199354 static kmutex_t vscan_drv_mutex; 14953c11029Sjm199354 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */ 150911106dfSjm199354 151911106dfSjm199354 /* 152911106dfSjm199354 * DDI entry points. 153911106dfSjm199354 */ 154911106dfSjm199354 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t); 155911106dfSjm199354 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t); 156911106dfSjm199354 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 157911106dfSjm199354 static int vscan_drv_open(dev_t *, int, int, cred_t *); 158911106dfSjm199354 static int vscan_drv_close(dev_t, int, int, cred_t *); 159911106dfSjm199354 static int vscan_drv_read(dev_t, struct uio *, cred_t *); 160911106dfSjm199354 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 161911106dfSjm199354 16253c11029Sjm199354 static boolean_t vscan_drv_in_use(void); 16353c11029Sjm199354 static void vscan_drv_delayed_disable(void); 164911106dfSjm199354 165911106dfSjm199354 166911106dfSjm199354 /* 167911106dfSjm199354 * module linkage info for the kernel 168911106dfSjm199354 */ 169911106dfSjm199354 static struct cb_ops cbops = { 170911106dfSjm199354 vscan_drv_open, /* cb_open */ 171911106dfSjm199354 vscan_drv_close, /* cb_close */ 172911106dfSjm199354 nodev, /* cb_strategy */ 173911106dfSjm199354 nodev, /* cb_print */ 174911106dfSjm199354 nodev, /* cb_dump */ 175911106dfSjm199354 vscan_drv_read, /* cb_read */ 176911106dfSjm199354 nodev, /* cb_write */ 177911106dfSjm199354 vscan_drv_ioctl, /* cb_ioctl */ 178911106dfSjm199354 nodev, /* cb_devmap */ 179911106dfSjm199354 nodev, /* cb_mmap */ 180911106dfSjm199354 nodev, /* cb_segmap */ 181911106dfSjm199354 nochpoll, /* cb_chpoll */ 182911106dfSjm199354 ddi_prop_op, /* cb_prop_op */ 183911106dfSjm199354 NULL, /* cb_streamtab */ 184911106dfSjm199354 D_MP, /* cb_flag */ 185911106dfSjm199354 CB_REV, /* cb_rev */ 186911106dfSjm199354 nodev, /* cb_aread */ 187911106dfSjm199354 nodev, /* cb_awrite */ 188911106dfSjm199354 }; 189911106dfSjm199354 190911106dfSjm199354 static struct dev_ops devops = { 191911106dfSjm199354 DEVO_REV, /* devo_rev */ 192911106dfSjm199354 0, /* devo_refcnt */ 193911106dfSjm199354 vscan_drv_getinfo, /* devo_getinfo */ 194911106dfSjm199354 nulldev, /* devo_identify */ 195911106dfSjm199354 nulldev, /* devo_probe */ 196911106dfSjm199354 vscan_drv_attach, /* devo_attach */ 197911106dfSjm199354 vscan_drv_detach, /* devo_detach */ 198911106dfSjm199354 nodev, /* devo_reset */ 199911106dfSjm199354 &cbops, /* devo_cb_ops */ 200911106dfSjm199354 NULL, /* devo_bus_ops */ 201911106dfSjm199354 NULL, /* devo_power */ 20219397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 203911106dfSjm199354 }; 204911106dfSjm199354 205911106dfSjm199354 static struct modldrv modldrv = { 206911106dfSjm199354 &mod_driverops, /* drv_modops */ 207911106dfSjm199354 "virus scanning", /* drv_linkinfo */ 208911106dfSjm199354 &devops, 209911106dfSjm199354 }; 210911106dfSjm199354 211911106dfSjm199354 static struct modlinkage modlinkage = { 212911106dfSjm199354 213911106dfSjm199354 MODREV_1, /* revision of the module, must be: MODREV_1 */ 214911106dfSjm199354 &modldrv, /* ptr to linkage structures */ 215911106dfSjm199354 NULL, 216911106dfSjm199354 }; 217911106dfSjm199354 218911106dfSjm199354 219911106dfSjm199354 /* 220911106dfSjm199354 * _init 221911106dfSjm199354 */ 222911106dfSjm199354 int 223911106dfSjm199354 _init(void) 224911106dfSjm199354 { 225911106dfSjm199354 int rc; 226911106dfSjm199354 227bfc848c6Sjm199354 vscan_drv_inst_state_sz = 228bfc848c6Sjm199354 sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1); 229911106dfSjm199354 230bfc848c6Sjm199354 if (vscan_door_init() != 0) 231911106dfSjm199354 return (DDI_FAILURE); 232911106dfSjm199354 233911106dfSjm199354 if (vscan_svc_init() != 0) { 234911106dfSjm199354 vscan_door_fini(); 235911106dfSjm199354 return (DDI_FAILURE); 236911106dfSjm199354 } 237911106dfSjm199354 238bfc848c6Sjm199354 mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL); 239bfc848c6Sjm199354 vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP); 240bfc848c6Sjm199354 241bfc848c6Sjm199354 cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL); 242911106dfSjm199354 243911106dfSjm199354 if ((rc = mod_install(&modlinkage)) != 0) { 244911106dfSjm199354 vscan_door_fini(); 245911106dfSjm199354 vscan_svc_fini(); 246bfc848c6Sjm199354 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 247bfc848c6Sjm199354 cv_destroy(&vscan_drv_cv); 248911106dfSjm199354 mutex_destroy(&vscan_drv_mutex); 249911106dfSjm199354 } 250911106dfSjm199354 251911106dfSjm199354 return (rc); 252911106dfSjm199354 } 253911106dfSjm199354 254911106dfSjm199354 255911106dfSjm199354 /* 256911106dfSjm199354 * _info 257911106dfSjm199354 */ 258911106dfSjm199354 int 259911106dfSjm199354 _info(struct modinfo *modinfop) 260911106dfSjm199354 { 261911106dfSjm199354 return (mod_info(&modlinkage, modinfop)); 262911106dfSjm199354 } 263911106dfSjm199354 264911106dfSjm199354 265911106dfSjm199354 /* 266911106dfSjm199354 * _fini 267911106dfSjm199354 */ 268911106dfSjm199354 int 269911106dfSjm199354 _fini(void) 270911106dfSjm199354 { 271911106dfSjm199354 int rc; 272911106dfSjm199354 273911106dfSjm199354 if (vscan_drv_in_use()) 274911106dfSjm199354 return (EBUSY); 275911106dfSjm199354 276911106dfSjm199354 if ((rc = mod_remove(&modlinkage)) == 0) { 277911106dfSjm199354 vscan_door_fini(); 278911106dfSjm199354 vscan_svc_fini(); 279bfc848c6Sjm199354 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz); 28053c11029Sjm199354 cv_destroy(&vscan_drv_cv); 281911106dfSjm199354 mutex_destroy(&vscan_drv_mutex); 282911106dfSjm199354 } 283911106dfSjm199354 284911106dfSjm199354 return (rc); 285911106dfSjm199354 } 286911106dfSjm199354 287911106dfSjm199354 288911106dfSjm199354 /* 289911106dfSjm199354 * DDI entry points. 290911106dfSjm199354 */ 291911106dfSjm199354 292911106dfSjm199354 /* 293911106dfSjm199354 * vscan_drv_getinfo 294911106dfSjm199354 */ 295911106dfSjm199354 /* ARGSUSED */ 296911106dfSjm199354 static int 297911106dfSjm199354 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 298911106dfSjm199354 { 299911106dfSjm199354 ulong_t inst = getminor((dev_t)arg); 300911106dfSjm199354 301911106dfSjm199354 switch (cmd) { 302911106dfSjm199354 case DDI_INFO_DEVT2DEVINFO: 303911106dfSjm199354 *result = vscan_drv_dip; 304911106dfSjm199354 return (DDI_SUCCESS); 305911106dfSjm199354 case DDI_INFO_DEVT2INSTANCE: 306911106dfSjm199354 *result = (void *)inst; 307911106dfSjm199354 return (DDI_SUCCESS); 308911106dfSjm199354 } 309911106dfSjm199354 return (DDI_FAILURE); 310911106dfSjm199354 } 311911106dfSjm199354 312911106dfSjm199354 313911106dfSjm199354 /* 314911106dfSjm199354 * vscan_drv_attach 315911106dfSjm199354 */ 316911106dfSjm199354 static int 317911106dfSjm199354 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 318911106dfSjm199354 { 319911106dfSjm199354 if (cmd != DDI_ATTACH) 320911106dfSjm199354 return (DDI_FAILURE); 321911106dfSjm199354 322911106dfSjm199354 if (ddi_get_instance(dip) != 0) 323911106dfSjm199354 return (DDI_FAILURE); 324911106dfSjm199354 325911106dfSjm199354 vscan_drv_dip = dip; 326911106dfSjm199354 32753c11029Sjm199354 /* create minor node 0 for daemon-driver synchronization */ 32853c11029Sjm199354 if (vscan_drv_create_node(0) == B_FALSE) 329911106dfSjm199354 return (DDI_FAILURE); 330911106dfSjm199354 331bfc848c6Sjm199354 vscan_drv_state = VS_DRV_IDLE; 332911106dfSjm199354 return (DDI_SUCCESS); 333911106dfSjm199354 } 334911106dfSjm199354 335911106dfSjm199354 336911106dfSjm199354 /* 337911106dfSjm199354 * vscan_drv_detach 338911106dfSjm199354 */ 339911106dfSjm199354 static int 340911106dfSjm199354 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 341911106dfSjm199354 { 342bfc848c6Sjm199354 int i; 343bfc848c6Sjm199354 344911106dfSjm199354 if (cmd != DDI_DETACH) 345911106dfSjm199354 return (DDI_FAILURE); 346911106dfSjm199354 347911106dfSjm199354 if (ddi_get_instance(dip) != 0) 348911106dfSjm199354 return (DDI_FAILURE); 349911106dfSjm199354 350911106dfSjm199354 if (vscan_drv_in_use()) 351911106dfSjm199354 return (DDI_FAILURE); 352911106dfSjm199354 35353c11029Sjm199354 /* remove all minor nodes */ 354911106dfSjm199354 vscan_drv_dip = NULL; 355911106dfSjm199354 ddi_remove_minor_node(dip, NULL); 356bfc848c6Sjm199354 for (i = 0; i <= vs_nodes_max; i++) 357bfc848c6Sjm199354 vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG; 358911106dfSjm199354 359bfc848c6Sjm199354 vscan_drv_state = VS_DRV_UNCONFIG; 360911106dfSjm199354 return (DDI_SUCCESS); 361911106dfSjm199354 } 362911106dfSjm199354 363911106dfSjm199354 364911106dfSjm199354 /* 365911106dfSjm199354 * vscan_drv_in_use 36653c11029Sjm199354 * 367bfc848c6Sjm199354 * If the driver state is not IDLE or UNCONFIG then the 368bfc848c6Sjm199354 * driver is in use. Otherwise, check the service interface 369bfc848c6Sjm199354 * (vscan_svc) to see if it is still in use - for example 370bfc848c6Sjm199354 * there there may be requests still in progress. 371911106dfSjm199354 */ 372911106dfSjm199354 static boolean_t 373911106dfSjm199354 vscan_drv_in_use() 374911106dfSjm199354 { 375bfc848c6Sjm199354 boolean_t in_use = B_FALSE; 37653c11029Sjm199354 37753c11029Sjm199354 mutex_enter(&vscan_drv_mutex); 378bfc848c6Sjm199354 if ((vscan_drv_state != VS_DRV_IDLE) && 379bfc848c6Sjm199354 (vscan_drv_state != VS_DRV_UNCONFIG)) { 380bfc848c6Sjm199354 in_use = B_TRUE; 381bfc848c6Sjm199354 } 38253c11029Sjm199354 mutex_exit(&vscan_drv_mutex); 38353c11029Sjm199354 384bfc848c6Sjm199354 if (in_use) 385bfc848c6Sjm199354 return (B_TRUE); 386bfc848c6Sjm199354 else 387bfc848c6Sjm199354 return (vscan_svc_in_use()); 388911106dfSjm199354 } 389911106dfSjm199354 390911106dfSjm199354 391911106dfSjm199354 /* 392911106dfSjm199354 * vscan_drv_open 393bfc848c6Sjm199354 * 394bfc848c6Sjm199354 * If inst == 0, this is vscand initializing. 395bfc848c6Sjm199354 * If the driver is in DELAYED_DISABLE, ie vscand previously 396bfc848c6Sjm199354 * disconnected without a clean shutdown and the driver is 397bfc848c6Sjm199354 * waiting for a period to allow vscand to reconnect, signal 398bfc848c6Sjm199354 * vscan_drv_cv to cancel the delayed disable. 399bfc848c6Sjm199354 * 400bfc848c6Sjm199354 * If inst != 0, open the file associated with inst. 401911106dfSjm199354 */ 402911106dfSjm199354 /* ARGSUSED */ 403911106dfSjm199354 static int 404911106dfSjm199354 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 405911106dfSjm199354 { 406911106dfSjm199354 int rc; 407911106dfSjm199354 int inst = getminor(*devp); 408911106dfSjm199354 409bfc848c6Sjm199354 if ((inst < 0) || (inst > vs_nodes_max)) 410911106dfSjm199354 return (EINVAL); 411911106dfSjm199354 412911106dfSjm199354 /* check if caller has privilege for virus scanning */ 413911106dfSjm199354 if ((rc = secpolicy_vscan(credp)) != 0) { 414911106dfSjm199354 DTRACE_PROBE1(vscan__priv, int, rc); 415911106dfSjm199354 return (EPERM); 416911106dfSjm199354 } 417911106dfSjm199354 418911106dfSjm199354 mutex_enter(&vscan_drv_mutex); 419911106dfSjm199354 if (inst == 0) { 420bfc848c6Sjm199354 switch (vscan_drv_state) { 421bfc848c6Sjm199354 case VS_DRV_IDLE: 422bfc848c6Sjm199354 vscan_drv_state = VS_DRV_CONNECTED; 423bfc848c6Sjm199354 break; 424bfc848c6Sjm199354 case VS_DRV_DELAYED_DISABLE: 42553c11029Sjm199354 cv_signal(&vscan_drv_cv); 426bfc848c6Sjm199354 vscan_drv_state = VS_DRV_CONNECTED; 427bfc848c6Sjm199354 break; 428bfc848c6Sjm199354 default: 429bfc848c6Sjm199354 DTRACE_PROBE1(vscan__drv__state__violation, 430bfc848c6Sjm199354 int, vscan_drv_state); 431911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 432911106dfSjm199354 return (EINVAL); 433911106dfSjm199354 } 434bfc848c6Sjm199354 } else { 435bfc848c6Sjm199354 if ((vscan_drv_state != VS_DRV_ENABLED) || 436bfc848c6Sjm199354 (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) { 437bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 438bfc848c6Sjm199354 return (EINVAL); 439bfc848c6Sjm199354 } 440bfc848c6Sjm199354 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 441911106dfSjm199354 } 442911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 443911106dfSjm199354 444911106dfSjm199354 return (0); 445911106dfSjm199354 } 446911106dfSjm199354 447911106dfSjm199354 448911106dfSjm199354 /* 449911106dfSjm199354 * vscan_drv_close 450bfc848c6Sjm199354 * 451bfc848c6Sjm199354 * If inst == 0, this is vscand detaching. 452bfc848c6Sjm199354 * If the driver is in ENABLED state vscand has terminated without 453bfc848c6Sjm199354 * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE 454bfc848c6Sjm199354 * state and initiate a delayed disable to allow vscand time to 455bfc848c6Sjm199354 * reconnect. 456bfc848c6Sjm199354 * 457bfc848c6Sjm199354 * If inst != 0, close the file associated with inst 458911106dfSjm199354 */ 459911106dfSjm199354 /* ARGSUSED */ 460911106dfSjm199354 static int 461911106dfSjm199354 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp) 462911106dfSjm199354 { 463911106dfSjm199354 int i, inst = getminor(dev); 464911106dfSjm199354 465bfc848c6Sjm199354 if ((inst < 0) || (inst > vs_nodes_max)) 466911106dfSjm199354 return (EINVAL); 467911106dfSjm199354 468911106dfSjm199354 mutex_enter(&vscan_drv_mutex); 469bfc848c6Sjm199354 if (inst != 0) { 470bfc848c6Sjm199354 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT; 471bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 472bfc848c6Sjm199354 return (0); 473bfc848c6Sjm199354 } 474911106dfSjm199354 475bfc848c6Sjm199354 /* instance 0 - daemon disconnect */ 476bfc848c6Sjm199354 if ((vscan_drv_state != VS_DRV_CONNECTED) && 477bfc848c6Sjm199354 (vscan_drv_state != VS_DRV_ENABLED)) { 478bfc848c6Sjm199354 DTRACE_PROBE1(vscan__drv__state__violation, 479bfc848c6Sjm199354 int, vscan_drv_state); 480bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 481bfc848c6Sjm199354 return (EINVAL); 482bfc848c6Sjm199354 } 483bfc848c6Sjm199354 484bfc848c6Sjm199354 for (i = 1; i <= vs_nodes_max; i++) { 485bfc848c6Sjm199354 if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG) 486bfc848c6Sjm199354 vscan_drv_inst_state[i] = VS_DRV_INST_INIT; 487bfc848c6Sjm199354 } 488bfc848c6Sjm199354 489bfc848c6Sjm199354 if (vscan_drv_state == VS_DRV_CONNECTED) { 490bfc848c6Sjm199354 vscan_drv_state = VS_DRV_IDLE; 491bfc848c6Sjm199354 } else { /* VS_DRV_ENABLED */ 492bfc848c6Sjm199354 cmn_err(CE_WARN, "Detected vscand exit without clean shutdown"); 49353c11029Sjm199354 if (thread_create(NULL, 0, vscan_drv_delayed_disable, 49453c11029Sjm199354 0, 0, &p0, TS_RUN, minclsyspri) == NULL) { 495bfc848c6Sjm199354 vscan_svc_disable(); 496bfc848c6Sjm199354 vscan_drv_state = VS_DRV_IDLE; 497911106dfSjm199354 } else { 498bfc848c6Sjm199354 vscan_drv_state = VS_DRV_DELAYED_DISABLE; 499bfc848c6Sjm199354 } 500911106dfSjm199354 } 501911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 502911106dfSjm199354 503bfc848c6Sjm199354 vscan_svc_scan_abort(); 504bfc848c6Sjm199354 vscan_door_close(); 505911106dfSjm199354 return (0); 506911106dfSjm199354 } 507911106dfSjm199354 508911106dfSjm199354 509911106dfSjm199354 /* 51053c11029Sjm199354 * vscan_drv_delayed_disable 51153c11029Sjm199354 * 51253c11029Sjm199354 * Invoked from vscan_drv_close if the daemon disconnects 51353c11029Sjm199354 * without first sending disable (e.g. daemon crashed). 514bfc848c6Sjm199354 * Delays for vs_reconnect_timeout before disabling, to allow 51553c11029Sjm199354 * the daemon to reconnect. During this time, scan requests 51653c11029Sjm199354 * will be processed locally (see vscan_svc.c) 51753c11029Sjm199354 */ 51853c11029Sjm199354 static void 51953c11029Sjm199354 vscan_drv_delayed_disable(void) 52053c11029Sjm199354 { 52153c11029Sjm199354 mutex_enter(&vscan_drv_mutex); 522*d3d50737SRafael Vanoni (void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex, 523*d3d50737SRafael Vanoni SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK); 52453c11029Sjm199354 525bfc848c6Sjm199354 if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) { 52653c11029Sjm199354 vscan_svc_disable(); 527bfc848c6Sjm199354 vscan_drv_state = VS_DRV_IDLE; 528bfc848c6Sjm199354 } else { 529bfc848c6Sjm199354 DTRACE_PROBE(vscan__reconnect); 53053c11029Sjm199354 } 53153c11029Sjm199354 mutex_exit(&vscan_drv_mutex); 53253c11029Sjm199354 } 53353c11029Sjm199354 53453c11029Sjm199354 53553c11029Sjm199354 /* 536911106dfSjm199354 * vscan_drv_read 537911106dfSjm199354 */ 538911106dfSjm199354 /* ARGSUSED */ 539911106dfSjm199354 static int 540911106dfSjm199354 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp) 541911106dfSjm199354 { 542911106dfSjm199354 int rc; 543911106dfSjm199354 int inst = getminor(dev); 544911106dfSjm199354 vnode_t *vp; 545911106dfSjm199354 546bfc848c6Sjm199354 if ((inst <= 0) || (inst > vs_nodes_max)) 547911106dfSjm199354 return (EINVAL); 548911106dfSjm199354 549911106dfSjm199354 mutex_enter(&vscan_drv_mutex); 550bfc848c6Sjm199354 if ((vscan_drv_state != VS_DRV_ENABLED) || 551bfc848c6Sjm199354 (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) { 552911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 553911106dfSjm199354 return (EINVAL); 554911106dfSjm199354 } 555bfc848c6Sjm199354 vscan_drv_inst_state[inst] = VS_DRV_INST_READING; 556911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 557911106dfSjm199354 558911106dfSjm199354 if ((vp = vscan_svc_get_vnode(inst)) == NULL) 559911106dfSjm199354 return (EINVAL); 560911106dfSjm199354 561911106dfSjm199354 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 562911106dfSjm199354 rc = VOP_READ(vp, uiop, 0, kcred, NULL); 563911106dfSjm199354 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 564911106dfSjm199354 565911106dfSjm199354 mutex_enter(&vscan_drv_mutex); 566bfc848c6Sjm199354 if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING) 567bfc848c6Sjm199354 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN; 568911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 569911106dfSjm199354 570911106dfSjm199354 return (rc); 571911106dfSjm199354 } 572911106dfSjm199354 573911106dfSjm199354 574911106dfSjm199354 /* 575911106dfSjm199354 * vscan_drv_ioctl 576bfc848c6Sjm199354 * 577bfc848c6Sjm199354 * Process ioctls from vscand: 578bfc848c6Sjm199354 * VS_IOCTL_ENABLE - vscand is ready to handle scan requests, 579bfc848c6Sjm199354 * enable VFS interface. 580bfc848c6Sjm199354 * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface 581bfc848c6Sjm199354 * VS_IOCTL_RESULT - scan response data 582bfc848c6Sjm199354 * VS_IOCTL_CONFIG - configuration data from vscand 583bfc848c6Sjm199354 * VS_IOCTL_MAX_REQ - provide the max request idx to vscand, 584bfc848c6Sjm199354 * to allow vscand to set appropriate resource allocation limits 585911106dfSjm199354 */ 586911106dfSjm199354 /* ARGSUSED */ 587911106dfSjm199354 static int 588911106dfSjm199354 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 589911106dfSjm199354 cred_t *credp, int *rvalp) 590911106dfSjm199354 { 591911106dfSjm199354 int inst = getminor(dev); 592911106dfSjm199354 vs_config_t conf; 593bfc848c6Sjm199354 vs_scan_rsp_t rsp; 594911106dfSjm199354 595911106dfSjm199354 if (inst != 0) 596911106dfSjm199354 return (EINVAL); 597911106dfSjm199354 598911106dfSjm199354 switch (cmd) { 599bfc848c6Sjm199354 case VS_IOCTL_ENABLE: 600911106dfSjm199354 mutex_enter(&vscan_drv_mutex); 601bfc848c6Sjm199354 if (vscan_drv_state != VS_DRV_CONNECTED) { 602bfc848c6Sjm199354 DTRACE_PROBE1(vscan__drv__state__violation, 603bfc848c6Sjm199354 int, vscan_drv_state); 604911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 605911106dfSjm199354 return (EINVAL); 606911106dfSjm199354 } 607bfc848c6Sjm199354 if ((vscan_door_open((int)arg) != 0) || 608bfc848c6Sjm199354 (vscan_svc_enable() != 0)) { 609bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 610bfc848c6Sjm199354 return (EINVAL); 611bfc848c6Sjm199354 } 612bfc848c6Sjm199354 vscan_drv_state = VS_DRV_ENABLED; 613911106dfSjm199354 mutex_exit(&vscan_drv_mutex); 614911106dfSjm199354 break; 615bfc848c6Sjm199354 616bfc848c6Sjm199354 case VS_IOCTL_DISABLE: 617bfc848c6Sjm199354 mutex_enter(&vscan_drv_mutex); 618bfc848c6Sjm199354 if (vscan_drv_state != VS_DRV_ENABLED) { 619bfc848c6Sjm199354 DTRACE_PROBE1(vscan__drv__state__violation, 620bfc848c6Sjm199354 int, vscan_drv_state); 621bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 622bfc848c6Sjm199354 return (EINVAL); 623bfc848c6Sjm199354 } 62453c11029Sjm199354 vscan_svc_disable(); 625bfc848c6Sjm199354 vscan_drv_state = VS_DRV_CONNECTED; 626bfc848c6Sjm199354 mutex_exit(&vscan_drv_mutex); 627911106dfSjm199354 break; 628bfc848c6Sjm199354 629bfc848c6Sjm199354 case VS_IOCTL_RESULT: 630bfc848c6Sjm199354 if (ddi_copyin((void *)arg, &rsp, 631bfc848c6Sjm199354 sizeof (vs_scan_rsp_t), 0) == -1) 632bfc848c6Sjm199354 return (EFAULT); 633bfc848c6Sjm199354 else 634bfc848c6Sjm199354 vscan_svc_scan_result(&rsp); 635bfc848c6Sjm199354 break; 636bfc848c6Sjm199354 637bfc848c6Sjm199354 case VS_IOCTL_CONFIG: 638911106dfSjm199354 if (ddi_copyin((void *)arg, &conf, 639911106dfSjm199354 sizeof (vs_config_t), 0) == -1) 640911106dfSjm199354 return (EFAULT); 641911106dfSjm199354 if (vscan_svc_configure(&conf) == -1) 642911106dfSjm199354 return (EINVAL); 643911106dfSjm199354 break; 644bfc848c6Sjm199354 645bfc848c6Sjm199354 case VS_IOCTL_MAX_REQ: 646bfc848c6Sjm199354 if (ddi_copyout(&vs_nodes_max, (void *)arg, 647bfc848c6Sjm199354 sizeof (uint32_t), 0) == -1) 648bfc848c6Sjm199354 return (EFAULT); 649bfc848c6Sjm199354 break; 650bfc848c6Sjm199354 651911106dfSjm199354 default: 652911106dfSjm199354 return (ENOTTY); 653911106dfSjm199354 } 654911106dfSjm199354 655911106dfSjm199354 return (0); 656911106dfSjm199354 } 65753c11029Sjm199354 65853c11029Sjm199354 65953c11029Sjm199354 /* 66053c11029Sjm199354 * vscan_drv_create_node 66153c11029Sjm199354 * 66253c11029Sjm199354 * Create minor node with which vscan daemon will communicate 66353c11029Sjm199354 * to access a file. Invoked from vscan_svc before scan request 66453c11029Sjm199354 * sent up to daemon. 66553c11029Sjm199354 * Minor node 0 is reserved for daemon-driver synchronization 66653c11029Sjm199354 * and is created during attach. 66753c11029Sjm199354 * All minor nodes are removed during detach. 66853c11029Sjm199354 */ 66953c11029Sjm199354 boolean_t 67053c11029Sjm199354 vscan_drv_create_node(int idx) 67153c11029Sjm199354 { 672bfc848c6Sjm199354 char name[VS_NODENAME_LEN]; 673bfc848c6Sjm199354 boolean_t rc = B_TRUE; 67453c11029Sjm199354 67553c11029Sjm199354 mutex_enter(&vscan_drv_mutex); 67653c11029Sjm199354 677bfc848c6Sjm199354 if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) { 678bfc848c6Sjm199354 (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx); 67953c11029Sjm199354 if (ddi_create_minor_node(vscan_drv_dip, name, 68053c11029Sjm199354 S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) { 681bfc848c6Sjm199354 vscan_drv_inst_state[idx] = VS_DRV_INST_INIT; 682bfc848c6Sjm199354 } else { 683bfc848c6Sjm199354 rc = B_FALSE; 68453c11029Sjm199354 } 685bfc848c6Sjm199354 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc); 68653c11029Sjm199354 } 68753c11029Sjm199354 68853c11029Sjm199354 mutex_exit(&vscan_drv_mutex); 68953c11029Sjm199354 69053c11029Sjm199354 return (rc); 69153c11029Sjm199354 } 692