mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	cifsd: add server handler for central processing and tranport layers
This adds server handler for central processing, transport layers(tcp, rdma, ipc) and a document describing cifsd architecture. Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Acked-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
		
							parent
							
								
									6efb943b86
								
							
						
					
					
						commit
						0626e6641f
					
				
					 15 changed files with 5741 additions and 0 deletions
				
			
		
							
								
								
									
										136
									
								
								Documentation/filesystems/cifs/cifsd.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Documentation/filesystems/cifs/cifsd.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,136 @@
 | 
			
		|||
.. SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
 | 
			
		||||
=========================
 | 
			
		||||
CIFSD - SMB3 Kernel Server
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
CIFSD is a linux kernel server which implements SMB3 protocol in kernel space
 | 
			
		||||
for sharing files over network.
 | 
			
		||||
 | 
			
		||||
CIFSD architecture
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
The subset of performance related operations belong in kernelspace and
 | 
			
		||||
the other subset which belong to operations which are not really related with
 | 
			
		||||
performance in userspace. So, DCE/RPC management that has historically resulted
 | 
			
		||||
into number of buffer overflow issues and dangerous security bugs and user
 | 
			
		||||
account management are implemented in user space as ksmbd.mountd.
 | 
			
		||||
File operations that are related with performance (open/read/write/close etc.)
 | 
			
		||||
in kernel space (ksmbd). This also allows for easier integration with VFS
 | 
			
		||||
interface for all file operations.
 | 
			
		||||
 | 
			
		||||
ksmbd (kernel daemon)
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
When the server daemon is started, It starts up a forker thread
 | 
			
		||||
(ksmbd/interface name) at initialization time and open a dedicated port 445
 | 
			
		||||
for listening to SMB requests. Whenever new clients make request, Forker
 | 
			
		||||
thread will accept the client connection and fork a new thread for dedicated
 | 
			
		||||
communication channel between the client and the server. It allows for parallel
 | 
			
		||||
processing of SMB requests(commands) from clients as well as allowing for new
 | 
			
		||||
clients to make new connections. Each instance is named ksmbd/1~n(port number)
 | 
			
		||||
to indicate connected clients. Depending on the SMB request types, each new
 | 
			
		||||
thread can decide to pass through the commands to the user space (ksmbd.mountd),
 | 
			
		||||
currently DCE/RPC commands are identified to be handled through the user space.
 | 
			
		||||
To further utilize the linux kernel, it has been chosen to process the commands
 | 
			
		||||
as workitems and to be executed in the handlers of the ksmbd-io kworker threads.
 | 
			
		||||
It allows for multiplexing of the handlers as the kernel take care of initiating
 | 
			
		||||
extra worker threads if the load is increased and vice versa, if the load is
 | 
			
		||||
decreased it destroys the extra worker threads. So, after connection is
 | 
			
		||||
established with client. Dedicated ksmbd/1..n(port number) takes complete
 | 
			
		||||
ownership of receiving/parsing of SMB commands. Each received command is worked
 | 
			
		||||
in parallel i.e., There can be multiple clients commands which are worked in
 | 
			
		||||
parallel. After receiving each command a separated kernel workitem is prepared
 | 
			
		||||
for each command which is further queued to be handled by ksmbd-io kworkers.
 | 
			
		||||
So, each SMB workitem is queued to the kworkers. This allows the benefit of load
 | 
			
		||||
sharing to be managed optimally by the default kernel and optimizing client
 | 
			
		||||
performance by handling client commands in parallel.
 | 
			
		||||
 | 
			
		||||
ksmbd.mountd (user space daemon)
 | 
			
		||||
--------------------------------
 | 
			
		||||
 | 
			
		||||
ksmbd.mountd is userspace process to, transfer user account and password that
 | 
			
		||||
are registered using ksmbd.adduser(part of utils for user space). Further it
 | 
			
		||||
allows sharing information parameters that parsed from smb.conf to ksmbd in
 | 
			
		||||
kernel. For the execution part it has a daemon which is continuously running
 | 
			
		||||
and connected to the kernel interface using netlink socket, it waits for the
 | 
			
		||||
requests(dcerpc and share/user info). It handles RPC calls (at a minimum few
 | 
			
		||||
dozen) that are most important for file server from NetShareEnum and
 | 
			
		||||
NetServerGetInfo. Complete DCE/RPC response is prepared from the user space
 | 
			
		||||
and passed over to the associated kernel thread for the client.
 | 
			
		||||
 | 
			
		||||
Key Features
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
The supported features are:
 | 
			
		||||
 * SMB3 protocols for basic file sharing
 | 
			
		||||
 * Auto negotiation
 | 
			
		||||
 * Compound requests
 | 
			
		||||
 * Oplock/Lease
 | 
			
		||||
 * Large MTU
 | 
			
		||||
 * NTLM/NTLMv2
 | 
			
		||||
 * HMAC-SHA256 Signing
 | 
			
		||||
 * Secure negotiate
 | 
			
		||||
 * Signing Update
 | 
			
		||||
 * Pre-authentication integrity(SMB 3.1.1)
 | 
			
		||||
 * SMB3 encryption(CCM, GCM)
 | 
			
		||||
 * SMB direct(RDMA)
 | 
			
		||||
 * SMB3.1.1 POSIX extension support
 | 
			
		||||
 * ACLs
 | 
			
		||||
 * Kerberos
 | 
			
		||||
 | 
			
		||||
The features that are planned or not supported:
 | 
			
		||||
 * SMB3 Multi-channel
 | 
			
		||||
 * Durable handle v1,v2
 | 
			
		||||
 * Persistent handles
 | 
			
		||||
 * Directory lease
 | 
			
		||||
 * SMB2 notify
 | 
			
		||||
 | 
			
		||||
How to run
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
1. Download ksmbd-tools and compile them.
 | 
			
		||||
	- https://github.com/cifsd-team/ksmbd-tools
 | 
			
		||||
 | 
			
		||||
2. Create user/password for SMB share.
 | 
			
		||||
 | 
			
		||||
	# mkdir /etc/ksmbd/
 | 
			
		||||
	# ksmbd.adduser -a <Enter USERNAME for SMB share access>
 | 
			
		||||
 | 
			
		||||
3. Create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file
 | 
			
		||||
	- Refer smb.conf.example and Documentation/configuration.txt
 | 
			
		||||
	  in ksmbd-tools
 | 
			
		||||
 | 
			
		||||
4. Insert ksmbd.ko module
 | 
			
		||||
 | 
			
		||||
	# insmod ksmbd.ko
 | 
			
		||||
 | 
			
		||||
5. Start ksmbd user space daemon
 | 
			
		||||
	# ksmbd.mountd
 | 
			
		||||
 | 
			
		||||
6. Access share from Windows or Linux using CIFS
 | 
			
		||||
 | 
			
		||||
Shutdown CIFSD
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
1. kill user and kernel space daemon
 | 
			
		||||
	# sudo ksmbd.control -s
 | 
			
		||||
 | 
			
		||||
How to turn debug print on
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
Each layer
 | 
			
		||||
/sys/class/ksmbd-control/debug
 | 
			
		||||
 | 
			
		||||
1. Enable all component prints
 | 
			
		||||
	# sudo ksmbd.control -d "all"
 | 
			
		||||
 | 
			
		||||
2. Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma)
 | 
			
		||||
	# sudo ksmbd.control -d "smb"
 | 
			
		||||
 | 
			
		||||
3. Show what prints are enable.
 | 
			
		||||
	# cat/sys/class/ksmbd-control/debug
 | 
			
		||||
	  [smb] auth vfs oplock ipc conn [rdma]
 | 
			
		||||
 | 
			
		||||
4. Disable prints:
 | 
			
		||||
	If you try the selected component once more, It is disabled without brackets.
 | 
			
		||||
							
								
								
									
										416
									
								
								fs/cifsd/connection.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								fs/cifsd/connection.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,416 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2016 Namjae Jeon <namjae.jeon@protocolfreedom.org>
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/freezer.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include "server.h"
 | 
			
		||||
#include "buffer_pool.h"
 | 
			
		||||
#include "smb_common.h"
 | 
			
		||||
#include "mgmt/ksmbd_ida.h"
 | 
			
		||||
#include "connection.h"
 | 
			
		||||
#include "transport_tcp.h"
 | 
			
		||||
#include "transport_rdma.h"
 | 
			
		||||
 | 
			
		||||
static DEFINE_MUTEX(init_lock);
 | 
			
		||||
 | 
			
		||||
static struct ksmbd_conn_ops default_conn_ops;
 | 
			
		||||
 | 
			
		||||
static LIST_HEAD(conn_list);
 | 
			
		||||
static DEFINE_RWLOCK(conn_list_lock);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_conn_free() - free resources of the connection instance
 | 
			
		||||
 *
 | 
			
		||||
 * @conn:	connection instance to be cleand up
 | 
			
		||||
 *
 | 
			
		||||
 * During the thread termination, the corresponding conn instance
 | 
			
		||||
 * resources(sock/memory) are released and finally the conn object is freed.
 | 
			
		||||
 */
 | 
			
		||||
