brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 94a52a7 Raw
174 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.4 */5 6#include <linux/list.h>7#include <linux/slab.h>8#include <linux/xarray.h>9 10#include "../transport_ipc.h"11#include "../connection.h"12 13#include "tree_connect.h"14#include "user_config.h"15#include "share_config.h"16#include "user_session.h"17 18struct ksmbd_tree_conn_status19ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)20{21	struct ksmbd_tree_conn_status status = {-ENOENT, NULL};22	struct ksmbd_tree_connect_response *resp = NULL;23	struct ksmbd_share_config *sc;24	struct ksmbd_tree_connect *tree_conn = NULL;25	struct sockaddr *peer_addr;26	struct ksmbd_conn *conn = work->conn;27	struct ksmbd_session *sess = work->sess;28	int ret;29 30	sc = ksmbd_share_config_get(work, share_name);31	if (!sc)32		return status;33 34	tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL);35	if (!tree_conn) {36		status.ret = -ENOMEM;37		goto out_error;38	}39 40	tree_conn->id = ksmbd_acquire_tree_conn_id(sess);41	if (tree_conn->id < 0) {42		status.ret = -EINVAL;43		goto out_error;44	}45 46	peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn);47	resp = ksmbd_ipc_tree_connect_request(sess,48					      sc,49					      tree_conn,50					      peer_addr);51	if (!resp) {52		status.ret = -EINVAL;53		goto out_error;54	}55 56	status.ret = resp->status;57	if (status.ret != KSMBD_TREE_CONN_STATUS_OK)58		goto out_error;59 60	tree_conn->flags = resp->connection_flags;61	if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) {62		struct ksmbd_share_config *new_sc;63 64		ksmbd_share_config_del(sc);65		new_sc = ksmbd_share_config_get(work, share_name);66		if (!new_sc) {67			pr_err("Failed to update stale share config\n");68			status.ret = -ESTALE;69			goto out_error;70		}71		ksmbd_share_config_put(sc);72		sc = new_sc;73	}74 75	tree_conn->user = sess->user;76	tree_conn->share_conf = sc;77	tree_conn->t_state = TREE_NEW;78	status.tree_conn = tree_conn;79	atomic_set(&tree_conn->refcount, 1);80	init_waitqueue_head(&tree_conn->refcount_q);81 82	ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,83			      GFP_KERNEL));84	if (ret) {85		status.ret = -ENOMEM;86		goto out_error;87	}88	kvfree(resp);89	return status;90 91out_error:92	if (tree_conn)93		ksmbd_release_tree_conn_id(sess, tree_conn->id);94	ksmbd_share_config_put(sc);95	kfree(tree_conn);96	kvfree(resp);97	return status;98}99 100void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)101{102	/*103	 * Checking waitqueue to releasing tree connect on104	 * tree disconnect. waitqueue_active is safe because it105	 * uses atomic operation for condition.106	 */107	if (!atomic_dec_return(&tcon->refcount) &&108	    waitqueue_active(&tcon->refcount_q))109		wake_up(&tcon->refcount_q);110}111 112int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,113			       struct ksmbd_tree_connect *tree_conn)114{115	int ret;116 117	write_lock(&sess->tree_conns_lock);118	xa_erase(&sess->tree_conns, tree_conn->id);119	write_unlock(&sess->tree_conns_lock);120 121	if (!atomic_dec_and_test(&tree_conn->refcount))122		wait_event(tree_conn->refcount_q,123			   atomic_read(&tree_conn->refcount) == 0);124 125	ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);126	ksmbd_release_tree_conn_id(sess, tree_conn->id);127	ksmbd_share_config_put(tree_conn->share_conf);128	kfree(tree_conn);129	return ret;130}131 132struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,133						  unsigned int id)134{135	struct ksmbd_tree_connect *tcon;136 137	read_lock(&sess->tree_conns_lock);138	tcon = xa_load(&sess->tree_conns, id);139	if (tcon) {140		if (tcon->t_state != TREE_CONNECTED)141			tcon = NULL;142		else if (!atomic_inc_not_zero(&tcon->refcount))143			tcon = NULL;144	}145	read_unlock(&sess->tree_conns_lock);146 147	return tcon;148}149 150int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)151{152	int ret = 0;153	struct ksmbd_tree_connect *tc;154	unsigned long id;155 156	if (!sess)157		return -EINVAL;158 159	xa_for_each(&sess->tree_conns, id, tc) {160		write_lock(&sess->tree_conns_lock);161		if (tc->t_state == TREE_DISCONNECTED) {162			write_unlock(&sess->tree_conns_lock);163			ret = -ENOENT;164			continue;165		}166		tc->t_state = TREE_DISCONNECTED;167		write_unlock(&sess->tree_conns_lock);168 169		ret |= ksmbd_tree_conn_disconnect(sess, tc);170	}171	xa_destroy(&sess->tree_conns);172	return ret;173}174