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, NULL }  /* ptr to linkage structures            */
 215 };
 216 
 217 
 218 /*
 219  * _init
 220  */
 221 int
 222 _init(void)
 223 {
 224         int rc;
 225 
 226         vscan_drv_inst_state_sz =
 227             sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
 228 
 229         if (vscan_door_init() != 0)
 230                 return (DDI_FAILURE);
 231 
 232         if (vscan_svc_init() != 0) {
 233                 vscan_door_fini();
 234                 return (DDI_FAILURE);
 235         }
 236 
 237         mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
 238         vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
 239 
 240         cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
 241 
 242         if ((rc  = mod_install(&modlinkage)) != 0) {
 243                 vscan_door_fini();
 244                 vscan_svc_fini();
 245                 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
 246                 cv_destroy(&vscan_drv_cv);
 247                 mutex_destroy(&vscan_drv_mutex);
 248         }
 249 
 250         return (rc);
 251 }
 252 
 253 
 254 /*
 255  * _info
 256  */
 257 int
 258 _info(struct modinfo *modinfop)
 259 {
 260         return (mod_info(&modlinkage, modinfop));
 261 }
 262 
 263 
 264 /*
 265  * _fini
 266  */
 267 int
 268 _fini(void)
 269 {
 270         int rc;
 271 
 272         if (vscan_drv_in_use())
 273                 return (EBUSY);
 274 
 275         if ((rc = mod_remove(&modlinkage)) == 0) {
 276                 vscan_door_fini();
 277                 vscan_svc_fini();
 278                 kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
 279                 cv_destroy(&vscan_drv_cv);
 280                 mutex_destroy(&vscan_drv_mutex);
 281         }
 282 
 283         return (rc);
 284 }
 285 
 286 
 287 /*
 288  * DDI entry points.
 289  */
 290 
 291 /*
 292  * vscan_drv_getinfo
 293  */
 294 /* ARGSUSED */
 295 static int
 296 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 297 {
 298         ulong_t inst = getminor((dev_t)arg);
 299 
 300         switch (cmd) {
 301         case DDI_INFO_DEVT2DEVINFO:
 302                 *result = vscan_drv_dip;
 303                 return (DDI_SUCCESS);
 304         case DDI_INFO_DEVT2INSTANCE:
 305                 *result = (void *)inst;
 306                 return (DDI_SUCCESS);
 307         }
 308         return (DDI_FAILURE);
 309 }
 310 
 311 
 312 /*
 313  * vscan_drv_attach
 314  */
 315 static int
 316 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 317 {
 318         if (cmd != DDI_ATTACH)
 319                 return (DDI_FAILURE);
 320 
 321         if (ddi_get_instance(dip) != 0)
 322                 return (DDI_FAILURE);
 323 
 324         vscan_drv_dip = dip;
 325 
 326         /* create minor node 0 for daemon-driver synchronization */
 327         if (vscan_drv_create_node(0) == B_FALSE)
 328                 return (DDI_FAILURE);
 329 
 330         vscan_drv_state = VS_DRV_IDLE;
 331         return (DDI_SUCCESS);
 332 }
 333 
 334 
 335 /*
 336  * vscan_drv_detach
 337  */
 338 static int
 339 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 340 {
 341         int i;
 342 
 343         if (cmd != DDI_DETACH)
 344                 return (DDI_FAILURE);
 345 
 346         if (ddi_get_instance(dip) != 0)
 347                 return (DDI_FAILURE);
 348 
 349         if (vscan_drv_in_use())
 350                 return (DDI_FAILURE);
 351 
 352         /* remove all minor nodes */
 353         vscan_drv_dip = NULL;
 354         ddi_remove_minor_node(dip, NULL);
 355         for (i = 0; i <= vs_nodes_max; i++)
 356                 vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
 357 
 358         vscan_drv_state = VS_DRV_UNCONFIG;
 359         return (DDI_SUCCESS);
 360 }
 361 
 362 
 363 /*
 364  * vscan_drv_in_use
 365  *
 366  * If the driver state is not IDLE or UNCONFIG then the
 367  * driver is in use. Otherwise, check the service interface
 368  * (vscan_svc) to see if it is still in use - for example
 369  * there there may be requests still in progress.
 370  */
 371 static boolean_t
 372 vscan_drv_in_use()
 373 {
 374         boolean_t in_use = B_FALSE;
 375 
 376         mutex_enter(&vscan_drv_mutex);
 377         if ((vscan_drv_state != VS_DRV_IDLE) &&
 378             (vscan_drv_state != VS_DRV_UNCONFIG)) {
 379                 in_use = B_TRUE;
 380         }
 381         mutex_exit(&vscan_drv_mutex);
 382 
 383         if (in_use)
 384                 return (B_TRUE);
 385         else
 386                 return (vscan_svc_in_use());
 387 }
 388 
 389 
 390 /*
 391  * vscan_drv_open
 392  *
 393  * If inst == 0, this is vscand initializing.
 394  * If the driver is in DELAYED_DISABLE, ie vscand previously
 395  * disconnected without a clean shutdown and the driver is
 396  * waiting for a period to allow vscand to reconnect, signal
 397  * vscan_drv_cv to cancel the delayed disable.
 398  *
 399  * If inst != 0, open the file associated with inst.
 400  */
 401 /* ARGSUSED */
 402 static int
 403 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 404 {
 405         int rc;
 406         int inst = getminor(*devp);
 407 
 408         if ((inst < 0) || (inst > vs_nodes_max))
 409                 return (EINVAL);
 410 
 411         /* check if caller has privilege for virus scanning */
 412         if ((rc = secpolicy_vscan(credp)) != 0) {
 413                 DTRACE_PROBE1(vscan__priv, int, rc);
 414                 return (EPERM);
 415         }
 416 
 417         mutex_enter(&vscan_drv_mutex);
 418         if (inst == 0) {
 419                 switch (vscan_drv_state) {
 420                 case VS_DRV_IDLE:
 421                         vscan_drv_state = VS_DRV_CONNECTED;
 422                         break;
 423                 case VS_DRV_DELAYED_DISABLE:
 424                         cv_signal(&vscan_drv_cv);
 425                         vscan_drv_state = VS_DRV_CONNECTED;
 426                         break;
 427                 default:
 428                         DTRACE_PROBE1(vscan__drv__state__violation,
 429                             int, vscan_drv_state);
 430                         mutex_exit(&vscan_drv_mutex);
 431                         return (EINVAL);
 432                 }
 433         } else {
 434                 if ((vscan_drv_state != VS_DRV_ENABLED) ||
 435                     (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
 436                         mutex_exit(&vscan_drv_mutex);
 437                         return (EINVAL);
 438                 }
 439                 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
 440         }
 441         mutex_exit(&vscan_drv_mutex);
 442 
 443         return (0);
 444 }
 445 
 446 
 447 /*
 448  * vscan_drv_close
 449  *
 450  * If inst == 0, this is vscand detaching.
 451  * If the driver is in ENABLED state vscand has terminated without
 452  * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
 453  * state and initiate a delayed disable to allow vscand time to
 454  * reconnect.
 455  *
 456  * If inst != 0, close the file associated with inst
 457  */
 458 /* ARGSUSED */
 459 static int
 460 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
 461 {
 462         int i, inst = getminor(dev);
 463 
 464         if ((inst < 0) || (inst > vs_nodes_max))
 465                 return (EINVAL);
 466 
 467         mutex_enter(&vscan_drv_mutex);
 468         if (inst != 0) {
 469                 vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
 470                 mutex_exit(&vscan_drv_mutex);
 471                 return (0);
 472         }
 473 
 474         /* instance 0 - daemon disconnect */
 475         if ((vscan_drv_state != VS_DRV_CONNECTED) &&
 476             (vscan_drv_state != VS_DRV_ENABLED)) {
 477                 DTRACE_PROBE1(vscan__drv__state__violation,
 478                     int, vscan_drv_state);
 479                 mutex_exit(&vscan_drv_mutex);
 480                 return (EINVAL);
 481         }
 482 
 483         for (i = 1; i <= vs_nodes_max; i++) {
 484                 if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
 485                         vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
 486         }
 487 
 488         if (vscan_drv_state == VS_DRV_CONNECTED) {
 489                 vscan_drv_state = VS_DRV_IDLE;
 490         } else { /* VS_DRV_ENABLED */
 491                 cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
 492                 if (thread_create(NULL, 0, vscan_drv_delayed_disable,
 493                     0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
 494                         vscan_svc_disable();
 495                         vscan_drv_state = VS_DRV_IDLE;
 496                 } else {
 497                         vscan_drv_state = VS_DRV_DELAYED_DISABLE;
 498                 }
 499         }
 500         mutex_exit(&vscan_drv_mutex);
 501 
 502         vscan_svc_scan_abort();
 503         vscan_door_close();
 504         return (0);
 505 }
 506 
 507 
 508 /*
 509  * vscan_drv_delayed_disable
 510  *
 511  * Invoked from vscan_drv_close if the daemon disconnects
 512  * without first sending disable (e.g. daemon crashed).
 513  * Delays for vs_reconnect_timeout before disabling, to allow
 514  * the daemon to reconnect. During this time, scan requests
 515  * will be processed locally (see vscan_svc.c)
 516  */
 517 static void
 518 vscan_drv_delayed_disable(void)
 519 {
 520         mutex_enter(&vscan_drv_mutex);
 521         (void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex,
 522             SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK);
 523 
 524         if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
 525                 vscan_svc_disable();
 526                 vscan_drv_state = VS_DRV_IDLE;
 527         } else {
 528                 DTRACE_PROBE(vscan__reconnect);
 529         }
 530         mutex_exit(&vscan_drv_mutex);
 531 }
 532 
 533 
 534 /*
 535  * vscan_drv_read
 536  */
 537 /* ARGSUSED */
 538 static int
 539 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
 540 {
 541         int rc;
 542         int inst = getminor(dev);
 543         vnode_t *vp;
 544 
 545         if ((inst <= 0) || (inst > vs_nodes_max))
 546                 return (EINVAL);
 547 
 548         mutex_enter(&vscan_drv_mutex);
 549         if ((vscan_drv_state != VS_DRV_ENABLED) ||
 550             (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
 551                 mutex_exit(&vscan_drv_mutex);
 552                 return (EINVAL);
 553         }
 554         vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
 555         mutex_exit(&vscan_drv_mutex);
 556 
 557         if ((vp = vscan_svc_get_vnode(inst)) == NULL)
 558                 return (EINVAL);
 559 
 560         (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
 561         rc = VOP_READ(vp, uiop, 0, kcred, NULL);
 562         VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
 563 
 564         mutex_enter(&vscan_drv_mutex);
 565         if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
 566                 vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
 567         mutex_exit(&vscan_drv_mutex);
 568 
 569         return (rc);
 570 }
 571 
 572 
 573 /*
 574  * vscan_drv_ioctl
 575  *
 576  * Process ioctls from vscand:
 577  * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
 578  *    enable VFS interface.
 579  * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
 580  * VS_IOCTL_RESULT - scan response data
 581  * VS_IOCTL_CONFIG - configuration data from vscand
 582  * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
 583  *    to allow vscand to set appropriate resource allocation limits
 584  */
 585 /* ARGSUSED */
 586 static int
 587 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
 588         cred_t *credp, int *rvalp)
 589 {
 590         int inst = getminor(dev);
 591         vs_config_t conf;
 592         vs_scan_rsp_t rsp;
 593 
 594         if (inst != 0)
 595                 return (EINVAL);
 596 
 597         switch (cmd) {
 598         case VS_IOCTL_ENABLE:
 599                 mutex_enter(&vscan_drv_mutex);
 600                 if (vscan_drv_state != VS_DRV_CONNECTED) {
 601                         DTRACE_PROBE1(vscan__drv__state__violation,
 602                             int, vscan_drv_state);
 603                         mutex_exit(&vscan_drv_mutex);
 604                         return (EINVAL);
 605                 }
 606                 if ((vscan_door_open((int)arg) != 0) ||
 607                     (vscan_svc_enable() != 0)) {
 608                         mutex_exit(&vscan_drv_mutex);
 609                         return (EINVAL);
 610                 }
 611                 vscan_drv_state = VS_DRV_ENABLED;
 612                 mutex_exit(&vscan_drv_mutex);
 613                 break;
 614 
 615         case VS_IOCTL_DISABLE:
 616                 mutex_enter(&vscan_drv_mutex);
 617                 if (vscan_drv_state != VS_DRV_ENABLED) {
 618                         DTRACE_PROBE1(vscan__drv__state__violation,
 619                             int, vscan_drv_state);
 620                         mutex_exit(&vscan_drv_mutex);
 621                         return (EINVAL);
 622                 }
 623                 vscan_svc_disable();
 624                 vscan_drv_state = VS_DRV_CONNECTED;
 625                 mutex_exit(&vscan_drv_mutex);
 626                 break;
 627 
 628         case VS_IOCTL_RESULT:
 629                 if (ddi_copyin((void *)arg, &rsp,
 630                     sizeof (vs_scan_rsp_t), 0) == -1)
 631                         return (EFAULT);
 632                 else
 633                         vscan_svc_scan_result(&rsp);
 634                 break;
 635 
 636         case VS_IOCTL_CONFIG:
 637                 if (ddi_copyin((void *)arg, &conf,
 638                     sizeof (vs_config_t), 0) == -1)
 639                         return (EFAULT);
 640                 if (vscan_svc_configure(&conf) == -1)
 641                         return (EINVAL);
 642                 break;
 643 
 644         case VS_IOCTL_MAX_REQ:
 645                 if (ddi_copyout(&vs_nodes_max, (void *)arg,
 646                     sizeof (uint32_t), 0) == -1)
 647                         return (EFAULT);
 648                 break;
 649 
 650         default:
 651                 return (ENOTTY);
 652         }
 653 
 654         return (0);
 655 }
 656 
 657 
 658 /*
 659  * vscan_drv_create_node
 660  *
 661  * Create minor node with which vscan daemon will communicate
 662  * to access a file. Invoked from vscan_svc before scan request
 663  * sent up to daemon.
 664  * Minor node 0 is reserved for daemon-driver synchronization
 665  * and is created during attach.
 666  * All minor nodes are removed during detach.
 667  */
 668 boolean_t
 669 vscan_drv_create_node(int idx)
 670 {
 671         char name[VS_NODENAME_LEN];
 672         boolean_t rc = B_TRUE;
 673 
 674         mutex_enter(&vscan_drv_mutex);
 675 
 676         if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
 677                 (void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
 678                 if (ddi_create_minor_node(vscan_drv_dip, name,
 679                     S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
 680                         vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
 681                 } else {
 682                         rc = B_FALSE;
 683                 }
 684                 DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
 685         }
 686 
 687         mutex_exit(&vscan_drv_mutex);
 688 
 689         return (rc);
 690 }