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/cmn_err.h>
  35 #include <sys/vnode.h>
  36 #include <fs/fs_subr.h>
  37 #include <sys/types.h>
  38 #include <sys/file.h>
  39 #include <sys/disp.h>
  40 #include <sys/sdt.h>
  41 #include <sys/cred.h>
  42 #include <sys/list.h>
  43 #include <sys/vscan.h>
  44 
  45 #define VS_REQ_MAGIC            0x52515354 /* 'RQST' */
  46 
  47 #define VS_REQS_DEFAULT         20000   /* pending scan requests - reql */
  48 #define VS_NODES_DEFAULT        128     /* concurrent file scans */
  49 #define VS_WORKERS_DEFAULT      32      /* worker threads */
  50 #define VS_SCANWAIT_DEFAULT     15*60   /* seconds to wait for scan result */
  51 #define VS_REQL_HANDLER_TIMEOUT 30
  52 #define VS_EXT_RECURSE_DEPTH    8
  53 
  54 /* access derived from scan result (VS_STATUS_XXX) and file attributes */
  55 #define VS_ACCESS_UNDEFINED     0
  56 #define VS_ACCESS_ALLOW         1       /* return 0 */
  57 #define VS_ACCESS_DENY          2       /* return EACCES */
  58 
  59 #define tolower(C)      (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
  60 #define offsetof(s, m)  (size_t)(&(((s *)0)->m))
  61 
  62 /* global variables - tunable via /etc/system */
  63 uint32_t vs_reqs_max = VS_REQS_DEFAULT; /* max scan requests */
  64 uint32_t vs_nodes_max = VS_NODES_DEFAULT; /* max in-progress scan requests */
  65 uint32_t vs_workers = VS_WORKERS_DEFAULT; /* max workers send reqs to vscand */
  66 uint32_t vs_scan_wait = VS_SCANWAIT_DEFAULT; /* secs to wait for scan result */
  67 
  68 
  69 /*
  70  * vscan_svc_state
  71  *
  72  *   +-----------------+
  73  *   | VS_SVC_UNCONFIG |
  74  *   +-----------------+
  75  *      |           ^
  76  *      | svc_init  | svc_fini
  77  *      v           |
  78  *   +-----------------+
  79  *   | VS_SVC_IDLE     |<----|
  80  *   +-----------------+         |
  81  *      |                    |
  82  *      | svc_enable         |
  83  *      |<----------------|  |
  84  *      v                 |  |
  85  *   +-----------------+  |  |
  86  *   | VS_SVC_ENABLED  |--|  |
  87  *   +-----------------+     |
  88  *      |                    |
  89  *      | svc_disable        | handler thread exit,
  90  *      v                    | all requests complete
  91  *   +-----------------+         |
  92  *   | VS_SVC_DISABLED |-----|
  93  *   +-----------------+
  94  *
  95  * svc_enable may occur when we are already in the ENABLED
  96  * state if vscand has exited without clean shutdown and
  97  * then reconnected within the delayed disable time period
  98  * (vs_reconnect_timeout) - see vscan_drv
  99  */
 100 
 101 typedef enum {
 102         VS_SVC_UNCONFIG,
 103         VS_SVC_IDLE,
 104         VS_SVC_ENABLED, /* service enabled and registered */
 105         VS_SVC_DISABLED /* service disabled and nunregistered */
 106 } vscan_svc_state_t;
 107 static vscan_svc_state_t vscan_svc_state = VS_SVC_UNCONFIG;
 108 
 109 
 110 /*
 111  * vscan_svc_req_state
 112  *
 113  * When a scan request is received from the file system it is
 114  * identified in or inserted into the vscan_svc_reql (INIT).
 115  * If the request is asynchronous 0 is then returned to the caller.
 116  * If the request is synchronous the req's refcnt is incremented
 117  * and the caller waits for the request to complete.
 118  * The refcnt is also incremented when the request is inserted
 119  * in vscan_svc_nodes, and decremented on scan_complete.
 120  *
 121  * vscan_svc_handler processes requests from the request list,
 122  * inserting them into vscan_svc_nodes and the task queue (QUEUED).
 123  * When the task queue call back (vscan_svc_do_scan) is invoked
 124  * the request transitions to IN_PROGRESS state. If the request
 125  * is sucessfully sent to vscand (door_call) and the door response
 126  * is SCANNING then the scan result will be received asynchronously.
 127  * Although unusual, it is possible that the async response is
 128  * received before the door call returns (hence the ASYNC_COMPLETE
 129  * state).
 130  * When the result has been determined / received,
 131  * vscan_svc_scan_complete is invoked to transition the request to
 132  * COMPLETE state, decrement refcnt and signal all waiting callers.
 133  * When the last waiting caller has processed the result (refcnt == 0)
 134  * the request is removed from vscan_svc_reql and vscan_svc_nodes
 135  * and deleted.
 136  *
 137  *      |                                                     ^
 138  *      | reql_insert                                         | refcnt == 0
 139  *      v                                                     | (delete)
 140  *   +------------------------+                   +---------------------+
 141  *   | VS_SVC_REQ_INIT        | -----DISABLE----> | VS_SVC_REQ_COMPLETE |
 142  *   +------------------------+                   +---------------------+
 143  *      |                                                     ^
 144  *      | insert_req, tq_dispatch                             |
 145  *      v                                                     |
 146  *   +------------------------+                               |
 147  *   | VS_SVC_REQ_QUEUED      |                           scan_complete
 148  *   +------------------------+                               |
 149  *      |                                                     |
 150  *      | tq_callback (do_scan)                               |
 151  *      |                                                     |
 152  *      v                        scan not req'd, error,       |
 153  *   +------------------------+  or door_result != SCANNING   |
 154  *   | VS_SVC_REQ_IN_PROGRESS |----------------->-------------|
 155  *   +------------------------+                               |
 156  *       |         |                                          |
 157  *       |         | door_result == SCANNING                  |
 158  *       |         v                                          |
 159  *       |     +---------------------------+    async result  |
 160  *       |     | VS_SVC_REQ_SCANNING       |-------->---------|
 161  *       |     +---------------------------+                  |
 162  *       |                                                    |
 163  *       | async result                                       |
 164  *       v                                                    |
 165  *    +---------------------------+      door_result = SCANNING   |
 166  *    | VS_SVC_REQ_ASYNC_COMPLETE |-------->------------------|
 167  *    +---------------------------+
 168  */
 169 typedef enum {
 170         VS_SVC_REQ_INIT,
 171         VS_SVC_REQ_QUEUED,
 172         VS_SVC_REQ_IN_PROGRESS,
 173         VS_SVC_REQ_SCANNING,
 174         VS_SVC_REQ_ASYNC_COMPLETE,
 175         VS_SVC_REQ_COMPLETE
 176 } vscan_svc_req_state_t;
 177 
 178 
 179 /*
 180  * vscan_svc_reql - the list of pending and in-progress scan requests
 181  */
 182 typedef struct vscan_req {
 183         uint32_t vsr_magic;     /* VS_REQ_MAGIC */
 184         list_node_t vsr_lnode;
 185         vnode_t *vsr_vp;
 186         uint32_t vsr_idx;       /* vscan_svc_nodes index */
 187         uint32_t vsr_seqnum;    /* unigue request id */
 188         uint32_t vsr_refcnt;
 189         kcondvar_t vsr_cv;
 190         vscan_svc_req_state_t vsr_state;
 191 } vscan_req_t;
 192 
 193 static list_t vscan_svc_reql;
 194 
 195 
 196 /*
 197  * vscan_svc_nodes - table of files being scanned
 198  *
 199  * The index into this table is passed in the door call to
 200  * vscand. vscand uses the idx to determine which minor node
 201  * to open to read the file data. Within the kernel driver
 202  * the minor device number can thus be used to identify the
 203  * table index to get the appropriate vnode.
 204  *
 205  * Instance 0 is reserved for the daemon/driver control
 206  * interface: enable/configure/disable
 207  */
 208 typedef struct vscan_svc_node {
 209         vscan_req_t *vsn_req;
 210         uint8_t vsn_quarantined;
 211         uint8_t vsn_modified;
 212         uint64_t vsn_size;
 213         timestruc_t vsn_mtime;
 214         vs_scanstamp_t vsn_scanstamp;
 215         uint32_t vsn_result;
 216         uint32_t vsn_access;
 217 } vscan_svc_node_t;
 218 
 219 static vscan_svc_node_t *vscan_svc_nodes;
 220 static int vscan_svc_nodes_sz;
 221 
 222 
 223 /* vscan_svc_taskq - queue of requests waiting to be sent to vscand */
 224 static taskq_t *vscan_svc_taskq = NULL;
 225 
 226 /* counts of entries in vscan_svc_reql, vscan_svc_nodes & vscan_svc_taskq */
 227 typedef struct {
 228         uint32_t vsc_reql;
 229         uint32_t vsc_node;
 230         uint32_t vsc_tq;
 231 } vscan_svc_counts_t;
 232 static vscan_svc_counts_t vscan_svc_counts;
 233 
 234 /*
 235  * vscan_svc_mutex protects the data pertaining to scan requests:
 236  * request list - vscan_svc_reql
 237  * node table - vscan_svc_nodes
 238  */
 239 static kmutex_t vscan_svc_mutex;
 240 
 241 /* unique request id for vscand request/response correlation */
 242 static uint32_t vscan_svc_seqnum = 0;
 243 
 244 /*
 245  * vscan_svc_cfg_mutex protects the configuration data:
 246  * vscan_svc_config, vscan_svc_types
 247  */
 248 static kmutex_t vscan_svc_cfg_mutex;
 249 
 250 /* configuration data - for virus scan exemption */
 251 static vs_config_t vscan_svc_config;
 252 static char *vscan_svc_types[VS_TYPES_MAX];
 253 
 254 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
 255 static kthread_t *vscan_svc_reql_thread;
 256 static kcondvar_t vscan_svc_reql_cv;
 257 static vscan_req_t *vscan_svc_reql_next; /* next pending scan request */
 258 
 259 /* local functions */
 260 int vscan_svc_scan_file(vnode_t *, cred_t *, int);
 261 static void vscan_svc_taskq_callback(void *);
 262 static int vscan_svc_exempt_file(vnode_t *, boolean_t *);
 263 static int vscan_svc_exempt_filetype(char *);
 264 static int vscan_svc_match_ext(char *, char *, int);
 265 static void vscan_svc_do_scan(vscan_req_t *);
 266 static vs_scan_req_t *vscan_svc_populate_req(int);
 267 static void vscan_svc_process_scan_result(int);
 268 static void vscan_svc_scan_complete(vscan_req_t *);
 269 static void vscan_svc_delete_req(vscan_req_t *);
 270 static int vscan_svc_insert_req(vscan_req_t *);
 271 static void vscan_svc_remove_req(int);
 272 static vscan_req_t *vscan_svc_reql_find(vnode_t *);
 273 static vscan_req_t *vscan_svc_reql_insert(vnode_t *);
 274 static void vscan_svc_reql_remove(vscan_req_t *);
 275 
 276 static int vscan_svc_getattr(int);
 277 static int vscan_svc_setattr(int, int);
 278 
 279 /* thread to insert reql entries into vscan_svc_nodes & vscan_svc_taskq */
 280 static void vscan_svc_reql_handler(void);
 281 
 282 
 283 /*
 284  * vscan_svc_init
 285  */
 286 int
 287 vscan_svc_init()
 288 {
 289         if (vscan_svc_state != VS_SVC_UNCONFIG) {
 290                 DTRACE_PROBE1(vscan__svc__state__violation,
 291                     int, vscan_svc_state);
 292                 return (-1);
 293         }
 294 
 295         mutex_init(&vscan_svc_mutex, NULL, MUTEX_DEFAULT, NULL);
 296         mutex_init(&vscan_svc_cfg_mutex, NULL, MUTEX_DEFAULT, NULL);
 297         cv_init(&vscan_svc_reql_cv, NULL, CV_DEFAULT, NULL);
 298 
 299         vscan_svc_nodes_sz = sizeof (vscan_svc_node_t) * (vs_nodes_max + 1);
 300         vscan_svc_nodes = kmem_zalloc(vscan_svc_nodes_sz, KM_SLEEP);
 301 
 302         vscan_svc_counts.vsc_reql = 0;
 303         vscan_svc_counts.vsc_node = 0;
 304         vscan_svc_counts.vsc_tq = 0;
 305 
 306         vscan_svc_state = VS_SVC_IDLE;
 307 
 308         return (0);
 309 }
 310 
 311 
 312 /*
 313  * vscan_svc_fini
 314  */
 315 void
 316 vscan_svc_fini()
 317 {
 318         if (vscan_svc_state != VS_SVC_IDLE) {
 319                 DTRACE_PROBE1(vscan__svc__state__violation,
 320                     int, vscan_svc_state);
 321                 return;
 322         }
 323 
 324         kmem_free(vscan_svc_nodes, vscan_svc_nodes_sz);
 325 
 326         cv_destroy(&vscan_svc_reql_cv);
 327         mutex_destroy(&vscan_svc_mutex);
 328         mutex_destroy(&vscan_svc_cfg_mutex);
 329         vscan_svc_state = VS_SVC_UNCONFIG;
 330 }
 331 
 332 
 333 /*
 334  * vscan_svc_enable
 335  */
 336 int
 337 vscan_svc_enable(void)
 338 {
 339         mutex_enter(&vscan_svc_mutex);
 340 
 341         switch (vscan_svc_state) {
 342         case VS_SVC_ENABLED:
 343                 /*
 344                  * it's possible (and okay) for vscan_svc_enable to be
 345                  * called when already enabled if vscand reconnects
 346                  * during a delayed disable
 347                  */
 348                 break;
 349         case VS_SVC_IDLE:
 350                 list_create(&vscan_svc_reql, sizeof (vscan_req_t),
 351                     offsetof(vscan_req_t, vsr_lnode));
 352                 vscan_svc_reql_next = list_head(&vscan_svc_reql);
 353 
 354                 vscan_svc_taskq = taskq_create("vscan_taskq", vs_workers,
 355                     MINCLSYSPRI, 1, INT_MAX, TASKQ_DYNAMIC);
 356                 ASSERT(vscan_svc_taskq != NULL);
 357 
 358                 vscan_svc_reql_thread = thread_create(NULL, 0,
 359                     vscan_svc_reql_handler, 0, 0, &p0, TS_RUN, MINCLSYSPRI);
 360                 ASSERT(vscan_svc_reql_thread != NULL);
 361 
 362                 /* ready to start processing requests */
 363                 vscan_svc_state = VS_SVC_ENABLED;
 364                 fs_vscan_register(vscan_svc_scan_file);
 365                 break;
 366         default:
 367                 DTRACE_PROBE1(vscan__svc__state__violation,
 368                     int, vscan_svc_state);
 369                 return (-1);
 370         }
 371 
 372         mutex_exit(&vscan_svc_mutex);
 373         return (0);
 374 }
 375 
 376 
 377 /*
 378  * vscan_svc_disable
 379  *
 380  * Resources allocated during vscan_svc_enable are free'd by
 381  * the handler thread immediately prior to exiting
 382  */
 383 void
 384 vscan_svc_disable(void)
 385 {
 386         mutex_enter(&vscan_svc_mutex);
 387 
 388         switch (vscan_svc_state) {
 389         case VS_SVC_ENABLED:
 390                 fs_vscan_register(NULL);
 391                 vscan_svc_state = VS_SVC_DISABLED;
 392                 cv_signal(&vscan_svc_reql_cv); /* wake handler thread */
 393                 break;
 394         default:
 395                 DTRACE_PROBE1(vscan__svc__state__violation, int,
 396                     vscan_svc_state);
 397         }
 398 
 399         mutex_exit(&vscan_svc_mutex);
 400 }
 401 
 402 
 403 /*
 404  * vscan_svc_in_use
 405  */
 406 boolean_t
 407 vscan_svc_in_use()
 408 {
 409         boolean_t in_use;
 410 
 411         mutex_enter(&vscan_svc_mutex);
 412 
 413         switch (vscan_svc_state) {
 414         case VS_SVC_IDLE:
 415         case VS_SVC_UNCONFIG:
 416                 in_use = B_FALSE;
 417                 break;
 418         default:
 419                 in_use = B_TRUE;
 420                 break;
 421         }
 422 
 423         mutex_exit(&vscan_svc_mutex);
 424         return (in_use);
 425 }
 426 
 427 
 428 /*
 429  * vscan_svc_get_vnode
 430  *
 431  * Get the file vnode indexed by idx.
 432  */
 433 vnode_t *
 434 vscan_svc_get_vnode(int idx)
 435 {
 436         vnode_t *vp = NULL;
 437 
 438         ASSERT(idx > 0);
 439         ASSERT(idx <= vs_nodes_max);
 440 
 441         mutex_enter(&vscan_svc_mutex);
 442         if (vscan_svc_nodes[idx].vsn_req)
 443                 vp = vscan_svc_nodes[idx].vsn_req->vsr_vp;
 444         mutex_exit(&vscan_svc_mutex);
 445 
 446         return (vp);
 447 }
 448 
 449 
 450 /*
 451  * vscan_svc_scan_file
 452  *
 453  * This function is the entry point for the file system to
 454  * request that a file be virus scanned.
 455  */
 456 int
 457 vscan_svc_scan_file(vnode_t *vp, cred_t *cr, int async)
 458 {
 459         int access;
 460         vscan_req_t *req;
 461         boolean_t allow;
 462         clock_t timeout, time_left;
 463 
 464         if ((vp == NULL) || (vp->v_path == NULL) || cr == NULL)
 465                 return (0);
 466 
 467         DTRACE_PROBE2(vscan__scan__file, char *, vp->v_path, int, async);
 468 
 469         /* check if size or type exempts file from scanning */
 470         if (vscan_svc_exempt_file(vp, &allow)) {
 471                 if ((allow == B_TRUE) || (async != 0))
 472                         return (0);
 473 
 474                 return (EACCES);
 475         }
 476 
 477         mutex_enter(&vscan_svc_mutex);
 478 
 479         if (vscan_svc_state != VS_SVC_ENABLED) {
 480                 DTRACE_PROBE1(vscan__svc__state__violation,
 481                     int, vscan_svc_state);
 482                 mutex_exit(&vscan_svc_mutex);
 483                 return (0);
 484         }
 485 
 486         /* insert (or find) request in list */
 487         if ((req = vscan_svc_reql_insert(vp)) == NULL) {
 488                 mutex_exit(&vscan_svc_mutex);
 489                 cmn_err(CE_WARN, "Virus scan request list full");
 490                 return ((async != 0) ? 0 : EACCES);
 491         }
 492 
 493         /* asynchronous request: return 0 */
 494         if (async) {
 495                 mutex_exit(&vscan_svc_mutex);
 496                 return (0);
 497         }
 498 
 499         /* synchronous scan request: wait for result */
 500         ++(req->vsr_refcnt);
 501         time_left = SEC_TO_TICK(vs_scan_wait);
 502         while ((time_left > 0) && (req->vsr_state != VS_SVC_REQ_COMPLETE)) {
 503                 timeout = time_left;
 504                 time_left = cv_reltimedwait_sig(&(req->vsr_cv),
 505                     &vscan_svc_mutex, timeout, TR_CLOCK_TICK);
 506         }
 507 
 508         if (time_left == -1) {
 509                 cmn_err(CE_WARN, "Virus scan request timeout %s (%d) \n",
 510                     vp->v_path, req->vsr_seqnum);
 511                 DTRACE_PROBE1(vscan__scan__timeout, vscan_req_t *, req);
 512         }
 513 
 514         ASSERT(req->vsr_magic == VS_REQ_MAGIC);
 515         if (vscan_svc_state == VS_SVC_DISABLED)
 516                 access = VS_ACCESS_ALLOW;
 517         else if (req->vsr_idx == 0)
 518                 access = VS_ACCESS_DENY;
 519         else
 520                 access = vscan_svc_nodes[req->vsr_idx].vsn_access;
 521 
 522         if ((--req->vsr_refcnt) == 0)
 523                 vscan_svc_delete_req(req);
 524 
 525         mutex_exit(&vscan_svc_mutex);
 526         return ((access == VS_ACCESS_ALLOW) ? 0 : EACCES);
 527 }
 528 
 529 
 530 /*
 531  * vscan_svc_reql_handler
 532  *
 533  * inserts scan requests (from vscan_svc_reql) into
 534  * vscan_svc_nodes and vscan_svc_taskq
 535  */
 536 static void
 537 vscan_svc_reql_handler(void)
 538 {
 539         vscan_req_t *req, *next;
 540 
 541         for (;;) {
 542                 mutex_enter(&vscan_svc_mutex);
 543 
 544                 if ((vscan_svc_state == VS_SVC_DISABLED) &&
 545                     (vscan_svc_counts.vsc_reql == 0)) {
 546                         /* free resources allocated durining enable */
 547                         taskq_destroy(vscan_svc_taskq);
 548                         vscan_svc_taskq = NULL;
 549                         list_destroy(&vscan_svc_reql);
 550                         vscan_svc_state = VS_SVC_IDLE;
 551                         mutex_exit(&vscan_svc_mutex);
 552                         return;
 553                 }
 554 
 555                 /*
 556                  * If disabled, scan_complete any pending requests.
 557                  * Otherwise insert pending requests into vscan_svc_nodes
 558                  * and vscan_svc_taskq. If no slots are available in
 559                  * vscan_svc_nodes break loop and wait for one
 560                  */
 561                 req = vscan_svc_reql_next;
 562 
 563                 while (req != NULL) {
 564                         ASSERT(req->vsr_magic == VS_REQ_MAGIC);
 565                         next = list_next(&vscan_svc_reql, req);
 566 
 567                         if (vscan_svc_state == VS_SVC_DISABLED) {
 568                                 vscan_svc_scan_complete(req);
 569                         } else {
 570                                 /* insert request into vscan_svc_nodes */
 571                                 if (vscan_svc_insert_req(req) == -1)
 572                                         break;
 573 
 574                                 /* add the scan request into the taskq */
 575                                 (void) taskq_dispatch(vscan_svc_taskq,
 576                                     vscan_svc_taskq_callback,
 577                                     (void *)req, TQ_SLEEP);
 578                                 ++(vscan_svc_counts.vsc_tq);
 579 
 580                                 req->vsr_state = VS_SVC_REQ_QUEUED;
 581                         }
 582                         req = next;
 583                 }
 584 
 585                 vscan_svc_reql_next = req;
 586 
 587                 DTRACE_PROBE2(vscan__req__counts, char *, "handler wait",
 588                     vscan_svc_counts_t *, &vscan_svc_counts);
 589 
 590                 (void) cv_reltimedwait(&vscan_svc_reql_cv, &vscan_svc_mutex,
 591                     SEC_TO_TICK(VS_REQL_HANDLER_TIMEOUT), TR_CLOCK_TICK);
 592 
 593                 DTRACE_PROBE2(vscan__req__counts, char *, "handler wake",
 594                     vscan_svc_counts_t *, &vscan_svc_counts);
 595 
 596                 mutex_exit(&vscan_svc_mutex);
 597         }
 598 }
 599 
 600 
 601 static void
 602 vscan_svc_taskq_callback(void *data)
 603 {
 604         vscan_req_t *req;
 605 
 606         mutex_enter(&vscan_svc_mutex);
 607 
 608         req = (vscan_req_t *)data;
 609         ASSERT(req->vsr_magic == VS_REQ_MAGIC);
 610         vscan_svc_do_scan(req);
 611         if (req->vsr_state != VS_SVC_REQ_SCANNING)
 612                 vscan_svc_scan_complete(req);
 613 
 614         --(vscan_svc_counts.vsc_tq);
 615         mutex_exit(&vscan_svc_mutex);
 616 }
 617 
 618 
 619 /*
 620  * vscan_svc_do_scan
 621  *
 622  * Note: To avoid potential deadlock it is important that
 623  * vscan_svc_mutex is not held during the call to
 624  * vscan_drv_create_note. vscan_drv_create_note enters
 625  * the vscan_drv_mutex and it is possible that a thread
 626  * holding that mutex could be waiting for vscan_svc_mutex.
 627  */
 628 static void
 629 vscan_svc_do_scan(vscan_req_t *req)
 630 {
 631         int idx, result;
 632         vscan_svc_node_t *node;
 633         vs_scan_req_t *door_req;
 634 
 635         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 636 
 637         idx = req->vsr_idx;
 638         node = &vscan_svc_nodes[idx];
 639 
 640         req->vsr_state = VS_SVC_REQ_IN_PROGRESS;
 641 
 642         /* if vscan not enabled (shutting down), allow ACCESS */
 643         if (vscan_svc_state != VS_SVC_ENABLED) {
 644                 node->vsn_access = VS_ACCESS_ALLOW;
 645                 return;
 646         }
 647 
 648         if (vscan_svc_getattr(idx) != 0) {
 649                 cmn_err(CE_WARN, "Can't access xattr for %s\n",
 650                     req->vsr_vp->v_path);
 651                 node->vsn_access = VS_ACCESS_DENY;
 652                 return;
 653         }
 654 
 655         /* valid scan_req ptr guaranteed */
 656         door_req = vscan_svc_populate_req(idx);
 657 
 658         /* free up mutex around create node and door call */
 659         mutex_exit(&vscan_svc_mutex);
 660         if (vscan_drv_create_node(idx) != B_TRUE)
 661                 result = VS_STATUS_ERROR;
 662         else
 663                 result = vscan_door_scan_file(door_req);
 664         kmem_free(door_req, sizeof (vs_scan_req_t));
 665         mutex_enter(&vscan_svc_mutex);
 666 
 667         if (result != VS_STATUS_SCANNING) {
 668                 vscan_svc_nodes[idx].vsn_result = result;
 669                 vscan_svc_process_scan_result(idx);
 670         } else { /* async response */
 671                 if (req->vsr_state == VS_SVC_REQ_IN_PROGRESS)
 672                         req->vsr_state = VS_SVC_REQ_SCANNING;
 673         }
 674 }
 675 
 676 
 677 /*
 678  * vscan_svc_populate_req
 679  *
 680  * Allocate a scan request to be sent to vscand, populating it
 681  * from the data in vscan_svc_nodes[idx].
 682  *
 683  * Returns: scan request object
 684  */
 685 static vs_scan_req_t *
 686 vscan_svc_populate_req(int idx)
 687 {
 688         vs_scan_req_t *scan_req;
 689         vscan_req_t *req;
 690         vscan_svc_node_t *node;
 691 
 692         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 693 
 694         node = &vscan_svc_nodes[idx];
 695         req = node->vsn_req;
 696         scan_req = kmem_zalloc(sizeof (vs_scan_req_t), KM_SLEEP);
 697 
 698         scan_req->vsr_idx = idx;
 699         scan_req->vsr_seqnum = req->vsr_seqnum;
 700         (void) strncpy(scan_req->vsr_path, req->vsr_vp->v_path, MAXPATHLEN);
 701         scan_req->vsr_size = node->vsn_size;
 702         scan_req->vsr_modified = node->vsn_modified;
 703         scan_req->vsr_quarantined = node->vsn_quarantined;
 704         scan_req->vsr_flags = 0;
 705         (void) strncpy(scan_req->vsr_scanstamp,
 706             node->vsn_scanstamp, sizeof (vs_scanstamp_t));
 707 
 708         return (scan_req);
 709 }
 710 
 711 
 712 /*
 713  * vscan_svc_scan_complete
 714  */
 715 static void
 716 vscan_svc_scan_complete(vscan_req_t *req)
 717 {
 718         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 719         ASSERT(req != NULL);
 720 
 721         req->vsr_state = VS_SVC_REQ_COMPLETE;
 722 
 723         if ((--req->vsr_refcnt) == 0)
 724                 vscan_svc_delete_req(req);
 725         else
 726                 cv_broadcast(&(req->vsr_cv));
 727 }
 728 
 729 
 730 /*
 731  * vscan_svc_delete_req
 732  */
 733 static void
 734 vscan_svc_delete_req(vscan_req_t *req)
 735 {
 736         int idx;
 737 
 738         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 739         ASSERT(req != NULL);
 740         ASSERT(req->vsr_refcnt == 0);
 741         ASSERT(req->vsr_state == VS_SVC_REQ_COMPLETE);
 742 
 743         if ((idx = req->vsr_idx) != 0)
 744                 vscan_svc_remove_req(idx);
 745 
 746         vscan_svc_reql_remove(req);
 747 
 748         cv_signal(&vscan_svc_reql_cv);
 749 }
 750 
 751 
 752 /*
 753  * vscan_svc_scan_result
 754  *
 755  * Invoked from vscan_drv.c on receipt of an ioctl containing
 756  * an async scan result (VS_DRV_IOCTL_RESULT)
 757  * If the vsr_seqnum in the response does not match that in the
 758  * vscan_svc_nodes entry the result is discarded.
 759  */
 760 void
 761 vscan_svc_scan_result(vs_scan_rsp_t *scan_rsp)
 762 {
 763         vscan_req_t *req;
 764         vscan_svc_node_t *node;
 765 
 766         mutex_enter(&vscan_svc_mutex);
 767 
 768         node = &vscan_svc_nodes[scan_rsp->vsr_idx];
 769 
 770         if ((req = node->vsn_req) == NULL) {
 771                 mutex_exit(&vscan_svc_mutex);
 772                 return;
 773         }
 774 
 775         ASSERT(req->vsr_magic == VS_REQ_MAGIC);
 776 
 777         if (scan_rsp->vsr_seqnum != req->vsr_seqnum) {
 778                 mutex_exit(&vscan_svc_mutex);
 779                 return;
 780         }
 781 
 782         node->vsn_result = scan_rsp->vsr_result;
 783         (void) strncpy(node->vsn_scanstamp,
 784             scan_rsp->vsr_scanstamp, sizeof (vs_scanstamp_t));
 785 
 786         vscan_svc_process_scan_result(scan_rsp->vsr_idx);
 787 
 788         if (node->vsn_req->vsr_state == VS_SVC_REQ_SCANNING)
 789                 vscan_svc_scan_complete(node->vsn_req);
 790         else
 791                 node->vsn_req->vsr_state = VS_SVC_REQ_ASYNC_COMPLETE;
 792 
 793         mutex_exit(&vscan_svc_mutex);
 794 }
 795 
 796 
 797 /*
 798  * vscan_svc_scan_abort
 799  *
 800  * Abort in-progress scan requests.
 801  */
 802 void
 803 vscan_svc_scan_abort()
 804 {
 805         int idx;
 806         vscan_req_t *req;
 807 
 808         mutex_enter(&vscan_svc_mutex);
 809 
 810         for (idx = 1; idx <= vs_nodes_max; idx++) {
 811                 if ((req = vscan_svc_nodes[idx].vsn_req) == NULL)
 812                         continue;
 813 
 814                 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
 815 
 816                 if (req->vsr_state == VS_SVC_REQ_SCANNING) {
 817                         DTRACE_PROBE1(vscan__abort, vscan_req_t *, req);
 818                         vscan_svc_process_scan_result(idx);
 819                         vscan_svc_scan_complete(req);
 820                 }
 821         }
 822 
 823         mutex_exit(&vscan_svc_mutex);
 824 }
 825 
 826 
 827 /*
 828  * vscan_svc_process_scan_result
 829  *
 830  * Sets vsn_access and updates file attributes based on vsn_result,
 831  * as follows:
 832  *
 833  * VS_STATUS_INFECTED
 834  *  deny access, set quarantine attribute, clear scanstamp
 835  * VS_STATUS_CLEAN
 836  *  allow access, set scanstamp,
 837  *  if file not modified since scan initiated, clear modified attribute
 838  * VS_STATUS_NO_SCAN
 839  *  deny access if file quarantined, otherwise allow access
 840  * VS_STATUS_UNDEFINED, VS_STATUS_ERROR
 841  *  deny access if file quarantined, modified or no scanstamp
 842  *  otherwise, allow access
 843  */
 844 static void
 845 vscan_svc_process_scan_result(int idx)
 846 {
 847         struct vattr attr;
 848         vnode_t *vp;
 849         timestruc_t *mtime;
 850         vscan_svc_node_t *node;
 851 
 852         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 853 
 854         node = &vscan_svc_nodes[idx];
 855 
 856         switch (node->vsn_result) {
 857         case VS_STATUS_INFECTED:
 858                 node->vsn_access = VS_ACCESS_DENY;
 859                 node->vsn_quarantined = 1;
 860                 node->vsn_scanstamp[0] = '\0';
 861                 (void) vscan_svc_setattr(idx,
 862                     XAT_AV_QUARANTINED | XAT_AV_SCANSTAMP);
 863                 break;
 864 
 865         case VS_STATUS_CLEAN:
 866                 node->vsn_access = VS_ACCESS_ALLOW;
 867 
 868                 /* if mtime has changed, don't clear the modified attribute */
 869                 vp = node->vsn_req->vsr_vp;
 870                 mtime = &(node->vsn_mtime);
 871                 attr.va_mask = AT_MTIME;
 872                 if ((VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) ||
 873                     (mtime->tv_sec != attr.va_mtime.tv_sec) ||
 874                     (mtime->tv_nsec != attr.va_mtime.tv_nsec)) {
 875                         DTRACE_PROBE1(vscan__mtime__changed, vscan_svc_node_t *,
 876                             node);
 877                         (void) vscan_svc_setattr(idx, XAT_AV_SCANSTAMP);
 878                         break;
 879                 }
 880 
 881                 node->vsn_modified = 0;
 882                 (void) vscan_svc_setattr(idx,
 883                     XAT_AV_SCANSTAMP | XAT_AV_MODIFIED);
 884                 break;
 885 
 886         case VS_STATUS_NO_SCAN:
 887                 if (node->vsn_quarantined)
 888                         node->vsn_access = VS_ACCESS_DENY;
 889                 else
 890                         node->vsn_access = VS_ACCESS_ALLOW;
 891                 break;
 892 
 893         case VS_STATUS_ERROR:
 894         case VS_STATUS_UNDEFINED:
 895         default:
 896                 if ((node->vsn_quarantined) ||
 897                     (node->vsn_modified) ||
 898                     (node->vsn_scanstamp[0] == '\0'))
 899                         node->vsn_access = VS_ACCESS_DENY;
 900                 else
 901                         node->vsn_access = VS_ACCESS_ALLOW;
 902                 break;
 903         }
 904 
 905         DTRACE_PROBE4(vscan__result,
 906             int, idx, int, node->vsn_req->vsr_seqnum,
 907             int, node->vsn_result, int, node->vsn_access);
 908 }
 909 
 910 
 911 /*
 912  * vscan_svc_getattr
 913  *
 914  * Get the vscan related system attributes, AT_SIZE & AT_MTIME.
 915  */
 916 static int
 917 vscan_svc_getattr(int idx)
 918 {
 919         xvattr_t xvattr;
 920         xoptattr_t *xoap = NULL;
 921         vnode_t *vp;
 922         vscan_svc_node_t *node;
 923 
 924         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 925 
 926         node = &vscan_svc_nodes[idx];
 927         if ((vp = node->vsn_req->vsr_vp) == NULL)
 928                 return (-1);
 929 
 930         /* get the attributes */
 931         xva_init(&xvattr); /* sets AT_XVATTR */
 932 
 933         xvattr.xva_vattr.va_mask |= AT_SIZE;
 934         xvattr.xva_vattr.va_mask |= AT_MTIME;
 935         XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
 936         XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
 937         XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
 938 
 939         if (VOP_GETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
 940                 return (-1);
 941 
 942         if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
 943                 cmn_err(CE_NOTE, "Virus scan request failed; "
 944                     "file system does not support virus scanning");
 945                 return (-1);
 946         }
 947 
 948         node->vsn_size = xvattr.xva_vattr.va_size;
 949         node->vsn_mtime.tv_sec = xvattr.xva_vattr.va_mtime.tv_sec;
 950         node->vsn_mtime.tv_nsec = xvattr.xva_vattr.va_mtime.tv_nsec;
 951 
 952         if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED) == 0)
 953                 return (-1);
 954         node->vsn_modified = xoap->xoa_av_modified;
 955 
 956         if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED) == 0)
 957                 return (-1);
 958         node->vsn_quarantined = xoap->xoa_av_quarantined;
 959 
 960         if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP) != 0) {
 961                 (void) memcpy(node->vsn_scanstamp,
 962                     xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
 963         }
 964 
 965         DTRACE_PROBE1(vscan__getattr, vscan_svc_node_t *, node);
 966         return (0);
 967 }
 968 
 969 
 970 /*
 971  * vscan_svc_setattr
 972  *
 973  * Set the vscan related system attributes.
 974  */
 975 static int
 976 vscan_svc_setattr(int idx, int which)
 977 {
 978         xvattr_t xvattr;
 979         xoptattr_t *xoap = NULL;
 980         vnode_t *vp;
 981         int len;
 982         vscan_svc_node_t *node;
 983 
 984         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
 985 
 986         node = &vscan_svc_nodes[idx];
 987         if ((vp = node->vsn_req->vsr_vp) == NULL)
 988                 return (-1);
 989 
 990         /* update the attributes */
 991         xva_init(&xvattr); /* sets AT_XVATTR */
 992         if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
 993                 return (-1);
 994 
 995         if (which & XAT_AV_MODIFIED) {
 996                 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
 997                 xoap->xoa_av_modified = node->vsn_modified;
 998         }
 999 
1000         if (which & XAT_AV_QUARANTINED) {
1001                 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
1002                 xoap->xoa_av_quarantined = node->vsn_quarantined;
1003         }
1004 
1005         if (which & XAT_AV_SCANSTAMP) {
1006                 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
1007                 len = strlen(node->vsn_scanstamp);
1008                 (void) memcpy(xoap->xoa_av_scanstamp,
1009                     node->vsn_scanstamp, len);
1010         }
1011 
1012         /* if access is denied, set mtime to invalidate client cache */
1013         if (node->vsn_access != VS_ACCESS_ALLOW) {
1014                 xvattr.xva_vattr.va_mask |= AT_MTIME;
1015                 gethrestime(&xvattr.xva_vattr.va_mtime);
1016         }
1017 
1018         if (VOP_SETATTR(vp, (vattr_t *)&xvattr, 0, kcred, NULL) != 0)
1019                 return (-1);
1020 
1021         DTRACE_PROBE2(vscan__setattr,
1022             vscan_svc_node_t *, node, int, which);
1023 
1024         return (0);
1025 }
1026 
1027 
1028 /*
1029  * vscan_svc_configure
1030  *
1031  * store configuration in vscan_svc_config
1032  * set up vscan_svc_types array of pointers into
1033  * vscan_svc_config.vsc_types for efficient searching
1034  */
1035 int
1036 vscan_svc_configure(vs_config_t *conf)
1037 {
1038         int count = 0;
1039         char *p, *beg, *end;
1040 
1041         mutex_enter(&vscan_svc_cfg_mutex);
1042 
1043         vscan_svc_config = *conf;
1044 
1045         (void) memset(vscan_svc_types, 0, sizeof (vscan_svc_types));
1046 
1047         beg = vscan_svc_config.vsc_types;
1048         end = beg + vscan_svc_config.vsc_types_len;
1049 
1050         for (p = beg; p < end; p += strlen(p) + 1) {
1051                 if (count >= VS_TYPES_MAX) {
1052                         mutex_exit(&vscan_svc_mutex);
1053                         return (-1);
1054                 }
1055 
1056                 vscan_svc_types[count] = p;
1057                 ++count;
1058         }
1059 
1060         mutex_exit(&vscan_svc_cfg_mutex);
1061         return (0);
1062 }
1063 
1064 
1065 /*
1066  * vscan_svc_exempt_file
1067  *
1068  * check if a file's size or type exempts it from virus scanning
1069  *
1070  * If the file is exempt from virus scanning, allow will be set
1071  * to define whether files access should be allowed (B_TRUE) or
1072  * denied (B_FALSE)
1073  *
1074  * Returns: 1 exempt
1075  *          0 scan required
1076  */
1077 static int
1078 vscan_svc_exempt_file(vnode_t *vp, boolean_t *allow)
1079 {
1080         struct vattr attr;
1081 
1082         ASSERT(vp != NULL);
1083         ASSERT(vp->v_path != NULL);
1084 
1085         attr.va_mask = AT_SIZE;
1086 
1087         if (VOP_GETATTR(vp, &attr, 0, kcred, NULL) != 0) {
1088                 *allow = B_FALSE;
1089                 return (0);
1090         }
1091 
1092         mutex_enter(&vscan_svc_cfg_mutex);
1093 
1094         if (attr.va_size > vscan_svc_config.vsc_max_size) {
1095                 DTRACE_PROBE2(vscan__exempt__filesize, char *,
1096                     vp->v_path, int, *allow);
1097 
1098                 *allow = (vscan_svc_config.vsc_allow) ? B_TRUE : B_FALSE;
1099                 mutex_exit(&vscan_svc_cfg_mutex);
1100                 return (1);
1101         }
1102 
1103         if (vscan_svc_exempt_filetype(vp->v_path)) {
1104                 DTRACE_PROBE1(vscan__exempt__filetype, char *, vp->v_path);
1105                 *allow = B_TRUE;
1106                 mutex_exit(&vscan_svc_cfg_mutex);
1107                 return (1);
1108         }
1109 
1110         mutex_exit(&vscan_svc_cfg_mutex);
1111         return (0);
1112 }
1113 
1114 
1115 /*
1116  * vscan_svc_exempt_filetype
1117  *
1118  * Each entry in vscan_svc_types includes a rule indicator (+,-)
1119  * followed by the match string for file types to which the rule
1120  * applies. Look for first match of file type in vscan_svc_types
1121  * and return 1 (exempt) if the indicator is '-', and 0 (not exempt)
1122  * if the indicator is '+'.
1123  * If vscan_svc_match_ext fails, or no match is found, return 0
1124  * (not exempt)
1125  *
1126  * Returns 1: exempt, 0: not exempt
1127  */
1128 static int
1129 vscan_svc_exempt_filetype(char *filepath)
1130 {
1131         int i, rc, exempt = 0;
1132         char *filename, *ext;
1133 
1134         ASSERT(MUTEX_HELD(&vscan_svc_cfg_mutex));
1135 
1136         if ((filename = strrchr(filepath, '/')) == 0)
1137                 filename = filepath;
1138         else
1139                 filename++;
1140 
1141         if ((ext = strrchr(filename, '.')) == NULL)
1142                 ext = "";
1143         else
1144                 ext++;
1145 
1146         for (i = 0; i < VS_TYPES_MAX; i ++) {
1147                 if (vscan_svc_types[i] == 0)
1148                         break;
1149 
1150                 rc = vscan_svc_match_ext(vscan_svc_types[i] + 1, ext, 1);
1151                 if (rc == -1)
1152                         break;
1153                 if (rc > 0) {
1154                         DTRACE_PROBE2(vscan__type__match, char *, ext,
1155                             char *, vscan_svc_types[i]);
1156                         exempt = (vscan_svc_types[i][0] == '-');
1157                         break;
1158                 }
1159         }
1160 
1161         return (exempt);
1162 }
1163 
1164 
1165 /*
1166  *  vscan_svc_match_ext
1167  *
1168  * Performs a case-insensitive match for two strings.  The first string
1169  * argument can contain the wildcard characters '?' and '*'
1170  *
1171  * Returns: 0 no match
1172  *          1 match
1173  *         -1 recursion error
1174  */
1175 static int
1176 vscan_svc_match_ext(char *patn, char *str, int depth)
1177 {
1178         int c1, c2;
1179         if (depth > VS_EXT_RECURSE_DEPTH)
1180                 return (-1);
1181 
1182         for (;;) {
1183                 switch (*patn) {
1184                 case 0:
1185                         return (*str == 0);
1186 
1187                 case '?':
1188                         if (*str != 0) {
1189                                 str++;
1190                                 patn++;
1191                                 continue;
1192                         }
1193                         return (0);
1194 
1195                 case '*':
1196                         patn++;
1197                         if (*patn == 0)
1198                                 return (1);
1199 
1200                         while (*str) {
1201                                 if (vscan_svc_match_ext(patn, str, depth + 1))
1202                                         return (1);
1203                                 str++;
1204                         }
1205                         return (0);
1206 
1207                 default:
1208                         if (*str != *patn) {
1209                                 c1 = *str;
1210                                 c2 = *patn;
1211 
1212                                 c1 = tolower(c1);
1213                                 c2 = tolower(c2);
1214                                 if (c1 != c2)
1215                                         return (0);
1216                         }
1217                         str++;
1218                         patn++;
1219                         continue;
1220                 }
1221         }
1222         /* NOT REACHED */
1223 }
1224 
1225 
1226 /*
1227  * vscan_svc_insert_req
1228  *
1229  * Insert request in next available available slot in vscan_svc_nodes
1230  *
1231  * Returns: idx of slot, or -1 if no slot available
1232  */
1233 static int
1234 vscan_svc_insert_req(vscan_req_t *req)
1235 {
1236         int idx;
1237         vscan_svc_node_t *node;
1238 
1239         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1240 
1241         if (vscan_svc_counts.vsc_node == vs_nodes_max)
1242                 return (-1);
1243 
1244         for (idx = 1; idx <= vs_nodes_max; idx++) {
1245                 if (vscan_svc_nodes[idx].vsn_req == NULL) {
1246                         req->vsr_idx = idx;
1247 
1248                         node = &vscan_svc_nodes[idx];
1249                         (void) memset(node, 0, sizeof (vscan_svc_node_t));
1250                         node->vsn_req = req;
1251                         node->vsn_modified = 1;
1252                         node->vsn_result = VS_STATUS_UNDEFINED;
1253                         node->vsn_access = VS_ACCESS_UNDEFINED;
1254 
1255                         ++(vscan_svc_counts.vsc_node);
1256                         return (idx);
1257                 }
1258         }
1259 
1260         return (-1);
1261 }
1262 
1263 
1264 /*
1265  * vscan_svc_remove_req
1266  */
1267 static void
1268 vscan_svc_remove_req(int idx)
1269 {
1270         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1271 
1272         if (idx != 0) {
1273                 (void) memset(&vscan_svc_nodes[idx], 0,
1274                     sizeof (vscan_svc_node_t));
1275                 --(vscan_svc_counts.vsc_node);
1276         }
1277 }
1278 
1279 
1280 /*
1281  * vscan_svc_reql_find
1282  */
1283 static vscan_req_t *
1284 vscan_svc_reql_find(vnode_t *vp)
1285 {
1286         vscan_req_t *req;
1287         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1288 
1289         req = list_head(&vscan_svc_reql);
1290 
1291         while (req != NULL) {
1292                 ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1293                 if ((req->vsr_vp == vp) &&
1294                     (req->vsr_state != VS_SVC_REQ_COMPLETE))
1295                         break;
1296 
1297                 req = list_next(&vscan_svc_reql, req);
1298         }
1299 
1300         return (req);
1301 }
1302 
1303 
1304 /*
1305  * vscan_svc_reql_insert
1306  */
1307 static vscan_req_t *
1308 vscan_svc_reql_insert(vnode_t *vp)
1309 {
1310         vscan_req_t *req;
1311 
1312         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1313 
1314         /* if request already in list then return it */
1315         if ((req = vscan_svc_reql_find(vp)) != NULL)
1316                 return (req);
1317 
1318         /* if list is full return NULL */
1319         if (vscan_svc_counts.vsc_reql == vs_reqs_max)
1320                 return (NULL);
1321 
1322         /* create a new request and insert into list */
1323         VN_HOLD(vp);
1324 
1325         req = kmem_zalloc(sizeof (vscan_req_t), KM_SLEEP);
1326 
1327         req->vsr_magic = VS_REQ_MAGIC;
1328         if (vscan_svc_seqnum == UINT32_MAX)
1329                 vscan_svc_seqnum = 0;
1330         req->vsr_seqnum = ++vscan_svc_seqnum;
1331         req->vsr_vp = vp;
1332         req->vsr_refcnt = 1; /* decremented in vscan_svc_scan_complete */
1333         req->vsr_state = VS_SVC_REQ_INIT;
1334         cv_init(&(req->vsr_cv), NULL, CV_DEFAULT, NULL);
1335 
1336         list_insert_tail(&vscan_svc_reql, req);
1337         if (vscan_svc_reql_next == NULL)
1338                 vscan_svc_reql_next = req;
1339 
1340         ++(vscan_svc_counts.vsc_reql);
1341 
1342         /* wake reql handler thread */
1343         cv_signal(&vscan_svc_reql_cv);
1344 
1345         return (req);
1346 }
1347 
1348 
1349 /*
1350  * vscan_svc_reql_remove
1351  */
1352 static void
1353 vscan_svc_reql_remove(vscan_req_t *req)
1354 {
1355         ASSERT(MUTEX_HELD(&vscan_svc_mutex));
1356         ASSERT(req->vsr_magic == VS_REQ_MAGIC);
1357 
1358         if (vscan_svc_reql_next == req)
1359                 vscan_svc_reql_next = list_next(&vscan_svc_reql, req);
1360 
1361         list_remove(&vscan_svc_reql, req);
1362         cv_destroy(&(req->vsr_cv));
1363         VN_RELE(req->vsr_vp);
1364 
1365         kmem_free(req, sizeof (vscan_req_t));
1366         --(vscan_svc_counts.vsc_reql);
1367 }