tree_connect.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2018 Samsung Electronics Co., Ltd.
  4. */
  5. #include <linux/list.h>
  6. #include <linux/slab.h>
  7. #include <linux/xarray.h>
  8. #include "../transport_ipc.h"
  9. #include "../connection.h"
  10. #include "tree_connect.h"
  11. #include "user_config.h"
  12. #include "share_config.h"
  13. #include "user_session.h"
  14. struct ksmbd_tree_conn_status
  15. ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
  16. {
  17. struct ksmbd_tree_conn_status status = {-ENOENT, NULL};
  18. struct ksmbd_tree_connect_response *resp = NULL;
  19. struct ksmbd_share_config *sc;
  20. struct ksmbd_tree_connect *tree_conn = NULL;
  21. struct sockaddr *peer_addr;
  22. struct ksmbd_conn *conn = work->conn;
  23. struct ksmbd_session *sess = work->sess;
  24. int ret;
  25. sc = ksmbd_share_config_get(work, share_name);
  26. if (!sc)
  27. return status;
  28. tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect),
  29. KSMBD_DEFAULT_GFP);
  30. if (!tree_conn) {
  31. status.ret = -ENOMEM;
  32. goto out_error;
  33. }
  34. tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
  35. if (tree_conn->id < 0) {
  36. status.ret = -EINVAL;
  37. goto out_error;
  38. }
  39. peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn);
  40. resp = ksmbd_ipc_tree_connect_request(sess,
  41. sc,
  42. tree_conn,
  43. peer_addr);
  44. if (!resp) {
  45. status.ret = -EINVAL;
  46. goto out_error;
  47. }
  48. status.ret = resp->status;
  49. if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
  50. goto out_error;
  51. tree_conn->flags = resp->connection_flags;
  52. if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) {
  53. struct ksmbd_share_config *new_sc;
  54. ksmbd_share_config_del(sc);
  55. new_sc = ksmbd_share_config_get(work, share_name);
  56. if (!new_sc) {
  57. pr_err("Failed to update stale share config\n");
  58. status.ret = -ESTALE;
  59. goto out_error;
  60. }
  61. ksmbd_share_config_put(sc);
  62. sc = new_sc;
  63. }
  64. tree_conn->user = sess->user;
  65. tree_conn->share_conf = sc;
  66. tree_conn->t_state = TREE_NEW;
  67. status.tree_conn = tree_conn;
  68. atomic_set(&tree_conn->refcount, 1);
  69. init_waitqueue_head(&tree_conn->refcount_q);
  70. ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
  71. KSMBD_DEFAULT_GFP));
  72. if (ret) {
  73. status.ret = -ENOMEM;
  74. goto out_error;
  75. }
  76. kvfree(resp);
  77. return status;
  78. out_error:
  79. if (tree_conn)
  80. ksmbd_release_tree_conn_id(sess, tree_conn->id);
  81. ksmbd_share_config_put(sc);
  82. kfree(tree_conn);
  83. kvfree(resp);
  84. return status;
  85. }
  86. void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
  87. {
  88. /*
  89. * Checking waitqueue to releasing tree connect on
  90. * tree disconnect. waitqueue_active is safe because it
  91. * uses atomic operation for condition.
  92. */
  93. if (!atomic_dec_return(&tcon->refcount) &&
  94. waitqueue_active(&tcon->refcount_q))
  95. wake_up(&tcon->refcount_q);
  96. }
  97. int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
  98. struct ksmbd_tree_connect *tree_conn)
  99. {
  100. int ret;
  101. write_lock(&sess->tree_conns_lock);
  102. xa_erase(&sess->tree_conns, tree_conn->id);
  103. write_unlock(&sess->tree_conns_lock);
  104. if (!atomic_dec_and_test(&tree_conn->refcount))
  105. wait_event(tree_conn->refcount_q,
  106. atomic_read(&tree_conn->refcount) == 0);
  107. ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
  108. ksmbd_release_tree_conn_id(sess, tree_conn->id);
  109. ksmbd_share_config_put(tree_conn->share_conf);
  110. kfree(tree_conn);
  111. return ret;
  112. }
  113. struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
  114. unsigned int id)
  115. {
  116. struct ksmbd_tree_connect *tcon;
  117. read_lock(&sess->tree_conns_lock);
  118. tcon = xa_load(&sess->tree_conns, id);
  119. if (tcon) {
  120. if (tcon->t_state != TREE_CONNECTED)
  121. tcon = NULL;
  122. else if (!atomic_inc_not_zero(&tcon->refcount))
  123. tcon = NULL;
  124. }
  125. read_unlock(&sess->tree_conns_lock);
  126. return tcon;
  127. }
  128. int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
  129. {
  130. int ret = 0;
  131. struct ksmbd_tree_connect *tc;
  132. unsigned long id;
  133. if (!sess)
  134. return -EINVAL;
  135. xa_for_each(&sess->tree_conns, id, tc) {
  136. write_lock(&sess->tree_conns_lock);
  137. if (tc->t_state == TREE_DISCONNECTED) {
  138. write_unlock(&sess->tree_conns_lock);
  139. ret = -ENOENT;
  140. continue;
  141. }
  142. tc->t_state = TREE_DISCONNECTED;
  143. write_unlock(&sess->tree_conns_lock);
  144. ret |= ksmbd_tree_conn_disconnect(sess, tc);
  145. }
  146. xa_destroy(&sess->tree_conns);
  147. return ret;
  148. }