summaryrefslogtreecommitdiff
path: root/fs/smb/client/smb1maperror.c
blob: 74530088d17d0316bb8998d52039b8982748c0e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *
 *   Copyright (c) International Business Machines  Corp., 2002,2008
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   Error mapping routines from Samba libsmb/errormap.c
 *   Copyright (C) Andrew Tridgell 2001
 *   Copyright (C) Luke Kenneth Casson Leighton 1997-2001.
 */

#include <linux/bsearch.h>
#include "cifsproto.h"
#include "smb1proto.h"
#include "smberr.h"
#include "nterr.h"
#include "cifs_debug.h"

static __always_inline int smb1_posix_error_cmp(const void *_key, const void *_pivot)
{
	__u16 key = *(__u16 *)_key;
	const struct smb_to_posix_error *pivot = _pivot;

	if (key < pivot->smb_err)
		return -1;
	if (key > pivot->smb_err)
		return 1;
	return 0;
}

static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
/*
 * Automatically generated by the `gen_smb1_mapping` script,
 * sorted by DOS error code (ascending).
 */
#include "smb1_err_dos_map.c"
};

static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
/*
 * Automatically generated by the `gen_smb1_mapping` script,
 * sorted by SRV error code (ascending).
 */
#include "smb1_err_srv_map.c"
};

/*****************************************************************************
 *convert a NT status code to a dos class/code
 *****************************************************************************/

static __always_inline int ntstatus_to_dos_cmp(const void *_key, const void *_pivot)
{
	__u32 key = *(__u32 *)_key;
	const struct ntstatus_to_dos_err *pivot = _pivot;

	if (key < pivot->ntstatus)
		return -1;
	if (key > pivot->ntstatus)
		return 1;
	return 0;
}

/* NT status -> dos error map */
static const struct ntstatus_to_dos_err ntstatus_to_dos_map[] = {
/*
 * Automatically generated by the `gen_smb1_mapping` script,
 * sorted by NT status code (ascending).
 */
#include "smb1_mapping_table.c"
};

static const struct ntstatus_to_dos_err *
search_ntstatus_to_dos_map(__u32 ntstatus)
{
	return __inline_bsearch(&ntstatus, ntstatus_to_dos_map,
				ARRAY_SIZE(ntstatus_to_dos_map),
				sizeof(struct ntstatus_to_dos_err),
				ntstatus_to_dos_cmp);
}

static const struct smb_to_posix_error *
search_mapping_table_ERRDOS(__u16 smb_err)
{
	return __inline_bsearch(&smb_err, mapping_table_ERRDOS,
				ARRAY_SIZE(mapping_table_ERRDOS),
				sizeof(struct smb_to_posix_error),
				smb1_posix_error_cmp);
}

static const struct smb_to_posix_error *
search_mapping_table_ERRSRV(__u16 smb_err)
{
	return __inline_bsearch(&smb_err, mapping_table_ERRSRV,
				ARRAY_SIZE(mapping_table_ERRSRV),
				sizeof(struct smb_to_posix_error),
				smb1_posix_error_cmp);
}

int
map_smb_to_linux_error(char *buf, bool logErr)
{
	struct smb_hdr *smb = (struct smb_hdr *)buf;
	int rc = -EIO;	/* if transport error smb error may not be set */
	__u8 smberrclass;
	__u16 smberrcode;
	const struct smb_to_posix_error *err_map = NULL;

	/* BB if NT Status codes - map NT BB */

	/* old style smb error codes */
	if (smb->Status.CifsError == 0)
		return 0;

	if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
		/* translate the newer STATUS codes to old style SMB errors
		 * and then to POSIX errors */
		__u32 err = le32_to_cpu(smb->Status.CifsError);
		const struct ntstatus_to_dos_err *map = search_ntstatus_to_dos_map(err);

		if (map) {
			if ((logErr && err != NT_STATUS_MORE_PROCESSING_REQUIRED) ||
			    (cifsFYI & CIFS_RC))
				pr_notice("Status code returned 0x%08x %s\n",
					  map->ntstatus, map->nt_errstr);

			smberrclass = map->dos_class;
			smberrcode = map->dos_code;
		} else {
			smberrclass = ERRHRD;
			smberrcode = ERRgeneral;
		}
	} else {
		smberrclass = smb->Status.DosError.ErrorClass;
		smberrcode = le16_to_cpu(smb->Status.DosError.Error);
	}

	/* old style errors */

	if (smberrclass == ERRDOS) {
		/* DOS class smb error codes - map DOS */
		/* 1 byte field no need to byte reverse */
		err_map = search_mapping_table_ERRDOS(smberrcode);
	} else if (smberrclass == ERRSRV) {
		/* server class of error codes */
		err_map = search_mapping_table_ERRSRV(smberrcode);
	}
	if (err_map)
		rc = err_map->posix_code;
	/* else ERRHRD class errors or junk  - return EIO */

	/* special cases for NT status codes which cannot be translated to DOS codes */
	if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
		__u32 err = le32_to_cpu(smb->Status.CifsError);
		if (err == (NT_STATUS_NOT_A_REPARSE_POINT))
			rc = -ENODATA;
		else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD))
			rc = -EPERM;
	}

	cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n",
		 le32_to_cpu(smb->Status.CifsError), rc);

	/* generic corrective action e.g. reconnect SMB session on
	 * ERRbaduid could be added */

	if (rc == -EIO)
		smb_EIO2(smb_eio_trace_smb1_received_error,
			 le32_to_cpu(smb->Status.CifsError),
			 le16_to_cpu(smb->Flags2));
	return rc;
}

