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