Commit | Line | Data |
---|---|---|
0626e664 NJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> | |
4 | * Copyright (C) 2018 Samsung Electronics Co., Ltd. | |
5 | */ | |
6 | ||
7 | #include "glob.h" | |
8 | #include "oplock.h" | |
9 | #include "misc.h" | |
10 | #include <linux/sched/signal.h> | |
11 | #include <linux/workqueue.h> | |
12 | #include <linux/sysfs.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/moduleparam.h> | |
15 | ||
16 | #include "server.h" | |
17 | #include "smb_common.h" | |
18 | #include "smbstatus.h" | |
0626e664 NJ |
19 | #include "connection.h" |
20 | #include "transport_ipc.h" | |
21 | #include "mgmt/user_session.h" | |
22 | #include "crypto_ctx.h" | |
23 | #include "auth.h" | |
24 | ||
25 | int ksmbd_debug_types; | |
26 | ||
27 | struct ksmbd_server_config server_conf; | |
28 | ||
29 | enum SERVER_CTRL_TYPE { | |
30 | SERVER_CTRL_TYPE_INIT, | |
31 | SERVER_CTRL_TYPE_RESET, | |
32 | }; | |
33 | ||
34 | struct server_ctrl_struct { | |
35 | int type; | |
36 | struct work_struct ctrl_work; | |
37 | }; | |
38 | ||
39 | static DEFINE_MUTEX(ctrl_lock); | |
40 | ||
41 | static int ___server_conf_set(int idx, char *val) | |
42 | { | |
43 | if (idx >= ARRAY_SIZE(server_conf.conf)) | |
44 | return -EINVAL; | |
45 | ||
46 | if (!val || val[0] == 0x00) | |
47 | return -EINVAL; | |
48 | ||
49 | kfree(server_conf.conf[idx]); | |
50 | server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); | |
51 | if (!server_conf.conf[idx]) | |
52 | return -ENOMEM; | |
53 | return 0; | |
54 | } | |
55 | ||
56 | int ksmbd_set_netbios_name(char *v) | |
57 | { | |
58 | return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); | |
59 | } | |
60 | ||
61 | int ksmbd_set_server_string(char *v) | |
62 | { | |
63 | return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); | |
64 | } | |
65 | ||
66 | int ksmbd_set_work_group(char *v) | |
67 | { | |
68 | return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); | |
69 | } | |
70 | ||
71 | char *ksmbd_netbios_name(void) | |
72 | { | |
73 | return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; | |
74 | } | |
75 | ||
76 | char *ksmbd_server_string(void) | |
77 | { | |
78 | return server_conf.conf[SERVER_CONF_SERVER_STRING]; | |
79 | } | |
80 | ||
81 | char *ksmbd_work_group(void) | |
82 | { | |
83 | return server_conf.conf[SERVER_CONF_WORK_GROUP]; | |
84 | } | |
85 | ||
86 | /** | |
87 | * check_conn_state() - check state of server thread connection | |
95fa1ce9 | 88 | * @work: smb work containing server thread information |
0626e664 NJ |
89 | * |
90 | * Return: 0 on valid connection, otherwise 1 to reconnect | |
91 | */ | |
92 | static inline int check_conn_state(struct ksmbd_work *work) | |
93 | { | |
94 | struct smb_hdr *rsp_hdr; | |
95 | ||
96 | if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { | |
e5066499 | 97 | rsp_hdr = work->response_buf; |
0626e664 NJ |
98 | rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; |
99 | return 1; | |
100 | } | |
101 | return 0; | |
102 | } | |
103 | ||
67307023 NJ |
104 | #define SERVER_HANDLER_CONTINUE 0 |
105 | #define SERVER_HANDLER_ABORT 1 | |
0626e664 | 106 | |
64b39f4a | 107 | static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, |
fc2d1b58 | 108 | u16 *cmd) |
0626e664 NJ |
109 | { |
110 | struct smb_version_cmds *cmds; | |
fc2d1b58 | 111 | u16 command; |
0626e664 NJ |
112 | int ret; |
113 | ||
114 | if (check_conn_state(work)) | |
67307023 | 115 | return SERVER_HANDLER_CONTINUE; |
0626e664 NJ |
116 | |
117 | if (ksmbd_verify_smb_message(work)) | |
67307023 | 118 | return SERVER_HANDLER_ABORT; |
0626e664 NJ |
119 | |
120 | command = conn->ops->get_cmd_val(work); | |
121 | *cmd = command; | |
122 | ||
123 | andx_again: | |
124 | if (command >= conn->max_cmds) { | |
125 | conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); | |
67307023 | 126 | return SERVER_HANDLER_CONTINUE; |
0626e664 NJ |
127 | } |
128 | ||
129 | cmds = &conn->cmds[command]; | |
130 | if (!cmds->proc) { | |
131 | ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); | |
132 | conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); | |
67307023 | 133 | return SERVER_HANDLER_CONTINUE; |
0626e664 NJ |
134 | } |
135 | ||
136 | if (work->sess && conn->ops->is_sign_req(work, command)) { | |
137 | ret = conn->ops->check_sign_req(work); | |
138 | if (!ret) { | |
139 | conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); | |
67307023 | 140 | return SERVER_HANDLER_CONTINUE; |
0626e664 NJ |
141 | } |
142 | } | |
143 | ||
144 | ret = cmds->proc(work); | |
145 | ||
146 | if (ret < 0) | |
147 | ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); | |
148 | /* AndX commands - chained request can return positive values */ | |
149 | else if (ret > 0) { | |
150 | command = ret; | |
151 | *cmd = command; | |
152 | goto andx_again; | |
153 | } | |
154 | ||
155 | if (work->send_no_response) | |
67307023 NJ |
156 | return SERVER_HANDLER_ABORT; |
157 | return SERVER_HANDLER_CONTINUE; | |
0626e664 NJ |
158 | } |
159 | ||
160 | static void __handle_ksmbd_work(struct ksmbd_work *work, | |
070fb21e | 161 | struct ksmbd_conn *conn) |
0626e664 | 162 | { |
64b39f4a | 163 | u16 command = 0; |
0626e664 NJ |
164 | int rc; |
165 | ||
166 | if (conn->ops->allocate_rsp_buf(work)) | |
167 | return; | |
168 | ||
169 | if (conn->ops->is_transform_hdr && | |
64b39f4a | 170 | conn->ops->is_transform_hdr(work->request_buf)) { |
0626e664 NJ |
171 | rc = conn->ops->decrypt_req(work); |
172 | if (rc < 0) { | |
173 | conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); | |
174 | goto send; | |
175 | } | |
176 | ||
177 | work->encrypted = true; | |
178 | } | |
179 | ||
180 | rc = conn->ops->init_rsp_hdr(work); | |
181 | if (rc) { | |
182 | /* either uid or tid is not correct */ | |
183 | conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); | |
184 | goto send; | |
185 | } | |
186 | ||
187 | if (conn->ops->check_user_session) { | |
188 | rc = conn->ops->check_user_session(work); | |
189 | if (rc < 0) { | |
190 | command = conn->ops->get_cmd_val(work); | |
191 | conn->ops->set_rsp_status(work, | |
192 | STATUS_USER_SESSION_DELETED); | |
193 | goto send; | |
194 | } else if (rc > 0) { | |
195 | rc = conn->ops->get_ksmbd_tcon(work); | |
196 | if (rc < 0) { | |
197 | conn->ops->set_rsp_status(work, | |
198 | STATUS_NETWORK_NAME_DELETED); | |
199 | goto send; | |
200 | } | |
201 | } | |
202 | } | |
203 | ||
204 | do { | |
205 | rc = __process_request(work, conn, &command); | |
67307023 | 206 | if (rc == SERVER_HANDLER_ABORT) |
0626e664 NJ |
207 | break; |
208 | ||
209 | /* | |
210 | * Call smb2_set_rsp_credits() function to set number of credits | |
211 | * granted in hdr of smb2 response. | |
212 | */ | |
213 | if (conn->ops->set_rsp_credits) { | |
214 | spin_lock(&conn->credits_lock); | |
215 | rc = conn->ops->set_rsp_credits(work); | |
216 | spin_unlock(&conn->credits_lock); | |
217 | if (rc < 0) { | |
218 | conn->ops->set_rsp_status(work, | |
219 | STATUS_INVALID_PARAMETER); | |
220 | goto send; | |
221 | } | |
222 | } | |
223 | ||
070fb21e NJ |
224 | if (work->sess && |
225 | (work->sess->sign || smb3_11_final_sess_setup_resp(work) || | |
0626e664 NJ |
226 | conn->ops->is_sign_req(work, command))) |
227 | conn->ops->set_sign_rsp(work); | |
228 | } while (is_chained_smb2_message(work)); | |
229 | ||
230 | if (work->send_no_response) | |
231 | return; | |
232 | ||
233 | send: | |
234 | smb3_preauth_hash_rsp(work); | |
235 | if (work->sess && work->sess->enc && work->encrypted && | |
64b39f4a | 236 | conn->ops->encrypt_resp) { |
0626e664 | 237 | rc = conn->ops->encrypt_resp(work); |
360c8ee6 | 238 | if (rc < 0) |
0626e664 | 239 | conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); |
0626e664 NJ |
240 | } |
241 | ||
242 | ksmbd_conn_write(work); | |
243 | } | |
244 | ||
245 | /** | |
246 | * handle_ksmbd_work() - process pending smb work requests | |
95fa1ce9 | 247 | * @wk: smb work containing request command buffer |
0626e664 NJ |
248 | * |
249 | * called by kworker threads to processing remaining smb work requests | |
250 | */ | |
251 | static void handle_ksmbd_work(struct work_struct *wk) | |
252 | { | |
253 | struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); | |
254 | struct ksmbd_conn *conn = work->conn; | |
255 | ||
256 | atomic64_inc(&conn->stats.request_served); | |
257 | ||
258 | __handle_ksmbd_work(work, conn); | |
259 | ||
260 | ksmbd_conn_try_dequeue_request(work); | |
261 | ksmbd_free_work_struct(work); | |
a14c5738 NJ |
262 | /* |
263 | * Checking waitqueue to dropping pending requests on | |
264 | * disconnection. waitqueue_active is safe because it | |
265 | * uses atomic operation for condition. | |
266 | */ | |
267 | if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) | |
268 | wake_up(&conn->r_count_q); | |
0626e664 NJ |
269 | } |
270 | ||
271 | /** | |
272 | * queue_ksmbd_work() - queue a smb request to worker thread queue | |
273 | * for proccessing smb command and sending response | |
274 | * @conn: connection instance | |
275 | * | |
276 | * read remaining data from socket create and submit work. | |
277 | */ | |
278 | static int queue_ksmbd_work(struct ksmbd_conn *conn) | |
279 | { | |
280 | struct ksmbd_work *work; | |
281 | ||
282 | work = ksmbd_alloc_work_struct(); | |
283 | if (!work) { | |
bde1694a | 284 | pr_err("allocation for work failed\n"); |
0626e664 NJ |
285 | return -ENOMEM; |
286 | } | |
287 | ||
288 | work->conn = conn; | |
289 | work->request_buf = conn->request_buf; | |
290 | conn->request_buf = NULL; | |
291 | ||
dc8289f9 | 292 | ksmbd_init_smb_server(work); |
0626e664 NJ |
293 | |
294 | ksmbd_conn_enqueue_request(work); | |
295 | atomic_inc(&conn->r_count); | |
296 | /* update activity on connection */ | |
297 | conn->last_active = jiffies; | |
298 | INIT_WORK(&work->work, handle_ksmbd_work); | |
299 | ksmbd_queue_work(work); | |
300 | return 0; | |
301 | } | |
302 | ||
303 | static int ksmbd_server_process_request(struct ksmbd_conn *conn) | |
304 | { | |
305 | return queue_ksmbd_work(conn); | |
306 | } | |
307 | ||
308 | static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) | |
309 | { | |
310 | ksmbd_sessions_deregister(conn); | |
311 | destroy_lease_table(conn); | |
312 | return 0; | |
313 | } | |
314 | ||
315 | static void ksmbd_server_tcp_callbacks_init(void) | |
316 | { | |
317 | struct ksmbd_conn_ops ops; | |
318 | ||
319 | ops.process_fn = ksmbd_server_process_request; | |
320 | ops.terminate_fn = ksmbd_server_terminate_conn; | |
321 | ||
322 | ksmbd_conn_init_server_callbacks(&ops); | |
323 | } | |
324 | ||
325 | static void server_conf_free(void) | |
326 | { | |
327 | int i; | |
328 | ||
329 | for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { | |
330 | kfree(server_conf.conf[i]); | |
331 | server_conf.conf[i] = NULL; | |
332 | } | |
333 | } | |
334 | ||
335 | static int server_conf_init(void) | |
336 | { | |
337 | WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); | |
338 | server_conf.enforced_signing = 0; | |
339 | server_conf.min_protocol = ksmbd_min_protocol(); | |
340 | server_conf.max_protocol = ksmbd_max_protocol(); | |
341 | server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; | |
342 | #ifdef CONFIG_SMB_SERVER_KERBEROS5 | |
343 | server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | | |
344 | KSMBD_AUTH_MSKRB5; | |
345 | #endif | |
346 | return 0; | |
347 | } | |
348 | ||
349 | static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) | |
350 | { | |
351 | int ret; | |
352 | ||
353 | ret = ksmbd_conn_transport_init(); | |
354 | if (ret) { | |
355 | server_queue_ctrl_reset_work(); | |
356 | return; | |
357 | } | |
358 | ||
359 | WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); | |
360 | } | |
361 | ||
362 | static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) | |
363 | { | |
364 | ksmbd_ipc_soft_reset(); | |
365 | ksmbd_conn_transport_destroy(); | |
366 | server_conf_free(); | |
367 | server_conf_init(); | |
368 | WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); | |
369 | } | |
370 | ||
371 | static void server_ctrl_handle_work(struct work_struct *work) | |
372 | { | |
373 | struct server_ctrl_struct *ctrl; | |
374 | ||
375 | ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); | |
376 | ||
377 | mutex_lock(&ctrl_lock); | |
378 | switch (ctrl->type) { | |
379 | case SERVER_CTRL_TYPE_INIT: | |
380 | server_ctrl_handle_init(ctrl); | |
381 | break; | |
382 | case SERVER_CTRL_TYPE_RESET: | |
383 | server_ctrl_handle_reset(ctrl); | |
384 | break; | |
385 | default: | |
386 | pr_err("Unknown server work type: %d\n", ctrl->type); | |
387 | } | |
388 | mutex_unlock(&ctrl_lock); | |
389 | kfree(ctrl); | |
390 | module_put(THIS_MODULE); | |
391 | } | |
392 | ||
393 | static int __queue_ctrl_work(int type) | |
394 | { | |
395 | struct server_ctrl_struct *ctrl; | |
396 | ||
397 | ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); | |
398 | if (!ctrl) | |
399 | return -ENOMEM; | |
400 | ||
401 | __module_get(THIS_MODULE); | |
402 | ctrl->type = type; | |
403 | INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); | |
404 | queue_work(system_long_wq, &ctrl->ctrl_work); | |
405 | return 0; | |
406 | } | |
407 | ||
408 | int server_queue_ctrl_init_work(void) | |
409 | { | |
410 | return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); | |
411 | } | |
412 | ||
413 | int server_queue_ctrl_reset_work(void) | |
414 | { | |
415 | return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); | |
416 | } | |
417 | ||
75a2d422 | 418 | static ssize_t stats_show(const struct class *class, const struct class_attribute *attr, |
070fb21e | 419 | char *buf) |
0626e664 NJ |
420 | { |
421 | /* | |
422 | * Inc this each time you change stats output format, | |
423 | * so user space will know what to do. | |
424 | */ | |
425 | static int stats_version = 2; | |
426 | static const char * const state[] = { | |
427 | "startup", | |
428 | "running", | |
429 | "reset", | |
430 | "shutdown" | |
431 | }; | |
72ee45fd | 432 | return sysfs_emit(buf, "%d %s %d %lu\n", stats_version, |
433 | state[server_conf.state], server_conf.tcp_port, | |
434 | server_conf.ipc_last_active / HZ); | |
0626e664 NJ |
435 | } |
436 | ||
75a2d422 GKH |
437 | static ssize_t kill_server_store(const struct class *class, |
438 | const struct class_attribute *attr, const char *buf, | |
070fb21e | 439 | size_t len) |
0626e664 NJ |
440 | { |
441 | if (!sysfs_streq(buf, "hard")) | |
442 | return len; | |
443 | ||
bde1694a | 444 | pr_info("kill command received\n"); |
0626e664 NJ |
445 | mutex_lock(&ctrl_lock); |
446 | WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); | |
447 | __module_get(THIS_MODULE); | |
448 | server_ctrl_handle_reset(NULL); | |
449 | module_put(THIS_MODULE); | |
450 | mutex_unlock(&ctrl_lock); | |
451 | return len; | |
452 | } | |
453 | ||
454 | static const char * const debug_type_strings[] = {"smb", "auth", "vfs", | |
070fb21e NJ |
455 | "oplock", "ipc", "conn", |
456 | "rdma"}; | |
0626e664 | 457 | |
75a2d422 | 458 | static ssize_t debug_show(const struct class *class, const struct class_attribute *attr, |
070fb21e | 459 | char *buf) |
0626e664 NJ |
460 | { |
461 | ssize_t sz = 0; | |
462 | int i, pos = 0; | |
463 | ||
464 | for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { | |
465 | if ((ksmbd_debug_types >> i) & 1) { | |
72ee45fd | 466 | pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]); |
0626e664 | 467 | } else { |
72ee45fd | 468 | pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]); |
0626e664 NJ |
469 | } |
470 | sz += pos; | |
0626e664 | 471 | } |
72ee45fd | 472 | sz += sysfs_emit_at(buf, sz, "\n"); |
0626e664 NJ |
473 | return sz; |
474 | } | |
475 | ||
75a2d422 | 476 | static ssize_t debug_store(const struct class *class, const struct class_attribute *attr, |
070fb21e | 477 | const char *buf, size_t len) |
0626e664 NJ |
478 | { |
479 | int i; | |
480 | ||
481 | for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { | |
482 | if (sysfs_streq(buf, "all")) { | |
483 | if (ksmbd_debug_types == KSMBD_DEBUG_ALL) | |
484 | ksmbd_debug_types = 0; | |
485 | else | |
486 | ksmbd_debug_types = KSMBD_DEBUG_ALL; | |
487 | break; | |
488 | } | |
489 | ||
490 | if (sysfs_streq(buf, debug_type_strings[i])) { | |
491 | if (ksmbd_debug_types & (1 << i)) | |
492 | ksmbd_debug_types &= ~(1 << i); | |
493 | else | |
494 | ksmbd_debug_types |= (1 << i); | |
495 | break; | |
496 | } | |
497 | } | |
498 | ||
499 | return len; | |
500 | } | |
501 | ||
502 | static CLASS_ATTR_RO(stats); | |
503 | static CLASS_ATTR_WO(kill_server); | |
504 | static CLASS_ATTR_RW(debug); | |
505 | ||
506 | static struct attribute *ksmbd_control_class_attrs[] = { | |
507 | &class_attr_stats.attr, | |
508 | &class_attr_kill_server.attr, | |
509 | &class_attr_debug.attr, | |
510 | NULL, | |
511 | }; | |
512 | ATTRIBUTE_GROUPS(ksmbd_control_class); | |
513 | ||
514 | static struct class ksmbd_control_class = { | |
515 | .name = "ksmbd-control", | |
0626e664 NJ |
516 | .class_groups = ksmbd_control_class_groups, |
517 | }; | |
518 | ||
519 | static int ksmbd_server_shutdown(void) | |
520 | { | |
521 | WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); | |
522 | ||
523 | class_unregister(&ksmbd_control_class); | |
524 | ksmbd_workqueue_destroy(); | |
525 | ksmbd_ipc_release(); | |
526 | ksmbd_conn_transport_destroy(); | |
0626e664 NJ |
527 | ksmbd_crypto_destroy(); |
528 | ksmbd_free_global_file_table(); | |
529 | destroy_lease_table(NULL); | |
c30f4eb8 NJ |
530 | ksmbd_work_pool_destroy(); |
531 | ksmbd_exit_file_cache(); | |
0626e664 NJ |
532 | server_conf_free(); |
533 | return 0; | |
534 | } | |
535 | ||
536 | static int __init ksmbd_server_init(void) | |
537 | { | |
538 | int ret; | |
539 | ||
540 | ret = class_register(&ksmbd_control_class); | |
541 | if (ret) { | |
bde1694a | 542 | pr_err("Unable to register ksmbd-control class\n"); |
0626e664 NJ |
543 | return ret; |
544 | } | |
545 | ||
546 | ksmbd_server_tcp_callbacks_init(); | |
547 | ||
548 | ret = server_conf_init(); | |
549 | if (ret) | |
849f59e1 | 550 | goto err_unregister; |
0626e664 | 551 | |
c30f4eb8 | 552 | ret = ksmbd_work_pool_init(); |
0626e664 | 553 | if (ret) |
849f59e1 | 554 | goto err_unregister; |
0626e664 | 555 | |
c30f4eb8 NJ |
556 | ret = ksmbd_init_file_cache(); |
557 | if (ret) | |
558 | goto err_destroy_work_pools; | |
559 | ||
0626e664 NJ |
560 | ret = ksmbd_ipc_init(); |
561 | if (ret) | |
c30f4eb8 | 562 | goto err_exit_file_cache; |
0626e664 NJ |
563 | |
564 | ret = ksmbd_init_global_file_table(); | |
565 | if (ret) | |
849f59e1 | 566 | goto err_ipc_release; |
0626e664 NJ |
567 | |
568 | ret = ksmbd_inode_hash_init(); | |
569 | if (ret) | |
849f59e1 | 570 | goto err_destroy_file_table; |
0626e664 NJ |
571 | |
572 | ret = ksmbd_crypto_create(); | |
573 | if (ret) | |
849f59e1 | 574 | goto err_release_inode_hash; |
0626e664 NJ |
575 | |
576 | ret = ksmbd_workqueue_init(); | |
577 | if (ret) | |
849f59e1 | 578 | goto err_crypto_destroy; |
e44fd508 | 579 | |
adc32821 | 580 | pr_warn_once("The ksmbd server is experimental\n"); |
e44fd508 | 581 | |
0626e664 NJ |
582 | return 0; |
583 | ||
849f59e1 DC |
584 | err_crypto_destroy: |
585 | ksmbd_crypto_destroy(); | |
586 | err_release_inode_hash: | |
587 | ksmbd_release_inode_hash(); | |
588 | err_destroy_file_table: | |
589 | ksmbd_free_global_file_table(); | |
590 | err_ipc_release: | |
591 | ksmbd_ipc_release(); | |
c30f4eb8 NJ |
592 | err_exit_file_cache: |
593 | ksmbd_exit_file_cache(); | |
594 | err_destroy_work_pools: | |
595 | ksmbd_work_pool_destroy(); | |
849f59e1 DC |
596 | err_unregister: |
597 | class_unregister(&ksmbd_control_class); | |
598 | ||
0626e664 NJ |
599 | return ret; |
600 | } | |
601 | ||
602 | /** | |
95fa1ce9 | 603 | * ksmbd_server_exit() - shutdown forker thread and free memory at module exit |
0626e664 NJ |
604 | */ |
605 | static void __exit ksmbd_server_exit(void) | |
606 | { | |
607 | ksmbd_server_shutdown(); | |
608 | ksmbd_release_inode_hash(); | |
609 | } | |
610 | ||
611 | MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); | |
612 | MODULE_VERSION(KSMBD_VERSION); | |
613 | MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); | |
614 | MODULE_LICENSE("GPL"); | |
0626e664 NJ |
615 | MODULE_SOFTDEP("pre: ecb"); |
616 | MODULE_SOFTDEP("pre: hmac"); | |
0626e664 NJ |
617 | MODULE_SOFTDEP("pre: md5"); |
618 | MODULE_SOFTDEP("pre: nls"); | |
619 | MODULE_SOFTDEP("pre: aes"); | |
620 | MODULE_SOFTDEP("pre: cmac"); | |
621 | MODULE_SOFTDEP("pre: sha256"); | |
622 | MODULE_SOFTDEP("pre: sha512"); | |
623 | MODULE_SOFTDEP("pre: aead2"); | |
624 | MODULE_SOFTDEP("pre: ccm"); | |
625 | MODULE_SOFTDEP("pre: gcm"); | |
5d2f0b10 | 626 | MODULE_SOFTDEP("pre: crc32"); |
0626e664 NJ |
627 | module_init(ksmbd_server_init) |
628 | module_exit(ksmbd_server_exit) |