Pull smb server fixes from Steve French: "Six SMB3 server fixes for various races found by RO0T Lab of Huawei: - Fix oops when racing between oplock break ack and freeing file - Simultaneous request fixes for parallel logoffs, and for parallel lock requests - Fixes for tree disconnect race, session expire race, and close/open race" * tag '6.6-rc4-ksmbd-server-fixes' of git://git.samba.org/ksmbd: ksmbd: fix race condition between tree conn lookup and disconnect ksmbd: fix race condition from parallel smb2 lock requests ksmbd: fix race condition from parallel smb2 logoff requests ksmbd: fix uaf in smb20_oplock_break_ack ksmbd: fix race condition with fp ksmbd: fix race condition between session lookup and expire
Unified · Split
| @@ -84,6 +84,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) · 83 unmodified lines | |||
| 84 | spin_lock_init(&conn->llist_lock); | 84 | spin_lock_init(&conn->llist_lock); |
| 85 | INIT_LIST_HEAD(&conn->lock_list); | 85 | INIT_LIST_HEAD(&conn->lock_list); |
| 86 | 86 | ||
| 87 | init_rwsem(&conn->session_lock); | ||
| 88 | |||
| 87 | down_write(&conn_list_lock); | 89 | down_write(&conn_list_lock); |
| 88 | list_add(&conn->conns_list, &conn_list); | 90 | list_add(&conn->conns_list, &conn_list); |
| 89 | up_write(&conn_list_lock); | 91 | up_write(&conn_list_lock); |
| @@ -50,6 +50,7 @@ struct ksmbd_conn { · 49 unmodified lines | |||
| 50 | struct nls_table *local_nls; | 50 | struct nls_table *local_nls; |
| 51 | struct unicode_map *um; | 51 | struct unicode_map *um; |
| 52 | struct list_head conns_list; | 52 | struct list_head conns_list; |
| 53 | struct rw_semaphore session_lock; | ||
| 53 | /* smb session 1 per user */ | 54 | /* smb session 1 per user */ |
| 54 | struct xarray sessions; | 55 | struct xarray sessions; |
| 55 | unsigned long last_active; | 56 | unsigned long last_active; |
| @@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, · 72 unmodified lines | |||
| 73 | 73 | ||
| 74 | tree_conn->user = sess->user; | 74 | tree_conn->user = sess->user; |
| 75 | tree_conn->share_conf = sc; | 75 | tree_conn->share_conf = sc; |
| 76 | tree_conn->t_state = TREE_NEW; | ||
| 76 | status.tree_conn = tree_conn; | 77 | status.tree_conn = tree_conn; |
| 78 | atomic_set(&tree_conn->refcount, 1); | ||
| 79 | init_waitqueue_head(&tree_conn->refcount_q); | ||
| 77 | 80 | ||
| 78 | ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, | 81 | ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, |
| 79 | GFP_KERNEL)); | 82 | GFP_KERNEL)); |
| @@ -93,14 +96,33 @@ out_error: · 13 unmodified lines | |||
| 93 | return status; | 96 | return status; |
| 94 | } | 97 | } |
| 95 | 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 | |||
| 96 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, | 111 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, |
| 97 | struct ksmbd_tree_connect *tree_conn) | 112 | struct ksmbd_tree_connect *tree_conn) |
| 98 | { | 113 | { |
| 99 | int ret; | 114 | int ret; |
| 100 | 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 | |||
| 101 | ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); | 124 | ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); |
| 102 | ksmbd_release_tree_conn_id(sess, tree_conn->id); | 125 | ksmbd_release_tree_conn_id(sess, tree_conn->id); |
| 103 | xa_erase(&sess->tree_conns, tree_conn->id); | ||
| 104 | ksmbd_share_config_put(tree_conn->share_conf); | 126 | ksmbd_share_config_put(tree_conn->share_conf); |
| 105 | kfree(tree_conn); | 127 | kfree(tree_conn); |
| 106 | return ret; | 128 | return ret; |
| @@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, · 4 unmodified lines | |||
| 111 | { | 133 | { |
| 112 | struct ksmbd_tree_connect *tcon; | 134 | struct ksmbd_tree_connect *tcon; |
| 113 | 135 | ||
| 136 | read_lock(&sess->tree_conns_lock); | ||
| 114 | tcon = xa_load(&sess->tree_conns, id); | 137 | tcon = xa_load(&sess->tree_conns, id); |
| 115 | if (tcon) { | 138 | if (tcon) { |
| 116 | if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) | 139 | if (tcon->t_state != TREE_CONNECTED) |
| 140 | tcon = NULL; | ||
| 141 | else if (!atomic_inc_not_zero(&tcon->refcount)) | ||
| 117 | tcon = NULL; | 142 | tcon = NULL; |
| 118 | } | 143 | } |
| 144 | read_unlock(&sess->tree_conns_lock); | ||
| 119 | 145 | ||
| 120 | return tcon; | 146 | return tcon; |
| 121 | } | 147 | } |
| @@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) · 7 unmodified lines | |||
| 129 | if (!sess) | 155 | if (!sess) |
| 130 | return -EINVAL; | 156 | return -EINVAL; |
| 131 | 157 | ||
| 132 | xa_for_each(&sess->tree_conns, id, tc) | 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 | |||
| 133 | ret |= ksmbd_tree_conn_disconnect(sess, tc); | 168 | ret |= ksmbd_tree_conn_disconnect(sess, tc); |
| 169 | } | ||
| 134 | xa_destroy(&sess->tree_conns); | 170 | xa_destroy(&sess->tree_conns); |
| 135 | return ret; | 171 | return ret; |
| 136 | } | 172 | } |
| @@ -14,7 +14,11 @@ struct ksmbd_share_config; · 13 unmodified lines | |||
| 14 | struct ksmbd_user; | 14 | struct ksmbd_user; |
| 15 | struct ksmbd_conn; | 15 | struct ksmbd_conn; |
| 16 | 16 | ||
| 17 | #define TREE_CONN_EXPIRE 1 | 17 | enum { |
| 18 | TREE_NEW = 0, | ||
| 19 | TREE_CONNECTED, | ||
| 20 | TREE_DISCONNECTED | ||
| 21 | }; | ||
| 18 | 22 | ||
| 19 | struct ksmbd_tree_connect { | 23 | struct ksmbd_tree_connect { |
| 20 | int id; | 24 | int id; |
| @@ -27,7 +31,9 @@ struct ksmbd_tree_connect { · 6 unmodified lines | |||
| 27 | 31 | ||
| 28 | int maximal_access; | 32 | int maximal_access; |
| 29 | bool posix_extensions; | 33 | bool posix_extensions; |
| 30 | unsigned long status; | 34 | atomic_t refcount; |
| 35 | wait_queue_head_t refcount_q; | ||
| 36 | unsigned int t_state; | ||
| 31 | }; | 37 | }; |
| 32 | 38 | ||
| 33 | struct ksmbd_tree_conn_status { | 39 | struct ksmbd_tree_conn_status { |
| @@ -46,6 +52,7 @@ struct ksmbd_session; · 12 unmodified lines | |||
| 46 | struct ksmbd_tree_conn_status | 52 | struct ksmbd_tree_conn_status |
| 47 | ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, | 53 | ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, |
| 48 | const char *share_name); | 54 | const char *share_name); |
| 55 | void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); | ||
| 49 | 56 | ||
| 50 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, | 57 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, |
| 51 | struct ksmbd_tree_connect *tree_conn); | 58 | struct ksmbd_tree_connect *tree_conn); |
| @@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 173 unmodified lines | |||
| 174 | unsigned long id; | 174 | unsigned long id; |
| 175 | struct ksmbd_session *sess; | 175 | struct ksmbd_session *sess; |
| 176 | 176 | ||
| 177 | down_write(&sessions_table_lock); | 177 | down_write(&conn->session_lock); |
| 178 | xa_for_each(&conn->sessions, id, sess) { | 178 | xa_for_each(&conn->sessions, id, sess) { |
| 179 | if (sess->state != SMB2_SESSION_VALID || | 179 | if (sess->state != SMB2_SESSION_VALID || |
| 180 | time_after(jiffies, | 180 | time_after(jiffies, |
| @@ -185,7 +185,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 4 unmodified lines | |||
| 185 | continue; | 185 | continue; |
| 186 | } | 186 | } |
| 187 | } | 187 | } |
| 188 | up_write(&sessions_table_lock); | 188 | up_write(&conn->session_lock); |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | int ksmbd_session_register(struct ksmbd_conn *conn, | 191 | int ksmbd_session_register(struct ksmbd_conn *conn, |
| @@ -227,7 +227,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) · 35 unmodified lines | |||
| 227 | } | 227 | } |
| 228 | } | 228 | } |
| 229 | } | 229 | } |
| 230 | up_write(&sessions_table_lock); | ||
| 230 | 231 | ||
| 232 | down_write(&conn->session_lock); | ||
| 231 | xa_for_each(&conn->sessions, id, sess) { | 233 | xa_for_each(&conn->sessions, id, sess) { |
| 232 | unsigned long chann_id; | 234 | unsigned long chann_id; |
| 233 | struct channel *chann; | 235 | struct channel *chann; |
| @@ -244,7 +246,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) · 10 unmodified lines | |||
| 244 | ksmbd_session_destroy(sess); | 246 | ksmbd_session_destroy(sess); |
| 245 | } | 247 | } |
| 246 | } | 248 | } |
| 247 | up_write(&sessions_table_lock); | 249 | up_write(&conn->session_lock); |
| 248 | } | 250 | } |
| 249 | 251 | ||
| 250 | struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, | 252 | struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, |
| @@ -252,9 +254,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, · 1 unmodified line | |||
| 252 | { | 254 | { |
| 253 | struct ksmbd_session *sess; | 255 | struct ksmbd_session *sess; |
| 254 | 256 | ||
| 257 | down_read(&conn->session_lock); | ||
| 255 | sess = xa_load(&conn->sessions, id); | 258 | sess = xa_load(&conn->sessions, id); |
| 256 | if (sess) | 259 | if (sess) |
| 257 | sess->last_active = jiffies; | 260 | sess->last_active = jiffies; |
| 261 | up_read(&conn->session_lock); | ||
| 258 | return sess; | 262 | return sess; |
| 259 | } | 263 | } |
| 260 | 264 | ||
| @@ -351,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol) · 90 unmodified lines | |||
| 351 | xa_init(&sess->ksmbd_chann_list); | 355 | xa_init(&sess->ksmbd_chann_list); |
| 352 | xa_init(&sess->rpc_handle_list); | 356 | xa_init(&sess->rpc_handle_list); |
| 353 | sess->sequence_number = 1; | 357 | sess->sequence_number = 1; |
| 358 | rwlock_init(&sess->tree_conns_lock); | ||
| 354 | 359 | ||
| 355 | ret = __init_smb2_session(sess); | 360 | ret = __init_smb2_session(sess); |
| 356 | if (ret) | 361 | if (ret) |
| @@ -60,6 +60,7 @@ struct ksmbd_session { · 59 unmodified lines | |||
| 60 | 60 | ||
| 61 | struct ksmbd_file_table file_table; | 61 | struct ksmbd_file_table file_table; |
| 62 | unsigned long last_active; | 62 | unsigned long last_active; |
| 63 | rwlock_t tree_conns_lock; | ||
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 65 | static inline int test_session_flag(struct ksmbd_session *sess, int bit) | 66 | static inline int test_session_flag(struct ksmbd_session *sess, int bit) |
| @@ -241,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, · 240 unmodified lines | |||
| 241 | } while (is_chained == true); | 241 | } while (is_chained == true); |
| 242 | 242 | ||
| 243 | send: | 243 | send: |
| 244 | if (work->tcon) | ||
| 245 | ksmbd_tree_connect_put(work->tcon); | ||
| 244 | smb3_preauth_hash_rsp(work); | 246 | smb3_preauth_hash_rsp(work); |
| 245 | if (work->sess && work->sess->enc && work->encrypted && | 247 | if (work->sess && work->sess->enc && work->encrypted && |
| 246 | conn->ops->encrypt_resp) { | 248 | conn->ops->encrypt_resp) { |
| @@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work) · 1992 unmodified lines | |||
| 1993 | if (conn->posix_ext_supported) | 1993 | if (conn->posix_ext_supported) |
| 1994 | status.tree_conn->posix_extensions = true; | 1994 | status.tree_conn->posix_extensions = true; |
| 1995 | 1995 | ||
| 1996 | write_lock(&sess->tree_conns_lock); | ||
| 1997 | status.tree_conn->t_state = TREE_CONNECTED; | ||
| 1998 | write_unlock(&sess->tree_conns_lock); | ||
| 1996 | rsp->StructureSize = cpu_to_le16(16); | 1999 | rsp->StructureSize = cpu_to_le16(16); |
| 1997 | out_err1: | 2000 | out_err1: |
| 1998 | rsp->Capabilities = 0; | 2001 | rsp->Capabilities = 0; |
| @@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work) · 123 unmodified lines | |||
| 2122 | 2125 | ||
| 2123 | ksmbd_debug(SMB, "request\n"); | 2126 | ksmbd_debug(SMB, "request\n"); |
| 2124 | 2127 | ||
| 2128 | if (!tcon) { | ||
| 2129 | ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); | ||
| 2130 | |||
| 2131 | rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | ||
| 2132 | err = -ENOENT; | ||
| 2133 | goto err_out; | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | ksmbd_close_tree_conn_fds(work); | ||
| 2137 | |||
| 2138 | write_lock(&sess->tree_conns_lock); | ||
| 2139 | if (tcon->t_state == TREE_DISCONNECTED) { | ||
| 2140 | write_unlock(&sess->tree_conns_lock); | ||
| 2141 | rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | ||
| 2142 | err = -ENOENT; | ||
| 2143 | goto err_out; | ||
| 2144 | } | ||
| 2145 | |||
| 2146 | WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); | ||
| 2147 | tcon->t_state = TREE_DISCONNECTED; | ||
| 2148 | write_unlock(&sess->tree_conns_lock); | ||
| 2149 | |||
| 2150 | err = ksmbd_tree_conn_disconnect(sess, tcon); | ||
| 2151 | if (err) { | ||
| 2152 | rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | ||
| 2153 | goto err_out; | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | work->tcon = NULL; | ||
| 2157 | |||
| 2125 | rsp->StructureSize = cpu_to_le16(4); | 2158 | rsp->StructureSize = cpu_to_le16(4); |
| 2126 | err = ksmbd_iov_pin_rsp(work, rsp, | 2159 | err = ksmbd_iov_pin_rsp(work, rsp, |
| 2127 | sizeof(struct smb2_tree_disconnect_rsp)); | 2160 | sizeof(struct smb2_tree_disconnect_rsp)); |
| 2128 | if (err) { | 2161 | if (err) { |
| 2129 | rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; | 2162 | rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; |
| 2130 | smb2_set_err_rsp(work); | 2163 | goto err_out; |
| 2131 | return err; | ||
| 2132 | } | 2164 | } |
| 2133 | 2165 | ||
| 2134 | if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { | 2166 | return 0; |
| 2135 | ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); | ||
| 2136 | 2167 | ||
| 2137 | rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | 2168 | err_out: |
| 2138 | smb2_set_err_rsp(work); | 2169 | smb2_set_err_rsp(work); |
| 2139 | return -ENOENT; | 2170 | return err; |
| 2140 | } | ||
| 2141 | 2171 | ||
| 2142 | ksmbd_close_tree_conn_fds(work); | ||
| 2143 | ksmbd_tree_conn_disconnect(sess, tcon); | ||
| 2144 | work->tcon = NULL; | ||
| 2145 | return 0; | ||
| 2146 | } | 2172 | } |
| 2147 | 2173 | ||
| 2148 | /** | 2174 | /** |
| @@ -2164,17 +2190,17 @@ int smb2_session_logoff(struct ksmbd_work *work) · 15 unmodified lines | |||
| 2164 | 2190 | ||
| 2165 | ksmbd_debug(SMB, "request\n"); | 2191 | ksmbd_debug(SMB, "request\n"); |
| 2166 | 2192 | ||
| 2167 | sess_id = le64_to_cpu(req->hdr.SessionId); | 2193 | ksmbd_conn_lock(conn); |
| 2168 | 2194 | if (!ksmbd_conn_good(conn)) { | |
| 2169 | rsp->StructureSize = cpu_to_le16(4); | 2195 | ksmbd_conn_unlock(conn); |
| 2170 | err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); | 2196 | rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; |
| 2171 | if (err) { | ||
| 2172 | rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; | ||
| 2173 | smb2_set_err_rsp(work); | 2197 | smb2_set_err_rsp(work); |
| 2174 | return err; | 2198 | return -ENOENT; |
| 2175 | } | 2199 | } |
| 2176 | 2200 | sess_id = le64_to_cpu(req->hdr.SessionId); | |
| 2177 | ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); | 2201 | ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); |
| 2202 | ksmbd_conn_unlock(conn); | ||
| 2203 | |||
| 2178 | ksmbd_close_session_fds(work); | 2204 | ksmbd_close_session_fds(work); |
| 2179 | ksmbd_conn_wait_idle(conn, sess_id); | 2205 | ksmbd_conn_wait_idle(conn, sess_id); |
| 2180 | 2206 | ||
| @@ -2196,6 +2222,14 @@ int smb2_session_logoff(struct ksmbd_work *work) · 15 unmodified lines | |||
| 2196 | ksmbd_free_user(sess->user); | 2222 | ksmbd_free_user(sess->user); |
| 2197 | sess->user = NULL; | 2223 | sess->user = NULL; |
| 2198 | ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); | 2224 | ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); |
| 2225 | |||
| 2226 | rsp->StructureSize = cpu_to_le16(4); | ||
| 2227 | err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); | ||
| 2228 | if (err) { | ||
| 2229 | rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; | ||
| 2230 | smb2_set_err_rsp(work); | ||
| 2231 | return err; | ||
| 2232 | } | ||
| 2199 | return 0; | 2233 | return 0; |
| 2200 | } | 2234 | } |
| 2201 | 2235 | ||
| @@ -3370,8 +3404,10 @@ err_out: · 1168 unmodified lines | |||
| 3370 | } | 3404 | } |
| 3371 | ksmbd_revert_fsids(work); | 3405 | ksmbd_revert_fsids(work); |
| 3372 | err_out1: | 3406 | err_out1: |
| 3373 | if (!rc) | 3407 | if (!rc) { |
| 3408 | ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); | ||
| 3374 | rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); | 3409 | rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); |
| 3410 | } | ||
| 3375 | if (rc) { | 3411 | if (rc) { |
| 3376 | if (rc == -EINVAL) | 3412 | if (rc == -EINVAL) |
| 3377 | rsp->hdr.Status = STATUS_INVALID_PARAMETER; | 3413 | rsp->hdr.Status = STATUS_INVALID_PARAMETER; |
| @@ -7028,10 +7064,6 @@ skip: · 3650 unmodified lines | |||
| 7028 | 7064 | ||
| 7029 | ksmbd_debug(SMB, | 7065 | ksmbd_debug(SMB, |
| 7030 | "would have to wait for getting lock\n"); | 7066 | "would have to wait for getting lock\n"); |
| 7031 | spin_lock(&work->conn->llist_lock); | ||
| 7032 | list_add_tail(&smb_lock->clist, | ||
| 7033 | &work->conn->lock_list); | ||
| 7034 | spin_unlock(&work->conn->llist_lock); | ||
| 7035 | list_add(&smb_lock->llist, &rollback_list); | 7067 | list_add(&smb_lock->llist, &rollback_list); |
| 7036 | 7068 | ||
| 7037 | argv = kmalloc(sizeof(void *), GFP_KERNEL); | 7069 | argv = kmalloc(sizeof(void *), GFP_KERNEL); |
| @@ -7062,9 +7094,6 @@ skip: · 24 unmodified lines | |||
| 7062 | 7094 | ||
| 7063 | if (work->state != KSMBD_WORK_ACTIVE) { | 7095 | if (work->state != KSMBD_WORK_ACTIVE) { |
| 7064 | list_del(&smb_lock->llist); | 7096 | list_del(&smb_lock->llist); |
| 7065 | spin_lock(&work->conn->llist_lock); | ||
| 7066 | list_del(&smb_lock->clist); | ||
| 7067 | spin_unlock(&work->conn->llist_lock); | ||
| 7068 | locks_free_lock(flock); | 7097 | locks_free_lock(flock); |
| 7069 | 7098 | ||
| 7070 | if (work->state == KSMBD_WORK_CANCELLED) { | 7099 | if (work->state == KSMBD_WORK_CANCELLED) { |
| @@ -7084,19 +7113,16 @@ skip: · 13 unmodified lines | |||
| 7084 | } | 7113 | } |
| 7085 | 7114 | ||
| 7086 | list_del(&smb_lock->llist); | 7115 | list_del(&smb_lock->llist); |
| 7087 | spin_lock(&work->conn->llist_lock); | ||
| 7088 | list_del(&smb_lock->clist); | ||
| 7089 | spin_unlock(&work->conn->llist_lock); | ||
| 7090 | release_async_work(work); | 7116 | release_async_work(work); |
| 7091 | goto retry; | 7117 | goto retry; |
| 7092 | } else if (!rc) { | 7118 | } else if (!rc) { |
| 7119 | list_add(&smb_lock->llist, &rollback_list); | ||
| 7093 | spin_lock(&work->conn->llist_lock); | 7120 | spin_lock(&work->conn->llist_lock); |
| 7094 | list_add_tail(&smb_lock->clist, | 7121 | list_add_tail(&smb_lock->clist, |
| 7095 | &work->conn->lock_list); | 7122 | &work->conn->lock_list); |
| 7096 | list_add_tail(&smb_lock->flist, | 7123 | list_add_tail(&smb_lock->flist, |
| 7097 | &fp->lock_list); | 7124 | &fp->lock_list); |
| 7098 | spin_unlock(&work->conn->llist_lock); | 7125 | spin_unlock(&work->conn->llist_lock); |
| 7099 | list_add(&smb_lock->llist, &rollback_list); | ||
| 7100 | ksmbd_debug(SMB, "successful in taking lock\n"); | 7126 | ksmbd_debug(SMB, "successful in taking lock\n"); |
| 7101 | } else { | 7127 | } else { |
| 7102 | goto out; | 7128 | goto out; |
| @@ -8036,10 +8062,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) · 933 unmodified lines | |||
| 8036 | goto err_out; | 8062 | goto err_out; |
| 8037 | } | 8063 | } |
| 8038 | 8064 | ||
| 8039 | opinfo_put(opinfo); | ||
| 8040 | ksmbd_fd_put(work, fp); | ||
| 8041 | opinfo->op_state = OPLOCK_STATE_NONE; | 8065 | opinfo->op_state = OPLOCK_STATE_NONE; |
| 8042 | wake_up_interruptible_all(&opinfo->oplock_q); | 8066 | wake_up_interruptible_all(&opinfo->oplock_q); |
| 8067 | opinfo_put(opinfo); | ||
| 8068 | ksmbd_fd_put(work, fp); | ||
| 8043 | 8069 | ||
| 8044 | rsp->StructureSize = cpu_to_le16(24); | 8070 | rsp->StructureSize = cpu_to_le16(24); |
| 8045 | rsp->OplockLevel = rsp_oplevel; | 8071 | rsp->OplockLevel = rsp_oplevel; |
| @@ -333,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) · 332 unmodified lines | |||
| 333 | 333 | ||
| 334 | static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) | 334 | static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) |
| 335 | { | 335 | { |
| 336 | if (fp->f_state != FP_INITED) | ||
| 337 | return NULL; | ||
| 338 | |||
| 336 | if (!atomic_inc_not_zero(&fp->refcount)) | 339 | if (!atomic_inc_not_zero(&fp->refcount)) |
| 337 | return NULL; | 340 | return NULL; |
| 338 | return fp; | 341 | return fp; |
| @@ -382,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) · 43 unmodified lines | |||
| 382 | return 0; | 385 | return 0; |
| 383 | 386 | ||
| 384 | ft = &work->sess->file_table; | 387 | ft = &work->sess->file_table; |
| 385 | read_lock(&ft->lock); | 388 | write_lock(&ft->lock); |
| 386 | fp = idr_find(ft->idr, id); | 389 | fp = idr_find(ft->idr, id); |
| 387 | if (fp) { | 390 | if (fp) { |
| 388 | set_close_state_blocked_works(fp); | 391 | set_close_state_blocked_works(fp); |
| 389 | 392 | ||
| 390 | if (!atomic_dec_and_test(&fp->refcount)) | 393 | if (fp->f_state != FP_INITED) |
| 391 | fp = NULL; | 394 | fp = NULL; |
| 395 | else { | ||
| 396 | fp->f_state = FP_CLOSED; | ||
| 397 | if (!atomic_dec_and_test(&fp->refcount)) | ||
| 398 | fp = NULL; | ||
| 399 | } | ||
| 392 | } | 400 | } |
| 393 | read_unlock(&ft->lock); | 401 | write_unlock(&ft->lock); |
| 394 | 402 | ||
| 395 | if (!fp) | 403 | if (!fp) |
| 396 | return -EINVAL; | 404 | return -EINVAL; |
| @@ -570,6 +578,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) · 173 unmodified lines | |||
| 570 | fp->tcon = work->tcon; | 578 | fp->tcon = work->tcon; |
| 571 | fp->volatile_id = KSMBD_NO_FID; | 579 | fp->volatile_id = KSMBD_NO_FID; |
| 572 | fp->persistent_id = KSMBD_NO_FID; | 580 | fp->persistent_id = KSMBD_NO_FID; |
| 581 | fp->f_state = FP_NEW; | ||
| 573 | fp->f_ci = ksmbd_inode_get(fp); | 582 | fp->f_ci = ksmbd_inode_get(fp); |
| 574 | 583 | ||
| 575 | if (!fp->f_ci) { | 584 | if (!fp->f_ci) { |
| @@ -591,6 +600,14 @@ err_out: · 15 unmodified lines | |||
| 591 | return ERR_PTR(ret); | 600 | return ERR_PTR(ret); |
| 592 | } | 601 | } |
| 593 | 602 | ||
| 603 | void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, | ||
| 604 | unsigned int state) | ||
| 605 | { | ||
| 606 | write_lock(&ft->lock); | ||
| 607 | fp->f_state = state; | ||
| 608 | write_unlock(&ft->lock); | ||
| 609 | } | ||
| 610 | |||
| 594 | static int | 611 | static int |
| 595 | __close_file_table_ids(struct ksmbd_file_table *ft, | 612 | __close_file_table_ids(struct ksmbd_file_table *ft, |
| 596 | struct ksmbd_tree_connect *tcon, | 613 | struct ksmbd_tree_connect *tcon, |
| @@ -60,6 +60,12 @@ struct ksmbd_inode { · 59 unmodified lines | |||
| 60 | __le32 m_fattr; | 60 | __le32 m_fattr; |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | enum { | ||
| 64 | FP_NEW = 0, | ||
| 65 | FP_INITED, | ||
| 66 | FP_CLOSED | ||
| 67 | }; | ||
| 68 | |||
| 63 | struct ksmbd_file { | 69 | struct ksmbd_file { |
| 64 | struct file *filp; | 70 | struct file *filp; |
| 65 | u64 persistent_id; | 71 | u64 persistent_id; |
| @@ -98,6 +104,7 @@ struct ksmbd_file { · 32 unmodified lines | |||
| 98 | /* if ls is happening on directory, below is valid*/ | 104 | /* if ls is happening on directory, below is valid*/ |
| 99 | struct ksmbd_readdir_data readdir_data; | 105 | struct ksmbd_readdir_data readdir_data; |
| 100 | int dot_dotdot[2]; | 106 | int dot_dotdot[2]; |
| 107 | unsigned int f_state; | ||
| 101 | }; | 108 | }; |
| 102 | 109 | ||
| 103 | static inline void set_ctx_actor(struct dir_context *ctx, | 110 | static inline void set_ctx_actor(struct dir_context *ctx, |
| @@ -142,6 +149,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); · 38 unmodified lines | |||
| 142 | int ksmbd_init_global_file_table(void); | 149 | int ksmbd_init_global_file_table(void); |
| 143 | void ksmbd_free_global_file_table(void); | 150 | void ksmbd_free_global_file_table(void); |
| 144 | void ksmbd_set_fd_limit(unsigned long limit); | 151 | void ksmbd_set_fd_limit(unsigned long limit); |
| 152 | void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, | ||
| 153 | unsigned int state); | ||
| 145 | 154 | ||
| 146 | /* | 155 | /* |
| 147 | * INODE hash | 156 | * INODE hash |