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