summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2012-10-10 20:07:26 -0400
committerGordon Ross <gwr@nexenta.com>2013-02-11 23:47:44 -0500
commitccc71be50bb49efb4e31004c77fb3e065e9c0596 (patch)
tree3d31fef3b3ec173d505bc1288177501b2026bdc7 /usr/src
parentbfbce3c1273efa22c185ea2995c57c37163fd7c3 (diff)
downloadillumos-gate-ccc71be50bb49efb4e31004c77fb3e065e9c0596.tar.gz
3523 SMB NT Notify returning too soon
Reviewed by: Albert Lee <trisk@nexenta.com> Reviewed by: Dan McDonald <danmcd@nexenta.com> Reviewed by: Yakov Zaytsev <yakov@nexenta.com> Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_common_transact.c7
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fem.c27
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsops.c17
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_node.c75
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c5
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c674
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c9
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_rename.c29
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_session.c14
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h16
-rw-r--r--usr/src/uts/common/smbsrv/smb_ktypes.h18
12 files changed, 406 insertions, 489 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
index 21dff73b3c..7d534665fd 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -127,7 +128,7 @@ smb_com_transaction(smb_request_t *sr)
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
- smbsr_error(sr, 0, ERRDOS, ERRsrverror);
+ smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
@@ -314,7 +315,7 @@ smb_com_transaction2(struct smb_request *sr)
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
- smbsr_error(sr, 0, ERRDOS, ERRsrverror);
+ smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
@@ -586,7 +587,7 @@ smb_com_nt_transact(struct smb_request *sr)
if (smb_xa_open(xa)) {
smb_xa_rele(sr->session, xa);
- smbsr_error(sr, 0, ERRDOS, ERRsrverror);
+ smbsr_error(sr, 0, ERRSRV, ERRsrverror);
return (SDRC_ERROR);
}
sr->r_xa = xa;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fem.c b/usr/src/uts/common/fs/smbsrv/smb_fem.c
index e32b5fc925..38cfab1f5c 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fem.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fem.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -223,7 +224,7 @@ smb_fem_fcn_create(
ct, vsecp);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
return (error);
}
@@ -257,7 +258,7 @@ smb_fem_fcn_remove(
error = vnext_remove(arg, name, cr, ct, flags);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
return (error);
}
@@ -281,10 +282,18 @@ smb_fem_fcn_rename(
error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
- if (error == 0)
- smb_node_notify_change(dnode);
+ if (error != 0)
+ return (error);
- return (error);
+ /*
+ * Note that renames in the same directory are normally
+ * delivered in {old,new} pairs, and clients expect them
+ * in that order, if both events are delivered.
+ */
+ smb_node_notify_change(dnode, FILE_ACTION_RENAMED_OLD_NAME, snm);
+ smb_node_notify_change(dnode, FILE_ACTION_RENAMED_NEW_NAME, tnm);
+
+ return (0);
}
static int
@@ -308,7 +317,7 @@ smb_fem_fcn_mkdir(
error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
return (error);
}
@@ -332,7 +341,7 @@ smb_fem_fcn_rmdir(
error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
return (error);
}
@@ -356,7 +365,7 @@ smb_fem_fcn_link(
error = vnext_link(arg, svp, tnm, cr, ct, flags);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
return (error);
}
@@ -381,7 +390,7 @@ smb_fem_fcn_symlink(
error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
if (error == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
return (error);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
index 68e32246cd..f9ec3fa674 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sid.h>
@@ -423,7 +424,8 @@ smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
/* notify change to the unnamed stream */
if (rc == 0)
- smb_node_notify_change(dnode);
+ smb_node_notify_change(dnode,
+ FILE_ACTION_ADDED_STREAM, fname);
return (rc);
}
@@ -679,9 +681,10 @@ smb_fsop_remove(
rc = smb_vop_stream_remove(fnode->vp, name, flags, cr);
/* notify change to the unnamed stream */
- if ((rc == 0) && fnode->n_dnode)
- smb_node_notify_change(fnode->n_dnode);
-
+ if ((rc == 0) && fnode->n_dnode) {
+ smb_node_notify_change(fnode->n_dnode,
+ FILE_ACTION_REMOVED_STREAM, fnode->od_name);
+ }
} else if (smb_is_stream_name(name)) {
smb_stream_parse_name(name, fname, sname);
@@ -710,8 +713,10 @@ smb_fsop_remove(
smb_node_release(fnode);
/* notify change to the unnamed stream */
- if (rc == 0)
- smb_node_notify_change(dnode);
+ if (rc == 0) {
+ smb_node_notify_change(dnode,
+ FILE_ACTION_REMOVED_STREAM, fname);
+ }
} else {
rc = smb_vop_remove(dnode->vp, name, flags, cr);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c
index bd69147095..bfb08c2d15 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
* SMB Node State Machine
@@ -784,15 +785,51 @@ smb_node_share_check(smb_node_t *node)
return (status);
}
+/*
+ * SMB Change Notification
+ */
+
+void
+smb_node_fcn_subscribe(smb_node_t *node, smb_request_t *sr)
+{
+ smb_node_fcn_t *fcn = &node->n_fcn;
+
+ mutex_enter(&fcn->fcn_mutex);
+ if (fcn->fcn_count == 0)
+ smb_fem_fcn_install(node);
+ fcn->fcn_count++;
+ list_insert_tail(&fcn->fcn_watchers, sr);
+ mutex_exit(&fcn->fcn_mutex);
+}
+
+void
+smb_node_fcn_unsubscribe(smb_node_t *node, smb_request_t *sr)
+{
+ smb_node_fcn_t *fcn = &node->n_fcn;
+
+ mutex_enter(&fcn->fcn_mutex);
+ list_remove(&fcn->fcn_watchers, sr);
+ fcn->fcn_count--;
+ if (fcn->fcn_count == 0)
+ smb_fem_fcn_uninstall(node);
+ mutex_exit(&fcn->fcn_mutex);
+}
+
void
-smb_node_notify_change(smb_node_t *node)
+smb_node_notify_change(smb_node_t *node, uint_t action, const char *name)
{
SMB_NODE_VALID(node);
- if (node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
- node->flags |= NODE_FLAGS_CHANGED;
- smb_process_node_notify_change_queue(node);
- }
+ smb_notify_event(node, action, name);
+
+ /*
+ * These two events come as a pair:
+ * FILE_ACTION_RENAMED_OLD_NAME
+ * FILE_ACTION_RENAMED_NEW_NAME
+ * Only do the parent notify for "new".
+ */
+ if (action == FILE_ACTION_RENAMED_OLD_NAME)
+ return;
smb_node_notify_parents(node);
}
@@ -808,17 +845,17 @@ smb_node_notify_change(smb_node_t *node)
void
smb_node_notify_parents(smb_node_t *dnode)
{
- smb_node_t *pnode = dnode;
+ smb_node_t *pnode; /* parent */
SMB_NODE_VALID(dnode);
+ pnode = dnode->n_dnode;
- while ((pnode = pnode->n_dnode) != NULL) {
+ while (pnode != NULL) {
SMB_NODE_VALID(pnode);
- if ((pnode->flags & NODE_FLAGS_NOTIFY_CHANGE) &&
- (pnode->flags & NODE_FLAGS_WATCH_TREE)) {
- pnode->flags |= NODE_FLAGS_CHANGED;
- smb_process_node_notify_change_queue(pnode);
- }
+ smb_notify_event(pnode, 0, dnode->od_name);
+ /* cd .. */
+ dnode = pnode;
+ pnode = dnode->n_dnode;
}
}
@@ -1140,6 +1177,9 @@ smb_node_constructor(void *buf, void *un, int kmflags)
offsetof(smb_ofile_t, f_nnd));
smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
offsetof(smb_lock_t, l_lnd));
+ mutex_init(&node->n_fcn.fcn_mutex, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&node->n_fcn.fcn_watchers, sizeof (smb_request_t),
+ offsetof(smb_request_t, sr_ncr.nc_lnd));
cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&node->n_oplock.ol_mutex, NULL, MUTEX_DEFAULT, NULL);
list_create(&node->n_oplock.ol_grants, sizeof (smb_oplock_grant_t),
@@ -1165,6 +1205,8 @@ smb_node_destructor(void *buf, void *un)
rw_destroy(&node->n_lock);
cv_destroy(&node->n_oplock.ol_cv);
mutex_destroy(&node->n_oplock.ol_mutex);
+ list_destroy(&node->n_fcn.fcn_watchers);
+ mutex_destroy(&node->n_fcn.fcn_mutex);
smb_llist_destructor(&node->n_lock_list);
smb_llist_destructor(&node->n_ofile_list);
list_destroy(&node->n_oplock.ol_grants);
@@ -1416,9 +1458,12 @@ smb_node_setattr(smb_request_t *sr, smb_node_t *node,
if (tmp_attr.sa_mask)
smb_node_set_cached_timestamps(node, &tmp_attr);
- if (tmp_attr.sa_mask & SMB_AT_MTIME || explicit_times & SMB_AT_MTIME) {
- if (node->n_dnode != NULL)
- smb_node_notify_change(node->n_dnode);
+ if ((tmp_attr.sa_mask & SMB_AT_MTIME) ||
+ (explicit_times & SMB_AT_MTIME)) {
+ if (node->n_dnode != NULL) {
+ smb_node_notify_change(node->n_dnode,
+ FILE_ACTION_MODIFIED, node->od_name);
+ }
}
return (0);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c b/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c
index 1e4d9df892..35bdf74b0d 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_cancel.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -79,9 +80,5 @@ smb_com_nt_cancel(smb_request_t *sr)
}
smb_slist_exit(&session->s_req_list);
- /* Now, search the notify change queue to find the request */
-
- smb_reply_specific_cancel_request(sr);
-
return (SDRC_NO_REPLY);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
index 2efd4cba5a..4b6befa344 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_notify_change.c
@@ -20,8 +20,8 @@
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -113,86 +113,50 @@
#include <smbsrv/smb_kproto.h>
#include <sys/sdt.h>
-static void smb_notify_change_daemon(smb_thread_t *, void *);
-
-static boolean_t smb_notify_initialized = B_FALSE;
-static smb_slist_t smb_ncr_list;
-static smb_slist_t smb_nce_list;
-static smb_thread_t smb_thread_notify_daemon;
-
/*
- * smb_notify_init
- *
- * This function is not multi-thread safe. The caller must make sure only one
- * thread makes the call.
+ * We add this flag to the CompletionFilter (see above) when the
+ * client sets WatchTree. Must not overlap FILE_NOTIFY_VALID_MASK.
*/
-int
-smb_notify_init(void)
-{
- int rc;
-
- if (smb_notify_initialized)
- return (0);
-
- smb_slist_constructor(&smb_ncr_list, sizeof (smb_request_t),
- offsetof(smb_request_t, sr_ncr.nc_lnd));
-
- smb_slist_constructor(&smb_nce_list, sizeof (smb_request_t),
- offsetof(smb_request_t, sr_ncr.nc_lnd));
+#define NODE_FLAGS_WATCH_TREE 0x10000000
+#if (NODE_FLAGS_WATCH_TREE & FILE_NOTIFY_VALID_MASK)
+#error "NODE_FLAGS_WATCH_TREE"
+#endif
- smb_thread_init(&smb_thread_notify_daemon,
- "smb_notify_change_daemon", smb_notify_change_daemon, NULL);
-
- rc = smb_thread_start(&smb_thread_notify_daemon);
- if (rc) {
- smb_thread_destroy(&smb_thread_notify_daemon);
- smb_slist_destructor(&smb_ncr_list);
- smb_slist_destructor(&smb_nce_list);
- return (rc);
- }
-
- smb_notify_initialized = B_TRUE;
-
- return (0);
-}
-
-/*
- * smb_notify_fini
- *
- * This function is not multi-thread safe. The caller must make sure only one
- * thread makes the call.
- */
-void
-smb_notify_fini(void)
-{
- if (!smb_notify_initialized)
- return;
+static void smb_notify_sr(smb_request_t *, uint_t, const char *);
- smb_thread_stop(&smb_thread_notify_daemon);
- smb_thread_destroy(&smb_thread_notify_daemon);
- smb_slist_destructor(&smb_ncr_list);
- smb_slist_destructor(&smb_nce_list);
- smb_notify_initialized = B_FALSE;
-}
+static int smb_notify_encode_action(struct smb_request *, struct smb_xa *,
+ uint32_t, char *);
/*
* smb_nt_transact_notify_change
*
- * This function is responsible for processing NOTIFY CHANGE requests.
- * Requests are stored in a global queue. This queue is processed when
- * a monitored directory is changed or client cancels one of its already
- * sent requests.
+ * Handle and SMB NT transact NOTIFY CHANGE request.
+ * Basically, wait until "something has changed", and either
+ * return information about what changed, or return a special
+ * error telling the client "many things changed".
+ *
+ * The implementation uses a per-node list of waiting notify
+ * requests like this one, each with a blocked worker thead.
+ * Later, FEM and/or smbsrv events wake these threads, which
+ * then send the reply to the client.
*/
smb_sdrc_t
-smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa)
+smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa)
{
uint32_t CompletionFilter;
unsigned char WatchTree;
+ smb_error_t err;
+ int rc;
smb_node_t *node;
if (smb_mbc_decodef(&xa->req_setup_mb, "lwb",
- &CompletionFilter, &sr->smb_fid, &WatchTree) != 0)
- return (SDRC_NOT_IMPLEMENTED);
+ &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) {
+ smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
+ return (SDRC_ERROR);
+ }
+ CompletionFilter &= FILE_NOTIFY_VALID_MASK;
+ if (WatchTree)
+ CompletionFilter |= NODE_FLAGS_WATCH_TREE;
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
@@ -201,7 +165,6 @@ smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa)
}
node = sr->fid_ofile->f_node;
-
if (node == NULL || !smb_node_is_dir(node)) {
/*
* Notify change requests are only valid on directories.
@@ -210,133 +173,70 @@ smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa)
return (SDRC_ERROR);
}
+ /*
+ * Prepare to receive event data.
+ */
+ sr->sr_ncr.nc_flags = CompletionFilter;
+ ASSERT(sr->sr_ncr.nc_action == 0);
+ ASSERT(sr->sr_ncr.nc_fname == NULL);
+ sr->sr_ncr.nc_fname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+
+ /*
+ * Subscribe to events on this node.
+ */
+ smb_node_fcn_subscribe(node, sr);
+
+ /*
+ * Wait for subscribed events to arrive.
+ * Expect SMB_REQ_STATE_EVENT_OCCURRED
+ * or SMB_REQ_STATE_CANCELED when signaled.
+ * Note it's possible (though rare) to already
+ * have SMB_REQ_STATE_CANCELED here.
+ */
mutex_enter(&sr->sr_mutex);
- switch (sr->sr_state) {
- case SMB_REQ_STATE_ACTIVE:
- node->waiting_event++;
- node->flags |= NODE_FLAGS_NOTIFY_CHANGE;
- if ((node->flags & NODE_FLAGS_CHANGED) == 0) {
- sr->sr_ncr.nc_node = node;
- sr->sr_ncr.nc_flags = CompletionFilter;
- if (WatchTree)
- sr->sr_ncr.nc_flags |= NODE_FLAGS_WATCH_TREE;
-
- sr->sr_keep = B_TRUE;
- sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;
-
- smb_slist_insert_tail(&smb_ncr_list, sr);
-
- /*
- * Monitor events system-wide.
- *
- * XXX: smb_node_ref() and smb_node_release()
- * take &node->n_lock. May need alternate forms
- * of these routines if node->n_lock is taken
- * around calls to smb_fem_fcn_install() and
- * smb_fem_fcn_uninstall().
- */
-
- smb_fem_fcn_install(node);
-
- mutex_exit(&sr->sr_mutex);
- return (SDRC_SR_KEPT);
- } else {
- /* node already changed, reply immediately */
- if (--node->waiting_event == 0)
- node->flags &=
- ~(NODE_FLAGS_NOTIFY_CHANGE |
- NODE_FLAGS_CHANGED);
- mutex_exit(&sr->sr_mutex);
- return (SDRC_SUCCESS);
- }
-
- case SMB_REQ_STATE_CANCELED:
- mutex_exit(&sr->sr_mutex);
- smbsr_error(sr, NT_STATUS_CANCELLED, 0, 0);
- return (SDRC_ERROR);
-
- default:
- ASSERT(0);
- mutex_exit(&sr->sr_mutex);
- return (SDRC_SUCCESS);
+ if (sr->sr_state == SMB_REQ_STATE_ACTIVE)
+ sr->sr_state = SMB_REQ_STATE_WAITING_EVENT;
+ while (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT) {
+ cv_wait(&sr->sr_ncr.nc_cv, &sr->sr_mutex);
}
-}
-
-/*
- * smb_reply_notify_change_request
- *
- * This function sends appropriate response to an already queued NOTIFY CHANGE
- * request. If node is changed (reply == NODE_FLAGS_CHANGED), a normal reply is
- * sent.
- * If client cancels the request or session dropped, an NT_STATUS_CANCELED
- * is sent in reply.
- */
-
-void
-smb_reply_notify_change_request(smb_request_t *sr)
-{
- smb_node_t *node;
- smb_srqueue_t *srq;
- int total_bytes, n_setup, n_param, n_data;
- int param_off, param_pad, data_off, data_pad;
- struct smb_xa *xa;
- smb_error_t err;
-
- SMB_REQ_VALID(sr);
- srq = sr->session->s_srqueue;
- smb_srqueue_waitq_to_runq(srq);
+ if (sr->sr_state == SMB_REQ_STATE_EVENT_OCCURRED)
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+ mutex_exit(&sr->sr_mutex);
- xa = sr->r_xa;
- node = sr->sr_ncr.nc_node;
+ /*
+ * Unsubscribe from events on this node.
+ */
+ smb_node_fcn_unsubscribe(node, sr);
- if (--node->waiting_event == 0) {
- node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED);
- smb_fem_fcn_uninstall(node);
- }
+ /*
+ * Build the reply
+ */
- mutex_enter(&sr->sr_mutex);
switch (sr->sr_state) {
- case SMB_REQ_STATE_EVENT_OCCURRED:
- sr->sr_state = SMB_REQ_STATE_ACTIVE;
-
- /* many things changed */
-
- (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0L);
-
- /* setup the NT transact reply */
-
- n_setup = MBC_LENGTH(&xa->rep_setup_mb);
- n_param = MBC_LENGTH(&xa->rep_param_mb);
- n_data = MBC_LENGTH(&xa->rep_data_mb);
-
- n_setup = (n_setup + 1) / 2; /* Convert to setup words */
- param_pad = 1; /* must be one */
- param_off = param_pad + 32 + 37 + (n_setup << 1) + 2;
- /* Pad to 4 bytes */
- data_pad = (4 - ((param_off + n_param) & 3)) % 4;
- /* Param off from hdr */
- data_off = param_off + n_param + data_pad;
- total_bytes = param_pad + n_param + data_pad + n_data;
-
- (void) smbsr_encode_result(sr, 18+n_setup, total_bytes,
- "b3.llllllllbCw#.C#.C",
- 18 + n_setup, /* wct */
- n_param, /* Total Parameter Bytes */
- n_data, /* Total Data Bytes */
- n_param, /* Total Parameter Bytes this buffer */
- param_off, /* Param offset from header start */
- 0, /* Param displacement */
- n_data, /* Total Data Bytes this buffer */
- data_off, /* Data offset from header start */
- 0, /* Data displacement */
- n_setup, /* suwcnt */
- &xa->rep_setup_mb, /* setup[] */
- total_bytes, /* Total data bytes */
- param_pad,
- &xa->rep_param_mb,
- data_pad,
- &xa->rep_data_mb);
+ case SMB_REQ_STATE_ACTIVE:
+ /*
+ * If we have event data, marshall it now, else just
+ * say "many things changed". Note that when we are
+ * woken by a WatchTree event (action == 0) then we
+ * don't have true event details, and only know the
+ * directory under which something changed. In that
+ * case we just say "many things changed".
+ */
+ if (sr->sr_ncr.nc_action != 0 && 0 ==
+ smb_notify_encode_action(sr, xa,
+ sr->sr_ncr.nc_action, sr->sr_ncr.nc_fname)) {
+ rc = SDRC_SUCCESS;
+ break;
+ }
+ /*
+ * This error says "many things changed".
+ */
+ err.status = NT_STATUS_NOTIFY_ENUM_DIR;
+ err.errcls = ERRDOS;
+ err.errcode = ERROR_NOTIFY_ENUM_DIR;
+ smbsr_set_error(sr, &err);
+ rc = SDRC_ERROR;
break;
case SMB_REQ_STATE_CANCELED:
@@ -344,284 +244,226 @@ smb_reply_notify_change_request(smb_request_t *sr)
err.errcls = ERRDOS;
err.errcode = ERROR_OPERATION_ABORTED;
smbsr_set_error(sr, &err);
-
- (void) smb_mbc_encodef(&sr->reply, "bwbw",
- (short)0, 0L, (short)0, 0L);
- sr->smb_wct = 0;
- sr->smb_bcc = 0;
+ rc = SDRC_ERROR;
break;
+
default:
ASSERT(0);
+ err.status = NT_STATUS_INTERNAL_ERROR;
+ err.errcls = ERRDOS;
+ err.errcode = ERROR_INTERNAL_ERROR;
+ smbsr_set_error(sr, &err);
+ rc = SDRC_ERROR;
+ break;
}
- mutex_exit(&sr->sr_mutex);
- /* Setup the header */
- (void) smb_mbc_poke(&sr->reply, 0, SMB_HEADER_ED_FMT,
- sr->first_smb_com,
- sr->smb_rcls,
- sr->smb_reh,
- sr->smb_err,
- sr->smb_flg | SMB_FLAGS_REPLY,
- sr->smb_flg2,
- sr->smb_pid_high,
- sr->smb_sig,
- sr->smb_tid,
- sr->smb_pid,
- sr->smb_uid,
- sr->smb_mid);
-
- if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
- smb_sign_reply(sr, NULL);
-
- /* send the reply */
- DTRACE_PROBE1(ncr__reply, struct smb_request *, sr)
- (void) smb_session_send(sr->session, 0, &sr->reply);
- smbsr_cleanup(sr);
+ if (sr->sr_ncr.nc_fname != NULL) {
+ kmem_free(sr->sr_ncr.nc_fname, MAXNAMELEN);
+ sr->sr_ncr.nc_fname = NULL;
+ }
- mutex_enter(&sr->sr_mutex);
- sr->sr_state = SMB_REQ_STATE_COMPLETED;
- mutex_exit(&sr->sr_mutex);
- smb_srqueue_runq_exit(srq);
- smb_request_free(sr);
+ return (rc);
}
/*
- * smb_process_session_notify_change_queue
+ * Encode a FILE_NOTIFY_INFORMATION struct.
*
- * This function traverses notify change request queue and sends
- * cancel replies to all of requests that are related to a specific
- * session.
+ * We only ever put one of these in a response, so this
+ * does not bother handling appending additional ones.
*/
-void
-smb_process_session_notify_change_queue(
- smb_session_t *session,
- smb_tree_t *tree)
+static int
+smb_notify_encode_action(struct smb_request *sr, struct smb_xa *xa,
+ uint32_t action, char *fname)
{
- smb_request_t *sr;
- smb_request_t *tmp;
- boolean_t sig = B_FALSE;
+ uint32_t namelen;
+ int rc;
- smb_slist_enter(&smb_ncr_list);
- smb_slist_enter(&smb_nce_list);
- sr = smb_slist_head(&smb_ncr_list);
- while (sr) {
- ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
- tmp = smb_slist_next(&smb_ncr_list, sr);
- if ((sr->session == session) &&
- (tree == NULL || sr->tid_tree == tree)) {
- mutex_enter(&sr->sr_mutex);
- switch (sr->sr_state) {
- case SMB_REQ_STATE_WAITING_EVENT:
- smb_slist_obj_move(
- &smb_nce_list,
- &smb_ncr_list,
- sr);
- smb_srqueue_waitq_enter(
- sr->session->s_srqueue);
- sr->sr_state = SMB_REQ_STATE_CANCELED;
- sig = B_TRUE;
- break;
- default:
- ASSERT(0);
- break;
- }
- mutex_exit(&sr->sr_mutex);
- }
- sr = tmp;
- }
- smb_slist_exit(&smb_nce_list);
- smb_slist_exit(&smb_ncr_list);
- if (sig)
- smb_thread_signal(&smb_thread_notify_daemon);
-}
+ if (action < FILE_ACTION_ADDED ||
+ action > FILE_ACTION_MODIFIED_STREAM)
+ return (-1);
-/*
- * smb_process_file_notify_change_queue
- *
- * This function traverses notify change request queue and sends
- * cancel replies to all of requests that are related to the
- * specified file.
- */
-void
-smb_process_file_notify_change_queue(struct smb_ofile *of)
-{
- smb_request_t *sr;
- smb_request_t *tmp;
- boolean_t sig = B_FALSE;
+ namelen = smb_ascii_or_unicode_strlen(sr, fname);
+ if (namelen == 0)
+ return (-1);
- smb_slist_enter(&smb_ncr_list);
- smb_slist_enter(&smb_nce_list);
- sr = smb_slist_head(&smb_ncr_list);
- while (sr) {
- ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
- tmp = smb_slist_next(&smb_ncr_list, sr);
- if (sr->fid_ofile == of) {
- mutex_enter(&sr->sr_mutex);
- switch (sr->sr_state) {
- case SMB_REQ_STATE_WAITING_EVENT:
- smb_slist_obj_move(&smb_nce_list,
- &smb_ncr_list, sr);
- smb_srqueue_waitq_enter(
- sr->session->s_srqueue);
- sr->sr_state = SMB_REQ_STATE_CANCELED;
- sig = B_TRUE;
- break;
- default:
- ASSERT(0);
- break;
- }
- mutex_exit(&sr->sr_mutex);
- }
- sr = tmp;
- }
- smb_slist_exit(&smb_nce_list);
- smb_slist_exit(&smb_ncr_list);
- if (sig)
- smb_thread_signal(&smb_thread_notify_daemon);
+ rc = smb_mbc_encodef(&xa->rep_data_mb, "%lllu", sr,
+ 0, /* NextEntryOffset */
+ action, namelen, fname);
+ return (rc);
}
/*
- * smb_reply_specific_cancel_request
+ * smb_notify_file_closed
*
- * This function searches global request list for a specific request. If found,
- * moves the request to event queue and kicks the notify change daemon.
+ * Cancel any change-notify calls on this open file.
*/
-
void
-smb_reply_specific_cancel_request(struct smb_request *zsr)
+smb_notify_file_closed(struct smb_ofile *of)
{
+ smb_session_t *ses;
smb_request_t *sr;
- smb_request_t *tmp;
- boolean_t sig = B_FALSE;
+ smb_slist_t *list;
- smb_slist_enter(&smb_ncr_list);
- smb_slist_enter(&smb_nce_list);
- sr = smb_slist_head(&smb_ncr_list);
+ SMB_OFILE_VALID(of);
+ ses = of->f_session;
+ SMB_SESSION_VALID(ses);
+ list = &ses->s_req_list;
+
+ smb_slist_enter(list);
+
+ sr = smb_slist_head(list);
while (sr) {
- ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
- tmp = smb_slist_next(&smb_ncr_list, sr);
- if ((sr->session == zsr->session) &&
- (sr->smb_uid == zsr->smb_uid) &&
- (sr->smb_pid == zsr->smb_pid) &&
- (sr->smb_tid == zsr->smb_tid) &&
- (sr->smb_mid == zsr->smb_mid)) {
- mutex_enter(&sr->sr_mutex);
- switch (sr->sr_state) {
- case SMB_REQ_STATE_WAITING_EVENT:
- smb_slist_obj_move(&smb_nce_list,
- &smb_ncr_list, sr);
- smb_srqueue_waitq_enter(
- sr->session->s_srqueue);
- sr->sr_state = SMB_REQ_STATE_CANCELED;
- sig = B_TRUE;
- break;
- default:
- ASSERT(0);
- break;
- }
- mutex_exit(&sr->sr_mutex);
+ SMB_REQ_VALID(sr);
+ if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
+ sr->fid_ofile == of) {
+ smb_request_cancel(sr);
}
- sr = tmp;
+ sr = smb_slist_next(list, sr);
}
- smb_slist_exit(&smb_nce_list);
- smb_slist_exit(&smb_ncr_list);
- if (sig)
- smb_thread_signal(&smb_thread_notify_daemon);
+
+ smb_slist_exit(list);
}
+
/*
- * smb_process_node_notify_change_queue
+ * smb_notify_event
+ *
+ * Post an event to the watchers on a given node.
+ *
+ * This makes one exception for RENAME, where we expect a
+ * pair of events for the {old,new} directory element names.
+ * This only delivers an event for the "new" name.
+ *
+ * The event delivery mechanism does not implement delivery of
+ * multiple events for one "NT Notify" call. One could do that,
+ * but modern clients don't actually use the event data. They
+ * set a max. received data size of zero, which means we discard
+ * the data and send the special "lots changed" error instead.
+ * Given that, there's not really any point in implementing the
+ * delivery of multiple events. In fact, we don't even need to
+ * implement single event delivery, but do so for completeness,
+ * for debug convenience, and to be nice to older clients that
+ * may actually want some event data instead of the error.
*
- * This function searches notify change request queue and sends
- * 'NODE MODIFIED' reply to all requests which are related to a
- * specific node.
- * WatchTree flag: We handle this flag in a special manner just
- * for DAVE clients. When something is changed, we notify all
- * requests which came from DAVE clients on the same volume which
- * has been modified. We don't care about the tree that they wanted
- * us to monitor. any change in any part of the volume will lead
- * to notifying all notify change requests from DAVE clients on the
- * different parts of the volume hierarchy.
+ * Given that we only deliver a single event for an "NT Notify"
+ * caller, we want to deliver the "new" name event. (The "old"
+ * name event is less important, even ignored by some clients.)
+ * Since we know these are delivered in pairs, we can simply
+ * discard the "old" name event, knowing that the "new" name
+ * event will be delivered immediately afterwards.
+ *
+ * So, why do event sources post the "old name" event at all?
+ * (1) For debugging, so we see both {old,new} names here.
+ * (2) If in the future someone decides to implement the
+ * delivery of both {old,new} events, the changes can be
+ * mostly isolated to this file.
*/
void
-smb_process_node_notify_change_queue(smb_node_t *node)
+smb_notify_event(smb_node_t *node, uint_t action, const char *name)
{
smb_request_t *sr;
- smb_request_t *tmp;
- smb_node_t *nc_node;
- boolean_t sig = B_FALSE;
+ smb_node_fcn_t *fcn;
- ASSERT(node->n_magic == SMB_NODE_MAGIC);
+ SMB_NODE_VALID(node);
+ fcn = &node->n_fcn;
- if (!(node->flags & NODE_FLAGS_NOTIFY_CHANGE))
- return;
+ if (action == FILE_ACTION_RENAMED_OLD_NAME)
+ return; /* see above */
- node->flags |= NODE_FLAGS_CHANGED;
+ mutex_enter(&fcn->fcn_mutex);
- smb_slist_enter(&smb_ncr_list);
- smb_slist_enter(&smb_nce_list);
- sr = smb_slist_head(&smb_ncr_list);
+ sr = list_head(&fcn->fcn_watchers);
while (sr) {
- ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
- tmp = smb_slist_next(&smb_ncr_list, sr);
-
- nc_node = sr->sr_ncr.nc_node;
- if (nc_node == node) {
- mutex_enter(&sr->sr_mutex);
- switch (sr->sr_state) {
- case SMB_REQ_STATE_WAITING_EVENT:
- smb_slist_obj_move(&smb_nce_list,
- &smb_ncr_list, sr);
- smb_srqueue_waitq_enter(
- sr->session->s_srqueue);
- sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED;
- sig = B_TRUE;
- break;
- default:
- ASSERT(0);
- break;
- }
- mutex_exit(&sr->sr_mutex);
- }
- sr = tmp;
+ smb_notify_sr(sr, action, name);
+ sr = list_next(&fcn->fcn_watchers, sr);
}
- smb_slist_exit(&smb_nce_list);
- smb_slist_exit(&smb_ncr_list);
- if (sig)
- smb_thread_signal(&smb_thread_notify_daemon);
+
+ mutex_exit(&fcn->fcn_mutex);
}
/*
- * smb_notify_change_daemon
+ * What completion filter (masks) apply to each of the
+ * FILE_ACTION_... events.
+ */
+static const uint32_t
+smb_notify_action_mask[] = {
+ /* 0: Special, used by smb_node_notify_parents() */
+ NODE_FLAGS_WATCH_TREE,
+
+ /* FILE_ACTION_ADDED */
+ FILE_NOTIFY_CHANGE_NAME,
+
+ /* FILE_ACTION_REMOVED */
+ FILE_NOTIFY_CHANGE_NAME,
+
+ /* FILE_ACTION_MODIFIED */
+ FILE_NOTIFY_CHANGE_ATTRIBUTES |
+ FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE |
+ FILE_NOTIFY_CHANGE_LAST_ACCESS |
+ FILE_NOTIFY_CHANGE_CREATION |
+ FILE_NOTIFY_CHANGE_EA |
+ FILE_NOTIFY_CHANGE_SECURITY,
+
+ /* FILE_ACTION_RENAMED_OLD_NAME */
+ FILE_NOTIFY_CHANGE_NAME,
+
+ /* FILE_ACTION_RENAMED_NEW_NAME */
+ FILE_NOTIFY_CHANGE_NAME,
+
+ /* FILE_ACTION_ADDED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_NAME,
+
+ /* FILE_ACTION_REMOVED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_NAME,
+
+ /* FILE_ACTION_MODIFIED_STREAM */
+ FILE_NOTIFY_CHANGE_STREAM_SIZE |
+ FILE_NOTIFY_CHANGE_STREAM_WRITE,
+};
+static const int smb_notify_action_nelm =
+ sizeof (smb_notify_action_mask) /
+ sizeof (smb_notify_action_mask[0]);
+
+/*
+ * smb_notify_sr
*
- * This function processes notify change event list and send appropriate
- * responses to the requests. This function executes in the system as an
- * indivdual thread.
+ * Post an event to an smb request waiting on some node.
+ *
+ * Note that node->fcn.mutex is held. This implies a
+ * lock order: node->fcn.mutex, then sr_mutex
*/
static void
-smb_notify_change_daemon(smb_thread_t *thread, void *arg)
+smb_notify_sr(smb_request_t *sr, uint_t action, const char *name)
{
- _NOTE(ARGUNUSED(arg))
+ smb_notify_change_req_t *ncr;
+ uint32_t mask;
- smb_request_t *sr;
- smb_request_t *tmp;
- list_t sr_list;
-
- list_create(&sr_list, sizeof (smb_request_t),
- offsetof(smb_request_t, sr_ncr.nc_lnd));
-
- while (smb_thread_continue(thread)) {
-
- while (smb_slist_move_tail(&sr_list, &smb_nce_list)) {
- sr = list_head(&sr_list);
- while (sr) {
- ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
- tmp = list_next(&sr_list, sr);
- list_remove(&sr_list, sr);
- smb_reply_notify_change_request(sr);
- sr = tmp;
- }
- }
+ SMB_REQ_VALID(sr);
+ ncr = &sr->sr_ncr;
+
+ /*
+ * Compute the completion filter mask bits for which
+ * we will signal waiting notify requests.
+ */
+ if (action >= smb_notify_action_nelm) {
+ ASSERT(0);
+ return;
}
- list_destroy(&sr_list);
+ mask = smb_notify_action_mask[action];
+
+ mutex_enter(&sr->sr_mutex);
+ if (sr->sr_state == SMB_REQ_STATE_WAITING_EVENT &&
+ (ncr->nc_flags & mask) != 0) {
+ sr->sr_state = SMB_REQ_STATE_EVENT_OCCURRED;
+ /*
+ * Save event data in the sr_ncr field so the
+ * reply handler can return it.
+ */
+ ncr->nc_action = action;
+ if (name != NULL)
+ (void) strlcpy(ncr->nc_fname, name, MAXNAMELEN);
+ cv_signal(&ncr->nc_cv);
+ }
+ mutex_exit(&sr->sr_mutex);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index 6855a795bd..39635170e9 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -324,11 +325,11 @@ smb_ofile_close(smb_ofile_t *of, uint32_t last_wtime)
of->f_cr);
/*
- * Cancel any notify change requests related
- * to this open instance.
+ * Cancel any notify change requests that
+ * may be using this open instance.
*/
- if (of->f_node->flags & NODE_FLAGS_NOTIFY_CHANGE)
- smb_process_file_notify_change_queue(of);
+ if (of->f_node->n_fcn.fcn_count)
+ smb_notify_file_closed(of);
smb_server_dec_files(of->f_server);
}
atomic_dec_32(&of->f_tree->t_open_files);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_rename.c b/usr/src/uts/common/fs/smbsrv/smb_rename.c
index ca09cf4935..8541fdc847 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_rename.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_rename.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/synch.h>
@@ -480,10 +481,25 @@ smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
src_dnode, src_fnode->od_name,
dst_dnode, new_name);
- smb_rename_release_src(sr);
+ if (rc == 0) {
+ /*
+ * Note that renames in the same directory are normally
+ * delivered in {old,new} pairs, and clients expect them
+ * in that order, if both events are delivered.
+ */
+ int a_src, a_dst; /* action codes */
+ if (src_dnode == dst_dnode) {
+ a_src = FILE_ACTION_RENAMED_OLD_NAME;
+ a_dst = FILE_ACTION_RENAMED_NEW_NAME;
+ } else {
+ a_src = FILE_ACTION_REMOVED;
+ a_dst = FILE_ACTION_ADDED;
+ }
+ smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
+ smb_node_notify_change(dst_dnode, a_dst, new_name);
+ }
- if (rc == 0)
- smb_node_notify_change(dst_dnode);
+ smb_rename_release_src(sr);
if (dst_fqi->fq_fnode) {
smb_node_end_crit(dst_fnode);
@@ -617,9 +633,12 @@ smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
+ if (rc == 0) {
+ smb_node_notify_change(dst_fqi->fq_dnode,
+ FILE_ACTION_ADDED, dst_fqi->fq_last_comp);
+ }
+
smb_rename_release_src(sr);
- if (rc == 0)
- smb_node_notify_change(dst_fqi->fq_dnode);
smb_node_release(dst_fqi->fq_dnode);
return (rc);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 9eeb7d8d50..3654744569 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -294,8 +294,6 @@ smb_server_svc_init(void)
continue;
if (rc = smb_fem_init())
continue;
- if (rc = smb_notify_init())
- continue;
if (rc = smb_net_init())
continue;
smb_llist_init();
@@ -306,7 +304,6 @@ smb_server_svc_init(void)
smb_llist_fini();
smb_net_fini();
- smb_notify_fini();
smb_fem_fini();
smb_node_fini();
smb_vop_fini();
@@ -328,7 +325,6 @@ smb_server_svc_fini(void)
if (smb_llist_get_count(&smb_servers) == 0) {
smb_llist_fini();
smb_net_fini();
- smb_notify_fini();
smb_fem_fini();
smb_node_fini();
smb_oplock_fini();
diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c
index 6e1bd6d69a..f2a337158f 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.c
@@ -19,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/atomic.h>
#include <sys/strsubr.h>
@@ -392,13 +392,14 @@ smb_request_cancel(smb_request_t *sr)
break;
case SMB_REQ_STATE_WAITING_EVENT:
- case SMB_REQ_STATE_EVENT_OCCURRED:
/*
- * Cancellations for these states are handled by the
- * notify-change code
+ * This request is waiting in change notify.
*/
+ sr->sr_state = SMB_REQ_STATE_CANCELED;
+ cv_signal(&sr->sr_ncr.nc_cv);
break;
+ case SMB_REQ_STATE_EVENT_OCCURRED:
case SMB_REQ_STATE_COMPLETED:
case SMB_REQ_STATE_CANCELED:
/*
@@ -785,8 +786,6 @@ smb_session_cancel_requests(
{
smb_request_t *sr;
- smb_process_session_notify_change_queue(session, tree);
-
smb_slist_enter(&session->s_req_list);
sr = smb_slist_head(&session->s_req_list);
@@ -1076,6 +1075,7 @@ smb_request_alloc(smb_session_t *session, int req_length)
bzero(sr, sizeof (smb_request_t));
mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&sr->sr_ncr.nc_cv, NULL, CV_DEFAULT, NULL);
smb_srm_init(sr);
sr->session = session;
sr->sr_server = session->s_server;
@@ -1104,6 +1104,7 @@ smb_request_free(smb_request_t *sr)
ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
ASSERT(sr->session);
ASSERT(sr->r_xa == NULL);
+ ASSERT(sr->sr_ncr.nc_fname == NULL);
if (sr->fid_ofile != NULL) {
smb_ofile_request_complete(sr->fid_ofile);
@@ -1132,6 +1133,7 @@ smb_request_free(smb_request_t *sr)
m_freem(sr->raw_data.chain);
sr->sr_magic = 0;
+ cv_destroy(&sr->sr_ncr.nc_cv);
mutex_destroy(&sr->sr_mutex);
kmem_cache_free(sr->sr_cache, sr);
}
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index 7c5c9f3b30..3006153d08 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -173,9 +173,6 @@ SMB_COM_DECL(write_raw);
SMB_NT_TRANSACT_DECL(nt_transact_create);
-int smb_notify_init(void);
-void smb_notify_fini(void);
-
smb_sdrc_t smb_nt_transact_notify_change(smb_request_t *, smb_xa_t *);
smb_sdrc_t smb_nt_transact_query_security_info(smb_request_t *, smb_xa_t *);
smb_sdrc_t smb_nt_transact_set_security_info(smb_request_t *, smb_xa_t *);
@@ -208,8 +205,6 @@ void smb_close_all_connections(void);
int smb_net_id(uint32_t);
-void smb_process_file_notify_change_queue(smb_ofile_t *of);
-
/*
* oplock functions - node operations
*/
@@ -446,7 +441,10 @@ uint32_t smb_node_open_check(smb_node_t *, uint32_t, uint32_t);
DWORD smb_node_rename_check(smb_node_t *);
DWORD smb_node_delete_check(smb_node_t *);
boolean_t smb_node_share_check(smb_node_t *);
-void smb_node_notify_change(smb_node_t *);
+
+void smb_node_fcn_subscribe(smb_node_t *, smb_request_t *);
+void smb_node_fcn_unsubscribe(smb_node_t *, smb_request_t *);
+void smb_node_notify_change(smb_node_t *, uint_t, const char *);
void smb_node_notify_parents(smb_node_t *);
int smb_node_getattr(smb_request_t *, smb_node_t *, smb_attr_t *);
int smb_node_setattr(smb_request_t *, smb_node_t *, cred_t *,
@@ -477,9 +475,9 @@ void smb_vfs_rele(smb_export_t *, vfs_t *);
void smb_vfs_rele_all(smb_export_t *);
/* NOTIFY CHANGE */
-void smb_process_session_notify_change_queue(smb_session_t *, smb_tree_t *);
-void smb_process_node_notify_change_queue(smb_node_t *);
-void smb_reply_specific_cancel_request(smb_request_t *);
+
+void smb_notify_event(smb_node_t *, uint_t, const char *);
+void smb_notify_file_closed(smb_ofile_t *of);
void smb_fem_fcn_install(smb_node_t *);
void smb_fem_fcn_uninstall(smb_node_t *);
diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h
index 4ce19673de..53bceab332 100644
--- a/usr/src/uts/common/smbsrv/smb_ktypes.h
+++ b/usr/src/uts/common/smbsrv/smb_ktypes.h
@@ -452,12 +452,18 @@ typedef struct {
} smb_rwx_t;
/* NOTIFY CHANGE */
+typedef struct smb_node_fcn {
+ kmutex_t fcn_mutex;
+ uint32_t fcn_count;
+ list_t fcn_watchers; /* smb_request_t, sr_ncr.nc_lnd */
+} smb_node_fcn_t;
typedef struct smb_notify_change_req {
- list_node_t nc_lnd;
- struct smb_node *nc_node;
- uint32_t nc_reply_type;
+ list_node_t nc_lnd; /* n_fcn.fcn_watchers */
+ kcondvar_t nc_cv; /* prot: sr_mutex */
uint32_t nc_flags;
+ uint32_t nc_action;
+ char *nc_fname;
} smb_notify_change_req_t;
/*
@@ -638,6 +644,7 @@ typedef struct smb_node {
volatile int waiting_event;
smb_times_t n_timestamps;
u_offset_t n_allocsz;
+ smb_node_fcn_t n_fcn;
smb_oplock_t n_oplock;
struct smb_node *n_dnode;
struct smb_node *n_unode;
@@ -652,10 +659,6 @@ typedef struct smb_node {
#define NODE_FLAGS_DFSLINK 0x00002000
#define NODE_FLAGS_VFSROOT 0x00004000
#define NODE_FLAGS_SYSTEM 0x00008000
-#define NODE_FLAGS_WATCH_TREE 0x10000000
-#define NODE_FLAGS_NOTIFY_CHANGE \
- (NODE_FLAGS_WATCH_TREE | FILE_NOTIFY_VALID_MASK)
-#define NODE_FLAGS_CHANGED 0x08000000
#define NODE_FLAGS_WRITE_THROUGH 0x00100000
#define NODE_XATTR_DIR 0x01000000
#define NODE_FLAGS_DELETE_ON_CLOSE 0x40000000
@@ -1602,7 +1605,6 @@ typedef struct smb_request {
kmutex_t sr_mutex;
list_node_t sr_session_lnd;
smb_req_state_t sr_state;
- boolean_t sr_keep;
kmem_cache_t *sr_cache;
struct smb_server *sr_server;
pid_t *sr_pid;