void ksmbd_conn_free(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	write_lock(&conn_list_lock);
 | 
			
		||||
	list_del(&conn->conns_list);
 | 
			
		||||
	write_unlock(&conn_list_lock);
 | 
			
		||||
 | 
			
		||||
	ksmbd_free_request(conn->request_buf);
 | 
			
		||||
	ksmbd_ida_free(conn->async_ida);
 | 
			
		||||
	kfree(conn->preauth_info);
 | 
			
		||||
	kfree(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_conn_alloc() - initialize a new connection instance
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	ksmbd_conn struct on success, otherwise NULL
 | 
			
		||||
 */
 | 
			
		||||
struct ksmbd_conn *ksmbd_conn_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	conn->need_neg = true;
 | 
			
		||||
	conn->status = KSMBD_SESS_NEW;
 | 
			
		||||
	conn->local_nls = load_nls("utf8");
 | 
			
		||||
	if (!conn->local_nls)
 | 
			
		||||
		conn->local_nls = load_nls_default();
 | 
			
		||||
	atomic_set(&conn->req_running, 0);
 | 
			
		||||
	atomic_set(&conn->r_count, 0);
 | 
			
		||||
	init_waitqueue_head(&conn->req_running_q);
 | 
			
		||||
	INIT_LIST_HEAD(&conn->conns_list);
 | 
			
		||||
	INIT_LIST_HEAD(&conn->sessions);
 | 
			
		||||
	INIT_LIST_HEAD(&conn->requests);
 | 
			
		||||
	INIT_LIST_HEAD(&conn->async_requests);
 | 
			
		||||
	spin_lock_init(&conn->request_lock);
 | 
			
		||||
	spin_lock_init(&conn->credits_lock);
 | 
			
		||||
	conn->async_ida = ksmbd_ida_alloc();
 | 
			
		||||
 | 
			
		||||
	write_lock(&conn_list_lock);
 | 
			
		||||
	list_add(&conn->conns_list, &conn_list);
 | 
			
		||||
	write_unlock(&conn_list_lock);
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *t;
 | 
			
		||||
	bool ret = false;
 | 
			
		||||
 | 
			
		||||
	read_lock(&conn_list_lock);
 | 
			
		||||
	list_for_each_entry(t, &conn_list, conns_list) {
 | 
			
		||||
		if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		ret = true;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	read_unlock(&conn_list_lock);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn = work->conn;
 | 
			
		||||
	struct list_head *requests_queue = NULL;
 | 
			
		||||
 | 
			
		||||
	if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
 | 
			
		||||
		requests_queue = &conn->requests;
 | 
			
		||||
		work->syncronous = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (requests_queue) {
 | 
			
		||||
		atomic_inc(&conn->req_running);
 | 
			
		||||
		spin_lock(&conn->request_lock);
 | 
			
		||||
		list_add_tail(&work->request_entry, requests_queue);
 | 
			
		||||
		spin_unlock(&conn->request_lock);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn = work->conn;
 | 
			
		||||
	int ret = 1;
 | 
			
		||||
 | 
			
		||||
	if (list_empty(&work->request_entry) &&
 | 
			
		||||
		list_empty(&work->async_request_entry))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	atomic_dec(&conn->req_running);
 | 
			
		||||
	spin_lock(&conn->request_lock);
 | 
			
		||||
	if (!work->multiRsp) {
 | 
			
		||||
		list_del_init(&work->request_entry);
 | 
			
		||||
		if (work->syncronous == false)
 | 
			
		||||
			list_del_init(&work->async_request_entry);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&conn->request_lock);
 | 
			
		||||
 | 
			
		||||
	wake_up_all(&conn->req_running_q);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ksmbd_conn_lock(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&conn->srv_mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ksmbd_conn_unlock(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	mutex_unlock(&conn->srv_mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_write(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn = work->conn;
 | 
			
		||||
	struct smb_hdr *rsp_hdr = RESPONSE_BUF(work);
 | 
			
		||||
	size_t len = 0;
 | 
			
		||||
	int sent;
 | 
			
		||||
	struct kvec iov[3];
 | 
			
		||||
	int iov_idx = 0;
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_try_dequeue_request(work);
 | 
			
		||||
	if (!rsp_hdr) {
 | 
			
		||||
		ksmbd_err("NULL response header\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (HAS_TRANSFORM_BUF(work)) {
 | 
			
		||||
		iov[iov_idx] = (struct kvec) { work->tr_buf,
 | 
			
		||||
				sizeof(struct smb2_transform_hdr) };
 | 
			
		||||
		len += iov[iov_idx++].iov_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (HAS_AUX_PAYLOAD(work)) {
 | 
			
		||||
		iov[iov_idx] = (struct kvec) { rsp_hdr, RESP_HDR_SIZE(work) };
 | 
			
		||||
		len += iov[iov_idx++].iov_len;
 | 
			
		||||
		iov[iov_idx] = (struct kvec) { AUX_PAYLOAD(work),
 | 
			
		||||
			AUX_PAYLOAD_SIZE(work) };
 | 
			
		||||
		len += iov[iov_idx++].iov_len;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (HAS_TRANSFORM_BUF(work))
 | 
			
		||||
			iov[iov_idx].iov_len = RESP_HDR_SIZE(work);
 | 
			
		||||
		else
 | 
			
		||||
			iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4;
 | 
			
		||||
		iov[iov_idx].iov_base = rsp_hdr;
 | 
			
		||||
		len += iov[iov_idx++].iov_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_lock(conn);
 | 
			
		||||
	sent = conn->transport->ops->writev(conn->transport, &iov[0],
 | 
			
		||||
					iov_idx, len,
 | 
			
		||||
					work->need_invalidate_rkey,
 | 
			
		||||
					work->remote_key);
 | 
			
		||||
	ksmbd_conn_unlock(conn);
 | 
			
		||||
 | 
			
		||||
	if (sent < 0) {
 | 
			
		||||
		ksmbd_err("Failed to send message: %d\n", sent);
 | 
			
		||||
		return sent;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
 | 
			
		||||
				void *buf, unsigned int buflen,
 | 
			
		||||
				u32 remote_key, u64 remote_offset,
 | 
			
		||||
				u32 remote_len)
 | 
			
		||||
{
 | 
			
		||||
	int ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (conn->transport->ops->rdma_read)
 | 
			
		||||
		ret = conn->transport->ops->rdma_read(conn->transport,
 | 
			
		||||
						buf, buflen,
 | 
			
		||||
						remote_key, remote_offset,
 | 
			
		||||
						remote_len);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
 | 
			
		||||
				void *buf, unsigned int buflen,
 | 
			
		||||
				u32 remote_key, u64 remote_offset,
 | 
			
		||||
				u32 remote_len)
 | 
			
		||||
{
 | 
			
		||||
	int ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (conn->transport->ops->rdma_write)
 | 
			
		||||
		ret = conn->transport->ops->rdma_write(conn->transport,
 | 
			
		||||
						buf, buflen,
 | 
			
		||||
						remote_key, remote_offset,
 | 
			
		||||
						remote_len);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ksmbd_conn_alive(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (!ksmbd_server_running())
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (conn->status == KSMBD_SESS_EXITING)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (kthread_should_stop())
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (atomic_read(&conn->stats.open_files_count) > 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Stop current session if the time that get last request from client
 | 
			
		||||
	 * is bigger than deadtime user configured and openning file count is
 | 
			
		||||
	 * zero.
 | 
			
		||||
	 */
 | 
			
		||||
	if (server_conf.deadtime > 0 &&
 | 
			
		||||
		time_after(jiffies, conn->last_active + server_conf.deadtime)) {
 | 
			
		||||
		ksmbd_debug(CONN, "No response from client in %lu minutes\n",
 | 
			
		||||
			server_conf.deadtime / SMB_ECHO_INTERVAL);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_conn_handler_loop() - session thread to listen on new smb requests
 | 
			
		||||
 * @p:		connection instance
 | 
			
		||||
 *
 | 
			
		||||
 * One thread each per connection
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	0 on success
 | 
			
		||||
 */
 | 
			
		||||
int ksmbd_conn_handler_loop(void *p)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
 | 
			
		||||
	struct ksmbd_transport *t = conn->transport;
 | 
			
		||||
	unsigned int pdu_size;
 | 
			
		||||
	char hdr_buf[4] = {0,};
 | 
			
		||||
	int size;
 | 
			
		||||
 | 
			
		||||
	mutex_init(&conn->srv_mutex);
 | 
			
		||||
	__module_get(THIS_MODULE);
 | 
			
		||||
 | 
			
		||||
	if (t->ops->prepare && t->ops->prepare(t))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	conn->last_active = jiffies;
 | 
			
		||||
	while (ksmbd_conn_alive(conn)) {
 | 
			
		||||
		if (try_to_freeze())
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		ksmbd_free_request(conn->request_buf);
 | 
			
		||||
		conn->request_buf = NULL;
 | 
			
		||||
 | 
			
		||||
		size = t->ops->read(t, hdr_buf, sizeof(hdr_buf));
 | 
			
		||||
		if (size != sizeof(hdr_buf))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		pdu_size = get_rfc1002_len(hdr_buf);
 | 
			
		||||
		ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
 | 
			
		||||
 | 
			
		||||
		/* make sure we have enough to get to SMB header end */
 | 
			
		||||
		if (!ksmbd_pdu_size_has_room(pdu_size)) {
 | 
			
		||||
			ksmbd_debug(CONN, "SMB request too short (%u bytes)\n",
 | 
			
		||||
				    pdu_size);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* 4 for rfc1002 length field */
 | 
			
		||||
		size = pdu_size + 4;
 | 
			
		||||
		conn->request_buf = ksmbd_alloc_request(size);
 | 
			
		||||
		if (!conn->request_buf)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf));
 | 
			
		||||
		if (!ksmbd_smb_request(conn))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * We already read 4 bytes to find out PDU size, now
 | 
			
		||||
		 * read in PDU
 | 
			
		||||
		 */
 | 
			
		||||
		size = t->ops->read(t, conn->request_buf + 4, pdu_size);
 | 
			
		||||
		if (size < 0) {
 | 
			
		||||
			ksmbd_err("sock_read failed: %d\n", size);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (size != pdu_size) {
 | 
			
		||||
			ksmbd_err("PDU error. Read: %d, Expected: %d\n",
 | 
			
		||||
				  size,
 | 
			
		||||
				  pdu_size);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!default_conn_ops.process_fn) {
 | 
			
		||||
			ksmbd_err("No connection request callback\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (default_conn_ops.process_fn(conn)) {
 | 
			
		||||
			ksmbd_err("Cannot handle request\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	/* Wait till all reference dropped to the Server object*/
 | 
			
		||||
	while (atomic_read(&conn->r_count) > 0)
 | 
			
		||||
		schedule_timeout(HZ);
 | 
			
		||||
 | 
			
		||||
	unload_nls(conn->local_nls);
 | 
			
		||||
	if (default_conn_ops.terminate_fn)
 | 
			
		||||
		default_conn_ops.terminate_fn(conn);
 | 
			
		||||
	t->ops->disconnect(t);
 | 
			
		||||
	module_put(THIS_MODULE);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
 | 
			
		||||
{
 | 
			
		||||
	default_conn_ops.process_fn = ops->process_fn;
 | 
			
		||||
	default_conn_ops.terminate_fn = ops->terminate_fn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_transport_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&init_lock);
 | 
			
		||||
	ret = ksmbd_tcp_init();
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("Failed to init TCP subsystem: %d\n", ret);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_rdma_init();
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("Failed to init KSMBD subsystem: %d\n", ret);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	mutex_unlock(&init_lock);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void stop_sessions(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn *conn;
 | 
			
		||||
 | 
			
		||||
again:
 | 
			
		||||
	read_lock(&conn_list_lock);
 | 
			
		||||
	list_for_each_entry(conn, &conn_list, conns_list) {
 | 
			
		||||
		struct task_struct *task;
 | 
			
		||||
 | 
			
		||||
		task = conn->transport->handler;
 | 
			
		||||
		if (task)
 | 
			
		||||
			ksmbd_debug(CONN, "Stop session handler %s/%d\n",
 | 
			
		||||
				  task->comm,
 | 
			
		||||
				  task_pid_nr(task));
 | 
			
		||||
		conn->status = KSMBD_SESS_EXITING;
 | 
			
		||||
	}
 | 
			
		||||
	read_unlock(&conn_list_lock);
 | 
			
		||||
 | 
			
		||||
	if (!list_empty(&conn_list)) {
 | 
			
		||||
		schedule_timeout_interruptible(HZ/10); /* 100ms */
 | 
			
		||||
		goto again;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_conn_transport_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&init_lock);
 | 
			
		||||
	ksmbd_tcp_destroy();
 | 
			
		||||
	ksmbd_rdma_destroy();
 | 
			
		||||
	stop_sessions();
 | 
			
		||||
	mutex_unlock(&init_lock);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										212
									
								
								fs/cifsd/connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								fs/cifsd/connection.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_CONNECTION_H__
 | 
			
		||||
#define __KSMBD_CONNECTION_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/list.h>
 | 
			
		||||
#include <linux/ip.h>
 | 
			
		||||
#include <net/sock.h>
 | 
			
		||||
#include <net/tcp.h>
 | 
			
		||||
#include <net/inet_connection_sock.h>
 | 
			
		||||
#include <net/request_sock.h>
 | 
			
		||||
#include <linux/kthread.h>
 | 
			
		||||
#include <linux/nls.h>
 | 
			
		||||
 | 
			
		||||
#include "smb_common.h"
 | 
			
		||||
#include "ksmbd_work.h"
 | 
			
		||||
 | 
			
		||||
#define KSMBD_SOCKET_BACKLOG		16
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * WARNING
 | 
			
		||||
 *
 | 
			
		||||
 * This is nothing but a HACK. Session status should move to channel
 | 
			
		||||
 * or to session. As of now we have 1 tcp_conn : 1 ksmbd_session, but
 | 
			
		||||
 * we need to change it to 1 tcp_conn : N ksmbd_sessions.
 | 
			
		||||
 */
 | 
			
		||||
enum {
 | 
			
		||||
	KSMBD_SESS_NEW = 0,
 | 
			
		||||
	KSMBD_SESS_GOOD,
 | 
			
		||||
	KSMBD_SESS_EXITING,
 | 
			
		||||
	KSMBD_SESS_NEED_RECONNECT,
 | 
			
		||||
	KSMBD_SESS_NEED_NEGOTIATE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ksmbd_stats {
 | 
			
		||||
	atomic_t			open_files_count;
 | 
			
		||||
	atomic64_t			request_served;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ksmbd_transport;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_conn {
 | 
			
		||||
	struct smb_version_values	*vals;
 | 
			
		||||
	struct smb_version_ops		*ops;
 | 
			
		||||
	struct smb_version_cmds		*cmds;
 | 
			
		||||
	unsigned int			max_cmds;
 | 
			
		||||
	struct mutex			srv_mutex;
 | 
			
		||||
	int				status;
 | 
			
		||||
	unsigned int			cli_cap;
 | 
			
		||||
	char				*request_buf;
 | 
			
		||||
	struct ksmbd_transport		*transport;
 | 
			
		||||
	struct nls_table		*local_nls;
 | 
			
		||||
	struct list_head		conns_list;
 | 
			
		||||
	/* smb session 1 per user */
 | 
			
		||||
	struct list_head		sessions;
 | 
			
		||||
	unsigned long			last_active;
 | 
			
		||||
	/* How many request are running currently */
 | 
			
		||||
	atomic_t			req_running;
 | 
			
		||||
	/* References which are made for this Server object*/
 | 
			
		||||
	atomic_t			r_count;
 | 
			
		||||
	unsigned short			total_credits;
 | 
			
		||||
	unsigned short			max_credits;
 | 
			
		||||
	spinlock_t			credits_lock;
 | 
			
		||||
	wait_queue_head_t		req_running_q;
 | 
			
		||||
	/* Lock to protect requests list*/
 | 
			
		||||
	spinlock_t			request_lock;
 | 
			
		||||
	struct list_head		requests;
 | 
			
		||||
	struct list_head		async_requests;
 | 
			
		||||
	int				connection_type;
 | 
			
		||||
	struct ksmbd_stats		stats;
 | 
			
		||||
	char				ClientGUID[SMB2_CLIENT_GUID_SIZE];
 | 
			
		||||
	union {
 | 
			
		||||
		/* pending trans request table */
 | 
			
		||||
		struct trans_state	*recent_trans;
 | 
			
		||||
		/* Used by ntlmssp */
 | 
			
		||||
		char			*ntlmssp_cryptkey;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct preauth_integrity_info	*preauth_info;
 | 
			
		||||
 | 
			
		||||
	bool				need_neg;
 | 
			
		||||
	unsigned int			auth_mechs;
 | 
			
		||||
	unsigned int			preferred_auth_mech;
 | 
			
		||||
	bool				sign;
 | 
			
		||||
	bool				use_spnego:1;
 | 
			
		||||
	__u16				cli_sec_mode;
 | 
			
		||||
	__u16				srv_sec_mode;
 | 
			
		||||
	/* dialect index that server chose */
 | 
			
		||||
	__u16				dialect;
 | 
			
		||||
 | 
			
		||||
	char				*mechToken;
 | 
			
		||||
 | 
			
		||||
	struct ksmbd_conn_ops	*conn_ops;
 | 
			
		||||
 | 
			
		||||
	/* Preauth Session Table */
 | 
			
		||||
	struct list_head		preauth_sess_table;
 | 
			
		||||
 | 
			
		||||
	struct sockaddr_storage		peer_addr;
 | 
			
		||||
 | 
			
		||||
	/* Identifier for async message */
 | 
			
		||||
	struct ksmbd_ida		*async_ida;
 | 
			
		||||
 | 
			
		||||
	__le16				cipher_type;
 | 
			
		||||
	__le16				compress_algorithm;
 | 
			
		||||
	bool				posix_ext_supported;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ksmbd_conn_ops {
 | 
			
		||||
	int	(*process_fn)(struct ksmbd_conn *conn);
 | 
			
		||||
	int	(*terminate_fn)(struct ksmbd_conn *conn);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ksmbd_transport_ops {
 | 
			
		||||
	int (*prepare)(struct ksmbd_transport *t);
 | 
			
		||||
	void (*disconnect)(struct ksmbd_transport *t);
 | 
			
		||||
	int (*read)(struct ksmbd_transport *t,
 | 
			
		||||
			char *buf, unsigned int size);
 | 
			
		||||
	int (*writev)(struct ksmbd_transport *t,
 | 
			
		||||
			struct kvec *iovs, int niov, int size,
 | 
			
		||||
			bool need_invalidate_rkey, unsigned int remote_key);
 | 
			
		||||
	int (*rdma_read)(struct ksmbd_transport *t,
 | 
			
		||||
				void *buf, unsigned int len, u32 remote_key,
 | 
			
		||||
				u64 remote_offset, u32 remote_len);
 | 
			
		||||
	int (*rdma_write)(struct ksmbd_transport *t,
 | 
			
		||||
				void *buf, unsigned int len, u32 remote_key,
 | 
			
		||||
				u64 remote_offset, u32 remote_len);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ksmbd_transport {
 | 
			
		||||
	struct ksmbd_conn		*conn;
 | 
			
		||||
	struct ksmbd_transport_ops	*ops;
 | 
			
		||||
	struct task_struct		*handler;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define KSMBD_TCP_RECV_TIMEOUT	(7 * HZ)
 | 
			
		||||
#define KSMBD_TCP_SEND_TIMEOUT	(5 * HZ)
 | 
			
		||||
#define KSMBD_TCP_PEER_SOCKADDR(c)	((struct sockaddr *)&((c)->peer_addr))
 | 
			
		||||
 | 
			
		||||
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
 | 
			
		||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_conn *ksmbd_conn_alloc(void);
 | 
			
		||||
void ksmbd_conn_free(struct ksmbd_conn *conn);
 | 
			
		||||
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
 | 
			
		||||
int ksmbd_conn_write(struct ksmbd_work *work);
 | 
			
		||||
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
 | 
			
		||||
				void *buf, unsigned int buflen,
 | 
			
		||||
				u32 remote_key, u64 remote_offset,
 | 
			
		||||
				u32 remote_len);
 | 
			
		||||
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
 | 
			
		||||
				void *buf, unsigned int buflen,
 | 
			
		||||
				u32 remote_key, u64 remote_offset,
 | 
			
		||||
				u32 remote_len);
 | 
			
		||||
 | 
			
		||||
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
 | 
			
		||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
 | 
			
		||||
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_handler_loop(void *p);
 | 
			
		||||
 | 
			
		||||
int ksmbd_conn_transport_init(void);
 | 
			
		||||
void ksmbd_conn_transport_destroy(void);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * WARNING
 | 
			
		||||
 *
 | 
			
		||||
 * This is a hack. We will move status to a proper place once we land
 | 
			
		||||
 * a multi-sessions support.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ksmbd_conn_good(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	return work->conn->status == KSMBD_SESS_GOOD;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	return work->conn->status == KSMBD_SESS_NEED_RECONNECT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ksmbd_conn_exiting(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	return work->conn->status == KSMBD_SESS_EXITING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_conn_set_good(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	work->conn->status = KSMBD_SESS_GOOD;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	work->conn->status = KSMBD_SESS_NEED_NEGOTIATE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	work->conn->status = KSMBD_SESS_NEED_RECONNECT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	work->conn->status = KSMBD_SESS_EXITING;
 | 
			
		||||
}
 | 
			
		||||
#endif /* __CONNECTION_H__ */
 | 
			
		||||
							
								
								
									
										67
									
								
								fs/cifsd/glob.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								fs/cifsd/glob.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_GLOB_H
 | 
			
		||||
#define __KSMBD_GLOB_H
 | 
			
		||||
 | 
			
		||||
#include <linux/ctype.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
 | 
			
		||||
#include "unicode.h"
 | 
			
		||||
#include "vfs_cache.h"
 | 
			
		||||
#include "smberr.h"
 | 
			
		||||
 | 
			
		||||
#define KSMBD_VERSION	"3.1.9"
 | 
			
		||||
 | 
			
		||||
/* @FIXME clean up this code */
 | 
			
		||||
 | 
			
		||||
extern int ksmbd_debug_types;
 | 
			
		||||
extern int ksmbd_caseless_search;
 | 
			
		||||
 | 
			
		||||
#define DATA_STREAM	1
 | 
			
		||||
#define DIR_STREAM	2
 | 
			
		||||
 | 
			
		||||
#define KSMBD_DEBUG_SMB         (1 << 0)
 | 
			
		||||
#define KSMBD_DEBUG_AUTH        (1 << 1)
 | 
			
		||||
#define KSMBD_DEBUG_VFS         (1 << 2)
 | 
			
		||||
#define KSMBD_DEBUG_OPLOCK      (1 << 3)
 | 
			
		||||
#define KSMBD_DEBUG_IPC         (1 << 4)
 | 
			
		||||
#define KSMBD_DEBUG_CONN        (1 << 5)
 | 
			
		||||
#define KSMBD_DEBUG_RDMA        (1 << 6)
 | 
			
		||||
#define KSMBD_DEBUG_ALL         (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH |	\
 | 
			
		||||
				KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK |	\
 | 
			
		||||
				KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN |	\
 | 
			
		||||
				KSMBD_DEBUG_RDMA)
 | 
			
		||||
 | 
			
		||||
#ifndef ksmbd_pr_fmt
 | 
			
		||||
#ifdef SUBMOD_NAME
 | 
			
		||||
#define ksmbd_pr_fmt(fmt)	"ksmbd: " SUBMOD_NAME ": " fmt
 | 
			
		||||
#else
 | 
			
		||||
#define ksmbd_pr_fmt(fmt)	"ksmbd: " fmt
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define ksmbd_debug(type, fmt, ...)				\
 | 
			
		||||
	do {							\
 | 
			
		||||
		if (ksmbd_debug_types & KSMBD_DEBUG_##type)	\
 | 
			
		||||
			pr_info(ksmbd_pr_fmt("%s:%d: " fmt),	\
 | 
			
		||||
				__func__,			\
 | 
			
		||||
				__LINE__,			\
 | 
			
		||||
				##__VA_ARGS__);			\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
#define ksmbd_info(fmt, ...)					\
 | 
			
		||||
			pr_info(ksmbd_pr_fmt(fmt), ##__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
#define ksmbd_err(fmt, ...)					\
 | 
			
		||||
			pr_err(ksmbd_pr_fmt("%s:%d: " fmt),	\
 | 
			
		||||
				__func__,			\
 | 
			
		||||
				__LINE__,			\
 | 
			
		||||
				##__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
#define UNICODE_LEN(x)		((x) * 2)
 | 
			
		||||
 | 
			
		||||
#endif /* __KSMBD_GLOB_H */
 | 
			
		||||
							
								
								
									
										285
									
								
								fs/cifsd/ksmbd_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								fs/cifsd/ksmbd_server.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,285 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *   linux-ksmbd-devel@lists.sourceforge.net
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LINUX_KSMBD_SERVER_H
 | 
			
		||||
#define _LINUX_KSMBD_SERVER_H
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
#define KSMBD_GENL_NAME		"SMBD_GENL"
 | 
			
		||||
#define KSMBD_GENL_VERSION		0x01
 | 
			
		||||
 | 
			
		||||
#ifndef ____ksmbd_align
 | 
			
		||||
#define ____ksmbd_align		__aligned(4)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ	48
 | 
			
		||||
#define KSMBD_REQ_MAX_HASH_SZ		18
 | 
			
		||||
#define KSMBD_REQ_MAX_SHARE_NAME	64
 | 
			
		||||
 | 
			
		||||
struct ksmbd_heartbeat {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Global config flags.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_INVALID		(0)
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_SMB2_LEASES		(1 << 0)
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_CACHE_TBUF		(1 << 1)
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_CACHE_RBUF		(1 << 2)
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION	(1 << 3)
 | 
			
		||||
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE	(1 << 4)
 | 
			
		||||
 | 
			
		||||
struct ksmbd_startup_request {
 | 
			
		||||
	__u32	flags;
 | 
			
		||||
	__s32	signing;
 | 
			
		||||
	__s8	min_prot[16];
 | 
			
		||||
	__s8	max_prot[16];
 | 
			
		||||
	__s8	netbios_name[16];
 | 
			
		||||
	__s8	work_group[64];
 | 
			
		||||
	__s8	server_string[64];
 | 
			
		||||
	__u16	tcp_port;
 | 
			
		||||
	__u16	ipc_timeout;
 | 
			
		||||
	__u32	deadtime;
 | 
			
		||||
	__u32	file_max;
 | 
			
		||||
	__u32	smb2_max_write;
 | 
			
		||||
	__u32	smb2_max_read;
 | 
			
		||||
	__u32	smb2_max_trans;
 | 
			
		||||
	__u32	share_fake_fscaps;
 | 
			
		||||
	__u32	sub_auth[3];
 | 
			
		||||
	__u32	ifc_list_sz;
 | 
			
		||||
	__s8	____payload[0];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
#define KSMBD_STARTUP_CONFIG_INTERFACES(s)	((s)->____payload)
 | 
			
		||||
 | 
			
		||||
struct ksmbd_shutdown_request {
 | 
			
		||||
	__s32	reserved;
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_login_request {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_login_response {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u32	gid;
 | 
			
		||||
	__u32	uid;
 | 
			
		||||
	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ];
 | 
			
		||||
	__u16	status;
 | 
			
		||||
	__u16	hash_sz;
 | 
			
		||||
	__s8	hash[KSMBD_REQ_MAX_HASH_SZ];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_share_config_request {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__s8	share_name[KSMBD_REQ_MAX_SHARE_NAME];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_share_config_response {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u32	flags;
 | 
			
		||||
	__u16	create_mask;
 | 
			
		||||
	__u16	directory_mask;
 | 
			
		||||
	__u16	force_create_mode;
 | 
			
		||||
	__u16	force_directory_mode;
 | 
			
		||||
	__u16	force_uid;
 | 
			
		||||
	__u16	force_gid;
 | 
			
		||||
	__u32	veto_list_sz;
 | 
			
		||||
	__s8	____payload[0];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
#define KSMBD_SHARE_CONFIG_VETO_LIST(s)	((s)->____payload)
 | 
			
		||||
#define KSMBD_SHARE_CONFIG_PATH(s)				\
 | 
			
		||||
	({							\
 | 
			
		||||
		char *p = (s)->____payload;			\
 | 
			
		||||
		if ((s)->veto_list_sz)				\
 | 
			
		||||
			p += (s)->veto_list_sz + 1;		\
 | 
			
		||||
		p;						\
 | 
			
		||||
	 })
 | 
			
		||||
 | 
			
		||||
struct ksmbd_tree_connect_request {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u16	account_flags;
 | 
			
		||||
	__u16	flags;
 | 
			
		||||
	__u64	session_id;
 | 
			
		||||
	__u64	connect_id;
 | 
			
		||||
	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ];
 | 
			
		||||
	__s8	share[KSMBD_REQ_MAX_SHARE_NAME];
 | 
			
		||||
	__s8	peer_addr[64];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_tree_connect_response {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u16	status;
 | 
			
		||||
	__u16	connection_flags;
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_tree_disconnect_request {
 | 
			
		||||
	__u64	session_id;
 | 
			
		||||
	__u64	connect_id;
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_logout_request {
 | 
			
		||||
	__s8	account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u32	flags;
 | 
			
		||||
	__u32	payload_sz;
 | 
			
		||||
	__u8	payload[0];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_spnego_authen_request {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	__u16	spnego_blob_len;
 | 
			
		||||
	__u8	spnego_blob[0];
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_spnego_authen_response {
 | 
			
		||||
	__u32	handle;
 | 
			
		||||
	struct ksmbd_login_response	login_response;
 | 
			
		||||
	__u16	session_key_len;
 | 
			
		||||
	__u16	spnego_blob_len;
 | 
			
		||||
	__u8	payload[0];		/* session key + AP_REP */
 | 
			
		||||
} ____ksmbd_align;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This also used as NETLINK attribute type value.
 | 
			
		||||
 *
 | 
			
		||||
 * NOTE:
 | 
			
		||||
 * Response message type value should be equal to
 | 
			
		||||
 * request message type value + 1.
 | 
			
		||||
 */
 | 
			
		||||
enum ksmbd_event {
 | 
			
		||||
	KSMBD_EVENT_UNSPEC			= 0,
 | 
			
		||||
	KSMBD_EVENT_HEARTBEAT_REQUEST,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_STARTING_UP,
 | 
			
		||||
	KSMBD_EVENT_SHUTTING_DOWN,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_LOGIN_REQUEST,
 | 
			
		||||
	KSMBD_EVENT_LOGIN_RESPONSE		= 5,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_SHARE_CONFIG_REQUEST,
 | 
			
		||||
	KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_TREE_CONNECT_REQUEST,
 | 
			
		||||
	KSMBD_EVENT_TREE_CONNECT_RESPONSE,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_TREE_DISCONNECT_REQUEST	= 10,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_LOGOUT_REQUEST,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_RPC_REQUEST,
 | 
			
		||||
	KSMBD_EVENT_RPC_RESPONSE,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
 | 
			
		||||
	KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE	= 15,
 | 
			
		||||
 | 
			
		||||
	KSMBD_EVENT_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum KSMBD_TREE_CONN_STATUS {
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_OK		= 0,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_NOMEM,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_NO_SHARE,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_NO_USER,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_INVALID_USER,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_HOST_DENIED	= 5,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_CONN_EXIST,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS,
 | 
			
		||||
	KSMBD_TREE_CONN_STATUS_ERROR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * User config flags.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_USER_FLAG_INVALID		(0)
 | 
			
		||||
#define KSMBD_USER_FLAG_OK		(1 << 0)
 | 
			
		||||
#define KSMBD_USER_FLAG_BAD_PASSWORD	(1 << 1)
 | 
			
		||||
#define KSMBD_USER_FLAG_BAD_UID		(1 << 2)
 | 
			
		||||
#define KSMBD_USER_FLAG_BAD_USER	(1 << 3)
 | 
			
		||||
#define KSMBD_USER_FLAG_GUEST_ACCOUNT	(1 << 4)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Share config flags.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_SHARE_FLAG_INVALID		(0)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_AVAILABLE		(1 << 0)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_BROWSEABLE		(1 << 1)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_WRITEABLE		(1 << 2)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_READONLY		(1 << 3)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_GUEST_OK		(1 << 4)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_GUEST_ONLY		(1 << 5)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS	(1 << 6)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_OPLOCKS		(1 << 7)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_PIPE			(1 << 8)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES		(1 << 9)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_INHERIT_SMACK		(1 << 10)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_INHERIT_OWNER		(1 << 11)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_STREAMS		(1 << 12)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS	(1 << 13)
 | 
			
		||||
#define KSMBD_SHARE_FLAG_ACL_XATTR		(1 << 14)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tree connect request flags.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1	(0)
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6	(1 << 0)
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2	(1 << 1)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tree connect flags.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT	(1 << 0)
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_READ_ONLY		(1 << 1)
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_WRITABLE		(1 << 2)
 | 
			
		||||
#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT	(1 << 3)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * RPC over IPC.
 | 
			
		||||
 */
 | 
			
		||||
#define KSMBD_RPC_METHOD_RETURN		(1 << 0)
 | 
			
		||||
#define KSMBD_RPC_SRVSVC_METHOD_INVOKE	(1 << 1)
 | 
			
		||||
#define KSMBD_RPC_SRVSVC_METHOD_RETURN	((1 << 1) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_WKSSVC_METHOD_INVOKE	(1 << 2)
 | 
			
		||||
#define KSMBD_RPC_WKSSVC_METHOD_RETURN	((1 << 2) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_IOCTL_METHOD		((1 << 3) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_OPEN_METHOD		(1 << 4)
 | 
			
		||||
#define KSMBD_RPC_WRITE_METHOD		(1 << 5)
 | 
			
		||||
#define KSMBD_RPC_READ_METHOD		((1 << 6) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_CLOSE_METHOD		(1 << 7)
 | 
			
		||||
#define KSMBD_RPC_RAP_METHOD		((1 << 8) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_RESTRICTED_CONTEXT	(1 << 9)
 | 
			
		||||
#define KSMBD_RPC_SAMR_METHOD_INVOKE	(1 << 10)
 | 
			
		||||
#define KSMBD_RPC_SAMR_METHOD_RETURN	((1 << 10) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
#define KSMBD_RPC_LSARPC_METHOD_INVOKE	(1 << 11)
 | 
			
		||||
#define KSMBD_RPC_LSARPC_METHOD_RETURN	((1 << 11) | KSMBD_RPC_METHOD_RETURN)
 | 
			
		||||
 | 
			
		||||
#define KSMBD_RPC_OK			0
 | 
			
		||||
#define KSMBD_RPC_EBAD_FUNC		0x00000001
 | 
			
		||||
#define KSMBD_RPC_EACCESS_DENIED	0x00000005
 | 
			
		||||
#define KSMBD_RPC_EBAD_FID		0x00000006
 | 
			
		||||
#define KSMBD_RPC_ENOMEM		0x00000008
 | 
			
		||||
#define KSMBD_RPC_EBAD_DATA		0x0000000D
 | 
			
		||||
#define KSMBD_RPC_ENOTIMPLEMENTED	0x00000040
 | 
			
		||||
#define KSMBD_RPC_EINVALID_PARAMETER	0x00000057
 | 
			
		||||
#define KSMBD_RPC_EMORE_DATA		0x000000EA
 | 
			
		||||
#define KSMBD_RPC_EINVALID_LEVEL	0x0000007C
 | 
			
		||||
#define KSMBD_RPC_SOME_NOT_MAPPED	0x00000107
 | 
			
		||||
 | 
			
		||||
#define KSMBD_CONFIG_OPT_DISABLED	0
 | 
			
		||||
#define KSMBD_CONFIG_OPT_ENABLED	1
 | 
			
		||||
#define KSMBD_CONFIG_OPT_AUTO		2
 | 
			
		||||
#define KSMBD_CONFIG_OPT_MANDATORY	3
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_KSMBD_SERVER_H */
 | 
			
		||||
							
								
								
									
										93
									
								
								fs/cifsd/ksmbd_work.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								fs/cifsd/ksmbd_work.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2019 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/list.h>
 | 
			
		||||
#include <linux/mm.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
 | 
			
		||||
#include "server.h"
 | 
			
		||||
#include "connection.h"
 | 
			
		||||
#include "ksmbd_work.h"
 | 
			
		||||
#include "buffer_pool.h"
 | 
			
		||||
#include "mgmt/ksmbd_ida.h"
 | 
			
		||||
 | 
			
		||||
/* @FIXME */
 | 
			
		||||
#include "ksmbd_server.h"
 | 
			
		||||
 | 
			
		||||
static struct kmem_cache *work_cache;
 | 
			
		||||
static struct workqueue_struct *ksmbd_wq;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_work *ksmbd_alloc_work_struct(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL);
 | 
			
		||||
 | 
			
		||||
	if (work) {
 | 
			
		||||
		work->compound_fid = KSMBD_NO_FID;
 | 
			
		||||
		work->compound_pfid = KSMBD_NO_FID;
 | 
			
		||||
		INIT_LIST_HEAD(&work->request_entry);
 | 
			
		||||
		INIT_LIST_HEAD(&work->async_request_entry);
 | 
			
		||||
		INIT_LIST_HEAD(&work->fp_entry);
 | 
			
		||||
		INIT_LIST_HEAD(&work->interim_entry);
 | 
			
		||||
	}
 | 
			
		||||
	return work;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_free_work_struct(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	WARN_ON(work->saved_cred != NULL);
 | 
			
		||||
	if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_TBUF &&
 | 
			
		||||
			work->set_trans_buf)
 | 
			
		||||
		ksmbd_release_buffer(RESPONSE_BUF(work));
 | 
			
		||||
	else
 | 
			
		||||
		ksmbd_free_response(RESPONSE_BUF(work));
 | 
			
		||||
 | 
			
		||||
	if (server_conf.flags & KSMBD_GLOBAL_FLAG_CACHE_RBUF &&
 | 
			
		||||
			work->set_read_buf)
 | 
			
		||||
		ksmbd_release_buffer(AUX_PAYLOAD(work));
 | 
			
		||||
	else
 | 
			
		||||
		ksmbd_free_response(AUX_PAYLOAD(work));
 | 
			
		||||
 | 
			
		||||
	ksmbd_free_response(TRANSFORM_BUF(work));
 | 
			
		||||
	ksmbd_free_request(REQUEST_BUF(work));
 | 
			
		||||
	if (work->async_id)
 | 
			
		||||
		ksmbd_release_id(work->conn->async_ida, work->async_id);
 | 
			
		||||
	kmem_cache_free(work_cache, work);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_work_pool_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	kmem_cache_destroy(work_cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_work_pool_init(void)
 | 
			
		||||
{
 | 
			
		||||
	work_cache = kmem_cache_create("ksmbd_work_cache",
 | 
			
		||||
					sizeof(struct ksmbd_work), 0,
 | 
			
		||||
					SLAB_HWCACHE_ALIGN, NULL);
 | 
			
		||||
	if (!work_cache)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_workqueue_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0);
 | 
			
		||||
	if (!ksmbd_wq)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_workqueue_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	flush_workqueue(ksmbd_wq);
 | 
			
		||||
	destroy_workqueue(ksmbd_wq);
 | 
			
		||||
	ksmbd_wq = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ksmbd_queue_work(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	return queue_work(ksmbd_wq, &work->work);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										124
									
								
								fs/cifsd/ksmbd_work.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								fs/cifsd/ksmbd_work.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2019 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_WORK_H__
 | 
			
		||||
#define __KSMBD_WORK_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/ctype.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
 | 
			
		||||
struct ksmbd_conn;
 | 
			
		||||
struct ksmbd_session;
 | 
			
		||||
struct ksmbd_tree_connect;
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	KSMBD_WORK_ACTIVE = 0,
 | 
			
		||||
	KSMBD_WORK_CANCELLED,
 | 
			
		||||
	KSMBD_WORK_CLOSED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* one of these for every pending CIFS request at the connection */
 | 
			
		||||
struct ksmbd_work {
 | 
			
		||||
	/* Server corresponding to this mid */
 | 
			
		||||
	struct ksmbd_conn               *conn;
 | 
			
		||||
	struct ksmbd_session            *sess;
 | 
			
		||||
	struct ksmbd_tree_connect       *tcon;
 | 
			
		||||
 | 
			
		||||
	/* Pointer to received SMB header */
 | 
			
		||||
	char                            *request_buf;
 | 
			
		||||
	/* Response buffer */
 | 
			
		||||
	char                            *response_buf;
 | 
			
		||||
 | 
			
		||||
	/* Read data buffer */
 | 
			
		||||
	char                            *aux_payload_buf;
 | 
			
		||||
 | 
			
		||||
	/* Next cmd hdr in compound req buf*/
 | 
			
		||||
	int                             next_smb2_rcv_hdr_off;
 | 
			
		||||
	/* Next cmd hdr in compound rsp buf*/
 | 
			
		||||
	int                             next_smb2_rsp_hdr_off;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Current Local FID assigned compound response if SMB2 CREATE
 | 
			
		||||
	 * command is present in compound request
 | 
			
		||||
	 */
 | 
			
		||||
	unsigned int                    compound_fid;
 | 
			
		||||
	unsigned int                    compound_pfid;
 | 
			
		||||
	unsigned int                    compound_sid;
 | 
			
		||||
 | 
			
		||||
	const struct cred		*saved_cred;
 | 
			
		||||
 | 
			
		||||
	/* Number of granted credits */
 | 
			
		||||
	unsigned int			credits_granted;
 | 
			
		||||
 | 
			
		||||
	/* response smb header size */
 | 
			
		||||
	unsigned int                    resp_hdr_sz;
 | 
			
		||||
	unsigned int                    response_sz;
 | 
			
		||||
	/* Read data count */
 | 
			
		||||
	unsigned int                    aux_payload_sz;
 | 
			
		||||
 | 
			
		||||
	void				*tr_buf;
 | 
			
		||||
 | 
			
		||||
	unsigned char			state;
 | 
			
		||||
	/* Multiple responses for one request e.g. SMB ECHO */
 | 
			
		||||
	bool                            multiRsp:1;
 | 
			
		||||
	/* No response for cancelled request */
 | 
			
		||||
	bool                            send_no_response:1;
 | 
			
		||||
	/* Request is encrypted */
 | 
			
		||||
	bool                            encrypted:1;
 | 
			
		||||
	/* Is this SYNC or ASYNC ksmbd_work */
 | 
			
		||||
	bool                            syncronous:1;
 | 
			
		||||
	bool                            need_invalidate_rkey:1;
 | 
			
		||||
	bool                            set_trans_buf:1;
 | 
			
		||||
	bool                            set_read_buf:1;
 | 
			
		||||
 | 
			
		||||
	unsigned int                    remote_key;
 | 
			
		||||
	/* cancel works */
 | 
			
		||||
	int                             async_id;
 | 
			
		||||
	void                            **cancel_argv;
 | 
			
		||||
	void                            (*cancel_fn)(void **argv);
 | 
			
		||||
 | 
			
		||||
	struct work_struct              work;
 | 
			
		||||
	/* List head at conn->requests */
 | 
			
		||||
	struct list_head                request_entry;
 | 
			
		||||
	/* List head at conn->async_requests */
 | 
			
		||||
	struct list_head                async_request_entry;
 | 
			
		||||
	struct list_head                fp_entry;
 | 
			
		||||
	struct list_head                interim_entry;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define WORK_CANCELLED(w)	((w)->state == KSMBD_WORK_CANCELLED)
 | 
			
		||||
#define WORK_CLOSED(w)		((w)->state == KSMBD_WORK_CLOSED)
 | 
			
		||||
#define WORK_ACTIVE(w)		((w)->state == KSMBD_WORK_ACTIVE)
 | 
			
		||||
 | 
			
		||||
#define RESPONSE_BUF(w)		((void *)(w)->response_buf)
 | 
			
		||||
#define REQUEST_BUF(w)		((void *)(w)->request_buf)
 | 
			
		||||
 | 
			
		||||
#define RESPONSE_BUF_NEXT(w)	\
 | 
			
		||||
	((void *)((w)->response_buf + (w)->next_smb2_rsp_hdr_off))
 | 
			
		||||
#define REQUEST_BUF_NEXT(w)	\
 | 
			
		||||
	((void *)((w)->request_buf + (w)->next_smb2_rcv_hdr_off))
 | 
			
		||||
 | 
			
		||||
#define RESPONSE_SZ(w)		((w)->response_sz)
 | 
			
		||||
 | 
			
		||||
#define INIT_AUX_PAYLOAD(w)	((w)->aux_payload_buf = NULL)
 | 
			
		||||
#define HAS_AUX_PAYLOAD(w)	((w)->aux_payload_sz != 0)
 | 
			
		||||
#define AUX_PAYLOAD(w)		((void *)((w)->aux_payload_buf))
 | 
			
		||||
#define AUX_PAYLOAD_SIZE(w)	((w)->aux_payload_sz)
 | 
			
		||||
#define RESP_HDR_SIZE(w)	((w)->resp_hdr_sz)
 | 
			
		||||
 | 
			
		||||
#define HAS_TRANSFORM_BUF(w)	((w)->tr_buf != NULL)
 | 
			
		||||
#define TRANSFORM_BUF(w)	((void *)((w)->tr_buf))
 | 
			
		||||
 | 
			
		||||
struct ksmbd_work *ksmbd_alloc_work_struct(void);
 | 
			
		||||
void ksmbd_free_work_struct(struct ksmbd_work *work);
 | 
			
		||||
 | 
			
		||||
void ksmbd_work_pool_destroy(void);
 | 
			
		||||
int ksmbd_work_pool_init(void);
 | 
			
		||||
 | 
			
		||||
int ksmbd_workqueue_init(void);
 | 
			
		||||
void ksmbd_workqueue_destroy(void);
 | 
			
		||||
bool ksmbd_queue_work(struct ksmbd_work *work);
 | 
			
		||||
 | 
			
		||||
#endif /* __KSMBD_WORK_H__ */
 | 
			
		||||
							
								
								
									
										635
									
								
								fs/cifsd/server.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										635
									
								
								fs/cifsd/server.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,635 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "glob.h"
 | 
			
		||||
#include "oplock.h"
 | 
			
		||||
#include "misc.h"
 | 
			
		||||
#include <linux/sched/signal.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
#include <linux/sysfs.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/moduleparam.h>
 | 
			
		||||
 | 
			
		||||
#include "server.h"
 | 
			
		||||
#include "smb_common.h"
 | 
			
		||||
#include "smbstatus.h"
 | 
			
		||||
#include "buffer_pool.h"
 | 
			
		||||
#include "connection.h"
 | 
			
		||||
#include "transport_ipc.h"
 | 
			
		||||
#include "mgmt/user_session.h"
 | 
			
		||||
#include "crypto_ctx.h"
 | 
			
		||||
#include "auth.h"
 | 
			
		||||
 | 
			
		||||
int ksmbd_debug_types;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_server_config server_conf;
 | 
			
		||||
 | 
			
		||||
enum SERVER_CTRL_TYPE {
 | 
			
		||||
	SERVER_CTRL_TYPE_INIT,
 | 
			
		||||
	SERVER_CTRL_TYPE_RESET,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct server_ctrl_struct {
 | 
			
		||||
	int			type;
 | 
			
		||||
	struct work_struct	ctrl_work;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static DEFINE_MUTEX(ctrl_lock);
 | 
			
		||||
 | 
			
		||||
static int ___server_conf_set(int idx, char *val)
 | 
			
		||||
{
 | 
			
		||||
	if (idx >= ARRAY_SIZE(server_conf.conf))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!val || val[0] == 0x00)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	kfree(server_conf.conf[idx]);
 | 
			
		||||
	server_conf.conf[idx] = kstrdup(val, GFP_KERNEL);
 | 
			
		||||
	if (!server_conf.conf[idx])
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_set_netbios_name(char *v)
 | 
			
		||||
{
 | 
			
		||||
	return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_set_server_string(char *v)
 | 
			
		||||
{
 | 
			
		||||
	return ___server_conf_set(SERVER_CONF_SERVER_STRING, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_set_work_group(char *v)
 | 
			
		||||
{
 | 
			
		||||
	return ___server_conf_set(SERVER_CONF_WORK_GROUP, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *ksmbd_netbios_name(void)
 | 
			
		||||
{
 | 
			
		||||
	return server_conf.conf[SERVER_CONF_NETBIOS_NAME];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *ksmbd_server_string(void)
 | 
			
		||||
{
 | 
			
		||||
	return server_conf.conf[SERVER_CONF_SERVER_STRING];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *ksmbd_work_group(void)
 | 
			
		||||
{
 | 
			
		||||
	return server_conf.conf[SERVER_CONF_WORK_GROUP];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * check_conn_state() - check state of server thread connection
 | 
			
		||||
 * @ksmbd_work:     smb work containing server thread information
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	0 on valid connection, otherwise 1 to reconnect
 | 
			
		||||
 */
 | 
			
		||||
static inline int check_conn_state(struct ksmbd_work *work)
 | 
			
		||||
{
 | 
			
		||||
	struct smb_hdr *rsp_hdr;
 | 
			
		||||
 | 
			
		||||
	if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) {
 | 
			
		||||
		rsp_hdr = RESPONSE_BUF(work);
 | 
			
		||||
		rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* @FIXME what a mess... god help. */
 | 
			
		||||
 | 
			
		||||
#define TCP_HANDLER_CONTINUE	0
 | 
			
		||||
#define TCP_HANDLER_ABORT	1
 | 
			
		||||
 | 
			
		||||
static int __process_request(struct ksmbd_work *work,
 | 
			
		||||
			     struct ksmbd_conn *conn,
 | 
			
		||||
			     uint16_t *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct smb_version_cmds *cmds;
 | 
			
		||||
	uint16_t command;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (check_conn_state(work))
 | 
			
		||||
		return TCP_HANDLER_CONTINUE;
 | 
			
		||||
 | 
			
		||||
	if (ksmbd_verify_smb_message(work))
 | 
			
		||||
		return TCP_HANDLER_ABORT;
 | 
			
		||||
 | 
			
		||||
	command = conn->ops->get_cmd_val(work);
 | 
			
		||||
	*cmd = command;
 | 
			
		||||
 | 
			
		||||
andx_again:
 | 
			
		||||
	if (command >= conn->max_cmds) {
 | 
			
		||||
		conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
 | 
			
		||||
		return TCP_HANDLER_CONTINUE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmds = &conn->cmds[command];
 | 
			
		||||
	if (!cmds->proc) {
 | 
			
		||||
		ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command);
 | 
			
		||||
		conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED);
 | 
			
		||||
		return TCP_HANDLER_CONTINUE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (work->sess && conn->ops->is_sign_req(work, command)) {
 | 
			
		||||
		ret = conn->ops->check_sign_req(work);
 | 
			
		||||
		if (!ret) {
 | 
			
		||||
			conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
 | 
			
		||||
			return TCP_HANDLER_CONTINUE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = cmds->proc(work);
 | 
			
		||||
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret);
 | 
			
		||||
	/* AndX commands - chained request can return positive values */
 | 
			
		||||
	else if (ret > 0) {
 | 
			
		||||
		command = ret;
 | 
			
		||||
		*cmd = command;
 | 
			
		||||
		goto andx_again;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (work->send_no_response)
 | 
			
		||||
		return TCP_HANDLER_ABORT;
 | 
			
		||||
	return TCP_HANDLER_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __handle_ksmbd_work(struct ksmbd_work *work,
 | 
			
		||||
				struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t command = 0;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (conn->ops->allocate_rsp_buf(work))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (conn->ops->is_transform_hdr &&
 | 
			
		||||
		conn->ops->is_transform_hdr(REQUEST_BUF(work))) {
 | 
			
		||||
		rc = conn->ops->decrypt_req(work);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			conn->ops->set_rsp_status(work, STATUS_DATA_ERROR);
 | 
			
		||||
			goto send;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		work->encrypted = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = conn->ops->init_rsp_hdr(work);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		/* either uid or tid is not correct */
 | 
			
		||||
		conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE);
 | 
			
		||||
		goto send;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (conn->ops->check_user_session) {
 | 
			
		||||
		rc = conn->ops->check_user_session(work);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			command = conn->ops->get_cmd_val(work);
 | 
			
		||||
			conn->ops->set_rsp_status(work,
 | 
			
		||||
					STATUS_USER_SESSION_DELETED);
 | 
			
		||||
			goto send;
 | 
			
		||||
		} else if (rc > 0) {
 | 
			
		||||
			rc = conn->ops->get_ksmbd_tcon(work);
 | 
			
		||||
			if (rc < 0) {
 | 
			
		||||
				conn->ops->set_rsp_status(work,
 | 
			
		||||
					STATUS_NETWORK_NAME_DELETED);
 | 
			
		||||
				goto send;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		rc = __process_request(work, conn, &command);
 | 
			
		||||
		if (rc == TCP_HANDLER_ABORT)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Call smb2_set_rsp_credits() function to set number of credits
 | 
			
		||||
		 * granted in hdr of smb2 response.
 | 
			
		||||
		 */
 | 
			
		||||
		if (conn->ops->set_rsp_credits) {
 | 
			
		||||
			spin_lock(&conn->credits_lock);
 | 
			
		||||
			rc = conn->ops->set_rsp_credits(work);
 | 
			
		||||
			spin_unlock(&conn->credits_lock);
 | 
			
		||||
			if (rc < 0) {
 | 
			
		||||
				conn->ops->set_rsp_status(work,
 | 
			
		||||
					STATUS_INVALID_PARAMETER);
 | 
			
		||||
				goto send;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (work->sess && (work->sess->sign ||
 | 
			
		||||
		     smb3_11_final_sess_setup_resp(work) ||
 | 
			
		||||
		     conn->ops->is_sign_req(work, command)))
 | 
			
		||||
			conn->ops->set_sign_rsp(work);
 | 
			
		||||
	} while (is_chained_smb2_message(work));
 | 
			
		||||
 | 
			
		||||
	if (work->send_no_response)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
send:
 | 
			
		||||
	smb3_preauth_hash_rsp(work);
 | 
			
		||||
	if (work->sess && work->sess->enc && work->encrypted &&
 | 
			
		||||
		conn->ops->encrypt_resp) {
 | 
			
		||||
		rc = conn->ops->encrypt_resp(work);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			conn->ops->set_rsp_status(work, STATUS_DATA_ERROR);
 | 
			
		||||
			goto send;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_write(work);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * handle_ksmbd_work() - process pending smb work requests
 | 
			
		||||
 * @ksmbd_work:	smb work containing request command buffer
 | 
			
		||||
 *
 | 
			
		||||
 * called by kworker threads to processing remaining smb work requests
 | 
			
		||||
 */
 | 
			
		||||
static void handle_ksmbd_work(struct work_struct *wk)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
 | 
			
		||||
	struct ksmbd_conn *conn = work->conn;
 | 
			
		||||
 | 
			
		||||
	atomic64_inc(&conn->stats.request_served);
 | 
			
		||||
 | 
			
		||||
	__handle_ksmbd_work(work, conn);
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_try_dequeue_request(work);
 | 
			
		||||
	ksmbd_free_work_struct(work);
 | 
			
		||||
	atomic_dec(&conn->r_count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * queue_ksmbd_work() - queue a smb request to worker thread queue
 | 
			
		||||
 *		for proccessing smb command and sending response
 | 
			
		||||
 * @conn:	connection instance
 | 
			
		||||
 *
 | 
			
		||||
 * read remaining data from socket create and submit work.
 | 
			
		||||
 */
 | 
			
		||||
static int queue_ksmbd_work(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_work *work;
 | 
			
		||||
 | 
			
		||||
	work = ksmbd_alloc_work_struct();
 | 
			
		||||
	if (!work) {
 | 
			
		||||
		ksmbd_err("allocation for work failed\n");
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	work->conn = conn;
 | 
			
		||||
	work->request_buf = conn->request_buf;
 | 
			
		||||
	conn->request_buf = NULL;
 | 
			
		||||
 | 
			
		||||
	if (ksmbd_init_smb_server(work)) {
 | 
			
		||||
		ksmbd_free_work_struct(work);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_enqueue_request(work);
 | 
			
		||||
	atomic_inc(&conn->r_count);
 | 
			
		||||
	/* update activity on connection */
 | 
			
		||||
	conn->last_active = jiffies;
 | 
			
		||||
	INIT_WORK(&work->work, handle_ksmbd_work);
 | 
			
		||||
	ksmbd_queue_work(work);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ksmbd_server_process_request(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	return queue_ksmbd_work(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_sessions_deregister(conn);
 | 
			
		||||
	destroy_lease_table(conn);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ksmbd_server_tcp_callbacks_init(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_conn_ops ops;
 | 
			
		||||
 | 
			
		||||
	ops.process_fn = ksmbd_server_process_request;
 | 
			
		||||
	ops.terminate_fn = ksmbd_server_terminate_conn;
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_init_server_callbacks(&ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void server_conf_free(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) {
 | 
			
		||||
		kfree(server_conf.conf[i]);
 | 
			
		||||
		server_conf.conf[i] = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int server_conf_init(void)
 | 
			
		||||
{
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP);
 | 
			
		||||
	server_conf.enforced_signing = 0;
 | 
			
		||||
	server_conf.min_protocol = ksmbd_min_protocol();
 | 
			
		||||
	server_conf.max_protocol = ksmbd_max_protocol();
 | 
			
		||||
	server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP;
 | 
			
		||||
#ifdef CONFIG_SMB_SERVER_KERBEROS5
 | 
			
		||||
	server_conf.auth_mechs |= KSMBD_AUTH_KRB5 |
 | 
			
		||||
				KSMBD_AUTH_MSKRB5;
 | 
			
		||||
#endif
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_conn_transport_init();
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		server_queue_ctrl_reset_work();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_ipc_soft_reset();
 | 
			
		||||
	ksmbd_conn_transport_destroy();
 | 
			
		||||
	server_conf_free();
 | 
			
		||||
	server_conf_init();
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void server_ctrl_handle_work(struct work_struct *work)
 | 
			
		||||
{
 | 
			
		||||
	struct server_ctrl_struct *ctrl;
 | 
			
		||||
 | 
			
		||||
	ctrl = container_of(work, struct server_ctrl_struct, ctrl_work);
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&ctrl_lock);
 | 
			
		||||
	switch (ctrl->type) {
 | 
			
		||||
	case SERVER_CTRL_TYPE_INIT:
 | 
			
		||||
		server_ctrl_handle_init(ctrl);
 | 
			
		||||
		break;
 | 
			
		||||
	case SERVER_CTRL_TYPE_RESET:
 | 
			
		||||
		server_ctrl_handle_reset(ctrl);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		pr_err("Unknown server work type: %d\n", ctrl->type);
 | 
			
		||||
	}
 | 
			
		||||
	mutex_unlock(&ctrl_lock);
 | 
			
		||||
	kfree(ctrl);
 | 
			
		||||
	module_put(THIS_MODULE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __queue_ctrl_work(int type)
 | 
			
		||||
{
 | 
			
		||||
	struct server_ctrl_struct *ctrl;
 | 
			
		||||
 | 
			
		||||
	ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL);
 | 
			
		||||
	if (!ctrl)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	__module_get(THIS_MODULE);
 | 
			
		||||
	ctrl->type = type;
 | 
			
		||||
	INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work);
 | 
			
		||||
	queue_work(system_long_wq, &ctrl->ctrl_work);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int server_queue_ctrl_init_work(void)
 | 
			
		||||
{
 | 
			
		||||
	return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int server_queue_ctrl_reset_work(void)
 | 
			
		||||
{
 | 
			
		||||
	return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t stats_show(struct class *class,
 | 
			
		||||
			  struct class_attribute *attr,
 | 
			
		||||
			  char *buf)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Inc this each time you change stats output format,
 | 
			
		||||
	 * so user space will know what to do.
 | 
			
		||||
	 */
 | 
			
		||||
	static int stats_version = 2;
 | 
			
		||||
	static const char * const state[] = {
 | 
			
		||||
		"startup",
 | 
			
		||||
		"running",
 | 
			
		||||
		"reset",
 | 
			
		||||
		"shutdown"
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	ssize_t sz = scnprintf(buf,
 | 
			
		||||
				PAGE_SIZE,
 | 
			
		||||
				"%d %s %d %lu\n",
 | 
			
		||||
				stats_version,
 | 
			
		||||
				state[server_conf.state],
 | 
			
		||||
				server_conf.tcp_port,
 | 
			
		||||
				server_conf.ipc_last_active / HZ);
 | 
			
		||||
	return sz;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t kill_server_store(struct class *class,
 | 
			
		||||
				 struct class_attribute *attr,
 | 
			
		||||
				 const char *buf,
 | 
			
		||||
				 size_t len)
 | 
			
		||||
{
 | 
			
		||||
	if (!sysfs_streq(buf, "hard"))
 | 
			
		||||
		return len;
 | 
			
		||||
 | 
			
		||||
	ksmbd_info("kill command received\n");
 | 
			
		||||
	mutex_lock(&ctrl_lock);
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);
 | 
			
		||||
	__module_get(THIS_MODULE);
 | 
			
		||||
	server_ctrl_handle_reset(NULL);
 | 
			
		||||
	module_put(THIS_MODULE);
 | 
			
		||||
	mutex_unlock(&ctrl_lock);
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char * const debug_type_strings[] = {"smb", "auth", "vfs",
 | 
			
		||||
						"oplock", "ipc", "conn",
 | 
			
		||||
						"rdma"};
 | 
			
		||||
 | 
			
		||||
static ssize_t debug_show(struct class *class,
 | 
			
		||||
		struct class_attribute *attr,
 | 
			
		||||
		char *buf)
 | 
			
		||||
{
 | 
			
		||||
	ssize_t sz = 0;
 | 
			
		||||
	int i, pos = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {
 | 
			
		||||
		if ((ksmbd_debug_types >> i) & 1) {
 | 
			
		||||
			pos = scnprintf(buf + sz,
 | 
			
		||||
					PAGE_SIZE - sz,
 | 
			
		||||
					"[%s] ",
 | 
			
		||||
					debug_type_strings[i]);
 | 
			
		||||
		} else {
 | 
			
		||||
			pos = scnprintf(buf + sz,
 | 
			
		||||
					PAGE_SIZE - sz,
 | 
			
		||||
					"%s ",
 | 
			
		||||
					debug_type_strings[i]);
 | 
			
		||||
		}
 | 
			
		||||
		sz += pos;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
 | 
			
		||||
	return sz;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t debug_store(struct class *class,
 | 
			
		||||
		struct class_attribute *attr,
 | 
			
		||||
		const char *buf,
 | 
			
		||||
		size_t len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {
 | 
			
		||||
		if (sysfs_streq(buf, "all")) {
 | 
			
		||||
			if (ksmbd_debug_types == KSMBD_DEBUG_ALL)
 | 
			
		||||
				ksmbd_debug_types = 0;
 | 
			
		||||
			else
 | 
			
		||||
				ksmbd_debug_types = KSMBD_DEBUG_ALL;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (sysfs_streq(buf, debug_type_strings[i])) {
 | 
			
		||||
			if (ksmbd_debug_types & (1 << i))
 | 
			
		||||
				ksmbd_debug_types &= ~(1 << i);
 | 
			
		||||
			else
 | 
			
		||||
				ksmbd_debug_types |= (1 << i);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CLASS_ATTR_RO(stats);
 | 
			
		||||
static CLASS_ATTR_WO(kill_server);
 | 
			
		||||
static CLASS_ATTR_RW(debug);
 | 
			
		||||
 | 
			
		||||
static struct attribute *ksmbd_control_class_attrs[] = {
 | 
			
		||||
	&class_attr_stats.attr,
 | 
			
		||||
	&class_attr_kill_server.attr,
 | 
			
		||||
	&class_attr_debug.attr,
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
ATTRIBUTE_GROUPS(ksmbd_control_class);
 | 
			
		||||
 | 
			
		||||
static struct class ksmbd_control_class = {
 | 
			
		||||
	.name		= "ksmbd-control",
 | 
			
		||||
	.owner		= THIS_MODULE,
 | 
			
		||||
	.class_groups	= ksmbd_control_class_groups,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int ksmbd_server_shutdown(void)
 | 
			
		||||
{
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN);
 | 
			
		||||
 | 
			
		||||
	class_unregister(&ksmbd_control_class);
 | 
			
		||||
	ksmbd_workqueue_destroy();
 | 
			
		||||
	ksmbd_ipc_release();
 | 
			
		||||
	ksmbd_conn_transport_destroy();
 | 
			
		||||
	ksmbd_free_session_table();
 | 
			
		||||
	ksmbd_crypto_destroy();
 | 
			
		||||
	ksmbd_free_global_file_table();
 | 
			
		||||
	destroy_lease_table(NULL);
 | 
			
		||||
	ksmbd_destroy_buffer_pools();
 | 
			
		||||
	server_conf_free();
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __init ksmbd_server_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = class_register(&ksmbd_control_class);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Unable to register ksmbd-control class\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_server_tcp_callbacks_init();
 | 
			
		||||
 | 
			
		||||
	ret = server_conf_init();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_init_buffer_pools();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_init_session_table();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_ipc_init();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_init_global_file_table();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_inode_hash_init();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_crypto_create();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_workqueue_init();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ksmbd_server_shutdown();
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * exit_smb_server() - shutdown forker thread and free memory at module exit
 | 
			
		||||
 */
 | 
			
		||||
static void __exit ksmbd_server_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_server_shutdown();
 | 
			
		||||
	ksmbd_release_inode_hash();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
 | 
			
		||||
MODULE_VERSION(KSMBD_VERSION);
 | 
			
		||||
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_SOFTDEP("pre: arc4");
 | 
			
		||||
MODULE_SOFTDEP("pre: ecb");
 | 
			
		||||
MODULE_SOFTDEP("pre: hmac");
 | 
			
		||||
MODULE_SOFTDEP("pre: md4");
 | 
			
		||||
MODULE_SOFTDEP("pre: md5");
 | 
			
		||||
MODULE_SOFTDEP("pre: nls");
 | 
			
		||||
MODULE_SOFTDEP("pre: aes");
 | 
			
		||||
MODULE_SOFTDEP("pre: cmac");
 | 
			
		||||
MODULE_SOFTDEP("pre: sha256");
 | 
			
		||||
MODULE_SOFTDEP("pre: sha512");
 | 
			
		||||
MODULE_SOFTDEP("pre: aead2");
 | 
			
		||||
MODULE_SOFTDEP("pre: ccm");
 | 
			
		||||
MODULE_SOFTDEP("pre: gcm");
 | 
			
		||||
module_init(ksmbd_server_init)
 | 
			
		||||
module_exit(ksmbd_server_exit)
 | 
			
		||||
							
								
								
									
										62
									
								
								fs/cifsd/server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								fs/cifsd/server.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __SERVER_H__
 | 
			
		||||
#define __SERVER_H__
 | 
			
		||||
 | 
			
		||||
#include "smbacl.h"
 | 
			
		||||
 | 
			
		||||
#define SERVER_STATE_STARTING_UP	0
 | 
			
		||||
#define SERVER_STATE_RUNNING		1
 | 
			
		||||
#define SERVER_STATE_RESETTING		2
 | 
			
		||||
#define SERVER_STATE_SHUTTING_DOWN	3
 | 
			
		||||
 | 
			
		||||
#define SERVER_CONF_NETBIOS_NAME	0
 | 
			
		||||
#define SERVER_CONF_SERVER_STRING	1
 | 
			
		||||
#define SERVER_CONF_WORK_GROUP		2
 | 
			
		||||
 | 
			
		||||
extern int ksmbd_debugging;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_server_config {
 | 
			
		||||
	unsigned int		flags;
 | 
			
		||||
	unsigned int		state;
 | 
			
		||||
	short			signing;
 | 
			
		||||
	short			enforced_signing;
 | 
			
		||||
	short			min_protocol;
 | 
			
		||||
	short			max_protocol;
 | 
			
		||||
	unsigned short		tcp_port;
 | 
			
		||||
	unsigned short		ipc_timeout;
 | 
			
		||||
	unsigned long		ipc_last_active;
 | 
			
		||||
	unsigned long		deadtime;
 | 
			
		||||
	unsigned int		share_fake_fscaps;
 | 
			
		||||
	struct smb_sid		domain_sid;
 | 
			
		||||
	unsigned int		auth_mechs;
 | 
			
		||||
 | 
			
		||||
	char			*conf[SERVER_CONF_WORK_GROUP + 1];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct ksmbd_server_config server_conf;
 | 
			
		||||
 | 
			
		||||
int ksmbd_set_netbios_name(char *v);
 | 
			
		||||
int ksmbd_set_server_string(char *v);
 | 
			
		||||
int ksmbd_set_work_group(char *v);
 | 
			
		||||
 | 
			
		||||
char *ksmbd_netbios_name(void);
 | 
			
		||||
char *ksmbd_server_string(void);
 | 
			
		||||
char *ksmbd_work_group(void);
 | 
			
		||||
 | 
			
		||||
static inline int ksmbd_server_running(void)
 | 
			
		||||
{
 | 
			
		||||
	return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int ksmbd_server_configurable(void)
 | 
			
		||||
{
 | 
			
		||||
	return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int server_queue_ctrl_init_work(void);
 | 
			
		||||
int server_queue_ctrl_reset_work(void);
 | 
			
		||||
#endif /* __SERVER_H__ */
 | 
			
		||||
							
								
								
									
										900
									
								
								fs/cifsd/transport_ipc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										900
									
								
								fs/cifsd/transport_ipc.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,900 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/jhash.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/rwsem.h>
 | 
			
		||||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/wait.h>
 | 
			
		||||
#include <linux/hashtable.h>
 | 
			
		||||
#include <net/net_namespace.h>
 | 
			
		||||
#include <net/genetlink.h>
 | 
			
		||||
#include <linux/socket.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
 | 
			
		||||
#include "vfs_cache.h"
 | 
			
		||||
#include "transport_ipc.h"
 | 
			
		||||
#include "buffer_pool.h"
 | 
			
		||||
#include "server.h"
 | 
			
		||||
#include "smb_common.h"
 | 
			
		||||
 | 
			
		||||
#include "mgmt/user_config.h"
 | 
			
		||||
#include "mgmt/share_config.h"
 | 
			
		||||
#include "mgmt/user_session.h"
 | 
			
		||||
#include "mgmt/tree_connect.h"
 | 
			
		||||
#include "mgmt/ksmbd_ida.h"
 | 
			
		||||
#include "connection.h"
 | 
			
		||||
#include "transport_tcp.h"
 | 
			
		||||
 | 
			
		||||
/* @FIXME fix this code */
 | 
			
		||||
extern int get_protocol_idx(char *str);
 | 
			
		||||
 | 
			
		||||
#define IPC_WAIT_TIMEOUT	(2 * HZ)
 | 
			
		||||
 | 
			
		||||
#define IPC_MSG_HASH_BITS	3
 | 
			
		||||
static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS);
 | 
			
		||||
static DECLARE_RWSEM(ipc_msg_table_lock);
 | 
			
		||||
static DEFINE_MUTEX(startup_lock);
 | 
			
		||||
 | 
			
		||||
static struct ksmbd_ida *ida;
 | 
			
		||||
 | 
			
		||||
static unsigned int ksmbd_tools_pid;
 | 
			
		||||
 | 
			
		||||
#define KSMBD_IPC_MSG_HANDLE(m)	(*(unsigned int *)m)
 | 
			
		||||
 | 
			
		||||
static bool ksmbd_ipc_validate_version(struct genl_info *m)
 | 
			
		||||
{
 | 
			
		||||
	if (m->genlhdr->version != KSMBD_GENL_VERSION) {
 | 
			
		||||
		ksmbd_err("%s. ksmbd: %d, kernel module: %d. %s.\n",
 | 
			
		||||
			  "Daemon and kernel module version mismatch",
 | 
			
		||||
			  m->genlhdr->version,
 | 
			
		||||
			  KSMBD_GENL_VERSION,
 | 
			
		||||
			  "User-space ksmbd should terminate");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_ipc_msg {
 | 
			
		||||
	unsigned int		type;
 | 
			
		||||
	unsigned int		sz;
 | 
			
		||||
	unsigned char		____payload[0];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define KSMBD_IPC_MSG_PAYLOAD(m)					\
 | 
			
		||||
	((void *)(((struct ksmbd_ipc_msg *)(m))->____payload))
 | 
			
		||||
 | 
			
		||||
struct ipc_msg_table_entry {
 | 
			
		||||
	unsigned int		handle;
 | 
			
		||||
	unsigned int		type;
 | 
			
		||||
	wait_queue_head_t	wait;
 | 
			
		||||
	struct hlist_node	ipc_table_hlist;
 | 
			
		||||
 | 
			
		||||
	void			*response;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct delayed_work ipc_timer_work;
 | 
			
		||||
 | 
			
		||||
static int handle_startup_event(struct sk_buff *skb, struct genl_info *info);
 | 
			
		||||
static int handle_unsupported_event(struct sk_buff *skb,
 | 
			
		||||
				    struct genl_info *info);
 | 
			
		||||
static int handle_generic_event(struct sk_buff *skb, struct genl_info *info);
 | 
			
		||||
static int ksmbd_ipc_heartbeat_request(void);
 | 
			
		||||
 | 
			
		||||
static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = {
 | 
			
		||||
	[KSMBD_EVENT_UNSPEC] = {
 | 
			
		||||
		.len = 0,
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_HEARTBEAT_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_heartbeat),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_STARTING_UP] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_startup_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_SHUTTING_DOWN] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_shutdown_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_LOGIN_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_login_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_LOGIN_RESPONSE] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_login_response),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_SHARE_CONFIG_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_share_config_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_share_config_response),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_TREE_CONNECT_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_tree_connect_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_TREE_CONNECT_RESPONSE] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_tree_connect_response),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_tree_disconnect_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_LOGOUT_REQUEST] = {
 | 
			
		||||
		.len = sizeof(struct ksmbd_logout_request),
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_RPC_REQUEST] = {
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_RPC_RESPONSE] = {
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = {
 | 
			
		||||
	},
 | 
			
		||||
	[KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = {
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct genl_ops ksmbd_genl_ops[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_UNSPEC,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_HEARTBEAT_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_STARTING_UP,
 | 
			
		||||
		.doit	= handle_startup_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_SHUTTING_DOWN,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_LOGIN_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_LOGIN_RESPONSE,
 | 
			
		||||
		.doit	= handle_generic_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_SHARE_CONFIG_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
 | 
			
		||||
		.doit	= handle_generic_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_TREE_CONNECT_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_TREE_CONNECT_RESPONSE,
 | 
			
		||||
		.doit	= handle_generic_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_TREE_DISCONNECT_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_LOGOUT_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_RPC_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_RPC_RESPONSE,
 | 
			
		||||
		.doit	= handle_generic_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
 | 
			
		||||
		.doit	= handle_unsupported_event,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE,
 | 
			
		||||
		.doit	= handle_generic_event,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct genl_family ksmbd_genl_family = {
 | 
			
		||||
	.name		= KSMBD_GENL_NAME,
 | 
			
		||||
	.version	= KSMBD_GENL_VERSION,
 | 
			
		||||
	.hdrsize	= 0,
 | 
			
		||||
	.maxattr	= KSMBD_EVENT_MAX,
 | 
			
		||||
	.netnsok	= true,
 | 
			
		||||
	.module		= THIS_MODULE,
 | 
			
		||||
	.ops		= ksmbd_genl_ops,
 | 
			
		||||
	.n_ops		= ARRAY_SIZE(ksmbd_genl_ops),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ksmbd_nl_init_fixup(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++)
 | 
			
		||||
		ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT |
 | 
			
		||||
						GENL_DONT_VALIDATE_DUMP;
 | 
			
		||||
 | 
			
		||||
	ksmbd_genl_family.policy = ksmbd_nl_policy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rpc_context_flags(struct ksmbd_session *sess)
 | 
			
		||||
{
 | 
			
		||||
	if (user_guest(sess->user))
 | 
			
		||||
		return KSMBD_RPC_RESTRICTED_CONTEXT;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipc_update_last_active(void)
 | 
			
		||||
{
 | 
			
		||||
	if (server_conf.ipc_timeout)
 | 
			
		||||
		server_conf.ipc_last_active = jiffies;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
 | 
			
		||||
 | 
			
		||||
	msg = ksmbd_alloc(msg_sz);
 | 
			
		||||
	if (msg)
 | 
			
		||||
		msg->sz = sz;
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipc_msg_free(struct ksmbd_ipc_msg *msg)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipc_msg_handle_free(int handle)
 | 
			
		||||
{
 | 
			
		||||
	if (handle >= 0)
 | 
			
		||||
		ksmbd_release_id(ida, handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_response(int type, void *payload, size_t sz)
 | 
			
		||||
{
 | 
			
		||||
	int handle = KSMBD_IPC_MSG_HANDLE(payload);
 | 
			
		||||
	struct ipc_msg_table_entry *entry;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ipc_update_last_active();
 | 
			
		||||
	down_read(&ipc_msg_table_lock);
 | 
			
		||||
	hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) {
 | 
			
		||||
		if (handle != entry->handle)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		entry->response = NULL;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Response message type value should be equal to
 | 
			
		||||
		 * request message type + 1.
 | 
			
		||||
		 */
 | 
			
		||||
		if (entry->type + 1 != type) {
 | 
			
		||||
			ksmbd_err("Waiting for IPC type %d, got %d. Ignore.\n",
 | 
			
		||||
				entry->type + 1, type);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		entry->response = ksmbd_alloc(sz);
 | 
			
		||||
		if (!entry->response) {
 | 
			
		||||
			ret = -ENOMEM;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memcpy(entry->response, payload, sz);
 | 
			
		||||
		wake_up_interruptible(&entry->wait);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	up_read(&ipc_msg_table_lock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ksmbd_set_fd_limit(req->file_max);
 | 
			
		||||
	server_conf.flags = req->flags;
 | 
			
		||||
	server_conf.signing = req->signing;
 | 
			
		||||
	server_conf.tcp_port = req->tcp_port;
 | 
			
		||||
	server_conf.ipc_timeout = req->ipc_timeout * HZ;
 | 
			
		||||
	server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL;
 | 
			
		||||
	server_conf.share_fake_fscaps = req->share_fake_fscaps;
 | 
			
		||||
	ksmbd_init_domain(req->sub_auth);
 | 
			
		||||
 | 
			
		||||
	if (req->smb2_max_read)
 | 
			
		||||
		init_smb2_max_read_size(req->smb2_max_read);
 | 
			
		||||
	if (req->smb2_max_write)
 | 
			
		||||
		init_smb2_max_write_size(req->smb2_max_write);
 | 
			
		||||
	if (req->smb2_max_trans)
 | 
			
		||||
		init_smb2_max_trans_size(req->smb2_max_trans);
 | 
			
		||||
 | 
			
		||||
	ret = ksmbd_set_netbios_name(req->netbios_name);
 | 
			
		||||
	ret |= ksmbd_set_server_string(req->server_string);
 | 
			
		||||
	ret |= ksmbd_set_work_group(req->work_group);
 | 
			
		||||
	ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
 | 
			
		||||
					req->ifc_list_sz);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Server configuration error: %s %s %s\n",
 | 
			
		||||
				req->netbios_name,
 | 
			
		||||
				req->server_string,
 | 
			
		||||
				req->work_group);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (req->min_prot[0]) {
 | 
			
		||||
		ret = ksmbd_lookup_protocol_idx(req->min_prot);
 | 
			
		||||
		if (ret >= 0)
 | 
			
		||||
			server_conf.min_protocol = ret;
 | 
			
		||||
	}
 | 
			
		||||
	if (req->max_prot[0]) {
 | 
			
		||||
		ret = ksmbd_lookup_protocol_idx(req->max_prot);
 | 
			
		||||
		if (ret >= 0)
 | 
			
		||||
			server_conf.max_protocol = ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (server_conf.ipc_timeout)
 | 
			
		||||
		schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_startup_event(struct sk_buff *skb, struct genl_info *info)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
 | 
			
		||||
	if (!netlink_capable(skb, CAP_NET_ADMIN))
 | 
			
		||||
		return -EPERM;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (!ksmbd_ipc_validate_version(info))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!info->attrs[KSMBD_EVENT_STARTING_UP])
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&startup_lock);
 | 
			
		||||
	if (!ksmbd_server_configurable()) {
 | 
			
		||||
		mutex_unlock(&startup_lock);
 | 
			
		||||
		ksmbd_err("Server reset is in progress, can't start daemon\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ksmbd_tools_pid) {
 | 
			
		||||
		if (ksmbd_ipc_heartbeat_request() == 0) {
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ksmbd_err("Reconnect to a new user space daemon\n");
 | 
			
		||||
	} else {
 | 
			
		||||
		struct ksmbd_startup_request *req;
 | 
			
		||||
 | 
			
		||||
		req = nla_data(info->attrs[info->genlhdr->cmd]);
 | 
			
		||||
		ret = ipc_server_config_on_startup(req);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto out;
 | 
			
		||||
		server_queue_ctrl_init_work();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_tools_pid = info->snd_portid;
 | 
			
		||||
	ipc_update_last_active();
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	mutex_unlock(&startup_lock);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_unsupported_event(struct sk_buff *skb,
 | 
			
		||||
				    struct genl_info *info)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_generic_event(struct sk_buff *skb, struct genl_info *info)
 | 
			
		||||
{
 | 
			
		||||
	void *payload;
 | 
			
		||||
	int sz;
 | 
			
		||||
	int type = info->genlhdr->cmd;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
 | 
			
		||||
	if (!netlink_capable(skb, CAP_NET_ADMIN))
 | 
			
		||||
		return -EPERM;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (type >= KSMBD_EVENT_MAX) {
 | 
			
		||||
		WARN_ON(1);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!ksmbd_ipc_validate_version(info))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!info->attrs[type])
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	payload = nla_data(info->attrs[info->genlhdr->cmd]);
 | 
			
		||||
	sz = nla_len(info->attrs[info->genlhdr->cmd]);
 | 
			
		||||
	return handle_response(type, payload, sz);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ipc_msg_send(struct ksmbd_ipc_msg *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct genlmsghdr *nlh;
 | 
			
		||||
	struct sk_buff *skb;
 | 
			
		||||
	int ret = -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!ksmbd_tools_pid)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	skb = genlmsg_new(msg->sz, GFP_KERNEL);
 | 
			
		||||
	if (!skb)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type);
 | 
			
		||||
	if (!nlh)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	ret = nla_put(skb, msg->type, msg->sz, KSMBD_IPC_MSG_PAYLOAD(msg));
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		genlmsg_cancel(skb, nlh);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	genlmsg_end(skb, nlh);
 | 
			
		||||
	ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid);
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		ipc_update_last_active();
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	nlmsg_free(skb);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg,
 | 
			
		||||
				  unsigned int handle)
 | 
			
		||||
{
 | 
			
		||||
	struct ipc_msg_table_entry entry;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if ((int)handle < 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	entry.type = msg->type;
 | 
			
		||||
	entry.response = NULL;
 | 
			
		||||
	init_waitqueue_head(&entry.wait);
 | 
			
		||||
 | 
			
		||||
	down_write(&ipc_msg_table_lock);
 | 
			
		||||
	entry.handle = handle;
 | 
			
		||||
	hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle);
 | 
			
		||||
	up_write(&ipc_msg_table_lock);
 | 
			
		||||
 | 
			
		||||
	ret = ipc_msg_send(msg);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	ret = wait_event_interruptible_timeout(entry.wait,
 | 
			
		||||
					       entry.response != NULL,
 | 
			
		||||
					       IPC_WAIT_TIMEOUT);
 | 
			
		||||
out:
 | 
			
		||||
	down_write(&ipc_msg_table_lock);
 | 
			
		||||
	hash_del(&entry.ipc_table_hlist);
 | 
			
		||||
	up_write(&ipc_msg_table_lock);
 | 
			
		||||
	return entry.response;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ksmbd_ipc_heartbeat_request(void)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST;
 | 
			
		||||
	ret = ipc_msg_send(msg);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_login_request *req;
 | 
			
		||||
	struct ksmbd_login_response *resp;
 | 
			
		||||
 | 
			
		||||
	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_LOGIN_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = ksmbd_acquire_id(ida);
 | 
			
		||||
	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_handle_free(req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_spnego_authen_response *
 | 
			
		||||
ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_spnego_authen_request *req;
 | 
			
		||||
	struct ksmbd_spnego_authen_response *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) +
 | 
			
		||||
			blob_len + 1);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = ksmbd_acquire_id(ida);
 | 
			
		||||
	req->spnego_blob_len = blob_len;
 | 
			
		||||
	memcpy(req->spnego_blob, spnego_blob, blob_len);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_handle_free(req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_tree_connect_response *
 | 
			
		||||
ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
 | 
			
		||||
			       struct ksmbd_share_config *share,
 | 
			
		||||
			       struct ksmbd_tree_connect *tree_conn,
 | 
			
		||||
			       struct sockaddr *peer_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_tree_connect_request *req;
 | 
			
		||||
	struct ksmbd_tree_connect_response *resp;
 | 
			
		||||
 | 
			
		||||
	if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
 | 
			
		||||
	req->handle = ksmbd_acquire_id(ida);
 | 
			
		||||
	req->account_flags = sess->user->flags;
 | 
			
		||||
	req->session_id = sess->id;
 | 
			
		||||
	req->connect_id = tree_conn->id;
 | 
			
		||||
	strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
 | 
			
		||||
	strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME);
 | 
			
		||||
	snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr);
 | 
			
		||||
 | 
			
		||||
	if (peer_addr->sa_family == AF_INET6)
 | 
			
		||||
		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6;
 | 
			
		||||
	if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
 | 
			
		||||
		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2;
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_handle_free(req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
 | 
			
		||||
				      unsigned long long connect_id)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_tree_disconnect_request *req;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->session_id = session_id;
 | 
			
		||||
	req->connect_id = connect_id;
 | 
			
		||||
 | 
			
		||||
	ret = ipc_msg_send(msg);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_logout_request(const char *account)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_logout_request *req;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_LOGOUT_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
 | 
			
		||||
 | 
			
		||||
	ret = ipc_msg_send(msg);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_share_config_response *
 | 
			
		||||
ksmbd_ipc_share_config_request(const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_share_config_request *req;
 | 
			
		||||
	struct ksmbd_share_config_response *resp;
 | 
			
		||||
 | 
			
		||||
	if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = ksmbd_acquire_id(ida);
 | 
			
		||||
	strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_handle_free(req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess,
 | 
			
		||||
					 int handle)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = handle;
 | 
			
		||||
	req->flags = ksmbd_session_rpc_method(sess, handle);
 | 
			
		||||
	req->flags |= KSMBD_RPC_OPEN_METHOD;
 | 
			
		||||
	req->payload_sz = 0;
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = handle;
 | 
			
		||||
	req->flags = ksmbd_session_rpc_method(sess, handle);
 | 
			
		||||
	req->flags |= KSMBD_RPC_CLOSE_METHOD;
 | 
			
		||||
	req->payload_sz = 0;
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle,
 | 
			
		||||
					  void *payload,
 | 
			
		||||
					  size_t payload_sz)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = handle;
 | 
			
		||||
	req->flags = ksmbd_session_rpc_method(sess, handle);
 | 
			
		||||
	req->flags |= rpc_context_flags(sess);
 | 
			
		||||
	req->flags |= KSMBD_RPC_WRITE_METHOD;
 | 
			
		||||
	req->payload_sz = payload_sz;
 | 
			
		||||
	memcpy(req->payload, payload, payload_sz);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess,
 | 
			
		||||
					 int handle)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = handle;
 | 
			
		||||
	req->flags = ksmbd_session_rpc_method(sess, handle);
 | 
			
		||||
	req->flags |= rpc_context_flags(sess);
 | 
			
		||||
	req->flags |= KSMBD_RPC_READ_METHOD;
 | 
			
		||||
	req->payload_sz = 0;
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle,
 | 
			
		||||
					  void *payload,
 | 
			
		||||
					  size_t payload_sz)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = handle;
 | 
			
		||||
	req->flags = ksmbd_session_rpc_method(sess, handle);
 | 
			
		||||
	req->flags |= rpc_context_flags(sess);
 | 
			
		||||
	req->flags |= KSMBD_RPC_IOCTL_METHOD;
 | 
			
		||||
	req->payload_sz = payload_sz;
 | 
			
		||||
	memcpy(req->payload, payload, payload_sz);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess,
 | 
			
		||||
					void *payload,
 | 
			
		||||
					size_t payload_sz)
 | 
			
		||||
{
 | 
			
		||||
	struct ksmbd_ipc_msg *msg;
 | 
			
		||||
	struct ksmbd_rpc_command *req;
 | 
			
		||||
	struct ksmbd_rpc_command *resp;
 | 
			
		||||
 | 
			
		||||
	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	msg->type = KSMBD_EVENT_RPC_REQUEST;
 | 
			
		||||
	req = KSMBD_IPC_MSG_PAYLOAD(msg);
 | 
			
		||||
	req->handle = ksmbd_acquire_id(ida);
 | 
			
		||||
	req->flags = rpc_context_flags(sess);
 | 
			
		||||
	req->flags |= KSMBD_RPC_RAP_METHOD;
 | 
			
		||||
	req->payload_sz = payload_sz;
 | 
			
		||||
	memcpy(req->payload, payload, payload_sz);
 | 
			
		||||
 | 
			
		||||
	resp = ipc_msg_send_request(msg, req->handle);
 | 
			
		||||
	ipc_msg_handle_free(req->handle);
 | 
			
		||||
	ipc_msg_free(msg);
 | 
			
		||||
	return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int __ipc_heartbeat(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long delta;
 | 
			
		||||
 | 
			
		||||
	if (!ksmbd_server_running())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (time_after(jiffies, server_conf.ipc_last_active)) {
 | 
			
		||||
		delta = (jiffies - server_conf.ipc_last_active);
 | 
			
		||||
	} else {
 | 
			
		||||
		ipc_update_last_active();
 | 
			
		||||
		schedule_delayed_work(&ipc_timer_work,
 | 
			
		||||
				      server_conf.ipc_timeout);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (delta < server_conf.ipc_timeout) {
 | 
			
		||||
		schedule_delayed_work(&ipc_timer_work,
 | 
			
		||||
				      server_conf.ipc_timeout - delta);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ksmbd_ipc_heartbeat_request() == 0) {
 | 
			
		||||
		schedule_delayed_work(&ipc_timer_work,
 | 
			
		||||
				      server_conf.ipc_timeout);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&startup_lock);
 | 
			
		||||
	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);
 | 
			
		||||
	server_conf.ipc_last_active = 0;
 | 
			
		||||
	ksmbd_tools_pid = 0;
 | 
			
		||||
	ksmbd_err("No IPC daemon response for %lus\n", delta / HZ);
 | 
			
		||||
	mutex_unlock(&startup_lock);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipc_timer_heartbeat(struct work_struct *w)
 | 
			
		||||
{
 | 
			
		||||
	if (__ipc_heartbeat())
 | 
			
		||||
		server_queue_ctrl_reset_work();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_id_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
	return ksmbd_acquire_id(ida);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_rpc_id_free(int handle)
 | 
			
		||||
{
 | 
			
		||||
	ksmbd_release_id(ida, handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_ipc_release(void)
 | 
			
		||||
{
 | 
			
		||||
	cancel_delayed_work_sync(&ipc_timer_work);
 | 
			
		||||
	ksmbd_ida_free(ida);
 | 
			
		||||
	genl_unregister_family(&ksmbd_genl_family);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_ipc_soft_reset(void)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&startup_lock);
 | 
			
		||||
	ksmbd_tools_pid = 0;
 | 
			
		||||
	cancel_delayed_work_sync(&ipc_timer_work);
 | 
			
		||||
	mutex_unlock(&startup_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ksmbd_nl_init_fixup();
 | 
			
		||||
	INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat);
 | 
			
		||||
 | 
			
		||||
	ret = genl_register_family(&ksmbd_genl_family);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Failed to register KSMBD netlink interface %d\n",
 | 
			
		||||
				ret);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ida = ksmbd_ida_alloc();
 | 
			
		||||
	if (!ida)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								fs/cifsd/transport_ipc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								fs/cifsd/transport_ipc.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_TRANSPORT_IPC_H__
 | 
			
		||||
#define __KSMBD_TRANSPORT_IPC_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/wait.h>
 | 
			
		||||
#include "ksmbd_server.h"  /* FIXME */
 | 
			
		||||
 | 
			
		||||
#define KSMBD_IPC_MAX_PAYLOAD	4096
 | 
			
		||||
 | 
			
		||||
struct ksmbd_login_response *
 | 
			
		||||
ksmbd_ipc_login_request(const char *account);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_session;
 | 
			
		||||
struct ksmbd_share_config;
 | 
			
		||||
struct ksmbd_tree_connect;
 | 
			
		||||
struct sockaddr;
 | 
			
		||||
 | 
			
		||||
struct ksmbd_tree_connect_response *
 | 
			
		||||
ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
 | 
			
		||||
			       struct ksmbd_share_config *share,
 | 
			
		||||
			       struct ksmbd_tree_connect *tree_conn,
 | 
			
		||||
			       struct sockaddr *peer_addr);
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
 | 
			
		||||
				      unsigned long long connect_id);
 | 
			
		||||
int ksmbd_ipc_logout_request(const char *account);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_share_config_response *
 | 
			
		||||
ksmbd_ipc_share_config_request(const char *name);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_spnego_authen_response *
 | 
			
		||||
ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len);
 | 
			
		||||
 | 
			
		||||
int ksmbd_ipc_id_alloc(void);
 | 
			
		||||
void ksmbd_rpc_id_free(int handle);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess,
 | 
			
		||||
					 int handle);
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle);
 | 
			
		||||
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle,
 | 
			
		||||
					  void *payload,
 | 
			
		||||
					  size_t payload_sz);
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess,
 | 
			
		||||
					 int handle);
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess,
 | 
			
		||||
					  int handle,
 | 
			
		||||
					  void *payload,
 | 
			
		||||
					  size_t payload_sz);
 | 
			
		||||
struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess,
 | 
			
		||||
					  void *payload,
 | 
			
		||||
					  size_t payload_sz);
 | 
			
		||||
 | 
			
		||||
void ksmbd_ipc_release(void);
 | 
			
		||||
void ksmbd_ipc_soft_reset(void);
 | 
			
		||||
int ksmbd_ipc_init(void);
 | 
			
		||||
#endif /* __KSMBD_TRANSPORT_IPC_H__ */
 | 
			
		||||
							
								
								
									
										2050
									
								
								fs/cifsd/transport_rdma.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2050
									
								
								fs/cifsd/transport_rdma.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										61
									
								
								fs/cifsd/transport_rdma.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								fs/cifsd/transport_rdma.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2017, Microsoft Corporation.
 | 
			
		||||
 *   Copyright (C) 2018, LG Electronics.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_TRANSPORT_RDMA_H__
 | 
			
		||||
#define __KSMBD_TRANSPORT_RDMA_H__
 | 
			
		||||
 | 
			
		||||
#define SMB_DIRECT_PORT	5445
 | 
			
		||||
 | 
			
		||||
/* SMB DIRECT negotiation request packet [MS-KSMBD] 2.2.1 */
 | 
			
		||||
struct smb_direct_negotiate_req {
 | 
			
		||||
	__le16 min_version;
 | 
			
		||||
	__le16 max_version;
 | 
			
		||||
	__le16 reserved;
 | 
			
		||||
	__le16 credits_requested;
 | 
			
		||||
	__le32 preferred_send_size;
 | 
			
		||||
	__le32 max_receive_size;
 | 
			
		||||
	__le32 max_fragmented_size;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
/* SMB DIRECT negotiation response packet [MS-KSMBD] 2.2.2 */
 | 
			
		||||
struct smb_direct_negotiate_resp {
 | 
			
		||||
	__le16 min_version;
 | 
			
		||||
	__le16 max_version;
 | 
			
		||||
	__le16 negotiated_version;
 | 
			
		||||
	__le16 reserved;
 | 
			
		||||
	__le16 credits_requested;
 | 
			
		||||
	__le16 credits_granted;
 | 
			
		||||
	__le32 status;
 | 
			
		||||
	__le32 max_readwrite_size;
 | 
			
		||||
	__le32 preferred_send_size;
 | 
			
		||||
	__le32 max_receive_size;
 | 
			
		||||
	__le32 max_fragmented_size;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001
 | 
			
		||||
 | 
			
		||||
/* SMB DIRECT data transfer packet with payload [MS-KSMBD] 2.2.3 */
 | 
			
		||||
struct smb_direct_data_transfer {
 | 
			
		||||
	__le16 credits_requested;
 | 
			
		||||
	__le16 credits_granted;
 | 
			
		||||
	__le16 flags;
 | 
			
		||||
	__le16 reserved;
 | 
			
		||||
	__le32 remaining_data_length;
 | 
			
		||||
	__le32 data_offset;
 | 
			
		||||
	__le32 data_length;
 | 
			
		||||
	__le32 padding;
 | 
			
		||||
	__u8 buffer[];
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SMB_SERVER_SMBDIRECT
 | 
			
		||||
int ksmbd_rdma_init(void);
 | 
			
		||||
int ksmbd_rdma_destroy(void);
 | 
			
		||||
#else
 | 
			
		||||
static inline int ksmbd_rdma_init(void) { return 0; }
 | 
			
		||||
static inline int ksmbd_rdma_destroy(void) { return 0; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */
 | 
			
		||||
							
								
								
									
										624
									
								
								fs/cifsd/transport_tcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										624
									
								
								fs/cifsd/transport_tcp.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,624 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/freezer.h>
 | 
			
		||||
 | 
			
		||||
#include "smb_common.h"
 | 
			
		||||
#include "server.h"
 | 
			
		||||
#include "auth.h"
 | 
			
		||||
#include "buffer_pool.h"
 | 
			
		||||
#include "connection.h"
 | 
			
		||||
#include "transport_tcp.h"
 | 
			
		||||
 | 
			
		||||
#define IFACE_STATE_DOWN		(1 << 0)
 | 
			
		||||
#define IFACE_STATE_CONFIGURED		(1 << 1)
 | 
			
		||||
 | 
			
		||||
struct interface {
 | 
			
		||||
	struct task_struct	*ksmbd_kthread;
 | 
			
		||||
	struct socket		*ksmbd_socket;
 | 
			
		||||
	struct list_head	entry;
 | 
			
		||||
	char			*name;
 | 
			
		||||
	struct mutex		sock_release_lock;
 | 
			
		||||
	int			state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static LIST_HEAD(iface_list);
 | 
			
		||||
 | 
			
		||||
static int bind_additional_ifaces;
 | 
			
		||||
 | 
			
		||||
struct tcp_transport {
 | 
			
		||||
	struct ksmbd_transport		transport;
 | 
			
		||||
	struct socket			*sock;
 | 
			
		||||
	struct kvec			*iov;
 | 
			
		||||
	unsigned int			nr_iov;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
 | 
			
		||||
 | 
			
		||||
static void tcp_stop_kthread(struct task_struct *kthread);
 | 
			
		||||
static struct interface *alloc_iface(char *ifname);
 | 
			
		||||
 | 
			
		||||
#define KSMBD_TRANS(t)	(&(t)->transport)
 | 
			
		||||
#define TCP_TRANS(t)	((struct tcp_transport *)container_of(t, \
 | 
			
		||||
				struct tcp_transport, transport))
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_tcp_nodelay(struct socket *sock)
 | 
			
		||||
{
 | 
			
		||||
	tcp_sock_set_nodelay(sock->sk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_tcp_reuseaddr(struct socket *sock)
 | 
			
		||||
{
 | 
			
		||||
	sock_set_reuseaddr(sock->sk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
 | 
			
		||||
{
 | 
			
		||||
	lock_sock(sock->sk);
 | 
			
		||||
	if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
 | 
			
		||||
		sock->sk->sk_rcvtimeo = secs * HZ;
 | 
			
		||||
	else
 | 
			
		||||
		sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
 | 
			
		||||
	release_sock(sock->sk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
 | 
			
		||||
{
 | 
			
		||||
	sock_set_sndtimeo(sock->sk, secs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct tcp_transport *alloc_transport(struct socket *client_sk)
 | 
			
		||||
{
 | 
			
		||||
	struct tcp_transport *t;
 | 
			
		||||
	struct ksmbd_conn *conn;
 | 
			
		||||
 | 
			
		||||
	t = kzalloc(sizeof(*t), GFP_KERNEL);
 | 
			
		||||
	if (!t)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	t->sock = client_sk;
 | 
			
		||||
 | 
			
		||||
	conn = ksmbd_conn_alloc();
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		kfree(t);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn->transport = KSMBD_TRANS(t);
 | 
			
		||||
	KSMBD_TRANS(t)->conn = conn;
 | 
			
		||||
	KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops;
 | 
			
		||||
	return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_transport(struct tcp_transport *t)
 | 
			
		||||
{
 | 
			
		||||
	kernel_sock_shutdown(t->sock, SHUT_RDWR);
 | 
			
		||||
	sock_release(t->sock);
 | 
			
		||||
	t->sock = NULL;
 | 
			
		||||
 | 
			
		||||
	ksmbd_conn_free(KSMBD_TRANS(t)->conn);
 | 
			
		||||
	kfree(t->iov);
 | 
			
		||||
	kfree(t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * kvec_array_init() - initialize a IO vector segment
 | 
			
		||||
 * @new:	IO vector to be initialized
 | 
			
		||||
 * @iov:	base IO vector
 | 
			
		||||
 * @nr_segs:	number of segments in base iov
 | 
			
		||||
 * @bytes:	total iovec length so far for read
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	Number of IO segments
 | 
			
		||||
 */
 | 
			
		||||
static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov,
 | 
			
		||||
				    unsigned int nr_segs, size_t bytes)
 | 
			
		||||
{
 | 
			
		||||
	size_t base = 0;
 | 
			
		||||
 | 
			
		||||
	while (bytes || !iov->iov_len) {
 | 
			
		||||
		int copy = min(bytes, iov->iov_len);
 | 
			
		||||
 | 
			
		||||
		bytes -= copy;
 | 
			
		||||
		base += copy;
 | 
			
		||||
		if (iov->iov_len == base) {
 | 
			
		||||
			iov++;
 | 
			
		||||
			nr_segs--;
 | 
			
		||||
			base = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(new, iov, sizeof(*iov) * nr_segs);
 | 
			
		||||
	new->iov_base += base;
 | 
			
		||||
	new->iov_len -= base;
 | 
			
		||||
	return nr_segs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * get_conn_iovec() - get connection iovec for reading from socket
 | 
			
		||||
 * @t:		TCP transport instance
 | 
			
		||||
 * @nr_segs:	number of segments in iov
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	return existing or newly allocate iovec
 | 
			
		||||
 */
 | 
			
		||||
static struct kvec *get_conn_iovec(struct tcp_transport *t,
 | 
			
		||||
				     unsigned int nr_segs)
 | 
			
		||||
{
 | 
			
		||||
	struct kvec *new_iov;
 | 
			
		||||
 | 
			
		||||
	if (t->iov && nr_segs <= t->nr_iov)
 | 
			
		||||
		return t->iov;
 | 
			
		||||
 | 
			
		||||
	/* not big enough -- allocate a new one and release the old */
 | 
			
		||||
	new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL);
 | 
			
		||||
	if (new_iov) {
 | 
			
		||||
		kfree(t->iov);
 | 
			
		||||
		t->iov = new_iov;
 | 
			
		||||
		t->nr_iov = nr_segs;
 | 
			
		||||
	}
 | 
			
		||||
	return new_iov;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa)
 | 
			
		||||
{
 | 
			
		||||
	switch (sa->sa_family) {
 | 
			
		||||
	case AF_INET:
 | 
			
		||||
		return ntohs(((struct sockaddr_in *)sa)->sin_port);
 | 
			
		||||
	case AF_INET6:
 | 
			
		||||
		return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_tcp_new_connection() - create a new tcp session on mount
 | 
			
		||||
 * @sock:	socket associated with new connection
 | 
			
		||||
 *
 | 
			
		||||
 * whenever a new connection is requested, create a conn thread
 | 
			
		||||
 * (session thread) to handle new incoming smb requests from the connection
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	0 on success, otherwise error
 | 
			
		||||
 */
 | 
			
		||||
static int ksmbd_tcp_new_connection(struct socket *client_sk)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr *csin;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct tcp_transport *t;
 | 
			
		||||
 | 
			
		||||
	t = alloc_transport(client_sk);
 | 
			
		||||
	if (!t)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn);
 | 
			
		||||
	if (kernel_getpeername(client_sk, csin) < 0) {
 | 
			
		||||
		ksmbd_err("client ip resolution failed\n");
 | 
			
		||||
		rc = -EINVAL;
 | 
			
		||||
		goto out_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop,
 | 
			
		||||
					KSMBD_TRANS(t)->conn,
 | 
			
		||||
					"ksmbd:%u", ksmbd_tcp_get_port(csin));
 | 
			
		||||
	if (IS_ERR(KSMBD_TRANS(t)->handler)) {
 | 
			
		||||
		ksmbd_err("cannot start conn thread\n");
 | 
			
		||||
		rc = PTR_ERR(KSMBD_TRANS(t)->handler);
 | 
			
		||||
		free_transport(t);
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
 | 
			
		||||
out_error:
 | 
			
		||||
	free_transport(t);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_kthread_fn() - listen to new SMB connections and callback server
 | 
			
		||||
 * @p:		arguments to forker thread
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	Returns a task_struct or ERR_PTR
 | 
			
		||||
 */
 | 
			
		||||
static int ksmbd_kthread_fn(void *p)
 | 
			
		||||
{
 | 
			
		||||
	struct socket *client_sk = NULL;
 | 
			
		||||
	struct interface *iface = (struct interface *)p;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	while (!kthread_should_stop()) {
 | 
			
		||||
		mutex_lock(&iface->sock_release_lock);
 | 
			
		||||
		if (!iface->ksmbd_socket) {
 | 
			
		||||
			mutex_unlock(&iface->sock_release_lock);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		ret = kernel_accept(iface->ksmbd_socket, &client_sk,
 | 
			
		||||
				O_NONBLOCK);
 | 
			
		||||
		mutex_unlock(&iface->sock_release_lock);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			if (ret == -EAGAIN)
 | 
			
		||||
				/* check for new connections every 100 msecs */
 | 
			
		||||
				schedule_timeout_interruptible(HZ / 10);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ksmbd_debug(CONN, "connect success: accepted new connection\n");
 | 
			
		||||
		client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
 | 
			
		||||
		client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
		ksmbd_tcp_new_connection(client_sk);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_debug(CONN, "releasing socket\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_create_ksmbd_kthread() - start forker thread
 | 
			
		||||
 *
 | 
			
		||||
 * start forker thread(ksmbd/0) at module init time to listen
 | 
			
		||||
 * on port 445 for new SMB connection requests. It creates per connection
 | 
			
		||||
 * server threads(ksmbd/x)
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	0 on success or error number
 | 
			
		||||
 */
 | 
			
		||||
static int ksmbd_tcp_run_kthread(struct interface *iface)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct task_struct *kthread;
 | 
			
		||||
 | 
			
		||||
	kthread = kthread_run(ksmbd_kthread_fn, (void *)iface,
 | 
			
		||||
		"ksmbd-%s", iface->name);
 | 
			
		||||
	if (IS_ERR(kthread)) {
 | 
			
		||||
		rc = PTR_ERR(kthread);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	iface->ksmbd_kthread = kthread;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_tcp_readv() - read data from socket in given iovec
 | 
			
		||||
 * @t:		TCP transport instance
 | 
			
		||||
 * @iov_orig:	base IO vector
 | 
			
		||||
 * @nr_segs:	number of segments in base iov
 | 
			
		||||
 * @to_read:	number of bytes to read from socket
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	on success return number of bytes read from socket,
 | 
			
		||||
 *		otherwise return error number
 | 
			
		||||
 */
 | 
			
		||||
static int ksmbd_tcp_readv(struct tcp_transport *t,
 | 
			
		||||
			   struct kvec *iov_orig,
 | 
			
		||||
			   unsigned int nr_segs,
 | 
			
		||||
			   unsigned int to_read)
 | 
			
		||||
{
 | 
			
		||||
	int length = 0;
 | 
			
		||||
	int total_read;
 | 
			
		||||
	unsigned int segs;
 | 
			
		||||
	struct msghdr ksmbd_msg;
 | 
			
		||||
	struct kvec *iov;
 | 
			
		||||
	struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn;
 | 
			
		||||
 | 
			
		||||
	iov = get_conn_iovec(t, nr_segs);
 | 
			
		||||
	if (!iov)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ksmbd_msg.msg_control = NULL;
 | 
			
		||||
	ksmbd_msg.msg_controllen = 0;
 | 
			
		||||
 | 
			
		||||
	for (total_read = 0; to_read; total_read += length, to_read -= length) {
 | 
			
		||||
		try_to_freeze();
 | 
			
		||||
 | 
			
		||||
		if (!ksmbd_conn_alive(conn)) {
 | 
			
		||||
			total_read = -ESHUTDOWN;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
 | 
			
		||||
 | 
			
		||||
		length = kernel_recvmsg(t->sock, &ksmbd_msg,
 | 
			
		||||
					iov, segs, to_read, 0);
 | 
			
		||||
 | 
			
		||||
		if (length == -EINTR) {
 | 
			
		||||
			total_read = -ESHUTDOWN;
 | 
			
		||||
			break;
 | 
			
		||||
		} else if (conn->status == KSMBD_SESS_NEED_RECONNECT) {
 | 
			
		||||
			total_read = -EAGAIN;
 | 
			
		||||
			break;
 | 
			
		||||
		} else if (length == -ERESTARTSYS || length == -EAGAIN) {
 | 
			
		||||
			usleep_range(1000, 2000);
 | 
			
		||||
			length = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		} else if (length <= 0) {
 | 
			
		||||
			total_read = -EAGAIN;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return total_read;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ksmbd_tcp_read() - read data from socket in given buffer
 | 
			
		||||
 * @t:		TCP transport instance
 | 
			
		||||
 * @buf:	buffer to store read data from socket
 | 
			
		||||
 * @to_read:	number of bytes to read from socket
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	on success return number of bytes read from socket,
 | 
			
		||||
 *		otherwise return error number
 | 
			
		||||
 */
 | 
			
		||||
static int ksmbd_tcp_read(struct ksmbd_transport *t,
 | 
			
		||||
		   char *buf,
 | 
			
		||||
		   unsigned int to_read)
 | 
			
		||||
{
 | 
			
		||||
	struct kvec iov;
 | 
			
		||||
 | 
			
		||||
	iov.iov_base = buf;
 | 
			
		||||
	iov.iov_len = to_read;
 | 
			
		||||
 | 
			
		||||
	return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ksmbd_tcp_writev(struct ksmbd_transport *t,
 | 
			
		||||
			struct kvec *iov, int nvecs, int size,
 | 
			
		||||
			bool need_invalidate, unsigned int remote_key)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL};
 | 
			
		||||
 | 
			
		||||
	return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
 | 
			
		||||
{
 | 
			
		||||
	free_transport(TCP_TRANS(t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void tcp_destroy_socket(struct socket *ksmbd_socket)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!ksmbd_socket)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* set zero to timeout */
 | 
			
		||||
	ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
 | 
			
		||||
	ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
 | 
			
		||||
 | 
			
		||||
	ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		ksmbd_err("Failed to shutdown socket: %d\n", ret);
 | 
			
		||||
	else
 | 
			
		||||
		sock_release(ksmbd_socket);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * create_socket - create socket for ksmbd/0
 | 
			
		||||
 *
 | 
			
		||||
 * Return:	Returns a task_struct or ERR_PTR
 | 
			
		||||
 */
 | 
			
		||||
static int create_socket(struct interface *iface)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct sockaddr_in6 sin6;
 | 
			
		||||
	struct sockaddr_in sin;
 | 
			
		||||
	struct socket *ksmbd_socket;
 | 
			
		||||
	bool ipv4 = false;
 | 
			
		||||
 | 
			
		||||
	ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Can't create socket for ipv6, try ipv4: %d\n", ret);
 | 
			
		||||
		ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP,
 | 
			
		||||
				&ksmbd_socket);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			ksmbd_err("Can't create socket for ipv4: %d\n", ret);
 | 
			
		||||
			goto out_error;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sin.sin_family = PF_INET;
 | 
			
		||||
		sin.sin_addr.s_addr = htonl(INADDR_ANY);
 | 
			
		||||
		sin.sin_port = htons(server_conf.tcp_port);
 | 
			
		||||
		ipv4 = true;
 | 
			
		||||
	} else {
 | 
			
		||||
		sin6.sin6_family = PF_INET6;
 | 
			
		||||
		sin6.sin6_addr = in6addr_any;
 | 
			
		||||
		sin6.sin6_port = htons(server_conf.tcp_port);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_tcp_nodelay(ksmbd_socket);
 | 
			
		||||
	ksmbd_tcp_reuseaddr(ksmbd_socket);
 | 
			
		||||
 | 
			
		||||
	ret = sock_setsockopt(ksmbd_socket,
 | 
			
		||||
				SOL_SOCKET,
 | 
			
		||||
				SO_BINDTODEVICE,
 | 
			
		||||
				KERNEL_SOCKPTR(iface->name),
 | 
			
		||||
				strlen(iface->name));
 | 
			
		||||
	if (ret != -ENODEV && ret < 0) {
 | 
			
		||||
		ksmbd_err("Failed to set SO_BINDTODEVICE: %d\n", ret);
 | 
			
		||||
		goto out_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ipv4)
 | 
			
		||||
		ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin,
 | 
			
		||||
				sizeof(sin));
 | 
			
		||||
	else
 | 
			
		||||
		ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6,
 | 
			
		||||
				sizeof(sin6));
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Failed to bind socket: %d\n", ret);
 | 
			
		||||
		goto out_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
 | 
			
		||||
	ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
	ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Port listen() error: %d\n", ret);
 | 
			
		||||
		goto out_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iface->ksmbd_socket = ksmbd_socket;
 | 
			
		||||
	ret = ksmbd_tcp_run_kthread(iface);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		ksmbd_err("Can't start ksmbd main kthread: %d\n", ret);
 | 
			
		||||
		goto out_error;
 | 
			
		||||
	}
 | 
			
		||||
	iface->state = IFACE_STATE_CONFIGURED;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_error:
 | 
			
		||||
	tcp_destroy_socket(ksmbd_socket);
 | 
			
		||||
	iface->ksmbd_socket = NULL;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
 | 
			
		||||
				void *ptr)
 | 
			
		||||
{
 | 
			
		||||
	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
 | 
			
		||||
	struct interface *iface;
 | 
			
		||||
	int ret, found = 0;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NETDEV_UP:
 | 
			
		||||
		if (netdev->priv_flags & IFF_BRIDGE_PORT)
 | 
			
		||||
			return NOTIFY_OK;
 | 
			
		||||
 | 
			
		||||
		list_for_each_entry(iface, &iface_list, entry) {
 | 
			
		||||
			if (!strcmp(iface->name, netdev->name)) {
 | 
			
		||||
				found = 1;
 | 
			
		||||
				if (iface->state != IFACE_STATE_DOWN)
 | 
			
		||||
					break;
 | 
			
		||||
				ret = create_socket(iface);
 | 
			
		||||
				if (ret)
 | 
			
		||||
					return NOTIFY_OK;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (!found && bind_additional_ifaces) {
 | 
			
		||||
			iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL));
 | 
			
		||||
			if (!iface)
 | 
			
		||||
				return NOTIFY_OK;
 | 
			
		||||
			ret = create_socket(iface);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case NETDEV_DOWN:
 | 
			
		||||
		list_for_each_entry(iface, &iface_list, entry) {
 | 
			
		||||
			if (!strcmp(iface->name, netdev->name) &&
 | 
			
		||||
			    iface->state == IFACE_STATE_CONFIGURED) {
 | 
			
		||||
				tcp_stop_kthread(iface->ksmbd_kthread);
 | 
			
		||||
				iface->ksmbd_kthread = NULL;
 | 
			
		||||
				mutex_lock(&iface->sock_release_lock);
 | 
			
		||||
				tcp_destroy_socket(iface->ksmbd_socket);
 | 
			
		||||
				iface->ksmbd_socket = NULL;
 | 
			
		||||
				mutex_unlock(&iface->sock_release_lock);
 | 
			
		||||
 | 
			
		||||
				iface->state = IFACE_STATE_DOWN;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NOTIFY_DONE;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct notifier_block ksmbd_netdev_notifier = {
 | 
			
		||||
	.notifier_call = ksmbd_netdev_event,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int ksmbd_tcp_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_netdevice_notifier(&ksmbd_netdev_notifier);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void tcp_stop_kthread(struct task_struct *kthread)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!kthread)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ret = kthread_stop(kthread);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		ksmbd_err("failed to stop forker thread\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ksmbd_tcp_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	struct interface *iface, *tmp;
 | 
			
		||||
 | 
			
		||||
	unregister_netdevice_notifier(&ksmbd_netdev_notifier);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
 | 
			
		||||
		list_del(&iface->entry);
 | 
			
		||||
		kfree(iface->name);
 | 
			
		||||
		ksmbd_free(iface);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct interface *alloc_iface(char *ifname)
 | 
			
		||||
{
 | 
			
		||||
	struct interface *iface;
 | 
			
		||||
 | 
			
		||||
	if (!ifname)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	iface = ksmbd_alloc(sizeof(struct interface));
 | 
			
		||||
	if (!iface) {
 | 
			
		||||
		kfree(ifname);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iface->name = ifname;
 | 
			
		||||
	iface->state = IFACE_STATE_DOWN;
 | 
			
		||||
	list_add(&iface->entry, &iface_list);
 | 
			
		||||
	mutex_init(&iface->sock_release_lock);
 | 
			
		||||
	return iface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz)
 | 
			
		||||
{
 | 
			
		||||
	int sz = 0;
 | 
			
		||||
 | 
			
		||||
	if (!ifc_list_sz) {
 | 
			
		||||
		struct net_device *netdev;
 | 
			
		||||
 | 
			
		||||
		rtnl_lock();
 | 
			
		||||
		for_each_netdev(&init_net, netdev) {
 | 
			
		||||
			if (netdev->priv_flags & IFF_BRIDGE_PORT)
 | 
			
		||||
				continue;
 | 
			
		||||
			if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL)))
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
		rtnl_unlock();
 | 
			
		||||
		bind_additional_ifaces = 1;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (ifc_list_sz > 0) {
 | 
			
		||||
		if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL)))
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
		sz = strlen(ifc_list);
 | 
			
		||||
		if (!sz)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		ifc_list += sz + 1;
 | 
			
		||||
		ifc_list_sz -= (sz + 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bind_additional_ifaces = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
 | 
			
		||||
	.read		= ksmbd_tcp_read,
 | 
			
		||||
	.writev		= ksmbd_tcp_writev,
 | 
			
		||||
	.disconnect	= ksmbd_tcp_disconnect,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										13
									
								
								fs/cifsd/transport_tcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								fs/cifsd/transport_tcp.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __KSMBD_TRANSPORT_TCP_H__
 | 
			
		||||
#define __KSMBD_TRANSPORT_TCP_H__
 | 
			
		||||
 | 
			
		||||
int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz);
 | 
			
		||||
int ksmbd_tcp_init(void);
 | 
			
		||||
void ksmbd_tcp_destroy(void);
 | 
			
		||||
 | 
			
		||||
#endif /* __KSMBD_TRANSPORT_TCP_H__ */
 | 
			
		||||
		Loading…
	
		Reference in a new issue