Home | History | Annotate | Download | only in smbsrv
      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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * File Change Notification (FCN)
     28  */
     29 
     30 /*
     31  * SMB: nt_transact_notify_change
     32  *
     33  *  Client Setup Words                 Description
     34  *  ================================== =================================
     35  *
     36  *  ULONG CompletionFilter;            Specifies operation to monitor
     37  *  USHORT Fid;                        Fid of directory to monitor
     38  *  BOOLEAN WatchTree;                 TRUE = watch all subdirectories too
     39  *  UCHAR Reserved;                    MBZ
     40  *
     41  * This command notifies the client when the directory specified by Fid is
     42  * modified.  It also returns the name(s) of the file(s) that changed.  The
     43  * command completes once the directory has been modified based on the
     44  * supplied CompletionFilter.  The command is a "single shot" and therefore
     45  * needs to be reissued to watch for more directory changes.
     46  *
     47  * A directory file must be opened before this command may be used.  Once
     48  * the directory is open, this command may be used to begin watching files
     49  * and subdirectories in the specified directory for changes.  The first
     50  * time the command is issued, the MaxParameterCount field in the transact
     51  * header determines the size of the buffer that will be used at the server
     52  * to buffer directory change information between issuances of the notify
     53  * change commands.
     54  *
     55  * When a change that is in the CompletionFilter is made to the directory,
     56  * the command completes.  The names of the files that have changed since
     57  * the last time the command was issued are returned to the client.  The
     58  * ParameterCount field of the response indicates the number of bytes that
     59  * are being returned.  If too many files have changed since the last time
     60  * the command was issued, then zero bytes are returned and an alternate
     61  * status code is returned in the Status field of the response.
     62  *
     63  * The CompletionFilter is a mask created as the sum of any of the
     64  * following flags:
     65  *
     66  * FILE_NOTIFY_CHANGE_FILE_NAME        0x00000001
     67  * FILE_NOTIFY_CHANGE_DIR_NAME         0x00000002
     68  * FILE_NOTIFY_CHANGE_NAME             0x00000003
     69  * FILE_NOTIFY_CHANGE_ATTRIBUTES       0x00000004
     70  * FILE_NOTIFY_CHANGE_SIZE             0x00000008
     71  * FILE_NOTIFY_CHANGE_LAST_WRITE       0x00000010
     72  * FILE_NOTIFY_CHANGE_LAST_ACCESS      0x00000020
     73  * FILE_NOTIFY_CHANGE_CREATION         0x00000040
     74  * FILE_NOTIFY_CHANGE_EA               0x00000080
     75  * FILE_NOTIFY_CHANGE_SECURITY         0x00000100
     76  * FILE_NOTIFY_CHANGE_STREAM_NAME      0x00000200
     77  * FILE_NOTIFY_CHANGE_STREAM_SIZE      0x00000400
     78  * FILE_NOTIFY_CHANGE_STREAM_WRITE     0x00000800
     79  *
     80  *  Server Response                    Description
     81  *  ================================== ================================
     82  *  ParameterCount                     # of bytes of change data
     83  *  Parameters[ ParameterCount ]       FILE_NOTIFY_INFORMATION
     84  *                                      structures
     85  *
     86  * The response contains FILE_NOTIFY_INFORMATION structures, as defined
     87  * below.  The NextEntryOffset field of the structure specifies the offset,
     88  * in bytes, from the start of the current entry to the next entry in the
     89  * list.  If this is the last entry in the list, this field is zero.  Each
     90  * entry in the list must be longword aligned, so NextEntryOffset must be a
     91  * multiple of four.
     92  *
     93  * typedef struct {
     94  *     ULONG NextEntryOffset;
     95  *     ULONG Action;
     96  *     ULONG FileNameLength;
     97  *     WCHAR FileName[1];
     98  * } FILE_NOTIFY_INFORMATION;
     99  *
    100  * Where Action describes what happened to the file named FileName:
    101  *
    102  * FILE_ACTION_ADDED            0x00000001
    103  * FILE_ACTION_REMOVED          0x00000002
    104  * FILE_ACTION_MODIFIED         0x00000003
    105  * FILE_ACTION_RENAMED_OLD_NAME 0x00000004
    106  * FILE_ACTION_RENAMED_NEW_NAME 0x00000005
    107  * FILE_ACTION_ADDED_STREAM     0x00000006
    108  * FILE_ACTION_REMOVED_STREAM   0x00000007
    109  * FILE_ACTION_MODIFIED_STREAM  0x00000008
    110  */
    111 
    112 #include <smbsrv/smb_kproto.h>
    113 #include <sys/sdt.h>
    114 
    115 static void smb_notify_change_daemon(smb_thread_t *, void *);
    116 static boolean_t smb_notify_change_required(smb_request_t *, smb_node_t *);
    117 
    118 static boolean_t	smb_notify_initialized = B_FALSE;
    119 static smb_slist_t	smb_ncr_list;
    120 static smb_slist_t	smb_nce_list;
    121 static smb_thread_t	smb_thread_notify_daemon;
    122 
    123 /*
    124  * smb_notify_init
    125  *
    126  * This function is not multi-thread safe. The caller must make sure only one
    127  * thread makes the call.
    128  */
    129 int
    130 smb_notify_init(void)
    131 {
    132 	int	rc;
    133 
    134 	if (smb_notify_initialized)
    135 		return (0);
    136 
    137 	smb_slist_constructor(&smb_ncr_list, sizeof (smb_request_t),
    138 	    offsetof(smb_request_t, sr_ncr.nc_lnd));
    139 
    140 	smb_slist_constructor(&smb_nce_list, sizeof (smb_request_t),
    141 	    offsetof(smb_request_t, sr_ncr.nc_lnd));
    142 
    143 	smb_thread_init(&smb_thread_notify_daemon,
    144 	    "smb_notify_change_daemon", smb_notify_change_daemon, NULL,
    145 	    NULL, NULL);
    146 
    147 	rc = smb_thread_start(&smb_thread_notify_daemon);
    148 	if (rc) {
    149 		smb_thread_destroy(&smb_thread_notify_daemon);
    150 		smb_slist_destructor(&smb_ncr_list);
    151 		smb_slist_destructor(&smb_nce_list);
    152 		return (rc);
    153 	}
    154 
    155 	smb_notify_initialized = B_TRUE;
    156 
    157 	return (0);
    158 }
    159 
    160 /*
    161  * smb_notify_fini
    162  *
    163  * This function is not multi-thread safe. The caller must make sure only one
    164  * thread makes the call.
    165  */
    166 void
    167 smb_notify_fini(void)
    168 {
    169 	if (!smb_notify_initialized)
    170 		return;
    171 
    172 	smb_thread_stop(&smb_thread_notify_daemon);
    173 	smb_thread_destroy(&smb_thread_notify_daemon);
    174 	smb_slist_destructor(&smb_ncr_list);
    175 	smb_slist_destructor(&smb_nce_list);
    176 	smb_notify_initialized = B_FALSE;
    177 }
    178 
    179 /*
    180  * smb_nt_transact_notify_change
    181  *
    182  * This function is responsible for processing NOTIFY CHANGE requests.
    183  * Requests are stored in a global queue. This queue is processed when
    184  * a monitored directory is changed or client cancels one of its already
    185  * sent requests.
    186  */
    187 smb_sdrc_t
    188 smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa)
    189 {
    190 	uint32_t		CompletionFilter;
    191 	unsigned char		WatchTree;
    192 	smb_node_t		*node;
    193 
    194 	if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
    195 	    &CompletionFilter, &sr->smb_fid, &WatchTree) != 0)
    196 		return (SDRC_NOT_IMPLEMENTED);
    197 
    198 	smbsr_lookup_file(sr);
    199 	if (sr->fid_ofile == NULL) {
    200 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
    201 		return (SDRC_ERROR);
    202 	}
    203 
    204 	node = sr->fid_ofile->f_node;
    205 
    206 	if (!smb_node_is_dir(node)) {
    207 		/*
    208 		 * Notify change requests are only valid on directories.
    209 		 */
    210 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 0, 0);
    211 		return (SDRC_ERROR);
    212 	}
    213 
    214 	mutex_enter(&sr->sr_mutex);
    215 	switch (sr->sr_state) {
    216 	case SMB_REQ_STATE_ACTIVE:
    217 		node->waiting_event++;
    218 		node->flags |= NODE_FLAGS_NOTIFY_CHANGE;
    219 		if ((node->flags & NODE_FLAGS_CHANGED) == 0) {
    220 			sr->sr_ncr.nc_node = node;
    221 			sr->sr_ncr.nc_flags = CompletionFilter;
    222 			if (WatchTree)
    223 				sr->sr_ncr.nc_flags |= NODE_FLAGS_WATCH_TREE;
    224 
    225 			sr->sr_keep = B_TRUE;
    226 			sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;
    227 
    228 			smb_slist_insert_tail(&smb_ncr_list, sr);
    229 
    230 			/*
    231 			 * Monitor events system-wide.
    232 			 *
    233 			 * XXX: smb_node_ref() and smb_node_release()
    234 			 * take &node->n_lock.  May need alternate forms
    235 			 * of these routines if node->n_lock is taken
    236 			 * around calls to smb_fem_fcn_install() and
    237 			 * smb_fem_fcn_uninstall().
    238 			 */
    239 
    240 			smb_fem_fcn_install(node);
    241 
    242 			mutex_exit(&sr->sr_mutex);
    243 			return (SDRC_SR_KEPT);
    244 		} else {
    245 			/* node already changed, reply immediately */
    246 			if (--node->waiting_event == 0)
    247 				node->flags &=
    248 				    ~(NODE_FLAGS_NOTIFY_CHANGE |
    249 				    NODE_FLAGS_CHANGED);
    250 			mutex_exit(&sr->sr_mutex);
    251 			return (SDRC_SUCCESS);
    252 		}
    253 
    254 	case SMB_REQ_STATE_CANCELED:
    255 		mutex_exit(&sr->sr_mutex);
    256 		smbsr_error(sr, NT_STATUS_CANCELLED, 0, 0);
    257 		return (SDRC_ERROR);
    258 
    259 	default:
    260 		ASSERT(0);
    261 		mutex_exit(&sr->sr_mutex);
    262 		return (SDRC_SUCCESS);
    263 	}
    264 }
    265 
    266 /*
    267  * smb_reply_notify_change_request
    268  *
    269  * This function sends appropriate response to an already queued NOTIFY CHANGE
    270  * request. If node is changed (reply == NODE_FLAGS_CHANGED), a normal reply is
    271  * sent.
    272  * If client cancels the request or session dropped, an NT_STATUS_CANCELED
    273  * is sent in reply.
    274  */
    275 
    276 void
    277 smb_reply_notify_change_request(smb_request_t *sr)
    278 {
    279 	smb_node_t	*node;
    280 	int		total_bytes, n_setup, n_param, n_data;
    281 	int		param_off, param_pad, data_off, data_pad;
    282 	struct		smb_xa *xa;
    283 	smb_error_t	err;
    284 
    285 	xa = sr->r_xa;
    286 	node = sr->sr_ncr.nc_node;
    287 
    288 	if (--node->waiting_event == 0) {
    289 		node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED);
    290 		smb_fem_fcn_uninstall(node);
    291 	}
    292 
    293 	mutex_enter(&sr->sr_mutex);
    294 	switch (sr->sr_state) {
    295 
    296 	case SMB_REQ_STATE_EVENT_OCCURRED:
    297 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
    298 
    299 		/* many things changed */
    300 
    301 		(void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0L);
    302 
    303 		/* setup the NT transact reply */
    304 
    305 		n_setup = MBC_LENGTH(&xa->rep_setup_mb);
    306 		n_param = MBC_LENGTH(&xa->rep_param_mb);
    307 		n_data  = MBC_LENGTH(&xa->rep_data_mb);
    308 
    309 		n_setup = (n_setup + 1) / 2; /* Convert to setup words */
    310 		param_pad = 1; /* must be one */
    311 		param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
    312 		/* Pad to 4 bytes */
    313 		data_pad = (4 - ((param_off + n_param) & 3)) % 4;
    314 		/* Param off from hdr */
    315 		data_off = param_off + n_param + data_pad;
    316 		total_bytes = param_pad + n_param + data_pad + n_data;
    317 
    318 		(void) smbsr_encode_result(sr, 18+n_setup, total_bytes,
    319 		    "b3.llllllllbCw#.C#.C",
    320 		    18 + n_setup,	/* wct */
    321 		    n_param,		/* Total Parameter Bytes */
    322 		    n_data,		/* Total Data Bytes */
    323 		    n_param,		/* Total Parameter Bytes this buffer */
    324 		    param_off,		/* Param offset from header start */
    325 		    0,			/* Param displacement */
    326 		    n_data,		/* Total Data Bytes this buffer */
    327 		    data_off,		/* Data offset from header start */
    328 		    0,			/* Data displacement */
    329 		    n_setup,		/* suwcnt */
    330 		    &xa->rep_setup_mb,	/* setup[] */
    331 		    total_bytes,	/* Total data bytes */
    332 		    param_pad,
    333 		    &xa->rep_param_mb,
    334 		    data_pad,
    335 		    &xa->rep_data_mb);
    336 		break;
    337 
    338 	case SMB_REQ_STATE_CANCELED:
    339 		err.severity = ERROR_SEVERITY_ERROR;
    340 		err.status   = NT_STATUS_CANCELLED;
    341 		err.errcls   = ERRDOS;
    342 		err.errcode  = ERROR_OPERATION_ABORTED;
    343 		smbsr_set_error(sr, &err);
    344 
    345 		(void) smb_mbc_encodef(&sr->reply, "bwbw",
    346 		    (short)0, 0L, (short)0, 0L);
    347 		sr->smb_wct = 0;
    348 		sr->smb_bcc = 0;
    349 		break;
    350 	default:
    351 		ASSERT(0);
    352 	}
    353 	mutex_exit(&sr->sr_mutex);
    354 
    355 	/* Setup the header */
    356 	(void) smb_mbc_poke(&sr->reply, 0, SMB_HEADER_ED_FMT,
    357 	    sr->first_smb_com,
    358 	    sr->smb_rcls,
    359 	    sr->smb_reh,
    360 	    sr->smb_err,
    361 	    sr->smb_flg | SMB_FLAGS_REPLY,
    362 	    sr->smb_flg2,
    363 	    sr->smb_pid_high,
    364 	    sr->smb_sig,
    365 	    sr->smb_tid,
    366 	    sr->smb_pid,
    367 	    sr->smb_uid,
    368 	    sr->smb_mid);
    369 
    370 	if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
    371 		smb_sign_reply(sr, NULL);
    372 
    373 	/* send the reply */
    374 	DTRACE_PROBE1(ncr__reply, struct smb_request *, sr)
    375 	(void) smb_session_send(sr->session, 0, &sr->reply);
    376 	smbsr_cleanup(sr);
    377 
    378 	mutex_enter(&sr->sr_mutex);
    379 	sr->sr_state = SMB_REQ_STATE_COMPLETED;
    380 	mutex_exit(&sr->sr_mutex);
    381 	smb_request_free(sr);
    382 }
    383 
    384 /*
    385  * smb_process_session_notify_change_queue
    386  *
    387  * This function traverses notify change request queue and sends
    388  * cancel replies to all of requests that are related to a specific
    389  * session.
    390  */
    391 void
    392 smb_process_session_notify_change_queue(
    393     smb_session_t	*session,
    394     smb_tree_t		*tree)
    395 {
    396 	smb_request_t	*sr;
    397 	smb_request_t	*tmp;
    398 	boolean_t	sig = B_FALSE;
    399 
    400 	smb_slist_enter(&smb_ncr_list);
    401 	smb_slist_enter(&smb_nce_list);
    402 	sr = smb_slist_head(&smb_ncr_list);
    403 	while (sr) {
    404 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
    405 		tmp = smb_slist_next(&smb_ncr_list, sr);
    406 		if ((sr->session == session) &&
    407 		    (tree == NULL || sr->tid_tree == tree)) {
    408 			mutex_enter(&sr->sr_mutex);
    409 			switch (sr->sr_state) {
    410 			case SMB_REQ_STATE_WAITING_EVENT:
    411 				smb_slist_obj_move(
    412 				    &smb_nce_list,
    413 				    &smb_ncr_list,
    414 				    sr);
    415 				sr->sr_state = SMB_REQ_STATE_CANCELED;
    416 				sig = B_TRUE;
    417 				break;
    418 			default:
    419 				ASSERT(0);
    420 				break;
    421 			}
    422 			mutex_exit(&sr->sr_mutex);
    423 		}
    424 		sr = tmp;
    425 	}
    426 	smb_slist_exit(&smb_nce_list);
    427 	smb_slist_exit(&smb_ncr_list);
    428 	if (sig)
    429 		smb_thread_signal(&smb_thread_notify_daemon);
    430 }
    431 
    432 /*
    433  * smb_process_file_notify_change_queue
    434  *
    435  * This function traverses notify change request queue and sends
    436  * cancel replies to all of requests that are related to the
    437  * specified file.
    438  */
    439 void
    440 smb_process_file_notify_change_queue(struct smb_ofile *of)
    441 {
    442 	smb_request_t	*sr;
    443 	smb_request_t	*tmp;
    444 	boolean_t	sig = B_FALSE;
    445 
    446 	smb_slist_enter(&smb_ncr_list);
    447 	smb_slist_enter(&smb_nce_list);
    448 	sr = smb_slist_head(&smb_ncr_list);
    449 	while (sr) {
    450 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
    451 		tmp = smb_slist_next(&smb_ncr_list, sr);
    452 		if (sr->fid_ofile == of) {
    453 			mutex_enter(&sr->sr_mutex);
    454 			switch (sr->sr_state) {
    455 			case SMB_REQ_STATE_WAITING_EVENT:
    456 				smb_slist_obj_move(&smb_nce_list,
    457 				    &smb_ncr_list, sr);
    458 				sr->sr_state = SMB_REQ_STATE_CANCELED;
    459 				sig = B_TRUE;
    460 				break;
    461 			default:
    462 				ASSERT(0);
    463 				break;
    464 			}
    465 			mutex_exit(&sr->sr_mutex);
    466 		}
    467 		sr = tmp;
    468 	}
    469 	smb_slist_exit(&smb_nce_list);
    470 	smb_slist_exit(&smb_ncr_list);
    471 	if (sig)
    472 		smb_thread_signal(&smb_thread_notify_daemon);
    473 }
    474 
    475 /*
    476  * smb_reply_specific_cancel_request
    477  *
    478  * This function searches global request list for a specific request. If found,
    479  * moves the request to event queue and kicks the notify change daemon.
    480  */
    481 
    482 void
    483 smb_reply_specific_cancel_request(struct smb_request *zsr)
    484 {
    485 	smb_request_t	*sr;
    486 	smb_request_t	*tmp;
    487 	boolean_t	sig = B_FALSE;
    488 
    489 	smb_slist_enter(&smb_ncr_list);
    490 	smb_slist_enter(&smb_nce_list);
    491 	sr = smb_slist_head(&smb_ncr_list);
    492 	while (sr) {
    493 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
    494 		tmp = smb_slist_next(&smb_ncr_list, sr);
    495 		if ((sr->session == zsr->session) &&
    496 		    (sr->smb_uid == zsr->smb_uid) &&
    497 		    (sr->smb_pid == zsr->smb_pid) &&
    498 		    (sr->smb_tid == zsr->smb_tid) &&
    499 		    (sr->smb_mid == zsr->smb_mid)) {
    500 			mutex_enter(&sr->sr_mutex);
    501 			switch (sr->sr_state) {
    502 			case SMB_REQ_STATE_WAITING_EVENT:
    503 				smb_slist_obj_move(&smb_nce_list,
    504 				    &smb_ncr_list, sr);
    505 				sr->sr_state = SMB_REQ_STATE_CANCELED;
    506 				sig = B_TRUE;
    507 				break;
    508 			default:
    509 				ASSERT(0);
    510 				break;
    511 			}
    512 			mutex_exit(&sr->sr_mutex);
    513 		}
    514 		sr = tmp;
    515 	}
    516 	smb_slist_exit(&smb_nce_list);
    517 	smb_slist_exit(&smb_ncr_list);
    518 	if (sig)
    519 		smb_thread_signal(&smb_thread_notify_daemon);
    520 }
    521 
    522 /*
    523  * smb_process_node_notify_change_queue
    524  *
    525  * This function searches notify change request queue and sends
    526  * 'NODE MODIFIED' reply to all requests which are related to a
    527  * specific node.
    528  * WatchTree flag: We handle this flag in a special manner just
    529  * for DAVE clients. When something is changed, we notify all
    530  * requests which came from DAVE clients on the same volume which
    531  * has been modified. We don't care about the tree that they wanted
    532  * us to monitor. any change in any part of the volume will lead
    533  * to notifying all notify change requests from DAVE clients on the
    534  * different parts of the volume hierarchy.
    535  */
    536 void
    537 smb_process_node_notify_change_queue(smb_node_t *node)
    538 {
    539 	smb_request_t	*sr;
    540 	smb_request_t	*tmp;
    541 	boolean_t	sig = B_FALSE;
    542 
    543 	ASSERT(node->n_magic == SMB_NODE_MAGIC);
    544 
    545 	if (!(node->flags & NODE_FLAGS_NOTIFY_CHANGE))
    546 		return;
    547 
    548 	node->flags |= NODE_FLAGS_CHANGED;
    549 
    550 	smb_slist_enter(&smb_ncr_list);
    551 	smb_slist_enter(&smb_nce_list);
    552 	sr = smb_slist_head(&smb_ncr_list);
    553 	while (sr) {
    554 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
    555 		tmp = smb_slist_next(&smb_ncr_list, sr);
    556 
    557 		if (smb_notify_change_required(sr, node)) {
    558 			mutex_enter(&sr->sr_mutex);
    559 			switch (sr->sr_state) {
    560 			case SMB_REQ_STATE_WAITING_EVENT:
    561 				smb_slist_obj_move(&smb_nce_list,
    562 				    &smb_ncr_list, sr);
    563 				sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED;
    564 				sig = B_TRUE;
    565 				break;
    566 			default:
    567 				ASSERT(0);
    568 				break;
    569 			}
    570 			mutex_exit(&sr->sr_mutex);
    571 		}
    572 		sr = tmp;
    573 	}
    574 	smb_slist_exit(&smb_nce_list);
    575 	smb_slist_exit(&smb_ncr_list);
    576 	if (sig)
    577 		smb_thread_signal(&smb_thread_notify_daemon);
    578 }
    579 
    580 /*
    581  * Change notification is required if:
    582  *	- the request node matches the specified node
    583  * or
    584  *	- the request is from a Mac client, the watch-tree flag
    585  *	is set and it is monitoring a tree on the same volume.
    586  */
    587 static boolean_t
    588 smb_notify_change_required(smb_request_t *sr, smb_node_t *node)
    589 {
    590 	smb_node_t *nc_node = sr->sr_ncr.nc_node;
    591 
    592 	if (nc_node == node)
    593 		return (B_TRUE);
    594 
    595 	if ((sr->sr_ncr.nc_flags & NODE_FLAGS_WATCH_TREE) &&
    596 	    (sr->session->native_os == NATIVE_OS_MACOS) &&
    597 	    smb_vfs_cmp(SMB_NODE_VFS(nc_node), SMB_NODE_VFS(node)))
    598 		return (B_TRUE);
    599 
    600 	return (B_FALSE);
    601 }
    602 
    603 /*
    604  * smb_notify_change_daemon
    605  *
    606  * This function processes notify change event list and send appropriate
    607  * responses to the requests. This function executes in the system as an
    608  * indivdual thread.
    609  */
    610 static void
    611 smb_notify_change_daemon(smb_thread_t *thread, void *arg)
    612 {
    613 	_NOTE(ARGUNUSED(arg))
    614 
    615 	smb_request_t	*sr;
    616 	smb_request_t	*tmp;
    617 	list_t		sr_list;
    618 
    619 	list_create(&sr_list, sizeof (smb_request_t),
    620 	    offsetof(smb_request_t, sr_ncr.nc_lnd));
    621 
    622 	while (smb_thread_continue(thread)) {
    623 
    624 		while (smb_slist_move_tail(&sr_list, &smb_nce_list)) {
    625 			sr = list_head(&sr_list);
    626 			while (sr) {
    627 				ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
    628 				tmp = list_next(&sr_list, sr);
    629 				list_remove(&sr_list, sr);
    630 				smb_reply_notify_change_request(sr);
    631 				sr = tmp;
    632 			}
    633 		}
    634 	}
    635 	list_destroy(&sr_list);
    636 }
    637