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 13 #include "tree_connect.h" 14 #include "user_config.h" 15 #include "share_config.h" 16 #include "user_session.h" 17 18 struct ksmbd_tree_conn_status 19 ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, 20 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 int ret; 28 29 sc = ksmbd_share_config_get(conn->um, share_name); 30 if (!sc) 31 return status; 32 33 tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); 34 if (!tree_conn) { 35 status.ret = -ENOMEM; 36 goto out_error; 37 } 38 39 tree_conn->id = ksmbd_acquire_tree_conn_id(sess); 40 if (tree_conn->id < 0) { 41 status.ret = -EINVAL; 42 goto out_error; 43 } 44 45 peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); 46 resp = ksmbd_ipc_tree_connect_request(sess, 47 sc, 48 tree_conn, 49 peer_addr); 50 if (!resp) { 51 status.ret = -EINVAL; 52 goto out_error; 53 } 54 55 status.ret = resp->status; 56 if (status.ret != KSMBD_TREE_CONN_STATUS_OK) 57 goto out_error; 58 59 tree_conn->flags = resp->connection_flags; 60 if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { 61 struct ksmbd_share_config *new_sc; 62 63 ksmbd_share_config_del(sc); 64 new_sc = ksmbd_share_config_get(conn->um, share_name); 65 if (!new_sc) { 66 pr_err("Failed to update stale share config\n"); 67 status.ret = -ESTALE; 68 goto out_error; 69 } 70 ksmbd_share_config_put(sc); 71 sc = new_sc; 72 } 73 74 tree_conn->user = sess->user; 75 tree_conn->share_conf = sc; 76 tree_conn->t_state = TREE_NEW; 77 status.tree_conn = tree_conn; 78 atomic_set(&tree_conn->refcount, 1); 79 init_waitqueue_head(&tree_conn->refcount_q); 80 81 ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, 82 GFP_KERNEL)); 83 if (ret) { 84 status.ret = -ENOMEM; 85 goto out_error; 86 } 87 kvfree(resp); 88 return status; 89 90 out_error: 91 if (tree_conn) 92 ksmbd_release_tree_conn_id(sess, tree_conn->id); 93 ksmbd_share_config_put(sc); 94 kfree(tree_conn); 95 kvfree(resp); 96 return status; 97 } 98 99 void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) 100 { 101 /* 102 * Checking waitqueue to releasing tree connect on 103 * tree disconnect. waitqueue_active is safe because it 104 * uses atomic operation for condition. 105 */ 106 if (!atomic_dec_return(&tcon->refcount) && 107 waitqueue_active(&tcon->refcount_q)) 108 wake_up(&tcon->refcount_q); 109 } 110 111 int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, 112 struct ksmbd_tree_connect *tree_conn) 113 { 114 int ret; 115 116 write_lock(&sess->tree_conns_lock); 117 xa_erase(&sess->tree_conns, tree_conn->id); 118 write_unlock(&sess->tree_conns_lock); 119 120 if (!atomic_dec_and_test(&tree_conn->refcount)) 121 wait_event(tree_conn->refcount_q, 122 atomic_read(&tree_conn->refcount) == 0); 123 124 ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); 125 ksmbd_release_tree_conn_id(sess, tree_conn->id); 126 ksmbd_share_config_put(tree_conn->share_conf); 127 kfree(tree_conn); 128 return ret; 129 } 130 131 struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, 132 unsigned int id) 133 { 134 struct ksmbd_tree_connect *tcon; 135 136 read_lock(&sess->tree_conns_lock); 137 tcon = xa_load(&sess->tree_conns, id); 138 if (tcon) { 139 if (tcon->t_state != TREE_CONNECTED) 140 tcon = NULL; 141 else if (!atomic_inc_not_zero(&tcon->refcount)) 142 tcon = NULL; 143 } 144 read_unlock(&sess->tree_conns_lock); 145 146 return tcon; 147 } 148 149 int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) 150 { 151 int ret = 0; 152 struct ksmbd_tree_connect *tc; 153 unsigned long id; 154 155 if (!sess) 156 return -EINVAL; 157 158 xa_for_each(&sess->tree_conns, id, tc) { 159 write_lock(&sess->tree_conns_lock); 160 if (tc->t_state == TREE_DISCONNECTED) { 161 write_unlock(&sess->tree_conns_lock); 162 ret = -ENOENT; 163 continue; 164 } 165 tc->t_state = TREE_DISCONNECTED; 166 write_unlock(&sess->tree_conns_lock); 167 168 ret |= ksmbd_tree_conn_disconnect(sess, tc); 169 } 170 xa_destroy(&sess->tree_conns); 171 return ret; 172 } 173