xref: /linux/fs/smb/client/smb1maperror.c (revision fab183d632628381b466a41479489541ac0e29a0)
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