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
smb_notify_act1(smb_request_t * sr,uint32_t buflen,uint32_t filter)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
smb_notify_act2(smb_request_t * sr)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
smb_notify_act3(smb_request_t * sr)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
smb_notify_get_events(smb_request_t * sr)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
smb_notify_cancel(smb_request_t * sr)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
smb_notify_wakeup(smb_request_t * sr)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
smb_notify_dispatch2(smb_request_t * sr)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
smb_notify_ofile(smb_ofile_t * of,uint_t action,const char * name)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
smb_notify_encode_action(smb_ofile_t * of,uint32_t action,const char * fname)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