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 } 756