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 2015 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. 45 * 46 * The CompletionFilter is a mask created as the sum of any of the 47 * following flags: 48 * 49 * FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 50 * FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 51 * FILE_NOTIFY_CHANGE_NAME 0x00000003 52 * FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 53 * FILE_NOTIFY_CHANGE_SIZE 0x00000008 54 * FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 55 * FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 56 * FILE_NOTIFY_CHANGE_CREATION 0x00000040 57 * FILE_NOTIFY_CHANGE_EA 0x00000080 58 * FILE_NOTIFY_CHANGE_SECURITY 0x00000100 59 * FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 60 * FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 61 * FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 62 * 63 * 64 * The response contains FILE_NOTIFY_INFORMATION structures, as defined 65 * below. The NextEntryOffset field of the structure specifies the offset, 66 * in bytes, from the start of the current entry to the next entry in the 67 * list. If this is the last entry in the list, this field is zero. Each 68 * entry in the list must be longword aligned, so NextEntryOffset must be a 69 * multiple of four. 70 * 71 * typedef struct { 72 * ULONG NextEntryOffset; 73 * ULONG Action; 74 * ULONG FileNameLength; 75 * WCHAR FileName[1]; 76 * } FILE_NOTIFY_INFORMATION; 77 * 78 * Where Action describes what happened to the file named FileName: 79 * 80 * FILE_ACTION_ADDED 0x00000001 81 * FILE_ACTION_REMOVED 0x00000002 82 * FILE_ACTION_MODIFIED 0x00000003 83 * FILE_ACTION_RENAMED_OLD_NAME 0x00000004 84 * FILE_ACTION_RENAMED_NEW_NAME 0x00000005 85 * FILE_ACTION_ADDED_STREAM 0x00000006 86 * FILE_ACTION_REMOVED_STREAM 0x00000007 87 * FILE_ACTION_MODIFIED_STREAM 0x00000008 88 */ 89 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 /* 98 * Cancel method for smb_notify_common() 99 * 100 * This request is waiting in change notify. 101 */ 102 static void 103 smb_notify_cancel(smb_request_t *sr) 104 { 105 cv_signal(&sr->sr_ncr.nc_cv); 106 } 107 108 uint32_t 109 smb_notify_common(smb_request_t *sr, mbuf_chain_t *mbc, 110 uint32_t CompletionFilter) 111 { 112 smb_notify_change_req_t *nc; 113 smb_node_t *node; 114 uint32_t status; 115 116 if (sr->fid_ofile == NULL) 117 return (NT_STATUS_INVALID_HANDLE); 118 119 node = sr->fid_ofile->f_node; 120 if (node == NULL || !smb_node_is_dir(node)) { 121 /* 122 * Notify change is only valid on directories. 123 */ 124 return (NT_STATUS_INVALID_PARAMETER); 125 } 126 127 /* 128 * Prepare to receive event data. 129 */ 130 nc = &sr->sr_ncr; 131 nc->nc_flags = CompletionFilter; 132 ASSERT(nc->nc_action == 0); 133 ASSERT(nc->nc_fname == NULL); 134 nc->nc_fname = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 135 136 /* 137 * Subscribe to events on this node. 138 */ 139 smb_node_fcn_subscribe(node, sr); 140 141 /* 142 * Wait for subscribed events to arrive. 143 * Expect SMB_REQ_STATE_EVENT_OCCURRED 144 * or SMB_REQ_STATE_CANCELLED when signaled. 145 * Note it's possible (though rare) to already 146 * have SMB_REQ_STATE_CANCELLED here. 147 */ 148 mutex_enter(&sr->sr_mutex); 149 if (sr->sr_state == SMB_REQ_STATE_ACTIVE) { 150 sr->sr_state = SMB_REQ_STATE_WAITING_EVENT; 151 sr->cancel_method = smb_notify_cancel; 152 } 153 while (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT) { 154 cv_wait(&nc->nc_cv, &sr->sr_mutex); 155 } 156 sr->cancel_method = NULL; 157 158 switch (sr->sr_state) { 159 case SMB_REQ_STATE_WAITING_EVENT: 160 case SMB_REQ_STATE_EVENT_OCCURRED: 161 /* normal wakeup */ 162 sr->sr_state = SMB_REQ_STATE_ACTIVE; 163 status = 0; 164 break; 165 166 case SMB_REQ_STATE_CANCEL_PENDING: 167 /* cancelled via smb_notify_cancel */ 168 sr->sr_state = SMB_REQ_STATE_CANCELLED; 169 status = NT_STATUS_CANCELLED; 170 break; 171 172 case SMB_REQ_STATE_CANCELLED: 173 /* cancelled before this function ran */ 174 status = NT_STATUS_CANCELLED; 175 break; 176 177 default: 178 status = NT_STATUS_INTERNAL_ERROR; 179 break; 180 } 181 182 mutex_exit(&sr->sr_mutex); 183 184 /* 185 * Unsubscribe from events on this node. 186 */ 187 smb_node_fcn_unsubscribe(node, sr); 188 189 /* 190 * Why did we wake up? 191 */ 192 if (status != 0) 193 goto out; 194 195 /* 196 * We have SMB_REQ_STATE_ACTIVE. 197 * 198 * If we have event data, marshall it now, else just 199 * say "many things changed". Note that when we get 200 * action FILE_ACTION_SUBDIR_CHANGED, we don't have 201 * any event details and only know that some subdir 202 * changed, so just report "many things changed". 203 */ 204 switch (nc->nc_action) { 205 206 case FILE_ACTION_ADDED: 207 case FILE_ACTION_REMOVED: 208 case FILE_ACTION_MODIFIED: 209 case FILE_ACTION_RENAMED_OLD_NAME: 210 case FILE_ACTION_RENAMED_NEW_NAME: 211 case FILE_ACTION_ADDED_STREAM: 212 case FILE_ACTION_REMOVED_STREAM: 213 case FILE_ACTION_MODIFIED_STREAM: 214 /* 215 * Build the reply 216 */ 217 status = smb_notify_encode_action(sr, mbc, 218 nc->nc_action, nc->nc_fname); 219 break; 220 221 case FILE_ACTION_SUBDIR_CHANGED: 222 status = NT_STATUS_NOTIFY_ENUM_DIR; 223 break; 224 225 case FILE_ACTION_DELETE_PENDING: 226 status = NT_STATUS_DELETE_PENDING; 227 break; 228 229 default: 230 ASSERT(0); 231 status = NT_STATUS_INTERNAL_ERROR; 232 break; 233 } 234 235 out: 236 kmem_free(nc->nc_fname, MAXNAMELEN); 237 nc->nc_fname = NULL; 238 return (status); 239 } 240 241 /* 242 * Encode a FILE_NOTIFY_INFORMATION struct. 243 * 244 * We only ever put one of these in a response, so this 245 * does not bother handling appending additional ones. 246 */ 247 static uint32_t 248 smb_notify_encode_action(struct smb_request *sr, mbuf_chain_t *mbc, 249 uint32_t action, char *fname) 250 { 251 uint32_t namelen; 252 253 ASSERT(FILE_ACTION_ADDED <= action && 254 action <= FILE_ACTION_MODIFIED_STREAM); 255 256 if (fname == NULL) 257 return (NT_STATUS_INTERNAL_ERROR); 258 namelen = smb_wcequiv_strlen(fname); 259 if (namelen == 0) 260 return (NT_STATUS_INTERNAL_ERROR); 261 262 if (smb_mbc_encodef(mbc, "%lllU", sr, 263 0, /* NextEntryOffset */ 264 action, namelen, fname)) 265 return (NT_STATUS_NOTIFY_ENUM_DIR); 266 267 return (0); 268 } 269 270 /* 271 * smb_notify_file_closed 272 * 273 * Cancel any change-notify calls on this open file. 274 */ 275 void 276 smb_notify_file_closed(struct smb_ofile *of) 277 { 278 smb_session_t *ses; 279 smb_request_t *sr; 280 smb_slist_t *list; 281 282 SMB_OFILE_VALID(of); 283 ses = of->f_session; 284 SMB_SESSION_VALID(ses); 285 list = &ses->s_req_list; 286 287 smb_slist_enter(list); 288 289 sr = smb_slist_head(list); 290 while (sr) { 291 SMB_REQ_VALID(sr); 292 if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT && 293 sr->fid_ofile == of) { 294 smb_request_cancel(sr); 295 } 296 sr = smb_slist_next(list, sr); 297 } 298 299 smb_slist_exit(list); 300 } 301 302 303 /* 304 * smb_notify_event 305 * 306 * Post an event to the watchers on a given node. 307 * 308 * This makes one exception for RENAME, where we expect a 309 * pair of events for the {old,new} directory element names. 310 * This only delivers an event for the "new" name. 311 * 312 * The event delivery mechanism does not implement delivery of 313 * multiple events for one "NT Notify" call. One could do that, 314 * but modern clients don't actually use the event data. They 315 * set a max. received data size of zero, which means we discard 316 * the data and send the special "lots changed" error instead. 317 * Given that, there's not really any point in implementing the 318 * delivery of multiple events. In fact, we don't even need to 319 * implement single event delivery, but do so for completeness, 320 * for debug convenience, and to be nice to older clients that 321 * may actually want some event data instead of the error. 322 * 323 * Given that we only deliver a single event for an "NT Notify" 324 * caller, we want to deliver the "new" name event. (The "old" 325 * name event is less important, even ignored by some clients.) 326 * Since we know these are delivered in pairs, we can simply 327 * discard the "old" name event, knowing that the "new" name 328 * event will be delivered immediately afterwards. 329 * 330 * So, why do event sources post the "old name" event at all? 331 * (1) For debugging, so we see both {old,new} names here. 332 * (2) If in the future someone decides to implement the 333 * delivery of both {old,new} events, the changes can be 334 * mostly isolated to this file. 335 */ 336 void 337 smb_notify_event(smb_node_t *node, uint_t action, const char *name) 338 { 339 smb_request_t *sr; 340 smb_node_fcn_t *fcn; 341 342 SMB_NODE_VALID(node); 343 fcn = &node->n_fcn; 344 345 if (action == FILE_ACTION_RENAMED_OLD_NAME) 346 return; /* see above */ 347 348 mutex_enter(&fcn->fcn_mutex); 349 350 sr = list_head(&fcn->fcn_watchers); 351 while (sr) { 352 smb_notify_sr(sr, action, name); 353 sr = list_next(&fcn->fcn_watchers, sr); 354 } 355 356 mutex_exit(&fcn->fcn_mutex); 357 } 358 359 /* 360 * What completion filter (masks) apply to each of the 361 * FILE_ACTION_... events. 362 */ 363 static const uint32_t 364 smb_notify_action_mask[] = { 365 0, /* not used */ 366 367 /* FILE_ACTION_ADDED */ 368 FILE_NOTIFY_CHANGE_NAME | 369 FILE_NOTIFY_CHANGE_LAST_WRITE, 370 371 /* FILE_ACTION_REMOVED */ 372 FILE_NOTIFY_CHANGE_NAME | 373 FILE_NOTIFY_CHANGE_LAST_WRITE, 374 375 /* FILE_ACTION_MODIFIED */ 376 FILE_NOTIFY_CHANGE_ATTRIBUTES | 377 FILE_NOTIFY_CHANGE_SIZE | 378 FILE_NOTIFY_CHANGE_LAST_WRITE | 379 FILE_NOTIFY_CHANGE_LAST_ACCESS | 380 FILE_NOTIFY_CHANGE_CREATION | 381 FILE_NOTIFY_CHANGE_EA | 382 FILE_NOTIFY_CHANGE_SECURITY, 383 384 /* FILE_ACTION_RENAMED_OLD_NAME */ 385 FILE_NOTIFY_CHANGE_NAME | 386 FILE_NOTIFY_CHANGE_LAST_WRITE, 387 388 /* FILE_ACTION_RENAMED_NEW_NAME */ 389 FILE_NOTIFY_CHANGE_NAME | 390 FILE_NOTIFY_CHANGE_LAST_WRITE, 391 392 /* FILE_ACTION_ADDED_STREAM */ 393 FILE_NOTIFY_CHANGE_STREAM_NAME, 394 395 /* FILE_ACTION_REMOVED_STREAM */ 396 FILE_NOTIFY_CHANGE_STREAM_NAME, 397 398 /* FILE_ACTION_MODIFIED_STREAM */ 399 FILE_NOTIFY_CHANGE_STREAM_SIZE | 400 FILE_NOTIFY_CHANGE_STREAM_WRITE, 401 402 /* FILE_ACTION_SUBDIR_CHANGED */ 403 NODE_FLAGS_WATCH_TREE, 404 405 /* FILE_ACTION_DELETE_PENDING */ 406 NODE_FLAGS_WATCH_TREE | 407 FILE_NOTIFY_VALID_MASK, 408 }; 409 static const int smb_notify_action_nelm = 410 sizeof (smb_notify_action_mask) / 411 sizeof (smb_notify_action_mask[0]); 412 413 /* 414 * smb_notify_sr 415 * 416 * Post an event to an smb request waiting on some node. 417 * 418 * Note that node->fcn.mutex is held. This implies a 419 * lock order: node->fcn.mutex, then sr_mutex 420 */ 421 static void 422 smb_notify_sr(smb_request_t *sr, uint_t action, const char *name) 423 { 424 smb_notify_change_req_t *ncr; 425 uint32_t mask; 426 427 SMB_REQ_VALID(sr); 428 ncr = &sr->sr_ncr; 429 430 /* 431 * Compute the completion filter mask bits for which 432 * we will signal waiting notify requests. 433 */ 434 VERIFY(action < smb_notify_action_nelm); 435 mask = smb_notify_action_mask[action]; 436 437 mutex_enter(&sr->sr_mutex); 438 if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT && 439 (ncr->nc_flags & mask) != 0) { 440 sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED; 441 /* 442 * Save event data in the sr_ncr field so the 443 * reply handler can return it. 444 */ 445 ncr->nc_action = action; 446 if (name != NULL) 447 (void) strlcpy(ncr->nc_fname, name, MAXNAMELEN); 448 cv_signal(&ncr->nc_cv); 449 } 450 mutex_exit(&sr->sr_mutex); 451 } 452