// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> * Copyright (C) 2018 Samsung Electronics Co., Ltd. */ #include "glob.h"#include "oplock.h"#include "misc.h"#include <linux/sched/signal.h>#include <linux/workqueue.h>#include <linux/sysfs.h>#include <linux/module.h>#include <linux/moduleparam.h> #include "server.h"#include "smb_common.h"#include "smb2pdu.h"#include "../common/smb2status.h"#include "connection.h"#include "transport_ipc.h"#include "mgmt/user_session.h"#include "crypto_ctx.h"#include "auth.h"#include "stats.h"#include "compress.h"#include "mgmt/share_config.h"#include "mgmt/tree_connect.h" int ksmbd_debug_types; struct ksmbd_server_config server_conf; enum SERVER_CTRL_TYPE { SERVER_CTRL_TYPE_INIT, SERVER_CTRL_TYPE_RESET,}; struct server_ctrl_struct { int type; struct work_struct ctrl_work;}; static DEFINE_MUTEX(ctrl_lock); static int ___server_conf_set(int idx, char *val){ if (idx >= ARRAY_SIZE(server_conf.conf)) return -EINVAL; if (!val || val[0] == 0x00) return -EINVAL; kfree(server_conf.conf[idx]); server_conf.conf[idx] = kstrdup(val, KSMBD_DEFAULT_GFP); if (!server_conf.conf[idx]) return -ENOMEM; return 0;} int ksmbd_set_netbios_name(char *v){ return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v);} int ksmbd_set_server_string(char *v){ return ___server_conf_set(SERVER_CONF_SERVER_STRING, v);} int ksmbd_set_work_group(char *v){ return ___server_conf_set(SERVER_CONF_WORK_GROUP, v);} char *ksmbd_netbios_name(void){ return server_conf.conf[SERVER_CONF_NETBIOS_NAME];} char *ksmbd_server_string(void){ return server_conf.conf[SERVER_CONF_SERVER_STRING];} char *ksmbd_work_group(void){ return server_conf.conf[SERVER_CONF_WORK_GROUP];} /** * check_conn_state() - check state of server thread connection * @work: smb work containing server thread information * * Return: 0 on valid connection, otherwise 1 to reconnect */static inline int check_conn_state(struct ksmbd_work *work){ struct smb_hdr *rsp_hdr; if (ksmbd_conn_exiting(work->conn) || ksmbd_conn_need_reconnect(work->conn)) { rsp_hdr = smb_get_msg(work->response_buf); rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; return 1; } return 0;} #define SERVER_HANDLER_CONTINUE 0#define SERVER_HANDLER_ABORT 1 static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, u16 *cmd){ struct smb_version_cmds *cmds; u16 command; bool signed_req; int ret; if (check_conn_state(work)) return SERVER_HANDLER_CONTINUE; if (ksmbd_verify_smb_message(work)) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; } command = conn->ops->get_cmd_val(work); *cmd = command; andx_again: if (command >= conn->max_cmds) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; } cmds = &conn->cmds[command]; if (!cmds->proc) { ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); return SERVER_HANDLER_ABORT; } signed_req = conn->ops->is_sign_req && conn->ops->is_sign_req(work, command); if (work->sess && work->sess->sign && !work->encrypted && !signed_req) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); return SERVER_HANDLER_ABORT; } if (work->sess && signed_req) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); return SERVER_HANDLER_ABORT; } } ret = cmds->proc(work); if (conn->ops->inc_reqs) { struct smb2_hdr *rsp = ksmbd_resp_buf_curr(work); conn->ops->inc_reqs(command, rsp->Status); } if (ret < 0) ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); /* AndX commands - chained request can return positive values */ else if (ret > 0) { command = ret; *cmd = command; goto andx_again; } if (work->send_no_response) return SERVER_HANDLER_ABORT; return SERVER_HANDLER_CONTINUE;} static void __handle_ksmbd_work(struct ksmbd_work *work, struct ksmbd_conn *conn){ u16 command = 0; int rc; bool is_chained = false; if (conn->ops->is_transform_hdr && conn->ops->is_transform_hdr(work->request_buf)) { rc = conn->ops->decrypt_req(work); if (rc < 0) { ksmbd_conn_abort(conn); return; } work->encrypted = true; /* * SMB3 applies compression before encryption. The receive loop * handles a plain compression transform before allocating work, but * an encrypted request exposes that transform only after decryption. */ if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId == SMB2_COMPRESSION_TRANSFORM_ID) { rc = ksmbd_decompress_work_request(work); if (rc < 0) { ksmbd_conn_abort(conn); return; } } /* The decrypted payload must now be a complete SMB2 request. */ if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId != SMB2_PROTO_NUMBER || get_rfc1002_len(work->request_buf) < sizeof(struct smb2_pdu)) { ksmbd_conn_abort(conn); return; } } if (conn->ops->allocate_rsp_buf(work)) return; rc = conn->ops->init_rsp_hdr(work); if (rc) { /* either uid or tid is not correct */ conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); goto send; } do { if (conn->ops->check_user_session) { rc = conn->ops->check_user_session(work); if (rc < 0) { if (rc == -EINVAL) conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); else if (rc == -EKEYEXPIRED) conn->ops->set_rsp_status(work, STATUS_NETWORK_SESSION_EXPIRED); else conn->ops->set_rsp_status(work, STATUS_USER_SESSION_DELETED); if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) { struct smb2_hdr *rsp_hdr; rsp_hdr = ksmbd_resp_buf_curr(work); if (rc == -EKEYEXPIRED && work->sess && conn->ops->set_sign_rsp) conn->ops->set_sign_rsp(work); else rsp_hdr->Flags |= SMB2_FLAGS_SIGNED; } goto send; } else if (rc > 0) { rc = conn->ops->get_ksmbd_tcon(work); if (rc < 0) { if (rc == -EINVAL) conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); else conn->ops->set_rsp_status(work, STATUS_NETWORK_NAME_DELETED); goto send; } if (work->tcon && test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ENCRYPT_DATA) && !work->encrypted) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); goto send; } } } rc = __process_request(work, conn, &command); if (rc == SERVER_HANDLER_ABORT) { smb2_complete_request_open(work); break; } /* * Call smb2_set_rsp_credits() function to set number of credits * granted in hdr of smb2 response. */ if (conn->ops->set_rsp_credits) { spin_lock(&conn->credits_lock); rc = conn->ops->set_rsp_credits(work); spin_unlock(&conn->credits_lock); if (rc < 0) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); smb2_complete_request_open(work); goto send; } } smb2_complete_request_open(work); is_chained = is_chained_smb2_message(work); if (work->sess && (work->sess->sign || smb3_11_final_sess_setup_resp(work) || conn->ops->is_sign_req(work, command))) { if (command == SMB2_SESSION_SETUP_HE && work->sess->dialect >= SMB30_PROT_ID && conn->dialect < SMB30_PROT_ID) smb3_set_sign_rsp(work); else conn->ops->set_sign_rsp(work); } } while (is_chained == true); send: smb2_complete_request_open(work); /* * Release any credit charge still outstanding for this request. On * the normal path smb2_set_rsp_credits() already returned it, but the * abort, error and send-no-response paths skip that call, so the * charge would otherwise leak and eventually exhaust the connection's * outstanding credit window. */ if (work->credit_charge) { spin_lock(&conn->credits_lock); conn->outstanding_credits -= work->credit_charge; work->credit_charge = 0; spin_unlock(&conn->credits_lock); } if (work->tcon) ksmbd_tree_connect_put(work->tcon); smb3_preauth_hash_rsp(work); /* * Preauthentication hashes cover the original SMB2 response. Apply the * transport compression wrapper only after updating the hash. */ if (work->compress_response) { rc = ksmbd_compress_response(work); if (rc < 0) ksmbd_debug(CONN, "Failed to compress response: %d\n", rc); } if (work->sess && work->sess->enc && work->encrypted && conn->ops->encrypt_resp) { rc = conn->ops->encrypt_resp(work); if (rc < 0) conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); } if (work->sess) ksmbd_user_session_put(work->sess); ksmbd_conn_write(work);} /** * handle_ksmbd_work() - process pending smb work requests * @wk: smb work containing request command buffer * * called by kworker threads to processing remaining smb work requests */static void handle_ksmbd_work(struct work_struct *wk){ struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); struct ksmbd_conn *conn = work->conn; atomic64_inc(&conn->stats.request_served); __handle_ksmbd_work(work, conn); ksmbd_conn_try_dequeue_request(work); ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn);} /** * queue_ksmbd_work() - queue a smb request to worker thread queue * for processing smb command and sending response * @conn: connection instance * * read remaining data from socket create and submit work. */static int queue_ksmbd_work(struct ksmbd_conn *conn){ struct ksmbd_work *work; int err; err = ksmbd_init_smb_server(conn); if (err) return 0; work = ksmbd_alloc_work_struct(); if (!work) { pr_err("allocation for work failed\n"); return -ENOMEM; } work->conn = conn; work->request_buf = conn->request_buf; conn->request_buf = NULL; ksmbd_conn_enqueue_request(work); ksmbd_conn_r_count_inc(conn); /* update activity on connection */ conn->last_active = jiffies; INIT_WORK(&work->work, handle_ksmbd_work); ksmbd_queue_work(work); return 0;} static int ksmbd_server_process_request(struct ksmbd_conn *conn){ return queue_ksmbd_work(conn);} static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn){ ksmbd_sessions_deregister(conn); destroy_lease_table(conn); return 0;} static void ksmbd_server_tcp_callbacks_init(void){ struct ksmbd_conn_ops ops; ops.process_fn = ksmbd_server_process_request; ops.terminate_fn = ksmbd_server_terminate_conn; ksmbd_conn_init_server_callbacks(&ops);} static void server_conf_free(void){ int i; for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { kfree(server_conf.conf[i]); server_conf.conf[i] = NULL; }} static int server_conf_init(void){ WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); server_conf.enforced_signing = 0; server_conf.min_protocol = ksmbd_min_protocol(); server_conf.max_protocol = ksmbd_max_protocol(); server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP;#ifdef CONFIG_SMB_SERVER_KERBEROS5 server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5;#endif server_conf.max_inflight_req = SMB2_MAX_CREDITS; return 0;} static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl){ int ret; ksmbd_proc_reset(); ret = ksmbd_conn_transport_init(); if (ret) { server_queue_ctrl_reset_work(); return; } pr_info("running\n"); WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING);} static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl){ ksmbd_ipc_soft_reset(); ksmbd_conn_transport_destroy(); ksmbd_stop_durable_scavenger(); server_conf_free(); server_conf_init(); WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP);} static void server_ctrl_handle_work(struct work_struct *work){ struct server_ctrl_struct *ctrl; ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); mutex_lock(&ctrl_lock); switch (ctrl->type) { case SERVER_CTRL_TYPE_INIT: server_ctrl_handle_init(ctrl); break; case SERVER_CTRL_TYPE_RESET: server_ctrl_handle_reset(ctrl); break; default: pr_err("Unknown server work type: %d\n", ctrl->type); } mutex_unlock(&ctrl_lock); kfree(ctrl);Lines 1–500 of 760. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> * Copyright (C) 2018 Samsung Electronics Co., Ltd. */ #include "glob.h" #include "oplock.h" #include "misc.h" #include <linux/sched/signal.h> #include <linux/workqueue.h> #include <linux/sysfs.h> #include <linux/module.h> #include <linux/moduleparam.h> #include "server.h" #include "smb_common.h" #include "smb2pdu.h" #include "../common/smb2status.h" #include "connection.h" #include "transport_ipc.h" #include "mgmt/user_session.h" #include "crypto_ctx.h" #include "auth.h" #include "stats.h" #include "compress.h" #include "mgmt/share_config.h" #include "mgmt/tree_connect.h" int ksmbd_debug_types; struct ksmbd_server_config server_conf; enum SERVER_CTRL_TYPE { SERVER_CTRL_TYPE_INIT, SERVER_CTRL_TYPE_RESET, }; struct server_ctrl_struct { int type; struct work_struct ctrl_work; }; static DEFINE_MUTEX(ctrl_lock); static int ___server_conf_set(int idx, char *val) { if (idx >= ARRAY_SIZE(server_conf.conf)) return -EINVAL; if (!val || val[0] == 0x00) return -EINVAL; kfree(server_conf.conf[idx]); server_conf.conf[idx] = kstrdup(val, KSMBD_DEFAULT_GFP); if (!server_conf.conf[idx]) return -ENOMEM; return 0; } int ksmbd_set_netbios_name(char *v) { return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); } int ksmbd_set_server_string(char *v) { return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); } int ksmbd_set_work_group(char *v) { return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); } char *ksmbd_netbios_name(void) { return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; } char *ksmbd_server_string(void) { return server_conf.conf[SERVER_CONF_SERVER_STRING]; } char *ksmbd_work_group(void) { return server_conf.conf[SERVER_CONF_WORK_GROUP]; } /** * check_conn_state() - check state of server thread connection * @work: smb work containing server thread information * * Return: 0 on valid connection, otherwise 1 to reconnect */ static inline int check_conn_state(struct ksmbd_work *work) { struct smb_hdr *rsp_hdr; if (ksmbd_conn_exiting(work->conn) || ksmbd_conn_need_reconnect(work->conn)) { rsp_hdr = smb_get_msg(work->response_buf); rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; return 1; } return 0; } #define SERVER_HANDLER_CONTINUE 0 #define SERVER_HANDLER_ABORT 1 static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, u16 *cmd) { struct smb_version_cmds *cmds; u16 command; bool signed_req; int ret; if (check_conn_state(work)) return SERVER_HANDLER_CONTINUE; if (ksmbd_verify_smb_message(work)) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; } command = conn->ops->get_cmd_val(work); *cmd = command; andx_again: if (command >= conn->max_cmds) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; } cmds = &conn->cmds[command]; if (!cmds->proc) { ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); return SERVER_HANDLER_ABORT; } signed_req = conn->ops->is_sign_req && conn->ops->is_sign_req(work, command); if (work->sess && work->sess->sign && !work->encrypted && !signed_req) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); return SERVER_HANDLER_ABORT; } if (work->sess && signed_req) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); return SERVER_HANDLER_ABORT; } } ret = cmds->proc(work); if (conn->ops->inc_reqs) { struct smb2_hdr *rsp = ksmbd_resp_buf_curr(work); conn->ops->inc_reqs(command, rsp->Status); } if (ret < 0) ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); /* AndX commands - chained request can return positive values */ else if (ret > 0) { command = ret; *cmd = command; goto andx_again; } if (work->send_no_response) return SERVER_HANDLER_ABORT; return SERVER_HANDLER_CONTINUE; } static void __handle_ksmbd_work(struct ksmbd_work *work, struct ksmbd_conn *conn) { u16 command = 0; int rc; bool is_chained = false; if (conn->ops->is_transform_hdr && conn->ops->is_transform_hdr(work->request_buf)) { rc = conn->ops->decrypt_req(work); if (rc < 0) { ksmbd_conn_abort(conn); return; } work->encrypted = true; /* * SMB3 applies compression before encryption. The receive loop * handles a plain compression transform before allocating work, but * an encrypted request exposes that transform only after decryption. */ if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId == SMB2_COMPRESSION_TRANSFORM_ID) { rc = ksmbd_decompress_work_request(work); if (rc < 0) { ksmbd_conn_abort(conn); return; } } /* The decrypted payload must now be a complete SMB2 request. */ if (((struct smb2_hdr *)smb_get_msg(work->request_buf))->ProtocolId != SMB2_PROTO_NUMBER || get_rfc1002_len(work->request_buf) < sizeof(struct smb2_pdu)) { ksmbd_conn_abort(conn); return; } } if (conn->ops->allocate_rsp_buf(work)) return; rc = conn->ops->init_rsp_hdr(work); if (rc) { /* either uid or tid is not correct */ conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); goto send; } do { if (conn->ops->check_user_session) { rc = conn->ops->check_user_session(work); if (rc < 0) { if (rc == -EINVAL) conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); else if (rc == -EKEYEXPIRED) conn->ops->set_rsp_status(work, STATUS_NETWORK_SESSION_EXPIRED); else conn->ops->set_rsp_status(work, STATUS_USER_SESSION_DELETED); if (conn->ops->is_sign_req(work, conn->ops->get_cmd_val(work))) { struct smb2_hdr *rsp_hdr; rsp_hdr = ksmbd_resp_buf_curr(work); if (rc == -EKEYEXPIRED && work->sess && conn->ops->set_sign_rsp) conn->ops->set_sign_rsp(work); else rsp_hdr->Flags |= SMB2_FLAGS_SIGNED; } goto send; } else if (rc > 0) { rc = conn->ops->get_ksmbd_tcon(work); if (rc < 0) { if (rc == -EINVAL) conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); else conn->ops->set_rsp_status(work, STATUS_NETWORK_NAME_DELETED); goto send; } if (work->tcon && test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_ENCRYPT_DATA) && !work->encrypted) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); goto send; } } } rc = __process_request(work, conn, &command); if (rc == SERVER_HANDLER_ABORT) { smb2_complete_request_open(work); break; } /* * Call smb2_set_rsp_credits() function to set number of credits * granted in hdr of smb2 response. */ if (conn->ops->set_rsp_credits) { spin_lock(&conn->credits_lock); rc = conn->ops->set_rsp_credits(work); spin_unlock(&conn->credits_lock); if (rc < 0) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); smb2_complete_request_open(work); goto send; } } smb2_complete_request_open(work); is_chained = is_chained_smb2_message(work); if (work->sess && (work->sess->sign || smb3_11_final_sess_setup_resp(work) || conn->ops->is_sign_req(work, command))) { if (command == SMB2_SESSION_SETUP_HE && work->sess->dialect >= SMB30_PROT_ID && conn->dialect < SMB30_PROT_ID) smb3_set_sign_rsp(work); else conn->ops->set_sign_rsp(work); } } while (is_chained == true); send: smb2_complete_request_open(work); /* * Release any credit charge still outstanding for this request. On * the normal path smb2_set_rsp_credits() already returned it, but the * abort, error and send-no-response paths skip that call, so the * charge would otherwise leak and eventually exhaust the connection's * outstanding credit window. */ if (work->credit_charge) { spin_lock(&conn->credits_lock); conn->outstanding_credits -= work->credit_charge; work->credit_charge = 0; spin_unlock(&conn->credits_lock); } if (work->tcon) ksmbd_tree_connect_put(work->tcon); smb3_preauth_hash_rsp(work); /* * Preauthentication hashes cover the original SMB2 response. Apply the * transport compression wrapper only after updating the hash. */ if (work->compress_response) { rc = ksmbd_compress_response(work); if (rc < 0) ksmbd_debug(CONN, "Failed to compress response: %d\n", rc); } if (work->sess && work->sess->enc && work->encrypted && conn->ops->encrypt_resp) { rc = conn->ops->encrypt_resp(work); if (rc < 0) conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); } if (work->sess) ksmbd_user_session_put(work->sess); ksmbd_conn_write(work); } /** * handle_ksmbd_work() - process pending smb work requests * @wk: smb work containing request command buffer * * called by kworker threads to processing remaining smb work requests */ static void handle_ksmbd_work(struct work_struct *wk) { struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); struct ksmbd_conn *conn = work->conn; atomic64_inc(&conn->stats.request_served); __handle_ksmbd_work(work, conn); ksmbd_conn_try_dequeue_request(work); ksmbd_free_work_struct(work); ksmbd_conn_r_count_dec(conn); } /** * queue_ksmbd_work() - queue a smb request to worker thread queue * for processing smb command and sending response * @conn: connection instance * * read remaining data from socket create and submit work. */ static int queue_ksmbd_work(struct ksmbd_conn *conn) { struct ksmbd_work *work; int err; err = ksmbd_init_smb_server(conn); if (err) return 0; work = ksmbd_alloc_work_struct(); if (!work) { pr_err("allocation for work failed\n"); return -ENOMEM; } work->conn = conn; work->request_buf = conn->request_buf; conn->request_buf = NULL; ksmbd_conn_enqueue_request(work); ksmbd_conn_r_count_inc(conn); /* update activity on connection */ conn->last_active = jiffies; INIT_WORK(&work->work, handle_ksmbd_work); ksmbd_queue_work(work); return 0; } static int ksmbd_server_process_request(struct ksmbd_conn *conn) { return queue_ksmbd_work(conn); } static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) { ksmbd_sessions_deregister(conn); destroy_lease_table(conn); return 0; } static void ksmbd_server_tcp_callbacks_init(void) { struct ksmbd_conn_ops ops; ops.process_fn = ksmbd_server_process_request; ops.terminate_fn = ksmbd_server_terminate_conn; ksmbd_conn_init_server_callbacks(&ops); } static void server_conf_free(void) { int i; for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { kfree(server_conf.conf[i]); server_conf.conf[i] = NULL; } } static int server_conf_init(void) { WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); server_conf.enforced_signing = 0; server_conf.min_protocol = ksmbd_min_protocol(); server_conf.max_protocol = ksmbd_max_protocol(); server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; #ifdef CONFIG_SMB_SERVER_KERBEROS5 server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5; #endif server_conf.max_inflight_req = SMB2_MAX_CREDITS; return 0; } static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) { int ret; ksmbd_proc_reset(); ret = ksmbd_conn_transport_init(); if (ret) { server_queue_ctrl_reset_work(); return; } pr_info("running\n"); WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); } static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) { ksmbd_ipc_soft_reset(); ksmbd_conn_transport_destroy(); ksmbd_stop_durable_scavenger(); server_conf_free(); server_conf_init(); WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); } static void server_ctrl_handle_work(struct work_struct *work) { struct server_ctrl_struct *ctrl; ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); mutex_lock(&ctrl_lock); switch (ctrl->type) { case SERVER_CTRL_TYPE_INIT: server_ctrl_handle_init(ctrl); break; case SERVER_CTRL_TYPE_RESET: server_ctrl_handle_reset(ctrl); break; default: pr_err("Unknown server work type: %d\n", ctrl->type); } mutex_unlock(&ctrl_lock); kfree(ctrl);