int
map_and_check_smb_error(struct TCP_Server_Info *server,
			struct mid_q_entry *mid, bool logErr)
{
	int rc;
	struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf;

	rc = map_smb_to_linux_error((char *)smb, logErr);
	if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) {
		/* possible ERRBaduid */
		__u8 class = smb->Status.DosError.ErrorClass;
		__u16 code = le16_to_cpu(smb->Status.DosError.Error);

		/* switch can be used to handle different errors */
		if (class == ERRSRV && code == ERRbaduid) {
			cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
				code);
			cifs_signal_cifsd_for_reconnect(server, false);
		}
	}

	return rc;
}

#define DEFINE_CHECK_SORT_FUNC(__array, __field)			\
static int __init __array ## _is_sorted(void)				\
{									\
	unsigned int i;							\
									\
	/* Check whether the array is sorted in ascending order */	\
	for (i = 1; i < ARRAY_SIZE(__array); i++) {			\
		if (__array[i].__field >=				\
		    __array[i - 1].__field)				\
			continue;					\
									\
		pr_err(#__array " array order is incorrect\n");		\
		return -EINVAL;						\
	}								\
									\
	return 0;							\
}

/* ntstatus_to_dos_map_is_sorted */
DEFINE_CHECK_SORT_FUNC(ntstatus_to_dos_map, ntstatus);
/* mapping_table_ERRDOS_is_sorted */
DEFINE_CHECK_SORT_FUNC(mapping_table_ERRDOS, smb_err);
/* mapping_table_ERRSRV_is_sorted */
DEFINE_CHECK_SORT_FUNC(mapping_table_ERRSRV, smb_err);

int __init smb1_init_maperror(void)
{
	int rc;

	rc = ntstatus_to_dos_map_is_sorted();
	if (rc)
		return rc;

	rc = mapping_table_ERRDOS_is_sorted();
	if (rc)
		return rc;

	rc = mapping_table_ERRSRV_is_sorted();
	if (rc)
		return rc;

	return rc;
}

#if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
#define EXPORT_SYMBOL_FOR_SMB_TEST(sym) \
	EXPORT_SYMBOL_FOR_MODULES(sym, "smb1maperror_test")

const struct ntstatus_to_dos_err *
search_ntstatus_to_dos_map_test(__u32 ntstatus)
{
	return search_ntstatus_to_dos_map(ntstatus);
}
EXPORT_SYMBOL_FOR_SMB_TEST(search_ntstatus_to_dos_map_test);

const struct ntstatus_to_dos_err *
ntstatus_to_dos_map_test = ntstatus_to_dos_map;
EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_map_test);

unsigned int ntstatus_to_dos_num = ARRAY_SIZE(ntstatus_to_dos_map);
EXPORT_SYMBOL_FOR_SMB_TEST(ntstatus_to_dos_num);

const struct smb_to_posix_error *
search_mapping_table_ERRDOS_test(__u16 smb_err)
{
	return search_mapping_table_ERRDOS(smb_err);
}
EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRDOS_test);

const struct smb_to_posix_error *
mapping_table_ERRDOS_test = mapping_table_ERRDOS;
EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_test);

unsigned int mapping_table_ERRDOS_num = ARRAY_SIZE(mapping_table_ERRDOS);
EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRDOS_num);

const struct smb_to_posix_error *
search_mapping_table_ERRSRV_test(__u16 smb_err)
{
	return search_mapping_table_ERRSRV(smb_err);
}
EXPORT_SYMBOL_FOR_SMB_TEST(search_mapping_table_ERRSRV_test);

const struct smb_to_posix_error *
mapping_table_ERRSRV_test = mapping_table_ERRSRV;
EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_test);

unsigned int mapping_table_ERRSRV_num = ARRAY_SIZE(mapping_table_ERRSRV);
EXPORT_SYMBOL_FOR_SMB_TEST(mapping_table_ERRSRV_num);
#endif