162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> 462306a36Sopenharmony_ci * Copyright (C) 2018 Samsung Electronics Co., Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "glob.h" 862306a36Sopenharmony_ci#include "oplock.h" 962306a36Sopenharmony_ci#include "misc.h" 1062306a36Sopenharmony_ci#include <linux/sched/signal.h> 1162306a36Sopenharmony_ci#include <linux/workqueue.h> 1262306a36Sopenharmony_ci#include <linux/sysfs.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/moduleparam.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "server.h" 1762306a36Sopenharmony_ci#include "smb_common.h" 1862306a36Sopenharmony_ci#include "smbstatus.h" 1962306a36Sopenharmony_ci#include "connection.h" 2062306a36Sopenharmony_ci#include "transport_ipc.h" 2162306a36Sopenharmony_ci#include "mgmt/user_session.h" 2262306a36Sopenharmony_ci#include "crypto_ctx.h" 2362306a36Sopenharmony_ci#include "auth.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint ksmbd_debug_types; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct ksmbd_server_config server_conf; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cienum SERVER_CTRL_TYPE { 3062306a36Sopenharmony_ci SERVER_CTRL_TYPE_INIT, 3162306a36Sopenharmony_ci SERVER_CTRL_TYPE_RESET, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct server_ctrl_struct { 3562306a36Sopenharmony_ci int type; 3662306a36Sopenharmony_ci struct work_struct ctrl_work; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic DEFINE_MUTEX(ctrl_lock); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int ___server_conf_set(int idx, char *val) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci if (idx >= ARRAY_SIZE(server_conf.conf)) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!val || val[0] == 0x00) 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci kfree(server_conf.conf[idx]); 5062306a36Sopenharmony_ci server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); 5162306a36Sopenharmony_ci if (!server_conf.conf[idx]) 5262306a36Sopenharmony_ci return -ENOMEM; 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciint ksmbd_set_netbios_name(char *v) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciint ksmbd_set_server_string(char *v) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciint ksmbd_set_work_group(char *v) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cichar *ksmbd_netbios_name(void) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cichar *ksmbd_server_string(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return server_conf.conf[SERVER_CONF_SERVER_STRING]; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cichar *ksmbd_work_group(void) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return server_conf.conf[SERVER_CONF_WORK_GROUP]; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * check_conn_state() - check state of server thread connection 8862306a36Sopenharmony_ci * @work: smb work containing server thread information 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Return: 0 on valid connection, otherwise 1 to reconnect 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic inline int check_conn_state(struct ksmbd_work *work) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct smb_hdr *rsp_hdr; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ksmbd_conn_exiting(work->conn) || 9762306a36Sopenharmony_ci ksmbd_conn_need_reconnect(work->conn)) { 9862306a36Sopenharmony_ci rsp_hdr = work->response_buf; 9962306a36Sopenharmony_ci rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; 10062306a36Sopenharmony_ci return 1; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define SERVER_HANDLER_CONTINUE 0 10662306a36Sopenharmony_ci#define SERVER_HANDLER_ABORT 1 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, 10962306a36Sopenharmony_ci u16 *cmd) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct smb_version_cmds *cmds; 11262306a36Sopenharmony_ci u16 command; 11362306a36Sopenharmony_ci int ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (check_conn_state(work)) 11662306a36Sopenharmony_ci return SERVER_HANDLER_CONTINUE; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (ksmbd_verify_smb_message(work)) { 11962306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); 12062306a36Sopenharmony_ci return SERVER_HANDLER_ABORT; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci command = conn->ops->get_cmd_val(work); 12462306a36Sopenharmony_ci *cmd = command; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciandx_again: 12762306a36Sopenharmony_ci if (command >= conn->max_cmds) { 12862306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); 12962306a36Sopenharmony_ci return SERVER_HANDLER_CONTINUE; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci cmds = &conn->cmds[command]; 13362306a36Sopenharmony_ci if (!cmds->proc) { 13462306a36Sopenharmony_ci ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); 13562306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); 13662306a36Sopenharmony_ci return SERVER_HANDLER_CONTINUE; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (work->sess && conn->ops->is_sign_req(work, command)) { 14062306a36Sopenharmony_ci ret = conn->ops->check_sign_req(work); 14162306a36Sopenharmony_ci if (!ret) { 14262306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); 14362306a36Sopenharmony_ci return SERVER_HANDLER_CONTINUE; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = cmds->proc(work); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (ret < 0) 15062306a36Sopenharmony_ci ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); 15162306a36Sopenharmony_ci /* AndX commands - chained request can return positive values */ 15262306a36Sopenharmony_ci else if (ret > 0) { 15362306a36Sopenharmony_ci command = ret; 15462306a36Sopenharmony_ci *cmd = command; 15562306a36Sopenharmony_ci goto andx_again; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (work->send_no_response) 15962306a36Sopenharmony_ci return SERVER_HANDLER_ABORT; 16062306a36Sopenharmony_ci return SERVER_HANDLER_CONTINUE; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void __handle_ksmbd_work(struct ksmbd_work *work, 16462306a36Sopenharmony_ci struct ksmbd_conn *conn) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u16 command = 0; 16762306a36Sopenharmony_ci int rc; 16862306a36Sopenharmony_ci bool is_chained = false; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (conn->ops->allocate_rsp_buf(work)) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (conn->ops->is_transform_hdr && 17462306a36Sopenharmony_ci conn->ops->is_transform_hdr(work->request_buf)) { 17562306a36Sopenharmony_ci rc = conn->ops->decrypt_req(work); 17662306a36Sopenharmony_ci if (rc < 0) { 17762306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); 17862306a36Sopenharmony_ci goto send; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci work->encrypted = true; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci rc = conn->ops->init_rsp_hdr(work); 18562306a36Sopenharmony_ci if (rc) { 18662306a36Sopenharmony_ci /* either uid or tid is not correct */ 18762306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); 18862306a36Sopenharmony_ci goto send; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci do { 19262306a36Sopenharmony_ci if (conn->ops->check_user_session) { 19362306a36Sopenharmony_ci rc = conn->ops->check_user_session(work); 19462306a36Sopenharmony_ci if (rc < 0) { 19562306a36Sopenharmony_ci if (rc == -EINVAL) 19662306a36Sopenharmony_ci conn->ops->set_rsp_status(work, 19762306a36Sopenharmony_ci STATUS_INVALID_PARAMETER); 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci conn->ops->set_rsp_status(work, 20062306a36Sopenharmony_ci STATUS_USER_SESSION_DELETED); 20162306a36Sopenharmony_ci goto send; 20262306a36Sopenharmony_ci } else if (rc > 0) { 20362306a36Sopenharmony_ci rc = conn->ops->get_ksmbd_tcon(work); 20462306a36Sopenharmony_ci if (rc < 0) { 20562306a36Sopenharmony_ci if (rc == -EINVAL) 20662306a36Sopenharmony_ci conn->ops->set_rsp_status(work, 20762306a36Sopenharmony_ci STATUS_INVALID_PARAMETER); 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci conn->ops->set_rsp_status(work, 21062306a36Sopenharmony_ci STATUS_NETWORK_NAME_DELETED); 21162306a36Sopenharmony_ci goto send; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci rc = __process_request(work, conn, &command); 21762306a36Sopenharmony_ci if (rc == SERVER_HANDLER_ABORT) 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Call smb2_set_rsp_credits() function to set number of credits 22262306a36Sopenharmony_ci * granted in hdr of smb2 response. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (conn->ops->set_rsp_credits) { 22562306a36Sopenharmony_ci spin_lock(&conn->credits_lock); 22662306a36Sopenharmony_ci rc = conn->ops->set_rsp_credits(work); 22762306a36Sopenharmony_ci spin_unlock(&conn->credits_lock); 22862306a36Sopenharmony_ci if (rc < 0) { 22962306a36Sopenharmony_ci conn->ops->set_rsp_status(work, 23062306a36Sopenharmony_ci STATUS_INVALID_PARAMETER); 23162306a36Sopenharmony_ci goto send; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci is_chained = is_chained_smb2_message(work); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (work->sess && 23862306a36Sopenharmony_ci (work->sess->sign || smb3_11_final_sess_setup_resp(work) || 23962306a36Sopenharmony_ci conn->ops->is_sign_req(work, command))) 24062306a36Sopenharmony_ci conn->ops->set_sign_rsp(work); 24162306a36Sopenharmony_ci } while (is_chained == true); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cisend: 24462306a36Sopenharmony_ci if (work->tcon) 24562306a36Sopenharmony_ci ksmbd_tree_connect_put(work->tcon); 24662306a36Sopenharmony_ci smb3_preauth_hash_rsp(work); 24762306a36Sopenharmony_ci if (work->sess && work->sess->enc && work->encrypted && 24862306a36Sopenharmony_ci conn->ops->encrypt_resp) { 24962306a36Sopenharmony_ci rc = conn->ops->encrypt_resp(work); 25062306a36Sopenharmony_ci if (rc < 0) 25162306a36Sopenharmony_ci conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ksmbd_conn_write(work); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * handle_ksmbd_work() - process pending smb work requests 25962306a36Sopenharmony_ci * @wk: smb work containing request command buffer 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * called by kworker threads to processing remaining smb work requests 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic void handle_ksmbd_work(struct work_struct *wk) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); 26662306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci atomic64_inc(&conn->stats.request_served); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci __handle_ksmbd_work(work, conn); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ksmbd_conn_try_dequeue_request(work); 27362306a36Sopenharmony_ci ksmbd_free_work_struct(work); 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Checking waitqueue to dropping pending requests on 27662306a36Sopenharmony_ci * disconnection. waitqueue_active is safe because it 27762306a36Sopenharmony_ci * uses atomic operation for condition. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) 28062306a36Sopenharmony_ci wake_up(&conn->r_count_q); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/** 28462306a36Sopenharmony_ci * queue_ksmbd_work() - queue a smb request to worker thread queue 28562306a36Sopenharmony_ci * for proccessing smb command and sending response 28662306a36Sopenharmony_ci * @conn: connection instance 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * read remaining data from socket create and submit work. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_cistatic int queue_ksmbd_work(struct ksmbd_conn *conn) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct ksmbd_work *work; 29362306a36Sopenharmony_ci int err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci work = ksmbd_alloc_work_struct(); 29662306a36Sopenharmony_ci if (!work) { 29762306a36Sopenharmony_ci pr_err("allocation for work failed\n"); 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci work->conn = conn; 30262306a36Sopenharmony_ci work->request_buf = conn->request_buf; 30362306a36Sopenharmony_ci conn->request_buf = NULL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci err = ksmbd_init_smb_server(work); 30662306a36Sopenharmony_ci if (err) { 30762306a36Sopenharmony_ci ksmbd_free_work_struct(work); 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ksmbd_conn_enqueue_request(work); 31262306a36Sopenharmony_ci atomic_inc(&conn->r_count); 31362306a36Sopenharmony_ci /* update activity on connection */ 31462306a36Sopenharmony_ci conn->last_active = jiffies; 31562306a36Sopenharmony_ci INIT_WORK(&work->work, handle_ksmbd_work); 31662306a36Sopenharmony_ci ksmbd_queue_work(work); 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int ksmbd_server_process_request(struct ksmbd_conn *conn) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci return queue_ksmbd_work(conn); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci ksmbd_sessions_deregister(conn); 32862306a36Sopenharmony_ci destroy_lease_table(conn); 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void ksmbd_server_tcp_callbacks_init(void) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct ksmbd_conn_ops ops; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ops.process_fn = ksmbd_server_process_request; 33762306a36Sopenharmony_ci ops.terminate_fn = ksmbd_server_terminate_conn; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ksmbd_conn_init_server_callbacks(&ops); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void server_conf_free(void) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci int i; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { 34762306a36Sopenharmony_ci kfree(server_conf.conf[i]); 34862306a36Sopenharmony_ci server_conf.conf[i] = NULL; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int server_conf_init(void) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); 35562306a36Sopenharmony_ci server_conf.enforced_signing = 0; 35662306a36Sopenharmony_ci server_conf.min_protocol = ksmbd_min_protocol(); 35762306a36Sopenharmony_ci server_conf.max_protocol = ksmbd_max_protocol(); 35862306a36Sopenharmony_ci server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; 35962306a36Sopenharmony_ci#ifdef CONFIG_SMB_SERVER_KERBEROS5 36062306a36Sopenharmony_ci server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | 36162306a36Sopenharmony_ci KSMBD_AUTH_MSKRB5; 36262306a36Sopenharmony_ci#endif 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = ksmbd_conn_transport_init(); 37162306a36Sopenharmony_ci if (ret) { 37262306a36Sopenharmony_ci server_queue_ctrl_reset_work(); 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci ksmbd_ipc_soft_reset(); 38262306a36Sopenharmony_ci ksmbd_conn_transport_destroy(); 38362306a36Sopenharmony_ci server_conf_free(); 38462306a36Sopenharmony_ci server_conf_init(); 38562306a36Sopenharmony_ci WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void server_ctrl_handle_work(struct work_struct *work) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct server_ctrl_struct *ctrl; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci mutex_lock(&ctrl_lock); 39562306a36Sopenharmony_ci switch (ctrl->type) { 39662306a36Sopenharmony_ci case SERVER_CTRL_TYPE_INIT: 39762306a36Sopenharmony_ci server_ctrl_handle_init(ctrl); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case SERVER_CTRL_TYPE_RESET: 40062306a36Sopenharmony_ci server_ctrl_handle_reset(ctrl); 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci pr_err("Unknown server work type: %d\n", ctrl->type); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci mutex_unlock(&ctrl_lock); 40662306a36Sopenharmony_ci kfree(ctrl); 40762306a36Sopenharmony_ci module_put(THIS_MODULE); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int __queue_ctrl_work(int type) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct server_ctrl_struct *ctrl; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); 41562306a36Sopenharmony_ci if (!ctrl) 41662306a36Sopenharmony_ci return -ENOMEM; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci __module_get(THIS_MODULE); 41962306a36Sopenharmony_ci ctrl->type = type; 42062306a36Sopenharmony_ci INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); 42162306a36Sopenharmony_ci queue_work(system_long_wq, &ctrl->ctrl_work); 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint server_queue_ctrl_init_work(void) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciint server_queue_ctrl_reset_work(void) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic ssize_t stats_show(const struct class *class, const struct class_attribute *attr, 43662306a36Sopenharmony_ci char *buf) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * Inc this each time you change stats output format, 44062306a36Sopenharmony_ci * so user space will know what to do. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci static int stats_version = 2; 44362306a36Sopenharmony_ci static const char * const state[] = { 44462306a36Sopenharmony_ci "startup", 44562306a36Sopenharmony_ci "running", 44662306a36Sopenharmony_ci "reset", 44762306a36Sopenharmony_ci "shutdown" 44862306a36Sopenharmony_ci }; 44962306a36Sopenharmony_ci return sysfs_emit(buf, "%d %s %d %lu\n", stats_version, 45062306a36Sopenharmony_ci state[server_conf.state], server_conf.tcp_port, 45162306a36Sopenharmony_ci server_conf.ipc_last_active / HZ); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic ssize_t kill_server_store(const struct class *class, 45562306a36Sopenharmony_ci const struct class_attribute *attr, const char *buf, 45662306a36Sopenharmony_ci size_t len) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci if (!sysfs_streq(buf, "hard")) 45962306a36Sopenharmony_ci return len; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci pr_info("kill command received\n"); 46262306a36Sopenharmony_ci mutex_lock(&ctrl_lock); 46362306a36Sopenharmony_ci WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); 46462306a36Sopenharmony_ci __module_get(THIS_MODULE); 46562306a36Sopenharmony_ci server_ctrl_handle_reset(NULL); 46662306a36Sopenharmony_ci module_put(THIS_MODULE); 46762306a36Sopenharmony_ci mutex_unlock(&ctrl_lock); 46862306a36Sopenharmony_ci return len; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic const char * const debug_type_strings[] = {"smb", "auth", "vfs", 47262306a36Sopenharmony_ci "oplock", "ipc", "conn", 47362306a36Sopenharmony_ci "rdma"}; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic ssize_t debug_show(const struct class *class, const struct class_attribute *attr, 47662306a36Sopenharmony_ci char *buf) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci ssize_t sz = 0; 47962306a36Sopenharmony_ci int i, pos = 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { 48262306a36Sopenharmony_ci if ((ksmbd_debug_types >> i) & 1) { 48362306a36Sopenharmony_ci pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]); 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci sz += pos; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci sz += sysfs_emit_at(buf, sz, "\n"); 49062306a36Sopenharmony_ci return sz; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic ssize_t debug_store(const struct class *class, const struct class_attribute *attr, 49462306a36Sopenharmony_ci const char *buf, size_t len) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci int i; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { 49962306a36Sopenharmony_ci if (sysfs_streq(buf, "all")) { 50062306a36Sopenharmony_ci if (ksmbd_debug_types == KSMBD_DEBUG_ALL) 50162306a36Sopenharmony_ci ksmbd_debug_types = 0; 50262306a36Sopenharmony_ci else 50362306a36Sopenharmony_ci ksmbd_debug_types = KSMBD_DEBUG_ALL; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (sysfs_streq(buf, debug_type_strings[i])) { 50862306a36Sopenharmony_ci if (ksmbd_debug_types & (1 << i)) 50962306a36Sopenharmony_ci ksmbd_debug_types &= ~(1 << i); 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci ksmbd_debug_types |= (1 << i); 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return len; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic CLASS_ATTR_RO(stats); 52062306a36Sopenharmony_cistatic CLASS_ATTR_WO(kill_server); 52162306a36Sopenharmony_cistatic CLASS_ATTR_RW(debug); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic struct attribute *ksmbd_control_class_attrs[] = { 52462306a36Sopenharmony_ci &class_attr_stats.attr, 52562306a36Sopenharmony_ci &class_attr_kill_server.attr, 52662306a36Sopenharmony_ci &class_attr_debug.attr, 52762306a36Sopenharmony_ci NULL, 52862306a36Sopenharmony_ci}; 52962306a36Sopenharmony_ciATTRIBUTE_GROUPS(ksmbd_control_class); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic struct class ksmbd_control_class = { 53262306a36Sopenharmony_ci .name = "ksmbd-control", 53362306a36Sopenharmony_ci .class_groups = ksmbd_control_class_groups, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int ksmbd_server_shutdown(void) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci class_unregister(&ksmbd_control_class); 54162306a36Sopenharmony_ci ksmbd_workqueue_destroy(); 54262306a36Sopenharmony_ci ksmbd_ipc_release(); 54362306a36Sopenharmony_ci ksmbd_conn_transport_destroy(); 54462306a36Sopenharmony_ci ksmbd_crypto_destroy(); 54562306a36Sopenharmony_ci ksmbd_free_global_file_table(); 54662306a36Sopenharmony_ci destroy_lease_table(NULL); 54762306a36Sopenharmony_ci ksmbd_work_pool_destroy(); 54862306a36Sopenharmony_ci ksmbd_exit_file_cache(); 54962306a36Sopenharmony_ci server_conf_free(); 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic int __init ksmbd_server_init(void) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci int ret; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = class_register(&ksmbd_control_class); 55862306a36Sopenharmony_ci if (ret) { 55962306a36Sopenharmony_ci pr_err("Unable to register ksmbd-control class\n"); 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ksmbd_server_tcp_callbacks_init(); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = server_conf_init(); 56662306a36Sopenharmony_ci if (ret) 56762306a36Sopenharmony_ci goto err_unregister; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = ksmbd_work_pool_init(); 57062306a36Sopenharmony_ci if (ret) 57162306a36Sopenharmony_ci goto err_unregister; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ret = ksmbd_init_file_cache(); 57462306a36Sopenharmony_ci if (ret) 57562306a36Sopenharmony_ci goto err_destroy_work_pools; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ret = ksmbd_ipc_init(); 57862306a36Sopenharmony_ci if (ret) 57962306a36Sopenharmony_ci goto err_exit_file_cache; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ret = ksmbd_init_global_file_table(); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci goto err_ipc_release; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ret = ksmbd_inode_hash_init(); 58662306a36Sopenharmony_ci if (ret) 58762306a36Sopenharmony_ci goto err_destroy_file_table; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = ksmbd_crypto_create(); 59062306a36Sopenharmony_ci if (ret) 59162306a36Sopenharmony_ci goto err_release_inode_hash; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci ret = ksmbd_workqueue_init(); 59462306a36Sopenharmony_ci if (ret) 59562306a36Sopenharmony_ci goto err_crypto_destroy; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cierr_crypto_destroy: 60062306a36Sopenharmony_ci ksmbd_crypto_destroy(); 60162306a36Sopenharmony_cierr_release_inode_hash: 60262306a36Sopenharmony_ci ksmbd_release_inode_hash(); 60362306a36Sopenharmony_cierr_destroy_file_table: 60462306a36Sopenharmony_ci ksmbd_free_global_file_table(); 60562306a36Sopenharmony_cierr_ipc_release: 60662306a36Sopenharmony_ci ksmbd_ipc_release(); 60762306a36Sopenharmony_cierr_exit_file_cache: 60862306a36Sopenharmony_ci ksmbd_exit_file_cache(); 60962306a36Sopenharmony_cierr_destroy_work_pools: 61062306a36Sopenharmony_ci ksmbd_work_pool_destroy(); 61162306a36Sopenharmony_cierr_unregister: 61262306a36Sopenharmony_ci class_unregister(&ksmbd_control_class); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return ret; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/** 61862306a36Sopenharmony_ci * ksmbd_server_exit() - shutdown forker thread and free memory at module exit 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_cistatic void __exit ksmbd_server_exit(void) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci ksmbd_server_shutdown(); 62362306a36Sopenharmony_ci rcu_barrier(); 62462306a36Sopenharmony_ci ksmbd_release_inode_hash(); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ciMODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); 62862306a36Sopenharmony_ciMODULE_VERSION(KSMBD_VERSION); 62962306a36Sopenharmony_ciMODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); 63062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 63162306a36Sopenharmony_ciMODULE_SOFTDEP("pre: ecb"); 63262306a36Sopenharmony_ciMODULE_SOFTDEP("pre: hmac"); 63362306a36Sopenharmony_ciMODULE_SOFTDEP("pre: md5"); 63462306a36Sopenharmony_ciMODULE_SOFTDEP("pre: nls"); 63562306a36Sopenharmony_ciMODULE_SOFTDEP("pre: aes"); 63662306a36Sopenharmony_ciMODULE_SOFTDEP("pre: cmac"); 63762306a36Sopenharmony_ciMODULE_SOFTDEP("pre: sha256"); 63862306a36Sopenharmony_ciMODULE_SOFTDEP("pre: sha512"); 63962306a36Sopenharmony_ciMODULE_SOFTDEP("pre: aead2"); 64062306a36Sopenharmony_ciMODULE_SOFTDEP("pre: ccm"); 64162306a36Sopenharmony_ciMODULE_SOFTDEP("pre: gcm"); 64262306a36Sopenharmony_ciMODULE_SOFTDEP("pre: crc32"); 64362306a36Sopenharmony_cimodule_init(ksmbd_server_init) 64462306a36Sopenharmony_cimodule_exit(ksmbd_server_exit) 645