xref: /openbmc/linux/fs/smb/server/mgmt/tree_connect.c (revision 33b235a6)
138c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0-or-later
238c8a9a5SSteve French /*
338c8a9a5SSteve French  *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
438c8a9a5SSteve French  */
538c8a9a5SSteve French 
638c8a9a5SSteve French #include <linux/list.h>
738c8a9a5SSteve French #include <linux/slab.h>
838c8a9a5SSteve French #include <linux/xarray.h>
938c8a9a5SSteve French 
1038c8a9a5SSteve French #include "../transport_ipc.h"
1138c8a9a5SSteve French #include "../connection.h"
1238c8a9a5SSteve French 
1338c8a9a5SSteve French #include "tree_connect.h"
1438c8a9a5SSteve French #include "user_config.h"
1538c8a9a5SSteve French #include "share_config.h"
1638c8a9a5SSteve French #include "user_session.h"
1738c8a9a5SSteve French 
1838c8a9a5SSteve French struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn * conn,struct ksmbd_session * sess,const char * share_name)1938c8a9a5SSteve French ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
2038c8a9a5SSteve French 			const char *share_name)
2138c8a9a5SSteve French {
2238c8a9a5SSteve French 	struct ksmbd_tree_conn_status status = {-ENOENT, NULL};
2338c8a9a5SSteve French 	struct ksmbd_tree_connect_response *resp = NULL;
2438c8a9a5SSteve French 	struct ksmbd_share_config *sc;
2538c8a9a5SSteve French 	struct ksmbd_tree_connect *tree_conn = NULL;
2638c8a9a5SSteve French 	struct sockaddr *peer_addr;
2738c8a9a5SSteve French 	int ret;
2838c8a9a5SSteve French 
2938c8a9a5SSteve French 	sc = ksmbd_share_config_get(conn->um, share_name);
3038c8a9a5SSteve French 	if (!sc)
3138c8a9a5SSteve French 		return status;
3238c8a9a5SSteve French 
3338c8a9a5SSteve French 	tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL);
3438c8a9a5SSteve French 	if (!tree_conn) {
3538c8a9a5SSteve French 		status.ret = -ENOMEM;
3638c8a9a5SSteve French 		goto out_error;
3738c8a9a5SSteve French 	}
3838c8a9a5SSteve French 
3938c8a9a5SSteve French 	tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
4038c8a9a5SSteve French 	if (tree_conn->id < 0) {
4138c8a9a5SSteve French 		status.ret = -EINVAL;
4238c8a9a5SSteve French 		goto out_error;
4338c8a9a5SSteve French 	}
4438c8a9a5SSteve French 
4538c8a9a5SSteve French 	peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn);
4638c8a9a5SSteve French 	resp = ksmbd_ipc_tree_connect_request(sess,
4738c8a9a5SSteve French 					      sc,
4838c8a9a5SSteve French 					      tree_conn,
4938c8a9a5SSteve French 					      peer_addr);
5038c8a9a5SSteve French 	if (!resp) {
5138c8a9a5SSteve French 		status.ret = -EINVAL;
5238c8a9a5SSteve French 		goto out_error;
5338c8a9a5SSteve French 	}
5438c8a9a5SSteve French 
5538c8a9a5SSteve French 	status.ret = resp->status;
5638c8a9a5SSteve French 	if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
5738c8a9a5SSteve French 		goto out_error;
5838c8a9a5SSteve French 
5938c8a9a5SSteve French 	tree_conn->flags = resp->connection_flags;
6038c8a9a5SSteve French 	if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) {
6138c8a9a5SSteve French 		struct ksmbd_share_config *new_sc;
6238c8a9a5SSteve French 
6338c8a9a5SSteve French 		ksmbd_share_config_del(sc);
6438c8a9a5SSteve French 		new_sc = ksmbd_share_config_get(conn->um, share_name);
6538c8a9a5SSteve French 		if (!new_sc) {
6638c8a9a5SSteve French 			pr_err("Failed to update stale share config\n");
6738c8a9a5SSteve French 			status.ret = -ESTALE;
6838c8a9a5SSteve French 			goto out_error;
6938c8a9a5SSteve French 		}
7038c8a9a5SSteve French 		ksmbd_share_config_put(sc);
7138c8a9a5SSteve French 		sc = new_sc;
7238c8a9a5SSteve French 	}
7338c8a9a5SSteve French 
7438c8a9a5SSteve French 	tree_conn->user = sess->user;
7538c8a9a5SSteve French 	tree_conn->share_conf = sc;
76*33b235a6SNamjae Jeon 	tree_conn->t_state = TREE_NEW;
7738c8a9a5SSteve French 	status.tree_conn = tree_conn;
78*33b235a6SNamjae Jeon 	atomic_set(&tree_conn->refcount, 1);
79*33b235a6SNamjae Jeon 	init_waitqueue_head(&tree_conn->refcount_q);
8038c8a9a5SSteve French 
8138c8a9a5SSteve French 	ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
8238c8a9a5SSteve French 			      GFP_KERNEL));
8338c8a9a5SSteve French 	if (ret) {
8438c8a9a5SSteve French 		status.ret = -ENOMEM;
8538c8a9a5SSteve French 		goto out_error;
8638c8a9a5SSteve French 	}
8738c8a9a5SSteve French 	kvfree(resp);
8838c8a9a5SSteve French 	return status;
8938c8a9a5SSteve French 
9038c8a9a5SSteve French out_error:
9138c8a9a5SSteve French 	if (tree_conn)
9238c8a9a5SSteve French 		ksmbd_release_tree_conn_id(sess, tree_conn->id);
9338c8a9a5SSteve French 	ksmbd_share_config_put(sc);
9438c8a9a5SSteve French 	kfree(tree_conn);
9538c8a9a5SSteve French 	kvfree(resp);
9638c8a9a5SSteve French 	return status;
9738c8a9a5SSteve French }
9838c8a9a5SSteve French 
ksmbd_tree_connect_put(struct ksmbd_tree_connect * tcon)99*33b235a6SNamjae Jeon void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
100*33b235a6SNamjae Jeon {
101*33b235a6SNamjae Jeon 	/*
102*33b235a6SNamjae Jeon 	 * Checking waitqueue to releasing tree connect on
103*33b235a6SNamjae Jeon 	 * tree disconnect. waitqueue_active is safe because it
104*33b235a6SNamjae Jeon 	 * uses atomic operation for condition.
105*33b235a6SNamjae Jeon 	 */
106*33b235a6SNamjae Jeon 	if (!atomic_dec_return(&tcon->refcount) &&
107*33b235a6SNamjae Jeon 	    waitqueue_active(&tcon->refcount_q))
108*33b235a6SNamjae Jeon 		wake_up(&tcon->refcount_q);
109*33b235a6SNamjae Jeon }
110*33b235a6SNamjae Jeon 
ksmbd_tree_conn_disconnect(struct ksmbd_session * sess,struct ksmbd_tree_connect * tree_conn)11138c8a9a5SSteve French int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
11238c8a9a5SSteve French 			       struct ksmbd_tree_connect *tree_conn)
11338c8a9a5SSteve French {
11438c8a9a5SSteve French 	int ret;
11538c8a9a5SSteve French 
116*33b235a6SNamjae Jeon 	write_lock(&sess->tree_conns_lock);
117*33b235a6SNamjae Jeon 	xa_erase(&sess->tree_conns, tree_conn->id);
118*33b235a6SNamjae Jeon 	write_unlock(&sess->tree_conns_lock);
119*33b235a6SNamjae Jeon 
120*33b235a6SNamjae Jeon 	if (!atomic_dec_and_test(&tree_conn->refcount))
121*33b235a6SNamjae Jeon 		wait_event(tree_conn->refcount_q,
122*33b235a6SNamjae Jeon 			   atomic_read(&tree_conn->refcount) == 0);
123*33b235a6SNamjae Jeon 
12438c8a9a5SSteve French 	ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
12538c8a9a5SSteve French 	ksmbd_release_tree_conn_id(sess, tree_conn->id);
12638c8a9a5SSteve French 	ksmbd_share_config_put(tree_conn->share_conf);
12738c8a9a5SSteve French 	kfree(tree_conn);
12838c8a9a5SSteve French 	return ret;
12938c8a9a5SSteve French }
13038c8a9a5SSteve French 
ksmbd_tree_conn_lookup(struct ksmbd_session * sess,unsigned int id)13138c8a9a5SSteve French struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
13238c8a9a5SSteve French 						  unsigned int id)
13338c8a9a5SSteve French {
13438c8a9a5SSteve French 	struct ksmbd_tree_connect *tcon;
13538c8a9a5SSteve French 
136*33b235a6SNamjae Jeon 	read_lock(&sess->tree_conns_lock);
13738c8a9a5SSteve French 	tcon = xa_load(&sess->tree_conns, id);
13838c8a9a5SSteve French 	if (tcon) {
139*33b235a6SNamjae Jeon 		if (tcon->t_state != TREE_CONNECTED)
140*33b235a6SNamjae Jeon 			tcon = NULL;
141*33b235a6SNamjae Jeon 		else if (!atomic_inc_not_zero(&tcon->refcount))
14238c8a9a5SSteve French 			tcon = NULL;
14338c8a9a5SSteve French 	}
144*33b235a6SNamjae Jeon 	read_unlock(&sess->tree_conns_lock);
14538c8a9a5SSteve French 
14638c8a9a5SSteve French 	return tcon;
14738c8a9a5SSteve French }
14838c8a9a5SSteve French 
ksmbd_tree_conn_session_logoff(struct ksmbd_session * sess)14938c8a9a5SSteve French int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
15038c8a9a5SSteve French {
15138c8a9a5SSteve French 	int ret = 0;
15238c8a9a5SSteve French 	struct ksmbd_tree_connect *tc;
15338c8a9a5SSteve French 	unsigned long id;
15438c8a9a5SSteve French 
15538c8a9a5SSteve French 	if (!sess)
15638c8a9a5SSteve French 		return -EINVAL;
15738c8a9a5SSteve French 
158*33b235a6SNamjae Jeon 	xa_for_each(&sess->tree_conns, id, tc) {
159*33b235a6SNamjae Jeon 		write_lock(&sess->tree_conns_lock);
160*33b235a6SNamjae Jeon 		if (tc->t_state == TREE_DISCONNECTED) {
161*33b235a6SNamjae Jeon 			write_unlock(&sess->tree_conns_lock);
162*33b235a6SNamjae Jeon 			ret = -ENOENT;
163*33b235a6SNamjae Jeon 			continue;
164*33b235a6SNamjae Jeon 		}
165*33b235a6SNamjae Jeon 		tc->t_state = TREE_DISCONNECTED;
166*33b235a6SNamjae Jeon 		write_unlock(&sess->tree_conns_lock);
167*33b235a6SNamjae Jeon 
16838c8a9a5SSteve French 		ret |= ksmbd_tree_conn_disconnect(sess, tc);
169*33b235a6SNamjae Jeon 	}
17038c8a9a5SSteve French 	xa_destroy(&sess->tree_conns);
17138c8a9a5SSteve French 	return ret;
17238c8a9a5SSteve French }
173