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