1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 *
4 * Copyright (c) International Business Machines Corp., 2002,2008
5 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * Error mapping routines from Samba libsmb/errormap.c
8 * Copyright (C) Andrew Tridgell 2001
9 * Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
10 */
11
12 #include <linux/bsearch.h>
13 #include "cifsproto.h"
14 #include "smb1proto.h"
15 #include "smberr.h"
16 #include "nterr.h"
17 #include "cifs_debug.h"
18
smb1_posix_error_cmp(const void * _key,const void * _pivot)19 static __always_inline int smb1_posix_error_cmp(const void *_key, const void *_pivot)
20 {
21 __u16 key = *(__u16 *)_key;
22 const struct smb_to_posix_error *pivot = _pivot;
23
24 if (key < pivot->smb_err)
25 return -1;
26 if (key > pivot->smb_err)
27 return 1;
28 return 0;
29 }
30
31 static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
32 /*
33 * Automatically generated by the `gen_smb1_mapping` script,
34 * sorted by DOS error code (ascending).
35 */
36 #include "smb1_err_dos_map.c"
37 };
38
39 static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
40 /*
41 * Automatically generated by the `gen_smb1_mapping` script,
42 * sorted by SRV error code (ascending).
43 */
44 #include "smb1_err_srv_map.c"
45 };
46
47 /*****************************************************************************
48 *convert a NT status code to a dos class/code
49 *****************************************************************************/
50
ntstatus_to_dos_cmp(const void * _key,const void * _pivot)51 static __always_inline int ntstatus_to_dos_cmp(const void *_key, const void *_pivot)
52 {
53 __u32 key = *(__u32 *)_key;
54 const struct ntstatus_to_dos_err *pivot = _pivot;
55
56 if (key < pivot->ntstatus)
57 return -1;
58 if (key > pivot->ntstatus)
59 return 1;
60 return 0;
61 }
62
63 /* NT status -> dos error map */
64 static const struct ntstatus_to_dos_err ntstatus_to_dos_map[] = {
65 /*
66 * Automatically generated by the `gen_smb1_mapping` script,
67 * sorted by NT status code (ascending).
68 */
69 #include "smb1_mapping_table.c"
70 };
71
72 static const struct ntstatus_to_dos_err *
search_ntstatus_to_dos_map(__u32 ntstatus)73 search_ntstatus_to_dos_map(__u32 ntstatus)
74 {
75 return __inline_bsearch(&ntstatus, ntstatus_to_dos_map,
76 ARRAY_SIZE(ntstatus_to_dos_map),
77 sizeof(struct ntstatus_to_dos_err),
78 ntstatus_to_dos_cmp);
79 }
80
81 static const struct smb_to_posix_error *
search_mapping_table_ERRDOS(__u16 smb_err)82 search_mapping_table_ERRDOS(__u16 smb_err)
83 {
84 return __inline_bsearch(&smb_err, mapping_table_ERRDOS,
85 ARRAY_SIZE(mapping_table_ERRDOS),
86 sizeof(struct smb_to_posix_error),
87 smb1_posix_error_cmp);
88 }
89
90 static const struct smb_to_posix_error *
search_mapping_table_ERRSRV(__u16 smb_err)91 search_mapping_table_ERRSRV(__u16 smb_err)
92 {
93 return __inline_bsearch(&smb_err, mapping_table_ERRSRV,
94 ARRAY_SIZE(mapping_table_ERRSRV),
95 sizeof(struct smb_to_posix_error),
96 smb1_posix_error_cmp);
97 }
98
99 int
map_smb_to_linux_error(char * buf,bool logErr)100 map_smb_to_linux_error(char *buf, bool logErr)
101 {
102 struct smb_hdr *smb = (struct smb_hdr *)buf;
103 int rc = -EIO; /* if transport error smb error may not be set */
104 __u8 smberrclass;
105 __u16 smberrcode;
106 const struct smb_to_posix_error *err_map = NULL;
107
108 /* BB if NT Status codes - map NT BB */
109
110 /* old style smb error codes */
111 if (smb->Status.CifsError == 0)
112 return 0;
113
114 if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
115 /* translate the newer STATUS codes to old style SMB errors
116 * and then to POSIX errors */
117 __u32 err = le32_to_cpu(smb->Status.CifsError);
118 const struct ntstatus_to_dos_err *map = search_ntstatus_to_dos_map(err);
119
120 if (map) {
121 if ((logErr && err != NT_STATUS_MORE_PROCESSING_REQUIRED) ||
122 (cifsFYI & CIFS_RC))
123 pr_notice("Status code returned 0x%08x %s\n",
124 map->ntstatus, map->nt_errstr);
125
126 smberrclass = map->dos_class;
127 smberrcode = map->dos_code;
128 } else {
129 smberrclass = ERRHRD;
130 smberrcode = ERRgeneral;
131 }
132 } else {
133 smberrclass = smb->Status.DosError.ErrorClass;
134 smberrcode = le16_to_cpu(smb->Status.DosError.Error);
135 }
136
137 /* old style errors */
138
139 if (smberrclass == ERRDOS) {
140 /* DOS class smb error codes - map DOS */
141 /* 1 byte field no need to byte reverse */
142 err_map = search_mapping_table_ERRDOS(smberrcode);
143 } else if (smberrclass == ERRSRV) {
144 /* server class of error codes */
145 err_map = search_mapping_table_ERRSRV(smberrcode);
146 }
147 if (err_map)
148 rc = err_map->posix_code;
149 /* else ERRHRD class errors or junk - return EIO */
150
151 /* special cases for NT status codes which cannot be translated to DOS codes */
152 if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
153 __u32 err = le32_to_cpu(smb->Status.CifsError);
154 if (err == (NT_STATUS_NOT_A_REPARSE_POINT))
155 rc = -ENODATA;
156 else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD))
157 rc = -EPERM;
158 }
159
160 cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n",
161 le32_to_cpu(smb->Status.CifsError), rc);
162
163 /* generic corrective action e.g. reconnect SMB session on
164 * ERRbaduid could be added */
165
166 if (rc == -EIO)
167 smb_EIO2(smb_eio_trace_smb1_received_error,
168 le32_to_cpu(smb->Status.CifsError),
169 le16_to_cpu(smb->Flags2));
170 return rc;
171 }
172
173 int
map_and_check_smb_error(struct TCP_Server_Info * server,struct mid_q_entry * mid,bool logErr)174 map_and_check_smb_error(struct TCP_Server_Info *server,
175 struct mid_q_entry *mid, bool logErr)
176 {
177 int rc;
178 struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf;
179
180 rc = map_smb_to_linux_error((char *)smb, logErr);
181 if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) {
182 /* possible ERRBaduid */
183 __u8 class = smb->Status.DosError.ErrorClass;
184 __u16 code = le16_to_cpu(smb->Status.DosError.Error);
185
186 /* switch can be used to handle different errors */
187 if (class == ERRSRV && code == ERRbaduid) {
188 cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
189 code);
190 cifs_signal_cifsd_for_reconnect(server, false);
191 }
192 }
193
194 return rc;
195 }
196
197 #define DEFINE_CHECK_SORT_FUNC(__array, __field) \
198 static int __init __array ## _is_sorted(void) \
199 { \
200 unsigned int i; \
201 \
202 /* Check whether the array is sorted in ascending order */ \
203 for (i = 1; i < ARRAY_SIZE(__array); i++) { \
204 if (__array[i].__field >= \
205 __array[i - 1].__field) \
206 continue; \
207 \
208 pr_err(#__array " array order is incorrect\n"); \
209 return -EINVAL; \
210 } \
211 \
212 return 0; \
213 }
214
215 /* ntstatus_to_dos_map_is_sorted */
216 DEFINE_CHECK_SORT_FUNC(ntstatus_to_dos_map, ntstatus);
217 /* mapping_table_ERRDOS_is_sorted */
218 DEFINE_CHECK_SORT_FUNC(mapping_table_ERRDOS, smb_err);
219 /* mapping_table_ERRSRV_is_sorted */
220 DEFINE_CHECK_SORT_FUNC(mapping_table_ERRSRV, smb_err);
221
smb1_init_maperror(void)222 int __init smb1_init_maperror(void)
223 {
224 int rc;
225
226 rc = ntstatus_to_dos_map_is_sorted();
227 if (rc)
228 return rc;
229
230 rc = mapping_table_ERRDOS_is_sorted();
231 if (rc)
232 return rc;
233
234 rc = mapping_table_ERRSRV_is_sorted();
235 if (rc)
236 return rc;
237
238 return rc;
239 }
240
241 #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
242 #define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \
243 EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test")
244
245 const struct ntstatus_to_dos_err *
search_ntstatus_to_dos_map_test(__u32 ntstatus)246 search_ntstatus_to_dos_map_test(__u32 ntstatus)
247 {
248 return search_ntstatus_to_dos_map(ntstatus);
249 }
250 EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test);
251
252 const struct ntstatus_to_dos_err *
253 ntstatus_to_dos_map_test = ntstatus_to_dos_map;
254 EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test);
255
256 unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map);
257 EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num);
258
259 const struct smb_to_posix_error *
search_mapping_table_ERRDOS_test(__u16 smb_err)260 search_mapping_table_ERRDOS_test(__u16 smb_err)
261 {
262 return search_mapping_table_ERRDOS(smb_err);
263 }
264 EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test);
265
266 const struct smb_to_posix_error *
267 mapping_table_ERRDOS_test = mapping_table_ERRDOS;
268 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test);
269
270 unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS);
271 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num);
272
273 const struct smb_to_posix_error *
search_mapping_table_ERRSRV_test(__u16 smb_err)274 search_mapping_table_ERRSRV_test(__u16 smb_err)
275 {
276 return search_mapping_table_ERRSRV(smb_err);
277 }
278 EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test);
279
280 const struct smb_to_posix_error *
281 mapping_table_ERRSRV_test = mapping_table_ERRSRV;
282 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test);
283
284 unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV);
285 EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num);
286 #endif
287