/* * smb_handle_write_raw * * Called from smb_session_daemon() when the SMB command is SMB_COM_WRITE_RAW. * Dispatches the command to the worker thread and waits until the worker * has completed processing the command. * * Returns 0 for success, non-zero for failure */ int smb_handle_write_raw(smb_session_t *session, smb_request_t *sr) { int drop_reason = 0; /* * Set flag to indicate that we are waiting for raw data. The * worker thread will actually retrieve the raw data directly * from the socket. This should be the only case when a worker * thread reads from the session socket. When the data is read * the worker will clear the flag. */ smb_rwx_rwenter(&session->s_lock, RW_WRITER); switch (session->s_state) { case SMB_SESSION_STATE_NEGOTIATED: case SMB_SESSION_STATE_OPLOCK_BREAKING: session->s_state = SMB_SESSION_STATE_WRITE_RAW_ACTIVE; smb_rwx_rwexit(&session->s_lock); smb_srqueue_waitq_enter(session->s_srqueue); sr->sr_state = SMB_REQ_STATE_SUBMITTED; (void) taskq_dispatch(session->s_server->sv_worker_pool, smb_session_worker, sr, TQ_SLEEP); smb_rwx_rwenter(&session->s_lock, RW_READER); while (session->s_state == SMB_SESSION_STATE_WRITE_RAW_ACTIVE) { (void) smb_rwx_rwwait(&session->s_lock, -1); } drop_reason = session->s_write_raw_status; break; default: drop_reason = 21; break; } smb_rwx_rwexit(&session->s_lock); return (drop_reason); }
/* * This is the SMB2 handler for new smb requests, called from * smb_session_reader after SMB negotiate is done. For most SMB2 * requests, we just enqueue them for the smb_session_worker to * execute via the task queue, so they can block for resources * without stopping the reader thread. A few protocol messages * are special cases and are handled directly here in the reader * thread so they don't wait for taskq scheduling. * * This function must either enqueue the new request for * execution via the task queue, or execute it directly * and then free it. If this returns non-zero, the caller * will drop the session. */ int smb2sr_newrq(smb_request_t *sr) { uint32_t magic; uint16_t command; int rc; magic = LE_IN32(sr->sr_request_buf); if (magic != SMB2_PROTOCOL_MAGIC) { smb_request_free(sr); /* will drop the connection */ return (EPROTO); } /* * Execute Cancel requests immediately, (here in the * reader thread) so they won't wait for any other * commands we might already have in the task queue. * Cancel also skips signature verification and * does not consume a sequence number. * [MS-SMB2] 3.2.4.24 Cancellation... */ command = LE_IN16((uint8_t *)sr->sr_request_buf + 12); if (command == SMB2_CANCEL) { rc = smb2sr_newrq_cancel(sr); smb_request_free(sr); return (rc); } /* * XXX With SMB3 this is supposed to increment based on * the number of credits consumed by a request. Todo */ if (sr->session->signing.flags & SMB_SIGNING_ENABLED) { /* XXX MS-SMB2 is unclear on this. todo */ sr->session->signing.seqnum++; sr->sr_seqnum = sr->session->signing.seqnum; sr->reply_seqnum = sr->sr_seqnum; } /* * Submit the request to the task queue, which calls * smb2_dispatch_request when the workload permits. */ sr->sr_time_submitted = gethrtime(); sr->sr_state = SMB_REQ_STATE_SUBMITTED; sr->work_func = smb2sr_work; smb_srqueue_waitq_enter(sr->session->s_srqueue); (void) taskq_dispatch(sr->session->s_server->sv_worker_pool, smb_session_worker, sr, TQ_SLEEP); return (0); }
/* * smb_process_node_notify_change_queue * * 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. */ void smb_process_node_notify_change_queue(smb_node_t *node) { smb_request_t *sr; smb_request_t *tmp; smb_node_t *nc_node; boolean_t sig = B_FALSE; ASSERT(node->n_magic == SMB_NODE_MAGIC); if (!(node->flags & NODE_FLAGS_NOTIFY_CHANGE)) return; node->flags |= NODE_FLAGS_CHANGED; 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); 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_slist_exit(&smb_nce_list); smb_slist_exit(&smb_ncr_list); if (sig) smb_thread_signal(&smb_thread_notify_daemon); }
/* * smb_process_session_notify_change_queue * * This function traverses notify change request queue and sends * cancel replies to all of requests that are related to a specific * session. */ void smb_process_session_notify_change_queue( smb_session_t *session, smb_tree_t *tree) { smb_request_t *sr; smb_request_t *tmp; boolean_t sig = B_FALSE; 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); }
void smb_reply_specific_cancel_request(struct smb_request *zsr) { smb_request_t *sr; smb_request_t *tmp; boolean_t sig = B_FALSE; 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 == 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); } sr = tmp; } smb_slist_exit(&smb_nce_list); smb_slist_exit(&smb_ncr_list); if (sig) smb_thread_signal(&smb_thread_notify_daemon); }