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