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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * File Change Notification (FCN)
  29  * Common parts shared by SMB1 & SMB2
  30  */
  31 
  32 /*
  33  * This command notifies the client when the specified directory
  34  * has changed, and optionally returns the names of files and
  35  * directories that changed, and how they changed.  The caller
  36  * specifies a "Completion Filter" to select which kinds of
  37  * changes they want to know about.
  38  *
  39  * When a change that's in the CompletionFilter is made to the directory,
  40  * the command completes.  The names of the files that have changed since
  41  * the last time the command was issued are returned to the client.
  42  * If too many files have changed since the last time the command was
  43  * issued, then zero bytes are returned and an alternate status code
  44  * is returned in the Status field of the response.
  45  *
  46  * The CompletionFilter is a mask created as the sum of any of the
  47  * following flags:
  48  *
  49  * FILE_NOTIFY_CHANGE_FILE_NAME        0x00000001
  50  * FILE_NOTIFY_CHANGE_DIR_NAME         0x00000002
  51  * FILE_NOTIFY_CHANGE_NAME             0x00000003
  52  * FILE_NOTIFY_CHANGE_ATTRIBUTES       0x00000004
  53  * FILE_NOTIFY_CHANGE_SIZE             0x00000008
  54  * FILE_NOTIFY_CHANGE_LAST_WRITE       0x00000010
  55  * FILE_NOTIFY_CHANGE_LAST_ACCESS      0x00000020
  56  * FILE_NOTIFY_CHANGE_CREATION         0x00000040
  57  * FILE_NOTIFY_CHANGE_EA               0x00000080
  58  * FILE_NOTIFY_CHANGE_SECURITY         0x00000100
  59  * FILE_NOTIFY_CHANGE_STREAM_NAME      0x00000200
  60  * FILE_NOTIFY_CHANGE_STREAM_SIZE      0x00000400
  61  * FILE_NOTIFY_CHANGE_STREAM_WRITE     0x00000800
  62  *
  63  *
  64  * The response contains FILE_NOTIFY_INFORMATION structures, as defined
  65  * below.  The NextEntryOffset field of the structure specifies the offset,
  66  * in bytes, from the start of the current entry to the next entry in the
  67  * list.  If this is the last entry in the list, this field is zero.  Each
  68  * entry in the list must be longword aligned, so NextEntryOffset must be a
  69  * multiple of four.
  70  *
  71  * typedef struct {
  72  *     ULONG NextEntryOffset;
  73  *     ULONG Action;
  74  *     ULONG FileNameLength;
  75  *     WCHAR FileName[1];
  76  * } FILE_NOTIFY_INFORMATION;
  77  *
  78  * Where Action describes what happened to the file named FileName:
  79  *
  80  * FILE_ACTION_ADDED            0x00000001
  81  * FILE_ACTION_REMOVED          0x00000002
  82  * FILE_ACTION_MODIFIED         0x00000003
  83  * FILE_ACTION_RENAMED_OLD_NAME 0x00000004
  84  * FILE_ACTION_RENAMED_NEW_NAME 0x00000005
  85  * FILE_ACTION_ADDED_STREAM     0x00000006
  86  * FILE_ACTION_REMOVED_STREAM   0x00000007
  87  * FILE_ACTION_MODIFIED_STREAM  0x00000008
  88  *
  89  * The internal interface between SMB1 and/or SMB2 protocol handlers
  90  * and this module has some sophistication to allow for:
  91  * (1) code sharing between SMB1 and SMB2(+)
  92  * (2) efficient handling of non-blocking scenarios
  93  * (3) long blocking calls without tying up a thread
  94  *
  95  * The interface has three calls (like a three act play)
  96  *
  97  * smb_notify_act1:
  98  *      Validate parameters, setup ofile buffer.
  99  *      If data already available, return it, all done.
 100  *      (In the "all done" case, skip act2 & act3.)
 101  *      If no data available, return a special error
 102  *      ("STATUS_PENDING") to tell the caller they must
 103  *      proceed with calls to act2 & act3.
 104  *
 105  * smb_notify_act2:
 106  *      Arrange wakeup after event delivery or cancellation.
 107  *      Return leaving the SR with no worker thread.
 108  *
 109  * smb_notify_act3:
 110  *      New taskq work thread runs this after the wakeup
 111  *      or cancellation arranged in act2 happens.  This
 112  *      returns the notification data and retires the SR.
 113  *
 114  * In the SMB2 notify handler, we call act1 during the initial
 115  * synchronous handling of the request.  If that returns anything
 116  * other than STATUS_PENDING, that request is fully complete.
 117  * If act1 returns STATUS_PENDING, SMB2 calls act2 as it's
 118  * "go async" handler, which arranges to call act3 later.
 119  *
 120  * In the SMB1 notify handler there is not separate sync. & async
 121  * handler so act1 and (if necessary) act2 are both called during
 122  * the initial handling of the request.
 123  *
 124  * About notify event buffering:
 125  *
 126  * An important (and poorly documented) feature of SMB notify is
 127  * that once a notify call has happened on a given directory handle,
 128  * the system CONTINUES to post events to the notify event buffer
 129  * for the handle, even when SMB notify calls are NOT running.
 130  * When the client next comes back with a notify call, we return
 131  * any events that were posted while they were "away".  This is
 132  * how clients track directory changes without missing events.
 133  *
 134  * About simultaneous notify calls:
 135  *
 136  * Note that SMB "notify" calls are destructive to events, much like
 137  * reading data from a pipe.  It therefore makes little sense to
 138  * allow multiple simultaneous callers.  However, we permit it
 139  * (like Windows does) as follows:  When multiple notify calls
 140  * are waiting for events, the next event wakes them all, and
 141  * only the last one out clears the event buffer.  They all get
 142  * whatever events are pending at the time they woke up.
 143  *
 144  * About NT_STATUS_NOTIFY_ENUM_DIR
 145  *
 146  * One more caution about NT_STATUS_NOTIFY_ENUM_DIR:  Some clients
 147  * are stupid about re-reading the directory almost continuously when
 148  * there are changes happening in the directory.  We want to bound
 149  * the rate of such directory re-reading, so before returning an
 150  * NT_STATUS_NOTIFY_ENUM_DIR, we delay just a little.  The length
 151  * of the delay can be adjusted via smb_notify_enum_dir_delay,
 152  * though it's not expected that should need to be changed.
 153  */
 154 
 155 #include <smbsrv/smb_kproto.h>
 156 #include <sys/sdt.h>
 157 
 158 /*
 159  * Length of the short delay we impose before returning
 160  * NT_STATUS_NOTIFY_ENUM_DIR (See above)
 161  */
 162 int smb_notify_enum_dir_delay = 100; /* mSec. */
 163 
 164 static uint32_t smb_notify_get_events(smb_request_t *);
 165 static void smb_notify_cancel(smb_request_t *);
 166 static void smb_notify_wakeup(smb_request_t *);
 167 static void smb_notify_dispatch2(smb_request_t *);
 168 static void smb_notify_encode_action(smb_ofile_t *,
 169         uint32_t, const char *);
 170 
 171 
 172 /*
 173  * smb_notify_act1()
 174  *
 175  * Check for events and consume, non-blocking.
 176  * Special return STATUS_PENDING means:
 177  * No events; caller must call "act2" next.
 178  *
 179  * See overall design notes, top of file.
 180  */
 181 uint32_t
 182 smb_notify_act1(smb_request_t *sr, uint32_t buflen, uint32_t filter)
 183 {
 184         smb_ofile_t     *of;
 185         smb_node_t      *node;
 186         smb_notify_t    *nc;
 187         uint32_t        status;
 188 
 189         /*
 190          * Validate parameters
 191          */
 192         if ((of = sr->fid_ofile) == NULL)
 193                 return (NT_STATUS_INVALID_HANDLE);
 194         nc = &of->f_notify;
 195         node = of->f_node;
 196         if (node == NULL || !smb_node_is_dir(node)) {
 197                 /* Notify change is only valid on directories. */
 198                 return (NT_STATUS_INVALID_PARAMETER);
 199         }
 200 
 201         mutex_enter(&of->f_mutex);
 202 
 203         /*
 204          * On the first FCN call with this ofile, subscribe to
 205          * events on the node.  The corresponding unsubscribe
 206          * happens in smb_ofile_delete().
 207          */
 208         if (nc->nc_subscribed == B_FALSE) {
 209                 nc->nc_subscribed = B_TRUE;
 210                 smb_node_fcn_subscribe(node);
 211                 /* In case this happened before we subscribed. */
 212                 if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
 213                         nc->nc_events |= FILE_NOTIFY_CHANGE_EV_DELETE;
 214                 }
 215                 /*
 216                  * Windows only lets you set these on the first call,
 217                  * so we may as well do the same.
 218                  */
 219                 nc->nc_buffer.max_bytes = buflen;
 220                 nc->nc_filter = filter;
 221         }
 222         /*
 223          * If we already have events, consume them.
 224          */
 225         sr->raw_data.max_bytes = buflen;
 226         if (nc->nc_events != 0) {
 227                 status = smb_notify_get_events(sr);
 228         } else {
 229                 /* Caller will come back for act2 */
 230                 status = NT_STATUS_PENDING;
 231         }
 232 
 233         mutex_exit(&of->f_mutex);
 234 
 235         /*
 236          * See: About NT_STATUS_NOTIFY_ENUM_DIR (above)
 237          */
 238         if (status == NT_STATUS_NOTIFY_ENUM_DIR &&
 239             smb_notify_enum_dir_delay > 0)
 240                 delay(MSEC_TO_TICK(smb_notify_enum_dir_delay));
 241 
 242         return (status);
 243 }
 244 
 245 /*
 246  * smb_notify_act2()
 247  *
 248  * Prepare to wait for events after act1 found that none were pending.
 249  * Assume the wait may be for a very long time.  (hours, days...)
 250  * Special return STATUS_PENDING means the SR will later be
 251  * scheduled again on a new worker thread, and this thread
 252  * MUST NOT touch it any longer (return SDRC_SR_KEPT).
 253  *
 254  * See overall design notes, top of file.
 255  */
 256 uint32_t
 257 smb_notify_act2(smb_request_t *sr)
 258 {
 259         smb_ofile_t     *of;
 260         smb_notify_t    *nc;
 261         uint32_t        status;
 262 
 263         /*
 264          * Sanity checks.
 265          */
 266         if ((of = sr->fid_ofile) == NULL)
 267                 return (NT_STATUS_INVALID_HANDLE);
 268         nc = &of->f_notify;
 269 
 270         mutex_enter(&of->f_mutex);
 271 
 272         /*
 273          * Prepare for a potentially long wait for events.
 274          * Normally transition from ACTIVE to WAITING_FCN1.
 275          *
 276          * Note we hold both of->f_mutex, sr->sr_mutex here,
 277          * taken in that order.
 278          */
 279         mutex_enter(&sr->sr_mutex);
 280         switch (sr->sr_state) {
 281         case SMB_REQ_STATE_ACTIVE:
 282                 /*
 283                  * This sr has no worker thread until smb_notify_act3
 284                  * or smb_notify_cancel (later, via taskq_dispatch).
 285                  */
 286                 sr->sr_state = SMB_REQ_STATE_WAITING_FCN1;
 287                 sr->cancel_method = smb_notify_cancel;
 288                 sr->sr_worker = NULL;
 289                 list_insert_tail(&nc->nc_waiters, sr);
 290                 status = NT_STATUS_PENDING;
 291                 break;
 292 
 293         case SMB_REQ_STATE_CANCELLED:
 294                 status = NT_STATUS_CANCELLED;
 295                 break;
 296         default:
 297                 status = NT_STATUS_INTERNAL_ERROR;
 298                 break;
 299         }
 300         mutex_exit(&sr->sr_mutex);
 301 
 302         /*
 303          * In case we missed any events before setting
 304          * state FCN1, schedule our own wakeup.
 305          */
 306         if (status == NT_STATUS_PENDING && nc->nc_events != 0) {
 307                 smb_notify_wakeup(sr);
 308         }
 309 
 310         mutex_exit(&of->f_mutex);
 311 
 312         /* Note: Never NT_STATUS_NOTIFY_ENUM_DIR here. */
 313         ASSERT(status != NT_STATUS_NOTIFY_ENUM_DIR);
 314 
 315         return (status);
 316 }
 317 
 318 /*
 319  * smb_notify_act3()
 320  *
 321  * This runs via the 2nd taskq_dispatch call, after we've either
 322  * seen a change notify event, or the request has been cancelled.
 323  * Complete it here.  This returns to SMB1 or SMB2 code to send
 324  * the response and free the request.
 325  *
 326  * See overall design notes, top of file.
 327  */
 328 uint32_t
 329 smb_notify_act3(smb_request_t *sr)
 330 {
 331         smb_ofile_t     *of;
 332         smb_notify_t    *nc;
 333         uint32_t        status;
 334 
 335         of = sr->fid_ofile;
 336         ASSERT(of != NULL);
 337         nc = &of->f_notify;
 338 
 339         mutex_enter(&of->f_mutex);
 340 
 341         mutex_enter(&sr->sr_mutex);
 342         ASSERT3P(sr->sr_worker, ==, NULL);
 343         sr->sr_worker = curthread;
 344         sr->cancel_method = NULL;
 345 
 346         list_remove(&nc->nc_waiters, sr);
 347 
 348         switch (sr->sr_state) {
 349         case SMB_REQ_STATE_WAITING_FCN2:
 350                 /*
 351                  * Got smb_notify_wakeup.
 352                  */
 353                 sr->sr_state = SMB_REQ_STATE_ACTIVE;
 354                 status = 0;
 355                 break;
 356 
 357         case SMB_REQ_STATE_CANCEL_PENDING:
 358                 /*
 359                  * Got smb_notify_cancel
 360                  */
 361                 sr->sr_state = SMB_REQ_STATE_CANCELLED;
 362                 status = NT_STATUS_CANCELLED;
 363                 break;
 364         default:
 365                 status = NT_STATUS_INTERNAL_ERROR;
 366                 break;
 367         }
 368         mutex_exit(&sr->sr_mutex);
 369 
 370         if (status == 0)
 371                 status = smb_notify_get_events(sr);
 372 
 373         mutex_exit(&of->f_mutex);
 374 
 375         /*
 376          * See: About NT_STATUS_NOTIFY_ENUM_DIR (above)
 377          */
 378         if (status == NT_STATUS_NOTIFY_ENUM_DIR &&
 379             smb_notify_enum_dir_delay > 0)
 380                 delay(MSEC_TO_TICK(smb_notify_enum_dir_delay));
 381 
 382         return (status);
 383 }
 384 
 385 static uint32_t
 386 smb_notify_get_events(smb_request_t *sr)
 387 {
 388         smb_ofile_t     *of;
 389         smb_notify_t    *nc;
 390         uint32_t        status;
 391         int             len;
 392 
 393         of = sr->fid_ofile;
 394         ASSERT(of != NULL);
 395         ASSERT(MUTEX_HELD(&of->f_mutex));
 396         nc = &of->f_notify;
 397 
 398         DTRACE_PROBE2(notify__get__events,
 399             smb_request_t, sr,
 400             uint32_t, nc->nc_events);
 401 
 402         /*
 403          * Special events which override other events
 404          */
 405         if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_CLOSED) {
 406                 status = NT_STATUS_NOTIFY_CLEANUP;
 407                 goto out;
 408         }
 409         if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_DELETE) {
 410                 status = NT_STATUS_DELETE_PENDING;
 411                 goto out;
 412         }
 413         if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_SUBDIR) {
 414                 status = NT_STATUS_NOTIFY_ENUM_DIR;
 415                 goto out;
 416         }
 417         if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_OVERFLOW) {
 418                 status = NT_STATUS_NOTIFY_ENUM_DIR;
 419                 goto out;
 420         }
 421 
 422         /*
 423          * Normal events (FILE_NOTIFY_VALID_MASK)
 424          *
 425          * At this point there should be some, or else
 426          * some sort of bug woke us up for nothing.
 427          */
 428         if ((nc->nc_events & FILE_NOTIFY_VALID_MASK) == 0) {
 429                 status = NT_STATUS_INTERNAL_ERROR;
 430                 goto out;
 431         }
 432 
 433         /*
 434          * Many Windows clients call change notify with a
 435          * zero-length buffer, expecting all events to be
 436          * reported as _ENUM_DIR.  Testing max_bytes here
 437          * because ROOM_FOR check below says "yes" if both
 438          * max_bytes and the amount we ask for are zero.
 439          */
 440         if (nc->nc_buffer.max_bytes <= 0) {
 441                 status = NT_STATUS_NOTIFY_ENUM_DIR;
 442                 goto out;
 443         }
 444 
 445         /*
 446          * Client gave us a non-zero output buffer, and
 447          * there was no overflow event (checked above)
 448          * so there should be some event data.
 449          */
 450         if ((len = nc->nc_buffer.chain_offset) <= 0) {
 451                 status = NT_STATUS_INTERNAL_ERROR;
 452                 goto out;
 453         }
 454 
 455         /*
 456          * If the current SR has a smaller output buffer
 457          * then what was setup by some previous notify,
 458          * we could have more data than will fit.
 459          */
 460         if (!MBC_ROOM_FOR(&sr->raw_data, len)) {
 461                 /* Would overflow caller's buffer. */
 462                 status = NT_STATUS_NOTIFY_ENUM_DIR;
 463                 goto out;
 464         }
 465 
 466         /*
 467          * Copy the event data to sr->raw_data.  In the copy,
 468          * zap the NextEntryOffset in the last entry, and
 469          * trim any extra bytes at the tail.
 470          */
 471         (void) smb_mbc_copy(&sr->raw_data, &nc->nc_buffer, 0, len);
 472         (void) smb_mbc_poke(&sr->raw_data, nc->nc_last_off, "l", 0);
 473         smb_mbuf_trim(sr->raw_data.chain, len);
 474         status = 0;
 475 
 476 out:
 477         /*
 478          * If there are no other SRs waiting on this ofile,
 479          * mark all events consumed, except for those that
 480          * remain until the ofile is closed.  That means
 481          * clear all bits EXCEPT: _EV_CLOSED, _EV_DELETE
 482          *
 483          * If there are other waiters (rare) all will get
 484          * the currently pending events, and then the
 485          * the last one out will clear the events.
 486          */
 487         if (list_is_empty(&nc->nc_waiters)) {
 488                 nc->nc_buffer.chain_offset = 0;
 489                 nc->nc_events &= (FILE_NOTIFY_CHANGE_EV_CLOSED |
 490                     FILE_NOTIFY_CHANGE_EV_DELETE);
 491         }
 492 
 493         return (status);
 494 }
 495 
 496 /*
 497  * Called by common code after a transition from
 498  * state WAITING_FCN1 to state CANCEL_PENDING.
 499  */
 500 static void
 501 smb_notify_cancel(smb_request_t *sr)
 502 {
 503         ASSERT3U(sr->sr_state, ==, SMB_REQ_STATE_CANCEL_PENDING);
 504         smb_notify_dispatch2(sr);
 505 }
 506 
 507 /*
 508  * Called after ofile event delivery to take a waiting smb request
 509  * from state FCN1 to state FCN2.  This may be called many times
 510  * (as events are delivered) but it must (exactly once) schedule
 511  * the taskq job to run smb_notify_act3().  Only the event that
 512  * takes us from state FCN1 to FCN2 schedules the taskq job.
 513  */
 514 static void
 515 smb_notify_wakeup(smb_request_t *sr)
 516 {
 517         boolean_t do_disp = B_FALSE;
 518 
 519         SMB_REQ_VALID(sr);
 520 
 521         mutex_enter(&sr->sr_mutex);
 522         if (sr->sr_state == SMB_REQ_STATE_WAITING_FCN1) {
 523                 sr->sr_state = SMB_REQ_STATE_WAITING_FCN2;
 524                 do_disp = B_TRUE;
 525         }
 526         mutex_exit(&sr->sr_mutex);
 527 
 528         if (do_disp) {
 529                 smb_notify_dispatch2(sr);
 530         }
 531 }
 532 
 533 /*
 534  * smb_notify_dispatch2()
 535  * Schedule a 2nd taskq call to finish up a change notify request;
 536  * (smb_notify_act3) either completing it or cancelling it.
 537  */
 538 static void
 539 smb_notify_dispatch2(smb_request_t *sr)
 540 {
 541         void (*tq_func)(void *);
 542 
 543         /*
 544          * Both of these call smb_notify_act3(), returning
 545          * to version-specific code to send the response.
 546          */
 547         if (sr->session->dialect >= SMB_VERS_2_BASE)
 548                 tq_func = smb2_change_notify_finish;
 549         else
 550                 tq_func = smb_nt_transact_notify_finish;
 551 
 552         (void) taskq_dispatch(sr->sr_server->sv_worker_pool,
 553             tq_func, sr, TQ_SLEEP);
 554 }
 555 
 556 
 557 /*
 558  * What completion filter (masks) apply to each of the
 559  * FILE_ACTION_... events.
 560  */
 561 static const uint32_t
 562 smb_notify_action_mask[] = {
 563         0,  /* not used */
 564 
 565         /* FILE_ACTION_ADDED     */
 566         FILE_NOTIFY_CHANGE_NAME |
 567         FILE_NOTIFY_CHANGE_LAST_WRITE,
 568 
 569         /* FILE_ACTION_REMOVED   */
 570         FILE_NOTIFY_CHANGE_NAME |
 571         FILE_NOTIFY_CHANGE_LAST_WRITE,
 572 
 573         /* FILE_ACTION_MODIFIED  */
 574         FILE_NOTIFY_CHANGE_ATTRIBUTES |
 575         FILE_NOTIFY_CHANGE_SIZE |
 576         FILE_NOTIFY_CHANGE_LAST_WRITE |
 577         FILE_NOTIFY_CHANGE_LAST_ACCESS |
 578         FILE_NOTIFY_CHANGE_CREATION |
 579         FILE_NOTIFY_CHANGE_EA |
 580         FILE_NOTIFY_CHANGE_SECURITY,
 581 
 582         /* FILE_ACTION_RENAMED_OLD_NAME */
 583         FILE_NOTIFY_CHANGE_NAME |
 584         FILE_NOTIFY_CHANGE_LAST_WRITE,
 585 
 586         /* FILE_ACTION_RENAMED_NEW_NAME */
 587         FILE_NOTIFY_CHANGE_NAME |
 588         FILE_NOTIFY_CHANGE_LAST_WRITE,
 589 
 590         /* FILE_ACTION_ADDED_STREAM */
 591         FILE_NOTIFY_CHANGE_STREAM_NAME,
 592 
 593         /* FILE_ACTION_REMOVED_STREAM */
 594         FILE_NOTIFY_CHANGE_STREAM_NAME,
 595 
 596         /* FILE_ACTION_MODIFIED_STREAM */
 597         FILE_NOTIFY_CHANGE_STREAM_SIZE |
 598         FILE_NOTIFY_CHANGE_STREAM_WRITE,
 599 
 600         /* FILE_ACTION_SUBDIR_CHANGED */
 601         FILE_NOTIFY_CHANGE_EV_SUBDIR,
 602 
 603         /* FILE_ACTION_DELETE_PENDING */
 604         FILE_NOTIFY_CHANGE_EV_DELETE,
 605 
 606         /* FILE_ACTION_HANDLE_CLOSED */
 607         FILE_NOTIFY_CHANGE_EV_CLOSED,
 608 };
 609 static const int smb_notify_action_nelm =
 610         sizeof (smb_notify_action_mask) /
 611         sizeof (smb_notify_action_mask[0]);
 612 
 613 /*
 614  * smb_notify_ofile
 615  *
 616  * Post an event to the change notify buffer for this ofile,
 617  * subject to the mask that selects subscribed event types.
 618  * If an SR is waiting for events and we've delivered some,
 619  * wake the SR.
 620  */
 621 void
 622 smb_notify_ofile(smb_ofile_t *of, uint_t action, const char *name)
 623 {
 624         smb_notify_t    *nc;
 625         smb_request_t   *sr;
 626         uint32_t        filter, events;
 627 
 628         SMB_OFILE_VALID(of);
 629 
 630         mutex_enter(&of->f_mutex);
 631         nc = &of->f_notify;
 632 
 633         /*
 634          * Compute the filter & event bits for this action,
 635          * which determine whether we'll post the event.
 636          * Note: always sensitive to: delete, closed.
 637          */
 638         filter = nc->nc_filter |
 639             FILE_NOTIFY_CHANGE_EV_DELETE |
 640             FILE_NOTIFY_CHANGE_EV_CLOSED;
 641         VERIFY(action < smb_notify_action_nelm);
 642         events = smb_notify_action_mask[action];
 643         if ((filter & events) == 0)
 644                 goto unlock_out;
 645 
 646         /*
 647          * OK, we're going to post this event.
 648          */
 649         switch (action) {
 650         case FILE_ACTION_ADDED:
 651         case FILE_ACTION_REMOVED:
 652         case FILE_ACTION_MODIFIED:
 653         case FILE_ACTION_RENAMED_OLD_NAME:
 654         case FILE_ACTION_RENAMED_NEW_NAME:
 655         case FILE_ACTION_ADDED_STREAM:
 656         case FILE_ACTION_REMOVED_STREAM:
 657         case FILE_ACTION_MODIFIED_STREAM:
 658                 /*
 659                  * Append this event to the buffer.
 660                  * Also keep track of events seen.
 661                  */
 662                 smb_notify_encode_action(of, action, name);
 663                 nc->nc_events |= events;
 664                 break;
 665 
 666         case FILE_ACTION_SUBDIR_CHANGED:
 667         case FILE_ACTION_DELETE_PENDING:
 668         case FILE_ACTION_HANDLE_CLOSED:
 669                 /*
 670                  * These are "internal" events, and therefore
 671                  * are not appended to the response buffer.
 672                  * Just record the event flags and wakeup.
 673                  */
 674                 nc->nc_events |= events;
 675                 break;
 676 
 677         default:
 678                 ASSERT(0);      /* bogus action */
 679                 break;
 680         }
 681 
 682         sr = list_head(&nc->nc_waiters);
 683         while (sr != NULL) {
 684                 smb_notify_wakeup(sr);
 685                 sr = list_next(&nc->nc_waiters, sr);
 686         }
 687 
 688 unlock_out:
 689         mutex_exit(&of->f_mutex);
 690 }
 691 
 692 /*
 693  * Encode a FILE_NOTIFY_INFORMATION struct.
 694  */
 695 static void
 696 smb_notify_encode_action(smb_ofile_t *of,
 697     uint32_t action, const char *fname)
 698 {
 699         smb_notify_t *nc = &of->f_notify;
 700         mbuf_chain_t *mbc;
 701         uint32_t namelen, totlen;
 702 
 703         ASSERT(nc != NULL);
 704         ASSERT(FILE_ACTION_ADDED <= action &&
 705             action <= FILE_ACTION_MODIFIED_STREAM);
 706         ASSERT(fname != NULL);
 707         ASSERT(MUTEX_HELD(&of->f_mutex));
 708 
 709         /* Once we've run out of room, stop trying to append. */
 710         if ((nc->nc_events & FILE_NOTIFY_CHANGE_EV_OVERFLOW) != 0)
 711                 return;
 712 
 713         if (fname == NULL)
 714                 return;
 715         namelen = smb_wcequiv_strlen(fname);
 716         if (namelen == 0)
 717                 return;
 718 
 719         /*
 720          * Layout is: 3 DWORDS, Unicode string, pad(4).
 721          */
 722         mbc = &nc->nc_buffer;
 723         totlen = (12 + namelen + 3) & ~3;
 724         if (MBC_ROOM_FOR(mbc, totlen) == 0) {
 725                 nc->nc_events |= FILE_NOTIFY_CHANGE_EV_OVERFLOW;
 726                 return;
 727         }
 728 
 729         /*
 730          * Keep track of where this entry starts (nc_last_off)
 731          * because after we put all entries, we need to zap
 732          * the NextEntryOffset field in the last one.
 733          */
 734         nc->nc_last_off = mbc->chain_offset;
 735 
 736         /*
 737          * Encode this entry, then 4-byte alignment padding.
 738          *
 739          * Note that smb_mbc_encodef with a "U" code puts a
 740          * Unicode string with a null termination.  We don't
 741          * want a null, but do want alignment padding.  We
 742          * get that by encoding with "U.." at the end of the
 743          * encoding string, which gets us two bytes for the
 744          * Unicode NULL, and two more zeros for the "..".
 745          * We then "back up" the chain_offset (finger) so it's
 746          * correctly 4-byte aligned.  We will sometimes have
 747          * written a couple more bytes than needed, but we'll
 748          * just overwrite those with the next entry.  At the
 749          * end, we trim the mbuf chain to the correct length.
 750          */
 751         (void) smb_mbc_encodef(mbc, "lllU..",
 752             totlen, /* NextEntryOffset */
 753             action, namelen, fname);
 754         mbc->chain_offset = nc->nc_last_off + totlen;
 755 }