1 /*
   2  * CDDL HEADER START
   3  *
   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.
   7  *
   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.
  12  *
  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]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/stat.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/time.h>
  31 #include <sys/varargs.h>
  32 #include <sys/conf.h>
  33 #include <sys/modctl.h>
  34 #include <sys/vnode.h>
  35 #include <fs/fs_subr.h>
  36 #include <sys/types.h>
  37 #include <sys/file.h>
  38 #include <sys/disp.h>
  39 #include <sys/vscan.h>
  40 #include <sys/policy.h>
  41 #include <sys/sdt.h>
  42 
  43 
  44 /* seconds to wait for daemon to reconnect before disabling */
  45 #define VS_DAEMON_WAIT_SEC      60
  46 
  47 /* length of minor node name - vscan%d */
  48 #define VS_NODENAME_LEN         16
  49 
  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 */
  53 
  54 /*
  55  * vscan_drv_state
  56  *
  57  * Operations on instance 0 represent vscand initiated state
  58  * transition events:
  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)
  63  *
  64  *   +------------------------+
  65  *   | VS_DRV_UNCONFIG        |
  66  *   +------------------------+
  67  *      |           ^
  68  *      | attach    | detach
  69  *      v           |
  70  *   +------------------------+
  71  *   | VS_DRV_IDLE            |<------|
  72  *   +------------------------+       |
  73  *      |           ^                 |
  74  *      | open(0)   | close(0)        |
  75  *      v           |                 |
  76  *   +------------------------+       |
  77  *   | VS_DRV_CONNECTED       |<-|    |
  78  *   +------------------------+  |    |
  79  *      |           ^            |    |
  80  *      | enable(0) | disable(0) |    |
  81  *      v           |            |    |
  82  *   +------------------------+  |    |
  83  *   | VS_DRV_ENABLED         |  |    |
  84  *   +------------------------+  |    |
  85  *      |                        |    |
  86  *      | close(0)            open(0) |
  87  *      v                        |    |
  88  *   +------------------------+  |    | timeout
  89  *   | VS_DRV_DELAYED_DISABLE | --    |
  90  *   +------------------------+ ------|
  91  *
  92  */
  93 typedef enum {
  94         VS_DRV_UNCONFIG,
  95         VS_DRV_IDLE,
  96         VS_DRV_CONNECTED,
  97         VS_DRV_ENABLED,
  98         VS_DRV_DELAYED_DISABLE
  99 } vscan_drv_state_t;
 100 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG;
 101 
 102 
 103 /*
 104  * vscan_drv_inst_state
 105  *
 106  * Instance 0 controls the state of the driver: vscan_drv_state.
 107  * vscan_drv_inst_state[0] should NOT be used.
 108  *
 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.
 114  *
 115  *   +------------------------+
 116  *   | VS_DRV_INST_UNCONFIG   |
 117  *   +------------------------+
 118  *      |                 ^
 119  *      | create_node(n)  | detach
 120  *      v                 |
 121  *   +------------------------+
 122  *   | VS_DRV_INST_INIT       |<-|
 123  *   +------------------------+  |
 124  *      |                        |
 125  *      | open(n)                |
 126  *      v                        |
 127  *   +------------------------+  |
 128  *   | VS_DRV_INST_OPEN       |--|
 129  *   +------------------------+  |
 130  *      |                        |
 131  *      | read(n)                |
 132  *      v                        | close(n)
 133  *   +------------------------+  |
 134  *   | VS_DRV_INST_READING    |--|
 135  *   +------------------------+
 136  */
 137 typedef enum {
 138         VS_DRV_INST_UNCONFIG = 0, /* minor node not created */
 139         VS_DRV_INST_INIT,
 140         VS_DRV_INST_OPEN,
 141         VS_DRV_INST_READING
 142 } vscan_drv_inst_state_t;
 143 
 144 static vscan_drv_inst_state_t *vscan_drv_inst_state;
 145 static int vscan_drv_inst_state_sz;
 146 
 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 */
 150 
 151 /*
 152  * DDI entry points.
 153  */
 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 *);
 161 
 162 static boolean_t vscan_drv_in_use(void);
 163 static void vscan_drv_delayed_disable(void);
 164 
 165 
 166 /*
 167  * module linkage info for the kernel
 168  */
 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 */
 174         nodev,                  /* cb_dump */
 175         vscan_drv_read,         /* cb_read */
 176         nodev,                  /* cb_write */
 177         vscan_drv_ioctl,        /* cb_ioctl */
 178         nodev,                  /* cb_devmap */
 179         nodev,                  /* cb_mmap */
 180         nodev,                  /* cb_segmap */
 181         nochpoll,               /* cb_chpoll */
 182         ddi_prop_op,            /* cb_prop_op */
 183         NULL,                   /* cb_streamtab */
 184         D_MP,                   /* cb_flag */
 185         CB_REV,                 /* cb_rev */
 186         nodev,                  /* cb_aread */
 187         nodev,                  /* cb_awrite */
 188 };
 189 
 190 static struct dev_ops devops = {
 191         DEVO_REV,               /* devo_rev */
 192         0,                      /* devo_refcnt */
 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 */
 203 };
 204 
 205 static struct modldrv modldrv = {
 206         &mod_driverops,             /* drv_modops */
 207         "virus scanning",       /* drv_linkinfo */
 208         &devops,
 209 };
 210 
 211 static struct modlinkage modlinkage = {
 212 
 213         MODREV_1,       /* revision of the module, must be: MODREV_1    */
 214         &modldrv,   /* ptr to linkage structures                    */
 215         NULL,
 216 };
 217 
 218 
 219 /*
 220  * _init
 221  */
 222 int
 223 _init(void)
 224 {
 225         int rc;
 226 
 227         vscan_drv_inst_state_sz =
 228             sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
 229 
 230         if (vscan_door_init() != 0)
 231                 return (DDI_FAILURE);
 232 
 233         if (vscan_svc_init() != 0) {
 234                 vscan_door_fini();
 235                 return (DDI_FAILURE);
 236         }
 237 
 238         mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
 239         vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
 240 
 241         cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
 242 
 243         if ((rc  = mod_install(&modlinkage)) != 0) {
 244                 vscan_door_fini();
 245                 vscan_svc_fini();
 246                 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
 247                 cv_destroy(&vscan_drv_cv);
 248                 mutex_destroy(&vscan_drv_mutex);
 249         }
 250 
 251         return (rc);
 252 }
 253 
 254 
 255 /*
 256  * _info
 257  */
 258 int
 259 _info(struct modinfo *modinfop)
 260 {
 261         return (mod_info(&modlinkage, modinfop));
 262 }
 263 
 264 
 265 /*
 266  * _fini
 267  */
 268 int
 269 _fini(void)
 270 {
 271         int rc;
 272 
 273         if (vscan_drv_in_use())
 274                 return (EBUSY);
 275 
 276         if ((rc = mod_remove(&modlinkage)) == 0) {
 277                 vscan_door_fini();
 278                 vscan_svc_fini();
 279                 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
 280                 cv_destroy(&vscan_drv_cv);
 281                 mutex_destroy(&vscan_drv_mutex);
 282         }
 283 
 284         return (rc);
 285 }
 286 
 287 
 288 /*
 289  * DDI entry points.
 290  */
 291 
 292 /*
 293  * vscan_drv_getinfo
 294  */
 295 /* ARGSUSED */
 296 static int
 297 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 298 {
 299         ulong_t inst = getminor((dev_t)arg);
 300 
 301         switch (cmd) {
 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);
 308         }
 309         return (DDI_FAILURE);
 310 }
 311 
 312 
 313 /*
 314  * vscan_drv_attach
 315  */
 316 static int
 317 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 318 {
 319         if (cmd != DDI_ATTACH)
 320                 return (DDI_FAILURE);
 321 
 322         if (ddi_get_instance(dip) != 0)
 323                 return (DDI_FAILURE);
 324 
 325         vscan_drv_dip = dip;
 326 
 327         /* create minor node 0 for daemon-driver synchronization */
 328         if (vscan_drv_create_node(0) == B_FALSE)
 329                 return (DDI_FAILURE);
 330 
 331         vscan_drv_state = VS_DRV_IDLE;
 332         return (DDI_SUCCESS);
 333 }
 334 
 335 
 336 /*
 337  * vscan_drv_detach
 338  */
 339 static int
 340 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 341 {
 342         int i;
 343 
 344         if (cmd != DDI_DETACH)
 345                 return (DDI_FAILURE);
 346 
 347         if (ddi_get_instance(dip) != 0)
 348                 return (DDI_FAILURE);
 349 
 350         if (vscan_drv_in_use())
 351                 return (DDI_FAILURE);
 352 
 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;
 358 
 359         vscan_drv_state = VS_DRV_UNCONFIG;
 360         return (DDI_SUCCESS);
 361 }
 362 
 363 
 364 /*
 365  * vscan_drv_in_use
 366  *
 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.
 371  */
 372 static boolean_t
 373 vscan_drv_in_use()
 374 {
 375         boolean_t in_use = B_FALSE;
 376 
 377         mutex_enter(&vscan_drv_mutex);
 378         if ((vscan_drv_state != VS_DRV_IDLE) &&
 379             (vscan_drv_state != VS_DRV_UNCONFIG)) {
 380                 in_use = B_TRUE;
 381         }
 382         mutex_exit(&vscan_drv_mutex);
 383 
 384         if (in_use)
 385                 return (B_TRUE);
 386         else
 387                 return (vscan_svc_in_use());
 388 }
 389 
 390 
 391 /*
 392  * vscan_drv_open
 393  *
 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.
 399  *
 400  * If inst != 0, open the file associated with inst.
 401  */
 402 /* ARGSUSED */
 403 static int
 404 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 405 {
 406         int rc;
 407         int inst = getminor(*devp);
 408 
 409         if ((inst < 0) || (inst > vs_nodes_max))
 410                 return (EINVAL);
 411 
 412         /* check if caller has privilege for virus scanning */
 413         if ((rc = secpolicy_vscan(credp)) != 0) {
 414                 DTRACE_PROBE1(vscan__priv, int, rc);
 415                 return (EPERM);
 416         }
 417 
 418         mutex_enter(&vscan_drv_mutex);
 419         if (inst == 0) {
 420                 switch (vscan_drv_state) {
 421                 case VS_DRV_IDLE:
 422                         vscan_drv_state = VS_DRV_CONNECTED;
 423                         break;
 424                 case VS_DRV_DELAYED_DISABLE:
 425                         cv_signal(&vscan_drv_cv);
 426                         vscan_drv_state = VS_DRV_CONNECTED;
 427                         break;
 428                 default:
 429                         DTRACE_PROBE1(vscan__drv__state__violation,
 430                             int, vscan_drv_state);
 431                         mutex_exit(&vscan_drv_mutex);
 432                         return (EINVAL);
 433                 }
 434         } else {
 435                 if ((vscan_drv_state != VS_DRV_ENABLED) ||
 436                     (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
 437                         mutex_exit(&vscan_drv_mutex);
 438                         return (EINVAL);
 439                 }
 440                 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
 441         }
 442         mutex_exit(&vscan_drv_mutex);
 443 
 444         return (0);
 445 }
 446 
 447 
 448 /*
 449  * vscan_drv_close
 450  *
 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
 455  * reconnect.
 456  *
 457  * If inst != 0, close the file associated with inst
 458  */
 459 /* ARGSUSED */
 460 static int
 461 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
 462 {
 463         int i, inst = getminor(dev);
 464 
 465         if ((inst < 0) || (inst > vs_nodes_max))
 466                 return (EINVAL);
 467 
 468         mutex_enter(&vscan_drv_mutex);
 469         if (inst != 0) {
 470                 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
 471                 mutex_exit(&vscan_drv_mutex);
 472                 return (0);
 473         }
 474 
 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);
 481                 return (EINVAL);
 482         }
 483 
 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;
 487         }
 488 
 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) {
 495                         vscan_svc_disable();
 496                         vscan_drv_state = VS_DRV_IDLE;
 497                 } else {
 498                         vscan_drv_state = VS_DRV_DELAYED_DISABLE;
 499                 }
 500         }
 501         mutex_exit(&vscan_drv_mutex);
 502 
 503         vscan_svc_scan_abort();
 504         vscan_door_close();
 505         return (0);
 506 }
 507 
 508 
 509 /*
 510  * vscan_drv_delayed_disable
 511  *
 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)
 517  */
 518 static void
 519 vscan_drv_delayed_disable(void)
 520 {
 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);
 524 
 525         if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
 526                 vscan_svc_disable();
 527                 vscan_drv_state = VS_DRV_IDLE;
 528         } else {
 529                 DTRACE_PROBE(vscan__reconnect);
 530         }
 531         mutex_exit(&vscan_drv_mutex);
 532 }
 533 
 534 
 535 /*
 536  * vscan_drv_read
 537  */
 538 /* ARGSUSED */
 539 static int
 540 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
 541 {
 542         int rc;
 543         int inst = getminor(dev);
 544         vnode_t *vp;
 545 
 546         if ((inst <= 0) || (inst > vs_nodes_max))
 547                 return (EINVAL);
 548 
 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);
 553                 return (EINVAL);
 554         }
 555         vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
 556         mutex_exit(&vscan_drv_mutex);
 557 
 558         if ((vp = vscan_svc_get_vnode(inst)) == NULL)
 559                 return (EINVAL);
 560 
 561         (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
 562         rc = VOP_READ(vp, uiop, 0, kcred, NULL);
 563         VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 564 
 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);
 569 
 570         return (rc);
 571 }
 572 
 573 
 574 /*
 575  * vscan_drv_ioctl
 576  *
 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
 585  */
 586 /* ARGSUSED */
 587 static int
 588 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
 589         cred_t *credp, int *rvalp)
 590 {
 591         int inst = getminor(dev);
 592         vs_config_t conf;
 593         vs_scan_rsp_t rsp;
 594 
 595         if (inst != 0)
 596                 return (EINVAL);
 597 
 598         switch (cmd) {
 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);
 605                         return (EINVAL);
 606                 }
 607                 if ((vscan_door_open((int)arg) != 0) ||
 608                     (vscan_svc_enable() != 0)) {
 609                         mutex_exit(&vscan_drv_mutex);
 610                         return (EINVAL);
 611                 }
 612                 vscan_drv_state = VS_DRV_ENABLED;
 613                 mutex_exit(&vscan_drv_mutex);
 614                 break;
 615 
 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);
 622                         return (EINVAL);
 623                 }
 624                 vscan_svc_disable();
 625                 vscan_drv_state = VS_DRV_CONNECTED;
 626                 mutex_exit(&vscan_drv_mutex);
 627                 break;
 628 
 629         case VS_IOCTL_RESULT:
 630                 if (ddi_copyin((void *)arg, &rsp,
 631                     sizeof (vs_scan_rsp_t), 0) == -1)
 632                         return (EFAULT);
 633                 else
 634                         vscan_svc_scan_result(&rsp);
 635                 break;
 636 
 637         case VS_IOCTL_CONFIG:
 638                 if (ddi_copyin((void *)arg, &conf,
 639                     sizeof (vs_config_t), 0) == -1)
 640                         return (EFAULT);
 641                 if (vscan_svc_configure(&conf) == -1)
 642                         return (EINVAL);
 643                 break;
 644 
 645         case VS_IOCTL_MAX_REQ:
 646                 if (ddi_copyout(&vs_nodes_max, (void *)arg,
 647                     sizeof (uint32_t), 0) == -1)
 648                         return (EFAULT);
 649                 break;
 650 
 651         default:
 652                 return (ENOTTY);
 653         }
 654 
 655         return (0);
 656 }
 657 
 658 
 659 /*
 660  * vscan_drv_create_node
 661  *
 662  * Create minor node with which vscan daemon will communicate
 663  * to access a file. Invoked from vscan_svc before scan request
 664  * sent up to daemon.
 665  * Minor node 0 is reserved for daemon-driver synchronization
 666  * and is created during attach.
 667  * All minor nodes are removed during detach.
 668  */
 669 boolean_t
 670 vscan_drv_create_node(int idx)
 671 {
 672         char name[VS_NODENAME_LEN];
 673         boolean_t rc = B_TRUE;
 674 
 675         mutex_enter(&vscan_drv_mutex);
 676 
 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;
 682                 } else {
 683                         rc = B_FALSE;
 684                 }
 685                 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
 686         }
 687 
 688         mutex_exit(&vscan_drv_mutex);
 689 
 690         return (rc);
 691 }