162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/jhash.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/rwsem.h>
962306a36Sopenharmony_ci#include <linux/mutex.h>
1062306a36Sopenharmony_ci#include <linux/wait.h>
1162306a36Sopenharmony_ci#include <linux/hashtable.h>
1262306a36Sopenharmony_ci#include <net/net_namespace.h>
1362306a36Sopenharmony_ci#include <net/genetlink.h>
1462306a36Sopenharmony_ci#include <linux/socket.h>
1562306a36Sopenharmony_ci#include <linux/workqueue.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "vfs_cache.h"
1862306a36Sopenharmony_ci#include "transport_ipc.h"
1962306a36Sopenharmony_ci#include "server.h"
2062306a36Sopenharmony_ci#include "smb_common.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "mgmt/user_config.h"
2362306a36Sopenharmony_ci#include "mgmt/share_config.h"
2462306a36Sopenharmony_ci#include "mgmt/user_session.h"
2562306a36Sopenharmony_ci#include "mgmt/tree_connect.h"
2662306a36Sopenharmony_ci#include "mgmt/ksmbd_ida.h"
2762306a36Sopenharmony_ci#include "connection.h"
2862306a36Sopenharmony_ci#include "transport_tcp.h"
2962306a36Sopenharmony_ci#include "transport_rdma.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define IPC_WAIT_TIMEOUT	(2 * HZ)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define IPC_MSG_HASH_BITS	3
3462306a36Sopenharmony_cistatic DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS);
3562306a36Sopenharmony_cistatic DECLARE_RWSEM(ipc_msg_table_lock);
3662306a36Sopenharmony_cistatic DEFINE_MUTEX(startup_lock);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic DEFINE_IDA(ipc_ida);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic unsigned int ksmbd_tools_pid;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic bool ksmbd_ipc_validate_version(struct genl_info *m)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	if (m->genlhdr->version != KSMBD_GENL_VERSION) {
4562306a36Sopenharmony_ci		pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n",
4662306a36Sopenharmony_ci		       "Daemon and kernel module version mismatch",
4762306a36Sopenharmony_ci		       m->genlhdr->version,
4862306a36Sopenharmony_ci		       KSMBD_GENL_VERSION,
4962306a36Sopenharmony_ci		       "User-space ksmbd should terminate");
5062306a36Sopenharmony_ci		return false;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	return true;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct ksmbd_ipc_msg {
5662306a36Sopenharmony_ci	unsigned int		type;
5762306a36Sopenharmony_ci	unsigned int		sz;
5862306a36Sopenharmony_ci	unsigned char		payload[];
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct ipc_msg_table_entry {
6262306a36Sopenharmony_ci	unsigned int		handle;
6362306a36Sopenharmony_ci	unsigned int		type;
6462306a36Sopenharmony_ci	wait_queue_head_t	wait;
6562306a36Sopenharmony_ci	struct hlist_node	ipc_table_hlist;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	void			*response;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct delayed_work ipc_timer_work;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int handle_startup_event(struct sk_buff *skb, struct genl_info *info);
7362306a36Sopenharmony_cistatic int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info);
7462306a36Sopenharmony_cistatic int handle_generic_event(struct sk_buff *skb, struct genl_info *info);
7562306a36Sopenharmony_cistatic int ksmbd_ipc_heartbeat_request(void);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = {
7862306a36Sopenharmony_ci	[KSMBD_EVENT_UNSPEC] = {
7962306a36Sopenharmony_ci		.len = 0,
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci	[KSMBD_EVENT_HEARTBEAT_REQUEST] = {
8262306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_heartbeat),
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	[KSMBD_EVENT_STARTING_UP] = {
8562306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_startup_request),
8662306a36Sopenharmony_ci	},
8762306a36Sopenharmony_ci	[KSMBD_EVENT_SHUTTING_DOWN] = {
8862306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_shutdown_request),
8962306a36Sopenharmony_ci	},
9062306a36Sopenharmony_ci	[KSMBD_EVENT_LOGIN_REQUEST] = {
9162306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_login_request),
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci	[KSMBD_EVENT_LOGIN_RESPONSE] = {
9462306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_login_response),
9562306a36Sopenharmony_ci	},
9662306a36Sopenharmony_ci	[KSMBD_EVENT_SHARE_CONFIG_REQUEST] = {
9762306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_share_config_request),
9862306a36Sopenharmony_ci	},
9962306a36Sopenharmony_ci	[KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = {
10062306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_share_config_response),
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci	[KSMBD_EVENT_TREE_CONNECT_REQUEST] = {
10362306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_tree_connect_request),
10462306a36Sopenharmony_ci	},
10562306a36Sopenharmony_ci	[KSMBD_EVENT_TREE_CONNECT_RESPONSE] = {
10662306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_tree_connect_response),
10762306a36Sopenharmony_ci	},
10862306a36Sopenharmony_ci	[KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = {
10962306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_tree_disconnect_request),
11062306a36Sopenharmony_ci	},
11162306a36Sopenharmony_ci	[KSMBD_EVENT_LOGOUT_REQUEST] = {
11262306a36Sopenharmony_ci		.len = sizeof(struct ksmbd_logout_request),
11362306a36Sopenharmony_ci	},
11462306a36Sopenharmony_ci	[KSMBD_EVENT_RPC_REQUEST] = {
11562306a36Sopenharmony_ci	},
11662306a36Sopenharmony_ci	[KSMBD_EVENT_RPC_RESPONSE] = {
11762306a36Sopenharmony_ci	},
11862306a36Sopenharmony_ci	[KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = {
11962306a36Sopenharmony_ci	},
12062306a36Sopenharmony_ci	[KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = {
12162306a36Sopenharmony_ci	},
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct genl_ops ksmbd_genl_ops[] = {
12562306a36Sopenharmony_ci	{
12662306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_UNSPEC,
12762306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
12862306a36Sopenharmony_ci	},
12962306a36Sopenharmony_ci	{
13062306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_HEARTBEAT_REQUEST,
13162306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
13262306a36Sopenharmony_ci	},
13362306a36Sopenharmony_ci	{
13462306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_STARTING_UP,
13562306a36Sopenharmony_ci		.doit	= handle_startup_event,
13662306a36Sopenharmony_ci	},
13762306a36Sopenharmony_ci	{
13862306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_SHUTTING_DOWN,
13962306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
14062306a36Sopenharmony_ci	},
14162306a36Sopenharmony_ci	{
14262306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_LOGIN_REQUEST,
14362306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
14462306a36Sopenharmony_ci	},
14562306a36Sopenharmony_ci	{
14662306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_LOGIN_RESPONSE,
14762306a36Sopenharmony_ci		.doit	= handle_generic_event,
14862306a36Sopenharmony_ci	},
14962306a36Sopenharmony_ci	{
15062306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_SHARE_CONFIG_REQUEST,
15162306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
15262306a36Sopenharmony_ci	},
15362306a36Sopenharmony_ci	{
15462306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
15562306a36Sopenharmony_ci		.doit	= handle_generic_event,
15662306a36Sopenharmony_ci	},
15762306a36Sopenharmony_ci	{
15862306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_TREE_CONNECT_REQUEST,
15962306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
16062306a36Sopenharmony_ci	},
16162306a36Sopenharmony_ci	{
16262306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_TREE_CONNECT_RESPONSE,
16362306a36Sopenharmony_ci		.doit	= handle_generic_event,
16462306a36Sopenharmony_ci	},
16562306a36Sopenharmony_ci	{
16662306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_TREE_DISCONNECT_REQUEST,
16762306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
16862306a36Sopenharmony_ci	},
16962306a36Sopenharmony_ci	{
17062306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_LOGOUT_REQUEST,
17162306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	{
17462306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_RPC_REQUEST,
17562306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
17662306a36Sopenharmony_ci	},
17762306a36Sopenharmony_ci	{
17862306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_RPC_RESPONSE,
17962306a36Sopenharmony_ci		.doit	= handle_generic_event,
18062306a36Sopenharmony_ci	},
18162306a36Sopenharmony_ci	{
18262306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
18362306a36Sopenharmony_ci		.doit	= handle_unsupported_event,
18462306a36Sopenharmony_ci	},
18562306a36Sopenharmony_ci	{
18662306a36Sopenharmony_ci		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE,
18762306a36Sopenharmony_ci		.doit	= handle_generic_event,
18862306a36Sopenharmony_ci	},
18962306a36Sopenharmony_ci};
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic struct genl_family ksmbd_genl_family = {
19262306a36Sopenharmony_ci	.name		= KSMBD_GENL_NAME,
19362306a36Sopenharmony_ci	.version	= KSMBD_GENL_VERSION,
19462306a36Sopenharmony_ci	.hdrsize	= 0,
19562306a36Sopenharmony_ci	.maxattr	= KSMBD_EVENT_MAX,
19662306a36Sopenharmony_ci	.netnsok	= true,
19762306a36Sopenharmony_ci	.module		= THIS_MODULE,
19862306a36Sopenharmony_ci	.ops		= ksmbd_genl_ops,
19962306a36Sopenharmony_ci	.n_ops		= ARRAY_SIZE(ksmbd_genl_ops),
20062306a36Sopenharmony_ci	.resv_start_op	= KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void ksmbd_nl_init_fixup(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int i;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++)
20862306a36Sopenharmony_ci		ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT |
20962306a36Sopenharmony_ci						GENL_DONT_VALIDATE_DUMP;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ksmbd_genl_family.policy = ksmbd_nl_policy;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int rpc_context_flags(struct ksmbd_session *sess)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	if (user_guest(sess->user))
21762306a36Sopenharmony_ci		return KSMBD_RPC_RESTRICTED_CONTEXT;
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void ipc_update_last_active(void)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	if (server_conf.ipc_timeout)
22462306a36Sopenharmony_ci		server_conf.ipc_last_active = jiffies;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
23062306a36Sopenharmony_ci	size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	msg = kvzalloc(msg_sz, GFP_KERNEL);
23362306a36Sopenharmony_ci	if (msg)
23462306a36Sopenharmony_ci		msg->sz = sz;
23562306a36Sopenharmony_ci	return msg;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void ipc_msg_free(struct ksmbd_ipc_msg *msg)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	kvfree(msg);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void ipc_msg_handle_free(int handle)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	if (handle >= 0)
24662306a36Sopenharmony_ci		ksmbd_release_id(&ipc_ida, handle);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int handle_response(int type, void *payload, size_t sz)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	unsigned int handle = *(unsigned int *)payload;
25262306a36Sopenharmony_ci	struct ipc_msg_table_entry *entry;
25362306a36Sopenharmony_ci	int ret = 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	ipc_update_last_active();
25662306a36Sopenharmony_ci	down_read(&ipc_msg_table_lock);
25762306a36Sopenharmony_ci	hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) {
25862306a36Sopenharmony_ci		if (handle != entry->handle)
25962306a36Sopenharmony_ci			continue;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		entry->response = NULL;
26262306a36Sopenharmony_ci		/*
26362306a36Sopenharmony_ci		 * Response message type value should be equal to
26462306a36Sopenharmony_ci		 * request message type + 1.
26562306a36Sopenharmony_ci		 */
26662306a36Sopenharmony_ci		if (entry->type + 1 != type) {
26762306a36Sopenharmony_ci			pr_err("Waiting for IPC type %d, got %d. Ignore.\n",
26862306a36Sopenharmony_ci			       entry->type + 1, type);
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		entry->response = kvzalloc(sz, GFP_KERNEL);
27262306a36Sopenharmony_ci		if (!entry->response) {
27362306a36Sopenharmony_ci			ret = -ENOMEM;
27462306a36Sopenharmony_ci			break;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		memcpy(entry->response, payload, sz);
27862306a36Sopenharmony_ci		wake_up_interruptible(&entry->wait);
27962306a36Sopenharmony_ci		ret = 0;
28062306a36Sopenharmony_ci		break;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	up_read(&ipc_msg_table_lock);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return ret;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ksmbd_set_fd_limit(req->file_max);
29262306a36Sopenharmony_ci	server_conf.flags = req->flags;
29362306a36Sopenharmony_ci	server_conf.signing = req->signing;
29462306a36Sopenharmony_ci	server_conf.tcp_port = req->tcp_port;
29562306a36Sopenharmony_ci	server_conf.ipc_timeout = req->ipc_timeout * HZ;
29662306a36Sopenharmony_ci	server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL;
29762306a36Sopenharmony_ci	server_conf.share_fake_fscaps = req->share_fake_fscaps;
29862306a36Sopenharmony_ci	ksmbd_init_domain(req->sub_auth);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (req->smb2_max_read)
30162306a36Sopenharmony_ci		init_smb2_max_read_size(req->smb2_max_read);
30262306a36Sopenharmony_ci	if (req->smb2_max_write)
30362306a36Sopenharmony_ci		init_smb2_max_write_size(req->smb2_max_write);
30462306a36Sopenharmony_ci	if (req->smb2_max_trans)
30562306a36Sopenharmony_ci		init_smb2_max_trans_size(req->smb2_max_trans);
30662306a36Sopenharmony_ci	if (req->smb2_max_credits)
30762306a36Sopenharmony_ci		init_smb2_max_credits(req->smb2_max_credits);
30862306a36Sopenharmony_ci	if (req->smbd_max_io_size)
30962306a36Sopenharmony_ci		init_smbd_max_io_size(req->smbd_max_io_size);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (req->max_connections)
31262306a36Sopenharmony_ci		server_conf.max_connections = req->max_connections;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = ksmbd_set_netbios_name(req->netbios_name);
31562306a36Sopenharmony_ci	ret |= ksmbd_set_server_string(req->server_string);
31662306a36Sopenharmony_ci	ret |= ksmbd_set_work_group(req->work_group);
31762306a36Sopenharmony_ci	ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
31862306a36Sopenharmony_ci					req->ifc_list_sz);
31962306a36Sopenharmony_ci	if (ret) {
32062306a36Sopenharmony_ci		pr_err("Server configuration error: %s %s %s\n",
32162306a36Sopenharmony_ci		       req->netbios_name, req->server_string,
32262306a36Sopenharmony_ci		       req->work_group);
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (req->min_prot[0]) {
32762306a36Sopenharmony_ci		ret = ksmbd_lookup_protocol_idx(req->min_prot);
32862306a36Sopenharmony_ci		if (ret >= 0)
32962306a36Sopenharmony_ci			server_conf.min_protocol = ret;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	if (req->max_prot[0]) {
33262306a36Sopenharmony_ci		ret = ksmbd_lookup_protocol_idx(req->max_prot);
33362306a36Sopenharmony_ci		if (ret >= 0)
33462306a36Sopenharmony_ci			server_conf.max_protocol = ret;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (server_conf.ipc_timeout)
33862306a36Sopenharmony_ci		schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout);
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int handle_startup_event(struct sk_buff *skb, struct genl_info *info)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	int ret = 0;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
34762306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
34862306a36Sopenharmony_ci		return -EPERM;
34962306a36Sopenharmony_ci#endif
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!ksmbd_ipc_validate_version(info))
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (!info->attrs[KSMBD_EVENT_STARTING_UP])
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	mutex_lock(&startup_lock);
35862306a36Sopenharmony_ci	if (!ksmbd_server_configurable()) {
35962306a36Sopenharmony_ci		mutex_unlock(&startup_lock);
36062306a36Sopenharmony_ci		pr_err("Server reset is in progress, can't start daemon\n");
36162306a36Sopenharmony_ci		return -EINVAL;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (ksmbd_tools_pid) {
36562306a36Sopenharmony_ci		if (ksmbd_ipc_heartbeat_request() == 0) {
36662306a36Sopenharmony_ci			ret = -EINVAL;
36762306a36Sopenharmony_ci			goto out;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		pr_err("Reconnect to a new user space daemon\n");
37162306a36Sopenharmony_ci	} else {
37262306a36Sopenharmony_ci		struct ksmbd_startup_request *req;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		req = nla_data(info->attrs[info->genlhdr->cmd]);
37562306a36Sopenharmony_ci		ret = ipc_server_config_on_startup(req);
37662306a36Sopenharmony_ci		if (ret)
37762306a36Sopenharmony_ci			goto out;
37862306a36Sopenharmony_ci		server_queue_ctrl_init_work();
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ksmbd_tools_pid = info->snd_portid;
38262306a36Sopenharmony_ci	ipc_update_last_active();
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciout:
38562306a36Sopenharmony_ci	mutex_unlock(&startup_lock);
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd);
39262306a36Sopenharmony_ci	return -EINVAL;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int handle_generic_event(struct sk_buff *skb, struct genl_info *info)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	void *payload;
39862306a36Sopenharmony_ci	int sz;
39962306a36Sopenharmony_ci	int type = info->genlhdr->cmd;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
40262306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
40362306a36Sopenharmony_ci		return -EPERM;
40462306a36Sopenharmony_ci#endif
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (type > KSMBD_EVENT_MAX) {
40762306a36Sopenharmony_ci		WARN_ON(1);
40862306a36Sopenharmony_ci		return -EINVAL;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!ksmbd_ipc_validate_version(info))
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (!info->attrs[type])
41562306a36Sopenharmony_ci		return -EINVAL;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	payload = nla_data(info->attrs[info->genlhdr->cmd]);
41862306a36Sopenharmony_ci	sz = nla_len(info->attrs[info->genlhdr->cmd]);
41962306a36Sopenharmony_ci	return handle_response(type, payload, sz);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int ipc_msg_send(struct ksmbd_ipc_msg *msg)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct genlmsghdr *nlh;
42562306a36Sopenharmony_ci	struct sk_buff *skb;
42662306a36Sopenharmony_ci	int ret = -EINVAL;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (!ksmbd_tools_pid)
42962306a36Sopenharmony_ci		return ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	skb = genlmsg_new(msg->sz, GFP_KERNEL);
43262306a36Sopenharmony_ci	if (!skb)
43362306a36Sopenharmony_ci		return -ENOMEM;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type);
43662306a36Sopenharmony_ci	if (!nlh)
43762306a36Sopenharmony_ci		goto out;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	ret = nla_put(skb, msg->type, msg->sz, msg->payload);
44062306a36Sopenharmony_ci	if (ret) {
44162306a36Sopenharmony_ci		genlmsg_cancel(skb, nlh);
44262306a36Sopenharmony_ci		goto out;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	genlmsg_end(skb, nlh);
44662306a36Sopenharmony_ci	ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid);
44762306a36Sopenharmony_ci	if (!ret)
44862306a36Sopenharmony_ci		ipc_update_last_active();
44962306a36Sopenharmony_ci	return ret;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ciout:
45262306a36Sopenharmony_ci	nlmsg_free(skb);
45362306a36Sopenharmony_ci	return ret;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct ipc_msg_table_entry entry;
45962306a36Sopenharmony_ci	int ret;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if ((int)handle < 0)
46262306a36Sopenharmony_ci		return NULL;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	entry.type = msg->type;
46562306a36Sopenharmony_ci	entry.response = NULL;
46662306a36Sopenharmony_ci	init_waitqueue_head(&entry.wait);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	down_write(&ipc_msg_table_lock);
46962306a36Sopenharmony_ci	entry.handle = handle;
47062306a36Sopenharmony_ci	hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle);
47162306a36Sopenharmony_ci	up_write(&ipc_msg_table_lock);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ret = ipc_msg_send(msg);
47462306a36Sopenharmony_ci	if (ret)
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(entry.wait,
47862306a36Sopenharmony_ci					       entry.response != NULL,
47962306a36Sopenharmony_ci					       IPC_WAIT_TIMEOUT);
48062306a36Sopenharmony_ciout:
48162306a36Sopenharmony_ci	down_write(&ipc_msg_table_lock);
48262306a36Sopenharmony_ci	hash_del(&entry.ipc_table_hlist);
48362306a36Sopenharmony_ci	up_write(&ipc_msg_table_lock);
48462306a36Sopenharmony_ci	return entry.response;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int ksmbd_ipc_heartbeat_request(void)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
49062306a36Sopenharmony_ci	int ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat));
49362306a36Sopenharmony_ci	if (!msg)
49462306a36Sopenharmony_ci		return -EINVAL;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST;
49762306a36Sopenharmony_ci	ret = ipc_msg_send(msg);
49862306a36Sopenharmony_ci	ipc_msg_free(msg);
49962306a36Sopenharmony_ci	return ret;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistruct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
50562306a36Sopenharmony_ci	struct ksmbd_login_request *req;
50662306a36Sopenharmony_ci	struct ksmbd_login_response *resp;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
50962306a36Sopenharmony_ci		return NULL;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request));
51262306a36Sopenharmony_ci	if (!msg)
51362306a36Sopenharmony_ci		return NULL;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_LOGIN_REQUEST;
51662306a36Sopenharmony_ci	req = (struct ksmbd_login_request *)msg->payload;
51762306a36Sopenharmony_ci	req->handle = ksmbd_acquire_id(&ipc_ida);
51862306a36Sopenharmony_ci	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
52162306a36Sopenharmony_ci	ipc_msg_handle_free(req->handle);
52262306a36Sopenharmony_ci	ipc_msg_free(msg);
52362306a36Sopenharmony_ci	return resp;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistruct ksmbd_spnego_authen_response *
52762306a36Sopenharmony_ciksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
53062306a36Sopenharmony_ci	struct ksmbd_spnego_authen_request *req;
53162306a36Sopenharmony_ci	struct ksmbd_spnego_authen_response *resp;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) +
53462306a36Sopenharmony_ci			blob_len + 1);
53562306a36Sopenharmony_ci	if (!msg)
53662306a36Sopenharmony_ci		return NULL;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST;
53962306a36Sopenharmony_ci	req = (struct ksmbd_spnego_authen_request *)msg->payload;
54062306a36Sopenharmony_ci	req->handle = ksmbd_acquire_id(&ipc_ida);
54162306a36Sopenharmony_ci	req->spnego_blob_len = blob_len;
54262306a36Sopenharmony_ci	memcpy(req->spnego_blob, spnego_blob, blob_len);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
54562306a36Sopenharmony_ci	ipc_msg_handle_free(req->handle);
54662306a36Sopenharmony_ci	ipc_msg_free(msg);
54762306a36Sopenharmony_ci	return resp;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistruct ksmbd_tree_connect_response *
55162306a36Sopenharmony_ciksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
55262306a36Sopenharmony_ci			       struct ksmbd_share_config *share,
55362306a36Sopenharmony_ci			       struct ksmbd_tree_connect *tree_conn,
55462306a36Sopenharmony_ci			       struct sockaddr *peer_addr)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
55762306a36Sopenharmony_ci	struct ksmbd_tree_connect_request *req;
55862306a36Sopenharmony_ci	struct ksmbd_tree_connect_response *resp;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
56162306a36Sopenharmony_ci		return NULL;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME)
56462306a36Sopenharmony_ci		return NULL;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request));
56762306a36Sopenharmony_ci	if (!msg)
56862306a36Sopenharmony_ci		return NULL;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST;
57162306a36Sopenharmony_ci	req = (struct ksmbd_tree_connect_request *)msg->payload;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	req->handle = ksmbd_acquire_id(&ipc_ida);
57462306a36Sopenharmony_ci	req->account_flags = sess->user->flags;
57562306a36Sopenharmony_ci	req->session_id = sess->id;
57662306a36Sopenharmony_ci	req->connect_id = tree_conn->id;
57762306a36Sopenharmony_ci	strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
57862306a36Sopenharmony_ci	strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME);
57962306a36Sopenharmony_ci	snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (peer_addr->sa_family == AF_INET6)
58262306a36Sopenharmony_ci		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6;
58362306a36Sopenharmony_ci	if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
58462306a36Sopenharmony_ci		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
58762306a36Sopenharmony_ci	ipc_msg_handle_free(req->handle);
58862306a36Sopenharmony_ci	ipc_msg_free(msg);
58962306a36Sopenharmony_ci	return resp;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ciint ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
59362306a36Sopenharmony_ci				      unsigned long long connect_id)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
59662306a36Sopenharmony_ci	struct ksmbd_tree_disconnect_request *req;
59762306a36Sopenharmony_ci	int ret;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request));
60062306a36Sopenharmony_ci	if (!msg)
60162306a36Sopenharmony_ci		return -ENOMEM;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST;
60462306a36Sopenharmony_ci	req = (struct ksmbd_tree_disconnect_request *)msg->payload;
60562306a36Sopenharmony_ci	req->session_id = session_id;
60662306a36Sopenharmony_ci	req->connect_id = connect_id;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	ret = ipc_msg_send(msg);
60962306a36Sopenharmony_ci	ipc_msg_free(msg);
61062306a36Sopenharmony_ci	return ret;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciint ksmbd_ipc_logout_request(const char *account, int flags)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
61662306a36Sopenharmony_ci	struct ksmbd_logout_request *req;
61762306a36Sopenharmony_ci	int ret;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
62062306a36Sopenharmony_ci		return -EINVAL;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request));
62362306a36Sopenharmony_ci	if (!msg)
62462306a36Sopenharmony_ci		return -ENOMEM;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_LOGOUT_REQUEST;
62762306a36Sopenharmony_ci	req = (struct ksmbd_logout_request *)msg->payload;
62862306a36Sopenharmony_ci	req->account_flags = flags;
62962306a36Sopenharmony_ci	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	ret = ipc_msg_send(msg);
63262306a36Sopenharmony_ci	ipc_msg_free(msg);
63362306a36Sopenharmony_ci	return ret;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistruct ksmbd_share_config_response *
63762306a36Sopenharmony_ciksmbd_ipc_share_config_request(const char *name)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
64062306a36Sopenharmony_ci	struct ksmbd_share_config_request *req;
64162306a36Sopenharmony_ci	struct ksmbd_share_config_response *resp;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME)
64462306a36Sopenharmony_ci		return NULL;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request));
64762306a36Sopenharmony_ci	if (!msg)
64862306a36Sopenharmony_ci		return NULL;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST;
65162306a36Sopenharmony_ci	req = (struct ksmbd_share_config_request *)msg->payload;
65262306a36Sopenharmony_ci	req->handle = ksmbd_acquire_id(&ipc_ida);
65362306a36Sopenharmony_ci	strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
65662306a36Sopenharmony_ci	ipc_msg_handle_free(req->handle);
65762306a36Sopenharmony_ci	ipc_msg_free(msg);
65862306a36Sopenharmony_ci	return resp;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
66462306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
66562306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
66862306a36Sopenharmony_ci	if (!msg)
66962306a36Sopenharmony_ci		return NULL;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
67262306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
67362306a36Sopenharmony_ci	req->handle = handle;
67462306a36Sopenharmony_ci	req->flags = ksmbd_session_rpc_method(sess, handle);
67562306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_OPEN_METHOD;
67662306a36Sopenharmony_ci	req->payload_sz = 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
67962306a36Sopenharmony_ci	ipc_msg_free(msg);
68062306a36Sopenharmony_ci	return resp;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
68662306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
68762306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
69062306a36Sopenharmony_ci	if (!msg)
69162306a36Sopenharmony_ci		return NULL;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
69462306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
69562306a36Sopenharmony_ci	req->handle = handle;
69662306a36Sopenharmony_ci	req->flags = ksmbd_session_rpc_method(sess, handle);
69762306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_CLOSE_METHOD;
69862306a36Sopenharmony_ci	req->payload_sz = 0;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
70162306a36Sopenharmony_ci	ipc_msg_free(msg);
70262306a36Sopenharmony_ci	return resp;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle,
70662306a36Sopenharmony_ci					  void *payload, size_t payload_sz)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
70962306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
71062306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
71362306a36Sopenharmony_ci	if (!msg)
71462306a36Sopenharmony_ci		return NULL;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
71762306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
71862306a36Sopenharmony_ci	req->handle = handle;
71962306a36Sopenharmony_ci	req->flags = ksmbd_session_rpc_method(sess, handle);
72062306a36Sopenharmony_ci	req->flags |= rpc_context_flags(sess);
72162306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_WRITE_METHOD;
72262306a36Sopenharmony_ci	req->payload_sz = payload_sz;
72362306a36Sopenharmony_ci	memcpy(req->payload, payload, payload_sz);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
72662306a36Sopenharmony_ci	ipc_msg_free(msg);
72762306a36Sopenharmony_ci	return resp;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
73362306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
73462306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
73762306a36Sopenharmony_ci	if (!msg)
73862306a36Sopenharmony_ci		return NULL;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
74162306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
74262306a36Sopenharmony_ci	req->handle = handle;
74362306a36Sopenharmony_ci	req->flags = ksmbd_session_rpc_method(sess, handle);
74462306a36Sopenharmony_ci	req->flags |= rpc_context_flags(sess);
74562306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_READ_METHOD;
74662306a36Sopenharmony_ci	req->payload_sz = 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
74962306a36Sopenharmony_ci	ipc_msg_free(msg);
75062306a36Sopenharmony_ci	return resp;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle,
75462306a36Sopenharmony_ci					  void *payload, size_t payload_sz)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
75762306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
75862306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
76162306a36Sopenharmony_ci	if (!msg)
76262306a36Sopenharmony_ci		return NULL;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
76562306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
76662306a36Sopenharmony_ci	req->handle = handle;
76762306a36Sopenharmony_ci	req->flags = ksmbd_session_rpc_method(sess, handle);
76862306a36Sopenharmony_ci	req->flags |= rpc_context_flags(sess);
76962306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_IOCTL_METHOD;
77062306a36Sopenharmony_ci	req->payload_sz = payload_sz;
77162306a36Sopenharmony_ci	memcpy(req->payload, payload, payload_sz);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
77462306a36Sopenharmony_ci	ipc_msg_free(msg);
77562306a36Sopenharmony_ci	return resp;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistruct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
77962306a36Sopenharmony_ci					size_t payload_sz)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct ksmbd_ipc_msg *msg;
78262306a36Sopenharmony_ci	struct ksmbd_rpc_command *req;
78362306a36Sopenharmony_ci	struct ksmbd_rpc_command *resp;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
78662306a36Sopenharmony_ci	if (!msg)
78762306a36Sopenharmony_ci		return NULL;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	msg->type = KSMBD_EVENT_RPC_REQUEST;
79062306a36Sopenharmony_ci	req = (struct ksmbd_rpc_command *)msg->payload;
79162306a36Sopenharmony_ci	req->handle = ksmbd_acquire_id(&ipc_ida);
79262306a36Sopenharmony_ci	req->flags = rpc_context_flags(sess);
79362306a36Sopenharmony_ci	req->flags |= KSMBD_RPC_RAP_METHOD;
79462306a36Sopenharmony_ci	req->payload_sz = payload_sz;
79562306a36Sopenharmony_ci	memcpy(req->payload, payload, payload_sz);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	resp = ipc_msg_send_request(msg, req->handle);
79862306a36Sopenharmony_ci	ipc_msg_handle_free(req->handle);
79962306a36Sopenharmony_ci	ipc_msg_free(msg);
80062306a36Sopenharmony_ci	return resp;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int __ipc_heartbeat(void)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	unsigned long delta;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (!ksmbd_server_running())
80862306a36Sopenharmony_ci		return 0;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	if (time_after(jiffies, server_conf.ipc_last_active)) {
81162306a36Sopenharmony_ci		delta = (jiffies - server_conf.ipc_last_active);
81262306a36Sopenharmony_ci	} else {
81362306a36Sopenharmony_ci		ipc_update_last_active();
81462306a36Sopenharmony_ci		schedule_delayed_work(&ipc_timer_work,
81562306a36Sopenharmony_ci				      server_conf.ipc_timeout);
81662306a36Sopenharmony_ci		return 0;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (delta < server_conf.ipc_timeout) {
82062306a36Sopenharmony_ci		schedule_delayed_work(&ipc_timer_work,
82162306a36Sopenharmony_ci				      server_conf.ipc_timeout - delta);
82262306a36Sopenharmony_ci		return 0;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (ksmbd_ipc_heartbeat_request() == 0) {
82662306a36Sopenharmony_ci		schedule_delayed_work(&ipc_timer_work,
82762306a36Sopenharmony_ci				      server_conf.ipc_timeout);
82862306a36Sopenharmony_ci		return 0;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	mutex_lock(&startup_lock);
83262306a36Sopenharmony_ci	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);
83362306a36Sopenharmony_ci	server_conf.ipc_last_active = 0;
83462306a36Sopenharmony_ci	ksmbd_tools_pid = 0;
83562306a36Sopenharmony_ci	pr_err("No IPC daemon response for %lus\n", delta / HZ);
83662306a36Sopenharmony_ci	mutex_unlock(&startup_lock);
83762306a36Sopenharmony_ci	return -EINVAL;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic void ipc_timer_heartbeat(struct work_struct *w)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	if (__ipc_heartbeat())
84362306a36Sopenharmony_ci		server_queue_ctrl_reset_work();
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ciint ksmbd_ipc_id_alloc(void)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	return ksmbd_acquire_id(&ipc_ida);
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_civoid ksmbd_rpc_id_free(int handle)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	ksmbd_release_id(&ipc_ida, handle);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_civoid ksmbd_ipc_release(void)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	cancel_delayed_work_sync(&ipc_timer_work);
85962306a36Sopenharmony_ci	genl_unregister_family(&ksmbd_genl_family);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_civoid ksmbd_ipc_soft_reset(void)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	mutex_lock(&startup_lock);
86562306a36Sopenharmony_ci	ksmbd_tools_pid = 0;
86662306a36Sopenharmony_ci	cancel_delayed_work_sync(&ipc_timer_work);
86762306a36Sopenharmony_ci	mutex_unlock(&startup_lock);
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciint ksmbd_ipc_init(void)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	int ret = 0;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	ksmbd_nl_init_fixup();
87562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	ret = genl_register_family(&ksmbd_genl_family);
87862306a36Sopenharmony_ci	if (ret) {
87962306a36Sopenharmony_ci		pr_err("Failed to register KSMBD netlink interface %d\n", ret);
88062306a36Sopenharmony_ci		cancel_delayed_work_sync(&ipc_timer_work);
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return ret;
88462306a36Sopenharmony_ci}
885