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