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 | 84 | spin_lock_init(&conn->llist_lock); |
| 85 | 85 | INIT_LIST_HEAD(&conn->lock_list); |
| 86 | 86 | |
| 87 | + init_rwsem(&conn->session_lock); | |
| 88 | + | |
| 87 | 89 | down_write(&conn_list_lock); |
| 88 | 90 | list_add(&conn->conns_list, &conn_list); |
| 89 | 91 | up_write(&conn_list_lock); |
| @@ -50,6 +50,7 @@ struct ksmbd_conn { · 49 unmodified lines | ||
| 50 | 50 | struct nls_table *local_nls; |
| 51 | 51 | struct unicode_map *um; |
| 52 | 52 | struct list_head conns_list; |
| 53 | + struct rw_semaphore session_lock; | |
| 53 | 54 | /* smb session 1 per user */ |
| 54 | 55 | struct xarray sessions; |
| 55 | 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 | 74 | tree_conn->user = sess->user; |
| 75 | 75 | tree_conn->share_conf = sc; |
| 76 | + tree_conn->t_state = TREE_NEW; | |
| 76 | 77 | status.tree_conn = tree_conn; |
| 78 | + atomic_set(&tree_conn->refcount, 1); | |
| 79 | + init_waitqueue_head(&tree_conn->refcount_q); | |
| 77 | 80 | |
| 78 | 81 | ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, |
| 79 | 82 | GFP_KERNEL)); |
| @@ -93,14 +96,33 @@ out_error: · 13 unmodified lines | ||
| 93 | 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 | 111 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, |
| 97 | 112 | struct ksmbd_tree_connect *tree_conn) |
| 98 | 113 | { |
| 99 | 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 | 124 | ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); |
| 102 | 125 | ksmbd_release_tree_conn_id(sess, tree_conn->id); |
| 103 | − xa_erase(&sess->tree_conns, tree_conn->id); | |
| 104 | 126 | ksmbd_share_config_put(tree_conn->share_conf); |
| 105 | 127 | kfree(tree_conn); |
| 106 | 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 | 134 | struct ksmbd_tree_connect *tcon; |
| 113 | 135 | |
| 136 | + read_lock(&sess->tree_conns_lock); | |
| 114 | 137 | tcon = xa_load(&sess->tree_conns, id); |
| 115 | 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 | 142 | tcon = NULL; |
| 118 | 143 | } |
| 144 | + read_unlock(&sess->tree_conns_lock); | |
| 119 | 145 | |
| 120 | 146 | return tcon; |
| 121 | 147 | } |
| @@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) · 7 unmodified lines | ||
| 129 | 155 | if (!sess) |
| 130 | 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 | 168 | ret |= ksmbd_tree_conn_disconnect(sess, tc); |
| 169 | + } | |
| 134 | 170 | xa_destroy(&sess->tree_conns); |
| 135 | 171 | return ret; |
| 136 | 172 | } |
| @@ -14,7 +14,11 @@ struct ksmbd_share_config; · 13 unmodified lines | ||
| 14 | 14 | struct ksmbd_user; |
| 15 | 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 | 23 | struct ksmbd_tree_connect { |
| 20 | 24 | int id; |
| @@ -27,7 +31,9 @@ struct ksmbd_tree_connect { · 6 unmodified lines | ||
| 27 | 31 | |
| 28 | 32 | int maximal_access; |
| 29 | 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 | 39 | struct ksmbd_tree_conn_status { |
| @@ -46,6 +52,7 @@ struct ksmbd_session; · 12 unmodified lines | ||
| 46 | 52 | struct ksmbd_tree_conn_status |
| 47 | 53 | ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, |
| 48 | 54 | const char *share_name); |
| 55 | +void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); | |
| 49 | 56 | |
| 50 | 57 | int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, |
| 51 | 58 | struct ksmbd_tree_connect *tree_conn); |
| @@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 173 unmodified lines | ||
| 174 | 174 | unsigned long id; |
| 175 | 175 | struct ksmbd_session *sess; |
| 176 | 176 | |
| 177 | − down_write(&sessions_table_lock); | |
| 177 | + down_write(&conn->session_lock); | |
| 178 | 178 | xa_for_each(&conn->sessions, id, sess) { |
| 179 | 179 | if (sess->state != SMB2_SESSION_VALID || |
| 180 | 180 | time_after(jiffies, |
| @@ -185,7 +185,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) · 4 unmodified lines | ||
| 185 | 185 | continue; |
| 186 | 186 | } |
| 187 | 187 | } |
| 188 | − up_write(&sessions_table_lock); | |
| 188 | + up_write(&conn->session_lock); | |
| 189 | 189 | } |
| 190 | 190 | |
| 191 | 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 | 233 | xa_for_each(&conn->sessions, id, sess) { |
| 232 | 234 | unsigned long chann_id; |
| 233 | 235 | struct channel *chann; |
| @@ -244,7 +246,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) · 10 unmodified lines | ||
| 244 | 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 | 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 | 255 | struct ksmbd_session *sess; |
| 254 | 256 | |
| 257 | + down_read(&conn->session_lock); | |
| 255 | 258 | sess = xa_load(&conn->sessions, id); |
| 256 | 259 | if (sess) |
| 257 | 260 | sess->last_active = jiffies; |
| 261 | + up_read(&conn->session_lock); | |
| 258 | 262 | return sess; |
| 259 | 263 | } |
| 260 | 264 | |
| @@ -351,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol) · 90 unmodified lines | ||
| 351 | 355 | xa_init(&sess->ksmbd_chann_list); |
| 352 | 356 | xa_init(&sess->rpc_handle_list); |
| 353 | 357 | sess->sequence_number = 1; |
| 358 | + rwlock_init(&sess->tree_conns_lock); | |
| 354 | 359 | |
| 355 | 360 | ret = __init_smb2_session(sess); |
| 356 | 361 | if (ret) |
| @@ -60,6 +60,7 @@ struct ksmbd_session { · 59 unmodified lines | ||
| 60 | 60 | |
| 61 | 61 | struct ksmbd_file_table file_table; |
| 62 | 62 | unsigned long last_active; |
| 63 | + rwlock_t tree_conns_lock; | |
| 63 | 64 | }; |
| 64 | 65 | |
| 65 | 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 | 241 | } while (is_chained == true); |
| 242 | 242 | |
| 243 | 243 | send: |
| 244 | + if (work->tcon) | |
| 245 | + ksmbd_tree_connect_put(work->tcon); | |
| 244 | 246 | smb3_preauth_hash_rsp(work); |
| 245 | 247 | if (work->sess && work->sess->enc && work->encrypted && |
| 246 | 248 | conn->ops->encrypt_resp) { |
| @@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work) · 1992 unmodified lines | ||
| 1993 | 1993 | if (conn->posix_ext_supported) |
| 1994 | 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 | 1999 | rsp->StructureSize = cpu_to_le16(16); |
| 1997 | 2000 | out_err1: |
| 1998 | 2001 | rsp->Capabilities = 0; |
| @@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work) · 123 unmodified lines | ||
| 2122 | 2125 | |
| 2123 | 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 | 2158 | rsp->StructureSize = cpu_to_le16(4); |
| 2126 | 2159 | err = ksmbd_iov_pin_rsp(work, rsp, |
| 2127 | 2160 | sizeof(struct smb2_tree_disconnect_rsp)); |
| 2128 | 2161 | if (err) { |
| 2129 | 2162 | rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; |
| 2130 | − smb2_set_err_rsp(work); | |
| 2131 | − return err; | |
| 2163 | + goto err_out; | |
| 2132 | 2164 | } |
| 2133 | 2165 | |
| 2134 | − if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { | |
| 2135 | − ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); | |
| 2166 | + return 0; | |
| 2136 | 2167 | |
| 2137 | − rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | |
| 2138 | − smb2_set_err_rsp(work); | |
| 2139 | − return -ENOENT; | |
| 2140 | − } | |
| 2168 | +err_out: | |
| 2169 | + smb2_set_err_rsp(work); | |
| 2170 | + return err; | |
| 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 | 2191 | ksmbd_debug(SMB, "request\n"); |
| 2166 | 2192 | |
| 2167 | − sess_id = le64_to_cpu(req->hdr.SessionId); | |
| 2168 | − | |
| 2169 | − rsp->StructureSize = cpu_to_le16(4); | |
| 2170 | − err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); | |
| 2171 | − if (err) { | |
| 2172 | − rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; | |
| 2193 | + ksmbd_conn_lock(conn); | |
| 2194 | + if (!ksmbd_conn_good(conn)) { | |
| 2195 | + ksmbd_conn_unlock(conn); | |
| 2196 | + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; | |
| 2173 | 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 | 2201 | ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); |
| 2202 | + ksmbd_conn_unlock(conn); | |
| 2203 | + | |
| 2178 | 2204 | ksmbd_close_session_fds(work); |
| 2179 | 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 | 2222 | ksmbd_free_user(sess->user); |
| 2197 | 2223 | sess->user = NULL; |
| 2198 | 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 | 2233 | return 0; |
| 2200 | 2234 | } |
| 2201 | 2235 | |
| @@ -3370,8 +3404,10 @@ err_out: · 1168 unmodified lines | ||
| 3370 | 3404 | } |
| 3371 | 3405 | ksmbd_revert_fsids(work); |
| 3372 | 3406 | err_out1: |
| 3373 | − if (!rc) | |
| 3407 | + if (!rc) { | |
| 3408 | + ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); | |
| 3374 | 3409 | rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); |
| 3410 | + } | |
| 3375 | 3411 | if (rc) { |
| 3376 | 3412 | if (rc == -EINVAL) |
| 3377 | 3413 | rsp->hdr.Status = STATUS_INVALID_PARAMETER; |
| @@ -7028,10 +7064,6 @@ skip: · 3650 unmodified lines | ||
| 7028 | 7064 | |
| 7029 | 7065 | ksmbd_debug(SMB, |
| 7030 | 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 | 7067 | list_add(&smb_lock->llist, &rollback_list); |
| 7036 | 7068 | |
| 7037 | 7069 | argv = kmalloc(sizeof(void *), GFP_KERNEL); |
| @@ -7062,9 +7094,6 @@ skip: · 24 unmodified lines | ||
| 7062 | 7094 | |
| 7063 | 7095 | if (work->state != KSMBD_WORK_ACTIVE) { |
| 7064 | 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 | 7097 | locks_free_lock(flock); |
| 7069 | 7098 | |
| 7070 | 7099 | if (work->state == KSMBD_WORK_CANCELLED) { |
| @@ -7084,19 +7113,16 @@ skip: · 13 unmodified lines | ||
| 7084 | 7113 | } |
| 7085 | 7114 | |
| 7086 | 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 | 7116 | release_async_work(work); |
| 7091 | 7117 | goto retry; |
| 7092 | 7118 | } else if (!rc) { |
| 7119 | + list_add(&smb_lock->llist, &rollback_list); | |
| 7093 | 7120 | spin_lock(&work->conn->llist_lock); |
| 7094 | 7121 | list_add_tail(&smb_lock->clist, |
| 7095 | 7122 | &work->conn->lock_list); |
| 7096 | 7123 | list_add_tail(&smb_lock->flist, |
| 7097 | 7124 | &fp->lock_list); |
| 7098 | 7125 | spin_unlock(&work->conn->llist_lock); |
| 7099 | − list_add(&smb_lock->llist, &rollback_list); | |
| 7100 | 7126 | ksmbd_debug(SMB, "successful in taking lock\n"); |
| 7101 | 7127 | } else { |
| 7102 | 7128 | goto out; |
| @@ -8036,10 +8062,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) · 933 unmodified lines | ||
| 8036 | 8062 | goto err_out; |
| 8037 | 8063 | } |
| 8038 | 8064 | |
| 8039 | − opinfo_put(opinfo); | |
| 8040 | − ksmbd_fd_put(work, fp); | |
| 8041 | 8065 | opinfo->op_state = OPLOCK_STATE_NONE; |
| 8042 | 8066 | wake_up_interruptible_all(&opinfo->oplock_q); |
| 8067 | + opinfo_put(opinfo); | |
| 8068 | + ksmbd_fd_put(work, fp); | |
| 8043 | 8069 | |
| 8044 | 8070 | rsp->StructureSize = cpu_to_le16(24); |
| 8045 | 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 | 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 | 339 | if (!atomic_inc_not_zero(&fp->refcount)) |
| 337 | 340 | return NULL; |
| 338 | 341 | return fp; |
| @@ -382,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) · 43 unmodified lines | ||
| 382 | 385 | return 0; |
| 383 | 386 | |
| 384 | 387 | ft = &work->sess->file_table; |
| 385 | − read_lock(&ft->lock); | |
| 388 | + write_lock(&ft->lock); | |
| 386 | 389 | fp = idr_find(ft->idr, id); |
| 387 | 390 | if (fp) { |
| 388 | 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 | 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 | 403 | if (!fp) |
| 396 | 404 | return -EINVAL; |
| @@ -570,6 +578,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) · 173 unmodified lines | ||
| 570 | 578 | fp->tcon = work->tcon; |
| 571 | 579 | fp->volatile_id = KSMBD_NO_FID; |
| 572 | 580 | fp->persistent_id = KSMBD_NO_FID; |
| 581 | + fp->f_state = FP_NEW; | |
| 573 | 582 | fp->f_ci = ksmbd_inode_get(fp); |
| 574 | 583 | |
| 575 | 584 | if (!fp->f_ci) { |
| @@ -591,6 +600,14 @@ err_out: · 15 unmodified lines | ||
| 591 | 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 | 611 | static int |
| 595 | 612 | __close_file_table_ids(struct ksmbd_file_table *ft, |
| 596 | 613 | struct ksmbd_tree_connect *tcon, |
| @@ -60,6 +60,12 @@ struct ksmbd_inode { · 59 unmodified lines | ||
| 60 | 60 | __le32 m_fattr; |
| 61 | 61 | }; |
| 62 | 62 | |
| 63 | +enum { | |
| 64 | + FP_NEW = 0, | |
| 65 | + FP_INITED, | |
| 66 | + FP_CLOSED | |
| 67 | +}; | |
| 68 | + | |
| 63 | 69 | struct ksmbd_file { |
| 64 | 70 | struct file *filp; |
| 65 | 71 | u64 persistent_id; |
| @@ -98,6 +104,7 @@ struct ksmbd_file { · 32 unmodified lines | ||
| 98 | 104 | /* if ls is happening on directory, below is valid*/ |
| 99 | 105 | struct ksmbd_readdir_data readdir_data; |
| 100 | 106 | int dot_dotdot[2]; |
| 107 | + unsigned int f_state; | |
| 101 | 108 | }; |
| 102 | 109 | |
| 103 | 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 | 149 | int ksmbd_init_global_file_table(void); |
| 143 | 150 | void ksmbd_free_global_file_table(void); |
| 144 | 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 | 156 | * INODE hash |