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