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