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 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. 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 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 | 350 FILE_NOTIFY_CHANGE_LAST_ACCESS | 351 FILE_NOTIFY_CHANGE_CREATION | 352 FILE_NOTIFY_CHANGE_EA | 353 FILE_NOTIFY_CHANGE_SECURITY, 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 } 423