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