xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_notify.c (revision 37e2cd25d56b334a2403f2540a0b0a1e6a40bcd1)
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  */
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 	 * It's possible this ofile has started closing, in which case
205 	 * we must not subscribe it for events etc.
206 	 */
207 	if (of->f_state != SMB_OFILE_STATE_OPEN) {
208 		mutex_exit(&of->f_mutex);
209 		return (NT_STATUS_FILE_CLOSED);
210 	}
211 
212 	/*
213 	 * On the first FCN call with this ofile, subscribe to
214 	 * events on the node.  The corresponding unsubscribe
215 	 * happens in smb_ofile_delete().
216 	 */
217 	if (nc->nc_subscribed == B_FALSE) {
218 		nc->nc_subscribed = B_TRUE;
219 		smb_node_fcn_subscribe(node);
220 		/* In case this happened before we subscribed. */
221 		if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
222 			nc->nc_events |= FILE_NOTIFY_CHANGE_EV_DELETE;
223 		}
224 		/*
225 		 * Windows only lets you set these on the first call,
226 		 * so we may as well do the same.
227 		 */
228 		nc->nc_buffer.max_bytes = buflen;
229 		nc->nc_filter = filter;
230 	}
231 	/*
232 	 * If we already have events, consume them.
233 	 */
234 	sr->raw_data.max_bytes = buflen;
235 	if (nc->nc_events != 0) {
236 		status = smb_notify_get_events(sr);
237 	} else {
238 		/* Caller will come back for act2 */
239 		status = NT_STATUS_PENDING;
240 	}
241 
242 	mutex_exit(&of->f_mutex);
243 
244 	/*
245 	 * See: About NT_STATUS_NOTIFY_ENUM_DIR (above)
246 	 */
247 	if (status == NT_STATUS_NOTIFY_ENUM_DIR &&
248 	    smb_notify_enum_dir_delay > 0)
249 		delay(MSEC_TO_TICK(smb_notify_enum_dir_delay));
250 
251 	return (status);
252 }
253 
254 /*
255  * smb_notify_act2()
256  *
257  * Prepare to wait for events after act1 found that none were pending.
258  * Assume the wait may be for a very long time.  (hours, days...)
259  * Special return STATUS_PENDING means the SR will later be
260  * scheduled again on a new worker thread, and this thread
261  * MUST NOT touch it any longer (return SDRC_SR_KEPT).
262  *
263  * See overall design notes, top of file.
264  */
265 uint32_t
266 smb_notify_act2(smb_request_t *sr)
267 {
268 	smb_ofile_t	*of;
269 	smb_notify_t	*nc;
270 	uint32_t	status;
271 
272 	/*
273 	 * Sanity checks.
274 	 */
275 	if ((of = sr->fid_ofile) == NULL)
276 		return (NT_STATUS_INVALID_HANDLE);
277 	nc = &of->f_notify;
278 
279 	mutex_enter(&of->f_mutex);
280 
281 	/*
282 	 * Prepare for a potentially long wait for events.
283 	 * Normally transition from ACTIVE to WAITING_FCN1.
284 	 *
285 	 * Note we hold both of->f_mutex, sr->sr_mutex here,
286 	 * taken in that order.
287 	 */
288 	mutex_enter(&sr->sr_mutex);
289 	switch (sr->sr_state) {
290 	case SMB_REQ_STATE_ACTIVE:
291 		/*
292 		 * This sr has no worker thread until smb_notify_act3
293 		 * or smb_notify_cancel (later, via taskq_dispatch).
294 		 */
295 		sr->sr_state = SMB_REQ_STATE_WAITING_FCN1;
296 		sr->cancel_method = smb_notify_cancel;
297 		sr->sr_worker = NULL;
298 		list_insert_tail(&nc->nc_waiters, sr);
299 		status = NT_STATUS_PENDING;
300 		break;
301 
302 	case SMB_REQ_STATE_CANCELLED:
303 		status = NT_STATUS_CANCELLED;
304 		break;
305 	default:
306 		status = NT_STATUS_INTERNAL_ERROR;
307 		break;
308 	}
309 	mutex_exit(&sr->sr_mutex);
310 
311 	/*
312 	 * In case we missed any events before setting
313 	 * state FCN1, schedule our own wakeup.
314 	 */
315 	if (status == NT_STATUS_PENDING && nc->nc_events != 0) {
316 		smb_notify_wakeup(sr);
317 	}
318 
319 	mutex_exit(&of->f_mutex);
320 
321 	/* Note: Never NT_STATUS_NOTIFY_ENUM_DIR here. */
322 	ASSERT(status != NT_STATUS_NOTIFY_ENUM_DIR);
323 
324 	return (status);
325 }
326 
327 /*
328  * smb_notify_act3()
329  *
330  * This runs via the 2nd taskq_dispatch call, after we've either
331  * seen a change notify event, or the request has been cancelled.
332  * Complete it here.  This returns to SMB1 or SMB2 code to send
333  * the response and free the request.
334  *
335  * See overall design notes, top of file.
336  */
337 uint32_t
338 smb_notify_act3(smb_request_t *sr)
339 {
340 	smb_ofile_t	*of;
341 	smb_notify_t	*nc;
342 	uint32_t	status;
343 
344 	of = sr->fid_ofile;
345 	ASSERT(of != NULL);
346 	nc = &of->f_notify;
347 
348 	mutex_enter(&of->f_mutex);
349 
350 	mutex_enter(&sr->sr_mutex);
351 	ASSERT3P(sr->sr_worker, ==, NULL);
352 	sr->sr_worker = curthread;
353 	sr->cancel_method = NULL;
354 
355 	list_remove(&nc->nc_waiters, sr);
356 
357 	switch (sr->sr_state) {
358 	case SMB_REQ_STATE_WAITING_FCN2:
359 		/*
360 		 * Got smb_notify_wakeup.
361 		 */
362 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
363 		status = 0;
364 		break;
365 
366 	case SMB_REQ_STATE_CANCEL_PENDING:
367 		/*
368 		 * Got smb_notify_cancel
369 		 */
370 		sr->sr_state = SMB_REQ_STATE_CANCELLED;
371 		status = NT_STATUS_CANCELLED;
372 		break;
373 	default:
374 		status = NT_STATUS_INTERNAL_ERROR;
375 		break;
376 	}
377 	mutex_exit(&sr->sr_mutex);
378 
379 	if (status == 0)
380 		status = smb_notify_get_events(sr);
381 
382 	mutex_exit(&of->f_mutex);
383 
384 	/*
385 	 * See: About NT_STATUS_NOTIFY_ENUM_DIR (above)
386 	 */
387 	if (status == NT_STATUS_NOTIFY_ENUM_DIR &&
388 	    smb_notify_enum_dir_delay > 0)
389 		delay(MSEC_TO_TICK(smb_notify_enum_dir_delay));
390 
391 	return (status);
392 }
393 
394 static uint32_t
395 smb_notify_get_events(smb_request_t *sr)
396 {
397 	smb_ofile_t	*of;
398 	smb_notify_t	*nc;
399 	uint32_t	status;
400 	int		len;
401 
402 	of = sr->fid_ofile;
403 	ASSERT(of != NULL);
404 	ASSERT(MUTEX_HELD(&of->f_mutex));
405 	nc = &of->f_notify;
406 
407 	DTRACE_PROBE2(notify__get__events,
408 	    smb_request_t, sr,
409 	    uint32_t, nc->nc_events);
410 
411 	/*
412 	 * Special events which override other events
413 	 */
414 	if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_CLOSED) {
415 		status = NT_STATUS_NOTIFY_CLEANUP;
416 		goto out;
417 	}
418 	if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_DELETE) {
419 		status = NT_STATUS_DELETE_PENDING;
420 		goto out;
421 	}
422 	if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_SUBDIR) {
423 		status = NT_STATUS_NOTIFY_ENUM_DIR;
424 		goto out;
425 	}
426 	if (nc->nc_events & FILE_NOTIFY_CHANGE_EV_OVERFLOW) {
427 		status = NT_STATUS_NOTIFY_ENUM_DIR;
428 		goto out;
429 	}
430 
431 	/*
432 	 * Normal events (FILE_NOTIFY_VALID_MASK)
433 	 *
434 	 * At this point there should be some, or else
435 	 * some sort of bug woke us up for nothing.
436 	 */
437 	if ((nc->nc_events & FILE_NOTIFY_VALID_MASK) == 0) {
438 		status = NT_STATUS_INTERNAL_ERROR;
439 		goto out;
440 	}
441 
442 	/*
443 	 * Many Windows clients call change notify with a
444 	 * zero-length buffer, expecting all events to be
445 	 * reported as _ENUM_DIR.  Testing max_bytes here
446 	 * because ROOM_FOR check below says "yes" if both
447 	 * max_bytes and the amount we ask for are zero.
448 	 */
449 	if (nc->nc_buffer.max_bytes <= 0) {
450 		status = NT_STATUS_NOTIFY_ENUM_DIR;
451 		goto out;
452 	}
453 
454 	/*
455 	 * Client gave us a non-zero output buffer, and
456 	 * there was no overflow event (checked above)
457 	 * so there should be some event data.
458 	 */
459 	if ((len = nc->nc_buffer.chain_offset) <= 0) {
460 		status = NT_STATUS_INTERNAL_ERROR;
461 		goto out;
462 	}
463 
464 	/*
465 	 * If the current SR has a smaller output buffer
466 	 * then what was setup by some previous notify,
467 	 * we could have more data than will fit.
468 	 */
469 	if (!MBC_ROOM_FOR(&sr->raw_data, len)) {
470 		/* Would overflow caller's buffer. */
471 		status = NT_STATUS_NOTIFY_ENUM_DIR;
472 		goto out;
473 	}
474 
475 	/*
476 	 * Copy the event data to sr->raw_data.  In the copy,
477 	 * zap the NextEntryOffset in the last entry, and
478 	 * trim any extra bytes at the tail.
479 	 */
480 	(void) smb_mbc_copy(&sr->raw_data, &nc->nc_buffer, 0, len);
481 	(void) smb_mbc_poke(&sr->raw_data, nc->nc_last_off, "l", 0);
482 	smb_mbuf_trim(sr->raw_data.chain, len);
483 	status = 0;
484 
485 out:
486 	/*
487 	 * If there are no other SRs waiting on this ofile,
488 	 * mark all events consumed, except for those that
489 	 * remain until the ofile is closed.  That means
490 	 * clear all bits EXCEPT: _EV_CLOSED, _EV_DELETE
491 	 *
492 	 * If there are other waiters (rare) all will get
493 	 * the currently pending events, and then the
494 	 * the last one out will clear the events.
495 	 */
496 	if (list_is_empty(&nc->nc_waiters)) {
497 		nc->nc_buffer.chain_offset = 0;
498 		nc->nc_events &= (FILE_NOTIFY_CHANGE_EV_CLOSED |
499 		    FILE_NOTIFY_CHANGE_EV_DELETE);
500 	}
501 
502 	return (status);
503 }
504 
505 /*
506  * Called by common code after a transition from
507  * state WAITING_FCN1 to state CANCEL_PENDING.
508  */
509 static void
510 smb_notify_cancel(smb_request_t *sr)
511 {
512 	ASSERT3U(sr->sr_state, ==, SMB_REQ_STATE_CANCEL_PENDING);
513 	smb_notify_dispatch2(sr);
514 }
515 
516 /*
517  * Called after ofile event delivery to take a waiting smb request
518  * from state FCN1 to state FCN2.  This may be called many times
519  * (as events are delivered) but it must (exactly once) schedule
520  * the taskq job to run smb_notify_act3().  Only the event that
521  * takes us from state FCN1 to FCN2 schedules the taskq job.
522  */
523 static void
524 smb_notify_wakeup(smb_request_t *sr)
525 {
526 	boolean_t do_disp = B_FALSE;
527 
528 	SMB_REQ_VALID(sr);
529 
530 	mutex_enter(&sr->sr_mutex);
531 	if (sr->sr_state == SMB_REQ_STATE_WAITING_FCN1) {
532 		sr->sr_state = SMB_REQ_STATE_WAITING_FCN2;
533 		do_disp = B_TRUE;
534 	}
535 	mutex_exit(&sr->sr_mutex);
536 
537 	if (do_disp) {
538 		smb_notify_dispatch2(sr);
539 	}
540 }
541 
542 /*
543  * smb_notify_dispatch2()
544  * Schedule a 2nd taskq call to finish up a change notify request;
545  * (smb_notify_act3) either completing it or cancelling it.
546  */
547 static void
548 smb_notify_dispatch2(smb_request_t *sr)
549 {
550 	void (*tq_func)(void *);
551 
552 	/*
553 	 * Both of these call smb_notify_act3(), returning
554 	 * to version-specific code to send the response.
555 	 */
556 	if (sr->session->dialect >= SMB_VERS_2_BASE)
557 		tq_func = smb2_change_notify_finish;
558 	else
559 		tq_func = smb_nt_transact_notify_finish;
560 
561 	(void) taskq_dispatch(sr->sr_server->sv_worker_pool,
562 	    tq_func, sr, TQ_SLEEP);
563 }
564 
565 
566 /*
567  * What completion filter (masks) apply to each of the
568  * FILE_ACTION_... events.
569  */
570 static const uint32_t
571 smb_notify_action_mask[] = {
572 	0,  /* not used */
573 
574 	/* FILE_ACTION_ADDED	 */
575 	FILE_NOTIFY_CHANGE_NAME |
576 	FILE_NOTIFY_CHANGE_LAST_WRITE,
577 
578 	/* FILE_ACTION_REMOVED	 */
579 	FILE_NOTIFY_CHANGE_NAME |
580 	FILE_NOTIFY_CHANGE_LAST_WRITE,
581 
582 	/* FILE_ACTION_MODIFIED	 */
583 	FILE_NOTIFY_CHANGE_ATTRIBUTES |
584 	FILE_NOTIFY_CHANGE_SIZE |
585 	FILE_NOTIFY_CHANGE_LAST_WRITE |
586 	FILE_NOTIFY_CHANGE_LAST_ACCESS |
587 	FILE_NOTIFY_CHANGE_CREATION |
588 	FILE_NOTIFY_CHANGE_EA |
589 	FILE_NOTIFY_CHANGE_SECURITY,
590 
591 	/* FILE_ACTION_RENAMED_OLD_NAME */
592 	FILE_NOTIFY_CHANGE_NAME |
593 	FILE_NOTIFY_CHANGE_LAST_WRITE,
594 
595 	/* FILE_ACTION_RENAMED_NEW_NAME */
596 	FILE_NOTIFY_CHANGE_NAME |
597 	FILE_NOTIFY_CHANGE_LAST_WRITE,
598 
599 	/* FILE_ACTION_ADDED_STREAM */
600 	FILE_NOTIFY_CHANGE_STREAM_NAME,
601 
602 	/* FILE_ACTION_REMOVED_STREAM */
603 	FILE_NOTIFY_CHANGE_STREAM_NAME,
604 
605 	/* FILE_ACTION_MODIFIED_STREAM */
606 	FILE_NOTIFY_CHANGE_STREAM_SIZE |
607 	FILE_NOTIFY_CHANGE_STREAM_WRITE,
608 
609 	/* FILE_ACTION_SUBDIR_CHANGED */
610 	FILE_NOTIFY_CHANGE_EV_SUBDIR,
611 
612 	/* FILE_ACTION_DELETE_PENDING */
613 	FILE_NOTIFY_CHANGE_EV_DELETE,
614 
615 	/* FILE_ACTION_HANDLE_CLOSED */
616 	FILE_NOTIFY_CHANGE_EV_CLOSED,
617 };
618 static const int smb_notify_action_nelm =
619 	sizeof (smb_notify_action_mask) /
620 	sizeof (smb_notify_action_mask[0]);
621 
622 /*
623  * smb_notify_ofile
624  *
625  * Post an event to the change notify buffer for this ofile,
626  * subject to the mask that selects subscribed event types.
627  * If an SR is waiting for events and we've delivered some,
628  * wake the SR.
629  */
630 void
631 smb_notify_ofile(smb_ofile_t *of, uint_t action, const char *name)
632 {
633 	smb_notify_t	*nc;
634 	smb_request_t	*sr;
635 	uint32_t	filter, events;
636 
637 	SMB_OFILE_VALID(of);
638 
639 	mutex_enter(&of->f_mutex);
640 	nc = &of->f_notify;
641 
642 	/*
643 	 * Compute the filter & event bits for this action,
644 	 * which determine whether we'll post the event.
645 	 * Note: always sensitive to: delete, closed.
646 	 */
647 	filter = nc->nc_filter |
648 	    FILE_NOTIFY_CHANGE_EV_DELETE |
649 	    FILE_NOTIFY_CHANGE_EV_CLOSED;
650 	VERIFY(action < smb_notify_action_nelm);
651 	events = smb_notify_action_mask[action];
652 	if ((filter & events) == 0)
653 		goto unlock_out;
654 
655 	/*
656 	 * OK, we're going to post this event.
657 	 */
658 	switch (action) {
659 	case FILE_ACTION_ADDED:
660 	case FILE_ACTION_REMOVED:
661 	case FILE_ACTION_MODIFIED:
662 	case FILE_ACTION_RENAMED_OLD_NAME:
663 	case FILE_ACTION_RENAMED_NEW_NAME:
664 	case FILE_ACTION_ADDED_STREAM:
665 	case FILE_ACTION_REMOVED_STREAM:
666 	case FILE_ACTION_MODIFIED_STREAM:
667 		/*
668 		 * Append this event to the buffer.
669 		 * Also keep track of events seen.
670 		 */
671 		smb_notify_encode_action(of, action, name);
672 		nc->nc_events |= events;
673 		break;
674 
675 	case FILE_ACTION_SUBDIR_CHANGED:
676 	case FILE_ACTION_DELETE_PENDING:
677 	case FILE_ACTION_HANDLE_CLOSED:
678 		/*
679 		 * These are "internal" events, and therefore
680 		 * are not appended to the response buffer.
681 		 * Just record the event flags and wakeup.
682 		 */
683 		nc->nc_events |= events;
684 		break;
685 
686 	default:
687 		ASSERT(0);	/* bogus action */
688 		break;
689 	}
690 
691 	sr = list_head(&nc->nc_waiters);
692 	while (sr != NULL) {
693 		smb_notify_wakeup(sr);
694 		sr = list_next(&nc->nc_waiters, sr);
695 	}
696 
697 unlock_out:
698 	mutex_exit(&of->f_mutex);
699 }
700 
701 /*
702  * Encode a FILE_NOTIFY_INFORMATION struct.
703  */
704 static void
705 smb_notify_encode_action(smb_ofile_t *of,
706     uint32_t action, const char *fname)
707 {
708 	smb_notify_t *nc = &of->f_notify;
709 	mbuf_chain_t *mbc;
710 	uint32_t namelen, totlen;
711 
712 	ASSERT(nc != NULL);
713 	ASSERT(FILE_ACTION_ADDED <= action &&
714 	    action <= FILE_ACTION_MODIFIED_STREAM);
715 	ASSERT(fname != NULL);
716 	ASSERT(MUTEX_HELD(&of->f_mutex));
717 
718 	/* Once we've run out of room, stop trying to append. */
719 	if ((nc->nc_events & FILE_NOTIFY_CHANGE_EV_OVERFLOW) != 0)
720 		return;
721 
722 	if (fname == NULL)
723 		return;
724 	namelen = smb_wcequiv_strlen(fname);
725 	if (namelen == 0)
726 		return;
727 
728 	/*
729 	 * Layout is: 3 DWORDS, Unicode string, pad(4).
730 	 */
731 	mbc = &nc->nc_buffer;
732 	totlen = (12 + namelen + 3) & ~3;
733 	if (MBC_ROOM_FOR(mbc, totlen) == 0) {
734 		nc->nc_events |= FILE_NOTIFY_CHANGE_EV_OVERFLOW;
735 		return;
736 	}
737 
738 	/*
739 	 * Keep track of where this entry starts (nc_last_off)
740 	 * because after we put all entries, we need to zap
741 	 * the NextEntryOffset field in the last one.
742 	 */
743 	nc->nc_last_off = mbc->chain_offset;
744 
745 	/*
746 	 * Encode this entry, then 4-byte alignment padding.
747 	 *
748 	 * Note that smb_mbc_encodef with a "U" code puts a
749 	 * Unicode string with a null termination.  We don't
750 	 * want a null, but do want alignment padding.  We
751 	 * get that by encoding with "U.." at the end of the
752 	 * encoding string, which gets us two bytes for the
753 	 * Unicode NULL, and two more zeros for the "..".
754 	 * We then "back up" the chain_offset (finger) so it's
755 	 * correctly 4-byte aligned.  We will sometimes have
756 	 * written a couple more bytes than needed, but we'll
757 	 * just overwrite those with the next entry.  At the
758 	 * end, we trim the mbuf chain to the correct length.
759 	 */
760 	(void) smb_mbc_encodef(mbc, "lllU..",
761 	    totlen, /* NextEntryOffset */
762 	    action, namelen, fname);
763 	mbc->chain_offset = nc->nc_last_off + totlen;
764 }
765