1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 *
4 * Functions which do error mapping of SMB2 status codes to POSIX errors
5 *
6 * Copyright (C) International Business Machines Corp., 2009
7 * Author(s): Steve French (sfrench@us.ibm.com)
8 *
9 */
10 #include <linux/errno.h>
11
12 #include <kunit/visibility.h>
13
14 #include "cifsproto.h"
15 #include "cifs_debug.h"
16 #include "smb2proto.h"
17 #include "smb2glob.h"
18 #include "../common/smb2status.h"
19 #include "trace.h"
20
21 static const struct status_to_posix_error smb2_error_map_table[] = {
22 /*
23 * Automatically generated by the `gen_smb2_mapping` script,
24 * sorted by NT status code (cpu-endian, ascending)
25 */
26 #include "smb2_mapping_table.c"
27 };
28
cmp_smb2_status(const void * _key,const void * _pivot)29 static __always_inline int cmp_smb2_status(const void *_key, const void *_pivot)
30 {
31 __u32 key = *(__u32 *)_key;
32 const struct status_to_posix_error *pivot = _pivot;
33
34 if (key < pivot->smb2_status)
35 return -1;
36 if (key > pivot->smb2_status)
37 return 1;
38 return 0;
39 }
40
smb2_get_err_map(__u32 smb2_status)41 static const struct status_to_posix_error *smb2_get_err_map(__u32 smb2_status)
42 {
43 const struct status_to_posix_error *err_map;
44
45 err_map = __inline_bsearch(&smb2_status, smb2_error_map_table,
46 ARRAY_SIZE(smb2_error_map_table),
47 sizeof(struct status_to_posix_error),
48 cmp_smb2_status);
49 return err_map;
50 }
51
52 int
map_smb2_to_linux_error(char * buf,bool log_err)53 map_smb2_to_linux_error(char *buf, bool log_err)
54 {
55 struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
56 int rc = -EIO;
57 __le32 smb2err = shdr->Status;
58 const struct status_to_posix_error *err_map;
59
60 if (smb2err == 0) {
61 trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId),
62 le64_to_cpu(shdr->SessionId),
63 le16_to_cpu(shdr->Command),
64 le64_to_cpu(shdr->MessageId));
65 return 0;
66 }
67
68 log_err = (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
69 (smb2err != STATUS_END_OF_FILE)) ||
70 (cifsFYI & CIFS_RC);
71
72 err_map = smb2_get_err_map(le32_to_cpu(smb2err));
73 if (!err_map)
74 goto out;
75
76 rc = err_map->posix_error;
77 if (log_err)
78 pr_notice("Status code returned 0x%08x %s\n",
79 err_map->smb2_status, err_map->status_string);
80
81 out:
82 /* on error mapping not found - return EIO */
83
84 cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n",
85 le32_to_cpu(smb2err), rc);
86
87 trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId),
88 le64_to_cpu(shdr->SessionId),
89 le16_to_cpu(shdr->Command),
90 le64_to_cpu(shdr->MessageId),
91 le32_to_cpu(smb2err), rc);
92 if (rc == -EIO)
93 smb_EIO1(smb_eio_trace_smb2_received_error, le32_to_cpu(smb2err));
94 return rc;
95 }
96
smb2_init_maperror(void)97 int __init smb2_init_maperror(void)
98 {
99 unsigned int i;
100
101 /* Check whether the array is sorted in ascending order */
102 for (i = 1; i < ARRAY_SIZE(smb2_error_map_table); i++) {
103 if (smb2_error_map_table[i].smb2_status >=
104 smb2_error_map_table[i - 1].smb2_status)
105 continue;
106
107 pr_err("smb2_error_map_table array order is incorrect\n");
108 return -EINVAL;
109 }
110
111 return 0;
112 }
113
114 #if IS_ENABLED(CONFIG_SMB_KUNIT_TESTS)
smb2_get_err_map_test(__u32 smb2_status)115 const struct status_to_posix_error *smb2_get_err_map_test(__u32 smb2_status)
116 {
117 return smb2_get_err_map(smb2_status);
118 }
119 EXPORT_SYMBOL_IF_KUNIT(smb2_get_err_map_test);
120
121 const struct status_to_posix_error *smb2_error_map_table_test = smb2_error_map_table;
122 EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_table_test);
123
124 unsigned int smb2_error_map_num = ARRAY_SIZE(smb2_error_map_table);
125 EXPORT_SYMBOL_IF_KUNIT(smb2_error_map_num);
126 #endif
127