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