xref: /linux/fs/smb/server/mgmt/tree_connect.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
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 #include "../stats.h"
13 
14 #include "tree_connect.h"
15 #include "user_config.h"
16 #include "share_config.h"
17 #include "user_session.h"
18 
19 struct ksmbd_tree_conn_status
20 ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
21 {
22 	struct ksmbd_tree_conn_status status = {-ENOENT, NULL};
23 	struct ksmbd_tree_connect_response *resp = NULL;
24 	struct ksmbd_share_config *sc;
25 	struct ksmbd_tree_connect *tree_conn = NULL;
26 	struct sockaddr *peer_addr;
27 	struct ksmbd_conn *conn = work->conn;
28 	struct ksmbd_session *sess = work->sess;
29 	int ret;
30 
31 	sc = ksmbd_share_config_get(work, share_name);
32 	if (!sc)
33 		return status;
34 
35 	tree_conn = kzalloc_obj(struct ksmbd_tree_connect, KSMBD_DEFAULT_GFP);
36 	if (!tree_conn) {
37 		status.ret = -ENOMEM;
38 		goto out_error;
39 	}
40 
41 	tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
42 	if (tree_conn->id < 0) {
43 		status.ret = -EINVAL;
44 		goto out_error;
45 	}
46 
47 	peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn);
48 	resp = ksmbd_ipc_tree_connect_request(sess,
49 					      sc,
50 					      tree_conn,
51 					      peer_addr);
52 	if (!resp) {
53 		status.ret = -EINVAL;
54 		goto out_error;
55 	}
56 
57 	status.ret = resp->status;
58 	if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
59 		goto out_error;
60 
61 	tree_conn->flags = resp->connection_flags;
62 	if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) {
63 		struct ksmbd_share_config *new_sc;
64 
65 		ksmbd_share_config_del(sc);
66 		new_sc = ksmbd_share_config_get(work, share_name);
67 		if (!new_sc) {
68 			pr_err("Failed to update stale share config\n");
69 			status.ret = -ESTALE;
70 			goto out_error;
71 		}
72 		ksmbd_share_config_put(sc);
73 		sc = new_sc;
74 	}
75 
76 	tree_conn->user = sess->user;
77 	tree_conn->share_conf = sc;
78 	tree_conn->t_state = TREE_NEW;
79 	status.tree_conn = tree_conn;
80 	atomic_set(&tree_conn->refcount, 1);
81 
82 	down_write(&sess->tree_conns_lock);
83 	ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
84 			      KSMBD_DEFAULT_GFP));
85 	if (!ret)
86 		atomic_inc(&tree_conn->refcount);
87 	up_write(&sess->tree_conns_lock);
88 	if (ret) {
89 		status.ret = -ENOMEM;
90 		goto out_error;
91 	}
92 	ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS);
93 	ksmbd_share_tree_conn_inc(sc);
94 	kvfree(resp);
95 	return status;
96 
97 out_error:
98 	if (tree_conn)
99 		ksmbd_release_tree_conn_id(sess, tree_conn->id);
100 	ksmbd_share_config_put(sc);
101 	kfree(tree_conn);
102 	kvfree(resp);
103 	return status;
104 }
105 
106 void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
107 {
108 	if (atomic_dec_and_test(&tcon->refcount)) {
109 		ksmbd_share_config_put(tcon->share_conf);
110 		kfree(tcon);
111 	}
112 }
113 
114 static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
115 					struct ksmbd_tree_connect *tree_conn)
116 {
117 	int ret;
118 
119 	ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
120 	ksmbd_release_tree_conn_id(sess, tree_conn->id);
121 	ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS);
122 	ksmbd_share_tree_conn_dec(tree_conn->share_conf);
123 	if (atomic_dec_and_test(&tree_conn->refcount)) {
124 		ksmbd_share_config_put(tree_conn->share_conf);
125 		kfree(tree_conn);
126 	}
127 	return ret;
128 }
129 
130 int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
131 			       struct ksmbd_tree_connect *tree_conn)
132 {
133 	down_write(&sess->tree_conns_lock);
134 	if (tree_conn->t_state == TREE_DISCONNECTED ||
135 	    xa_load(&sess->tree_conns, tree_conn->id) != tree_conn) {
136 		up_write(&sess->tree_conns_lock);
137 		return -ENOENT;
138 	}
139 	tree_conn->t_state = TREE_DISCONNECTED;
140 	xa_erase(&sess->tree_conns, tree_conn->id);
141 	up_write(&sess->tree_conns_lock);
142 
143 	return __ksmbd_tree_conn_disconnect(sess, tree_conn);
144 }
145 
146 struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
147 						  unsigned int id)
148 {
149 	struct ksmbd_tree_connect *tcon;
150 
151 	down_read(&sess->tree_conns_lock);
152 	tcon = xa_load(&sess->tree_conns, id);
153 	if (tcon) {
154 		if (tcon->t_state != TREE_CONNECTED)
155 			tcon = NULL;
156 		else if (!atomic_inc_not_zero(&tcon->refcount))
157 			tcon = NULL;
158 	}
159 	up_read(&sess->tree_conns_lock);
160 
161 	return tcon;
162 }
163 
164 int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
165 {
166 	int ret = 0;
167 	struct ksmbd_tree_connect *tc;
168 	unsigned long id;
169 
170 	if (!sess)
171 		return -EINVAL;
172 
173 	down_write(&sess->tree_conns_lock);
174 	xa_for_each(&sess->tree_conns, id, tc) {
175 		if (tc->t_state == TREE_DISCONNECTED) {
176 			ret = -ENOENT;
177 			continue;
178 		}
179 		tc->t_state = TREE_DISCONNECTED;
180 
181 		xa_erase(&sess->tree_conns, tc->id);
182 		ret |= __ksmbd_tree_conn_disconnect(sess, tc);
183 	}
184 	xa_destroy(&sess->tree_conns);
185 	up_write(&sess->tree_conns_lock);
186 
187 	return ret;
188 }
189