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