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