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