xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_notify.c (revision 35ae87c4fef8c54d83986b5a4a531666f7efaebd)
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